1
0
mirror of https://github.com/bpatrik/pigallery2.git synced 2024-12-25 02:04:15 +02:00

upgrading to typeconfig2.0

This commit is contained in:
Patrik J. Braun 2020-01-28 18:36:52 +01:00
parent 1b43fb47f7
commit 4e3f464cf8
68 changed files with 930 additions and 684 deletions

180
MANPAGE.md Normal file
View File

@ -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: <appname> [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
<appname> can be configured through the configuration file, cli switches and environmental variables.
All settings are case sensitive.
Example for setting config MyConf through cli: '<appname> --MyConf=5'
and through env variable: 'SET MyConf=5' .
Default values can be also overwritten by prefixing the options with 'default-',
like '<appname> --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')
```

View File

@ -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);

View File

@ -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'));

6
package-lock.json generated
View File

@ -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"
}

View File

@ -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"
},

View File

@ -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: [

View File

@ -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<ConfigClass>(null, originalConf);
const message = new Message<PrivateConfigClass>(null, <any>originalConf.toJSON({attachDefaults: true}));
res.json(message);
}

View File

@ -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 {

View File

@ -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 = <ClientConfig.MapConfig>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 = <ClientConfig.MapConfig>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(<ClientConfig.MetaFileConfig>req.body.settings, original);
Config.Client.MetaFile = <ClientConfig.MetaFileConfig>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(<ClientConfig.SharingConfig>req.body.settings, original);
Config.Client.Sharing = <ClientConfig.SharingConfig>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(<ClientConfig.RandomPhotoConfig>req.body.settings, original);
Config.Client.RandomPhoto = <ClientConfig.RandomPhotoConfig>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(<ClientConfig.SearchConfig>req.body.settings, original);
Config.Client.Search = <ClientConfig.SearchConfig>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(<ClientConfig.FacesConfig>req.body.settings, original);
Config.Client.Faces = <ClientConfig.FacesConfig>req.body.settings;
@ -250,7 +250,7 @@ export class SettingsMWs {
try {
Config.Client.authenticationRequired = <boolean>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 = <boolean>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;

View File

@ -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 {

View File

@ -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]';

View File

@ -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');
}
}

View File

@ -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 {

View File

@ -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;
}
}

View File

@ -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 {

View File

@ -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 {

View File

@ -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;

View File

@ -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 {

View File

@ -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 {

View File

@ -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 {

View File

@ -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;

View File

@ -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();
});

View File

@ -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');

View File

@ -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<PrivateConfigClass & IConfigClass> {
const pc = ConfigClassBuilder.attachInterface(new PrivateConfigClass());
await pc.load();
return pc;
}
}
export const Config = ConfigClassBuilder.attachInterface(new PrivateConfigClass());
Config.loadSync();

View File

@ -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() {
(<any>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 (<any>this)['__NOTE'];
}
}

View File

@ -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;
}

View File

@ -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<DatabaseType, IPrivateConfig>({
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;
}

View File

@ -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']]
}
}
]
}
};
}

View File

@ -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();
}

View File

@ -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();
}
}

View File

@ -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);
}

View File

@ -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: []
};
}

View File

@ -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;

View File

@ -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()

View File

@ -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<T extends { [key: string]: any }, S extends AbstractSettingsService<T> = AbstractSettingsService<T>>
@ -44,7 +45,7 @@ export abstract class SettingsComponent<T extends { [key: string]: any }, S exte
public _settingsService: S,
protected notification: NotificationService,
public i18n: I18n,
private sliceFN?: (s: IPrivateConfig) => 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<T extends { [key: string]: any }, S exte
return this.hasAvailableSettings;
}
onNewSettings = (s: IPrivateConfig) => {
onNewSettings = (s: WebConfig) => {
this.settings = Utils.clone(this.sliceFN(s));
this.original = Utils.clone(this.settings);
this.ngOnChanges();

View File

@ -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',

View File

@ -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<ServerConfig.DataBaseConfig> {

View File

@ -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',

View File

@ -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<ClientConfig.FacesConfig> {

View File

@ -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',

View File

@ -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<ServerConfig.IndexingConfig> {

View File

@ -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',

View File

@ -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<ServerConfig.JobConfig> {

View File

@ -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({

View File

@ -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<ClientConfig.MapConfig> {

View File

@ -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({

View File

@ -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<ClientConfig.MetaFileConfig> {

View File

@ -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({

View File

@ -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<{

View File

@ -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',

View File

@ -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<ClientConfig.SharingConfig> {

View File

@ -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',

View File

@ -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<ClientConfig.SearchConfig> {

View File

@ -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<IPrivateConfig>;
public settings: BehaviorSubject<WebConfig>;
private fetchingSettings = false;
constructor(private _networkService: NetworkService) {
this.settings = new BehaviorSubject<IPrivateConfig>(new PrivateConfigDefaultsClass());
this.settings = new BehaviorSubject<WebConfig>(new WebConfig());
}
public async getSettings(): Promise<void> {
@ -19,7 +20,9 @@ export class SettingsService {
}
this.fetchingSettings = true;
try {
this.settings.next(await this._networkService.getJson<Promise<IPrivateConfig>>('/settings'));
const wcg = WebConfigClassBuilder.attachInterface(new WebConfig());
wcg.load(await this._networkService.getJson<Promise<WebConfig>>('/settings'));
this.settings.next(wcg);
} catch (e) {
console.error(e);
}

View File

@ -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',

View File

@ -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<ClientConfig.SharingConfig> {

View File

@ -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',

View File

@ -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

View File

@ -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<boolean> {
return (await this._networkService.getJson<Promise<IPrivateConfig>>('/settings')).Client.authenticationRequired;
return (await this._networkService.getJson<Promise<WebConfig>>('/settings')).Client.authenticationRequired;
}
public updateSettings(settings: boolean): Promise<void> {

View File

@ -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({

View File

@ -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 }> {

View File

@ -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;

View File

@ -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);

View File

@ -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';

View File

@ -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));
});

View File

@ -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';

View File

@ -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();

View File

@ -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 {