diff --git a/e2e/src/api/specs/server.e2e-spec.ts b/e2e/src/api/specs/server.e2e-spec.ts new file mode 100644 index 0000000000..808ce36363 --- /dev/null +++ b/e2e/src/api/specs/server.e2e-spec.ts @@ -0,0 +1,200 @@ +import { LoginResponseDto } 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', () => { + 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('GET /server/about', () => { + it('should require authentication', async () => { + const { status, body } = await request(app).get('/server/about'); + expect(status).toBe(401); + expect(body).toEqual(errorDto.unauthorized); + }); + + it('should return about information', async () => { + const { status, body } = await request(app) + .get('/server/about') + .set('Authorization', `Bearer ${admin.accessToken}`); + expect(status).toBe(200); + expect(body).toEqual({ + version: expect.any(String), + versionUrl: expect.any(String), + repository: 'immich-app/immich', + repositoryUrl: 'https://github.com/immich-app/immich', + build: '1234567890', + buildUrl: 'https://github.com/immich-app/immich/actions/runs/1234567890', + buildImage: 'e2e', + buildImageUrl: 'https://github.com/immich-app/immich/pkgs/container/immich-server', + sourceRef: 'e2e', + sourceCommit: 'e2eeeeeeeeeeeeeeeeee', + sourceUrl: 'https://github.com/immich-app/immich/commit/e2eeeeeeeeeeeeeeeeee', + nodejs: expect.any(String), + ffmpeg: expect.any(String), + imagemagick: expect.any(String), + libvips: expect.any(String), + exiftool: expect.any(String), + }); + }); + }); + + describe('GET /server/storage', () => { + it('should require authentication', async () => { + const { status, body } = await request(app).get('/server/storage'); + expect(status).toBe(401); + expect(body).toEqual(errorDto.unauthorized); + }); + + it('should return the disk information', async () => { + const { status, body } = await request(app) + .get('/server/storage') + .set('Authorization', `Bearer ${admin.accessToken}`); + expect(status).toBe(200); + expect(body).toEqual({ + diskAvailable: expect.any(String), + diskAvailableRaw: expect.any(Number), + diskSize: expect.any(String), + diskSizeRaw: expect.any(Number), + diskUsagePercentage: expect.any(Number), + diskUse: expect.any(String), + diskUseRaw: expect.any(Number), + }); + }); + }); + + describe('GET /server/ping', () => { + it('should respond with pong', async () => { + const { status, body } = await request(app).get('/server/ping'); + expect(status).toBe(200); + expect(body).toEqual({ res: 'pong' }); + }); + }); + + describe('GET /server/version', () => { + it('should respond with the server version', async () => { + const { status, body } = await request(app).get('/server/version'); + expect(status).toBe(200); + expect(body).toEqual({ + major: expect.any(Number), + minor: expect.any(Number), + patch: expect.any(Number), + }); + }); + }); + + describe('GET /server/features', () => { + it('should respond with the server features', async () => { + const { status, body } = await request(app).get('/server/features'); + expect(status).toBe(200); + expect(body).toEqual({ + smartSearch: false, + configFile: false, + duplicateDetection: false, + facialRecognition: false, + map: true, + reverseGeocoding: true, + oauth: false, + oauthAutoLaunch: false, + passwordLogin: true, + search: true, + sidecar: true, + trash: true, + email: false, + }); + }); + }); + + describe('GET /server/config', () => { + it('should respond with the server configuration', async () => { + const { status, body } = await request(app).get('/server/config'); + expect(status).toBe(200); + expect(body).toEqual({ + loginPageMessage: '', + oauthButtonText: 'Login with OAuth', + trashDays: 30, + userDeleteDelay: 7, + isInitialized: true, + externalDomain: '', + isOnboarded: false, + }); + }); + }); + + describe('GET /server/statistics', () => { + it('should require authentication', async () => { + const { status, body } = await request(app).get('/server/statistics'); + expect(status).toBe(401); + expect(body).toEqual(errorDto.unauthorized); + }); + + it('should only work for admins', async () => { + const { status, body } = await request(app) + .get('/server/statistics') + .set('Authorization', `Bearer ${nonAdmin.accessToken}`); + expect(status).toBe(403); + expect(body).toEqual(errorDto.forbidden); + }); + + it('should return the server stats', async () => { + const { status, body } = await request(app) + .get('/server/statistics') + .set('Authorization', `Bearer ${admin.accessToken}`); + expect(status).toBe(200); + expect(body).toEqual({ + photos: 0, + usage: 0, + usageByUser: [ + { + quotaSizeInBytes: null, + photos: 0, + usage: 0, + userName: 'Immich Admin', + userId: admin.userId, + videos: 0, + }, + { + quotaSizeInBytes: null, + photos: 0, + usage: 0, + userName: 'User 1', + userId: nonAdmin.userId, + videos: 0, + }, + ], + videos: 0, + }); + }); + }); + + describe('GET /server/media-types', () => { + it('should return accepted media types', async () => { + const { status, body } = await request(app).get('/server/media-types'); + expect(status).toBe(200); + expect(body).toEqual({ + sidecar: ['.xmp'], + image: expect.any(Array), + video: expect.any(Array), + }); + }); + }); + + describe('GET /server/theme', () => { + it('should respond with the server theme', async () => { + const { status, body } = await request(app).get('/server/theme'); + expect(status).toBe(200); + expect(body).toEqual({ + customCss: '', + }); + }); + }); +}); diff --git a/mobile/openapi/README.md b/mobile/openapi/README.md index 691393eb63..046ccb5b61 100644 Binary files a/mobile/openapi/README.md and b/mobile/openapi/README.md differ diff --git a/mobile/openapi/lib/api.dart b/mobile/openapi/lib/api.dart index fafd17659b..695a482e82 100644 Binary files a/mobile/openapi/lib/api.dart and b/mobile/openapi/lib/api.dart differ diff --git a/mobile/openapi/lib/api/deprecated_api.dart b/mobile/openapi/lib/api/deprecated_api.dart new file mode 100644 index 0000000000..18518cca69 Binary files /dev/null and b/mobile/openapi/lib/api/deprecated_api.dart differ diff --git a/mobile/openapi/lib/api/server_info_api.dart b/mobile/openapi/lib/api/server_info_api.dart index 2ddcaa630b..dc58a94fd0 100644 Binary files a/mobile/openapi/lib/api/server_info_api.dart and b/mobile/openapi/lib/api/server_info_api.dart differ diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json index 0ac2cd53c9..6db23aa8c4 100644 --- a/open-api/immich-openapi-specs.json +++ b/open-api/immich-openapi-specs.json @@ -4720,6 +4720,8 @@ }, "/server-info/about": { "get": { + "deprecated": true, + "description": "This property was deprecated in v1.107.0", "operationId": "getAboutInfo", "parameters": [], "responses": { @@ -4746,12 +4748,18 @@ } ], "tags": [ - "Server Info" - ] + "Server Info", + "Deprecated" + ], + "x-immich-lifecycle": { + "deprecatedAt": "v1.107.0" + } } }, "/server-info/config": { "get": { + "deprecated": true, + "description": "This property was deprecated in v1.107.0", "operationId": "getServerConfig", "parameters": [], "responses": { @@ -4767,12 +4775,18 @@ } }, "tags": [ - "Server Info" - ] + "Server Info", + "Deprecated" + ], + "x-immich-lifecycle": { + "deprecatedAt": "v1.107.0" + } } }, "/server-info/features": { "get": { + "deprecated": true, + "description": "This property was deprecated in v1.107.0", "operationId": "getServerFeatures", "parameters": [], "responses": { @@ -4788,12 +4802,18 @@ } }, "tags": [ - "Server Info" - ] + "Server Info", + "Deprecated" + ], + "x-immich-lifecycle": { + "deprecatedAt": "v1.107.0" + } } }, "/server-info/media-types": { "get": { + "deprecated": true, + "description": "This property was deprecated in v1.107.0", "operationId": "getSupportedMediaTypes", "parameters": [], "responses": { @@ -4809,12 +4829,18 @@ } }, "tags": [ - "Server Info" - ] + "Server Info", + "Deprecated" + ], + "x-immich-lifecycle": { + "deprecatedAt": "v1.107.0" + } } }, "/server-info/ping": { "get": { + "deprecated": true, + "description": "This property was deprecated in v1.107.0", "operationId": "pingServer", "parameters": [], "responses": { @@ -4830,12 +4856,18 @@ } }, "tags": [ - "Server Info" - ] + "Server Info", + "Deprecated" + ], + "x-immich-lifecycle": { + "deprecatedAt": "v1.107.0" + } } }, "/server-info/statistics": { "get": { + "deprecated": true, + "description": "This property was deprecated in v1.107.0", "operationId": "getServerStatistics", "parameters": [], "responses": { @@ -4862,12 +4894,18 @@ } ], "tags": [ - "Server Info" - ] + "Server Info", + "Deprecated" + ], + "x-immich-lifecycle": { + "deprecatedAt": "v1.107.0" + } } }, "/server-info/storage": { "get": { + "deprecated": true, + "description": "This property was deprecated in v1.107.0", "operationId": "getStorage", "parameters": [], "responses": { @@ -4894,12 +4932,18 @@ } ], "tags": [ - "Server Info" - ] + "Server Info", + "Deprecated" + ], + "x-immich-lifecycle": { + "deprecatedAt": "v1.107.0" + } } }, "/server-info/theme": { "get": { + "deprecated": true, + "description": "This property was deprecated in v1.107.0", "operationId": "getTheme", "parameters": [], "responses": { @@ -4915,12 +4959,18 @@ } }, "tags": [ - "Server Info" - ] + "Server Info", + "Deprecated" + ], + "x-immich-lifecycle": { + "deprecatedAt": "v1.107.0" + } } }, "/server-info/version": { "get": { + "deprecated": true, + "description": "This property was deprecated in v1.107.0", "operationId": "getServerVersion", "parameters": [], "responses": { @@ -4936,8 +4986,12 @@ } }, "tags": [ - "Server Info" - ] + "Server Info", + "Deprecated" + ], + "x-immich-lifecycle": { + "deprecatedAt": "v1.107.0" + } } }, "/sessions": { diff --git a/open-api/typescript-sdk/src/fetch-client.ts b/open-api/typescript-sdk/src/fetch-client.ts index ddf6c958b8..5c8ee9c70f 100644 --- a/open-api/typescript-sdk/src/fetch-client.ts +++ b/open-api/typescript-sdk/src/fetch-client.ts @@ -2385,6 +2385,9 @@ export function getSearchSuggestions({ country, make, model, state, $type }: { ...opts })); } +/** + * This property was deprecated in v1.107.0 + */ export function getAboutInfo(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -2393,6 +2396,9 @@ export function getAboutInfo(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This property was deprecated in v1.107.0 + */ export function getServerConfig(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -2401,6 +2407,9 @@ export function getServerConfig(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This property was deprecated in v1.107.0 + */ export function getServerFeatures(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -2409,6 +2418,9 @@ export function getServerFeatures(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This property was deprecated in v1.107.0 + */ export function getSupportedMediaTypes(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -2417,6 +2429,9 @@ export function getSupportedMediaTypes(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This property was deprecated in v1.107.0 + */ export function pingServer(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -2425,6 +2440,9 @@ export function pingServer(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This property was deprecated in v1.107.0 + */ export function getServerStatistics(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -2433,6 +2451,9 @@ export function getServerStatistics(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This property was deprecated in v1.107.0 + */ export function getStorage(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -2441,6 +2462,9 @@ export function getStorage(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This property was deprecated in v1.107.0 + */ export function getTheme(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; @@ -2449,6 +2473,9 @@ export function getTheme(opts?: Oazapfts.RequestOpts) { ...opts })); } +/** + * This property was deprecated in v1.107.0 + */ export function getServerVersion(opts?: Oazapfts.RequestOpts) { return oazapfts.ok(oazapfts.fetchJson<{ status: 200; diff --git a/server/src/controllers/index.ts b/server/src/controllers/index.ts index ba52f9d7b9..9675cf6d3b 100644 --- a/server/src/controllers/index.ts +++ b/server/src/controllers/index.ts @@ -20,6 +20,7 @@ import { PartnerController } from 'src/controllers/partner.controller'; import { PersonController } from 'src/controllers/person.controller'; import { SearchController } from 'src/controllers/search.controller'; import { ServerInfoController } from 'src/controllers/server-info.controller'; +import { ServerController } from 'src/controllers/server.controller'; import { SessionController } from 'src/controllers/session.controller'; import { SharedLinkController } from 'src/controllers/shared-link.controller'; import { SyncController } from 'src/controllers/sync.controller'; @@ -53,6 +54,7 @@ export const controllers = [ PersonController, ReportController, SearchController, + ServerController, ServerInfoController, SessionController, SharedLinkController, diff --git a/server/src/controllers/server-info.controller.ts b/server/src/controllers/server-info.controller.ts index 2aaac4a0ff..812016f4eb 100644 --- a/server/src/controllers/server-info.controller.ts +++ b/server/src/controllers/server-info.controller.ts @@ -1,5 +1,6 @@ import { Controller, Get } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; +import { EndpointLifecycle } from 'src/decorators'; import { ServerAboutResponseDto, ServerConfigDto, @@ -10,63 +11,72 @@ import { ServerStorageResponseDto, ServerThemeDto, ServerVersionResponseDto, -} from 'src/dtos/server-info.dto'; +} from 'src/dtos/server.dto'; import { Authenticated } from 'src/middleware/auth.guard'; -import { ServerInfoService } from 'src/services/server-info.service'; +import { ServerService } from 'src/services/server.service'; import { VersionService } from 'src/services/version.service'; @ApiTags('Server Info') @Controller('server-info') export class ServerInfoController { constructor( - private service: ServerInfoService, + private service: ServerService, private versionService: VersionService, ) {} @Get('about') + @EndpointLifecycle({ deprecatedAt: 'v1.107.0' }) @Authenticated() getAboutInfo(): Promise { return this.service.getAboutInfo(); } @Get('storage') + @EndpointLifecycle({ deprecatedAt: 'v1.107.0' }) @Authenticated() getStorage(): Promise { return this.service.getStorage(); } @Get('ping') + @EndpointLifecycle({ deprecatedAt: 'v1.107.0' }) pingServer(): ServerPingResponse { return this.service.ping(); } @Get('version') + @EndpointLifecycle({ deprecatedAt: 'v1.107.0' }) getServerVersion(): ServerVersionResponseDto { return this.versionService.getVersion(); } @Get('features') + @EndpointLifecycle({ deprecatedAt: 'v1.107.0' }) getServerFeatures(): Promise { return this.service.getFeatures(); } @Get('theme') + @EndpointLifecycle({ deprecatedAt: 'v1.107.0' }) getTheme(): Promise { return this.service.getTheme(); } @Get('config') + @EndpointLifecycle({ deprecatedAt: 'v1.107.0' }) getServerConfig(): Promise { return this.service.getConfig(); } @Authenticated({ admin: true }) + @EndpointLifecycle({ deprecatedAt: 'v1.107.0' }) @Get('statistics') getServerStatistics(): Promise { return this.service.getStatistics(); } @Get('media-types') + @EndpointLifecycle({ deprecatedAt: 'v1.107.0' }) getSupportedMediaTypes(): ServerMediaTypesResponseDto { return this.service.getSupportedMediaTypes(); } diff --git a/server/src/controllers/server.controller.ts b/server/src/controllers/server.controller.ts new file mode 100644 index 0000000000..45d992908f --- /dev/null +++ b/server/src/controllers/server.controller.ts @@ -0,0 +1,82 @@ +import { Controller, Get } from '@nestjs/common'; +import { ApiExcludeEndpoint, ApiTags } from '@nestjs/swagger'; +import { + ServerAboutResponseDto, + ServerConfigDto, + ServerFeaturesDto, + ServerMediaTypesResponseDto, + ServerPingResponse, + ServerStatsResponseDto, + ServerStorageResponseDto, + ServerThemeDto, + ServerVersionResponseDto, +} from 'src/dtos/server.dto'; +import { Authenticated } from 'src/middleware/auth.guard'; +import { ServerService } from 'src/services/server.service'; +import { VersionService } from 'src/services/version.service'; + +@ApiTags('Server') +@Controller('server') +export class ServerController { + constructor( + private service: ServerService, + private versionService: VersionService, + ) {} + + @Get('about') + @Authenticated() + @ApiExcludeEndpoint() + getAboutInfo(): Promise { + return this.service.getAboutInfo(); + } + + @Get('storage') + @Authenticated() + @ApiExcludeEndpoint() + getStorage(): Promise { + return this.service.getStorage(); + } + + @Get('ping') + @ApiExcludeEndpoint() + pingServer(): ServerPingResponse { + return this.service.ping(); + } + + @Get('version') + @ApiExcludeEndpoint() + getServerVersion(): ServerVersionResponseDto { + return this.versionService.getVersion(); + } + + @Get('features') + @ApiExcludeEndpoint() + getServerFeatures(): Promise { + return this.service.getFeatures(); + } + + @Get('theme') + @ApiExcludeEndpoint() + getTheme(): Promise { + return this.service.getTheme(); + } + + @Get('config') + @ApiExcludeEndpoint() + getServerConfig(): Promise { + return this.service.getConfig(); + } + + @Authenticated({ admin: true }) + @Get('statistics') + @ApiExcludeEndpoint() + getServerStatistics(): Promise { + return this.service.getStatistics(); + } + + @Get('media-types') + @ApiExcludeEndpoint() + getSupportedMediaTypes(): ServerMediaTypesResponseDto { + return this.service.getSupportedMediaTypes(); + } +} diff --git a/server/src/dtos/server-info.dto.ts b/server/src/dtos/server.dto.ts similarity index 100% rename from server/src/dtos/server-info.dto.ts rename to server/src/dtos/server.dto.ts diff --git a/server/src/interfaces/event.interface.ts b/server/src/interfaces/event.interface.ts index 6b52f21d8d..da1e7b1aa5 100644 --- a/server/src/interfaces/event.interface.ts +++ b/server/src/interfaces/event.interface.ts @@ -1,6 +1,6 @@ import { SystemConfig } from 'src/config'; import { AssetResponseDto } from 'src/dtos/asset-response.dto'; -import { ReleaseNotification, ServerVersionResponseDto } from 'src/dtos/server-info.dto'; +import { ReleaseNotification, ServerVersionResponseDto } from 'src/dtos/server.dto'; export const IEventRepository = 'IEventRepository'; diff --git a/server/src/services/index.ts b/server/src/services/index.ts index 2e79bf6fd2..ab680f15e3 100644 --- a/server/src/services/index.ts +++ b/server/src/services/index.ts @@ -21,7 +21,7 @@ import { NotificationService } from 'src/services/notification.service'; import { PartnerService } from 'src/services/partner.service'; import { PersonService } from 'src/services/person.service'; import { SearchService } from 'src/services/search.service'; -import { ServerInfoService } from 'src/services/server-info.service'; +import { ServerService } from 'src/services/server.service'; import { SessionService } from 'src/services/session.service'; import { SharedLinkService } from 'src/services/shared-link.service'; import { SmartInfoService } from 'src/services/smart-info.service'; @@ -61,7 +61,7 @@ export const services = [ PartnerService, PersonService, SearchService, - ServerInfoService, + ServerService, SessionService, SharedLinkService, SmartInfoService, diff --git a/server/src/services/server-info.service.spec.ts b/server/src/services/server.service.spec.ts similarity index 97% rename from server/src/services/server-info.service.spec.ts rename to server/src/services/server.service.spec.ts index b1200cadc5..b1fbb9c2e9 100644 --- a/server/src/services/server-info.service.spec.ts +++ b/server/src/services/server.service.spec.ts @@ -3,7 +3,7 @@ import { IServerInfoRepository } from 'src/interfaces/server-info.interface'; import { IStorageRepository } from 'src/interfaces/storage.interface'; import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interface'; import { IUserRepository } from 'src/interfaces/user.interface'; -import { ServerInfoService } from 'src/services/server-info.service'; +import { ServerService } from 'src/services/server.service'; import { newLoggerRepositoryMock } from 'test/repositories/logger.repository.mock'; import { newServerInfoRepositoryMock } from 'test/repositories/server-info.repository.mock'; import { newStorageRepositoryMock } from 'test/repositories/storage.repository.mock'; @@ -11,8 +11,8 @@ import { newSystemMetadataRepositoryMock } from 'test/repositories/system-metada import { newUserRepositoryMock } from 'test/repositories/user.repository.mock'; import { Mocked } from 'vitest'; -describe(ServerInfoService.name, () => { - let sut: ServerInfoService; +describe(ServerService.name, () => { + let sut: ServerService; let storageMock: Mocked; let userMock: Mocked; let serverInfoMock: Mocked; @@ -26,7 +26,7 @@ describe(ServerInfoService.name, () => { systemMock = newSystemMetadataRepositoryMock(); loggerMock = newLoggerRepositoryMock(); - sut = new ServerInfoService(userMock, storageMock, systemMock, serverInfoMock, loggerMock); + sut = new ServerService(userMock, storageMock, systemMock, serverInfoMock, loggerMock); }); it('should work', () => { diff --git a/server/src/services/server-info.service.ts b/server/src/services/server.service.ts similarity index 97% rename from server/src/services/server-info.service.ts rename to server/src/services/server.service.ts index 3f495aa635..e257b435c1 100644 --- a/server/src/services/server-info.service.ts +++ b/server/src/services/server.service.ts @@ -12,7 +12,7 @@ import { ServerStatsResponseDto, ServerStorageResponseDto, UsageByUserDto, -} from 'src/dtos/server-info.dto'; +} from 'src/dtos/server.dto'; import { SystemMetadataKey } from 'src/entities/system-metadata.entity'; import { OnEvents } from 'src/interfaces/event.interface'; import { ILoggerRepository } from 'src/interfaces/logger.interface'; @@ -25,7 +25,7 @@ import { mimeTypes } from 'src/utils/mime-types'; import { isDuplicateDetectionEnabled, isFacialRecognitionEnabled, isSmartSearchEnabled } from 'src/utils/misc'; @Injectable() -export class ServerInfoService implements OnEvents { +export class ServerService implements OnEvents { private configCore: SystemConfigCore; constructor( @@ -35,7 +35,7 @@ export class ServerInfoService implements OnEvents { @Inject(IServerInfoRepository) private serverInfoRepository: IServerInfoRepository, @Inject(ILoggerRepository) private logger: ILoggerRepository, ) { - this.logger.setContext(ServerInfoService.name); + this.logger.setContext(ServerService.name); this.configCore = SystemConfigCore.create(systemMetadataRepository, this.logger); } diff --git a/server/src/services/version.service.ts b/server/src/services/version.service.ts index a42e550be6..8408e53bfe 100644 --- a/server/src/services/version.service.ts +++ b/server/src/services/version.service.ts @@ -4,7 +4,7 @@ import semver, { SemVer } from 'semver'; import { isDev, serverVersion } from 'src/constants'; import { SystemConfigCore } from 'src/cores/system-config.core'; import { OnServerEvent } from 'src/decorators'; -import { ReleaseNotification, ServerVersionResponseDto } from 'src/dtos/server-info.dto'; +import { ReleaseNotification, ServerVersionResponseDto } from 'src/dtos/server.dto'; import { SystemMetadataKey, VersionCheckMetadata } from 'src/entities/system-metadata.entity'; import { ClientEvent, IEventRepository, OnEvents, ServerEvent, ServerEventMap } from 'src/interfaces/event.interface'; import { IJobRepository, JobName, JobStatus } from 'src/interfaces/job.interface';