refactor(server): use swagger (#2639)
This commit is contained in:
parent
3ea2fe1c48
commit
422ad20641
3 changed files with 97 additions and 95 deletions
|
@ -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' });
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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}`,
|
||||
|
|
|
@ -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;
|
||||
}
|
Loading…
Reference in a new issue