Files

673 lines
16 KiB
JavaScript

const express = require('express');
const router = express.Router();
const DatabaseManager = require('../database/database-manager');
const RegistryDatabase = require('../database/registry-database');
const RegistryMigration = require('../services/registry-migration');
const { defaultLogger } = require('../utils/logger');
// 创建logger实例
const logger = defaultLogger.child('DatabaseRouter');
// 全局数据库管理器实例
let databaseManager = null;
let registryDatabase = null;
/**
* 设置数据库实例
*/
function setDatabaseInstances(dbManager, regDatabase) {
databaseManager = dbManager;
registryDatabase = regDatabase;
}
/**
* 测试数据库连接
* POST /api/database/test-connection
*/
router.post('/test-connection', async (req, res) => {
try {
const { host, port, user, password, database, ssl } = req.body;
if (!host || !port || !user || !database) {
return res.status(400).json({
success: false,
error: '缺少必要的连接参数'
});
}
// 创建临时数据库管理器进行测试
const testManager = new DatabaseManager();
// 初始化连接池
await testManager.init({
host,
port: parseInt(port),
user,
password,
database,
ssl: ssl || false
});
const result = await testManager.testConnection();
if (result.success) {
res.json({
success: true,
data: {
message: '数据库连接成功',
serverVersion: result.serverVersion,
connectionTime: result.connectionTime
}
});
} else {
res.status(400).json({
success: false,
error: result.error
});
}
// 关闭测试连接
await testManager.close();
} catch (error) {
logger.error('测试数据库连接失败:', error);
res.status(500).json({
success: false,
error: error.message
});
}
});
/**
* 保存数据库配置
* POST /api/database/config
*/
router.post('/config', async (req, res) => {
try {
const { host, port, user, password, database, connectionLimit, ssl } = req.body;
if (!host || !port || !user || !database) {
return res.status(400).json({
success: false,
error: '缺少必要的连接参数'
});
}
const config = {
host,
port: parseInt(port),
user,
password,
database,
connectionLimit: parseInt(connectionLimit) || 10,
ssl: ssl || false
};
// 先测试连接
const testManager = new DatabaseManager();
await testManager.init(config);
const testResult = await testManager.testConnection();
if (!testResult.success) {
await testManager.close();
return res.status(400).json({
success: false,
error: `连接测试失败: ${testResult.error}`
});
}
await testManager.close();
// 保存配置到文件
const fs = require('fs-extra');
const path = require('path');
// 检测是否在pkg打包环境中运行
const isPkg = process.pkg !== undefined;
const configPath = isPkg
? path.join(process.cwd(), 'data', 'database.json') // 打包环境:当前工作目录的data文件夹
: path.join(__dirname, '..', '..', 'data', 'database.json'); // 开发环境:项目根目录的data文件夹
await fs.ensureDir(path.dirname(configPath));
await fs.writeJson(configPath, config, { spaces: 2 });
// 重新初始化全局数据库管理器
if (databaseManager) {
await databaseManager.close();
}
databaseManager = new DatabaseManager();
await databaseManager.init(config);
// 初始化注册表数据库
registryDatabase = new RegistryDatabase(databaseManager);
await registryDatabase.init();
res.json({
success: true,
data: {
message: '数据库配置已保存并连接成功',
config: {
host: config.host,
port: config.port,
user: config.user,
database: config.database,
connectionLimit: config.connectionLimit,
ssl: config.ssl
}
}
});
} catch (error) {
logger.error('保存数据库配置失败:', error);
res.status(500).json({
success: false,
error: error.message
});
}
});
/**
* 获取数据库配置
* GET /api/database/config
*/
router.get('/config', async (req, res) => {
try {
const fs = require('fs-extra');
const path = require('path');
// 检测是否在pkg打包环境中运行
const isPkg = process.pkg !== undefined;
const configPath = isPkg
? path.join(process.cwd(), 'data', 'database.json') // 打包环境:当前工作目录的data文件夹
: path.join(__dirname, '..', '..', 'data', 'database.json'); // 开发环境:项目根目录的data文件夹
if (await fs.pathExists(configPath)) {
const config = await fs.readJson(configPath);
// 不返回密码
const safeConfig = {
host: config.host,
port: config.port,
user: config.user,
database: config.database,
connectionLimit: config.connectionLimit,
ssl: config.ssl,
hasPassword: !!config.password
};
res.json({
success: true,
data: safeConfig
});
} else {
res.json({
success: true,
data: null
});
}
} catch (error) {
logger.error('获取数据库配置失败:', error);
res.status(500).json({
success: false,
error: error.message
});
}
});
/**
* 获取数据库连接状态
* GET /api/database/status
*/
router.get('/status', async (req, res) => {
try {
if (!databaseManager) {
return res.json({
success: true,
data: {
connected: false,
message: '数据库未配置'
}
});
}
const isConnected = databaseManager.isConnected;
res.json({
success: true,
data: {
connected: isConnected,
message: isConnected ? '数据库已连接' : '数据库连接断开'
}
});
} catch (error) {
logger.error('获取数据库状态失败:', error);
res.status(500).json({
success: false,
error: error.message
});
}
});
/**
* 初始化数据库表
* POST /api/database/init-tables
*/
router.post('/init-tables', async (req, res) => {
try {
if (!databaseManager) {
return res.status(400).json({
success: false,
error: '数据库未配置'
});
}
if (!registryDatabase) {
registryDatabase = new RegistryDatabase(databaseManager);
}
await registryDatabase.init();
res.json({
success: true,
data: {
message: '数据库表初始化成功'
}
});
} catch (error) {
logger.error('初始化数据库表失败:', error);
res.status(500).json({
success: false,
error: error.message
});
}
});
/**
* 数据迁移:JSON到数据库
* POST /api/database/migrate/json-to-db
*/
router.post('/migrate/json-to-db', async (req, res) => {
try {
const { overwrite = true, createBackup = true } = req.body;
if (!databaseManager || !registryDatabase) {
return res.status(400).json({
success: false,
error: '数据库未配置或未初始化'
});
}
const downloadService = req.backend.getDownloadService();
const jsonRegistry = downloadService.downloadRegistry;
const fileManager = downloadService.fileManager;
const migration = new RegistryMigration(jsonRegistry, registryDatabase, fileManager);
const result = await migration.performMigration('json-to-db', overwrite, createBackup);
res.json({
success: true,
data: result
});
} catch (error) {
logger.error('JSON到数据库迁移失败:', error);
res.status(500).json({
success: false,
error: error.message
});
}
});
/**
* 数据迁移:数据库到JSON
* POST /api/database/migrate/db-to-json
*/
router.post('/migrate/db-to-json', async (req, res) => {
try {
const { overwrite = true, createBackup = true } = req.body;
if (!databaseManager || !registryDatabase) {
return res.status(400).json({
success: false,
error: '数据库未配置或未初始化'
});
}
const downloadService = req.backend.getDownloadService();
const jsonRegistry = downloadService.downloadRegistry;
const fileManager = downloadService.fileManager;
const migration = new RegistryMigration(jsonRegistry, registryDatabase, fileManager);
const result = await migration.performMigration('db-to-json', overwrite, createBackup);
res.json({
success: true,
data: result
});
} catch (error) {
logger.error('数据库到JSON迁移失败:', error);
res.status(500).json({
success: false,
error: error.message
});
}
});
/**
* 比较注册表差异
* GET /api/database/compare-registries
*/
router.get('/compare-registries', async (req, res) => {
try {
if (!databaseManager || !registryDatabase) {
return res.status(400).json({
success: false,
error: '数据库未配置或未初始化'
});
}
const downloadService = req.backend.getDownloadService();
const jsonRegistry = downloadService.downloadRegistry;
const fileManager = downloadService.fileManager;
const migration = new RegistryMigration(jsonRegistry, registryDatabase, fileManager);
const comparison = await migration.compareRegistries();
res.json({
success: true,
data: comparison
});
} catch (error) {
logger.error('比较注册表失败:', error);
res.status(500).json({
success: false,
error: error.message
});
}
});
/**
* 断开数据库连接
* POST /api/database/disconnect
*/
router.post('/disconnect', async (req, res) => {
try {
if (databaseManager) {
await databaseManager.disconnect();
databaseManager = null;
registryDatabase = null;
}
res.json({
success: true,
data: {
message: '数据库连接已断开'
}
});
} catch (error) {
logger.error('断开数据库连接失败:', error);
res.status(500).json({
success: false,
error: error.message
});
}
});
// 数据库注册表相关API
/**
* 获取数据库注册表统计信息
* GET /api/database/registry/stats
*/
router.get('/registry/stats', async (req, res) => {
try {
if (!registryDatabase) {
return res.status(400).json({
success: false,
error: '数据库注册表未初始化'
});
}
const stats = await registryDatabase.getStats();
res.json({
success: true,
data: stats
});
} catch (error) {
logger.error('获取数据库注册表统计信息失败:', error);
res.status(500).json({
success: false,
error: error.message
});
}
});
/**
* 导出数据库注册表
* GET /api/database/registry/export
*/
router.get('/registry/export', async (req, res) => {
try {
if (!registryDatabase) {
return res.status(400).json({
success: false,
error: '数据库注册表未初始化'
});
}
const registryData = await registryDatabase.exportRegistry();
res.setHeader('Content-Type', 'application/json');
res.setHeader('Content-Disposition', 'attachment; filename="database-registry.json"');
res.json({
success: true,
data: registryData
});
} catch (error) {
logger.error('导出数据库注册表失败:', error);
res.status(500).json({
success: false,
error: error.message
});
}
});
/**
* 导入数据到数据库注册表
* POST /api/database/registry/import
*/
router.post('/registry/import', async (req, res) => {
try {
const { registryData } = req.body;
if (!registryData) {
return res.status(400).json({
success: false,
error: '缺少注册表数据'
});
}
if (!registryDatabase) {
return res.status(400).json({
success: false,
error: '数据库注册表未初始化'
});
}
const result = await registryDatabase.importRegistry(registryData);
res.json({
success: true,
data: result
});
} catch (error) {
logger.error('导入数据库注册表失败:', error);
res.status(500).json({
success: false,
error: error.message
});
}
});
/**
* 从文件系统重建数据库注册表
* POST /api/database/registry/rebuild
*/
router.post('/registry/rebuild', async (req, res) => {
try {
if (!registryDatabase) {
return res.status(400).json({
success: false,
error: '数据库注册表未初始化'
});
}
const downloadService = req.backend.getDownloadService();
const fileManager = downloadService.fileManager;
// 生成任务ID
const taskId = `db-registry-rebuild-${Date.now()}`;
// 立即返回任务ID,不等待完成
res.json({
success: true,
data: {
taskId,
status: 'started',
message: '数据库注册表重建任务已启动'
}
});
// 异步执行重建任务
setImmediate(async () => {
try {
// 设置任务状态为进行中
global.dbRegistryRebuildTasks = global.dbRegistryRebuildTasks || new Map();
global.dbRegistryRebuildTasks.set(taskId, {
status: 'running',
startTime: Date.now(),
progress: {
scannedArtists: 0,
scannedArtworks: 0,
addedArtworks: 0,
skippedArtworks: 0,
currentArtist: null
}
});
const result = await registryDatabase.rebuildFromFileSystem(fileManager, taskId);
// 更新任务状态为完成
global.dbRegistryRebuildTasks.set(taskId, {
status: 'completed',
startTime: global.dbRegistryRebuildTasks.get(taskId).startTime,
endTime: Date.now(),
result: result
});
logger.info(`数据库注册表重建任务完成: ${taskId}`, result);
} catch (error) {
logger.error(`数据库注册表重建任务失败: ${taskId}`, error);
// 更新任务状态为失败
global.dbRegistryRebuildTasks.set(taskId, {
status: 'failed',
startTime: global.dbRegistryRebuildTasks.get(taskId).startTime,
endTime: Date.now(),
error: error.message
});
}
});
} catch (error) {
logger.error('启动数据库注册表重建任务失败:', error);
res.status(500).json({
success: false,
error: error.message
});
}
});
/**
* 获取数据库注册表重建任务状态
* GET /api/database/registry/rebuild/status/:taskId
*/
router.get('/registry/rebuild/status/:taskId', async (req, res) => {
try {
const { taskId } = req.params;
global.dbRegistryRebuildTasks = global.dbRegistryRebuildTasks || new Map();
const task = global.dbRegistryRebuildTasks.get(taskId);
if (!task) {
return res.status(404).json({
success: false,
error: '任务不存在'
});
}
res.json({
success: true,
data: task
});
} catch (error) {
logger.error('获取数据库注册表重建任务状态失败:', error);
res.status(500).json({
success: false,
error: error.message
});
}
});
/**
* 清理数据库注册表
* POST /api/database/registry/cleanup
*/
router.post('/registry/cleanup', async (req, res) => {
try {
if (!registryDatabase) {
return res.status(400).json({
success: false,
error: '数据库注册表未初始化'
});
}
const downloadService = req.backend.getDownloadService();
const fileManager = downloadService.fileManager;
const result = await registryDatabase.cleanupRegistry(fileManager);
res.json({
success: true,
data: result
});
} catch (error) {
logger.error('清理数据库注册表失败:', error);
res.status(500).json({
success: false,
error: error.message
});
}
});
// 导出数据库管理器实例,供其他模块使用
router.getDatabaseManager = () => databaseManager;
router.getRegistryDatabase = () => registryDatabase;
router.setDatabaseInstances = setDatabaseInstances;
module.exports = router;