From 9c4178e50855ac0167305d20544403ab2bb48aad Mon Sep 17 00:00:00 2001 From: "Patrik J. Braun" Date: Wed, 28 Dec 2022 19:12:18 +0100 Subject: [PATCH] Rewriting config structure and UI #569 --- MANPAGE.md | 957 ++++++++++-------- benchmark/BenchmarkRunner.ts | 11 +- benchmark/index.ts | 7 +- package-lock.json | 14 +- package.json | 2 +- src/backend/ProjectPath.ts | 6 +- src/backend/middlewares/AlbumMWs.ts | 16 +- src/backend/middlewares/GalleryMWs.ts | 10 +- src/backend/middlewares/MetaFileMWs.ts | 4 +- src/backend/middlewares/RenderingMWs.ts | 2 +- src/backend/middlewares/SharingMWs.ts | 10 +- src/backend/middlewares/admin/AdminMWs.ts | 4 +- src/backend/middlewares/admin/SettingsMWs.ts | 512 +--------- .../thumbnail/PhotoConverterMWs.ts | 6 +- .../thumbnail/ThumbnailGeneratorMWs.ts | 14 +- .../middlewares/user/AuthenticationMWs.ts | 22 +- src/backend/middlewares/user/UserMWs.ts | 8 +- src/backend/model/Localizations.ts | 4 +- .../model/database/memory/GalleryManager.ts | 22 +- .../model/database/sql/GalleryManager.ts | 24 +- .../model/database/sql/PreviewManager.ts | 16 +- .../model/database/sql/SQLConnection.ts | 12 +- .../model/database/sql/SearchManager.ts | 34 +- .../model/database/sql/SharingManager.ts | 2 +- .../database/sql/enitites/EntityUtils.ts | 4 +- .../model/diagnostics/ConfigDiagnostics.ts | 177 ++-- .../model/fileprocessing/GPXProcessing.ts | 8 +- .../model/fileprocessing/PhotoProcessing.ts | 30 +- .../model/fileprocessing/VideoProcessing.ts | 30 +- src/backend/model/jobs/JobManager.ts | 6 +- src/backend/model/jobs/JobProgressManager.ts | 2 +- src/backend/model/jobs/jobs/DBResetJob.ts | 2 +- src/backend/model/jobs/jobs/FileJob.ts | 10 +- .../model/jobs/jobs/GPXCompressionJob.ts | 3 +- src/backend/model/jobs/jobs/IndexingJob.ts | 2 +- src/backend/model/jobs/jobs/JobProgress.ts | 2 +- .../model/jobs/jobs/PhotoConvertingJob.ts | 4 +- .../model/jobs/jobs/PreviewFillingJob.ts | 2 +- .../model/jobs/jobs/PreviewResetJob.ts | 2 +- .../model/jobs/jobs/ThumbnailGenerationJob.ts | 6 +- .../model/jobs/jobs/VideoConvertingJob.ts | 2 +- .../model/threading/DiskMangerWorker.ts | 16 +- src/backend/model/threading/MetadataLoader.ts | 8 +- src/backend/routes/AlbumRouter.ts | 6 +- src/backend/routes/ErrorRouter.ts | 2 +- src/backend/routes/GalleryRouter.ts | 30 +- src/backend/routes/LoggerRouter.ts | 2 +- src/backend/routes/NotificationRouter.ts | 2 +- src/backend/routes/PersonRouter.ts | 10 +- src/backend/routes/PublicRouter.ts | 37 +- src/backend/routes/SharingRouter.ts | 12 +- src/backend/routes/UserRouter.ts | 14 +- src/backend/routes/admin/AdminRouter.ts | 12 +- src/backend/routes/admin/SettingsRouter.ts | 119 +-- src/backend/server.ts | 14 +- src/common/CookieNames.ts | 2 +- src/common/SupportedFormats.ts | 8 +- src/common/Utils.ts | 14 +- src/common/config/private/Config.ts | 26 +- src/common/config/private/PrivateConfig.ts | 690 ++++++++++--- src/common/config/private/WebConfig.ts | 18 +- src/common/config/public/ClientConfig.ts | 891 ++++++++++++---- src/common/config/public/Config.ts | 8 +- .../entities/settings/BasicConfigDTO.ts | 32 - .../entities/settings/OtherConfigDTO.ts | 7 - src/frontend/app/app.component.ts | 23 +- src/frontend/app/app.module.ts | 256 +++-- src/frontend/app/model/navigation.service.ts | 6 +- .../model/network/authentication.service.ts | 8 +- .../app/model/network/network.service.spec.ts | 12 +- .../app/model/network/network.service.ts | 33 +- .../app/model/network/user.service.ts | 27 +- src/frontend/app/model/query.service.ts | 4 +- src/frontend/app/pipes/GPXFilesFilterPipe.ts | 2 +- .../app/ui/admin/admin.component.html | 152 +-- src/frontend/app/ui/admin/admin.component.ts | 21 +- .../app/ui/duplicates/duplicates.component.ts | 2 +- src/frontend/app/ui/faces/Person.ts | 4 +- .../app/ui/faces/face/face.component.ts | 2 +- src/frontend/app/ui/frame/frame.component.ts | 12 +- src/frontend/app/ui/gallery/Media.ts | 12 +- src/frontend/app/ui/gallery/MediaIcon.ts | 14 +- .../app/ui/gallery/cache.gallery.service.ts | 19 +- .../app/ui/gallery/content.service.ts | 2 +- .../app/ui/gallery/gallery.component.html | 2 +- .../app/ui/gallery/gallery.component.ts | 10 +- .../ui/gallery/grid/grid.gallery.component.ts | 6 +- .../photo/photo.grid.gallery.component.ts | 6 +- .../controls.lightbox.gallery.component.ts | 6 +- .../info-panel.lightbox.gallery.component.ts | 6 +- .../media/media.lightbox.gallery.component.ts | 6 +- .../lightbox.map.gallery.component.ts | 12 +- .../ui/gallery/map/map.gallery.component.ts | 2 +- .../app/ui/gallery/map/map.service.ts | 18 +- .../navigator.gallery.component.html | 6 +- .../navigator/navigator.gallery.component.ts | 6 +- .../ui/gallery/navigator/sorting.service.ts | 10 +- .../random-query-builder.gallery.component.ts | 2 +- .../search-field-base.gallery.component.ts | 4 +- .../search/search.gallery.component.ts | 30 +- .../gallery/share/share.gallery.component.ts | 6 +- .../app/ui/gallery/thumbnailLoader.service.ts | 2 +- .../app/ui/language/language.component.ts | 4 +- src/frontend/app/ui/login/login.component.ts | 2 +- .../_abstract/abstract.settings.component.ts | 196 ++-- .../_abstract/abstract.settings.service.ts | 7 - .../settings-entry.component.html | 200 +++- .../settings-entry.component.ts | 223 ++-- .../albums/albums.settings.component.ts | 21 +- .../albums/albums.settings.service.ts | 4 +- .../basic/basic.settings.component.html | 106 -- .../basic/basic.settings.component.ts | 85 -- .../database/database.settings.component.html | 2 +- .../database/database.settings.component.ts | 24 +- .../faces/faces.settings.component.ts | 25 +- .../settings/faces/faces.settings.service.ts | 6 +- .../indexing/indexing.settings.component.html | 2 +- .../indexing/indexing.settings.component.ts | 32 +- .../indexing/indexing.settings.service.ts | 2 +- .../settings/jobs/jobs.settings.component.ts | 10 +- .../ui/settings/jobs/jobs.settings.service.ts | 2 +- .../job-progress.settings.component.html | 2 +- .../settings/map/map.settings.component.html | 4 +- .../ui/settings/map/map.settings.component.ts | 21 +- .../ui/settings/map/map.settings.service.ts | 2 +- .../metafile.settings.component.html | 17 +- .../metafiles/metafile.settings.component.ts | 26 +- .../metafiles/metafile.settings.service.ts | 14 +- .../other/other.settings.component.html | 8 +- .../other/other.settings.component.ts | 41 +- .../settings/other/other.settings.service.ts | 5 +- .../photo/photo.settings.component.html | 2 - .../photo/photo.settings.component.ts | 37 +- .../settings/photo/photo.settings.service.ts | 10 +- .../preview/preview.settings.component.html | 4 +- .../preview/preview.settings.component.ts | 28 +- .../preview/preview.settings.service.ts | 2 +- .../random-photo.settings.component.ts | 21 +- .../random-photo.settings.service.ts | 4 +- .../search/search.settings.component.html | 8 +- .../search/search.settings.component.ts | 21 +- .../search/search.settings.service.ts | 4 +- .../app/ui/settings/settings.service.ts | 34 +- .../share/share.settings.component.html | 1 - .../share/share.settings.component.ts | 26 +- .../settings/share/share.settings.service.ts | 8 +- .../template.component.css} | 0 .../settings/template/template.component.html | 81 ++ .../settings/template/template.component.ts | 78 ++ .../template.settings.service.ts} | 14 +- .../thumbnail.settings.component.html | 5 +- .../thumbnail/thumbnail.settings.component.ts | 15 +- .../thumbnail/thumbnail.settings.service.ts | 13 +- .../usermanager.settings.service.ts | 2 +- .../video/video.settings.component.html | 6 - .../video/video.settings.component.ts | 17 +- .../settings/video/video.settings.service.ts | 10 +- .../ui/sharelogin/share-login.component.ts | 2 +- src/frontend/index.html | 2 +- test/backend/DBTestHelper.ts | 10 +- test/backend/integration/model/sql/typeorm.ts | 4 +- .../integration/routers/GalleryRouter.ts | 18 +- .../integration/routers/SharingRouter.ts | 12 +- .../backend/integration/routers/UserRouter.ts | 46 +- .../routers/admin/SettingsRouter.ts | 12 +- .../middlewares/user/AuthenticationMWs.ts | 2 +- .../fileprocessing/PhotoProcessing.spec.ts | 16 +- .../fileprocessing/VideoProcessing.spec.ts | 8 +- .../unit/model/sql/IndexingManager.spec.ts | 42 +- .../unit/model/sql/PreviewManager.spec.ts | 26 +- .../unit/model/sql/SearchManager.spec.ts | 26 +- .../model/threading/DiskMangerWorker.spec.ts | 8 +- .../model/threading/MetaDataLoader.spec.ts | 6 +- 173 files changed, 3572 insertions(+), 2954 deletions(-) delete mode 100644 src/common/entities/settings/BasicConfigDTO.ts delete mode 100644 src/common/entities/settings/OtherConfigDTO.ts delete mode 100644 src/frontend/app/ui/settings/basic/basic.settings.component.html delete mode 100644 src/frontend/app/ui/settings/basic/basic.settings.component.ts rename src/frontend/app/ui/settings/{basic/basic.settings.component.css => template/template.component.css} (100%) create mode 100644 src/frontend/app/ui/settings/template/template.component.html create mode 100644 src/frontend/app/ui/settings/template/template.component.ts rename src/frontend/app/ui/settings/{basic/basic.settings.service.ts => template/template.settings.service.ts} (53%) diff --git a/MANPAGE.md b/MANPAGE.md index be0315b4..27ef21fa 100644 --- a/MANPAGE.md +++ b/MANPAGE.md @@ -26,471 +26,562 @@ 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, converted photos, videos will be stored here (write permission required) (default: 'demo/tmp') - --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-Video-transcoding-crf Constant Rate Factor. The range of the CRF scale is 0–51, where 0 is lossless, 23 is the default, and 51 is worst quality possible. (default: 23) - --Server-Media-Video-transcoding-preset A preset is a collection of options that will provide a certain encoding speed to compression ratio (default: 'medium') - --Server-Media-Video-transcoding-customOptions It will be sent to ffmpeg as it is, as custom options. (default: []) - --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-Preview-SearchQuery (default: {"type":100,"text":""}) - --Server-Preview-Sorting (default: [6,4]) - --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-sqlite-DBFileName (default: 'sqlite.db') - --Server-Database-mysql-host (default: 'localhost') - --Server-Database-mysql-port (default: 3306) - --Server-Database-mysql-database (default: 'pigallery2') - --Server-Database-mysql-username (default: '') - --Server-Database-mysql-password (default: '') - --Server-Database-enforcedUsers Creates these users in the DB if they do not exist. If a user with this name exist, it wont be overwritten, even if the role is different. (default: []) - --Server-Sharing-updateTimeout (default: 300000) - --Server-sessionTimeout unit: ms (default: 604800000) - --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: [".Trash-1000",".dtrash","$RECYCLE.BIN"]) - --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-Log-logServerTiming (default: false) - --Server-Jobs-maxSavedProgress Job history size (default: 10) - --Server-Jobs-scheduled (default: [{"name":"Indexing","jobName":"Indexing","config":{"indexChangesOnly":true},"allowParallelRun":false,"trigger":{"type":1}},{"name":"Preview Filling","jobName":"Preview Filling","config":{},"allowParallelRun":false,"trigger":{"type":1}},{"name":"Thumbnail Generation","jobName":"Thumbnail Generation","config":{"sizes":[240],"indexedOnly":true},"allowParallelRun":false,"trigger":{"type":4,"afterScheduleName":"Preview Filling"}},{"name":"Photo Converting","jobName":"Photo Converting","config":{"indexedOnly":true},"allowParallelRun":false,"trigger":{"type":4,"afterScheduleName":"Thumbnail Generation"}},{"name":"Video Converting","jobName":"Video Converting","config":{"indexedOnly":true},"allowParallelRun":false,"trigger":{"type":4,"afterScheduleName":"Photo Converting"}},{"name":"Temp Folder Cleaning","jobName":"Temp Folder Cleaning","config":{"indexedOnly":true},"allowParallelRun":false,"trigger":{"type":4,"afterScheduleName":"Video Converting"}}]) - --Client-applicationTitle (default: 'PiGallery 2') - --Client-publicUrl (default: '') - --Client-urlBase (default: '') - --Client-Search-enabled (default: true) - --Client-Search-searchCacheTimeout (default: 3600000) - --Client-Search-AutoComplete-enabled (default: true) - --Client-Search-AutoComplete-targetItemsPerCategory (default: 5) - --Client-Search-AutoComplete-maxItems (default: 30) - --Client-Search-AutoComplete-cacheTimeout (default: 3600000) - --Client-Search-maxMediaResult (default: 10000) - --Client-Search-listDirectories Search returns also with directories, not just media (default: false) - --Client-Search-listMetafiles Search also returns with metafiles from directories that contain a media file of the matched search result (default: true) - --Client-Search-maxDirectoryResult (default: 200) - --Client-Sharing-enabled (default: true) - --Client-Sharing-passwordProtected (default: true) - --Client-Album-enabled (default: true) - --Client-Map-enabled (default: true) - --Client-Map-maxPreviewMarkers Maximum number of markers to be shown on the map preview on the gallery page. (default: 50) - --Client-Map-useImageMarkers (default: true) - --Client-Map-mapProvider (default: 'OpenStreetMap') - --Client-Map-mapboxAccessToken (default: '') - --Client-Map-customLayers (default: [{"name":"street","url":""}]) - --Client-RandomPhoto-enabled Enables random link generation. NOTE: With the current implementation, it poses a security risk. See https://github.com/bpatrik/pigallery2/issues/392 (default: false) - --Client-Other-customHTMLHead (default: '') - --Client-Other-enableCache (default: true) - --Client-Other-enableOnScrollRendering (default: true) - --Client-Other-defaultPhotoSortingMethod (default: 'ascDate') - --Client-Other-enableDirectorySortingByDate If enabled directories will be sorted by date, like photos, otherwise by name. Directory date is the last modification time of that directory not the creation date of the oldest photo (default: false) - --Client-Other-enableOnScrollThumbnailPrioritising (default: true) - --Client-Other-NavBar-showItemCount (default: true) - --Client-Other-captionFirstNaming (default: false) - --Client-Other-enableDownloadZip (default: false) - --Client-Other-enableDirectoryFlattening Adds a button to flattens the file structure, by listing the content of all subdirectories. (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-Media-Photo-loadFullImageOnZoom Enables loading the full resolution image on zoom in the ligthbox (preview). (default: true) - --Client-MetaFile-gpx Reads *.gpx files and renders them on the map. (default: true) - --Client-MetaFile-markdown Reads *.md files in a directory and shows the next to the map. (default: true) - --Client-MetaFile-pg2conf Reads *.pg2conf files (You can use it for custom sorting and save search (albums)). (default: true) - --Client-Faces-enabled (default: true) - --Client-Faces-keywordsToPersons (default: true) - --Client-Faces-writeAccessMinRole (default: 'Admin') - --Client-Faces-readAccessMinRole (default: 'User') + --Server-applicationTitle (default: 'PiGallery 2') + --Server-publicUrl (default: '') + --Server-urlBase (default: '') + --Server-apiPath PiGallery api path. (default: '/pgapi') + --Server-customHTMLHead (default: '') + --Server-sessionSecret (default: []) + --Server-sessionTimeout unit: ms (default: 604800000) + --Server-port (default: 80) + --Server-host (default: '0.0.0.0') + --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-Log-level (default: 'info') + --Server-Log-sqlLevel (default: 'error') + --Server-Log-logServerTiming (default: false) + --Users-authenticationRequired (default: true) + --Users-unAuthenticatedUserRole (default: 'Admin') + --Users-enforcedUsers Creates these users in the DB if they do not exist. If a user with this name exist, it wont be overwritten, even if the role is different. (default: []) + --Gallery-enableCache (default: true) + --Gallery-enableOnScrollRendering (default: true) + --Gallery-defaultPhotoSortingMethod Default sorting method for directory results (default: 'ascDate') + --Gallery-defaultSearchSortingMethod Default sorting method for search results (default: 'descDate') + --Gallery-enableDirectorySortingByDate If enabled directories will be sorted by date, like photos, otherwise by name. Directory date is the last modification time of that directory not the creation date of the oldest photo (default: false) + --Gallery-enableOnScrollThumbnailPrioritising (default: true) + --Gallery-NavBar-showItemCount (default: true) + --Gallery-NavBar-links List of the navigation bar links (default: [{"type":1},{"type":3},{"type":2}]) + --Gallery-captionFirstNaming (default: false) + --Gallery-enableDownloadZip (default: false) + --Gallery-enableDirectoryFlattening Adds a button to flattens the file structure, by listing the content of all subdirectories. (default: false) + --Gallery-defaultSlideshowSpeed Default time interval for displaying a photo in the slide show (default: 5) + --Media-Thumbnail-iconSize (default: 45) + --Media-Thumbnail-personThumbnailSize (default: 200) + --Media-Thumbnail-thumbnailSizes (default: [240,480]) + --Media-Thumbnail-useLanczos3 if true, 'lanczos3' will used to scale photos, otherwise faster but lowe quality 'nearest'. (default: true) + --Media-Thumbnail-quality Thumbnail image quality (default: 80) + --Media-Thumbnail-personFaceMargin (default: 0.6) + --Media-Video-enabled (default: true) + --Media-Video-supportedFormatsWithTranscoding Video formats that are supported after transcoding (with the build-in ffmpeg support) (default: ["avi","mkv","mov","wmv","flv","mts","m2ts","mpg","3gp","m4v","mpeg","vob","divx","xvid","ts"]) + --Media-Video-supportedFormats Video formats that are supported also without transcoding (default: ["mp4","webm","ogv","ogg"]) + --Media-Video-transcoding-bitRate (default: 5242880) + --Media-Video-transcoding-resolution (default: 720) + --Media-Video-transcoding-fps (default: 25) + --Media-Video-transcoding-codec (default: 'libx264') + --Media-Video-transcoding-format (default: 'mp4') + --Media-Video-transcoding-crf Constant Rate Factor. The range of the CRF scale is 0–51, where 0 is lossless, 23 is the default, and 51 is worst quality possible. (default: 23) + --Media-Video-transcoding-preset A preset is a collection of options that will provide a certain encoding speed to compression ratio (default: 'medium') + --Media-Video-transcoding-customOptions It will be sent to ffmpeg as it is, as custom options. (default: []) + --Media-Photo-Converting-enabled (default: true) + --Media-Photo-Converting-onTheFly Converts photos on the fly, when they are requested. (default: true) + --Media-Photo-Converting-resolution (default: 1080) + --Media-Photo-loadFullImageOnZoom Enables loading the full resolution image on zoom in the ligthbox (preview). (default: true) + --Media-Photo-supportedFormats (default: ["gif","jpeg","jpg","jpe","png","webp","svg"]) + --Media-folder Images are loaded from this folder (read permission required) (default: 'demo/images') + --Media-tempFolder Thumbnails, converted photos, videos will be stored here (write permission required) (default: 'demo/tmp') + --Media-photoMetadataSize Only this many bites will be loaded when scanning photo/video for metadata. (default: 524288) + --MetaFile-gpx Reads *.gpx files and renders them on the map. (default: true) + --MetaFile-GPXCompressing-enabled (default: true) + --MetaFile-GPXCompressing-onTheFly Compresses gpx files on-the-fly, when they are requested. (default: true) + --MetaFile-GPXCompressing-minDistance Filters out entry that are closer than this in meters. (default: 5) + --MetaFile-GPXCompressing-minTimeDistance Filters out entry that are closer than this in time in milliseconds. (default: 5000) + --MetaFile-markdown Reads *.md files in a directory and shows the next to the map. (default: true) + --MetaFile-pg2conf Reads *.pg2conf files (You can use it for custom sorting and save search (albums)). (default: true) + --MetaFile-supportedFormats (default: ["gpx","pg2conf","md"]) + --Album-enabled (default: true) + --Search-enabled (default: true) + --Search-searchCacheTimeout (default: 3600000) + --Search-AutoComplete-enabled (default: true) + --Search-AutoComplete-targetItemsPerCategory (default: 5) + --Search-AutoComplete-maxItems (default: 30) + --Search-AutoComplete-cacheTimeout (default: 3600000) + --Search-maxMediaResult (default: 10000) + --Search-listDirectories Search returns also with directories, not just media (default: false) + --Search-listMetafiles Search also returns with metafiles from directories that contain a media file of the matched search result (default: true) + --Search-maxDirectoryResult (default: 200) + --Sharing-enabled (default: true) + --Sharing-passwordProtected Enables password protected sharing links. (default: true) + --Sharing-updateTimeout After creating a sharing link, it can be updated for this long (in ms). (default: 300000) + --Map-enabled (default: true) + --Map-maxPreviewMarkers Maximum number of markers to be shown on the map preview on the gallery page. (default: 50) + --Map-useImageMarkers (default: true) + --Map-mapProvider (default: 'OpenStreetMap') + --Map-mapboxAccessToken (default: '') + --Map-customLayers (default: [{"name":"street","url":""}]) + --Faces-enabled (default: true) + --Faces-keywordsToPersons (default: true) + --Faces-writeAccessMinRole (default: 'Admin') + --Faces-readAccessMinRole (default: 'User') + --RandomPhoto-enabled Enables random link generation. (default: true) + --Database-type (default: 'sqlite') + --Database-dbFolder (default: 'db') + --Database-sqlite-DBFileName (default: 'sqlite.db') + --Database-mysql-host (default: 'localhost') + --Database-mysql-port (default: 3306) + --Database-mysql-database (default: 'pigallery2') + --Database-mysql-username (default: '') + --Database-mysql-password (default: '') + --Indexing-cachedFolderTimeout (default: 3600000) + --Indexing-reIndexingSensitivity (default: 'low') + --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: [".Trash-1000",".dtrash","$RECYCLE.BIN"]) + --Indexing-excludeFileList Any folder that contains a file with this name will be excluded from indexing. (default: []) + --Preview-SearchQuery (default: {"type":100,"text":""}) + --Preview-Sorting (default: [6,4]) + --Duplicates-listingLimit (default: 1000) + --Jobs-maxSavedProgress Job history size (default: 20) + --Jobs-mediaProcessingBatchSize Job loads this many photos or videos form the DB for processing (default: 1000) + --Jobs-scheduled (default: [{"name":"Indexing","jobName":"Indexing","config":{"indexChangesOnly":true},"allowParallelRun":false,"trigger":{"type":1}},{"name":"Preview Filling","jobName":"Preview Filling","config":{},"allowParallelRun":false,"trigger":{"type":1}},{"name":"Thumbnail Generation","jobName":"Thumbnail Generation","config":{"sizes":[240],"indexedOnly":true},"allowParallelRun":false,"trigger":{"type":4,"afterScheduleName":"Preview Filling"}},{"name":"Photo Converting","jobName":"Photo Converting","config":{"indexedOnly":true},"allowParallelRun":false,"trigger":{"type":4,"afterScheduleName":"Thumbnail Generation"}},{"name":"Video Converting","jobName":"Video Converting","config":{"indexedOnly":true},"allowParallelRun":false,"trigger":{"type":4,"afterScheduleName":"Photo Converting"}},{"name":"Temp Folder Cleaning","jobName":"Temp Folder Cleaning","config":{"indexedOnly":true},"allowParallelRun":false,"trigger":{"type":4,"afterScheduleName":"Video Converting"}}]) 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, converted photos, videos will be stored here (write permission required) (default: 'demo/tmp') - 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-Video-transcoding-crf Constant Rate Factor. The range of the CRF scale is 0–51, where 0 is lossless, 23 is the default, and 51 is worst quality possible. (default: 23) - Server-Media-Video-transcoding-preset A preset is a collection of options that will provide a certain encoding speed to compression ratio (default: 'medium') - Server-Media-Video-transcoding-customOptions It will be sent to ffmpeg as it is, as custom options. (default: []) - 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-Preview-SearchQuery (default: {"type":100,"text":""}) - Server-Preview-Sorting (default: [6,4]) - 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-sqlite-DBFileName (default: 'sqlite.db') - Server-Database-mysql-host (default: 'localhost') - MYSQL_HOST same as Server-Database-mysql-host - Server-Database-mysql-port (default: 3306) - MYSQL_PORT same as Server-Database-mysql-port - Server-Database-mysql-database (default: 'pigallery2') - MYSQL_DATABASE 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_PASSWORD same as Server-Database-mysql-password - Server-Database-enforcedUsers Creates these users in the DB if they do not exist. If a user with this name exist, it wont be overwritten, even if the role is different. (default: []) - Server-Sharing-updateTimeout (default: 300000) - Server-sessionTimeout unit: ms (default: 604800000) - 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: [".Trash-1000",".dtrash","$RECYCLE.BIN"]) - 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-Log-logServerTiming (default: false) - Server-Jobs-maxSavedProgress Job history size (default: 10) - Server-Jobs-scheduled (default: [{"name":"Indexing","jobName":"Indexing","config":{"indexChangesOnly":true},"allowParallelRun":false,"trigger":{"type":1}},{"name":"Preview Filling","jobName":"Preview Filling","config":{},"allowParallelRun":false,"trigger":{"type":1}},{"name":"Thumbnail Generation","jobName":"Thumbnail Generation","config":{"sizes":[240],"indexedOnly":true},"allowParallelRun":false,"trigger":{"type":4,"afterScheduleName":"Preview Filling"}},{"name":"Photo Converting","jobName":"Photo Converting","config":{"indexedOnly":true},"allowParallelRun":false,"trigger":{"type":4,"afterScheduleName":"Thumbnail Generation"}},{"name":"Video Converting","jobName":"Video Converting","config":{"indexedOnly":true},"allowParallelRun":false,"trigger":{"type":4,"afterScheduleName":"Photo Converting"}},{"name":"Temp Folder Cleaning","jobName":"Temp Folder Cleaning","config":{"indexedOnly":true},"allowParallelRun":false,"trigger":{"type":4,"afterScheduleName":"Video Converting"}}]) - Client-applicationTitle (default: 'PiGallery 2') - Client-publicUrl (default: '') - Client-urlBase (default: '') - Client-Search-enabled (default: true) - Client-Search-searchCacheTimeout (default: 3600000) - Client-Search-AutoComplete-enabled (default: true) - Client-Search-AutoComplete-targetItemsPerCategory (default: 5) - Client-Search-AutoComplete-maxItems (default: 30) - Client-Search-AutoComplete-cacheTimeout (default: 3600000) - Client-Search-maxMediaResult (default: 10000) - Client-Search-listDirectories Search returns also with directories, not just media (default: false) - Client-Search-listMetafiles Search also returns with metafiles from directories that contain a media file of the matched search result (default: true) - Client-Search-maxDirectoryResult (default: 200) - Client-Sharing-enabled (default: true) - Client-Sharing-passwordProtected (default: true) - Client-Album-enabled (default: true) - Client-Map-enabled (default: true) - Client-Map-maxPreviewMarkers Maximum number of markers to be shown on the map preview on the gallery page. (default: 50) - Client-Map-useImageMarkers (default: true) - Client-Map-mapProvider (default: 'OpenStreetMap') - Client-Map-mapboxAccessToken (default: '') - Client-Map-customLayers (default: [{"name":"street","url":""}]) - Client-RandomPhoto-enabled Enables random link generation. NOTE: With the current implementation, it poses a security risk. See https://github.com/bpatrik/pigallery2/issues/392 (default: false) - Client-Other-customHTMLHead (default: '') - Client-Other-enableCache (default: true) - Client-Other-enableOnScrollRendering (default: true) - Client-Other-defaultPhotoSortingMethod (default: 'ascDate') - Client-Other-enableDirectorySortingByDate If enabled directories will be sorted by date, like photos, otherwise by name. Directory date is the last modification time of that directory not the creation date of the oldest photo (default: false) - Client-Other-enableOnScrollThumbnailPrioritising (default: true) - Client-Other-NavBar-showItemCount (default: true) - Client-Other-captionFirstNaming (default: false) - Client-Other-enableDownloadZip (default: false) - Client-Other-enableDirectoryFlattening Adds a button to flattens the file structure, by listing the content of all subdirectories. (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-Media-Photo-loadFullImageOnZoom Enables loading the full resolution image on zoom in the ligthbox (preview). (default: true) - Client-MetaFile-gpx Reads *.gpx files and renders them on the map. (default: true) - Client-MetaFile-markdown Reads *.md files in a directory and shows the next to the map. (default: true) - Client-MetaFile-pg2conf Reads *.pg2conf files (You can use it for custom sorting and save search (albums)). (default: true) - Client-Faces-enabled (default: true) - Client-Faces-keywordsToPersons (default: true) - Client-Faces-writeAccessMinRole (default: 'Admin') - Client-Faces-readAccessMinRole (default: 'User') + Server-applicationTitle (default: 'PiGallery 2') + Server-publicUrl (default: '') + Server-urlBase (default: '') + Server-apiPath PiGallery api path. (default: '/pgapi') + Server-customHTMLHead (default: '') + Server-sessionSecret (default: []) + Server-sessionTimeout unit: ms (default: 604800000) + Server-port (default: 80) + PORT same as Server-port + Server-host (default: '0.0.0.0') + 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-Log-level (default: 'info') + Server-Log-sqlLevel (default: 'error') + Server-Log-logServerTiming (default: false) + Users-authenticationRequired (default: true) + Users-unAuthenticatedUserRole (default: 'Admin') + Users-enforcedUsers Creates these users in the DB if they do not exist. If a user with this name exist, it wont be overwritten, even if the role is different. (default: []) + Gallery-enableCache (default: true) + Gallery-enableOnScrollRendering (default: true) + Gallery-defaultPhotoSortingMethod Default sorting method for directory results (default: 'ascDate') + Gallery-defaultSearchSortingMethod Default sorting method for search results (default: 'descDate') + Gallery-enableDirectorySortingByDate If enabled directories will be sorted by date, like photos, otherwise by name. Directory date is the last modification time of that directory not the creation date of the oldest photo (default: false) + Gallery-enableOnScrollThumbnailPrioritising (default: true) + Gallery-NavBar-showItemCount (default: true) + Gallery-NavBar-links List of the navigation bar links (default: [{"type":1},{"type":3},{"type":2}]) + Gallery-captionFirstNaming (default: false) + Gallery-enableDownloadZip (default: false) + Gallery-enableDirectoryFlattening Adds a button to flattens the file structure, by listing the content of all subdirectories. (default: false) + Gallery-defaultSlideshowSpeed Default time interval for displaying a photo in the slide show (default: 5) + Media-Thumbnail-iconSize (default: 45) + Media-Thumbnail-personThumbnailSize (default: 200) + Media-Thumbnail-thumbnailSizes (default: [240,480]) + Media-Thumbnail-useLanczos3 if true, 'lanczos3' will used to scale photos, otherwise faster but lowe quality 'nearest'. (default: true) + Media-Thumbnail-quality Thumbnail image quality (default: 80) + Media-Thumbnail-personFaceMargin (default: 0.6) + Media-Video-enabled (default: true) + Media-Video-supportedFormatsWithTranscoding Video formats that are supported after transcoding (with the build-in ffmpeg support) (default: ["avi","mkv","mov","wmv","flv","mts","m2ts","mpg","3gp","m4v","mpeg","vob","divx","xvid","ts"]) + Media-Video-supportedFormats Video formats that are supported also without transcoding (default: ["mp4","webm","ogv","ogg"]) + Media-Video-transcoding-bitRate (default: 5242880) + Media-Video-transcoding-resolution (default: 720) + Media-Video-transcoding-fps (default: 25) + Media-Video-transcoding-codec (default: 'libx264') + Media-Video-transcoding-format (default: 'mp4') + Media-Video-transcoding-crf Constant Rate Factor. The range of the CRF scale is 0–51, where 0 is lossless, 23 is the default, and 51 is worst quality possible. (default: 23) + Media-Video-transcoding-preset A preset is a collection of options that will provide a certain encoding speed to compression ratio (default: 'medium') + Media-Video-transcoding-customOptions It will be sent to ffmpeg as it is, as custom options. (default: []) + Media-Photo-Converting-enabled (default: true) + Media-Photo-Converting-onTheFly Converts photos on the fly, when they are requested. (default: true) + Media-Photo-Converting-resolution (default: 1080) + Media-Photo-loadFullImageOnZoom Enables loading the full resolution image on zoom in the ligthbox (preview). (default: true) + Media-Photo-supportedFormats (default: ["gif","jpeg","jpg","jpe","png","webp","svg"]) + Media-folder Images are loaded from this folder (read permission required) (default: 'demo/images') + Media-tempFolder Thumbnails, converted photos, videos will be stored here (write permission required) (default: 'demo/tmp') + Media-photoMetadataSize Only this many bites will be loaded when scanning photo/video for metadata. (default: 524288) + MetaFile-gpx Reads *.gpx files and renders them on the map. (default: true) + MetaFile-GPXCompressing-enabled (default: true) + MetaFile-GPXCompressing-onTheFly Compresses gpx files on-the-fly, when they are requested. (default: true) + MetaFile-GPXCompressing-minDistance Filters out entry that are closer than this in meters. (default: 5) + MetaFile-GPXCompressing-minTimeDistance Filters out entry that are closer than this in time in milliseconds. (default: 5000) + MetaFile-markdown Reads *.md files in a directory and shows the next to the map. (default: true) + MetaFile-pg2conf Reads *.pg2conf files (You can use it for custom sorting and save search (albums)). (default: true) + MetaFile-supportedFormats (default: ["gpx","pg2conf","md"]) + Album-enabled (default: true) + Search-enabled (default: true) + Search-searchCacheTimeout (default: 3600000) + Search-AutoComplete-enabled (default: true) + Search-AutoComplete-targetItemsPerCategory (default: 5) + Search-AutoComplete-maxItems (default: 30) + Search-AutoComplete-cacheTimeout (default: 3600000) + Search-maxMediaResult (default: 10000) + Search-listDirectories Search returns also with directories, not just media (default: false) + Search-listMetafiles Search also returns with metafiles from directories that contain a media file of the matched search result (default: true) + Search-maxDirectoryResult (default: 200) + Sharing-enabled (default: true) + Sharing-passwordProtected Enables password protected sharing links. (default: true) + Sharing-updateTimeout After creating a sharing link, it can be updated for this long (in ms). (default: 300000) + Map-enabled (default: true) + Map-maxPreviewMarkers Maximum number of markers to be shown on the map preview on the gallery page. (default: 50) + Map-useImageMarkers (default: true) + Map-mapProvider (default: 'OpenStreetMap') + Map-mapboxAccessToken (default: '') + Map-customLayers (default: [{"name":"street","url":""}]) + Faces-enabled (default: true) + Faces-keywordsToPersons (default: true) + Faces-writeAccessMinRole (default: 'Admin') + Faces-readAccessMinRole (default: 'User') + RandomPhoto-enabled Enables random link generation. (default: true) + Database-type (default: 'sqlite') + Database-dbFolder (default: 'db') + Database-sqlite-DBFileName (default: 'sqlite.db') + Database-mysql-host (default: 'localhost') + MYSQL_HOST same as Database-mysql-host + Database-mysql-port (default: 3306) + MYSQL_PORT same as Database-mysql-port + Database-mysql-database (default: 'pigallery2') + MYSQL_DATABASE same as Database-mysql-database + Database-mysql-username (default: '') + MYSQL_USERNAME same as Database-mysql-username + Database-mysql-password (default: '') + MYSQL_PASSWORD same as Database-mysql-password + Indexing-cachedFolderTimeout (default: 3600000) + Indexing-reIndexingSensitivity (default: 'low') + 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: [".Trash-1000",".dtrash","$RECYCLE.BIN"]) + Indexing-excludeFileList Any folder that contains a file with this name will be excluded from indexing. (default: []) + Preview-SearchQuery (default: {"type":100,"text":""}) + Preview-Sorting (default: [6,4]) + Duplicates-listingLimit (default: 1000) + Jobs-maxSavedProgress Job history size (default: 20) + Jobs-mediaProcessingBatchSize Job loads this many photos or videos form the DB for processing (default: 1000) + Jobs-scheduled (default: [{"name":"Indexing","jobName":"Indexing","config":{"indexChangesOnly":true},"allowParallelRun":false,"trigger":{"type":1}},{"name":"Preview Filling","jobName":"Preview Filling","config":{},"allowParallelRun":false,"trigger":{"type":1}},{"name":"Thumbnail Generation","jobName":"Thumbnail Generation","config":{"sizes":[240],"indexedOnly":true},"allowParallelRun":false,"trigger":{"type":4,"afterScheduleName":"Preview Filling"}},{"name":"Photo Converting","jobName":"Photo Converting","config":{"indexedOnly":true},"allowParallelRun":false,"trigger":{"type":4,"afterScheduleName":"Thumbnail Generation"}},{"name":"Video Converting","jobName":"Video Converting","config":{"indexedOnly":true},"allowParallelRun":false,"trigger":{"type":4,"afterScheduleName":"Photo Converting"}},{"name":"Temp Folder Cleaning","jobName":"Temp Folder Cleaning","config":{"indexedOnly":true},"allowParallelRun":false,"trigger":{"type":4,"afterScheduleName":"Video Converting"}}]) ``` ### `config.json` sample: ```json { "Server": { + "applicationTitle": "PiGallery 2", + "publicUrl": "", + "urlBase": "", + "//[apiPath]": "PiGallery api path.", + "apiPath": "/pgapi", + "customHTMLHead": "", "sessionSecret": [], + "//[sessionTimeout]": "unit: ms", + "sessionTimeout": 604800000, "port": 80, "host": "0.0.0.0", - "Media": { - "//[folder]": "Images are loaded from this folder (read permission required)", - "folder": "demo/images", - "//[tempFolder]": "Thumbnails, converted photos, videos will be stored here (write permission required)", - "tempFolder": "demo/tmp", - "Video": { - "transcoding": { - "bitRate": 5242880, - "resolution": 720, - "fps": 25, - "codec": "libx264", - "format": "mp4", - "//[crf]": "Constant Rate Factor. The range of the CRF scale is 0–51, where 0 is lossless, 23 is the default, and 51 is worst quality possible.", - "crf": 23, - "//[preset]": "A preset is a collection of options that will provide a certain encoding speed to compression ratio", - "preset": "medium", - "//[customOptions]": "It will be sent to ffmpeg as it is, as custom options.", - "customOptions": [] - } - }, - "Photo": { - "Converting": { - "//[onTheFly]": "Converts photos on the fly, when they are requested.", - "onTheFly": true, - "resolution": 1080 - } - }, - "Thumbnail": { - "//[qualityPriority]": "if true, photos will have better quality.", - "qualityPriority": true, - "personFaceMargin": 0.6 - } - }, - "Preview": { - "SearchQuery": { - "type": 100, - "text": "" - }, - "Sorting": [ - 6, - 4 - ] - }, "Threading": { "//[enabled]": "App can run on multiple thread", "enabled": true, "//[thumbnailThreads]": "Number of threads that are used to generate thumbnails. If 0, number of 'CPU cores -1' threads will be used.", "thumbnailThreads": 0 }, - "Database": { - "type": "sqlite", - "dbFolder": "db", - "sqlite": { - "DBFileName": "sqlite.db" - }, - "mysql": { - "host": "localhost", - "port": 3306, - "database": "pigallery2", - "username": "", - "password": "" - }, - "//[enforcedUsers]": "Creates these users in the DB if they do not exist. If a user with this name exist, it wont be overwritten, even if the role is different.", - "enforcedUsers": [] - }, - "Sharing": { - "updateTimeout": 300000 - }, - "//[sessionTimeout]": "unit: ms", - "sessionTimeout": 604800000, - "Indexing": { - "cachedFolderTimeout": 3600000, - "reIndexingSensitivity": "low", - "//[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.", - "excludeFolderList": [ - ".Trash-1000", - ".dtrash", - "$RECYCLE.BIN" - ], - "//[excludeFileList]": "Any folder that contains a file with this name will be excluded from indexing.", - "excludeFileList": [] - }, - "//[photoMetadataSize]": "only this many bites will be loaded when scanning photo for metadata", - "photoMetadataSize": 524288, - "Duplicates": { - "listingLimit": 1000 - }, "Log": { "level": "info", "sqlLevel": "error", "logServerTiming": false - }, - "Jobs": { - "//[maxSavedProgress]": "Job history size", - "maxSavedProgress": 10, - "scheduled": [ - { - "name": "Indexing", - "jobName": "Indexing", - "config": { - "indexChangesOnly": true - }, - "allowParallelRun": false, - "trigger": { - "type": "never" - } - }, - { - "name": "Preview Filling", - "jobName": "Preview Filling", - "config": {}, - "allowParallelRun": false, - "trigger": { - "type": "never" - } - }, - { - "name": "Thumbnail Generation", - "jobName": "Thumbnail Generation", - "config": { - "sizes": [ - 240 - ], - "indexedOnly": true - }, - "allowParallelRun": false, - "trigger": { - "type": "after", - "afterScheduleName": "Preview Filling" - } - }, - { - "name": "Photo Converting", - "jobName": "Photo Converting", - "config": { - "indexedOnly": true - }, - "allowParallelRun": false, - "trigger": { - "type": "after", - "afterScheduleName": "Thumbnail Generation" - } - }, - { - "name": "Video Converting", - "jobName": "Video Converting", - "config": { - "indexedOnly": true - }, - "allowParallelRun": false, - "trigger": { - "type": "after", - "afterScheduleName": "Photo Converting" - } - }, - { - "name": "Temp Folder Cleaning", - "jobName": "Temp Folder Cleaning", - "config": { - "indexedOnly": true - }, - "allowParallelRun": false, - "trigger": { - "type": "after", - "afterScheduleName": "Video Converting" - } - } - ] } }, - "Client": { - "applicationTitle": "PiGallery 2", - "publicUrl": "", - "urlBase": "", - "Search": { - "enabled": true, - "searchCacheTimeout": 3600000, - "AutoComplete": { - "enabled": true, - "targetItemsPerCategory": 5, - "maxItems": 30, - "cacheTimeout": 3600000 - }, - "maxMediaResult": 10000, - "//[listDirectories]": "Search returns also with directories, not just media", - "listDirectories": false, - "//[listMetafiles]": "Search also returns with metafiles from directories that contain a media file of the matched search result", - "listMetafiles": true, - "maxDirectoryResult": 200 - }, - "Sharing": { - "enabled": true, - "passwordProtected": true - }, - "Album": { - "enabled": true - }, - "Map": { - "enabled": true, - "//[maxPreviewMarkers]": "Maximum number of markers to be shown on the map preview on the gallery page.", - "maxPreviewMarkers": 50, - "useImageMarkers": true, - "mapProvider": "OpenStreetMap", - "mapboxAccessToken": "", - "customLayers": [ + "Users": { + "authenticationRequired": true, + "unAuthenticatedUserRole": "Admin", + "//[enforcedUsers]": "Creates these users in the DB if they do not exist. If a user with this name exist, it wont be overwritten, even if the role is different.", + "enforcedUsers": [] + }, + "Gallery": { + "enableCache": true, + "enableOnScrollRendering": true, + "//[defaultPhotoSortingMethod]": "Default sorting method for directory results", + "defaultPhotoSortingMethod": "ascDate", + "//[defaultSearchSortingMethod]": "Default sorting method for search results", + "defaultSearchSortingMethod": "descDate", + "//[enableDirectorySortingByDate]": "If enabled directories will be sorted by date, like photos, otherwise by name. Directory date is the last modification time of that directory not the creation date of the oldest photo", + "enableDirectorySortingByDate": false, + "enableOnScrollThumbnailPrioritising": true, + "NavBar": { + "showItemCount": true, + "//[links]": "List of the navigation bar links", + "links": [ { - "name": "street", - "url": "" + "type": "gallery" + }, + { + "type": "albums" + }, + { + "type": "faces" } ] }, - "RandomPhoto": { - "//[enabled]": "Enables random link generation. NOTE: With the current implementation, it poses a security risk. See https://github.com/bpatrik/pigallery2/issues/392", - "enabled": false + "captionFirstNaming": false, + "enableDownloadZip": false, + "//[enableDirectoryFlattening]": "Adds a button to flattens the file structure, by listing the content of all subdirectories.", + "enableDirectoryFlattening": false, + "//[defaultSlideshowSpeed]": "Default time interval for displaying a photo in the slide show", + "defaultSlideshowSpeed": 5 + }, + "Media": { + "Thumbnail": { + "iconSize": 45, + "personThumbnailSize": 200, + "thumbnailSizes": [ + 240, + 480 + ], + "//[useLanczos3]": "if true, 'lanczos3' will used to scale photos, otherwise faster but lowe quality 'nearest'.", + "useLanczos3": true, + "//[quality]": "Thumbnail image quality", + "quality": 80, + "personFaceMargin": 0.6 }, - "Other": { - "customHTMLHead": "", - "enableCache": true, - "enableOnScrollRendering": true, - "defaultPhotoSortingMethod": "ascDate", - "//[enableDirectorySortingByDate]": "If enabled directories will be sorted by date, like photos, otherwise by name. Directory date is the last modification time of that directory not the creation date of the oldest photo", - "enableDirectorySortingByDate": false, - "enableOnScrollThumbnailPrioritising": true, - "NavBar": { - "showItemCount": true - }, - "captionFirstNaming": false, - "enableDownloadZip": false, - "//[enableDirectoryFlattening]": "Adds a button to flattens the file structure, by listing the content of all subdirectories.", - "enableDirectoryFlattening": false - }, - "authenticationRequired": true, - "unAuthenticatedUserRole": "Admin", - "Media": { - "Thumbnail": { - "iconSize": 45, - "personThumbnailSize": 200, - "thumbnailSizes": [ - 240, - 480 - ] - }, - "Video": { - "enabled": true - }, - "Photo": { - "Converting": { - "enabled": true - }, - "//[loadFullImageOnZoom]": "Enables loading the full resolution image on zoom in the ligthbox (preview).", - "loadFullImageOnZoom": true + "Video": { + "enabled": true, + "//[supportedFormatsWithTranscoding]": "Video formats that are supported after transcoding (with the build-in ffmpeg support)", + "supportedFormatsWithTranscoding": [ + "avi", + "mkv", + "mov", + "wmv", + "flv", + "mts", + "m2ts", + "mpg", + "3gp", + "m4v", + "mpeg", + "vob", + "divx", + "xvid", + "ts" + ], + "//[supportedFormats]": "Video formats that are supported also without transcoding", + "supportedFormats": [ + "mp4", + "webm", + "ogv", + "ogg" + ], + "transcoding": { + "bitRate": 5242880, + "resolution": 720, + "fps": 25, + "codec": "libx264", + "format": "mp4", + "//[crf]": "Constant Rate Factor. The range of the CRF scale is 0–51, where 0 is lossless, 23 is the default, and 51 is worst quality possible.", + "crf": 23, + "//[preset]": "A preset is a collection of options that will provide a certain encoding speed to compression ratio", + "preset": "medium", + "//[customOptions]": "It will be sent to ffmpeg as it is, as custom options.", + "customOptions": [] } }, - "MetaFile": { - "//[gpx]": "Reads *.gpx files and renders them on the map.", - "gpx": true, - "//[markdown]": "Reads *.md files in a directory and shows the next to the map.", - "markdown": true, - "//[pg2conf]": "Reads *.pg2conf files (You can use it for custom sorting and save search (albums)).", - "pg2conf": true + "Photo": { + "Converting": { + "enabled": true, + "//[onTheFly]": "Converts photos on the fly, when they are requested.", + "onTheFly": true, + "resolution": 1080 + }, + "//[loadFullImageOnZoom]": "Enables loading the full resolution image on zoom in the ligthbox (preview).", + "loadFullImageOnZoom": true, + "supportedFormats": [ + "gif", + "jpeg", + "jpg", + "jpe", + "png", + "webp", + "svg" + ] }, - "Faces": { + "//[folder]": "Images are loaded from this folder (read permission required)", + "folder": "demo/images", + "//[tempFolder]": "Thumbnails, converted photos, videos will be stored here (write permission required)", + "tempFolder": "demo/tmp", + "//[photoMetadataSize]": "Only this many bites will be loaded when scanning photo/video for metadata.", + "photoMetadataSize": 524288 + }, + "MetaFile": { + "//[gpx]": "Reads *.gpx files and renders them on the map.", + "gpx": true, + "GPXCompressing": { "enabled": true, - "keywordsToPersons": true, - "writeAccessMinRole": "Admin", - "readAccessMinRole": "User" + "//[onTheFly]": "Compresses gpx files on-the-fly, when they are requested.", + "onTheFly": true, + "//[minDistance]": "Filters out entry that are closer than this in meters.", + "minDistance": 5, + "//[minTimeDistance]": "Filters out entry that are closer than this in time in milliseconds.", + "minTimeDistance": 5000 + }, + "//[markdown]": "Reads *.md files in a directory and shows the next to the map.", + "markdown": true, + "//[pg2conf]": "Reads *.pg2conf files (You can use it for custom sorting and save search (albums)).", + "pg2conf": true, + "supportedFormats": [ + "gpx", + "pg2conf", + "md" + ] + }, + "Album": { + "enabled": true + }, + "Search": { + "enabled": true, + "searchCacheTimeout": 3600000, + "AutoComplete": { + "enabled": true, + "targetItemsPerCategory": 5, + "maxItems": 30, + "cacheTimeout": 3600000 + }, + "maxMediaResult": 10000, + "//[listDirectories]": "Search returns also with directories, not just media", + "listDirectories": false, + "//[listMetafiles]": "Search also returns with metafiles from directories that contain a media file of the matched search result", + "listMetafiles": true, + "maxDirectoryResult": 200 + }, + "Sharing": { + "enabled": true, + "//[passwordProtected]": "Enables password protected sharing links.", + "passwordProtected": true, + "//[updateTimeout]": [ + "After creating a sharing link, it can be updated for this long (in ms)." + ], + "updateTimeout": 300000 + }, + "Map": { + "enabled": true, + "//[maxPreviewMarkers]": "Maximum number of markers to be shown on the map preview on the gallery page.", + "maxPreviewMarkers": 50, + "useImageMarkers": true, + "mapProvider": "OpenStreetMap", + "mapboxAccessToken": "", + "customLayers": [ + { + "name": "street", + "url": "" + } + ] + }, + "Faces": { + "enabled": true, + "keywordsToPersons": true, + "writeAccessMinRole": "Admin", + "readAccessMinRole": "User" + }, + "RandomPhoto": { + "//[enabled]": "Enables random link generation.", + "enabled": true + }, + "Database": { + "type": "sqlite", + "dbFolder": "db", + "sqlite": { + "DBFileName": "sqlite.db" + }, + "mysql": { + "host": "localhost", + "port": 3306, + "database": "pigallery2", + "username": "", + "password": "" } + }, + "Indexing": { + "cachedFolderTimeout": 3600000, + "reIndexingSensitivity": "low", + "//[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.", + "excludeFolderList": [ + ".Trash-1000", + ".dtrash", + "$RECYCLE.BIN" + ], + "//[excludeFileList]": "Any folder that contains a file with this name will be excluded from indexing.", + "excludeFileList": [] + }, + "Preview": { + "SearchQuery": { + "type": 100, + "text": "" + }, + "Sorting": [ + 6, + 4 + ] + }, + "Duplicates": { + "listingLimit": 1000 + }, + "Jobs": { + "//[maxSavedProgress]": "Job history size", + "maxSavedProgress": 20, + "//[mediaProcessingBatchSize]": "Job loads this many photos or videos form the DB for processing", + "mediaProcessingBatchSize": 1000, + "scheduled": [ + { + "name": "Indexing", + "jobName": "Indexing", + "config": { + "indexChangesOnly": true + }, + "allowParallelRun": false, + "trigger": { + "type": "never" + } + }, + { + "name": "Preview Filling", + "jobName": "Preview Filling", + "config": {}, + "allowParallelRun": false, + "trigger": { + "type": "never" + } + }, + { + "name": "Thumbnail Generation", + "jobName": "Thumbnail Generation", + "config": { + "sizes": [ + 240 + ], + "indexedOnly": true + }, + "allowParallelRun": false, + "trigger": { + "type": "after", + "afterScheduleName": "Preview Filling" + } + }, + { + "name": "Photo Converting", + "jobName": "Photo Converting", + "config": { + "indexedOnly": true + }, + "allowParallelRun": false, + "trigger": { + "type": "after", + "afterScheduleName": "Thumbnail Generation" + } + }, + { + "name": "Video Converting", + "jobName": "Video Converting", + "config": { + "indexedOnly": true + }, + "allowParallelRun": false, + "trigger": { + "type": "after", + "afterScheduleName": "Photo Converting" + } + }, + { + "name": "Temp Folder Cleaning", + "jobName": "Temp Folder Cleaning", + "config": { + "indexedOnly": true + }, + "allowParallelRun": false, + "trigger": { + "type": "after", + "afterScheduleName": "Video Converting" + } + } + ] } }``` \ No newline at end of file diff --git a/benchmark/BenchmarkRunner.ts b/benchmark/BenchmarkRunner.ts index b5da92e7..407352b4 100644 --- a/benchmark/BenchmarkRunner.ts +++ b/benchmark/BenchmarkRunner.ts @@ -80,7 +80,7 @@ export class BenchmarkRunner { }; constructor(public RUNS: number) { - Config.Client.authenticationRequired = false; + Config.Users.authenticationRequired = false; } async bmSaveDirectory(): Promise { @@ -124,7 +124,7 @@ export class BenchmarkRunner { await ObjectManagers.InitSQLManagers(); }, null, async (): Promise => { - Config.Server.Indexing.reIndexingSensitivity = ReIndexingSensitivity.low; + Config.Indexing.reIndexingSensitivity = ReIndexingSensitivity.low; await this.init(); await this.setupDB(); }); @@ -138,7 +138,7 @@ export class BenchmarkRunner { await ObjectManagers.InitSQLManagers(); }, null, async (): Promise => { - Config.Server.Indexing.reIndexingSensitivity = ReIndexingSensitivity.low; + Config.Indexing.reIndexingSensitivity = ReIndexingSensitivity.low; await this.setupDB(); }); BMPersonRouter.addGetPersons(bm.BmExpressApp); @@ -313,8 +313,8 @@ export class BenchmarkRunner { Config.Server.Threading.enabled = false; await ObjectManagers.reset(); await fs.promises.rm(ProjectPath.DBFolder, {recursive: true, force: true}); - Config.Server.Database.type = DatabaseType.sqlite; - Config.Server.Jobs.scheduled = []; + Config.Database.type = DatabaseType.sqlite; + Config.Jobs.scheduled = []; await ObjectManagers.InitSQLManagers(); }; @@ -331,6 +331,7 @@ export class BenchmarkRunner { }, onProgressUpdate: (progress: JobProgress): void => { + // empty } }; indexingJob.start({indexChangesOnly: false}).catch(console.error); diff --git a/benchmark/index.ts b/benchmark/index.ts index 5a1ecb06..c372c805 100644 --- a/benchmark/index.ts +++ b/benchmark/index.ts @@ -6,7 +6,7 @@ import {BMConfig} from './BMConfig'; import {DirectoryDTOUtils} from '../src/common/entities/DirectoryDTO'; -Config.Server.Media.folder = BMConfig.path; +Config.Media.folder = BMConfig.path; ProjectPath.reset(); const RUNS = BMConfig.RUNS; @@ -18,12 +18,13 @@ const printLine = (text: string) => { const printHeader = async (statistic: string) => { const dt = new Date(); + // eslint-disable-next-line @typescript-eslint/no-var-requires printLine('## PiGallery2 v' + require('./../package.json').version + ', ' + Utils.zeroPrefix(dt.getDate(), 2) + '.' + Utils.zeroPrefix(dt.getMonth() + 1, 2) + '.' + dt.getFullYear()); - if (Config.Server.Environment && Config.Server.Environment.buildCommitHash) { - printLine('**Version**: v' + Config.Server.Environment.appVersion + ', built at: ' + new Date(Config.Server.Environment.buildTime) + ', git commit:' + Config.Server.Environment.buildCommitHash); + if (Config.Environment && Config.Environment.buildCommitHash) { + printLine('**Version**: v' + Config.Environment.appVersion + ', built at: ' + new Date(Config.Environment.buildTime) + ', git commit:' + Config.Environment.buildCommitHash); } printLine('**System**: ' + BMConfig.system); printLine('\n**Gallery**: ' + statistic + '\n'); diff --git a/package-lock.json b/package-lock.json index 35434256..31a86740 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,7 +27,7 @@ "sharp": "0.31.2", "ts-exif-parser": "0.2.2", "ts-node-iptc": "1.0.11", - "typeconfig": "2.0.26", + "typeconfig": "2.0.29", "typeorm": "0.3.10", "xml2js": "0.4.23" }, @@ -20825,9 +20825,9 @@ } }, "node_modules/typeconfig": { - "version": "2.0.26", - "resolved": "https://registry.npmjs.org/typeconfig/-/typeconfig-2.0.26.tgz", - "integrity": "sha512-qAF+0F/Yln3V8dyjwI5G8VwihkDGG/RcAaKpK0LGmu2Lsn9qLoAIcakSm68t+9TW5owC4KLaz3h4mvBhKyColQ==", + "version": "2.0.29", + "resolved": "https://registry.npmjs.org/typeconfig/-/typeconfig-2.0.29.tgz", + "integrity": "sha512-0eQ4QkRUTxG1sy6aFuQV/uxpFamFShhdVQFVkMqhLUpNCAcgNS8VJkY4ZxVp09RnDdoR+L/Oj/3f655XyeNcPQ==", "dependencies": { "minimist": "1.2.7" } @@ -38126,9 +38126,9 @@ } }, "typeconfig": { - "version": "2.0.26", - "resolved": "https://registry.npmjs.org/typeconfig/-/typeconfig-2.0.26.tgz", - "integrity": "sha512-qAF+0F/Yln3V8dyjwI5G8VwihkDGG/RcAaKpK0LGmu2Lsn9qLoAIcakSm68t+9TW5owC4KLaz3h4mvBhKyColQ==", + "version": "2.0.29", + "resolved": "https://registry.npmjs.org/typeconfig/-/typeconfig-2.0.29.tgz", + "integrity": "sha512-0eQ4QkRUTxG1sy6aFuQV/uxpFamFShhdVQFVkMqhLUpNCAcgNS8VJkY4ZxVp09RnDdoR+L/Oj/3f655XyeNcPQ==", "requires": { "minimist": "1.2.7" } diff --git a/package.json b/package.json index ccc5cb0d..5989ce9c 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "sharp": "0.31.2", "ts-exif-parser": "0.2.2", "ts-node-iptc": "1.0.11", - "typeconfig": "2.0.26", + "typeconfig": "2.0.29", "xml2js": "0.4.23", "typeorm": "0.3.10" }, diff --git a/src/backend/ProjectPath.ts b/src/backend/ProjectPath.ts index 76c52504..4cf11b1f 100644 --- a/src/backend/ProjectPath.ts +++ b/src/backend/ProjectPath.ts @@ -30,11 +30,11 @@ class ProjectPathClass { reset(): void { this.Root = path.join(__dirname, '/../../'); this.FrontendFolder = path.join(this.Root, 'dist'); - this.ImageFolder = this.getAbsolutePath(Config.Server.Media.folder); - this.TempFolder = this.getAbsolutePath(Config.Server.Media.tempFolder); + this.ImageFolder = this.getAbsolutePath(Config.Media.folder); + this.TempFolder = this.getAbsolutePath(Config.Media.tempFolder); this.TranscodedFolder = path.join(this.TempFolder, 'tc'); this.FacesFolder = path.join(this.TempFolder, 'f'); - this.DBFolder = this.getAbsolutePath(Config.Server.Database.dbFolder); + this.DBFolder = this.getAbsolutePath(Config.Database.dbFolder); // create thumbnail folder if not exist if (!fs.existsSync(this.TempFolder)) { diff --git a/src/backend/middlewares/AlbumMWs.ts b/src/backend/middlewares/AlbumMWs.ts index 67f9e1c7..bae840f7 100644 --- a/src/backend/middlewares/AlbumMWs.ts +++ b/src/backend/middlewares/AlbumMWs.ts @@ -1,8 +1,8 @@ -import { NextFunction, Request, Response } from 'express'; -import { ErrorCodes, ErrorDTO } from '../../common/entities/Error'; -import { ObjectManagers } from '../model/ObjectManagers'; -import { Utils } from '../../common/Utils'; -import { Config } from '../../common/config/private/Config'; +import {NextFunction, Request, Response} from 'express'; +import {ErrorCodes, ErrorDTO} from '../../common/entities/Error'; +import {ObjectManagers} from '../model/ObjectManagers'; +import {Utils} from '../../common/Utils'; +import {Config} from '../../common/config/private/Config'; export class AlbumMWs { public static async listAlbums( @@ -10,7 +10,7 @@ export class AlbumMWs { res: Response, next: NextFunction ): Promise { - if (Config.Client.Album.enabled === false) { + if (Config.Album.enabled === false) { return next(); } try { @@ -29,7 +29,7 @@ export class AlbumMWs { res: Response, next: NextFunction ): Promise { - if (Config.Client.Album.enabled === false) { + if (Config.Album.enabled === false) { return next(); } if (!req.params['id'] || !Utils.isUInt32(parseInt(req.params['id'], 10))) { @@ -57,7 +57,7 @@ export class AlbumMWs { res: Response, next: NextFunction ): Promise { - if (Config.Client.Album.enabled === false) { + if (Config.Album.enabled === false) { return next(); } if ( diff --git a/src/backend/middlewares/GalleryMWs.ts b/src/backend/middlewares/GalleryMWs.ts index 2159c5d5..7f1f67dc 100644 --- a/src/backend/middlewares/GalleryMWs.ts +++ b/src/backend/middlewares/GalleryMWs.ts @@ -88,7 +88,7 @@ export class GalleryMWs { res: Response, next: NextFunction ): Promise { - if (Config.Client.Other.enableDownloadZip === false) { + if (Config.Gallery.enableDownloadZip === false) { return next(); } const directoryName = req.params['directory'] || '/'; @@ -157,7 +157,7 @@ export class GalleryMWs { return next(); } - if (Config.Client.Media.Video.enabled === false) { + if (Config.Media.Video.enabled === false) { if (cw.directory) { const removeVideos = (dir: ParentDirectoryDTO): void => { dir.media = dir.media.filter( @@ -241,7 +241,7 @@ export class GalleryMWs { next: NextFunction ): Promise { if ( - Config.Client.Search.enabled === false || + Config.Search.enabled === false || !req.params['searchQueryDTO'] ) { return next(); @@ -283,7 +283,7 @@ export class GalleryMWs { res: Response, next: NextFunction ): Promise { - if (Config.Client.Search.AutoComplete.enabled === false) { + if (Config.Search.AutoComplete.enabled === false) { return next(); } if (!req.params['text']) { @@ -314,7 +314,7 @@ export class GalleryMWs { next: NextFunction ): Promise { if ( - Config.Client.RandomPhoto.enabled === false || + Config.RandomPhoto.enabled === false || !req.params['searchQueryDTO'] ) { return next(); diff --git a/src/backend/middlewares/MetaFileMWs.ts b/src/backend/middlewares/MetaFileMWs.ts index dfa93ccb..3321fd0e 100644 --- a/src/backend/middlewares/MetaFileMWs.ts +++ b/src/backend/middlewares/MetaFileMWs.ts @@ -13,7 +13,7 @@ export class MetaFileMWs { return next(); } // if conversion is not enabled redirect, so browser can cache the full - if (Config.Client.MetaFile.GPXCompressing.enabled === false) { + if (Config.MetaFile.GPXCompressing.enabled === false) { return res.redirect(req.originalUrl.slice(0, -1 * '\\bestFit'.length)); } const fullPath = req.resultPipe as string; @@ -28,7 +28,7 @@ export class MetaFileMWs { return next(); } - if (Config.Server.MetaFile.GPXCompressing.onTheFly === true) { + if (Config.MetaFile.GPXCompressing.onTheFly === true) { req.resultPipe = await GPXProcessing.compressGPX(fullPath); return next(); } diff --git a/src/backend/middlewares/RenderingMWs.ts b/src/backend/middlewares/RenderingMWs.ts index c8ec2d91..e7dc731c 100644 --- a/src/backend/middlewares/RenderingMWs.ts +++ b/src/backend/middlewares/RenderingMWs.ts @@ -107,7 +107,7 @@ export class RenderingMWs { const originalConf = await Config.original(); // These are sensitive information, do not send to the client side originalConf.Server.sessionSecret = null; - originalConf.Server.Database.enforcedUsers = null; + originalConf.Users.enforcedUsers = null; const message = new Message( null, originalConf.toJSON({ diff --git a/src/backend/middlewares/SharingMWs.ts b/src/backend/middlewares/SharingMWs.ts index c9b1d74c..0a51fb1f 100644 --- a/src/backend/middlewares/SharingMWs.ts +++ b/src/backend/middlewares/SharingMWs.ts @@ -13,7 +13,7 @@ export class SharingMWs { res: Response, next: NextFunction ): Promise { - if (Config.Client.Sharing.enabled === false) { + if (Config.Sharing.enabled === false) { return next(); } const sharingKey = req.params[QueryParams.gallery.sharingKey_params]; @@ -40,7 +40,7 @@ export class SharingMWs { res: Response, next: NextFunction ): Promise { - if (Config.Client.Sharing.enabled === false) { + if (Config.Sharing.enabled === false) { return next(); } if ( @@ -105,7 +105,7 @@ export class SharingMWs { res: Response, next: NextFunction ): Promise { - if (Config.Client.Sharing.enabled === false) { + if (Config.Sharing.enabled === false) { return next(); } if ( @@ -159,7 +159,7 @@ export class SharingMWs { res: Response, next: NextFunction ): Promise { - if (Config.Client.Sharing.enabled === false) { + if (Config.Sharing.enabled === false) { return next(); } if ( @@ -195,7 +195,7 @@ export class SharingMWs { res: Response, next: NextFunction ): Promise { - if (Config.Client.Sharing.enabled === false) { + if (Config.Sharing.enabled === false) { return next(); } try { diff --git a/src/backend/middlewares/admin/AdminMWs.ts b/src/backend/middlewares/admin/AdminMWs.ts index 1569314a..3812dd24 100644 --- a/src/backend/middlewares/admin/AdminMWs.ts +++ b/src/backend/middlewares/admin/AdminMWs.ts @@ -13,7 +13,7 @@ export class AdminMWs { res: Response, next: NextFunction ): Promise { - if (Config.Server.Database.type === DatabaseType.memory) { + if (Config.Database.type === DatabaseType.memory) { return next( new ErrorDTO( ErrorCodes.GENERAL_ERROR, @@ -60,7 +60,7 @@ export class AdminMWs { res: Response, next: NextFunction ): Promise { - if (Config.Server.Database.type === DatabaseType.memory) { + if (Config.Database.type === DatabaseType.memory) { return next( new ErrorDTO( ErrorCodes.GENERAL_ERROR, diff --git a/src/backend/middlewares/admin/SettingsMWs.ts b/src/backend/middlewares/admin/SettingsMWs.ts index fea8ff79..d832b5a1 100644 --- a/src/backend/middlewares/admin/SettingsMWs.ts +++ b/src/backend/middlewares/admin/SettingsMWs.ts @@ -1,94 +1,40 @@ -/**/ import {NextFunction, Request, Response} from 'express'; import {ErrorCodes, ErrorDTO} from '../../../common/entities/Error'; -import {ObjectManagers} from '../../model/ObjectManagers'; import {Logger} from '../../Logger'; import {Config} from '../../../common/config/private/Config'; import {ConfigDiagnostics} from '../../model/diagnostics/ConfigDiagnostics'; -import {BasicConfigDTO, BasicConfigDTOUtil} from '../../../common/entities/settings/BasicConfigDTO'; -import {OtherConfigDTO} from '../../../common/entities/settings/OtherConfigDTO'; -import {ProjectPath} from '../../ProjectPath'; -import { - DatabaseType, - ServerDataBaseConfig, - ServerIndexingConfig, - ServerJobConfig, ServerMetaFileConfig, - ServerPhotoConfig, - ServerPreviewConfig, - ServerThumbnailConfig, - ServerVideoConfig -} from '../../../common/config/private/PrivateConfig'; -import { - ClientAlbumConfig, - ClientFacesConfig, - ClientMapConfig, - ClientMetaFileConfig, - ClientPhotoConfig, - ClientRandomPhotoConfig, - ClientSearchConfig, - ClientSharingConfig, - ClientThumbnailConfig, - ClientVideoConfig -} from '../../../common/config/public/ClientConfig'; const LOG_TAG = '[SettingsMWs]'; export class SettingsMWs { - public static async updateDatabaseSettings(req: Request, res: Response, next: NextFunction): Promise { - - if ((typeof req.body === 'undefined') || (typeof req.body.settings === 'undefined')) { - return next(new ErrorDTO(ErrorCodes.INPUT_ERROR, 'settings is needed')); - } - - const databaseSettings = req.body.settings as ServerDataBaseConfig; - - try { - if (databaseSettings.type !== DatabaseType.memory) { - await ConfigDiagnostics.testDatabase(databaseSettings); - } - Config.Server.Database = databaseSettings; - // only updating explicitly set config (not saving config set by the diagnostics) - const original = await Config.original(); - original.Server.Database = databaseSettings; - if (databaseSettings.type === DatabaseType.memory) { - original.Client.Sharing.enabled = false; - original.Client.Search.enabled = false; - } - original.save(); - await ConfigDiagnostics.runDiagnostics(); - Logger.info(LOG_TAG, 'new config:'); - Logger.info(LOG_TAG, JSON.stringify(Config, null, '\t')); - - await ObjectManagers.reset(); - if (Config.Server.Database.type !== DatabaseType.memory) { - await ObjectManagers.InitSQLManagers(); - } else { - await ObjectManagers.InitMemoryManagers(); - } - - return next(); - } catch (err) { - if (err instanceof Error) { - return next(new ErrorDTO(ErrorCodes.SETTINGS_ERROR, 'Error while saving database settings: ' + err.toString(), err)); - } - return next(new ErrorDTO(ErrorCodes.SETTINGS_ERROR, 'Error while saving database settings', err)); - } - } - - public static async updateMapSettings(req: Request, res: Response, next: NextFunction): Promise { - if ((typeof req.body === 'undefined') || (typeof req.body.settings === 'undefined')) { + /** + * General settings updating servcie + * @param req + * @param res + * @param next + */ + public static async updateSettings(req: Request, res: Response, next: NextFunction): Promise { + if ((typeof req.body === 'undefined') + || (typeof req.body.settings === 'undefined') + || (typeof req.body.settingsPath !== 'string')) { return next(new ErrorDTO(ErrorCodes.INPUT_ERROR, 'settings is needed')); } try { - await ConfigDiagnostics.testMapConfig(req.body.settings as ClientMapConfig); + const settings = req.body.settings; // Top level settings JSON + const settingsPath: string = req.body.settingsPath; // Name of the top level settings - Config.Client.Map = (req.body.settings as ClientMapConfig); - // only updating explicitly set config (not saving config set by the diagnostics) const original = await Config.original(); - original.Client.Map = (req.body.settings as ClientMapConfig); + // only updating explicitly set config (not saving config set by the diagnostics) + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + original[settingsPath] = settings; + await ConfigDiagnostics.testConfig(original); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + Config[settingsPath] = settings; original.save(); await ConfigDiagnostics.runDiagnostics(); Logger.info(LOG_TAG, 'new config:'); @@ -102,423 +48,5 @@ export class SettingsMWs { } } - public static async updatePreviewSettings(req: Request, res: Response, next: NextFunction): Promise { - if ((typeof req.body === 'undefined') || (typeof req.body.settings === 'undefined')) { - return next(new ErrorDTO(ErrorCodes.INPUT_ERROR, 'settings is needed')); - } - - try { - await ConfigDiagnostics.testPreviewConfig(req.body.settings as ServerPreviewConfig); - - Config.Server.Preview = (req.body.settings as ServerPreviewConfig); - // only updating explicitly set config (not saving config set by the diagnostics) - const original = await Config.original(); - original.Server.Preview = (req.body.settings as ServerPreviewConfig); - original.save(); - await ConfigDiagnostics.runDiagnostics(); - Logger.info(LOG_TAG, 'new config:'); - Logger.info(LOG_TAG, JSON.stringify(Config, null, '\t')); - return next(); - } catch (err) { - if (err instanceof Error) { - return next(new ErrorDTO(ErrorCodes.SETTINGS_ERROR, 'Settings error: ' + err.toString(), err)); - } - return next(new ErrorDTO(ErrorCodes.SETTINGS_ERROR, 'Settings error: ' + JSON.stringify(err, null, ' '), err)); - } - } - - public static async updateVideoSettings(req: Request, res: Response, next: NextFunction): Promise { - if ((typeof req.body === 'undefined') || (typeof req.body.settings === 'undefined')) { - return next(new ErrorDTO(ErrorCodes.INPUT_ERROR, 'settings is needed')); - } - - try { - const settings: { - server: ServerVideoConfig, - client: ClientVideoConfig - } = req.body.settings; - - - const original = await Config.original(); - await ConfigDiagnostics.testClientVideoConfig(settings.client); - await ConfigDiagnostics.testServerVideoConfig(settings.server, original); - Config.Server.Media.Video = settings.server; - Config.Client.Media.Video = settings.client; - // only updating explicitly set config (not saving config set by the diagnostics) - original.Server.Media.Video = settings.server; - original.Client.Media.Video = settings.client; - original.save(); - await ConfigDiagnostics.runDiagnostics(); - Logger.info(LOG_TAG, 'new config:'); - Logger.info(LOG_TAG, JSON.stringify(Config, null, '\t')); - return next(); - } catch (err) { - if (err instanceof Error) { - return next(new ErrorDTO(ErrorCodes.SETTINGS_ERROR, 'Settings error: ' + err.toString(), err)); - } - return next(new ErrorDTO(ErrorCodes.SETTINGS_ERROR, 'Settings error: ' + JSON.stringify(err, null, ' '), err)); - } - } - - public static async updateMetaFileSettings(req: Request, res: Response, next: NextFunction): Promise { - if ((typeof req.body === 'undefined') || (typeof req.body.settings === 'undefined')) { - return next(new ErrorDTO(ErrorCodes.INPUT_ERROR, 'settings is needed')); - } - - try { - const settings: { - server: ServerMetaFileConfig, - client: ClientMetaFileConfig - } = req.body.settings; - - const original = await Config.original(); - await ConfigDiagnostics.testClientMetaFileConfig(settings.client, original); - await ConfigDiagnostics.testServerMetaFileConfig(settings.server, original); - - Config.Client.MetaFile = settings.client; - Config.Server.MetaFile = settings.server; - // only updating explicitly set config (not saving config set by the diagnostics) - - original.Client.MetaFile = settings.client; - original.Server.MetaFile = settings.server; - original.save(); - await ConfigDiagnostics.runDiagnostics(); - Logger.info(LOG_TAG, 'new config:'); - Logger.info(LOG_TAG, JSON.stringify(Config, null, '\t')); - return next(); - } catch (err) { - if (err instanceof Error) { - return next(new ErrorDTO(ErrorCodes.SETTINGS_ERROR, 'Settings error: ' + err.toString(), err)); - } - return next(new ErrorDTO(ErrorCodes.SETTINGS_ERROR, 'Settings error: ' + JSON.stringify(err, null, ' '), err)); - } - } - - - public static async updateAlbumsSettings(req: Request, res: Response, next: NextFunction): Promise { - if ((typeof req.body === 'undefined') || (typeof req.body.settings === 'undefined')) { - return next(new ErrorDTO(ErrorCodes.INPUT_ERROR, 'settings is needed')); - } - - try { - const original = await Config.original(); - await ConfigDiagnostics.testAlbumsConfig(req.body.settings as ClientAlbumConfig, original); - - Config.Client.Album = (req.body.settings as ClientAlbumConfig); - // only updating explicitly set config (not saving config set by the diagnostics) - - original.Client.Album = (req.body.settings as ClientAlbumConfig); - original.save(); - await ConfigDiagnostics.runDiagnostics(); - Logger.info(LOG_TAG, 'new config:'); - Logger.info(LOG_TAG, JSON.stringify(Config, null, '\t')); - return next(); - } catch (err) { - if (err instanceof Error) { - return next(new ErrorDTO(ErrorCodes.SETTINGS_ERROR, 'Settings error: ' + err.toString(), err)); - } - return next(new ErrorDTO(ErrorCodes.SETTINGS_ERROR, 'Settings error: ' + JSON.stringify(err, null, ' '), err)); - } - } - - public static async updateShareSettings(req: Request, res: Response, next: NextFunction): Promise { - if ((typeof req.body === 'undefined') || (typeof req.body.settings === 'undefined')) { - return next(new ErrorDTO(ErrorCodes.INPUT_ERROR, 'settings is needed')); - } - - try { - // only updating explicitly set config (not saving config set by the diagnostics) - const original = await Config.original(); - await ConfigDiagnostics.testSharingConfig(req.body.settings as ClientSharingConfig, original); - - Config.Client.Sharing = (req.body.settings as ClientSharingConfig); - original.Client.Sharing = (req.body.settings as ClientSharingConfig); - original.save(); - await ConfigDiagnostics.runDiagnostics(); - Logger.info(LOG_TAG, 'new config:'); - Logger.info(LOG_TAG, JSON.stringify(Config, null, '\t')); - return next(); - } catch (err) { - if (err instanceof Error) { - return next(new ErrorDTO(ErrorCodes.SETTINGS_ERROR, 'Settings error: ' + err.toString(), err)); - } - return next(new ErrorDTO(ErrorCodes.SETTINGS_ERROR, 'Settings error: ' + JSON.stringify(err, null, ' '), err)); - } - } - - public static async updateRandomPhotoSettings(req: Request, res: Response, next: NextFunction): Promise { - if ((typeof req.body === 'undefined') || (typeof req.body.settings === 'undefined')) { - return next(new ErrorDTO(ErrorCodes.INPUT_ERROR, 'settings is needed')); - } - - try { - // only updating explicitly set config (not saving config set by the diagnostics) - const original = await Config.original(); - await ConfigDiagnostics.testRandomPhotoConfig(req.body.settings as ClientRandomPhotoConfig, original); - - Config.Client.RandomPhoto = (req.body.settings as ClientRandomPhotoConfig); - original.Client.RandomPhoto = (req.body.settings as ClientRandomPhotoConfig); - original.save(); - await ConfigDiagnostics.runDiagnostics(); - Logger.info(LOG_TAG, 'new config:'); - Logger.info(LOG_TAG, JSON.stringify(Config, null, '\t')); - return next(); - } catch (err) { - if (err instanceof Error) { - return next(new ErrorDTO(ErrorCodes.SETTINGS_ERROR, 'Settings error: ' + err.toString(), err)); - } - return next(new ErrorDTO(ErrorCodes.SETTINGS_ERROR, 'Settings error: ' + JSON.stringify(err, null, ' '), err)); - } - } - - public static async updateSearchSettings(req: Request, res: Response, next: NextFunction): Promise { - if ((typeof req.body === 'undefined') || (typeof req.body.settings === 'undefined')) { - return next(new ErrorDTO(ErrorCodes.INPUT_ERROR, 'settings is needed')); - } - - try { - // only updating explicitly set config (not saving config set by the diagnostics) - const original = await Config.original(); - await ConfigDiagnostics.testSearchConfig(req.body.settings as ClientSearchConfig, original); - - Config.Client.Search = (req.body.settings as ClientSearchConfig); - original.Client.Search = (req.body.settings as ClientSearchConfig); - original.save(); - await ConfigDiagnostics.runDiagnostics(); - Logger.info(LOG_TAG, 'new config:'); - Logger.info(LOG_TAG, JSON.stringify(Config, null, '\t')); - return next(); - } catch (err) { - if (err instanceof Error) { - return next(new ErrorDTO(ErrorCodes.SETTINGS_ERROR, 'Settings error: ' + err.toString(), err)); - } - return next(new ErrorDTO(ErrorCodes.SETTINGS_ERROR, 'Settings error: ' + JSON.stringify(err, null, ' '), err)); - } - } - - public static async updateFacesSettings(req: Request, res: Response, next: NextFunction): Promise { - if ((typeof req.body === 'undefined') || (typeof req.body.settings === 'undefined')) { - return next(new ErrorDTO(ErrorCodes.INPUT_ERROR, 'settings is needed')); - } - - try { - // only updating explicitly set config (not saving config set by the diagnostics) - const original = await Config.original(); - await ConfigDiagnostics.testFacesConfig(req.body.settings as ClientFacesConfig, original); - - Config.Client.Faces = (req.body.settings as ClientFacesConfig); - original.Client.Faces = (req.body.settings as ClientFacesConfig); - original.save(); - await ConfigDiagnostics.runDiagnostics(); - Logger.info(LOG_TAG, 'new config:'); - Logger.info(LOG_TAG, JSON.stringify(Config, null, '\t')); - return next(); - } catch (err) { - if (err instanceof Error) { - return next(new ErrorDTO(ErrorCodes.SETTINGS_ERROR, 'Settings error: ' + err.toString(), err)); - } - return next(new ErrorDTO(ErrorCodes.SETTINGS_ERROR, 'Settings error: ' + JSON.stringify(err, null, ' '), err)); - } - } - - public static async updateAuthenticationSettings(req: Request, res: Response, next: NextFunction): Promise { - if ((typeof req.body === 'undefined') || (typeof req.body.settings === 'undefined')) { - return next(new ErrorDTO(ErrorCodes.INPUT_ERROR, 'settings is needed')); - } - - try { - Config.Client.authenticationRequired = (req.body.settings as boolean); - // only updating explicitly set config (not saving config set by the diagnostics) - const original = await Config.original(); - original.Client.authenticationRequired = (req.body.settings as boolean); - if (original.Client.authenticationRequired === false) { - original.Client.Sharing.enabled = false; - } - original.save(); - await ConfigDiagnostics.runDiagnostics(); - Logger.info(LOG_TAG, 'new config:'); - Logger.info(LOG_TAG, JSON.stringify(Config, null, '\t')); - return next(); - } catch (err) { - if (err instanceof Error) { - return next(new ErrorDTO(ErrorCodes.SETTINGS_ERROR, 'Settings error: ' + err.toString(), err)); - } - return next(new ErrorDTO(ErrorCodes.SETTINGS_ERROR, 'Settings error: ' + JSON.stringify(err, null, ' '), err)); - } - } - - public static async updateThumbnailSettings(req: Request, res: Response, next: NextFunction): Promise { - if ((typeof req.body === 'undefined') || (typeof req.body.settings === 'undefined')) { - return next(new ErrorDTO(ErrorCodes.INPUT_ERROR, 'settings is needed')); - } - - try { - const settings: { - server: ServerThumbnailConfig, - client: ClientThumbnailConfig - } = req.body.settings; - - await ConfigDiagnostics.testServerThumbnailConfig(settings.server); - await ConfigDiagnostics.testClientThumbnailConfig(settings.client); - 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 = await Config.original(); - original.Server.Media.Thumbnail = settings.server; - original.Client.Media.Thumbnail = settings.client; - original.save(); - ProjectPath.reset(); - await ConfigDiagnostics.runDiagnostics(); - Logger.info(LOG_TAG, 'new config:'); - Logger.info(LOG_TAG, JSON.stringify(Config, null, '\t')); - return next(); - } catch (err) { - if (err instanceof Error) { - return next(new ErrorDTO(ErrorCodes.SETTINGS_ERROR, 'Settings error: ' + err.toString(), err)); - } - return next(new ErrorDTO(ErrorCodes.SETTINGS_ERROR, 'Settings error: ' + JSON.stringify(err, null, ' '), err)); - } - } - - public static async updatePhotoSettings(req: Request, res: Response, next: NextFunction): Promise { - if ((typeof req.body === 'undefined') || (typeof req.body.settings === 'undefined')) { - return next(new ErrorDTO(ErrorCodes.INPUT_ERROR, 'settings is needed')); - } - - try { - const settings: { - server: ServerPhotoConfig, - client: ClientPhotoConfig - } = req.body.settings; - - await ConfigDiagnostics.testServerPhotoConfig(settings.server); - await ConfigDiagnostics.testClientPhotoConfig(settings.client); - 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 = await Config.original(); - original.Server.Media.Photo = settings.server; - original.Client.Media.Photo = settings.client; - original.save(); - ProjectPath.reset(); - await ConfigDiagnostics.runDiagnostics(); - Logger.info(LOG_TAG, 'new config:'); - Logger.info(LOG_TAG, JSON.stringify(Config, null, '\t')); - return next(); - } catch (err) { - if (err instanceof Error) { - return next(new ErrorDTO(ErrorCodes.SETTINGS_ERROR, 'Settings error: ' + err.toString(), err)); - } - return next(new ErrorDTO(ErrorCodes.SETTINGS_ERROR, 'Settings error: ' + JSON.stringify(err, null, ' '), err)); - } - } - - public static async updateBasicSettings(req: Request, res: Response, next: NextFunction): Promise { - if ((typeof req.body === 'undefined') || (typeof req.body.settings === 'undefined')) { - return next(new ErrorDTO(ErrorCodes.INPUT_ERROR, 'settings is needed')); - } - - try { - const settings: BasicConfigDTO = req.body.settings; - await ConfigDiagnostics.testImageFolder(settings.imagesFolder); - - BasicConfigDTOUtil.mapToConf(Config, settings); - // only updating explicitly set config (not saving config set by the diagnostics) - const original = await Config.original(); - BasicConfigDTOUtil.mapToConf(original, settings); - original.save(); - ProjectPath.reset(); - await ConfigDiagnostics.runDiagnostics(); - Logger.info(LOG_TAG, 'new config:'); - Logger.info(LOG_TAG, JSON.stringify(Config, null, '\t')); - return next(); - } catch (err) { - if (err instanceof Error) { - return next(new ErrorDTO(ErrorCodes.SETTINGS_ERROR, 'Settings error: ' + err.toString(), err)); - } - return next(new ErrorDTO(ErrorCodes.SETTINGS_ERROR, 'Settings error: ' + JSON.stringify(err, null, ' '), err)); - } - } - - public static async updateOtherSettings(req: Request, res: Response, next: NextFunction): Promise { - if ((typeof req.body === 'undefined') || (typeof req.body.settings === 'undefined')) { - return next(new ErrorDTO(ErrorCodes.INPUT_ERROR, 'settings is needed')); - } - - try { - const settings: OtherConfigDTO = req.body.settings; - Config.Client.Other = settings.Client; - - // only updating explicitly set config (not saving config set by the diagnostics) - const original = await Config.original(); - original.Client.Other = settings.Client; - original.Server.Threading.enabled = settings.Server.enabled; - original.Server.Threading.thumbnailThreads = settings.Server.thumbnailThreads; - await original.save(); - await ConfigDiagnostics.runDiagnostics(); - Logger.info(LOG_TAG, 'new config:'); - Logger.info(LOG_TAG, JSON.stringify(Config, null, '\t')); - return next(); - } catch (err) { - if (err instanceof Error) { - return next(new ErrorDTO(ErrorCodes.SETTINGS_ERROR, 'Settings error: ' + err.toString(), err)); - } - return next(new ErrorDTO(ErrorCodes.SETTINGS_ERROR, 'Settings error: ' + JSON.stringify(err, null, ' '), err)); - } - } - - public static async updateIndexingSettings(req: Request, res: Response, next: NextFunction): Promise { - if ((typeof req.body === 'undefined') || (typeof req.body.settings === 'undefined')) { - return next(new ErrorDTO(ErrorCodes.INPUT_ERROR, 'settings is needed')); - } - - try { - const settings: ServerIndexingConfig = req.body.settings; - Config.Server.Indexing = settings; - - // only updating explicitly set config (not saving config set by the diagnostics) - const original = await Config.original(); - original.Server.Indexing = settings; - original.save(); - await ConfigDiagnostics.runDiagnostics(); - Logger.info(LOG_TAG, 'new config:'); - Logger.info(LOG_TAG, JSON.stringify(Config, null, '\t')); - return next(); - } catch (err) { - if (err instanceof Error) { - return next(new ErrorDTO(ErrorCodes.SETTINGS_ERROR, 'Settings error: ' + err.toString(), err)); - } - return next(new ErrorDTO(ErrorCodes.SETTINGS_ERROR, 'Settings error: ' + JSON.stringify(err, null, ' '), err)); - } - } - - public static async updateJobSettings(req: Request, res: Response, next: NextFunction): Promise { - if ((typeof req.body === 'undefined') || (typeof req.body.settings === 'undefined')) { - return next(new ErrorDTO(ErrorCodes.INPUT_ERROR, 'settings is needed')); - } - - try { - - // only updating explicitly set config (not saving config set by the diagnostics) - const settings: ServerJobConfig = req.body.settings; - const original = await Config.original(); - await ConfigDiagnostics.testTasksConfig(settings, original); - - Config.Server.Jobs = settings; - original.Server.Jobs = settings; - original.save(); - - await ConfigDiagnostics.runDiagnostics(); - ObjectManagers.getInstance().JobManager.runSchedules(); - Logger.info(LOG_TAG, 'new config:'); - Logger.info(LOG_TAG, JSON.stringify(Config, null, '\t')); - return next(); - } catch (err) { - if (err instanceof Error) { - return next(new ErrorDTO(ErrorCodes.SETTINGS_ERROR, 'Settings error: ' + err.toString(), err)); - } - return next(new ErrorDTO(ErrorCodes.SETTINGS_ERROR, 'Settings error: ' + JSON.stringify(err, null, ' '), err)); - } - } } diff --git a/src/backend/middlewares/thumbnail/PhotoConverterMWs.ts b/src/backend/middlewares/thumbnail/PhotoConverterMWs.ts index ebbacf62..d3511091 100644 --- a/src/backend/middlewares/thumbnail/PhotoConverterMWs.ts +++ b/src/backend/middlewares/thumbnail/PhotoConverterMWs.ts @@ -13,14 +13,14 @@ export class PhotoConverterMWs { return next(); } // if conversion is not enabled redirect, so browser can cache the full - if (Config.Client.Media.Photo.Converting.enabled === false) { + if (Config.Media.Photo.Converting.enabled === false) { return res.redirect(req.originalUrl.slice(0, -1 * '\\bestFit'.length)); } const fullMediaPath = req.resultPipe as string; const convertedVideo = PhotoProcessing.generateConvertedPath( fullMediaPath, - Config.Server.Media.Photo.Converting.resolution + Config.Media.Photo.Converting.resolution ); // check if converted photo exist @@ -29,7 +29,7 @@ export class PhotoConverterMWs { return next(); } - if (Config.Server.Media.Photo.Converting.onTheFly === true) { + if (Config.Media.Photo.Converting.onTheFly === true) { req.resultPipe = await PhotoProcessing.convertPhoto(fullMediaPath); return next(); } diff --git a/src/backend/middlewares/thumbnail/ThumbnailGeneratorMWs.ts b/src/backend/middlewares/thumbnail/ThumbnailGeneratorMWs.ts index 583b4db7..ff3d943e 100644 --- a/src/backend/middlewares/thumbnail/ThumbnailGeneratorMWs.ts +++ b/src/backend/middlewares/thumbnail/ThumbnailGeneratorMWs.ts @@ -18,7 +18,7 @@ import {PersonEntry} from '../../model/database/sql/enitites/PersonEntry'; export class ThumbnailGeneratorMWs { private static ThumbnailMapEntries = - Config.Client.Media.Thumbnail.generateThumbnailMapEntries(); + Config.Media.Thumbnail.generateThumbnailMapEntries(); @ServerTime('2.th', 'Thumbnail decoration') public static async addThumbnailInformation( @@ -38,7 +38,7 @@ export class ThumbnailGeneratorMWs { // regenerate in case the list change since startup ThumbnailGeneratorMWs.ThumbnailMapEntries = - Config.Client.Media.Thumbnail.generateThumbnailMapEntries(); + Config.Media.Thumbnail.generateThumbnailMapEntries(); if (cw.directory) { ThumbnailGeneratorMWs.addThInfoTODir(cw.directory); } @@ -70,7 +70,7 @@ export class ThumbnailGeneratorMWs { } try { - const size: number = Config.Client.Media.Thumbnail.personThumbnailSize; + const size: number = Config.Media.Thumbnail.personThumbnailSize; const persons: PersonEntry[] = req.resultPipe as PersonEntry[]; @@ -148,11 +148,11 @@ export class ThumbnailGeneratorMWs { const mediaPath = req.resultPipe as string; let size: number = parseInt(req.params.size, 10) || - Config.Client.Media.Thumbnail.thumbnailSizes[0]; + Config.Media.Thumbnail.thumbnailSizes[0]; // validate size - if (Config.Client.Media.Thumbnail.thumbnailSizes.indexOf(size) === -1) { - size = Config.Client.Media.Thumbnail.thumbnailSizes[0]; + if (Config.Media.Thumbnail.thumbnailSizes.indexOf(size) === -1) { + size = Config.Media.Thumbnail.thumbnailSizes[0]; } try { @@ -189,7 +189,7 @@ export class ThumbnailGeneratorMWs { // load parameters const mediaPath = req.resultPipe as string; - const size: number = Config.Client.Media.Thumbnail.iconSize; + const size: number = Config.Media.Thumbnail.iconSize; try { req.resultPipe = await PhotoProcessing.generateThumbnail( diff --git a/src/backend/middlewares/user/AuthenticationMWs.ts b/src/backend/middlewares/user/AuthenticationMWs.ts index df79a9ff..d3f88476 100644 --- a/src/backend/middlewares/user/AuthenticationMWs.ts +++ b/src/backend/middlewares/user/AuthenticationMWs.ts @@ -18,10 +18,10 @@ export class AuthenticationMWs { res: Response, next: NextFunction ): Promise { - if (Config.Client.authenticationRequired === false) { + if (Config.Users.authenticationRequired === false) { req.session['user'] = { - name: UserRoles[Config.Client.unAuthenticatedUserRole], - role: Config.Client.unAuthenticatedUserRole, + name: UserRoles[Config.Users.unAuthenticatedUserRole], + role: Config.Users.unAuthenticatedUserRole, } as UserDTO; return next(); } @@ -42,10 +42,10 @@ export class AuthenticationMWs { res: Response, next: NextFunction ): Promise { - if (Config.Client.authenticationRequired === false) { + if (Config.Users.authenticationRequired === false) { req.session['user'] = { - name: UserRoles[Config.Client.unAuthenticatedUserRole], - role: Config.Client.unAuthenticatedUserRole, + name: UserRoles[Config.Users.unAuthenticatedUserRole], + role: Config.Users.unAuthenticatedUserRole, } as UserDTO; return next(); } @@ -133,7 +133,7 @@ export class AuthenticationMWs { res: Response, next: NextFunction ): Promise { - if (Config.Client.Sharing.enabled === false) { + if (Config.Sharing.enabled === false) { return next(); } // not enough parameter @@ -160,7 +160,7 @@ export class AuthenticationMWs { if ( !sharing || sharing.expires < Date.now() || - (Config.Client.Sharing.passwordProtected === true && + (Config.Sharing.passwordProtected === true && sharing.password && !PasswordHelper.comparePassword(password, sharing.password)) ) { @@ -201,7 +201,7 @@ export class AuthenticationMWs { res: Response, next: NextFunction ): Promise { - if (Config.Client.authenticationRequired === false) { + if (Config.Users.authenticationRequired === false) { return res.sendStatus(404); } @@ -253,7 +253,7 @@ export class AuthenticationMWs { private static async getSharingUser(req: Request): Promise { if ( - Config.Client.Sharing.enabled === true && + Config.Sharing.enabled === true && (!!req.query[QueryParams.gallery.sharingKey_query] || !!req.params[QueryParams.gallery.sharingKey_params]) ) { @@ -270,7 +270,7 @@ export class AuthenticationMWs { } if ( - Config.Client.Sharing.passwordProtected === true && + Config.Sharing.passwordProtected === true && sharing.password ) { return null; diff --git a/src/backend/middlewares/user/UserMWs.ts b/src/backend/middlewares/user/UserMWs.ts index 774fbed2..760cc195 100644 --- a/src/backend/middlewares/user/UserMWs.ts +++ b/src/backend/middlewares/user/UserMWs.ts @@ -10,7 +10,7 @@ export class UserMWs { res: Response, next: NextFunction ): Promise { - if (Config.Client.authenticationRequired === false) { + if (Config.Users.authenticationRequired === false) { return next(new ErrorDTO(ErrorCodes.USER_MANAGEMENT_DISABLED)); } if ( @@ -35,7 +35,7 @@ export class UserMWs { res: Response, next: NextFunction ): Promise { - if (Config.Client.authenticationRequired === false) { + if (Config.Users.authenticationRequired === false) { return next(new ErrorDTO(ErrorCodes.USER_MANAGEMENT_DISABLED)); } if ( @@ -60,7 +60,7 @@ export class UserMWs { res: Response, next: NextFunction ): Promise { - if (Config.Client.authenticationRequired === false) { + if (Config.Users.authenticationRequired === false) { return next(new ErrorDTO(ErrorCodes.USER_MANAGEMENT_DISABLED)); } if ( @@ -88,7 +88,7 @@ export class UserMWs { res: Response, next: NextFunction ): Promise { - if (Config.Client.authenticationRequired === false) { + if (Config.Users.authenticationRequired === false) { return next(new ErrorDTO(ErrorCodes.USER_MANAGEMENT_DISABLED)); } diff --git a/src/backend/model/Localizations.ts b/src/backend/model/Localizations.ts index f9eae329..1cacfc66 100644 --- a/src/backend/model/Localizations.ts +++ b/src/backend/model/Localizations.ts @@ -12,9 +12,9 @@ export class Localizations { .filter((f): boolean => fs.statSync(path.join(ProjectPath.FrontendFolder, f)).isDirectory() ); - Config.Client.languages = dirCont.filter( + Config.Server.languages = dirCont.filter( (d): boolean => notLanguage.indexOf(d) === -1 ); - Config.Client.languages.sort(); + Config.Server.languages.sort(); } } diff --git a/src/backend/model/database/memory/GalleryManager.ts b/src/backend/model/database/memory/GalleryManager.ts index 2ff8dd2d..23d06c4d 100644 --- a/src/backend/model/database/memory/GalleryManager.ts +++ b/src/backend/model/database/memory/GalleryManager.ts @@ -1,13 +1,13 @@ import {DirectoryDTOUtils, ParentDirectoryDTO} from '../../../../common/entities/DirectoryDTO'; -import { IGalleryManager } from '../interfaces/IGalleryManager'; +import {IGalleryManager} from '../interfaces/IGalleryManager'; import * as path from 'path'; import * as fs from 'fs'; -import { DiskManager } from '../../DiskManger'; -import { ProjectPath } from '../../../ProjectPath'; -import { Config } from '../../../../common/config/private/Config'; -import { DiskMangerWorker } from '../../threading/DiskMangerWorker'; -import { ReIndexingSensitivity } from '../../../../common/config/private/PrivateConfig'; -import { ServerPG2ConfMap } from '../../../../common/PG2ConfMap'; +import {DiskManager} from '../../DiskManger'; +import {ProjectPath} from '../../../ProjectPath'; +import {Config} from '../../../../common/config/private/Config'; +import {DiskMangerWorker} from '../../threading/DiskMangerWorker'; +import {ReIndexingSensitivity} from '../../../../common/config/private/PrivateConfig'; +import {ServerPG2ConfMap} from '../../../../common/PG2ConfMap'; export class GalleryManager implements IGalleryManager { public async listDirectory( @@ -15,7 +15,7 @@ export class GalleryManager implements IGalleryManager { knownLastModified?: number, knownLastScanned?: number ): Promise { - // If it seems that the content did not changed, do not work on it + // If it seems that the content did not change, do not work on it if (knownLastModified && knownLastScanned) { const stat = fs.statSync( path.join(ProjectPath.ImageFolder, relativeDirectoryName) @@ -23,10 +23,10 @@ export class GalleryManager implements IGalleryManager { const lastModified = DiskMangerWorker.calcLastModified(stat); if ( Date.now() - knownLastScanned <= - Config.Server.Indexing.cachedFolderTimeout && + Config.Indexing.cachedFolderTimeout && lastModified === knownLastModified && - Config.Server.Indexing.reIndexingSensitivity < - ReIndexingSensitivity.high + Config.Indexing.reIndexingSensitivity < + ReIndexingSensitivity.high ) { return Promise.resolve(null); } diff --git a/src/backend/model/database/sql/GalleryManager.ts b/src/backend/model/database/sql/GalleryManager.ts index 58cc63e0..984eb476 100644 --- a/src/backend/model/database/sql/GalleryManager.ts +++ b/src/backend/model/database/sql/GalleryManager.ts @@ -62,15 +62,15 @@ export class GalleryManager implements IGalleryManager, ISQLGalleryManager { dir.lastScanned === knownLastScanned ) { if ( - Config.Server.Indexing.reIndexingSensitivity === + Config.Indexing.reIndexingSensitivity === ReIndexingSensitivity.low ) { return null; } if ( Date.now() - dir.lastScanned <= - Config.Server.Indexing.cachedFolderTimeout && - Config.Server.Indexing.reIndexingSensitivity === + Config.Indexing.cachedFolderTimeout && + Config.Indexing.reIndexingSensitivity === ReIndexingSensitivity.medium ) { return null; @@ -101,10 +101,10 @@ export class GalleryManager implements IGalleryManager, ISQLGalleryManager { // not indexed since a while, index it in a lazy manner if ( (Date.now() - dir.lastScanned > - Config.Server.Indexing.cachedFolderTimeout && - Config.Server.Indexing.reIndexingSensitivity >= + Config.Indexing.cachedFolderTimeout && + Config.Indexing.reIndexingSensitivity >= ReIndexingSensitivity.medium) || - Config.Server.Indexing.reIndexingSensitivity >= + Config.Indexing.reIndexingSensitivity >= ReIndexingSensitivity.high ) { // on the fly reindexing @@ -114,7 +114,7 @@ export class GalleryManager implements IGalleryManager, ISQLGalleryManager { 'lazy reindexing reason: cache timeout: lastScanned: ' + (Date.now() - dir.lastScanned) + 'ms ago, cachedFolderTimeout:' + - Config.Server.Indexing.cachedFolderTimeout + Config.Indexing.cachedFolderTimeout ); ObjectManagers.getInstance() .IndexingManager.indexDirectory(relativeDirectoryName) @@ -186,7 +186,7 @@ export class GalleryManager implements IGalleryManager, ISQLGalleryManager { ) .innerJoinAndSelect('media.directory', 'directory') .orderBy('media.name, media.metadata.fileSize') - .limit(Config.Server.Duplicates.listingLimit) + .limit(Config.Duplicates.listingLimit) .getMany(); const duplicateParis: DuplicatesDTO[] = []; @@ -259,7 +259,7 @@ export class GalleryManager implements IGalleryManager, ISQLGalleryManager { ) .innerJoinAndSelect('media.directory', 'directory') .orderBy('media.metadata.creationDate, media.metadata.fileSize') - .limit(Config.Server.Duplicates.listingLimit) + .limit(Config.Duplicates.listingLimit) .getMany(); processDuplicates( @@ -354,9 +354,9 @@ export class GalleryManager implements IGalleryManager, ISQLGalleryManager { // TODO: do better filtering // NOTE: it should not cause an issue as it also do not save to the DB if ( - Config.Client.MetaFile.gpx === true || - Config.Client.MetaFile.pg2conf === true || - Config.Client.MetaFile.markdown === true + Config.MetaFile.gpx === true || + Config.MetaFile.pg2conf === true || + Config.MetaFile.markdown === true ) { query.leftJoinAndSelect('directory.metaFile', 'metaFile'); } diff --git a/src/backend/model/database/sql/PreviewManager.ts b/src/backend/model/database/sql/PreviewManager.ts index 84ae197f..3cb2bed4 100644 --- a/src/backend/model/database/sql/PreviewManager.ts +++ b/src/backend/model/database/sql/PreviewManager.ts @@ -29,7 +29,7 @@ export class PreviewManager implements IPreviewManager { private static setSorting( query: SelectQueryBuilder ): SelectQueryBuilder { - for (const sort of Config.Server.Preview.Sorting) { + for (const sort of Config.Preview.Sorting) { switch (sort) { case SortingMethods.descDate: query.addOrderBy('media.metadata.creationDate', 'DESC'); @@ -127,15 +127,15 @@ export class PreviewManager implements IPreviewManager { let previewMedia = null; if ( - Config.Server.Preview.SearchQuery && - !Utils.equalsFilter(Config.Server.Preview.SearchQuery, { + Config.Preview.SearchQuery && + !Utils.equalsFilter(Config.Preview.SearchQuery, { type: SearchQueryTypes.any_text, text: '', } as TextSearch) ) { const previewFilterQuery = await ( ObjectManagers.getInstance().SearchManager as ISQLSearchManager - ).prepareAndBuildWhereQuery(Config.Server.Preview.SearchQuery); + ).prepareAndBuildWhereQuery(Config.Preview.SearchQuery); previewMedia = await previewQuery() .andWhere(previewFilterQuery) .limit(1) @@ -177,7 +177,7 @@ export class PreviewManager implements IPreviewManager { q.where('media.directory = :dir', { dir: dir.id, }); - if (Config.Server.Database.type === DatabaseType.mysql) { + if (Config.Database.type === DatabaseType.mysql) { q.orWhere("directory.path like :path || '%'", { path: DiskMangerWorker.pathFromParent(dir), }); @@ -201,8 +201,8 @@ export class PreviewManager implements IPreviewManager { let previewMedia: PreviewPhotoDTOWithID = null; if ( - Config.Server.Preview.SearchQuery && - !Utils.equalsFilter(Config.Server.Preview.SearchQuery, { + Config.Preview.SearchQuery && + !Utils.equalsFilter(Config.Preview.SearchQuery, { type: SearchQueryTypes.any_text, text: '', } as TextSearch) @@ -211,7 +211,7 @@ export class PreviewManager implements IPreviewManager { .andWhere( await ( ObjectManagers.getInstance().SearchManager as ISQLSearchManager - ).prepareAndBuildWhereQuery(Config.Server.Preview.SearchQuery) + ).prepareAndBuildWhereQuery(Config.Preview.SearchQuery) ) .limit(1) .getOne(); diff --git a/src/backend/model/database/sql/SQLConnection.ts b/src/backend/model/database/sql/SQLConnection.ts index cdda3318..9623cc0a 100644 --- a/src/backend/model/database/sql/SQLConnection.ts +++ b/src/backend/model/database/sql/SQLConnection.ts @@ -40,7 +40,7 @@ export class SQLConnection { public static async getConnection(): Promise { if (this.connection == null) { - const options: any = this.getDriver(Config.Server.Database); + const options: any = this.getDriver(Config.Database); // options.name = 'main'; options.entities = [ UserEntity, @@ -62,7 +62,7 @@ export class SQLConnection { } Logger.debug( LOG_TAG, - 'Creating connection: ' + DatabaseType[Config.Server.Database.type], + 'Creating connection: ' + DatabaseType[Config.Database.type], ', with driver:', options.type ); @@ -108,16 +108,16 @@ export class SQLConnection { public static async init(): Promise { const connection = await this.getConnection(); - if (Config.Client.authenticationRequired !== true) { + if (Config.Users.authenticationRequired !== true) { return; } // Adding enforced users to the db const userRepository = connection.getRepository(UserEntity); if ( - Array.isArray(Config.Server.Database.enforcedUsers) && - Config.Server.Database.enforcedUsers.length > 0 + Array.isArray(Config.Users.enforcedUsers) && + Config.Users.enforcedUsers.length > 0 ) { - for (const uc of Config.Server.Database.enforcedUsers) { + for (const uc of Config.Users.enforcedUsers) { const user = await userRepository.findOneBy({ name: uc.name }); if (!user) { Logger.info(LOG_TAG, 'Saving enforced user: ' + uc.name); diff --git a/src/backend/model/database/sql/SearchManager.ts b/src/backend/model/database/sql/SearchManager.ts index 02e0d3c3..8aae1a1d 100644 --- a/src/backend/model/database/sql/SearchManager.ts +++ b/src/backend/model/database/sql/SearchManager.ts @@ -85,7 +85,7 @@ export class SearchManager implements ISQLSearchManager { .where('photo.metadata.keywords LIKE :text COLLATE ' + SQL_COLLATE, { text: '%' + text + '%', }) - .limit(Config.Client.Search.AutoComplete.targetItemsPerCategory * 2) + .limit(Config.Search.AutoComplete.targetItemsPerCategory * 2) .getRawMany() ) .map( @@ -120,7 +120,7 @@ export class SearchManager implements ISQLSearchManager { text: '%' + text + '%', }) .limit( - Config.Client.Search.AutoComplete.targetItemsPerCategory * 2 + Config.Search.AutoComplete.targetItemsPerCategory * 2 ) .orderBy('person.count', 'DESC') .getRawMany() @@ -161,7 +161,7 @@ export class SearchManager implements ISQLSearchManager { .groupBy( 'photo.metadata.positionData.country, photo.metadata.positionData.state, photo.metadata.positionData.city' ) - .limit(Config.Client.Search.AutoComplete.targetItemsPerCategory * 2) + .limit(Config.Search.AutoComplete.targetItemsPerCategory * 2) .getRawMany() ) .filter((pm): boolean => !!pm) @@ -199,7 +199,7 @@ export class SearchManager implements ISQLSearchManager { text: '%' + text + '%', }) .limit( - Config.Client.Search.AutoComplete.targetItemsPerCategory * 2 + Config.Search.AutoComplete.targetItemsPerCategory * 2 ) .getRawMany() ).map((r) => r.name), @@ -223,7 +223,7 @@ export class SearchManager implements ISQLSearchManager { {text: '%' + text + '%'} ) .limit( - Config.Client.Search.AutoComplete.targetItemsPerCategory * 2 + Config.Search.AutoComplete.targetItemsPerCategory * 2 ) .getRawMany() ).map((r) => r.caption), @@ -246,7 +246,7 @@ export class SearchManager implements ISQLSearchManager { text: '%' + text + '%', }) .limit( - Config.Client.Search.AutoComplete.targetItemsPerCategory * 2 + Config.Search.AutoComplete.targetItemsPerCategory * 2 ) .getRawMany() ).map((r) => r.name), @@ -260,13 +260,13 @@ export class SearchManager implements ISQLSearchManager { // if not enough items are available, load more from one category if ( [].concat(...partialResult).length < - Config.Client.Search.AutoComplete.maxItems + Config.Search.AutoComplete.maxItems ) { result = [].concat(...partialResult); } else { result = [].concat( ...partialResult.map((l) => - l.slice(0, Config.Client.Search.AutoComplete.targetItemsPerCategory) + l.slice(0, Config.Search.AutoComplete.targetItemsPerCategory) ) ); } @@ -292,16 +292,16 @@ export class SearchManager implements ISQLSearchManager { .select(['media', ...this.DIRECTORY_SELECT]) .where(this.buildWhereQuery(query)) .leftJoin('media.directory', 'directory') - .limit(Config.Client.Search.maxMediaResult + 1) + .limit(Config.Search.maxMediaResult + 1) .getMany(); - if (result.media.length > Config.Client.Search.maxMediaResult) { + if (result.media.length > Config.Search.maxMediaResult) { result.resultOverflow = true; } - if (Config.Client.Search.listMetafiles === true) { + if (Config.Search.listMetafiles === true) { const dIds = Array.from(new Set(result.media.map(m => (m.directory as unknown as { id: number }).id))); result.metaFile = await connection .getRepository(FileEntity) @@ -312,7 +312,7 @@ export class SearchManager implements ISQLSearchManager { .getMany(); } - if (Config.Client.Search.listDirectories === true) { + if (Config.Search.listDirectories === true) { const dirQuery = this.filterDirectoryQuery(query); if (dirQuery !== null) { result.directories = await connection @@ -321,7 +321,7 @@ export class SearchManager implements ISQLSearchManager { .where(this.buildWhereQuery(dirQuery, true)) .leftJoinAndSelect('directory.preview', 'preview') .leftJoinAndSelect('preview.directory', 'previewDirectory') - .limit(Config.Client.Search.maxDirectoryResult + 1) + .limit(Config.Search.maxDirectoryResult + 1) .select([ 'directory', 'preview.name', @@ -339,7 +339,7 @@ export class SearchManager implements ISQLSearchManager { } } if ( - result.directories.length > Config.Client.Search.maxDirectoryResult + result.directories.length > Config.Search.maxDirectoryResult ) { result.resultOverflow = true; } @@ -358,7 +358,7 @@ export class SearchManager implements ISQLSearchManager { .innerJoin('media.directory', 'directory') .where(await this.prepareAndBuildWhereQuery(query)); - if (Config.Server.Database.type === DatabaseType.mysql) { + if (Config.Database.type === DatabaseType.mysql) { return await sqlQuery.groupBy('RAND(), media.id').limit(1).getOne(); } return await sqlQuery.groupBy('RANDOM()').limit(1).getOne(); @@ -672,7 +672,7 @@ export class SearchManager implements ISQLSearchManager { } // MySQL uses C escape syntax in strings, details: // https://stackoverflow.com/questions/14926386/how-to-search-for-slash-in-mysql-and-why-escaping-not-required-for-wher - if (Config.Server.Database.type === DatabaseType.mysql) { + if (Config.Database.type === DatabaseType.mysql) { /// this reqExp replaces the "\\" to "\\\\\" return '%' + str.replace(new RegExp('\\\\', 'g'), '\\\\') + '%'; } @@ -841,7 +841,7 @@ export class SearchManager implements ISQLSearchManager { case SearchQueryTypes.OR: return { type: query.type, - list: (query as SearchListQuery).list.map( + list: ((query as SearchListQuery).list || []).map( (q): SearchQueryDTO => this.flattenSameOfQueries(q) ), } as SearchListQuery; diff --git a/src/backend/model/database/sql/SharingManager.ts b/src/backend/model/database/sql/SharingManager.ts index fb6a692d..a176db18 100644 --- a/src/backend/model/database/sql/SharingManager.ts +++ b/src/backend/model/database/sql/SharingManager.ts @@ -63,7 +63,7 @@ export class SharingManager implements ISharingManager { }); if ( - sharing.timeStamp < Date.now() - Config.Server.Sharing.updateTimeout && + sharing.timeStamp < Date.now() - Config.Sharing.updateTimeout && forceUpdate !== true ) { throw new Error("Sharing is locked, can't update anymore"); diff --git a/src/backend/model/database/sql/enitites/EntityUtils.ts b/src/backend/model/database/sql/enitites/EntityUtils.ts index 0cda99e9..8dfe8cf7 100644 --- a/src/backend/model/database/sql/enitites/EntityUtils.ts +++ b/src/backend/model/database/sql/enitites/EntityUtils.ts @@ -4,13 +4,13 @@ import { DatabaseType } from '../../../../../common/config/private/PrivateConfig export class ColumnCharsetCS implements ColumnOptions { public get charset(): string { - return Config.Server.Database.type === DatabaseType.mysql + return Config.Database.type === DatabaseType.mysql ? 'utf8mb4' : 'utf8'; } public get collation(): string { - return Config.Server.Database.type === DatabaseType.mysql + return Config.Database.type === DatabaseType.mysql ? 'utf8mb4_bin' : null; } diff --git a/src/backend/model/diagnostics/ConfigDiagnostics.ts b/src/backend/model/diagnostics/ConfigDiagnostics.ts index 7bba6cc9..3e3e8f02 100644 --- a/src/backend/model/diagnostics/ConfigDiagnostics.ts +++ b/src/backend/model/diagnostics/ConfigDiagnostics.ts @@ -1,4 +1,4 @@ -import {Config} from '../../../common/config/private/Config'; +import {Config, PrivateConfigClass} from '../../../common/config/private/Config'; import {Logger} from '../../Logger'; import {NotificationManager} from '../NotifocationManager'; import {SQLConnection} from '../database/sql/SQLConnection'; @@ -20,7 +20,6 @@ import { } from '../../../common/config/public/ClientConfig'; import { DatabaseType, - IPrivateConfig, ServerDataBaseConfig, ServerJobConfig, ServerMetaFileConfig, @@ -41,11 +40,11 @@ const LOG_TAG = '[ConfigDiagnostics]'; export class ConfigDiagnostics { static testAlbumsConfig( albumConfig: ClientAlbumConfig, - original: IPrivateConfig + original: PrivateConfigClass ): void { if ( albumConfig.enabled === true && - original.Server.Database.type === DatabaseType.memory + original.Database.type === DatabaseType.memory ) { throw new Error('Memory Database does not support albums'); } @@ -83,25 +82,25 @@ export class ConfigDiagnostics { } } - static async testClientMetaFileConfig( + static async testMetaFileConfig( metaFileConfig: ClientMetaFileConfig, - config: IPrivateConfig + config: PrivateConfigClass ): Promise { - if (metaFileConfig.gpx === true && config.Client.Map.enabled === false) { + if (metaFileConfig.gpx === true && config.Map.enabled === false) { throw new Error('*.gpx meta files are not supported without MAP'); } } - static async testServerMetaFileConfig( - metaFileConfig: ServerMetaFileConfig, - config: IPrivateConfig - ): Promise { - // nothing to check at the moment - } - - static testClientVideoConfig(videoConfig: ClientVideoConfig): Promise { + static testVideoConfig(videoConfig: ServerVideoConfig, + config: PrivateConfigClass): Promise { return new Promise((resolve, reject) => { try { + + if (config.Media.Video.enabled === true) { + if (videoConfig.transcoding.fps <= 0) { + throw new Error('fps should be grater than 0'); + } + } if (videoConfig.enabled === true) { const ffmpeg = FFmpegFactory.get(); ffmpeg().getAvailableCodecs((err: Error) => { @@ -134,16 +133,6 @@ export class ConfigDiagnostics { }); } - static async testServerVideoConfig( - videoConfig: ServerVideoConfig, - config: IPrivateConfig - ): Promise { - if (config.Client.Media.Video.enabled === true) { - if (videoConfig.transcoding.fps <= 0) { - throw new Error('fps should be grater than 0'); - } - } - } static async testSharp(): Promise { // eslint-disable-next-line @typescript-eslint/no-var-requires @@ -172,25 +161,16 @@ export class ConfigDiagnostics { }); } - static async testServerPhotoConfig(server: ServerPhotoConfig): Promise { - return; - } - static async testClientPhotoConfig(client: ClientPhotoConfig): Promise { - return; - } - - public static async testServerThumbnailConfig( - server: ServerThumbnailConfig + static async testThumbnailConfig( + thumbnailConfig: ServerThumbnailConfig ): Promise { - if (server.personFaceMargin < 0 || server.personFaceMargin > 1) { + + + if (thumbnailConfig.personFaceMargin < 0 || thumbnailConfig.personFaceMargin > 1) { throw new Error('personFaceMargin should be between 0 and 1'); } - } - static async testClientThumbnailConfig( - thumbnailConfig: ClientThumbnailConfig - ): Promise { if (isNaN(thumbnailConfig.iconSize) || thumbnailConfig.iconSize <= 0) { throw new Error( 'IconSize has to be >= 0 integer, got: ' + thumbnailConfig.iconSize @@ -209,20 +189,20 @@ export class ConfigDiagnostics { static async testTasksConfig( task: ServerJobConfig, - config: IPrivateConfig + config: PrivateConfigClass ): Promise { return; } static async testFacesConfig( faces: ClientFacesConfig, - config: IPrivateConfig + config: PrivateConfigClass ): Promise { if (faces.enabled === true) { - if (config.Server.Database.type === DatabaseType.memory) { + if (config.Database.type === DatabaseType.memory) { throw new Error('Memory Database do not support faces'); } - if (config.Client.Search.enabled === false) { + if (config.Search.enabled === false) { throw new Error('Faces support needs enabled search'); } } @@ -230,11 +210,11 @@ export class ConfigDiagnostics { static async testSearchConfig( search: ClientSearchConfig, - config: IPrivateConfig + config: PrivateConfigClass ): Promise { if ( search.enabled === true && - config.Server.Database.type === DatabaseType.memory + config.Database.type === DatabaseType.memory ) { throw new Error('Memory Database do not support searching'); } @@ -242,17 +222,17 @@ export class ConfigDiagnostics { static async testSharingConfig( sharing: ClientSharingConfig, - config: IPrivateConfig + config: PrivateConfigClass ): Promise { if ( sharing.enabled === true && - config.Server.Database.type === DatabaseType.memory + config.Database.type === DatabaseType.memory ) { throw new Error('Memory Database do not support sharing'); } if ( sharing.enabled === true && - config.Client.authenticationRequired === false + config.Users.authenticationRequired === false ) { throw new Error('In case of no authentication, sharing is not supported'); } @@ -260,11 +240,11 @@ export class ConfigDiagnostics { static async testRandomPhotoConfig( sharing: ClientRandomPhotoConfig, - config: IPrivateConfig + config: PrivateConfigClass ): Promise { if ( sharing.enabled === true && - config.Server.Database.type === DatabaseType.memory + config.Database.type === DatabaseType.memory ) { throw new Error('Memory Database do not support random photo'); } @@ -307,10 +287,30 @@ export class ConfigDiagnostics { } } + static async testConfig(config: PrivateConfigClass): Promise { + + await ConfigDiagnostics.testDatabase(config.Database); + await ConfigDiagnostics.testSharp(); + await ConfigDiagnostics.testTempFolder(config.Media.tempFolder); + await ConfigDiagnostics.testVideoConfig(config.Media.Video, config); + await ConfigDiagnostics.testMetaFileConfig(config.MetaFile, config); + await ConfigDiagnostics.testAlbumsConfig(config.Album, config); + await ConfigDiagnostics.testImageFolder(config.Media.folder); + await ConfigDiagnostics.testThumbnailConfig(config.Media.Thumbnail); + await ConfigDiagnostics.testSearchConfig(config.Search, config); + await ConfigDiagnostics.testPreviewConfig(config.Preview); + await ConfigDiagnostics.testFacesConfig(config.Faces, config); + await ConfigDiagnostics.testTasksConfig(config.Jobs, config); + await ConfigDiagnostics.testSharingConfig(config.Sharing, config); + await ConfigDiagnostics.testRandomPhotoConfig(config.Sharing, config); + await ConfigDiagnostics.testMapConfig(config.Map); + + } + static async runDiagnostics(): Promise { - if (Config.Server.Database.type !== DatabaseType.memory) { + if (Config.Database.type !== DatabaseType.memory) { try { - await ConfigDiagnostics.testDatabase(Config.Server.Database); + await ConfigDiagnostics.testDatabase(Config.Database); } catch (ex) { const err: Error = ex; Logger.warn(LOG_TAG, '[SQL error]', err.toString()); @@ -342,7 +342,7 @@ export class ConfigDiagnostics { } try { - await ConfigDiagnostics.testTempFolder(Config.Server.Media.tempFolder); + await ConfigDiagnostics.testTempFolder(Config.Media.tempFolder); } catch (ex) { const err: Error = ex; NotificationManager.error('Thumbnail folder error', err.toString()); @@ -350,11 +350,7 @@ export class ConfigDiagnostics { } try { - await ConfigDiagnostics.testClientVideoConfig(Config.Client.Media.Video); - await ConfigDiagnostics.testServerVideoConfig( - Config.Server.Media.Video, - Config - ); + await ConfigDiagnostics.testVideoConfig(Config.Media.Video, Config); } catch (ex) { const err: Error = ex; NotificationManager.warning( @@ -366,12 +362,12 @@ export class ConfigDiagnostics { 'Video support error, switching off..', err.toString() ); - Config.Client.Media.Video.enabled = false; + Config.Media.Video.enabled = false; } try { - await ConfigDiagnostics.testClientMetaFileConfig( - Config.Client.MetaFile, + await ConfigDiagnostics.testMetaFileConfig( + Config.MetaFile, Config ); } catch (ex) { @@ -385,30 +381,11 @@ export class ConfigDiagnostics { 'Meta file support error, switching off..', err.toString() ); - Config.Client.MetaFile.gpx = false; + Config.MetaFile.gpx = false; } try { - await ConfigDiagnostics.testServerMetaFileConfig( - Config.Server.MetaFile, - Config - ); - } catch (ex) { - const err: Error = ex; - NotificationManager.warning( - 'Meta file support error, switching off gpx..', - err.toString() - ); - Logger.warn( - LOG_TAG, - 'Meta file support error, switching off..', - err.toString() - ); - Config.Client.MetaFile.gpx = false; - } - - try { - await ConfigDiagnostics.testAlbumsConfig(Config.Client.Album, Config); + await ConfigDiagnostics.testAlbumsConfig(Config.Album, Config); } catch (ex) { const err: Error = ex; NotificationManager.warning( @@ -420,19 +397,19 @@ export class ConfigDiagnostics { 'Meta file support error, switching off..', err.toString() ); - Config.Client.Album.enabled = false; + Config.Album.enabled = false; } try { - await ConfigDiagnostics.testImageFolder(Config.Server.Media.folder); + await ConfigDiagnostics.testImageFolder(Config.Media.folder); } catch (ex) { const err: Error = ex; NotificationManager.error('Images folder error', err.toString()); Logger.error(LOG_TAG, 'Images folder error', err.toString()); } try { - await ConfigDiagnostics.testClientThumbnailConfig( - Config.Client.Media.Thumbnail + await ConfigDiagnostics.testThumbnailConfig( + Config.Media.Thumbnail ); } catch (ex) { const err: Error = ex; @@ -441,7 +418,7 @@ export class ConfigDiagnostics { } try { - await ConfigDiagnostics.testSearchConfig(Config.Client.Search, Config); + await ConfigDiagnostics.testSearchConfig(Config.Search, Config); } catch (ex) { const err: Error = ex; NotificationManager.warning( @@ -454,11 +431,11 @@ export class ConfigDiagnostics { 'Search is not supported with these settings, switching off..', err.toString() ); - Config.Client.Search.enabled = false; + Config.Search.enabled = false; } try { - await ConfigDiagnostics.testPreviewConfig(Config.Server.Preview); + await ConfigDiagnostics.testPreviewConfig(Config.Preview); } catch (ex) { const err: Error = ex; NotificationManager.warning( @@ -470,14 +447,14 @@ export class ConfigDiagnostics { 'Preview settings are not valid, resetting search query', err.toString() ); - Config.Server.Preview.SearchQuery = { + Config.Preview.SearchQuery = { type: SearchQueryTypes.any_text, text: '', } as TextSearch; } try { - await ConfigDiagnostics.testFacesConfig(Config.Client.Faces, Config); + await ConfigDiagnostics.testFacesConfig(Config.Faces, Config); } catch (ex) { const err: Error = ex; NotificationManager.warning( @@ -490,11 +467,11 @@ export class ConfigDiagnostics { 'Faces are not supported with these settings, switching off..', err.toString() ); - Config.Client.Faces.enabled = false; + Config.Faces.enabled = false; } try { - await ConfigDiagnostics.testTasksConfig(Config.Server.Jobs, Config); + await ConfigDiagnostics.testTasksConfig(Config.Jobs, Config); } catch (ex) { const err: Error = ex; NotificationManager.warning( @@ -507,11 +484,11 @@ export class ConfigDiagnostics { 'Some Tasks not supported with these settings, switching off..', err.toString() ); - Config.Client.Faces.enabled = false; + Config.Faces.enabled = false; } try { - await ConfigDiagnostics.testSharingConfig(Config.Client.Sharing, Config); + await ConfigDiagnostics.testSharingConfig(Config.Sharing, Config); } catch (ex) { const err: Error = ex; NotificationManager.warning( @@ -524,12 +501,12 @@ export class ConfigDiagnostics { 'Sharing is not supported with these settings, switching off..', err.toString() ); - Config.Client.Sharing.enabled = false; + Config.Sharing.enabled = false; } try { await ConfigDiagnostics.testRandomPhotoConfig( - Config.Client.Sharing, + Config.Sharing, Config ); } catch (ex) { @@ -544,11 +521,11 @@ export class ConfigDiagnostics { 'Random Media is not supported with these settings, switching off..', err.toString() ); - Config.Client.Sharing.enabled = false; + Config.Sharing.enabled = false; } try { - await ConfigDiagnostics.testMapConfig(Config.Client.Map); + await ConfigDiagnostics.testMapConfig(Config.Map); } catch (ex) { const err: Error = ex; NotificationManager.warning( @@ -562,7 +539,7 @@ export class ConfigDiagnostics { 'Please adjust the config properly.', err.toString() ); - Config.Client.Map.mapProvider = MapProviders.OpenStreetMap; + Config.Map.mapProvider = MapProviders.OpenStreetMap; } } } diff --git a/src/backend/model/fileprocessing/GPXProcessing.ts b/src/backend/model/fileprocessing/GPXProcessing.ts index 38f6786d..6b050dfa 100644 --- a/src/backend/model/fileprocessing/GPXProcessing.ts +++ b/src/backend/model/fileprocessing/GPXProcessing.ts @@ -19,8 +19,8 @@ export class GPXProcessing { ProjectPath.TranscodedFolder, ProjectPath.getRelativePathToImages(path.dirname(filePath)), path.basename(filePath) - + '_' + Config.Server.MetaFile.GPXCompressing.minDistance + 'm' + - Config.Server.MetaFile.GPXCompressing.minTimeDistance + 'ms' + + + '_' + Config.MetaFile.GPXCompressing.minDistance + 'm' + + Config.MetaFile.GPXCompressing.minTimeDistance + 'ms' + path.extname(filePath)); } @@ -112,8 +112,8 @@ export class GPXProcessing { const timeDelta = (Date.parse(list[i].time[0]) - Date.parse(list[i - 1].time[0])); // mill sec. const dist = distance(list[i - 1], list[i]); // meters - return !(timeDelta < Config.Server.MetaFile.GPXCompressing.minTimeDistance && - dist < Config.Server.MetaFile.GPXCompressing.minDistance); + return !(timeDelta < Config.MetaFile.GPXCompressing.minTimeDistance && + dist < Config.MetaFile.GPXCompressing.minDistance); }; gpxObj.gpx.trk[0].trkseg[0].trkpt = items.filter(gpxEntryFilter).map((v) => { diff --git a/src/backend/model/fileprocessing/PhotoProcessing.ts b/src/backend/model/fileprocessing/PhotoProcessing.ts index cf33c672..41c2d85a 100644 --- a/src/backend/model/fileprocessing/PhotoProcessing.ts +++ b/src/backend/model/fileprocessing/PhotoProcessing.ts @@ -26,20 +26,20 @@ export class PhotoProcessing { if (Config.Server.Threading.enabled === true) { if (Config.Server.Threading.thumbnailThreads > 0) { - Config.Client.Media.Thumbnail.concurrentThumbnailGenerations = + Config.Media.Thumbnail.concurrentThumbnailGenerations = Config.Server.Threading.thumbnailThreads; } else { - Config.Client.Media.Thumbnail.concurrentThumbnailGenerations = Math.max( + Config.Media.Thumbnail.concurrentThumbnailGenerations = Math.max( 1, os.cpus().length - 1 ); } } else { - Config.Client.Media.Thumbnail.concurrentThumbnailGenerations = 1; + Config.Media.Thumbnail.concurrentThumbnailGenerations = 1; } this.taskQue = new TaskExecuter( - Config.Client.Media.Thumbnail.concurrentThumbnailGenerations, + Config.Media.Thumbnail.concurrentThumbnailGenerations, (input): Promise => PhotoWorker.render(input) ); @@ -57,7 +57,7 @@ export class PhotoProcessing { photo.directory.name, photo.name ); - const size: number = Config.Client.Media.Thumbnail.personThumbnailSize; + const size: number = Config.Media.Thumbnail.personThumbnailSize; const faceRegion = person.sampleRegion.media.metadata.faces.find(f => f.name === person.name); // generate thumbnail path const thPath = PhotoProcessing.generatePersonThumbnailPath( @@ -77,11 +77,11 @@ export class PhotoProcessing { const margin = { x: Math.round( faceRegion.box.width * - Config.Server.Media.Thumbnail.personFaceMargin + Config.Media.Thumbnail.personFaceMargin ), y: Math.round( faceRegion.box.height * - Config.Server.Media.Thumbnail.personFaceMargin + Config.Media.Thumbnail.personFaceMargin ), }; @@ -102,8 +102,8 @@ export class PhotoProcessing { width: faceRegion.box.width + margin.x, height: faceRegion.box.height + margin.y, }, - useLanczos3: Config.Server.Media.Thumbnail.useLanczos3, - quality: Config.Server.Media.Thumbnail.quality, + useLanczos3: Config.Media.Thumbnail.useLanczos3, + quality: Config.Media.Thumbnail.quality, } as RendererInput; input.cut.width = Math.min( input.cut.width, @@ -124,7 +124,7 @@ export class PhotoProcessing { return path.join( ProjectPath.TranscodedFolder, ProjectPath.getRelativePathToImages(path.dirname(mediaPath)), - file + '_' + size + 'q' + Config.Server.Media.Thumbnail.quality + PhotoProcessing.CONVERTED_EXTENSION + file + '_' + size + 'q' + Config.Media.Thumbnail.quality + PhotoProcessing.CONVERTED_EXTENSION ); } @@ -177,8 +177,8 @@ export class PhotoProcessing { if ( (size + '').length !== sizeStr.length || - (Config.Client.Media.Thumbnail.thumbnailSizes.indexOf(size) === -1 && - Config.Server.Media.Photo.Converting.resolution !== size) + (Config.Media.Thumbnail.thumbnailSizes.indexOf(size) === -1 && + Config.Media.Photo.Converting.resolution !== size) ) { return false; } @@ -208,7 +208,7 @@ export class PhotoProcessing { public static async convertPhoto(mediaPath: string): Promise { return this.generateThumbnail( mediaPath, - Config.Server.Media.Photo.Converting.resolution, + Config.Media.Photo.Converting.resolution, ThumbnailSourceType.Photo, false ); @@ -255,8 +255,8 @@ export class PhotoProcessing { size, outPath, makeSquare, - useLanczos3: Config.Server.Media.Thumbnail.useLanczos3, - quality: Config.Server.Media.Thumbnail.quality, + useLanczos3: Config.Media.Thumbnail.useLanczos3, + quality: Config.Media.Thumbnail.quality, } as RendererInput; const outDir = path.dirname(input.outPath); diff --git a/src/backend/model/fileprocessing/VideoProcessing.ts b/src/backend/model/fileprocessing/VideoProcessing.ts index 65b5d1ea..429f4d8b 100644 --- a/src/backend/model/fileprocessing/VideoProcessing.ts +++ b/src/backend/model/fileprocessing/VideoProcessing.ts @@ -83,27 +83,27 @@ export class VideoProcessing { videoPath, output: { path: outPath, - codec: Config.Server.Media.Video.transcoding.codec, - format: Config.Server.Media.Video.transcoding.format, - crf: Config.Server.Media.Video.transcoding.crf, - preset: Config.Server.Media.Video.transcoding.preset, - customOptions: Config.Server.Media.Video.transcoding.customOptions, + codec: Config.Media.Video.transcoding.codec, + format: Config.Media.Video.transcoding.format, + crf: Config.Media.Video.transcoding.crf, + preset: Config.Media.Video.transcoding.preset, + customOptions: Config.Media.Video.transcoding.customOptions, }, }; - if (metaData.bitRate > Config.Server.Media.Video.transcoding.bitRate) { + if (metaData.bitRate > Config.Media.Video.transcoding.bitRate) { renderInput.output.bitRate = - Config.Server.Media.Video.transcoding.bitRate; + Config.Media.Video.transcoding.bitRate; } - if (metaData.fps > Config.Server.Media.Video.transcoding.fps) { - renderInput.output.fps = Config.Server.Media.Video.transcoding.fps; + if (metaData.fps > Config.Media.Video.transcoding.fps) { + renderInput.output.fps = Config.Media.Video.transcoding.fps; } if ( - Config.Server.Media.Video.transcoding.resolution < metaData.size.height + Config.Media.Video.transcoding.resolution < metaData.size.height ) { renderInput.output.resolution = - Config.Server.Media.Video.transcoding.resolution; + Config.Media.Video.transcoding.resolution; } const outDir = path.dirname(renderInput.output.path); @@ -119,12 +119,12 @@ export class VideoProcessing { protected static getConvertedFilePostFix(): string { return ( - Math.round(Config.Server.Media.Video.transcoding.bitRate / 1024) + + Math.round(Config.Media.Video.transcoding.bitRate / 1024) + 'k' + - Config.Server.Media.Video.transcoding.codec.toString().toLowerCase() + - Config.Server.Media.Video.transcoding.resolution + + Config.Media.Video.transcoding.codec.toString().toLowerCase() + + Config.Media.Video.transcoding.resolution + '.' + - Config.Server.Media.Video.transcoding.format.toLowerCase() + Config.Media.Video.transcoding.format.toLowerCase() ); } } diff --git a/src/backend/model/jobs/JobManager.ts b/src/backend/model/jobs/JobManager.ts index cc69757c..e1419a96 100644 --- a/src/backend/model/jobs/JobManager.ts +++ b/src/backend/model/jobs/JobManager.ts @@ -93,11 +93,11 @@ export class JobManager implements IJobManager, IJobListener { if (state !== JobProgressStates.finished || soloRun === true) { return; } - const sch = Config.Server.Jobs.scheduled.find( + const sch = Config.Jobs.scheduled.find( (s): boolean => s.jobName === job.Name ); if (sch) { - const children = Config.Server.Jobs.scheduled.filter( + const children = Config.Jobs.scheduled.filter( (s): boolean => s.trigger.type === JobTriggerType.after && (s.trigger as AfterJobTrigger).afterScheduleName === sch.name @@ -135,7 +135,7 @@ export class JobManager implements IJobManager, IJobListener { public runSchedules(): void { this.stopSchedules(); Logger.info(LOG_TAG, 'Running job schedules'); - Config.Server.Jobs.scheduled.forEach((s): void => this.runSchedule(s)); + Config.Jobs.scheduled.forEach((s): void => this.runSchedule(s)); } protected findJob(jobName: string): IJob { diff --git a/src/backend/model/jobs/JobProgressManager.ts b/src/backend/model/jobs/JobProgressManager.ts index 4c2ac514..45350e70 100644 --- a/src/backend/model/jobs/JobProgressManager.ts +++ b/src/backend/model/jobs/JobProgressManager.ts @@ -59,7 +59,7 @@ export class JobProgressManager { while ( Object.keys(this.db.progresses).length > - Config.Server.Jobs.maxSavedProgress + Config.Jobs.maxSavedProgress ) { let min: string = null; for (const key of Object.keys(this.db.progresses)) { diff --git a/src/backend/model/jobs/jobs/DBResetJob.ts b/src/backend/model/jobs/jobs/DBResetJob.ts index 0c9c3971..3ced0316 100644 --- a/src/backend/model/jobs/jobs/DBResetJob.ts +++ b/src/backend/model/jobs/jobs/DBResetJob.ts @@ -13,7 +13,7 @@ export class DBRestJob extends Job { protected readonly IsInstant = true; public get Supported(): boolean { - return Config.Server.Database.type !== DatabaseType.memory; + return Config.Database.type !== DatabaseType.memory; } protected async init(): Promise { diff --git a/src/backend/model/jobs/jobs/FileJob.ts b/src/backend/model/jobs/jobs/FileJob.ts index 481218b7..f63f67e0 100644 --- a/src/backend/model/jobs/jobs/FileJob.ts +++ b/src/backend/model/jobs/jobs/FileJob.ts @@ -35,7 +35,7 @@ export abstract class FileJob { const DBBased = this.config.indexedOnly && - Config.Server.Database.type !== DatabaseType.memory; + Config.Database.type !== DatabaseType.memory; if ( this.fileQueue.length === 0 && ((this.directoryQueue.length === 0 && !DBBased) || @@ -176,7 +176,7 @@ export abstract class FileJob 0; @@ -227,7 +227,7 @@ export abstract class FileJob { diff --git a/src/backend/model/jobs/jobs/IndexingJob.ts b/src/backend/model/jobs/jobs/IndexingJob.ts index c0d0f3d6..cb4a7dd4 100644 --- a/src/backend/model/jobs/jobs/IndexingJob.ts +++ b/src/backend/model/jobs/jobs/IndexingJob.ts @@ -33,7 +33,7 @@ export class IndexingJob< ]; public get Supported(): boolean { - return Config.Server.Database.type !== DatabaseType.memory; + return Config.Database.type !== DatabaseType.memory; } protected async init(): Promise { diff --git a/src/backend/model/jobs/jobs/JobProgress.ts b/src/backend/model/jobs/jobs/JobProgress.ts index e38c80bc..f156dcd3 100644 --- a/src/backend/model/jobs/jobs/JobProgress.ts +++ b/src/backend/model/jobs/jobs/JobProgress.ts @@ -87,7 +87,7 @@ export class JobProgress { }; log(log: string): void { - while (this.logs.length > Config.Server.Jobs.maxSavedProgress) { + while (this.logs.length > Config.Jobs.maxSavedProgress) { this.logs.shift(); } this.logs.push({ diff --git a/src/backend/model/jobs/jobs/PhotoConvertingJob.ts b/src/backend/model/jobs/jobs/PhotoConvertingJob.ts index 7ab9d34d..bc405937 100644 --- a/src/backend/model/jobs/jobs/PhotoConvertingJob.ts +++ b/src/backend/model/jobs/jobs/PhotoConvertingJob.ts @@ -11,13 +11,13 @@ export class PhotoConvertingJob extends FileJob { } public get Supported(): boolean { - return Config.Client.Media.Photo.Converting.enabled === true; + return Config.Media.Photo.Converting.enabled === true; } protected async shouldProcess(mPath: string): Promise { return !(await PhotoProcessing.convertedPhotoExist( mPath, - Config.Server.Media.Photo.Converting.resolution + Config.Media.Photo.Converting.resolution )); } diff --git a/src/backend/model/jobs/jobs/PreviewFillingJob.ts b/src/backend/model/jobs/jobs/PreviewFillingJob.ts index e6f9a69d..23c77d9b 100644 --- a/src/backend/model/jobs/jobs/PreviewFillingJob.ts +++ b/src/backend/model/jobs/jobs/PreviewFillingJob.ts @@ -14,7 +14,7 @@ export class PreviewFillingJob extends Job { status: 'Persons' | 'Albums' | 'Directory' = 'Persons'; public get Supported(): boolean { - return Config.Server.Database.type !== DatabaseType.memory; + return Config.Database.type !== DatabaseType.memory; } protected async init(): Promise { diff --git a/src/backend/model/jobs/jobs/PreviewResetJob.ts b/src/backend/model/jobs/jobs/PreviewResetJob.ts index 77eb90f4..f32d6f39 100644 --- a/src/backend/model/jobs/jobs/PreviewResetJob.ts +++ b/src/backend/model/jobs/jobs/PreviewResetJob.ts @@ -13,7 +13,7 @@ export class PreviewRestJob extends Job { protected readonly IsInstant = true; public get Supported(): boolean { - return Config.Server.Database.type !== DatabaseType.memory; + return Config.Database.type !== DatabaseType.memory; } protected async init(): Promise { diff --git a/src/backend/model/jobs/jobs/ThumbnailGenerationJob.ts b/src/backend/model/jobs/jobs/ThumbnailGenerationJob.ts index 23cb3845..4a16c117 100644 --- a/src/backend/model/jobs/jobs/ThumbnailGenerationJob.ts +++ b/src/backend/model/jobs/jobs/ThumbnailGenerationJob.ts @@ -20,7 +20,7 @@ export class ThumbnailGenerationJob extends FileJob<{ type: 'number-array', name: backendTexts.sizeToGenerate.name, description: backendTexts.sizeToGenerate.description, - defaultValue: [Config.Client.Media.Thumbnail.thumbnailSizes[0]], + defaultValue: [Config.Media.Thumbnail.thumbnailSizes[0]], }); } @@ -37,11 +37,11 @@ export class ThumbnailGenerationJob extends FileJob<{ throw new Error( 'unknown thumbnails sizes: ' + config.sizes + - '. It should be an array from:' + Config.Client.Media.Thumbnail.thumbnailSizes + '. It should be an array from:' + Config.Media.Thumbnail.thumbnailSizes ); } for (const item of config.sizes) { - if (Config.Client.Media.Thumbnail.thumbnailSizes.indexOf(item) === -1) { + if (Config.Media.Thumbnail.thumbnailSizes.indexOf(item) === -1) { throw new Error( 'unknown thumbnails size: ' + item + diff --git a/src/backend/model/jobs/jobs/VideoConvertingJob.ts b/src/backend/model/jobs/jobs/VideoConvertingJob.ts index 592810d9..51dc5639 100644 --- a/src/backend/model/jobs/jobs/VideoConvertingJob.ts +++ b/src/backend/model/jobs/jobs/VideoConvertingJob.ts @@ -13,7 +13,7 @@ export class VideoConvertingJob extends FileJob { } public get Supported(): boolean { - return Config.Client.Media.Video.enabled === true; + return Config.Media.Video.enabled === true; } protected async shouldProcess(mPath: string): Promise { diff --git a/src/backend/model/threading/DiskMangerWorker.ts b/src/backend/model/threading/DiskMangerWorker.ts index d6f5ef36..688cec1c 100644 --- a/src/backend/model/threading/DiskMangerWorker.ts +++ b/src/backend/model/threading/DiskMangerWorker.ts @@ -52,15 +52,15 @@ export class DiskMangerWorker { absoluteDirectoryName: string ): Promise { if ( - Config.Server.Indexing.excludeFolderList.length === 0 && - Config.Server.Indexing.excludeFileList.length === 0 + Config.Indexing.excludeFolderList.length === 0 && + Config.Indexing.excludeFileList.length === 0 ) { return false; } const absoluteName = path.normalize(path.join(absoluteDirectoryName, name)); const relativeName = path.normalize(path.join(relativeDirectoryName, name)); - for (const exclude of Config.Server.Indexing.excludeFolderList) { + for (const exclude of Config.Indexing.excludeFolderList) { if (exclude.startsWith('/')) { if (exclude === absoluteName) { return true; @@ -76,7 +76,7 @@ export class DiskMangerWorker { } } // exclude dirs that have the given files (like .ignore) - for (const exclude of Config.Server.Indexing.excludeFileList) { + for (const exclude of Config.Indexing.excludeFileList) { try { await fsp.access(path.join(absoluteName, exclude)); return true; @@ -200,7 +200,7 @@ export class DiskMangerWorker { } } else if (VideoProcessing.isVideo(fullFilePath)) { if ( - Config.Client.Media.Video.enabled === false || + Config.Media.Video.enabled === false || settings.noVideo === true || settings.previewOnly === true ) { @@ -249,11 +249,11 @@ export class DiskMangerWorker { switch (extension) { case '.gpx': - return Config.Client.MetaFile.gpx; + return Config.MetaFile.gpx; case '.md': - return Config.Client.MetaFile.markdown; + return Config.MetaFile.markdown; case '.pg2conf': - return Config.Client.MetaFile.pg2conf; + return Config.MetaFile.pg2conf; } return false; diff --git a/src/backend/model/threading/MetadataLoader.ts b/src/backend/model/threading/MetadataLoader.ts index bf5df8d3..ec2789d3 100644 --- a/src/backend/model/threading/MetadataLoader.ts +++ b/src/backend/model/threading/MetadataLoader.ts @@ -96,8 +96,8 @@ export class MetadataLoader { return new Promise((resolve, reject) => { const fd = fs.openSync(fullPath, 'r'); - const data = Buffer.allocUnsafe(Config.Server.photoMetadataSize); - fs.read(fd, data, 0, Config.Server.photoMetadataSize, 0, (err) => { + const data = Buffer.allocUnsafe(Config.Media.photoMetadataSize); + fs.read(fd, data, 0, Config.Media.photoMetadataSize, 0, (err) => { fs.closeSync(fd); if (err) { return reject({file: fullPath, error: err}); @@ -298,7 +298,7 @@ export class MetadataLoader { metadata.size.height = height; } - if (Config.Client.Faces.enabled) { + if (Config.Faces.enabled) { const faces: FaceRegion[] = []; if ( exif.Regions && @@ -394,7 +394,7 @@ export class MetadataLoader { } if (faces.length > 0) { metadata.faces = faces; // save faces - if (Config.Client.Faces.keywordsToPersons) { + if (Config.Faces.keywordsToPersons) { // remove faces from keywords metadata.faces.forEach((f) => { const index = metadata.keywords.indexOf(f.name); diff --git a/src/backend/routes/AlbumRouter.ts b/src/backend/routes/AlbumRouter.ts index 28b38947..5f529cac 100644 --- a/src/backend/routes/AlbumRouter.ts +++ b/src/backend/routes/AlbumRouter.ts @@ -16,7 +16,7 @@ export class AlbumRouter { private static addListAlbums(app: Express): void { app.get( - [Config.Client.apiPath + '/albums'], + [Config.Server.apiPath + '/albums'], // common part AuthenticationMWs.authenticate, AuthenticationMWs.authorise(UserRoles.User), @@ -31,7 +31,7 @@ export class AlbumRouter { private static addDeleteAlbum(app: Express): void { app.delete( - [Config.Client.apiPath + '/albums/:id'], + [Config.Server.apiPath + '/albums/:id'], // common part AuthenticationMWs.authenticate, AuthenticationMWs.authorise(UserRoles.Admin), @@ -46,7 +46,7 @@ export class AlbumRouter { private static addAddSavedSearch(app: Express): void { app.put( - [Config.Client.apiPath + '/albums/saved-searches'], + [Config.Server.apiPath + '/albums/saved-searches'], // common part AuthenticationMWs.authenticate, AuthenticationMWs.authorise(UserRoles.Admin), diff --git a/src/backend/routes/ErrorRouter.ts b/src/backend/routes/ErrorRouter.ts index b959ff64..a50ee1be 100644 --- a/src/backend/routes/ErrorRouter.ts +++ b/src/backend/routes/ErrorRouter.ts @@ -11,7 +11,7 @@ export class ErrorRouter { } private static addApiErrorHandler(app: Express): void { - app.use(Config.Client.apiPath + '/*', RenderingMWs.renderError); + app.use(Config.Server.apiPath + '/*', RenderingMWs.renderError); } private static addGenericHandler(app: Express): void { diff --git a/src/backend/routes/GalleryRouter.ts b/src/backend/routes/GalleryRouter.ts index 31dc8a73..be9768b7 100644 --- a/src/backend/routes/GalleryRouter.ts +++ b/src/backend/routes/GalleryRouter.ts @@ -34,7 +34,7 @@ export class GalleryRouter { protected static addDirectoryList(app: Express): void { app.get( - [Config.Client.apiPath + '/gallery/content/:directory(*)', Config.Client.apiPath + '/gallery/', Config.Client.apiPath + '/gallery//'], + [Config.Server.apiPath + '/gallery/content/:directory(*)', Config.Server.apiPath + '/gallery/', Config.Server.apiPath + '/gallery//'], // common part AuthenticationMWs.authenticate, AuthenticationMWs.normalizePathParam('directory'), @@ -52,7 +52,7 @@ export class GalleryRouter { protected static addDirectoryZip(app: Express): void { app.get( - [Config.Client.apiPath + '/gallery/zip/:directory(*)'], + [Config.Server.apiPath + '/gallery/zip/:directory(*)'], // common part AuthenticationMWs.authenticate, AuthenticationMWs.normalizePathParam('directory'), @@ -67,7 +67,7 @@ export class GalleryRouter { protected static addGetImage(app: Express): void { app.get( [ - Config.Client.apiPath + '/gallery/content/:mediaPath(*.(' + + Config.Server.apiPath + '/gallery/content/:mediaPath(*.(' + SupportedFormats.Photos.join('|') + '))', ], @@ -86,7 +86,7 @@ export class GalleryRouter { protected static addGetBestFitImage(app: Express): void { app.get( [ - Config.Client.apiPath + '/gallery/content/:mediaPath(*.(' + + Config.Server.apiPath + '/gallery/content/:mediaPath(*.(' + SupportedFormats.Photos.join('|') + '))/bestFit', ], @@ -106,7 +106,7 @@ export class GalleryRouter { protected static addGetVideo(app: Express): void { app.get( [ - Config.Client.apiPath + '/gallery/content/:mediaPath(*.(' + + Config.Server.apiPath + '/gallery/content/:mediaPath(*.(' + SupportedFormats.Videos.join('|') + '))', ], @@ -125,7 +125,7 @@ export class GalleryRouter { protected static addGetBestFitVideo(app: Express): void { app.get( [ - Config.Client.apiPath + '/gallery/content/:mediaPath(*.(' + + Config.Server.apiPath + '/gallery/content/:mediaPath(*.(' + SupportedFormats.Videos.join('|') + '))/bestFit', ], @@ -145,7 +145,7 @@ export class GalleryRouter { protected static addGetMetaFile(app: Express): void { app.get( [ - Config.Client.apiPath + '/gallery/content/:mediaPath(*.(' + + Config.Server.apiPath + '/gallery/content/:mediaPath(*.(' + SupportedFormats.MetaFiles.join('|') + '))', ], @@ -164,7 +164,7 @@ export class GalleryRouter { protected static addGetBestFitMetaFile(app: Express): void { app.get( [ - Config.Client.apiPath + '/gallery/content/:mediaPath(*.(' + + Config.Server.apiPath + '/gallery/content/:mediaPath(*.(' + SupportedFormats.MetaFiles.join('|') + '))/bestFit', ], @@ -183,7 +183,7 @@ export class GalleryRouter { protected static addRandom(app: Express): void { app.get( - [Config.Client.apiPath + '/gallery/random/:searchQueryDTO'], + [Config.Server.apiPath + '/gallery/random/:searchQueryDTO'], // common part AuthenticationMWs.authenticate, AuthenticationMWs.authorise(UserRoles.Guest), @@ -199,7 +199,7 @@ export class GalleryRouter { protected static addGetPhotoThumbnail(app: Express): void { app.get( - Config.Client.apiPath + '/gallery/content/:mediaPath(*.(' + + Config.Server.apiPath + '/gallery/content/:mediaPath(*.(' + SupportedFormats.Photos.join('|') + '))/thumbnail/:size?', // common part @@ -217,7 +217,7 @@ export class GalleryRouter { protected static addGetVideoThumbnail(app: Express): void { app.get( - Config.Client.apiPath + '/gallery/content/:mediaPath(*.(' + + Config.Server.apiPath + '/gallery/content/:mediaPath(*.(' + SupportedFormats.Videos.join('|') + '))/thumbnail/:size?', // common part @@ -235,7 +235,7 @@ export class GalleryRouter { protected static addGetVideoIcon(app: Express): void { app.get( - Config.Client.apiPath + '/gallery/content/:mediaPath(*.(' + + Config.Server.apiPath + '/gallery/content/:mediaPath(*.(' + SupportedFormats.Videos.join('|') + '))/icon', // common part @@ -253,7 +253,7 @@ export class GalleryRouter { protected static addGetImageIcon(app: Express): void { app.get( - Config.Client.apiPath + '/gallery/content/:mediaPath(*.(' + + Config.Server.apiPath + '/gallery/content/:mediaPath(*.(' + SupportedFormats.Photos.join('|') + '))/icon', // common part @@ -271,7 +271,7 @@ export class GalleryRouter { protected static addSearch(app: Express): void { app.get( - Config.Client.apiPath + '/search/:searchQueryDTO(*)', + Config.Server.apiPath + '/search/:searchQueryDTO(*)', // common part AuthenticationMWs.authenticate, AuthenticationMWs.authorise(UserRoles.Guest), @@ -288,7 +288,7 @@ export class GalleryRouter { protected static addAutoComplete(app: Express): void { app.get( - Config.Client.apiPath + '/autocomplete/:text(*)', + Config.Server.apiPath + '/autocomplete/:text(*)', // common part AuthenticationMWs.authenticate, AuthenticationMWs.authorise(UserRoles.Guest), diff --git a/src/backend/routes/LoggerRouter.ts b/src/backend/routes/LoggerRouter.ts index a2e0945a..730bf653 100644 --- a/src/backend/routes/LoggerRouter.ts +++ b/src/backend/routes/LoggerRouter.ts @@ -42,7 +42,7 @@ export class LoggerRouter { return next(); }); - app.get(Config.Client.apiPath + '*', (req: Request, res: Response, next: NextFunction): any => { + app.get(Config.Server.apiPath + '*', (req: Request, res: Response, next: NextFunction): any => { LoggerRouter.log(Logger.verbose, req, res); return next(); }); diff --git a/src/backend/routes/NotificationRouter.ts b/src/backend/routes/NotificationRouter.ts index 5700dae1..9708c506 100644 --- a/src/backend/routes/NotificationRouter.ts +++ b/src/backend/routes/NotificationRouter.ts @@ -14,7 +14,7 @@ export class NotificationRouter { private static addGetNotifications(app: Express): void { app.get( - Config.Client.apiPath + '/notifications', + Config.Server.apiPath + '/notifications', AuthenticationMWs.authenticate, AuthenticationMWs.authorise(UserRoles.Guest), VersionMWs.injectGalleryVersion, diff --git a/src/backend/routes/PersonRouter.ts b/src/backend/routes/PersonRouter.ts index 44615ede..e7d17cce 100644 --- a/src/backend/routes/PersonRouter.ts +++ b/src/backend/routes/PersonRouter.ts @@ -17,10 +17,10 @@ export class PersonRouter { protected static updatePerson(app: Express): void { app.post( - [Config.Client.apiPath + '/person/:name'], + [Config.Server.apiPath + '/person/:name'], // common part AuthenticationMWs.authenticate, - AuthenticationMWs.authorise(Config.Client.Faces.writeAccessMinRole), + AuthenticationMWs.authorise(Config.Faces.writeAccessMinRole), VersionMWs.injectGalleryVersion, // specific part @@ -32,10 +32,10 @@ export class PersonRouter { protected static addGetPersons(app: Express): void { app.get( - [Config.Client.apiPath + '/person'], + [Config.Server.apiPath + '/person'], // common part AuthenticationMWs.authenticate, - AuthenticationMWs.authorise(Config.Client.Faces.readAccessMinRole), + AuthenticationMWs.authorise(Config.Faces.readAccessMinRole), VersionMWs.injectGalleryVersion, // specific part @@ -50,7 +50,7 @@ export class PersonRouter { protected static getPersonThumbnail(app: Express): void { app.get( - [Config.Client.apiPath + '/person/:name/thumbnail'], + [Config.Server.apiPath + '/person/:name/thumbnail'], // common part AuthenticationMWs.authenticate, AuthenticationMWs.authorise(UserRoles.User), diff --git a/src/backend/routes/PublicRouter.ts b/src/backend/routes/PublicRouter.ts index a3123b76..eb0db89a 100644 --- a/src/backend/routes/PublicRouter.ts +++ b/src/backend/routes/PublicRouter.ts @@ -1,14 +1,15 @@ -import { Express, NextFunction, Request, Response } from 'express'; +import {Express, NextFunction, Request, Response} from 'express'; import * as path from 'path'; import * as fs from 'fs'; import * as ejs from 'ejs'; -import { Config } from '../../common/config/private/Config'; -import { ProjectPath } from '../ProjectPath'; -import { AuthenticationMWs } from '../middlewares/user/AuthenticationMWs'; -import { CookieNames } from '../../common/CookieNames'; -import { ErrorCodes, ErrorDTO } from '../../common/entities/Error'; -import { UserDTO } from '../../common/entities/UserDTO'; -import { ServerTimeEntry } from '../middlewares/ServerTimingMWs'; +import {Config} from '../../common/config/private/Config'; +import {ProjectPath} from '../ProjectPath'; +import {AuthenticationMWs} from '../middlewares/user/AuthenticationMWs'; +import {CookieNames} from '../../common/CookieNames'; +import {ErrorCodes, ErrorDTO} from '../../common/entities/Error'; +import {UserDTO} from '../../common/entities/UserDTO'; +import {ServerTimeEntry} from '../middlewares/ServerTimingMWs'; +import {ClientConfig} from '../../common/config/public/ClientConfig'; declare global { // eslint-disable-next-line @typescript-eslint/no-namespace @@ -32,7 +33,7 @@ export class PublicRouter { let selectedLocale = req.locale; if (req.cookies && req.cookies[CookieNames.lang]) { if ( - Config.Client.languages.indexOf(req.cookies[CookieNames.lang]) !== -1 + Config.Server.languages.indexOf(req.cookies[CookieNames.lang]) !== -1 ) { selectedLocale = req.cookies[CookieNames.lang]; } @@ -57,7 +58,7 @@ export class PublicRouter { const redirectToBase = (locale: string) => { return (req: Request, res: Response) => { - if (Config.Client.languages.indexOf(locale) !== -1) { + if (Config.Server.languages.indexOf(locale) !== -1) { res.cookie(CookieNames.lang, locale); } res.redirect('/?ln=' + locale); @@ -82,19 +83,17 @@ export class PublicRouter { res.tpl.user.csrfToken = req.csrfToken(); } } - const confCopy = { - Client: Config.Client.toJSON({ attachVolatile: true }), - }; + const confCopy = Config.toJSON({attachVolatile: true, keepTags: {client: true}}) as unknown as ClientConfig; // Escaping html tags, like - confCopy.Client.Other.customHTMLHead = - confCopy.Client.Other.customHTMLHead + confCopy.Server.customHTMLHead = + confCopy.Server.customHTMLHead .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, '''); res.tpl.Config = confCopy; - res.tpl.customHTMLHead = Config.Client.Other.customHTMLHead; + res.tpl.customHTMLHead = Config.Server.customHTMLHead; return next(); }); @@ -105,7 +104,7 @@ export class PublicRouter { app.get('/manifest.json', (req: Request, res: Response) => { res.send({ - name: Config.Client.applicationTitle, + name: Config.Server.applicationTitle, icons: [ { src: 'assets/icon_inv.png', @@ -115,7 +114,7 @@ export class PublicRouter { display: 'standalone', orientation: 'any', start_url: - Config.Client.publicUrl === '' ? '.' : Config.Client.publicUrl, + Config.Server.publicUrl === '' ? '.' : Config.Server.publicUrl, background_color: '#000000', theme_color: '#000000', }); @@ -137,7 +136,7 @@ export class PublicRouter { setLocale, renderIndex ); - Config.Client.languages.forEach((l) => { + Config.Server.languages.forEach((l) => { app.get( [ '/' + l + '/', diff --git a/src/backend/routes/SharingRouter.ts b/src/backend/routes/SharingRouter.ts index d0fef68b..60223790 100644 --- a/src/backend/routes/SharingRouter.ts +++ b/src/backend/routes/SharingRouter.ts @@ -19,7 +19,7 @@ export class SharingRouter { private static addShareLogin(app: express.Express): void { app.post( - Config.Client.apiPath + '/share/login', + Config.Server.apiPath + '/share/login', AuthenticationMWs.inverseAuthenticate, AuthenticationMWs.shareLogin, ServerTimingMWs.addServerTiming, @@ -29,7 +29,7 @@ export class SharingRouter { private static addGetSharing(app: express.Express): void { app.get( - Config.Client.apiPath + '/share/:' + QueryParams.gallery.sharingKey_params, + Config.Server.apiPath + '/share/:' + QueryParams.gallery.sharingKey_params, AuthenticationMWs.authenticate, AuthenticationMWs.authorise(UserRoles.LimitedGuest), SharingMWs.getSharing, @@ -40,7 +40,7 @@ export class SharingRouter { private static addCreateSharing(app: express.Express): void { app.post( - [Config.Client.apiPath + '/share/:directory(*)', Config.Client.apiPath + '/share/', Config.Client.apiPath + '/share//'], + [Config.Server.apiPath + '/share/:directory(*)', Config.Server.apiPath + '/share/', Config.Server.apiPath + '/share//'], AuthenticationMWs.authenticate, AuthenticationMWs.authorise(UserRoles.User), SharingMWs.createSharing, @@ -51,7 +51,7 @@ export class SharingRouter { private static addUpdateSharing(app: express.Express): void { app.put( - [Config.Client.apiPath + '/share/:directory(*)', Config.Client.apiPath + '/share/', Config.Client.apiPath + '/share//'], + [Config.Server.apiPath + '/share/:directory(*)', Config.Server.apiPath + '/share/', Config.Server.apiPath + '/share//'], AuthenticationMWs.authenticate, AuthenticationMWs.authorise(UserRoles.User), SharingMWs.updateSharing, @@ -62,7 +62,7 @@ export class SharingRouter { private static addDeleteSharing(app: express.Express): void { app.delete( - [Config.Client.apiPath + '/share/:sharingKey'], + [Config.Server.apiPath + '/share/:sharingKey'], AuthenticationMWs.authenticate, AuthenticationMWs.authorise(UserRoles.Admin), SharingMWs.deleteSharing, @@ -73,7 +73,7 @@ export class SharingRouter { private static addListSharing(app: express.Express): void { app.get( - [Config.Client.apiPath + '/share/list'], + [Config.Server.apiPath + '/share/list'], AuthenticationMWs.authenticate, AuthenticationMWs.authorise(UserRoles.User), SharingMWs.listSharing, diff --git a/src/backend/routes/UserRouter.ts b/src/backend/routes/UserRouter.ts index 5c3ce9a4..8d06ed1b 100644 --- a/src/backend/routes/UserRouter.ts +++ b/src/backend/routes/UserRouter.ts @@ -21,7 +21,7 @@ export class UserRouter { private static addLogin(app: Express): void { app.post( - Config.Client.apiPath + '/user/login', + Config.Server.apiPath + '/user/login', AuthenticationMWs.inverseAuthenticate, AuthenticationMWs.login, ServerTimingMWs.addServerTiming, @@ -31,7 +31,7 @@ export class UserRouter { private static addLogout(app: Express): void { app.post( - Config.Client.apiPath + '/user/logout', + Config.Server.apiPath + '/user/logout', AuthenticationMWs.logout, ServerTimingMWs.addServerTiming, RenderingMWs.renderOK @@ -40,7 +40,7 @@ export class UserRouter { private static addGetSessionUser(app: Express): void { app.get( - Config.Client.apiPath + '/user/me', + Config.Server.apiPath + '/user/me', AuthenticationMWs.authenticate, ServerTimingMWs.addServerTiming, RenderingMWs.renderSessionUser @@ -49,7 +49,7 @@ export class UserRouter { private static addCreateUser(app: Express): void { app.put( - Config.Client.apiPath + '/user', + Config.Server.apiPath + '/user', AuthenticationMWs.authenticate, AuthenticationMWs.authorise(UserRoles.Admin), UserMWs.createUser, @@ -60,7 +60,7 @@ export class UserRouter { private static addDeleteUser(app: Express): void { app.delete( - Config.Client.apiPath + '/user/:id', + Config.Server.apiPath + '/user/:id', AuthenticationMWs.authenticate, AuthenticationMWs.authorise(UserRoles.Admin), UserRequestConstrainsMWs.notSelfRequest, @@ -72,7 +72,7 @@ export class UserRouter { private static addListUsers(app: Express): void { app.get( - Config.Client.apiPath + '/user/list', + Config.Server.apiPath + '/user/list', AuthenticationMWs.authenticate, AuthenticationMWs.authorise(UserRoles.Admin), UserMWs.listUsers, @@ -83,7 +83,7 @@ export class UserRouter { private static addChangeRole(app: Express): void { app.post( - Config.Client.apiPath + '/user/:id/role', + Config.Server.apiPath + '/user/:id/role', AuthenticationMWs.authenticate, AuthenticationMWs.authorise(UserRoles.Admin), UserRequestConstrainsMWs.notSelfRequestOr2Admins, diff --git a/src/backend/routes/admin/AdminRouter.ts b/src/backend/routes/admin/AdminRouter.ts index cbfac8c5..f8d768d1 100644 --- a/src/backend/routes/admin/AdminRouter.ts +++ b/src/backend/routes/admin/AdminRouter.ts @@ -14,7 +14,7 @@ export class AdminRouter { private static addGetStatistic(app: Express): void { app.get( - Config.Client.apiPath + '/admin/statistic', + Config.Server.apiPath + '/admin/statistic', AuthenticationMWs.authenticate, AuthenticationMWs.authorise(UserRoles.Admin), AdminMWs.loadStatistic, @@ -24,7 +24,7 @@ export class AdminRouter { private static addGetDuplicates(app: Express): void { app.get( - Config.Client.apiPath + '/admin/duplicates', + Config.Server.apiPath + '/admin/duplicates', AuthenticationMWs.authenticate, AuthenticationMWs.authorise(UserRoles.Admin), AdminMWs.getDuplicates, @@ -34,28 +34,28 @@ export class AdminRouter { private static addJobs(app: Express): void { app.get( - Config.Client.apiPath + '/admin/jobs/available', + Config.Server.apiPath + '/admin/jobs/available', AuthenticationMWs.authenticate, AuthenticationMWs.authorise(UserRoles.Admin), AdminMWs.getAvailableJobs, RenderingMWs.renderResult ); app.get( - Config.Client.apiPath + '/admin/jobs/scheduled/progress', + Config.Server.apiPath + '/admin/jobs/scheduled/progress', AuthenticationMWs.authenticate, AuthenticationMWs.authorise(UserRoles.Admin), AdminMWs.getJobProgresses, RenderingMWs.renderResult ); app.post( - Config.Client.apiPath + '/admin/jobs/scheduled/:id/start', + Config.Server.apiPath + '/admin/jobs/scheduled/:id/start', AuthenticationMWs.authenticate, AuthenticationMWs.authorise(UserRoles.Admin), AdminMWs.startJob, RenderingMWs.renderResult ); app.post( - Config.Client.apiPath + '/admin/jobs/scheduled/:id/stop', + Config.Server.apiPath + '/admin/jobs/scheduled/:id/stop', AuthenticationMWs.authenticate, AuthenticationMWs.authorise(UserRoles.Admin), AdminMWs.stopJob, diff --git a/src/backend/routes/admin/SettingsRouter.ts b/src/backend/routes/admin/SettingsRouter.ts index 4add6499..a540531d 100644 --- a/src/backend/routes/admin/SettingsRouter.ts +++ b/src/backend/routes/admin/SettingsRouter.ts @@ -12,132 +12,19 @@ export class SettingsRouter { private static addSettings(app: Express): void { app.get( - Config.Client.apiPath + '/settings', + Config.Server.apiPath + '/settings', AuthenticationMWs.authenticate, AuthenticationMWs.authorise(UserRoles.Admin), RenderingMWs.renderConfig ); app.put( - Config.Client.apiPath + '/settings/database', + Config.Server.apiPath + '/settings', AuthenticationMWs.authenticate, AuthenticationMWs.authorise(UserRoles.Admin), - SettingsMWs.updateDatabaseSettings, + SettingsMWs.updateSettings, RenderingMWs.renderOK ); - app.put( - Config.Client.apiPath + '/settings/map', - AuthenticationMWs.authenticate, - AuthenticationMWs.authorise(UserRoles.Admin), - SettingsMWs.updateMapSettings, - RenderingMWs.renderOK - ); - app.put( - Config.Client.apiPath + '/settings/video', - AuthenticationMWs.authenticate, - AuthenticationMWs.authorise(UserRoles.Admin), - SettingsMWs.updateVideoSettings, - RenderingMWs.renderOK - ); - app.put( - Config.Client.apiPath + '/settings/photo', - AuthenticationMWs.authenticate, - AuthenticationMWs.authorise(UserRoles.Admin), - SettingsMWs.updatePhotoSettings, - RenderingMWs.renderOK - ); - app.put( - Config.Client.apiPath + '/settings/metafile', - AuthenticationMWs.authenticate, - AuthenticationMWs.authorise(UserRoles.Admin), - SettingsMWs.updateMetaFileSettings, - RenderingMWs.renderOK - ); - - app.put( - Config.Client.apiPath + '/settings/authentication', - AuthenticationMWs.authenticate, - AuthenticationMWs.authorise(UserRoles.Admin), - SettingsMWs.updateAuthenticationSettings, - RenderingMWs.renderOK - ); - app.put( - Config.Client.apiPath + '/settings/thumbnail', - AuthenticationMWs.authenticate, - AuthenticationMWs.authorise(UserRoles.Admin), - SettingsMWs.updateThumbnailSettings, - RenderingMWs.renderOK - ); - app.put( - Config.Client.apiPath + '/settings/search', - AuthenticationMWs.authenticate, - AuthenticationMWs.authorise(UserRoles.Admin), - SettingsMWs.updateSearchSettings, - RenderingMWs.renderOK - ); - app.put( - Config.Client.apiPath + '/settings/preview', - AuthenticationMWs.authenticate, - AuthenticationMWs.authorise(UserRoles.Admin), - SettingsMWs.updatePreviewSettings, - RenderingMWs.renderOK - ); - app.put( - Config.Client.apiPath + '/settings/faces', - AuthenticationMWs.authenticate, - AuthenticationMWs.authorise(UserRoles.Admin), - SettingsMWs.updateFacesSettings, - RenderingMWs.renderOK - ); - app.put( - Config.Client.apiPath + '/settings/albums', - AuthenticationMWs.authenticate, - AuthenticationMWs.authorise(UserRoles.Admin), - SettingsMWs.updateAlbumsSettings, - RenderingMWs.renderOK - ); - app.put( - Config.Client.apiPath + '/settings/share', - AuthenticationMWs.authenticate, - AuthenticationMWs.authorise(UserRoles.Admin), - SettingsMWs.updateShareSettings, - RenderingMWs.renderOK - ); - app.put( - Config.Client.apiPath + '/settings/randomPhoto', - AuthenticationMWs.authenticate, - AuthenticationMWs.authorise(UserRoles.Admin), - SettingsMWs.updateRandomPhotoSettings, - RenderingMWs.renderOK - ); - app.put( - Config.Client.apiPath + '/settings/basic', - AuthenticationMWs.authenticate, - AuthenticationMWs.authorise(UserRoles.Admin), - SettingsMWs.updateBasicSettings, - RenderingMWs.renderOK - ); - app.put( - Config.Client.apiPath + '/settings/other', - AuthenticationMWs.authenticate, - AuthenticationMWs.authorise(UserRoles.Admin), - SettingsMWs.updateOtherSettings, - RenderingMWs.renderOK - ); - app.put( - Config.Client.apiPath + '/settings/indexing', - AuthenticationMWs.authenticate, - AuthenticationMWs.authorise(UserRoles.Admin), - SettingsMWs.updateIndexingSettings, - RenderingMWs.renderOK - ); - app.put( - Config.Client.apiPath + '/settings/jobs', - AuthenticationMWs.authenticate, - AuthenticationMWs.authorise(UserRoles.Admin), - SettingsMWs.updateJobSettings, - RenderingMWs.renderOK - ); } } diff --git a/src/backend/server.ts b/src/backend/server.ts index e2e5815d..11cdcfac 100644 --- a/src/backend/server.ts +++ b/src/backend/server.ts @@ -92,11 +92,11 @@ export class Server { this.app.use( csuf.unless((req: Request) => { return ( - Config.Client.authenticationRequired === false || - [Config.Client.apiPath + '/user/login', Config.Client.apiPath + '/user/logout', Config.Client.apiPath + '/share/login'].indexOf( + Config.Users.authenticationRequired === false || + [Config.Server.apiPath + '/user/login', Config.Server.apiPath + '/user/logout', Config.Server.apiPath + '/share/login'].indexOf( req.originalUrl ) !== -1 || - (Config.Client.Sharing.enabled === true && + (Config.Sharing.enabled === true && !!req.query[QueryParams.gallery.sharingKey_query]) ); }) @@ -104,11 +104,11 @@ export class Server { // enable token generation but do not check it this.app.post( - [Config.Client.apiPath + '/user/login', Config.Client.apiPath + '/share/login'], + [Config.Server.apiPath + '/user/login', Config.Server.apiPath + '/share/login'], _csrf({ignoreMethods: ['POST']}) ); this.app.get( - [Config.Client.apiPath + '/user/me', Config.Client.apiPath + '/share/:' + QueryParams.gallery.sharingKey_params], + [Config.Server.apiPath + '/user/me', Config.Server.apiPath + '/share/:' + QueryParams.gallery.sharingKey_params], _csrf({ignoreMethods: ['GET']}) ); @@ -116,8 +116,8 @@ export class Server { PhotoProcessing.init(); Localizations.init(); - this.app.use(locale(Config.Client.languages, 'en')); - if (Config.Server.Database.type !== DatabaseType.memory) { + this.app.use(locale(Config.Server.languages, 'en')); + if (Config.Database.type !== DatabaseType.memory) { await ObjectManagers.InitSQLManagers(); } else { await ObjectManagers.InitMemoryManagers(); diff --git a/src/common/CookieNames.ts b/src/common/CookieNames.ts index fcb6d60c..0098d49e 100644 --- a/src/common/CookieNames.ts +++ b/src/common/CookieNames.ts @@ -1,5 +1,5 @@ export class CookieNames { public static lang = 'pigallery2-lang'; public static session = 'pigallery2-session'; - public static advancedSettings = 'advanced-settings'; + public static configPriority = 'config-priority'; } diff --git a/src/common/SupportedFormats.ts b/src/common/SupportedFormats.ts index fd0b9438..0716ff73 100644 --- a/src/common/SupportedFormats.ts +++ b/src/common/SupportedFormats.ts @@ -11,16 +11,16 @@ if (typeof window !== 'undefined') { } export const SupportedFormats = { - Photos: Config.Client.Media.Photo.supportedFormats, + Photos: Config.Media.Photo.supportedFormats, // Browser supported video formats // Read more: https://www.w3schools.com/html/html5_video.asp - Videos: Config.Client.Media.Video.supportedFormats, - MetaFiles: Config.Client.MetaFile.supportedFormats, + Videos: Config.Media.Video.supportedFormats, + MetaFiles: Config.MetaFile.supportedFormats, // These formats need to be transcoded (with the build-in ffmpeg support) TranscodeNeed: { // based on libvips, all supported formats for sharp: https://github.com/libvips/libvips Photos: [] as string[], - Videos: Config.Client.Media.Video.supportedFormatsWithTranscoding, + Videos: Config.Media.Video.supportedFormatsWithTranscoding, }, // -------------------------------------------- // Below this, it is autogenerated, DO NOT EDIT diff --git a/src/common/Utils.ts b/src/common/Utils.ts index 26876faa..2d80c2c1 100644 --- a/src/common/Utils.ts +++ b/src/common/Utils.ts @@ -62,7 +62,7 @@ export class Utils { /** * Checks if the two input (let them be objects or arrays or just primitives) are equal */ - static equalsFilter(object: any, filter: any): boolean { + static equalsFilter(object: any, filter: any, skipProp: string[] = []): boolean { if (typeof filter !== 'object' || filter == null) { return object === filter; } @@ -75,8 +75,11 @@ export class Utils { } const keys = Object.keys(filter); for (const key of keys) { + if (skipProp.includes(key)) { + continue; + } if (typeof filter[key] === 'object') { - if (Utils.equalsFilter(object[key], filter[key]) === false) { + if (Utils.equalsFilter(object[key], filter[key], skipProp) === false) { return false; } } else if (object[key] !== filter[key]) { @@ -180,7 +183,7 @@ export class Utils { } const key = parseInt(enumMember, 10); if (key >= 0) { - arr.push({ key, value: EnumType[enumMember] }); + arr.push({key, value: EnumType[enumMember]}); } } return arr; @@ -261,10 +264,11 @@ export class Utils { export class LRU { data: { [key: string]: { value: V; usage: number } } = {}; - constructor(public readonly size: number) {} + constructor(public readonly size: number) { + } set(key: string, value: V): void { - this.data[key] = { usage: Date.now(), value }; + this.data[key] = {usage: Date.now(), value}; if (Object.keys(this.data).length > this.size) { let oldestK = key; let oldest = this.data[oldestK].usage; diff --git a/src/common/config/private/Config.ts b/src/common/config/private/Config.ts index 771cf9cd..21ab3c96 100644 --- a/src/common/config/private/Config.ts +++ b/src/common/config/private/Config.ts @@ -1,10 +1,9 @@ /* eslint-disable @typescript-eslint/no-var-requires */ -import { IPrivateConfig, ServerConfig } from './PrivateConfig'; -import { ClientConfig } from '../public/ClientConfig'; +import {ServerConfig} from './PrivateConfig'; import * as crypto from 'crypto'; import * as path from 'path'; -import { ConfigClass, ConfigClassBuilder } from 'typeconfig/node'; -import { ConfigProperty, IConfigClass } from 'typeconfig/common'; +import {ConfigClass, ConfigClassBuilder} from 'typeconfig/node'; +import {IConfigClass} from 'typeconfig/common'; declare const process: any; @@ -32,14 +31,10 @@ const upTime = new Date().toISOString(); }, }, }) -export class PrivateConfigClass implements IPrivateConfig { - @ConfigProperty({ type: ServerConfig }) - Server: ServerConfig = new ServerConfig(); - @ConfigProperty({ type: ClientConfig }) - Client: IConfigClass & ClientConfig = new ClientConfig() as IConfigClass & - ClientConfig; +export class PrivateConfigClass extends ServerConfig { constructor() { + super(); if (!this.Server.sessionSecret || this.Server.sessionSecret.length === 0) { this.Server.sessionSecret = [ crypto.randomBytes(256).toString('hex'), @@ -48,14 +43,14 @@ export class PrivateConfigClass implements IPrivateConfig { ]; } - this.Server.Environment.appVersion = + this.Environment.appVersion = require('../../../../package.json').version; - this.Server.Environment.buildTime = + this.Environment.buildTime = require('../../../../package.json').buildTime; - this.Server.Environment.buildCommitHash = + this.Environment.buildCommitHash = require('../../../../package.json').buildCommitHash; - this.Server.Environment.upTime = upTime; - this.Server.Environment.isDocker = !!process.env.PI_DOCKER; + this.Environment.upTime = upTime; + this.Environment.isDocker = !!process.env.PI_DOCKER; } async original(): Promise { @@ -69,3 +64,4 @@ export const Config = ConfigClassBuilder.attachInterface( new PrivateConfigClass() ); Config.loadSync(); + diff --git a/src/common/config/private/PrivateConfig.ts b/src/common/config/private/PrivateConfig.ts index 9d40a717..560ba78e 100644 --- a/src/common/config/private/PrivateConfig.ts +++ b/src/common/config/private/PrivateConfig.ts @@ -5,7 +5,14 @@ import { JobTrigger, JobTriggerType, } from '../../entities/job/JobScheduleDTO'; -import {ClientConfig, ClientMetaFileConfig} from '../public/ClientConfig'; +import { + ClientConfig, + ClientGPXCompressingConfig, ClientMediaConfig, + ClientMetaFileConfig, ClientPhotoConfig, ClientPhotoConvertingConfig, + ClientServiceConfig, + ClientSharingConfig, ClientThumbnailConfig, + ClientUserConfig, ClientVideoConfig, ConfigPriority, MapProviders, TAGS +} from '../public/ClientConfig'; import {SubConfigClass} from 'typeconfig/src/decorators/class/SubConfigClass'; import {ConfigProperty} from 'typeconfig/src/decorators/property/ConfigPropoerty'; import {DefaultsJobs} from '../../entities/job/JobDTO'; @@ -17,6 +24,12 @@ import { import {SortingMethods} from '../../entities/SortingMethods'; import {UserRoles} from '../../entities/UserDTO'; +if (typeof $localize === 'undefined') { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + global.$localize = (s) => s; +} + export enum DatabaseType { memory = 1, mysql = 2, @@ -71,36 +84,104 @@ export type videoFormatType = 'mp4' | 'webm'; @SubConfigClass() export class MySQLConfig { - @ConfigProperty({envAlias: 'MYSQL_HOST'}) + @ConfigProperty({ + envAlias: 'MYSQL_HOST', + tags: + { + name: $localize`Host`, + priority: ConfigPriority.advanced + }, + }) host: string = 'localhost'; - @ConfigProperty({envAlias: 'MYSQL_PORT', min: 0, max: 65535}) + @ConfigProperty({ + envAlias: 'MYSQL_PORT', min: 0, max: 65535, + tags: + { + name: $localize`Port`, + priority: ConfigPriority.advanced + }, + }) port: number = 3306; - @ConfigProperty({envAlias: 'MYSQL_DATABASE'}) + @ConfigProperty({ + envAlias: 'MYSQL_DATABASE', + tags: + { + name: $localize`Database`, + priority: ConfigPriority.advanced + }, + }) database: string = 'pigallery2'; - @ConfigProperty({envAlias: 'MYSQL_USERNAME'}) + @ConfigProperty({ + envAlias: 'MYSQL_USERNAME', + tags: + { + name: $localize`Username`, + priority: ConfigPriority.advanced + }, + }) username: string = ''; - @ConfigProperty({envAlias: 'MYSQL_PASSWORD', type: 'password'}) + @ConfigProperty({ + envAlias: 'MYSQL_PASSWORD', type: 'password', + tags: + { + name: $localize`Password`, + priority: ConfigPriority.advanced + } + }) password: string = ''; } @SubConfigClass() export class SQLiteConfig { - @ConfigProperty() + @ConfigProperty({ + tags: + { + name: $localize`Sqlite db filename`, + priority: ConfigPriority.underTheHood + }, + description: $localize`Sqlite will save the db with this filename.`, + }) DBFileName: string = 'sqlite.db'; } @SubConfigClass() export class UserConfig { - @ConfigProperty() + @ConfigProperty({ + tags: + { + name: $localize`Name`, + priority: ConfigPriority.underTheHood + } + }) name: string; - @ConfigProperty({type: UserRoles}) + @ConfigProperty({ + type: UserRoles, + tags: + { + name: $localize`Role`, + priority: ConfigPriority.underTheHood + }, + }) role: UserRoles; - @ConfigProperty({description: 'Unencrypted, temporary password'}) - password: string; - - @ConfigProperty({description: 'Encrypted password'}) + @ConfigProperty({ + tags: + { + name: $localize`Password`, + priority: ConfigPriority.underTheHood + }, + description: $localize`Unencrypted, temporary password. App will encrypt it and delete this.` + }) + password: string = ''; + @ConfigProperty({ + tags: + { + name: $localize`Encrypted password`, + priority: ConfigPriority.underTheHood, + secret: true + }, + }) encryptedPassword: string | undefined; constructor(name: string, password: string, role: UserRoles) { @@ -112,115 +193,278 @@ export class UserConfig { @SubConfigClass() export class ServerDataBaseConfig { - @ConfigProperty({ + @ConfigProperty({ type: DatabaseType, - onNewValue: (value, config) => { + onNewValue: (value: DatabaseType, config: ServerConfig) => { if (config && value === DatabaseType.memory) { - config.Client.Search.enabled = false; - config.Client.Sharing.enabled = false; + config.Search.enabled = false; + config.Sharing.enabled = false; } }, + tags: + { + name: $localize`Type`, + priority: ConfigPriority.advanced + }, }) type: DatabaseType = DatabaseType.sqlite; - @ConfigProperty() + @ConfigProperty({ + tags: + { + name: $localize`Database folder`, + priority: ConfigPriority.advanced + }, + description: $localize`All file-based data will be stored here (sqlite database, user database in case of memory db, job history data).`, + }) dbFolder: string = 'db'; - @ConfigProperty() + @ConfigProperty({ + tags: + { + name: $localize`SQLite`, + relevant: (c: any) => c.type === DatabaseType.sqlite, + } + }) sqlite?: SQLiteConfig = new SQLiteConfig(); - @ConfigProperty() + @ConfigProperty({ + tags: + { + name: $localize`MySQL`, + relevant: (c: any) => c.type === DatabaseType.mysql, + } + }) mysql?: MySQLConfig = new MySQLConfig(); + +} + + +@SubConfigClass() +export class ServerUserConfig extends ClientUserConfig { @ConfigProperty({ arrayType: UserConfig, - description: - 'Creates these users in the DB if they do not exist. If a user with this name exist, it wont be overwritten, even if the role is different.', + tags: + { + name: $localize`Enforced users`, + priority: ConfigPriority.underTheHood + }, + description: $localize`Creates these users in the DB if they do not exist. If a user with this name exist, it won't be overwritten, even if the role is different.`, }) enforcedUsers: UserConfig[] = []; } + @SubConfigClass() -export class ServerThumbnailConfig { - @ConfigProperty({description: 'if true, \'lanczos3\' will used to scale photos, otherwise faster but lowe quality \'nearest\'.'}) +export class ServerThumbnailConfig extends ClientThumbnailConfig { + @ConfigProperty({ + tags: + { + name: $localize`Enforced users`, + priority: ConfigPriority.underTheHood + }, + description: $localize`if true, 'lanczos3' will used to scale photos, otherwise faster but lowe quality 'nearest'.` + }) useLanczos3: boolean = true; - @ConfigProperty({description: 'Thumbnail image quality', max: 100, min: 1, type: 'unsignedInt'}) + @ConfigProperty({ + max: 100, min: 1, type: 'unsignedInt', + tags: + { + name: $localize`Converted photo and thumbnail quality`, + priority: ConfigPriority.underTheHood + }, + description: $localize`Between 0-100.` + }) quality = 80; - @ConfigProperty({type: 'ratio'}) + @ConfigProperty({ + type: 'ratio', + tags: + { + name: $localize`Person face margin`, + priority: ConfigPriority.underTheHood + }, + description: $localize`Person face size ratio on the face thumbnail.` + }) personFaceMargin: number = 0.6; // in ration [0-1] } @SubConfigClass() -export class ServerGPXCompressingConfig { +export class ServerGPXCompressingConfig extends ClientGPXCompressingConfig { @ConfigProperty({ - description: 'Compresses gpx files on-the-fly, when they are requested.', + tags: + { + name: $localize`OnTheFly *.gpx compression`, + priority: ConfigPriority.advanced, + uiDisabled: (sc: ServerGPXCompressingConfig, c: ServerConfig) => !c.Map.enabled || !sc.enabled || !c.MetaFile.gpx + }, + description: $localize`Enables on the fly *.gpx compression.`, }) onTheFly: boolean = true; - @ConfigProperty({type: 'unsignedInt', description: 'Filters out entry that are closer than this in meters.'}) + @ConfigProperty({ + type: 'unsignedInt', + tags: + { + name: $localize`Min distance`, + priority: ConfigPriority.underTheHood, + uiDisabled: (sc: ServerGPXCompressingConfig, c: ServerConfig) => !c.Map.enabled || !sc.enabled || !c.MetaFile.gpx + }, + description: $localize`Filters out entry that are closer than this in meters.` + }) minDistance: number = 5; - @ConfigProperty({type: 'unsignedInt', description: 'Filters out entry that are closer than this in time in milliseconds.'}) + @ConfigProperty({ + type: 'unsignedInt', + tags: + { + name: $localize`Min time delta`, + priority: ConfigPriority.underTheHood, + uiDisabled: (sc: ServerGPXCompressingConfig, c: ServerConfig) => !c.Map.enabled || !sc.enabled || !c.MetaFile.gpx + }, + description: $localize`Filters out entry that are closer than this in time in milliseconds.` + }) minTimeDistance: number = 5000; } @SubConfigClass() -export class ServerMetaFileConfig { - @ConfigProperty() +export class ServerMetaFileConfig extends ClientMetaFileConfig { + @ConfigProperty({ + tags: + { + name: $localize`GPX compression`, + priority: ConfigPriority.advanced + } + }) GPXCompressing: ServerGPXCompressingConfig = new ServerGPXCompressingConfig(); } @SubConfigClass() -export class ServerSharingConfig { - @ConfigProperty() +export class ServerSharingConfig extends ClientSharingConfig { + @ConfigProperty({ + type: 'unsignedInt', + tags: + { + name: $localize`Update timeout`, + priority: ConfigPriority.underTheHood, + unit: 'ms' + } as TAGS, + description: $localize`After creating a sharing link, it can be updated for this long.` + }) updateTimeout: number = 1000 * 60 * 5; } @SubConfigClass() export class ServerIndexingConfig { - @ConfigProperty() + @ConfigProperty({ + type: 'unsignedInt', + tags: + { + name: $localize`Index cache timeout`, + priority: ConfigPriority.underTheHood, + unit: 'ms' + } as TAGS, + description: $localize`If there was no indexing in this time, it reindexes. (skipped if indexes are in DB and sensitivity is low).` + }) cachedFolderTimeout: number = 1000 * 60 * 60; // Do not rescans the folder if seems ok - @ConfigProperty({type: ReIndexingSensitivity}) + @ConfigProperty({ + type: ReIndexingSensitivity, + tags: + { + name: $localize`Folder reindexing sensitivity`, + priority: ConfigPriority.advanced + }, + description: $localize`Set the reindexing sensitivity. High value check the folders for change more often.` + }) 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.', + tags: + { + name: $localize`Exclude Folder List`, + priority: ConfigPriority.advanced, + uiOptional: true, + uiAllowSpaces: true + } as TAGS, + description: $localize`Folders to exclude from indexing. 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[] = ['.Trash-1000', '.dtrash', '$RECYCLE.BIN']; @ConfigProperty({ arrayType: 'string', - description: - 'Any folder that contains a file with this name will be excluded from indexing.', + tags: + { + name: $localize`Exclude File List`, + priority: ConfigPriority.advanced, + uiOptional: true + + } as TAGS, + description: $localize`Files that mark a folder to be excluded from indexing. Any folder that contains a file with this name will be excluded from indexing.`, }) excludeFileList: string[] = []; } @SubConfigClass() export class ServerThreadingConfig { - @ConfigProperty({description: 'App can run on multiple thread'}) + @ConfigProperty({ + tags: + { + name: $localize`Threading`, + priority: ConfigPriority.underTheHood + }, + description: $localize`Runs directory scanning and thumbnail generation in a different 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.', + tags: + { + name: $localize`Thumbnail threads`, + priority: ConfigPriority.underTheHood + }, + description: $localize`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 ServerDuplicatesConfig { - @ConfigProperty() - listingLimit: number = 1000; // maximum number of duplicates to list + @ConfigProperty({ + type: 'unsignedInt', + tags: + { + name: $localize`Max duplicates`, + priority: ConfigPriority.underTheHood + }, + description: $localize`Maximum number of duplicates to list.` + }) + listingLimit: number = 1000; } @SubConfigClass() export class ServerLogConfig { - @ConfigProperty({type: LogLevel}) + @ConfigProperty({ + type: LogLevel, + tags: { + name: $localize`Level`, + priority: ConfigPriority.advanced, + }, + description: $localize`Logging level.` + }) level: LogLevel = LogLevel.info; - @ConfigProperty({type: SQLLogLevel}) + @ConfigProperty({ + type: SQLLogLevel, + tags: { + name: $localize`Sql Level`, + priority: ConfigPriority.underTheHood, + }, + description: $localize`Logging level for SQL queries.` + }) sqlLevel: SQLLogLevel = SQLLogLevel.error; - @ConfigProperty() + @ConfigProperty({ + tags: { + name: $localize`Server timing`, + priority: ConfigPriority.underTheHood, + }, + description: $localize`If enabled. The app ads "Server-Timing" http header to the response.` + }) logServerTiming: boolean = false; } @@ -315,9 +559,25 @@ export class JobScheduleConfig implements JobScheduleDTO { @SubConfigClass() export class ServerJobConfig { - @ConfigProperty({type: 'integer', description: 'Job history size'}) + @ConfigProperty({ + type: 'unsignedInt', + tags: + { + name: $localize`Max saved progress`, + priority: ConfigPriority.underTheHood + }, + description: $localize`Job history size.` + }) maxSavedProgress: number = 20; - @ConfigProperty({type: 'integer', description: 'Job loads this many photos or videos form the DB for processing'}) + @ConfigProperty({ + type: 'unsignedInt', + tags: + { + name: $localize`Processing batch size`, + priority: ConfigPriority.underTheHood + }, + description: $localize`Jobs load this many photos or videos form the DB for processing at once.` + }) mediaProcessingBatchSize: number = 1000; @ConfigProperty({arrayType: JobScheduleConfig}) scheduled: JobScheduleConfig[] = [ @@ -368,67 +628,177 @@ export class ServerJobConfig { @SubConfigClass() export class VideoTranscodingConfig { - @ConfigProperty({type: 'unsignedInt'}) - bitRate: number = 5 * 1024 * 1024; - @ConfigProperty({type: 'unsignedInt'}) - resolution: videoResolutionType = 720; - @ConfigProperty({type: 'positiveFloat'}) - fps: number = 25; - @ConfigProperty() - codec: videoCodecType = 'libx264'; - @ConfigProperty() - format: videoFormatType = 'mp4'; @ConfigProperty({ type: 'unsignedInt', - description: - 'Constant Rate Factor. The range of the CRF scale is 0–51, where 0 is lossless, 23 is the default, and 51 is worst quality possible.', - max: 51, + tags: + { + name: $localize`Bit rate`, + priority: ConfigPriority.advanced, + unit: 'bps' + }, + description: $localize`Target bit rate of the output video will be scaled down this this. This should be less than the upload rate of your home server.` + }) + bitRate: number = 5 * 1024 * 1024; + @ConfigProperty({ + type: 'unsignedInt', + tags: + { + name: $localize`Resolution`, + priority: ConfigPriority.advanced, + uiOptions: [720, 1080, 1440, 2160, 4320], + unit: 'px' + }, + description: $localize`The height of the output video will be scaled down to this, while keeping the aspect ratio.` + }) + resolution: videoResolutionType = 720; + @ConfigProperty({ + type: 'positiveFloat', + tags: + { + name: $localize`FPS`, + priority: ConfigPriority.advanced, + uiOptions: [24, 25, 30, 48, 50, 60] + }, + description: $localize`Target frame per second (fps) of the output video will be scaled down this this.` + }) + fps: number = 25; + @ConfigProperty({ + tags: + { + name: $localize`Format`, + priority: ConfigPriority.advanced, + uiOptions: ['mp4', 'webm'] + } + }) + format: videoFormatType = 'mp4'; + @ConfigProperty({ + tags: + { + name: $localize`MP4 codec`, + priority: ConfigPriority.advanced, + uiOptions: ['libx264', 'libx265'], + relevant: (c: any) => c.format === 'mp4' + } + }) + mp4Codec: videoCodecType = 'libx264'; + @ConfigProperty({ + tags: + { + name: $localize`Webm Codec`, + priority: ConfigPriority.advanced, + uiOptions: ['libvpx', 'libvpx-vp9'], + relevant: (c: any) => c.format === 'webm' + } + }) + webmCodec: videoCodecType = 'libvpx'; + @ConfigProperty({ + type: 'unsignedInt', max: 51, + tags: + { + name: $localize`CRF`, + priority: ConfigPriority.underTheHood, + }, + description: $localize`The range of the Constant Rate Factor (CRF) scale is 0–51, where 0 is lossless, 23 is the default, and 51 is worst quality possible.`, + }) crf: number = 23; @ConfigProperty({ type: FFmpegPresets, - description: - 'A preset is a collection of options that will provide a certain encoding speed to compression ratio', + tags: + { + name: $localize`Preset`, + priority: ConfigPriority.advanced, + }, + description: $localize`A preset is a collection of options that will provide a certain encoding speed to compression ratio. A slower preset will provide better compression (compression is quality per filesize).`, }) preset: FFmpegPresets = FFmpegPresets.medium; @ConfigProperty({ arrayType: 'string', - description: 'It will be sent to ffmpeg as it is, as custom options.', + tags: { + name: $localize`Custom Options`, + priority: ConfigPriority.underTheHood, + hint: '-pass 2; -minrate 1M; -maxrate 1M; -bufsize 2M', + uiAllowSpaces: true + }, + description: $localize`It will be sent to ffmpeg as it is, as custom options.`, }) customOptions: string[] = []; } @SubConfigClass() -export class ServerVideoConfig { - @ConfigProperty() +export class ServerVideoConfig extends ClientVideoConfig { + @ConfigProperty({ + tags: { + name: $localize`Video transcoding`, + priority: ConfigPriority.advanced, + uiDisabled: (sb: ClientVideoConfig) => !sb.enabled + }, + description: $localize`To ensure smooth video playback, video transcoding is recommended to a lower bit rate than the server's upload rate. The transcoded videos will be save to the thumbnail folder. You can trigger the transcoding manually, but you can also create an automatic encoding job in advanced settings mode.` + }) transcoding: VideoTranscodingConfig = new VideoTranscodingConfig(); } @SubConfigClass() -export class PhotoConvertingConfig { +export class PhotoConvertingConfig extends ClientPhotoConvertingConfig { @ConfigProperty({ - description: 'Converts photos on the fly, when they are requested.', + tags: { + name: $localize`On the fly converting`, + priority: ConfigPriority.underTheHood, + uiDisabled: (sc: PhotoConvertingConfig) => + !sc.enabled + + }, + description: $localize`Converts photos on the fly, when they are requested.`, }) onTheFly: boolean = true; - @ConfigProperty({type: 'unsignedInt'}) + @ConfigProperty({ + type: 'unsignedInt', + tags: { + name: $localize`Resolution`, + priority: ConfigPriority.advanced, + uiDisabled: (sc: PhotoConvertingConfig) => + !sc.enabled + }, + description: $localize`The shorter edge of the converted photo will be scaled down to this, while keeping the aspect ratio.`, + }) resolution: videoResolutionType = 1080; } @SubConfigClass() -export class ServerPhotoConfig { - @ConfigProperty() +export class ServerPhotoConfig extends ClientPhotoConfig { + @ConfigProperty({ + tags: { + name: $localize`Photo resizing`, + priority: ConfigPriority.advanced, + } + }) Converting: PhotoConvertingConfig = new PhotoConvertingConfig(); } @SubConfigClass() export class ServerPreviewConfig { - @ConfigProperty({type: 'object'}) + @ConfigProperty({ + type: 'object', + tags: { + name: $localize`Preview Filter query`, + priority: ConfigPriority.advanced, + uiType: 'SearchQuery' + }, + description: $localize`Filters the sub-folders with this search query. If filter results no photo, the app will search again without the filter.`, + }) SearchQuery: SearchQueryDTO = { type: SearchQueryTypes.any_text, text: '', } as TextSearch; - @ConfigProperty({arrayType: SortingMethods}) + @ConfigProperty({ + arrayType: SortingMethods, + tags: { + name: $localize`Preview Sorting`, + priority: ConfigPriority.advanced + }, + description: $localize`If multiple preview is available sorts them by these methods and selects the first one.`, + }) Sorting: SortingMethods[] = [ SortingMethods.descRating, SortingMethods.descDate, @@ -436,25 +806,120 @@ export class ServerPreviewConfig { } @SubConfigClass() -export class ServerMediaConfig { +export class ServerMediaConfig extends ClientMediaConfig { @ConfigProperty({ - description: - 'Images are loaded from this folder (read permission required)', + tags: { + name: $localize`Images folder`, + priority: ConfigPriority.basic, + dockerSensitive: true + }, + description: $localize`Images are loaded from this folder (read permission required)`, + }) folder: string = 'demo/images'; @ConfigProperty({ - description: - 'Thumbnails, converted photos, videos will be stored here (write permission required)', + tags: { + name: $localize`Temp folder`, + priority: ConfigPriority.basic, + dockerSensitive: true + }, + description: $localize`Thumbnails, converted photos, videos will be stored here (write permission required)`, }) tempFolder: string = 'demo/tmp'; - @ConfigProperty() + + @ConfigProperty({ + type: 'unsignedInt', + tags: { + name: $localize`Metadata read buffer`, + priority: ConfigPriority.underTheHood, + githubIssue: 398, + unit: 'bytes' + }, + description: $localize`Only this many bites will be loaded when scanning photo/video for metadata. Increase this number if your photos shows up as square.`, + }) + photoMetadataSize: number = 512 * 1024; // only this many bites will be loaded when scanning photo for metadata + + @ConfigProperty({ + tags: { + name: $localize`Video`, + priority: ConfigPriority.advanced + }, + description: $localize`Video support uses ffmpeg. ffmpeg and ffprobe binaries need to be available in the PATH or the @ffmpeg-installer/ffmpeg and @ffprobe-installer/ffprobe optional node packages need to be installed.` + }) Video: ServerVideoConfig = new ServerVideoConfig(); - @ConfigProperty() + @ConfigProperty({ + tags: { + name: $localize`Photo`, + priority: ConfigPriority.advanced + } + }) Photo: ServerPhotoConfig = new ServerPhotoConfig(); - @ConfigProperty() + @ConfigProperty({ + tags: { + name: $localize`Thumbnail`, + priority: ConfigPriority.advanced + } + }) Thumbnail: ServerThumbnailConfig = new ServerThumbnailConfig(); } +@SubConfigClass() +export class ServerServiceConfig extends ClientServiceConfig { + @ConfigProperty({ + arrayType: 'string', + tags: {secret: true} + }) + sessionSecret: string[] = []; + + @ConfigProperty({ + type: 'unsignedInt', + tags: { + name: $localize`Session Timeout`, + priority: ConfigPriority.underTheHood, + unit: 'ms' + }, + description: $localize`Users kept logged in for this long time.`, + }) + sessionTimeout: number = 1000 * 60 * 60 * 24 * 7; // in ms + + @ConfigProperty({ + tags: { + name: $localize`Port`, + priority: ConfigPriority.advanced, + dockerSensitive: true + }, + description: $localize`Port number. Port 80 is usually what you need.`, + type: 'unsignedInt', envAlias: 'PORT', min: 0, max: 65535 + }) + port: number = 80; + + @ConfigProperty({ + tags: { + name: $localize`Host`, + priority: ConfigPriority.advanced, + dockerSensitive: true + }, + description: $localize`Server will accept connections from this IPv6 or IPv4 address.`, + }) + host: string = '0.0.0.0'; + + @ConfigProperty({ + tags: { + name: $localize`Threading`, + priority: ConfigPriority.underTheHood, + } + }) + Threading: ServerThreadingConfig = new ServerThreadingConfig(); + + @ConfigProperty({ + tags: { + name: $localize`Logs`, + priority: ConfigPriority.advanced, + } + }) + Log: ServerLogConfig = new ServerLogConfig(); +} + @SubConfigClass() export class ServerEnvironmentConfig { @ConfigProperty({volatile: true}) @@ -469,47 +934,40 @@ export class ServerEnvironmentConfig { isDocker: boolean | undefined; } -@SubConfigClass() -export class ServerConfig { +@SubConfigClass() +export class ServerConfig extends ClientConfig { + @ConfigProperty({volatile: true}) Environment: ServerEnvironmentConfig = new ServerEnvironmentConfig(); - @ConfigProperty({arrayType: 'string'}) - sessionSecret: string[] = []; - @ConfigProperty({type: 'unsignedInt', envAlias: 'PORT', min: 0, max: 65535}) - port: number = 80; + @ConfigProperty() - host: string = '0.0.0.0'; - @ConfigProperty() - Media: ServerMediaConfig = new ServerMediaConfig(); - @ConfigProperty() - Preview: ServerPreviewConfig = new ServerPreviewConfig(); - @ConfigProperty() - Threading: ServerThreadingConfig = new ServerThreadingConfig(); + Server: ServerServiceConfig = new ServerServiceConfig(); + @ConfigProperty() Database: ServerDataBaseConfig = new ServerDataBaseConfig(); + @ConfigProperty() - Sharing: ServerSharingConfig = new ServerSharingConfig(); - @ConfigProperty({type: 'unsignedInt', description: 'unit: ms'}) - sessionTimeout: number = 1000 * 60 * 60 * 24 * 7; // in ms + Users: ServerUserConfig = new ServerUserConfig(); + @ConfigProperty() Indexing: ServerIndexingConfig = new ServerIndexingConfig(); - @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: ServerDuplicatesConfig = new ServerDuplicatesConfig(); - @ConfigProperty() - Log: ServerLogConfig = new ServerLogConfig(); - @ConfigProperty() - Jobs: ServerJobConfig = new ServerJobConfig(); + Media: ServerMediaConfig = new ServerMediaConfig(); + @ConfigProperty() MetaFile: ServerMetaFileConfig = new ServerMetaFileConfig(); + + @ConfigProperty() + Preview: ServerPreviewConfig = new ServerPreviewConfig(); + + @ConfigProperty() + Sharing: ServerSharingConfig = new ServerSharingConfig(); + + @ConfigProperty() + Duplicates: ServerDuplicatesConfig = new ServerDuplicatesConfig(); + + @ConfigProperty() + Jobs: ServerJobConfig = new ServerJobConfig(); } -export interface IPrivateConfig { - Server: ServerConfig; - Client: ClientConfig; -} diff --git a/src/common/config/private/WebConfig.ts b/src/common/config/private/WebConfig.ts index fe8ff3ef..313c56a2 100644 --- a/src/common/config/private/WebConfig.ts +++ b/src/common/config/private/WebConfig.ts @@ -1,19 +1,21 @@ /* eslint-disable @typescript-eslint/no-inferrable-types */ import 'reflect-metadata'; -import {ClientConfig} from '../public/ClientConfig'; import {ServerConfig} from './PrivateConfig'; import {WebConfigClass} from 'typeconfig/web'; -import {ConfigProperty, ConfigState} from 'typeconfig/common'; +import {ConfigState} from 'typeconfig/common'; +import {WebConfigClassBuilder} from '../../../../node_modules/typeconfig/src/decorators/builders/WebConfigClassBuilder'; +import {IWebConfigClass} from '../../../../node_modules/typeconfig/src/decorators/class/IWebConfigClass'; +import { TAGS } from '../public/ClientConfig'; @WebConfigClass({softReadonly: true}) -export class WebConfig { +export class WebConfig extends ServerConfig { @ConfigState() State: any; - @ConfigProperty() - Server: ServerConfig = new ServerConfig(); - @ConfigProperty() - Client: ClientConfig = new ClientConfig(); - + clone(): IWebConfigClass & WebConfig { + const wcg = WebConfigClassBuilder.attachInterface(new WebConfig()); + wcg.load(WebConfigClassBuilder.attachInterface(this).toJSON()); + return wcg; + } } diff --git a/src/common/config/public/ClientConfig.ts b/src/common/config/public/ClientConfig.ts index a862a178..d98862ae 100644 --- a/src/common/config/public/ClientConfig.ts +++ b/src/common/config/public/ClientConfig.ts @@ -3,8 +3,14 @@ import 'reflect-metadata'; import {SortingMethods} from '../../entities/SortingMethods'; import {UserRoles} from '../../entities/UserDTO'; import {ConfigProperty, SubConfigClass} from 'typeconfig/common'; -import {IPrivateConfig} from '../private/PrivateConfig'; -import {FromDateSearch, SearchQueryDTO, SearchQueryTypes, TextSearch} from '../../entities/SearchQueryDTO'; +import {SearchQueryDTO} from '../../entities/SearchQueryDTO'; + +if (typeof $localize === 'undefined') { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + global.$localize = (s) => s; +} + export enum MapProviders { OpenStreetMap = 1, @@ -12,104 +18,303 @@ export enum MapProviders { Custom = 3, } -@SubConfigClass() +export enum ConfigPriority { + basic = 0, advanced, underTheHood +} + +export type TAGS = { + client?: true, + priority?: ConfigPriority, + name?: string, + relevant?: (c: ClientConfig) => boolean, + dockerSensitive?: boolean, + hint?: string,// UI hint + githubIssue?: number, + secret?: boolean, // these config properties should never travel out of the server + experimental?: boolean, //is it a beta feature + unit?: string, // Unit info to display on UI + uiType?: 'SearchQuery', // Hint for the UI about the type + uiOptions?: (string | number)[], //Hint for the UI about the recommended options + uiAllowSpaces?: boolean + uiOptional?: boolean; //makes the tag not "required" + uiDisabled?: (subConfig: any, config: ClientConfig) => boolean +}; + +@SubConfigClass({tags: {client: true}}) export class AutoCompleteConfig { - @ConfigProperty() - enabled: boolean = true; - @ConfigProperty({type: 'unsignedInt'}) - targetItemsPerCategory: number = 5; - @ConfigProperty({type: 'unsignedInt'}) - maxItems: number = 30; - @ConfigProperty({type: 'unsignedInt'}) - cacheTimeout: number = 1000 * 60 * 60; -} - -@SubConfigClass() -export class ClientSearchConfig { - @ConfigProperty() - enabled: boolean = true; - @ConfigProperty({type: 'unsignedInt'}) - searchCacheTimeout: number = 1000 * 60 * 60; - @ConfigProperty() - AutoComplete: AutoCompleteConfig = new AutoCompleteConfig(); - @ConfigProperty({type: 'unsignedInt'}) - maxMediaResult: number = 10000; @ConfigProperty({ - description: 'Search returns also with directories, not just media', - }) - listDirectories: boolean = false; - @ConfigProperty({ - description: - 'Search also returns with metafiles from directories that contain a media file of the matched search result', - }) - listMetafiles: boolean = true; - @ConfigProperty({type: 'unsignedInt'}) - maxDirectoryResult: number = 200; -} - -@SubConfigClass() -export class ClientAlbumConfig { - @ConfigProperty() - enabled: boolean = true; -} - -@SubConfigClass() -export class ClientSharingConfig { - @ConfigProperty() - enabled: boolean = true; - @ConfigProperty() - passwordProtected: boolean = true; -} - -@SubConfigClass() -export class ClientRandomPhotoConfig { - @ConfigProperty({description: 'Enables random link generation.'}) - enabled: boolean = true; -} - -@SubConfigClass() -export class MapLayers { - @ConfigProperty() - name: string = 'street'; - @ConfigProperty() - url: string = ''; -} - -@SubConfigClass() -export class ClientMapConfig { - @ConfigProperty({ - onNewValue: (value, config) => { - if (value === false) { - config.Client.MetaFile.gpx = false; - } - }, + tags: + { + name: $localize`Enable Autocomplete`, + priority: ConfigPriority.advanced + }, + description: $localize`Show hints while typing search query.` }) enabled: boolean = true; @ConfigProperty({ type: 'unsignedInt', - description: - 'Maximum number of markers to be shown on the map preview on the gallery page.', + tags: + { + name: $localize`Max items per category`, + priority: ConfigPriority.underTheHood + }, + description: $localize`Maximum number autocomplete items shown per category.` }) - maxPreviewMarkers: number = 50; - @ConfigProperty() - useImageMarkers: boolean = true; - @ConfigProperty({type: MapProviders}) - mapProvider: MapProviders = MapProviders.OpenStreetMap; - @ConfigProperty() - mapboxAccessToken: string = ''; - @ConfigProperty({arrayType: MapLayers}) - customLayers: MapLayers[] = [new MapLayers()]; + targetItemsPerCategory: number = 5; + @ConfigProperty({ + type: 'unsignedInt', + tags: + { + name: $localize`Maximum items`, + priority: ConfigPriority.underTheHood + }, + description: $localize`Maximum number autocomplete items shown at once.` + }) + maxItems: number = 30; + @ConfigProperty({ + type: 'unsignedInt', + tags: + { + name: $localize`Cache timeout`, + priority: ConfigPriority.underTheHood, + unit: 'ms' + }, + description: $localize`Autocomplete cache timeout. ` + }) + cacheTimeout: number = 1000 * 60 * 60; } -@SubConfigClass() +@SubConfigClass({tags: {client: true}}) +export class ClientSearchConfig { + @ConfigProperty({ + tags: + { + name: $localize`Enable`, + priority: ConfigPriority.advanced + }, + description: $localize`Enables searching.` + }) + enabled: boolean = true; + @ConfigProperty({ + type: 'unsignedInt', + tags: + { + name: $localize`Cache timeout`, + priority: ConfigPriority.underTheHood, + unit: 'ms' + }, + description: $localize`Search cache timeout.` + }) + searchCacheTimeout: number = 1000 * 60 * 60; + @ConfigProperty({ + tags: + { + name: $localize`Autocomplete`, + priority: ConfigPriority.advanced + }, + }) + AutoComplete: AutoCompleteConfig = new AutoCompleteConfig(); + @ConfigProperty({ + type: 'unsignedInt', + tags: + { + name: $localize`Maximum media result`, + priority: ConfigPriority.advanced + }, + description: $localize`Maximum number of photos and videos that are listed in one search result.` + }) + maxMediaResult: number = 10000; + @ConfigProperty({ + type: 'unsignedInt', tags: + { + name: $localize`Maximum directory result`, + priority: ConfigPriority.advanced + }, + description: $localize`Maximum number of directories that are listed in one search result.` + }) + maxDirectoryResult: number = 200; + @ConfigProperty({ + tags: + { + name: $localize`List directories`, + priority: ConfigPriority.advanced + }, + description: $localize`Search returns also with directories, not just media.` + }) + listDirectories: boolean = false; + @ConfigProperty({ + tags: + { + name: $localize`List metafiles`, + priority: ConfigPriority.advanced + }, + description: $localize`Search also returns with metafiles from directories that contain a media file of the matched search result.`, + }) + listMetafiles: boolean = true; +} + +@SubConfigClass({tags: {client: true}}) +export class ClientAlbumConfig { + @ConfigProperty({ + tags: + { + name: $localize`Enable`, + priority: ConfigPriority.advanced + } + }) + enabled: boolean = true; +} + +@SubConfigClass({tags: {client: true}}) +export class ClientSharingConfig { + @ConfigProperty({ + tags: + { + name: $localize`Enable`, + priority: ConfigPriority.advanced + }, + description: $localize`Enables sharing.`, + }) + enabled: boolean = true; + @ConfigProperty({ + tags: + { + name: $localize`Password protected`, + priority: ConfigPriority.advanced + }, + description: $localize`Enables password protected sharing links.`, + }) + passwordProtected: boolean = true; +} + +@SubConfigClass({tags: {client: true}}) +export class ClientRandomPhotoConfig { + @ConfigProperty({ + tags: + { + name: $localize`Enable`, + priority: ConfigPriority.advanced + }, + description: $localize`Enables random link generation.`, + }) + enabled: boolean = true; +} + +@SubConfigClass({tags: {client: true}}) +export class MapLayers { + @ConfigProperty({ + tags: + { + priority: ConfigPriority.advanced + }, + description: $localize`Name of a map layer.`, + }) + name: string = 'street'; + @ConfigProperty({ + tags: + { + priority: ConfigPriority.advanced, + hint: 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png' + }, + description: $localize`Url of a map layer.`, + }) + url: string = ''; +} + +@SubConfigClass({tags: {client: true}}) +export class ClientMapConfig { + @ConfigProperty({ + onNewValue: (value, config) => { + if (value === false) { + config.MetaFile.gpx = false; + } + }, + tags: { + priority: ConfigPriority.advanced, + name: $localize`Enable` + } + }) + enabled: boolean = true; + @ConfigProperty({ + tags: { + name: $localize`Image Markers`, + priority: ConfigPriority.underTheHood + }, + description: $localize`Map will use thumbnail images as markers instead of the default pin.`, + }) + useImageMarkers: boolean = true; + @ConfigProperty({ + type: MapProviders, + tags: { + name: $localize`Map Provider`, + priority: ConfigPriority.advanced + } + }) + mapProvider: MapProviders = MapProviders.OpenStreetMap; + @ConfigProperty({ + tags: + { + name: $localize`Mapbox access token`, + relevant: (c: any) => c.mapProvider === MapProviders.Mapbox, + priority: ConfigPriority.advanced + }, + description: $localize`MapBox needs an access token to work, create one at https://www.mapbox.com.`, + }) + mapboxAccessToken: string = ''; + @ConfigProperty({ + arrayType: MapLayers, + description: $localize`The map module will use these urls to fetch the map tiles.`, + tags: { + relevant: (c: any) => c.mapProvider === MapProviders.Custom, + name: $localize`Custom Layers`, + priority: ConfigPriority.advanced + } + + }) + customLayers: MapLayers[] = [new MapLayers()]; + + @ConfigProperty({ + type: 'unsignedInt', + tags: { + name: $localize`Max Preview Markers`, + priority: ConfigPriority.underTheHood + }, + description: $localize`Maximum number of markers to be shown on the map preview on the gallery page.`, + }) + maxPreviewMarkers: number = 50; +} + +@SubConfigClass({tags: {client: true}}) export class ClientThumbnailConfig { - @ConfigProperty({type: 'unsignedInt', max: 100}) + @ConfigProperty({ + type: 'unsignedInt', max: 100, + tags: { + name: $localize`Max Preview Markers`, + priority: ConfigPriority.underTheHood + }, + description: $localize`Icon size (used on maps).`, + }) iconSize: number = 45; - @ConfigProperty({type: 'unsignedInt'}) + @ConfigProperty({ + type: 'unsignedInt', tags: { + name: $localize`Person thumbnail size`, + priority: ConfigPriority.underTheHood + }, + description: $localize`Person (face) thumbnail size.`, + }) personThumbnailSize: number = 200; - @ConfigProperty({arrayType: 'unsignedInt'}) + @ConfigProperty({ + arrayType: 'unsignedInt', tags: { + name: $localize`Thumbnail sizes`, + priority: ConfigPriority.advanced + }, + description: $localize`Size of the thumbnails. The best matching size will be generated. More sizes give better quality, but use more storage and CPU to render. If size is 240, that shorter side of the thumbnail will have 160 pixels.`, + }) thumbnailSizes: number[] = [240, 480]; - @ConfigProperty({volatile: true}) + @ConfigProperty({ + volatile: true, + description: 'Updated to match he number of CPUs. This manny thumbnail will be concurrently generated.', + }) concurrentThumbnailGenerations: number = 1; /** @@ -135,15 +340,42 @@ export enum NavigationLinkTypes { gallery = 1, faces, albums, search, url } -@SubConfigClass() +@SubConfigClass({tags: {client: true}}) export class NavigationLinkConfig { - @ConfigProperty({type: NavigationLinkTypes}) + @ConfigProperty({ + type: NavigationLinkTypes, + tags: { + name: $localize`Type`, + priority: ConfigPriority.advanced + } as TAGS + }) type: NavigationLinkTypes = NavigationLinkTypes.gallery; - @ConfigProperty({type: 'string'}) + @ConfigProperty({ + type: 'string', + tags: { + name: $localize`Name`, + priority: ConfigPriority.advanced + } + }) name?: string; - @ConfigProperty({type: 'object'}) + @ConfigProperty({ + type: 'object', + tags: { + name: $localize`SearchQuery`, + priority: ConfigPriority.advanced, + uiType: 'SearchQuery', + relevant: (c: NavigationLinkConfig) => c.type === NavigationLinkTypes.search + } + }) SearchQuery?: SearchQueryDTO; - @ConfigProperty({type: 'string'}) + @ConfigProperty({ + type: 'string', + tags: { + name: $localize`Url`, + priority: ConfigPriority.advanced, + relevant: (c: NavigationLinkConfig) => c.type === NavigationLinkTypes.url + } + }) url?: string; @@ -157,11 +389,26 @@ export class NavigationLinkConfig { } } -@SubConfigClass() +@SubConfigClass({tags: {client: true}}) export class NavBarConfig { - @ConfigProperty() + @ConfigProperty({ + tags: { + name: $localize`Show item count`, + priority: ConfigPriority.underTheHood + }, + description: $localize`Shows the number photos and videos on the navigation bar.`, + }) showItemCount: boolean = true; - @ConfigProperty({arrayType: NavigationLinkConfig, description: 'List of the navigation bar links'}) + @ConfigProperty({ + arrayType: NavigationLinkConfig, + tags: { + name: $localize`Links`, + priority: ConfigPriority.advanced, + experimental: true, + githubIssue: 174 + }, + description: $localize`Visible links in the top menu.` + }) links: NavigationLinkConfig[] = [ new NavigationLinkConfig(NavigationLinkTypes.gallery), new NavigationLinkConfig(NavigationLinkTypes.albums), @@ -169,180 +416,462 @@ export class NavBarConfig { ]; } -@SubConfigClass() -export class ClientOtherConfig { - @ConfigProperty() - customHTMLHead: string = ''; - @ConfigProperty() +@SubConfigClass({tags: {client: true, priority: ConfigPriority.advanced}}) +export class ClientGalleryConfig { + @ConfigProperty({ + tags: { + name: $localize`Cache`, + priority: ConfigPriority.underTheHood, + }, + description: $localize`Caches directory contents and search results for better performance.` + }) enableCache: boolean = true; - @ConfigProperty() + @ConfigProperty({ + tags: { + name: $localize`Scroll based thumbnail generation`, + priority: ConfigPriority.advanced, + }, + description: $localize`Those thumbnails get higher priority that are visible on the screen.` + }) enableOnScrollRendering: boolean = true; - @ConfigProperty({type: SortingMethods, description: 'Default sorting method for directory results'}) + @ConfigProperty({ + type: SortingMethods, tags: { + name: $localize`Default sorting`, + priority: ConfigPriority.advanced, + }, + description: $localize`Default sorting method for photo and video in a directory results.` + }) defaultPhotoSortingMethod: SortingMethods = SortingMethods.ascDate; - @ConfigProperty({type: SortingMethods, description: 'Default sorting method for search results'}) + @ConfigProperty({ + type: SortingMethods, tags: { + name: $localize`Default search sorting`, + priority: ConfigPriority.advanced, + }, + description: $localize`Default sorting method for photo and video in a search results.` + }) defaultSearchSortingMethod: SortingMethods = SortingMethods.descDate; @ConfigProperty({ - description: - 'If enabled directories will be sorted by date, like photos, otherwise by name. Directory date is the last modification time of that directory not the creation date of the oldest photo', + tags: { + name: $localize`Sort directories by date`, + priority: ConfigPriority.advanced, + }, + description: $localize`If enabled, directories will be sorted by date, like photos, otherwise by name. Directory date is the last modification time of that directory not the creation date of the oldest photo.` }) enableDirectorySortingByDate: boolean = false; - @ConfigProperty() + @ConfigProperty({ + tags: { + name: $localize`On scroll thumbnail prioritising`, + priority: ConfigPriority.underTheHood, + }, + description: $localize`Those thumbnails will be rendered first that are in view.` + }) enableOnScrollThumbnailPrioritising: boolean = true; - @ConfigProperty() + @ConfigProperty({ + type: NavBarConfig, + tags: { + name: $localize`Navigation bar`, + priority: ConfigPriority.advanced, + } + }) NavBar: NavBarConfig = new NavBarConfig(); - @ConfigProperty() + @ConfigProperty({ + tags: { + name: $localize`Caption first naming`, + priority: ConfigPriority.advanced, + }, + description: $localize`Show the caption (IPTC 120) tags from the EXIF data instead of the filenames.` + }) captionFirstNaming: boolean = false; // shows the caption instead of the filename in the photo grid - @ConfigProperty() + @ConfigProperty({ + tags: { + name: $localize`Download Zip`, + priority: ConfigPriority.advanced, + experimental: true, + githubIssue: 52 + }, + description: $localize`Enable download zip of a directory contents Directory flattening. (Does not work for searches.)` + }) enableDownloadZip: boolean = false; @ConfigProperty({ - description: - 'Adds a button to flattens the file structure, by listing the content of all subdirectories.', + tags: { + name: $localize`Directory flattening`, + priority: ConfigPriority.advanced, + experimental: true, + githubIssue: 174 + }, + description: $localize`Adds a button to flattens the file structure, by listing the content of all subdirectories. (Won't work if the gallery has multiple folders with the same path.)` }) enableDirectoryFlattening: boolean = false; - @ConfigProperty({description:"Default time interval for displaying a photo in the slide show"}) + @ConfigProperty({ + tags: { + name: $localize`Default slideshow speed`, + priority: ConfigPriority.advanced, + githubIssue: 570, + unit: 's' + }, + description: $localize`Default time interval for displaying a photo in the slide show.` + }) defaultSlideshowSpeed: number = 5; } -@SubConfigClass() +@SubConfigClass({tags: {client: true}}) export class ClientVideoConfig { - @ConfigProperty() + @ConfigProperty({ + tags: { + name: $localize`Enable`, + priority: ConfigPriority.advanced, + } + }) enabled: boolean = true; @ConfigProperty({ arrayType: 'string', - description: 'Video formats that are supported after transcoding (with the build-in ffmpeg support)' + tags: { + name: $localize`Supported formats with transcoding`, + priority: ConfigPriority.underTheHood, + uiDisabled: (sb: ClientVideoConfig) => !sb.enabled + } as TAGS, + description: $localize`Video formats that are supported after transcoding (with the build-in ffmpeg support).` }) supportedFormatsWithTranscoding: string[] = ['avi', 'mkv', 'mov', 'wmv', 'flv', 'mts', 'm2ts', 'mpg', '3gp', 'm4v', 'mpeg', 'vob', 'divx', 'xvid', 'ts']; // Browser supported video formats // Read more: https://www.w3schools.com/html/html5_video.asp - @ConfigProperty({arrayType: 'string', description: 'Video formats that are supported also without transcoding'}) + @ConfigProperty({ + arrayType: 'string', + tags: { + name: $localize`Supported formats without transcoding`, + priority: ConfigPriority.underTheHood, + uiDisabled: (sb: ClientVideoConfig) => !sb.enabled + }, + description: $localize`Video formats that are supported also without transcoding. Browser supported formats: https://www.w3schools.com/html/html5_video.asp` + }) supportedFormats: string[] = ['mp4', 'webm', 'ogv', 'ogg']; } -@SubConfigClass() -export class PhotoConvertingConfig { - @ConfigProperty() - enabled: boolean = true; -} - -@SubConfigClass() -export class ClientPhotoConfig { - @ConfigProperty() - Converting: PhotoConvertingConfig = new PhotoConvertingConfig(); +@SubConfigClass({tags: {client: true}}) +export class ClientPhotoConvertingConfig { @ConfigProperty({ - description: - 'Enables loading the full resolution image on zoom in the ligthbox (preview).', + tags: { + name: $localize`Enable` + } as TAGS, + description: $localize`Enable photo converting.` + }) + enabled: boolean = true; + + @ConfigProperty({ + tags: { + name: $localize`Load full resolution image on zoom.`, + priority: ConfigPriority.advanced, + uiDisabled: (sc: ClientPhotoConvertingConfig) => + !sc.enabled + }, + description: $localize`Enables loading the full resolution image on zoom in the ligthbox (preview).`, }) loadFullImageOnZoom: boolean = true; - @ConfigProperty({arrayType: 'string'}) +} + +@SubConfigClass({tags: {client: true}}) +export class ClientPhotoConfig { + @ConfigProperty({ + tags: { + name: $localize`Photo converting`, + priority: ConfigPriority.advanced + } + }) + Converting: ClientPhotoConvertingConfig = new ClientPhotoConvertingConfig(); + + @ConfigProperty({ + arrayType: 'string', + tags: { + name: $localize`Supported photo formats`, + priority: ConfigPriority.underTheHood + }, + description: $localize`Photo formats that are supported.`, + }) supportedFormats: string[] = ['gif', 'jpeg', 'jpg', 'jpe', 'png', 'webp', 'svg']; } -@SubConfigClass() +@SubConfigClass({tags: {client: true}}) export class ClientGPXCompressingConfig { - @ConfigProperty() + @ConfigProperty({ + tags: { + name: $localize`Enable GPX compressing`, + priority: ConfigPriority.advanced, + githubIssue: 504, + uiDisabled: (sc: any, c: ClientConfig) => !c.Map.enabled + }, + description: $localize`Enables lossy (based on delta time and distance. Too frequent points are removed) GPX compression.` + }) enabled: boolean = true; } -@SubConfigClass() +@SubConfigClass({tags: {client: true}}) export class ClientMediaConfig { - @ConfigProperty() + @ConfigProperty({ + tags: { + name: $localize`Thumbnail`, + priority: ConfigPriority.advanced + } + }) Thumbnail: ClientThumbnailConfig = new ClientThumbnailConfig(); - @ConfigProperty() + @ConfigProperty({ + tags: { + name: $localize`Video`, + priority: ConfigPriority.advanced + } + }) Video: ClientVideoConfig = new ClientVideoConfig(); - @ConfigProperty() + @ConfigProperty({ + tags: { + name: $localize`Photo`, + priority: ConfigPriority.advanced + } + }) Photo: ClientPhotoConfig = new ClientPhotoConfig(); } -@SubConfigClass() +@SubConfigClass({tags: {client: true}}) export class ClientMetaFileConfig { @ConfigProperty({ - description: 'Reads *.gpx files and renders them on the map.', + tags: { + name: $localize`*.gpx files`, + priority: ConfigPriority.advanced, + uiDisabled: (sb, c) => !c.Map.enabled + } as TAGS, + description: $localize`Reads *.gpx files and renders them on the map.` }) gpx: boolean = true; - @ConfigProperty() + @ConfigProperty({ + tags: { + name: $localize`GPX compression`, + priority: ConfigPriority.advanced, + uiDisabled: (sb, c) => !c.Map.enabled || !sb.gpx + } as TAGS + }) GPXCompressing: ClientGPXCompressingConfig = new ClientGPXCompressingConfig(); @ConfigProperty({ - description: - 'Reads *.md files in a directory and shows the next to the map.', + tags: { + name: $localize`Markdown files`, + priority: ConfigPriority.advanced + }, + description: $localize`Reads *.md files in a directory and shows the next to the map.` }) markdown: boolean = true; @ConfigProperty({ - description: - 'Reads *.pg2conf files (You can use it for custom sorting and save search (albums)).', + tags: { + name: $localize`*.pg2conf files`, + priority: ConfigPriority.advanced + }, + description: $localize`Reads *.pg2conf files (You can use it for custom sorting and saved search (albums)).` }) pg2conf: boolean = true; - @ConfigProperty({arrayType: 'string'}) + @ConfigProperty({ + arrayType: 'string', + tags: { + name: $localize`Supported formats`, + priority: ConfigPriority.underTheHood + }, + description: $localize`The app will read and process these files.` + }) supportedFormats: string[] = ['gpx', 'pg2conf', 'md']; } -@SubConfigClass() +@SubConfigClass({tags: {client: true}}) export class ClientFacesConfig { - @ConfigProperty() + @ConfigProperty({ + tags: { + name: $localize`Enabled`, + priority: ConfigPriority.advanced + } + }) enabled: boolean = true; - @ConfigProperty() + @ConfigProperty({ + tags: { + name: $localize`Override keywords`, + priority: ConfigPriority.underTheHood + }, + description: $localize`If a photo has the same face (person) name and keyword, the app removes the duplicate, keeping the face only.` + }) keywordsToPersons: boolean = true; - @ConfigProperty({type: UserRoles}) + @ConfigProperty({ + type: UserRoles, tags: { + name: $localize`Face starring right`, + priority: ConfigPriority.underTheHood + }, + description: $localize`Required minimum right to star (favourite) a face.` + }) writeAccessMinRole: UserRoles = UserRoles.Admin; - @ConfigProperty({type: UserRoles}) + @ConfigProperty({ + type: UserRoles, tags: { + name: $localize`Face listing right`, + priority: ConfigPriority.underTheHood + }, + description: $localize`Required minimum right to show the faces tab.` + }) readAccessMinRole: UserRoles = UserRoles.User; } -@SubConfigClass() -export class ClientConfig { +@SubConfigClass({tags: {client: true}}) +export class ClientServiceConfig { - @ConfigProperty() + @ConfigProperty({ + tags: { + name: $localize`Page title` + } + }) applicationTitle: string = 'PiGallery 2'; - @ConfigProperty() + @ConfigProperty({ + description: $localize`If you access the page form local network its good to know the public url for creating sharing link.`, + tags: { + name: $localize`Page public url`, + } + }) publicUrl: string = ''; - @ConfigProperty() + @ConfigProperty({ + description: $localize`If you access the gallery under a sub url (like: http://mydomain.com/myGallery), set it here. If it is not working you might miss the '/' from the beginning of the url.`, + tags: { + name: $localize`Url Base`, + hint: '/myGallery', + priority: ConfigPriority.advanced + } + }) urlBase: string = ''; - @ConfigProperty({description: 'PiGallery api path.'}) + @ConfigProperty({ + description: 'PiGallery api path.', + tags: { + name: $localize`Api path`, + priority: ConfigPriority.underTheHood + } + }) apiPath: string = '/pgapi'; - @ConfigProperty() - Search: ClientSearchConfig = new ClientSearchConfig(); - - @ConfigProperty() - Sharing: ClientSharingConfig = new ClientSharingConfig(); - - @ConfigProperty() - Album: ClientAlbumConfig = new ClientAlbumConfig(); - - @ConfigProperty() - Map: ClientMapConfig = new ClientMapConfig(); - - @ConfigProperty() - RandomPhoto: ClientRandomPhotoConfig = new ClientRandomPhotoConfig(); - - @ConfigProperty() - Other: ClientOtherConfig = new ClientOtherConfig(); - - @ConfigProperty() - authenticationRequired: boolean = true; - - @ConfigProperty({type: UserRoles}) - unAuthenticatedUserRole: UserRoles = UserRoles.Admin; - @ConfigProperty({arrayType: 'string', volatile: true}) languages: string[] | undefined; - @ConfigProperty() + @ConfigProperty({ + description: $localize`Injects the content of this between the HTML tags of the app. (You can use it add analytics or custom code to the app).`, + tags: { + name: $localize`Custom HTML Head`, + priority: ConfigPriority.advanced, + githubIssue: 404 + } + }) + customHTMLHead: string = ''; +} + +@SubConfigClass({tags: {client: true}}) +export class ClientUserConfig { + + @ConfigProperty({ + tags: { + name: $localize`Password protection`, + priority: ConfigPriority.advanced, + }, + description: $localize`Enables user management with login to password protect the gallery.`, + }) + authenticationRequired: boolean = true; + + @ConfigProperty({ + type: UserRoles, tags: { + name: $localize`Default user right`, + priority: ConfigPriority.advanced, + relevant: (c: any) => c.authenticationRequired === false + }, + description: $localize`Default user right when password protection is disabled.`, + }) + unAuthenticatedUserRole: UserRoles = UserRoles.Admin; +} + + +@SubConfigClass({tags: {client: true}}) +export class ClientConfig { + + @ConfigProperty({ + tags: { + name: $localize`Server` + } as TAGS, + }) + Server: ClientServiceConfig = new ClientServiceConfig(); + + @ConfigProperty({ + tags: { + name: $localize`Users` + } as TAGS, + }) + Users: ClientUserConfig = new ClientUserConfig(); + + @ConfigProperty({ + tags: { + name: $localize`Gallery` + } as TAGS, + }) + Gallery: ClientGalleryConfig = new ClientGalleryConfig(); + + @ConfigProperty({ + tags: { + name: $localize`Media` + } as TAGS, + }) Media: ClientMediaConfig = new ClientMediaConfig(); - @ConfigProperty() + @ConfigProperty({ + tags: { + name: $localize`Meta file` + } as TAGS, + }) MetaFile: ClientMetaFileConfig = new ClientMetaFileConfig(); - @ConfigProperty() + @ConfigProperty({ + tags: { + name: $localize`Album` + } as TAGS, + }) + Album: ClientAlbumConfig = new ClientAlbumConfig(); + + @ConfigProperty({ + tags: { + name: $localize`Search` + } as TAGS, + }) + Search: ClientSearchConfig = new ClientSearchConfig(); + + @ConfigProperty({ + tags: { + name: $localize`Sharing` + } as TAGS, + }) + Sharing: ClientSharingConfig = new ClientSharingConfig(); + + @ConfigProperty({ + tags: { + name: $localize`Map` + } as TAGS, + }) + Map: ClientMapConfig = new ClientMapConfig(); + + @ConfigProperty({ + tags: { + name: $localize`Faces` + } as TAGS, + }) Faces: ClientFacesConfig = new ClientFacesConfig(); + + @ConfigProperty({ + tags: { + name: $localize`Random photo`, + githubIssue: 392 + } as TAGS, + description: $localize`This feature enables you to generate 'random photo' urls. That URL returns a photo random selected from your gallery. You can use the url with 3rd party application like random changing desktop background. Note: With the current implementation, random link also requires login.` + }) + RandomPhoto: ClientRandomPhotoConfig = new ClientRandomPhotoConfig(); } diff --git a/src/common/config/public/Config.ts b/src/common/config/public/Config.ts index 1a1cb635..15bb3421 100644 --- a/src/common/config/public/Config.ts +++ b/src/common/config/public/Config.ts @@ -8,9 +8,7 @@ import {IWebConfigClass} from 'typeconfig/common'; * These configuration will be available at frontend and backend too */ @WebConfigClass() -export class ClientClass { - @ConfigProperty() - public Client: ClientConfig = new ClientConfig(); +export class ClientClass extends ClientConfig{ } // ConfigInject is getting injected form the server side to the global scope @@ -29,7 +27,7 @@ if ( Config.load(ServerInject.ConfigInject); } -if (Config.Client.publicUrl === '') { - Config.Client.publicUrl = location.origin; +if (Config.Server.publicUrl === '') { + Config.Server.publicUrl = location.origin; } diff --git a/src/common/entities/settings/BasicConfigDTO.ts b/src/common/entities/settings/BasicConfigDTO.ts deleted file mode 100644 index 4e4b29d1..00000000 --- a/src/common/entities/settings/BasicConfigDTO.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { IPrivateConfig } from '../../config/private/PrivateConfig'; - -export interface BasicConfigDTO { - imagesFolder: string; - tempFolder: string; - publicUrl: string; - urlBase: string; - applicationTitle: string; - port: number; - host: string; -} - -export const BasicConfigDTOUtil = { - mapToDTO: (s: IPrivateConfig): BasicConfigDTO => ({ - port: s.Server.port, - host: s.Server.host, - imagesFolder: s.Server.Media.folder, - tempFolder: s.Server.Media.tempFolder, - applicationTitle: s.Client.applicationTitle, - publicUrl: s.Client.publicUrl, - urlBase: s.Client.urlBase, - }), - mapToConf: (config: IPrivateConfig, input: BasicConfigDTO) => { - config.Server.port = input.port; - config.Server.host = input.host; - config.Server.Media.folder = input.imagesFolder; - config.Server.Media.tempFolder = input.tempFolder; - config.Client.publicUrl = input.publicUrl; - config.Client.urlBase = input.urlBase; - config.Client.applicationTitle = input.applicationTitle; - }, -}; diff --git a/src/common/entities/settings/OtherConfigDTO.ts b/src/common/entities/settings/OtherConfigDTO.ts deleted file mode 100644 index 69deae21..00000000 --- a/src/common/entities/settings/OtherConfigDTO.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { ServerThreadingConfig } from '../../config/private/PrivateConfig'; -import { ClientOtherConfig } from '../../config/public/ClientConfig'; - -export interface OtherConfigDTO { - Server: ServerThreadingConfig; - Client: ClientOtherConfig; -} diff --git a/src/frontend/app/app.component.ts b/src/frontend/app/app.component.ts index 1af431f2..e68a5e00 100644 --- a/src/frontend/app/app.component.ts +++ b/src/frontend/app/app.component.ts @@ -1,17 +1,17 @@ -import { Component, OnDestroy, OnInit } from '@angular/core'; -import { AuthenticationService } from './model/network/authentication.service'; -import { Router } from '@angular/router'; -import { Config } from '../../common/config/public/Config'; -import { Title } from '@angular/platform-browser'; -import { ShareService } from './ui/gallery/share.service'; +import {Component, OnDestroy, OnInit} from '@angular/core'; +import {AuthenticationService} from './model/network/authentication.service'; +import {Router} from '@angular/router'; +import {Config} from '../../common/config/public/Config'; +import {Title} from '@angular/platform-browser'; +import {ShareService} from './ui/gallery/share.service'; import 'hammerjs'; -import { Subscription } from 'rxjs'; -import { QueryParams } from '../../common/QueryParams'; +import {Subscription} from 'rxjs'; import {NavigationService} from './model/navigation.service'; @Component({ selector: 'app-pi-gallery2', - template: ` `, + template: ` + `, }) export class AppComponent implements OnInit, OnDestroy { private subscription: Subscription = null; @@ -22,10 +22,11 @@ export class AppComponent implements OnInit, OnDestroy { private shareService: ShareService, private navigation: NavigationService, private title: Title - ) {} + ) { + } async ngOnInit(): Promise { - this.title.setTitle(Config.Client.applicationTitle); + this.title.setTitle(Config.Server.applicationTitle); await this.shareService.wait(); this.subscription = this.authenticationService.user.subscribe(() => { if (this.authenticationService.isAuthenticated()) { diff --git a/src/frontend/app/app.module.ts b/src/frontend/app/app.module.ts index 6bfb7c60..2369a1a1 100644 --- a/src/frontend/app/app.module.ts +++ b/src/frontend/app/app.module.ts @@ -1,139 +1,112 @@ -import { Injectable, NgModule } from '@angular/core'; -import { - BrowserModule, - HAMMER_GESTURE_CONFIG, - HammerGestureConfig, - HammerModule, -} from '@angular/platform-browser'; -import { FormsModule } from '@angular/forms'; -import { AppComponent } from './app.component'; -import { UserService } from './model/network/user.service'; -import { ContentService } from './ui/gallery/content.service'; -import { NetworkService } from './model/network/network.service'; -import { GalleryCacheService } from './ui/gallery/cache.gallery.service'; -import { FullScreenService } from './ui/gallery/fullscreen.service'; -import { AuthenticationService } from './model/network/authentication.service'; -import { UserMangerSettingsComponent } from './ui/settings/usermanager/usermanager.settings.component'; -import { FrameComponent } from './ui/frame/frame.component'; -import { LeafletModule } from '@asymmetrik/ngx-leaflet'; -import { LoadingBarModule } from '@ngx-loading-bar/core'; -import { GalleryLightboxMediaComponent } from './ui/gallery/lightbox/media/media.lightbox.gallery.component'; -import { GalleryPhotoLoadingComponent } from './ui/gallery/grid/photo/loading/loading.photo.grid.gallery.component'; -import { GalleryNavigatorComponent } from './ui/gallery/navigator/navigator.gallery.component'; -import { GallerySearchComponent } from './ui/gallery/search/search.gallery.component'; -import { GalleryLightboxComponent } from './ui/gallery/lightbox/lightbox.gallery.component'; -import { GalleryDirectoryComponent } from './ui/gallery/directories/directory/directory.gallery.component'; -import { GalleryGridComponent } from './ui/gallery/grid/grid.gallery.component'; -import { GalleryPhotoComponent } from './ui/gallery/grid/photo/photo.grid.gallery.component'; -import { LoginComponent } from './ui/login/login.component'; -import { AdminComponent } from './ui/admin/admin.component'; -import { GalleryComponent } from './ui/gallery/gallery.component'; -import { StringifyRole } from './pipes/StringifyRolePipe'; -import { GPXFilesFilterPipe } from './pipes/GPXFilesFilterPipe'; -import { GalleryMapComponent } from './ui/gallery/map/map.gallery.component'; -import { GalleryMapLightboxComponent } from './ui/gallery/map/lightbox/lightbox.map.gallery.component'; -import { ThumbnailManagerService } from './ui/gallery/thumbnailManager.service'; -import { OverlayService } from './ui/gallery/overlay.service'; -import { GalleryShareComponent } from './ui/gallery/share/share.gallery.component'; -import { ShareLoginComponent } from './ui/sharelogin/share-login.component'; -import { ShareService } from './ui/gallery/share.service'; -import { ModalModule } from 'ngx-bootstrap/modal'; -import { BsDatepickerModule } from 'ngx-bootstrap/datepicker'; -import { DatabaseSettingsComponent } from './ui/settings/database/database.settings.component'; -import { ToastrModule } from 'ngx-toastr'; -import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { NotificationService } from './model/notification.service'; -import { JwBootstrapSwitchNg2Module } from 'jw-bootstrap-switch-ng2'; -import { ClipboardModule } from 'ngx-clipboard'; -import { NavigationService } from './model/navigation.service'; -import { InfoPanelLightboxComponent } from './ui/gallery/lightbox/infopanel/info-panel.lightbox.gallery.component'; -import { MapSettingsComponent } from './ui/settings/map/map.settings.component'; -import { TooltipModule } from 'ngx-bootstrap/tooltip'; -import { BsDropdownModule } from 'ngx-bootstrap/dropdown'; -import { CollapseModule } from 'ngx-bootstrap/collapse'; -import { PopoverModule } from 'ngx-bootstrap/popover'; -import { ThumbnailSettingsComponent } from './ui/settings/thumbnail/thumbnail.settings.component'; -import { SearchSettingsComponent } from './ui/settings/search/search.settings.component'; -import { SettingsService } from './ui/settings/settings.service'; -import { ShareSettingsComponent } from './ui/settings/share/share.settings.component'; -import { BasicSettingsComponent } from './ui/settings/basic/basic.settings.component'; -import { OtherSettingsComponent } from './ui/settings/other/other.settings.component'; -import { - HTTP_INTERCEPTORS, - HttpClient, - HttpClientModule, -} from '@angular/common/http'; -import { DefaultUrlSerializer, UrlSerializer, UrlTree } from '@angular/router'; -import { IndexingSettingsComponent } from './ui/settings/indexing/indexing.settings.component'; -import { LanguageComponent } from './ui/language/language.component'; -import { QueryService } from './model/query.service'; -import { IconizeSortingMethod } from './pipes/IconizeSortingMethod'; -import { StringifySortingMethod } from './pipes/StringifySortingMethod'; -import { RandomQueryBuilderGalleryComponent } from './ui/gallery/random-query-builder/random-query-builder.gallery.component'; -import { RandomPhotoSettingsComponent } from './ui/settings/random-photo/random-photo.settings.component'; -import { VideoSettingsComponent } from './ui/settings/video/video.settings.component'; -import { DurationPipe } from './pipes/DurationPipe'; -import { MapService } from './ui/gallery/map/map.service'; -import { MetaFileSettingsComponent } from './ui/settings/metafiles/metafile.settings.component'; -import { ThumbnailLoaderService } from './ui/gallery/thumbnailLoader.service'; -import { FileSizePipe } from './pipes/FileSizePipe'; -import { DuplicateService } from './ui/duplicates/duplicates.service'; -import { DuplicateComponent } from './ui/duplicates/duplicates.component'; -import { DuplicatesPhotoComponent } from './ui/duplicates/photo/photo.duplicates.component'; -import { SeededRandomService } from './model/seededRandom.service'; -import { FacesComponent } from './ui/faces/faces.component'; -import { FacesService } from './ui/faces/faces.service'; -import { FaceComponent } from './ui/faces/face/face.component'; -import { VersionService } from './model/version.service'; -import { DirectoriesComponent } from './ui/gallery/directories/directories.component'; -import { ControlsLightboxComponent } from './ui/gallery/lightbox/controls/controls.lightbox.gallery.component'; -import { FacesSettingsComponent } from './ui/settings/faces/faces.settings.component'; -import { TimepickerModule } from 'ngx-bootstrap/timepicker'; -import { TimeStampDatePickerComponent } from './ui/utils/timestamp-datepicker/datepicker.component'; -import { TimeStampTimePickerComponent } from './ui/utils/timestamp-timepicker/timepicker.component'; -import { PhotoSettingsComponent } from './ui/settings/photo/photo.settings.component'; -import { JobProgressComponent } from './ui/settings/jobs/progress/job-progress.settings.component'; -import { JobsSettingsComponent } from './ui/settings/jobs/jobs.settings.component'; -import { ScheduledJobsService } from './ui/settings/scheduled-jobs.service'; -import { BackendtextService } from './model/backendtext.service'; -import { JobButtonComponent } from './ui/settings/jobs/button/job-button.settings.component'; -import { ErrorInterceptor } from './model/network/helper/error.interceptor'; -import { CSRFInterceptor } from './model/network/helper/csrf.interceptor'; -import { SettingsEntryComponent } from './ui/settings/_abstract/settings-entry/settings-entry.component'; -import { GallerySearchQueryEntryComponent } from './ui/gallery/search/query-enrty/query-entry.search.gallery.component'; -import { StringifySearchQuery } from './pipes/StringifySearchQuery'; -import { AutoCompleteService } from './ui/gallery/search/autocomplete.service'; -import { SearchQueryParserService } from './ui/gallery/search/search-query-parser.service'; -import { GallerySearchFieldBaseComponent } from './ui/gallery/search/search-field-base/search-field-base.gallery.component'; -import { AppRoutingModule } from './app.routing'; -import { CookieService } from 'ngx-cookie-service'; -import { LeafletMarkerClusterModule } from '@asymmetrik/ngx-leaflet-markercluster'; -import { icon, Marker } from 'leaflet'; -import { AlbumsComponent } from './ui/albums/albums.component'; -import { AlbumComponent } from './ui/albums/album/album.component'; -import { AlbumsService } from './ui/albums/albums.service'; -import { GallerySearchQueryBuilderComponent } from './ui/gallery/search/query-builder/query-bulder.gallery.component'; -import { SavedSearchPopupComponent } from './ui/albums/saved-search-popup/saved-search-popup.component'; -import { AlbumsSettingsComponent } from './ui/settings/albums/albums.settings.component'; -import { MarkdownModule } from 'ngx-markdown'; -import { GalleryBlogComponent } from './ui/gallery/blog/blog.gallery.component'; -import { MDFilesFilterPipe } from './pipes/MDFilesFilterPipe'; -import { FileDTOToPathPipe } from './pipes/FileDTOToPathPipe'; -import { BlogService } from './ui/gallery/blog/blog.service'; -import { PhotoFilterPipe } from './pipes/PhotoFilterPipe'; -import { PreviewSettingsComponent } from './ui/settings/preview/preview.settings.component'; -import { GallerySearchFieldComponent } from './ui/gallery/search/search-field/search-field.gallery.component'; -import { GalleryFilterComponent } from './ui/gallery/filter/filter.gallery.component'; -import { GallerySortingService } from './ui/gallery/navigator/sorting.service'; -import { FilterService } from './ui/gallery/filter/filter.service'; +import {Injectable, NgModule} from '@angular/core'; +import {BrowserModule, HAMMER_GESTURE_CONFIG, HammerGestureConfig, HammerModule,} from '@angular/platform-browser'; +import {FormsModule} from '@angular/forms'; +import {AppComponent} from './app.component'; +import {UserService} from './model/network/user.service'; +import {ContentService} from './ui/gallery/content.service'; +import {NetworkService} from './model/network/network.service'; +import {GalleryCacheService} from './ui/gallery/cache.gallery.service'; +import {FullScreenService} from './ui/gallery/fullscreen.service'; +import {AuthenticationService} from './model/network/authentication.service'; +import {FrameComponent} from './ui/frame/frame.component'; +import {LeafletModule} from '@asymmetrik/ngx-leaflet'; +import {LoadingBarModule} from '@ngx-loading-bar/core'; +import {GalleryLightboxMediaComponent} from './ui/gallery/lightbox/media/media.lightbox.gallery.component'; +import {GalleryPhotoLoadingComponent} from './ui/gallery/grid/photo/loading/loading.photo.grid.gallery.component'; +import {GalleryNavigatorComponent} from './ui/gallery/navigator/navigator.gallery.component'; +import {GallerySearchComponent} from './ui/gallery/search/search.gallery.component'; +import {GalleryLightboxComponent} from './ui/gallery/lightbox/lightbox.gallery.component'; +import {GalleryDirectoryComponent} from './ui/gallery/directories/directory/directory.gallery.component'; +import {GalleryGridComponent} from './ui/gallery/grid/grid.gallery.component'; +import {GalleryPhotoComponent} from './ui/gallery/grid/photo/photo.grid.gallery.component'; +import {LoginComponent} from './ui/login/login.component'; +import {AdminComponent} from './ui/admin/admin.component'; +import {GalleryComponent} from './ui/gallery/gallery.component'; +import {StringifyRole} from './pipes/StringifyRolePipe'; +import {GPXFilesFilterPipe} from './pipes/GPXFilesFilterPipe'; +import {GalleryMapComponent} from './ui/gallery/map/map.gallery.component'; +import {GalleryMapLightboxComponent} from './ui/gallery/map/lightbox/lightbox.map.gallery.component'; +import {ThumbnailManagerService} from './ui/gallery/thumbnailManager.service'; +import {OverlayService} from './ui/gallery/overlay.service'; +import {GalleryShareComponent} from './ui/gallery/share/share.gallery.component'; +import {ShareLoginComponent} from './ui/sharelogin/share-login.component'; +import {ShareService} from './ui/gallery/share.service'; +import {ModalModule} from 'ngx-bootstrap/modal'; +import {BsDatepickerModule} from 'ngx-bootstrap/datepicker'; +import {ToastrModule} from 'ngx-toastr'; +import {BrowserAnimationsModule} from '@angular/platform-browser/animations'; +import {NotificationService} from './model/notification.service'; +import {JwBootstrapSwitchNg2Module} from 'jw-bootstrap-switch-ng2'; +import {ClipboardModule} from 'ngx-clipboard'; +import {NavigationService} from './model/navigation.service'; +import {InfoPanelLightboxComponent} from './ui/gallery/lightbox/infopanel/info-panel.lightbox.gallery.component'; +import {TooltipModule} from 'ngx-bootstrap/tooltip'; +import {BsDropdownModule} from 'ngx-bootstrap/dropdown'; +import {CollapseModule} from 'ngx-bootstrap/collapse'; +import {PopoverModule} from 'ngx-bootstrap/popover'; +import {SettingsService} from './ui/settings/settings.service'; +import {HTTP_INTERCEPTORS, HttpClient, HttpClientModule,} from '@angular/common/http'; +import {DefaultUrlSerializer, UrlSerializer, UrlTree} from '@angular/router'; +import {LanguageComponent} from './ui/language/language.component'; +import {QueryService} from './model/query.service'; +import {IconizeSortingMethod} from './pipes/IconizeSortingMethod'; +import {StringifySortingMethod} from './pipes/StringifySortingMethod'; +import {RandomQueryBuilderGalleryComponent} from './ui/gallery/random-query-builder/random-query-builder.gallery.component'; +import {DurationPipe} from './pipes/DurationPipe'; +import {MapService} from './ui/gallery/map/map.service'; +import {ThumbnailLoaderService} from './ui/gallery/thumbnailLoader.service'; +import {FileSizePipe} from './pipes/FileSizePipe'; +import {DuplicateService} from './ui/duplicates/duplicates.service'; +import {DuplicateComponent} from './ui/duplicates/duplicates.component'; +import {DuplicatesPhotoComponent} from './ui/duplicates/photo/photo.duplicates.component'; +import {SeededRandomService} from './model/seededRandom.service'; +import {FacesComponent} from './ui/faces/faces.component'; +import {FacesService} from './ui/faces/faces.service'; +import {FaceComponent} from './ui/faces/face/face.component'; +import {VersionService} from './model/version.service'; +import {DirectoriesComponent} from './ui/gallery/directories/directories.component'; +import {ControlsLightboxComponent} from './ui/gallery/lightbox/controls/controls.lightbox.gallery.component'; +import {TimepickerModule} from 'ngx-bootstrap/timepicker'; +import {TimeStampDatePickerComponent} from './ui/utils/timestamp-datepicker/datepicker.component'; +import {TimeStampTimePickerComponent} from './ui/utils/timestamp-timepicker/timepicker.component'; +import {ScheduledJobsService} from './ui/settings/scheduled-jobs.service'; +import {BackendtextService} from './model/backendtext.service'; +import {ErrorInterceptor} from './model/network/helper/error.interceptor'; +import {CSRFInterceptor} from './model/network/helper/csrf.interceptor'; +import {SettingsEntryComponent} from './ui/settings/_abstract/settings-entry/settings-entry.component'; +import {GallerySearchQueryEntryComponent} from './ui/gallery/search/query-enrty/query-entry.search.gallery.component'; +import {StringifySearchQuery} from './pipes/StringifySearchQuery'; +import {AutoCompleteService} from './ui/gallery/search/autocomplete.service'; +import {SearchQueryParserService} from './ui/gallery/search/search-query-parser.service'; +import {GallerySearchFieldBaseComponent} from './ui/gallery/search/search-field-base/search-field-base.gallery.component'; +import {AppRoutingModule} from './app.routing'; +import {CookieService} from 'ngx-cookie-service'; +import {LeafletMarkerClusterModule} from '@asymmetrik/ngx-leaflet-markercluster'; +import {icon, Marker} from 'leaflet'; +import {AlbumsComponent} from './ui/albums/albums.component'; +import {AlbumComponent} from './ui/albums/album/album.component'; +import {AlbumsService} from './ui/albums/albums.service'; +import {GallerySearchQueryBuilderComponent} from './ui/gallery/search/query-builder/query-bulder.gallery.component'; +import {SavedSearchPopupComponent} from './ui/albums/saved-search-popup/saved-search-popup.component'; +import {MarkdownModule} from 'ngx-markdown'; +import {GalleryBlogComponent} from './ui/gallery/blog/blog.gallery.component'; +import {MDFilesFilterPipe} from './pipes/MDFilesFilterPipe'; +import {FileDTOToPathPipe} from './pipes/FileDTOToPathPipe'; +import {BlogService} from './ui/gallery/blog/blog.service'; +import {PhotoFilterPipe} from './pipes/PhotoFilterPipe'; +import {GallerySearchFieldComponent} from './ui/gallery/search/search-field/search-field.gallery.component'; +import {GalleryFilterComponent} from './ui/gallery/filter/filter.gallery.component'; +import {GallerySortingService} from './ui/gallery/navigator/sorting.service'; +import {FilterService} from './ui/gallery/filter/filter.service'; +import {TemplateComponent} from './ui/settings/template/template.component'; @Injectable() export class MyHammerConfig extends HammerGestureConfig { events: string[] = ['pinch']; overrides = { - pan: { threshold: 1 }, - swipe: { direction: 31 }, // enable swipe up - pinch: { enable: true }, + pan: {threshold: 1}, + swipe: {direction: 31}, // enable swipe up + pinch: {enable: true}, }; } @@ -194,7 +167,7 @@ Marker.prototype.options.icon = iconDefault; LoadingBarModule, LeafletModule, LeafletMarkerClusterModule, - MarkdownModule.forRoot({ loader: HttpClient }), + MarkdownModule.forRoot({loader: HttpClient}), ], declarations: [ AppComponent, @@ -242,7 +215,8 @@ Marker.prototype.options.icon = iconDefault; DuplicatesPhotoComponent, // Settings SettingsEntryComponent, - UserMangerSettingsComponent, + TemplateComponent, + /* UserMangerSettingsComponent, DatabaseSettingsComponent, MapSettingsComponent, ThumbnailSettingsComponent, @@ -252,15 +226,14 @@ Marker.prototype.options.icon = iconDefault; SearchSettingsComponent, ShareSettingsComponent, RandomPhotoSettingsComponent, - BasicSettingsComponent, FacesSettingsComponent, AlbumsSettingsComponent, OtherSettingsComponent, IndexingSettingsComponent, - JobProgressComponent, JobsSettingsComponent, + JobProgressComponent, JobButtonComponent, - PreviewSettingsComponent, + PreviewSettingsComponent,*/ // Pipes StringifyRole, @@ -275,10 +248,10 @@ Marker.prototype.options.icon = iconDefault; PhotoFilterPipe, ], providers: [ - { provide: HTTP_INTERCEPTORS, useClass: CSRFInterceptor, multi: true }, - { provide: HTTP_INTERCEPTORS, useClass: ErrorInterceptor, multi: true }, - { provide: UrlSerializer, useClass: CustomUrlSerializer }, - { provide: HAMMER_GESTURE_CONFIG, useClass: MyHammerConfig }, + {provide: HTTP_INTERCEPTORS, useClass: CSRFInterceptor, multi: true}, + {provide: HTTP_INTERCEPTORS, useClass: ErrorInterceptor, multi: true}, + {provide: UrlSerializer, useClass: CustomUrlSerializer}, + {provide: HAMMER_GESTURE_CONFIG, useClass: MyHammerConfig}, StringifySortingMethod, NetworkService, ShareService, @@ -311,4 +284,5 @@ Marker.prototype.options.icon = iconDefault; ], bootstrap: [AppComponent], }) -export class AppModule {} +export class AppModule { +} diff --git a/src/frontend/app/model/navigation.service.ts b/src/frontend/app/model/navigation.service.ts index 4ac2b469..19056eb4 100644 --- a/src/frontend/app/model/navigation.service.ts +++ b/src/frontend/app/model/navigation.service.ts @@ -33,8 +33,8 @@ export class NavigationService { if (this.shareService.isSharing()) { return this.router.navigate(['share', this.shareService.getSharingKey()]); } else { - if (Config.Client.Other.NavBar.links && Config.Client.Other.NavBar.links.length > 0) { - switch (Config.Client.Other.NavBar.links[0].type) { + if (Config.Gallery.NavBar.links && Config.Gallery.NavBar.links.length > 0) { + switch (Config.Gallery.NavBar.links[0].type) { case NavigationLinkTypes.gallery: return this.router.navigate(['gallery', '']); case NavigationLinkTypes.albums: @@ -42,7 +42,7 @@ export class NavigationService { case NavigationLinkTypes.faces: return this.router.navigate(['faces', '']); case NavigationLinkTypes.search: - return this.router.navigate(['search', JSON.stringify(Config.Client.Other.NavBar.links[0].SearchQuery)]); + return this.router.navigate(['search', JSON.stringify(Config.Gallery.NavBar.links[0].SearchQuery)]); } } diff --git a/src/frontend/app/model/network/authentication.service.ts b/src/frontend/app/model/network/authentication.service.ts index b1b8e6bb..1d4505b8 100644 --- a/src/frontend/app/model/network/authentication.service.ts +++ b/src/frontend/app/model/network/authentication.service.ts @@ -41,10 +41,10 @@ export class AuthenticationService { } this.getSessionUser().catch(console.error); } else { - if (Config.Client.authenticationRequired === false) { + if (Config.Users.authenticationRequired === false) { this.user.next({ - name: UserRoles[Config.Client.unAuthenticatedUserRole], - role: Config.Client.unAuthenticatedUserRole, + name: UserRoles[Config.Users.unAuthenticatedUserRole], + role: Config.Users.unAuthenticatedUserRole, } as UserDTO); } } @@ -82,7 +82,7 @@ export class AuthenticationService { } public isAuthenticated(): boolean { - if (Config.Client.authenticationRequired === false) { + if (Config.Users.authenticationRequired === false) { return true; } return !!this.user.value; diff --git a/src/frontend/app/model/network/network.service.spec.ts b/src/frontend/app/model/network/network.service.spec.ts index 11b35229..0986ccfb 100644 --- a/src/frontend/app/model/network/network.service.spec.ts +++ b/src/frontend/app/model/network/network.service.spec.ts @@ -63,7 +63,7 @@ describe('NetworkService Success tests', () => { expect(err).toBeUndefined(); }); - let mockReq = httpMock.expectOne(Config.Client.apiPath + testUrl); + let mockReq = httpMock.expectOne(Config.Server.apiPath + testUrl); expect(mockReq.cancelled).toBeFalsy(); expect(mockReq.request.responseType).toEqual('json'); mockReq.flush(testResponseMessage); @@ -79,7 +79,7 @@ describe('NetworkService Success tests', () => { expect(err).toBeUndefined(); }); - mockReq = httpMock.expectOne(Config.Client.apiPath + testUrl); + mockReq = httpMock.expectOne(Config.Server.apiPath + testUrl); expect(mockReq.cancelled).toBeFalsy(); expect(mockReq.request.responseType).toEqual('json'); expect(mockReq.request.body).toEqual({}); @@ -157,7 +157,7 @@ describe('NetworkService Fail tests', () => { }) .catch((err) => { expect(err).toBe( - `Http failure response for ${Config.Client.apiPath}/test/url: 0 ` + testError + `Http failure response for ${Config.Server.apiPath}/test/url: 0 ` + testError ); }); @@ -178,7 +178,7 @@ describe('NetworkService Fail tests', () => { }) .catch((err) => { expect(err).toBe( - `Http failure response for ${Config.Client.apiPath}/test/url: 0 ` + testError + `Http failure response for ${Config.Server.apiPath}/test/url: 0 ` + testError ); }); @@ -200,7 +200,7 @@ describe('NetworkService Fail tests', () => { }) .catch((err) => { expect(err).toBe( - `Http failure response for ${Config.Client.apiPath}/test/url: 0 ` + testError + `Http failure response for ${Config.Server.apiPath}/test/url: 0 ` + testError ); }); @@ -222,7 +222,7 @@ describe('NetworkService Fail tests', () => { }) .catch((err) => { expect(err).toBe( - `Http failure response for ${Config.Client.apiPath}/test/url: 0 ` + testError + `Http failure response for ${Config.Server.apiPath}/test/url: 0 ` + testError ); }); diff --git a/src/frontend/app/model/network/network.service.ts b/src/frontend/app/model/network/network.service.ts index 67314625..8b9fc6b1 100644 --- a/src/frontend/app/model/network/network.service.ts +++ b/src/frontend/app/model/network/network.service.ts @@ -1,23 +1,24 @@ -import { Injectable } from '@angular/core'; -import { HttpClient, HttpResponse } from '@angular/common/http'; -import { Message } from '../../../../common/entities/Message'; -import { LoadingBarService } from '@ngx-loading-bar/core'; -import { ErrorCodes, ErrorDTO } from '../../../../common/entities/Error'; -import { Config } from '../../../../common/config/public/Config'; -import { Utils } from '../../../../common/Utils'; -import { CustomHeaders } from '../../../../common/CustomHeaders'; -import { VersionService } from '../version.service'; +import {Injectable} from '@angular/core'; +import {HttpClient, HttpResponse} from '@angular/common/http'; +import {Message} from '../../../../common/entities/Message'; +import {LoadingBarService} from '@ngx-loading-bar/core'; +import {ErrorCodes, ErrorDTO} from '../../../../common/entities/Error'; +import {Config} from '../../../../common/config/public/Config'; +import {Utils} from '../../../../common/Utils'; +import {CustomHeaders} from '../../../../common/CustomHeaders'; +import {VersionService} from '../version.service'; @Injectable() export class NetworkService { - readonly apiBaseUrl = Utils.concatUrls(Config.Client.urlBase, Config.Client.apiPath); + readonly apiBaseUrl = Utils.concatUrls(Config.Server.urlBase, Config.Server.apiPath); private globalErrorHandlers: Array<(error: ErrorDTO) => boolean> = []; constructor( private http: HttpClient, private loadingBarService: LoadingBarService, private versionService: VersionService - ) {} + ) { + } public static buildUrl(url: string, data?: { [key: string]: any }): string { if (data) { @@ -50,7 +51,7 @@ export class NetworkService { }; return this.http - .get(this.apiBaseUrl + url, { responseType: 'text' }) + .get(this.apiBaseUrl + url, {responseType: 'text'}) .toPromise() .then(process) .catch(err); @@ -70,7 +71,7 @@ export class NetworkService { }; return this.http - .get(this.apiBaseUrl + url, { responseType: 'text' }) + .get(this.apiBaseUrl + url, {responseType: 'text'}) .toPromise() .then(process) .catch(err); @@ -130,13 +131,13 @@ export class NetworkService { switch (method) { case 'get': return this.http - .get>(this.apiBaseUrl + url, { observe: 'response' }) + .get>(this.apiBaseUrl + url, {observe: 'response'}) .toPromise() .then(process) .catch(err); case 'delete': return this.http - .delete>(this.apiBaseUrl + url, { observe: 'response' }) + .delete>(this.apiBaseUrl + url, {observe: 'response'}) .toPromise() .then(process) .catch(err); @@ -150,7 +151,7 @@ export class NetworkService { .catch(err); case 'put': return this.http - .put>(this.apiBaseUrl + url, body, { observe: 'response' }) + .put>(this.apiBaseUrl + url, body, {observe: 'response'}) .toPromise() .then(process) .catch(err); diff --git a/src/frontend/app/model/network/user.service.ts b/src/frontend/app/model/network/user.service.ts index cf6002eb..0cdc2149 100644 --- a/src/frontend/app/model/network/user.service.ts +++ b/src/frontend/app/model/network/user.service.ts @@ -1,17 +1,18 @@ -import { Injectable } from '@angular/core'; -import { LoginCredential } from '../../../../common/entities/LoginCredential'; -import { NetworkService } from './network.service'; -import { UserDTO } from '../../../../common/entities/UserDTO'; -import { Config } from '../../../../common/config/public/Config'; -import { ShareService } from '../../ui/gallery/share.service'; -import { QueryParams } from '../../../../common/QueryParams'; +import {Injectable} from '@angular/core'; +import {LoginCredential} from '../../../../common/entities/LoginCredential'; +import {NetworkService} from './network.service'; +import {UserDTO} from '../../../../common/entities/UserDTO'; +import {Config} from '../../../../common/config/public/Config'; +import {ShareService} from '../../ui/gallery/share.service'; +import {QueryParams} from '../../../../common/QueryParams'; @Injectable() export class UserService { constructor( private networkService: NetworkService, private shareService: ShareService - ) {} + ) { + } public async logout(): Promise { return this.networkService.postJson('/user/logout'); @@ -26,16 +27,16 @@ export class UserService { public async shareLogin(password: string): Promise { return this.networkService.postJson( '/share/login?' + - QueryParams.gallery.sharingKey_query + - '=' + - this.shareService.getSharingKey(), - { password } + QueryParams.gallery.sharingKey_query + + '=' + + this.shareService.getSharingKey(), + {password} ); } public async getSessionUser(): Promise { await this.shareService.wait(); - if (Config.Client.Sharing.enabled === true) { + if (Config.Sharing.enabled === true) { if (this.shareService.isSharing()) { const query: any = {}; query[QueryParams.gallery.sharingKey_query] = diff --git a/src/frontend/app/model/query.service.ts b/src/frontend/app/model/query.service.ts index 877bef10..56e581d3 100644 --- a/src/frontend/app/model/query.service.ts +++ b/src/frontend/app/model/query.service.ts @@ -34,7 +34,7 @@ export class QueryService { if (media) { query[QueryParams.gallery.photo] = this.getMediaStringId(media); } - if (Config.Client.Sharing.enabled === true) { + if (Config.Sharing.enabled === true) { if (this.shareService.isSharing()) { query[QueryParams.gallery.sharingKey_query] = this.shareService.getSharingKey(); @@ -47,7 +47,7 @@ export class QueryService { [key: string]: any; } { const params: { [key: string]: any } = {}; - if (Config.Client.Sharing.enabled === true) { + if (Config.Sharing.enabled === true) { if (this.shareService.isSharing()) { params[QueryParams.gallery.sharingKey_query] = this.shareService.getSharingKey(); diff --git a/src/frontend/app/pipes/GPXFilesFilterPipe.ts b/src/frontend/app/pipes/GPXFilesFilterPipe.ts index 8260834b..4e6cdee0 100644 --- a/src/frontend/app/pipes/GPXFilesFilterPipe.ts +++ b/src/frontend/app/pipes/GPXFilesFilterPipe.ts @@ -5,7 +5,7 @@ import { Config } from '../../../common/config/public/Config'; @Pipe({ name: 'gpxFiles' }) export class GPXFilesFilterPipe implements PipeTransform { transform(metaFiles: FileDTO[]): FileDTO[] | null { - if (!Config.Client.MetaFile.gpx) { + if (!Config.MetaFile.gpx) { return []; } if (!metaFiles) { diff --git a/src/frontend/app/ui/admin/admin.component.html b/src/frontend/app/ui/admin/admin.component.html index cbde817b..6c6f40eb 100644 --- a/src/frontend/app/ui/admin/admin.component.html +++ b/src/frontend/app/ui/admin/admin.component.html @@ -1,12 +1,12 @@ - Application version: v{{(settingsService.settings | async).Server.Environment.appVersion}} - + Application version: v{{(settingsService.settings | async).Environment.appVersion}} +
- Built at: {{(settingsService.settings | async).Server.Environment.buildTime | date:'medium' }} + Built at: {{(settingsService.settings | async).Environment.buildTime | date:'medium' }}
- +
- Git commit: {{(settingsService.settings | async).Server.Environment.buildCommitHash}} + Git commit: {{(settingsService.settings | async).Environment.buildCommitHash}}
@@ -45,27 +45,23 @@ class="version" href="https://github.com/bpatrik/pigallery2/releases"> App version: {{'v' + ((settingsService.settings | async).Server.Environment.appVersion || '----')}} + i18n>App version: {{'v' + ((settingsService.settings | async).Environment.appVersion || '----')}}
- - - + +
@@ -89,64 +85,70 @@
- - - - - - - - - - - - - - - - - + + + + + + + + + + + + + +
-
+
Up time: {{(settingsService.settings | async).Server.Environment.upTime | date:'medium'}} + -->: {{(settingsService.settings | async).Environment.upTime | date:'medium'}}
diff --git a/src/frontend/app/ui/admin/admin.component.ts b/src/frontend/app/ui/admin/admin.component.ts index 8ff3df1d..a32aa294 100644 --- a/src/frontend/app/ui/admin/admin.component.ts +++ b/src/frontend/app/ui/admin/admin.component.ts @@ -9,6 +9,8 @@ import {PageHelper} from '../../model/page.helper'; import {SettingsService} from '../settings/settings.service'; import {CookieNames} from '../../../../common/CookieNames'; import {CookieService} from 'ngx-cookie-service'; +import {ConfigPriority} from '../../../../common/config/public/ClientConfig'; +import {Utils} from '../../../../common/Utils'; @Component({ selector: 'app-admin', @@ -16,24 +18,20 @@ import {CookieService} from 'ngx-cookie-service'; styleUrls: ['./admin.component.css'], }) export class AdminComponent implements OnInit, AfterViewInit { - simplifiedMode = true; @ViewChildren('setting') settingsComponents: QueryList; @ViewChildren('setting', {read: ElementRef}) settingsComponentsElemRef: QueryList; contents: ISettingsComponent[] = []; + configPriorities: { key: number; value: string; }[]; + public readonly ConfigPriority = ConfigPriority; constructor( private authService: AuthenticationService, private navigation: NavigationService, public notificationService: NotificationService, public settingsService: SettingsService, - private cookieService: CookieService ) { - if (this.cookieService.check(CookieNames.advancedSettings)) { - this.simplifiedMode = !( - this.cookieService.get(CookieNames.advancedSettings) === 'true' - ); - } + this.configPriorities = Utils.enumToArray(ConfigPriority); } ngAfterViewInit(): void { @@ -67,15 +65,6 @@ export class AdminComponent implements OnInit, AfterViewInit { } return 'info'; } - - modeToggle(): void { - // save it for some years - this.cookieService.set( - CookieNames.advancedSettings, - this.simplifiedMode ? 'false' : 'true', - 365 * 50 - ); - } } diff --git a/src/frontend/app/ui/duplicates/duplicates.component.ts b/src/frontend/app/ui/duplicates/duplicates.component.ts index 823d6829..302db2ee 100644 --- a/src/frontend/app/ui/duplicates/duplicates.component.ts +++ b/src/frontend/app/ui/duplicates/duplicates.component.ts @@ -170,7 +170,7 @@ export class DuplicateComponent implements OnDestroy { private shouldRenderMore(): boolean { return ( - Config.Client.Other.enableOnScrollRendering === false || + Config.Gallery.enableOnScrollRendering === false || PageHelper.ScrollY >= PageHelper.MaxScrollY * 0.7 || document.body.clientHeight * 0.85 < window.innerHeight ); diff --git a/src/frontend/app/ui/faces/Person.ts b/src/frontend/app/ui/faces/Person.ts index 18e8690e..3e1a0abc 100644 --- a/src/frontend/app/ui/faces/Person.ts +++ b/src/frontend/app/ui/faces/Person.ts @@ -11,8 +11,8 @@ export class Person implements PersonDTO { public static getThumbnailUrl(that: PersonDTO): string { return Utils.concatUrls( - Config.Client.urlBase, - Config.Client.apiPath + '/person/', + Config.Server.urlBase, + Config.Server.apiPath + '/person/', encodeURIComponent(that.name), '/thumbnail' ); diff --git a/src/frontend/app/ui/faces/face/face.component.ts b/src/frontend/app/ui/faces/face/face.component.ts index c73ea478..970a67de 100644 --- a/src/frontend/app/ui/faces/face/face.component.ts +++ b/src/frontend/app/ui/faces/face/face.component.ts @@ -38,7 +38,7 @@ export class FaceComponent implements OnInit, OnDestroy { get CanUpdate(): boolean { return ( this.authenticationService.user.getValue().role >= - Config.Client.Faces.writeAccessMinRole + Config.Faces.writeAccessMinRole ); } diff --git a/src/frontend/app/ui/frame/frame.component.ts b/src/frontend/app/ui/frame/frame.component.ts index 85735d9c..4aebcd0c 100644 --- a/src/frontend/app/ui/frame/frame.component.ts +++ b/src/frontend/app/ui/frame/frame.component.ts @@ -19,10 +19,10 @@ import {Utils} from '../../../../common/Utils'; }) export class FrameComponent { public readonly user: BehaviorSubject; - public readonly authenticationRequired = Config.Client.authenticationRequired; - public readonly title = Config.Client.applicationTitle; + public readonly authenticationRequired = Config.Users.authenticationRequired; + public readonly title = Config.Server.applicationTitle; public collapsed = true; - public readonly navbarLinks = Config.Client.Other.NavBar.links; + public readonly navbarLinks = Config.Gallery.NavBar.links; public readonly NavigationLinkTypes = NavigationLinkTypes; public readonly stringify = JSON.stringify; @@ -41,9 +41,9 @@ export class FrameComponent { isFacesAvailable(): boolean { return ( - Config.Client.Faces.enabled && + Config.Faces.enabled && this.user.value && - this.user.value.role >= Config.Client.Faces.readAccessMinRole + this.user.value.role >= Config.Faces.readAccessMinRole ); } @@ -70,7 +70,7 @@ export class FrameComponent { } isAlbumsAvailable(): boolean { - return Config.Client.Album.enabled; + return Config.Album.enabled; } } diff --git a/src/frontend/app/ui/gallery/Media.ts b/src/frontend/app/ui/gallery/Media.ts index bbbc5c0b..38531702 100644 --- a/src/frontend/app/ui/gallery/Media.ts +++ b/src/frontend/app/ui/gallery/Media.ts @@ -5,7 +5,7 @@ import {MediaDTO, MediaDTOUtils} from '../../../../common/entities/MediaDTO'; export class Media extends MediaIcon { static readonly sortedThumbnailSizes = - Config.Client.Media.Thumbnail.thumbnailSizes.sort((a, b): number => a - b); + Config.Media.Thumbnail.thumbnailSizes.sort((a, b): number => a - b); constructor( media: MediaDTO, @@ -37,7 +37,7 @@ export class Media extends MediaIcon { const size = this.getThumbnailSize(); if (this.media.missingThumbnails) { - for (const thSize of Config.Client.Media.Thumbnail.thumbnailSizes) { + for (const thSize of Config.Media.Thumbnail.thumbnailSizes) { // eslint-disable-next-line no-bitwise if ( (this.media.missingThumbnails & MediaIcon.ThumbnailMap[thSize]) === @@ -69,8 +69,8 @@ export class Media extends MediaIcon { getReplacementThumbnailPath(): string { const size = this.getReplacementThumbnailSize(); return Utils.concatUrls( - Config.Client.urlBase, - Config.Client.apiPath, + Config.Server.urlBase, + Config.Server.apiPath, '/gallery/content/', this.getRelativePath(), 'thumbnail', @@ -85,8 +85,8 @@ export class Media extends MediaIcon { getThumbnailPath(): string { const size = this.getThumbnailSize(); return Utils.concatUrls( - Config.Client.urlBase, - Config.Client.apiPath, + Config.Server.urlBase, + Config.Server.apiPath, '/gallery/content/', this.getRelativePath(), 'thumbnail', diff --git a/src/frontend/app/ui/gallery/MediaIcon.ts b/src/frontend/app/ui/gallery/MediaIcon.ts index 4e6d746c..f8aaf8ed 100644 --- a/src/frontend/app/ui/gallery/MediaIcon.ts +++ b/src/frontend/app/ui/gallery/MediaIcon.ts @@ -4,7 +4,7 @@ import { MediaDTO } from '../../../../common/entities/MediaDTO'; export class MediaIcon { protected static readonly ThumbnailMap = - Config.Client.Media.Thumbnail.generateThumbnailMap(); + Config.Media.Thumbnail.generateThumbnailMap(); protected replacementSizeCache: number | boolean = false; @@ -16,14 +16,14 @@ export class MediaIcon { iconLoaded(): void { this.media.missingThumbnails -= - MediaIcon.ThumbnailMap[Config.Client.Media.Thumbnail.iconSize]; + MediaIcon.ThumbnailMap[Config.Media.Thumbnail.iconSize]; } isIconAvailable(): boolean { // eslint-disable-next-line no-bitwise return ( (this.media.missingThumbnails & - MediaIcon.ThumbnailMap[Config.Client.Media.Thumbnail.iconSize]) === + MediaIcon.ThumbnailMap[Config.Media.Thumbnail.iconSize]) === 0 ); } @@ -48,8 +48,8 @@ export class MediaIcon { getIconPath(): string { return Utils.concatUrls( - Config.Client.urlBase, - Config.Client.apiPath, + Config.Server.urlBase, + Config.Server.apiPath, '/gallery/content/', this.getRelativePath(), 'icon' @@ -58,8 +58,8 @@ export class MediaIcon { getMediaPath(): string { return Utils.concatUrls( - Config.Client.urlBase, - Config.Client.apiPath, + Config.Server.urlBase, + Config.Server.apiPath, '/gallery/content/', this.getRelativePath() ); diff --git a/src/frontend/app/ui/gallery/cache.gallery.service.ts b/src/frontend/app/ui/gallery/cache.gallery.service.ts index ce806a9f..2b335d73 100644 --- a/src/frontend/app/ui/gallery/cache.gallery.service.ts +++ b/src/frontend/app/ui/gallery/cache.gallery.service.ts @@ -3,7 +3,6 @@ import {DirectoryPathDTO, ParentDirectoryDTO,} from '../../../../common/entities import {Utils} from '../../../../common/Utils'; import {Config} from '../../../../common/config/public/Config'; import {IAutoCompleteItem} from '../../../../common/entities/AutoCompleteItem'; -import {SearchResultDTO} from '../../../../common/entities/SearchResultDTO'; import {MediaDTO} from '../../../../common/entities/MediaDTO'; import {SortingMethods} from '../../../../common/entities/SortingMethods'; import {VersionService} from '../../model/version.service'; @@ -58,7 +57,7 @@ export class GalleryCacheService { const value: CacheItem = JSON.parse(tmp); if ( value.timestamp < - Date.now() - Config.Client.Search.searchCacheTimeout + Date.now() - Config.Search.searchCacheTimeout ) { localStorage.removeItem(key); return null; @@ -134,7 +133,7 @@ export class GalleryCacheService { text: string, type: SearchQueryTypes ): IAutoCompleteItem[] { - if (Config.Client.Other.enableCache === false) { + if (Config.Gallery.enableCache === false) { return null; } const key = @@ -146,7 +145,7 @@ export class GalleryCacheService { const value: CacheItem = JSON.parse(tmp); if ( value.timestamp < - Date.now() - Config.Client.Search.AutoComplete.cacheTimeout + Date.now() - Config.Search.AutoComplete.cacheTimeout ) { localStorage.removeItem(key); return null; @@ -161,7 +160,7 @@ export class GalleryCacheService { type: SearchQueryTypes, items: Array ): void { - if (Config.Client.Other.enableCache === false) { + if (Config.Gallery.enableCache === false) { return; } const key = @@ -181,7 +180,7 @@ export class GalleryCacheService { } public getSearch(query: SearchQueryDTO): ContentWrapperWithError { - if (Config.Client.Other.enableCache === false) { + if (Config.Gallery.enableCache === false) { return null; } const key = GalleryCacheService.SEARCH_PREFIX + JSON.stringify(query); @@ -189,7 +188,7 @@ export class GalleryCacheService { } public setSearch(cw: ContentWrapperWithError): void { - if (Config.Client.Other.enableCache === false) { + if (Config.Gallery.enableCache === false) { return; } const tmp: CacheItem = { @@ -206,7 +205,7 @@ export class GalleryCacheService { } public getDirectory(directoryName: string): ContentWrapperWithError { - if (Config.Client.Other.enableCache === false) { + if (Config.Gallery.enableCache === false) { return null; } try { @@ -223,7 +222,7 @@ export class GalleryCacheService { } public setDirectory(cw: ContentWrapper): void { - if (Config.Client.Other.enableCache === false) { + if (Config.Gallery.enableCache === false) { return; } @@ -248,7 +247,7 @@ export class GalleryCacheService { * @param media: MediaBaseDTO */ public mediaUpdated(media: MediaDTO): void { - if (Config.Client.Other.enableCache === false) { + if (Config.Gallery.enableCache === false) { return; } diff --git a/src/frontend/app/ui/gallery/content.service.ts b/src/frontend/app/ui/gallery/content.service.ts index dbc3efb8..9adb9378 100644 --- a/src/frontend/app/ui/gallery/content.service.ts +++ b/src/frontend/app/ui/gallery/content.service.ts @@ -57,7 +57,7 @@ export class ContentService { // prepare server request const params: { [key: string]: any } = {}; - if (Config.Client.Sharing.enabled === true) { + if (Config.Sharing.enabled === true) { if (this.shareService.isSharing()) { params[QueryParams.gallery.sharingKey_query] = this.shareService.getSharingKey(); diff --git a/src/frontend/app/ui/gallery/gallery.component.html b/src/frontend/app/ui/gallery/gallery.component.html index 33555c15..bd59d60c 100644 --- a/src/frontend/app/ui/gallery/gallery.component.html +++ b/src/frontend/app/ui/gallery/gallery.component.html @@ -50,7 +50,7 @@
+ *ngIf="config.MetaFile.markdown && directoryContent?.metaFile && (directoryContent.metaFile | mdFiles).length>0"> diff --git a/src/frontend/app/ui/gallery/gallery.component.ts b/src/frontend/app/ui/gallery/gallery.component.ts index 17655847..cc72d01c 100644 --- a/src/frontend/app/ui/gallery/gallery.component.ts +++ b/src/frontend/app/ui/gallery/gallery.component.ts @@ -63,7 +63,7 @@ export class GalleryComponent implements OnInit, OnDestroy { private filterService: FilterService, private sortingService: GallerySortingService ) { - this.mapEnabled = Config.Client.Map.enabled; + this.mapEnabled = Config.Map.enabled; PageHelper.showScrollY(); } @@ -120,17 +120,17 @@ export class GalleryComponent implements OnInit, OnDestroy { !this.authService.isAuthenticated() && (!this.shareService.isSharing() || (this.shareService.isSharing() && - Config.Client.Sharing.passwordProtected === true)) + Config.Sharing.passwordProtected === true)) ) { return this.navigation.toLogin(); } this.showSearchBar = - Config.Client.Search.enabled && this.authService.canSearch(); + Config.Search.enabled && this.authService.canSearch(); this.showShare = - Config.Client.Sharing.enabled && + Config.Sharing.enabled && this.authService.isAuthorized(UserRoles.User); this.showRandomPhotoBuilder = - Config.Client.RandomPhoto.enabled && + Config.RandomPhoto.enabled && this.authService.isAuthorized(UserRoles.User); this.subscription.content = this.sortingService .applySorting( diff --git a/src/frontend/app/ui/gallery/grid/grid.gallery.component.ts b/src/frontend/app/ui/gallery/grid/grid.gallery.component.ts index 4f531b54..d13c45ba 100644 --- a/src/frontend/app/ui/gallery/grid/grid.gallery.component.ts +++ b/src/frontend/app/ui/gallery/grid/grid.gallery.component.ts @@ -139,7 +139,7 @@ export class GalleryGridComponent ngAfterViewInit(): void { this.lightbox.setGridPhotoQL(this.gridPhotoQL); - if (Config.Client.Other.enableOnScrollThumbnailPrioritising === true) { + if (Config.Gallery.enableOnScrollThumbnailPrioritising === true) { this.gridPhotoQL.changes.subscribe((): void => { this.scrollListenerPhotos = this.gridPhotoQL.filter( (pc): boolean => pc.ScrollListener @@ -206,7 +206,7 @@ export class GalleryGridComponent window.requestAnimationFrame((): void => { this.renderPhotos(); - if (Config.Client.Other.enableOnScrollThumbnailPrioritising === true) { + if (Config.Gallery.enableOnScrollThumbnailPrioritising === true) { this.scrollListenerPhotos.forEach( (pc: GalleryPhotoComponent): void => { pc.onScroll(); @@ -301,7 +301,7 @@ export class GalleryGridComponent private shouldRenderMore(offset = 0): boolean { const bottomOffset = this.getMaxRowHeight() * 2; return ( - Config.Client.Other.enableOnScrollRendering === false || + Config.Gallery.enableOnScrollRendering === false || PageHelper.ScrollY >= document.body.clientHeight + offset - diff --git a/src/frontend/app/ui/gallery/grid/photo/photo.grid.gallery.component.ts b/src/frontend/app/ui/gallery/grid/photo/photo.grid.gallery.component.ts index 3b4b51f2..885d0482 100644 --- a/src/frontend/app/ui/gallery/grid/photo/photo.grid.gallery.component.ts +++ b/src/frontend/app/ui/gallery/grid/photo/photo.grid.gallery.component.ts @@ -52,7 +52,7 @@ export class GalleryPhotoComponent implements IRenderable, OnInit, OnDestroy { private authService: AuthenticationService ) { this.searchEnabled = - Config.Client.Search.enabled && this.authService.canSearch(); + Config.Search.enabled && this.authService.canSearch(); } get ScrollListener(): boolean { @@ -60,7 +60,7 @@ export class GalleryPhotoComponent implements IRenderable, OnInit, OnDestroy { } get Title(): string { - if (Config.Client.Other.captionFirstNaming === false) { + if (Config.Gallery.captionFirstNaming === false) { return this.gridMedia.media.name; } if ((this.gridMedia.media as PhotoDTO).metadata.caption) { @@ -83,7 +83,7 @@ export class GalleryPhotoComponent implements IRenderable, OnInit, OnDestroy { (metadata.faces && metadata.faces.length > 0) ) { this.keywords = []; - if (Config.Client.Faces.enabled) { + if (Config.Faces.enabled) { const names: string[] = (metadata.faces || []).map( (f): string => f.name ); diff --git a/src/frontend/app/ui/gallery/lightbox/controls/controls.lightbox.gallery.component.ts b/src/frontend/app/ui/gallery/lightbox/controls/controls.lightbox.gallery.component.ts index 16d0c20a..e134e512 100644 --- a/src/frontend/app/ui/gallery/lightbox/controls/controls.lightbox.gallery.component.ts +++ b/src/frontend/app/ui/gallery/lightbox/controls/controls.lightbox.gallery.component.ts @@ -54,7 +54,7 @@ export class ControlsLightboxComponent implements OnDestroy, OnInit, OnChanges { @Input() mediaElement: GalleryLightboxMediaComponent; @Input() photoFrameDim = {width: 1, height: 1, aspect: 1}; - public readonly facesEnabled = Config.Client.Faces.enabled; + public readonly facesEnabled = Config.Faces.enabled; public zoom = 1; public playBackState: PlayBackStates = PlayBackStates.Paused; @@ -82,7 +82,7 @@ export class ControlsLightboxComponent implements OnDestroy, OnInit, OnChanges { private cacheService: GalleryCacheService ) { this.searchEnabled = - Config.Client.Search.enabled && this.authService.canSearch(); + Config.Search.enabled && this.authService.canSearch(); } @@ -129,7 +129,7 @@ export class ControlsLightboxComponent implements OnDestroy, OnInit, OnChanges { if (this.cacheService.getSlideshowSpeed()) { this.selectedSlideshowSpeed = this.cacheService.getSlideshowSpeed(); } else { - this.selectedSlideshowSpeed = Config.Client.Other.defaultSlideshowSpeed; + this.selectedSlideshowSpeed = Config.Gallery.defaultSlideshowSpeed; } } diff --git a/src/frontend/app/ui/gallery/lightbox/infopanel/info-panel.lightbox.gallery.component.ts b/src/frontend/app/ui/gallery/lightbox/infopanel/info-panel.lightbox.gallery.component.ts index 1b355e20..c28781c3 100644 --- a/src/frontend/app/ui/gallery/lightbox/infopanel/info-panel.lightbox.gallery.component.ts +++ b/src/frontend/app/ui/gallery/lightbox/infopanel/info-panel.lightbox.gallery.component.ts @@ -54,9 +54,9 @@ export class InfoPanelLightboxComponent implements OnInit, OnChanges { public mapService: MapService, private authService: AuthenticationService ) { - this.mapEnabled = Config.Client.Map.enabled; + this.mapEnabled = Config.Map.enabled; this.searchEnabled = - Config.Client.Search.enabled && this.authService.canSearch(); + Config.Search.enabled && this.authService.canSearch(); this.baseLayer = tileLayer(mapService.MapLayer, { attribution: mapService.ShortAttributions, }); @@ -114,7 +114,7 @@ export class InfoPanelLightboxComponent implements OnInit, OnChanges { (metadata.faces && metadata.faces.length > 0) ) { this.keywords = []; - if (Config.Client.Faces.enabled) { + if (Config.Faces.enabled) { const names: string[] = (metadata.faces || []).map( (f): string => f.name ); diff --git a/src/frontend/app/ui/gallery/lightbox/media/media.lightbox.gallery.component.ts b/src/frontend/app/ui/gallery/lightbox/media/media.lightbox.gallery.component.ts index ca63253e..035efb17 100644 --- a/src/frontend/app/ui/gallery/lightbox/media/media.lightbox.gallery.component.ts +++ b/src/frontend/app/ui/gallery/lightbox/media/media.lightbox.gallery.component.ts @@ -216,7 +216,7 @@ export class GalleryLightboxMediaComponent implements OnChanges { this.imageLoadFinished.next = true; return; } - if (Config.Client.Media.Photo.Converting.enabled === true) { + if (Config.Media.Photo.Converting.enabled === true) { this.nextImage.src = this.nextGridMedia.getBestFitMediaPath(); } else { this.nextImage.src = this.nextGridMedia.getMediaPath(); @@ -240,10 +240,10 @@ export class GalleryLightboxMediaComponent implements OnChanges { if ( this.zoom === 1 || - Config.Client.Media.Photo.loadFullImageOnZoom === false + Config.Media.Photo.Converting.loadFullImageOnZoom === false ) { if (this.photo.src == null) { - if (Config.Client.Media.Photo.Converting.enabled === true) { + if (Config.Media.Photo.Converting.enabled === true) { this.photo.src = this.gridMedia.getBestFitMediaPath(); this.photo.isBestFit = true; } else { diff --git a/src/frontend/app/ui/gallery/map/lightbox/lightbox.map.gallery.component.ts b/src/frontend/app/ui/gallery/map/lightbox/lightbox.map.gallery.component.ts index ccad3acc..450afeb5 100644 --- a/src/frontend/app/ui/gallery/map/lightbox/lightbox.map.gallery.component.ts +++ b/src/frontend/app/ui/gallery/map/lightbox/lightbox.map.gallery.component.ts @@ -74,12 +74,12 @@ export class GalleryMapLightboxComponent implements OnChanges { center: latLng(0, 0), }; private smallIconSize = new Point( - Config.Client.Media.Thumbnail.iconSize * 0.75, - Config.Client.Media.Thumbnail.iconSize * 0.75 + Config.Media.Thumbnail.iconSize * 0.75, + Config.Media.Thumbnail.iconSize * 0.75 ); private iconSize = new Point( - Config.Client.Media.Thumbnail.iconSize, - Config.Client.Media.Thumbnail.iconSize + Config.Media.Thumbnail.iconSize, + Config.Media.Thumbnail.iconSize ); private usedIconSize = this.iconSize; private mapLayersControlOption: LeafletControlLayersConfig & { @@ -302,7 +302,7 @@ export class GalleryMapLightboxComponent implements OnChanges { } // Setting photo icon - if (Config.Client.Map.useImageMarkers === true) { + if (Config.Map.useImageMarkers === true) { mkr.on('add', () => { mkr.off('add'); const iconTh = this.thumbnailService.getIcon(new MediaIcon(p)); @@ -384,7 +384,7 @@ export class GalleryMapLightboxComponent implements OnChanges { } onLeafletZoom(): void { - if (Config.Client.Map.useImageMarkers === false) { + if (Config.Map.useImageMarkers === false) { return; } if ( diff --git a/src/frontend/app/ui/gallery/map/map.gallery.component.ts b/src/frontend/app/ui/gallery/map/map.gallery.component.ts index 36fc9fc5..fd9a0db3 100644 --- a/src/frontend/app/ui/gallery/map/map.gallery.component.ts +++ b/src/frontend/app/ui/gallery/map/map.gallery.component.ts @@ -79,7 +79,7 @@ export class GalleryMapComponent implements OnChanges, IRenderable { p.metadata.positionData.GPSData.longitude ); }) - .slice(0, Config.Client.Map.maxPreviewMarkers) + .slice(0, Config.Map.maxPreviewMarkers) .map((p): Marker => { return marker({ lat: p.metadata.positionData.GPSData.latitude, diff --git a/src/frontend/app/ui/gallery/map/map.service.ts b/src/frontend/app/ui/gallery/map/map.service.ts index 040db593..6d8f8395 100644 --- a/src/frontend/app/ui/gallery/map/map.service.ts +++ b/src/frontend/app/ui/gallery/map/map.service.ts @@ -25,19 +25,19 @@ export class MapService { name: 'street', url: 'https://api.mapbox.com/styles/v1/mapbox/streets-v11/tiles/256/{z}/{x}/{y}?access_token=' + - Config.Client.Map.mapboxAccessToken, + Config.Map.mapboxAccessToken, }, { name: 'satellite', url: 'https://api.mapbox.com/styles/v1/mapbox/satellite-v9/tiles/256/{z}/{x}/{y}?access_token=' + - Config.Client.Map.mapboxAccessToken, + Config.Map.mapboxAccessToken, }, { name: 'hybrid', url: 'https://api.mapbox.com/styles/v1/mapbox/satellite-streets-v11/tiles/256/{z}/{x}/{y}?access_token=' + - Config.Client.Map.mapboxAccessToken, + Config.Map.mapboxAccessToken, }, ]; } @@ -46,11 +46,11 @@ export class MapService { const OSM = 'OSM'; const MB = 'Mapbox'; - if (Config.Client.Map.mapProvider === MapProviders.OpenStreetMap) { + if (Config.Map.mapProvider === MapProviders.OpenStreetMap) { return ' © ' + OSM; } - if (Config.Client.Map.mapProvider === MapProviders.Mapbox) { + if (Config.Map.mapProvider === MapProviders.Mapbox) { return OSM + ' | ' + MB; } return ''; @@ -61,11 +61,11 @@ export class MapService { '© OpenStreetMap'; const MB = '© Mapbox'; - if (Config.Client.Map.mapProvider === MapProviders.OpenStreetMap) { + if (Config.Map.mapProvider === MapProviders.OpenStreetMap) { return OSM; } - if (Config.Client.Map.mapProvider === MapProviders.Mapbox) { + if (Config.Map.mapProvider === MapProviders.Mapbox) { return OSM + ' | ' + MB; } return ''; @@ -76,9 +76,9 @@ export class MapService { } public get Layers(): { name: string; url: string }[] { - switch (Config.Client.Map.mapProvider) { + switch (Config.Map.mapProvider) { case MapProviders.Custom: - return Config.Client.Map.customLayers; + return Config.Map.customLayers; case MapProviders.Mapbox: return MapService.MAPBOXLAYERS; case MapProviders.OpenStreetMap: diff --git a/src/frontend/app/ui/gallery/navigator/navigator.gallery.component.html b/src/frontend/app/ui/gallery/navigator/navigator.gallery.component.html index 45f3a14d..34b06dfd 100644 --- a/src/frontend/app/ui/gallery/navigator/navigator.gallery.component.html +++ b/src/frontend/app/ui/gallery/navigator/navigator.gallery.component.html @@ -18,14 +18,14 @@
- +
{{ItemCount}} items
 
- +  
- + diff --git a/src/frontend/app/ui/gallery/navigator/navigator.gallery.component.ts b/src/frontend/app/ui/gallery/navigator/navigator.gallery.component.ts index a320ad5a..4d822a72 100644 --- a/src/frontend/app/ui/gallery/navigator/navigator.gallery.component.ts +++ b/src/frontend/app/ui/gallery/navigator/navigator.gallery.component.ts @@ -30,7 +30,7 @@ export class GalleryNavigatorComponent { public SortingMethods = SortingMethods; public sortingMethodsType: { key: number; value: string }[] = []; public readonly config = Config; - // DefaultSorting = Config.Client.Other.defaultPhotoSortingMethod; + // DefaultSorting = Config.Gallery.defaultPhotoSortingMethod; public readonly SearchQueryTypes = SearchQueryTypes; public wrappedContent: Observable; public directoryContent: Observable; @@ -138,8 +138,8 @@ export class GalleryNavigatorComponent { queryParams += e[0] + '=' + e[1]; }); return Utils.concatUrls( - Config.Client.urlBase, - Config.Client.apiPath, + Config.Server.urlBase, + Config.Server.apiPath, '/gallery/zip/', c.directory.path, c.directory.name, diff --git a/src/frontend/app/ui/gallery/navigator/sorting.service.ts b/src/frontend/app/ui/gallery/navigator/sorting.service.ts index 4eda3de1..3c08b601 100644 --- a/src/frontend/app/ui/gallery/navigator/sorting.service.ts +++ b/src/frontend/app/ui/gallery/navigator/sorting.service.ts @@ -24,7 +24,7 @@ export class GallerySortingService { private rndService: SeededRandomService ) { this.sorting = new BehaviorSubject( - Config.Client.Other.defaultPhotoSortingMethod + Config.Gallery.defaultPhotoSortingMethod ); this.galleryService.content.subscribe((c) => { if (c.directory) { @@ -47,9 +47,9 @@ export class GallerySortingService { } } if (cw.searchResult) { - return Config.Client.Other.defaultSearchSortingMethod; + return Config.Gallery.defaultSearchSortingMethod; } - return Config.Client.Other.defaultPhotoSortingMethod; + return Config.Gallery.defaultPhotoSortingMethod; } setSorting(sorting: SortingMethods): void { @@ -96,7 +96,7 @@ export class GallerySortingService { break; case SortingMethods.ascDate: if ( - Config.Client.Other.enableDirectorySortingByDate === true + Config.Gallery.enableDirectorySortingByDate === true ) { c.directories.sort( (a, b) => a.lastModified - b.lastModified @@ -115,7 +115,7 @@ export class GallerySortingService { break; case SortingMethods.descDate: if ( - Config.Client.Other.enableDirectorySortingByDate === true + Config.Gallery.enableDirectorySortingByDate === true ) { c.directories.sort( (a, b) => b.lastModified - a.lastModified diff --git a/src/frontend/app/ui/gallery/random-query-builder/random-query-builder.gallery.component.ts b/src/frontend/app/ui/gallery/random-query-builder/random-query-builder.gallery.component.ts index 23f7819d..e6a99add 100644 --- a/src/frontend/app/ui/gallery/random-query-builder/random-query-builder.gallery.component.ts +++ b/src/frontend/app/ui/gallery/random-query-builder/random-query-builder.gallery.component.ts @@ -60,7 +60,7 @@ export class RandomQueryBuilderGalleryComponent implements OnInit, OnDestroy { onQueryChange(): void { this.url = NetworkService.buildUrl( - Config.Client.publicUrl + Config.Client.apiPath + '/gallery/random/' + this.HTMLSearchQuery + Config.Server.publicUrl + Config.Server.apiPath + '/gallery/random/' + this.HTMLSearchQuery ); } diff --git a/src/frontend/app/ui/gallery/search/search-field-base/search-field-base.gallery.component.ts b/src/frontend/app/ui/gallery/search/search-field-base/search-field-base.gallery.component.ts index 7a3bfd1e..ba3ccf21 100644 --- a/src/frontend/app/ui/gallery/search/search-field-base/search-field-base.gallery.component.ts +++ b/src/frontend/app/ui/gallery/search/search-field-base/search-field-base.gallery.component.ts @@ -141,7 +141,7 @@ export class GallerySearchFieldBaseComponent onSearchChange(event: KeyboardEvent): void { const searchText = this.getAutocompleteToken(); if ( - Config.Client.Search.AutoComplete.enabled && + Config.Search.AutoComplete.enabled && this.cache.lastAutocomplete !== searchText.current ) { this.cache.lastAutocomplete = searchText.current; @@ -267,7 +267,7 @@ export class GallerySearchFieldBaseComponent current: string; prev: string; }): Promise { - if (!Config.Client.Search.AutoComplete.enabled) { + if (!Config.Search.AutoComplete.enabled) { return; } diff --git a/src/frontend/app/ui/gallery/search/search.gallery.component.ts b/src/frontend/app/ui/gallery/search/search.gallery.component.ts index 06d7e757..a001ba94 100644 --- a/src/frontend/app/ui/gallery/search/search.gallery.component.ts +++ b/src/frontend/app/ui/gallery/search/search.gallery.component.ts @@ -1,23 +1,23 @@ -import { Component, OnDestroy, TemplateRef } from '@angular/core'; -import { AutoCompleteService } from './autocomplete.service'; -import { ActivatedRoute, Params, Router, RouterLink } from '@angular/router'; -import { ContentService } from '../content.service'; -import { Subscription } from 'rxjs'; -import { NavigationService } from '../../../model/navigation.service'; -import { QueryParams } from '../../../../../common/QueryParams'; +import {Component, OnDestroy, TemplateRef} from '@angular/core'; +import {AutoCompleteService} from './autocomplete.service'; +import {ActivatedRoute, Params, Router, RouterLink} from '@angular/router'; +import {ContentService} from '../content.service'; +import {Subscription} from 'rxjs'; +import {NavigationService} from '../../../model/navigation.service'; +import {QueryParams} from '../../../../../common/QueryParams'; import { MetadataSearchQueryTypes, SearchQueryDTO, SearchQueryTypes, TextSearch, } from '../../../../../common/entities/SearchQueryDTO'; -import { BsModalService } from 'ngx-bootstrap/modal'; -import { BsModalRef } from 'ngx-bootstrap/modal/bs-modal-ref.service'; -import { SearchQueryParserService } from './search-query-parser.service'; -import { AlbumsService } from '../../albums/albums.service'; -import { Config } from '../../../../../common/config/public/Config'; -import { UserRoles } from '../../../../../common/entities/UserDTO'; -import { AuthenticationService } from '../../../model/network/authentication.service'; +import {BsModalService} from 'ngx-bootstrap/modal'; +import {BsModalRef} from 'ngx-bootstrap/modal/bs-modal-ref.service'; +import {SearchQueryParserService} from './search-query-parser.service'; +import {AlbumsService} from '../../albums/albums.service'; +import {Config} from '../../../../../common/config/public/Config'; +import {UserRoles} from '../../../../../common/entities/UserDTO'; +import {AuthenticationService} from '../../../model/network/authentication.service'; @Component({ selector: 'app-gallery-search', @@ -73,7 +73,7 @@ export class GallerySearchComponent implements OnDestroy { get CanCreateAlbum(): boolean { return ( - Config.Client.Album.enabled && + Config.Album.enabled && this.authenticationService.user.getValue().role >= UserRoles.Admin ); } diff --git a/src/frontend/app/ui/gallery/share/share.gallery.component.ts b/src/frontend/app/ui/gallery/share/share.gallery.component.ts index 37f09668..6e3dc814 100644 --- a/src/frontend/app/ui/gallery/share/share.gallery.component.ts +++ b/src/frontend/app/ui/gallery/share/share.gallery.component.ts @@ -30,7 +30,7 @@ export class GalleryShareComponent implements OnInit, OnDestroy { currentDir = ''; sharing: SharingDTO = null; contentSubscription: Subscription = null; - readonly passwordProtection = Config.Client.Sharing.passwordProtected; + readonly passwordProtection = Config.Sharing.passwordProtected; readonly ValidityTypes = ValidityTypes; modalRef: BsModalRef; @@ -99,7 +99,7 @@ export class GalleryShareComponent implements OnInit, OnDestroy { this.input.password, this.calcValidity() ); - this.url = Config.Client.publicUrl + '/share/' + this.sharing.sharingKey; + this.url = Config.Server.publicUrl + '/share/' + this.sharing.sharingKey; } async get(): Promise { @@ -109,7 +109,7 @@ export class GalleryShareComponent implements OnInit, OnDestroy { this.input.includeSubfolders, this.calcValidity() ); - this.url = Config.Client.publicUrl + '/share/' + this.sharing.sharingKey; + this.url = Config.Server.publicUrl + '/share/' + this.sharing.sharingKey; } async openModal(template: TemplateRef): Promise { diff --git a/src/frontend/app/ui/gallery/thumbnailLoader.service.ts b/src/frontend/app/ui/gallery/thumbnailLoader.service.ts index 23994e9c..4ae84b33 100644 --- a/src/frontend/app/ui/gallery/thumbnailLoader.service.ts +++ b/src/frontend/app/ui/gallery/thumbnailLoader.service.ts @@ -25,7 +25,7 @@ export class ThumbnailLoaderService { if ( this.que.length === 0 || this.runningRequests >= - Config.Client.Media.Thumbnail.concurrentThumbnailGenerations + Config.Media.Thumbnail.concurrentThumbnailGenerations ) { return; } diff --git a/src/frontend/app/ui/language/language.component.ts b/src/frontend/app/ui/language/language.component.ts index e8474be9..52491714 100644 --- a/src/frontend/app/ui/language/language.component.ts +++ b/src/frontend/app/ui/language/language.component.ts @@ -12,10 +12,10 @@ export class LanguageComponent { @Input() isDark: boolean; languages: string[] = []; current: string = null; - urlBase = Config.Client.urlBase; + urlBase = Config.Server.urlBase; constructor(private cookieService: CookieService) { - this.languages = Config.Client.languages.sort(); + this.languages = Config.Server.languages.sort(); if (this.cookieService.get(CookieNames.lang) != null) { this.current = this.cookieService.get(CookieNames.lang); } diff --git a/src/frontend/app/ui/login/login.component.ts b/src/frontend/app/ui/login/login.component.ts index 61701361..53cdda34 100644 --- a/src/frontend/app/ui/login/login.component.ts +++ b/src/frontend/app/ui/login/login.component.ts @@ -21,7 +21,7 @@ export class LoginComponent implements OnInit { private navigation: NavigationService ) { this.loginCredential = new LoginCredential(); - this.title = Config.Client.applicationTitle; + this.title = Config.Server.applicationTitle; } ngOnInit(): void { 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 2494056a..405290de 100644 --- a/src/frontend/app/ui/settings/_abstract/abstract.settings.component.ts +++ b/src/frontend/app/ui/settings/_abstract/abstract.settings.component.ts @@ -7,50 +7,65 @@ import { Output, ViewChild, } from '@angular/core'; -import { AuthenticationService } from '../../../model/network/authentication.service'; -import { UserRoles } from '../../../../../common/entities/UserDTO'; -import { Utils } from '../../../../../common/Utils'; -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 { Subscription } from 'rxjs'; -import { ISettingsComponent } from './ISettingsComponent'; -import { WebConfig } from '../../../../../common/config/private/WebConfig'; -import { FormControl } from '@angular/forms'; +import {AuthenticationService} from '../../../model/network/authentication.service'; +import {UserRoles} from '../../../../../common/entities/UserDTO'; +import {Utils} from '../../../../../common/Utils'; +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 {Subscription} from 'rxjs'; +import {ISettingsComponent} from './ISettingsComponent'; +import {WebConfig} from '../../../../../common/config/private/WebConfig'; +import {FormControl} from '@angular/forms'; +import {ConfigPriority, TAGS} from '../../../../../common/config/public/ClientConfig'; +import {SettingsService} from '../settings.service'; +import {IConfigClass} from 'typeconfig/common'; +import {IConfigClassPrivate} from '../../../../../../node_modules/typeconfig/src/decorators/class/IConfigClass'; +import {IPropertyMetadata} from '../../../../../../node_modules/typeconfig/src/decorators/property/IPropertyState'; +import {IWebConfigClass, IWebConfigClassPrivate} from '../../../../../../node_modules/typeconfig/src/decorators/class/IWebConfigClass'; +import {WebConfigClassBuilder} from '../../../../../../node_modules/typeconfig/src/decorators/builders/WebConfigClassBuilder'; interface ConfigState { value: T; original: T; default: T; readonly: boolean; - onChange: ()=>unknown; + tags: TAGS; + onChange: () => unknown; isEnumType: boolean; isConfigType: boolean; + isConfigArrayType: boolean; + toJSON: () => T; } -interface RecursiveState extends ConfigState { +export interface RecursiveState extends ConfigState { + shouldHide: any; + volatile: any; + tags: any; + isConfigType: any; + isConfigArrayType: any; + onChange: any; + isEnumType: any; value: any; original: any; default: any; readonly: any; - onChange: any; - isEnumType: any; - isConfigType: any; + toJSON: any; [key: string]: RecursiveState; } + @Directive() export abstract class SettingsComponentDirective< - T extends { [key: string]: any }, + T extends RecursiveState, S extends AbstractSettingsService = AbstractSettingsService -> implements OnInit, OnDestroy, OnChanges, ISettingsComponent -{ +> implements OnInit, OnDestroy, OnChanges, ISettingsComponent { @Input() - public simplifiedMode = true; + public configPriority = ConfigPriority.basic; - @ViewChild('settingsForm', { static: true }) + @ViewChild('settingsForm', {static: true}) form: FormControl; @Output() @@ -62,18 +77,25 @@ export abstract class SettingsComponentDirective< public states: RecursiveState = {} as RecursiveState; private subscription: Subscription = null; - private readonly settingsSubscription: Subscription = null; + private settingsSubscription: Subscription = null; + protected sliceFN?: (s: WebConfig) => T; protected constructor( - private name: string, + protected name: string, public icon: string, protected authService: AuthenticationService, private navigation: NavigationService, public settingsService: S, protected notification: NotificationService, - private sliceFN?: (s: WebConfig) => T + protected globalSettingsService: SettingsService, + sliceFN?: (s: IWebConfigClassPrivate & WebConfig) => T ) { - if (this.sliceFN) { + this.setSliceFN(sliceFN); + } + + setSliceFN(sliceFN?: (s: IWebConfigClassPrivate & WebConfig) => T) { + if (sliceFN) { + this.sliceFN = sliceFN; this.settingsSubscription = this.settingsService.Settings.subscribe( this.onNewSettings ); @@ -90,56 +112,73 @@ export abstract class SettingsComponentDirective< } get HasAvailableSettings(): boolean { - return this.hasAvailableSettings; + return !this.states?.shouldHide || !this.states?.shouldHide(); } onNewSettings = (s: WebConfig) => { - this.states = Utils.clone(this.sliceFN(s.State) as any); - const addOriginal = (obj: any) => { - for (const k of Object.keys(obj)) { - if (typeof obj[k].value === 'undefined') { - if (typeof obj[k] === 'object') { - addOriginal(obj[k]); + this.states = this.sliceFN(s.clone()) as RecursiveState; + const instrument = (st: RecursiveState, parent: RecursiveState) => { + const shouldHide = (state: RecursiveState) => { + return () => { + if (state.volatile) { + return true; + } + if (state.tags && + ((state.tags.relevant && !state.tags.relevant(parent.value)) + || state.tags.secret)) { + return true; } - continue; - } - obj[k].original = Utils.clone(obj[k].value); - obj[k].onChange = this.onOptionChange; + // if all sub elements are hidden, hide the parent too. + if (state.isConfigType) { + if (Object.keys(state.value.__state).findIndex(k => !st.value.__state[k].shouldHide()) === -1) { + return true; + } + } + + + if (state.isConfigArrayType) { + for (let i = 0; i < state.value?.length; ++i) { + if (Object.keys(state.value[i].__state).findIndex(k => !(st.value[i].__state[k].shouldHide && st.value[i].__state[k].shouldHide())) === -1) { + return true; + } + } + return false; + } + return ( + state.tags?.priority > this.globalSettingsService.configPriority && + Utils.equalsFilter(state.value, state.default, + ['__propPath', '__created', '__prototype', '__rootConfig']) && + Utils.equalsFilter(state.original, state.default, + ['__propPath', '__created', '__prototype', '__rootConfig']) + ); + }; + }; + + st.shouldHide = shouldHide(st); + st.onChange = this.onOptionChange; + st.rootConfig = parent?.value; + if (typeof st.value !== 'undefined') { + st.original = Utils.clone(st.value); + } + if (st.isConfigType) { + for (const k of Object.keys(st.value.__state)) { + instrument(st.value.__state[k], st); + } + } + if (st.isConfigArrayType) { + for (let i = 0; i < st.value?.length; ++i) { + for (const k of Object.keys(st.value[i].__state)) { + instrument(st.value[i].__state[k], st); + } + } } }; - addOriginal(this.states); + instrument(this.states, null); + this.ngOnChanges(); }; - settingsSame(newSettings: T, original: T): boolean { - if (typeof original !== 'object' || original == null) { - return newSettings === original; - } - if (!newSettings) { - return false; - } - if (Array.isArray(original) && original.length !== newSettings.length) { - return false; - } - const keys = Object.keys(newSettings); - for (const key of keys) { - if (typeof original[key] === 'undefined') { - console.warn('unknown settings: ' + key); - return false; - } - if (typeof original[key] === 'object') { - if (this.settingsSame(newSettings[key], original[key]) === false) { - return false; - } - } else if (newSettings[key] !== original[key]) { - return false; - } - } - - return true; - } - onOptionChange = () => { setTimeout(() => { const settingsSame = (state: RecursiveState): boolean => { @@ -147,7 +186,8 @@ export abstract class SettingsComponentDirective< return true; } if (typeof state.original === 'object') { - return Utils.equalsFilter(state.original, state.value); + return Utils.equalsFilter(state.value, state.original, + ['__propPath', '__created', '__prototype', '__rootConfig', '__state']); } if (typeof state.original !== 'undefined') { return state.value === state.original; @@ -185,10 +225,6 @@ export abstract class SettingsComponentDirective< } ngOnChanges(): void { - this.hasAvailableSettings = - (this.settingsService.isSupported() && - this.settingsService.showInSimplifiedMode()) || - !this.simplifiedMode; } ngOnDestroy(): void { @@ -205,25 +241,7 @@ export abstract class SettingsComponentDirective< } stateToSettings(): T { - const ret: T = {} as T; - - const add = (obj: Record, to: Record): void => { - for (const key of Object.keys(obj)) { - to[key] = {} as RecursiveState; - if ( - obj[key].isConfigType || - (typeof obj[key] === 'object' && - typeof obj[key].value === 'undefined') - ) { - add(obj[key], to[key]); - continue; - } - to[key] = obj[key].value; - } - }; - add(this.states, ret); - - return ret; + return WebConfigClassBuilder.attachInterface(this.states.value).toJSON(); } public async save(): Promise { diff --git a/src/frontend/app/ui/settings/_abstract/abstract.settings.service.ts b/src/frontend/app/ui/settings/_abstract/abstract.settings.service.ts index 9f8db8a3..2e589bd4 100644 --- a/src/frontend/app/ui/settings/_abstract/abstract.settings.service.ts +++ b/src/frontend/app/ui/settings/_abstract/abstract.settings.service.ts @@ -13,13 +13,6 @@ export abstract class AbstractSettingsService { return this.settingsService.getSettings(); } - public showInSimplifiedMode(): boolean { - return true; - } - - isSupported(): boolean { - return true; - } abstract updateSettings(settings: T): Promise; } diff --git a/src/frontend/app/ui/settings/_abstract/settings-entry/settings-entry.component.html b/src/frontend/app/ui/settings/_abstract/settings-entry/settings-entry.component.html index 273c7f35..9e94116c 100644 --- a/src/frontend/app/ui/settings/_abstract/settings-entry/settings-entry.component.html +++ b/src/frontend/app/ui/settings/_abstract/settings-entry/settings-entry.component.html @@ -16,28 +16,39 @@ - +
+ +
+ {{state.tags?.unit}} +
+
@@ -58,7 +69,7 @@ [id]="idName" [name]="idName" [title]="title" - [disabled]="state.readonly || disabled" + [disabled]="state.readonly || Disabled" switch-on-color="primary" [switch-inverse]="true" switch-off-text="Disabled" @@ -71,6 +82,158 @@ [(ngModel)]="state.value"> + +
+ + + + + + + + + + + + + +
NameTile Url*
+ + + +
+ +
+ +
+
+
+ + +
+
+
+ +
+
+ +
+
+ + + + +
+
+ +
+ +
+
+
+ +
+
+
+
+ + +
+
+
+ +
+
+ +
+
+ +
+
+ +
+ +
+
+
+ +
+
+
+
+
@@ -80,9 +243,9 @@ [name]="'list_'+idName+i" [title]="title" (ngModelChange)="onChange($event)" - [disabled]="state.readonly || disabled" + [disabled]="state.readonly || Disabled" class="form-select" [(ngModel)]="state.value[i]"> - @@ -118,6 +281,7 @@
{{description}} + ';' separated list. {{linkText || link}} diff --git a/src/frontend/app/ui/settings/_abstract/settings-entry/settings-entry.component.ts b/src/frontend/app/ui/settings/_abstract/settings-entry/settings-entry.component.ts index 00d67f20..6ef0bda7 100644 --- a/src/frontend/app/ui/settings/_abstract/settings-entry/settings-entry.component.ts +++ b/src/frontend/app/ui/settings/_abstract/settings-entry/settings-entry.component.ts @@ -1,4 +1,4 @@ -import { Component, forwardRef, Input, OnChanges } from '@angular/core'; +import {Component, forwardRef, Input, OnChanges} from '@angular/core'; import { ControlValueAccessor, FormControl, @@ -7,22 +7,38 @@ import { ValidationErrors, Validator, } from '@angular/forms'; -import { Utils } from '../../../../../../common/Utils'; -import { propertyTypes } from 'typeconfig/common'; -import { SearchQueryParserService } from '../../../gallery/search/search-query-parser.service'; +import {Utils} from '../../../../../../common/Utils'; +import {IConfigClass, propertyTypes} from 'typeconfig/common'; +import {SearchQueryParserService} from '../../../gallery/search/search-query-parser.service'; +import { + ClientThumbnailConfig, + ConfigPriority, + MapLayers, + NavigationLinkConfig, NavigationLinkTypes, + TAGS +} from '../../../../../../common/config/public/ClientConfig'; +import {SettingsService} from '../../settings.service'; +import {WebConfig} from '../../../../../../common/config/private/WebConfig'; +import {UserConfig} from '../../../../../../common/config/private/PrivateConfig'; interface IState { + shouldHide(): boolean; + isEnumArrayType: boolean; isEnumType: boolean; isConfigType: boolean; + isConfigArrayType: boolean; default: any; value: any; min?: number; max?: number; type: propertyTypes; - arrayType: propertyTypes; + arrayType: any; original: any; + rootConfig: WebConfig; readonly?: boolean; + description?: string; + tags: TAGS; } @Component({ @@ -44,66 +60,64 @@ interface IState { ], }) export class SettingsEntryComponent - implements ControlValueAccessor, Validator, OnChanges -{ - @Input() name: string; + implements ControlValueAccessor, Validator, OnChanges { + @Input() defaultName: string; + name: string; @Input() required: boolean; @Input() dockerWarning: boolean; - @Input() optionMap: (v: { key: number; value: string }) => { - key: number; - value: string; - }; @Input() placeholder: string; - @Input() options: { key: number | string; value: number | string }[]; - @Input() simplifiedMode = false; @Input() allowSpaces = false; @Input() description: string; @Input() link: string; @Input() linkText: string; - @Input() typeOverride: 'searchQuery'; + @Input() typeOverride: 'SearchQuery'; state: IState; isNumberArray = false; isNumber = false; type = 'text'; - optionsView: { key: number | string; value: string | number }[] = []; title: string; idName: string; - disabled: boolean; private readonly GUID = Utils.GUID(); - private singleValueWrapper: { set: (value: any) => void; get: () => any }; + public NavigationLinkTypesEnum = Utils.enumToArray(NavigationLinkTypes); + NavigationLinkTypes = NavigationLinkTypes; - constructor(private searchQueryParserService: SearchQueryParserService) {} + constructor(private searchQueryParserService: SearchQueryParserService, + public settingsService: SettingsService, + ) { + } get changed(): boolean { - if (this.disabled) { + if (this.Disabled) { + return false; + } + if (this.state.isConfigArrayType) { + for (let i = 0; i < this.state.value?.length; ++i) { + for (const k of Object.keys(this.state.value[i].__state)) { + if (!Utils.equalsFilter( + this.state.value[i]?.__state[k]?.value, + this.state.default[i]?.__state[k]?.value, + ['__propPath', '__created', '__prototype', '__rootConfig'])) { + return true; + } + } + } return false; } return !Utils.equalsFilter(this.state.value, this.state.default); } get shouldHide(): boolean { - if (Array.isArray(this.state.value)) { - return ( - this.simplifiedMode && - Utils.equalsFilter(this.state.value, this.state.default) && - Utils.equalsFilter(this.state.original, this.state.default) - ); - } - return ( - this.simplifiedMode && - this.state.value === this.state.default && - this.state.original === this.state.default - ); + return this.state.shouldHide && this.state.shouldHide(); } get PlaceHolder(): string { - return this.placeholder || this.state.default; + return this.placeholder || this.state.tags?.hint || this.state.default; } get defaultStr(): string { - if (this.typeOverride === 'searchQuery') { + if (this.Type === 'SearchQuery') { return ( - "'" + this.searchQueryParserService.stringify(this.state.default) + "'" + '\'' + this.searchQueryParserService.stringify(this.state.default) + '\'' ); } @@ -114,28 +128,49 @@ export class SettingsEntryComponent return this.state.default; } - get Type(): any { - return this.typeOverride || this.state.type; + get Type(): string | object { + return this.typeOverride || this.state.tags?.uiType || this.state.type; } - get StringValue(): any { + get ArrayType(): string { + if (this.state.arrayType === MapLayers) { + return 'MapLayers'; + } + if (this.state.arrayType === NavigationLinkConfig) { + return 'NavigationLinkConfig'; + } + if (this.state.arrayType === UserConfig) { + return 'UserConfig'; + } + this.state.arrayType; + } + + get StringValue(): string { if ( this.state.type === 'array' && (this.state.arrayType === 'string' || this.isNumberArray) ) { - return this.state.value.join(';'); + return (this.state.value || []).join(';'); + } + + if (this.state.isConfigType) { + return JSON.stringify(this.state.value.toJSON()); + } + + if (typeof this.state.value === 'object') { + return JSON.stringify(this.state.value); } return this.state.value; } - set StringValue(value: any) { + set StringValue(value: string) { if ( this.state.type === 'array' && (this.state.arrayType === 'string' || this.isNumberArray) ) { value = value.replace(new RegExp(',', 'g'), ';'); - if (this.allowSpaces === false) { + if (!this.allowSpaces) { value = value.replace(new RegExp(' ', 'g'), ';'); } this.state.value = value.split(';').filter((v: string) => v !== ''); @@ -146,21 +181,31 @@ export class SettingsEntryComponent } return; } + + if (typeof this.state.value === 'object') { + this.state.value = JSON.parse(value); + } + this.state.value = value; if (this.isNumber) { this.state.value = parseFloat(value); } + } - setDisabledState?(isDisabled: boolean): void { - this.disabled = isDisabled; + + get Disabled() { + if (!this.state?.tags?.uiDisabled) { + return false; + } + return this.state.tags.uiDisabled(this.state.rootConfig, this.settingsService.settings.value); } ngOnChanges(): void { if (!this.state) { return; } - if (this.options) { + if (this.state.tags?.uiOptions) { this.state.isEnumType = true; } this.title = ''; @@ -168,6 +213,7 @@ export class SettingsEntryComponent this.title = $localize`readonly` + ', '; } this.title += $localize`default value` + ': ' + this.defaultStr; + this.name = this.state?.tags?.name || this.defaultName; if (this.name) { this.idName = this.GUID + this.name.toLowerCase().replace(new RegExp(' ', 'gm'), '-'); @@ -182,20 +228,7 @@ export class SettingsEntryComponent this.state.type === 'integer' || this.state.type === 'float' || this.state.type === 'positiveFloat'; - const eClass = this.state.isEnumType - ? this.state.type - : this.state.arrayType; - if (this.state.isEnumType || this.state.isEnumArrayType) { - if (this.options) { - this.optionsView = this.options; - } else { - if (this.optionMap) { - this.optionsView = Utils.enumToArray(eClass).map(this.optionMap); - } else { - this.optionsView = Utils.enumToArray(eClass); - } - } - } + if (this.isNumber) { this.type = 'number'; @@ -204,9 +237,38 @@ export class SettingsEntryComponent } else { this.type = 'text'; } + this.description = this.description || this.state.description; + if (this.state.tags) { + if (typeof this.dockerWarning === 'undefined') { + this.dockerWarning = this.state.tags.dockerSensitive && this.settingsService.settings.value.Environment.isDocker; + } + if (this.state.tags.githubIssue) { + this.link = `https://github.com/bpatrik/pigallery2/issues/` + this.state.tags.githubIssue; + this.linkText = $localize`See` + ' ' + this.state.tags.githubIssue; + } + this.name = this.name || this.state.tags.name; + this.allowSpaces = this.allowSpaces || this.state.tags.uiAllowSpaces; + this.required = this.required || !this.state.tags.uiOptional; + } } - validate(control: FormControl): ValidationErrors { + getOptionsView(state: IState) { + let optionsView: { key: number | string; value: string | number }[]; + const eClass = state.isEnumType + ? state.type + : state.arrayType; + if (state.tags?.uiOptions) { + optionsView = state.tags?.uiOptions.map(o => ({ + key: o, + value: o + (state.tags?.unit ? state.tags?.unit : '') + })); + } else { + optionsView = Utils.enumToArray(eClass); + } + return optionsView; + } + + validate(): ValidationErrors { if ( !this.required || (this.state && @@ -216,37 +278,64 @@ export class SettingsEntryComponent ) { return null; } - return { required: true }; + return {required: true}; } - // eslint-disable-next-line @typescript-eslint/no-empty-function - public onChange(value: any): void {} + public onChange = (value: unknown): void => { + // empty + }; - // eslint-disable-next-line @typescript-eslint/no-empty-function - public onTouched(): void {} + public onTouched = (): void => { + // empty + }; public writeValue(obj: IState): void { this.state = obj; this.ngOnChanges(); } - public registerOnChange(fn: any): void { + public registerOnChange(fn: (v: never) => void): void { this.onChange = fn; } - public registerOnTouched(fn: any): void { + public registerOnTouched(fn: () => void): void { this.onTouched = fn; } AddNew(): void { + this.state.value = this.state.value || []; if (this.state.type === 'array') { + if (this.state.isConfigArrayType && this.state.arrayType) { + this.state.value.push(new this.state.arrayType()); + return; + } this.state.value.push(this.state.value[this.state.value.length - 1]); } + console.dir(this.state.value); } remove(i: number): void { - (this.state.value as any[]).splice(i, 1); + (this.state.value as unknown[]).splice(i, 1); } + + /** + * MapLayer function + */ + addNewLayer(): void { + this.state.value.push({ + name: 'Layer-' + this.state.value.length, + url: '', + }); + } + + removeLayer(layer: MapLayers): void { + this.state.value.splice( + this.state.value.indexOf(layer), + 1 + ); + } + + } diff --git a/src/frontend/app/ui/settings/albums/albums.settings.component.ts b/src/frontend/app/ui/settings/albums/albums.settings.component.ts index 9d328aa6..db404670 100644 --- a/src/frontend/app/ui/settings/albums/albums.settings.component.ts +++ b/src/frontend/app/ui/settings/albums/albums.settings.component.ts @@ -1,10 +1,11 @@ -import { Component } from '@angular/core'; -import { AlbumsSettingsService } from './albums.settings.service'; -import { SettingsComponentDirective } 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 { ClientAlbumConfig } from '../../../../../common/config/public/ClientConfig'; +import {Component} from '@angular/core'; +import {AlbumsSettingsService} from './albums.settings.service'; +import {SettingsComponentDirective} 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 {ClientAlbumConfig} from '../../../../../common/config/public/ClientConfig'; +import {SettingsService} from '../settings.service'; @Component({ selector: 'app-settings-albums', @@ -20,7 +21,8 @@ export class AlbumsSettingsComponent extends SettingsComponentDirective s.Client.Album + globalSettingsService, + (s) => s.Album ); } } diff --git a/src/frontend/app/ui/settings/albums/albums.settings.service.ts b/src/frontend/app/ui/settings/albums/albums.settings.service.ts index f233e928..30208faf 100644 --- a/src/frontend/app/ui/settings/albums/albums.settings.service.ts +++ b/src/frontend/app/ui/settings/albums/albums.settings.service.ts @@ -14,10 +14,10 @@ export class AlbumsSettingsService extends AbstractSettingsService -
-
- {{Name}} -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- - - diff --git a/src/frontend/app/ui/settings/basic/basic.settings.component.ts b/src/frontend/app/ui/settings/basic/basic.settings.component.ts deleted file mode 100644 index 219a02fe..00000000 --- a/src/frontend/app/ui/settings/basic/basic.settings.component.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { Component } from '@angular/core'; -import { SettingsComponentDirective } 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 { BasicSettingsService } from './basic.settings.service'; -import { - BasicConfigDTO, - BasicConfigDTOUtil, -} from '../../../../../common/entities/settings/BasicConfigDTO'; - -@Component({ - selector: 'app-settings-basic', - templateUrl: './basic.settings.component.html', - styleUrls: [ - './basic.settings.component.css', - '../_abstract/abstract.settings.component.css', - ], - providers: [BasicSettingsService], -}) -export class BasicSettingsComponent extends SettingsComponentDirective { - urlPlaceholder = location.origin; - urlBaseChanged = false; - urlError = false; - - constructor( - authService: AuthenticationService, - navigation: NavigationService, - settingsService: BasicSettingsService, - notification: NotificationService - ) { - super( - $localize`Basic`, - 'star', - authService, - navigation, - settingsService, - notification, - BasicConfigDTOUtil.mapToDTO - ); - this.checkUrlError(); - } - - public async save(): Promise { - const val = await super.save(); - if (val === true) { - this.notification.info( - $localize`Restart the server to apply the new settings` - ); - } - return val; - } - - calcBaseUrl(): string { - const url = this.states.publicUrl.value - .replace(new RegExp('\\\\', 'g'), '/') - .replace(new RegExp('http://', 'g'), '') - .replace(new RegExp('https://', 'g'), ''); - if (url.indexOf('/') !== -1) { - return url.substring(url.indexOf('/')); - } - return ''; - } - - checkUrlError(): void { - this.urlError = this.states.urlBase.value !== this.calcBaseUrl(); - } - - onUrlChanged(): void { - if (this.urlBaseChanged === false) { - this.states.urlBase.value = this.calcBaseUrl(); - } else { - this.checkUrlError(); - } - } - - onUrlBaseChanged = () => { - this.urlBaseChanged = true; - - this.checkUrlError(); - }; -} - - - diff --git a/src/frontend/app/ui/settings/database/database.settings.component.html b/src/frontend/app/ui/settings/database/database.settings.component.html index 757e2eb7..c79e34be 100644 --- a/src/frontend/app/ui/settings/database/database.settings.component.html +++ b/src/frontend/app/ui/settings/database/database.settings.component.html @@ -23,7 +23,7 @@ description="All file-based data will be stored here (sqlite database, user database in case of memory db, job history data)" [ngModel]="states.dbFolder" i18n-name i18n-description - [dockerWarning]="(settingsService.Settings | async).Server.Environment.isDocker" + [dockerWarning]="(settingsService.Settings | async).Environment.isDocker" [required]="true"> 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 728df03e..e0118766 100644 --- a/src/frontend/app/ui/settings/database/database.settings.component.ts +++ b/src/frontend/app/ui/settings/database/database.settings.component.ts @@ -1,14 +1,15 @@ -import { Component, OnInit } from '@angular/core'; -import { AuthenticationService } from '../../../model/network/authentication.service'; -import { Utils } from '../../../../../common/Utils'; -import { NotificationService } from '../../../model/notification.service'; -import { NavigationService } from '../../../model/navigation.service'; -import { SettingsComponentDirective } from '../_abstract/abstract.settings.component'; -import { DatabaseSettingsService } from './database.settings.service'; +import {Component, OnInit} from '@angular/core'; +import {AuthenticationService} from '../../../model/network/authentication.service'; +import {Utils} from '../../../../../common/Utils'; +import {NotificationService} from '../../../model/notification.service'; +import {NavigationService} from '../../../model/navigation.service'; +import {SettingsComponentDirective} from '../_abstract/abstract.settings.component'; +import {DatabaseSettingsService} from './database.settings.service'; import { DatabaseType, ServerDataBaseConfig, } from '../../../../../common/config/private/PrivateConfig'; +import {SettingsService} from '../settings.service'; @Component({ selector: 'app-settings-database', @@ -21,8 +22,7 @@ import { }) export class DatabaseSettingsComponent extends SettingsComponentDirective - implements OnInit -{ + implements OnInit { public types = Utils.enumToArray(DatabaseType); public DatabaseType = DatabaseType; @@ -30,7 +30,8 @@ export class DatabaseSettingsComponent authService: AuthenticationService, navigation: NavigationService, settingsService: DatabaseSettingsService, - notification: NotificationService + notification: NotificationService, + globalSettingsService: SettingsService ) { super( $localize`Database`, @@ -39,7 +40,8 @@ export class DatabaseSettingsComponent navigation, settingsService, notification, - (s) => s.Server.Database + globalSettingsService, + (s) => s.Database ); } 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 576e630d..78bfc916 100644 --- a/src/frontend/app/ui/settings/faces/faces.settings.component.ts +++ b/src/frontend/app/ui/settings/faces/faces.settings.component.ts @@ -1,12 +1,13 @@ -import { Component } from '@angular/core'; -import { SettingsComponentDirective } 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 { FacesSettingsService } from './faces.settings.service'; -import { Utils } from '../../../../../common/Utils'; -import { UserRoles } from '../../../../../common/entities/UserDTO'; -import { ClientFacesConfig } from '../../../../../common/config/public/ClientConfig'; +import {Component} from '@angular/core'; +import {SettingsComponentDirective} 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 {FacesSettingsService} from './faces.settings.service'; +import {Utils} from '../../../../../common/Utils'; +import {UserRoles} from '../../../../../common/entities/UserDTO'; +import {ClientFacesConfig} from '../../../../../common/config/public/ClientConfig'; +import {SettingsService} from '../settings.service'; @Component({ selector: 'app-settings-faces', @@ -27,7 +28,8 @@ export class FacesSettingsComponent extends SettingsComponentDirective s.Client.Faces + globalSettingsService, + (s) => s.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 5047f589..c8e96ffb 100644 --- a/src/frontend/app/ui/settings/faces/faces.settings.service.ts +++ b/src/frontend/app/ui/settings/faces/faces.settings.service.ts @@ -14,15 +14,15 @@ export class FacesSettingsService extends AbstractSettingsService - + - implements OnInit, OnDestroy -{ + implements OnInit, OnDestroy { types: { key: number; value: string }[] = []; JobProgressStates = JobProgressStates; readonly indexingJobName = DefaultsJobs[DefaultsJobs.Indexing]; @@ -46,7 +46,8 @@ export class IndexingSettingsComponent navigation: NavigationService, settingsService: IndexingSettingsService, public jobsService: ScheduledJobsService, - notification: NotificationService + notification: NotificationService, + globalSettingsService: SettingsService ) { super( $localize`Folder indexing`, @@ -55,18 +56,19 @@ export class IndexingSettingsComponent navigation, settingsService, notification, - (s) => s.Server.Indexing + globalSettingsService, + (s) => s.Indexing ); } get Config(): any { - return { indexChangesOnly: true }; + return {indexChangesOnly: true}; } get Progress(): JobProgressDTO { return this.jobsService.progress.value[ JobDTOUtils.getHashName(DefaultsJobs[DefaultsJobs.Indexing], this.Config) - ]; + ]; } ngOnDestroy(): void { 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 e022299e..c6e856a6 100644 --- a/src/frontend/app/ui/settings/indexing/indexing.settings.service.ts +++ b/src/frontend/app/ui/settings/indexing/indexing.settings.service.ts @@ -46,7 +46,7 @@ export class IndexingSettingsService extends AbstractSettingsService s.Server.Jobs + globalSettingsService, + (s) => s.Jobs ); - this.hasAvailableSettings = !this.simplifiedMode; + this.hasAvailableSettings = this.configPriority > ConfigPriority.basic; this.JobTriggerTypeMap = [ {key: JobTriggerType.after, value: $localize`after`}, {key: JobTriggerType.never, value: $localize`never`}, 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 705fb438..f0c43d2d 100644 --- a/src/frontend/app/ui/settings/jobs/jobs.settings.service.ts +++ b/src/frontend/app/ui/settings/jobs/jobs.settings.service.ts @@ -22,7 +22,7 @@ export class JobsSettingsService extends AbstractSettingsService
{{TimeElapsed| duration:':'}}
-
{{TimeAll| duration:':'}}
diff --git a/src/frontend/app/ui/settings/map/map.settings.component.html b/src/frontend/app/ui/settings/map/map.settings.component.html index f308cd2a..9cabfbc3 100644 --- a/src/frontend/app/ui/settings/map/map.settings.component.html +++ b/src/frontend/app/ui/settings/map/map.settings.component.html @@ -28,8 +28,7 @@ name="Use image markers" description="Map will use thumbnail images as markers instead of the default pin." i18n-name i18n-description="" - [ngModel]="states.useImageMarkers" - [simplifiedMode]="simplifiedMode"> + [ngModel]="states.useImageMarkers"> @@ -37,7 +36,6 @@ name="Map provider" i18n-name [ngModel]="states.mapProvider" - [simplifiedMode]="simplifiedMode" [required]="true"> 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 f49e4e2b..54414c93 100644 --- a/src/frontend/app/ui/settings/map/map.settings.component.ts +++ b/src/frontend/app/ui/settings/map/map.settings.component.ts @@ -1,15 +1,16 @@ -import { Component } from '@angular/core'; -import { MapSettingsService } from './map.settings.service'; -import { SettingsComponentDirective } 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 { Utils } from '../../../../../common/Utils'; +import {Component} from '@angular/core'; +import {MapSettingsService} from './map.settings.service'; +import {SettingsComponentDirective} 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 {Utils} from '../../../../../common/Utils'; import { ClientMapConfig, MapLayers, MapProviders, } from '../../../../../common/config/public/ClientConfig'; +import {SettingsService} from '../settings.service'; @Component({ selector: 'app-settings-map', @@ -28,7 +29,8 @@ export class MapSettingsComponent extends SettingsComponentDirective s.Client.Map + globalSettingsService, + (s) => s.Map ); this.mapProviders = Utils.enumToArray(MapProviders); 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 a8e7874b..81235607 100644 --- a/src/frontend/app/ui/settings/map/map.settings.service.ts +++ b/src/frontend/app/ui/settings/map/map.settings.service.ts @@ -13,7 +13,7 @@ export class MapSettingsService extends AbstractSettingsService super(settingsService); } - showInSimplifiedMode(): boolean { + hasAvailableSettings(): boolean { return false; } diff --git a/src/frontend/app/ui/settings/metafiles/metafile.settings.component.html b/src/frontend/app/ui/settings/metafiles/metafile.settings.component.html index 0db0488d..5e3585c8 100644 --- a/src/frontend/app/ui/settings/metafiles/metafile.settings.component.html +++ b/src/frontend/app/ui/settings/metafiles/metafile.settings.component.html @@ -11,14 +11,14 @@ name="Markdown files" description="Reads *.md files in a directory and shows the next to the map." i18n-description i18n-name - [ngModel]="states.client.markdown"> + [ngModel]="states.markdown"> + [ngModel]="states.pg2conf">
@@ -27,8 +27,8 @@ name="*.gpx files" description="Reads *.gpx files and renders them on the map." i18n-description i18n-name - [disabled]="!(settingsService.Settings | async).Client.Map.enabled" - [ngModel]="states.client.gpx"> + [disabled]="!(settingsService.Settings | async).Map.enabled" + [ngModel]="states.gpx"> + [disabled]="!(settingsService.Settings | async).Map.enabled || !states.gpx.value" + [ngModel]="states.GPXCompressing.enabled"> @@ -61,7 +60,7 @@ (click)="reset()" i18n>Reset -
+
{ +export class MetaFileSettingsComponent extends SettingsComponentDirective { constructor( authService: AuthenticationService, navigation: NavigationService, settingsService: MetaFileSettingsService, notification: NotificationService, public jobsService: ScheduledJobsService, + globalSettingsService: SettingsService ) { super( $localize`Meta file`, @@ -37,15 +36,12 @@ export class MetaFileSettingsComponent extends SettingsComponentDirective<{ navigation, settingsService, notification, - (s) => ({ - client: s.Client.MetaFile, - server: s.Server.MetaFile, - }) + globalSettingsService, + (s) => (s.MetaFile) ); } - readonly jobName = DefaultsJobs[DefaultsJobs['GPX Compression']]; 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 7f1b8220..9ec73a32 100644 --- a/src/frontend/app/ui/settings/metafiles/metafile.settings.service.ts +++ b/src/frontend/app/ui/settings/metafiles/metafile.settings.service.ts @@ -6,10 +6,7 @@ import {ClientMetaFileConfig} from '../../../../../common/config/public/ClientCo import {ServerMetaFileConfig} from '../../../../../common/config/private/PrivateConfig'; @Injectable() -export class MetaFileSettingsService extends AbstractSettingsService<{ - server: ServerMetaFileConfig; - client: ClientMetaFileConfig; -}> { +export class MetaFileSettingsService extends AbstractSettingsService { constructor( private networkService: NetworkService, settingsService: SettingsService @@ -18,17 +15,14 @@ export class MetaFileSettingsService extends AbstractSettingsService<{ } public isSupported(): boolean { - return this.settingsService.settings.value.Client.Map.enabled === true; + return this.settingsService.settings.value.Map.enabled === true; } - showInSimplifiedMode(): boolean { + hasAvailableSettings(): boolean { return false; } - public updateSettings(settings: { - server: ServerMetaFileConfig; - client: ClientMetaFileConfig; - }): Promise { + public updateSettings(settings: ServerMetaFileConfig): Promise { return this.networkService.putJson('/settings/metafile', {settings}); } } diff --git a/src/frontend/app/ui/settings/other/other.settings.component.html b/src/frontend/app/ui/settings/other/other.settings.component.html index c1196b7a..263d0cbd 100644 --- a/src/frontend/app/ui/settings/other/other.settings.component.html +++ b/src/frontend/app/ui/settings/other/other.settings.component.html @@ -24,7 +24,6 @@ i18n-description i18n-name [ngModel]="states.Server.thumbnailThreads" [options]="threads" - [simplifiedMode]="simplifiedMode || states.Server.enabled.value == false" [required]="true"> @@ -85,7 +84,6 @@ description="Show the number of items (photos) in the folder." i18n-description i18n-name [ngModel]="states.Client.NavBar.showItemCount" - [simplifiedMode]="simplifiedMode" [required]="true"> @@ -95,7 +93,6 @@ i18n-name [ngModel]="states.Client.defaultPhotoSortingMethod" [optionMap]="sortingMap" - [simplifiedMode]="simplifiedMode" [required]="true"> @@ -106,7 +103,6 @@ link="https://github.com/bpatrik/pigallery2/issues/566" linkText="See 566." [optionMap]="sortingMap" - [simplifiedMode]="simplifiedMode" [required]="true"> @@ -115,7 +111,6 @@ description="If enabled, directories will be sorted by date, like photos, otherwise by name. Directory date is the last modification time of that directory not the creation date of the oldest photo." i18n-description i18n-name [ngModel]="states.Client.enableDirectorySortingByDate" - [simplifiedMode]="simplifiedMode" [required]="true"> @@ -123,8 +118,7 @@ name="Custom HTML Head" description="Injects the content of this between the HTML tags of the app. (You can use it add analytics or custom code to the app)" [ngModel]="states.Client.customHTMLHead" - i18n-name - [simplifiedMode]="simplifiedMode"> + i18n-name> diff --git a/src/frontend/app/ui/settings/other/other.settings.component.ts b/src/frontend/app/ui/settings/other/other.settings.component.ts index 2da8522e..89c1d60c 100644 --- a/src/frontend/app/ui/settings/other/other.settings.component.ts +++ b/src/frontend/app/ui/settings/other/other.settings.component.ts @@ -1,13 +1,14 @@ -import { Component, OnChanges } from '@angular/core'; -import { SettingsComponentDirective } 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 { OtherSettingsService } from './other.settings.service'; -import { OtherConfigDTO } from '../../../../../common/entities/settings/OtherConfigDTO'; -import { Utils } from '../../../../../common/Utils'; -import { SortingMethods } from '../../../../../common/entities/SortingMethods'; -import { StringifySortingMethod } from '../../../pipes/StringifySortingMethod'; +import {Component, OnChanges} from '@angular/core'; +import {SettingsComponentDirective} 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 {OtherSettingsService} from './other.settings.service'; +import {Utils} from '../../../../../common/Utils'; +import {SortingMethods} from '../../../../../common/entities/SortingMethods'; +import {StringifySortingMethod} from '../../../pipes/StringifySortingMethod'; +import {ConfigPriority} from '../../../../../common/config/public/ClientConfig'; +import {SettingsService} from '../settings.service'; @Component({ selector: 'app-settings-other', @@ -19,13 +20,12 @@ import { StringifySortingMethod } from '../../../pipes/StringifySortingMethod'; providers: [OtherSettingsService], }) export class OtherSettingsComponent - extends SettingsComponentDirective - implements OnChanges -{ + extends SettingsComponentDirective + implements OnChanges { types: { key: number; value: string }[] = []; threads: { key: number; value: string }[] = [ - { key: 0, value: 'auto' }, - ].concat(Utils.createRange(1, 100).map((v) => ({ key: v, value: '' + v }))); + {key: 0, value: 'auto'}, + ].concat(Utils.createRange(1, 100).map((v) => ({key: v, value: '' + v}))); sortingMap: any; constructor( @@ -33,7 +33,8 @@ export class OtherSettingsComponent navigation: NavigationService, settingsService: OtherSettingsService, notification: NotificationService, - private formatter: StringifySortingMethod + private formatter: StringifySortingMethod, + globalSettingsService: SettingsService ) { super( $localize`Other`, @@ -42,9 +43,9 @@ export class OtherSettingsComponent navigation, settingsService, notification, + globalSettingsService, (s) => ({ - Server: s.Server.Threading, - Client: s.Client.Other, + Server: s.Gallery, }) ); this.sortingMap = (v: { key: number; value: string }) => { @@ -52,11 +53,11 @@ export class OtherSettingsComponent return v; }; this.types = Utils.enumToArray(SortingMethods); - this.hasAvailableSettings = !this.simplifiedMode; + this.hasAvailableSettings = this.configPriority > ConfigPriority.basic; } ngOnChanges(): void { - this.hasAvailableSettings = !this.simplifiedMode; + this.hasAvailableSettings = this.configPriority > ConfigPriority.basic; } public async save(): Promise { diff --git a/src/frontend/app/ui/settings/other/other.settings.service.ts b/src/frontend/app/ui/settings/other/other.settings.service.ts index 784ed720..f173bcdf 100644 --- a/src/frontend/app/ui/settings/other/other.settings.service.ts +++ b/src/frontend/app/ui/settings/other/other.settings.service.ts @@ -2,10 +2,9 @@ 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 { OtherConfigDTO } from '../../../../../common/entities/settings/OtherConfigDTO'; @Injectable() -export class OtherSettingsService extends AbstractSettingsService { +export class OtherSettingsService extends AbstractSettingsService { constructor( private networkService: NetworkService, settingsService: SettingsService @@ -13,7 +12,7 @@ export class OtherSettingsService extends AbstractSettingsService { + public updateSettings(settings: any): Promise { return this.networkService.putJson('/settings/other', { settings }); } } diff --git a/src/frontend/app/ui/settings/photo/photo.settings.component.html b/src/frontend/app/ui/settings/photo/photo.settings.component.html index 0b65d25d..9284da1c 100644 --- a/src/frontend/app/ui/settings/photo/photo.settings.component.html +++ b/src/frontend/app/ui/settings/photo/photo.settings.component.html @@ -20,7 +20,6 @@ description="Converts photos on the fly, when they are requested." i18n-description i18n-name [ngModel]="states.server.Converting.onTheFly" - [simplifiedMode]="simplifiedMode" [disabled]="!states.client.Converting.enabled.value"> @@ -39,7 +38,6 @@ description="Enables loading the full resolution image on zoom in the ligthbox (preview)." i18n-description i18n-name [ngModel]="states.client.loadFullImageOnZoom" - [simplifiedMode]="simplifiedMode" [disabled]="!states.client.Converting.enabled.value"> 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 b2c4650f..ee124309 100644 --- a/src/frontend/app/ui/settings/photo/photo.settings.component.ts +++ b/src/frontend/app/ui/settings/photo/photo.settings.component.ts @@ -1,10 +1,10 @@ -import { Component } from '@angular/core'; -import { PhotoSettingsService } from './photo.settings.service'; -import { SettingsComponentDirective } 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 { ScheduledJobsService } from '../scheduled-jobs.service'; +import {Component} from '@angular/core'; +import {PhotoSettingsService} from './photo.settings.service'; +import {SettingsComponentDirective} 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 {ScheduledJobsService} from '../scheduled-jobs.service'; import { DefaultsJobs, JobDTOUtils, @@ -13,8 +13,9 @@ import { JobProgressDTO, JobProgressStates, } from '../../../../../common/entities/job/JobProgressDTO'; -import { ServerPhotoConfig } from '../../../../../common/config/private/PrivateConfig'; -import { ClientPhotoConfig } from '../../../../../common/config/public/ClientConfig'; +import {ServerPhotoConfig} from '../../../../../common/config/private/PrivateConfig'; +import {ClientPhotoConfig} from '../../../../../common/config/public/ClientConfig'; +import {SettingsService} from '../settings.service'; @Component({ selector: 'app-settings-photo', @@ -25,10 +26,7 @@ import { ClientPhotoConfig } from '../../../../../common/config/public/ClientCon ], providers: [PhotoSettingsService], }) -export class PhotoSettingsComponent extends SettingsComponentDirective<{ - server: ServerPhotoConfig; - client: ClientPhotoConfig; -}> { +export class PhotoSettingsComponent extends SettingsComponentDirective { readonly resolutionTypes = [720, 1080, 1440, 2160, 4320]; resolutions: { key: number; value: string }[] = []; @@ -39,7 +37,8 @@ export class PhotoSettingsComponent extends SettingsComponentDirective<{ navigation: NavigationService, settingsService: PhotoSettingsService, public jobsService: ScheduledJobsService, - notification: NotificationService + notification: NotificationService, + globalSettingsService: SettingsService ) { super( $localize`Photo`, @@ -48,13 +47,11 @@ export class PhotoSettingsComponent extends SettingsComponentDirective<{ navigation, settingsService, notification, - (s) => ({ - client: s.Client.Media.Photo, - server: s.Server.Media.Photo, - }) + globalSettingsService, + (s) => s.Media.Photo ); const currentRes = - settingsService.Settings.value.Server.Media.Photo.Converting.resolution; + settingsService.Settings.value.Media.Photo.Converting.resolution; if (this.resolutionTypes.indexOf(currentRes) === -1) { this.resolutionTypes.push(currentRes); } @@ -67,7 +64,7 @@ export class PhotoSettingsComponent extends SettingsComponentDirective<{ get Progress(): JobProgressDTO { return this.jobsService.progress.value[ JobDTOUtils.getHashName(DefaultsJobs[DefaultsJobs['Photo Converting']]) - ]; + ]; } } 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 b8060c07..c7eefb8f 100644 --- a/src/frontend/app/ui/settings/photo/photo.settings.service.ts +++ b/src/frontend/app/ui/settings/photo/photo.settings.service.ts @@ -6,10 +6,7 @@ import { ServerPhotoConfig } from '../../../../../common/config/private/PrivateC import { ClientPhotoConfig } from '../../../../../common/config/public/ClientConfig'; @Injectable() -export class PhotoSettingsService extends AbstractSettingsService<{ - server: ServerPhotoConfig; - client: ClientPhotoConfig; -}> { +export class PhotoSettingsService extends AbstractSettingsService { constructor( private networkService: NetworkService, settingsService: SettingsService @@ -17,10 +14,7 @@ export class PhotoSettingsService extends AbstractSettingsService<{ super(settingsService); } - public updateSettings(settings: { - server: ServerPhotoConfig; - client: ClientPhotoConfig; - }): Promise { + public updateSettings(settings: ServerPhotoConfig): Promise { return this.networkService.putJson('/settings/photo', { settings }); } } diff --git a/src/frontend/app/ui/settings/preview/preview.settings.component.html b/src/frontend/app/ui/settings/preview/preview.settings.component.html index 42ba5bd4..929c13d0 100644 --- a/src/frontend/app/ui/settings/preview/preview.settings.component.html +++ b/src/frontend/app/ui/settings/preview/preview.settings.component.html @@ -11,8 +11,7 @@ description="Filters the sub-folders with this search query" i18n-description i18n-name [ngModel]="states.SearchQuery" - [simplifiedMode]="simplifiedMode" - [typeOverride]="'searchQuery'" + [typeOverride]="'SearchQuery'" [required]="true"> @@ -21,7 +20,6 @@ description="If multiple preview is available sorts them by these methods and selects the first one." i18n-description i18n-name [ngModel]="states.Sorting" - [simplifiedMode]="simplifiedMode" [required]="true"> diff --git a/src/frontend/app/ui/settings/preview/preview.settings.component.ts b/src/frontend/app/ui/settings/preview/preview.settings.component.ts index 8aae8aea..916d122f 100644 --- a/src/frontend/app/ui/settings/preview/preview.settings.component.ts +++ b/src/frontend/app/ui/settings/preview/preview.settings.component.ts @@ -1,19 +1,20 @@ -import { Component, OnInit } from '@angular/core'; -import { SettingsComponentDirective } 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 {Component, OnInit} from '@angular/core'; +import {SettingsComponentDirective} 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 { DefaultsJobs, JobDTOUtils, } from '../../../../../common/entities/job/JobDTO'; -import { ScheduledJobsService } from '../scheduled-jobs.service'; +import {ScheduledJobsService} from '../scheduled-jobs.service'; import { JobProgressDTO, JobProgressStates, } from '../../../../../common/entities/job/JobProgressDTO'; -import { ServerPreviewConfig } from '../../../../../common/config/private/PrivateConfig'; -import { PreviewSettingsService } from './preview.settings.service'; +import {ServerPreviewConfig} from '../../../../../common/config/private/PrivateConfig'; +import {PreviewSettingsService} from './preview.settings.service'; +import {SettingsService} from '../settings.service'; @Component({ selector: 'app-settings-preview', @@ -26,8 +27,7 @@ import { PreviewSettingsService } from './preview.settings.service'; }) export class PreviewSettingsComponent extends SettingsComponentDirective - implements OnInit -{ + implements OnInit { JobProgressStates = JobProgressStates; readonly jobName = DefaultsJobs[DefaultsJobs['Preview Filling']]; readonly resetJobName = DefaultsJobs[DefaultsJobs['Preview Reset']]; @@ -37,7 +37,8 @@ export class PreviewSettingsComponent navigation: NavigationService, settingsService: PreviewSettingsService, notification: NotificationService, - public jobsService: ScheduledJobsService + public jobsService: ScheduledJobsService, + globalSettingsService: SettingsService ) { super( $localize`Preview`, @@ -46,7 +47,8 @@ export class PreviewSettingsComponent navigation, settingsService, notification, - (s) => s.Server.Preview + globalSettingsService, + (s) => s.Preview ); } @@ -57,7 +59,7 @@ export class PreviewSettingsComponent get Progress(): JobProgressDTO { return this.jobsService.progress.value[ JobDTOUtils.getHashName(this.jobName, this.Config) - ]; + ]; } ngOnInit(): void { diff --git a/src/frontend/app/ui/settings/preview/preview.settings.service.ts b/src/frontend/app/ui/settings/preview/preview.settings.service.ts index 466f1378..b7182f44 100644 --- a/src/frontend/app/ui/settings/preview/preview.settings.service.ts +++ b/src/frontend/app/ui/settings/preview/preview.settings.service.ts @@ -13,7 +13,7 @@ export class PreviewSettingsService extends AbstractSettingsService s.Client.RandomPhoto + globalSettingsService, + (s) => s.RandomPhoto ); } } 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 04378c21..9e7de15d 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 @@ -14,13 +14,13 @@ export class RandomPhotoSettingsService extends AbstractSettingsService @@ -39,7 +38,6 @@ description="Maximum number of photos and videos that listed in one search result" i18n-description i18n-name [ngModel]="states.maxMediaResult" - [simplifiedMode]="simplifiedMode" [required]="true"> @@ -47,16 +45,14 @@ name="List directories" description="Search will also return with directories" i18n-description i18n-name - [ngModel]="states.listDirectories" - [simplifiedMode]="simplifiedMode"> + [ngModel]="states.listDirectories"> + [ngModel]="states.listMetafiles"> 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 ebda60b4..ad63fe40 100644 --- a/src/frontend/app/ui/settings/search/search.settings.component.ts +++ b/src/frontend/app/ui/settings/search/search.settings.component.ts @@ -1,10 +1,11 @@ -import { Component } from '@angular/core'; -import { SettingsComponentDirective } 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 { SearchSettingsService } from './search.settings.service'; -import { ClientSearchConfig } from '../../../../../common/config/public/ClientConfig'; +import {Component} from '@angular/core'; +import {SettingsComponentDirective} 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 {SearchSettingsService} from './search.settings.service'; +import {ClientSearchConfig} from '../../../../../common/config/public/ClientConfig'; +import {SettingsService} from '../settings.service'; @Component({ selector: 'app-settings-search', @@ -20,7 +21,8 @@ export class SearchSettingsComponent extends SettingsComponentDirective s.Client.Search + globalSettingsService, + (s) => s.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 1e046083..0ace6960 100644 --- a/src/frontend/app/ui/settings/search/search.settings.service.ts +++ b/src/frontend/app/ui/settings/search/search.settings.service.ts @@ -14,13 +14,13 @@ export class SearchSettingsService extends AbstractSettingsService; private fetchingSettings = false; - constructor(private networkService: NetworkService) { + constructor(private networkService: NetworkService, + private cookieService: CookieService) { this.settings = new BehaviorSubject(new WebConfig()); this.getSettings().catch(console.error); + + if (this.cookieService.check(CookieNames.configPriority)) { + this.configPriority = + parseInt(this.cookieService.get(CookieNames.configPriority)); + + } + } public async getSettings(): Promise { @@ -31,4 +43,14 @@ export class SettingsService { } this.fetchingSettings = false; } + + configPriorityChanged(): void { + // save it for some years + this.cookieService.set( + CookieNames.configPriority, + this.configPriority.toString(), + 365 * 50 + ); + + } } diff --git a/src/frontend/app/ui/settings/share/share.settings.component.html b/src/frontend/app/ui/settings/share/share.settings.component.html index 1e1772a8..de9cd65b 100644 --- a/src/frontend/app/ui/settings/share/share.settings.component.html +++ b/src/frontend/app/ui/settings/share/share.settings.component.html @@ -30,7 +30,6 @@ description="Enables password protected sharing links." i18n-description i18n-name [ngModel]="states.passwordProtected" - [simplifiedMode]="simplifiedMode" [required]="true"> 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 8e27a166..02f8c278 100644 --- a/src/frontend/app/ui/settings/share/share.settings.component.ts +++ b/src/frontend/app/ui/settings/share/share.settings.component.ts @@ -1,11 +1,12 @@ -import { Component, OnInit } from '@angular/core'; -import { SettingsComponentDirective } 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 { ShareSettingsService } from './share.settings.service'; -import { ClientSharingConfig } from '../../../../../common/config/public/ClientConfig'; -import { SharingDTO } from '../../../../../common/entities/SharingDTO'; +import {Component, OnInit} from '@angular/core'; +import {SettingsComponentDirective} 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 {ShareSettingsService} from './share.settings.service'; +import {ClientSharingConfig} from '../../../../../common/config/public/ClientConfig'; +import {SharingDTO} from '../../../../../common/entities/SharingDTO'; +import {SettingsService} from '../settings.service'; @Component({ selector: 'app-settings-share', @@ -18,15 +19,15 @@ import { SharingDTO } from '../../../../../common/entities/SharingDTO'; }) export class ShareSettingsComponent extends SettingsComponentDirective - implements OnInit -{ + implements OnInit { public shares: SharingDTO[] = []; constructor( authService: AuthenticationService, navigation: NavigationService, settingsService: ShareSettingsService, - notification: NotificationService + notification: NotificationService, + globalSettingsService: SettingsService ) { super( $localize`Share`, @@ -35,7 +36,8 @@ export class ShareSettingsComponent navigation, settingsService, notification, - (s) => s.Client.Sharing + globalSettingsService, + (s) => s.Sharing ); } 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 8c3645f5..bb4360d4 100644 --- a/src/frontend/app/ui/settings/share/share.settings.service.ts +++ b/src/frontend/app/ui/settings/share/share.settings.service.ts @@ -15,15 +15,15 @@ export class ShareSettingsService extends AbstractSettingsService { - if (!this.settingsService.settings.value.Client.Sharing.enabled) { + if (!this.settingsService.settings.value.Sharing.enabled) { return Promise.resolve([]); } return this.networkService.getJson('/share/list'); diff --git a/src/frontend/app/ui/settings/basic/basic.settings.component.css b/src/frontend/app/ui/settings/template/template.component.css similarity index 100% rename from src/frontend/app/ui/settings/basic/basic.settings.component.css rename to src/frontend/app/ui/settings/template/template.component.css diff --git a/src/frontend/app/ui/settings/template/template.component.html b/src/frontend/app/ui/settings/template/template.component.html new file mode 100644 index 00000000..c8a0c5bd --- /dev/null +++ b/src/frontend/app/ui/settings/template/template.component.html @@ -0,0 +1,81 @@ +
+
+
+ {{Name}} +
+ + +
+
+
+ + + + + + + +
+ {{Name}} config is not supported with these settings. +
+ + +
+
+ + + + + + + +
+
+
{{rStates?.value.__state[ck].tags?.name || ck}}
+
+
+
+
+
+
+ + +
+
+
+
+
+
+ diff --git a/src/frontend/app/ui/settings/template/template.component.ts b/src/frontend/app/ui/settings/template/template.component.ts new file mode 100644 index 00000000..e8df0bed --- /dev/null +++ b/src/frontend/app/ui/settings/template/template.component.ts @@ -0,0 +1,78 @@ +import {Component, Input, OnInit} from '@angular/core'; +import {MapSettingsService} from '../map/map.settings.service'; +import {TemplateSettingsService} from './template.settings.service'; +import {AuthenticationService} from '../../../model/network/authentication.service'; +import {NavigationService} from '../../../model/navigation.service'; +import {NotificationService} from '../../../model/notification.service'; +import {SettingsComponentDirective} from '../_abstract/abstract.settings.component'; +import {ClientMapConfig} from '../../../../../common/config/public/ClientConfig'; +import {ServerConfig} from '../../../../../common/config/private/PrivateConfig'; +import {SettingsService} from '../settings.service'; +import {WebConfig} from '../../../../../common/config/private/WebConfig'; + + +@Component({ + selector: 'app-settings-template', + templateUrl: './template.component.html', + styleUrls: ['./template.component.css', + '../_abstract/abstract.settings.component.css'], + providers: [TemplateSettingsService], +}) +export class TemplateComponent extends SettingsComponentDirective implements OnInit { + + @Input() ConfigPath: keyof ServerConfig; + @Input() icon: string; + public configKeys: string[] = []; + + constructor( + authService: AuthenticationService, + navigation: NavigationService, + settingsService: TemplateSettingsService, + notification: NotificationService, + globalSettingsService: SettingsService + ) { + super( + `Template`, + '', + authService, + navigation, + settingsService, + notification, + globalSettingsService + ); + + } + + + ngOnInit(): void { + super.ngOnInit(); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + if (!this.ConfigPath) { + this.setSliceFN(c => ({value: c, isConfigType: true, type: WebConfig})); + } else { + this.setSliceFN(c => c.__state[this.ConfigPath]); + } + this.name = this.states.tags?.name || this.ConfigPath; + } + + getKeys(states: any) { + const s = states.value.__state; + return Object.keys(s).sort((a, b) => { + if ((s[a].isConfigType || s[a].isConfigArrayType) !== (s[b].isConfigType || s[b].isConfigArrayType)) { + if (s[a].isConfigType || s[a].isConfigArrayType) { + return 1; + } else { + return -1; + } + } + if (s[a].tags?.priority !== s[b].tags?.priority) { + return s[a].tags?.priority - s[b].tags?.priority; + } + + return (s[a].tags?.name as string || a).localeCompare(s[b].tags?.name || b); + + }); + } + +} diff --git a/src/frontend/app/ui/settings/basic/basic.settings.service.ts b/src/frontend/app/ui/settings/template/template.settings.service.ts similarity index 53% rename from src/frontend/app/ui/settings/basic/basic.settings.service.ts rename to src/frontend/app/ui/settings/template/template.settings.service.ts index 7ab452bb..dbe6bfb4 100644 --- a/src/frontend/app/ui/settings/basic/basic.settings.service.ts +++ b/src/frontend/app/ui/settings/template/template.settings.service.ts @@ -1,11 +1,11 @@ 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 { BasicConfigDTO } from '../../../../../common/entities/settings/BasicConfigDTO'; +import { AbstractSettingsService } from '../_abstract/abstract.settings.service'; +import { ClientMapConfig } from '../../../../../common/config/public/ClientConfig'; @Injectable() -export class BasicSettingsService extends AbstractSettingsService { +export class TemplateSettingsService extends AbstractSettingsService { constructor( private networkService: NetworkService, settingsService: SettingsService @@ -13,7 +13,11 @@ export class BasicSettingsService extends AbstractSettingsService { - return this.networkService.putJson('/settings/basic', { settings }); + hasAvailableSettings(): boolean { + return true; + } + + public updateSettings(settings: ClientMapConfig): Promise { + return this.networkService.putJson('/settings', { settings }); } } diff --git a/src/frontend/app/ui/settings/thumbnail/thumbnail.settings.component.html b/src/frontend/app/ui/settings/thumbnail/thumbnail.settings.component.html index 12f8f2ba..d1fa3d28 100644 --- a/src/frontend/app/ui/settings/thumbnail/thumbnail.settings.component.html +++ b/src/frontend/app/ui/settings/thumbnail/thumbnail.settings.component.html @@ -11,8 +11,7 @@ name="Thumbnail Quality" description="High quality may be slow." i18n-description i18n-name - [ngModel]="states.server.qualityPriority" - [simplifiedMode]="simplifiedMode"> + [ngModel]="states.server.qualityPriority"> @@ -21,7 +20,6 @@ description="Icon size (used on maps)." i18n-description i18n-name [ngModel]="states.client.iconSize" - [simplifiedMode]="simplifiedMode" [required]="true"> @@ -31,7 +29,6 @@ i18n-name placeholder="240; 480" [ngModel]="states.client.thumbnailSizes" - [simplifiedMode]="simplifiedMode" [required]="true"> Size of the thumbnails. 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 506393a3..b8fae8a5 100644 --- a/src/frontend/app/ui/settings/thumbnail/thumbnail.settings.component.ts +++ b/src/frontend/app/ui/settings/thumbnail/thumbnail.settings.component.ts @@ -9,6 +9,7 @@ import {ScheduledJobsService} from '../scheduled-jobs.service'; import {JobProgressDTO, JobProgressStates,} from '../../../../../common/entities/job/JobProgressDTO'; import {ServerThumbnailConfig} from '../../../../../common/config/private/PrivateConfig'; import {ClientThumbnailConfig} from '../../../../../common/config/public/ClientConfig'; +import {SettingsService} from '../settings.service'; @Component({ selector: 'app-settings-thumbnail', @@ -20,10 +21,7 @@ import {ClientThumbnailConfig} from '../../../../../common/config/public/ClientC providers: [ThumbnailSettingsService], }) export class ThumbnailSettingsComponent - extends SettingsComponentDirective<{ - server: ServerThumbnailConfig; - client: ClientThumbnailConfig; - }> + extends SettingsComponentDirective implements OnInit { JobProgressStates = JobProgressStates; readonly jobName = DefaultsJobs[DefaultsJobs['Thumbnail Generation']]; @@ -33,7 +31,8 @@ export class ThumbnailSettingsComponent navigation: NavigationService, settingsService: ThumbnailSettingsService, notification: NotificationService, - public jobsService: ScheduledJobsService + public jobsService: ScheduledJobsService, + globalSettingsService: SettingsService ) { super( $localize`Thumbnail`, @@ -42,10 +41,8 @@ export class ThumbnailSettingsComponent navigation, settingsService, notification, - (s) => ({ - client: s.Client.Media.Thumbnail, - server: s.Server.Media.Thumbnail, - }) + globalSettingsService, + (s) => s.Media.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 724b5c6e..49794492 100644 --- a/src/frontend/app/ui/settings/thumbnail/thumbnail.settings.service.ts +++ b/src/frontend/app/ui/settings/thumbnail/thumbnail.settings.service.ts @@ -3,13 +3,9 @@ import { NetworkService } from '../../../model/network/network.service'; import { AbstractSettingsService } from '../_abstract/abstract.settings.service'; import { SettingsService } from '../settings.service'; import { ServerThumbnailConfig } from '../../../../../common/config/private/PrivateConfig'; -import { ClientThumbnailConfig } from '../../../../../common/config/public/ClientConfig'; @Injectable() -export class ThumbnailSettingsService extends AbstractSettingsService<{ - server: ServerThumbnailConfig; - client: ClientThumbnailConfig; -}> { +export class ThumbnailSettingsService extends AbstractSettingsService { constructor( private networkService: NetworkService, settingsService: SettingsService @@ -17,14 +13,11 @@ export class ThumbnailSettingsService extends AbstractSettingsService<{ super(settingsService); } - showInSimplifiedMode(): boolean { + hasAvailableSettings(): boolean { return false; } - public updateSettings(settings: { - server: ServerThumbnailConfig; - client: ClientThumbnailConfig; - }): Promise { + public updateSettings(settings: ServerThumbnailConfig): Promise { return this.networkService.putJson('/settings/thumbnail', { settings }); } } 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 23f524d2..0c60fb46 100644 --- a/src/frontend/app/ui/settings/usermanager/usermanager.settings.service.ts +++ b/src/frontend/app/ui/settings/usermanager/usermanager.settings.service.ts @@ -14,7 +14,7 @@ export class UserManagerSettingsService { public async getSettings(): Promise { return (await this.networkService.getJson>('/settings')) - .Client.authenticationRequired; + .Users.authenticationRequired; } public updateSettings(settings: boolean): Promise { diff --git a/src/frontend/app/ui/settings/video/video.settings.component.html b/src/frontend/app/ui/settings/video/video.settings.component.html index 727cc3bf..33919d6b 100644 --- a/src/frontend/app/ui/settings/video/video.settings.component.html +++ b/src/frontend/app/ui/settings/video/video.settings.component.html @@ -47,7 +47,6 @@ name="Format" i18n-name [ngModel]="states.server.transcoding.format" - [simplifiedMode]="simplifiedMode" (ngModelChange)="formatChanged($event)" [options]="formats" [required]="true"> @@ -57,7 +56,6 @@ name="Codec" i18n-name [ngModel]="states.server.transcoding.codec" - [simplifiedMode]="simplifiedMode" [options]="codecs[states.server.transcoding.format.value]" [required]="true"> @@ -80,7 +78,6 @@ [ngModel]="states.server.transcoding.fps" (change)="updateBitRate()" [options]="fps" - [simplifiedMode]="simplifiedMode" [required]="true"> @@ -113,7 +110,6 @@ description="The range of the Constant Rate Factor (CRF) scale is 0–51, where 0 is lossless, 23 is the default, and 51 is worst quality possible." i18n-name i18n-description [ngModel]="states.server.transcoding.crf" - [simplifiedMode]="simplifiedMode" [required]="true"> @@ -122,7 +118,6 @@ description="A preset is a collection of options that will provide a certain encoding speed to compression ratio. A slower preset will provide better compression (compression is quality per filesize)." i18n-name i18n-description [ngModel]="states.server.transcoding.preset" - [simplifiedMode]="simplifiedMode" [required]="true"> @@ -133,7 +128,6 @@ i18n-name i18n-description [ngModel]="states.server.transcoding.customOptions" [allowSpaces]="true" - [simplifiedMode]="simplifiedMode" [required]="true"> 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 3f529c07..d25fe533 100644 --- a/src/frontend/app/ui/settings/video/video.settings.component.ts +++ b/src/frontend/app/ui/settings/video/video.settings.component.ts @@ -20,6 +20,7 @@ import { videoResolutionType, } from '../../../../../common/config/private/PrivateConfig'; import { ClientVideoConfig } from '../../../../../common/config/public/ClientConfig'; +import {SettingsService} from '../settings.service'; @Component({ selector: 'app-settings-video', @@ -30,10 +31,7 @@ import { ClientVideoConfig } from '../../../../../common/config/public/ClientCon ], providers: [VideoSettingsService], }) -export class VideoSettingsComponent extends SettingsComponentDirective<{ - server: ServerVideoConfig; - client: ClientVideoConfig; -}> { +export class VideoSettingsComponent extends SettingsComponentDirective { readonly resolutionTypes: videoResolutionType[] = [ 360, 480, 720, 1080, 1440, 2160, 4320, ]; @@ -64,7 +62,8 @@ export class VideoSettingsComponent extends SettingsComponentDirective<{ navigation: NavigationService, settingsService: VideoSettingsService, public jobsService: ScheduledJobsService, - notification: NotificationService + notification: NotificationService, + globalSettingsService: SettingsService ) { super( $localize`Video`, @@ -73,14 +72,12 @@ export class VideoSettingsComponent extends SettingsComponentDirective<{ navigation, settingsService, notification, - (s) => ({ - client: s.Client.Media.Video, - server: s.Server.Media.Video, - }) + globalSettingsService, + (s) => s.Media.Video ); const currentRes = - settingsService.Settings.value.Server.Media.Video.transcoding.resolution; + settingsService.Settings.value.Media.Video.transcoding.resolution; if (this.resolutionTypes.indexOf(currentRes) === -1) { this.resolutionTypes.push(currentRes); } 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 66f52747..44225304 100644 --- a/src/frontend/app/ui/settings/video/video.settings.service.ts +++ b/src/frontend/app/ui/settings/video/video.settings.service.ts @@ -6,10 +6,7 @@ import { ClientVideoConfig } from '../../../../../common/config/public/ClientCon import { ServerVideoConfig } from '../../../../../common/config/private/PrivateConfig'; @Injectable() -export class VideoSettingsService extends AbstractSettingsService<{ - server: ServerVideoConfig; - client: ClientVideoConfig; -}> { +export class VideoSettingsService extends AbstractSettingsService { constructor( private networkService: NetworkService, settingsService: SettingsService @@ -17,10 +14,7 @@ export class VideoSettingsService extends AbstractSettingsService<{ super(settingsService); } - public updateSettings(settings: { - server: ServerVideoConfig; - client: ClientVideoConfig; - }): Promise { + public updateSettings(settings: ServerVideoConfig): Promise { return this.networkService.putJson('/settings/video', { settings }); } } diff --git a/src/frontend/app/ui/sharelogin/share-login.component.ts b/src/frontend/app/ui/sharelogin/share-login.component.ts index 14c4ecd3..e0e1b47e 100644 --- a/src/frontend/app/ui/sharelogin/share-login.component.ts +++ b/src/frontend/app/ui/sharelogin/share-login.component.ts @@ -18,7 +18,7 @@ export class ShareLoginComponent implements OnInit { private authService: AuthenticationService, private navigation: NavigationService ) { - this.title = Config.Client.applicationTitle; + this.title = Config.Server.applicationTitle; } ngOnInit(): void { diff --git a/src/frontend/index.html b/src/frontend/index.html index 29fb21c6..bf193d46 100644 --- a/src/frontend/index.html +++ b/src/frontend/index.html @@ -1,7 +1,7 @@ - + Loading.. diff --git a/test/backend/DBTestHelper.ts b/test/backend/DBTestHelper.ts index ad09745d..e7155d6c 100644 --- a/test/backend/DBTestHelper.ts +++ b/test/backend/DBTestHelper.ts @@ -160,7 +160,7 @@ export class DBTestHelper { } else if (this.dbType === DatabaseType.mysql) { await this.initMySQL(); } else if (this.dbType === DatabaseType.memory) { - Config.Server.Database.type = DatabaseType.memory; + Config.Database.type = DatabaseType.memory; } } @@ -215,8 +215,8 @@ export class DBTestHelper { private async clearUpMysql(): Promise { Logger.debug(LOG_TAG, 'clearing up mysql'); await ObjectManagers.reset(); - Config.Server.Database.type = DatabaseType.mysql; - Config.Server.Database.mysql.database = 'pigallery2_test'; + Config.Database.type = DatabaseType.mysql; + Config.Database.mysql.database = 'pigallery2_test'; await fs.promises.rm(this.tempDir, {recursive: true, force: true}); const conn = await SQLConnection.getConnection(); await conn.query('DROP DATABASE IF EXISTS ' + conn.options.database); @@ -235,8 +235,8 @@ export class DBTestHelper { private async clearUpSQLite(): Promise { Logger.debug(LOG_TAG, 'clearing up sqlite'); - Config.Server.Database.type = DatabaseType.sqlite; - Config.Server.Database.dbFolder = this.tempDir; + Config.Database.type = DatabaseType.sqlite; + Config.Database.dbFolder = this.tempDir; ProjectPath.reset(); await ObjectManagers.reset(); await fs.promises.rm(this.tempDir, {recursive: true, force: true}); diff --git a/test/backend/integration/model/sql/typeorm.ts b/test/backend/integration/model/sql/typeorm.ts index 898e1624..2df29c6c 100644 --- a/test/backend/integration/model/sql/typeorm.ts +++ b/test/backend/integration/model/sql/typeorm.ts @@ -26,8 +26,8 @@ describe('Typeorm integration', () => { const setUpSqlDB = async () => { await fs.promises.rm(tempDir, {recursive: true, force: true}); - Config.Server.Database.type = DatabaseType.sqlite; - Config.Server.Database.dbFolder = tempDir; + Config.Database.type = DatabaseType.sqlite; + Config.Database.dbFolder = tempDir; ProjectPath.reset(); }; diff --git a/test/backend/integration/routers/GalleryRouter.ts b/test/backend/integration/routers/GalleryRouter.ts index fbbba2e7..1ea001f6 100644 --- a/test/backend/integration/routers/GalleryRouter.ts +++ b/test/backend/integration/routers/GalleryRouter.ts @@ -30,11 +30,11 @@ describe('GalleryRouter', (sqlHelper: DBTestHelper) => { const setUp = async () => { await sqlHelper.initDB(); await fs.promises.rm(tempDir, {recursive: true, force: true}); - Config.Client.authenticationRequired = false; + Config.Users.authenticationRequired = false; Config.Server.Threading.enabled = false; - Config.Client.Media.Video.enabled = true; - Config.Server.Media.folder = path.join(__dirname, '../../assets'); - Config.Server.Media.tempFolder = path.join(__dirname, '../../tmp'); + Config.Media.Video.enabled = true; + Config.Media.folder = path.join(__dirname, '../../assets'); + Config.Media.tempFolder = path.join(__dirname, '../../tmp'); ProjectPath.reset(); // ProjectPath.ImageFolder = path.join(__dirname, '../../assets'); // ProjectPath.TempFolder = tempDir; @@ -55,7 +55,7 @@ describe('GalleryRouter', (sqlHelper: DBTestHelper) => { it('should load gallery', async () => { const result = await (chai.request(server.App) as SuperAgentStatic) - .get(Config.Client.apiPath + '/gallery/content/'); + .get(Config.Server.apiPath + '/gallery/content/'); (result.should as any).have.status(200); expect(result.body.error).to.be.equal(null); @@ -64,12 +64,12 @@ describe('GalleryRouter', (sqlHelper: DBTestHelper) => { }); it('should load gallery twice (to force loading form db)', async () => { - Config.Server.Indexing.reIndexingSensitivity = ReIndexingSensitivity.low; + Config.Indexing.reIndexingSensitivity = ReIndexingSensitivity.low; const _ = await (chai.request(server.App) as SuperAgentStatic) - .get(Config.Client.apiPath + '/gallery/content/orientation'); + .get(Config.Server.apiPath + '/gallery/content/orientation'); const result = await (chai.request(server.App) as SuperAgentStatic) - .get(Config.Client.apiPath + '/gallery/content/orientation'); + .get(Config.Server.apiPath + '/gallery/content/orientation'); (result.should as any).have.status(200); expect(result.body.error).to.be.equal(null); @@ -87,7 +87,7 @@ describe('GalleryRouter', (sqlHelper: DBTestHelper) => { it('should get video without transcoding', async () => { const result = await (chai.request(server.App) as SuperAgentStatic) - .get(Config.Client.apiPath + '/gallery/content/video.mp4/bestFit'); + .get(Config.Server.apiPath + '/gallery/content/video.mp4/bestFit'); (result.should as any).have.status(200); expect(result.body).to.be.instanceof(Buffer); diff --git a/test/backend/integration/routers/SharingRouter.ts b/test/backend/integration/routers/SharingRouter.ts index fd1ab86c..69e1fe0c 100644 --- a/test/backend/integration/routers/SharingRouter.ts +++ b/test/backend/integration/routers/SharingRouter.ts @@ -34,11 +34,11 @@ describe('SharingRouter', () => { let server: Server; const setUp = async () => { await fs.promises.rm(tempDir, {recursive: true, force: true}); - Config.Client.authenticationRequired = true; + Config.Users.authenticationRequired = true; Config.Server.Threading.enabled = false; - Config.Client.Sharing.enabled = true; - Config.Server.Database.type = DatabaseType.sqlite; - Config.Server.Database.dbFolder = tempDir; + Config.Sharing.enabled = true; + Config.Database.type = DatabaseType.sqlite; + Config.Database.dbFolder = tempDir; server = new Server(); await server.onStarted.wait(); @@ -64,14 +64,14 @@ describe('SharingRouter', () => { const shareLogin = async (srv: Server, sharingKey: string, password?: string): Promise => { return (chai.request(srv.App) as SuperAgentStatic) - .post(Config.Client.apiPath + '/share/login?' + QueryParams.gallery.sharingKey_query + '=' + sharingKey) + .post(Config.Server.apiPath + '/share/login?' + QueryParams.gallery.sharingKey_query + '=' + sharingKey) .send({password}); }; const login = async (srv: Server): Promise => { const result = await (chai.request(srv.App) as SuperAgentStatic) - .post(Config.Client.apiPath + '/user/login') + .post(Config.Server.apiPath + '/user/login') .send({ loginCredential: { password: testUser.password, diff --git a/test/backend/integration/routers/UserRouter.ts b/test/backend/integration/routers/UserRouter.ts index 21a9d088..ce203d1c 100644 --- a/test/backend/integration/routers/UserRouter.ts +++ b/test/backend/integration/routers/UserRouter.ts @@ -36,8 +36,8 @@ describe('UserRouter', () => { const setUp = async () => { await fs.promises.rm(tempDir, {recursive: true, force: true}); Config.Server.Threading.enabled = false; - Config.Server.Database.type = DatabaseType.sqlite; - Config.Server.Database.dbFolder = tempDir; + Config.Database.type = DatabaseType.sqlite; + Config.Database.dbFolder = tempDir; ProjectPath.reset(); @@ -64,7 +64,7 @@ describe('UserRouter', () => { const login = async (srv: Server): Promise => { const result = await (chai.request(srv.App) as SuperAgentStatic) - .post(Config.Client.apiPath + '/user/login') + .post(Config.Server.apiPath + '/user/login') .send({ loginCredential: { password: testUser.password, @@ -82,14 +82,14 @@ describe('UserRouter', () => { beforeEach(setUp); afterEach(tearDown); it('it should login', async () => { - Config.Client.authenticationRequired = true; + Config.Users.authenticationRequired = true; await login(server); }); it('it skip login', async () => { - Config.Client.authenticationRequired = false; + Config.Users.authenticationRequired = false; const result = await chai.request(server.App) - .post(Config.Client.apiPath + '/user/login'); + .post(Config.Server.apiPath + '/user/login'); result.res.should.have.status(404); }); @@ -102,12 +102,12 @@ describe('UserRouter', () => { beforeEach(setUp); afterEach(tearDown); it('it should GET the authenticated user', async () => { - Config.Client.authenticationRequired = true; + Config.Users.authenticationRequired = true; const loginRes = await login(server); const result = await chai.request(server.App) - .get(Config.Client.apiPath + '/user/me') + .get(Config.Server.apiPath + '/user/me') .set('Cookie', loginRes.res.headers['set-cookie']) .set('CSRF-Token', loginRes.body.result.csrfToken); @@ -115,17 +115,17 @@ describe('UserRouter', () => { }); it('it should not authenticate', async () => { - Config.Client.authenticationRequired = true; + Config.Users.authenticationRequired = true; const result = await chai.request(server.App) - .get(Config.Client.apiPath + '/user/me'); + .get(Config.Server.apiPath + '/user/me'); result.res.should.have.status(401); }); it('it should authenticate as user with sharing key', async () => { - Config.Client.authenticationRequired = true; - Config.Client.Sharing.enabled = true; + Config.Users.authenticationRequired = true; + Config.Sharing.enabled = true; const sharingKey = (await RouteTestingHelper.createSharing(testUser)).sharingKey; @@ -134,7 +134,7 @@ describe('UserRouter', () => { const q: any = {}; q[QueryParams.gallery.sharingKey_query] = sharingKey; const result = await chai.request(server.App) - .get(Config.Client.apiPath + '/user/me?' + QueryParams.gallery.sharingKey_query + '=' + sharingKey) + .get(Config.Server.apiPath + '/user/me?' + QueryParams.gallery.sharingKey_query + '=' + sharingKey) .set('Cookie', loginRes.res.headers['set-cookie']) .set('CSRF-Token', loginRes.body.result.csrfToken); @@ -144,29 +144,29 @@ describe('UserRouter', () => { it('it should authenticate with sharing key', async () => { - Config.Client.authenticationRequired = true; - Config.Client.Sharing.enabled = true; + Config.Users.authenticationRequired = true; + Config.Sharing.enabled = true; const sharing = (await RouteTestingHelper.createSharing(testUser)); const q: any = {}; q[QueryParams.gallery.sharingKey_query] = sharing.sharingKey; const result = await chai.request(server.App) - .get(Config.Client.apiPath + '/user/me?' + QueryParams.gallery.sharingKey_query + '=' + sharing.sharingKey); + .get(Config.Server.apiPath + '/user/me?' + QueryParams.gallery.sharingKey_query + '=' + sharing.sharingKey); checkUserResult(result, RouteTestingHelper.getExpectedSharingUser(sharing)); }); it('it should not authenticate with sharing key without password', async () => { - Config.Client.authenticationRequired = true; - Config.Client.Sharing.enabled = true; + Config.Users.authenticationRequired = true; + Config.Sharing.enabled = true; const sharing = (await RouteTestingHelper.createSharing(testUser, 'pass_secret')); const q: any = {}; q[QueryParams.gallery.sharingKey_query] = sharing.sharingKey; const result = await chai.request(server.App) - .get(Config.Client.apiPath + '/user/me?' + QueryParams.gallery.sharingKey_query + '=' + sharing.sharingKey); + .get(Config.Server.apiPath + '/user/me?' + QueryParams.gallery.sharingKey_query + '=' + sharing.sharingKey); result.should.have.status(401); @@ -176,14 +176,14 @@ describe('UserRouter', () => { }); it('it should authenticate as guest', async () => { - Config.Client.authenticationRequired = false; + Config.Users.authenticationRequired = false; const result = await chai.request(server.App) - .get(Config.Client.apiPath + '/user/me'); + .get(Config.Server.apiPath + '/user/me'); const expectedGuestUser = { - name: UserRoles[Config.Client.unAuthenticatedUserRole], - role: Config.Client.unAuthenticatedUserRole + name: UserRoles[Config.Users.unAuthenticatedUserRole], + role: Config.Users.unAuthenticatedUserRole } as UserDTO; diff --git a/test/backend/integration/routers/admin/SettingsRouter.ts b/test/backend/integration/routers/admin/SettingsRouter.ts index fa978881..ccad8c95 100644 --- a/test/backend/integration/routers/admin/SettingsRouter.ts +++ b/test/backend/integration/routers/admin/SettingsRouter.ts @@ -18,8 +18,8 @@ describe('SettingsRouter', () => { beforeEach(async () => { await fs.promises.rm(tempDir, {recursive: true, force: true}); Config.Server.Threading.enabled = false; - Config.Server.Database.type = DatabaseType.sqlite; - Config.Server.Database.dbFolder = tempDir; + Config.Database.type = DatabaseType.sqlite; + Config.Database.dbFolder = tempDir; ProjectPath.reset(); }); @@ -31,20 +31,20 @@ describe('SettingsRouter', () => { describe('/GET settings', () => { it('it should GET the settings', async () => { - Config.Client.authenticationRequired = false; + Config.Users.authenticationRequired = false; const originalSettings = await Config.original(); originalSettings.Server.sessionSecret = null; - originalSettings.Server.Database.enforcedUsers = null; + originalSettings.Users.enforcedUsers = null; const srv = new Server(); await srv.onStarted.wait(); const result = await chai.request(srv.App) - .get(Config.Client.apiPath + '/settings'); + .get(Config.Server.apiPath + '/settings'); result.res.should.have.status(200); result.body.should.be.a('object'); should.equal(result.body.error, null); result.body.result.Server.Environment.upTime = null; - originalSettings.Server.Environment.upTime = null; + originalSettings.Environment.upTime = null; result.body.result.should.deep.equal(JSON.parse(JSON.stringify(originalSettings.toJSON({attachState: true, attachVolatile: true})))); }); }); diff --git a/test/backend/unit/middlewares/user/AuthenticationMWs.ts b/test/backend/unit/middlewares/user/AuthenticationMWs.ts index 4b877e81..e57e6a54 100644 --- a/test/backend/unit/middlewares/user/AuthenticationMWs.ts +++ b/test/backend/unit/middlewares/user/AuthenticationMWs.ts @@ -45,7 +45,7 @@ describe('Authentication middleware', () => { query: {}, params: {} }; - Config.Client.authenticationRequired = true; + Config.Users.authenticationRequired = true; const next: any = (err: ErrorDTO) => { expect(err).not.to.be.undefined; expect(err.code).to.be.eql(ErrorCodes.NOT_AUTHENTICATED); diff --git a/test/backend/unit/model/fileprocessing/PhotoProcessing.spec.ts b/test/backend/unit/model/fileprocessing/PhotoProcessing.spec.ts index 0ba5ffa3..c3e5e414 100644 --- a/test/backend/unit/model/fileprocessing/PhotoProcessing.spec.ts +++ b/test/backend/unit/model/fileprocessing/PhotoProcessing.spec.ts @@ -11,24 +11,24 @@ describe('PhotoProcessing', () => { it('should generate converted file path', async () => { Config.load(); - Config.Client.Media.Thumbnail.thumbnailSizes = []; + Config.Media.Thumbnail.thumbnailSizes = []; ProjectPath.ImageFolder = path.join(__dirname, './../../../assets'); const photoPath = path.join(ProjectPath.ImageFolder, 'test_png.png'); expect(await PhotoProcessing .isValidConvertedPath(PhotoProcessing.generateConvertedPath(photoPath, - Config.Server.Media.Photo.Converting.resolution))) + Config.Media.Photo.Converting.resolution))) .to.be.true; expect(await PhotoProcessing .isValidConvertedPath(PhotoProcessing.generateConvertedPath(photoPath + 'noPath', - Config.Server.Media.Photo.Converting.resolution))) + Config.Media.Photo.Converting.resolution))) .to.be.false; { const convertedPath = PhotoProcessing.generateConvertedPath(photoPath, - Config.Server.Media.Photo.Converting.resolution); - Config.Server.Media.Photo.Converting.resolution = (1 as any); + Config.Media.Photo.Converting.resolution); + Config.Media.Photo.Converting.resolution = (1 as any); expect(await PhotoProcessing.isValidConvertedPath(convertedPath)).to.be.false; } }); @@ -37,12 +37,12 @@ describe('PhotoProcessing', () => { it('should generate converted thumbnail path', async () => { Config.load(); - Config.Server.Media.Photo.Converting.resolution = (null as any); - Config.Client.Media.Thumbnail.thumbnailSizes = [10, 20]; + Config.Media.Photo.Converting.resolution = (null as any); + Config.Media.Thumbnail.thumbnailSizes = [10, 20]; ProjectPath.ImageFolder = path.join(__dirname, './../../../assets'); const photoPath = path.join(ProjectPath.ImageFolder, 'test_png.png'); - for (const thSize of Config.Client.Media.Thumbnail.thumbnailSizes) { + for (const thSize of Config.Media.Thumbnail.thumbnailSizes) { expect(await PhotoProcessing .isValidConvertedPath(PhotoProcessing.generateConvertedPath(photoPath, thSize))) .to.be.true; diff --git a/test/backend/unit/model/fileprocessing/VideoProcessing.spec.ts b/test/backend/unit/model/fileprocessing/VideoProcessing.spec.ts index a0c5f6a0..059f88e0 100644 --- a/test/backend/unit/model/fileprocessing/VideoProcessing.spec.ts +++ b/test/backend/unit/model/fileprocessing/VideoProcessing.spec.ts @@ -22,22 +22,22 @@ describe('VideoProcessing', () => { { const convertedPath = VideoProcessing.generateConvertedFilePath(videoPath); - Config.Server.Media.Video.transcoding.bitRate = 10; + Config.Media.Video.transcoding.bitRate = 10; expect(await VideoProcessing.isValidConvertedPath(convertedPath)).to.be.false; } { const convertedPath = VideoProcessing.generateConvertedFilePath(videoPath); - Config.Server.Media.Video.transcoding.codec = 'codec_text' as any; + Config.Media.Video.transcoding.codec = 'codec_text' as any; expect(await VideoProcessing.isValidConvertedPath(convertedPath)).to.be.false; } { const convertedPath = VideoProcessing.generateConvertedFilePath(videoPath); - Config.Server.Media.Video.transcoding.format = 'format_test' as any; + Config.Media.Video.transcoding.format = 'format_test' as any; expect(await VideoProcessing.isValidConvertedPath(convertedPath)).to.be.false; } { const convertedPath = VideoProcessing.generateConvertedFilePath(videoPath); - Config.Server.Media.Video.transcoding.resolution = 1 as any; + Config.Media.Video.transcoding.resolution = 1 as any; expect(await VideoProcessing.isValidConvertedPath(convertedPath)).to.be.false; } }); diff --git a/test/backend/unit/model/sql/IndexingManager.spec.ts b/test/backend/unit/model/sql/IndexingManager.spec.ts index 0f578e8e..4fef52b6 100644 --- a/test/backend/unit/model/sql/IndexingManager.spec.ts +++ b/test/backend/unit/model/sql/IndexingManager.spec.ts @@ -71,7 +71,7 @@ describe('IndexingManager', (sqlHelper: DBTestHelper) => { afterEach(async () => { Config.loadSync(); - Config.Server.Preview.Sorting = [SortingMethods.descRating]; + Config.Preview.Sorting = [SortingMethods.descRating]; await sqlHelper.clearDB(); }); @@ -358,7 +358,7 @@ describe('IndexingManager', (sqlHelper: DBTestHelper) => { const sp2 = TestHelper.getRandomizedPhotoEntry(subDir, 'subPhoto2', 0); sp2.metadata.rating = 3; subDir.preview = sp1; - Config.Server.Preview.Sorting = [SortingMethods.descRating]; + Config.Preview.Sorting = [SortingMethods.descRating]; DirectoryDTOUtils.removeReferences(subDir); await im.saveToDB(Utils.clone(subDir) as ParentDirectoryDTO); @@ -396,7 +396,7 @@ describe('IndexingManager', (sqlHelper: DBTestHelper) => { const sp2 = TestHelper.getRandomizedPhotoEntry(subDir, 'subPhoto2', 0); sp2.metadata.rating = 3; subDir.preview = sp1; - Config.Server.Preview.Sorting = [SortingMethods.descRating]; + Config.Preview.Sorting = [SortingMethods.descRating]; DirectoryDTOUtils.removeReferences(subDir); @@ -433,7 +433,7 @@ describe('IndexingManager', (sqlHelper: DBTestHelper) => { const sp2 = TestHelper.getRandomizedPhotoEntry(subDir, 'subPhoto2', 0); sp2.metadata.rating = 3; subDir.preview = sp1; - Config.Server.Preview.Sorting = [SortingMethods.descRating]; + Config.Preview.Sorting = [SortingMethods.descRating]; DirectoryDTOUtils.removeReferences(parent); await im.saveToDB(Utils.clone(parent) as ParentDirectoryDTO); @@ -492,14 +492,14 @@ describe('IndexingManager', (sqlHelper: DBTestHelper) => { const p2 = TestHelper.getRandomizedPhotoEntry(parent, 'Photo2'); const gpx = TestHelper.getRandomizedGPXEntry(parent, 'GPX1'); DirectoryDTOUtils.removeReferences(parent); - Config.Client.MetaFile.gpx = true; - Config.Client.MetaFile.markdown = true; - Config.Client.MetaFile.pg2conf = true; + Config.MetaFile.gpx = true; + Config.MetaFile.markdown = true; + Config.MetaFile.pg2conf = true; await im.saveToDB(Utils.clone(parent) as ParentDirectoryDTO); - Config.Client.MetaFile.gpx = false; - Config.Client.MetaFile.markdown = false; - Config.Client.MetaFile.pg2conf = false; + Config.MetaFile.gpx = false; + Config.MetaFile.markdown = false; + Config.MetaFile.pg2conf = false; const conn = await SQLConnection.getConnection(); const selected = await gm.getParentDirFromId(conn, (await gm.getDirIdAndTime(conn, parent.name, parent.path)).id); @@ -550,9 +550,9 @@ describe('IndexingManager', (sqlHelper: DBTestHelper) => { const conn = await SQLConnection.getConnection(); const gm = new GalleryManagerTest(); const im = new IndexingManagerTest(); - Config.Client.MetaFile.gpx = true; - Config.Client.MetaFile.markdown = true; - Config.Client.MetaFile.pg2conf = true; + Config.MetaFile.gpx = true; + Config.MetaFile.markdown = true; + Config.MetaFile.pg2conf = true; const parent = TestHelper.getRandomizedDirectoryEntry(); const p1 = TestHelper.getRandomizedPhotoEntry(parent, 'Photo1'); const p2 = TestHelper.getRandomizedPhotoEntry(parent, 'Photo2'); @@ -563,7 +563,7 @@ describe('IndexingManager', (sqlHelper: DBTestHelper) => { const sp2 = TestHelper.getRandomizedPhotoEntry(subDir, 'subPhoto2', 1); sp2.metadata.rating = 3; subDir.preview = sp1; - Config.Server.Preview.Sorting = [SortingMethods.descRating]; + Config.Preview.Sorting = [SortingMethods.descRating]; DirectoryDTOUtils.removeReferences(parent); const s1 = im.queueForSave(Utils.clone(parent) as ParentDirectoryDTO); @@ -615,9 +615,9 @@ describe('IndexingManager', (sqlHelper: DBTestHelper) => { const conn = await SQLConnection.getConnection(); const gm = new GalleryManagerTest(); const im = new IndexingManagerTest(); - Config.Client.MetaFile.gpx = true; - Config.Client.MetaFile.markdown = true; - Config.Client.MetaFile.pg2conf = true; + Config.MetaFile.gpx = true; + Config.MetaFile.markdown = true; + Config.MetaFile.pg2conf = true; const parent = TestHelper.getRandomizedDirectoryEntry(); DirectoryDTOUtils.removeReferences(parent); await im.saveToDB(Utils.clone(parent) as ParentDirectoryDTO); @@ -657,7 +657,7 @@ describe('IndexingManager', (sqlHelper: DBTestHelper) => { }); it('with re indexing severity low', async () => { - Config.Server.Indexing.reIndexingSensitivity = ReIndexingSensitivity.low; + Config.Indexing.reIndexingSensitivity = ReIndexingSensitivity.low; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore @@ -696,10 +696,10 @@ describe('IndexingManager', (sqlHelper: DBTestHelper) => { it('.saved_searches.pg2conf', async () => { Config.Server.Threading.enabled = false; - Config.Client.Album.enabled = true; - Config.Client.Faces.enabled = true; + Config.Album.enabled = true; + Config.Faces.enabled = true; - Config.Server.Media.folder = path.join(__dirname, '/../../../assets'); + Config.Media.folder = path.join(__dirname, '/../../../assets'); ProjectPath.ImageFolder = path.join(__dirname, '/../../../assets'); const im = new IndexingManagerTest(); const am = new AlbumManager(); diff --git a/test/backend/unit/model/sql/PreviewManager.spec.ts b/test/backend/unit/model/sql/PreviewManager.spec.ts index 63433243..9e6f2cfc 100644 --- a/test/backend/unit/model/sql/PreviewManager.spec.ts +++ b/test/backend/unit/model/sql/PreviewManager.spec.ts @@ -160,8 +160,8 @@ describe('PreviewManager', (sqlHelper: DBTestHelper) => { }); afterEach(() => { - Config.Server.Preview.SearchQuery = null; - Config.Server.Preview.Sorting = [SortingMethods.descRating, SortingMethods.descDate]; + Config.Preview.SearchQuery = null; + Config.Preview.Sorting = [SortingMethods.descRating, SortingMethods.descDate]; }); @@ -181,22 +181,22 @@ describe('PreviewManager', (sqlHelper: DBTestHelper) => { it('should sort directory preview', async () => { const pm = new PreviewManager(); - Config.Server.Preview.Sorting = [SortingMethods.descRating, SortingMethods.descDate]; + Config.Preview.Sorting = [SortingMethods.descRating, SortingMethods.descDate]; expect(Utils.clone(await pm.setAndGetPreviewForDirectory(subDir))).to.deep.equalInAnyOrder(previewifyMedia(p2)); - Config.Server.Preview.Sorting = [SortingMethods.descDate]; + Config.Preview.Sorting = [SortingMethods.descDate]; expect(Utils.clone(await pm.setAndGetPreviewForDirectory(subDir))).to.deep.equalInAnyOrder(previewifyMedia(pFaceLess)); - Config.Server.Preview.Sorting = [SortingMethods.descRating]; + Config.Preview.Sorting = [SortingMethods.descRating]; expect(Utils.clone(await pm.setAndGetPreviewForDirectory(dir))).to.deep.equalInAnyOrder(previewifyMedia(p4)); - Config.Server.Preview.Sorting = [SortingMethods.descName]; + Config.Preview.Sorting = [SortingMethods.descName]; expect(Utils.clone(await pm.setAndGetPreviewForDirectory(dir))).to.deep.equalInAnyOrder(previewifyMedia(v)); }); it('should get preview for directory', async () => { const pm = new PreviewManager(); - Config.Server.Preview.SearchQuery = {type: SearchQueryTypes.any_text, text: 'Boba'} as TextSearch; + Config.Preview.SearchQuery = {type: SearchQueryTypes.any_text, text: 'Boba'} as TextSearch; expect(Utils.clone(await pm.setAndGetPreviewForDirectory(subDir))).to.deep.equalInAnyOrder(previewifyMedia(p)); - Config.Server.Preview.SearchQuery = {type: SearchQueryTypes.any_text, text: 'Derem'} as TextSearch; + Config.Preview.SearchQuery = {type: SearchQueryTypes.any_text, text: 'Derem'} as TextSearch; expect(Utils.clone(await pm.setAndGetPreviewForDirectory(subDir))).to.deep.equalInAnyOrder(previewifyMedia(p2)); expect(Utils.clone(await pm.setAndGetPreviewForDirectory(dir))).to.deep.equalInAnyOrder(previewifyMedia(p2)); expect(Utils.clone(await pm.setAndGetPreviewForDirectory(subDir2))).to.deep.equalInAnyOrder(previewifyMedia(p4)); @@ -205,21 +205,21 @@ describe('PreviewManager', (sqlHelper: DBTestHelper) => { it('should get preview for saved search', async () => { const pm = new PreviewManager(); - Config.Server.Preview.SearchQuery = null; + Config.Preview.SearchQuery = null; expect(Utils.clone(await pm.getAlbumPreview({ searchQuery: { type: SearchQueryTypes.any_text, text: 'sw' } as TextSearch }))).to.deep.equalInAnyOrder(previewifyMedia(p4)); - Config.Server.Preview.SearchQuery = {type: SearchQueryTypes.any_text, text: 'Boba'} as TextSearch; + Config.Preview.SearchQuery = {type: SearchQueryTypes.any_text, text: 'Boba'} as TextSearch; expect(Utils.clone(await pm.getAlbumPreview({ searchQuery: { type: SearchQueryTypes.any_text, text: 'sw' } as TextSearch }))).to.deep.equalInAnyOrder(previewifyMedia(p)); - Config.Server.Preview.SearchQuery = {type: SearchQueryTypes.any_text, text: 'Derem'} as TextSearch; + Config.Preview.SearchQuery = {type: SearchQueryTypes.any_text, text: 'Derem'} as TextSearch; expect(Utils.clone(await pm.getAlbumPreview({ searchQuery: { type: SearchQueryTypes.any_text, @@ -227,7 +227,7 @@ describe('PreviewManager', (sqlHelper: DBTestHelper) => { } as TextSearch }))).to.deep.equalInAnyOrder(previewifyMedia(p2)); // Having a preview search query that does not return valid result - Config.Server.Preview.SearchQuery = {type: SearchQueryTypes.any_text, text: 'wont find it'} as TextSearch; + Config.Preview.SearchQuery = {type: SearchQueryTypes.any_text, text: 'wont find it'} as TextSearch; expect(Utils.clone(await pm.getAlbumPreview({ searchQuery: { type: SearchQueryTypes.any_text, @@ -235,7 +235,7 @@ describe('PreviewManager', (sqlHelper: DBTestHelper) => { } as TextSearch }))).to.deep.equalInAnyOrder(previewifyMedia(p2)); // having a saved search that does not have any image - Config.Server.Preview.SearchQuery = {type: SearchQueryTypes.any_text, text: 'Derem'} as TextSearch; + Config.Preview.SearchQuery = {type: SearchQueryTypes.any_text, text: 'Derem'} as TextSearch; expect(Utils.clone(await pm.getAlbumPreview({ searchQuery: { type: SearchQueryTypes.any_text, diff --git a/test/backend/unit/model/sql/SearchManager.spec.ts b/test/backend/unit/model/sql/SearchManager.spec.ts index 67b819b1..2f7f8be0 100644 --- a/test/backend/unit/model/sql/SearchManager.spec.ts +++ b/test/backend/unit/model/sql/SearchManager.spec.ts @@ -140,15 +140,15 @@ describe('SearchManager', (sqlHelper: DBTestHelper) => { before(async () => { await setUpSqlDB(); - Config.Client.Search.listDirectories = true; - Config.Client.Search.listMetafiles = false; + Config.Search.listDirectories = true; + Config.Search.listMetafiles = false; }); after(async () => { await sqlHelper.clearDB(); - Config.Client.Search.listDirectories = false; - Config.Client.Search.listMetafiles = false; + Config.Search.listDirectories = false; + Config.Search.listMetafiles = false; }); it('should get autocomplete', async () => { @@ -174,14 +174,14 @@ describe('SearchManager', (sqlHelper: DBTestHelper) => { expect((await sm.autocomplete('arch', SearchQueryTypes.any_text))).eql([ new AutoCompleteItem('Research City', SearchQueryTypes.position)]); - Config.Client.Search.AutoComplete.targetItemsPerCategory = 99999; + Config.Search.AutoComplete.targetItemsPerCategory = 99999; expect((await sm.autocomplete('wa', SearchQueryTypes.any_text))).to.deep.equalInAnyOrder([ new AutoCompleteItem('star wars', SearchQueryTypes.keyword), new AutoCompleteItem('Anakin Skywalker', SearchQueryTypes.person), new AutoCompleteItem('Luke Skywalker', SearchQueryTypes.person), new AutoCompleteItem('wars dir', SearchQueryTypes.directory)]); - Config.Client.Search.AutoComplete.targetItemsPerCategory = 1; + Config.Search.AutoComplete.targetItemsPerCategory = 1; expect((await sm.autocomplete('a', SearchQueryTypes.any_text))).to.deep.equalInAnyOrder([ new AutoCompleteItem('Ajan Kloss', SearchQueryTypes.position), new AutoCompleteItem('Tipoca City', SearchQueryTypes.position), @@ -195,7 +195,7 @@ describe('SearchManager', (sqlHelper: DBTestHelper) => { new AutoCompleteItem('Jedha', SearchQueryTypes.position), new AutoCompleteItem('wars dir', SearchQueryTypes.directory), new AutoCompleteItem('The Phantom Menace', SearchQueryTypes.directory)]); - Config.Client.Search.AutoComplete.targetItemsPerCategory = 5; + Config.Search.AutoComplete.targetItemsPerCategory = 5; expect((await sm.autocomplete('sw', SearchQueryTypes.any_text))).to.deep.equalInAnyOrder([ new AutoCompleteItem('sw1.jpg', SearchQueryTypes.file_name), @@ -261,12 +261,12 @@ describe('SearchManager', (sqlHelper: DBTestHelper) => { describe('advanced search', async () => { afterEach(async () => { - Config.Client.Search.listDirectories = false; - Config.Client.Search.listMetafiles = false; + Config.Search.listDirectories = false; + Config.Search.listMetafiles = false; }); afterEach(async () => { - Config.Client.Search.listDirectories = false; - Config.Client.Search.listMetafiles = false; + Config.Search.listDirectories = false; + Config.Search.listMetafiles = false; }); it('should AND', async () => { @@ -1233,7 +1233,7 @@ describe('SearchManager', (sqlHelper: DBTestHelper) => { }) as any).timeout(40000); it('search result should return directory', async () => { - Config.Client.Search.listDirectories = true; + Config.Search.listDirectories = true; const sm = new SearchManager(); const cloned = Utils.clone(searchifyDir(subDir)); @@ -1259,7 +1259,7 @@ describe('SearchManager', (sqlHelper: DBTestHelper) => { } as SearchResultDTO)); }); it('search result should return meta files', async () => { - Config.Client.Search.listMetafiles = true; + Config.Search.listMetafiles = true; const sm = new SearchManager(); const query = { diff --git a/test/backend/unit/model/threading/DiskMangerWorker.spec.ts b/test/backend/unit/model/threading/DiskMangerWorker.spec.ts index 71b83e93..80289d70 100644 --- a/test/backend/unit/model/threading/DiskMangerWorker.spec.ts +++ b/test/backend/unit/model/threading/DiskMangerWorker.spec.ts @@ -12,14 +12,14 @@ describe('DiskMangerWorker', () => { // loading default settings (this might have been changed by other tests) before(() => { Config.loadSync(); - Config.Server.Database.type = DatabaseType.sqlite; - Config.Client.Faces.enabled = true; - Config.Client.Faces.keywordsToPersons = true; + Config.Database.type = DatabaseType.sqlite; + Config.Faces.enabled = true; + Config.Faces.keywordsToPersons = true; }); it('should parse metadata', async () => { - Config.Server.Media.folder = path.join(__dirname, '/../../../assets'); + Config.Media.folder = path.join(__dirname, '/../../../assets'); ProjectPath.ImageFolder = path.join(__dirname, '/../../../assets'); const dir = await DiskMangerWorker.scanDirectory('/'); // should match the number of media (photo/video) files in the assets folder diff --git a/test/backend/unit/model/threading/MetaDataLoader.spec.ts b/test/backend/unit/model/threading/MetaDataLoader.spec.ts index 4d211e7c..f3859c1b 100644 --- a/test/backend/unit/model/threading/MetaDataLoader.spec.ts +++ b/test/backend/unit/model/threading/MetaDataLoader.spec.ts @@ -14,9 +14,9 @@ describe('MetadataLoader', () => { before(() => { Config.loadSync(); - Config.Server.Database.type = DatabaseType.sqlite; - Config.Client.Faces.enabled = true; - Config.Client.Faces.keywordsToPersons = true; + Config.Database.type = DatabaseType.sqlite; + Config.Faces.enabled = true; + Config.Faces.keywordsToPersons = true; });