104 lines
3.3 KiB
TypeScript
104 lines
3.3 KiB
TypeScript
|
|
import { NestFactory } from '@nestjs/core';
|
|||
|
|
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
|
|||
|
|
import { AppModule } from './app.module';
|
|||
|
|
|
|||
|
|
const SWAGGER_BASIC_AUTH_USER = process.env.SWAGGER_USER || 'admin';
|
|||
|
|
const SWAGGER_BASIC_AUTH_PASSWORD = process.env.SWAGGER_PASSWORD || 'change_me';
|
|||
|
|
|
|||
|
|
function isLocalhost(url: string): boolean {
|
|||
|
|
return url.includes('localhost') || url.includes('127.0.0.1');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function isSwaggerEnabled(): boolean {
|
|||
|
|
if (process.env.NODE_ENV === 'production') {
|
|||
|
|
return process.env.ENABLE_SWAGGER === 'true';
|
|||
|
|
}
|
|||
|
|
return process.env.ENABLE_SWAGGER !== 'false';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function needsBasicAuth(): boolean {
|
|||
|
|
if (process.env.NODE_ENV === 'production') {
|
|||
|
|
return process.env.ENABLE_SWAGGER === 'true';
|
|||
|
|
}
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function createBasicAuthMiddleware() {
|
|||
|
|
return (req: any, res: any, next: any) => {
|
|||
|
|
const authHeader = req.headers.authorization;
|
|||
|
|
|
|||
|
|
if (!authHeader || !authHeader.startsWith('Basic ')) {
|
|||
|
|
res.setHeader('WWW-Authenticate', 'Basic realm="Swagger API Docs"');
|
|||
|
|
res.status(401).send('Authentication required');
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const base64Credentials = authHeader.split(' ')[1];
|
|||
|
|
const credentials = Buffer.from(base64Credentials, 'base64').toString('utf-8');
|
|||
|
|
const [username, password] = credentials.split(':');
|
|||
|
|
|
|||
|
|
if (username === SWAGGER_BASIC_AUTH_USER && password === SWAGGER_BASIC_AUTH_PASSWORD) {
|
|||
|
|
next();
|
|||
|
|
} else {
|
|||
|
|
res.status(401).send('Invalid credentials');
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async function bootstrap() {
|
|||
|
|
const app = await NestFactory.create(AppModule);
|
|||
|
|
|
|||
|
|
app.enableCors({
|
|||
|
|
origin: ['https://longde.cloud', 'http://localhost:4321'],
|
|||
|
|
methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'],
|
|||
|
|
credentials: true,
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
if (isSwaggerEnabled()) {
|
|||
|
|
const config = new DocumentBuilder()
|
|||
|
|
.setTitle('龙de AI 学习产品 API')
|
|||
|
|
.setDescription('AI 学习产品后端 API 文档(v0.1),包含用户管理、学习路径、AI 对话、反馈等功能。')
|
|||
|
|
.setVersion('0.1.0')
|
|||
|
|
.addTag('health', '服务健康检查')
|
|||
|
|
.addTag('auth', '用户认证')
|
|||
|
|
.addTag('users', '用户管理')
|
|||
|
|
.addTag('knowledge', '知识库')
|
|||
|
|
.addTag('learning', '学习路径与进度')
|
|||
|
|
.addTag('ai', 'AI 分析与对话')
|
|||
|
|
.addTag('review', '复习任务')
|
|||
|
|
.addTag('feedback', '用户反馈')
|
|||
|
|
.addTag('waitlist', '等待名单')
|
|||
|
|
.build();
|
|||
|
|
|
|||
|
|
const document = SwaggerModule.createDocument(app, config);
|
|||
|
|
|
|||
|
|
if (needsBasicAuth()) {
|
|||
|
|
app.use('/api-docs', createBasicAuthMiddleware() as any);
|
|||
|
|
app.use('/api-docs-json', createBasicAuthMiddleware() as any);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
SwaggerModule.setup('api-docs', app, document, {
|
|||
|
|
swaggerOptions: {
|
|||
|
|
persistAuthorization: true,
|
|||
|
|
},
|
|||
|
|
customCss: '.swagger-ui .topbar { display: none }',
|
|||
|
|
customSiteTitle: '龙de API 文档',
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
app.getHttpAdapter().get('/api-docs-json', (_req: any, res: any) => {
|
|||
|
|
res.json(document);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
console.log('[Swagger] API 文档已启用');
|
|||
|
|
if (needsBasicAuth()) {
|
|||
|
|
console.log(`[Swagger] Basic Auth 已启用 (${SWAGGER_BASIC_AUTH_USER})`);
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
console.log('[Swagger] API 文档已禁用');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const port = process.env.PORT ?? 3000;
|
|||
|
|
await app.listen(port);
|
|||
|
|
console.log(`[API] Server running on http://localhost:${port}`);
|
|||
|
|
}
|
|||
|
|
bootstrap();
|