Files
WorthPath/apps/api/src/main.ts
T

118 lines
4.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'reflect-metadata';
import 'dotenv/config';
import { setupOpenApi } from './openapi';
import { Module, Catch, ArgumentsHost, ExceptionFilter, HttpException } from '@nestjs/common';
import { NestFactory, APP_GUARD } from '@nestjs/core';
import cookieParser from 'cookie-parser';
import helmet from 'helmet';
import { json } from 'express';
import { AuthController, AuthBusinessService, AuthGuard, AuthService } from './auth';
import { CalendarController, CalendarBusinessService } from './calendar';
import { SchedulesController, SchedulesBusinessService } from './schedules';
import { TransfersController, TransfersBusinessService } from './transfers';
import { PortfolioController, PortfolioBusinessService } from './portfolio';
import { BackupController, BackupBusinessService } from './backup';
import { IconsController, IconsBusinessService, IconsService } from './icons';
import { MetalsService, MetalsController, MetalsBusinessService } from './metals';
import { Database } from './database';
import { RatesService, SettingsController, SettingsBusinessService } from './rates';
import { ZodError } from 'zod';
import { Prisma } from '@prisma/client';
import { AgentOAuth } from './mcp/oauth';
import { AgentCatalogue } from './mcp/catalogue';
import { AgentOperations } from './mcp/operations';
import { AgentFiles } from './mcp/files';
import { AgentTransport } from './mcp/transport';
import { AgentManagementController } from './mcp/management';
@Catch()
class SafeErrors implements ExceptionFilter {
catch(error: unknown, host: ArgumentsHost) {
let status = 500,
message = '服务暂时不可用,请稍后重试';
if (error instanceof ZodError) {
status = 400;
message = error.issues.map((i) => `${i.path.join('.')}: ${i.message}`).join(';');
} else if (error instanceof HttpException) {
status = error.getStatus();
message = error.message;
} else if (
error instanceof Prisma.PrismaClientKnownRequestError &&
['P2002', 'P2034'].includes(error.code)
) {
status = 409;
message = '数据已存在或已被其他操作更新,请刷新后重试';
} else if (error instanceof SyntaxError) {
status = 400;
message = 'JSON 文件或请求格式无效';
} else if ((error as { status?: number })?.status === 413) {
status = 413;
message = '文件不能超过 8 MB';
}
host.switchToHttp().getResponse().setHeader('Cache-Control', 'no-store');
host.switchToHttp().getResponse().status(status).json({ message });
}
}
@Module({
providers: [
Database,
AuthService,
RatesService,
MetalsService,
IconsService,
AuthBusinessService,
CalendarBusinessService,
SchedulesBusinessService,
TransfersBusinessService,
PortfolioBusinessService,
BackupBusinessService,
IconsBusinessService,
MetalsBusinessService,
SettingsBusinessService,
AgentOAuth,
AgentCatalogue,
AgentOperations,
AgentFiles,
AgentTransport,
{ provide: APP_GUARD, useClass: AuthGuard },
],
controllers: [
TransfersController,
SchedulesController,
CalendarController,
IconsController,
AuthController,
PortfolioController,
MetalsController,
SettingsController,
BackupController,
AgentManagementController,
],
})
class AppModule {}
async function bootstrap() {
if (!process.env.DATABASE_URL || !process.env.WEB_ORIGIN)
throw Error('Missing local environment configuration');
if (process.env.NODE_ENV === 'production' && process.env.WEB_ORIGIN === '*')
throw Error('Production requires an explicit web origin');
if (process.env.NODE_ENV === 'production' && process.env.COOKIE_SECURE !== 'true')
throw Error('Production requires secure cookies');
const app = await NestFactory.create(AppModule, { logger: false, bodyParser: false });
app.use(helmet());
app.use(json({ limit: '8mb' }));
app.use(cookieParser());
app.use((_req: unknown, res: { setHeader: (k: string, v: string) => void }, next: () => void) => {
res.setHeader('Cache-Control', 'no-store');
next();
});
app.get(AgentTransport).install(app.getHttpAdapter().getInstance());
app.useGlobalFilters(new SafeErrors());
setupOpenApi(app);
app.enableShutdownHooks();
await app.listen(Number(process.env.PORT || 3100), '0.0.0.0');
console.log('WorthPath API ready');
}
void bootstrap().catch(() => {
console.error('API startup failed. Check local configuration and database availability.');
process.exitCode = 1;
});