670 lines
16 KiB
JavaScript
670 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(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; |