feat: ship self-hosted KOC LOOP workflows

This commit is contained in:
巫凤萍
2026-08-11 23:07:26 +08:00
parent 1f1887c860
commit ddad4b7659
88 changed files with 6056 additions and 8938 deletions

View File

@@ -0,0 +1,59 @@
import { copyFile, mkdir, readdir, readFile, writeFile } from "node:fs/promises";
import path from "node:path";
const sourceRoot = process.argv[2];
const targetRoot = process.env.UPLOAD_DIR || process.argv[3];
if (!sourceRoot || !targetRoot) {
throw new Error(
"用法UPLOAD_DIR=/data/koc/uploads npm run storage:import -- /path/to/r2-export",
);
}
const contentTypes = {
".avif": "image/avif",
".gif": "image/gif",
".jpeg": "image/jpeg",
".jpg": "image/jpeg",
".png": "image/png",
".webp": "image/webp",
};
let copied = 0;
async function walk(directory) {
const entries = await readdir(directory, { withFileTypes: true });
for (const entry of entries) {
const absolute = path.join(directory, entry.name);
if (entry.isDirectory()) {
await walk(absolute);
continue;
}
if (entry.name.endsWith(".metadata.json")) continue;
const relative = path.relative(sourceRoot, absolute);
if (relative.startsWith("..") || path.isAbsolute(relative)) {
throw new Error("导入目录越界");
}
const destination = path.join(targetRoot, relative);
await mkdir(path.dirname(destination), { recursive: true });
await copyFile(absolute, destination);
const sourceMetadata = `${absolute}.metadata.json`;
const metadataDestination = `${destination}.metadata.json`;
let metadata;
try {
metadata = JSON.parse(await readFile(sourceMetadata, "utf8"));
} catch {
metadata = {
contentType:
contentTypes[path.extname(entry.name).toLowerCase()] ||
"application/octet-stream",
customMetadata: { source: "r2-export" },
uploadedAt: new Date().toISOString(),
};
}
await writeFile(metadataDestination, JSON.stringify(metadata), "utf8");
copied += 1;
}
}
await mkdir(targetRoot, { recursive: true });
await walk(sourceRoot);
console.info(`imported objects: ${copied}`);