- 实现用户登录、登出、密码修改等认证功能 - 添加会话管理和权限控制中间件 - 创建图片批注组件和相关API路由 - 实现批注的增删改查功能 - 添加Docker和Git忽略配置文件 - 创建系统架构文档和开发约定说明 - 集成认证模块到前端应用路由中
47 lines
2.6 KiB
TypeScript
47 lines
2.6 KiB
TypeScript
import fs from 'node:fs';
|
||
import path from 'node:path';
|
||
import Database from 'better-sqlite3';
|
||
import { newDb } from 'pg-mem';
|
||
|
||
const tables = [
|
||
'operation_groups', 'users', 'projects', 'collections', 'notes', 'images', 'annotations',
|
||
'work_comments', 'work_versions', 'review_events', 'sessions', 'customer_sessions',
|
||
'audit_logs', 'api_keys', 'storage_configs',
|
||
] as const;
|
||
const booleanColumns: Record<string, Set<string>> = {
|
||
users: new Set(['must_change_password']), projects: new Set(['customer_access_enabled']),
|
||
};
|
||
|
||
const memory = newDb({ autoCreateForeignKeyIndices: true });
|
||
const adapter = memory.adapters.createPg();
|
||
const client = new adapter.Client();
|
||
const sqlite = new Database(path.resolve('data/app.db'), { readonly: true });
|
||
|
||
try {
|
||
await client.connect();
|
||
const schema = fs.readFileSync(path.resolve('db/postgres/schema.sql'), 'utf8')
|
||
.replace(/^BEGIN;|COMMIT;$/gm, '')
|
||
.replace(/CREATE UNIQUE INDEX IF NOT EXISTS one_active_storage_config[^;]+;/, '');
|
||
await client.query(schema);
|
||
for (const table of tables) {
|
||
const exists = sqlite.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name=?").get(table);
|
||
if (!exists) continue;
|
||
const rows = sqlite.prepare(`SELECT * FROM "${table}" ORDER BY id`).all() as Record<string, unknown>[];
|
||
for (const row of rows) {
|
||
const columns = Object.keys(row);
|
||
const values = columns.map((column) => booleanColumns[table]?.has(column) ? Boolean(row[column]) : row[column]);
|
||
await client.query(`INSERT INTO "${table}" (${columns.map((column) => `"${column}"`).join(',')}) VALUES (${columns.map((_, index) => `$${index + 1}`).join(',')})`, values);
|
||
}
|
||
const pgCount = Number((await client.query(`SELECT COUNT(*)::int AS count FROM "${table}"`)).rows[0].count);
|
||
if (pgCount !== rows.length) throw new Error(`${table} 行数不一致:SQLite=${rows.length}, PostgreSQL=${pgCount}`);
|
||
}
|
||
const current = (await client.query(`SELECT n.version_number,n.review_status,i.storage_provider FROM notes n JOIN images i ON i.note_id=n.id AND i.version_number=n.version_number WHERE n.id=1 LIMIT 1`)).rows[0];
|
||
if (!current || Number(current.version_number) < 1) throw new Error('作品版本关系未正确迁移');
|
||
const activeStorage = Number((await client.query("SELECT COUNT(*)::int AS count FROM storage_configs WHERE status='active'")).rows[0].count);
|
||
if (activeStorage > 1) throw new Error('活动对象存储配置超过一个');
|
||
process.stdout.write(`PostgreSQL schema 与迁移映射验证通过:${tables.length} 张表,当前作品 V${current.version_number},存储=${current.storage_provider}\n`);
|
||
} finally {
|
||
sqlite.close();
|
||
await client.end();
|
||
}
|