diff --git a/backend/middlewares/AdminMWs.ts b/backend/middlewares/AdminMWs.ts index 9db3b344..c17b9a68 100644 --- a/backend/middlewares/AdminMWs.ts +++ b/backend/middlewares/AdminMWs.ts @@ -70,6 +70,29 @@ export class AdminMWs { } } + public static async updateSearchSettings(req: Request, res: Response, next: NextFunction) { + 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 = Config.original(); + await ConfigDiagnostics.testSearchConfig(req.body.settings, original); + + Config.Client.Search = req.body.settings; + original.Client.Search = req.body.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) { + return next(new ErrorDTO(ErrorCodes.SETTINGS_ERROR, "Settings error: " + JSON.stringify(err, null, ' '), err)); + } + } + + public static async updateAuthenticationSettings(req: Request, res: Response, next: NextFunction) { if ((typeof req.body === 'undefined') || (typeof req.body.settings === 'undefined')) { return next(new ErrorDTO(ErrorCodes.INPUT_ERROR, "settings is needed")); diff --git a/backend/model/ConfigDiagnostics.ts b/backend/model/ConfigDiagnostics.ts index fefd42d7..5e7cfc8e 100644 --- a/backend/model/ConfigDiagnostics.ts +++ b/backend/model/ConfigDiagnostics.ts @@ -2,6 +2,7 @@ import {Config} from "../../common/config/private/Config"; import { DataBaseConfig, DatabaseType, + IPrivateConfig, ThumbnailConfig, ThumbnailProcessingLib } from "../../common/config/private/IPrivateConfig"; @@ -89,15 +90,15 @@ export class ConfigDiagnostics { } - static async testSearchConfig(search: ClientConfig.SearchConfig) { - if (search.enabled == true && Config.Server.database.type == DatabaseType.memory) { + static async testSearchConfig(search: ClientConfig.SearchConfig, config: IPrivateConfig) { + if (search.enabled == true && config.Server.database.type == DatabaseType.memory) { throw "Memory Database do not support searching"; } } - static async testSharingConfig(sharing: ClientConfig.SharingConfig) { - if (sharing.enabled == true && Config.Server.database.type == DatabaseType.memory) { + static async testSharingConfig(sharing: ClientConfig.SharingConfig, config: IPrivateConfig) { + if (sharing.enabled == true && config.Server.database.type == DatabaseType.memory) { throw "Memory Database do not support sharing"; } } @@ -160,7 +161,7 @@ export class ConfigDiagnostics { try { - await ConfigDiagnostics.testSearchConfig(Config.Client.Search); + await ConfigDiagnostics.testSearchConfig(Config.Client.Search, Config); } catch (err) { NotificationManager.warning("Search is not supported with these settings. Disabling temporally. Please adjust the config properly.", err); Logger.warn(LOG_TAG, "Search is not supported with these settings, switching off..", err); @@ -168,7 +169,7 @@ export class ConfigDiagnostics { } try { - await ConfigDiagnostics.testSharingConfig(Config.Client.Sharing); + await ConfigDiagnostics.testSharingConfig(Config.Client.Sharing, Config); } catch (err) { NotificationManager.warning("Sharing is not supported with these settings. Disabling temporally. Please adjust the config properly.", err); Logger.warn(LOG_TAG, "Sharing is not supported with these settings, switching off..", err); diff --git a/backend/routes/AdminRouter.ts b/backend/routes/AdminRouter.ts index ece46560..3674f1da 100644 --- a/backend/routes/AdminRouter.ts +++ b/backend/routes/AdminRouter.ts @@ -61,6 +61,12 @@ export class AdminRouter { AdminMWs.updateThumbnailSettings, RenderingMWs.renderOK ); + app.put("/api/settings/search", + AuthenticationMWs.authenticate, + AuthenticationMWs.authorise(UserRoles.Admin), + AdminMWs.updateSearchSettings, + RenderingMWs.renderOK + ); }; diff --git a/frontend/app/admin/admin.component.html b/frontend/app/admin/admin.component.html index ee01d69a..44eeee28 100644 --- a/frontend/app/admin/admin.component.html +++ b/frontend/app/admin/admin.component.html @@ -23,7 +23,8 @@ - + + diff --git a/frontend/app/app.module.ts b/frontend/app/app.module.ts index e113d050..c130c2f5 100644 --- a/frontend/app/app.module.ts +++ b/frontend/app/app.module.ts @@ -48,6 +48,8 @@ import {InfoPanelLightboxComponent} from "./gallery/lightbox/infopanel/info-pane import {MapSettingsComponent} from "./settings/map/map.settings.component"; import {TooltipModule} from "ngx-bootstrap/tooltip"; import {ThumbnailSettingsComponent} from "./settings/thumbnail/thumbanil.settings.component"; +import {SearchSettingsComponent} from "./settings/search/search.settings.component"; +import {SettingsService} from "./settings/settings.service"; @Injectable() export class GoogleMapsConfig { apiKey: string; @@ -97,6 +99,7 @@ export class GoogleMapsConfig { DatabaseSettingsComponent, MapSettingsComponent, ThumbnailSettingsComponent, + SearchSettingsComponent, StringifyRole], providers: [ {provide: LAZY_MAPS_API_CONFIG, useClass: GoogleMapsConfig}, @@ -111,6 +114,7 @@ export class GoogleMapsConfig { NotificationService, FullScreenService, NavigationService, + SettingsService, OverlayService], bootstrap: [AppComponent] diff --git a/frontend/app/settings/_abstract/abstract.settings.component.ts b/frontend/app/settings/_abstract/abstract.settings.component.ts index 4dfbc3a1..399efa6e 100644 --- a/frontend/app/settings/_abstract/abstract.settings.component.ts +++ b/frontend/app/settings/_abstract/abstract.settings.component.ts @@ -5,15 +5,13 @@ import {Utils} from "../../../../common/Utils"; import {ErrorDTO} from "../../../../common/entities/Error"; import {NotificationService} from "../../model/notification.service"; import {NavigationService} from "../../model/navigation.service"; -import {ISettingsService} from "./abstract.settings.service"; +import {AbstractSettingsService} from "./abstract.settings.service"; export abstract class SettingsComponent implements OnInit, OnDestroy { @ViewChild('settingsForm') form; - public settings: T = {}; public inProgress = false; - private original: T = {}; public error: string = null; public changed: boolean = false; private subscription = null; @@ -21,7 +19,7 @@ export abstract class SettingsComponent implements OnInit, OnDestroy { constructor(private name, private _authService: AuthenticationService, private _navigation: NavigationService, - protected _settingsService: ISettingsService, + public _settingsService: AbstractSettingsService, private notification: NotificationService) { } @@ -31,11 +29,10 @@ export abstract class SettingsComponent implements OnInit, OnDestroy { this._navigation.toLogin(); return; } - this.original = Utils.clone(this.settings); this.getSettings(); this.subscription = this.form.valueChanges.subscribe((data) => { - this.changed = !Utils.equalsFilter(this.settings, this.original); + this.changed = !Utils.equalsFilter(this._settingsService.settings, this._settingsService.original); }); } @@ -46,10 +43,7 @@ export abstract class SettingsComponent implements OnInit, OnDestroy { } private async getSettings() { - const s = await this._settingsService.getSettings(); - this.original = Utils.clone(s); - this.settings = s; - console.log(this.settings); + await this._settingsService.getSettings(); this.changed = false; } @@ -58,12 +52,11 @@ export abstract class SettingsComponent implements OnInit, OnDestroy { } - public async save() { this.inProgress = true; this.error = ""; try { - await this._settingsService.updateSettings(this.settings); + await this._settingsService.updateSettings(this._settingsService.settings); await this.getSettings(); this.notification.success(this.name + ' settings saved', "Success"); } catch (err) { diff --git a/frontend/app/settings/_abstract/abstract.settings.service.ts b/frontend/app/settings/_abstract/abstract.settings.service.ts index 4717cf57..bec2cfa7 100644 --- a/frontend/app/settings/_abstract/abstract.settings.service.ts +++ b/frontend/app/settings/_abstract/abstract.settings.service.ts @@ -1,4 +1,29 @@ -export interface ISettingsService { - getSettings(): Promise; - updateSettings(settings: T): Promise; +import {Utils} from "../../../../common/Utils"; +import {SettingsService} from "../settings.service"; +import {IPrivateConfig} from "../../../../common/config/private/IPrivateConfig"; +export abstract class AbstractSettingsService { + public settings: T = {}; + public original: T = {}; + + constructor(protected _settingsService: SettingsService, private sliceFN: (s: IPrivateConfig) => T) { + this.original = Utils.clone(this.settings); + this._settingsService.settings.subscribe(this.onNewSettings); + this.onNewSettings(this._settingsService.settings.value); + } + + onNewSettings = (s) => { + this.settings = Utils.clone(this.sliceFN(s)); + this.original = Utils.clone(this.settings); + }; + + + public getSettings(): Promise { + return this._settingsService.getSettings(); + } + + isSupported() { + return true; + } + + abstract updateSettings(settings: T): Promise; } diff --git a/frontend/app/settings/database/database.settings.component.html b/frontend/app/settings/database/database.settings.component.html index b269161d..b00baf4d 100644 --- a/frontend/app/settings/database/database.settings.component.html +++ b/frontend/app/settings/database/database.settings.component.html @@ -6,20 +6,20 @@

Type:

- - +

MySQL settings:

+ [(ngModel)]="_settingsService.settings.mysql.host" name="host" required> + [(ngModel)]="_settingsService.settings.mysql.database" name="database" required> + [(ngModel)]="_settingsService.settings.mysql.username" name="username"> + [(ngModel)]="_settingsService.settings.mysql.password" name="password">
diff --git a/frontend/app/settings/database/database.settings.component.ts b/frontend/app/settings/database/database.settings.component.ts index 009b95db..8d411be6 100644 --- a/frontend/app/settings/database/database.settings.component.ts +++ b/frontend/app/settings/database/database.settings.component.ts @@ -15,18 +15,15 @@ import {DatabaseSettingsService} from "./database.settings.service"; providers: [DatabaseSettingsService], }) export class DatabaseSettingsComponent extends SettingsComponent { - public settings: DataBaseConfig = { - type: DatabaseType.memory, - mysql: {} - }; + public types: Array = []; public DatabaseType: any; constructor(_authService: AuthenticationService, _navigation: NavigationService, - _dbSettings: DatabaseSettingsService, + _settingsService: DatabaseSettingsService, notification: NotificationService) { - super("Database", _authService, _navigation, _dbSettings, notification); + super("Database", _authService, _navigation, _settingsService, notification); } ngOnInit() { diff --git a/frontend/app/settings/database/database.settings.service.ts b/frontend/app/settings/database/database.settings.service.ts index 1dcf7bd4..75f7eacd 100644 --- a/frontend/app/settings/database/database.settings.service.ts +++ b/frontend/app/settings/database/database.settings.service.ts @@ -1,15 +1,16 @@ import {Injectable} from "@angular/core"; import {NetworkService} from "../../model/network/network.service"; -import {DataBaseConfig, IPrivateConfig} from "../../../../common/config/private/IPrivateConfig"; +import {DataBaseConfig} from "../../../../common/config/private/IPrivateConfig"; +import {AbstractSettingsService} from "../_abstract/abstract.settings.service"; +import {SettingsService} from "../settings.service"; @Injectable() -export class DatabaseSettingsService { - constructor(private _networkService: NetworkService) { +export class DatabaseSettingsService extends AbstractSettingsService { + constructor(private _networkService: NetworkService, + _settingsService: SettingsService) { + super(_settingsService, s => s.Server.database); } - public async getSettings(): Promise { - return (await >this._networkService.getJson("/settings")).Server.database; - } public updateSettings(settings: DataBaseConfig): Promise { return this._networkService.putJson("/settings/database", {settings: settings}); diff --git a/frontend/app/settings/map/map.settings.component.html b/frontend/app/settings/map/map.settings.component.html index 763377a1..1f6b1927 100644 --- a/frontend/app/settings/map/map.settings.component.html +++ b/frontend/app/settings/map/map.settings.component.html @@ -13,7 +13,7 @@ [switch-disabled]="inProgress" [switch-handle-width]="'100'" [switch-label-width]="'20'" - [(ngModel)]="settings.enabled"> + [(ngModel)]="_settingsService.settings.enabled"> @@ -22,8 +22,8 @@ To show the images on a map, google api key is need diff --git a/frontend/app/settings/map/map.settings.component.ts b/frontend/app/settings/map/map.settings.component.ts index 244954e9..4fd90de3 100644 --- a/frontend/app/settings/map/map.settings.component.ts +++ b/frontend/app/settings/map/map.settings.component.ts @@ -14,16 +14,12 @@ import {ClientConfig} from "../../../../common/config/public/ConfigClass"; providers: [MapSettingsService], }) export class MapSettingsComponent extends SettingsComponent { - public settings: ClientConfig.MapConfig = { - enabled: true, - googleApiKey: "" - }; constructor(_authService: AuthenticationService, _navigation: NavigationService, - _settingsSettings: MapSettingsService, + _settingsService: MapSettingsService, notification: NotificationService) { - super("Map", _authService, _navigation, _settingsSettings, notification); + super("Map", _authService, _navigation, _settingsService, notification); } diff --git a/frontend/app/settings/map/map.settings.service.ts b/frontend/app/settings/map/map.settings.service.ts index 926dacb0..25410431 100644 --- a/frontend/app/settings/map/map.settings.service.ts +++ b/frontend/app/settings/map/map.settings.service.ts @@ -1,15 +1,15 @@ import {Injectable} from "@angular/core"; import {NetworkService} from "../../model/network/network.service"; -import {IPrivateConfig} from "../../../../common/config/private/IPrivateConfig"; import {ClientConfig} from "../../../../common/config/public/ConfigClass"; +import {SettingsService} from "../settings.service"; +import {AbstractSettingsService} from "../_abstract/abstract.settings.service"; @Injectable() -export class MapSettingsService { - constructor(private _networkService: NetworkService) { - } +export class MapSettingsService extends AbstractSettingsService { + constructor(private _networkService: NetworkService, + _settingsService: SettingsService) { + super(_settingsService, s => s.Client.Map); - public async getSettings(): Promise { - return (await >this._networkService.getJson("/settings")).Client.Map; } public updateSettings(settings: ClientConfig.MapConfig): Promise { diff --git a/frontend/app/settings/search/search.settings.component.css b/frontend/app/settings/search/search.settings.component.css new file mode 100644 index 00000000..e69de29b diff --git a/frontend/app/settings/search/search.settings.component.html b/frontend/app/settings/search/search.settings.component.html new file mode 100644 index 00000000..d9f6fbdf --- /dev/null +++ b/frontend/app/settings/search/search.settings.component.html @@ -0,0 +1,73 @@ +
+
+
+

Search settings

+
+ + +
+
+
+ + +
+ +
+ + +
+
+ + +
+ +
+ + +
+
+ + + +
+
+ +
diff --git a/frontend/app/settings/search/search.settings.component.ts b/frontend/app/settings/search/search.settings.component.ts new file mode 100644 index 00000000..0d3da22d --- /dev/null +++ b/frontend/app/settings/search/search.settings.component.ts @@ -0,0 +1,29 @@ +import {Component} from "@angular/core"; +import {SettingsComponent} from "../_abstract/abstract.settings.component"; +import {AuthenticationService} from "../../model/network/authentication.service"; +import {NavigationService} from "../../model/navigation.service"; +import {NotificationService} from "../../model/notification.service"; +import {ClientConfig} from "../../../../common/config/public/ConfigClass"; +import {SearchSettingsService} from "./search.settings.service"; + +@Component({ + selector: 'settings-search', + templateUrl: './search.settings.component.html', + styleUrls: ['./search.settings.component.css', + './../_abstract/abstract.settings.component.css'], + providers: [SearchSettingsService], +}) +export class SearchSettingsComponent extends SettingsComponent { + + constructor(_authService: AuthenticationService, + _navigation: NavigationService, + _settingsService: SearchSettingsService, + notification: NotificationService) { + super("Search", _authService, _navigation, _settingsService, notification); + } + + +} + + + diff --git a/frontend/app/settings/search/search.settings.service.ts b/frontend/app/settings/search/search.settings.service.ts new file mode 100644 index 00000000..e70d122f --- /dev/null +++ b/frontend/app/settings/search/search.settings.service.ts @@ -0,0 +1,25 @@ +import {Injectable} from "@angular/core"; +import {NetworkService} from "../../model/network/network.service"; +import {DatabaseType} from "../../../../common/config/private/IPrivateConfig"; +import {ClientConfig} from "../../../../common/config/public/ConfigClass"; +import {SettingsService} from "../settings.service"; +import {AbstractSettingsService} from "../_abstract/abstract.settings.service"; + +@Injectable() +export class SearchSettingsService extends AbstractSettingsService { + constructor(private _networkService: NetworkService, + _settingsService: SettingsService) { + super(_settingsService, s => s.Client.Search); + + } + + + public isSupported(): boolean { + return this._settingsService.settings.value.Server.database.type != DatabaseType.memory; + } + + public updateSettings(settings: ClientConfig.SearchConfig): Promise { + return this._networkService.putJson("/settings/search", {settings: settings}); + } + +} diff --git a/frontend/app/settings/settings.service.ts b/frontend/app/settings/settings.service.ts new file mode 100644 index 00000000..b2decf3e --- /dev/null +++ b/frontend/app/settings/settings.service.ts @@ -0,0 +1,58 @@ +import {Injectable} from "@angular/core"; +import {BehaviorSubject} from "rxjs/BehaviorSubject"; +import {DatabaseType, IPrivateConfig, ThumbnailProcessingLib} from "../../../common/config/private/IPrivateConfig"; +import {NetworkService} from "../model/network/network.service"; +@Injectable() +export class SettingsService { + public settings: BehaviorSubject; + + constructor(private _networkService: NetworkService) { + this.settings = new BehaviorSubject({ + Client: { + Search: { + enabled: true, + autocompleteEnabled: true, + instantSearchEnabled: true + }, + Thumbnail: { + iconSize: 30, + thumbnailSizes: [] + }, + Sharing: { + enabled: true, + passwordProtected: true + }, + Map: { + enabled: true, + googleApiKey: "" + }, publicUrl: "", + applicationTitle: "", + enableCache: true, + enableOnScrollRendering: true, + enableOnScrollThumbnailPrioritising: true, + authenticationRequired: true + }, Server: { + database: { + type: DatabaseType.memory + }, + imagesFolder: "", + sharing: { + updateTimeout: 2000 + }, + enableThreading: true, + port: 80, + thumbnail: { + folder: "", + qualityPriority: true, + processingLibrary: ThumbnailProcessingLib.sharp + } + } + }); + } + + public async getSettings(): Promise { + this.settings.next(await >this._networkService.getJson("/settings")); + } + + +} diff --git a/frontend/app/settings/thumbnail/thumbanil.settings.component.html b/frontend/app/settings/thumbnail/thumbanil.settings.component.html index 63791131..7c61e4da 100644 --- a/frontend/app/settings/thumbnail/thumbanil.settings.component.html +++ b/frontend/app/settings/thumbnail/thumbanil.settings.component.html @@ -5,7 +5,8 @@
- @@ -13,12 +14,15 @@
- - Make sure that sharp node module is installed (npm install sharp) - Make sure that gm node module and Make sure that sharp node module is installed (npm install sharp) + Make sure that gm node module and GraphicsMagick are installed (npm install sharp)
@@ -29,7 +33,7 @@
Thumbnails will be saved in this folder. Write access is required @@ -48,7 +52,7 @@ [switch-on-text]="'High'" [switch-handle-width]="'100'" [switch-label-width]="'20'" - [(ngModel)]="settings.server.qualityPriority"> + [(ngModel)]="_settingsService.settings.server.qualityPriority"> High quality may be slow. Especially with Jimp. @@ -60,7 +64,7 @@
{ - public settings: { server: ThumbnailConfig, client: ClientConfig.ThumbnailConfig } = <{ server: ThumbnailConfig, client: ClientConfig.ThumbnailConfig }> { - server: { - folder: "", - processingLibrary: ThumbnailProcessingLib.Jimp, - qualityPriority: true - }, - client: { - iconSize: null, - thumbnailSizes: [] - } - }; types: Array = []; ThumbnailProcessingLib: any; constructor(_authService: AuthenticationService, _navigation: NavigationService, - _settingsSettings: ThumbnailSettingsService, + _settingsService: ThumbnailSettingsService, notification: NotificationService) { - super("Thumbnail", _authService, _navigation, _settingsSettings, notification); + super("Thumbnail", _authService, _navigation, _settingsService, notification); } get ThumbnailSizes(): string { - return this.settings.client.thumbnailSizes.join("; "); + return this._settingsService.settings.client.thumbnailSizes.join("; "); } set ThumbnailSizes(value: string) { value = value.replace(new RegExp(',', 'g'), ";"); value = value.replace(new RegExp(' ', 'g'), ";"); - this.settings.client.thumbnailSizes = value.split(";").map(s => parseInt(s)).filter(i => !isNaN(i) && i > 0); + this._settingsService.settings.client.thumbnailSizes = value.split(";").map(s => parseInt(s)).filter(i => !isNaN(i) && i > 0); } ngOnInit() { diff --git a/frontend/app/settings/thumbnail/thumbanil.settings.service.ts b/frontend/app/settings/thumbnail/thumbanil.settings.service.ts index 40b8b7f2..f6a3bd11 100644 --- a/frontend/app/settings/thumbnail/thumbanil.settings.service.ts +++ b/frontend/app/settings/thumbnail/thumbanil.settings.service.ts @@ -1,18 +1,17 @@ import {Injectable} from "@angular/core"; import {NetworkService} from "../../model/network/network.service"; import {ClientConfig} from "../../../../common/config/public/ConfigClass"; -import {IPrivateConfig, ThumbnailConfig} from "../../../../common/config/private/IPrivateConfig"; -import {ISettingsService} from "../_abstract/abstract.settings.service"; +import {ThumbnailConfig} from "../../../../common/config/private/IPrivateConfig"; +import {AbstractSettingsService} from "../_abstract/abstract.settings.service"; +import {SettingsService} from "../settings.service"; @Injectable() -export class ThumbnailSettingsService implements ISettingsService<{ server: ThumbnailConfig, client: ClientConfig.ThumbnailConfig }> { - constructor(private _networkService: NetworkService) { +export class ThumbnailSettingsService extends AbstractSettingsService<{ server: ThumbnailConfig, client: ClientConfig.ThumbnailConfig }> { + constructor(private _networkService: NetworkService, + _settingsService: SettingsService) { + super(_settingsService, s => ({client: s.Client.Thumbnail, server: s.Server.thumbnail})); } - public async getSettings(): Promise<{ server: ThumbnailConfig, client: ClientConfig.ThumbnailConfig }> { - const settings = (await >this._networkService.getJson("/settings")); - return {server: settings.Server.thumbnail, client: settings.Client.Thumbnail}; - } public updateSettings(settings: { server: ThumbnailConfig, client: ClientConfig.ThumbnailConfig }): Promise { return this._networkService.putJson("/settings/thumbnail", {settings: settings});