mirror of
https://github.com/immich-app/immich.git
synced 2024-12-25 10:43:13 +02:00
feat(server): better api error messages (for unhandled exceptions) (#4817)
* feat(server): better error messages * chore: open api * chore: remove debug log * fix: syntax error * fix: e2e test
This commit is contained in:
parent
d4ef6f52bb
commit
2e424fe249
1146
cli/src/api/open-api/api.ts
generated
1146
cli/src/api/open-api/api.ts
generated
File diff suppressed because it is too large
Load Diff
@ -17,7 +17,7 @@ class OAuthService {
|
||||
// Resolve API server endpoint from user provided serverUrl
|
||||
await _apiService.resolveAndSetEndpoint(serverUrl);
|
||||
|
||||
return await _apiService.oAuthApi.generateConfig(
|
||||
return await _apiService.oAuthApi.generateOAuthConfig(
|
||||
OAuthConfigDto(redirectUri: '$callbackUrlScheme:/'),
|
||||
);
|
||||
}
|
||||
@ -29,7 +29,7 @@ class OAuthService {
|
||||
callbackUrlScheme: callbackUrlScheme,
|
||||
);
|
||||
|
||||
return await _apiService.oAuthApi.callback(
|
||||
return await _apiService.oAuthApi.finishOAuth(
|
||||
OAuthCallbackDto(
|
||||
url: result,
|
||||
),
|
||||
|
@ -65,7 +65,7 @@ class LoginForm extends HookConsumerWidget {
|
||||
isLoadingServer.value = true;
|
||||
final endpoint = await apiService.resolveAndSetEndpoint(serverUrl);
|
||||
|
||||
final loginConfig = await apiService.oAuthApi.generateConfig(
|
||||
final loginConfig = await apiService.oAuthApi.generateOAuthConfig(
|
||||
OAuthConfigDto(redirectUri: serverUrl),
|
||||
);
|
||||
|
||||
|
BIN
mobile/openapi/README.md
generated
BIN
mobile/openapi/README.md
generated
Binary file not shown.
BIN
mobile/openapi/doc/APIKeyApi.md
generated
BIN
mobile/openapi/doc/APIKeyApi.md
generated
Binary file not shown.
BIN
mobile/openapi/doc/AssetApi.md
generated
BIN
mobile/openapi/doc/AssetApi.md
generated
Binary file not shown.
BIN
mobile/openapi/doc/AuthenticationApi.md
generated
BIN
mobile/openapi/doc/AuthenticationApi.md
generated
Binary file not shown.
BIN
mobile/openapi/doc/LibraryApi.md
generated
BIN
mobile/openapi/doc/LibraryApi.md
generated
Binary file not shown.
BIN
mobile/openapi/doc/OAuthApi.md
generated
BIN
mobile/openapi/doc/OAuthApi.md
generated
Binary file not shown.
BIN
mobile/openapi/doc/ServerInfoApi.md
generated
BIN
mobile/openapi/doc/ServerInfoApi.md
generated
Binary file not shown.
BIN
mobile/openapi/doc/SystemConfigApi.md
generated
BIN
mobile/openapi/doc/SystemConfigApi.md
generated
Binary file not shown.
BIN
mobile/openapi/lib/api/api_key_api.dart
generated
BIN
mobile/openapi/lib/api/api_key_api.dart
generated
Binary file not shown.
BIN
mobile/openapi/lib/api/asset_api.dart
generated
BIN
mobile/openapi/lib/api/asset_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/library_api.dart
generated
BIN
mobile/openapi/lib/api/library_api.dart
generated
Binary file not shown.
BIN
mobile/openapi/lib/api/o_auth_api.dart
generated
BIN
mobile/openapi/lib/api/o_auth_api.dart
generated
Binary file not shown.
BIN
mobile/openapi/lib/api/server_info_api.dart
generated
BIN
mobile/openapi/lib/api/server_info_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/test/api_key_api_test.dart
generated
BIN
mobile/openapi/test/api_key_api_test.dart
generated
Binary file not shown.
BIN
mobile/openapi/test/asset_api_test.dart
generated
BIN
mobile/openapi/test/asset_api_test.dart
generated
Binary file not shown.
BIN
mobile/openapi/test/authentication_api_test.dart
generated
BIN
mobile/openapi/test/authentication_api_test.dart
generated
Binary file not shown.
BIN
mobile/openapi/test/library_api_test.dart
generated
BIN
mobile/openapi/test/library_api_test.dart
generated
Binary file not shown.
BIN
mobile/openapi/test/o_auth_api_test.dart
generated
BIN
mobile/openapi/test/o_auth_api_test.dart
generated
Binary file not shown.
BIN
mobile/openapi/test/server_info_api_test.dart
generated
BIN
mobile/openapi/test/server_info_api_test.dart
generated
Binary file not shown.
BIN
mobile/openapi/test/system_config_api_test.dart
generated
BIN
mobile/openapi/test/system_config_api_test.dart
generated
Binary file not shown.
@ -678,7 +678,7 @@
|
||||
},
|
||||
"/api-key": {
|
||||
"get": {
|
||||
"operationId": "getKeys",
|
||||
"operationId": "getApiKeys",
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
@ -711,7 +711,7 @@
|
||||
]
|
||||
},
|
||||
"post": {
|
||||
"operationId": "createKey",
|
||||
"operationId": "createApiKey",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"content": {
|
||||
@ -753,7 +753,7 @@
|
||||
},
|
||||
"/api-key/{id}": {
|
||||
"delete": {
|
||||
"operationId": "deleteKey",
|
||||
"operationId": "deleteApiKey",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
@ -786,7 +786,7 @@
|
||||
]
|
||||
},
|
||||
"get": {
|
||||
"operationId": "getKey",
|
||||
"operationId": "getApiKey",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
@ -826,7 +826,7 @@
|
||||
]
|
||||
},
|
||||
"put": {
|
||||
"operationId": "updateKey",
|
||||
"operationId": "updateApiKey",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "id",
|
||||
@ -1084,7 +1084,7 @@
|
||||
"/asset/bulk-upload-check": {
|
||||
"post": {
|
||||
"description": "Checks if assets exist by checksums",
|
||||
"operationId": "bulkUploadCheck",
|
||||
"operationId": "checkBulkUpload",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"content": {
|
||||
@ -1855,7 +1855,7 @@
|
||||
},
|
||||
"/asset/statistics": {
|
||||
"get": {
|
||||
"operationId": "getAssetStats",
|
||||
"operationId": "getAssetStatistics",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "isArchived",
|
||||
@ -1977,7 +1977,7 @@
|
||||
},
|
||||
"/asset/time-bucket": {
|
||||
"get": {
|
||||
"operationId": "getByTimeBucket",
|
||||
"operationId": "getTimeBucket",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "size",
|
||||
@ -2596,7 +2596,7 @@
|
||||
},
|
||||
"/auth/admin-sign-up": {
|
||||
"post": {
|
||||
"operationId": "adminSignUp",
|
||||
"operationId": "signUpAdmin",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"content": {
|
||||
@ -2943,7 +2943,7 @@
|
||||
},
|
||||
"/library": {
|
||||
"get": {
|
||||
"operationId": "getAllForUser",
|
||||
"operationId": "getLibraries",
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
@ -3265,7 +3265,7 @@
|
||||
},
|
||||
"/oauth/authorize": {
|
||||
"post": {
|
||||
"operationId": "authorizeOAuth",
|
||||
"operationId": "startOAuth",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"content": {
|
||||
@ -3296,7 +3296,7 @@
|
||||
},
|
||||
"/oauth/callback": {
|
||||
"post": {
|
||||
"operationId": "callback",
|
||||
"operationId": "finishOAuth",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"content": {
|
||||
@ -3329,7 +3329,7 @@
|
||||
"post": {
|
||||
"deprecated": true,
|
||||
"description": "@deprecated use feature flags and /oauth/authorize",
|
||||
"operationId": "generateConfig",
|
||||
"operationId": "generateOAuthConfig",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"content": {
|
||||
@ -3360,7 +3360,7 @@
|
||||
},
|
||||
"/oauth/link": {
|
||||
"post": {
|
||||
"operationId": "link",
|
||||
"operationId": "linkOAuthAccount",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"content": {
|
||||
@ -3402,7 +3402,7 @@
|
||||
},
|
||||
"/oauth/mobile-redirect": {
|
||||
"get": {
|
||||
"operationId": "mobileRedirect",
|
||||
"operationId": "redirectOAuthToMobile",
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
@ -3416,7 +3416,7 @@
|
||||
},
|
||||
"/oauth/unlink": {
|
||||
"post": {
|
||||
"operationId": "unlink",
|
||||
"operationId": "unlinkOAuthAccount",
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"201": {
|
||||
@ -4307,9 +4307,9 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"/server-info/stats": {
|
||||
"/server-info/statistics": {
|
||||
"get": {
|
||||
"operationId": "getStats",
|
||||
"operationId": "getServerStatistics",
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
@ -4837,7 +4837,7 @@
|
||||
},
|
||||
"/system-config/defaults": {
|
||||
"get": {
|
||||
"operationId": "getDefaults",
|
||||
"operationId": "getConfigDefaults",
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
|
@ -331,17 +331,17 @@ describe(AssetService.name, () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('getByTimeBucket', () => {
|
||||
describe('getTimeBucket', () => {
|
||||
it('should return the assets for a album time bucket if user has album.read', async () => {
|
||||
accessMock.album.hasOwnerAccess.mockResolvedValue(true);
|
||||
assetMock.getByTimeBucket.mockResolvedValue([assetStub.image]);
|
||||
assetMock.getTimeBucket.mockResolvedValue([assetStub.image]);
|
||||
|
||||
await expect(
|
||||
sut.getByTimeBucket(authStub.admin, { size: TimeBucketSize.DAY, timeBucket: 'bucket', albumId: 'album-id' }),
|
||||
sut.getTimeBucket(authStub.admin, { size: TimeBucketSize.DAY, timeBucket: 'bucket', albumId: 'album-id' }),
|
||||
).resolves.toEqual(expect.arrayContaining([expect.objectContaining({ id: 'asset-id' })]));
|
||||
|
||||
expect(accessMock.album.hasOwnerAccess).toHaveBeenCalledWith(authStub.admin.id, 'album-id');
|
||||
expect(assetMock.getByTimeBucket).toBeCalledWith('bucket', {
|
||||
expect(assetMock.getTimeBucket).toBeCalledWith('bucket', {
|
||||
size: TimeBucketSize.DAY,
|
||||
timeBucket: 'bucket',
|
||||
albumId: 'album-id',
|
||||
@ -349,17 +349,17 @@ describe(AssetService.name, () => {
|
||||
});
|
||||
|
||||
it('should return the assets for a archive time bucket if user has archive.read', async () => {
|
||||
assetMock.getByTimeBucket.mockResolvedValue([assetStub.image]);
|
||||
assetMock.getTimeBucket.mockResolvedValue([assetStub.image]);
|
||||
|
||||
await expect(
|
||||
sut.getByTimeBucket(authStub.admin, {
|
||||
sut.getTimeBucket(authStub.admin, {
|
||||
size: TimeBucketSize.DAY,
|
||||
timeBucket: 'bucket',
|
||||
isArchived: true,
|
||||
userId: authStub.admin.id,
|
||||
}),
|
||||
).resolves.toEqual(expect.arrayContaining([expect.objectContaining({ id: 'asset-id' })]));
|
||||
expect(assetMock.getByTimeBucket).toBeCalledWith('bucket', {
|
||||
expect(assetMock.getTimeBucket).toBeCalledWith('bucket', {
|
||||
size: TimeBucketSize.DAY,
|
||||
timeBucket: 'bucket',
|
||||
isArchived: true,
|
||||
@ -368,16 +368,16 @@ describe(AssetService.name, () => {
|
||||
});
|
||||
|
||||
it('should return the assets for a library time bucket if user has library.read', async () => {
|
||||
assetMock.getByTimeBucket.mockResolvedValue([assetStub.image]);
|
||||
assetMock.getTimeBucket.mockResolvedValue([assetStub.image]);
|
||||
|
||||
await expect(
|
||||
sut.getByTimeBucket(authStub.admin, {
|
||||
sut.getTimeBucket(authStub.admin, {
|
||||
size: TimeBucketSize.DAY,
|
||||
timeBucket: 'bucket',
|
||||
userId: authStub.admin.id,
|
||||
}),
|
||||
).resolves.toEqual(expect.arrayContaining([expect.objectContaining({ id: 'asset-id' })]));
|
||||
expect(assetMock.getByTimeBucket).toBeCalledWith('bucket', {
|
||||
expect(assetMock.getTimeBucket).toBeCalledWith('bucket', {
|
||||
size: TimeBucketSize.DAY,
|
||||
timeBucket: 'bucket',
|
||||
userId: authStub.admin.id,
|
||||
|
@ -194,12 +194,12 @@ export class AssetService {
|
||||
return this.assetRepository.getTimeBuckets(dto);
|
||||
}
|
||||
|
||||
async getByTimeBucket(
|
||||
async getTimeBucket(
|
||||
authUser: AuthUserDto,
|
||||
dto: TimeBucketAssetDto,
|
||||
): Promise<AssetResponseDto[] | SanitizedAssetResponseDto[]> {
|
||||
await this.timeBucketChecks(authUser, dto);
|
||||
const assets = await this.assetRepository.getByTimeBucket(dto.timeBucket, dto);
|
||||
const assets = await this.assetRepository.getTimeBucket(dto.timeBucket, dto);
|
||||
if (authUser.isShowMetadata) {
|
||||
return assets.map((asset) => mapAsset(asset, { withStack: true }));
|
||||
} else {
|
||||
|
@ -315,13 +315,8 @@ export class AuthService {
|
||||
const redirectUri = this.normalize(config, url.split('?')[0]);
|
||||
const client = await this.getOAuthClient(config);
|
||||
const params = client.callbackParams(url);
|
||||
try {
|
||||
const tokens = await client.callback(redirectUri, params, { state: params.state });
|
||||
return client.userinfo<OAuthProfile>(tokens.access_token || '');
|
||||
} catch (error: Error | any) {
|
||||
this.logger.error(`Unable to complete OAuth login: ${error}`, error?.stack);
|
||||
throw new InternalServerErrorException(`Unable to complete OAuth login: ${error}`, { cause: error });
|
||||
}
|
||||
const tokens = await client.callback(redirectUri, params, { state: params.state });
|
||||
return client.userinfo<OAuthProfile>(tokens.access_token || '');
|
||||
}
|
||||
|
||||
private async getOAuthClient(config: SystemConfig) {
|
||||
|
@ -123,6 +123,6 @@ export interface IAssetRepository {
|
||||
getMapMarkers(ownerId: string, options?: MapMarkerSearchOptions): Promise<MapMarker[]>;
|
||||
getStatistics(ownerId: string, options: AssetStatsOptions): Promise<AssetStats>;
|
||||
getTimeBuckets(options: TimeBucketOptions): Promise<TimeBucketItem[]>;
|
||||
getByTimeBucket(timeBucket: string, options: TimeBucketOptions): Promise<AssetEntity[]>;
|
||||
getTimeBucket(timeBucket: string, options: TimeBucketOptions): Promise<AssetEntity[]>;
|
||||
upsertExif(exif: Partial<ExifEntity>): Promise<void>;
|
||||
}
|
||||
|
@ -220,7 +220,7 @@ describe(ServerInfoService.name, () => {
|
||||
},
|
||||
]);
|
||||
|
||||
await expect(sut.getStats()).resolves.toEqual({
|
||||
await expect(sut.getStatistics()).resolves.toEqual({
|
||||
photos: 120,
|
||||
videos: 31,
|
||||
usage: 1123455,
|
||||
|
@ -92,7 +92,7 @@ export class ServerInfoService {
|
||||
};
|
||||
}
|
||||
|
||||
async getStats(): Promise<ServerStatsResponseDto> {
|
||||
async getStatistics(): Promise<ServerStatsResponseDto> {
|
||||
const userStats: UserStatsQueryResponse[] = await this.userRepository.getUserStats();
|
||||
const serverStats = new ServerStatsResponseDto();
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { LibraryType, UserEntity } from '@app/infra/entities';
|
||||
import { BadRequestException, ForbiddenException, InternalServerErrorException, Logger } from '@nestjs/common';
|
||||
import { BadRequestException, ForbiddenException } from '@nestjs/common';
|
||||
import path from 'path';
|
||||
import sanitize from 'sanitize-filename';
|
||||
import { AuthUserDto } from '../auth';
|
||||
@ -61,26 +61,21 @@ export class UserCore {
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if (dto.password) {
|
||||
dto.password = await this.cryptoRepository.hashBcrypt(dto.password, SALT_ROUNDS);
|
||||
}
|
||||
|
||||
if (dto.storageLabel === '') {
|
||||
dto.storageLabel = null;
|
||||
}
|
||||
|
||||
if (dto.externalPath === '') {
|
||||
dto.externalPath = null;
|
||||
} else if (dto.externalPath) {
|
||||
dto.externalPath = path.normalize(dto.externalPath);
|
||||
}
|
||||
|
||||
return this.userRepository.update(id, dto);
|
||||
} catch (e) {
|
||||
Logger.error(e, 'Failed to update user info');
|
||||
throw new InternalServerErrorException('Failed to update user info');
|
||||
if (dto.password) {
|
||||
dto.password = await this.cryptoRepository.hashBcrypt(dto.password, SALT_ROUNDS);
|
||||
}
|
||||
|
||||
if (dto.storageLabel === '') {
|
||||
dto.storageLabel = null;
|
||||
}
|
||||
|
||||
if (dto.externalPath === '') {
|
||||
dto.externalPath = null;
|
||||
} else if (dto.externalPath) {
|
||||
dto.externalPath = path.normalize(dto.externalPath);
|
||||
}
|
||||
|
||||
return this.userRepository.update(id, dto);
|
||||
}
|
||||
|
||||
async createUser(dto: Partial<UserEntity> & { email: string }): Promise<UserEntity> {
|
||||
@ -96,30 +91,25 @@ export class UserCore {
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const payload: Partial<UserEntity> = { ...dto };
|
||||
if (payload.password) {
|
||||
payload.password = await this.cryptoRepository.hashBcrypt(payload.password, SALT_ROUNDS);
|
||||
}
|
||||
if (payload.storageLabel) {
|
||||
payload.storageLabel = sanitize(payload.storageLabel);
|
||||
}
|
||||
|
||||
const userEntity = await this.userRepository.create(payload);
|
||||
await this.libraryRepository.create({
|
||||
owner: { id: userEntity.id } as UserEntity,
|
||||
name: 'Default Library',
|
||||
assets: [],
|
||||
type: LibraryType.UPLOAD,
|
||||
importPaths: [],
|
||||
exclusionPatterns: [],
|
||||
isVisible: true,
|
||||
});
|
||||
|
||||
return userEntity;
|
||||
} catch (e) {
|
||||
Logger.error(e, 'Create new user');
|
||||
throw new InternalServerErrorException('Failed to register new user');
|
||||
const payload: Partial<UserEntity> = { ...dto };
|
||||
if (payload.password) {
|
||||
payload.password = await this.cryptoRepository.hashBcrypt(payload.password, SALT_ROUNDS);
|
||||
}
|
||||
if (payload.storageLabel) {
|
||||
payload.storageLabel = sanitize(payload.storageLabel);
|
||||
}
|
||||
|
||||
const userEntity = await this.userRepository.create(payload);
|
||||
await this.libraryRepository.create({
|
||||
owner: { id: userEntity.id } as UserEntity,
|
||||
name: 'Default Library',
|
||||
assets: [],
|
||||
type: LibraryType.UPLOAD,
|
||||
importPaths: [],
|
||||
exclusionPatterns: [],
|
||||
isVisible: true,
|
||||
});
|
||||
|
||||
return userEntity;
|
||||
}
|
||||
}
|
||||
|
@ -17,8 +17,8 @@ import {
|
||||
import { ApiBody, ApiConsumes, ApiHeader, ApiOkResponse, ApiTags } from '@nestjs/swagger';
|
||||
import { Response as Res } from 'express';
|
||||
import { AuthUser, Authenticated, SharedLinkRoute } from '../../app.guard';
|
||||
import { FileUploadInterceptor, ImmichFile, Route, mapToUploadFile } from '../../app.interceptor';
|
||||
import { UUIDParamDto } from '../../controllers/dto/uuid-param.dto';
|
||||
import { FileUploadInterceptor, ImmichFile, Route, mapToUploadFile } from '../../interceptors';
|
||||
import FileNotEmptyValidator from '../validation/file-not-empty-validator';
|
||||
import { AssetService } from './asset.service';
|
||||
import { AssetBulkUploadCheckDto } from './dto/asset-check.dto';
|
||||
@ -204,7 +204,7 @@ export class AssetController {
|
||||
*/
|
||||
@Post('/bulk-upload-check')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
bulkUploadCheck(
|
||||
checkBulkUpload(
|
||||
@AuthUser() authUser: AuthUserDto,
|
||||
@Body(ValidationPipe) dto: AssetBulkUploadCheckDto,
|
||||
): Promise<AssetBulkUploadCheckResponseDto> {
|
||||
|
@ -2,14 +2,13 @@ import { DomainModule } from '@app/domain';
|
||||
import { InfraModule } from '@app/infra';
|
||||
import { AssetEntity } from '@app/infra/entities';
|
||||
import { Module, OnModuleDestroy, OnModuleInit } from '@nestjs/common';
|
||||
import { APP_GUARD } from '@nestjs/core';
|
||||
import { APP_GUARD, APP_INTERCEPTOR } from '@nestjs/core';
|
||||
import { ScheduleModule } from '@nestjs/schedule';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { AssetRepository, IAssetRepository } from './api-v1/asset/asset-repository';
|
||||
import { AssetController as AssetControllerV1 } from './api-v1/asset/asset.controller';
|
||||
import { AssetService } from './api-v1/asset/asset.service';
|
||||
import { AppGuard } from './app.guard';
|
||||
import { FileUploadInterceptor } from './app.interceptor';
|
||||
import { AppService } from './app.service';
|
||||
import {
|
||||
APIKeyController,
|
||||
@ -31,6 +30,7 @@ import {
|
||||
TagController,
|
||||
UserController,
|
||||
} from './controllers';
|
||||
import { ErrorInterceptor, FileUploadInterceptor } from './interceptors';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@ -61,10 +61,9 @@ import {
|
||||
PersonController,
|
||||
],
|
||||
providers: [
|
||||
//
|
||||
{ provide: APP_GUARD, useExisting: AppGuard },
|
||||
{ provide: APP_INTERCEPTOR, useClass: ErrorInterceptor },
|
||||
{ provide: APP_GUARD, useClass: AppGuard },
|
||||
{ provide: IAssetRepository, useClass: AssetRepository },
|
||||
AppGuard,
|
||||
AppService,
|
||||
AssetService,
|
||||
FileUploadInterceptor,
|
||||
|
@ -47,6 +47,9 @@ function sortKeys<T>(obj: T): T {
|
||||
return result as T;
|
||||
}
|
||||
|
||||
export const routeToErrorMessage = (methodName: string) =>
|
||||
'Failed to ' + methodName.replace(/[A-Z]+/g, (letter) => ` ${letter.toLowerCase()}`);
|
||||
|
||||
const patchOpenAPI = (document: OpenAPIObject) => {
|
||||
document.paths = sortKeys(document.paths);
|
||||
if (document.components?.schemas) {
|
||||
@ -78,6 +81,10 @@ const patchOpenAPI = (document: OpenAPIObject) => {
|
||||
delete operation.summary;
|
||||
}
|
||||
|
||||
if (operation.operationId) {
|
||||
// console.log(`${routeToErrorMessage(operation.operationId).padEnd(40)} (${operation.operationId})`);
|
||||
}
|
||||
|
||||
if (operation.description === '') {
|
||||
delete operation.description;
|
||||
}
|
||||
|
@ -20,22 +20,22 @@ export class APIKeyController {
|
||||
constructor(private service: APIKeyService) {}
|
||||
|
||||
@Post()
|
||||
createKey(@AuthUser() authUser: AuthUserDto, @Body() dto: APIKeyCreateDto): Promise<APIKeyCreateResponseDto> {
|
||||
createApiKey(@AuthUser() authUser: AuthUserDto, @Body() dto: APIKeyCreateDto): Promise<APIKeyCreateResponseDto> {
|
||||
return this.service.create(authUser, dto);
|
||||
}
|
||||
|
||||
@Get()
|
||||
getKeys(@AuthUser() authUser: AuthUserDto): Promise<APIKeyResponseDto[]> {
|
||||
getApiKeys(@AuthUser() authUser: AuthUserDto): Promise<APIKeyResponseDto[]> {
|
||||
return this.service.getAll(authUser);
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
getKey(@AuthUser() authUser: AuthUserDto, @Param() { id }: UUIDParamDto): Promise<APIKeyResponseDto> {
|
||||
getApiKey(@AuthUser() authUser: AuthUserDto, @Param() { id }: UUIDParamDto): Promise<APIKeyResponseDto> {
|
||||
return this.service.getById(authUser, id);
|
||||
}
|
||||
|
||||
@Put(':id')
|
||||
updateKey(
|
||||
updateApiKey(
|
||||
@AuthUser() authUser: AuthUserDto,
|
||||
@Param() { id }: UUIDParamDto,
|
||||
@Body() dto: APIKeyUpdateDto,
|
||||
@ -44,7 +44,7 @@ export class APIKeyController {
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
deleteKey(@AuthUser() authUser: AuthUserDto, @Param() { id }: UUIDParamDto): Promise<void> {
|
||||
deleteApiKey(@AuthUser() authUser: AuthUserDto, @Param() { id }: UUIDParamDto): Promise<void> {
|
||||
return this.service.delete(authUser, id);
|
||||
}
|
||||
}
|
||||
|
@ -39,10 +39,11 @@ import {
|
||||
import { ApiOkResponse, ApiTags } from '@nestjs/swagger';
|
||||
import { AuthUser, Authenticated, SharedLinkRoute } from '../app.guard';
|
||||
import { UseValidation, asStreamableFile } from '../app.utils';
|
||||
import { Route } from '../interceptors';
|
||||
import { UUIDParamDto } from './dto/uuid-param.dto';
|
||||
|
||||
@ApiTags('Asset')
|
||||
@Controller('asset')
|
||||
@Controller(Route.ASSET)
|
||||
@Authenticated()
|
||||
@UseValidation()
|
||||
export class AssetController {
|
||||
@ -86,7 +87,7 @@ export class AssetController {
|
||||
}
|
||||
|
||||
@Get('statistics')
|
||||
getAssetStats(@AuthUser() authUser: AuthUserDto, @Query() dto: AssetStatsDto): Promise<AssetStatsResponseDto> {
|
||||
getAssetStatistics(@AuthUser() authUser: AuthUserDto, @Query() dto: AssetStatsDto): Promise<AssetStatsResponseDto> {
|
||||
return this.service.getStatistics(authUser, dto);
|
||||
}
|
||||
|
||||
@ -98,8 +99,8 @@ export class AssetController {
|
||||
|
||||
@Authenticated({ isShared: true })
|
||||
@Get('time-bucket')
|
||||
getByTimeBucket(@AuthUser() authUser: AuthUserDto, @Query() dto: TimeBucketAssetDto): Promise<AssetResponseDto[]> {
|
||||
return this.service.getByTimeBucket(authUser, dto) as Promise<AssetResponseDto[]>;
|
||||
getTimeBucket(@AuthUser() authUser: AuthUserDto, @Query() dto: TimeBucketAssetDto): Promise<AssetResponseDto[]> {
|
||||
return this.service.getTimeBucket(authUser, dto) as Promise<AssetResponseDto[]>;
|
||||
}
|
||||
|
||||
@Post('jobs')
|
||||
|
@ -43,7 +43,7 @@ export class AuthController {
|
||||
@PublicRoute()
|
||||
@Post('admin-sign-up')
|
||||
@ApiBadRequestResponse({ description: 'The server already has an admin' })
|
||||
adminSignUp(@Body() signUpCredential: SignUpDto): Promise<AdminSignupResponseDto> {
|
||||
signUpAdmin(@Body() signUpCredential: SignUpDto): Promise<AdminSignupResponseDto> {
|
||||
return this.service.adminSignUp(signUpCredential);
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,7 @@ export class LibraryController {
|
||||
constructor(private service: LibraryService) {}
|
||||
|
||||
@Get()
|
||||
getAllForUser(@AuthUser() authUser: AuthUserDto): Promise<ResponseDto[]> {
|
||||
getLibraries(@AuthUser() authUser: AuthUserDto): Promise<ResponseDto[]> {
|
||||
return this.service.getAllForUser(authUser);
|
||||
}
|
||||
|
||||
|
@ -25,7 +25,7 @@ export class OAuthController {
|
||||
@PublicRoute()
|
||||
@Get('mobile-redirect')
|
||||
@Redirect()
|
||||
mobileRedirect(@Req() req: Request) {
|
||||
redirectOAuthToMobile(@Req() req: Request) {
|
||||
return {
|
||||
url: this.service.getMobileRedirect(req.url),
|
||||
statusCode: HttpStatus.TEMPORARY_REDIRECT,
|
||||
@ -35,19 +35,19 @@ export class OAuthController {
|
||||
/** @deprecated use feature flags and /oauth/authorize */
|
||||
@PublicRoute()
|
||||
@Post('config')
|
||||
generateConfig(@Body() dto: OAuthConfigDto): Promise<OAuthConfigResponseDto> {
|
||||
generateOAuthConfig(@Body() dto: OAuthConfigDto): Promise<OAuthConfigResponseDto> {
|
||||
return this.service.generateConfig(dto);
|
||||
}
|
||||
|
||||
@PublicRoute()
|
||||
@Post('authorize')
|
||||
authorizeOAuth(@Body() dto: OAuthConfigDto): Promise<OAuthAuthorizeResponseDto> {
|
||||
startOAuth(@Body() dto: OAuthConfigDto): Promise<OAuthAuthorizeResponseDto> {
|
||||
return this.service.authorize(dto);
|
||||
}
|
||||
|
||||
@PublicRoute()
|
||||
@Post('callback')
|
||||
async callback(
|
||||
async finishOAuth(
|
||||
@Res({ passthrough: true }) res: Response,
|
||||
@Body() dto: OAuthCallbackDto,
|
||||
@GetLoginDetails() loginDetails: LoginDetails,
|
||||
@ -58,12 +58,12 @@ export class OAuthController {
|
||||
}
|
||||
|
||||
@Post('link')
|
||||
link(@AuthUser() authUser: AuthUserDto, @Body() dto: OAuthCallbackDto): Promise<UserResponseDto> {
|
||||
linkOAuthAccount(@AuthUser() authUser: AuthUserDto, @Body() dto: OAuthCallbackDto): Promise<UserResponseDto> {
|
||||
return this.service.link(authUser, dto);
|
||||
}
|
||||
|
||||
@Post('unlink')
|
||||
unlink(@AuthUser() authUser: AuthUserDto): Promise<UserResponseDto> {
|
||||
unlinkOAuthAccount(@AuthUser() authUser: AuthUserDto): Promise<UserResponseDto> {
|
||||
return this.service.unlink(authUser);
|
||||
}
|
||||
}
|
||||
|
@ -57,9 +57,9 @@ export class ServerInfoController {
|
||||
}
|
||||
|
||||
@AdminRoute()
|
||||
@Get('stats')
|
||||
getStats(): Promise<ServerStatsResponseDto> {
|
||||
return this.service.getStats();
|
||||
@Get('statistics')
|
||||
getServerStatistics(): Promise<ServerStatsResponseDto> {
|
||||
return this.service.getStatistics();
|
||||
}
|
||||
|
||||
@PublicRoute()
|
||||
|
@ -17,7 +17,7 @@ export class SystemConfigController {
|
||||
}
|
||||
|
||||
@Get('defaults')
|
||||
getDefaults(): SystemConfigDto {
|
||||
getConfigDefaults(): SystemConfigDto {
|
||||
return this.service.getDefaults();
|
||||
}
|
||||
|
||||
|
@ -22,8 +22,8 @@ import {
|
||||
} from '@nestjs/common';
|
||||
import { ApiBody, ApiConsumes, ApiTags } from '@nestjs/swagger';
|
||||
import { AdminRoute, AuthUser, Authenticated } from '../app.guard';
|
||||
import { FileUploadInterceptor, Route } from '../app.interceptor';
|
||||
import { UseValidation, asStreamableFile } from '../app.utils';
|
||||
import { FileUploadInterceptor, Route } from '../interceptors';
|
||||
import { UUIDParamDto } from './dto/uuid-param.dto';
|
||||
|
||||
@ApiTags('User')
|
||||
|
32
server/src/immich/interceptors/error.interceptor.ts
Normal file
32
server/src/immich/interceptors/error.interceptor.ts
Normal file
@ -0,0 +1,32 @@
|
||||
import {
|
||||
CallHandler,
|
||||
ExecutionContext,
|
||||
HttpException,
|
||||
Injectable,
|
||||
InternalServerErrorException,
|
||||
Logger,
|
||||
NestInterceptor,
|
||||
} from '@nestjs/common';
|
||||
import { Observable, catchError, throwError } from 'rxjs';
|
||||
import { routeToErrorMessage } from '../app.utils';
|
||||
|
||||
@Injectable()
|
||||
export class ErrorInterceptor implements NestInterceptor {
|
||||
private logger = new Logger(ErrorInterceptor.name);
|
||||
|
||||
async intercept(context: ExecutionContext, next: CallHandler<any>): Promise<Observable<any>> {
|
||||
return next.handle().pipe(
|
||||
catchError((error) =>
|
||||
throwError(() => {
|
||||
if (error instanceof HttpException === false) {
|
||||
const errorMessage = routeToErrorMessage(context.getHandler().name);
|
||||
this.logger.error(errorMessage, error, error?.errors);
|
||||
return new InternalServerErrorException(errorMessage);
|
||||
} else {
|
||||
return error;
|
||||
}
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -7,7 +7,7 @@ import { createHash } from 'crypto';
|
||||
import { NextFunction, RequestHandler } from 'express';
|
||||
import multer, { StorageEngine, diskStorage } from 'multer';
|
||||
import { Observable } from 'rxjs';
|
||||
import { AuthRequest } from './app.guard';
|
||||
import { AuthRequest } from '../app.guard';
|
||||
|
||||
export enum Route {
|
||||
ASSET = 'asset',
|
2
server/src/immich/interceptors/index.ts
Normal file
2
server/src/immich/interceptors/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from './error.interceptor';
|
||||
export * from './file.interceptor';
|
@ -493,7 +493,7 @@ export class AssetRepository implements IAssetRepository {
|
||||
.getRawMany();
|
||||
}
|
||||
|
||||
getByTimeBucket(timeBucket: string, options: TimeBucketOptions): Promise<AssetEntity[]> {
|
||||
getTimeBucket(timeBucket: string, options: TimeBucketOptions): Promise<AssetEntity[]> {
|
||||
const truncated = dateTrunc(options);
|
||||
return (
|
||||
this.getBuilder(options)
|
||||
|
@ -103,9 +103,9 @@ describe(`${ServerInfoController.name} (e2e)`, () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('GET /server-info/stats', () => {
|
||||
describe('GET /server-info/statistics', () => {
|
||||
it('should require authentication', async () => {
|
||||
const { status, body } = await request(server).get('/server-info/stats');
|
||||
const { status, body } = await request(server).get('/server-info/statistics');
|
||||
expect(status).toBe(401);
|
||||
expect(body).toEqual(errorStub.unauthorized);
|
||||
});
|
||||
@ -115,7 +115,7 @@ describe(`${ServerInfoController.name} (e2e)`, () => {
|
||||
await api.userApi.create(server, accessToken, { ...loginDto, firstName: 'test', lastName: 'test' });
|
||||
const { accessToken: userAccessToken } = await api.authApi.login(server, loginDto);
|
||||
const { status, body } = await request(server)
|
||||
.get('/server-info/stats')
|
||||
.get('/server-info/statistics')
|
||||
.set('Authorization', `Bearer ${userAccessToken}`);
|
||||
expect(status).toBe(403);
|
||||
expect(body).toEqual(errorStub.forbidden);
|
||||
@ -123,7 +123,7 @@ describe(`${ServerInfoController.name} (e2e)`, () => {
|
||||
|
||||
it('should return the server stats', async () => {
|
||||
const { status, body } = await request(server)
|
||||
.get('/server-info/stats')
|
||||
.get('/server-info/statistics')
|
||||
.set('Authorization', `Bearer ${accessToken}`);
|
||||
expect(status).toBe(200);
|
||||
expect(body).toEqual({
|
||||
|
@ -26,7 +26,7 @@ export const newAssetRepositoryMock = (): jest.Mocked<IAssetRepository> => {
|
||||
findLivePhotoMatch: jest.fn(),
|
||||
getMapMarkers: jest.fn(),
|
||||
getStatistics: jest.fn(),
|
||||
getByTimeBucket: jest.fn(),
|
||||
getTimeBucket: jest.fn(),
|
||||
getTimeBuckets: jest.fn(),
|
||||
restoreAll: jest.fn(),
|
||||
softDeleteAll: jest.fn(),
|
||||
|
1146
web/src/api/open-api/api.ts
generated
1146
web/src/api/open-api/api.ts
generated
File diff suppressed because it is too large
Load Diff
@ -36,23 +36,19 @@ export const oauth = {
|
||||
authorize: async (location: Location) => {
|
||||
try {
|
||||
const redirectUri = location.href.split('?')[0];
|
||||
const { data } = await api.oauthApi.authorizeOAuth({ oAuthConfigDto: { redirectUri } });
|
||||
const { data } = await api.oauthApi.startOAuth({ oAuthConfigDto: { redirectUri } });
|
||||
goto(data.url);
|
||||
} catch (error) {
|
||||
handleError(error, 'Unable to login with OAuth');
|
||||
}
|
||||
},
|
||||
getConfig: (location: Location) => {
|
||||
const redirectUri = location.href.split('?')[0];
|
||||
return api.oauthApi.generateConfig({ oAuthConfigDto: { redirectUri } });
|
||||
},
|
||||
login: (location: Location) => {
|
||||
return api.oauthApi.callback({ oAuthCallbackDto: { url: location.href } });
|
||||
return api.oauthApi.finishOAuth({ oAuthCallbackDto: { url: location.href } });
|
||||
},
|
||||
link: (location: Location): AxiosPromise<UserResponseDto> => {
|
||||
return api.oauthApi.link({ oAuthCallbackDto: { url: location.href } });
|
||||
return api.oauthApi.linkOAuthAccount({ oAuthCallbackDto: { url: location.href } });
|
||||
},
|
||||
unlink: () => {
|
||||
return api.oauthApi.unlink();
|
||||
return api.oauthApi.unlinkOAuthAccount();
|
||||
},
|
||||
};
|
||||
|
@ -32,7 +32,7 @@
|
||||
async function getConfigs() {
|
||||
[savedConfig, defaultConfig] = await Promise.all([
|
||||
api.systemConfigApi.getConfig().then((res) => res.data.ffmpeg),
|
||||
api.systemConfigApi.getDefaults().then((res) => res.data.ffmpeg),
|
||||
api.systemConfigApi.getConfigDefaults().then((res) => res.data.ffmpeg),
|
||||
]);
|
||||
}
|
||||
|
||||
@ -76,7 +76,7 @@
|
||||
}
|
||||
|
||||
async function resetToDefault() {
|
||||
const { data: configs } = await api.systemConfigApi.getDefaults();
|
||||
const { data: configs } = await api.systemConfigApi.getConfigDefaults();
|
||||
|
||||
ffmpegConfig = { ...configs.ffmpeg };
|
||||
defaultConfig = { ...configs.ffmpeg };
|
||||
|
@ -22,7 +22,7 @@
|
||||
async function getConfigs() {
|
||||
[savedConfig, defaultConfig] = await Promise.all([
|
||||
api.systemConfigApi.getConfig().then((res) => res.data.job),
|
||||
api.systemConfigApi.getDefaults().then((res) => res.data.job),
|
||||
api.systemConfigApi.getConfigDefaults().then((res) => res.data.job),
|
||||
]);
|
||||
}
|
||||
|
||||
@ -59,7 +59,7 @@
|
||||
}
|
||||
|
||||
async function resetToDefault() {
|
||||
const { data: configs } = await api.systemConfigApi.getDefaults();
|
||||
const { data: configs } = await api.systemConfigApi.getConfigDefaults();
|
||||
|
||||
jobConfig = { ...configs.job };
|
||||
defaultConfig = { ...configs.job };
|
||||
|
@ -28,7 +28,7 @@
|
||||
async function getConfigs() {
|
||||
[savedConfig, defaultConfig] = await Promise.all([
|
||||
api.systemConfigApi.getConfig().then((res) => res.data.library),
|
||||
api.systemConfigApi.getDefaults().then((res) => res.data.library),
|
||||
api.systemConfigApi.getConfigDefaults().then((res) => res.data.library),
|
||||
]);
|
||||
}
|
||||
|
||||
@ -68,7 +68,7 @@
|
||||
}
|
||||
|
||||
async function resetToDefault() {
|
||||
const { data: configs } = await api.systemConfigApi.getDefaults();
|
||||
const { data: configs } = await api.systemConfigApi.getConfigDefaults();
|
||||
|
||||
libraryConfig = { ...configs.library };
|
||||
defaultConfig = { ...configs.library };
|
||||
|
@ -22,7 +22,7 @@
|
||||
async function refreshConfig() {
|
||||
[savedConfig, defaultConfig] = await Promise.all([
|
||||
api.systemConfigApi.getConfig().then((res) => res.data.machineLearning),
|
||||
api.systemConfigApi.getDefaults().then((res) => res.data.machineLearning),
|
||||
api.systemConfigApi.getConfigDefaults().then((res) => res.data.machineLearning),
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -22,7 +22,7 @@
|
||||
async function refreshConfig() {
|
||||
[savedConfig, defaultConfig] = await Promise.all([
|
||||
api.systemConfigApi.getConfig().then((res) => res.data),
|
||||
api.systemConfigApi.getDefaults().then((res) => res.data),
|
||||
api.systemConfigApi.getConfigDefaults().then((res) => res.data),
|
||||
]);
|
||||
}
|
||||
|
||||
@ -65,7 +65,7 @@
|
||||
}
|
||||
|
||||
async function resetToDefault() {
|
||||
const { data: configs } = await api.systemConfigApi.getDefaults();
|
||||
const { data: configs } = await api.systemConfigApi.getConfigDefaults();
|
||||
|
||||
config = cloneDeep(configs);
|
||||
defaultConfig = cloneDeep(configs);
|
||||
|
@ -18,7 +18,7 @@
|
||||
async function getConfigs() {
|
||||
[savedConfig, defaultConfig] = await Promise.all([
|
||||
api.systemConfigApi.getConfig().then((res) => res.data.newVersionCheck),
|
||||
api.systemConfigApi.getDefaults().then((res) => res.data.newVersionCheck),
|
||||
api.systemConfigApi.getConfigDefaults().then((res) => res.data.newVersionCheck),
|
||||
]);
|
||||
}
|
||||
|
||||
@ -55,7 +55,7 @@
|
||||
}
|
||||
|
||||
async function resetToDefault() {
|
||||
const { data: configs } = await api.systemConfigApi.getDefaults();
|
||||
const { data: configs } = await api.systemConfigApi.getConfigDefaults();
|
||||
|
||||
newVersionCheckConfig = { ...configs.newVersionCheck };
|
||||
defaultConfig = { ...configs.newVersionCheck };
|
||||
|
@ -29,7 +29,7 @@
|
||||
async function getConfigs() {
|
||||
[savedConfig, defaultConfig] = await Promise.all([
|
||||
api.systemConfigApi.getConfig().then((res) => res.data.oauth),
|
||||
api.systemConfigApi.getDefaults().then((res) => res.data.oauth),
|
||||
api.systemConfigApi.getConfigDefaults().then((res) => res.data.oauth),
|
||||
]);
|
||||
}
|
||||
|
||||
@ -90,7 +90,7 @@
|
||||
}
|
||||
|
||||
async function resetToDefault() {
|
||||
const { data: defaultConfig } = await api.systemConfigApi.getDefaults();
|
||||
const { data: defaultConfig } = await api.systemConfigApi.getConfigDefaults();
|
||||
|
||||
oauthConfig = { ...defaultConfig.oauth };
|
||||
|
||||
|
@ -20,7 +20,7 @@
|
||||
async function getConfigs() {
|
||||
[savedConfig, defaultConfig] = await Promise.all([
|
||||
api.systemConfigApi.getConfig().then((res) => res.data.passwordLogin),
|
||||
api.systemConfigApi.getDefaults().then((res) => res.data.passwordLogin),
|
||||
api.systemConfigApi.getConfigDefaults().then((res) => res.data.passwordLogin),
|
||||
]);
|
||||
}
|
||||
|
||||
@ -77,7 +77,7 @@
|
||||
}
|
||||
|
||||
async function resetToDefault() {
|
||||
const { data: configs } = await api.systemConfigApi.getDefaults();
|
||||
const { data: configs } = await api.systemConfigApi.getConfigDefaults();
|
||||
|
||||
passwordLoginConfig = { ...configs.passwordLogin };
|
||||
defaultConfig = { ...configs.passwordLogin };
|
||||
|
@ -26,7 +26,7 @@
|
||||
async function getConfigs() {
|
||||
[savedConfig, defaultConfig, templateOptions] = await Promise.all([
|
||||
api.systemConfigApi.getConfig().then((res) => res.data.storageTemplate),
|
||||
api.systemConfigApi.getDefaults().then((res) => res.data.storageTemplate),
|
||||
api.systemConfigApi.getConfigDefaults().then((res) => res.data.storageTemplate),
|
||||
api.systemConfigApi.getStorageTemplateOptions().then((res) => res.data),
|
||||
]);
|
||||
|
||||
@ -119,7 +119,7 @@
|
||||
}
|
||||
|
||||
async function resetToDefault() {
|
||||
const { data: defaultConfig } = await api.systemConfigApi.getDefaults();
|
||||
const { data: defaultConfig } = await api.systemConfigApi.getConfigDefaults();
|
||||
|
||||
storageConfig.template = defaultConfig.storageTemplate.template;
|
||||
|
||||
|
@ -19,7 +19,7 @@
|
||||
async function getConfigs() {
|
||||
[savedConfig, defaultConfig] = await Promise.all([
|
||||
api.systemConfigApi.getConfig().then((res) => res.data.theme),
|
||||
api.systemConfigApi.getDefaults().then((res) => res.data.theme),
|
||||
api.systemConfigApi.getConfigDefaults().then((res) => res.data.theme),
|
||||
]);
|
||||
}
|
||||
|
||||
@ -56,7 +56,7 @@
|
||||
}
|
||||
|
||||
async function resetToDefault() {
|
||||
const { data: configs } = await api.systemConfigApi.getDefaults();
|
||||
const { data: configs } = await api.systemConfigApi.getConfigDefaults();
|
||||
|
||||
themeConfig = { ...configs.theme };
|
||||
defaultConfig = { ...configs.theme };
|
||||
|
@ -20,7 +20,7 @@
|
||||
async function getConfigs() {
|
||||
[savedConfig, defaultConfig] = await Promise.all([
|
||||
api.systemConfigApi.getConfig().then((res) => res.data.thumbnail),
|
||||
api.systemConfigApi.getDefaults().then((res) => res.data.thumbnail),
|
||||
api.systemConfigApi.getConfigDefaults().then((res) => res.data.thumbnail),
|
||||
]);
|
||||
}
|
||||
|
||||
@ -37,7 +37,7 @@
|
||||
}
|
||||
|
||||
async function resetToDefault() {
|
||||
const { data: configs } = await api.systemConfigApi.getDefaults();
|
||||
const { data: configs } = await api.systemConfigApi.getConfigDefaults();
|
||||
|
||||
thumbnailConfig = { ...configs.thumbnail };
|
||||
defaultConfig = { ...configs.thumbnail };
|
||||
|
@ -20,7 +20,7 @@
|
||||
async function getConfigs() {
|
||||
[savedConfig, defaultConfig] = await Promise.all([
|
||||
api.systemConfigApi.getConfig().then((res) => res.data.trash),
|
||||
api.systemConfigApi.getDefaults().then((res) => res.data.trash),
|
||||
api.systemConfigApi.getConfigDefaults().then((res) => res.data.trash),
|
||||
]);
|
||||
}
|
||||
|
||||
@ -53,7 +53,7 @@
|
||||
}
|
||||
|
||||
async function resetToDefault() {
|
||||
const { data: configs } = await api.systemConfigApi.getDefaults();
|
||||
const { data: configs } = await api.systemConfigApi.getConfigDefaults();
|
||||
|
||||
trashConfig = { ...configs.trash };
|
||||
defaultConfig = { ...configs.trash };
|
||||
|
@ -30,7 +30,7 @@
|
||||
const firstName = form.get('firstName');
|
||||
const lastName = form.get('lastName');
|
||||
|
||||
const { status } = await api.authenticationApi.adminSignUp({
|
||||
const { status } = await api.authenticationApi.signUpAdmin({
|
||||
signUpDto: {
|
||||
email: String(email),
|
||||
password: String(password),
|
||||
|
@ -2,7 +2,7 @@
|
||||
import { page } from '$app/stores';
|
||||
import { locale, sidebarSettings } from '$lib/stores/preferences.store';
|
||||
import { featureFlags } from '$lib/stores/server-config.store';
|
||||
import { AssetApiGetAssetStatsRequest, api } from '@api';
|
||||
import { AssetApiGetAssetStatisticsRequest, api } from '@api';
|
||||
import {
|
||||
mdiAccount,
|
||||
mdiAccountMultiple,
|
||||
@ -23,8 +23,8 @@
|
||||
import SideBarButton from './side-bar-button.svelte';
|
||||
import SideBarSection from './side-bar-section.svelte';
|
||||
|
||||
const getStats = async (dto: AssetApiGetAssetStatsRequest) => {
|
||||
const { data: stats } = await api.assetApi.getAssetStats(dto);
|
||||
const getStats = async (dto: AssetApiGetAssetStatisticsRequest) => {
|
||||
const { data: stats } = await api.assetApi.getAssetStatistics(dto);
|
||||
return stats;
|
||||
};
|
||||
|
||||
|
@ -82,7 +82,7 @@
|
||||
};
|
||||
|
||||
async function readLibraryList() {
|
||||
const { data } = await api.libraryApi.getAllForUser();
|
||||
const { data } = await api.libraryApi.getLibraries();
|
||||
libraries = data;
|
||||
|
||||
dropdownOpen.length = libraries.length;
|
||||
|
@ -25,14 +25,14 @@
|
||||
};
|
||||
|
||||
async function refreshKeys() {
|
||||
const { data } = await api.keyApi.getKeys();
|
||||
const { data } = await api.keyApi.getApiKeys();
|
||||
keys = data;
|
||||
}
|
||||
|
||||
const handleCreate = async (event: CustomEvent<APIKeyResponseDto>) => {
|
||||
try {
|
||||
const dto = event.detail;
|
||||
const { data } = await api.keyApi.createKey({ aPIKeyCreateDto: dto });
|
||||
const { data } = await api.keyApi.createApiKey({ aPIKeyCreateDto: dto });
|
||||
secret = data.secret;
|
||||
} catch (error) {
|
||||
handleError(error, 'Unable to create a new API Key');
|
||||
@ -50,7 +50,7 @@
|
||||
const dto = event.detail;
|
||||
|
||||
try {
|
||||
await api.keyApi.updateKey({ id: editKey.id, aPIKeyUpdateDto: { name: dto.name } });
|
||||
await api.keyApi.updateApiKey({ id: editKey.id, aPIKeyUpdateDto: { name: dto.name } });
|
||||
notificationController.show({
|
||||
message: `Saved API Key`,
|
||||
type: NotificationType.Info,
|
||||
@ -69,7 +69,7 @@
|
||||
}
|
||||
|
||||
try {
|
||||
await api.keyApi.deleteKey({ id: deleteKey.id });
|
||||
await api.keyApi.deleteApiKey({ id: deleteKey.id });
|
||||
notificationController.show({
|
||||
message: `Removed API Key: ${deleteKey.name}`,
|
||||
type: NotificationType.Info,
|
||||
|
@ -196,7 +196,7 @@ export class AssetStore {
|
||||
|
||||
bucket.cancelToken = new AbortController();
|
||||
|
||||
const { data: assets } = await api.assetApi.getByTimeBucket(
|
||||
const { data: assets } = await api.assetApi.getTimeBucket(
|
||||
{
|
||||
...this.options,
|
||||
timeBucket: bucketDate,
|
||||
@ -206,7 +206,7 @@ export class AssetStore {
|
||||
);
|
||||
|
||||
if (this.albumId) {
|
||||
const { data: albumAssets } = await api.assetApi.getByTimeBucket(
|
||||
const { data: albumAssets } = await api.assetApi.getTimeBucket(
|
||||
{
|
||||
albumId: this.albumId,
|
||||
timeBucket: bucketDate,
|
||||
|
@ -8,7 +8,7 @@ export const load = (async ({ parent, locals }) => {
|
||||
throw redirect(302, AppRoute.AUTH_LOGIN);
|
||||
}
|
||||
|
||||
const { data: keys } = await locals.api.keyApi.getKeys();
|
||||
const { data: keys } = await locals.api.keyApi.getApiKeys();
|
||||
const { data: devices } = await locals.api.authenticationApi.getAuthDevices();
|
||||
const { data: partners } = await locals.api.partnerApi.getPartners({ direction: 'shared-by' });
|
||||
|
||||
|
@ -11,7 +11,7 @@ export const load = (async ({ parent, locals: { api } }) => {
|
||||
throw redirect(302, AppRoute.PHOTOS);
|
||||
}
|
||||
|
||||
const { data: stats } = await api.serverInfoApi.getStats();
|
||||
const { data: stats } = await api.serverInfoApi.getServerStatistics();
|
||||
|
||||
return {
|
||||
user,
|
||||
|
@ -11,7 +11,7 @@
|
||||
|
||||
onMount(async () => {
|
||||
setIntervalHandler = setInterval(async () => {
|
||||
const { data: stats } = await api.serverInfoApi.getStats();
|
||||
const { data: stats } = await api.serverInfoApi.getServerStatistics();
|
||||
data.stats = stats;
|
||||
}, 5000);
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user