Files
delivery-desk/api/app.ts

107 lines
3.2 KiB
TypeScript
Raw Normal View History

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 worksRoutes from './routes/works.js';
import projectWorksRoutes from './routes/projectWorks.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/works', worksRoutes);
app.use('/api/projects/:projectId/works', projectWorksRoutes);
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;