diff --git a/MANPAGE.md b/MANPAGE.md new file mode 100644 index 0000000..8cdb7da --- /dev/null +++ b/MANPAGE.md @@ -0,0 +1,180 @@ +# Pigallery 2 man page +pigallery2 uses [typeconfig](https://github.com/bpatrik/typeconfig) for configuration + +`npm start -- --help` prints the following: + +```bash +Usage: [options] + +Meta cli options: +--help prints this manual +--config-path sets the config file location +--config-attachDefs prints the defaults to the config file +--config-attachDesc prints description to the config file +--config-rewrite-cli updates the config file with the options from cli switches +--config-rewrite-env updates the config file with the options from environmental variables +--config-string-enum enums are stored as string in the config file (instead of numbers) +--config-save-if-not-exist creates config file if not exist +--config-save-and-exist creates config file and terminates + + can be configured through the configuration file, cli switches and environmental variables. +All settings are case sensitive. +Example for setting config MyConf through cli: ' --MyConf=5' +and through env variable: 'SET MyConf=5' . + +Default values can be also overwritten by prefixing the options with 'default-', + like ' --default-MyConf=5' and 'SET default-MyConf=5' + +App CLI options: + --Server-sessionSecret (default: []) + --Server-port (default: 80) + --Server-host (default: '0.0.0.0') + --Server-Media-folder Images are loaded from this folder (read permission required) (default: 'demo/images') + --Server-Media-tempFolder Thumbnails, coverted photos, videos will be stored here (write permission required) (default: 'demo/tmp') + --Server-Media-photoProcessingLibrary (default: 'sharp') + --Server-Media-Video-transcoding-bitRate (default: 5242880) + --Server-Media-Video-transcoding-resolution (default: 720) + --Server-Media-Video-transcoding-fps (default: 25) + --Server-Media-Video-transcoding-codec (default: 'libx264') + --Server-Media-Video-transcoding-format (default: 'mp4') + --Server-Media-Photo-Converting-onTheFly Converts photos on the fly, when they are requested. (default: true) + --Server-Media-Photo-Converting-resolution (default: 1080) + --Server-Media-Thumbnail-qualityPriority if true, photos will have better quality. (default: true) + --Server-Media-Thumbnail-personFaceMargin (default: 0.6) + --Server-Threading-enabled App can run on multiple thread (default: true) + --Server-Threading-thumbnailThreads Number of threads that are used to generate thumbnails. If 0, number of 'CPU cores -1' threads will be used. (default: 0) + --Server-Database-type (default: 'sqlite') + --Server-Database-dbFolder (default: 'db') + --Server-Database-mysql-host (default: '') + --Server-Database-mysql-database (default: '') + --Server-Database-mysql-username (default: '') + --Server-Database-mysql-password (default: '') + --Server-Sharing-updateTimeout (default: 300000) + --Server-sessionTimeout unit: ms (default: 604800000) + --Server-Indexing-folderPreviewSize (default: 2) + --Server-Indexing-cachedFolderTimeout (default: 3600000) + --Server-Indexing-reIndexingSensitivity (default: 'low') + --Server-Indexing-excludeFolderList If an entry starts with '/' it is treated as an absolute path. If it doesn't start with '/' but contains a '/', the path is relative to the image directory. If it doesn't contain a '/', any folder with this name will be excluded. (default: []) + --Server-Indexing-excludeFileList Any folder that contains a file with this name will be excluded from indexing. (default: []) + --Server-photoMetadataSize only this many bites will be loaded when scanning photo for metadata (default: 524288) + --Server-Duplicates-listingLimit (default: 1000) + --Server-Log-level (default: 'info') + --Server-Log-sqlLevel (default: 'error') + --Server-Jobs-maxSavedProgress Job history size (default: 10) + --Server-Jobs-scheduled (default: [{"name":"Indexing","jobName":"Indexing","config":{},"allowParallelRun":false,"trigger":{"type":1}},{"name":"Thumbnail Generation","jobName":"Thumbnail Generation","config":{"sizes":[240]},"allowParallelRun":false,"trigger":{"type":4,"afterScheduleName":"Indexing"}},{"name":"Photo Converting","jobName":"Photo Converting","config":{},"allowParallelRun":false,"trigger":{"type":4,"afterScheduleName":"Thumbnail Generation"}},{"name":"Video Converting","jobName":"Video Converting","config":{},"allowParallelRun":false,"trigger":{"type":4,"afterScheduleName":"Photo Converting"}},{"name":"Temp Folder Cleaning","jobName":"Temp Folder Cleaning","config":{},"allowParallelRun":false,"trigger":{"type":4,"afterScheduleName":"Photo Converting"}}]) + --Client-applicationTitle (default: 'PiGallery 2') + --Client-publicUrl (default: '') + --Client-urlBase (default: '') + --Client-Search-enabled (default: true) + --Client-Search-instantSearchEnabled (default: true) + --Client-Search-InstantSearchTimeout (default: 3000) + --Client-Search-instantSearchCacheTimeout (default: 3600000) + --Client-Search-searchCacheTimeout (default: 3600000) + --Client-Search-AutoComplete-enabled (default: true) + --Client-Search-AutoComplete-maxItemsPerCategory (default: 5) + --Client-Search-AutoComplete-cacheTimeout (default: 3600000) + --Client-Sharing-enabled (default: true) + --Client-Sharing-passwordProtected (default: true) + --Client-Map-enabled (default: true) + --Client-Map-useImageMarkers (default: true) + --Client-Map-mapProvider (default: 0) + --Client-Map-mapboxAccessToken (default: '') + --Client-Map-customLayers (default: [{"name":"street","url":""}]) + --Client-RandomPhoto-enabled (default: true) + --Client-Other-enableCache (default: true) + --Client-Other-enableOnScrollRendering (default: true) + --Client-Other-defaultPhotoSortingMethod (default: 'ascDate') + --Client-Other-enableOnScrollThumbnailPrioritising (default: true) + --Client-Other-NavBar-showItemCount (default: true) + --Client-Other-captionFirstNaming (default: false) + --Client-authenticationRequired (default: true) + --Client-unAuthenticatedUserRole (default: 'Admin') + --Client-Media-Thumbnail-iconSize (default: 45) + --Client-Media-Thumbnail-personThumbnailSize (default: 200) + --Client-Media-Thumbnail-thumbnailSizes (default: [240,480]) + --Client-Media-Video-enabled (default: true) + --Client-Media-Photo-Converting-enabled (default: true) + --Client-MetaFile-enabled (default: true) + --Client-Faces-enabled (default: true) + --Client-Faces-keywordsToPersons (default: true) + --Client-Faces-writeAccessMinRole (default: 'Admin') + +Environmental variables: + Server-sessionSecret (default: []) + Server-port (default: 80) + PORT same as Server-port + Server-host (default: '0.0.0.0') + Server-Media-folder Images are loaded from this folder (read permission required) (default: 'demo/images') + Server-Media-tempFolder Thumbnails, coverted photos, videos will be stored here (write permission required) (default: 'demo/tmp') + Server-Media-photoProcessingLibrary (default: 'sharp') + Server-Media-Video-transcoding-bitRate (default: 5242880) + Server-Media-Video-transcoding-resolution (default: 720) + Server-Media-Video-transcoding-fps (default: 25) + Server-Media-Video-transcoding-codec (default: 'libx264') + Server-Media-Video-transcoding-format (default: 'mp4') + Server-Media-Photo-Converting-onTheFly Converts photos on the fly, when they are requested. (default: true) + Server-Media-Photo-Converting-resolution (default: 1080) + Server-Media-Thumbnail-qualityPriority if true, photos will have better quality. (default: true) + Server-Media-Thumbnail-personFaceMargin (default: 0.6) + Server-Threading-enabled App can run on multiple thread (default: true) + Server-Threading-thumbnailThreads Number of threads that are used to generate thumbnails. If 0, number of 'CPU cores -1' threads will be used. (default: 0) + Server-Database-type (default: 'sqlite') + Server-Database-dbFolder (default: 'db') + Server-Database-mysql-host (default: '') + MYSQL_HOST same as Server-Database-mysql-host + Server-Database-mysql-database (default: '') + MYSQL_PASSWORD same as Server-Database-mysql-database + Server-Database-mysql-username (default: '') + MYSQL_USERNAME same as Server-Database-mysql-username + Server-Database-mysql-password (default: '') + MYSQL_DATABASE same as Server-Database-mysql-password + Server-Sharing-updateTimeout (default: 300000) + Server-sessionTimeout unit: ms (default: 604800000) + Server-Indexing-folderPreviewSize (default: 2) + Server-Indexing-cachedFolderTimeout (default: 3600000) + Server-Indexing-reIndexingSensitivity (default: 'low') + Server-Indexing-excludeFolderList If an entry starts with '/' it is treated as an absolute path. If it doesn't start with '/' but contains a '/', the path is relative to the image directory. If it doesn't contain a '/', any folder with this name will be excluded. (default: []) + Server-Indexing-excludeFileList Any folder that contains a file with this name will be excluded from indexing. (default: []) + Server-photoMetadataSize only this many bites will be loaded when scanning photo for metadata (default: 524288) + Server-Duplicates-listingLimit (default: 1000) + Server-Log-level (default: 'info') + Server-Log-sqlLevel (default: 'error') + Server-Jobs-maxSavedProgress Job history size (default: 10) + Server-Jobs-scheduled (default: [{"name":"Indexing","jobName":"Indexing","config":{},"allowParallelRun":false,"trigger":{"type":1}},{"name":"Thumbnail Generation","jobName":"Thumbnail Generation","config":{"sizes":[240]},"allowParallelRun":false,"trigger":{"type":4,"afterScheduleName":"Indexing"}},{"name":"Photo Converting","jobName":"Photo Converting","config":{},"allowParallelRun":false,"trigger":{"type":4,"afterScheduleName":"Thumbnail Generation"}},{"name":"Video Converting","jobName":"Video Converting","config":{},"allowParallelRun":false,"trigger":{"type":4,"afterScheduleName":"Photo Converting"}},{"name":"Temp Folder Cleaning","jobName":"Temp Folder Cleaning","config":{},"allowParallelRun":false,"trigger":{"type":4,"afterScheduleName":"Photo Converting"}}]) + Client-applicationTitle (default: 'PiGallery 2') + Client-publicUrl (default: '') + Client-urlBase (default: '') + Client-Search-enabled (default: true) + Client-Search-instantSearchEnabled (default: true) + Client-Search-InstantSearchTimeout (default: 3000) + Client-Search-instantSearchCacheTimeout (default: 3600000) + Client-Search-searchCacheTimeout (default: 3600000) + Client-Search-AutoComplete-enabled (default: true) + Client-Search-AutoComplete-maxItemsPerCategory (default: 5) + Client-Search-AutoComplete-cacheTimeout (default: 3600000) + Client-Sharing-enabled (default: true) + Client-Sharing-passwordProtected (default: true) + Client-Map-enabled (default: true) + Client-Map-useImageMarkers (default: true) + Client-Map-mapProvider (default: 0) + Client-Map-mapboxAccessToken (default: '') + Client-Map-customLayers (default: [{"name":"street","url":""}]) + Client-RandomPhoto-enabled (default: true) + Client-Other-enableCache (default: true) + Client-Other-enableOnScrollRendering (default: true) + Client-Other-defaultPhotoSortingMethod (default: 'ascDate') + Client-Other-enableOnScrollThumbnailPrioritising (default: true) + Client-Other-NavBar-showItemCount (default: true) + Client-Other-captionFirstNaming (default: false) + Client-authenticationRequired (default: true) + Client-unAuthenticatedUserRole (default: 'Admin') + Client-Media-Thumbnail-iconSize (default: 45) + Client-Media-Thumbnail-personThumbnailSize (default: 200) + Client-Media-Thumbnail-thumbnailSizes (default: [240,480]) + Client-Media-Video-enabled (default: true) + Client-Media-Photo-Converting-enabled (default: true) + Client-MetaFile-enabled (default: true) + Client-Faces-enabled (default: true) + Client-Faces-keywordsToPersons (default: true) + Client-Faces-writeAccessMinRole (default: 'Admin') +``` \ No newline at end of file diff --git a/benchmark/Benchmarks.ts b/benchmark/Benchmarks.ts index 46359d1..040ca24 100644 --- a/benchmark/Benchmarks.ts +++ b/benchmark/Benchmarks.ts @@ -10,7 +10,7 @@ import {SearchTypes} from '../src/common/entities/AutoCompleteItem'; import {Utils} from '../src/common/Utils'; import {GalleryManager} from '../src/backend/model/database/sql/GalleryManager'; import {DirectoryDTO} from '../src/common/entities/DirectoryDTO'; -import {ServerConfig} from '../src/common/config/private/IPrivateConfig'; +import {ServerConfig} from '../src/common/config/private/PrivateConfig'; const rimrafPR = util.promisify(rimraf); diff --git a/gulpfile.ts b/gulpfile.ts index 49d27fd..5507696 100644 --- a/gulpfile.ts +++ b/gulpfile.ts @@ -10,6 +10,8 @@ import * as child_process from 'child_process'; // @ts-ignore import * as jeditor from 'gulp-json-editor'; import {XLIFF} from 'xlf-google-translate'; +import {Config} from './src/common/config/private/Config'; +import {ConfigClassBuilder} from 'typeconfig/src/decorators/builders/ConfigClassBuilder'; const execPr = util.promisify(child_process.exec); @@ -342,6 +344,14 @@ gulp.task('add-translation-only', (cb) => { translate([lng], cb); }); +gulp.task('generate-man', async (cb) => { + let txt = '# Pigallery 2 man page\n'; + txt += 'pigallery2 uses [typeconfig](https://github.com/bpatrik/typeconfig) for configuration\n\n'; + txt += '`npm start -- --help` prints the following:\n\n'; + txt += '```bash\n' + ConfigClassBuilder.attachPrivateInterface(Config).__printMan() + '```'; + await fsp.writeFile('MANPAGE.md', txt); + cb(); +}); gulp.task('add-translation', gulp.series('extract-locale', 'add-translation-only')); diff --git a/package-lock.json b/package-lock.json index 3ffc804..d018a89 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18721,9 +18721,9 @@ } }, "typeconfig": { - "version": "1.0.8-d", - "resolved": "https://registry.npmjs.org/typeconfig/-/typeconfig-1.0.8-d.tgz", - "integrity": "sha512-WmuJEs/ZcSELWjRUZCN26wYZ5hZiT2jQ3CYrWjMyv+J7o245DEM/WJ/52ThNIdLaWpdvPW6uHMZMGe3AiHkZaA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/typeconfig/-/typeconfig-2.0.0.tgz", + "integrity": "sha512-IwZH+P8J4qhyrOfKzJZJx6raEkaBjjZIiE+rzrWgdOhqVhO5Cv+pkOQdNo+z/Wq5wER5YWeDNX7Wdbqv0jt6IA==", "requires": { "optimist": "0.6.1" } diff --git a/package.json b/package.json index 77daf38..31f85da 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,8 @@ "run-dev": "ng build --aot --watch --output-path=./dist --i18n-locale en --i18n-file src/frontend/translate/messages.en.xlf --i18n-missing-translation warning", "build-stats": "ng build --aot --prod --stats-json --output-path=./dist --i18n-locale en --i18n-file src/frontend/translate/messages.en.xlf --i18n-missing-translation warning", "merge-new-translation": "gulp merge-new-translation", - "add-translation": "gulp add-translation" + "add-translation": "gulp add-translation", + "generate-man": "gulp generate-man" }, "repository": { "type": "git", @@ -46,7 +47,7 @@ "sqlite3": "4.1.1", "ts-exif-parser": "0.1.4", "ts-node-iptc": "1.0.11", - "typeconfig": "1.0.8-d", + "typeconfig": "2.0.0", "typeorm": "0.2.21", "winston": "2.4.4" }, diff --git a/src/backend/Logger.ts b/src/backend/Logger.ts index f898b73..74d1450 100644 --- a/src/backend/Logger.ts +++ b/src/backend/Logger.ts @@ -1,6 +1,6 @@ import * as winston from 'winston'; import {Config} from '../common/config/private/Config'; -import {ServerConfig} from '../common/config/private/IPrivateConfig'; +import {ServerConfig} from '../common/config/private/PrivateConfig'; export const winstonSettings = { transports: [ diff --git a/src/backend/middlewares/RenderingMWs.ts b/src/backend/middlewares/RenderingMWs.ts index 2d886aa..cbe1718 100644 --- a/src/backend/middlewares/RenderingMWs.ts +++ b/src/backend/middlewares/RenderingMWs.ts @@ -1,8 +1,7 @@ import {NextFunction, Request, Response} from 'express'; import {ErrorCodes, ErrorDTO} from '../../common/entities/Error'; import {Message} from '../../common/entities/Message'; -import {Config} from '../../common/config/private/Config'; -import {ConfigClass} from '../../common/config/private/ConfigClass'; +import {Config, PrivateConfigClass} from '../../common/config/private/Config'; import {UserDTO, UserRoles} from '../../common/entities/UserDTO'; import {NotificationManager} from '../model/NotifocationManager'; import {Logger} from '../Logger'; @@ -61,10 +60,10 @@ export class RenderingMWs { } - public static renderConfig(req: Request, res: Response, next: NextFunction) { - const originalConf = Config.original(); + public static async renderConfig(req: Request, res: Response, next: NextFunction) { + const originalConf = await Config.original(); originalConf.Server.sessionSecret = null; - const message = new Message(null, originalConf); + const message = new Message(null, originalConf.toJSON({attachDefaults: true})); res.json(message); } diff --git a/src/backend/middlewares/admin/AdminMWs.ts b/src/backend/middlewares/admin/AdminMWs.ts index 424a05c..a2fc04f 100644 --- a/src/backend/middlewares/admin/AdminMWs.ts +++ b/src/backend/middlewares/admin/AdminMWs.ts @@ -3,7 +3,7 @@ import {ErrorCodes, ErrorDTO} from '../../../common/entities/Error'; import {ObjectManagers} from '../../model/ObjectManagers'; import {Config} from '../../../common/config/private/Config'; import {ISQLGalleryManager} from '../../model/database/sql/IGalleryManager'; -import {ServerConfig} from '../../../common/config/private/IPrivateConfig'; +import {ServerConfig} from '../../../common/config/private/PrivateConfig'; export class AdminMWs { diff --git a/src/backend/middlewares/admin/SettingsMWs.ts b/src/backend/middlewares/admin/SettingsMWs.ts index b375c49..d50fffc 100644 --- a/src/backend/middlewares/admin/SettingsMWs.ts +++ b/src/backend/middlewares/admin/SettingsMWs.ts @@ -6,15 +6,15 @@ import {Logger} from '../../Logger'; import {SQLConnection} from '../../model/database/sql/SQLConnection'; import {Config} from '../../../common/config/private/Config'; import {ConfigDiagnostics} from '../../model/diagnostics/ConfigDiagnostics'; -import {ClientConfig} from '../../../common/config/public/ConfigClass'; import {BasicConfigDTO} from '../../../common/entities/settings/BasicConfigDTO'; import {OtherConfigDTO} from '../../../common/entities/settings/OtherConfigDTO'; import {ProjectPath} from '../../ProjectPath'; -import {ConfigClass} from '../../../common/config/private/ConfigClass'; -import {ServerConfig} from '../../../common/config/private/IPrivateConfig'; +import {ServerConfig} from '../../../common/config/private/PrivateConfig'; +import {ClientConfig} from '../../../common/config/public/ClientConfig'; const LOG_TAG = '[SettingsMWs]'; + export class SettingsMWs { public static async updateDatabaseSettings(req: Request, res: Response, next: NextFunction) { @@ -31,7 +31,7 @@ export class SettingsMWs { } Config.Server.Database = databaseSettings; // only updating explicitly set config (not saving config set by the diagnostics) - const original = Config.original(); + const original = await Config.original(); original.Server.Database = databaseSettings; if (databaseSettings.type === ServerConfig.DatabaseType.memory) { original.Client.Sharing.enabled = false; @@ -68,7 +68,7 @@ export class SettingsMWs { Config.Client.Map = req.body.settings; // only updating explicitly set config (not saving config set by the diagnostics) - const original = Config.original(); + const original = await Config.original(); original.Client.Map = req.body.settings; original.save(); await ConfigDiagnostics.runDiagnostics(); @@ -95,7 +95,7 @@ export class SettingsMWs { } = req.body.settings; - const original = Config.original(); + const original = await Config.original(); await ConfigDiagnostics.testClientVideoConfig(settings.client); await ConfigDiagnostics.testServerVideoConfig(settings.server, original); Config.Server.Media.Video = settings.server; @@ -122,7 +122,7 @@ export class SettingsMWs { } try { - const original = Config.original(); + const original = await Config.original(); await ConfigDiagnostics.testMetaFileConfig(req.body.settings, original); Config.Client.MetaFile = req.body.settings; @@ -149,7 +149,7 @@ export class SettingsMWs { try { // only updating explicitly set config (not saving config set by the diagnostics) - const original = Config.original(); + const original = await Config.original(); await ConfigDiagnostics.testSharingConfig(req.body.settings, original); Config.Client.Sharing = req.body.settings; @@ -174,7 +174,7 @@ export class SettingsMWs { try { // only updating explicitly set config (not saving config set by the diagnostics) - const original = Config.original(); + const original = await Config.original(); await ConfigDiagnostics.testRandomPhotoConfig(req.body.settings, original); Config.Client.RandomPhoto = req.body.settings; @@ -199,7 +199,7 @@ export class SettingsMWs { try { // only updating explicitly set config (not saving config set by the diagnostics) - const original = Config.original(); + const original = await Config.original(); await ConfigDiagnostics.testSearchConfig(req.body.settings, original); Config.Client.Search = req.body.settings; @@ -224,7 +224,7 @@ export class SettingsMWs { try { // only updating explicitly set config (not saving config set by the diagnostics) - const original = Config.original(); + const original = await Config.original(); await ConfigDiagnostics.testFacesConfig(req.body.settings, original); Config.Client.Faces = req.body.settings; @@ -250,7 +250,7 @@ export class SettingsMWs { try { Config.Client.authenticationRequired = req.body.settings; // only updating explicitly set config (not saving config set by the diagnostics) - const original = Config.original(); + const original = await Config.original(); original.Client.authenticationRequired = req.body.settings; if (original.Client.authenticationRequired === false) { original.Client.Sharing.enabled = false; @@ -284,7 +284,7 @@ export class SettingsMWs { Config.Server.Media.Thumbnail = settings.server; Config.Client.Media.Thumbnail = settings.client; // only updating explicitly set config (not saving config set by the diagnostics) - const original = Config.original(); + const original = await Config.original(); original.Server.Media.Thumbnail = settings.server; original.Client.Media.Thumbnail = settings.client; original.save(); @@ -320,7 +320,7 @@ export class SettingsMWs { Config.Server.Media.Photo = settings.server; Config.Client.Media.Photo = settings.client; // only updating explicitly set config (not saving config set by the diagnostics) - const original = Config.original(); + const original = await Config.original(); original.Server.Media.photoProcessingLibrary = settings.photoProcessingLibrary; original.Server.Media.Photo = settings.server; original.Client.Media.Photo = settings.client; @@ -353,7 +353,7 @@ export class SettingsMWs { Config.Client.urlBase = settings.urlBase; Config.Client.applicationTitle = settings.applicationTitle; // only updating explicitly set config (not saving config set by the diagnostics) - const original = Config.original(); + const original = await Config.original(); original.Server.port = settings.port; original.Server.host = settings.host; original.Server.Media.folder = settings.imagesFolder; @@ -389,7 +389,7 @@ export class SettingsMWs { Config.Client.Other.NavBar.showItemCount = settings.Client.NavBar.showItemCount; // only updating explicitly set config (not saving config set by the diagnostics) - const original: ConfigClass = Config.original(); + const original = await Config.original(); original.Client.Other.enableCache = settings.Client.enableCache; original.Client.Other.captionFirstNaming = settings.Client.captionFirstNaming; original.Client.Other.enableOnScrollRendering = settings.Client.enableOnScrollRendering; @@ -398,7 +398,7 @@ export class SettingsMWs { original.Client.Other.NavBar.showItemCount = settings.Client.NavBar.showItemCount; original.Server.Threading.enabled = settings.Server.enabled; original.Server.Threading.thumbnailThreads = settings.Server.thumbnailThreads; - original.save(); + await original.save(); await ConfigDiagnostics.runDiagnostics(); Logger.info(LOG_TAG, 'new config:'); Logger.info(LOG_TAG, JSON.stringify(Config, null, '\t')); @@ -421,7 +421,7 @@ export class SettingsMWs { Config.Server.Indexing = settings; // only updating explicitly set config (not saving config set by the diagnostics) - const original = Config.original(); + const original = await Config.original(); original.Server.Indexing = settings; original.save(); await ConfigDiagnostics.runDiagnostics(); @@ -445,7 +445,7 @@ export class SettingsMWs { // only updating explicitly set config (not saving config set by the diagnostics) const settings: ServerConfig.JobConfig = req.body.settings; - const original = Config.original(); + const original = await Config.original(); await ConfigDiagnostics.testTasksConfig(settings, original); Config.Server.Jobs = settings; diff --git a/src/backend/model/database/memory/GalleryManager.ts b/src/backend/model/database/memory/GalleryManager.ts index 8b42b53..8ed1473 100644 --- a/src/backend/model/database/memory/GalleryManager.ts +++ b/src/backend/model/database/memory/GalleryManager.ts @@ -7,7 +7,7 @@ import {ProjectPath} from '../../../ProjectPath'; import {Config} from '../../../../common/config/private/Config'; import {PhotoDTO} from '../../../../common/entities/PhotoDTO'; import {DiskMangerWorker} from '../../threading/DiskMangerWorker'; -import {ServerConfig} from '../../../../common/config/private/IPrivateConfig'; +import {ServerConfig} from '../../../../common/config/private/PrivateConfig'; export class GalleryManager implements IGalleryManager { diff --git a/src/backend/model/database/sql/GalleryManager.ts b/src/backend/model/database/sql/GalleryManager.ts index e37cfa3..465cff3 100644 --- a/src/backend/model/database/sql/GalleryManager.ts +++ b/src/backend/model/database/sql/GalleryManager.ts @@ -18,7 +18,7 @@ import {Logger} from '../../../Logger'; import {FaceRegionEntry} from './enitites/FaceRegionEntry'; import {ObjectManagers} from '../../ObjectManagers'; import {DuplicatesDTO} from '../../../../common/entities/DuplicatesDTO'; -import {ServerConfig} from '../../../../common/config/private/IPrivateConfig'; +import {ServerConfig} from '../../../../common/config/private/PrivateConfig'; const LOG_TAG = '[GalleryManager]'; diff --git a/src/backend/model/database/sql/SQLConnection.ts b/src/backend/model/database/sql/SQLConnection.ts index 1ff7136..b3c9bd8 100644 --- a/src/backend/model/database/sql/SQLConnection.ts +++ b/src/backend/model/database/sql/SQLConnection.ts @@ -18,7 +18,7 @@ import {FaceRegionEntry} from './enitites/FaceRegionEntry'; import {PersonEntry} from './enitites/PersonEntry'; import {Utils} from '../../../../common/Utils'; import * as path from 'path'; -import {ServerConfig} from '../../../../common/config/private/IPrivateConfig'; +import {ServerConfig} from '../../../../common/config/private/PrivateConfig'; export class SQLConnection { @@ -110,6 +110,10 @@ export class SQLConnection { } } + public static getSQLiteDB(config: ServerConfig.DataBaseConfig) { + return path.join(ProjectPath.getAbsolutePath(config.dbFolder), 'sqlite.db'); + } + private static async createConnection(options: ConnectionOptions) { if (options.type === 'sqlite') { return await createConnection(options); @@ -185,8 +189,4 @@ export class SQLConnection { return driver; } - public static getSQLiteDB(config: ServerConfig.DataBaseConfig) { - return path.join(ProjectPath.getAbsolutePath(config.dbFolder), 'sqlite.db'); - } - } diff --git a/src/backend/model/database/sql/enitites/EntityUtils.ts b/src/backend/model/database/sql/enitites/EntityUtils.ts index 00ea653..8862aaa 100644 --- a/src/backend/model/database/sql/enitites/EntityUtils.ts +++ b/src/backend/model/database/sql/enitites/EntityUtils.ts @@ -1,6 +1,6 @@ import {Config} from '../../../../../common/config/private/Config'; -import {ServerConfig} from '../../../../../common/config/private/IPrivateConfig'; import {ColumnOptions} from 'typeorm/decorator/options/ColumnOptions'; +import {ServerConfig} from '../../../../../common/config/private/PrivateConfig'; export class ColumnCharsetCS implements ColumnOptions { diff --git a/src/backend/model/diagnostics/ConfigDiagnostics.ts b/src/backend/model/diagnostics/ConfigDiagnostics.ts index ec58ed3..4004895 100644 --- a/src/backend/model/diagnostics/ConfigDiagnostics.ts +++ b/src/backend/model/diagnostics/ConfigDiagnostics.ts @@ -1,12 +1,13 @@ -import {Config} from '../../../common/config/private/Config'; +import {Config, PrivateConfigClass} from '../../../common/config/private/Config'; import {Logger} from '../../Logger'; import {NotificationManager} from '../NotifocationManager'; import {ProjectPath} from '../../ProjectPath'; import {SQLConnection} from '../database/sql/SQLConnection'; import * as fs from 'fs'; -import {ClientConfig} from '../../../common/config/public/ConfigClass'; import {FFmpegFactory} from '../FFmpegFactory'; -import {IPrivateConfig, ServerConfig} from '../../../common/config/private/IPrivateConfig'; +import {ClientConfig} from '../../../common/config/public/ClientConfig'; +import {ServerConfig} from '../../../common/config/private/PrivateConfig'; +import MapLayers = ClientConfig.MapLayers; const LOG_TAG = '[ConfigDiagnostics]'; @@ -39,7 +40,7 @@ export class ConfigDiagnostics { } - static async testMetaFileConfig(metaFileConfig: ClientConfig.MetaFileConfig, config: IPrivateConfig) { + static async testMetaFileConfig(metaFileConfig: ClientConfig.MetaFileConfig, config: PrivateConfig) { if (metaFileConfig.enabled === true && config.Client.Map.enabled === false) { throw new Error('*.gpx meta files are not supported without MAP'); @@ -72,7 +73,7 @@ export class ConfigDiagnostics { }); } - static async testServerVideoConfig(videoConfig: ServerConfig.VideoConfig, config: IPrivateConfig) { + static async testServerVideoConfig(videoConfig: ServerConfig.VideoConfig, config: PrivateConfig) { if (config.Client.Media.Video.enabled === true) { if (videoConfig.transcoding.fps <= 0) { throw new Error('fps should be grater than 0'); @@ -157,11 +158,11 @@ export class ConfigDiagnostics { } - static async testTasksConfig(task: ServerConfig.JobConfig, config: IPrivateConfig) { + static async testTasksConfig(task: ServerConfig.JobConfig, config: PrivateConfig) { } - static async testFacesConfig(faces: ClientConfig.FacesConfig, config: IPrivateConfig) { + static async testFacesConfig(faces: ClientConfig.FacesConfig, config: PrivateConfig) { if (faces.enabled === true) { if (config.Server.Database.type === ServerConfig.DatabaseType.memory) { throw new Error('Memory Database do not support faces'); @@ -172,7 +173,7 @@ export class ConfigDiagnostics { } } - static async testSearchConfig(search: ClientConfig.SearchConfig, config: IPrivateConfig) { + static async testSearchConfig(search: ClientConfig.SearchConfig, config: PrivateConfig) { if (search.enabled === true && config.Server.Database.type === ServerConfig.DatabaseType.memory) { throw new Error('Memory Database do not support searching'); @@ -180,7 +181,7 @@ export class ConfigDiagnostics { } - static async testSharingConfig(sharing: ClientConfig.SharingConfig, config: IPrivateConfig) { + static async testSharingConfig(sharing: ClientConfig.SharingConfig, config: PrivateConfig) { if (sharing.enabled === true && config.Server.Database.type === ServerConfig.DatabaseType.memory) { throw new Error('Memory Database do not support sharing'); @@ -191,7 +192,7 @@ export class ConfigDiagnostics { } } - static async testRandomPhotoConfig(sharing: ClientConfig.RandomPhotoConfig, config: IPrivateConfig) { + static async testRandomPhotoConfig(sharing: ClientConfig.RandomPhotoConfig, config: PrivateConfig) { if (sharing.enabled === true && config.Server.Database.type === ServerConfig.DatabaseType.memory) { throw new Error('Memory Database do not support random photo'); @@ -212,7 +213,7 @@ export class ConfigDiagnostics { throw new Error('Custom maps need at least one valid layer'); } if (map.mapProvider === ClientConfig.MapProviders.Custom) { - map.customLayers.forEach(l => { + map.customLayers.forEach((l: MapLayers) => { if (!l.url || l.url.length === 0) { throw new Error('Custom maps url need to be a valid layer'); } @@ -231,7 +232,7 @@ export class ConfigDiagnostics { Logger.warn(LOG_TAG, '[SQL error]', err.toString()); Logger.warn(LOG_TAG, 'Error during initializing SQL falling back temporally to memory DB'); NotificationManager.warning('Error during initializing SQL falling back temporally to memory DB', err.toString()); - Config.setDatabaseType(ServerConfig.DatabaseType.memory); + Config.Server.Database.type = ServerConfig.DatabaseType.memory; } } diff --git a/src/backend/model/fileprocessing/PhotoProcessing.ts b/src/backend/model/fileprocessing/PhotoProcessing.ts index 1bd40e2..76fe1fb 100644 --- a/src/backend/model/fileprocessing/PhotoProcessing.ts +++ b/src/backend/model/fileprocessing/PhotoProcessing.ts @@ -7,9 +7,9 @@ import {Config} from '../../../common/config/private/Config'; import {ThumbnailTH} from '../threading/ThreadPool'; import {PhotoWorker, RendererInput, ThumbnailSourceType} from '../threading/PhotoWorker'; import {ITaskExecuter, TaskExecuter} from '../threading/TaskExecuter'; -import {ServerConfig} from '../../../common/config/private/IPrivateConfig'; import {FaceRegion, PhotoDTO} from '../../../common/entities/PhotoDTO'; import {SupportedFormats} from '../../../common/SupportedFormats'; +import {ServerConfig} from '../../../common/config/private/PrivateConfig'; export class PhotoProcessing { diff --git a/src/backend/model/jobs/jobs/DBResetJob.ts b/src/backend/model/jobs/jobs/DBResetJob.ts index 9d05f36..b3d493e 100644 --- a/src/backend/model/jobs/jobs/DBResetJob.ts +++ b/src/backend/model/jobs/jobs/DBResetJob.ts @@ -2,7 +2,7 @@ import {ObjectManagers} from '../../ObjectManagers'; import {Config} from '../../../../common/config/private/Config'; import {ConfigTemplateEntry, DefaultsJobs} from '../../../../common/entities/job/JobDTO'; import {Job} from './Job'; -import {ServerConfig} from '../../../../common/config/private/IPrivateConfig'; +import {ServerConfig} from '../../../../common/config/private/PrivateConfig'; export class DBRestJob extends Job { diff --git a/src/backend/model/jobs/jobs/FileJob.ts b/src/backend/model/jobs/jobs/FileJob.ts index ed23d30..f96902e 100644 --- a/src/backend/model/jobs/jobs/FileJob.ts +++ b/src/backend/model/jobs/jobs/FileJob.ts @@ -5,7 +5,6 @@ import {DiskManager} from '../../DiskManger'; import {DiskMangerWorker} from '../../threading/DiskMangerWorker'; import {Logger} from '../../../Logger'; import {Config} from '../../../../common/config/private/Config'; -import {ServerConfig} from '../../../../common/config/private/IPrivateConfig'; import {FileDTO} from '../../../../common/entities/FileDTO'; import {SQLConnection} from '../../database/sql/SQLConnection'; import {MediaEntity} from '../../database/sql/enitites/MediaEntity'; @@ -13,6 +12,7 @@ import {PhotoEntity} from '../../database/sql/enitites/PhotoEntity'; import {VideoEntity} from '../../database/sql/enitites/VideoEntity'; import {backendTexts} from '../../../../common/BackendTexts'; import {ProjectPath} from '../../../ProjectPath'; +import {ServerConfig} from '../../../../common/config/private/PrivateConfig'; declare var global: NodeJS.Global; diff --git a/src/backend/model/jobs/jobs/IndexingJob.ts b/src/backend/model/jobs/jobs/IndexingJob.ts index 7bdc54c..b170dbe 100644 --- a/src/backend/model/jobs/jobs/IndexingJob.ts +++ b/src/backend/model/jobs/jobs/IndexingJob.ts @@ -3,8 +3,8 @@ import * as path from 'path'; import {Config} from '../../../../common/config/private/Config'; import {Job} from './Job'; import {ConfigTemplateEntry, DefaultsJobs} from '../../../../common/entities/job/JobDTO'; -import {ServerConfig} from '../../../../common/config/private/IPrivateConfig'; import {JobProgressStates} from '../../../../common/entities/job/JobProgressDTO'; +import {ServerConfig} from '../../../../common/config/private/PrivateConfig'; export class IndexingJob extends Job { diff --git a/src/backend/model/threading/PhotoWorker.ts b/src/backend/model/threading/PhotoWorker.ts index 81fd9b8..76550d8 100644 --- a/src/backend/model/threading/PhotoWorker.ts +++ b/src/backend/model/threading/PhotoWorker.ts @@ -3,7 +3,7 @@ import {Dimensions, State} from 'gm'; import {Logger} from '../../Logger'; import {FfmpegCommand, FfprobeData} from 'fluent-ffmpeg'; import {FFmpegFactory} from '../FFmpegFactory'; -import {ServerConfig} from '../../../common/config/private/IPrivateConfig'; +import {ServerConfig} from '../../../common/config/private/PrivateConfig'; export class PhotoWorker { diff --git a/src/backend/model/threading/VideoConverterWorker.ts b/src/backend/model/threading/VideoConverterWorker.ts index d1265e6..bb5315d 100644 --- a/src/backend/model/threading/VideoConverterWorker.ts +++ b/src/backend/model/threading/VideoConverterWorker.ts @@ -2,7 +2,7 @@ import {Logger} from '../../Logger'; import {promises as fsp} from 'fs'; import {FfmpegCommand} from 'fluent-ffmpeg'; import {FFmpegFactory} from '../FFmpegFactory'; -import {ServerConfig} from '../../../common/config/private/IPrivateConfig'; +import {ServerConfig} from '../../../common/config/private/PrivateConfig'; export interface VideoConverterInput { diff --git a/src/backend/model/threading/Worker.ts b/src/backend/model/threading/Worker.ts index 79de401..271e575 100644 --- a/src/backend/model/threading/Worker.ts +++ b/src/backend/model/threading/Worker.ts @@ -3,7 +3,7 @@ import {Logger} from '../../Logger'; import {PhotoWorker, RendererInput} from './PhotoWorker'; import {DirectoryDTO} from '../../../common/entities/DirectoryDTO'; import {Utils} from '../../../common/Utils'; -import {ServerConfig} from '../../../common/config/private/IPrivateConfig'; +import {ServerConfig} from '../../../common/config/private/PrivateConfig'; declare var process: NodeJS.Process; declare var global: NodeJS.Global; diff --git a/src/backend/routes/PublicRouter.ts b/src/backend/routes/PublicRouter.ts index de7f094..f889a36 100644 --- a/src/backend/routes/PublicRouter.ts +++ b/src/backend/routes/PublicRouter.ts @@ -82,7 +82,7 @@ export class PublicRouter { res.tpl.user.csrfToken = req.csrfToken(); } } - res.tpl.clientConfig = Config.Client; + res.tpl.clientConfig = {Client: Config.Client}; return next(); }); diff --git a/src/backend/server.ts b/src/backend/server.ts index d34d652..2c54c48 100644 --- a/src/backend/server.ts +++ b/src/backend/server.ts @@ -1,3 +1,4 @@ +import {Config} from '../common/config/private/Config'; import * as _express from 'express'; import {Request} from 'express'; import * as _bodyParser from 'body-parser'; @@ -8,19 +9,18 @@ import {Server as HttpServer} from 'http'; import * as locale from 'locale'; import {ObjectManagers} from './model/ObjectManagers'; import {Logger} from './Logger'; -import {Config} from '../common/config/private/Config'; import {LoggerRouter} from './routes/LoggerRouter'; import {DiskManager} from './model/DiskManger'; import {ConfigDiagnostics} from './model/diagnostics/ConfigDiagnostics'; import {Localizations} from './model/Localizations'; import {CookieNames} from '../common/CookieNames'; import {Router} from './routes/Router'; -import {ServerConfig} from '../common/config/private/IPrivateConfig'; import {PhotoProcessing} from './model/fileprocessing/PhotoProcessing'; import * as _csrf from 'csurf'; import * as unless from 'express-unless'; import {Event} from '../common/event/Event'; import {QueryParams} from '../common/QueryParams'; +import {ServerConfig} from '../common/config/private/PrivateConfig'; const _session = require('cookie-session'); diff --git a/src/common/config/private/Config.ts b/src/common/config/private/Config.ts index 0b1e6e2..ea493f6 100644 --- a/src/common/config/private/Config.ts +++ b/src/common/config/private/Config.ts @@ -1,5 +1,55 @@ -import {ConfigClass} from './ConfigClass'; +import {IPrivateConfig, ServerConfig} from './PrivateConfig'; +import {ClientConfig} from '../public/ClientConfig'; +import * as crypto from 'crypto'; +import * as path from 'path'; +import {ConfigClass} from 'typeconfig/src/decorators/class/ConfigClass'; +import {ConfigProperty} from 'typeconfig/src/decorators/property/ConfigPropoerty'; +import {IConfigClass} from 'typeconfig/src/decorators/class/IConfigClass'; +import {ConfigClassBuilder} from 'typeconfig/src/decorators/builders/ConfigClassBuilder'; -export let Config = new ConfigClass(); -Config.load(); +@ConfigClass({ + configPath: path.join(__dirname, './../../../../config.json'), + saveIfNotExist: true, + attachDescription: true, + enumsAsString: true, + cli: { + enable: { + configPath: true, + attachDefaults: true, + attachDescription: true, + rewriteCLIConfig: true, + rewriteENVConfig: true, + enumsAsString: true, + saveIfNotExist: true, + exitOnConfig: true + }, + defaults: { + enabled: true + } + } +}) +export class PrivateConfigClass implements IPrivateConfig { + @ConfigProperty() + Server: ServerConfig.Config = new ServerConfig.Config(); + @ConfigProperty() + Client: ClientConfig.Config = new ClientConfig.Config(); + + constructor() { + if (!this.Server.sessionSecret || this.Server.sessionSecret.length === 0) { + this.Server.sessionSecret = [crypto.randomBytes(256).toString('hex'), + crypto.randomBytes(256).toString('hex'), + crypto.randomBytes(256).toString('hex')]; + } + } + + async original(): Promise { + const pc = ConfigClassBuilder.attachInterface(new PrivateConfigClass()); + await pc.load(); + return pc; + } + +} + +export const Config = ConfigClassBuilder.attachInterface(new PrivateConfigClass()); +Config.loadSync(); diff --git a/src/common/config/private/ConfigClass.ts b/src/common/config/private/ConfigClass.ts deleted file mode 100644 index d63dc8c..0000000 --- a/src/common/config/private/ConfigClass.ts +++ /dev/null @@ -1,98 +0,0 @@ -import {IPrivateConfig, ServerConfig} from './IPrivateConfig'; -import * as path from 'path'; -import * as crypto from 'crypto'; -import {ConfigLoader} from 'typeconfig'; -import {Utils} from '../../Utils'; -import {UserRoles} from '../../entities/UserDTO'; -import {PrivateConfigDefaultsClass} from './PrivateConfigDefaultsClass'; - -declare var process: NodeJS.Process; - -let CONFIG_PATH = path.join(__dirname, './../../../../config.json'); -// TODO: refactor this -let configPath = process.argv.find(s => s.startsWith('--config-path=')); -if (configPath) { - configPath = configPath.replace('--config-path=', ''); - if (path.isAbsolute(configPath)) { - CONFIG_PATH = configPath; - } else { - CONFIG_PATH = path.join(__dirname, './../../../../', configPath); - } - console.log('using config path:' + CONFIG_PATH); -} - -/** - * This configuration will be only at backend - */ -export class ConfigClass extends PrivateConfigDefaultsClass implements IPrivateConfig { - - - public setDatabaseType(type: ServerConfig.DatabaseType) { - this.Server.Database.type = type; - if (type === ServerConfig.DatabaseType.memory) { - this.Client.Search.enabled = false; - this.Client.Sharing.enabled = false; - } - } - - public load() { - this.addComment(); - ConfigLoader.loadBackendConfig(this, CONFIG_PATH, - [['PORT', 'Server-port'], - ['MYSQL_HOST', 'Server-Database-mysql-host'], - ['MYSQL_PASSWORD', 'Server-Database-mysql-password'], - ['MYSQL_USERNAME', 'Server-Database-mysql-username'], - ['MYSQL_DATABASE', 'Server-Database-mysql-database']], - process.argv.indexOf('--force-rewrite-config') !== -1); - this.removeComment(); - - if (process.argv.indexOf('--config-only') !== -1) { - console.log('started with \'--config-only\' flag. Saving config and exiting.'); - process.exit(); - } - - if (Utils.enumToArray(UserRoles).map(r => r.key).indexOf(this.Client.unAuthenticatedUserRole) === -1) { - throw new Error('Unknown user role for Client.unAuthenticatedUserRole, found: ' + this.Client.unAuthenticatedUserRole); - } - if (Utils.enumToArray(ServerConfig.LogLevel).map(r => r.key).indexOf(this.Server.Log.level) === -1) { - throw new Error('Unknown Server.log.level, found: ' + this.Server.Log.level); - } - if (Utils.enumToArray(ServerConfig.SQLLogLevel).map(r => r.key).indexOf(this.Server.Log.sqlLevel) === -1) { - throw new Error('Unknown Server.log.level, found: ' + this.Server.Log.sqlLevel); - } - - if (Array.isArray(this.Server.sessionSecret) === false) { - // if not secret is set, generate one randomly - this.Server.sessionSecret = [crypto.randomBytes(256).toString('hex'), - crypto.randomBytes(256).toString('hex'), - crypto.randomBytes(256).toString('hex')]; - } - - } - - public save() { - try { - this.addComment(); - ConfigLoader.saveConfigFile(CONFIG_PATH, this); - this.removeComment(); - } catch (e) { - throw new Error('Error during saving config: ' + e.toString()); - } - } - - public original(): ConfigClass { - const cfg = new ConfigClass(); - cfg.load(); - return cfg; - } - - private addComment() { - (this)['__NOTE'] = 'NOTE: this config is not intended for manual edit, ' + - 'use the app UI instead as it has comments and descriptions.'; - } - - private removeComment() { - delete (this)['__NOTE']; - } -} - diff --git a/src/common/config/private/IPrivateConfig.ts b/src/common/config/private/IPrivateConfig.ts deleted file mode 100644 index c82ecfc..0000000 --- a/src/common/config/private/IPrivateConfig.ts +++ /dev/null @@ -1,137 +0,0 @@ -import {ClientConfig} from '../public/ConfigClass'; -import {JobScheduleDTO} from '../../entities/job/JobScheduleDTO'; - -export module ServerConfig { - export enum DatabaseType { - memory = 1, mysql = 2, sqlite = 3 - } - - export enum LogLevel { - error = 1, warn = 2, info = 3, verbose = 4, debug = 5, silly = 6 - } - - export enum SQLLogLevel { - none = 1, error = 2, all = 3 - } - - export enum PhotoProcessingLib { - sharp = 3, - Jimp = 1, - gm = 2, - } - - export interface MySQLConfig { - host: string; - database: string; - username: string; - password: string; - } - - /* - export interface SQLiteConfig { - } - - export interface MemoryConfig { - }*/ - - export interface DataBaseConfig { - type: DatabaseType; - dbFolder: string; - mysql?: MySQLConfig; - // sqlite?: SQLiteConfig; - // memory?: MemoryConfig; - } - - export interface ThumbnailConfig { - qualityPriority: boolean; - personFaceMargin: number; // in ration [0-1] - } - - export interface SharingConfig { - updateTimeout: number; - } - - export enum ReIndexingSensitivity { - low = 1, medium = 2, high = 3 - } - - export interface IndexingConfig { - folderPreviewSize: number; - cachedFolderTimeout: number; // Do not rescans the folder if seems ok - reIndexingSensitivity: ReIndexingSensitivity; - excludeFolderList: string[]; - excludeFileList: string[]; - } - - export interface ThreadingConfig { - enabled: boolean; - thumbnailThreads: number; - } - - export interface DuplicatesConfig { - listingLimit: number; // maximum number of duplicates to list - } - - export interface LogConfig { - level: LogLevel; - sqlLevel: SQLLogLevel; - } - - export interface JobConfig { - maxSavedProgress: number; - scheduled: JobScheduleDTO[]; - } - - export type codecType = 'libvpx-vp9' | 'libx264' | 'libvpx' | 'libx265'; - export type resolutionType = 240 | 360 | 480 | 720 | 1080 | 1440 | 2160 | 4320; - export type formatType = 'mp4' | 'webm'; - - export interface VideoConfig { - transcoding: { - bitRate: number, - resolution: resolutionType, - fps: number, - codec: codecType, - format: formatType - }; - } - - - export interface PhotoConfig { - Converting: { - onTheFly: boolean; - resolution: resolutionType - }; - } - - export interface MediaConfig { - folder: string; - tempFolder: string; - photoProcessingLibrary: PhotoProcessingLib; - Video: VideoConfig; - Photo: PhotoConfig; - Thumbnail: ThumbnailConfig; - } - - - export interface Config { - sessionSecret: string[]; - port: number; - host: string; - Media: MediaConfig; - Threading: ThreadingConfig; - Database: DataBaseConfig; - Sharing: SharingConfig; - sessionTimeout: number; // in ms - Indexing: IndexingConfig; - photoMetadataSize: number; // only this many bites will be loaded when scanning photo for metadata - Duplicates: DuplicatesConfig; - Log: LogConfig; - Jobs: JobConfig; - } -} - -export interface IPrivateConfig { - Server: ServerConfig.Config; - Client: ClientConfig.Config; -} diff --git a/src/common/config/private/PrivateConfig.ts b/src/common/config/private/PrivateConfig.ts new file mode 100644 index 0000000..d544aaa --- /dev/null +++ b/src/common/config/private/PrivateConfig.ts @@ -0,0 +1,325 @@ +/* tslint:disable:no-inferrable-types */ +import 'reflect-metadata'; +import {DefaultsJobs} from '../../entities/job/JobDTO'; +import {JobScheduleDTO, JobTrigger, JobTriggerType} from '../../entities/job/JobScheduleDTO'; +import {ClientConfig} from '../public/ClientConfig'; +import { SubConfigClass } from 'typeconfig/src/decorators/class/SubConfigClass'; +import { ConfigProperty } from 'typeconfig/src/decorators/property/ConfigPropoerty'; + +export module ServerConfig { + export enum DatabaseType { + memory = 1, mysql = 2, sqlite = 3 + } + + export enum LogLevel { + error = 1, warn = 2, info = 3, verbose = 4, debug = 5, silly = 6 + } + + export enum SQLLogLevel { + none = 1, error = 2, all = 3 + } + + export enum PhotoProcessingLib { + sharp = 3, + Jimp = 1, + gm = 2, + } + + export enum ReIndexingSensitivity { + low = 1, medium = 2, high = 3 + } + + + export type codecType = 'libvpx-vp9' | 'libx264' | 'libvpx' | 'libx265'; + export type resolutionType = 240 | 360 | 480 | 720 | 1080 | 1440 | 2160 | 4320; + export type formatType = 'mp4' | 'webm'; + + @SubConfigClass() + export class MySQLConfig { + @ConfigProperty({envAlias: 'MYSQL_HOST'}) + host: string = ''; + @ConfigProperty({envAlias: 'MYSQL_PASSWORD'}) + database: string = ''; + @ConfigProperty({envAlias: 'MYSQL_USERNAME'}) + username: string = ''; + @ConfigProperty({envAlias: 'MYSQL_DATABASE'}) + password: string = ''; + } + + + @SubConfigClass() + export class DataBaseConfig { + @ConfigProperty({ + type: DatabaseType, + onNewValue: (value, config) => { + if (value === ServerConfig.DatabaseType.memory) { + config.Client.Search.enabled = false; + config.Client.Sharing.enabled = false; + } + } + }) + type: DatabaseType = DatabaseType.sqlite; + + @ConfigProperty() + dbFolder: string = 'db'; + + @ConfigProperty() + mysql?: MySQLConfig = new MySQLConfig(); + } + + @SubConfigClass() + export class ThumbnailConfig { + @ConfigProperty({description: 'if true, photos will have better quality.'}) + qualityPriority: boolean = true; + @ConfigProperty({type: 'ratio'}) + personFaceMargin: number = 0.6; // in ration [0-1] + } + + @SubConfigClass() + export class SharingConfig { + @ConfigProperty() + updateTimeout: number = 1000 * 60 * 5; + } + + + @SubConfigClass() + export class IndexingConfig { + @ConfigProperty() + folderPreviewSize: number = 2; + @ConfigProperty() + cachedFolderTimeout: number = 1000 * 60 * 60; // Do not rescans the folder if seems ok + @ConfigProperty({type: ReIndexingSensitivity}) + reIndexingSensitivity: ReIndexingSensitivity = ReIndexingSensitivity.low; + @ConfigProperty({ + arrayType: String, + description: 'If an entry starts with \'/\' it is treated as an absolute path.' + + ' If it doesn\'t start with \'/\' but contains a \'/\', the path is relative to the image directory.' + + ' If it doesn\'t contain a \'/\', any folder with this name will be excluded.' + }) + excludeFolderList: string[] = []; + @ConfigProperty({arrayType: String, description: 'Any folder that contains a file with this name will be excluded from indexing.'}) + excludeFileList: string[] = []; + } + + @SubConfigClass() + export class ThreadingConfig { + @ConfigProperty({description: 'App can run on multiple thread'}) + enabled: boolean = true; + @ConfigProperty({description: 'Number of threads that are used to generate thumbnails. If 0, number of \'CPU cores -1\' threads will be used.'}) + thumbnailThreads: number = 0; // if zero-> CPU count -1 + } + + @SubConfigClass() + export class DuplicatesConfig { + @ConfigProperty() + listingLimit: number = 1000; // maximum number of duplicates to list + } + + @SubConfigClass() + export class LogConfig { + @ConfigProperty({type: LogLevel}) + level: LogLevel = LogLevel.info; + @ConfigProperty({type: SQLLogLevel}) + sqlLevel: SQLLogLevel = SQLLogLevel.error; + } + + + @SubConfigClass() + export class NeverJobTrigger implements JobTrigger { + @ConfigProperty({type: JobTriggerType}) + readonly type = JobTriggerType.never; + } + + @SubConfigClass() + export class ScheduledJobTrigger implements JobTrigger { + @ConfigProperty({type: JobTriggerType}) + readonly type = JobTriggerType.scheduled; + + @ConfigProperty() + time: number; // data time + } + + @SubConfigClass() + export class PeriodicJobTrigger implements JobTrigger { + @ConfigProperty({type: JobTriggerType}) + readonly type = JobTriggerType.periodic; + @ConfigProperty() + periodicity: number; // 0-6: week days 7 every day + @ConfigProperty() + atTime: number; // day time + } + + @SubConfigClass() + export class AfterJobTrigger implements JobTrigger { + + @ConfigProperty({type: JobTriggerType}) + readonly type = JobTriggerType.after; + @ConfigProperty() + afterScheduleName: string; // runs after schedule + + constructor(afterScheduleName: string) { + this.afterScheduleName = afterScheduleName; + } + } + + + @SubConfigClass() + export class JobScheduleConfig implements JobScheduleDTO { + + @ConfigProperty() + name: string; + @ConfigProperty() + jobName: string; + @ConfigProperty() + config: any = {}; + @ConfigProperty() + allowParallelRun: boolean; + @ConfigProperty({ + typeBuilder: (v: JobTrigger) => { + switch (v.type) { + case JobTriggerType.after: + return AfterJobTrigger; + case JobTriggerType.never: + return NeverJobTrigger; + case JobTriggerType.scheduled: + return ScheduledJobTrigger; + case JobTriggerType.periodic: + return PeriodicJobTrigger; + } + return null; + } + }) + trigger: AfterJobTrigger | NeverJobTrigger | PeriodicJobTrigger | ScheduledJobTrigger; + + constructor(name: string, jobName: string, allowParallelRun: boolean, + trigger: AfterJobTrigger | NeverJobTrigger | PeriodicJobTrigger | ScheduledJobTrigger, config: any) { + this.name = name; + this.jobName = jobName; + this.config = config; + this.allowParallelRun = allowParallelRun; + this.trigger = trigger; + } + } + + @SubConfigClass() + export class JobConfig { + @ConfigProperty({type: 'integer', description: 'Job history size'}) + maxSavedProgress: number = 10; + @ConfigProperty({arrayType: JobScheduleConfig}) + scheduled: JobScheduleConfig[] = [ + new JobScheduleConfig(DefaultsJobs[DefaultsJobs.Indexing], + DefaultsJobs[DefaultsJobs.Indexing], + false, + new NeverJobTrigger(), {} + ), + new JobScheduleConfig(DefaultsJobs[DefaultsJobs['Thumbnail Generation']], + DefaultsJobs[DefaultsJobs['Thumbnail Generation']], + false, + new AfterJobTrigger(DefaultsJobs[DefaultsJobs.Indexing]), {sizes: [240]} + ), + new JobScheduleConfig(DefaultsJobs[DefaultsJobs['Photo Converting']], + DefaultsJobs[DefaultsJobs['Photo Converting']], + false, + new AfterJobTrigger(DefaultsJobs[DefaultsJobs['Thumbnail Generation']]), {} + ), + new JobScheduleConfig(DefaultsJobs[DefaultsJobs['Video Converting']], + DefaultsJobs[DefaultsJobs['Video Converting']], + false, + new AfterJobTrigger(DefaultsJobs[DefaultsJobs['Photo Converting']]), {} + ), + new JobScheduleConfig(DefaultsJobs[DefaultsJobs['Temp Folder Cleaning']], + DefaultsJobs[DefaultsJobs['Temp Folder Cleaning']], + false, + new AfterJobTrigger(DefaultsJobs[DefaultsJobs['Photo Converting']]), {} + ), + ]; + } + + + @SubConfigClass() + export class VideoTranscodingConfig { + @ConfigProperty({type: 'unsignedInt'}) + bitRate: number = 5 * 1024 * 1024; + @ConfigProperty({type: 'unsignedInt'}) + resolution: resolutionType = 720; + @ConfigProperty({type: 'positiveFloat'}) + fps: number = 25; + @ConfigProperty() + codec: codecType = 'libx264'; + @ConfigProperty() + format: formatType = 'mp4'; + } + + @SubConfigClass() + export class VideoConfig { + @ConfigProperty() + transcoding: VideoTranscodingConfig = new VideoTranscodingConfig(); + } + + @SubConfigClass() + export class PhotoConvertingConfig { + @ConfigProperty({description: 'Converts photos on the fly, when they are requested.'}) + onTheFly: boolean = true; + @ConfigProperty({type: 'unsignedInt'}) + resolution: resolutionType = 1080; + } + + @SubConfigClass() + export class PhotoConfig { + @ConfigProperty() + Converting: PhotoConvertingConfig = new PhotoConvertingConfig(); + } + + @SubConfigClass() + export class MediaConfig { + @ConfigProperty({description: 'Images are loaded from this folder (read permission required)'}) + folder: string = 'demo/images'; + @ConfigProperty({description: 'Thumbnails, coverted photos, videos will be stored here (write permission required)'}) + tempFolder: string = 'demo/tmp'; + @ConfigProperty({type: ServerConfig.PhotoProcessingLib}) + photoProcessingLibrary: PhotoProcessingLib = ServerConfig.PhotoProcessingLib.sharp; + @ConfigProperty() + Video: VideoConfig = new VideoConfig(); + @ConfigProperty() + Photo: PhotoConfig = new PhotoConfig(); + @ConfigProperty() + Thumbnail: ThumbnailConfig = new ThumbnailConfig(); + } + + + @SubConfigClass() + export class Config { + @ConfigProperty({arrayType: String}) + sessionSecret: string[] = []; + @ConfigProperty({type: 'unsignedInt', envAlias: 'PORT'}) + port: number = 80; + @ConfigProperty() + host: string = '0.0.0.0'; + @ConfigProperty() + Media: MediaConfig = new MediaConfig(); + @ConfigProperty() + Threading: ThreadingConfig = new ThreadingConfig(); + @ConfigProperty() + Database: DataBaseConfig = new DataBaseConfig(); + @ConfigProperty() + Sharing: SharingConfig = new SharingConfig(); + @ConfigProperty({description: 'unit: ms'}) + sessionTimeout: number = 1000 * 60 * 60 * 24 * 7; // in ms + @ConfigProperty() + Indexing: IndexingConfig = new IndexingConfig(); + @ConfigProperty({type: 'unsignedInt', description: 'only this many bites will be loaded when scanning photo for metadata'}) + photoMetadataSize: number = 512 * 1024; // only this many bites will be loaded when scanning photo for metadata + @ConfigProperty() + Duplicates: DuplicatesConfig = new DuplicatesConfig(); + @ConfigProperty() + Log: LogConfig = new LogConfig(); + @ConfigProperty() + Jobs: JobConfig = new JobConfig(); + } +} + +export interface IPrivateConfig { + Server: ServerConfig.Config; + Client: ClientConfig.Config; + +} diff --git a/src/common/config/private/PrivateConfigDefaultsClass.ts b/src/common/config/private/PrivateConfigDefaultsClass.ts deleted file mode 100644 index ff81d50..0000000 --- a/src/common/config/private/PrivateConfigDefaultsClass.ts +++ /dev/null @@ -1,127 +0,0 @@ -import {PublicConfigClass} from '../public/ConfigClass'; -import {IPrivateConfig, ServerConfig} from './IPrivateConfig'; -import {JobTriggerType} from '../../entities/job/JobScheduleDTO'; -import {DefaultsJobs} from '../../entities/job/JobDTO'; - -/** - * This configuration will be only at backend - */ -export class PrivateConfigDefaultsClass extends PublicConfigClass implements IPrivateConfig { - - public Server: ServerConfig.Config = { - port: 80, - sessionSecret: null, - host: '0.0.0.0', - Media: { - folder: 'demo/images', - tempFolder: 'demo/tmp', - photoProcessingLibrary: ServerConfig.PhotoProcessingLib.sharp, - Thumbnail: { - qualityPriority: true, - personFaceMargin: 0.6 - }, - Photo: { - Converting: { - onTheFly: true, - resolution: 1080 - } - }, - Video: { - transcoding: { - bitRate: 5 * 1024 * 1024, - codec: 'libx264', - format: 'mp4', - fps: 25, - resolution: 720 - } - } - }, - Log: { - level: ServerConfig.LogLevel.info, - sqlLevel: ServerConfig.SQLLogLevel.error - }, - sessionTimeout: 1000 * 60 * 60 * 24 * 7, - photoMetadataSize: 512 * 1024, - Database: { - type: ServerConfig.DatabaseType.sqlite, - dbFolder: 'db', - mysql: { - host: '', - username: '', - password: '', - database: '' - - } - }, - Sharing: { - updateTimeout: 1000 * 60 * 5 - }, - Threading: { - enabled: true, - thumbnailThreads: 0 - }, - Indexing: { - folderPreviewSize: 2, - cachedFolderTimeout: 1000 * 60 * 60, - reIndexingSensitivity: ServerConfig.ReIndexingSensitivity.low, - excludeFolderList: [], - excludeFileList: [] - }, - Duplicates: { - listingLimit: 1000 - }, - Jobs: { - maxSavedProgress: 10, - scheduled: [ - { - name: DefaultsJobs[DefaultsJobs.Indexing], - jobName: DefaultsJobs[DefaultsJobs.Indexing], - allowParallelRun: false, - config: {}, - trigger: {type: JobTriggerType.never} - }, - { - name: DefaultsJobs[DefaultsJobs['Thumbnail Generation']], - jobName: DefaultsJobs[DefaultsJobs['Thumbnail Generation']], - config: {sizes: [240]}, - allowParallelRun: false, - trigger: { - type: JobTriggerType.after, - afterScheduleName: DefaultsJobs[DefaultsJobs.Indexing] - } - }, - /* { - name: DefaultsJobs[DefaultsJobs['Photo Converting']], - jobName: DefaultsJobs[DefaultsJobs['Photo Converting']], - config: {}, - parallelRunEnabled:false, - trigger: { - type: JobTriggerType.after, - afterScheduleName: DefaultsJobs[DefaultsJobs['Thumbnail Generation']] - } - },*/ - { - name: DefaultsJobs[DefaultsJobs['Video Converting']], - jobName: DefaultsJobs[DefaultsJobs['Video Converting']], - config: {}, - allowParallelRun: false, - trigger: { - type: JobTriggerType.after, - afterScheduleName: DefaultsJobs[DefaultsJobs['Thumbnail Generation']] - } - }, - { - name: DefaultsJobs[DefaultsJobs['Temp Folder Cleaning']], - jobName: DefaultsJobs[DefaultsJobs['Temp Folder Cleaning']], - config: {}, - allowParallelRun: false, - trigger: { - type: JobTriggerType.after, - afterScheduleName: DefaultsJobs[DefaultsJobs['Video Converting']] - } - } - ] - } - }; -} - diff --git a/src/common/config/private/WebConfig.ts b/src/common/config/private/WebConfig.ts new file mode 100644 index 0000000..ac19b85 --- /dev/null +++ b/src/common/config/private/WebConfig.ts @@ -0,0 +1,20 @@ +/* tslint:disable:no-inferrable-types */ +import 'reflect-metadata'; +import {ClientConfig} from '../public/ClientConfig'; +import {ServerConfig} from './PrivateConfig'; +import {WebConfigClass} from 'typeconfig/src/decorators/class/WebConfigClass'; +import {ConfigProperty} from 'typeconfig/src/decorators/property/ConfigPropoerty'; +import {ConfigDefaults} from 'typeconfig/src/decorators/property/ConfigDefaults'; + + +@WebConfigClass() +export class WebConfig { + @ConfigDefaults() + Defaults: WebConfig; + + @ConfigProperty() + Server: ServerConfig.Config = new ServerConfig.Config(); + @ConfigProperty() + Client: ClientConfig.Config = new ClientConfig.Config(); + +} diff --git a/src/common/config/public/ClientConfig.ts b/src/common/config/public/ClientConfig.ts new file mode 100644 index 0000000..c51d4d7 --- /dev/null +++ b/src/common/config/public/ClientConfig.ts @@ -0,0 +1,198 @@ +/* tslint:disable:no-inferrable-types */ +import 'reflect-metadata'; +import {SortingMethods} from '../../entities/SortingMethods'; +import {UserRoles} from '../../entities/UserDTO'; +import { SubConfigClass } from 'typeconfig/src/decorators/class/SubConfigClass'; +import { ConfigProperty } from 'typeconfig/src/decorators/property/ConfigPropoerty'; + + +export module ClientConfig { + + export enum MapProviders { + OpenStreetMap = 0, Mapbox = 1, Custom = 2 + } + + @SubConfigClass() + export class AutoCompleteConfig { + @ConfigProperty() + enabled = true; + @ConfigProperty() + maxItemsPerCategory = 5; + @ConfigProperty() + cacheTimeout: number = 1000 * 60 * 60; + } + + @SubConfigClass() + export class SearchConfig { + @ConfigProperty() + enabled: boolean = true; + @ConfigProperty() + instantSearchEnabled: boolean = true; + @ConfigProperty() + InstantSearchTimeout: number = 3000; + @ConfigProperty() + instantSearchCacheTimeout: number = 1000 * 60 * 60; + @ConfigProperty() + searchCacheTimeout: number = 1000 * 60 * 60; + @ConfigProperty() + AutoComplete: AutoCompleteConfig = new AutoCompleteConfig(); + } + + @SubConfigClass() + export class SharingConfig { + @ConfigProperty() + enabled: boolean = true; + @ConfigProperty() + passwordProtected: boolean = true; + } + + @SubConfigClass() + export class RandomPhotoConfig { + @ConfigProperty() + enabled: boolean = true; + } + + @SubConfigClass() + export class MapLayers { + @ConfigProperty() + name: string = 'street'; + @ConfigProperty() + url: string = ''; + } + + @SubConfigClass() + export class MapConfig { + @ConfigProperty() + enabled: boolean = true; + @ConfigProperty() + useImageMarkers: boolean = true; + @ConfigProperty() + mapProvider: MapProviders = MapProviders.OpenStreetMap; + @ConfigProperty() + mapboxAccessToken: string = ''; + @ConfigProperty({arrayType: MapLayers}) + customLayers: MapLayers[] = [new MapLayers()]; + } + + @SubConfigClass() + export class ThumbnailConfig { + @ConfigProperty() + iconSize: number = 45; + @ConfigProperty() + personThumbnailSize: number = 200; + @ConfigProperty({arrayType: Number}) + thumbnailSizes: number[] = [240, 480]; + @ConfigProperty({volatile: true}) + concurrentThumbnailGenerations: number = 1; + } + + @SubConfigClass() + export class NavBarConfig { + @ConfigProperty() + showItemCount: boolean = true; + } + + @SubConfigClass() + export class OtherConfig { + @ConfigProperty() + enableCache: boolean = true; + @ConfigProperty() + enableOnScrollRendering: boolean = true; + @ConfigProperty({type: SortingMethods}) + defaultPhotoSortingMethod: SortingMethods = SortingMethods.ascDate; + @ConfigProperty() + enableOnScrollThumbnailPrioritising: boolean = true; + @ConfigProperty() + NavBar: NavBarConfig = new NavBarConfig(); + @ConfigProperty() + captionFirstNaming: boolean = false; // shows the caption instead of the filename in the photo grid + } + + @SubConfigClass() + export class VideoConfig { + @ConfigProperty() + enabled: boolean = true; + } + + @SubConfigClass() + export class PhotoConvertingConfig { + @ConfigProperty() + enabled: boolean = true; + } + + @SubConfigClass() + export class PhotoConfig { + @ConfigProperty() + Converting: PhotoConvertingConfig = new PhotoConvertingConfig(); + } + + @SubConfigClass() + export class MediaConfig { + @ConfigProperty() + Thumbnail: ThumbnailConfig = new ThumbnailConfig(); + @ConfigProperty() + Video: VideoConfig = new VideoConfig(); + @ConfigProperty() + Photo: PhotoConfig = new PhotoConfig(); + } + + @SubConfigClass() + export class MetaFileConfig { + @ConfigProperty() + enabled: boolean = true; + } + + @SubConfigClass() + export class FacesConfig { + @ConfigProperty() + enabled: boolean = true; + @ConfigProperty() + keywordsToPersons: boolean = true; + @ConfigProperty({type: UserRoles}) + writeAccessMinRole: UserRoles = UserRoles.Admin; + } + + @SubConfigClass() + export class Config { + + + @ConfigProperty({volatile: true}) + upTime: string; + @ConfigProperty({volatile: true}) + appVersion: string; + @ConfigProperty({volatile: true}) + buildTime: string; + @ConfigProperty({volatile: true}) + buildCommitHash: string; + @ConfigProperty() + applicationTitle: string = 'PiGallery 2'; + @ConfigProperty() + publicUrl: string = ''; + @ConfigProperty() + urlBase: string = ''; + @ConfigProperty() + Search: SearchConfig = new SearchConfig(); + @ConfigProperty() + Sharing: SharingConfig = new SharingConfig(); + @ConfigProperty() + Map: MapConfig = new MapConfig(); + @ConfigProperty() + RandomPhoto: RandomPhotoConfig = new RandomPhotoConfig(); + @ConfigProperty() + Other: OtherConfig = new OtherConfig(); + @ConfigProperty() + authenticationRequired: boolean = true; + @ConfigProperty({type: UserRoles}) + unAuthenticatedUserRole: UserRoles = UserRoles.Admin; + @ConfigProperty({arrayType: String, volatile: true}) + languages: string[]; + @ConfigProperty() + Media: MediaConfig = new MediaConfig(); + @ConfigProperty() + MetaFile: MetaFileConfig = new MetaFileConfig(); + @ConfigProperty() + Faces: FacesConfig = new FacesConfig(); + } + +} + diff --git a/src/common/config/public/Config.ts b/src/common/config/public/Config.ts index 21333df..2882d71 100644 --- a/src/common/config/public/Config.ts +++ b/src/common/config/public/Config.ts @@ -1,16 +1,28 @@ -import {PublicConfigClass} from './ConfigClass'; -import {WebConfigLoader} from 'typeconfig/src/WebConfigLoader'; +import {ClientConfig} from './ClientConfig'; +import {WebConfigClass} from 'typeconfig/src/decorators/class/WebConfigClass'; +import {WebConfigClassBuilder} from 'typeconfig/src/decorators/builders/WebConfigClassBuilder'; +import {ConfigProperty} from 'typeconfig/src/decorators/property/ConfigPropoerty'; -declare module ServerInject { - export const ConfigInject: PublicConfigClass; +/** + * These configuration will be available at frontend and backend too + */ +@WebConfigClass() +export class ClientClass { + + @ConfigProperty() + public Client: ClientConfig.Config = new ClientConfig.Config(); } -export let Config = new PublicConfigClass(); +declare module ServerInject { + export const ConfigInject: ClientClass; +} + +export let Config = WebConfigClassBuilder.attachInterface(new ClientClass()); if (typeof ServerInject !== 'undefined' && typeof ServerInject.ConfigInject !== 'undefined') { - WebConfigLoader.loadFrontendConfig(Config.Client, ServerInject.ConfigInject); + Config.load(ServerInject.ConfigInject); } diff --git a/src/common/config/public/ConfigClass.ts b/src/common/config/public/ConfigClass.ts deleted file mode 100644 index 78abffc..0000000 --- a/src/common/config/public/ConfigClass.ts +++ /dev/null @@ -1,195 +0,0 @@ -import {SortingMethods} from '../../entities/SortingMethods'; -import {UserRoles} from '../../entities/UserDTO'; - -export module ClientConfig { - - export enum MapProviders { - OpenStreetMap = 0, Mapbox = 1, Custom = 2 - } - - export interface AutoCompleteConfig { - enabled: boolean; - maxItemsPerCategory: number; - cacheTimeout: number; - } - - export interface SearchConfig { - enabled: boolean; - instantSearchEnabled: boolean; - InstantSearchTimeout: number; - instantSearchCacheTimeout: number; - searchCacheTimeout: number; - AutoComplete: AutoCompleteConfig; - } - - export interface SharingConfig { - enabled: boolean; - passwordProtected: boolean; - } - - export interface RandomPhotoConfig { - enabled: boolean; - } - - export interface MapLayers { - name: string; - url: string; - } - - export interface MapConfig { - enabled: boolean; - useImageMarkers: boolean; - mapProvider: MapProviders; - mapboxAccessToken: string; - customLayers: MapLayers[]; - } - - export interface ThumbnailConfig { - iconSize: number; - personThumbnailSize: number; - thumbnailSizes: number[]; - concurrentThumbnailGenerations: number; - } - - export interface NavBarConfig { - showItemCount: boolean; - } - - export interface OtherConfig { - enableCache: boolean; - enableOnScrollRendering: boolean; - defaultPhotoSortingMethod: SortingMethods; - enableOnScrollThumbnailPrioritising: boolean; - NavBar: NavBarConfig; - captionFirstNaming: boolean; // shows the caption instead of the filename in the photo grid - } - - export interface VideoConfig { - enabled: boolean; - } - - export interface PhotoConfig { - Converting: { - enabled: boolean; - }; - } - - export interface MediaConfig { - Thumbnail: ThumbnailConfig; - Video: VideoConfig; - Photo: PhotoConfig; - } - - export interface MetaFileConfig { - enabled: boolean; - } - - export interface FacesConfig { - enabled: boolean; - keywordsToPersons: boolean; - writeAccessMinRole: UserRoles; - } - - export interface Config { - upTime: string; - appVersion: string; - buildTime: string; - buildCommitHash: string; - applicationTitle: string; - publicUrl: string; - urlBase: string; - Search: SearchConfig; - Sharing: SharingConfig; - Map: MapConfig; - RandomPhoto: RandomPhotoConfig; - Other: OtherConfig; - authenticationRequired: boolean; - unAuthenticatedUserRole: UserRoles; - languages: string[]; - Media: MediaConfig; - MetaFile: MetaFileConfig; - Faces: FacesConfig; - } - -} - -/** - * These configuration will be available at frontend and backend too - */ -export class PublicConfigClass { - - public Client: ClientConfig.Config = { - applicationTitle: 'PiGallery 2', - appVersion: '', - buildCommitHash: '', - buildTime: '', - upTime: '', - Media: { - Video: { - enabled: true - }, - Photo: { - Converting: { - enabled: true - } - }, - Thumbnail: { - concurrentThumbnailGenerations: 1, - thumbnailSizes: [240, 480], - iconSize: 45, - personThumbnailSize: 200 - } - }, - Search: { - enabled: true, - instantSearchEnabled: true, - InstantSearchTimeout: 3000, - searchCacheTimeout: 1000 * 60 * 60, - instantSearchCacheTimeout: 1000 * 60 * 60, - AutoComplete: { - enabled: true, - cacheTimeout: 1000 * 60 * 60, - maxItemsPerCategory: 5 - } - }, - Sharing: { - enabled: true, - passwordProtected: true - }, - Map: { - enabled: true, - useImageMarkers: true, - mapProvider: ClientConfig.MapProviders.OpenStreetMap, - mapboxAccessToken: '', - customLayers: [{name: 'street', url: ''}] - }, - RandomPhoto: { - enabled: true - }, - MetaFile: { - enabled: true - }, - Other: { - captionFirstNaming: false, - enableCache: true, - enableOnScrollRendering: true, - enableOnScrollThumbnailPrioritising: true, - defaultPhotoSortingMethod: SortingMethods.ascDate, - NavBar: { - showItemCount: true - } - }, - Faces: { - enabled: true, - keywordsToPersons: true, - writeAccessMinRole: UserRoles.Admin - }, - authenticationRequired: true, - unAuthenticatedUserRole: UserRoles.Admin, - publicUrl: '', - urlBase: '', - languages: [] - }; - -} - diff --git a/src/common/entities/settings/OtherConfigDTO.ts b/src/common/entities/settings/OtherConfigDTO.ts index cdb37cf..dcf20ac 100644 --- a/src/common/entities/settings/OtherConfigDTO.ts +++ b/src/common/entities/settings/OtherConfigDTO.ts @@ -1,5 +1,6 @@ -import {ClientConfig} from '../../config/public/ConfigClass'; -import {ServerConfig} from '../../config/private/IPrivateConfig'; +import {ServerConfig} from '../../config/private/PrivateConfig'; +import {ClientConfig} from '../../config/public/ClientConfig'; + export interface OtherConfigDTO { Server: ServerConfig.ThreadingConfig; diff --git a/src/frontend/app/ui/gallery/map/map.service.ts b/src/frontend/app/ui/gallery/map/map.service.ts index f3098ee..4e76413 100644 --- a/src/frontend/app/ui/gallery/map/map.service.ts +++ b/src/frontend/app/ui/gallery/map/map.service.ts @@ -3,7 +3,7 @@ import {NetworkService} from '../../../model/network/network.service'; import {FileDTO} from '../../../../../common/entities/FileDTO'; import {Utils} from '../../../../../common/Utils'; import {Config} from '../../../../../common/config/public/Config'; -import {ClientConfig} from '../../../../../common/config/public/ConfigClass'; +import {ClientConfig} from '../../../../../common/config/public/ClientConfig'; import MapLayers = ClientConfig.MapLayers; @Injectable() diff --git a/src/frontend/app/ui/settings/_abstract/abstract.settings.component.ts b/src/frontend/app/ui/settings/_abstract/abstract.settings.component.ts index 18022c8..931535e 100644 --- a/src/frontend/app/ui/settings/_abstract/abstract.settings.component.ts +++ b/src/frontend/app/ui/settings/_abstract/abstract.settings.component.ts @@ -6,10 +6,11 @@ import {ErrorDTO} from '../../../../../common/entities/Error'; import {NotificationService} from '../../../model/notification.service'; import {NavigationService} from '../../../model/navigation.service'; import {AbstractSettingsService} from './abstract.settings.service'; -import {IPrivateConfig} from '../../../../../common/config/private/IPrivateConfig'; import {I18n} from '@ngx-translate/i18n-polyfill'; import {Subscription} from 'rxjs'; import {ISettingsComponent} from './ISettingsComponent'; +import {WebConfig} from '../../../../../common/config/private/WebConfig'; + export abstract class SettingsComponent = AbstractSettingsService> @@ -44,7 +45,7 @@ export abstract class SettingsComponent T) { + private sliceFN?: (s: WebConfig) => T) { if (this.sliceFN) { this._settingsSubscription = this._settingsService.Settings.subscribe(this.onNewSettings); this.onNewSettings(this._settingsService._settingsService.settings.value); @@ -68,7 +69,7 @@ export abstract class SettingsComponent { + onNewSettings = (s: WebConfig) => { this.settings = Utils.clone(this.sliceFN(s)); this.original = Utils.clone(this.settings); this.ngOnChanges(); diff --git a/src/frontend/app/ui/settings/database/database.settings.component.ts b/src/frontend/app/ui/settings/database/database.settings.component.ts index 644ee9e..72377f9 100644 --- a/src/frontend/app/ui/settings/database/database.settings.component.ts +++ b/src/frontend/app/ui/settings/database/database.settings.component.ts @@ -6,7 +6,7 @@ import {NavigationService} from '../../../model/navigation.service'; import {SettingsComponent} from '../_abstract/abstract.settings.component'; import {DatabaseSettingsService} from './database.settings.service'; import {I18n} from '@ngx-translate/i18n-polyfill'; -import {ServerConfig} from '../../../../../common/config/private/IPrivateConfig'; +import {ServerConfig} from '../../../../../common/config/private/PrivateConfig'; @Component({ selector: 'app-settings-database', diff --git a/src/frontend/app/ui/settings/database/database.settings.service.ts b/src/frontend/app/ui/settings/database/database.settings.service.ts index 6c8ac35..aa5e30c 100644 --- a/src/frontend/app/ui/settings/database/database.settings.service.ts +++ b/src/frontend/app/ui/settings/database/database.settings.service.ts @@ -2,7 +2,7 @@ import {Injectable} from '@angular/core'; import {NetworkService} from '../../../model/network/network.service'; import {AbstractSettingsService} from '../_abstract/abstract.settings.service'; import {SettingsService} from '../settings.service'; -import {ServerConfig} from '../../../../../common/config/private/IPrivateConfig'; +import {ServerConfig} from '../../../../../common/config/private/PrivateConfig'; @Injectable() export class DatabaseSettingsService extends AbstractSettingsService { diff --git a/src/frontend/app/ui/settings/faces/faces.settings.component.ts b/src/frontend/app/ui/settings/faces/faces.settings.component.ts index e312af1..ad3ffa6 100644 --- a/src/frontend/app/ui/settings/faces/faces.settings.component.ts +++ b/src/frontend/app/ui/settings/faces/faces.settings.component.ts @@ -3,11 +3,11 @@ import {SettingsComponent} from '../_abstract/abstract.settings.component'; import {AuthenticationService} from '../../../model/network/authentication.service'; import {NavigationService} from '../../../model/navigation.service'; import {NotificationService} from '../../../model/notification.service'; -import {ClientConfig} from '../../../../../common/config/public/ConfigClass'; import {FacesSettingsService} from './faces.settings.service'; import {I18n} from '@ngx-translate/i18n-polyfill'; import {Utils} from '../../../../../common/Utils'; import {UserRoles} from '../../../../../common/entities/UserDTO'; +import {ClientConfig} from '../../../../../common/config/public/ClientConfig'; @Component({ selector: 'app-settings-faces', diff --git a/src/frontend/app/ui/settings/faces/faces.settings.service.ts b/src/frontend/app/ui/settings/faces/faces.settings.service.ts index fb50bf9..a3add9f 100644 --- a/src/frontend/app/ui/settings/faces/faces.settings.service.ts +++ b/src/frontend/app/ui/settings/faces/faces.settings.service.ts @@ -1,9 +1,9 @@ import {Injectable} from '@angular/core'; import {NetworkService} from '../../../model/network/network.service'; -import {ClientConfig} from '../../../../../common/config/public/ConfigClass'; import {SettingsService} from '../settings.service'; import {AbstractSettingsService} from '../_abstract/abstract.settings.service'; -import {ServerConfig} from '../../../../../common/config/private/IPrivateConfig'; +import {ClientConfig} from '../../../../../common/config/public/ClientConfig'; +import {ServerConfig} from '../../../../../common/config/private/PrivateConfig'; @Injectable() export class FacesSettingsService extends AbstractSettingsService { diff --git a/src/frontend/app/ui/settings/indexing/indexing.settings.component.ts b/src/frontend/app/ui/settings/indexing/indexing.settings.component.ts index 7867b32..6df8fff 100644 --- a/src/frontend/app/ui/settings/indexing/indexing.settings.component.ts +++ b/src/frontend/app/ui/settings/indexing/indexing.settings.component.ts @@ -9,8 +9,8 @@ import {Utils} from '../../../../../common/Utils'; import {I18n} from '@ngx-translate/i18n-polyfill'; import {ScheduledJobsService} from '../scheduled-jobs.service'; import {DefaultsJobs, JobDTO} from '../../../../../common/entities/job/JobDTO'; -import {ServerConfig} from '../../../../../common/config/private/IPrivateConfig'; import {JobProgressStates} from '../../../../../common/entities/job/JobProgressDTO'; +import {ServerConfig} from '../../../../../common/config/private/PrivateConfig'; @Component({ selector: 'app-settings-indexing', diff --git a/src/frontend/app/ui/settings/indexing/indexing.settings.service.ts b/src/frontend/app/ui/settings/indexing/indexing.settings.service.ts index a0f776d..73de677 100644 --- a/src/frontend/app/ui/settings/indexing/indexing.settings.service.ts +++ b/src/frontend/app/ui/settings/indexing/indexing.settings.service.ts @@ -6,8 +6,8 @@ import {BehaviorSubject} from 'rxjs'; import {StatisticDTO} from '../../../../../common/entities/settings/StatisticDTO'; import {ScheduledJobsService} from '../scheduled-jobs.service'; import {DefaultsJobs} from '../../../../../common/entities/job/JobDTO'; -import {ServerConfig} from '../../../../../common/config/private/IPrivateConfig'; import {first} from 'rxjs/operators'; +import {ServerConfig} from '../../../../../common/config/private/PrivateConfig'; @Injectable() export class IndexingSettingsService extends AbstractSettingsService { diff --git a/src/frontend/app/ui/settings/jobs/jobs.settings.component.ts b/src/frontend/app/ui/settings/jobs/jobs.settings.component.ts index 9313a47..31187a5 100644 --- a/src/frontend/app/ui/settings/jobs/jobs.settings.component.ts +++ b/src/frontend/app/ui/settings/jobs/jobs.settings.component.ts @@ -14,11 +14,11 @@ import { PeriodicJobTrigger, ScheduledJobTrigger } from '../../../../../common/entities/job/JobScheduleDTO'; -import {ServerConfig} from '../../../../../common/config/private/IPrivateConfig'; import {ConfigTemplateEntry} from '../../../../../common/entities/job/JobDTO'; import {ModalDirective} from 'ngx-bootstrap/modal'; import {JobProgressDTO, JobProgressStates} from '../../../../../common/entities/job/JobProgressDTO'; import {BackendtextService} from '../../../model/backendtext.service'; +import {ServerConfig} from '../../../../../common/config/private/PrivateConfig'; @Component({ selector: 'app-settings-jobs', diff --git a/src/frontend/app/ui/settings/jobs/jobs.settings.service.ts b/src/frontend/app/ui/settings/jobs/jobs.settings.service.ts index 0a133c4..2815b3d 100644 --- a/src/frontend/app/ui/settings/jobs/jobs.settings.service.ts +++ b/src/frontend/app/ui/settings/jobs/jobs.settings.service.ts @@ -4,7 +4,7 @@ import {SettingsService} from '../settings.service'; import {AbstractSettingsService} from '../_abstract/abstract.settings.service'; import {BehaviorSubject} from 'rxjs'; import {JobDTO} from '../../../../../common/entities/job/JobDTO'; -import {ServerConfig} from '../../../../../common/config/private/IPrivateConfig'; +import {ServerConfig} from '../../../../../common/config/private/PrivateConfig'; @Injectable() export class JobsSettingsService extends AbstractSettingsService { diff --git a/src/frontend/app/ui/settings/map/map.settings.component.ts b/src/frontend/app/ui/settings/map/map.settings.component.ts index f0e3269..625cba8 100644 --- a/src/frontend/app/ui/settings/map/map.settings.component.ts +++ b/src/frontend/app/ui/settings/map/map.settings.component.ts @@ -4,9 +4,9 @@ import {SettingsComponent} from '../_abstract/abstract.settings.component'; import {AuthenticationService} from '../../../model/network/authentication.service'; import {NavigationService} from '../../../model/navigation.service'; import {NotificationService} from '../../../model/notification.service'; -import {ClientConfig} from '../../../../../common/config/public/ConfigClass'; import {I18n} from '@ngx-translate/i18n-polyfill'; import {Utils} from '../../../../../common/Utils'; +import {ClientConfig} from '../../../../../common/config/public/ClientConfig'; @Component({ diff --git a/src/frontend/app/ui/settings/map/map.settings.service.ts b/src/frontend/app/ui/settings/map/map.settings.service.ts index 08af94b..ada3f2d 100644 --- a/src/frontend/app/ui/settings/map/map.settings.service.ts +++ b/src/frontend/app/ui/settings/map/map.settings.service.ts @@ -1,8 +1,8 @@ import {Injectable} from '@angular/core'; import {NetworkService} from '../../../model/network/network.service'; -import {ClientConfig} from '../../../../../common/config/public/ConfigClass'; import {SettingsService} from '../settings.service'; import {AbstractSettingsService} from '../_abstract/abstract.settings.service'; +import {ClientConfig} from '../../../../../common/config/public/ClientConfig'; @Injectable() export class MapSettingsService extends AbstractSettingsService { diff --git a/src/frontend/app/ui/settings/metafiles/metafile.settings.component.ts b/src/frontend/app/ui/settings/metafiles/metafile.settings.component.ts index 774e9c0..932ad80 100644 --- a/src/frontend/app/ui/settings/metafiles/metafile.settings.component.ts +++ b/src/frontend/app/ui/settings/metafiles/metafile.settings.component.ts @@ -4,8 +4,8 @@ import {SettingsComponent} from '../_abstract/abstract.settings.component'; import {AuthenticationService} from '../../../model/network/authentication.service'; import {NavigationService} from '../../../model/navigation.service'; import {NotificationService} from '../../../model/notification.service'; -import {ClientConfig} from '../../../../../common/config/public/ConfigClass'; import {I18n} from '@ngx-translate/i18n-polyfill'; +import {ClientConfig} from '../../../../../common/config/public/ClientConfig'; @Component({ diff --git a/src/frontend/app/ui/settings/metafiles/metafile.settings.service.ts b/src/frontend/app/ui/settings/metafiles/metafile.settings.service.ts index a10fb0d..57e2b2c 100644 --- a/src/frontend/app/ui/settings/metafiles/metafile.settings.service.ts +++ b/src/frontend/app/ui/settings/metafiles/metafile.settings.service.ts @@ -1,8 +1,8 @@ import {Injectable} from '@angular/core'; import {NetworkService} from '../../../model/network/network.service'; -import {ClientConfig} from '../../../../../common/config/public/ConfigClass'; import {SettingsService} from '../settings.service'; import {AbstractSettingsService} from '../_abstract/abstract.settings.service'; +import {ClientConfig} from '../../../../../common/config/public/ClientConfig'; @Injectable() export class MetaFileSettingsService extends AbstractSettingsService { diff --git a/src/frontend/app/ui/settings/photo/photo.settings.component.ts b/src/frontend/app/ui/settings/photo/photo.settings.component.ts index 4a50ee4..67170c1 100644 --- a/src/frontend/app/ui/settings/photo/photo.settings.component.ts +++ b/src/frontend/app/ui/settings/photo/photo.settings.component.ts @@ -4,13 +4,13 @@ import {SettingsComponent} from '../_abstract/abstract.settings.component'; import {AuthenticationService} from '../../../model/network/authentication.service'; import {NavigationService} from '../../../model/navigation.service'; import {NotificationService} from '../../../model/notification.service'; -import {ClientConfig} from '../../../../../common/config/public/ConfigClass'; import {I18n} from '@ngx-translate/i18n-polyfill'; import {ScheduledJobsService} from '../scheduled-jobs.service'; -import {ServerConfig} from '../../../../../common/config/private/IPrivateConfig'; import {Utils} from '../../../../../common/Utils'; import {DefaultsJobs, JobDTO} from '../../../../../common/entities/job/JobDTO'; import {JobProgressStates} from '../../../../../common/entities/job/JobProgressDTO'; +import {ServerConfig} from '../../../../../common/config/private/PrivateConfig'; +import {ClientConfig} from '../../../../../common/config/public/ClientConfig'; @Component({ diff --git a/src/frontend/app/ui/settings/photo/photo.settings.service.ts b/src/frontend/app/ui/settings/photo/photo.settings.service.ts index 5b1ae6f..92a5bff 100644 --- a/src/frontend/app/ui/settings/photo/photo.settings.service.ts +++ b/src/frontend/app/ui/settings/photo/photo.settings.service.ts @@ -1,9 +1,9 @@ import {Injectable} from '@angular/core'; import {NetworkService} from '../../../model/network/network.service'; -import {ClientConfig} from '../../../../../common/config/public/ConfigClass'; import {SettingsService} from '../settings.service'; import {AbstractSettingsService} from '../_abstract/abstract.settings.service'; -import {ServerConfig} from '../../../../../common/config/private/IPrivateConfig'; +import {ServerConfig} from '../../../../../common/config/private/PrivateConfig'; +import {ClientConfig} from '../../../../../common/config/public/ClientConfig'; @Injectable() export class PhotoSettingsService extends AbstractSettingsService<{ diff --git a/src/frontend/app/ui/settings/random-photo/random-photo.settings.component.ts b/src/frontend/app/ui/settings/random-photo/random-photo.settings.component.ts index 72ad8c7..1f3f6a0 100644 --- a/src/frontend/app/ui/settings/random-photo/random-photo.settings.component.ts +++ b/src/frontend/app/ui/settings/random-photo/random-photo.settings.component.ts @@ -3,9 +3,9 @@ import {SettingsComponent} from '../_abstract/abstract.settings.component'; import {AuthenticationService} from '../../../model/network/authentication.service'; import {NavigationService} from '../../../model/navigation.service'; import {NotificationService} from '../../../model/notification.service'; -import {ClientConfig} from '../../../../../common/config/public/ConfigClass'; import {RandomPhotoSettingsService} from './random-photo.settings.service'; import {I18n} from '@ngx-translate/i18n-polyfill'; +import {ClientConfig} from '../../../../../common/config/public/ClientConfig'; @Component({ selector: 'app-settings-random-photo', diff --git a/src/frontend/app/ui/settings/random-photo/random-photo.settings.service.ts b/src/frontend/app/ui/settings/random-photo/random-photo.settings.service.ts index f2fb525..f21a027 100644 --- a/src/frontend/app/ui/settings/random-photo/random-photo.settings.service.ts +++ b/src/frontend/app/ui/settings/random-photo/random-photo.settings.service.ts @@ -1,9 +1,9 @@ import {Injectable} from '@angular/core'; import {NetworkService} from '../../../model/network/network.service'; -import {ClientConfig} from '../../../../../common/config/public/ConfigClass'; import {SettingsService} from '../settings.service'; import {AbstractSettingsService} from '../_abstract/abstract.settings.service'; -import {ServerConfig} from '../../../../../common/config/private/IPrivateConfig'; +import {ServerConfig} from '../../../../../common/config/private/PrivateConfig'; +import {ClientConfig} from '../../../../../common/config/public/ClientConfig'; @Injectable() export class RandomPhotoSettingsService extends AbstractSettingsService { diff --git a/src/frontend/app/ui/settings/search/search.settings.component.ts b/src/frontend/app/ui/settings/search/search.settings.component.ts index 67cf5c5..0d80e2a 100644 --- a/src/frontend/app/ui/settings/search/search.settings.component.ts +++ b/src/frontend/app/ui/settings/search/search.settings.component.ts @@ -3,9 +3,9 @@ import {SettingsComponent} from '../_abstract/abstract.settings.component'; import {AuthenticationService} from '../../../model/network/authentication.service'; import {NavigationService} from '../../../model/navigation.service'; import {NotificationService} from '../../../model/notification.service'; -import {ClientConfig} from '../../../../../common/config/public/ConfigClass'; import {SearchSettingsService} from './search.settings.service'; import {I18n} from '@ngx-translate/i18n-polyfill'; +import {ClientConfig} from '../../../../../common/config/public/ClientConfig'; @Component({ selector: 'app-settings-search', diff --git a/src/frontend/app/ui/settings/search/search.settings.service.ts b/src/frontend/app/ui/settings/search/search.settings.service.ts index 8e0768e..50e0741 100644 --- a/src/frontend/app/ui/settings/search/search.settings.service.ts +++ b/src/frontend/app/ui/settings/search/search.settings.service.ts @@ -1,9 +1,9 @@ import {Injectable} from '@angular/core'; import {NetworkService} from '../../../model/network/network.service'; -import {ClientConfig} from '../../../../../common/config/public/ConfigClass'; import {SettingsService} from '../settings.service'; import {AbstractSettingsService} from '../_abstract/abstract.settings.service'; -import {ServerConfig} from '../../../../../common/config/private/IPrivateConfig'; +import {ServerConfig} from '../../../../../common/config/private/PrivateConfig'; +import {ClientConfig} from '../../../../../common/config/public/ClientConfig'; @Injectable() export class SearchSettingsService extends AbstractSettingsService { diff --git a/src/frontend/app/ui/settings/settings.service.ts b/src/frontend/app/ui/settings/settings.service.ts index 1c60406..89617b9 100644 --- a/src/frontend/app/ui/settings/settings.service.ts +++ b/src/frontend/app/ui/settings/settings.service.ts @@ -1,16 +1,17 @@ import {Injectable} from '@angular/core'; import {BehaviorSubject} from 'rxjs'; import {NetworkService} from '../../model/network/network.service'; -import {IPrivateConfig} from '../../../../common/config/private/IPrivateConfig'; -import {PrivateConfigDefaultsClass} from '../../../../common/config/private/PrivateConfigDefaultsClass'; + +import {WebConfig} from '../../../../common/config/private/WebConfig'; +import {WebConfigClassBuilder} from 'typeconfig/src/decorators/builders/WebConfigClassBuilder'; @Injectable() export class SettingsService { - public settings: BehaviorSubject; + public settings: BehaviorSubject; private fetchingSettings = false; constructor(private _networkService: NetworkService) { - this.settings = new BehaviorSubject(new PrivateConfigDefaultsClass()); + this.settings = new BehaviorSubject(new WebConfig()); } public async getSettings(): Promise { @@ -19,7 +20,9 @@ export class SettingsService { } this.fetchingSettings = true; try { - this.settings.next(await this._networkService.getJson>('/settings')); + const wcg = WebConfigClassBuilder.attachInterface(new WebConfig()); + wcg.load(await this._networkService.getJson>('/settings')); + this.settings.next(wcg); } catch (e) { console.error(e); } diff --git a/src/frontend/app/ui/settings/share/share.settings.component.ts b/src/frontend/app/ui/settings/share/share.settings.component.ts index 0954334..73a521a 100644 --- a/src/frontend/app/ui/settings/share/share.settings.component.ts +++ b/src/frontend/app/ui/settings/share/share.settings.component.ts @@ -3,9 +3,9 @@ import {SettingsComponent} from '../_abstract/abstract.settings.component'; import {AuthenticationService} from '../../../model/network/authentication.service'; import {NavigationService} from '../../../model/navigation.service'; import {NotificationService} from '../../../model/notification.service'; -import {ClientConfig} from '../../../../../common/config/public/ConfigClass'; import {ShareSettingsService} from './share.settings.service'; import {I18n} from '@ngx-translate/i18n-polyfill'; +import {ClientConfig} from '../../../../../common/config/public/ClientConfig'; @Component({ selector: 'app-settings-share', diff --git a/src/frontend/app/ui/settings/share/share.settings.service.ts b/src/frontend/app/ui/settings/share/share.settings.service.ts index 4278a26..06e6f0b 100644 --- a/src/frontend/app/ui/settings/share/share.settings.service.ts +++ b/src/frontend/app/ui/settings/share/share.settings.service.ts @@ -1,9 +1,9 @@ import {Injectable} from '@angular/core'; import {NetworkService} from '../../../model/network/network.service'; -import {ClientConfig} from '../../../../../common/config/public/ConfigClass'; import {SettingsService} from '../settings.service'; import {AbstractSettingsService} from '../_abstract/abstract.settings.service'; -import {ServerConfig} from '../../../../../common/config/private/IPrivateConfig'; +import {ClientConfig} from '../../../../../common/config/public/ClientConfig'; +import {ServerConfig} from '../../../../../common/config/private/PrivateConfig'; @Injectable() export class ShareSettingsService extends AbstractSettingsService { diff --git a/src/frontend/app/ui/settings/thumbnail/thumbnail.settings.component.ts b/src/frontend/app/ui/settings/thumbnail/thumbnail.settings.component.ts index 14ef2ee..efb0cf8 100644 --- a/src/frontend/app/ui/settings/thumbnail/thumbnail.settings.component.ts +++ b/src/frontend/app/ui/settings/thumbnail/thumbnail.settings.component.ts @@ -3,13 +3,13 @@ import {SettingsComponent} from '../_abstract/abstract.settings.component'; import {AuthenticationService} from '../../../model/network/authentication.service'; import {NavigationService} from '../../../model/navigation.service'; import {NotificationService} from '../../../model/notification.service'; -import {ClientConfig} from '../../../../../common/config/public/ConfigClass'; import {ThumbnailSettingsService} from './thumbnail.settings.service'; import {I18n} from '@ngx-translate/i18n-polyfill'; -import {ServerConfig} from '../../../../../common/config/private/IPrivateConfig'; import {DefaultsJobs, JobDTO} from '../../../../../common/entities/job/JobDTO'; import {ScheduledJobsService} from '../scheduled-jobs.service'; import {JobProgressStates} from '../../../../../common/entities/job/JobProgressDTO'; +import {ServerConfig} from '../../../../../common/config/private/PrivateConfig'; +import {ClientConfig} from '../../../../../common/config/public/ClientConfig'; @Component({ selector: 'app-settings-thumbnail', diff --git a/src/frontend/app/ui/settings/thumbnail/thumbnail.settings.service.ts b/src/frontend/app/ui/settings/thumbnail/thumbnail.settings.service.ts index 83984e6..a02da28 100644 --- a/src/frontend/app/ui/settings/thumbnail/thumbnail.settings.service.ts +++ b/src/frontend/app/ui/settings/thumbnail/thumbnail.settings.service.ts @@ -1,9 +1,9 @@ import {Injectable} from '@angular/core'; import {NetworkService} from '../../../model/network/network.service'; -import {ClientConfig} from '../../../../../common/config/public/ConfigClass'; import {AbstractSettingsService} from '../_abstract/abstract.settings.service'; import {SettingsService} from '../settings.service'; -import {ServerConfig} from '../../../../../common/config/private/IPrivateConfig'; +import {ServerConfig} from '../../../../../common/config/private/PrivateConfig'; +import {ClientConfig} from '../../../../../common/config/public/ClientConfig'; @Injectable() export class ThumbnailSettingsService diff --git a/src/frontend/app/ui/settings/usermanager/usermanager.settings.service.ts b/src/frontend/app/ui/settings/usermanager/usermanager.settings.service.ts index b89b83b..bd4a2ed 100644 --- a/src/frontend/app/ui/settings/usermanager/usermanager.settings.service.ts +++ b/src/frontend/app/ui/settings/usermanager/usermanager.settings.service.ts @@ -1,7 +1,9 @@ import {Injectable} from '@angular/core'; import {UserDTO} from '../../../../../common/entities/UserDTO'; import {NetworkService} from '../../../model/network/network.service'; -import {IPrivateConfig} from '../../../../../common/config/private/IPrivateConfig'; + +import {WebConfig} from '../../../../../common/config/private/WebConfig'; + @Injectable() export class UserManagerSettingsService { @@ -15,7 +17,7 @@ export class UserManagerSettingsService { } public async getSettings(): Promise { - return (await this._networkService.getJson>('/settings')).Client.authenticationRequired; + return (await this._networkService.getJson>('/settings')).Client.authenticationRequired; } public updateSettings(settings: boolean): Promise { diff --git a/src/frontend/app/ui/settings/video/video.settings.component.ts b/src/frontend/app/ui/settings/video/video.settings.component.ts index 7b986af..cb9de35 100644 --- a/src/frontend/app/ui/settings/video/video.settings.component.ts +++ b/src/frontend/app/ui/settings/video/video.settings.component.ts @@ -4,12 +4,12 @@ import {SettingsComponent} from '../_abstract/abstract.settings.component'; import {AuthenticationService} from '../../../model/network/authentication.service'; import {NavigationService} from '../../../model/navigation.service'; import {NotificationService} from '../../../model/notification.service'; -import {ClientConfig} from '../../../../../common/config/public/ConfigClass'; import {I18n} from '@ngx-translate/i18n-polyfill'; import {ScheduledJobsService} from '../scheduled-jobs.service'; import {DefaultsJobs, JobDTO} from '../../../../../common/entities/job/JobDTO'; -import {ServerConfig} from '../../../../../common/config/private/IPrivateConfig'; import {JobProgressStates} from '../../../../../common/entities/job/JobProgressDTO'; +import {ServerConfig} from '../../../../../common/config/private/PrivateConfig'; +import {ClientConfig} from '../../../../../common/config/public/ClientConfig'; @Component({ diff --git a/src/frontend/app/ui/settings/video/video.settings.service.ts b/src/frontend/app/ui/settings/video/video.settings.service.ts index 92e84e7..bfe0c34 100644 --- a/src/frontend/app/ui/settings/video/video.settings.service.ts +++ b/src/frontend/app/ui/settings/video/video.settings.service.ts @@ -1,9 +1,9 @@ import {Injectable} from '@angular/core'; import {NetworkService} from '../../../model/network/network.service'; -import {ClientConfig} from '../../../../../common/config/public/ConfigClass'; import {SettingsService} from '../settings.service'; import {AbstractSettingsService} from '../_abstract/abstract.settings.service'; -import {ServerConfig} from '../../../../../common/config/private/IPrivateConfig'; +import {ClientConfig} from '../../../../../common/config/public/ClientConfig'; +import {ServerConfig} from '../../../../../common/config/private/PrivateConfig'; @Injectable() export class VideoSettingsService extends AbstractSettingsService<{ server: ServerConfig.VideoConfig, client: ClientConfig.VideoConfig }> { diff --git a/test/backend/SQLTestHelper.ts b/test/backend/SQLTestHelper.ts index d5f014e..afe2923 100644 --- a/test/backend/SQLTestHelper.ts +++ b/test/backend/SQLTestHelper.ts @@ -3,7 +3,7 @@ import * as path from 'path'; import * as util from 'util'; import * as rimraf from 'rimraf'; import {SQLConnection} from '../../src/backend/model/database/sql/SQLConnection'; -import {ServerConfig} from '../../src/common/config/private/IPrivateConfig'; +import {ServerConfig} from '../../src/common/config/private/PrivateConfig'; declare let describe: any; const savedDescribe = describe; diff --git a/test/backend/integration/model/sql/typeorm.ts b/test/backend/integration/model/sql/typeorm.ts index 2c27bc9..7b9e352 100644 --- a/test/backend/integration/model/sql/typeorm.ts +++ b/test/backend/integration/model/sql/typeorm.ts @@ -17,7 +17,7 @@ import { } from '../../../../../src/backend/model/database/sql/enitites/PhotoEntity'; import {MediaDimensionEntity} from '../../../../../src/backend/model/database/sql/enitites/MediaEntity'; import {VersionEntity} from '../../../../../src/backend/model/database/sql/enitites/VersionEntity'; -import {ServerConfig} from '../../../../../src/common/config/private/IPrivateConfig'; +import {ServerConfig} from '../../../../../src/common/config/private/PrivateConfig'; const rimrafPR = util.promisify(rimraf); diff --git a/test/backend/integration/routers/GalleryRouter.ts b/test/backend/integration/routers/GalleryRouter.ts index 5b59eb3..92ca549 100644 --- a/test/backend/integration/routers/GalleryRouter.ts +++ b/test/backend/integration/routers/GalleryRouter.ts @@ -1,5 +1,4 @@ import {Config} from '../../../../src/common/config/private/Config'; -import {ServerConfig} from '../../../../src/common/config/private/IPrivateConfig'; import {Server} from '../../../../src/backend/server'; import * as path from 'path'; import * as util from 'util'; @@ -8,6 +7,7 @@ import * as rimraf from 'rimraf'; import {SQLConnection} from '../../../../src/backend/model/database/sql/SQLConnection'; import {SuperAgentStatic} from 'superagent'; import {ProjectPath} from '../../../../src/backend/ProjectPath'; +import {ServerConfig} from '../../../../src/common/config/private/PrivateConfig'; process.env.NODE_ENV = 'test'; diff --git a/test/backend/integration/routers/SharingRouter.ts b/test/backend/integration/routers/SharingRouter.ts index 0796a02..f4a25de 100644 --- a/test/backend/integration/routers/SharingRouter.ts +++ b/test/backend/integration/routers/SharingRouter.ts @@ -1,5 +1,4 @@ import {Config} from '../../../../src/common/config/private/Config'; -import {ServerConfig} from '../../../../src/common/config/private/IPrivateConfig'; import {Server} from '../../../../src/backend/server'; import {LoginCredential} from '../../../../src/common/entities/LoginCredential'; import {UserDTO, UserRoles} from '../../../../src/common/entities/UserDTO'; @@ -13,6 +12,7 @@ import {SuperAgentStatic} from 'superagent'; import {RouteTestingHelper} from './RouteTestingHelper'; import {QueryParams} from '../../../../src/common/QueryParams'; import {ErrorCodes} from '../../../../src/common/entities/Error'; +import {ServerConfig} from '../../../../src/common/config/private/PrivateConfig'; process.env.NODE_ENV = 'test'; @@ -109,7 +109,7 @@ describe('SharingRouter', () => { }); it('should login with no-password share', async () => { - const sharing = await RouteTestingHelper.createSharing(testUser,); + const sharing = await RouteTestingHelper.createSharing(testUser); const res = await shareLogin(server, sharing.sharingKey, sharing.password); shouldBeValidUser(res, RouteTestingHelper.getExpectedSharingUser(sharing)); }); diff --git a/test/backend/integration/routers/UserRouter.ts b/test/backend/integration/routers/UserRouter.ts index 9348512..bf0b628 100644 --- a/test/backend/integration/routers/UserRouter.ts +++ b/test/backend/integration/routers/UserRouter.ts @@ -1,5 +1,4 @@ import {Config} from '../../../../src/common/config/private/Config'; -import {ServerConfig} from '../../../../src/common/config/private/IPrivateConfig'; import {Server} from '../../../../src/backend/server'; import {LoginCredential} from '../../../../src/common/entities/LoginCredential'; import {UserDTO, UserRoles} from '../../../../src/common/entities/UserDTO'; @@ -13,6 +12,7 @@ import {Utils} from '../../../../src/common/Utils'; import {SuperAgentStatic} from 'superagent'; import {RouteTestingHelper} from './RouteTestingHelper'; import {ErrorCodes} from '../../../../src/common/entities/Error'; +import {ServerConfig} from '../../../../src/common/config/private/PrivateConfig'; process.env.NODE_ENV = 'test'; diff --git a/test/backend/integration/routers/admin/SettingsRouter.ts b/test/backend/integration/routers/admin/SettingsRouter.ts index 5f24bfe..0d2ed87 100644 --- a/test/backend/integration/routers/admin/SettingsRouter.ts +++ b/test/backend/integration/routers/admin/SettingsRouter.ts @@ -2,9 +2,9 @@ import * as path from 'path'; import * as util from 'util'; import * as rimraf from 'rimraf'; import {Config} from '../../../../../src/common/config/private/Config'; -import {ServerConfig} from '../../../../../src/common/config/private/IPrivateConfig'; import {SQLConnection} from '../../../../../src/backend/model/database/sql/SQLConnection'; import {Server} from '../../../../../src/backend/server'; +import {ServerConfig} from '../../../../../src/common/config/private/PrivateConfig'; process.env.NODE_ENV = 'test'; const chai: any = require('chai'); @@ -32,7 +32,7 @@ describe('SettingsRouter', () => { describe('/GET settings', () => { it('it should GET all the books', async () => { Config.Client.authenticationRequired = false; - const originalSettings = Config.original(); + const originalSettings = await Config.original(); originalSettings.Server.sessionSecret = null; const srv = new Server(); await srv.onStarted.wait(); diff --git a/test/backend/unit/model/sql/IndexingManager.ts b/test/backend/unit/model/sql/IndexingManager.ts index 1cd4c0b..cc6e400 100644 --- a/test/backend/unit/model/sql/IndexingManager.ts +++ b/test/backend/unit/model/sql/IndexingManager.ts @@ -16,7 +16,7 @@ import {PersonManager} from '../../../../../src/backend/model/database/sql/Perso import {SQLTestHelper} from '../../../SQLTestHelper'; import {VersionManager} from '../../../../../src/backend/model/database/sql/VersionManager'; import {DiskMangerWorker} from '../../../../../src/backend/model/threading/DiskMangerWorker'; -import {ServerConfig} from '../../../../../src/common/config/private/IPrivateConfig'; +import {ServerConfig} from '../../../../../src/common/config/private/PrivateConfig'; class GalleryManagerTest extends GalleryManager {