111
This commit is contained in:
1001
scripts/compress-fonts.js
Normal file
1001
scripts/compress-fonts.js
Normal file
File diff suppressed because it is too large
Load Diff
135
scripts/init-content-repo.js
Normal file
135
scripts/init-content-repo.js
Normal file
@@ -0,0 +1,135 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Mizuki 内容仓库初始化脚本
|
||||
* 帮助用户快速设置代码内容分离
|
||||
*/
|
||||
|
||||
import { execSync } from "child_process";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import readline from "readline";
|
||||
import { fileURLToPath } from "url";
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
const rootDir = path.resolve(__dirname, "..");
|
||||
|
||||
// 加载 .env 文件的辅助函数
|
||||
function loadEnvFile(envPath) {
|
||||
if (!fs.existsSync(envPath)) return;
|
||||
|
||||
const envContent = fs.readFileSync(envPath, "utf-8");
|
||||
envContent.split("\n").forEach((line) => {
|
||||
line = line.trim();
|
||||
if (!line || line.startsWith("#")) return;
|
||||
|
||||
const match = line.match(/^([^=]+)=(.*)$/);
|
||||
if (match) {
|
||||
const key = match[1].trim();
|
||||
let value = match[2].trim();
|
||||
value = value.replace(/^["']|["']$/g, "");
|
||||
process.env[key] = value;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 加载现有的 .env 文件
|
||||
loadEnvFile(path.join(rootDir, ".env"));
|
||||
|
||||
const rl = readline.createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout,
|
||||
});
|
||||
|
||||
function question(query) {
|
||||
return new Promise((resolve) => rl.question(query, resolve));
|
||||
}
|
||||
|
||||
function exec(command, options = {}) {
|
||||
try {
|
||||
return execSync(command, { stdio: "inherit", ...options });
|
||||
} catch (error) {
|
||||
console.error(`Command execution failed: ${command}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
console.log("Mizuki Content Repository Initialization\n");
|
||||
|
||||
console.log("Using independent repository mode to manage content\n");
|
||||
|
||||
// 询问内容仓库 URL
|
||||
const repoUrl = await question("Content repository URL: ");
|
||||
|
||||
if (!repoUrl.trim()) {
|
||||
console.error("Error: Content repository URL cannot be empty!");
|
||||
rl.close();
|
||||
return;
|
||||
}
|
||||
|
||||
// 确认信息
|
||||
console.log("\nConfiguration:");
|
||||
console.log(" Mode: Independent Repository");
|
||||
console.log(` Repository: ${repoUrl.trim()}`);
|
||||
|
||||
const confirm = await question("\nConfirm initialization? (y/n): ");
|
||||
|
||||
if (confirm.toLowerCase() !== "y") {
|
||||
console.log("Initialization cancelled");
|
||||
rl.close();
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("\nStarting initialization...\n");
|
||||
|
||||
// 创建 .env 文件
|
||||
const envPath = path.join(rootDir, ".env");
|
||||
const envContent = `# Mizuki Content Repository Configuration
|
||||
# Auto-generated by initialization script
|
||||
|
||||
CONTENT_REPO_URL=${repoUrl.trim()}
|
||||
CONTENT_DIR=./content
|
||||
|
||||
# Umami configuration (optional)
|
||||
# UMAMI_API_KEY=your_api_key_here
|
||||
|
||||
# bcrypt configuration
|
||||
BCRYPT_SALT_ROUNDS=12
|
||||
`;
|
||||
|
||||
fs.writeFileSync(envPath, envContent);
|
||||
console.log("Created .env file");
|
||||
|
||||
// 同步内容
|
||||
console.log("Synchronizing content repository...");
|
||||
try {
|
||||
exec("pnpm run sync-content", {
|
||||
cwd: rootDir,
|
||||
env: {
|
||||
...process.env,
|
||||
CONTENT_REPO_URL: repoUrl.trim(),
|
||||
},
|
||||
});
|
||||
console.log("Content synchronized successfully");
|
||||
} catch (error) {
|
||||
console.error(
|
||||
"Content synchronization failed. Run manually: pnpm run sync-content",
|
||||
);
|
||||
}
|
||||
|
||||
// 提示后续步骤
|
||||
console.log("\nInitialization completed\n");
|
||||
console.log("\nDocumentation:");
|
||||
console.log("- Content repository: docs/CONTENT_REPOSITORY.md");
|
||||
console.log("- Migration guide: docs/MIGRATION_GUIDE.md");
|
||||
|
||||
rl.close();
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
console.error("Initialization failed:", error);
|
||||
rl.close();
|
||||
process.exit(1);
|
||||
});
|
||||
59
scripts/new-post.js
Normal file
59
scripts/new-post.js
Normal file
@@ -0,0 +1,59 @@
|
||||
/* This is a script to create a new post markdown file with front-matter */
|
||||
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
|
||||
function getDate() {
|
||||
const today = new Date();
|
||||
const year = today.getFullYear();
|
||||
const month = String(today.getMonth() + 1).padStart(2, "0");
|
||||
const day = String(today.getDate()).padStart(2, "0");
|
||||
|
||||
return `${year}-${month}-${day}`;
|
||||
}
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
|
||||
if (args.length === 0) {
|
||||
console.error(`Error: No filename argument provided
|
||||
Usage: npm run new-post -- <filename>`);
|
||||
process.exit(1); // Terminate the script and return error code 1
|
||||
}
|
||||
|
||||
let fileName = args[0];
|
||||
|
||||
// Add .md extension if not present
|
||||
const fileExtensionRegex = /\.(md|mdx)$/i;
|
||||
if (!fileExtensionRegex.test(fileName)) {
|
||||
fileName += ".md";
|
||||
}
|
||||
|
||||
const targetDir = "./src/content/posts/";
|
||||
const fullPath = path.join(targetDir, fileName);
|
||||
|
||||
if (fs.existsSync(fullPath)) {
|
||||
console.error(`Error: File ${fullPath} already exists `);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// recursive mode creates multi-level directories
|
||||
const dirPath = path.dirname(fullPath);
|
||||
if (!fs.existsSync(dirPath)) {
|
||||
fs.mkdirSync(dirPath, { recursive: true });
|
||||
}
|
||||
|
||||
const content = `---
|
||||
title: ${args[0]}
|
||||
published: ${getDate()}
|
||||
description: ''
|
||||
image: ''
|
||||
tags: []
|
||||
category: ''
|
||||
draft: false
|
||||
lang: ''
|
||||
---
|
||||
`;
|
||||
|
||||
fs.writeFileSync(path.join(targetDir, fileName), content);
|
||||
|
||||
console.log(`Post ${fullPath} created`);
|
||||
153
scripts/sync-content.js
Normal file
153
scripts/sync-content.js
Normal file
@@ -0,0 +1,153 @@
|
||||
import { execSync } from "child_process";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
const rootDir = path.resolve(__dirname, "..");
|
||||
|
||||
// 加载 .env 文件
|
||||
const envPath = path.join(rootDir, ".env");
|
||||
if (fs.existsSync(envPath)) {
|
||||
const envContent = fs.readFileSync(envPath, "utf-8");
|
||||
envContent.split("\n").forEach((line) => {
|
||||
line = line.trim();
|
||||
// 跳过注释和空行
|
||||
if (!line || line.startsWith("#")) return;
|
||||
|
||||
const match = line.match(/^([^=]+)=(.*)$/);
|
||||
if (match) {
|
||||
const key = match[1].trim();
|
||||
let value = match[2].trim();
|
||||
// 移除引号
|
||||
value = value.replace(/^["']|["']$/g, "");
|
||||
process.env[key] = value;
|
||||
}
|
||||
});
|
||||
console.log("Loaded .env configuration file\n");
|
||||
}
|
||||
|
||||
// 从环境变量读取配置
|
||||
const ENABLE_CONTENT_SYNC = process.env.ENABLE_CONTENT_SYNC !== "false"; // 默认启用
|
||||
const CONTENT_REPO_URL = process.env.CONTENT_REPO_URL || "";
|
||||
const CONTENT_DIR = process.env.CONTENT_DIR || path.join(rootDir, "content");
|
||||
|
||||
console.log("Starting content synchronization...\n");
|
||||
|
||||
// 检查是否启用内容分离
|
||||
if (!ENABLE_CONTENT_SYNC) {
|
||||
console.log("Content separation is disabled (ENABLE_CONTENT_SYNC=false)");
|
||||
console.log(
|
||||
"Tip: Local content will be used, will not sync from remote repository",
|
||||
);
|
||||
console.log(" To enable content separation feature, set in .env:");
|
||||
console.log(" ENABLE_CONTENT_SYNC=true");
|
||||
console.log(" CONTENT_REPO_URL=<your-repo-url>\n");
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// 检查内容目录是否存在
|
||||
if (!fs.existsSync(CONTENT_DIR)) {
|
||||
console.log(`Content directory does not exist: ${CONTENT_DIR}`);
|
||||
console.log("Using independent repository mode");
|
||||
|
||||
if (!CONTENT_REPO_URL) {
|
||||
console.warn("Warning: CONTENT_REPO_URL not set, will use local content");
|
||||
console.log(
|
||||
"Tip: Please set CONTENT_REPO_URL environment variable or manually create content directory",
|
||||
);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
try {
|
||||
console.log(`Cloning content repository: ${CONTENT_REPO_URL}`);
|
||||
execSync(`git clone --depth 1 ${CONTENT_REPO_URL} ${CONTENT_DIR}`, {
|
||||
stdio: "inherit",
|
||||
cwd: rootDir,
|
||||
});
|
||||
console.log("Content repository cloned successfully");
|
||||
} catch (error) {
|
||||
console.error("Clone failed:", error.message);
|
||||
process.exit(1);
|
||||
}
|
||||
} else {
|
||||
console.log(`Content directory already exists: ${CONTENT_DIR}`);
|
||||
|
||||
if (fs.existsSync(path.join(CONTENT_DIR, ".git"))) {
|
||||
try {
|
||||
console.log("Pulling latest content...");
|
||||
execSync("git pull --allow-unrelated-histories", {
|
||||
stdio: "inherit",
|
||||
cwd: CONTENT_DIR,
|
||||
});
|
||||
console.log("Content updated successfully");
|
||||
} catch (error) {
|
||||
console.warn("Content update failed:", error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 创建符号链接或复制内容
|
||||
console.log("\nSetting up content links...");
|
||||
|
||||
const contentMappings = [
|
||||
{ src: "posts", dest: "src/content/posts" },
|
||||
{ src: "spec", dest: "src/content/spec" },
|
||||
{ src: "data", dest: "src/data" },
|
||||
{ src: "images", dest: "public/images" },
|
||||
];
|
||||
|
||||
for (const mapping of contentMappings) {
|
||||
const srcPath = path.join(CONTENT_DIR, mapping.src);
|
||||
const destPath = path.join(rootDir, mapping.dest);
|
||||
|
||||
if (!fs.existsSync(srcPath)) {
|
||||
console.log(`Skipping non-existent source: ${mapping.src}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 如果目标已存在且不是符号链接,备份它
|
||||
if (fs.existsSync(destPath) && !fs.lstatSync(destPath).isSymbolicLink()) {
|
||||
const backupPath = `${destPath}.backup`;
|
||||
console.log(
|
||||
`Backing up existing content: ${mapping.dest} -> ${mapping.dest}.backup`,
|
||||
);
|
||||
if (fs.existsSync(backupPath)) {
|
||||
fs.rmSync(backupPath, { recursive: true, force: true });
|
||||
}
|
||||
fs.renameSync(destPath, backupPath);
|
||||
}
|
||||
|
||||
// 删除现有的符号链接
|
||||
if (fs.existsSync(destPath)) {
|
||||
fs.unlinkSync(destPath);
|
||||
}
|
||||
|
||||
// 创建符号链接 (Windows 需要管理员权限,否则复制文件)
|
||||
try {
|
||||
const relPath = path.relative(path.dirname(destPath), srcPath);
|
||||
fs.symlinkSync(relPath, destPath, "junction");
|
||||
console.log(`Created symbolic link: ${mapping.dest} -> ${mapping.src}`);
|
||||
} catch (error) {
|
||||
console.log(`Copying content: ${mapping.src} -> ${mapping.dest}`);
|
||||
copyRecursive(srcPath, destPath);
|
||||
}
|
||||
}
|
||||
|
||||
console.log("\nContent synchronization completed\n");
|
||||
|
||||
// 递归复制函数
|
||||
function copyRecursive(src, dest) {
|
||||
if (fs.statSync(src).isDirectory()) {
|
||||
if (!fs.existsSync(dest)) {
|
||||
fs.mkdirSync(dest, { recursive: true });
|
||||
}
|
||||
const files = fs.readdirSync(src);
|
||||
for (const file of files) {
|
||||
copyRecursive(path.join(src, file), path.join(dest, file));
|
||||
}
|
||||
} else {
|
||||
fs.copyFileSync(src, dest);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user