mirror of
https://github.com/immich-app/immich.git
synced 2024-12-25 10:43:13 +02:00
parent
78c7ff855d
commit
171b6bb0a6
@ -1,4 +1,4 @@
|
||||
import { LoginResponseDto, getServerConfig } from '@immich/sdk';
|
||||
import { LoginResponseDto } from '@immich/sdk';
|
||||
import { createUserDto } from 'src/fixtures';
|
||||
import { errorDto } from 'src/responses';
|
||||
import { app, utils } from 'src/utils';
|
||||
@ -162,19 +162,4 @@ describe('/server-info', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('POST /server-info/admin-onboarding', () => {
|
||||
it('should set admin onboarding', async () => {
|
||||
const config = await getServerConfig({});
|
||||
expect(config.isOnboarded).toBe(false);
|
||||
|
||||
const { status } = await request(app)
|
||||
.post('/server-info/admin-onboarding')
|
||||
.set('Authorization', `Bearer ${admin.accessToken}`);
|
||||
expect(status).toBe(204);
|
||||
|
||||
const newConfig = await getServerConfig({});
|
||||
expect(newConfig.isOnboarded).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
76
e2e/src/api/specs/system-metadata.e2e-spec.ts
Normal file
76
e2e/src/api/specs/system-metadata.e2e-spec.ts
Normal file
@ -0,0 +1,76 @@
|
||||
import { LoginResponseDto, getServerConfig } from '@immich/sdk';
|
||||
import { createUserDto } from 'src/fixtures';
|
||||
import { errorDto } from 'src/responses';
|
||||
import { app, utils } from 'src/utils';
|
||||
import request from 'supertest';
|
||||
import { beforeAll, describe, expect, it } from 'vitest';
|
||||
|
||||
describe('/server-info', () => {
|
||||
let admin: LoginResponseDto;
|
||||
let nonAdmin: LoginResponseDto;
|
||||
|
||||
beforeAll(async () => {
|
||||
await utils.resetDatabase();
|
||||
admin = await utils.adminSetup({ onboarding: false });
|
||||
nonAdmin = await utils.userSetup(admin.accessToken, createUserDto.user1);
|
||||
});
|
||||
|
||||
describe('POST /system-metadata/admin-onboarding', () => {
|
||||
it('should require authentication', async () => {
|
||||
const { status, body } = await request(app).post('/system-metadata/admin-onboarding').send({ isOnboarded: true });
|
||||
expect(status).toBe(401);
|
||||
expect(body).toEqual(errorDto.unauthorized);
|
||||
});
|
||||
|
||||
it('should only work for admins', async () => {
|
||||
const { status, body } = await request(app)
|
||||
.post('/system-metadata/admin-onboarding')
|
||||
.set('Authorization', `Bearer ${nonAdmin.accessToken}`)
|
||||
.send({ isOnboarded: true });
|
||||
expect(status).toBe(403);
|
||||
expect(body).toEqual(errorDto.forbidden);
|
||||
});
|
||||
|
||||
it('should set admin onboarding', async () => {
|
||||
const config = await getServerConfig({});
|
||||
expect(config.isOnboarded).toBe(false);
|
||||
|
||||
const { status } = await request(app)
|
||||
.post('/system-metadata/admin-onboarding')
|
||||
.set('Authorization', `Bearer ${admin.accessToken}`)
|
||||
.send({ isOnboarded: true });
|
||||
expect(status).toBe(204);
|
||||
|
||||
const newConfig = await getServerConfig({});
|
||||
expect(newConfig.isOnboarded).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('GET /system-metadata/reverse-geocoding-state', () => {
|
||||
it('should require authentication', async () => {
|
||||
const { status, body } = await request(app).get('/system-metadata/reverse-geocoding-state');
|
||||
expect(status).toBe(401);
|
||||
expect(body).toEqual(errorDto.unauthorized);
|
||||
});
|
||||
|
||||
it('should only work for admins', async () => {
|
||||
const { status, body } = await request(app)
|
||||
.get('/system-metadata/reverse-geocoding-state')
|
||||
.set('Authorization', `Bearer ${nonAdmin.accessToken}`);
|
||||
expect(status).toBe(403);
|
||||
expect(body).toEqual(errorDto.forbidden);
|
||||
});
|
||||
|
||||
it('should get the reverse geocoding state', async () => {
|
||||
const { status, body } = await request(app)
|
||||
.get('/system-metadata/reverse-geocoding-state')
|
||||
.set('Authorization', `Bearer ${admin.accessToken}`);
|
||||
|
||||
expect(status).toBe(200);
|
||||
expect(body).toEqual({
|
||||
lastUpdate: expect.any(String),
|
||||
lastImportFileName: 'cities500.txt',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
@ -24,8 +24,8 @@ import {
|
||||
getConfigDefaults,
|
||||
login,
|
||||
searchMetadata,
|
||||
setAdminOnboarding,
|
||||
signUpAdmin,
|
||||
updateAdminOnboarding,
|
||||
updateConfig,
|
||||
validate,
|
||||
} from '@immich/sdk';
|
||||
@ -264,7 +264,10 @@ export const utils = {
|
||||
await signUpAdmin({ signUpDto: signupDto.admin });
|
||||
const response = await login({ loginCredentialDto: loginDto.admin });
|
||||
if (options.onboarding) {
|
||||
await setAdminOnboarding({ headers: asBearerAuth(response.accessToken) });
|
||||
await updateAdminOnboarding(
|
||||
{ adminOnboardingUpdateDto: { isOnboarded: true } },
|
||||
{ headers: asBearerAuth(response.accessToken) },
|
||||
);
|
||||
}
|
||||
return response;
|
||||
},
|
||||
|
9
mobile/openapi/.openapi-generator/FILES
generated
9
mobile/openapi/.openapi-generator/FILES
generated
@ -13,6 +13,7 @@ doc/ActivityCreateDto.md
|
||||
doc/ActivityResponseDto.md
|
||||
doc/ActivityStatisticsResponseDto.md
|
||||
doc/AddUsersDto.md
|
||||
doc/AdminOnboardingUpdateDto.md
|
||||
doc/AlbumApi.md
|
||||
doc/AlbumCountResponseDto.md
|
||||
doc/AlbumResponseDto.md
|
||||
@ -123,6 +124,7 @@ doc/QueueStatusDto.md
|
||||
doc/ReactionLevel.md
|
||||
doc/ReactionType.md
|
||||
doc/RecognitionConfig.md
|
||||
doc/ReverseGeocodingStateResponseDto.md
|
||||
doc/ScanLibraryDto.md
|
||||
doc/SearchAlbumResponseDto.md
|
||||
doc/SearchApi.md
|
||||
@ -174,6 +176,7 @@ doc/SystemConfigTemplateStorageOptionDto.md
|
||||
doc/SystemConfigThemeDto.md
|
||||
doc/SystemConfigTrashDto.md
|
||||
doc/SystemConfigUserDto.md
|
||||
doc/SystemMetadataApi.md
|
||||
doc/TagApi.md
|
||||
doc/TagResponseDto.md
|
||||
doc/TagTypeEnum.md
|
||||
@ -226,6 +229,7 @@ lib/api/sessions_api.dart
|
||||
lib/api/shared_link_api.dart
|
||||
lib/api/sync_api.dart
|
||||
lib/api/system_config_api.dart
|
||||
lib/api/system_metadata_api.dart
|
||||
lib/api/tag_api.dart
|
||||
lib/api/timeline_api.dart
|
||||
lib/api/trash_api.dart
|
||||
@ -242,6 +246,7 @@ lib/model/activity_create_dto.dart
|
||||
lib/model/activity_response_dto.dart
|
||||
lib/model/activity_statistics_response_dto.dart
|
||||
lib/model/add_users_dto.dart
|
||||
lib/model/admin_onboarding_update_dto.dart
|
||||
lib/model/album_count_response_dto.dart
|
||||
lib/model/album_response_dto.dart
|
||||
lib/model/all_job_status_response_dto.dart
|
||||
@ -343,6 +348,7 @@ lib/model/queue_status_dto.dart
|
||||
lib/model/reaction_level.dart
|
||||
lib/model/reaction_type.dart
|
||||
lib/model/recognition_config.dart
|
||||
lib/model/reverse_geocoding_state_response_dto.dart
|
||||
lib/model/scan_library_dto.dart
|
||||
lib/model/search_album_response_dto.dart
|
||||
lib/model/search_asset_response_dto.dart
|
||||
@ -419,6 +425,7 @@ test/activity_create_dto_test.dart
|
||||
test/activity_response_dto_test.dart
|
||||
test/activity_statistics_response_dto_test.dart
|
||||
test/add_users_dto_test.dart
|
||||
test/admin_onboarding_update_dto_test.dart
|
||||
test/album_api_test.dart
|
||||
test/album_count_response_dto_test.dart
|
||||
test/album_response_dto_test.dart
|
||||
@ -534,6 +541,7 @@ test/queue_status_dto_test.dart
|
||||
test/reaction_level_test.dart
|
||||
test/reaction_type_test.dart
|
||||
test/recognition_config_test.dart
|
||||
test/reverse_geocoding_state_response_dto_test.dart
|
||||
test/scan_library_dto_test.dart
|
||||
test/search_album_response_dto_test.dart
|
||||
test/search_api_test.dart
|
||||
@ -585,6 +593,7 @@ test/system_config_template_storage_option_dto_test.dart
|
||||
test/system_config_theme_dto_test.dart
|
||||
test/system_config_trash_dto_test.dart
|
||||
test/system_config_user_dto_test.dart
|
||||
test/system_metadata_api_test.dart
|
||||
test/tag_api_test.dart
|
||||
test/tag_response_dto_test.dart
|
||||
test/tag_type_enum_test.dart
|
||||
|
BIN
mobile/openapi/README.md
generated
BIN
mobile/openapi/README.md
generated
Binary file not shown.
BIN
mobile/openapi/doc/AdminOnboardingUpdateDto.md
generated
Normal file
BIN
mobile/openapi/doc/AdminOnboardingUpdateDto.md
generated
Normal file
Binary file not shown.
BIN
mobile/openapi/doc/ReverseGeocodingStateResponseDto.md
generated
Normal file
BIN
mobile/openapi/doc/ReverseGeocodingStateResponseDto.md
generated
Normal file
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/SystemMetadataApi.md
generated
Normal file
BIN
mobile/openapi/doc/SystemMetadataApi.md
generated
Normal file
Binary file not shown.
BIN
mobile/openapi/lib/api.dart
generated
BIN
mobile/openapi/lib/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_metadata_api.dart
generated
Normal file
BIN
mobile/openapi/lib/api/system_metadata_api.dart
generated
Normal file
Binary file not shown.
BIN
mobile/openapi/lib/api_client.dart
generated
BIN
mobile/openapi/lib/api_client.dart
generated
Binary file not shown.
BIN
mobile/openapi/lib/model/admin_onboarding_update_dto.dart
generated
Normal file
BIN
mobile/openapi/lib/model/admin_onboarding_update_dto.dart
generated
Normal file
Binary file not shown.
BIN
mobile/openapi/lib/model/reverse_geocoding_state_response_dto.dart
generated
Normal file
BIN
mobile/openapi/lib/model/reverse_geocoding_state_response_dto.dart
generated
Normal file
Binary file not shown.
BIN
mobile/openapi/test/admin_onboarding_update_dto_test.dart
generated
Normal file
BIN
mobile/openapi/test/admin_onboarding_update_dto_test.dart
generated
Normal file
Binary file not shown.
BIN
mobile/openapi/test/reverse_geocoding_state_response_dto_test.dart
generated
Normal file
BIN
mobile/openapi/test/reverse_geocoding_state_response_dto_test.dart
generated
Normal file
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_metadata_api_test.dart
generated
Normal file
BIN
mobile/openapi/test/system_metadata_api_test.dart
generated
Normal file
Binary file not shown.
@ -4908,31 +4908,6 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"/server-info/admin-onboarding": {
|
||||
"post": {
|
||||
"operationId": "setAdminOnboarding",
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"bearer": []
|
||||
},
|
||||
{
|
||||
"cookie": []
|
||||
},
|
||||
{
|
||||
"api_key": []
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"Server Info"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/server-info/config": {
|
||||
"get": {
|
||||
"operationId": "getServerConfig",
|
||||
@ -5885,6 +5860,103 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"/system-metadata/admin-onboarding": {
|
||||
"get": {
|
||||
"operationId": "getAdminOnboarding",
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/AdminOnboardingUpdateDto"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"bearer": []
|
||||
},
|
||||
{
|
||||
"cookie": []
|
||||
},
|
||||
{
|
||||
"api_key": []
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"System Metadata"
|
||||
]
|
||||
},
|
||||
"post": {
|
||||
"operationId": "updateAdminOnboarding",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/AdminOnboardingUpdateDto"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true
|
||||
},
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"bearer": []
|
||||
},
|
||||
{
|
||||
"cookie": []
|
||||
},
|
||||
{
|
||||
"api_key": []
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"System Metadata"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/system-metadata/reverse-geocoding-state": {
|
||||
"get": {
|
||||
"operationId": "getReverseGeocodingState",
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ReverseGeocodingStateResponseDto"
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"bearer": []
|
||||
},
|
||||
{
|
||||
"cookie": []
|
||||
},
|
||||
{
|
||||
"api_key": []
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"System Metadata"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/tag": {
|
||||
"get": {
|
||||
"operationId": "getAllTags",
|
||||
@ -7180,6 +7252,17 @@
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"AdminOnboardingUpdateDto": {
|
||||
"properties": {
|
||||
"isOnboarded": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"isOnboarded"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"AlbumCountResponseDto": {
|
||||
"properties": {
|
||||
"notShared": {
|
||||
@ -9618,6 +9701,23 @@
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"ReverseGeocodingStateResponseDto": {
|
||||
"properties": {
|
||||
"lastImportFileName": {
|
||||
"nullable": true,
|
||||
"type": "string"
|
||||
},
|
||||
"lastUpdate": {
|
||||
"nullable": true,
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"lastImportFileName",
|
||||
"lastUpdate"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"ScanLibraryDto": {
|
||||
"properties": {
|
||||
"refreshAllFiles": {
|
||||
|
@ -998,6 +998,13 @@ export type SystemConfigTemplateStorageOptionDto = {
|
||||
weekOptions: string[];
|
||||
yearOptions: string[];
|
||||
};
|
||||
export type AdminOnboardingUpdateDto = {
|
||||
isOnboarded: boolean;
|
||||
};
|
||||
export type ReverseGeocodingStateResponseDto = {
|
||||
lastImportFileName: string | null;
|
||||
lastUpdate: string | null;
|
||||
};
|
||||
export type CreateTagDto = {
|
||||
name: string;
|
||||
"type": TagTypeEnum;
|
||||
@ -2330,12 +2337,6 @@ export function getServerInfo(opts?: Oazapfts.RequestOpts) {
|
||||
...opts
|
||||
}));
|
||||
}
|
||||
export function setAdminOnboarding(opts?: Oazapfts.RequestOpts) {
|
||||
return oazapfts.ok(oazapfts.fetchText("/server-info/admin-onboarding", {
|
||||
...opts,
|
||||
method: "POST"
|
||||
}));
|
||||
}
|
||||
export function getServerConfig(opts?: Oazapfts.RequestOpts) {
|
||||
return oazapfts.ok(oazapfts.fetchJson<{
|
||||
status: 200;
|
||||
@ -2597,6 +2598,31 @@ export function getStorageTemplateOptions(opts?: Oazapfts.RequestOpts) {
|
||||
...opts
|
||||
}));
|
||||
}
|
||||
export function getAdminOnboarding(opts?: Oazapfts.RequestOpts) {
|
||||
return oazapfts.ok(oazapfts.fetchJson<{
|
||||
status: 200;
|
||||
data: AdminOnboardingUpdateDto;
|
||||
}>("/system-metadata/admin-onboarding", {
|
||||
...opts
|
||||
}));
|
||||
}
|
||||
export function updateAdminOnboarding({ adminOnboardingUpdateDto }: {
|
||||
adminOnboardingUpdateDto: AdminOnboardingUpdateDto;
|
||||
}, opts?: Oazapfts.RequestOpts) {
|
||||
return oazapfts.ok(oazapfts.fetchText("/system-metadata/admin-onboarding", oazapfts.json({
|
||||
...opts,
|
||||
method: "POST",
|
||||
body: adminOnboardingUpdateDto
|
||||
})));
|
||||
}
|
||||
export function getReverseGeocodingState(opts?: Oazapfts.RequestOpts) {
|
||||
return oazapfts.ok(oazapfts.fetchJson<{
|
||||
status: 200;
|
||||
data: ReverseGeocodingStateResponseDto;
|
||||
}>("/system-metadata/reverse-geocoding-state", {
|
||||
...opts
|
||||
}));
|
||||
}
|
||||
export function getAllTags(opts?: Oazapfts.RequestOpts) {
|
||||
return oazapfts.ok(oazapfts.fetchJson<{
|
||||
status: 200;
|
||||
|
@ -21,6 +21,7 @@ import { SessionController } from 'src/controllers/session.controller';
|
||||
import { SharedLinkController } from 'src/controllers/shared-link.controller';
|
||||
import { SyncController } from 'src/controllers/sync.controller';
|
||||
import { SystemConfigController } from 'src/controllers/system-config.controller';
|
||||
import { SystemMetadataController } from 'src/controllers/system-metadata.controller';
|
||||
import { TagController } from 'src/controllers/tag.controller';
|
||||
import { TimelineController } from 'src/controllers/timeline.controller';
|
||||
import { TrashController } from 'src/controllers/trash.controller';
|
||||
@ -51,6 +52,7 @@ export const controllers = [
|
||||
SharedLinkController,
|
||||
SyncController,
|
||||
SystemConfigController,
|
||||
SystemMetadataController,
|
||||
TagController,
|
||||
TimelineController,
|
||||
TrashController,
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Controller, Get, HttpCode, HttpStatus, Post } from '@nestjs/common';
|
||||
import { Controller, Get } from '@nestjs/common';
|
||||
import { ApiTags } from '@nestjs/swagger';
|
||||
import {
|
||||
ServerConfigDto,
|
||||
@ -65,11 +65,4 @@ export class ServerInfoController {
|
||||
getSupportedMediaTypes(): ServerMediaTypesResponseDto {
|
||||
return this.service.getSupportedMediaTypes();
|
||||
}
|
||||
|
||||
@AdminRoute()
|
||||
@Post('admin-onboarding')
|
||||
@HttpCode(HttpStatus.NO_CONTENT)
|
||||
setAdminOnboarding(): Promise<void> {
|
||||
return this.service.setAdminOnboarding();
|
||||
}
|
||||
}
|
||||
|
28
server/src/controllers/system-metadata.controller.ts
Normal file
28
server/src/controllers/system-metadata.controller.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import { Body, Controller, Get, HttpCode, HttpStatus, Post } from '@nestjs/common';
|
||||
import { ApiTags } from '@nestjs/swagger';
|
||||
import { AdminOnboardingUpdateDto, ReverseGeocodingStateResponseDto } from 'src/dtos/system-metadata.dto';
|
||||
import { Authenticated } from 'src/middleware/auth.guard';
|
||||
import { SystemMetadataService } from 'src/services/system-metadata.service';
|
||||
|
||||
@ApiTags('System Metadata')
|
||||
@Controller('system-metadata')
|
||||
@Authenticated({ admin: true })
|
||||
export class SystemMetadataController {
|
||||
constructor(private service: SystemMetadataService) {}
|
||||
|
||||
@Get('admin-onboarding')
|
||||
getAdminOnboarding(): Promise<AdminOnboardingUpdateDto> {
|
||||
return this.service.getAdminOnboarding();
|
||||
}
|
||||
|
||||
@Post('admin-onboarding')
|
||||
@HttpCode(HttpStatus.NO_CONTENT)
|
||||
updateAdminOnboarding(@Body() dto: AdminOnboardingUpdateDto): Promise<void> {
|
||||
return this.service.updateAdminOnboarding(dto);
|
||||
}
|
||||
|
||||
@Get('reverse-geocoding-state')
|
||||
getReverseGeocodingState(): Promise<ReverseGeocodingStateResponseDto> {
|
||||
return this.service.getReverseGeocodingState();
|
||||
}
|
||||
}
|
15
server/src/dtos/system-metadata.dto.ts
Normal file
15
server/src/dtos/system-metadata.dto.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import { IsBoolean } from 'class-validator';
|
||||
|
||||
export class AdminOnboardingUpdateDto {
|
||||
@IsBoolean()
|
||||
isOnboarded!: boolean;
|
||||
}
|
||||
|
||||
export class AdminOnboardingResponseDto {
|
||||
isOnboarded!: boolean;
|
||||
}
|
||||
|
||||
export class ReverseGeocodingStateResponseDto {
|
||||
lastUpdate!: string | null;
|
||||
lastImportFileName!: string | null;
|
||||
}
|
@ -36,8 +36,8 @@ export class MetadataRepository implements IMetadataRepository {
|
||||
this.logger.log('Initializing metadata repository');
|
||||
const geodataDate = await readFile(geodataDatePath, 'utf8');
|
||||
|
||||
// TODO move to metadata service init
|
||||
const geocodingMetadata = await this.systemMetadataRepository.get(SystemMetadataKey.REVERSE_GEOCODING_STATE);
|
||||
|
||||
if (geocodingMetadata?.lastUpdate === geodataDate) {
|
||||
return;
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ import { StorageTemplateService } from 'src/services/storage-template.service';
|
||||
import { StorageService } from 'src/services/storage.service';
|
||||
import { SyncService } from 'src/services/sync.service';
|
||||
import { SystemConfigService } from 'src/services/system-config.service';
|
||||
import { SystemMetadataService } from 'src/services/system-metadata.service';
|
||||
import { TagService } from 'src/services/tag.service';
|
||||
import { TimelineService } from 'src/services/timeline.service';
|
||||
import { TrashService } from 'src/services/trash.service';
|
||||
@ -58,6 +59,7 @@ export const services = [
|
||||
StorageTemplateService,
|
||||
SyncService,
|
||||
SystemConfigService,
|
||||
SystemMetadataService,
|
||||
TagService,
|
||||
TimelineService,
|
||||
TrashService,
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { serverVersion } from 'src/constants';
|
||||
import { SystemMetadataKey } from 'src/entities/system-metadata.entity';
|
||||
import { IEventRepository } from 'src/interfaces/event.interface';
|
||||
import { ILoggerRepository } from 'src/interfaces/logger.interface';
|
||||
import { IServerInfoRepository } from 'src/interfaces/server-info.interface';
|
||||
@ -207,13 +206,6 @@ describe(ServerInfoService.name, () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('setAdminOnboarding', () => {
|
||||
it('should set admin onboarding to true', async () => {
|
||||
await sut.setAdminOnboarding();
|
||||
expect(systemMetadataMock.set).toHaveBeenCalledWith(SystemMetadataKey.ADMIN_ONBOARDING, { isOnboarded: true });
|
||||
});
|
||||
});
|
||||
|
||||
describe('getStats', () => {
|
||||
it('should total up usage by user', async () => {
|
||||
userMock.getUserStats.mockResolvedValue([
|
||||
|
@ -51,7 +51,9 @@ export class ServerInfoService {
|
||||
|
||||
const featureFlags = await this.getFeatures();
|
||||
if (featureFlags.configFile) {
|
||||
await this.setAdminOnboarding();
|
||||
await this.systemMetadataRepository.set(SystemMetadataKey.ADMIN_ONBOARDING, {
|
||||
isOnboarded: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -105,10 +107,6 @@ export class ServerInfoService {
|
||||
};
|
||||
}
|
||||
|
||||
setAdminOnboarding(): Promise<void> {
|
||||
return this.systemMetadataRepository.set(SystemMetadataKey.ADMIN_ONBOARDING, { isOnboarded: true });
|
||||
}
|
||||
|
||||
async getStatistics(): Promise<ServerStatsResponseDto> {
|
||||
const userStats: UserStatsQueryResponse[] = await this.userRepository.getUserStats();
|
||||
const serverStats = new ServerStatsResponseDto();
|
||||
|
31
server/src/services/system-metadata.service.spec.ts
Normal file
31
server/src/services/system-metadata.service.spec.ts
Normal file
@ -0,0 +1,31 @@
|
||||
import { SystemMetadataKey } from 'src/entities/system-metadata.entity';
|
||||
import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interface';
|
||||
import { SystemMetadataService } from 'src/services/system-metadata.service';
|
||||
import { newSystemMetadataRepositoryMock } from 'test/repositories/system-metadata.repository.mock';
|
||||
import { Mocked } from 'vitest';
|
||||
|
||||
describe(SystemMetadataService.name, () => {
|
||||
let sut: SystemMetadataService;
|
||||
let metadataMock: Mocked<ISystemMetadataRepository>;
|
||||
|
||||
beforeEach(() => {
|
||||
metadataMock = newSystemMetadataRepositoryMock();
|
||||
sut = new SystemMetadataService(metadataMock);
|
||||
});
|
||||
|
||||
it('should work', () => {
|
||||
expect(sut).toBeDefined();
|
||||
});
|
||||
|
||||
describe('updateAdminOnboarding', () => {
|
||||
it('should update isOnboarded to true', async () => {
|
||||
await expect(sut.updateAdminOnboarding({ isOnboarded: true })).resolves.toBeUndefined();
|
||||
expect(metadataMock.set).toHaveBeenCalledWith(SystemMetadataKey.ADMIN_ONBOARDING, { isOnboarded: true });
|
||||
});
|
||||
|
||||
it('should update isOnboarded to false', async () => {
|
||||
await expect(sut.updateAdminOnboarding({ isOnboarded: false })).resolves.toBeUndefined();
|
||||
expect(metadataMock.set).toHaveBeenCalledWith(SystemMetadataKey.ADMIN_ONBOARDING, { isOnboarded: false });
|
||||
});
|
||||
});
|
||||
});
|
29
server/src/services/system-metadata.service.ts
Normal file
29
server/src/services/system-metadata.service.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import {
|
||||
AdminOnboardingResponseDto,
|
||||
AdminOnboardingUpdateDto,
|
||||
ReverseGeocodingStateResponseDto,
|
||||
} from 'src/dtos/system-metadata.dto';
|
||||
import { SystemMetadataKey } from 'src/entities/system-metadata.entity';
|
||||
import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interface';
|
||||
|
||||
@Injectable()
|
||||
export class SystemMetadataService {
|
||||
constructor(@Inject(ISystemMetadataRepository) private repository: ISystemMetadataRepository) {}
|
||||
|
||||
async getAdminOnboarding(): Promise<AdminOnboardingResponseDto> {
|
||||
const value = await this.repository.get(SystemMetadataKey.ADMIN_ONBOARDING);
|
||||
return { isOnboarded: false, ...value };
|
||||
}
|
||||
|
||||
async updateAdminOnboarding(dto: AdminOnboardingUpdateDto): Promise<void> {
|
||||
await this.repository.set(SystemMetadataKey.ADMIN_ONBOARDING, {
|
||||
isOnboarded: dto.isOnboarded,
|
||||
});
|
||||
}
|
||||
|
||||
async getReverseGeocodingState(): Promise<ReverseGeocodingStateResponseDto> {
|
||||
const value = await this.repository.get(SystemMetadataKey.REVERSE_GEOCODING_STATE);
|
||||
return { lastUpdate: null, lastImportFileName: null, ...value };
|
||||
}
|
||||
}
|
@ -5,7 +5,7 @@
|
||||
import OnboadingStorageTemplate from '$lib/components/onboarding-page/onboarding-storage-template.svelte';
|
||||
import OnboardingTheme from '$lib/components/onboarding-page/onboarding-theme.svelte';
|
||||
import { AppRoute, QueryParameter } from '$lib/constants';
|
||||
import { setAdminOnboarding } from '@immich/sdk';
|
||||
import { updateAdminOnboarding } from '@immich/sdk';
|
||||
|
||||
let index = 0;
|
||||
|
||||
@ -28,7 +28,7 @@
|
||||
|
||||
const handleDoneClicked = async () => {
|
||||
if (index >= onboardingSteps.length - 1) {
|
||||
await setAdminOnboarding();
|
||||
await updateAdminOnboarding({ adminOnboardingUpdateDto: { isOnboarded: true } });
|
||||
await goto(AppRoute.PHOTOS);
|
||||
} else {
|
||||
index++;
|
||||
|
Loading…
Reference in New Issue
Block a user