feat(server,web): server config (#4006)

* feat: server config

* chore: open api

* fix: redirect /map to /photos when disabled
This commit is contained in:
Jason Rasmussen 2023-09-08 22:51:46 -04:00 committed by GitHub
parent 3edade6761
commit f1db257628
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
48 changed files with 1103 additions and 162 deletions

View file

@ -79,16 +79,21 @@ export class ServerMediaTypesResponseDto {
sidecar!: string[];
}
export class ServerFeaturesDto implements FeatureFlags {
configFile!: boolean;
clipEncode!: boolean;
facialRecognition!: boolean;
sidecar!: boolean;
search!: boolean;
tagImage!: boolean;
export class ServerConfigDto {
oauthButtonText!: string;
loginPageMessage!: string;
mapTileUrl!: string;
}
// TODO: use these instead of `POST oauth/config`
export class ServerFeaturesDto implements FeatureFlags {
clipEncode!: boolean;
configFile!: boolean;
facialRecognition!: boolean;
map!: boolean;
oauth!: boolean;
oauthAutoLaunch!: boolean;
passwordLogin!: boolean;
sidecar!: boolean;
search!: boolean;
tagImage!: boolean;
}

View file

@ -143,22 +143,34 @@ describe(ServerInfoService.name, () => {
it('should respond the server version', () => {
expect(sut.getVersion()).toEqual(serverVersion);
});
});
describe('getFeatures', () => {
it('should respond the server features', async () => {
await expect(sut.getFeatures()).resolves.toEqual({
clipEncode: true,
facialRecognition: true,
oauth: false,
oauthAutoLaunch: false,
passwordLogin: true,
search: true,
sidecar: true,
tagImage: true,
configFile: false,
});
expect(configMock.load).toHaveBeenCalled();
describe('getFeatures', () => {
it('should respond the server features', async () => {
await expect(sut.getFeatures()).resolves.toEqual({
clipEncode: true,
facialRecognition: true,
map: true,
oauth: false,
oauthAutoLaunch: false,
passwordLogin: true,
search: true,
sidecar: true,
tagImage: true,
configFile: false,
});
expect(configMock.load).toHaveBeenCalled();
});
});
describe('getConfig', () => {
it('should respond the server configuration', async () => {
await expect(sut.getConfig()).resolves.toEqual({
loginPageMessage: '',
oauthButtonText: 'Login with OAuth',
mapTileUrl: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
});
expect(configMock.load).toHaveBeenCalled();
});
});

View file

@ -5,6 +5,7 @@ import { IStorageRepository, StorageCore, StorageFolder } from '../storage';
import { ISystemConfigRepository, SystemConfigCore } from '../system-config';
import { IUserRepository, UserStatsQueryResponse } from '../user';
import {
ServerConfigDto,
ServerFeaturesDto,
ServerInfoResponseDto,
ServerMediaTypesResponseDto,
@ -55,6 +56,19 @@ export class ServerInfoService {
return this.configCore.getFeatures();
}
async getConfig(): Promise<ServerConfigDto> {
const config = await this.configCore.getConfig();
// TODO move to system config
const loginPageMessage = process.env.PUBLIC_LOGIN_PAGE_MESSAGE || '';
return {
loginPageMessage,
mapTileUrl: config.map.tileUrl,
oauthButtonText: config.oauth.buttonText,
};
}
async getStats(): Promise<ServerStatsResponseDto> {
const userStats: UserStatsQueryResponse[] = await this.userRepository.getUserStats();
const serverStats = new ServerStatsResponseDto();

View file

@ -0,0 +1,9 @@
import { IsBoolean, IsString } from 'class-validator';
export class SystemConfigMapDto {
@IsBoolean()
enabled!: boolean;
@IsString()
tileUrl!: string;
}

View file

@ -5,6 +5,7 @@ import { IsObject, ValidateNested } from 'class-validator';
import { SystemConfigFFmpegDto } from './system-config-ffmpeg.dto';
import { SystemConfigJobDto } from './system-config-job.dto';
import { SystemConfigMachineLearningDto } from './system-config-machine-learning.dto';
import { SystemConfigMapDto } from './system-config-map.dto';
import { SystemConfigOAuthDto } from './system-config-oauth.dto';
import { SystemConfigPasswordLoginDto } from './system-config-password-login.dto';
import { SystemConfigStorageTemplateDto } from './system-config-storage-template.dto';
@ -20,6 +21,11 @@ export class SystemConfigDto implements SystemConfig {
@IsObject()
machineLearning!: SystemConfigMachineLearningDto;
@Type(() => SystemConfigMapDto)
@ValidateNested()
@IsObject()
map!: SystemConfigMapDto;
@Type(() => SystemConfigOAuthDto)
@ValidateNested()
@IsObject()

View file

@ -55,7 +55,6 @@ export const defaults = Object.freeze<SystemConfig>({
[QueueName.THUMBNAIL_GENERATION]: { concurrency: 5 },
[QueueName.VIDEO_CONVERSION]: { concurrency: 1 },
},
machineLearning: {
enabled: process.env.IMMICH_MACHINE_LEARNING_ENABLED !== 'false',
url: process.env.IMMICH_MACHINE_LEARNING_URL || 'http://immich-machine-learning:3003',
@ -75,6 +74,10 @@ export const defaults = Object.freeze<SystemConfig>({
maxDistance: 0.6,
},
},
map: {
enabled: true,
tileUrl: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
},
oauth: {
enabled: false,
issuerUrl: '',
@ -108,6 +111,7 @@ export enum FeatureFlag {
CLIP_ENCODE = 'clipEncode',
FACIAL_RECOGNITION = 'facialRecognition',
TAG_IMAGE = 'tagImage',
MAP = 'map',
SIDECAR = 'sidecar',
SEARCH = 'search',
OAUTH = 'oauth',
@ -169,6 +173,7 @@ export class SystemConfigCore {
[FeatureFlag.CLIP_ENCODE]: mlEnabled && config.machineLearning.clip.enabled,
[FeatureFlag.FACIAL_RECOGNITION]: mlEnabled && config.machineLearning.facialRecognition.enabled,
[FeatureFlag.TAG_IMAGE]: mlEnabled && config.machineLearning.classification.enabled,
[FeatureFlag.MAP]: config.map.enabled,
[FeatureFlag.SIDECAR]: true,
[FeatureFlag.SEARCH]: process.env.TYPESENSE_ENABLED !== 'false',

View file

@ -73,6 +73,10 @@ const updatedConfig = Object.freeze<SystemConfig>({
maxDistance: 0.6,
},
},
map: {
enabled: true,
tileUrl: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
},
oauth: {
autoLaunch: true,
autoRegister: true,

View file

@ -1,4 +1,5 @@
import {
ServerConfigDto,
ServerFeaturesDto,
ServerInfoResponseDto,
ServerInfoService,
@ -42,6 +43,12 @@ export class ServerInfoController {
return this.service.getFeatures();
}
@PublicRoute()
@Get('config')
getServerConfig(): Promise<ServerConfigDto> {
return this.service.getConfig();
}
@AdminRoute()
@Get('stats')
getStats(): Promise<ServerStatsResponseDto> {

View file

@ -58,6 +58,9 @@ export enum SystemConfigKey {
MACHINE_LEARNING_FACIAL_RECOGNITION_MIN_SCORE = 'machineLearning.facialRecognition.minScore',
MACHINE_LEARNING_FACIAL_RECOGNITION_MAX_DISTANCE = 'machineLearning.facialRecognition.maxDistance',
MAP_ENABLED = 'map.enabled',
MAP_TILE_URL = 'map.tileUrl',
OAUTH_ENABLED = 'oauth.enabled',
OAUTH_ISSUER_URL = 'oauth.issuerUrl',
OAUTH_CLIENT_ID = 'oauth.clientId',
@ -164,6 +167,10 @@ export interface SystemConfig {
maxDistance: number;
};
};
map: {
enabled: boolean;
tileUrl: string;
};
oauth: {
enabled: boolean;
issuerUrl: string;