UNPKG

mira-app-server

Version:

Mira Server - standalone server application using mira-app-core

621 lines 30.4 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.LibraryRoutes = void 0; const express_1 = require("express"); const path = __importStar(require("path")); const LibraryWatcher_1 = require("../LibraryWatcher"); class LibraryRoutes { constructor(backend) { this.backend = backend; this.router = (0, express_1.Router)(); this.setupRoutes(); } setupRoutes() { // 获取资源库列表 this.router.get('/', async (req, res) => { try { const libraries = []; // 读取配置文件以获取所有库的信息(包括禁用的) const fsPromises = require('fs').promises; const librarysPath = path.join(this.backend.dataPath, 'librarys.json'); let libraryConfigs = []; try { const data = await fsPromises.readFile(librarysPath, 'utf8'); libraryConfigs = JSON.parse(data); } catch (error) { console.warn('Failed to read librarys.json:', error); } for (const config of libraryConfigs) { const id = config.id; const libraryObj = this.backend.libraries.getLibrary(id); let stats = { totalFiles: 0, totalSize: 0 }; const status = this.backend.libraries.getLibraryStatus(id); // 获取库的配置信息(优先从内存,然后从文件) const libraryConfig = this.backend.libraries.getLibraryConfig(id) || config; // 如果库服务存在且活跃,获取统计信息 if (libraryObj?.libraryService && this.backend.libraries.isLibraryActive(id)) { try { stats = await libraryObj.libraryService.getStats(); } catch (error) { console.warn(`Failed to get stats for library ${id}:`, error); } } // 处理路径字段,兼容新旧配置格式 const configPath = libraryConfig.path || libraryConfig.customFields?.path || ''; libraries.push({ id: id, name: libraryConfig.name, path: configPath, status: status, fileCount: stats.totalFiles, size: stats.totalSize, description: libraryConfig.description || '', icon: libraryConfig.icon || 'default', ...(libraryConfig.pluginsDir && { pluginsDir: libraryConfig.pluginsDir }), ...(libraryConfig.customFields && { customFields: libraryConfig.customFields }), ...(libraryConfig.allowedRoles && { allowedRoles: libraryConfig.allowedRoles }), createdAt: libraryConfig.createdAt || new Date().toISOString(), updatedAt: libraryConfig.updatedAt || new Date().toISOString() }); } res.json(libraries); } catch (error) { console.error('Error getting libraries:', error); res.status(500).json({ error: 'Failed to get libraries' }); } }); // 创建新资源库 this.router.post('/', async (req, res) => { try { const { name, path: libraryPath, description, icon, customFields, pluginsDir, allowedRoles } = req.body; if (!name || !libraryPath) { return res.status(400).json({ error: 'Name and path are required' }); } // 检查路径是否已存在 for (const [id, libraryObj] of Object.entries(this.backend.libraries.getLibraries())) { const existingPath = libraryObj.libraryService?.config?.path || libraryObj.libraryService?.config?.customFields?.path; if (existingPath === libraryPath) { return res.status(400).json({ error: 'Library with this path already exists' }); } } // 生成新的 ID const newId = Date.now().toString(); // 创建库配置,支持新的字段结构 const libraryConfig = { id: newId, name, path: libraryPath, description: description || '', icon: icon || 'default', customFields: { path: libraryPath, enableHash: customFields?.enableHash || false, enableAutoSync: customFields?.enableAutoSync ?? true, enableAutoBackup: customFields?.enableAutoBackup ?? true, ...(customFields || {}) }, ...(allowedRoles && { allowedRoles }), createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), status: 'active' // 新建的库默认为活动状态 }; // 添加插件目录(如果提供) if (pluginsDir) { libraryConfig.pluginsDir = pluginsDir; } // 读取现有的 librarys.json const fsPromises = require('fs').promises; const librarysPath = path.join(this.backend.dataPath, 'librarys.json'); let libraries = []; try { const data = await fsPromises.readFile(librarysPath, 'utf8'); libraries = JSON.parse(data); } catch (error) { // 文件不存在,使用空数组 libraries = []; } // 添加新库到配置 libraries.push(libraryConfig); // 写回文件 await fsPromises.writeFile(librarysPath, JSON.stringify(libraries, null, 2), 'utf8'); // 加载新库 try { await this.backend.libraries.load(libraryConfig); } catch (loadError) { console.warn('Failed to load new library immediately:', loadError); } // 返回创建的库信息 const createdLibrary = { id: newId, name, path: libraryPath, status: 'active', fileCount: 0, size: 0, description: description || '', icon: icon || 'default', customFields: libraryConfig.customFields, ...(pluginsDir && { pluginsDir }), ...(allowedRoles && { allowedRoles }), createdAt: libraryConfig.createdAt, updatedAt: libraryConfig.updatedAt }; res.status(201).json(createdLibrary); } catch (error) { console.error('Error creating library:', error); res.status(500).json({ error: 'Failed to create library' }); } }); // 更新资源库 this.router.put('/:id', async (req, res) => { try { const { id } = req.params; const { name, path: libraryPath, description, icon, customFields, pluginsDir, allowedRoles } = req.body; const libraryObj = this.backend.libraries.getLibrary(id); if (!libraryObj) { return res.status(404).json({ error: 'Library not found' }); } // 获取当前配置(可能来自活跃的服务或保存的配置) const currentConfig = this.backend.libraries.getLibraryConfig(id); if (!currentConfig) { return res.status(404).json({ error: 'Library configuration not found' }); } // 如果路径变了,检查新路径是否与其他库冲突 const currentPath = currentConfig.path || currentConfig.customFields?.path; if (libraryPath && libraryPath !== currentPath) { for (const [otherId, otherLibraryObj] of Object.entries(this.backend.libraries.getLibraries())) { const otherConfig = this.backend.libraries.getLibraryConfig(otherId); const otherPath = otherConfig?.path || otherConfig?.customFields?.path; if (otherId !== id && otherPath === libraryPath) { return res.status(400).json({ error: 'Library with this path already exists' }); } } } // 更新配置,支持新的字段结构 const updatedConfig = { ...currentConfig, name: name || currentConfig.name, path: libraryPath || currentConfig.path, description: description !== undefined ? description : currentConfig.description, icon: icon || currentConfig.icon || 'default', customFields: { ...currentConfig.customFields, path: libraryPath || currentConfig.path, enableHash: customFields?.enableHash !== undefined ? customFields.enableHash : (currentConfig.customFields?.enableHash || false), ...(customFields || {}) }, updatedAt: new Date().toISOString() }; // 更新插件目录 if (pluginsDir !== undefined) { if (pluginsDir) { updatedConfig.pluginsDir = pluginsDir; } else { delete updatedConfig.pluginsDir; } } // 更新 allowedRoles if (allowedRoles !== undefined) { updatedConfig.allowedRoles = allowedRoles; } // 更新内存中的配置(如果库是活跃的) if (libraryObj.libraryService) { libraryObj.libraryService.config = updatedConfig; // 处理 watcher 启停 const shouldWatch = updatedConfig.customFields?.enableAutoSync ?? true; if (shouldWatch && !libraryObj.watcher) { const watcher = new LibraryWatcher_1.LibraryWatcher(libraryObj.libraryService, this.backend.getWebSocketServer()); libraryObj.watcher = watcher; watcher.start(); } else if (!shouldWatch && libraryObj.watcher) { await libraryObj.watcher.stop(); libraryObj.watcher = undefined; } } else if (libraryObj.savedConfig) { // 更新保存的配置 libraryObj.savedConfig = updatedConfig; } // 同时更新 librarys.json 文件 const fsPromises = require('fs').promises; const librarysPath = path.join(this.backend.dataPath, 'librarys.json'); try { const data = await fsPromises.readFile(librarysPath, 'utf8'); let libraries = JSON.parse(data); // 找到并更新对应的库配置 const libraryIndex = libraries.findIndex((lib) => lib.id === id); if (libraryIndex !== -1) { libraries[libraryIndex] = updatedConfig; await fsPromises.writeFile(librarysPath, JSON.stringify(libraries, null, 2), 'utf8'); } } catch (fileError) { console.warn('Failed to update librarys.json:', fileError); } // 获取更新后的统计信息 let stats = { totalFiles: 0, totalSize: 0 }; if (libraryObj.libraryService) { try { stats = await libraryObj.libraryService.getStats(); } catch (error) { console.warn('Failed to get stats after update:', error); } } const updatedLibrary = { id: id, name: updatedConfig.name, path: updatedConfig.path || updatedConfig.customFields?.path, status: this.backend.libraries.getLibraryStatus(id), fileCount: stats.totalFiles, size: stats.totalSize, description: updatedConfig.description || '', icon: updatedConfig.icon, customFields: updatedConfig.customFields, ...(updatedConfig.pluginsDir && { pluginsDir: updatedConfig.pluginsDir }), ...(updatedConfig.allowedRoles && { allowedRoles: updatedConfig.allowedRoles }), createdAt: updatedConfig.createdAt, updatedAt: updatedConfig.updatedAt }; res.json(updatedLibrary); } catch (error) { console.error('Error updating library:', error); res.status(500).json({ error: 'Failed to update library' }); } }); // 切换资源库状态 this.router.patch('/:id/status', async (req, res) => { try { const { id } = req.params; const { status } = req.body; if (!status || !['active', 'inactive'].includes(status)) { return res.status(400).json({ error: 'Invalid status. Must be active or inactive' }); } const libraryObj = this.backend.libraries.getLibrary(id); if (!libraryObj) { return res.status(404).json({ error: 'Library not found' }); } // 获取当前状态 const currentStatus = this.backend.libraries.getLibraryStatus(id); if (currentStatus === status) { return res.json({ message: `Library is already ${status}` }); } // 执行启用/禁用操作 let success = false; if (status === 'active') { success = await this.backend.libraries.enableLibrary(id); } else { success = await this.backend.libraries.disableLibrary(id); } if (!success) { return res.status(500).json({ error: `Failed to ${status === 'active' ? 'enable' : 'disable'} library` }); } // 更新配置文件中的状态 try { const fsPromises = require('fs').promises; const librarysPath = path.join(this.backend.dataPath, 'librarys.json'); const data = await fsPromises.readFile(librarysPath, 'utf8'); let libraries = JSON.parse(data); // 找到并更新对应的库配置 const libraryIndex = libraries.findIndex((lib) => lib.id === id); if (libraryIndex !== -1) { libraries[libraryIndex].status = status; libraries[libraryIndex].updatedAt = new Date().toISOString(); await fsPromises.writeFile(librarysPath, JSON.stringify(libraries, null, 2), 'utf8'); } } catch (fileError) { console.warn('Failed to update librarys.json:', fileError); // 即使文件更新失败,也不影响内存中的状态切换 } res.json({ message: `Library ${status === 'active' ? 'activated' : 'deactivated'} successfully`, status: status }); } catch (error) { console.error('Error toggling library status:', error); res.status(500).json({ error: 'Failed to toggle library status' }); } }); // 获取资源库统计信息 this.router.get('/:id/stats', async (req, res) => { try { const { id } = req.params; const libraryObj = this.backend.libraries.getLibrary(id); if (!libraryObj) { return res.status(404).json({ error: 'Library not found' }); } // 检查库是否处于活动状态 if (!this.backend.libraries.isLibraryActive(id)) { return res.status(400).json({ error: 'Library is inactive', message: 'Cannot get statistics for inactive library. Please enable the library first.' }); } if (!libraryObj.libraryService) { return res.status(500).json({ error: 'Library service not available' }); } const stats = await libraryObj.libraryService.getStats(); // 获取文件夹数量 let totalFolders = 0; try { const folderResult = await libraryObj.libraryService.getSql('SELECT COUNT(*) as total_folders FROM files WHERE type = "folder" AND recycled = 0'); totalFolders = folderResult[0]?.total_folders || 0; } catch (error) { console.warn('Failed to get folder count:', error); } // 获取标签数量 let totalTags = 0; try { const tagResult = await libraryObj.libraryService.getSql('SELECT COUNT(DISTINCT tag) as total_tags FROM file_tags'); totalTags = tagResult[0]?.total_tags || 0; } catch (error) { console.warn('Failed to get tag count:', error); } // 获取文件类型分布 let fileTypes = {}; try { const typeResult = await libraryObj.libraryService.getSql('SELECT type, COUNT(*) as count FROM files WHERE recycled = 0 GROUP BY type'); fileTypes = typeResult.reduce((acc, row) => { acc[row.type] = row.count; return acc; }, {}); } catch (error) { console.warn('Failed to get file types:', error); } const detailedStats = { totalFiles: stats.totalFiles || 0, totalFolders: totalFolders, totalSize: stats.totalSize || 0, totalTags: totalTags, fileTypes: fileTypes, lastUpdated: new Date().toISOString() }; res.json(detailedStats); } catch (error) { console.error('Error getting library stats:', error); res.status(500).json({ error: 'Failed to get library stats' }); } }); // SQL 查询接口 this.router.post('/:id/query', async (req, res) => { try { const { id } = req.params; const { sql, params } = req.body; if (!sql) { return res.status(400).json({ error: 'SQL query is required' }); } const libraryObj = this.backend.libraries.getLibrary(id); if (!libraryObj) { return res.status(404).json({ error: 'Library not found' }); } // 检查库是否处于活动状态 if (!this.backend.libraries.isLibraryActive(id)) { return res.status(400).json({ error: 'Library is inactive', message: 'Cannot execute SQL query on inactive library. Please enable the library first.' }); } if (!libraryObj.libraryService) { return res.status(500).json({ error: 'Library service not available' }); } // 执行查询,支持参数化查询 const result = await libraryObj.libraryService.getSql(sql, params); res.json({ success: true, data: result }); } catch (error) { console.error('Error executing SQL query:', error); res.status(500).json({ success: false, error: 'Failed to execute SQL query', message: error instanceof Error ? error.message : 'Unknown error' }); } }); // SQL 执行接口 (INSERT, UPDATE, DELETE) this.router.post('/:id/execute', async (req, res) => { try { const { id } = req.params; const { sql, params } = req.body; if (!sql) { return res.status(400).json({ error: 'SQL statement is required' }); } const libraryObj = this.backend.libraries.getLibrary(id); if (!libraryObj) { return res.status(404).json({ error: 'Library not found' }); } // 检查库是否处于活动状态 if (!this.backend.libraries.isLibraryActive(id)) { return res.status(400).json({ error: 'Library is inactive', message: 'Cannot execute SQL statement on inactive library. Please enable the library first.' }); } if (!libraryObj.libraryService) { return res.status(500).json({ error: 'Library service not available' }); } // 执行 SQL 语句,支持参数化查询 const result = await libraryObj.libraryService.runSql(sql, params); res.json({ success: true, lastID: result.lastID, changes: result.changes }); } catch (error) { console.error('Error executing SQL statement:', error); res.status(500).json({ success: false, error: 'Failed to execute SQL statement', message: error instanceof Error ? error.message : 'Unknown error' }); } }); // 获取表结构信息 this.router.get('/:id/schema/:table', async (req, res) => { try { const { id, table } = req.params; const libraryObj = this.backend.libraries.getLibrary(id); if (!libraryObj) { return res.status(404).json({ error: 'Library not found' }); } // 检查库是否处于活动状态 if (!this.backend.libraries.isLibraryActive(id)) { return res.status(400).json({ error: 'Library is inactive', message: 'Cannot get table schema from inactive library. Please enable the library first.' }); } if (!libraryObj.libraryService) { return res.status(500).json({ error: 'Library service not available' }); } // 获取表结构 const schemaQuery = `PRAGMA table_info(${table})`; const schema = await libraryObj.libraryService.getSql(schemaQuery); res.json({ success: true, data: schema }); } catch (error) { console.error('Error getting table schema:', error); res.status(500).json({ success: false, error: 'Failed to get table schema', message: error instanceof Error ? error.message : 'Unknown error' }); } }); // 更新记录接口 this.router.put('/:id/record/:table/:recordId', async (req, res) => { try { const { id, table, recordId } = req.params; const updateData = req.body; const libraryObj = this.backend.libraries.getLibrary(id); if (!libraryObj) { return res.status(404).json({ error: 'Library not found' }); } // 检查库是否处于活动状态 if (!this.backend.libraries.isLibraryActive(id)) { return res.status(400).json({ error: 'Library is inactive', message: 'Cannot update records in inactive library. Please enable the library first.' }); } if (!libraryObj.libraryService) { return res.status(500).json({ error: 'Library service not available' }); } // 构建 UPDATE SQL const updateFields = Object.keys(updateData) .filter(key => key !== 'id') .map(key => `${key} = ?`) .join(', '); const values = Object.keys(updateData) .filter(key => key !== 'id') .map(key => updateData[key]); const updateSql = `UPDATE ${table} SET ${updateFields} WHERE id = ?`; values.push(recordId); const result = await libraryObj.libraryService.getSql(updateSql, values); res.json({ success: true, data: result }); } catch (error) { console.error('Error updating record:', error); res.status(500).json({ success: false, error: 'Failed to update record', message: error instanceof Error ? error.message : 'Unknown error' }); } }); // 删除资源库 this.router.delete('/:id', async (req, res) => { try { const { id } = req.params; const libraryObj = this.backend.libraries.getLibrary(id); if (!libraryObj) { return res.status(404).json({ error: 'Library not found' }); } // 读取现有的 librarys.json const fsPromises = require('fs').promises; const librarysPath = path.join(this.backend.dataPath, 'librarys.json'); let libraries = []; try { const data = await fsPromises.readFile(librarysPath, 'utf8'); libraries = JSON.parse(data); } catch (error) { return res.status(500).json({ error: 'Failed to read libraries configuration' }); } // 从配置中移除库 libraries = libraries.filter((lib) => lib.id !== id); // 写回文件 await fsPromises.writeFile(librarysPath, JSON.stringify(libraries, null, 2), 'utf8'); // 关闭库服务 if (libraryObj.libraryService) { try { libraryObj.libraryService.close(); } catch (closeError) { console.warn('Failed to close library service:', closeError); } } // 从内存中移除 this.backend.libraries.removeLibrary(id); res.json({ message: 'Library deleted successfully' }); } catch (error) { console.error('Error deleting library:', error); res.status(500).json({ error: 'Failed to delete library' }); } }); } getRouter() { return this.router; } } exports.LibraryRoutes = LibraryRoutes; //# sourceMappingURL=LibraryRoutes.js.map