Initial commit

This commit is contained in:
JetSprow
2026-04-29 05:12:39 +10:00
commit 27dbca9cbf
379 changed files with 43486 additions and 0 deletions

View File

@@ -0,0 +1,95 @@
import { mkdtemp, rm, writeFile } from "fs/promises";
import os from "os";
import path from "path";
import { spawn } from "child_process";
function getDatabaseUrl() {
const url = process.env.DATABASE_URL;
if (!url) {
throw new Error("DATABASE_URL 未配置");
}
return url;
}
function runCommand(
command: string,
args: string[],
options?: {
input?: string;
},
) {
return new Promise<{ stdout: string; stderr: string }>((resolve, reject) => {
const child = spawn(command, args, {
env: process.env,
stdio: "pipe",
});
let stdout = "";
let stderr = "";
child.stdout.on("data", (chunk) => {
stdout += chunk.toString();
});
child.stderr.on("data", (chunk) => {
stderr += chunk.toString();
});
child.on("error", reject);
child.on("close", (code) => {
if (code === 0) {
resolve({ stdout, stderr });
return;
}
reject(new Error(stderr || `${command} exited with code ${code}`));
});
if (options?.input) {
child.stdin.write(options.input);
}
child.stdin.end();
});
}
export async function createDatabaseBackupSql() {
const databaseUrl = getDatabaseUrl();
const { stdout } = await runCommand("pg_dump", [
"--clean",
"--if-exists",
"--no-owner",
"--no-privileges",
databaseUrl,
]);
return stdout;
}
export async function restoreDatabaseBackupSql(sql: string) {
if (!sql.trim()) {
throw new Error("备份内容不能为空");
}
const databaseUrl = getDatabaseUrl();
const tempDir = await mkdtemp(path.join(os.tmpdir(), "jboard-restore-"));
const filePath = path.join(tempDir, "restore.sql");
try {
await writeFile(filePath, sql, "utf8");
await runCommand("psql", [
databaseUrl,
"-v",
"ON_ERROR_STOP=1",
"-f",
filePath,
]);
} finally {
await rm(tempDir, { recursive: true, force: true });
}
}
export async function restoreDatabaseBackupFile(file: File) {
const sql = Buffer.from(await file.arrayBuffer()).toString("utf8");
if (!sql) {
throw new Error("无法读取备份文件");
}
await restoreDatabaseBackupSql(sql);
}