chore: 初始化仓库
中华文明全图鉴——文物全图系统(PC Web 地图 + NestJS API + 管理后台)。 含三大 IP(文物南迁北归 / 国宝海外回归 / 博物馆手艺人)、AI 文物对话、 文物地图与详情、以及 demo-video-kit 演示视频生成工具。
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
export { runMigrations } from "./migrate";
|
||||
export { runSeeds } from "./seed";
|
||||
export { getPool } from "./pool";
|
||||
@@ -0,0 +1,8 @@
|
||||
import { runMigrations } from "./migrate";
|
||||
|
||||
runMigrations()
|
||||
.then(() => process.exit(0))
|
||||
.catch((err) => {
|
||||
console.error("[migration] 失败:", err);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -0,0 +1,40 @@
|
||||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
import { getPool } from "./pool";
|
||||
|
||||
export async function runMigrations(): Promise<void> {
|
||||
const pool = getPool();
|
||||
const migrationsDir = path.resolve(__dirname, "../migrations");
|
||||
|
||||
const files = fs
|
||||
.readdirSync(migrationsDir)
|
||||
.filter((f) => f.endsWith(".sql"))
|
||||
.sort();
|
||||
|
||||
await pool.query(`
|
||||
CREATE TABLE IF NOT EXISTS _migrations (
|
||||
id SERIAL PRIMARY KEY,
|
||||
filename VARCHAR(255) UNIQUE NOT NULL,
|
||||
applied_at TIMESTAMPTZ DEFAULT NOW()
|
||||
)
|
||||
`);
|
||||
|
||||
for (const file of files) {
|
||||
const row = await pool.query(
|
||||
"SELECT id FROM _migrations WHERE filename = $1",
|
||||
[file]
|
||||
);
|
||||
if (row.rows.length > 0) {
|
||||
console.log(`[migration] skip ${file} (already applied)`);
|
||||
continue;
|
||||
}
|
||||
const sql = fs.readFileSync(path.join(migrationsDir, file), "utf-8");
|
||||
console.log(`[migration] apply ${file}`);
|
||||
await pool.query(sql);
|
||||
await pool.query("INSERT INTO _migrations (filename) VALUES ($1)", [file]);
|
||||
console.log(`[migration] done ${file}`);
|
||||
}
|
||||
|
||||
console.log("[migration] all migrations complete");
|
||||
await pool.end();
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import { Pool } from "pg";
|
||||
import * as dotenv from "dotenv";
|
||||
import * as path from "path";
|
||||
|
||||
dotenv.config({ path: path.resolve(__dirname, "../../../.env") });
|
||||
|
||||
let pool: Pool | null = null;
|
||||
|
||||
export function getPool(): Pool {
|
||||
if (!pool) {
|
||||
const url = process.env["DATABASE_URL"];
|
||||
if (!url) throw new Error("DATABASE_URL 未设置");
|
||||
pool = new Pool({ connectionString: url });
|
||||
}
|
||||
return pool;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import { runSeeds } from "./seed";
|
||||
|
||||
runSeeds()
|
||||
.then(() => process.exit(0))
|
||||
.catch((err) => {
|
||||
console.error("[seed] 失败:", err);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -0,0 +1,23 @@
|
||||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
import { getPool } from "./pool";
|
||||
|
||||
export async function runSeeds(): Promise<void> {
|
||||
const pool = getPool();
|
||||
const seedsDir = path.resolve(__dirname, "../seeds");
|
||||
|
||||
const files = fs
|
||||
.readdirSync(seedsDir)
|
||||
.filter((f) => f.endsWith(".sql"))
|
||||
.sort();
|
||||
|
||||
for (const file of files) {
|
||||
const sql = fs.readFileSync(path.join(seedsDir, file), "utf-8");
|
||||
console.log(`[seed] apply ${file}`);
|
||||
await pool.query(sql);
|
||||
console.log(`[seed] done ${file}`);
|
||||
}
|
||||
|
||||
console.log("[seed] all seeds complete");
|
||||
await pool.end();
|
||||
}
|
||||
Reference in New Issue
Block a user