mirror of
https://github.com/samsonjs/immich.git
synced 2026-04-27 15:07:45 +00:00
feat: better endpoint descriptions (#20439)
This commit is contained in:
parent
d5a01c0310
commit
749f999f2a
34 changed files with 948 additions and 222 deletions
BIN
mobile/openapi/lib/api/activities_api.dart
generated
BIN
mobile/openapi/lib/api/activities_api.dart
generated
Binary file not shown.
BIN
mobile/openapi/lib/api/albums_api.dart
generated
BIN
mobile/openapi/lib/api/albums_api.dart
generated
Binary file not shown.
BIN
mobile/openapi/lib/api/api_keys_api.dart
generated
BIN
mobile/openapi/lib/api/api_keys_api.dart
generated
Binary file not shown.
BIN
mobile/openapi/lib/api/assets_api.dart
generated
BIN
mobile/openapi/lib/api/assets_api.dart
generated
Binary file not shown.
BIN
mobile/openapi/lib/api/authentication_api.dart
generated
BIN
mobile/openapi/lib/api/authentication_api.dart
generated
Binary file not shown.
BIN
mobile/openapi/lib/api/deprecated_api.dart
generated
BIN
mobile/openapi/lib/api/deprecated_api.dart
generated
Binary file not shown.
BIN
mobile/openapi/lib/api/download_api.dart
generated
BIN
mobile/openapi/lib/api/download_api.dart
generated
Binary file not shown.
BIN
mobile/openapi/lib/api/duplicates_api.dart
generated
BIN
mobile/openapi/lib/api/duplicates_api.dart
generated
Binary file not shown.
BIN
mobile/openapi/lib/api/faces_api.dart
generated
BIN
mobile/openapi/lib/api/faces_api.dart
generated
Binary file not shown.
BIN
mobile/openapi/lib/api/jobs_api.dart
generated
BIN
mobile/openapi/lib/api/jobs_api.dart
generated
Binary file not shown.
BIN
mobile/openapi/lib/api/libraries_api.dart
generated
BIN
mobile/openapi/lib/api/libraries_api.dart
generated
Binary file not shown.
BIN
mobile/openapi/lib/api/map_api.dart
generated
BIN
mobile/openapi/lib/api/map_api.dart
generated
Binary file not shown.
BIN
mobile/openapi/lib/api/memories_api.dart
generated
BIN
mobile/openapi/lib/api/memories_api.dart
generated
Binary file not shown.
BIN
mobile/openapi/lib/api/notifications_api.dart
generated
BIN
mobile/openapi/lib/api/notifications_api.dart
generated
Binary file not shown.
BIN
mobile/openapi/lib/api/partners_api.dart
generated
BIN
mobile/openapi/lib/api/partners_api.dart
generated
Binary file not shown.
BIN
mobile/openapi/lib/api/people_api.dart
generated
BIN
mobile/openapi/lib/api/people_api.dart
generated
Binary file not shown.
BIN
mobile/openapi/lib/api/search_api.dart
generated
BIN
mobile/openapi/lib/api/search_api.dart
generated
Binary file not shown.
BIN
mobile/openapi/lib/api/server_api.dart
generated
BIN
mobile/openapi/lib/api/server_api.dart
generated
Binary file not shown.
BIN
mobile/openapi/lib/api/sessions_api.dart
generated
BIN
mobile/openapi/lib/api/sessions_api.dart
generated
Binary file not shown.
BIN
mobile/openapi/lib/api/shared_links_api.dart
generated
BIN
mobile/openapi/lib/api/shared_links_api.dart
generated
Binary file not shown.
BIN
mobile/openapi/lib/api/stacks_api.dart
generated
BIN
mobile/openapi/lib/api/stacks_api.dart
generated
Binary file not shown.
BIN
mobile/openapi/lib/api/sync_api.dart
generated
BIN
mobile/openapi/lib/api/sync_api.dart
generated
Binary file not shown.
BIN
mobile/openapi/lib/api/system_config_api.dart
generated
BIN
mobile/openapi/lib/api/system_config_api.dart
generated
Binary file not shown.
BIN
mobile/openapi/lib/api/system_metadata_api.dart
generated
BIN
mobile/openapi/lib/api/system_metadata_api.dart
generated
Binary file not shown.
BIN
mobile/openapi/lib/api/tags_api.dart
generated
BIN
mobile/openapi/lib/api/tags_api.dart
generated
Binary file not shown.
BIN
mobile/openapi/lib/api/timeline_api.dart
generated
BIN
mobile/openapi/lib/api/timeline_api.dart
generated
Binary file not shown.
BIN
mobile/openapi/lib/api/trash_api.dart
generated
BIN
mobile/openapi/lib/api/trash_api.dart
generated
Binary file not shown.
BIN
mobile/openapi/lib/api/users_admin_api.dart
generated
BIN
mobile/openapi/lib/api/users_admin_api.dart
generated
Binary file not shown.
BIN
mobile/openapi/lib/api/users_api.dart
generated
BIN
mobile/openapi/lib/api/users_api.dart
generated
Binary file not shown.
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -413,6 +413,11 @@ export enum LogLevel {
|
||||||
Fatal = 'fatal',
|
Fatal = 'fatal',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum ApiCustomExtension {
|
||||||
|
Permission = 'x-immich-permission',
|
||||||
|
AdminOnly = 'x-immich-admin-only',
|
||||||
|
}
|
||||||
|
|
||||||
export enum MetadataKey {
|
export enum MetadataKey {
|
||||||
AuthRoute = 'auth_route',
|
AuthRoute = 'auth_route',
|
||||||
AdminRoute = 'admin_route',
|
AdminRoute = 'admin_route',
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import { Reflector } from '@nestjs/core';
|
||||||
import { ApiBearerAuth, ApiCookieAuth, ApiExtension, ApiOkResponse, ApiQuery, ApiSecurity } from '@nestjs/swagger';
|
import { ApiBearerAuth, ApiCookieAuth, ApiExtension, ApiOkResponse, ApiQuery, ApiSecurity } from '@nestjs/swagger';
|
||||||
import { Request } from 'express';
|
import { Request } from 'express';
|
||||||
import { AuthDto } from 'src/dtos/auth.dto';
|
import { AuthDto } from 'src/dtos/auth.dto';
|
||||||
import { ImmichQuery, MetadataKey, Permission } from 'src/enum';
|
import { ApiCustomExtension, ImmichQuery, MetadataKey, Permission } from 'src/enum';
|
||||||
import { LoggingRepository } from 'src/repositories/logging.repository';
|
import { LoggingRepository } from 'src/repositories/logging.repository';
|
||||||
import { AuthService, LoginDetails } from 'src/services/auth.service';
|
import { AuthService, LoginDetails } from 'src/services/auth.service';
|
||||||
import { UAParser } from 'ua-parser-js';
|
import { UAParser } from 'ua-parser-js';
|
||||||
|
|
@ -19,16 +19,20 @@ type AdminRoute = { admin?: true };
|
||||||
type SharedLinkRoute = { sharedLink?: true };
|
type SharedLinkRoute = { sharedLink?: true };
|
||||||
type AuthenticatedOptions = { permission?: Permission } & (AdminRoute | SharedLinkRoute);
|
type AuthenticatedOptions = { permission?: Permission } & (AdminRoute | SharedLinkRoute);
|
||||||
|
|
||||||
export const Authenticated = (options?: AuthenticatedOptions): MethodDecorator => {
|
export const Authenticated = (options: AuthenticatedOptions = {}): MethodDecorator => {
|
||||||
const decorators: MethodDecorator[] = [
|
const decorators: MethodDecorator[] = [
|
||||||
ApiBearerAuth(),
|
ApiBearerAuth(),
|
||||||
ApiCookieAuth(),
|
ApiCookieAuth(),
|
||||||
ApiSecurity(MetadataKey.ApiKeySecurity),
|
ApiSecurity(MetadataKey.ApiKeySecurity),
|
||||||
SetMetadata(MetadataKey.AuthRoute, options || {}),
|
SetMetadata(MetadataKey.AuthRoute, options),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
if ((options as AdminRoute).admin) {
|
||||||
|
decorators.push(ApiExtension(ApiCustomExtension.AdminOnly, true));
|
||||||
|
}
|
||||||
|
|
||||||
if (options?.permission) {
|
if (options?.permission) {
|
||||||
decorators.push(ApiExtension('x-immich-permission', options.permission));
|
decorators.push(ApiExtension(ApiCustomExtension.Permission, options.permission ?? Permission.All));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((options as SharedLinkRoute)?.sharedLink) {
|
if ((options as SharedLinkRoute)?.sharedLink) {
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,11 @@ import {
|
||||||
SwaggerDocumentOptions,
|
SwaggerDocumentOptions,
|
||||||
SwaggerModule,
|
SwaggerModule,
|
||||||
} from '@nestjs/swagger';
|
} from '@nestjs/swagger';
|
||||||
import { ReferenceObject, SchemaObject } from '@nestjs/swagger/dist/interfaces/open-api-spec.interface';
|
import {
|
||||||
|
OperationObject,
|
||||||
|
ReferenceObject,
|
||||||
|
SchemaObject,
|
||||||
|
} from '@nestjs/swagger/dist/interfaces/open-api-spec.interface';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { writeFileSync } from 'node:fs';
|
import { writeFileSync } from 'node:fs';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
|
|
@ -15,7 +19,7 @@ import parse from 'picomatch/lib/parse';
|
||||||
import { SystemConfig } from 'src/config';
|
import { SystemConfig } from 'src/config';
|
||||||
import { CLIP_MODEL_INFO, serverVersion } from 'src/constants';
|
import { CLIP_MODEL_INFO, serverVersion } from 'src/constants';
|
||||||
import { extraSyncModels } from 'src/dtos/sync.dto';
|
import { extraSyncModels } from 'src/dtos/sync.dto';
|
||||||
import { ImmichCookie, ImmichHeader, MetadataKey } from 'src/enum';
|
import { ApiCustomExtension, ImmichCookie, ImmichHeader, MetadataKey } from 'src/enum';
|
||||||
import { LoggingRepository } from 'src/repositories/logging.repository';
|
import { LoggingRepository } from 'src/repositories/logging.repository';
|
||||||
|
|
||||||
export class ImmichStartupError extends Error {}
|
export class ImmichStartupError extends Error {}
|
||||||
|
|
@ -198,7 +202,12 @@ const patchOpenAPI = (document: OpenAPIObject) => {
|
||||||
trace: path.trace,
|
trace: path.trace,
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const operation of Object.values(operations)) {
|
for (const operation of Object.values(operations) as Array<
|
||||||
|
OperationObject & {
|
||||||
|
[ApiCustomExtension.AdminOnly]?: boolean;
|
||||||
|
[ApiCustomExtension.Permission]?: string;
|
||||||
|
}
|
||||||
|
>) {
|
||||||
if (!operation) {
|
if (!operation) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -211,12 +220,21 @@ const patchOpenAPI = (document: OpenAPIObject) => {
|
||||||
// console.log(`${routeToErrorMessage(operation.operationId).padEnd(40)} (${operation.operationId})`);
|
// console.log(`${routeToErrorMessage(operation.operationId).padEnd(40)} (${operation.operationId})`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (operation.description === '') {
|
const adminOnly = operation[ApiCustomExtension.AdminOnly] ?? false;
|
||||||
delete operation.description;
|
const permission = operation[ApiCustomExtension.Permission];
|
||||||
}
|
if (permission) {
|
||||||
|
let description = (operation.description || '').trim();
|
||||||
|
if (description && !description.endsWith('.')) {
|
||||||
|
description += '. ';
|
||||||
|
}
|
||||||
|
|
||||||
if (operation.parameters) {
|
operation.description =
|
||||||
operation.parameters = _.orderBy(operation.parameters, 'name');
|
description +
|
||||||
|
`This endpoint ${adminOnly ? 'is an admin-only route, and ' : ''}requires the \`${permission}\` permission.`;
|
||||||
|
|
||||||
|
if (operation.parameters) {
|
||||||
|
operation.parameters = _.orderBy(operation.parameters, 'name');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue