Kaynağa Gözat

refactor(server): use swagger (#2639)

Jason Rasmussen 2 yıl önce
ebeveyn
işleme
422ad20641

+ 91 - 0
server/apps/immich/src/app.utils.ts

@@ -1,4 +1,16 @@
+import { IMMICH_ACCESS_COOKIE, IMMICH_API_KEY_HEADER, IMMICH_API_KEY_NAME, SERVER_VERSION } from '@app/domain';
+import { INestApplication } from '@nestjs/common';
+import {
+  DocumentBuilder,
+  OpenAPIObject,
+  SwaggerCustomOptions,
+  SwaggerDocumentOptions,
+  SwaggerModule,
+} from '@nestjs/swagger';
 import { Response } from 'express';
+import { writeFileSync } from 'fs';
+import path from 'path';
+import { Metadata } from './decorators/authenticated.decorator';
 import { DownloadArchive } from './modules/download/download.service';
 
 export const handleDownload = (download: DownloadArchive, res: Response) => {
@@ -8,3 +20,82 @@ export const handleDownload = (download: DownloadArchive, res: Response) => {
   res.setHeader('X-Immich-Archive-Complete', `${download.complete}`);
   return download.stream;
 };
+
+const patchOpenAPI = (document: OpenAPIObject) => {
+  for (const path of Object.values(document.paths)) {
+    const operations = {
+      get: path.get,
+      put: path.put,
+      post: path.post,
+      delete: path.delete,
+      options: path.options,
+      head: path.head,
+      patch: path.patch,
+      trace: path.trace,
+    };
+
+    for (const operation of Object.values(operations)) {
+      if (!operation) {
+        continue;
+      }
+
+      if ((operation.security || []).find((item) => !!item[Metadata.PUBLIC_SECURITY])) {
+        delete operation.security;
+      }
+
+      if (operation.summary === '') {
+        delete operation.summary;
+      }
+
+      if (operation.description === '') {
+        delete operation.description;
+      }
+    }
+  }
+
+  return document;
+};
+
+export const useSwagger = (app: INestApplication, isDev: boolean) => {
+  const config = new DocumentBuilder()
+    .setTitle('Immich')
+    .setDescription('Immich API')
+    .setVersion(SERVER_VERSION)
+    .addBearerAuth({
+      type: 'http',
+      scheme: 'Bearer',
+      in: 'header',
+    })
+    .addCookieAuth(IMMICH_ACCESS_COOKIE)
+    .addApiKey(
+      {
+        type: 'apiKey',
+        in: 'header',
+        name: IMMICH_API_KEY_HEADER,
+      },
+      IMMICH_API_KEY_NAME,
+    )
+    .addServer('/api')
+    .build();
+
+  const options: SwaggerDocumentOptions = {
+    operationIdFactory: (controllerKey: string, methodKey: string) => methodKey,
+  };
+
+  const doc = SwaggerModule.createDocument(app, config, options);
+
+  const customOptions: SwaggerCustomOptions = {
+    swaggerOptions: {
+      persistAuthorization: true,
+    },
+    customSiteTitle: 'Immich API Documentation',
+  };
+
+  SwaggerModule.setup('doc', app, doc, customOptions);
+
+  if (isDev) {
+    // Generate API Documentation only in development mode
+    const outputPath = path.resolve(process.cwd(), 'immich-openapi-specs.json');
+    writeFileSync(outputPath, JSON.stringify(patchOpenAPI(doc), null, 2), { encoding: 'utf8' });
+  }
+};

+ 6 - 58
server/apps/immich/src/main.ts

@@ -1,26 +1,17 @@
-import {
-  getLogLevels,
-  IMMICH_ACCESS_COOKIE,
-  IMMICH_API_KEY_HEADER,
-  IMMICH_API_KEY_NAME,
-  MACHINE_LEARNING_ENABLED,
-  SearchService,
-  SERVER_VERSION,
-} from '@app/domain';
+import { getLogLevels, MACHINE_LEARNING_ENABLED, SearchService, SERVER_VERSION } from '@app/domain';
 import { RedisIoAdapter } from '@app/infra';
 import { Logger } from '@nestjs/common';
 import { NestFactory } from '@nestjs/core';
 import { NestExpressApplication } from '@nestjs/platform-express';
-import { DocumentBuilder, SwaggerDocumentOptions, SwaggerModule } from '@nestjs/swagger';
 import { json } from 'body-parser';
 import cookieParser from 'cookie-parser';
-import { writeFileSync } from 'fs';
-import path from 'path';
 import { AppModule } from './app.module';
 import { AppService } from './app.service';
-import { patchOpenAPI } from './utils/patch-open-api.util';
+import { useSwagger } from './app.utils';
 
 const logger = new Logger('ImmichServer');
+const isDev = process.env.NODE_ENV === 'development';
+const serverPort = Number(process.env.SERVER_PORT) || 3001;
 
 async function bootstrap() {
   const app = await NestFactory.create<NestExpressApplication>(AppModule, {
@@ -31,57 +22,14 @@ async function bootstrap() {
   app.set('etag', 'strong');
   app.use(cookieParser());
   app.use(json({ limit: '10mb' }));
-  if (process.env.NODE_ENV === 'development') {
+  if (isDev) {
     app.enableCors();
   }
-
-  const serverPort = Number(process.env.SERVER_PORT) || 3001;
-
   app.useWebSocketAdapter(new RedisIoAdapter(app));
-
-  const config = new DocumentBuilder()
-    .setTitle('Immich')
-    .setDescription('Immich API')
-    .setVersion(SERVER_VERSION)
-    .addBearerAuth({
-      type: 'http',
-      scheme: 'Bearer',
-      in: 'header',
-    })
-    .addCookieAuth(IMMICH_ACCESS_COOKIE)
-    .addApiKey(
-      {
-        type: 'apiKey',
-        in: 'header',
-        name: IMMICH_API_KEY_HEADER,
-      },
-      IMMICH_API_KEY_NAME,
-    )
-    .addServer('/api')
-    .build();
-
-  const apiDocumentOptions: SwaggerDocumentOptions = {
-    operationIdFactory: (controllerKey: string, methodKey: string) => methodKey,
-  };
-
-  const apiDocument = SwaggerModule.createDocument(app, config, apiDocumentOptions);
-
-  SwaggerModule.setup('doc', app, apiDocument, {
-    swaggerOptions: {
-      persistAuthorization: true,
-    },
-    customSiteTitle: 'Immich API Documentation',
-  });
-
+  useSwagger(app, isDev);
   await app.get(AppService).init();
 
   await app.listen(serverPort, () => {
-    if (process.env.NODE_ENV == 'development') {
-      // Generate API Documentation only in development mode
-      const outputPath = path.resolve(process.cwd(), 'immich-openapi-specs.json');
-      writeFileSync(outputPath, JSON.stringify(patchOpenAPI(apiDocument), null, 2), { encoding: 'utf8' });
-    }
-
     const envName = (process.env.NODE_ENV || 'development').toUpperCase();
     logger.log(
       `Running Immich Server in ${envName} environment - version ${SERVER_VERSION} - Listening on port: ${serverPort}`,

+ 0 - 37
server/apps/immich/src/utils/patch-open-api.util.ts

@@ -1,37 +0,0 @@
-import { OpenAPIObject } from '@nestjs/swagger';
-import { Metadata } from '../decorators/authenticated.decorator';
-
-export function patchOpenAPI(document: OpenAPIObject) {
-  for (const path of Object.values(document.paths)) {
-    const operations = {
-      get: path.get,
-      put: path.put,
-      post: path.post,
-      delete: path.delete,
-      options: path.options,
-      head: path.head,
-      patch: path.patch,
-      trace: path.trace,
-    };
-
-    for (const operation of Object.values(operations)) {
-      if (!operation) {
-        continue;
-      }
-
-      if ((operation.security || []).find((item) => !!item[Metadata.PUBLIC_SECURITY])) {
-        delete operation.security;
-      }
-
-      if (operation.summary === '') {
-        delete operation.summary;
-      }
-
-      if (operation.description === '') {
-        delete operation.description;
-      }
-    }
-  }
-
-  return document;
-}