feat(auth): 添加认证模块和图片批注功能(项目初始化)
- 实现用户登录、登出、密码修改等认证功能 - 添加会话管理和权限控制中间件 - 创建图片批注组件和相关API路由 - 实现批注的增删改查功能 - 添加Docker和Git忽略配置文件 - 创建系统架构文档和开发约定说明 - 集成认证模块到前端应用路由中
This commit is contained in:
102
api/app.ts
Normal file
102
api/app.ts
Normal file
@@ -0,0 +1,102 @@
|
||||
import 'express-async-errors';
|
||||
/**
|
||||
* Express 应用主入口
|
||||
*/
|
||||
import express, {
|
||||
type Request,
|
||||
type Response,
|
||||
} from 'express';
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import cors from 'cors';
|
||||
import dotenv from 'dotenv';
|
||||
import notesRoutes from './routes/notes.js';
|
||||
import imagesRoutes from './routes/images.js';
|
||||
import annotationsRoutes from './routes/annotations.js';
|
||||
import projectsRoutes from './routes/projects.js';
|
||||
import commentsRoutes from './routes/comments.js';
|
||||
import authRoutes from './routes/auth.js';
|
||||
import { optionalAuth } from './auth.js';
|
||||
import groupsRoutes from './routes/groups.js';
|
||||
import managementRoutes from './routes/management.js';
|
||||
import storageRoutes from './routes/storage.js';
|
||||
import reviewRoutes from './routes/review.js';
|
||||
import { UPLOADS_DIR } from './upload.js';
|
||||
import { database, databaseDialect } from './database.js';
|
||||
|
||||
dotenv.config();
|
||||
|
||||
const app: express.Application = express();
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
app.set('trust proxy', 1);
|
||||
|
||||
const allowedOrigins = process.env.CORS_ORIGIN?.split(',').map((item) => item.trim()).filter(Boolean);
|
||||
app.use(cors({ origin: allowedOrigins?.length ? allowedOrigins : true, credentials: true }));
|
||||
app.use(express.json({ limit: '20mb' }));
|
||||
app.use(express.urlencoded({ extended: true, limit: '20mb' }));
|
||||
app.use(optionalAuth);
|
||||
|
||||
// 静态图片资源
|
||||
app.use(
|
||||
'/uploads',
|
||||
express.static(UPLOADS_DIR, {
|
||||
maxAge: '7d',
|
||||
immutable: true,
|
||||
}),
|
||||
);
|
||||
|
||||
/**
|
||||
* API 路由
|
||||
*/
|
||||
app.use('/api/notes', notesRoutes);
|
||||
app.use('/api/images', imagesRoutes);
|
||||
app.use('/api/annotations', annotationsRoutes);
|
||||
app.use('/api/projects', projectsRoutes);
|
||||
app.use('/api', commentsRoutes);
|
||||
app.use('/api/auth', authRoutes);
|
||||
app.use('/api/groups', groupsRoutes);
|
||||
app.use('/api/management', managementRoutes);
|
||||
app.use('/api/management/storage-configs', storageRoutes);
|
||||
app.use('/api/review', reviewRoutes);
|
||||
|
||||
/**
|
||||
* health
|
||||
*/
|
||||
app.use('/api/health', async (_req: Request, res: Response) => {
|
||||
await database.one('SELECT 1 AS ready');
|
||||
res.status(200).json({ success: true, message: 'ok', database: databaseDialect });
|
||||
});
|
||||
|
||||
const distDir = path.resolve(__dirname, '..', 'dist');
|
||||
if (process.env.NODE_ENV === 'production' && fs.existsSync(distDir)) {
|
||||
app.use(express.static(distDir, { maxAge: '1h' }));
|
||||
app.get('*', (req, res, next) => {
|
||||
if (req.path.startsWith('/api/')) { next(); return; }
|
||||
res.sendFile(path.join(distDir, 'index.html'));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 错误处理
|
||||
*/
|
||||
app.use((err: Error, _req: Request, res: Response, _next: unknown) => {
|
||||
void _next;
|
||||
console.error('[API Error]', err);
|
||||
// multer 文件类型/大小错误
|
||||
const message = err.message || '服务器内部错误';
|
||||
if (err.message?.includes('不支持的文件类型')) {
|
||||
res.status(400).json({ success: false, error: message });
|
||||
return;
|
||||
}
|
||||
res.status(500).json({ success: false, error: message });
|
||||
});
|
||||
|
||||
/**
|
||||
* 404
|
||||
*/
|
||||
app.use((_req: Request, res: Response) => {
|
||||
res.status(404).json({ success: false, error: 'API not found' });
|
||||
});
|
||||
|
||||
export default app;
|
||||
Reference in New Issue
Block a user