153 lines
5.0 KiB
TypeScript
153 lines
5.0 KiB
TypeScript
|
|
import sqlite3, { OPEN_READWRITE, OPEN_CREATE } from 'sqlite3';
|
|
import path from 'path';
|
|
import fs from 'fs';
|
|
import { tableDefinitions } from './schema.registry';
|
|
import { runMigrations } from './migrations'; // +++ Import runMigrations +++
|
|
|
|
const dbDir = path.join(__dirname, '..', '..', 'data');
|
|
const dbFilename = 'nexus-terminal.db';
|
|
const dbPath = path.join(dbDir, dbFilename);
|
|
|
|
if (!fs.existsSync(dbDir)) {
|
|
try {
|
|
fs.mkdirSync(dbDir, { recursive: true });
|
|
} catch (mkdirErr: any) {
|
|
console.error(`[数据库文件系统] 创建目录 ${dbDir} 失败:`, mkdirErr.message);
|
|
throw new Error(`创建数据库目录失败: ${mkdirErr.message}`);
|
|
}
|
|
} else {
|
|
}
|
|
|
|
const verboseSqlite3 = sqlite3.verbose();
|
|
let dbInstancePromise: Promise<sqlite3.Database> | null = null;
|
|
|
|
interface RunResult {
|
|
lastID: number;
|
|
changes: number;
|
|
}
|
|
|
|
|
|
export const runDb = (db: sqlite3.Database, sql: string, params: any[] = []): Promise<RunResult> => {
|
|
return new Promise((resolve, reject) => {
|
|
db.run(sql, params, function (err: Error | null) {
|
|
if (err) {
|
|
console.error(`[数据库错误] SQL: ${sql.substring(0, 100)}... 参数: ${JSON.stringify(params)} 错误: ${err.message}`);
|
|
reject(err);
|
|
} else {
|
|
resolve({ lastID: this.lastID, changes: this.changes });
|
|
}
|
|
});
|
|
});
|
|
};
|
|
|
|
|
|
export const getDb = <T = any>(db: sqlite3.Database, sql: string, params: any[] = []): Promise<T | undefined> => {
|
|
return new Promise((resolve, reject) => {
|
|
db.get(sql, params, (err: Error | null, row: T) => {
|
|
if (err) {
|
|
console.error(`[数据库错误] SQL: ${sql.substring(0, 100)}... 参数: ${JSON.stringify(params)} 错误: ${err.message}`);
|
|
reject(err);
|
|
} else {
|
|
resolve(row);
|
|
}
|
|
});
|
|
});
|
|
};
|
|
|
|
|
|
export const allDb = <T = any>(db: sqlite3.Database, sql: string, params: any[] = []): Promise<T[]> => {
|
|
return new Promise((resolve, reject) => {
|
|
db.all(sql, params, (err: Error | null, rows: T[]) => {
|
|
if (err) {
|
|
console.error(`[数据库错误] SQL: ${sql.substring(0, 100)}... 参数: ${JSON.stringify(params)} 错误: ${err.message}`);
|
|
reject(err);
|
|
} else {
|
|
resolve(rows);
|
|
}
|
|
});
|
|
});
|
|
};
|
|
|
|
|
|
const runDatabaseInitializations = async (db: sqlite3.Database): Promise<void> => {
|
|
try {
|
|
await runDb(db, 'PRAGMA foreign_keys = ON;');
|
|
for (const tableDef of tableDefinitions) {
|
|
await runDb(db, tableDef.sql);
|
|
if (tableDef.init) {
|
|
await tableDef.init(db);
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error('[DB Init] 数据库初始化序列失败:', error);
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
|
|
export const getDbInstance = (): Promise<sqlite3.Database> => {
|
|
if (!dbInstancePromise) {
|
|
dbInstancePromise = new Promise((resolve, reject) => {
|
|
|
|
const db = new verboseSqlite3.Database(dbPath, sqlite3.OPEN_READWRITE | sqlite3.OPEN_CREATE, async (err) => { // Mark callback as async
|
|
|
|
if (err) {
|
|
console.error(`[数据库连接] 打开数据库文件 ${dbPath} 时出错:`, err.message);
|
|
dbInstancePromise = null;
|
|
reject(err);
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
// 运行初始表创建
|
|
await runDatabaseInitializations(db);
|
|
// +++ 运行数据库迁移 +++
|
|
await runMigrations(db);
|
|
console.log('[数据库] 初始化和迁移完成。'); // 添加日志确认
|
|
resolve(db);
|
|
} catch (initError) {
|
|
console.error('[数据库] 连接后初始化失败,正在关闭连接...');
|
|
dbInstancePromise = null;
|
|
db.close((closeErr) => {
|
|
if (closeErr) console.error('[数据库] 初始化失败后关闭连接时出错:', closeErr.message);
|
|
reject(initError);
|
|
});
|
|
|
|
}
|
|
});
|
|
});
|
|
}
|
|
return dbInstancePromise;
|
|
};
|
|
|
|
|
|
process.on('SIGINT', async () => {
|
|
if (dbInstancePromise) {
|
|
console.log('[DB] 收到 SIGINT,尝试关闭数据库连接...');
|
|
try {
|
|
|
|
const db = await dbInstancePromise;
|
|
db.close((err) => {
|
|
if (err) {
|
|
console.error('[DB] 关闭数据库时出错:', err.message);
|
|
} else {
|
|
console.log('[DB] 数据库连接已关闭。');
|
|
}
|
|
process.exit(err ? 1 : 0);
|
|
});
|
|
} catch (error) {
|
|
console.error('[DB] 获取数据库实例以关闭时出错 (可能初始化失败):', error);
|
|
process.exit(1);
|
|
}
|
|
} else {
|
|
console.log('[DB] 收到 SIGINT,但数据库连接从未初始化或已失败。');
|
|
process.exit(0);
|
|
}
|
|
});
|
|
|
|
|