You've already forked pigallery2
							
							
				mirror of
				https://github.com/bpatrik/pigallery2.git
				synced 2025-10-30 23:57:43 +02:00 
			
		
		
		
	improving config check and setting
This commit is contained in:
		| @@ -5,6 +5,8 @@ import {Logger} from "../Logger"; | ||||
| import {MySQLConnection} from "../model/mysql/MySQLConnection"; | ||||
| import {DataBaseConfig, DatabaseType} from "../../common/config/private/IPrivateConfig"; | ||||
| import {Config} from "../../common/config/private/Config"; | ||||
| import {ConfigDiagnostics} from "../model/ConfigDiagnostics"; | ||||
| import {MapConfig} from "../../common/config/public/ConfigClass"; | ||||
|  | ||||
|  | ||||
| const LOG_TAG = "[AdminMWs]"; | ||||
| @@ -13,18 +15,22 @@ export class AdminMWs { | ||||
|  | ||||
|   public static async updateDatabaseSettings(req: Request, res: Response, next: NextFunction) { | ||||
|  | ||||
|     if ((typeof req.body === 'undefined') || (typeof req.body.databaseSettings === 'undefined')) { | ||||
|       return next(new Error(ErrorCodes.INPUT_ERROR, "databaseSettings is needed")); | ||||
|     if ((typeof req.body === 'undefined') || (typeof req.body.settings === 'undefined')) { | ||||
|       return next(new Error(ErrorCodes.INPUT_ERROR, "settings is needed")); | ||||
|     } | ||||
|  | ||||
|     const databaseSettings = <DataBaseConfig>req.body.databaseSettings; | ||||
|     const databaseSettings = <DataBaseConfig>req.body.settings; | ||||
|  | ||||
|     try { | ||||
|       if (databaseSettings.type == DatabaseType.mysql) { | ||||
|         await MySQLConnection.tryConnection(databaseSettings); | ||||
|       } | ||||
|       Config.Server.database = databaseSettings; | ||||
|       Config.save(); | ||||
|       //only updating explicitly set config (not saving config set by the diagnostics) | ||||
|       const original = Config.original(); | ||||
|       original.Server.database = databaseSettings; | ||||
|       original.save(); | ||||
|       await ConfigDiagnostics.runDiagnostics(); | ||||
|       Logger.info(LOG_TAG, "new config:"); | ||||
|       Logger.info(LOG_TAG, JSON.stringify(Config, null, '\t')); | ||||
|  | ||||
| @@ -43,19 +49,55 @@ export class AdminMWs { | ||||
|  | ||||
|  | ||||
|   public static async testDatabaseSettings(req: Request, res: Response, next: NextFunction) { | ||||
|     if ((typeof req.body === 'undefined') || (typeof req.body.databaseSettings === 'undefined')) { | ||||
|       return next(new Error(ErrorCodes.INPUT_ERROR, "databaseSettings is needed")); | ||||
|     if ((typeof req.body === 'undefined') || (typeof req.body.settings === 'undefined')) { | ||||
|       return next(new Error(ErrorCodes.INPUT_ERROR, "settings is needed")); | ||||
|     } | ||||
|  | ||||
|     const databaseSettings = <DataBaseConfig>req.body.databaseSettings; | ||||
|     const databaseSettings = <DataBaseConfig>req.body.settings; | ||||
|  | ||||
|     try { | ||||
|       if (databaseSettings.type == DatabaseType.mysql) { | ||||
|         await MySQLConnection.tryConnection(databaseSettings); | ||||
|         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")); | ||||
|     } | ||||
|  | ||||
|     try { | ||||
|       await ConfigDiagnostics.testMapConfig(<MapConfig>req.body.settings); | ||||
|  | ||||
|       Config.Client.Map = <MapConfig>req.body.settings; | ||||
|       //only updating explicitly set config (not saving config set by the diagnostics) | ||||
|       const original = Config.original(); | ||||
|       original.Client.Map = <MapConfig>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)); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   public static async testMapSettings(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")); | ||||
|     } | ||||
|  | ||||
|     try { | ||||
|       await ConfigDiagnostics.testMapConfig(<MapConfig>req.body.settings); | ||||
|       return next(); | ||||
|     } catch (err) { | ||||
|       return next(new Error(ErrorCodes.SETTINGS_ERROR, "Settings error: " + JSON.stringify(err, null, '  '), err)); | ||||
|     } | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -89,7 +89,7 @@ export class GalleryMWs { | ||||
|  | ||||
|  | ||||
|   public static async search(req: Request, res: Response, next: NextFunction) { | ||||
|     if (Config.Client.Search.searchEnabled === false) { | ||||
|     if (Config.Client.Search.enabled === false) { | ||||
|       return next(); | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -7,6 +7,7 @@ import {Config} from "../../common/config/private/Config"; | ||||
| import {PrivateConfigClass} from "../../common/config/private/PrivateConfigClass"; | ||||
| import {UserRoles} from "../../common/entities/UserDTO"; | ||||
| import {NotificationManager} from "../model/NotifocationManager"; | ||||
| import {Logger} from "../Logger"; | ||||
|  | ||||
| export class RenderingMWs { | ||||
|  | ||||
| @@ -59,8 +60,17 @@ export class RenderingMWs { | ||||
|   public static renderError(err: any, req: Request, res: Response, next: NextFunction): any { | ||||
|  | ||||
|     if (err instanceof Error) { | ||||
|       if (!(req.session.user && req.session.user.role >= UserRoles.Developer)) { | ||||
|         delete (err.details); | ||||
|       if (err.details) { | ||||
|         if (!(req.session.user && req.session.user.role >= UserRoles.Developer)) { | ||||
|           Logger.warn("Handled error:", err.details.toString() || err.details); | ||||
|           delete (err.details); | ||||
|         } else { | ||||
|           try { | ||||
|             err.details = err.details.toString() || err.details; | ||||
|           } catch (err) { | ||||
|             console.error(err); | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|       let message = new Message<any>(err, null); | ||||
|       return res.json(message); | ||||
|   | ||||
| @@ -32,6 +32,24 @@ export class AuthenticationMWs { | ||||
|     return null; | ||||
|   } | ||||
|  | ||||
|  | ||||
|   public static async tryAuthenticate(req: Request, res: Response, next: NextFunction) { | ||||
|     if (Config.Client.authenticationRequired === false) { | ||||
|       req.session.user = <UserDTO>{name: "Admin", role: UserRoles.Admin}; | ||||
|       return next(); | ||||
|     } | ||||
|     try { | ||||
|       const user = await AuthenticationMWs.getSharingUser(req); | ||||
|       if (!!user) { | ||||
|         req.session.user = user; | ||||
|         return next(); | ||||
|       } | ||||
|     } catch (err) { | ||||
|     } | ||||
|  | ||||
|     return next(); | ||||
|  | ||||
|   } | ||||
|   public static async authenticate(req: Request, res: Response, next: NextFunction) { | ||||
|  | ||||
|     if (Config.Client.authenticationRequired === false) { | ||||
|   | ||||
							
								
								
									
										168
									
								
								backend/model/ConfigDiagnostics.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										168
									
								
								backend/model/ConfigDiagnostics.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,168 @@ | ||||
| import {Config} from "../../common/config/private/Config"; | ||||
| import { | ||||
|   DataBaseConfig, | ||||
|   DatabaseType, | ||||
|   ThumbnailConfig, | ||||
|   ThumbnailProcessingLib | ||||
| } from "../../common/config/private/IPrivateConfig"; | ||||
| import {Logger} from "../Logger"; | ||||
| 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"; | ||||
|  | ||||
| const LOG_TAG = "[ConfigDiagnostics]"; | ||||
| export class ConfigDiagnostics { | ||||
|  | ||||
|   static async testDatabase(databaseConfig: DataBaseConfig) { | ||||
|     if (databaseConfig.type == DatabaseType.mysql) { | ||||
|       await MySQLConnection.tryConnection(databaseConfig); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|  | ||||
|   static async testThumbnailLib(processingLibrary: ThumbnailProcessingLib) { | ||||
|     switch (processingLibrary) { | ||||
|       case ThumbnailProcessingLib.sharp: | ||||
|         const sharp = require("sharp"); | ||||
|         sharp(); | ||||
|         break; | ||||
|       case  ThumbnailProcessingLib.gm: | ||||
|         const gm = require("gm"); | ||||
|         await new Promise((resolve, reject) => { | ||||
|           gm(ProjectPath.FrontendFolder + "/assets/icon.png").size((err, value) => { | ||||
|             if (!err) { | ||||
|               return reject(err); | ||||
|             } | ||||
|           }); | ||||
|         }); | ||||
|         break; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   static async testThumbnailFolder(folder: string) { | ||||
|     await new Promise((resolve, reject) => { | ||||
|       if (!fs.existsSync(folder)) { | ||||
|         reject("Thumbnail folder not exists: " + folder); | ||||
|       } | ||||
|       fs.access(folder, fs.constants.W_OK, function (err) { | ||||
|         if (err) { | ||||
|           reject({message: "Error during getting write access to temp folder", error: err}); | ||||
|         } | ||||
|       }); | ||||
|       resolve(); | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   static async testImageFolder(folder: string) { | ||||
|     await new Promise((resolve, reject) => { | ||||
|       if (!fs.existsSync(folder)) { | ||||
|         reject("Images folder not exists: " + folder); | ||||
|       } | ||||
|       fs.access(folder, fs.constants.R_OK, function (err) { | ||||
|         if (err) { | ||||
|           reject({message: "Error during getting read access to images folder", error: err}); | ||||
|         } | ||||
|       }); | ||||
|       resolve(); | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|  | ||||
|   static async testThumbnailConfig(thumbnailConfig: ThumbnailConfig) { | ||||
|     await ConfigDiagnostics.testThumbnailLib(thumbnailConfig.processingLibrary); | ||||
|     await ConfigDiagnostics.testThumbnailFolder(thumbnailConfig.folder); | ||||
|   } | ||||
|  | ||||
|  | ||||
|   static async testSearchConfig(search: SearchConfig) { | ||||
|     if (search.enabled == true && Config.Server.database.type == DatabaseType.memory) { | ||||
|       throw "Memory Database do not support searching"; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|  | ||||
|   static async testSharingConfig(sharing: SharingConfig) { | ||||
|     if (sharing.enabled == true && Config.Server.database.type == DatabaseType.memory) { | ||||
|       throw "Memory Database do not support sharing"; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   static async testMapConfig(map: MapConfig) { | ||||
|     if (map.enabled == true && (!map.googleApiKey || map.googleApiKey.length == 0)) { | ||||
|       throw "Maps need a valid google api key"; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|  | ||||
|   static async runDiagnostics() { | ||||
|  | ||||
|     if (Config.Server.database.type == DatabaseType.mysql) { | ||||
|       try { | ||||
|         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 to memory DB"); | ||||
|         NotificationManager.warning("Error during initializing mysql falling back to memory DB", err); | ||||
|         Config.setDatabaseType(DatabaseType.memory); | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     if (Config.Server.thumbnail.processingLibrary != ThumbnailProcessingLib.Jimp) { | ||||
|       try { | ||||
|         await ConfigDiagnostics.testThumbnailLib(Config.Server.thumbnail.processingLibrary); | ||||
|       } catch (err) { | ||||
|         NotificationManager.warning("Thumbnail hardware acceleration is not possible." + | ||||
|           " '" + ThumbnailProcessingLib[Config.Server.thumbnail.processingLibrary] + "' node module is not found." + | ||||
|           " Falling back to JS based thumbnail generation", err); | ||||
|         Logger.warn(LOG_TAG, "[Thumbnail hardware acceleration] module error: ", err); | ||||
|         Logger.warn(LOG_TAG, "Thumbnail hardware acceleration is not possible." + | ||||
|           " '" + ThumbnailProcessingLib[Config.Server.thumbnail.processingLibrary] + "' node module is not found." + | ||||
|           " Falling back to JS based thumbnail generation"); | ||||
|         Config.Server.thumbnail.processingLibrary = ThumbnailProcessingLib.Jimp; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     try { | ||||
|       await ConfigDiagnostics.testThumbnailFolder(Config.Server.thumbnail.folder) | ||||
|     } catch (err) { | ||||
|       NotificationManager.error("Thumbnail folder error", err); | ||||
|       Logger.error(LOG_TAG, "Thumbnail folder error", err); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     try { | ||||
|       await ConfigDiagnostics.testImageFolder(Config.Server.imagesFolder) | ||||
|     } catch (err) { | ||||
|       NotificationManager.error("Images folder error", err); | ||||
|       Logger.error(LOG_TAG, "Images folder error", err); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     try { | ||||
|       await ConfigDiagnostics.testSearchConfig(Config.Client.Search); | ||||
|     } catch (err) { | ||||
|       NotificationManager.warning("Search is not supported with these settings, switching off..", err); | ||||
|       Logger.warn(LOG_TAG, "Search is not supported with these settings, switching off..", err); | ||||
|       Config.Client.Search.enabled = false; | ||||
|     } | ||||
|  | ||||
|     try { | ||||
|       await ConfigDiagnostics.testSharingConfig(Config.Client.Sharing); | ||||
|     } catch (err) { | ||||
|       NotificationManager.warning("Sharing is not supported with these settings, switching off..", err); | ||||
|       Logger.warn(LOG_TAG, "Sharing is not supported with these settings, switching off..", err); | ||||
|       Config.Client.Sharing.enabled = false; | ||||
|     } | ||||
|  | ||||
|     try { | ||||
|       await ConfigDiagnostics.testMapConfig(Config.Client.Map); | ||||
|     } catch (err) { | ||||
|       NotificationManager.warning("Maps is not supported with these settings, switching off..", err); | ||||
|       Logger.warn(LOG_TAG, "Maps is not supported with these settings, switching off..", err); | ||||
|       Config.Client.Map.enabled = false; | ||||
|     } | ||||
|  | ||||
|   } | ||||
| } | ||||
| @@ -14,7 +14,8 @@ export class ObjectManagerRepository { | ||||
|   private static _instance: ObjectManagerRepository = null; | ||||
|  | ||||
|  | ||||
|   public static InitMemoryManagers() { | ||||
|   public static async InitMemoryManagers() { | ||||
|     await ObjectManagerRepository.reset(); | ||||
|     const GalleryManager = require("./memory/GalleryManager").GalleryManager; | ||||
|     const UserManager = require("./memory/UserManager").UserManager; | ||||
|     const SearchManager = require("./memory/SearchManager").SearchManager; | ||||
| @@ -26,6 +27,7 @@ export class ObjectManagerRepository { | ||||
|   } | ||||
|  | ||||
|   public static async InitMySQLManagers() { | ||||
|     await ObjectManagerRepository.reset(); | ||||
|     await MySQLConnection.init(); | ||||
|     const GalleryManager = require("./mysql/GalleryManager").GalleryManager; | ||||
|     const UserManager = require("./mysql/UserManager").UserManager; | ||||
| @@ -45,7 +47,8 @@ export class ObjectManagerRepository { | ||||
|     return this._instance; | ||||
|   } | ||||
|  | ||||
|   public static reset() { | ||||
|   public static async reset() { | ||||
|     await MySQLConnection.close(); | ||||
|     this._instance = null; | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -4,5 +4,4 @@ export interface ISearchManager { | ||||
|   autocomplete(text: string): Promise<AutoCompleteItem[]>; | ||||
|   search(text: string, searchType: SearchTypes): Promise<SearchResultDTO>; | ||||
|   instantSearch(text: string): Promise<SearchResultDTO>; | ||||
|   isSupported(): boolean; | ||||
| } | ||||
|   | ||||
| @@ -3,5 +3,4 @@ export interface ISharingManager { | ||||
|   findOne(filter: any): Promise<SharingDTO>; | ||||
|   createSharing(sharing: SharingDTO): Promise<SharingDTO>; | ||||
|   updateSharing(sharing: SharingDTO): Promise<SharingDTO>; | ||||
|   isSupported(): boolean; | ||||
| } | ||||
|   | ||||
| @@ -15,10 +15,4 @@ export class SearchManager implements ISearchManager { | ||||
|     throw new Error("Method not implemented."); | ||||
|   } | ||||
|  | ||||
|   isSupported(): boolean { | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|  | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -3,9 +3,6 @@ import {SharingDTO} from "../../../common/entities/SharingDTO"; | ||||
|  | ||||
| export class SharingManager implements ISharingManager { | ||||
|  | ||||
|   isSupported(): boolean { | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   findOne(filter: any): Promise<SharingDTO> { | ||||
|     throw new Error("not implemented"); | ||||
|   | ||||
| @@ -86,7 +86,7 @@ export class MySQLConnection { | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
|   public static async  init(): Promise<void> { | ||||
|   public static async init(): Promise<void> { | ||||
|     const connection = await this.getConnection(); | ||||
|     let userRepository = connection.getRepository(UserEntity); | ||||
|     let admins = await userRepository.find({role: UserRoles.Admin}); | ||||
| @@ -100,5 +100,12 @@ export class MySQLConnection { | ||||
|  | ||||
|   } | ||||
|  | ||||
|   public static async close() { | ||||
|     try { | ||||
|       await getConnection().close(); | ||||
|     } catch (err) { | ||||
|     } | ||||
|   } | ||||
|  | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -8,10 +8,6 @@ import {PositionMetaData} from "../../../common/entities/PhotoDTO"; | ||||
|  | ||||
| export class SearchManager implements ISearchManager { | ||||
|  | ||||
|   isSupported(): boolean { | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
|   async autocomplete(text: string) { | ||||
|  | ||||
|     const connection = await MySQLConnection.getConnection(); | ||||
|   | ||||
| @@ -7,10 +7,6 @@ import {PasswordHelper} from "../PasswordHelper"; | ||||
|  | ||||
| export class SharingManager implements ISharingManager { | ||||
|  | ||||
|   isSupported(): boolean { | ||||
|     return true; | ||||
|   } | ||||
|  | ||||
|   private async removeExpiredLink() { | ||||
|     const connection = await MySQLConnection.getConnection(); | ||||
|     return connection | ||||
|   | ||||
| @@ -9,24 +9,21 @@ import * as exif_parser from "exif-parser"; | ||||
| import {ProjectPath} from "../../ProjectPath"; | ||||
|  | ||||
| const LOG_TAG = "[DiskManagerTask]"; | ||||
|  | ||||
|  | ||||
| export class DiskMangerWorker { | ||||
|   private static isImage(fullPath: string) { | ||||
|     let imageMimeTypes = [ | ||||
|       'bmp', | ||||
|       'gif', | ||||
|       'jpeg', 'jpg', 'jpe', | ||||
|       'png', | ||||
|       'tiff', 'tif', | ||||
|       'webp', | ||||
|       'ico', | ||||
|       'tga' | ||||
|     const extensions = [ | ||||
|       '.bmp', | ||||
|       '.gif', | ||||
|       '.jpeg', '.jpg', '.jpe', | ||||
|       '.png', | ||||
|       '.tiff', '.tif', | ||||
|       '.webp', | ||||
|       '.ico', | ||||
|       '.tga' | ||||
|     ]; | ||||
|  | ||||
|     let extension = path.extname(fullPath).toLowerCase(); | ||||
|  | ||||
|     return imageMimeTypes.indexOf(extension) !== -1; | ||||
|     const extension = path.extname(fullPath).toLowerCase(); | ||||
|     return extensions.indexOf(extension) !== -1; | ||||
|   } | ||||
|  | ||||
|   private static loadPhotoMetadata(fullPath: string): Promise<PhotoMetadata> { | ||||
|   | ||||
| @@ -48,6 +48,20 @@ export class AdminRouter { | ||||
|       AdminMWs.testDatabaseSettings, | ||||
|       RenderingMWs.renderOK | ||||
|     ); | ||||
|  | ||||
|     app.put("/api/settings/map", | ||||
|       AuthenticationMWs.authenticate, | ||||
|       AuthenticationMWs.authorise(UserRoles.Admin), | ||||
|       AdminMWs.updateMapSettings, | ||||
|       RenderingMWs.renderOK | ||||
|     ); | ||||
|  | ||||
|     app.post("/api/settings/test/map", | ||||
|       AuthenticationMWs.authenticate, | ||||
|       AuthenticationMWs.authorise(UserRoles.Admin), | ||||
|       AdminMWs.testMapSettings, | ||||
|       RenderingMWs.renderOK | ||||
|     ); | ||||
|   }; | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -4,39 +4,42 @@ import * as _path from "path"; | ||||
| import {Utils} from "../../common/Utils"; | ||||
| import {Config} from "../../common/config/private/Config"; | ||||
| import {ProjectPath} from "../ProjectPath"; | ||||
| import {AuthenticationMWs} from "../middlewares/user/AuthenticationMWs"; | ||||
|  | ||||
| export class PublicRouter { | ||||
|  | ||||
|   public static route(app) { | ||||
|     app.use((req: Request, res: Response, next: NextFunction) => { | ||||
|       res.tpl = {}; | ||||
|     const renderIndex = (req: Request, res: Response) => { | ||||
|       res.sendFile(_path.resolve(__dirname, './../../dist/index.html')); | ||||
|     }; | ||||
|  | ||||
|       res.tpl.user = null; | ||||
|       if (req.session.user) { | ||||
|         let user = Utils.clone(req.session.user); | ||||
|         delete user.password; | ||||
|         res.tpl.user = user; | ||||
|       } | ||||
|       res.tpl.clientConfig = Config.Client; | ||||
|     app.use( | ||||
|       (req: Request, res: Response, next: NextFunction) => { | ||||
|         res.tpl = {}; | ||||
|  | ||||
|       return next(); | ||||
|     }); | ||||
|         res.tpl.user = null; | ||||
|         if (req.session.user) { | ||||
|           let user = Utils.clone(req.session.user); | ||||
|           delete user.password; | ||||
|           res.tpl.user = user; | ||||
|         } | ||||
|         res.tpl.clientConfig = Config.Client; | ||||
|  | ||||
|         return next(); | ||||
|       }); | ||||
|  | ||||
|     app.get('/config_inject.js', (req: Request, res: Response) => { | ||||
|       res.render(_path.resolve(__dirname, './../../dist/config_inject.ejs'), res.tpl); | ||||
|     }); | ||||
|     app.get(['/', '/login', "/gallery*", "/share*", "/admin", "/search*"], (req: Request, res: Response) => { | ||||
|       res.sendFile(_path.resolve(__dirname, './../../dist/index.html')); | ||||
|     }); | ||||
|     app.get(['/', '/login', "/gallery*", "/share*", "/admin", "/search*"], | ||||
|       AuthenticationMWs.tryAuthenticate, | ||||
|       renderIndex | ||||
|     ); | ||||
|  | ||||
|     app.use(_express.static(ProjectPath.FrontendFolder)); | ||||
|     app.use('/node_modules', _express.static(_path.resolve(__dirname, './../../node_modules'))); | ||||
|     app.use('/common', _express.static(_path.resolve(__dirname, './../../common'))); | ||||
|  | ||||
|     const renderIndex = (req: Request, res: Response) => { | ||||
|       res.render(_path.resolve(__dirname, './../../dist/index.html')); | ||||
|     }; | ||||
|  | ||||
|  | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -11,13 +11,12 @@ import {SharingRouter} from "./routes/SharingRouter"; | ||||
| import {ObjectManagerRepository} from "./model/ObjectManagerRepository"; | ||||
| import {Logger} from "./Logger"; | ||||
| import {Config} from "../common/config/private/Config"; | ||||
| import {DatabaseType, ThumbnailProcessingLib} from "../common/config/private/IPrivateConfig"; | ||||
| import {DatabaseType} from "../common/config/private/IPrivateConfig"; | ||||
| import {LoggerRouter} from "./routes/LoggerRouter"; | ||||
| import {ProjectPath} from "./ProjectPath"; | ||||
| import {ThumbnailGeneratorMWs} from "./middlewares/thumbnail/ThumbnailGeneratorMWs"; | ||||
| import {DiskManager} from "./model/DiskManger"; | ||||
| import {NotificationRouter} from "./routes/NotificationRouter"; | ||||
| import {NotificationManager} from "./model/NotifocationManager"; | ||||
| import {ConfigDiagnostics} from "./model/ConfigDiagnostics"; | ||||
|  | ||||
| const LOG_TAG = "[server]"; | ||||
| export class Server { | ||||
| @@ -34,7 +33,7 @@ export class Server { | ||||
|  | ||||
|   async init() { | ||||
|     Logger.info(LOG_TAG, "running diagnostics..."); | ||||
|     await this.runDiagnostics(); | ||||
|     await ConfigDiagnostics.runDiagnostics(); | ||||
|     Logger.info(LOG_TAG, "using config:"); | ||||
|     Logger.info(LOG_TAG, JSON.stringify(Config, null, '\t')); | ||||
|  | ||||
| @@ -67,6 +66,11 @@ export class Server { | ||||
|  | ||||
|     DiskManager.init(); | ||||
|     ThumbnailGeneratorMWs.init(); | ||||
|     if (Config.Server.database.type == DatabaseType.mysql) { | ||||
|       await  ObjectManagerRepository.InitMySQLManagers(); | ||||
|     } else { | ||||
|       await  ObjectManagerRepository.InitMemoryManagers(); | ||||
|     } | ||||
|  | ||||
|     PublicRouter.route(this.app); | ||||
|  | ||||
| @@ -93,87 +97,6 @@ export class Server { | ||||
|  | ||||
|   } | ||||
|  | ||||
|   async runDiagnostics() { | ||||
|  | ||||
|  | ||||
|     if (Config.Server.database.type == DatabaseType.mysql) { | ||||
|       try { | ||||
|         await ObjectManagerRepository.InitMySQLManagers(); | ||||
|       } catch (err) { | ||||
|         Logger.warn(LOG_TAG, "[MYSQL error]", err); | ||||
|         Logger.warn(LOG_TAG, "Error during initializing mysql falling back to memory DB"); | ||||
|  | ||||
|         NotificationManager.warning("Error during initializing mysql falling back to memory DB", err); | ||||
|         Config.setDatabaseType(DatabaseType.memory); | ||||
|         await ObjectManagerRepository.InitMemoryManagers(); | ||||
|       } | ||||
|     } else { | ||||
|       await ObjectManagerRepository.InitMemoryManagers(); | ||||
|     } | ||||
|  | ||||
|     if (Config.Server.thumbnail.processingLibrary == ThumbnailProcessingLib.sharp) { | ||||
|       try { | ||||
|         const sharp = require("sharp"); | ||||
|         sharp(); | ||||
|  | ||||
|       } catch (err) { | ||||
|         NotificationManager.warning("Thumbnail hardware acceleration is not possible." + | ||||
|           " 'Sharp' node module is not found." + | ||||
|           " Falling back to JS based thumbnail generation", err); | ||||
|         Logger.warn(LOG_TAG, "[Thumbnail hardware acceleration] sharp module error: ", err); | ||||
|         Logger.warn(LOG_TAG, "Thumbnail hardware acceleration is not possible." + | ||||
|           " 'Sharp' node module is not found." + | ||||
|           " Falling back to JS based thumbnail generation"); | ||||
|         Config.Server.thumbnail.processingLibrary = ThumbnailProcessingLib.Jimp; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     if (Config.Server.thumbnail.processingLibrary == ThumbnailProcessingLib.gm) { | ||||
|       try { | ||||
|         const gm = require("gm"); | ||||
|         gm(ProjectPath.FrontendFolder + "/assets/icon.png").size((err, value) => { | ||||
|           if (!err) { | ||||
|             return; | ||||
|           } | ||||
|           NotificationManager.warning("Thumbnail hardware acceleration is not possible." + | ||||
|             " 'gm' node module is not found." + | ||||
|             " Falling back to JS based thumbnail generation", err); | ||||
|           Logger.warn(LOG_TAG, "[Thumbnail hardware acceleration] gm module error: ", err); | ||||
|           Logger.warn(LOG_TAG, "Thumbnail hardware acceleration is not possible." + | ||||
|             " 'gm' node module is not found." + | ||||
|             " Falling back to JS based thumbnail generation"); | ||||
|           Config.Server.thumbnail.processingLibrary = ThumbnailProcessingLib.Jimp; | ||||
|         }); | ||||
|  | ||||
|       } catch (err) { | ||||
|         NotificationManager.warning("Thumbnail hardware acceleration is not possible." + | ||||
|           " 'gm' node module is not found." + | ||||
|           " Falling back to JS based thumbnail generation", err); | ||||
|         Logger.warn(LOG_TAG, "[Thumbnail hardware acceleration] gm module error: ", err); | ||||
|         Logger.warn(LOG_TAG, "Thumbnail hardware acceleration is not possible." + | ||||
|           " 'gm' node module is not found." + | ||||
|           " Falling back to JS based thumbnail generation"); | ||||
|         Config.Server.thumbnail.processingLibrary = ThumbnailProcessingLib.Jimp; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     if (Config.Client.Search.searchEnabled == true && | ||||
|       ObjectManagerRepository.getInstance().SearchManager.isSupported() == false) { | ||||
|  | ||||
|       NotificationManager.warning("Search is not supported with these settings, switching off.."); | ||||
|       Logger.warn(LOG_TAG, "Search is not supported with these settings, switching off.."); | ||||
|       Config.Client.Search.searchEnabled = false; | ||||
|     } | ||||
|     if (Config.Client.Sharing.enabled == true && | ||||
|       ObjectManagerRepository.getInstance().SharingManager.isSupported() == false) { | ||||
|  | ||||
|       NotificationManager.warning("Sharing is not supported with these settings, switching off.."); | ||||
|       Logger.warn(LOG_TAG, "Sharing is not supported with these settings, switching off.."); | ||||
|       Config.Client.Sharing.enabled = false; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|  | ||||
|   /** | ||||
|    * Event listener for HTTP server "error" event. | ||||
|   | ||||
| @@ -19,10 +19,10 @@ export class PrivateConfigClass extends PublicConfigClass implements IPrivateCon | ||||
|     database: { | ||||
|       type: DatabaseType.mysql, | ||||
|       mysql: { | ||||
|         host: "localhost", | ||||
|         username: "root", | ||||
|         password: "root", | ||||
|         database: "pigallery2" | ||||
|         host: "", | ||||
|         username: "", | ||||
|         password: "", | ||||
|         database: "" | ||||
|  | ||||
|       } | ||||
|     }, | ||||
| @@ -36,9 +36,10 @@ export class PrivateConfigClass extends PublicConfigClass implements IPrivateCon | ||||
|   public setDatabaseType(type: DatabaseType) { | ||||
|     this.Server.database.type = type; | ||||
|     if (type === DatabaseType.memory) { | ||||
|       this.Client.Search.searchEnabled = false; | ||||
|       this.Client.Search.enabled = false; | ||||
|       this.Client.Search.instantSearchEnabled = false; | ||||
|       this.Client.Search.autocompleteEnabled = false; | ||||
|       this.Client.Sharing.enabled = false; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -1,26 +1,31 @@ | ||||
| interface SearchConfig { | ||||
|   searchEnabled: boolean | ||||
| export interface SearchConfig { | ||||
|   enabled: boolean | ||||
|   instantSearchEnabled: boolean | ||||
|   autocompleteEnabled: boolean | ||||
| } | ||||
|  | ||||
| interface SharingConfig { | ||||
| export interface SharingConfig { | ||||
|   enabled: boolean; | ||||
|   passwordProtected: boolean; | ||||
| } | ||||
|  | ||||
| export interface MapConfig { | ||||
|   enabled: boolean; | ||||
|   googleApiKey: string; | ||||
| } | ||||
|  | ||||
| export interface ClientConfig { | ||||
|   applicationTitle: string; | ||||
|   iconSize: number; | ||||
|   thumbnailSizes: Array<number>; | ||||
|   Search: SearchConfig; | ||||
|   Sharing: SharingConfig; | ||||
|   Map: MapConfig; | ||||
|   concurrentThumbnailGenerations: number; | ||||
|   enableCache: boolean; | ||||
|   enableOnScrollRendering: boolean; | ||||
|   enableOnScrollThumbnailPrioritising: boolean; | ||||
|   authenticationRequired: boolean; | ||||
|   googleApiKey: string; | ||||
|   publicUrl: string; | ||||
| } | ||||
|  | ||||
| @@ -34,7 +39,7 @@ export class PublicConfigClass { | ||||
|     thumbnailSizes: [200, 400, 600], | ||||
|     iconSize: 30, | ||||
|     Search: { | ||||
|       searchEnabled: true, | ||||
|       enabled: true, | ||||
|       instantSearchEnabled: true, | ||||
|       autocompleteEnabled: true | ||||
|     }, | ||||
| @@ -42,12 +47,15 @@ export class PublicConfigClass { | ||||
|       enabled: true, | ||||
|       passwordProtected: true | ||||
|     }, | ||||
|     Map: { | ||||
|       enabled: true, | ||||
|       googleApiKey: "" | ||||
|     }, | ||||
|     concurrentThumbnailGenerations: 1, | ||||
|     enableCache: false, | ||||
|     enableOnScrollRendering: true, | ||||
|     enableOnScrollThumbnailPrioritising: true, | ||||
|     authenticationRequired: true, | ||||
|     googleApiKey: "", | ||||
|     publicUrl: "" | ||||
|   }; | ||||
|  | ||||
|   | ||||
| @@ -23,5 +23,6 @@ | ||||
|  | ||||
|     <settings-usermanager *ngIf="userManagementEnable"></settings-usermanager> | ||||
|     <settings-database></settings-database> | ||||
|     <settings-map></settings-map> | ||||
|   </div> | ||||
| </app-frame> | ||||
|   | ||||
| @@ -41,17 +41,18 @@ import {DatabaseSettingsComponent} from "./settings/database/database.settings.c | ||||
| import {ToastModule} from "ng2-toastr/ng2-toastr"; | ||||
| import {BrowserAnimationsModule} from "@angular/platform-browser/animations"; | ||||
| import {NotificationService} from "./model/notification.service"; | ||||
|  | ||||
| import {JWBootstrapSwitchModule} from "jw-bootstrap-switch-ng2"; | ||||
| 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"; | ||||
|  | ||||
| @Injectable() | ||||
| export class GoogleMapsConfig { | ||||
|   apiKey: string; | ||||
|  | ||||
|   constructor() { | ||||
|     this.apiKey = Config.Client.googleApiKey; | ||||
|     this.apiKey = Config.Client.Map.googleApiKey; | ||||
|   } | ||||
| } | ||||
|  | ||||
| @@ -63,6 +64,7 @@ export class GoogleMapsConfig { | ||||
|     BrowserAnimationsModule, | ||||
|     appRoutes, | ||||
|     ClipboardModule, | ||||
|     JWBootstrapSwitchModule, | ||||
|     ToastModule.forRoot(), | ||||
|     ModalModule.forRoot(), | ||||
|     AgmCoreModule.forRoot(), | ||||
| @@ -91,6 +93,7 @@ export class GoogleMapsConfig { | ||||
|     //Settings | ||||
|     UserMangerSettingsComponent, | ||||
|     DatabaseSettingsComponent, | ||||
|     MapSettingsComponent, | ||||
|     StringifyRole], | ||||
|   providers: [ | ||||
|     {provide: LAZY_MAPS_API_CONFIG, useClass: GoogleMapsConfig}, | ||||
|   | ||||
| @@ -22,7 +22,7 @@ | ||||
|         <ng-content select="[navbar]"></ng-content> | ||||
|         <li class="divider-vertical"> | ||||
|         </li> | ||||
|         <li> | ||||
|         <li *ngIf="authenticationRequired"> | ||||
|           <p class="navbar-text" *ngIf="user.value"> | ||||
|             <span class="glyphicon glyphicon-user" aria-hidden="true"></span> {{user.value.name}}</p> | ||||
|         </li> | ||||
|   | ||||
| @@ -24,7 +24,8 @@ | ||||
|       <gallery-directory *ngFor="let directory of directories" | ||||
|                          [directory]="directory"></gallery-directory> | ||||
|     </div> | ||||
|     <gallery-map *ngIf="isPhotoWithLocation" [photos]="_galleryService.content.value.directory.photos"></gallery-map> | ||||
|     <gallery-map *ngIf="isPhotoWithLocation && mapEnabled" | ||||
|                  [photos]="_galleryService.content.value.directory.photos"></gallery-map> | ||||
|     <gallery-grid [photos]="_galleryService.content.value.directory.photos" [lightbox]="lightbox"></gallery-grid> | ||||
|   </div> | ||||
|  | ||||
| @@ -42,7 +43,8 @@ | ||||
|       </li> | ||||
|     </ol> | ||||
|  | ||||
|     <gallery-map *ngIf="isPhotoWithLocation" [photos]="_galleryService.content.value.searchResult.photos"></gallery-map> | ||||
|     <gallery-map *ngIf="isPhotoWithLocation && mapEnabled" | ||||
|                  [photos]="_galleryService.content.value.searchResult.photos"></gallery-map> | ||||
|  | ||||
|     <div class="directories"> | ||||
|       <gallery-directory *ngFor="let directory of directories" | ||||
|   | ||||
| @@ -34,6 +34,7 @@ export class GalleryComponent implements OnInit, OnDestroy { | ||||
|     timer: null | ||||
|   }; | ||||
|   public countDown = null; | ||||
|   public mapEnabled = true; | ||||
|  | ||||
|   constructor(public _galleryService: GalleryService, | ||||
|               private _authService: AuthenticationService, | ||||
| @@ -41,6 +42,7 @@ export class GalleryComponent implements OnInit, OnDestroy { | ||||
|               private shareService: ShareService, | ||||
|               private _route: ActivatedRoute, | ||||
|               private _navigation: NavigationService) { | ||||
|     this.mapEnabled = Config.Client.Map.enabled; | ||||
|  | ||||
|   } | ||||
|  | ||||
| @@ -67,7 +69,7 @@ export class GalleryComponent implements OnInit, OnDestroy { | ||||
|  | ||||
|       return this._navigation.toLogin(); | ||||
|     } | ||||
|     this.showSearchBar = Config.Client.Search.searchEnabled && this._authService.isAuthorized(UserRoles.Guest); | ||||
|     this.showSearchBar = Config.Client.Search.enabled && this._authService.isAuthorized(UserRoles.Guest); | ||||
|     this.showShare = Config.Client.Sharing.enabled && this._authService.isAuthorized(UserRoles.User); | ||||
|  | ||||
|     this.subscription.content = this._galleryService.content.subscribe(this.onContentChange); | ||||
|   | ||||
| @@ -34,7 +34,7 @@ export class GalleryGridComponent implements OnChanges, AfterViewInit { | ||||
|   photosToRender: Array<GridPhoto> = []; | ||||
|   containerWidth: number = 0; | ||||
|  | ||||
|   private IMAGE_MARGIN = 2; | ||||
|   public IMAGE_MARGIN = 2; | ||||
|   private TARGET_COL_COUNT = 5; | ||||
|   private MIN_ROW_COUNT = 2; | ||||
|   private MAX_ROW_COUNT = 5; | ||||
|   | ||||
| @@ -43,7 +43,7 @@ export class GalleryPhotoComponent implements IRenderable, OnInit, OnDestroy { | ||||
|  | ||||
|   constructor(private thumbnailService: ThumbnailManagerService) { | ||||
|     this.SearchTypes = SearchTypes; | ||||
|     this.searchEnabled = Config.Client.Search.searchEnabled; | ||||
|     this.searchEnabled = Config.Client.Search.enabled; | ||||
|   } | ||||
|  | ||||
|   ngOnInit() { | ||||
|   | ||||
| @@ -76,7 +76,7 @@ | ||||
|     </div> | ||||
|   </div> | ||||
|  | ||||
|   <div id="map" *ngIf="hasGPS()"> | ||||
|   <div id="map" *ngIf="hasGPS() && mapEnabled"> | ||||
|     <agm-map | ||||
|       [disableDefaultUI]="true" | ||||
|       [zoomControl]="false" | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| import {Component, ElementRef, Input} from "@angular/core"; | ||||
| import {PhotoDTO} from "../../../../../common/entities/PhotoDTO"; | ||||
| import {Config} from "../../../../../common/config/public/Config"; | ||||
|  | ||||
| @Component({ | ||||
|   selector: 'info-panel', | ||||
| @@ -9,8 +10,10 @@ import {PhotoDTO} from "../../../../../common/entities/PhotoDTO"; | ||||
| export class InfoPanelLightboxComponent { | ||||
|   @Input() photo: PhotoDTO; | ||||
|  | ||||
|   public mapEnabled = true; | ||||
|  | ||||
|   constructor(public elementRef: ElementRef) { | ||||
|     this.mapEnabled = Config.Client.Map.enabled; | ||||
|   } | ||||
|  | ||||
|   calcMpx() { | ||||
|   | ||||
| @@ -61,7 +61,7 @@ export class GallerySearchComponent { | ||||
|   } | ||||
|  | ||||
|   public onSearch() { | ||||
|     if (Config.Client.Search.searchEnabled) { | ||||
|     if (Config.Client.Search.enabled) { | ||||
|       this._galleryService.search(this.searchText); | ||||
|     } | ||||
|   } | ||||
|   | ||||
| @@ -18,9 +18,11 @@ export class AuthenticationService { | ||||
|   constructor(private _userService: UserService) { | ||||
|     this.user = new BehaviorSubject(null); | ||||
|  | ||||
|     console.log(ServerInject.user); | ||||
|     //picking up session.. | ||||
|     if (this.isAuthenticated() == false && Cookie.get('pigallery2-session') != null) { | ||||
|       if (typeof ServerInject !== "undefined" && typeof ServerInject.user !== "undefined") { | ||||
|         console.log(ServerInject.user); | ||||
|         this.user.next(ServerInject.user); | ||||
|       } | ||||
|       this.getSessionUser(); | ||||
|   | ||||
| @@ -0,0 +1,93 @@ | ||||
| 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 {NotificationService} from "../../model/notification.service"; | ||||
| import {NavigationService} from "../../model/navigation.service"; | ||||
| import {ISettingsService} from "./abstract.settings.service"; | ||||
|  | ||||
|  | ||||
| export abstract class SettingsComponent<T> implements OnInit, OnDestroy { | ||||
|  | ||||
|   @ViewChild('settingsForm') form; | ||||
|   public settings: T; | ||||
|   public inProgress = false; | ||||
|   private original: T; | ||||
|   public tested = false; | ||||
|   public error: string = null; | ||||
|   public changed: boolean = false; | ||||
|   private subscription; | ||||
|  | ||||
|   constructor(private name, | ||||
|               private _authService: AuthenticationService, | ||||
|               private _navigation: NavigationService, | ||||
|               protected _settingsService: ISettingsService<T>, | ||||
|               private notification: NotificationService) { | ||||
|   } | ||||
|  | ||||
|   ngOnInit() { | ||||
|     if (!this._authService.isAuthenticated() || | ||||
|       this._authService.user.value.role < UserRoles.Admin) { | ||||
|       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.tested = false; | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   ngOnDestroy() { | ||||
|     if (this.subscription != null) { | ||||
|       this.subscription.unsubscribe(); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private async getSettings() { | ||||
|     const s = await this._settingsService.getSettings(); | ||||
|     this.original = Utils.clone(s); | ||||
|     this.settings = s; | ||||
|     this.tested = false; | ||||
|     this.changed = false; | ||||
|   } | ||||
|  | ||||
|   public reset() { | ||||
|     this.getSettings(); | ||||
|   } | ||||
|  | ||||
|  | ||||
|   public async test() { | ||||
|     this.inProgress = true; | ||||
|     try { | ||||
|       this.error = ""; | ||||
|       await this._settingsService.testSettings(this.settings); | ||||
|       this.tested = true; | ||||
|     } catch (err) { | ||||
|       console.log(err); | ||||
|       if (err.message) { | ||||
|         this.error = (<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; | ||||
|   } | ||||
|  | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| @@ -0,0 +1,5 @@ | ||||
| export interface ISettingsService<T> { | ||||
|   getSettings(): Promise<T>; | ||||
|   updateSettings(settings: T): Promise<void>; | ||||
|   testSettings(settings: T): Promise<void> ; | ||||
| } | ||||
| @@ -1,12 +1,11 @@ | ||||
| import {Component, OnInit, ViewChild} from "@angular/core"; | ||||
| import {Component} from "@angular/core"; | ||||
| import {AuthenticationService} from "../../model/network/authentication.service"; | ||||
| import {Router} from "@angular/router"; | ||||
| import {UserRoles} from "../../../../common/entities/UserDTO"; | ||||
| import {DatabaseSettingsService} from "./database.settings.service"; | ||||
| import {DataBaseConfig, DatabaseType} from "../../../../common/config/private/IPrivateConfig"; | ||||
| import {Utils} from "../../../../common/Utils"; | ||||
| import {Error} from "../../../../common/entities/Error"; | ||||
| import {NotificationService} from "../../model/notification.service"; | ||||
| import {NavigationService} from "../../model/navigation.service"; | ||||
| import {SettingsComponent} from "../_abstract/abstract.settings.component"; | ||||
| import {DatabaseSettingsService} from "./database.settings.service"; | ||||
|  | ||||
| @Component({ | ||||
|   selector: 'settings-database', | ||||
| @@ -14,84 +13,28 @@ import {NotificationService} from "../../model/notification.service"; | ||||
|   styleUrls: ['./database.settings.component.css'], | ||||
|   providers: [DatabaseSettingsService], | ||||
| }) | ||||
| export class DatabaseSettingsComponent implements OnInit { | ||||
|   @ViewChild('settingsForm') form; | ||||
|  | ||||
| export class DatabaseSettingsComponent extends SettingsComponent<DataBaseConfig> { | ||||
|   public settings: DataBaseConfig = <DataBaseConfig> { | ||||
|     type: DatabaseType.memory, | ||||
|     mysql: {} | ||||
|   }; | ||||
|   inProgress = false; | ||||
|   private original: DataBaseConfig; | ||||
|   public types: Array<any> = []; | ||||
|   public DatabaseType: any; | ||||
|   public tested = false; | ||||
|   public error: string = null; | ||||
|   public changed: boolean = false; | ||||
|  | ||||
|   constructor(private _authService: AuthenticationService, | ||||
|               private _router: Router, | ||||
|               private _dbSettings: DatabaseSettingsService, | ||||
|               private notification: NotificationService) { | ||||
|     this.original = Utils.clone(this.settings); | ||||
|   constructor(_authService: AuthenticationService, | ||||
|               _navigation: NavigationService, | ||||
|               _dbSettings: DatabaseSettingsService, | ||||
|               notification: NotificationService) { | ||||
|     super("Database", _authService, _navigation, _dbSettings, notification); | ||||
|   } | ||||
|  | ||||
|   ngOnInit() { | ||||
|     if (!this._authService.isAuthenticated() || | ||||
|       this._authService.user.value.role < UserRoles.Admin) { | ||||
|       this._router.navigate(['login']); | ||||
|       return; | ||||
|     } | ||||
|     super.ngOnInit(); | ||||
|     this.types = Utils | ||||
|       .enumToArray(DatabaseType); | ||||
|     this.DatabaseType = DatabaseType; | ||||
|     this.getSettings(); | ||||
|  | ||||
|     this.form.valueChanges.subscribe((data) => { | ||||
|       this.changed = !Utils.equalsFilter(this.settings, this.original); | ||||
|  | ||||
|       this.tested = false; | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   private async getSettings() { | ||||
|     const s = await this._dbSettings.getSettings(); | ||||
|     this.original = Utils.clone(s); | ||||
|     this.settings = s; | ||||
|     this.tested = false; | ||||
|     this.changed = false; | ||||
|   } | ||||
|  | ||||
|   public reset() { | ||||
|     this.getSettings(); | ||||
|   } | ||||
|  | ||||
|  | ||||
|   public async test() { | ||||
|     this.inProgress = true; | ||||
|     try { | ||||
|       this.error = ""; | ||||
|       await this._dbSettings.testSettings(this.settings); | ||||
|       this.tested = true; | ||||
|     } catch (err) { | ||||
|       console.log(err); | ||||
|       if (err.message) { | ||||
|         this.error = (<Error>err).message; | ||||
|       } | ||||
|     } | ||||
|     this.inProgress = false; | ||||
|   } | ||||
|  | ||||
|   public async save() { | ||||
|     if (typeof this.settings.type == "undefined" || !this.tested) { | ||||
|       return; | ||||
|     } | ||||
|     this.inProgress = true; | ||||
|     await this._dbSettings.updateSettings(this.settings); | ||||
|     await this.getSettings(); | ||||
|     this.notification.success('Database settings saved', "Success"); | ||||
|     this.inProgress = false; | ||||
|   } | ||||
|  | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,32 +1,21 @@ | ||||
| import {Injectable} from "@angular/core"; | ||||
| import {NetworkService} from "../../model/network/network.service"; | ||||
| import {DataBaseConfig, IPrivateConfig} from "../../../../common/config/private/IPrivateConfig"; | ||||
| import {NavigationService} from "../../model/navigation.service"; | ||||
| import {UserRoles} from "../../../../common/entities/UserDTO"; | ||||
| import {AuthenticationService} from "../../model/network/authentication.service"; | ||||
|  | ||||
| @Injectable() | ||||
| export class DatabaseSettingsService { | ||||
|  | ||||
|  | ||||
|   constructor(private _networkService: NetworkService, private _authService: AuthenticationService, private _navigation: NavigationService) { | ||||
|  | ||||
|     if (!this._authService.isAuthenticated() || | ||||
|       this._authService.user.value.role < UserRoles.Admin) { | ||||
|       this._navigation.toLogin(); | ||||
|       return; | ||||
|     } | ||||
|   constructor(private _networkService: NetworkService) { | ||||
|   } | ||||
|  | ||||
|   public async  getSettings(): Promise<DataBaseConfig> { | ||||
|     return (await <Promise<IPrivateConfig>>this._networkService.getJson("/settings")).Server.database; | ||||
|   } | ||||
|  | ||||
|   public updateSettings(settings): Promise<void> { | ||||
|     return this._networkService.putJson("/settings/database", {databaseSettings: settings}); | ||||
|   public updateSettings(settings: DataBaseConfig): Promise<void> { | ||||
|     return this._networkService.putJson("/settings/database", {settings: settings}); | ||||
|   } | ||||
|  | ||||
|   public testSettings(settings): Promise<void> { | ||||
|     return this._networkService.postJson<void>("/settings/test/database", {databaseSettings: settings}); | ||||
|   public testSettings(settings: DataBaseConfig): Promise<void> { | ||||
|     return this._networkService.postJson<void>("/settings/test/database", {settings: settings}); | ||||
|   } | ||||
| } | ||||
|   | ||||
							
								
								
									
										11
									
								
								frontend/app/settings/map/map.settings.component.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								frontend/app/settings/map/map.settings.component.css
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| .title { | ||||
|   margin-left: -5px; | ||||
| } | ||||
|  | ||||
| .btn { | ||||
|   margin-left: 10px; | ||||
| } | ||||
|  | ||||
| .form-control { | ||||
|   margin: 5px 0; | ||||
| } | ||||
							
								
								
									
										42
									
								
								frontend/app/settings/map/map.settings.component.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								frontend/app/settings/map/map.settings.component.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | ||||
| <div class="panel panel-default"> | ||||
|   <div class="panel-heading"> | ||||
|     <h3 class="panel-title">Map settings</h3> | ||||
|   </div> | ||||
|   <div class="panel-body"> | ||||
|     <div [hidden]="!error" class="alert alert-danger" role="alert"><strong>Error: </strong>{{error}}</div> | ||||
|     <form #settingsForm="ngForm"> | ||||
|       <p> | ||||
|         <bSwitch | ||||
|           name="enabled" | ||||
|           [switch-on-color]="'success'" | ||||
|           [switch-inverse]="'inverse'" | ||||
|           [switch-off-text]="'Disabled'" | ||||
|           [switch-on-text]="'Enabled'" | ||||
|           [switch-handle-width]="'100'" | ||||
|           [switch-label-width]="'20'" | ||||
|           [(ngModel)]="settings.enabled"> | ||||
|         </bSwitch> | ||||
|       </p> | ||||
|       <input type="text" class="form-control" placeholder="Google api key" autofocus | ||||
|              [(ngModel)]="settings.googleApiKey" | ||||
|              [disabled]="!settings.enabled" | ||||
|              name="googleApiKey" required> | ||||
|       <span class="help-block">To show the images on a map, <a | ||||
|         href="https://developers.google.com/maps/documentation/javascript/get-api-key">google api key</a> is need</span> | ||||
|  | ||||
|     </form> | ||||
|     <button class="btn btn-primary pull-right" | ||||
|             *ngIf="tested==false" | ||||
|             [disabled]="!settingsForm.form.valid || !changed || inProgress" | ||||
|             (click)="test()">Test | ||||
|     </button> | ||||
|     <button class="btn btn-success pull-right" | ||||
|             *ngIf="tested==true" | ||||
|             [disabled]="!settingsForm.form.valid || !changed || inProgress" | ||||
|             (click)="save()">Save | ||||
|     </button> | ||||
|     <button class="btn btn-default pull-right" | ||||
|             (click)="reset()">Reset | ||||
|     </button> | ||||
|   </div> | ||||
| </div> | ||||
							
								
								
									
										32
									
								
								frontend/app/settings/map/map.settings.component.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								frontend/app/settings/map/map.settings.component.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | ||||
| 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"; | ||||
|  | ||||
| @Component({ | ||||
|   selector: 'settings-map', | ||||
|   templateUrl: './map.settings.component.html', | ||||
|   styleUrls: ['./map.settings.component.css'], | ||||
|   providers: [MapSettingsService], | ||||
| }) | ||||
| export class MapSettingsComponent extends SettingsComponent<MapConfig> { | ||||
|   public settings: MapConfig = <MapConfig> { | ||||
|     enabled: true, | ||||
|     googleApiKey: "" | ||||
|   }; | ||||
|  | ||||
|   constructor(_authService: AuthenticationService, | ||||
|               _navigation: NavigationService, | ||||
|               _settingsSettings: MapSettingsService, | ||||
|               notification: NotificationService) { | ||||
|     super("Map", _authService, _navigation, _settingsSettings, notification); | ||||
|   } | ||||
|  | ||||
|  | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
							
								
								
									
										22
									
								
								frontend/app/settings/map/map.settings.service.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								frontend/app/settings/map/map.settings.service.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | ||||
| 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"; | ||||
|  | ||||
| @Injectable() | ||||
| export class MapSettingsService { | ||||
|   constructor(private _networkService: NetworkService) { | ||||
|   } | ||||
|  | ||||
|   public async  getSettings(): Promise<MapConfig> { | ||||
|     return (await <Promise<IPrivateConfig>>this._networkService.getJson("/settings")).Client.Map; | ||||
|   } | ||||
|  | ||||
|   public updateSettings(settings: MapConfig): Promise<void> { | ||||
|     return this._networkService.putJson("/settings/map", {settings: settings}); | ||||
|   } | ||||
|  | ||||
|   public testSettings(settings: MapConfig): Promise<void> { | ||||
|     return this._networkService.postJson<void>("/settings/test/map", {settings: settings}); | ||||
|   } | ||||
| } | ||||
| @@ -7,6 +7,8 @@ | ||||
|   <link rel="shortcut icon" href="assets/icon.png"> | ||||
|   <meta name="viewport" content="width=device-width, initial-scale=1"> | ||||
|   <link href="../node_modules/ng2-toastr/bundles/ng2-toastr.min.css" rel="stylesheet"/> | ||||
|   <link rel="stylesheet" | ||||
|         href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-switch/3.3.2/css/bootstrap3/bootstrap-switch.css"> | ||||
|   <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"> | ||||
|   <script type="text/javascript"  src="config_inject.js"></script> | ||||
| </head> | ||||
|   | ||||
| @@ -7,3 +7,7 @@ | ||||
|   margin-right: 0; | ||||
| } | ||||
|  | ||||
| .bootstrap-switch-label { | ||||
|   margin-right: -4px; | ||||
|   margin-left: -4px; | ||||
| } | ||||
|   | ||||
| @@ -74,6 +74,7 @@ | ||||
|     "intl": "^1.2.5", | ||||
|     "jasmine-core": "^2.6.4", | ||||
|     "jasmine-spec-reporter": "~4.1.1", | ||||
|     "jw-bootstrap-switch-ng2": "^1.0.3", | ||||
|     "karma": "^1.7.0", | ||||
|     "karma-cli": "^1.0.1", | ||||
|     "karma-coverage-istanbul-reporter": "^1.3.0", | ||||
|   | ||||
		Reference in New Issue
	
	Block a user