UNPKG

mira-app-server

Version:

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

382 lines 15.9 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.MiraHttpServer = void 0; const mira_app_core_1 = require("mira-app-core"); const http_1 = __importDefault(require("http")); const express_1 = __importDefault(require("express")); const axios_1 = __importDefault(require("axios")); const AuthRouter_1 = require("./AuthRouter"); function createHttpLoggerMiddleware() { return (req, res, next) => { const startTime = Date.now(); const timestamp = new Date().toISOString(); // 记录请求信息 const requestData = { method: req.method, url: req.originalUrl || req.url, headers: req.headers, query: req.query, params: req.params, body: req.body, ip: req.ip || req.connection.remoteAddress || 'unknown', userAgent: req.get('User-Agent') || 'unknown', timestamp }; // 输出请求信息 console.log('\n' + '='.repeat(80)); console.log(`📥 HTTP REQUEST [${timestamp}]`); console.log(`🔗 ${requestData.method.toUpperCase()} ${requestData.url}`); console.log(`🌐 IP: ${requestData.ip}`); console.log(`🔍 User-Agent: ${requestData.userAgent}`); if (Object.keys(requestData.query).length > 0) { console.log(`❓ Query Parameters:`, JSON.stringify(requestData.query, null, 2)); } if (Object.keys(requestData.params).length > 0) { console.log(`📍 Route Parameters:`, JSON.stringify(requestData.params, null, 2)); } console.log(`📤 Request Headers:`, JSON.stringify(requestData.headers, null, 2)); if (requestData.body && Object.keys(requestData.body).length > 0) { console.log(`📦 Request Body:`, JSON.stringify(requestData.body, null, 2)); } // 拦截响应 const originalSend = res.send; const originalJson = res.json; let responseBody = null; // 重写 send 方法 res.send = function (data) { responseBody = data; return originalSend.call(this, data); }; // 重写 json 方法 res.json = function (data) { responseBody = data; return originalJson.call(this, data); }; // 监听响应完成 res.on('finish', () => { const responseTime = Date.now() - startTime; const responseData = { statusCode: res.statusCode, statusMessage: res.statusMessage, headers: res.getHeaders(), body: responseBody, responseTime }; // 输出响应信息 console.log(`📤 HTTP RESPONSE [${new Date().toISOString()}]`); console.log(`📊 Status: ${responseData.statusCode} ${responseData.statusMessage}`); console.log(`⏱️ Response Time: ${responseData.responseTime}ms`); console.log(`📤 Response Headers:`, JSON.stringify(responseData.headers, null, 2)); if (responseData.body !== null && responseData.body !== undefined) { let bodyStr = responseData.body; if (typeof responseData.body === 'object') { bodyStr = JSON.stringify(responseData.body, null, 2); } // 限制输出长度避免控制台过于拥挤 if (bodyStr.length > 1000) { console.log(`📦 Response Body (truncated):`, bodyStr.substring(0, 1000) + '...[truncated]'); } else { console.log(`📦 Response Body:`, bodyStr); } } console.log('='.repeat(80) + '\n'); }); next(); }; } class MiraHttpServer { constructor(backend, dataDir = './data') { this.backend = backend; this.app = (0, express_1.default)(); this.httpServer = http_1.default.createServer(this.app); this.httpRouter = new mira_app_core_1.HttpRouter(backend); this.authRouter = new AuthRouter_1.AuthRouter(dataDir); this.setupMiddleware(); } async initialize() { await this.authRouter.initialize(); this.setupRoutes(); } // 开放功能 async request(options) { try { const response = await axios_1.default.request({ method: options.method, url: options.url, headers: options.headers, data: options.data }); return response.data; } catch (error) { if (axios_1.default.isAxiosError(error)) { throw new Error(`Request failed: ${error.message}`); } throw error; } } setupMiddleware() { // 添加HTTP请求日志中间件 this.app.use(createHttpLoggerMiddleware()); // CORS 中间件 this.app.use((req, res, next) => { res.header('Access-Control-Allow-Origin', '*'); res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH, OPTIONS'); res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, Authorization'); if (req.method === 'OPTIONS') { res.sendStatus(200); } else { next(); } }); // JSON 解析中间件 this.app.use(express_1.default.json({ limit: '10mb' })); this.app.use(express_1.default.urlencoded({ extended: true, limit: '10mb' })); // 静态文件中间件 this.app.use('/static', express_1.default.static('public')); } setupRoutes() { // 认证路由(挂载到根路径,因为前端期望 /auth/* 路径) this.app.use('/auth', this.authRouter.getRouter()); // 管理员管理路由 this.app.get('/api/admins', this.authRouter.authMiddleware(), async (req, res) => { try { // 获取所有用户(管理员) const users = await this.authRouter.getAuthService().getUserStorage().getAllUsers(); const admins = users.map((user) => ({ id: user.id.toString(), username: user.username, email: user.email || `${user.username}@mira.local`, role: 'admin', createdAt: user.created_at ? new Date(user.created_at).toISOString() : new Date().toISOString(), updatedAt: user.updated_at ? new Date(user.updated_at).toISOString() : new Date().toISOString() })); res.json(admins); } catch (error) { console.error('Error getting admins:', error); res.status(500).json({ error: 'Failed to get admins' }); } }); // 创建管理员 this.app.post('/api/admins', this.authRouter.authMiddleware(), async (req, res) => { try { const { username, email, password } = req.body; if (!username || !password) { return res.status(400).json({ success: false, message: '用户名和密码不能为空' }); } const userStorage = this.authRouter.getAuthService().getUserStorage(); // 检查用户名是否已存在(包括已删除的用户) const existingUser = await userStorage.findUserByUsernameIncludeInactive(username); if (existingUser) { if (existingUser.is_active) { return res.status(400).json({ success: false, message: '用户名已存在' }); } else { return res.status(400).json({ success: false, message: '该用户名已被使用(已删除的账户),请使用其他用户名' }); } } // 创建新管理员 const now = Date.now(); const hashedPassword = userStorage.hashPassword(password); const newAdmin = { username, email: email || `${username}@mira.local`, password: hashedPassword, role: 'admin', permissions: ['admin'], created_at: now, updated_at: now, is_active: true }; const adminId = await userStorage.createUser(newAdmin); res.json({ success: true, message: '管理员创建成功', data: { id: adminId.toString() } }); console.log(`✅ Created new admin: ${username}`); } catch (error) { console.error('Error creating admin:', error); // 处理数据库约束错误 if (error?.code === 'SQLITE_CONSTRAINT' && error?.message?.includes('UNIQUE constraint failed: users.username')) { return res.status(400).json({ success: false, message: '用户名已存在,请使用其他用户名' }); } res.status(500).json({ success: false, message: '创建管理员失败', error: error?.message || '未知错误' }); } }); // 更新管理员 this.app.put('/api/admins/:id', this.authRouter.authMiddleware(), async (req, res) => { try { const { id } = req.params; const { email, username, password } = req.body; const userStorage = this.authRouter.getAuthService().getUserStorage(); const updateData = {}; if (email !== undefined) { updateData.email = email; } if (username !== undefined) { updateData.username = username; } if (password !== undefined && password.trim() !== '') { updateData.password = password; // UserStorage.updateUser 会自动hash密码 } const success = await userStorage.updateUser(parseInt(id), updateData); if (success) { res.json({ success: true, message: '管理员信息更新成功' }); console.log(`✅ Updated admin: ${id}`); } else { res.status(404).json({ success: false, message: '管理员不存在或更新失败' }); } } catch (error) { console.error('Error updating admin:', error); res.status(500).json({ success: false, message: '更新管理员失败', error: error?.message || '未知错误' }); } }); // 删除管理员 this.app.delete('/api/admins/:id', this.authRouter.authMiddleware(), async (req, res) => { try { const { id } = req.params; const currentUser = req.user; // 防止删除自己 if (currentUser && currentUser.id.toString() === id) { return res.status(400).json({ success: false, message: '不能删除自己的账户' }); } const userStorage = this.authRouter.getAuthService().getUserStorage(); const success = await userStorage.softDeleteUser(parseInt(id)); if (success) { res.json({ success: true, message: '管理员删除成功' }); console.log(`✅ Deleted admin: ${id}`); } else { res.status(404).json({ success: false, message: '管理员不存在或删除失败' }); } } catch (error) { console.error('Error deleting admin:', error); res.status(500).json({ success: false, message: '删除管理员失败', error: error?.message || '未知错误' }); } }); // API 路由(业务逻辑路由) this.app.use('/api', this.httpRouter.getRouter()); // API 健康检查端点 this.app.get('/api/health', (req, res) => { res.json({ success: true, status: 'ok', timestamp: new Date().toISOString(), uptime: process.uptime(), version: process.env.npm_package_version || '1.0.0', nodeVersion: process.version, environment: process.env.NODE_ENV || 'development' }); }); // 健康检查端点 this.app.get('/health', (req, res) => { res.json({ status: 'ok', timestamp: new Date().toISOString(), uptime: process.uptime(), version: process.env.npm_package_version || '1.0.0' }); }); // 404 处理 this.app.use('*', (req, res) => { res.status(404).json({ error: 'Not Found', message: `Cannot ${req.method} ${req.originalUrl}`, timestamp: new Date().toISOString() }); }); // 错误处理中间件 this.app.use((err, req, res, next) => { console.error('HTTP Server Error:', err); res.status(err.status || 500).json({ error: err.name || 'Internal Server Error', message: err.message || 'An unexpected error occurred', timestamp: new Date().toISOString(), ...(process.env.NODE_ENV === 'development' && { stack: err.stack }) }); }); } start(port = 8081) { return new Promise((resolve, reject) => { this.httpServer.listen(port, (err) => { if (err) { reject(err); } else { console.log(`🚀 HTTP Server started on port ${port}`); console.log(`📍 Health check: http://localhost:${port}/health`); console.log(`🔗 API base URL: http://localhost:${port}/api`); resolve(); } }); }); } stop() { return new Promise((resolve) => { this.httpServer.close(() => { console.log('📴 HTTP Server stopped'); resolve(); }); }); } getApp() { return this.app; } getHttpServer() { return this.httpServer; } getAuthRouter() { return this.authRouter; } } exports.MiraHttpServer = MiraHttpServer; //# sourceMappingURL=HttpServer.js.map