mirror of
https://github.com/immich-app/immich.git
synced 2025-01-06 03:46:47 +01:00
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 { Response } from 'express';
|
||||||
|
import { writeFileSync } from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
import { Metadata } from './decorators/authenticated.decorator';
|
||||||
import { DownloadArchive } from './modules/download/download.service';
|
import { DownloadArchive } from './modules/download/download.service';
|
||||||
|
|
||||||
export const handleDownload = (download: DownloadArchive, res: Response) => {
|
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}`);
|
res.setHeader('X-Immich-Archive-Complete', `${download.complete}`);
|
||||||
return download.stream;
|
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 {
|
import { getLogLevels, MACHINE_LEARNING_ENABLED, SearchService, SERVER_VERSION } from '@app/domain';
|
||||||
getLogLevels,
|
|
||||||
IMMICH_ACCESS_COOKIE,
|
|
||||||
IMMICH_API_KEY_HEADER,
|
|
||||||
IMMICH_API_KEY_NAME,
|
|
||||||
MACHINE_LEARNING_ENABLED,
|
|
||||||
SearchService,
|
|
||||||
SERVER_VERSION,
|
|
||||||
} from '@app/domain';
|
|
||||||
import { RedisIoAdapter } from '@app/infra';
|
import { RedisIoAdapter } from '@app/infra';
|
||||||
import { Logger } from '@nestjs/common';
|
import { Logger } from '@nestjs/common';
|
||||||
import { NestFactory } from '@nestjs/core';
|
import { NestFactory } from '@nestjs/core';
|
||||||
import { NestExpressApplication } from '@nestjs/platform-express';
|
import { NestExpressApplication } from '@nestjs/platform-express';
|
||||||
import { DocumentBuilder, SwaggerDocumentOptions, SwaggerModule } from '@nestjs/swagger';
|
|
||||||
import { json } from 'body-parser';
|
import { json } from 'body-parser';
|
||||||
import cookieParser from 'cookie-parser';
|
import cookieParser from 'cookie-parser';
|
||||||
import { writeFileSync } from 'fs';
|
|
||||||
import path from 'path';
|
|
||||||
import { AppModule } from './app.module';
|
import { AppModule } from './app.module';
|
||||||
import { AppService } from './app.service';
|
import { AppService } from './app.service';
|
||||||
import { patchOpenAPI } from './utils/patch-open-api.util';
|
import { useSwagger } from './app.utils';
|
||||||
|
|
||||||
const logger = new Logger('ImmichServer');
|
const logger = new Logger('ImmichServer');
|
||||||
|
const isDev = process.env.NODE_ENV === 'development';
|
||||||
|
const serverPort = Number(process.env.SERVER_PORT) || 3001;
|
||||||
|
|
||||||
async function bootstrap() {
|
async function bootstrap() {
|
||||||
const app = await NestFactory.create<NestExpressApplication>(AppModule, {
|
const app = await NestFactory.create<NestExpressApplication>(AppModule, {
|
||||||
|
@ -31,57 +22,14 @@ async function bootstrap() {
|
||||||
app.set('etag', 'strong');
|
app.set('etag', 'strong');
|
||||||
app.use(cookieParser());
|
app.use(cookieParser());
|
||||||
app.use(json({ limit: '10mb' }));
|
app.use(json({ limit: '10mb' }));
|
||||||
if (process.env.NODE_ENV === 'development') {
|
if (isDev) {
|
||||||
app.enableCors();
|
app.enableCors();
|
||||||
}
|
}
|
||||||
|
|
||||||
const serverPort = Number(process.env.SERVER_PORT) || 3001;
|
|
||||||
|
|
||||||
app.useWebSocketAdapter(new RedisIoAdapter(app));
|
app.useWebSocketAdapter(new RedisIoAdapter(app));
|
||||||
|
useSwagger(app, isDev);
|
||||||
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',
|
|
||||||
});
|
|
||||||
|
|
||||||
await app.get(AppService).init();
|
await app.get(AppService).init();
|
||||||
|
|
||||||
await app.listen(serverPort, () => {
|
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();
|
const envName = (process.env.NODE_ENV || 'development').toUpperCase();
|
||||||
logger.log(
|
logger.log(
|
||||||
`Running Immich Server in ${envName} environment - version ${SERVER_VERSION} - Listening on port: ${serverPort}`,
|
`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