mirror of
https://github.com/immich-app/immich
synced 2025-10-17 18:19:27 +00:00
136 lines
3.3 KiB
TypeScript
136 lines
3.3 KiB
TypeScript
import {
|
|
ImmichReadStream,
|
|
IMMICH_ACCESS_COOKIE,
|
|
IMMICH_API_KEY_HEADER,
|
|
IMMICH_API_KEY_NAME,
|
|
SERVER_VERSION,
|
|
} from '@app/domain';
|
|
import { INestApplication, StreamableFile } from '@nestjs/common';
|
|
import {
|
|
DocumentBuilder,
|
|
OpenAPIObject,
|
|
SwaggerCustomOptions,
|
|
SwaggerDocumentOptions,
|
|
SwaggerModule,
|
|
} from '@nestjs/swagger';
|
|
import { writeFileSync } from 'fs';
|
|
import path from 'path';
|
|
|
|
import { applyDecorators, UsePipes, ValidationPipe } from '@nestjs/common';
|
|
import { Metadata } from './app.guard';
|
|
|
|
export function UseValidation() {
|
|
return applyDecorators(
|
|
UsePipes(
|
|
new ValidationPipe({
|
|
transform: true,
|
|
whitelist: true,
|
|
}),
|
|
),
|
|
);
|
|
}
|
|
|
|
export const asStreamableFile = ({ stream, type, length }: ImmichReadStream) => {
|
|
return new StreamableFile(stream, { type, length });
|
|
};
|
|
|
|
export function patchFormData(latin1: string) {
|
|
return Buffer.from(latin1, 'latin1').toString('utf8');
|
|
}
|
|
|
|
function sortKeys<T extends object>(obj: T): T {
|
|
if (!obj) {
|
|
return obj;
|
|
}
|
|
|
|
const result: Partial<T> = {};
|
|
const keys = Object.keys(obj).sort() as Array<keyof T>;
|
|
for (const key of keys) {
|
|
result[key] = obj[key];
|
|
}
|
|
return result as T;
|
|
}
|
|
|
|
const patchOpenAPI = (document: OpenAPIObject) => {
|
|
document.paths = sortKeys(document.paths);
|
|
if (document.components?.schemas) {
|
|
document.components.schemas = sortKeys(document.components.schemas);
|
|
}
|
|
|
|
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' });
|
|
}
|
|
};
|