diff --git a/.angular-cli.json b/.angular-cli.json index 2eb7b035..d1c232ba 100644 --- a/.angular-cli.json +++ b/.angular-cli.json @@ -20,6 +20,7 @@ "testTsconfig": "tsconfig.spec.json", "prefix": "app", "styles": [ + "../node_modules/bootstrap/dist/css/bootstrap.css", "styles.css" ], "scripts": [], diff --git a/backend/middlewares/AdminMWs.ts b/backend/middlewares/AdminMWs.ts index 1d395d05..9db3b344 100644 --- a/backend/middlewares/AdminMWs.ts +++ b/backend/middlewares/AdminMWs.ts @@ -1,12 +1,13 @@ import {NextFunction, Request, Response} from "express"; -import {Error, ErrorCodes} from "../../common/entities/Error"; +import {ErrorCodes, ErrorDTO} from "../../common/entities/Error"; import {ObjectManagerRepository} from "../model/ObjectManagerRepository"; import {Logger} from "../Logger"; import {MySQLConnection} from "../model/mysql/MySQLConnection"; -import {DataBaseConfig, DatabaseType} from "../../common/config/private/IPrivateConfig"; +import {DataBaseConfig, DatabaseType, ThumbnailConfig} from "../../common/config/private/IPrivateConfig"; import {Config} from "../../common/config/private/Config"; import {ConfigDiagnostics} from "../model/ConfigDiagnostics"; -import {MapConfig} from "../../common/config/public/ConfigClass"; +import {ClientConfig} from "../../common/config/public/ConfigClass"; +import set = Reflect.set; const LOG_TAG = "[AdminMWs]"; @@ -16,7 +17,7 @@ export class AdminMWs { public static async updateDatabaseSettings(req: Request, res: Response, next: NextFunction) { if ((typeof req.body === 'undefined') || (typeof req.body.settings === 'undefined')) { - return next(new Error(ErrorCodes.INPUT_ERROR, "settings is needed")); + return next(new ErrorDTO(ErrorCodes.INPUT_ERROR, "settings is needed")); } const databaseSettings = req.body.settings; @@ -43,61 +44,83 @@ export class AdminMWs { return next(); } catch (err) { - return next(new Error(ErrorCodes.SETTINGS_ERROR, "Error saving database settings", err)); + return next(new ErrorDTO(ErrorCodes.SETTINGS_ERROR, "ErrorDTO saving database settings", err)); } } - - public static async testDatabaseSettings(req: Request, res: Response, next: NextFunction) { - if ((typeof req.body === 'undefined') || (typeof req.body.settings === 'undefined')) { - return next(new Error(ErrorCodes.INPUT_ERROR, "settings is needed")); - } - - const databaseSettings = req.body.settings; - - try { - if (databaseSettings.type == DatabaseType.mysql) { - await ConfigDiagnostics.testDatabase(databaseSettings); - } - return next(); - } catch (err) { - return next(new Error(ErrorCodes.SETTINGS_ERROR, "Settings error: " + JSON.stringify(err, null, ' '), err)); - } - } - - public static async updateMapSettings(req: Request, res: Response, next: NextFunction) { if ((typeof req.body === 'undefined') || (typeof req.body.settings === 'undefined')) { - return next(new Error(ErrorCodes.INPUT_ERROR, "settings is needed")); + return next(new ErrorDTO(ErrorCodes.INPUT_ERROR, "settings is needed")); } try { - await ConfigDiagnostics.testMapConfig(req.body.settings); + await ConfigDiagnostics.testMapConfig(req.body.settings); - Config.Client.Map = req.body.settings; + Config.Client.Map = req.body.settings; //only updating explicitly set config (not saving config set by the diagnostics) const original = Config.original(); - original.Client.Map = req.body.settings; + original.Client.Map = 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 Error(ErrorCodes.SETTINGS_ERROR, "Settings error: " + JSON.stringify(err, null, ' '), err)); + return next(new ErrorDTO(ErrorCodes.SETTINGS_ERROR, "Settings error: " + JSON.stringify(err, null, ' '), err)); } } - public static async testMapSettings(req: Request, res: Response, next: NextFunction) { + public static async updateAuthenticationSettings(req: Request, res: Response, next: NextFunction) { if ((typeof req.body === 'undefined') || (typeof req.body.settings === 'undefined')) { - return next(new Error(ErrorCodes.INPUT_ERROR, "settings is needed")); + return next(new ErrorDTO(ErrorCodes.INPUT_ERROR, "settings is needed")); } try { - await ConfigDiagnostics.testMapConfig(req.body.settings); + Config.Client.authenticationRequired = req.body.settings; + //only updating explicitly set config (not saving config set by the diagnostics) + const original = Config.original(); + original.Client.authenticationRequired = 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 Error(ErrorCodes.SETTINGS_ERROR, "Settings error: " + JSON.stringify(err, null, ' '), 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) { + if ((typeof req.body === 'undefined') || (typeof req.body.settings === 'undefined')) { + return next(new ErrorDTO(ErrorCodes.INPUT_ERROR, "settings is needed")); + } + + try { + const settings: { + server: ThumbnailConfig, + client: ClientConfig.ThumbnailConfig + } = req.body.settings; + + await ConfigDiagnostics.testServerThumbnailConfig(settings.server); + await ConfigDiagnostics.testClientThumbnailConfig(settings.client); + Config.Server.thumbnail = settings.server; + Config.Client.Thumbnail = settings.client; + //only updating explicitly set config (not saving config set by the diagnostics) + const original = Config.original(); + original.Server.thumbnail = settings.server; + original.Client.Thumbnail = 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)); + } + } + + } diff --git a/backend/middlewares/GalleryMWs.ts b/backend/middlewares/GalleryMWs.ts index 2cf84e18..952960e7 100644 --- a/backend/middlewares/GalleryMWs.ts +++ b/backend/middlewares/GalleryMWs.ts @@ -1,7 +1,7 @@ import * as path from "path"; import * as fs from "fs"; import {NextFunction, Request, Response} from "express"; -import {Error, ErrorCodes} from "../../common/entities/Error"; +import {ErrorCodes, ErrorDTO} from "../../common/entities/Error"; import {DirectoryDTO} from "../../common/entities/DirectoryDTO"; import {ObjectManagerRepository} from "../model/ObjectManagerRepository"; import {SearchTypes} from "../../common/entities/AutoCompleteItem"; @@ -37,7 +37,7 @@ export class GalleryMWs { return next(); } catch (err) { - return next(new Error(ErrorCodes.GENERAL_ERROR, "Error during listing the directory", err)); + return next(new ErrorDTO(ErrorCodes.GENERAL_ERROR, "ErrorDTO during listing the directory", err)); } } @@ -80,7 +80,7 @@ export class GalleryMWs { //check if thumbnail already exist if (fs.existsSync(fullImagePath) === false) { - return next(new Error(ErrorCodes.GENERAL_ERROR, "no such file:" + fullImagePath)); + return next(new ErrorDTO(ErrorCodes.GENERAL_ERROR, "no such file:" + fullImagePath)); } req.resultPipe = fullImagePath; @@ -108,7 +108,7 @@ export class GalleryMWs { req.resultPipe = new ContentWrapper(null, result); return next(); } catch (err) { - return next(new Error(ErrorCodes.GENERAL_ERROR, "Error during searching", err)); + return next(new ErrorDTO(ErrorCodes.GENERAL_ERROR, "ErrorDTO during searching", err)); } } @@ -128,7 +128,7 @@ export class GalleryMWs { req.resultPipe = new ContentWrapper(null, result); return next(); } catch (err) { - return next(new Error(ErrorCodes.GENERAL_ERROR, "Error during searching", err)); + return next(new ErrorDTO(ErrorCodes.GENERAL_ERROR, "ErrorDTO during searching", err)); } } @@ -144,7 +144,7 @@ export class GalleryMWs { req.resultPipe = await ObjectManagerRepository.getInstance().SearchManager.autocomplete(req.params.text); return next(); } catch (err) { - return next(new Error(ErrorCodes.GENERAL_ERROR, "Error during searching", err)); + return next(new ErrorDTO(ErrorCodes.GENERAL_ERROR, "ErrorDTO during searching", err)); } } diff --git a/backend/middlewares/RenderingMWs.ts b/backend/middlewares/RenderingMWs.ts index c2a0ee02..42347858 100644 --- a/backend/middlewares/RenderingMWs.ts +++ b/backend/middlewares/RenderingMWs.ts @@ -1,5 +1,5 @@ import {NextFunction, Request, Response} from "express"; -import {Error, ErrorCodes} from "../../common/entities/Error"; +import {ErrorCodes, ErrorDTO} from "../../common/entities/Error"; import {Utils} from "../../common/Utils"; import {Message} from "../../common/entities/Message"; import {SharingDTO} from "../../common/entities/SharingDTO"; @@ -21,7 +21,7 @@ export class RenderingMWs { public static renderSessionUser(req: Request, res: Response, next: NextFunction) { if (!(req.session.user)) { - return next(new Error(ErrorCodes.GENERAL_ERROR, "User not exists")); + return next(new ErrorDTO(ErrorCodes.GENERAL_ERROR, "User not exists")); } const user = Utils.clone(req.session.user); @@ -59,7 +59,7 @@ export class RenderingMWs { public static renderError(err: any, req: Request, res: Response, next: NextFunction): any { - if (err instanceof Error) { + if (err instanceof ErrorDTO) { if (err.details) { if (!(req.session.user && req.session.user.role >= UserRoles.Developer)) { Logger.warn("Handled error:", err.details.toString() || err.details); diff --git a/backend/middlewares/SharingMWs.ts b/backend/middlewares/SharingMWs.ts index 25bff569..67e1b123 100644 --- a/backend/middlewares/SharingMWs.ts +++ b/backend/middlewares/SharingMWs.ts @@ -1,7 +1,7 @@ import {NextFunction, Request, Response} from "express"; import {CreateSharingDTO, SharingDTO} from "../../common/entities/SharingDTO"; import {ObjectManagerRepository} from "../model/ObjectManagerRepository"; -import {Error, ErrorCodes} from "../../common/entities/Error"; +import {ErrorCodes, ErrorDTO} from "../../common/entities/Error"; const LOG_TAG = "[SharingMWs]"; export class SharingMWs { @@ -26,14 +26,14 @@ export class SharingMWs { return next(); } catch (err) { - return next(new Error(ErrorCodes.GENERAL_ERROR, "Error during retrieving sharing link", err)); + return next(new ErrorDTO(ErrorCodes.GENERAL_ERROR, "ErrorDTO during retrieving sharing link", err)); } } public static async createSharing(req: Request, res: Response, next: NextFunction) { if ((typeof req.body === 'undefined') || (typeof req.body.createSharing === 'undefined')) { - return next(new Error(ErrorCodes.INPUT_ERROR, "createSharing filed is missing")); + return next(new ErrorDTO(ErrorCodes.INPUT_ERROR, "createSharing filed is missing")); } const createSharing: CreateSharingDTO = req.body.createSharing; let sharingKey = SharingMWs.generateKey(); @@ -68,13 +68,13 @@ export class SharingMWs { return next(); } catch (err) { - return next(new Error(ErrorCodes.GENERAL_ERROR, "Error during creating sharing link", err)); + return next(new ErrorDTO(ErrorCodes.GENERAL_ERROR, "ErrorDTO during creating sharing link", err)); } } public static async updateSharing(req: Request, res: Response, next: NextFunction) { if ((typeof req.body === 'undefined') || (typeof req.body.updateSharing === 'undefined')) { - return next(new Error(ErrorCodes.INPUT_ERROR, "updateSharing filed is missing")); + return next(new ErrorDTO(ErrorCodes.INPUT_ERROR, "updateSharing filed is missing")); } const updateSharing: CreateSharingDTO = req.body.updateSharing; const directoryName = req.params.directory || "/"; @@ -95,7 +95,7 @@ export class SharingMWs { return next(); } catch (err) { - return next(new Error(ErrorCodes.GENERAL_ERROR, "Error during creating sharing link", err)); + return next(new ErrorDTO(ErrorCodes.GENERAL_ERROR, "ErrorDTO during creating sharing link", err)); } } diff --git a/backend/middlewares/thumbnail/ThumbnailGeneratorMWs.ts b/backend/middlewares/thumbnail/ThumbnailGeneratorMWs.ts index 44cd9307..0dbe000e 100644 --- a/backend/middlewares/thumbnail/ThumbnailGeneratorMWs.ts +++ b/backend/middlewares/thumbnail/ThumbnailGeneratorMWs.ts @@ -4,7 +4,7 @@ import * as crypto from "crypto"; import * as fs from "fs"; import * as os from "os"; import {NextFunction, Request, Response} from "express"; -import {Error, ErrorCodes} from "../../../common/entities/Error"; +import {ErrorCodes, ErrorDTO} from "../../../common/entities/Error"; import {ContentWrapper} from "../../../common/entities/ConentWrapper"; import {DirectoryDTO} from "../../../common/entities/DirectoryDTO"; import {ProjectPath} from "../../ProjectPath"; @@ -62,8 +62,8 @@ export class ThumbnailGeneratorMWs { let thumbnailFolder = ProjectPath.ThumbnailFolder; for (let i = 0; i < photos.length; i++) { let fullImagePath = path.join(ProjectPath.ImageFolder, photos[i].directory.path, photos[i].directory.name, photos[i].name); - for (let j = 0; j < Config.Client.thumbnailSizes.length; j++) { - let size = Config.Client.thumbnailSizes[j]; + for (let j = 0; j < Config.Client.Thumbnail.thumbnailSizes.length; j++) { + let size = Config.Client.Thumbnail.thumbnailSizes[j]; let thPath = path.join(thumbnailFolder, ThumbnailGeneratorMWs.generateThumbnailName(fullImagePath, size)); if (fs.existsSync(thPath) === true) { if (typeof photos[i].readyThumbnails == "undefined") { @@ -72,7 +72,7 @@ export class ThumbnailGeneratorMWs { photos[i].readyThumbnails.push(size); } } - let iconPath = path.join(thumbnailFolder, ThumbnailGeneratorMWs.generateThumbnailName(fullImagePath, Config.Client.iconSize)); + let iconPath = path.join(thumbnailFolder, ThumbnailGeneratorMWs.generateThumbnailName(fullImagePath, Config.Client.Thumbnail.iconSize)); if (fs.existsSync(iconPath) === true) { photos[i].readyIcon = true; } @@ -103,11 +103,11 @@ export class ThumbnailGeneratorMWs { //load parameters let imagePath = req.resultPipe; - let size: number = parseInt(req.params.size) || Config.Client.thumbnailSizes[0]; + let size: number = parseInt(req.params.size) || Config.Client.Thumbnail.thumbnailSizes[0]; //validate size - if (Config.Client.thumbnailSizes.indexOf(size) === -1) { - size = Config.Client.thumbnailSizes[0]; + if (Config.Client.Thumbnail.thumbnailSizes.indexOf(size) === -1) { + size = Config.Client.Thumbnail.thumbnailSizes[0]; } ThumbnailGeneratorMWs.generateImage(imagePath, size, false, req, res, next); @@ -121,7 +121,7 @@ export class ThumbnailGeneratorMWs { //load parameters let imagePath = req.resultPipe; - let size: number = Config.Client.iconSize; + let size: number = Config.Client.Thumbnail.iconSize; ThumbnailGeneratorMWs.generateImage(imagePath, size, true, req, res, next); @@ -162,7 +162,7 @@ export class ThumbnailGeneratorMWs { return next(); } } catch (error) { - return next(new Error(ErrorCodes.THUMBNAIL_GENERATION_ERROR, "Error during generating thumbnail", error)); + return next(new ErrorDTO(ErrorCodes.THUMBNAIL_GENERATION_ERROR, "ErrorDTO during generating thumbnail", error)); } } diff --git a/backend/middlewares/user/AuthenticationMWs.ts b/backend/middlewares/user/AuthenticationMWs.ts index 7d119929..59ef14a7 100644 --- a/backend/middlewares/user/AuthenticationMWs.ts +++ b/backend/middlewares/user/AuthenticationMWs.ts @@ -1,6 +1,6 @@ /// import {NextFunction, Request, Response} from "express"; -import {Error, ErrorCodes} from "../../../common/entities/Error"; +import {ErrorCodes, ErrorDTO} from "../../../common/entities/Error"; import {UserDTO, UserRoles} from "../../../common/entities/UserDTO"; import {ObjectManagerRepository} from "../../model/ObjectManagerRepository"; import {Config} from "../../../common/config/private/Config"; @@ -63,10 +63,10 @@ export class AuthenticationMWs { return next(); } } catch (err) { - return next(new Error(ErrorCodes.CREDENTIAL_NOT_FOUND, null, err)); + return next(new ErrorDTO(ErrorCodes.CREDENTIAL_NOT_FOUND, null, err)); } if (typeof req.session.user === 'undefined') { - return next(new Error(ErrorCodes.NOT_AUTHENTICATED)); + return next(new ErrorDTO(ErrorCodes.NOT_AUTHENTICATED)); } return next(); } @@ -74,7 +74,7 @@ export class AuthenticationMWs { public static authorise(role: UserRoles) { return (req: Request, res: Response, next: NextFunction) => { if (req.session.user.role < role) { - return next(new Error(ErrorCodes.NOT_AUTHORISED)); + return next(new ErrorDTO(ErrorCodes.NOT_AUTHORISED)); } return next(); }; @@ -92,12 +92,12 @@ export class AuthenticationMWs { return next(); } - return next(new Error(ErrorCodes.PERMISSION_DENIED)); + return next(new ErrorDTO(ErrorCodes.PERMISSION_DENIED)); } public static inverseAuthenticate(req: Request, res: Response, next: NextFunction) { if (typeof req.session.user !== 'undefined') { - return next(new Error(ErrorCodes.ALREADY_AUTHENTICATED)); + return next(new ErrorDTO(ErrorCodes.ALREADY_AUTHENTICATED)); } return next(); } @@ -107,7 +107,7 @@ export class AuthenticationMWs { //not enough parameter if ((typeof req.body === 'undefined') || (typeof req.body.loginCredential === 'undefined') || (typeof req.body.loginCredential.username === 'undefined') || (typeof req.body.loginCredential.password === 'undefined')) { - return next(new Error(ErrorCodes.INPUT_ERROR)); + return next(new ErrorDTO(ErrorCodes.INPUT_ERROR)); } //TODO: implement remember me try { @@ -127,10 +127,10 @@ export class AuthenticationMWs { return next(); } } catch (err) { - return next(new Error(ErrorCodes.CREDENTIAL_NOT_FOUND, null, err)); + return next(new ErrorDTO(ErrorCodes.CREDENTIAL_NOT_FOUND, null, err)); } - return next(new Error(ErrorCodes.CREDENTIAL_NOT_FOUND)); + return next(new ErrorDTO(ErrorCodes.CREDENTIAL_NOT_FOUND)); } @@ -144,7 +144,7 @@ export class AuthenticationMWs { } //not enough parameter if ((!req.query.sk && !req.params.sharingKey)) { - return next(new Error(ErrorCodes.INPUT_ERROR, "no sharing key provided")); + return next(new ErrorDTO(ErrorCodes.INPUT_ERROR, "no sharing key provided")); } try { @@ -156,7 +156,7 @@ export class AuthenticationMWs { if (!sharing || sharing.expires < Date.now() || (Config.Client.Sharing.passwordProtected === true && sharing.password && !PasswordHelper.comparePassword(password, sharing.password))) { - return next(new Error(ErrorCodes.CREDENTIAL_NOT_FOUND)); + return next(new ErrorDTO(ErrorCodes.CREDENTIAL_NOT_FOUND)); } let path = sharing.path; @@ -168,7 +168,7 @@ export class AuthenticationMWs { return next(); } catch (err) { - return next(new Error(ErrorCodes.GENERAL_ERROR, null, err)); + return next(new ErrorDTO(ErrorCodes.GENERAL_ERROR, null, err)); } } diff --git a/backend/middlewares/user/UserMWs.ts b/backend/middlewares/user/UserMWs.ts index 966e5ecf..0c5b9dc4 100644 --- a/backend/middlewares/user/UserMWs.ts +++ b/backend/middlewares/user/UserMWs.ts @@ -1,5 +1,5 @@ import {NextFunction, Request, Response} from "express"; -import {Error, ErrorCodes} from "../../../common/entities/Error"; +import {ErrorCodes, ErrorDTO} from "../../../common/entities/Error"; import {ObjectManagerRepository} from "../../model/ObjectManagerRepository"; import {Utils} from "../../../common/Utils"; import {Config} from "../../../common/config/private/Config"; @@ -8,7 +8,7 @@ export class UserMWs { public static async changePassword(req: Request, res: Response, next: NextFunction) { if (Config.Client.authenticationRequired === false) { - return next(new Error(ErrorCodes.USER_MANAGEMENT_DISABLED)); + return next(new ErrorDTO(ErrorCodes.USER_MANAGEMENT_DISABLED)); } if ((typeof req.body === 'undefined') || (typeof req.body.userModReq === 'undefined') || (typeof req.body.userModReq.id === 'undefined') @@ -22,14 +22,14 @@ export class UserMWs { return next(); } catch (err) { - return next(new Error(ErrorCodes.GENERAL_ERROR, null, err)); + return next(new ErrorDTO(ErrorCodes.GENERAL_ERROR, null, err)); } } public static async createUser(req: Request, res: Response, next: NextFunction) { if (Config.Client.authenticationRequired === false) { - return next(new Error(ErrorCodes.USER_MANAGEMENT_DISABLED)); + return next(new ErrorDTO(ErrorCodes.USER_MANAGEMENT_DISABLED)); } if ((typeof req.body === 'undefined') || (typeof req.body.newUser === 'undefined')) { return next(); @@ -40,7 +40,7 @@ export class UserMWs { return next(); } catch (err) { - return next(new Error(ErrorCodes.USER_CREATION_ERROR, null, err)); + return next(new ErrorDTO(ErrorCodes.USER_CREATION_ERROR, null, err)); } @@ -48,7 +48,7 @@ export class UserMWs { public static async deleteUser(req: Request, res: Response, next: NextFunction) { if (Config.Client.authenticationRequired === false) { - return next(new Error(ErrorCodes.USER_MANAGEMENT_DISABLED)); + return next(new ErrorDTO(ErrorCodes.USER_MANAGEMENT_DISABLED)); } if ((typeof req.params === 'undefined') || (typeof req.params.id === 'undefined')) { return next(); @@ -60,7 +60,7 @@ export class UserMWs { return next(); } catch (err) { - return next(new Error(ErrorCodes.GENERAL_ERROR, null, err)); + return next(new ErrorDTO(ErrorCodes.GENERAL_ERROR, null, err)); } @@ -68,7 +68,7 @@ export class UserMWs { public static async changeRole(req: Request, res: Response, next: NextFunction) { if (Config.Client.authenticationRequired === false) { - return next(new Error(ErrorCodes.USER_MANAGEMENT_DISABLED)); + return next(new ErrorDTO(ErrorCodes.USER_MANAGEMENT_DISABLED)); } if ((typeof req.params === 'undefined') || (typeof req.params.id === 'undefined') || (typeof req.body === 'undefined') || (typeof req.body.newRole === 'undefined')) { @@ -80,14 +80,14 @@ export class UserMWs { return next(); } catch (err) { - return next(new Error(ErrorCodes.GENERAL_ERROR, null, err)); + return next(new ErrorDTO(ErrorCodes.GENERAL_ERROR, null, err)); } } public static async listUsers(req: Request, res: Response, next: NextFunction) { if (Config.Client.authenticationRequired === false) { - return next(new Error(ErrorCodes.USER_MANAGEMENT_DISABLED)); + return next(new ErrorDTO(ErrorCodes.USER_MANAGEMENT_DISABLED)); } try { @@ -99,7 +99,7 @@ export class UserMWs { req.resultPipe = result; next(); } catch (err) { - return next(new Error(ErrorCodes.GENERAL_ERROR, null, err)); + return next(new ErrorDTO(ErrorCodes.GENERAL_ERROR, null, err)); } } diff --git a/backend/middlewares/user/UserRequestConstrainsMWs.ts b/backend/middlewares/user/UserRequestConstrainsMWs.ts index f8895723..29aa1327 100644 --- a/backend/middlewares/user/UserRequestConstrainsMWs.ts +++ b/backend/middlewares/user/UserRequestConstrainsMWs.ts @@ -1,5 +1,5 @@ import {NextFunction, Request, Response} from "express"; -import {Error, ErrorCodes} from "../../../common/entities/Error"; +import {ErrorCodes, ErrorDTO} from "../../../common/entities/Error"; import {UserRoles} from "../../../common/entities/UserDTO"; import {ObjectManagerRepository} from "../../model/ObjectManagerRepository"; @@ -10,7 +10,7 @@ export class UserRequestConstrainsMWs { return next(); } if (req.session.user.id !== req.params.id) { - return next(new Error(ErrorCodes.NOT_AUTHORISED)); + return next(new ErrorDTO(ErrorCodes.NOT_AUTHORISED)); } return next(); @@ -23,7 +23,7 @@ export class UserRequestConstrainsMWs { } if (req.session.user.id === req.params.id) { - return next(new Error(ErrorCodes.NOT_AUTHORISED)); + return next(new ErrorDTO(ErrorCodes.NOT_AUTHORISED)); } return next(); @@ -42,12 +42,12 @@ export class UserRequestConstrainsMWs { try { const result = await ObjectManagerRepository.getInstance().UserManager.find({minRole: UserRoles.Admin}); if (result.length <= 1) { - return next(new Error(ErrorCodes.GENERAL_ERROR)); + return next(new ErrorDTO(ErrorCodes.GENERAL_ERROR)); } return next(); } catch (err) { - return next(new Error(ErrorCodes.GENERAL_ERROR)); + return next(new ErrorDTO(ErrorCodes.GENERAL_ERROR)); } } diff --git a/backend/model/ConfigDiagnostics.ts b/backend/model/ConfigDiagnostics.ts index 10804b71..fefd42d7 100644 --- a/backend/model/ConfigDiagnostics.ts +++ b/backend/model/ConfigDiagnostics.ts @@ -10,7 +10,7 @@ import {NotificationManager} from "./NotifocationManager"; import {ProjectPath} from "../ProjectPath"; import {MySQLConnection} from "./mysql/MySQLConnection"; import * as fs from "fs"; -import {MapConfig, SearchConfig, SharingConfig} from "../../common/config/public/ConfigClass"; +import {ClientConfig} from "../../common/config/public/ConfigClass"; const LOG_TAG = "[ConfigDiagnostics]"; export class ConfigDiagnostics { @@ -32,9 +32,10 @@ export class ConfigDiagnostics { const gm = require("gm"); await new Promise((resolve, reject) => { gm(ProjectPath.FrontendFolder + "/assets/icon.png").size((err, value) => { - if (!err) { - return reject(err); + if (err) { + return reject(err.toString()); } + return resolve(); }); }); break; @@ -45,7 +46,7 @@ export class ConfigDiagnostics { await new Promise((resolve, reject) => { fs.access(folder, fs.constants.W_OK, (err) => { if (err) { - reject({message: "Error during getting write access to temp folder", error: err}); + reject({message: "ErrorDTO during getting write access to temp folder", error: err.toString()}); } }); resolve(); @@ -59,7 +60,7 @@ export class ConfigDiagnostics { } fs.access(folder, fs.constants.R_OK, (err) => { if (err) { - reject({message: "Error during getting read access to images folder", error: err}); + reject({message: "ErrorDTO during getting read access to images folder", error: err.toString()}); } }); resolve(); @@ -67,26 +68,41 @@ export class ConfigDiagnostics { } - static async testThumbnailConfig(thumbnailConfig: ThumbnailConfig) { + static async testServerThumbnailConfig(thumbnailConfig: ThumbnailConfig) { await ConfigDiagnostics.testThumbnailLib(thumbnailConfig.processingLibrary); await ConfigDiagnostics.testThumbnailFolder(thumbnailConfig.folder); } + static async testClientThumbnailConfig(thumbnailConfig: ClientConfig.ThumbnailConfig) { + if (isNaN(thumbnailConfig.iconSize) || thumbnailConfig.iconSize <= 0) { + throw "IconSize has to be >= 0 integer, got: " + thumbnailConfig.iconSize; + } - static async testSearchConfig(search: SearchConfig) { + if (!thumbnailConfig.thumbnailSizes.length) { + throw "At least one thumbnail size is needed"; + } + for (let i = 0; i < thumbnailConfig.thumbnailSizes.length; i++) { + if (isNaN(thumbnailConfig.thumbnailSizes[i]) || thumbnailConfig.thumbnailSizes[i] <= 0) { + throw "Thumbnail size has to be >= 0 integer, got: " + thumbnailConfig.thumbnailSizes[i]; + } + } + } + + + static async testSearchConfig(search: ClientConfig.SearchConfig) { if (search.enabled == true && Config.Server.database.type == DatabaseType.memory) { throw "Memory Database do not support searching"; } } - static async testSharingConfig(sharing: SharingConfig) { + static async testSharingConfig(sharing: ClientConfig.SharingConfig) { if (sharing.enabled == true && Config.Server.database.type == DatabaseType.memory) { throw "Memory Database do not support sharing"; } } - static async testMapConfig(map: MapConfig) { + static async testMapConfig(map: ClientConfig.MapConfig) { if (map.enabled == true && (!map.googleApiKey || map.googleApiKey.length == 0)) { throw "Maps need a valid google api key"; } @@ -100,8 +116,8 @@ export class ConfigDiagnostics { await ConfigDiagnostics.testDatabase(Config.Server.database); } catch (err) { Logger.warn(LOG_TAG, "[MYSQL error]", err); - Logger.warn(LOG_TAG, "Error during initializing mysql falling back temporally to memory DB"); - NotificationManager.warning("Error during initializing mysql falling back temporally to memory DB", err); + Logger.warn(LOG_TAG, "ErrorDTO during initializing mysql falling back temporally to memory DB"); + NotificationManager.warning("ErrorDTO during initializing mysql falling back temporally to memory DB", err); Config.setDatabaseType(DatabaseType.memory); } } @@ -135,6 +151,12 @@ export class ConfigDiagnostics { NotificationManager.error("Images folder error", err); Logger.error(LOG_TAG, "Images folder error", err); } + try { + await ConfigDiagnostics.testClientThumbnailConfig(Config.Client.Thumbnail) + } catch (err) { + NotificationManager.error("Thumbnail settings error", err); + Logger.error(LOG_TAG, "Thumbnail settings error", err); + } try { diff --git a/backend/model/memory/UserManager.ts b/backend/model/memory/UserManager.ts index c606fa1c..f179e9d7 100644 --- a/backend/model/memory/UserManager.ts +++ b/backend/model/memory/UserManager.ts @@ -35,7 +35,7 @@ export class UserManager implements IUserManager { this.createUser({name: "developer", password: "developer", role: UserRoles.Developer}); this.createUser({name: "admin", password: "admin", role: UserRoles.Admin}); this.createUser({name: "user", password: "user", role: UserRoles.User}); - this.createUser({name: "guest", password: "guest", role: UserRoles.LimitedGuest}); + this.createUser({name: "guest", password: "guest", role: UserRoles.Guest}); } @@ -54,8 +54,10 @@ export class UserManager implements IUserManager { public async find(filter: any) { let pass = filter.password; delete filter.password; - const users = await this.db.get("users"); + const users = (await this.db.get("users")).slice(); let i = users.length; + console.log("filer", filter); + console.log(users); while (i--) { if (pass && !(PasswordHelper.comparePassword(pass, users[i].password))) { users.splice(i, 1); @@ -65,6 +67,7 @@ export class UserManager implements IUserManager { users.splice(i, 1); } } + console.log(users); return users; } diff --git a/backend/routes/AdminRouter.ts b/backend/routes/AdminRouter.ts index 356d62d6..ece46560 100644 --- a/backend/routes/AdminRouter.ts +++ b/backend/routes/AdminRouter.ts @@ -42,13 +42,6 @@ export class AdminRouter { RenderingMWs.renderOK ); - app.post("/api/settings/test/database", - AuthenticationMWs.authenticate, - AuthenticationMWs.authorise(UserRoles.Admin), - AdminMWs.testDatabaseSettings, - RenderingMWs.renderOK - ); - app.put("/api/settings/map", AuthenticationMWs.authenticate, AuthenticationMWs.authorise(UserRoles.Admin), @@ -56,12 +49,19 @@ export class AdminRouter { RenderingMWs.renderOK ); - app.post("/api/settings/test/map", + app.put("/api/settings/authentication", AuthenticationMWs.authenticate, AuthenticationMWs.authorise(UserRoles.Admin), - AdminMWs.testMapSettings, + AdminMWs.updateAuthenticationSettings, RenderingMWs.renderOK ); + app.put("/api/settings/thumbnail", + AuthenticationMWs.authenticate, + AuthenticationMWs.authorise(UserRoles.Admin), + AdminMWs.updateThumbnailSettings, + RenderingMWs.renderOK + ); + }; diff --git a/backend/routes/ErrorRouter.ts b/backend/routes/ErrorRouter.ts index f5c57d35..67fe6333 100644 --- a/backend/routes/ErrorRouter.ts +++ b/backend/routes/ErrorRouter.ts @@ -1,5 +1,5 @@ import {RenderingMWs} from "../middlewares/RenderingMWs"; -import {Error, ErrorCodes} from "../../common/entities/Error"; +import {ErrorCodes, ErrorDTO} from "../../common/entities/Error"; import {Logger} from "../Logger"; import Request = Express.Request; import Response = Express.Response; @@ -21,7 +21,7 @@ export class ErrorRouter { app.use((err: any, req: Request, res: Response, next: Function) => { //Flush out the stack to the console Logger.error("Unexpected error:", err); - next(new Error(ErrorCodes.SERVER_ERROR, "Unknown server side error", err)); + next(new ErrorDTO(ErrorCodes.SERVER_ERROR, "Unknown server side error", err)); }, RenderingMWs.renderError ); diff --git a/common/Utils.ts b/common/Utils.ts index 5775d05a..36ee8ae3 100644 --- a/common/Utils.ts +++ b/common/Utils.ts @@ -6,6 +6,9 @@ export class Utils { } static equalsFilter(object: any, filter: any): boolean { + if (typeof filter !== "object" || filter == null) { + return object == filter; + } const keys = Object.keys(filter); for (let i = 0; i < keys.length; i++) { const key = keys[i]; diff --git a/common/config/private/IPrivateConfig.ts b/common/config/private/IPrivateConfig.ts index 186bb526..66c9d95b 100644 --- a/common/config/private/IPrivateConfig.ts +++ b/common/config/private/IPrivateConfig.ts @@ -40,5 +40,5 @@ export interface ServerConfig { } export interface IPrivateConfig { Server: ServerConfig; - Client: ClientConfig; + Client: ClientConfig.Config; } diff --git a/common/config/public/ConfigClass.ts b/common/config/public/ConfigClass.ts index 186470d7..6cdf0605 100644 --- a/common/config/public/ConfigClass.ts +++ b/common/config/public/ConfigClass.ts @@ -1,43 +1,50 @@ -export interface SearchConfig { - enabled: boolean - instantSearchEnabled: boolean - autocompleteEnabled: boolean -} +export module ClientConfig { + export interface SearchConfig { + enabled: boolean + instantSearchEnabled: boolean + autocompleteEnabled: boolean + } -export interface SharingConfig { - enabled: boolean; - passwordProtected: boolean; -} + export interface SharingConfig { + enabled: boolean; + passwordProtected: boolean; + } -export interface MapConfig { - enabled: boolean; - googleApiKey: string; -} + export interface MapConfig { + enabled: boolean; + googleApiKey: string; + } + export interface ThumbnailConfig { + iconSize: number; + thumbnailSizes: Array; + } -export interface ClientConfig { - applicationTitle: string; - iconSize: number; - thumbnailSizes: Array; - Search: SearchConfig; - Sharing: SharingConfig; - Map: MapConfig; - concurrentThumbnailGenerations: number; - enableCache: boolean; - enableOnScrollRendering: boolean; - enableOnScrollThumbnailPrioritising: boolean; - authenticationRequired: boolean; - publicUrl: string; -} + export interface Config { + applicationTitle: string; + Thumbnail: ThumbnailConfig; + Search: SearchConfig; + Sharing: SharingConfig; + Map: MapConfig; + concurrentThumbnailGenerations: number; + enableCache: boolean; + enableOnScrollRendering: boolean; + enableOnScrollThumbnailPrioritising: boolean; + authenticationRequired: boolean; + publicUrl: string; + } +} /** * These configuration will be available at frontend and backend too */ export class PublicConfigClass { - public Client: ClientConfig = { + public Client: ClientConfig.Config = { applicationTitle: "PiGallery 2", - thumbnailSizes: [200, 400, 600], - iconSize: 30, + Thumbnail: { + thumbnailSizes: [200, 400, 600], + iconSize: 30 + }, Search: { enabled: true, instantSearchEnabled: true, diff --git a/common/entities/Error.ts b/common/entities/Error.ts index de194a5e..a75b3610 100644 --- a/common/entities/Error.ts +++ b/common/entities/Error.ts @@ -20,7 +20,7 @@ export enum ErrorCodes{ SETTINGS_ERROR = 11 } -export class Error { +export class ErrorDTO { constructor(public code: ErrorCodes, public message?: string, public details?: any) { } } diff --git a/common/entities/Message.ts b/common/entities/Message.ts index 4618fa02..cb8a28d2 100644 --- a/common/entities/Message.ts +++ b/common/entities/Message.ts @@ -1,10 +1,10 @@ -import {Error} from "./Error"; +import {ErrorDTO} from "./Error"; export class Message { - public error: Error = null; + public error: ErrorDTO = null; public result: T = null; - constructor(error: Error, result: T) { + constructor(error: ErrorDTO, result: T) { this.error = error; this.result = result; } diff --git a/frontend/app/admin/admin.component.html b/frontend/app/admin/admin.component.html index c4b4c3a4..ee01d69a 100644 --- a/frontend/app/admin/admin.component.html +++ b/frontend/app/admin/admin.component.html @@ -21,8 +21,9 @@ - + + diff --git a/frontend/app/admin/admin.component.ts b/frontend/app/admin/admin.component.ts index 7928b091..5c678925 100644 --- a/frontend/app/admin/admin.component.ts +++ b/frontend/app/admin/admin.component.ts @@ -1,28 +1,25 @@ import {Component, OnInit} from "@angular/core"; import {AuthenticationService} from "../model/network/authentication.service"; -import {Router} from "@angular/router"; import {UserRoles} from "../../../common/entities/UserDTO"; -import {Config} from "../../../common/config/public/Config"; import {NotificationService} from "../model/notification.service"; import {NotificationType} from "../../../common/entities/NotificationDTO"; +import {NavigationService} from "../model/navigation.service"; @Component({ selector: 'admin', templateUrl: './admin.component.html', styleUrls: ['./admin.component.css'] }) export class AdminComponent implements OnInit { - userManagementEnable: boolean = false; constructor(private _authService: AuthenticationService, - private _router: Router, + private _navigation: NavigationService, public notificationService: NotificationService) { - this.userManagementEnable = Config.Client.authenticationRequired; } ngOnInit() { if (!this._authService.isAuthenticated() || this._authService.user.value.role < UserRoles.Admin) { - this._router.navigate(['login']); + this._navigation.toLogin(); return; } } diff --git a/frontend/app/app.module.ts b/frontend/app/app.module.ts index 6e8b82c5..e113d050 100644 --- a/frontend/app/app.module.ts +++ b/frontend/app/app.module.ts @@ -46,7 +46,8 @@ import {ClipboardModule} from "ngx-clipboard"; import {NavigationService} from "./model/navigation.service"; import {InfoPanelLightboxComponent} from "./gallery/lightbox/infopanel/info-panel.lightbox.gallery.component"; import {MapSettingsComponent} from "./settings/map/map.settings.component"; - +import {TooltipModule} from "ngx-bootstrap/tooltip"; +import {ThumbnailSettingsComponent} from "./settings/thumbnail/thumbanil.settings.component"; @Injectable() export class GoogleMapsConfig { apiKey: string; @@ -65,6 +66,7 @@ export class GoogleMapsConfig { appRoutes, ClipboardModule, JWBootstrapSwitchModule, + TooltipModule.forRoot(), ToastModule.forRoot(), ModalModule.forRoot(), AgmCoreModule.forRoot(), @@ -94,6 +96,7 @@ export class GoogleMapsConfig { UserMangerSettingsComponent, DatabaseSettingsComponent, MapSettingsComponent, + ThumbnailSettingsComponent, StringifyRole], providers: [ {provide: LAZY_MAPS_API_CONFIG, useClass: GoogleMapsConfig}, diff --git a/frontend/app/gallery/Photo.ts b/frontend/app/gallery/Photo.ts index 0f2a0f90..28dd0a9d 100644 --- a/frontend/app/gallery/Photo.ts +++ b/frontend/app/gallery/Photo.ts @@ -19,7 +19,7 @@ export class Photo extends IconPhoto { getThumbnailSize() { let renderSize = Math.sqrt(this.renderWidth * this.renderHeight); - return Utils.findClosest(renderSize, Config.Client.thumbnailSizes); + return Utils.findClosest(renderSize, Config.Client.Thumbnail.thumbnailSizes); } getReplacementThumbnailSize(): number { diff --git a/frontend/app/settings/_abstract/abstract.settings.component.css b/frontend/app/settings/_abstract/abstract.settings.component.css new file mode 100644 index 00000000..0f548e61 --- /dev/null +++ b/frontend/app/settings/_abstract/abstract.settings.component.css @@ -0,0 +1,37 @@ +.title { + margin-left: -5px; +} + +.btn { + margin-left: 10px; +} + +.form-control { + margin: 5px 0; +} + +.panel-title { + display: inline-block; + margin-top: 8px; + padding: 0; +} + +.panel-heading { + height: 44px; +} + +.panel-heading bSwitch { + display: inline-block; +} + +.panel-heading { + padding: 5px 15px; +} + +.switch-wrapper { + display: inline-block; + float: none; + text-align: right; + padding: 0; + +} diff --git a/frontend/app/settings/_abstract/abstract.settings.component.ts b/frontend/app/settings/_abstract/abstract.settings.component.ts index 3160524b..4dfbc3a1 100644 --- a/frontend/app/settings/_abstract/abstract.settings.component.ts +++ b/frontend/app/settings/_abstract/abstract.settings.component.ts @@ -2,7 +2,7 @@ import {OnDestroy, OnInit, ViewChild} from "@angular/core"; import {AuthenticationService} from "../../model/network/authentication.service"; import {UserRoles} from "../../../../common/entities/UserDTO"; import {Utils} from "../../../../common/Utils"; -import {Error} from "../../../../common/entities/Error"; +import {ErrorDTO} from "../../../../common/entities/Error"; import {NotificationService} from "../../model/notification.service"; import {NavigationService} from "../../model/navigation.service"; import {ISettingsService} from "./abstract.settings.service"; @@ -11,13 +11,12 @@ import {ISettingsService} from "./abstract.settings.service"; export abstract class SettingsComponent implements OnInit, OnDestroy { @ViewChild('settingsForm') form; - public settings: T; + public settings: T = {}; public inProgress = false; - private original: T; - public tested = false; + private original: T = {}; public error: string = null; public changed: boolean = false; - private subscription; + private subscription = null; constructor(private name, private _authService: AuthenticationService, @@ -37,8 +36,6 @@ export abstract class SettingsComponent implements OnInit, OnDestroy { this.subscription = this.form.valueChanges.subscribe((data) => { this.changed = !Utils.equalsFilter(this.settings, this.original); - - this.tested = false; }); } @@ -52,7 +49,7 @@ export abstract class SettingsComponent implements OnInit, OnDestroy { const s = await this._settingsService.getSettings(); this.original = Utils.clone(s); this.settings = s; - this.tested = false; + console.log(this.settings); this.changed = false; } @@ -61,30 +58,22 @@ export abstract class SettingsComponent implements OnInit, OnDestroy { } - public async test() { + + public async save() { this.inProgress = true; + this.error = ""; try { - this.error = ""; - await this._settingsService.testSettings(this.settings); - this.tested = true; + await this._settingsService.updateSettings(this.settings); + await this.getSettings(); + this.notification.success(this.name + ' settings saved', "Success"); } catch (err) { console.log(err); if (err.message) { - this.error = (err).message; + this.error = (err).message; } } this.inProgress = false; - } - public async save() { - if (!this.tested) { - return; - } - this.inProgress = true; - await this._settingsService.updateSettings(this.settings); - await this.getSettings(); - this.notification.success(this.name + ' settings saved', "Success"); - this.inProgress = false; } } diff --git a/frontend/app/settings/_abstract/abstract.settings.service.ts b/frontend/app/settings/_abstract/abstract.settings.service.ts index acc757fc..4717cf57 100644 --- a/frontend/app/settings/_abstract/abstract.settings.service.ts +++ b/frontend/app/settings/_abstract/abstract.settings.service.ts @@ -1,5 +1,4 @@ export interface ISettingsService { getSettings(): Promise; updateSettings(settings: T): Promise; - testSettings(settings: T): Promise ; } diff --git a/frontend/app/settings/database/database.settings.component.css b/frontend/app/settings/database/database.settings.component.css index 86f118d8..e69de29b 100644 --- a/frontend/app/settings/database/database.settings.component.css +++ b/frontend/app/settings/database/database.settings.component.css @@ -1,11 +0,0 @@ -.title { - margin-left: -5px; -} - -.btn { - margin-left: 10px; -} - -.form-control { - margin: 5px 0; -} diff --git a/frontend/app/settings/database/database.settings.component.html b/frontend/app/settings/database/database.settings.component.html index f8d89308..b269161d 100644 --- a/frontend/app/settings/database/database.settings.component.html +++ b/frontend/app/settings/database/database.settings.component.html @@ -23,13 +23,7 @@ - diff --git a/frontend/app/settings/database/database.settings.component.ts b/frontend/app/settings/database/database.settings.component.ts index c6fd27a4..009b95db 100644 --- a/frontend/app/settings/database/database.settings.component.ts +++ b/frontend/app/settings/database/database.settings.component.ts @@ -10,7 +10,8 @@ import {DatabaseSettingsService} from "./database.settings.service"; @Component({ selector: 'settings-database', templateUrl: './database.settings.component.html', - styleUrls: ['./database.settings.component.css'], + styleUrls: ['./database.settings.component.css', + './../_abstract/abstract.settings.component.css'], providers: [DatabaseSettingsService], }) export class DatabaseSettingsComponent extends SettingsComponent { diff --git a/frontend/app/settings/database/database.settings.service.ts b/frontend/app/settings/database/database.settings.service.ts index 46e4acd3..1dcf7bd4 100644 --- a/frontend/app/settings/database/database.settings.service.ts +++ b/frontend/app/settings/database/database.settings.service.ts @@ -15,7 +15,4 @@ export class DatabaseSettingsService { return this._networkService.putJson("/settings/database", {settings: settings}); } - public testSettings(settings: DataBaseConfig): Promise { - return this._networkService.postJson("/settings/test/database", {settings: settings}); - } } diff --git a/frontend/app/settings/map/map.settings.component.css b/frontend/app/settings/map/map.settings.component.css index 86f118d8..e69de29b 100644 --- a/frontend/app/settings/map/map.settings.component.css +++ b/frontend/app/settings/map/map.settings.component.css @@ -1,11 +0,0 @@ -.title { - margin-left: -5px; -} - -.btn { - margin-left: 10px; -} - -.form-control { - margin: 5px 0; -} diff --git a/frontend/app/settings/map/map.settings.component.html b/frontend/app/settings/map/map.settings.component.html index ec1841af..763377a1 100644 --- a/frontend/app/settings/map/map.settings.component.html +++ b/frontend/app/settings/map/map.settings.component.html @@ -1,22 +1,26 @@ -
-
-

Map settings

-
-
- -
-

+ +

+
+

Map settings

+
-

+
+
+
+ + + 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 788717f6..244954e9 100644 --- a/frontend/app/settings/map/map.settings.component.ts +++ b/frontend/app/settings/map/map.settings.component.ts @@ -1,19 +1,20 @@ import {Component} from "@angular/core"; -import {MapConfig} from "../../../../common/config/public/ConfigClass"; import {MapSettingsService} from "./map.settings.service"; 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"; @Component({ selector: 'settings-map', templateUrl: './map.settings.component.html', - styleUrls: ['./map.settings.component.css'], + styleUrls: ['./map.settings.component.css', + './../_abstract/abstract.settings.component.css'], providers: [MapSettingsService], }) -export class MapSettingsComponent extends SettingsComponent { - public settings: MapConfig = { +export class MapSettingsComponent extends SettingsComponent { + public settings: ClientConfig.MapConfig = { enabled: true, googleApiKey: "" }; diff --git a/frontend/app/settings/map/map.settings.service.ts b/frontend/app/settings/map/map.settings.service.ts index 9a415c48..926dacb0 100644 --- a/frontend/app/settings/map/map.settings.service.ts +++ b/frontend/app/settings/map/map.settings.service.ts @@ -1,22 +1,19 @@ import {Injectable} from "@angular/core"; import {NetworkService} from "../../model/network/network.service"; -import {MapConfig} from "../../../../common/config/public/ConfigClass"; import {IPrivateConfig} from "../../../../common/config/private/IPrivateConfig"; +import {ClientConfig} from "../../../../common/config/public/ConfigClass"; @Injectable() export class MapSettingsService { constructor(private _networkService: NetworkService) { } - public async getSettings(): Promise { + public async getSettings(): Promise { return (await >this._networkService.getJson("/settings")).Client.Map; } - public updateSettings(settings: MapConfig): Promise { + public updateSettings(settings: ClientConfig.MapConfig): Promise { return this._networkService.putJson("/settings/map", {settings: settings}); } - public testSettings(settings: MapConfig): Promise { - return this._networkService.postJson("/settings/test/map", {settings: settings}); - } } diff --git a/frontend/app/settings/thumbnail/thumbanil.settings.component.css b/frontend/app/settings/thumbnail/thumbanil.settings.component.css new file mode 100644 index 00000000..e69de29b diff --git a/frontend/app/settings/thumbnail/thumbanil.settings.component.html b/frontend/app/settings/thumbnail/thumbanil.settings.component.html new file mode 100644 index 00000000..63791131 --- /dev/null +++ b/frontend/app/settings/thumbnail/thumbanil.settings.component.html @@ -0,0 +1,101 @@ +
+
+
+

Thumbnail settings

+
+
+ + + +
+
+ +
+ + Make sure that sharp node module is installed (npm install sharp) + Make sure that gm node module and GraphicsMagick are installed (npm install sharp) + +
+
+ +
+ +
+ + Thumbnails will be saved in this folder. Write access is required + +
+
+
+ +
+ + + High quality may be slow. Especially with Jimp. + +
+
+ +
+ +
+ + Icon size (used on maps) + +
+
+ +
+ +
+ + + Size of the thumbnails.
+ The best matching size will be generated. (More size gives better quality, but use storage to store and CPU to render.)
+ ';' separated integers. If size is 200, tha thumbnail will have 200^2 pixels. +
+ +
+
+
+ + + +
+
+ + +
diff --git a/frontend/app/settings/thumbnail/thumbanil.settings.component.ts b/frontend/app/settings/thumbnail/thumbanil.settings.component.ts new file mode 100644 index 00000000..c2dd8d47 --- /dev/null +++ b/frontend/app/settings/thumbnail/thumbanil.settings.component.ts @@ -0,0 +1,65 @@ +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 {ThumbnailConfig, ThumbnailProcessingLib} from "../../../../common/config/private/IPrivateConfig"; +import {ClientConfig} from "../../../../common/config/public/ConfigClass"; +import {ThumbnailSettingsService} from "./thumbanil.settings.service"; +import {Utils} from "../../../../common/Utils"; + +@Component({ + selector: 'settings-thumbnail', + templateUrl: './thumbanil.settings.component.html', + styleUrls: ['./thumbanil.settings.component.css', + './../_abstract/abstract.settings.component.css'], + providers: [ThumbnailSettingsService], +}) +export class ThumbnailSettingsComponent extends SettingsComponent<{ server: ThumbnailConfig, client: ClientConfig.ThumbnailConfig }> { + 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, + notification: NotificationService) { + super("Thumbnail", _authService, _navigation, _settingsSettings, notification); + } + + get ThumbnailSizes(): string { + return this.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); + } + + ngOnInit() { + super.ngOnInit(); + this.types = Utils + .enumToArray(ThumbnailProcessingLib).map((v) => { + if (v.value.toLowerCase() == "sharp") { + v.value += " (recommended)"; + } + return v; + }); + this.ThumbnailProcessingLib = ThumbnailProcessingLib; + } + +} + + + diff --git a/frontend/app/settings/thumbnail/thumbanil.settings.service.ts b/frontend/app/settings/thumbnail/thumbanil.settings.service.ts new file mode 100644 index 00000000..40b8b7f2 --- /dev/null +++ b/frontend/app/settings/thumbnail/thumbanil.settings.service.ts @@ -0,0 +1,21 @@ +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"; + +@Injectable() +export class ThumbnailSettingsService implements ISettingsService<{ server: ThumbnailConfig, client: ClientConfig.ThumbnailConfig }> { + constructor(private _networkService: NetworkService) { + } + + 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}); + } + +} diff --git a/frontend/app/settings/usermanager/usermanager.settings.component.css b/frontend/app/settings/usermanager/usermanager.settings.component.css index 8e8a5e56..aa18afef 100644 --- a/frontend/app/settings/usermanager/usermanager.settings.component.css +++ b/frontend/app/settings/usermanager/usermanager.settings.component.css @@ -1,3 +1,7 @@ .form-control { margin: 5px 0; } + +.panel-info { + text-align: center; +} diff --git a/frontend/app/settings/usermanager/usermanager.settings.component.html b/frontend/app/settings/usermanager/usermanager.settings.component.html index 04cb35c6..ee2a5cc4 100644 --- a/frontend/app/settings/usermanager/usermanager.settings.component.html +++ b/frontend/app/settings/usermanager/usermanager.settings.component.html @@ -1,43 +1,64 @@
-

User management

+

Password protection

+
+ + +
+ - - - - - - - - - - - - - - -
NameRole
{{user.name}} - - - {{user.role | stringifyRole}} - - -
+ + + + + + + + + + + + + + + +
NameRole
{{user.name}} + + + {{user.role | stringifyRole}} + + +
- + +
+
+ To protect the site with password / have login enable this +
diff --git a/frontend/app/settings/usermanager/usermanager.settings.component.ts b/frontend/app/settings/usermanager/usermanager.settings.component.ts index b8058511..fc056c23 100644 --- a/frontend/app/settings/usermanager/usermanager.settings.component.ts +++ b/frontend/app/settings/usermanager/usermanager.settings.component.ts @@ -5,11 +5,14 @@ import {Utils} from "../../../../common/Utils"; import {UserManagerSettingsService} from "./usermanager.settings.service"; import {ModalDirective} from "ngx-bootstrap/modal"; import {NavigationService} from "../../model/navigation.service"; +import {NotificationService} from "../../model/notification.service"; +import {ErrorCodes, ErrorDTO} from "../../../../common/entities/Error"; @Component({ selector: 'settings-usermanager', templateUrl: './usermanager.settings.component.html', - styleUrls: ['./usermanager.settings.component.css'], + styleUrls: ['./usermanager.settings.component.css', + './../_abstract/abstract.settings.component.css'], providers: [UserManagerSettingsService], }) export class UserMangerSettingsComponent implements OnInit { @@ -17,10 +20,17 @@ export class UserMangerSettingsComponent implements OnInit { public newUser = {}; public userRoles: Array = []; public users: Array = []; + public enabled = true; + public error: string = null; + public inProgress = false; - constructor(private _authService: AuthenticationService, private _navigation: NavigationService, private _userSettings: UserManagerSettingsService) { + constructor(private _authService: AuthenticationService, + private _navigation: NavigationService, + private _userSettings: UserManagerSettingsService, + private notification: NotificationService) { } + ngOnInit() { if (!this._authService.isAuthenticated() || this._authService.user.value.role < UserRoles.Admin) { @@ -33,11 +43,23 @@ export class UserMangerSettingsComponent implements OnInit { .filter(r => r.key <= this._authService.user.value.role) .sort((a, b) => a.key - b.key); + this.getSettings(); this.getUsersList(); } private async getUsersList() { - this.users = await this._userSettings.getUsers(); + try { + this.users = await this._userSettings.getUsers(); + } catch (err) { + this.users = []; + if ((err).code != ErrorCodes.USER_MANAGEMENT_DISABLED) { + throw err; + } + } + } + + private async getSettings() { + this.enabled = await this._userSettings.getSettings(); } @@ -72,6 +94,28 @@ export class UserMangerSettingsComponent implements OnInit { await this.getUsersList(); this.childModal.hide(); } + + async switched(event: { previousValue: false, currentValue: true }) { + this.inProgress = true; + this.error = ""; + this.enabled = event.currentValue; + try { + await this._userSettings.updateSettings(this.enabled); + await this.getSettings(); + if (this.enabled == true) { + this.notification.success('Password protection enabled', "Success"); + this.getUsersList(); + } else { + this.notification.success('Password protection disabled', "Success"); + } + } catch (err) { + console.log(err); + if (err.message) { + this.error = (err).message; + } + } + this.inProgress = false; + } } diff --git a/frontend/app/settings/usermanager/usermanager.settings.service.ts b/frontend/app/settings/usermanager/usermanager.settings.service.ts index 17ab37c0..c29025ed 100644 --- a/frontend/app/settings/usermanager/usermanager.settings.service.ts +++ b/frontend/app/settings/usermanager/usermanager.settings.service.ts @@ -1,6 +1,7 @@ import {Injectable} from "@angular/core"; import {UserDTO} from "../../../../common/entities/UserDTO"; import {NetworkService} from "../../model/network/network.service"; +import {IPrivateConfig} from "../../../../common/config/private/IPrivateConfig"; @Injectable() export class UserManagerSettingsService { @@ -13,6 +14,13 @@ export class UserManagerSettingsService { return this._networkService.putJson("/user", {newUser: user}); } + public async getSettings(): Promise { + return (await >this._networkService.getJson("/settings")).Client.authenticationRequired; + } + + public updateSettings(settings: boolean): Promise { + return this._networkService.putJson("/settings/authentication", {settings: settings}); + } public getUsers(): Promise> { return this._networkService.getJson("/user/list"); diff --git a/frontend/index.html b/frontend/index.html index f4750bed..73b31e93 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -7,9 +7,11 @@ + + - + href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-switch/3.3.4/css/bootstrap3/bootstrap-switch.css"> + diff --git a/package.json b/package.json index 33f60f3f..6e5aef36 100644 --- a/package.json +++ b/package.json @@ -63,6 +63,7 @@ "@types/node": "^8.0.13", "@types/sharp": "^0.17.2", "@types/winston": "^2.3.3", + "bootstrap": "^3.3.7", "chai": "^4.1.0", "codelyzer": "~3.1.2", "core-js": "^2.4.1", diff --git a/test/backend/unit/middlewares/uesr/AuthenticationMWs.ts b/test/backend/unit/middlewares/uesr/AuthenticationMWs.ts index dfe7ef7e..f1b6652a 100644 --- a/test/backend/unit/middlewares/uesr/AuthenticationMWs.ts +++ b/test/backend/unit/middlewares/uesr/AuthenticationMWs.ts @@ -1,6 +1,6 @@ import {expect} from "chai"; import {AuthenticationMWs} from "../../../../../backend/middlewares/user/AuthenticationMWs"; -import {Error, ErrorCodes} from "../../../../../common/entities/Error"; +import {ErrorCodes, ErrorDTO} from "../../../../../common/entities/Error"; import {UserDTO, UserRoles} from "../../../../../common/entities/UserDTO"; import {ObjectManagerRepository} from "../../../../../backend/model/ObjectManagerRepository"; import {UserManager} from "../../../../../backend/model/memory/UserManager"; @@ -38,7 +38,7 @@ describe('Authentication middleware', () => { }; Config.Client.authenticationRequired = true; let res: any = {}; - let next: any = (err: Error) => { + let next: any = (err: ErrorDTO) => { expect(err).not.to.be.undefined; expect(err.code).to.be.eql(ErrorCodes.NOT_AUTHENTICATED); done(); @@ -71,7 +71,7 @@ describe('Authentication middleware', () => { } }; let res: any = {}; - let next: any = (err: Error) => { + let next: any = (err: ErrorDTO) => { expect(err).not.to.be.undefined; expect(err.code).to.be.eql(ErrorCodes.ALREADY_AUTHENTICATED); done(); @@ -106,7 +106,7 @@ describe('Authentication middleware', () => { } } }; - let next: any = (err: Error) => { + let next: any = (err: ErrorDTO) => { expect(err).not.to.be.undefined; expect(err.code).to.be.eql(ErrorCodes.NOT_AUTHORISED); done(); @@ -121,13 +121,13 @@ describe('Authentication middleware', () => { ObjectManagerRepository.reset(); }); - describe('should call input Error next on missing...', () => { + describe('should call input ErrorDTO next on missing...', () => { it('body', (done) => { let req: any = { query: {}, params: {} }; - let next: any = (err: Error) => { + let next: any = (err: ErrorDTO) => { expect(err).not.to.be.undefined; expect(err.code).to.be.eql(ErrorCodes.INPUT_ERROR); done(); @@ -142,7 +142,7 @@ describe('Authentication middleware', () => { query: {}, params: {} }; - let next: any = (err: Error) => { + let next: any = (err: ErrorDTO) => { expect(err).not.to.be.undefined; expect(err.code).to.be.eql(ErrorCodes.INPUT_ERROR); done(); @@ -159,7 +159,7 @@ describe('Authentication middleware', () => { query: {}, params: {} }; - let next: any = (err: Error) => { + let next: any = (err: ErrorDTO) => { expect(err).not.to.be.undefined; expect(err.code).to.be.eql(ErrorCodes.INPUT_ERROR); done(); @@ -181,7 +181,7 @@ describe('Authentication middleware', () => { query: {}, params: {} }; - let next: any = (err: Error) => { + let next: any = (err: ErrorDTO) => { expect(err).not.to.be.undefined; expect(err.code).to.be.eql(ErrorCodes.CREDENTIAL_NOT_FOUND); done(); @@ -208,7 +208,7 @@ describe('Authentication middleware', () => { query: {}, params: {} }; - let next: any = (err: Error) => { + let next: any = (err: ErrorDTO) => { expect(err).to.be.undefined; expect(req.session.user).to.be.eql("test user"); done();