Files
immich/server/src/entities/system-config.entity.ts

340 lines
9.2 KiB
TypeScript
Raw Normal View History

import { ConcurrentQueueName } from 'src/interfaces/job.interface';
import { Column, Entity, PrimaryColumn } from 'typeorm';
export type SystemConfigValue = string | string[] | number | boolean;
// https://stackoverflow.com/a/47058976
// https://stackoverflow.com/a/70692231
type PathsToStringProps<T> = T extends SystemConfigValue
? []
: {
[K in keyof T]: [K, ...PathsToStringProps<T[K]>];
}[keyof T];
type Join<T extends string[], D extends string> = T extends []
? never
: T extends [infer F]
? F
: T extends [infer F, ...infer R]
? F extends string
? `${F}${D}${Join<Extract<R, string[]>, D>}`
: never
: string;
// dot notation matches path in `SystemConfig`
// TODO: migrate to key value per section
export const SystemConfigKey = {
FFMPEG_CRF: 'ffmpeg.crf',
FFMPEG_THREADS: 'ffmpeg.threads',
FFMPEG_PRESET: 'ffmpeg.preset',
FFMPEG_TARGET_VIDEO_CODEC: 'ffmpeg.targetVideoCodec',
FFMPEG_ACCEPTED_VIDEO_CODECS: 'ffmpeg.acceptedVideoCodecs',
FFMPEG_TARGET_AUDIO_CODEC: 'ffmpeg.targetAudioCodec',
FFMPEG_ACCEPTED_AUDIO_CODECS: 'ffmpeg.acceptedAudioCodecs',
FFMPEG_TARGET_RESOLUTION: 'ffmpeg.targetResolution',
FFMPEG_MAX_BITRATE: 'ffmpeg.maxBitrate',
FFMPEG_BFRAMES: 'ffmpeg.bframes',
FFMPEG_REFS: 'ffmpeg.refs',
FFMPEG_GOP_SIZE: 'ffmpeg.gopSize',
FFMPEG_NPL: 'ffmpeg.npl',
FFMPEG_TEMPORAL_AQ: 'ffmpeg.temporalAQ',
FFMPEG_CQ_MODE: 'ffmpeg.cqMode',
FFMPEG_TWO_PASS: 'ffmpeg.twoPass',
FFMPEG_PREFERRED_HW_DEVICE: 'ffmpeg.preferredHwDevice',
FFMPEG_TRANSCODE: 'ffmpeg.transcode',
FFMPEG_ACCEL: 'ffmpeg.accel',
FFMPEG_TONEMAP: 'ffmpeg.tonemap',
JOB_THUMBNAIL_GENERATION_CONCURRENCY: 'job.thumbnailGeneration.concurrency',
JOB_METADATA_EXTRACTION_CONCURRENCY: 'job.metadataExtraction.concurrency',
JOB_VIDEO_CONVERSION_CONCURRENCY: 'job.videoConversion.concurrency',
JOB_FACE_DETECTION_CONCURRENCY: 'job.faceDetection.concurrency',
JOB_CLIP_ENCODING_CONCURRENCY: 'job.smartSearch.concurrency',
JOB_BACKGROUND_TASK_CONCURRENCY: 'job.backgroundTask.concurrency',
JOB_SEARCH_CONCURRENCY: 'job.search.concurrency',
JOB_SIDECAR_CONCURRENCY: 'job.sidecar.concurrency',
JOB_LIBRARY_CONCURRENCY: 'job.library.concurrency',
JOB_MIGRATION_CONCURRENCY: 'job.migration.concurrency',
LIBRARY_SCAN_ENABLED: 'library.scan.enabled',
LIBRARY_SCAN_CRON_EXPRESSION: 'library.scan.cronExpression',
LIBRARY_WATCH_ENABLED: 'library.watch.enabled',
LOGGING_ENABLED: 'logging.enabled',
LOGGING_LEVEL: 'logging.level',
MACHINE_LEARNING_ENABLED: 'machineLearning.enabled',
MACHINE_LEARNING_URL: 'machineLearning.url',
MACHINE_LEARNING_CLIP_ENABLED: 'machineLearning.clip.enabled',
MACHINE_LEARNING_CLIP_MODEL_NAME: 'machineLearning.clip.modelName',
MACHINE_LEARNING_FACIAL_RECOGNITION_ENABLED: 'machineLearning.facialRecognition.enabled',
MACHINE_LEARNING_FACIAL_RECOGNITION_MODEL_NAME: 'machineLearning.facialRecognition.modelName',
MACHINE_LEARNING_FACIAL_RECOGNITION_MIN_SCORE: 'machineLearning.facialRecognition.minScore',
MACHINE_LEARNING_FACIAL_RECOGNITION_MAX_DISTANCE: 'machineLearning.facialRecognition.maxDistance',
MACHINE_LEARNING_FACIAL_RECOGNITION_MIN_FACES: 'machineLearning.facialRecognition.minFaces',
MAP_ENABLED: 'map.enabled',
MAP_LIGHT_STYLE: 'map.lightStyle',
MAP_DARK_STYLE: 'map.darkStyle',
feat(server): email notifications (#8447) * feat(server): add `react-mail` as mail template engine and `nodemailer` * feat(server): add `smtp` related configs to `SystemConfig` * feat(web): add page for SMTP settings * feat(server): add `react-email.adapter` This adapter render the React-Email into HTML and plain/text email. The output is set as the body of the email. * feat(server): add `MailRepository` and `MailService` Allow to use the NestJS-modules-mailer module to send SMTP emails. This is the base transport for the `NotificationRepository` * feat(server): register the job dispatcher and Job for async email This allows to queue email sending jobs for the `EmailService`. * feat(server): add `NotificationRepository` and `NotificationService` This act as a middleware to properly route the notification to the right transport. As POC I've only implemented a simple SMTP transport. * feat(server): add `welcome` email template * feat(server): add the first notification on `createUser` in `UserService` This trigger an event for the `NotificationRepository` that once processes by using the global config and per-user config will carry the payload to the right notification transport. * chore: clean up * chore: clean up web * fix: type errors" * fix package lock * fix mail sending, option to ignore certs * chore: open api * chore: clean up * remove unused import * feat: email feature flag * chore: remove unused interface * small styling --------- Co-authored-by: Jason Rasmussen <jrasm91@gmail.com> Co-authored-by: Daniel Dietzler <mail@ddietzler.dev> Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
2024-05-02 16:43:18 +02:00
NOTIFICATIONS_SMTP_ENABLED: 'notifications.smtp.enabled',
NOTIFICATIONS_SMTP_FROM: 'notifications.smtp.from',
NOTIFICATIONS_SMTP_REPLY_TO: 'notifications.smtp.replyTo',
NOTIFICATIONS_SMTP_TRANSPORT_IGNORE_CERT: 'notifications.smtp.transport.ignoreCert',
NOTIFICATIONS_SMTP_TRANSPORT_HOST: 'notifications.smtp.transport.host',
NOTIFICATIONS_SMTP_TRANSPORT_PORT: 'notifications.smtp.transport.port',
NOTIFICATIONS_SMTP_TRANSPORT_USERNAME: 'notifications.smtp.transport.username',
NOTIFICATIONS_SMTP_TRANSPORT_PASSWORD: 'notifications.smtp.transport.password',
REVERSE_GEOCODING_ENABLED: 'reverseGeocoding.enabled',
NEW_VERSION_CHECK_ENABLED: 'newVersionCheck.enabled',
OAUTH_AUTO_LAUNCH: 'oauth.autoLaunch',
OAUTH_AUTO_REGISTER: 'oauth.autoRegister',
OAUTH_BUTTON_TEXT: 'oauth.buttonText',
OAUTH_CLIENT_ID: 'oauth.clientId',
OAUTH_CLIENT_SECRET: 'oauth.clientSecret',
OAUTH_DEFAULT_STORAGE_QUOTA: 'oauth.defaultStorageQuota',
OAUTH_ENABLED: 'oauth.enabled',
OAUTH_ISSUER_URL: 'oauth.issuerUrl',
OAUTH_MOBILE_OVERRIDE_ENABLED: 'oauth.mobileOverrideEnabled',
OAUTH_MOBILE_REDIRECT_URI: 'oauth.mobileRedirectUri',
OAUTH_SCOPE: 'oauth.scope',
OAUTH_SIGNING_ALGORITHM: 'oauth.signingAlgorithm',
OAUTH_STORAGE_LABEL_CLAIM: 'oauth.storageLabelClaim',
OAUTH_STORAGE_QUOTA_CLAIM: 'oauth.storageQuotaClaim',
PASSWORD_LOGIN_ENABLED: 'passwordLogin.enabled',
SERVER_EXTERNAL_DOMAIN: 'server.externalDomain',
SERVER_LOGIN_PAGE_MESSAGE: 'server.loginPageMessage',
STORAGE_TEMPLATE_ENABLED: 'storageTemplate.enabled',
STORAGE_TEMPLATE_HASH_VERIFICATION_ENABLED: 'storageTemplate.hashVerificationEnabled',
STORAGE_TEMPLATE: 'storageTemplate.template',
IMAGE_THUMBNAIL_FORMAT: 'image.thumbnailFormat',
IMAGE_THUMBNAIL_SIZE: 'image.thumbnailSize',
IMAGE_PREVIEW_FORMAT: 'image.previewFormat',
IMAGE_PREVIEW_SIZE: 'image.previewSize',
IMAGE_QUALITY: 'image.quality',
IMAGE_COLORSPACE: 'image.colorspace',
IMAGE_EXTRACT_EMBEDDED: 'image.extractEmbedded',
TRASH_ENABLED: 'trash.enabled',
TRASH_DAYS: 'trash.days',
THEME_CUSTOM_CSS: 'theme.customCss',
USER_DELETE_DELAY: 'user.deleteDelay',
} as const satisfies Record<string, Join<PathsToStringProps<SystemConfig>, '.'>>;
export type SystemConfigKeyPaths = (typeof SystemConfigKey)[keyof typeof SystemConfigKey];
@Entity('system_config')
export class SystemConfigEntity<T = SystemConfigValue> {
@PrimaryColumn({ type: 'varchar' })
key!: SystemConfigKeyPaths;
@Column({ type: 'varchar', nullable: true, transformer: { to: JSON.stringify, from: JSON.parse } })
value!: T;
}
export enum TranscodePolicy {
ALL = 'all',
OPTIMAL = 'optimal',
BITRATE = 'bitrate',
REQUIRED = 'required',
DISABLED = 'disabled',
}
export enum TranscodeTarget {
NONE,
AUDIO,
VIDEO,
ALL,
}
export enum VideoCodec {
H264 = 'h264',
HEVC = 'hevc',
VP9 = 'vp9',
AV1 = 'av1',
}
export enum AudioCodec {
MP3 = 'mp3',
AAC = 'aac',
LIBOPUS = 'libopus',
}
export enum TranscodeHWAccel {
NVENC = 'nvenc',
QSV = 'qsv',
VAAPI = 'vaapi',
RKMPP = 'rkmpp',
DISABLED = 'disabled',
}
export enum ToneMapping {
HABLE = 'hable',
MOBIUS = 'mobius',
REINHARD = 'reinhard',
DISABLED = 'disabled',
}
export enum CQMode {
AUTO = 'auto',
CQP = 'cqp',
ICQ = 'icq',
}
export enum Colorspace {
SRGB = 'srgb',
P3 = 'p3',
}
export enum ImageFormat {
JPEG = 'jpeg',
WEBP = 'webp',
}
export enum LogLevel {
VERBOSE = 'verbose',
DEBUG = 'debug',
LOG = 'log',
WARN = 'warn',
ERROR = 'error',
FATAL = 'fatal',
}
export interface SystemConfig {
ffmpeg: {
crf: number;
threads: number;
preset: string;
targetVideoCodec: VideoCodec;
acceptedVideoCodecs: VideoCodec[];
targetAudioCodec: AudioCodec;
acceptedAudioCodecs: AudioCodec[];
targetResolution: string;
maxBitrate: string;
bframes: number;
refs: number;
gopSize: number;
npl: number;
temporalAQ: boolean;
cqMode: CQMode;
twoPass: boolean;
preferredHwDevice: string;
transcode: TranscodePolicy;
accel: TranscodeHWAccel;
tonemap: ToneMapping;
};
feat(server): separate face clustering job (#5598) * separate facial clustering job * update api * fixed some tests * invert clustering * hdbscan * update api * remove commented code * wip dbscan * cleanup removed cluster endpoint remove commented code * fixes updated tests minor fixes and formatting fixed queuing refinements * scale search range based on library size * defer non-core faces * optimizations removed unused query option * assign faces individually for correctness fixed unit tests remove unused method * don't select face embedding update sql linting fixed ml typing * updated job mock * paginate people query * select face embeddings because typeorm * fix setting face detection concurrency * update sql formatting linting * simplify logic remove unused imports * more specific delete signature * more accurate typing for face stubs * add migration formatting * chore: better typing * don't select embedding by default remove unused import * updated sql * use normal try/catch * stricter concurrency typing and enforcement * update api * update job concurrency panel to show disabled queues formatting * check jobId in queueAll fix tests * remove outdated comment * better facial recognition icon * wording wording formatting * fixed tests * fix * formatting & sql * try to fix sql check * more detailed description * update sql * formatting * wording * update `minFaces` description --------- Co-authored-by: Jason Rasmussen <jrasm91@gmail.com> Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
2024-01-18 00:08:48 -05:00
job: Record<ConcurrentQueueName, { concurrency: number }>;
logging: {
enabled: boolean;
level: LogLevel;
};
machineLearning: {
enabled: boolean;
url: string;
clip: {
enabled: boolean;
modelName: string;
};
facialRecognition: {
enabled: boolean;
modelName: string;
minScore: number;
minFaces: number;
maxDistance: number;
};
};
map: {
enabled: boolean;
lightStyle: string;
darkStyle: string;
};
reverseGeocoding: {
enabled: boolean;
};
oauth: {
autoLaunch: boolean;
autoRegister: boolean;
buttonText: string;
clientId: string;
clientSecret: string;
defaultStorageQuota: number;
enabled: boolean;
issuerUrl: string;
mobileOverrideEnabled: boolean;
mobileRedirectUri: string;
scope: string;
signingAlgorithm: string;
storageLabelClaim: string;
storageQuotaClaim: string;
};
passwordLogin: {
enabled: boolean;
};
storageTemplate: {
enabled: boolean;
hashVerificationEnabled: boolean;
template: string;
};
image: {
thumbnailFormat: ImageFormat;
thumbnailSize: number;
previewFormat: ImageFormat;
previewSize: number;
quality: number;
colorspace: Colorspace;
extractEmbedded: boolean;
};
newVersionCheck: {
enabled: boolean;
};
feat(server): trash asset (#4015) * refactor(server): delete assets endpoint * fix: formatting * chore: cleanup * chore: open api * chore(mobile): replace DeleteAssetDTO with BulkIdsDTOs * feat: trash an asset * chore(server): formatting * chore: open api * chore: wording * chore: open-api * feat(server): add withDeleted to getAssets queries * WIP: mobile-recycle-bin * feat(server): recycle-bin to system config * feat(web): use recycle-bin system config * chore(server): domain assetcore removed * chore(server): rename recycle-bin to trash * chore(web): rename recycle-bin to trash * chore(server): always send soft deleted assets for getAllByUserId * chore(web): formatting * feat(server): permanent delete assets older than trashed period * feat(web): trash empty placeholder image * feat(server): empty trash * feat(web): empty trash * WIP: mobile-recycle-bin * refactor(server): empty / restore trash to separate endpoint * test(server): handle failures * test(server): fix e2e server-info test * test(server): deletion test refactor * feat(mobile): use map settings from server-config to enable / disable map * feat(mobile): trash asset * fix(server): operations on assets in trash * feat(web): show trash statistics * fix(web): handle trash enabled * fix(mobile): restore updates from trash * fix(server): ignore trashed assets for person * fix(server): add / remove search index when trashed / restored * chore(web): format * fix(server): asset service test * fix(server): include trashed assts for duplicates from uploads * feat(mobile): no dialog for trash, always dialog for permanent delete * refactor(mobile): use isar where instead of dart filter * refactor(mobile): asset provide - handle deletes in single db txn * chore(mobile): review changes * feat(web): confirmation before empty trash * server: review changes * fix(server): handle library changes * fix: filter external assets from getting trashed / deleted * fix(server): empty-bin * feat: broadcast config update events through ws * change order of trash button on mobile * styling * fix(mobile): do not show trashed toast for local only assets --------- Co-authored-by: Jason Rasmussen <jrasm91@gmail.com> Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
2023-10-06 07:01:14 +00:00
trash: {
enabled: boolean;
days: number;
};
theme: {
customCss: string;
};
library: {
scan: {
enabled: boolean;
cronExpression: string;
};
feat(server): Automatic watching of library folders (#6192) * feat: initial watch support * allow offline files * chore: ignore query errors when resetting e2e db * revert db query * add savepoint * guard the user query * chore: openapi and db migration * wip * support multiple libraries * fix tests * wip * can now cleanup chokidar watchers * fix unit tests * add library watch queue * add missing init from merge * wip * can now filter file extensions * remove watch api from non job client * Fix e2e test * watch library with updated import path and exclusion pattern * add library watch frontend ui * case sensitive watching extensions * can auto watch libraries * move watcher e2e tests to separate file * don't watch libraries from a queue * use event emitters * shorten e2e test timeout * refactor chokidar code to filesystem provider * expose chokidar parameters to config file * fix storage mock * set default config for library watching * add fs provider mocks * cleanup * add more unit tests for watcher * chore: fix format + sql * add more tests * move unwatch feature back to library service * add file event unit tests * chore: formatting * add documentation * fix e2e tests * chore: fix e2e tests * fix library updating * test cleanup * fix typo * cleanup * fixing as per pr comments * reduce library watch config file * update storage config and mocks * move negative event tests to unit tests * fix library watcher e2e * make watch configuration global * remove the feature flag * refactor watcher teardown * fix microservices init * centralize asset scan job queue * improve docs * add more tests * chore: open api * initialize app service * fix docs * fix library watch feature flag * Update docs/docs/features/libraries.md Co-authored-by: Daniel Dietzler <36593685+danieldietzler@users.noreply.github.com> * fix: import right app service * don't be truthy * fix test speling * stricter library update tests * move fs watcher mock to external file * subscribe to config changes * docker does not need polling * make library watch() private * feat: add configuration ui --------- Co-authored-by: Daniel Dietzler <36593685+danieldietzler@users.noreply.github.com> Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
2024-01-31 09:15:54 +01:00
watch: {
enabled: boolean;
};
};
feat(server): email notifications (#8447) * feat(server): add `react-mail` as mail template engine and `nodemailer` * feat(server): add `smtp` related configs to `SystemConfig` * feat(web): add page for SMTP settings * feat(server): add `react-email.adapter` This adapter render the React-Email into HTML and plain/text email. The output is set as the body of the email. * feat(server): add `MailRepository` and `MailService` Allow to use the NestJS-modules-mailer module to send SMTP emails. This is the base transport for the `NotificationRepository` * feat(server): register the job dispatcher and Job for async email This allows to queue email sending jobs for the `EmailService`. * feat(server): add `NotificationRepository` and `NotificationService` This act as a middleware to properly route the notification to the right transport. As POC I've only implemented a simple SMTP transport. * feat(server): add `welcome` email template * feat(server): add the first notification on `createUser` in `UserService` This trigger an event for the `NotificationRepository` that once processes by using the global config and per-user config will carry the payload to the right notification transport. * chore: clean up * chore: clean up web * fix: type errors" * fix package lock * fix mail sending, option to ignore certs * chore: open api * chore: clean up * remove unused import * feat: email feature flag * chore: remove unused interface * small styling --------- Co-authored-by: Jason Rasmussen <jrasm91@gmail.com> Co-authored-by: Daniel Dietzler <mail@ddietzler.dev> Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
2024-05-02 16:43:18 +02:00
notifications: {
smtp: {
enabled: boolean;
from: string;
replyTo: string;
transport: {
ignoreCert: boolean;
host: string;
port: number;
username: string;
password: string;
};
};
};
server: {
externalDomain: string;
loginPageMessage: string;
};
user: {
deleteDelay: number;
};
}