Przeglądaj źródła

feat: update Swagger setup to prevent caching and improve JSON document URL

Dave 2 miesięcy temu
rodzic
commit
b9a7fc52fe

+ 25 - 12
apps/box-app-api/src/main.ts

@@ -25,15 +25,19 @@ async function bootstrap() {
   const port =
     configService.get<number>('APP_PORT') ?? Number(process.env.PORT ?? 3301);
 
-  const crossOrigin =
-    configService.get<string>('APP_CROSS_ORIGIN') ??
-    configService.get<string>('CROSS_ORIGIN') ??
-    '*';
+  const corsOrigin = configService.get<string>('APP_CORS_ORIGIN') ?? '*';
+  const corsOriginOption =
+    corsOrigin === '*'
+      ? true
+      : corsOrigin
+          .split(',')
+          .map((o) => o.trim())
+          .filter(Boolean);
 
   app.enableCors({
-    origin:
-      crossOrigin === '*' ? true : crossOrigin.split(',').map((o) => o.trim()),
+    origin: corsOriginOption,
     methods: 'GET,PUT,PATCH,POST,DELETE',
+    credentials: true,
   });
 
   // 👇 Important: this makes /health become /api/v1/health
@@ -44,11 +48,6 @@ async function bootstrap() {
   app.use(helmet());
   app.use(compression());
 
-  app.enableCors({
-    origin: true,
-    credentials: true,
-  });
-
   app.useGlobalPipes(
     new ValidationPipe({
       whitelist: true,
@@ -68,21 +67,35 @@ async function bootstrap() {
     )
     .setVersion('1.0.0')
     .build();
+
   const swaggerDocument = SwaggerModule.createDocument(app, swaggerConfig);
+
   SwaggerModule.setup('api-docs', app, swaggerDocument, {
-    jsonDocumentUrl: 'api-docs/json',
+    jsonDocumentUrl: '/api-docs-json',
     swaggerOptions: {
       persistAuthorization: true,
       docExpansion: 'none',
     },
   });
 
+  // Prevent Swagger UI/JSON from being cached (browser + proxies)
+  app.use(['/api-docs', '/api-docs-json'], (req, res, next) => {
+    res.setHeader(
+      'Cache-Control',
+      'no-store, no-cache, must-revalidate, proxy-revalidate',
+    );
+    res.setHeader('Pragma', 'no-cache');
+    res.setHeader('Expires', '0');
+    next();
+  });
+
   await app.listen(port, host);
 
   const url = `http://${host}:${port}`;
 
   logger.log(`🚀 box-app-api listening on ${url} (global prefix: /api/v1)`);
   logger.log(`📖 Swagger 文档: ${url}/api-docs`);
+  logger.log(`📄 Swagger JSON: ${url}/api-docs-json`);
 }
 
 bootstrap().catch((error) => {

+ 34 - 9
apps/box-mgnt-api/src/main.ts

@@ -64,19 +64,23 @@ async function bootstrap() {
   const port =
     configService.get<number>('APP_PORT') ?? Number(process.env.PORT ?? 3300);
 
-  const crossOrigin =
-    configService.get<string>('APP_CROSS_ORIGIN') ??
-    configService.get<string>('CROSS_ORIGIN') ??
-    '*';
+  const corsOrigin = configService.get<string>('APP_CORS_ORIGIN') ?? '*';
+  const corsOriginOption =
+    corsOrigin === '*'
+      ? true
+      : corsOrigin
+          .split(',')
+          .map((o) => o.trim())
+          .filter(Boolean);
 
   app.enableCors({
-    origin:
-      crossOrigin === '*' ? true : crossOrigin.split(',').map((o) => o.trim()),
+    origin: corsOriginOption,
     methods: 'GET,PUT,PATCH,POST,DELETE',
+    credentials: true,
   });
 
   if (process.env.NODE_ENV !== 'production') {
-    const config = new DocumentBuilder()
+    const swaggerConfig = new DocumentBuilder()
       .setTitle('盒子管理系统管理后台 API 文档')
       .setDescription('盒子管理系统管理后台接口 (api/v1/mgnt/*)')
       .setVersion('1.0')
@@ -88,14 +92,34 @@ async function bootstrap() {
       .addTag('系统 - 菜单', '管理后台菜单管理接口 (mgnt/menus)')
       .build();
 
-    const document = SwaggerModule.createDocument(app, config);
-    SwaggerModule.setup('api-docs', app, document, {
+    const swaggerDocument = SwaggerModule.createDocument(app, swaggerConfig);
+
+    SwaggerModule.setup('api-docs', app, swaggerDocument, {
+      jsonDocumentUrl: '/api-docs-json',
       swaggerOptions: {
         docExpansion: 'none',
         tagsSorter: 'alpha',
         operationsSorter: 'alpha',
       },
     });
+
+    // Prevent Swagger UI/JSON from being cached (browser + proxies)
+    const fastify = app.getHttpAdapter().getInstance();
+    fastify.addHook('onSend', (request, reply, payload, done) => {
+      if (
+        request.url?.startsWith('/api-docs') ||
+        request.url === '/api-docs-json'
+      ) {
+        reply.header(
+          'Cache-Control',
+          'no-store, no-cache, must-revalidate, proxy-revalidate',
+        );
+        reply.header('Pragma', 'no-cache');
+        reply.header('Expires', '0');
+      }
+
+      done(null, payload);
+    });
   }
 
   await app.listen(port, host);
@@ -103,6 +127,7 @@ async function bootstrap() {
   const url = `http://${host}:${port}`;
   console.log(`🚀 box-mgnt-api is running at: ${url}`);
   console.log(`📚 API Documentation: ${url}/api-docs`);
+  console.log(`📄 Swagger JSON: ${url}/api-docs-json`);
   console.log(`🔧 Management APIs: ${url}/api/v1/mgnt/*`);
 }
 

+ 25 - 12
apps/box-stats-api/src/main.ts

@@ -23,15 +23,19 @@ async function bootstrap() {
   const port =
     configService.get<number>('APP_PORT') ?? Number(process.env.PORT ?? 3302);
 
-  const crossOrigin =
-    configService.get<string>('APP_CROSS_ORIGIN') ??
-    configService.get<string>('CROSS_ORIGIN') ??
-    '*';
+  const corsOrigin = configService.get<string>('APP_CORS_ORIGIN') ?? '*';
+  const corsOriginOption =
+    corsOrigin === '*'
+      ? true
+      : corsOrigin
+          .split(',')
+          .map((o) => o.trim())
+          .filter(Boolean);
 
   app.enableCors({
-    origin:
-      crossOrigin === '*' ? true : crossOrigin.split(',').map((o) => o.trim()),
+    origin: corsOriginOption,
     methods: 'GET,PUT,PATCH,POST,DELETE',
+    credentials: true,
   });
 
   // 👇 Important: this makes /health become /api/v1/health
@@ -39,11 +43,6 @@ async function bootstrap() {
     exclude: ['/'],
   });
 
-  app.enableCors({
-    origin: true,
-    credentials: true,
-  });
-
   app.useGlobalPipes(
     new ValidationPipe({
       whitelist: true,
@@ -63,21 +62,35 @@ async function bootstrap() {
     )
     .setVersion('1.0.0')
     .build();
+
   const swaggerDocument = SwaggerModule.createDocument(app, swaggerConfig);
+
   SwaggerModule.setup('api-docs', app, swaggerDocument, {
-    jsonDocumentUrl: 'api-docs/json',
+    jsonDocumentUrl: '/api-docs-json',
     swaggerOptions: {
       persistAuthorization: true,
       docExpansion: 'none',
     },
   });
 
+  // Prevent Swagger UI/JSON from being cached (browser + proxies)
+  app.use(['/api-docs', '/api-docs-json'], (req, res, next) => {
+    res.setHeader(
+      'Cache-Control',
+      'no-store, no-cache, must-revalidate, proxy-revalidate',
+    );
+    res.setHeader('Pragma', 'no-cache');
+    res.setHeader('Expires', '0');
+    next();
+  });
+
   await app.listen(port, host);
 
   const url = `http://${host}:${port}`;
 
   logger.log(`🚀 box-stats-api listening on ${url} (global prefix: /api/v1)`);
   logger.log(`📖 Swagger 文档: ${url}/api-docs`);
+  logger.log(`📄 Swagger JSON: ${url}/api-docs-json`);
 }
 
 bootstrap().catch((error) => {