/** * API 服务器 */ import express, { type Request, type Response, type NextFunction, } from 'express' import cors from 'cors' import dotenv from 'dotenv' import filesRoutes from './core/files/routes.js' import eventsRoutes from './core/events/routes.js' import settingsRoutes from './core/settings/routes.js' import uploadRoutes from './core/upload/routes.js' import searchRoutes from './core/search/routes.js' import type { ApiResponse } from '../shared/types.js' import { errorHandler } from './middlewares/errorHandler.js' import { NOTEBOOK_ROOT } from './config/paths.js' import { ModuleManager } from './infra/moduleManager.js' import { ServiceContainer } from './infra/container.js' import { apiModules } from './modules/index.js' import { validateModuleConsistency } from './infra/moduleValidator.js' import path from 'path' import fs from 'fs' import { fileURLToPath } from 'url' const __filename = fileURLToPath(import.meta.url) const __dirname = path.dirname(__filename) dotenv.config({ path: path.resolve(__dirname, './.env') }) const app: express.Application = express() export const container = new ServiceContainer() export const moduleManager = new ModuleManager(container) app.use(cors()) app.use(express.json({ limit: '200mb' })) app.use(express.urlencoded({ extended: true, limit: '200mb' })) /** * Core Routes */ app.use('/api/files', filesRoutes) app.use('/api/events', eventsRoutes) app.use('/api/settings', settingsRoutes) app.use('/api/upload', uploadRoutes) app.use('/api/search', searchRoutes) /** * Module Routes (loaded dynamically via ModuleManager) */ for (const module of apiModules) { await moduleManager.register(module) } await validateModuleConsistency(apiModules) for (const module of moduleManager.getAllModules()) { await moduleManager.activate(module.metadata.id) const router = await module.createRouter(container) app.use('/api' + module.metadata.basePath, router) } app.get('/background.png', (req, res, next) => { const customBgPath = path.join(NOTEBOOK_ROOT, '.config', 'background.png') if (fs.existsSync(customBgPath)) { res.sendFile(customBgPath) } else { next() } }) /** * health */ app.use( '/api/health', (_req: Request, res: Response): void => { const response: ApiResponse<{ message: string }> = { success: true, data: { message: 'ok' }, } res.status(200).json(response) }, ) /** * 404 handler */ app.use((req: Request, res: Response, next: NextFunction) => { if (req.path.startsWith('/api')) { const response: ApiResponse = { success: false, error: { code: 'NOT_FOUND', message: 'API不存在' }, } res.status(404).json(response) } else { next() } }) app.use(errorHandler) export default app