diff --git a/backend/Logger.ts b/backend/Logger.ts index 7c2557d..9ff9d08 100644 --- a/backend/Logger.ts +++ b/backend/Logger.ts @@ -7,8 +7,7 @@ declare module 'winston' { } } - -export const Logger = new winston.Logger({ +export const winstonSettings = { transports: [ new winston.transports.Console({ level: 'silly', @@ -29,4 +28,6 @@ export const Logger = new winston.Logger({ }) ], exitOnError: false -}); \ No newline at end of file +}; + +export const Logger = new winston.Logger(winstonSettings); \ No newline at end of file diff --git a/backend/ProjectPath.ts b/backend/ProjectPath.ts index e65324f..f488678 100644 --- a/backend/ProjectPath.ts +++ b/backend/ProjectPath.ts @@ -1,5 +1,5 @@ import * as path from "path"; -import {Config} from "./config/Config"; +import {Config} from "../common/config/private/Config"; class ProjectPathClass { public Root: string; diff --git a/backend/config/Config.ts b/backend/config/Config.ts deleted file mode 100644 index f14bf9e..0000000 --- a/backend/config/Config.ts +++ /dev/null @@ -1,28 +0,0 @@ -import {ConfigLoader} from "./ConfigLoader"; -import * as path from "path"; -import {ConfigClass, DatabaseType} from "../../common/config/Config"; - -export let Config = new ConfigClass(); - -Config.Server = { - port: 80, - imagesFolder: "demo/images", - thumbnail: { - folder: "demo/TEMP", - hardwareAcceleration: true - }, - database: { - type: DatabaseType.mysql, - mysql: { - host: "localhost", - username: "root", - password: "root", - database: "pigallery2" - - } - } -}; - -ConfigLoader.init(Config, path.join(__dirname, './../../config.json'), [["PORT", "Server-port"]]); - - \ No newline at end of file diff --git a/backend/config/ConfigLoader.ts b/backend/config/ConfigLoader.ts deleted file mode 100644 index c0f4e1c..0000000 --- a/backend/config/ConfigLoader.ts +++ /dev/null @@ -1,116 +0,0 @@ -import * as fs from "fs"; -import * as optimist from "optimist"; - -export class ConfigLoader { - - static init(configObject: any, configFilePath?: string, envAlias: Array> = []) { - this.processConfigFile(configFilePath, configObject); - this.processArguments(configObject); - this.processEnvVariables(configObject, envAlias); - - } - - private static processEnvVariables(configObject: any, envAlias: Array>) { - let varAliases = {}; - envAlias.forEach((alias) => { - if (process.env[alias[0]]) { - varAliases[alias[1]] = process.env[alias[0]]; - } - }); - this.processHierarchyVar(configObject, varAliases); - this.processHierarchyVar(configObject, process.env); - }; - - private static processArguments(configObject: any) { - let argv = optimist.argv; - delete(argv._); - delete(argv.$0); - this.processHierarchyVar(configObject, argv); - }; - - - private static processHierarchyVar(configObject: any, vars: any) { - let config = {}; - - Object.keys(vars).forEach((key) => { - let keyArray = key.split("-"); - let value = vars[key]; - - //recursive settings - let setObject = (object: any, keyArray: Array, value: any): void => { - let key = keyArray.shift(); - object[key] = object[key] || {}; - - if (keyArray.length == 0) { - //convert to boolean - if (value.toLowerCase && value.toLowerCase() === "false") { - value = false; - } - if (value.toLowerCase && value.toLowerCase() === "true") { - value = true; - } - - object[key] = value; - return; - } - - return setObject(object[key], keyArray, value); - }; - setObject(config, keyArray, value); - - }); - - this.loadObject(configObject, config); - } - - private static processConfigFile(configFilePath: string, configObject: any) { - if (typeof configFilePath !== 'undefined') { - if (ConfigLoader.loadConfigFile(configFilePath, configObject) === false) { - ConfigLoader.saveConfigFile(configFilePath, configObject); - } - } - }; - - private static loadConfigFile(configFilePath: string, configObject: any): boolean { - if (fs.existsSync(configFilePath) === false) { - return false; - } - try { - let config = JSON.parse(fs.readFileSync(configFilePath, 'utf8')); - - this.loadObject(configObject, config); - return true; - } catch (err) { - - } - return false; - } - - - private static saveConfigFile(configFilePath: string, configObject: any) { - try { - fs.writeFileSync(configFilePath, JSON.stringify(configObject, null, 4)); - } catch (err) { - - } - } - - - private static loadObject(targetObject, sourceObject) { - Object.keys(sourceObject).forEach((key) => { - if (typeof targetObject[key] === "undefined") { - return; - } - - if (Array.isArray(targetObject[key])) { - return targetObject[key] = sourceObject[key]; - } - - if (typeof targetObject[key] === "object") { - return this.loadObject(targetObject[key], sourceObject[key]); - } - - targetObject[key] = sourceObject[key]; - }); - } -} \ No newline at end of file diff --git a/backend/middlewares/GalleryMWs.ts b/backend/middlewares/GalleryMWs.ts index 5aa4911..a069d3a 100644 --- a/backend/middlewares/GalleryMWs.ts +++ b/backend/middlewares/GalleryMWs.ts @@ -8,9 +8,9 @@ import {AutoCompleteItem, SearchTypes} from "../../common/entities/AutoCompleteI import {ContentWrapper} from "../../common/entities/ConentWrapper"; import {SearchResultDTO} from "../../common/entities/SearchResult"; import {PhotoDTO} from "../../common/entities/PhotoDTO"; -import {Config} from "../config/Config"; import {ProjectPath} from "../ProjectPath"; import {Logger} from "../Logger"; +import {Config} from "../../common/config/private/Config"; const LOG_TAG = "[GalleryMWs]"; diff --git a/backend/middlewares/thumbnail/ThumbnailGeneratorMWs.ts b/backend/middlewares/thumbnail/ThumbnailGeneratorMWs.ts index c68e760..fe70b54 100644 --- a/backend/middlewares/thumbnail/ThumbnailGeneratorMWs.ts +++ b/backend/middlewares/thumbnail/ThumbnailGeneratorMWs.ts @@ -5,12 +5,12 @@ import * as fs from "fs"; import * as os from "os"; import {NextFunction, Request, Response} from "express"; import {Error, ErrorCodes} from "../../../common/entities/Error"; -import {Config} from "../../config/Config"; import {ContentWrapper} from "../../../common/entities/ConentWrapper"; import {DirectoryDTO} from "../../../common/entities/DirectoryDTO"; import {ProjectPath} from "../../ProjectPath"; import {PhotoDTO} from "../../../common/entities/PhotoDTO"; import {hardwareRenderer, softwareRenderer} from "./THRenderers"; +import {Config} from "../../../common/config/private/Config"; Config.Client.concurrentThumbnailGenerations = Math.max(1, os.cpus().length - 1); @@ -18,14 +18,21 @@ Config.Client.concurrentThumbnailGenerations = Math.max(1, os.cpus().length - 1) const Pool = require('threads').Pool; const pool = new Pool(Config.Client.concurrentThumbnailGenerations); -if (Config.Server.thumbnail.hardwareAcceleration == true) { - pool.run(hardwareRenderer); -} else { - pool.run(softwareRenderer); -} export class ThumbnailGeneratorMWs { + private static poolsInited = false; + private static initPools() { + if (this.poolsInited == true) { + return + } + if (Config.Server.thumbnail.hardwareAcceleration == true) { + pool.run(hardwareRenderer); + } else { + pool.run(softwareRenderer); + } + this.poolsInited = true; + } private static addThInfoTODir(directory: DirectoryDTO) { if (typeof directory.photos == "undefined") { @@ -111,6 +118,7 @@ export class ThumbnailGeneratorMWs { } + private static generateImage(imagePath: string, size: number, makeSquare: boolean, req: Request, res: Response, next: NextFunction) { //generate thumbnail path @@ -129,6 +137,7 @@ export class ThumbnailGeneratorMWs { fs.mkdirSync(ProjectPath.ThumbnailFolder); } + this.initPools(); //run on other thread pool.send({imagePath: imagePath, size: size, thPath: thPath, makeSquare: makeSquare, __dirname: __dirname}) .on('done', (out) => { diff --git a/backend/middlewares/user/AuthenticationMWs.ts b/backend/middlewares/user/AuthenticationMWs.ts index e96429f..6242243 100644 --- a/backend/middlewares/user/AuthenticationMWs.ts +++ b/backend/middlewares/user/AuthenticationMWs.ts @@ -1,9 +1,9 @@ /// import {NextFunction, Request, Response} from "express"; import {Error, ErrorCodes} from "../../../common/entities/Error"; -import {UserRoles, UserDTO} from "../../../common/entities/UserDTO"; +import {UserDTO, UserRoles} from "../../../common/entities/UserDTO"; import {ObjectManagerRepository} from "../../model/ObjectManagerRepository"; -import {Config} from "../../config/Config"; +import {Config} from "../../../common/config/private/Config"; export class AuthenticationMWs { diff --git a/backend/middlewares/user/UserMWs.ts b/backend/middlewares/user/UserMWs.ts index e833c9d..624e868 100644 --- a/backend/middlewares/user/UserMWs.ts +++ b/backend/middlewares/user/UserMWs.ts @@ -2,8 +2,8 @@ import {NextFunction, Request, Response} from "express"; import {Error, ErrorCodes} from "../../../common/entities/Error"; import {ObjectManagerRepository} from "../../model/ObjectManagerRepository"; import {UserDTO} from "../../../common/entities/UserDTO"; -import {Config} from "../../config/Config"; import {Utils} from "../../../common/Utils"; +import {Config} from "../../../common/config/private/Config"; export class UserMWs { diff --git a/backend/model/DiskManger.ts b/backend/model/DiskManger.ts index c77b6c6..f7402e5 100644 --- a/backend/model/DiskManger.ts +++ b/backend/model/DiskManger.ts @@ -138,7 +138,6 @@ pool.run( try { for (let i = 0; i < list.length; i++) { let file = list[i]; - console.log(list[i]); let fullFilePath = path.normalize(path.resolve(directoryInfo.absoluteDirectoryName, file)); if (photosOnly == false && fs.statSync(fullFilePath).isDirectory()) { let promise = parseDir({ @@ -195,7 +194,7 @@ pool.run( export class DiskManager { public static scanDirectory(relativeDirectoryName: string, cb: (error: any, result: DirectoryDTO) => void) { - Logger.silly(LOG_TAG, "scanDirectory"); + Logger.silly(LOG_TAG, "scanning directory:", relativeDirectoryName); let directoryName = path.basename(relativeDirectoryName); let directoryParent = path.join(path.dirname(relativeDirectoryName), path.sep); let absoluteDirectoryName = path.join(ProjectPath.ImageFolder, relativeDirectoryName); diff --git a/backend/model/mysql/MySQLConnection.ts b/backend/model/mysql/MySQLConnection.ts index 51ec06b..c1a8dad 100644 --- a/backend/model/mysql/MySQLConnection.ts +++ b/backend/model/mysql/MySQLConnection.ts @@ -1,10 +1,10 @@ import "reflect-metadata"; -import {createConnection, Connection} from "typeorm"; -import {Config} from "../../config/Config"; +import {Connection, createConnection} from "typeorm"; import {UserEntity} from "./enitites/UserEntity"; import {UserRoles} from "../../../common/entities/UserDTO"; import {PhotoEntity, PhotoMetadataEntity} from "./enitites/PhotoEntity"; import {DirectoryEntity} from "./enitites/DirectoryEntity"; +import {Config} from "../../../common/config/private/Config"; export class MySQLConnection { diff --git a/backend/routes/AdminRouter.ts b/backend/routes/AdminRouter.ts index 8e21303..72bcb8a 100644 --- a/backend/routes/AdminRouter.ts +++ b/backend/routes/AdminRouter.ts @@ -2,22 +2,22 @@ import {AuthenticationMWs} from "../middlewares/user/AuthenticationMWs"; import {UserRoles} from "../../common/entities/UserDTO"; export class AdminRouter { - constructor(private app: any) { + public static route(app: any) { - this.addResetDB(); - this.addIndexGallery(); + this.addResetDB(app); + this.addIndexGallery(app); } - private addResetDB() { - this.app.post("/api/admin/db/reset", + private static addResetDB(app) { + app.post("/api/admin/db/reset", AuthenticationMWs.authenticate, AuthenticationMWs.authorise(UserRoles.Admin) //TODO: implement ); }; - private addIndexGallery() { - this.app.post("/api/admin/gallery/index", + private static addIndexGallery(app) { + app.post("/api/admin/gallery/index", AuthenticationMWs.authenticate, AuthenticationMWs.authorise(UserRoles.Admin) //TODO: implement diff --git a/backend/routes/ErrorRouter.ts b/backend/routes/ErrorRouter.ts index 4f27a04..6389fb6 100644 --- a/backend/routes/ErrorRouter.ts +++ b/backend/routes/ErrorRouter.ts @@ -1,26 +1,27 @@ import {RenderingMWs} from "../middlewares/RenderingMWs"; import {Error, ErrorCodes} from "../../common/entities/Error"; +import {Logger} from "../Logger"; import Request = Express.Request; import Response = Express.Response; export class ErrorRouter { - constructor(private app: any) { + public static route(app: any) { - this.addApiErrorHandler(); - this.addGenericHandler(); + this.addApiErrorHandler(app); + this.addGenericHandler(app); } - private addApiErrorHandler() { - this.app.use("/api/*", + private static addApiErrorHandler(app) { + app.use("/api/*", RenderingMWs.renderError ); }; - private addGenericHandler() { - this.app.use((err: any, req: Request, res: Response, next: Function) => { + private static addGenericHandler(app) { + app.use((err: any, req: Request, res: Response, next: Function) => { //Flush out the stack to the console - console.error(err.stack); + Logger.error(err); next(new Error(ErrorCodes.SERVER_ERROR, "Unknown server side error")); }, RenderingMWs.renderError diff --git a/backend/routes/GalleryRouter.ts b/backend/routes/GalleryRouter.ts index 7868294..5df7358 100644 --- a/backend/routes/GalleryRouter.ts +++ b/backend/routes/GalleryRouter.ts @@ -4,20 +4,20 @@ import {RenderingMWs} from "../middlewares/RenderingMWs"; import {ThumbnailGeneratorMWs} from "../middlewares/thumbnail/ThumbnailGeneratorMWs"; export class GalleryRouter { - constructor(private app: any) { + public static route(app: any) { - this.addGetImageIcon(); - this.addGetImageThumbnail(); - this.addGetImage(); - this.addDirectoryList(); + this.addGetImageIcon(app); + this.addGetImageThumbnail(app); + this.addGetImage(app); + this.addDirectoryList(app); - this.addSearch(); - this.addInstantSearch(); - this.addAutoComplete(); + this.addSearch(app); + this.addInstantSearch(app); + this.addAutoComplete(app); } - private addDirectoryList() { - this.app.get(["/api/gallery/content/:directory(*)", "/api/gallery/", "/api/gallery//"], + private static addDirectoryList(app) { + app.get(["/api/gallery/content/:directory(*)", "/api/gallery/", "/api/gallery//"], AuthenticationMWs.authenticate, GalleryMWs.listDirectory, ThumbnailGeneratorMWs.addThumbnailInformation, @@ -27,16 +27,16 @@ export class GalleryRouter { }; - private addGetImage() { - this.app.get(["/api/gallery/content/:imagePath(*\.(jpg|bmp|png|gif|jpeg))"], + private static addGetImage(app) { + app.get(["/api/gallery/content/:imagePath(*\.(jpg|bmp|png|gif|jpeg))"], AuthenticationMWs.authenticate, GalleryMWs.loadImage, RenderingMWs.renderFile ); }; - private addGetImageThumbnail() { - this.app.get("/api/gallery/content/:imagePath(*\.(jpg|bmp|png|gif|jpeg))/thumbnail/:size?", + private static addGetImageThumbnail(app) { + app.get("/api/gallery/content/:imagePath(*\.(jpg|bmp|png|gif|jpeg))/thumbnail/:size?", AuthenticationMWs.authenticate, GalleryMWs.loadImage, ThumbnailGeneratorMWs.generateThumbnail, @@ -44,8 +44,8 @@ export class GalleryRouter { ); }; - private addGetImageIcon() { - this.app.get("/api/gallery/content/:imagePath(*\.(jpg|bmp|png|gif|jpeg))/icon", + private static addGetImageIcon(app) { + app.get("/api/gallery/content/:imagePath(*\.(jpg|bmp|png|gif|jpeg))/icon", AuthenticationMWs.authenticate, GalleryMWs.loadImage, ThumbnailGeneratorMWs.generateIcon, @@ -53,8 +53,8 @@ export class GalleryRouter { ); }; - private addSearch() { - this.app.get("/api/search/:text", + private static addSearch(app) { + app.get("/api/search/:text", AuthenticationMWs.authenticate, GalleryMWs.search, ThumbnailGeneratorMWs.addThumbnailInformation, @@ -63,8 +63,8 @@ export class GalleryRouter { ); }; - private addInstantSearch() { - this.app.get("/api/instant-search/:text", + private static addInstantSearch(app) { + app.get("/api/instant-search/:text", AuthenticationMWs.authenticate, GalleryMWs.instantSearch, ThumbnailGeneratorMWs.addThumbnailInformation, @@ -73,8 +73,8 @@ export class GalleryRouter { ); }; - private addAutoComplete() { - this.app.get("/api/autocomplete/:text", + private static addAutoComplete(app) { + app.get("/api/autocomplete/:text", AuthenticationMWs.authenticate, GalleryMWs.autocomplete, RenderingMWs.renderResult diff --git a/backend/routes/PublicRouter.ts b/backend/routes/PublicRouter.ts index 73d8bb4..843073e 100644 --- a/backend/routes/PublicRouter.ts +++ b/backend/routes/PublicRouter.ts @@ -2,11 +2,12 @@ import * as _express from "express"; import {NextFunction, Request, Response} from "express"; import * as _path from "path"; import {Utils} from "../../common/Utils"; -import {Config} from "../config/Config"; +import {Config} from "../../common/config/private/Config"; export class PublicRouter { - constructor(private app) { - this.app.use((req:Request, res:Response, next:NextFunction) => { + + public static route(app) { + app.use((req: Request, res: Response, next: NextFunction) => { res.tpl = {}; res.tpl.user = null; @@ -20,15 +21,15 @@ export class PublicRouter { return next(); }); - this.app.use(_express.static(_path.resolve(__dirname, './../../frontend'))); - this.app.use('/node_modules', _express.static(_path.resolve(__dirname, './../../node_modules'))); - this.app.use('/common', _express.static(_path.resolve(__dirname, './../../common'))); + app.use(_express.static(_path.resolve(__dirname, './../../frontend'))); + 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, './../../frontend/index.ejs'), res.tpl); }; - this.app.get(['/', '/login', "/gallery*", "/admin", "/search*"], renderIndex); + app.get(['/', '/login', "/gallery*", "/admin", "/search*"], renderIndex); } diff --git a/backend/routes/SharingRouter.ts b/backend/routes/SharingRouter.ts index a3a3579..67f480e 100644 --- a/backend/routes/SharingRouter.ts +++ b/backend/routes/SharingRouter.ts @@ -2,22 +2,22 @@ import {AuthenticationMWs} from "../middlewares/user/AuthenticationMWs"; import {UserRoles} from "../../common/entities/UserDTO"; export class SharingRouter { - constructor(private app: any) { + public static route(app: any) { - this.addGetSharing(); - this.addUpdateSharing(); + this.addGetSharing(app); + this.addUpdateSharing(app); } - private addGetSharing() { - this.app.get("/api/share/:directory", + private static addGetSharing(app) { + app.get("/api/share/:directory", AuthenticationMWs.authenticate, AuthenticationMWs.authorise(UserRoles.User) //TODO: implement ); }; - private addUpdateSharing() { - this.app.post("/api/share/:directory", + private static addUpdateSharing(app) { + app.post("/api/share/:directory", AuthenticationMWs.authenticate, AuthenticationMWs.authorise(UserRoles.User) //TODO: implement diff --git a/backend/routes/UserRouter.ts b/backend/routes/UserRouter.ts index 69f263c..e4b0151 100644 --- a/backend/routes/UserRouter.ts +++ b/backend/routes/UserRouter.ts @@ -5,29 +5,29 @@ import {UserRequestConstrainsMWs} from "../middlewares/user/UserRequestConstrain import {RenderingMWs} from "../middlewares/RenderingMWs"; export class UserRouter { - constructor(private app) { - this.addLogin(); - this.addLogout(); - this.addGetSessionUser(); - this.addChangePassword(); + public static route(app) { + this.addLogin(app); + this.addLogout(app); + this.addGetSessionUser(app); + this.addChangePassword(app); - this.addCreateUser(); - this.addDeleteUser(); - this.addListUsers(); - this.addChangeRole(); + this.addCreateUser(app); + this.addDeleteUser(app); + this.addListUsers(app); + this.addChangeRole(app); } - private addLogin() { - this.app.post("/api/user/login", + private static addLogin(app) { + app.post("/api/user/login", AuthenticationMWs.inverseAuthenticate, AuthenticationMWs.login, RenderingMWs.renderSessionUser ); }; - private addLogout() { - this.app.post("/api/user/logout", + private static addLogout(app) { + app.post("/api/user/logout", AuthenticationMWs.authenticate, AuthenticationMWs.logout, RenderingMWs.renderOK @@ -35,16 +35,16 @@ export class UserRouter { }; - private addGetSessionUser() { - this.app.get("/api/user/login", + private static addGetSessionUser(app) { + app.get("/api/user/login", AuthenticationMWs.authenticate, RenderingMWs.renderSessionUser ); }; - private addChangePassword() { - this.app.post("/api/user/:id/password", + private static addChangePassword(app) { + app.post("/api/user/:id/password", AuthenticationMWs.authenticate, UserRequestConstrainsMWs.forceSelfRequest, UserMWs.changePassword, @@ -53,8 +53,8 @@ export class UserRouter { }; - private addCreateUser() { - this.app.put("/api/user", + private static addCreateUser(app) { + app.put("/api/user", AuthenticationMWs.authenticate, AuthenticationMWs.authorise(UserRoles.Admin), UserMWs.createUser, @@ -62,8 +62,8 @@ export class UserRouter { ); }; - private addDeleteUser() { - this.app.delete("/api/user/:id", + private static addDeleteUser(app) { + app.delete("/api/user/:id", AuthenticationMWs.authenticate, AuthenticationMWs.authorise(UserRoles.Admin), UserRequestConstrainsMWs.notSelfRequest, @@ -73,8 +73,8 @@ export class UserRouter { }; - private addListUsers() { - this.app.get("/api/user/list", + private static addListUsers(app) { + app.get("/api/user/list", AuthenticationMWs.authenticate, AuthenticationMWs.authorise(UserRoles.Admin), UserMWs.listUsers, @@ -82,8 +82,8 @@ export class UserRouter { ); }; - private addChangeRole() { - this.app.post("/api/user/:id/role", + private static addChangeRole(app) { + app.post("/api/user/:id/role", AuthenticationMWs.authenticate, AuthenticationMWs.authorise(UserRoles.Admin), UserRequestConstrainsMWs.notSelfRequestOr2Admins, diff --git a/backend/server.ts b/backend/server.ts index 256503f..73cae7e 100644 --- a/backend/server.ts +++ b/backend/server.ts @@ -1,18 +1,19 @@ import * as _express from "express"; import * as _session from "express-session"; import * as _bodyParser from "body-parser"; -import * as _debug from "debug"; import * as _http from "http"; +import * as winston from "winston"; +import * as expressWinston from "express-winston"; import {PublicRouter} from "./routes/PublicRouter"; import {UserRouter} from "./routes/UserRouter"; import {GalleryRouter} from "./routes/GalleryRouter"; import {AdminRouter} from "./routes/AdminRouter"; import {ErrorRouter} from "./routes/ErrorRouter"; import {SharingRouter} from "./routes/SharingRouter"; -import {DatabaseType} from "../common/config/Config"; import {ObjectManagerRepository} from "./model/ObjectManagerRepository"; -import {Config} from "./config/Config"; import {Logger} from "./Logger"; +import {Config} from "../common/config/private/Config"; +import {DatabaseType} from "../common/config/private/IPrivateConfig"; const LOG_TAG = "[server]"; export class Server { @@ -22,18 +23,46 @@ export class Server { private server: any; constructor() { + this.init(); + } + + async init() { Logger.info(LOG_TAG, "config:"); Logger.info(LOG_TAG, JSON.stringify(Config, null, '\t')); - this.debug = _debug("PiGallery2:server"); this.app = _express(); - this.app.set('view engine', 'ejs'); + this.app.use(expressWinston.logger({ + transports: [ + new winston.transports.Console({ + level: 'silly', + json: false, + colorize: true, + timestamp: function () { + return (new Date()).toLocaleString(); + }, + formatter: (options) => { + // Return string will be passed to logger. + return options.timestamp() + '[' + winston['config']['colorize'](options.level, options.level.toUpperCase()) + '] ' + + (undefined !== options.message ? options.message : '') + + (options.meta && Object.keys(options.meta).length ? '\n\t' + JSON.stringify(options.meta) : '' ); + }, + debugStdout: true + }) + ], + meta: false, // optional: control whether you want to log the meta data about the request (default to true) + msg: "HTTP {{req.method}} {{req.url}}", // optional: customize the default logging message. E.g. "{{res.statusCode}} {{req.method}} {{res.responseTime}}ms {{req.url}}" + expressFormat: true, // Use the default Express/morgan request formatting. Enabling this will override any msg if true. Will only output colors with colorize set to true + colorize: true, // Color the text and status code, using the Express/morgan color palette (text: gray, status: default green, 3XX cyan, 4XX yellow, 5XX red). + level: (req) => { + if (req.url.indexOf("/api/") !== -1) { + return "verbose"; + } + return req.url.indexOf("node_modules") !== -1 ? "silly" : "debug" + } + })); - if (process.env.DEBUG) { - let _morgan = require('morgan'); - this.app.use(_morgan('dev')); - } + this.app.set('view engine', 'ejs'); /** @@ -57,19 +86,26 @@ export class Server { this.app.use(_bodyParser.json()); if (Config.Server.database.type == DatabaseType.mysql) { - ObjectManagerRepository.InitMySQLManagers().catch((err) => { - Logger.warn(LOG_TAG, "Error during initailizing mysql falling back to memory DB", err); + 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"); Config.setDatabaseType(DatabaseType.memory); - ObjectManagerRepository.InitMemoryManagers(); - }); + await ObjectManagerRepository.InitMemoryManagers(); + } } else { - ObjectManagerRepository.InitMemoryManagers(); + await ObjectManagerRepository.InitMemoryManagers(); } if (Config.Server.thumbnail.hardwareAcceleration == true) { try { - const sharp = require.resolve("sharp"); + const sharp = require("sharp"); + sharp(); + } catch (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"); @@ -77,14 +113,14 @@ export class Server { } } - new PublicRouter(this.app); + PublicRouter.route(this.app); - new UserRouter(this.app); - new GalleryRouter(this.app); - new SharingRouter(this.app); - new AdminRouter(this.app); + UserRouter.route(this.app); + GalleryRouter.route(this.app); + SharingRouter.route(this.app); + AdminRouter.route(this.app); - new ErrorRouter(this.app); + ErrorRouter.route(this.app); // Get PORT from environment and store in Express. @@ -138,7 +174,7 @@ export class Server { const bind = typeof addr === 'string' ? 'pipe ' + addr : 'port ' + addr.port; - Logger.debug(LOG_TAG, 'Listening on ' + bind); + Logger.info(LOG_TAG, 'Listening on ' + bind); }; } diff --git a/common/config/private/Config.ts b/common/config/private/Config.ts new file mode 100644 index 0000000..9bed34c --- /dev/null +++ b/common/config/private/Config.ts @@ -0,0 +1,12 @@ +import * as path from "path"; +import {PrivateConfigClass} from "./PrivateConfigClass"; +import {ConfigLoader} from "typeconfig"; + + +export let Config = new PrivateConfigClass(); + + +ConfigLoader.loadBackendConfig(Config, + path.join(__dirname, './../../../config.json'), + [["PORT", "Server-port"]]); + \ No newline at end of file diff --git a/common/config/private/IPrivateConfig.ts b/common/config/private/IPrivateConfig.ts new file mode 100644 index 0000000..ff02088 --- /dev/null +++ b/common/config/private/IPrivateConfig.ts @@ -0,0 +1,28 @@ +export enum DatabaseType{ + memory = 0, mysql = 1 +} +export enum LogLevel { + error, warn, info, debug, verbose +} + +export interface MySQLConfig { + host: string; + database: string; + username: string; + password: string; +} +export interface DataBaseConfig { + type: DatabaseType; + mysql?: MySQLConfig; +} +export interface ThumbnailConfig { + folder: string; + hardwareAcceleration: boolean; +} + +export interface ServerConfig { + port: number; + imagesFolder: string; + thumbnail: ThumbnailConfig; + database: DataBaseConfig; +} diff --git a/common/config/private/PrivateConfigClass.ts b/common/config/private/PrivateConfigClass.ts new file mode 100644 index 0000000..08abda9 --- /dev/null +++ b/common/config/private/PrivateConfigClass.ts @@ -0,0 +1,39 @@ +import {PublicConfigClass} from "../public/ConfigClass"; +import {DatabaseType, ServerConfig} from "./IPrivateConfig"; + + +/** + * This configuration will be only at backend + */ +export class PrivateConfigClass extends PublicConfigClass { + + public Server: ServerConfig = { + port: 80, + imagesFolder: "demo/images", + thumbnail: { + folder: "demo/TEMP", + hardwareAcceleration: true + }, + database: { + type: DatabaseType.mysql, + mysql: { + host: "localhost", + username: "root", + password: "root", + database: "pigallery2" + + } + } + }; + + public setDatabaseType(type: DatabaseType) { + this.Server.database.type = type; + if (type === DatabaseType.memory) { + this.Client.Search.searchEnabled = false; + this.Client.Search.instantSearchEnabled = false; + this.Client.Search.autocompleteEnabled = false; + } + } + +} + diff --git a/common/config/public/Config.ts b/common/config/public/Config.ts new file mode 100644 index 0000000..6e7a248 --- /dev/null +++ b/common/config/public/Config.ts @@ -0,0 +1,15 @@ +import {PublicConfigClass} from "./ConfigClass"; +import {WebConfigLoader} from "typeconfig/src/WebConfigLoader"; + + +declare module ServerInject { + export const ConfigInject; +} + +export let Config = new PublicConfigClass(); + +if (typeof ServerInject !== "undefined" && typeof ServerInject.ConfigInject !== "undefined") { + WebConfigLoader.loadFrontendConfig(Config.Client, ServerInject.ConfigInject); +} + + \ No newline at end of file diff --git a/common/config/Config.ts b/common/config/public/ConfigClass.ts similarity index 52% rename from common/config/Config.ts rename to common/config/public/ConfigClass.ts index 613cbbb..fee5d07 100644 --- a/common/config/Config.ts +++ b/common/config/public/ConfigClass.ts @@ -1,29 +1,3 @@ -export enum DatabaseType{ - memory = 0, mysql = 1 -} - -interface MySQLConfig { - host: string; - database: string; - username: string; - password: string; -} -interface DataBaseConfig { - type: DatabaseType; - mysql?: MySQLConfig; -} -interface ThumbnailConfig { - folder: string; - hardwareAcceleration: boolean; -} - -interface ServerConfig { - port: number; - imagesFolder: string; - thumbnail: ThumbnailConfig; - database: DataBaseConfig; -} - interface SearchConfig { searchEnabled: boolean instantSearchEnabled: boolean @@ -41,9 +15,11 @@ interface ClientConfig { authenticationRequired: boolean; googleApiKey: string; } -export class ConfigClass { - public Server: ServerConfig = null; +/** + * These configuration will be available at frontend and backend too + */ +export class PublicConfigClass { public Client: ClientConfig = { thumbnailSizes: [200, 400, 600], @@ -61,14 +37,5 @@ export class ConfigClass { googleApiKey: "" }; - public setDatabaseType(type: DatabaseType) { - this.Server.database.type = type; - if (type === DatabaseType.memory) { - this.Client.Search.searchEnabled = false; - this.Client.Search.instantSearchEnabled = false; - this.Client.Search.autocompleteEnabled = false; - } - } } - \ No newline at end of file diff --git a/frontend/app/admin/admin.component.ts b/frontend/app/admin/admin.component.ts index 44be25c..aa15e22 100644 --- a/frontend/app/admin/admin.component.ts +++ b/frontend/app/admin/admin.component.ts @@ -2,7 +2,7 @@ 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 "../config/Config"; +import {Config} from "../../../common/config/public/Config"; @Component({ selector: 'admin', templateUrl: 'app/admin/admin.component.html', diff --git a/frontend/app/app.module.ts b/frontend/app/app.module.ts index 234eb61..36afba9 100644 --- a/frontend/app/app.module.ts +++ b/frontend/app/app.module.ts @@ -26,11 +26,11 @@ import {LoginComponent} from "./login/login.component"; import {AdminComponent} from "./admin/admin.component"; import {GalleryComponent} from "./gallery/gallery.component"; import {StringifyRole} from "./pipes/StringifyRolePipe"; -import {Config} from "./config/Config"; import {GalleryMapComponent} from "./gallery/map/map.gallery.component"; import {GalleryMapLightboxComponent} from "./gallery/map/lightbox/lightbox.map.gallery.component"; import {ThumbnailManagerService} from "./gallery/thumnailManager.service"; import {OverlayService} from "./gallery/overlay.service"; +import {Config} from "../../common/config/public/Config"; @NgModule({ imports: [ diff --git a/frontend/app/config/Config.ts b/frontend/app/config/Config.ts deleted file mode 100644 index f92d358..0000000 --- a/frontend/app/config/Config.ts +++ /dev/null @@ -1,12 +0,0 @@ -import {ConfigClass} from "../../../common/config/Config"; -import {Utils} from "../../../common/Utils"; - -declare module ServerInject { - export let ConfigInject: ConfigClass; -} - -export let Config = new ConfigClass(); - -if (typeof ServerInject !== "undefined" && typeof ServerInject.ConfigInject !== "undefined") { - Utils.updateKeys(Config.Client, ServerInject.ConfigInject); -} \ No newline at end of file diff --git a/frontend/app/frame/frame.component.ts b/frontend/app/frame/frame.component.ts index a1f38d1..0c633a4 100644 --- a/frontend/app/frame/frame.component.ts +++ b/frontend/app/frame/frame.component.ts @@ -2,7 +2,7 @@ import {Component, ViewEncapsulation} from "@angular/core"; import {RouterLink} from "@angular/router"; import {AuthenticationService} from "../model/network/authentication.service"; import {UserDTO} from "../../../common/entities/UserDTO"; -import {Config} from "../config/Config"; +import {Config} from "../../../common/config/public/Config"; @Component({ selector: 'app-frame', diff --git a/frontend/app/gallery/Photo.ts b/frontend/app/gallery/Photo.ts index 8bf6fbc..a839b8b 100644 --- a/frontend/app/gallery/Photo.ts +++ b/frontend/app/gallery/Photo.ts @@ -1,7 +1,7 @@ import {PhotoDTO} from "../../../common/entities/PhotoDTO"; import {Utils} from "../../../common/Utils"; -import {Config} from "../config/Config"; import {IconPhoto} from "./IconPhoto"; +import {Config} from "../../../common/config/public/Config"; export class Photo extends IconPhoto { diff --git a/frontend/app/gallery/cache.gallery.service.ts b/frontend/app/gallery/cache.gallery.service.ts index ea97882..248508d 100644 --- a/frontend/app/gallery/cache.gallery.service.ts +++ b/frontend/app/gallery/cache.gallery.service.ts @@ -2,7 +2,7 @@ import {Injectable} from "@angular/core"; import {PhotoDTO} from "../../../common/entities/PhotoDTO"; import {DirectoryDTO} from "../../../common/entities/DirectoryDTO"; import {Utils} from "../../../common/Utils"; -import {Config} from "../config/Config"; +import {Config} from "../../../common/config/public/Config"; @Injectable() export class GalleryCacheService { diff --git a/frontend/app/gallery/gallery.component.ts b/frontend/app/gallery/gallery.component.ts index 582b531..5eee4d8 100644 --- a/frontend/app/gallery/gallery.component.ts +++ b/frontend/app/gallery/gallery.component.ts @@ -1,11 +1,11 @@ import {Component, OnInit, ViewChild} from "@angular/core"; import {AuthenticationService} from "../model/network/authentication.service"; -import {Router, ActivatedRoute, Params} from "@angular/router"; +import {ActivatedRoute, Params, Router} from "@angular/router"; import {GalleryService} from "./gallery.service"; import {GalleryGridComponent} from "./grid/grid.gallery.component"; import {GallerySearchComponent} from "./search/search.gallery.component"; -import {Config} from "../config/Config"; import {SearchTypes} from "../../../common/entities/AutoCompleteItem"; +import {Config} from "../../../common/config/public/Config"; @Component({ selector: 'gallery', diff --git a/frontend/app/gallery/grid/grid.gallery.component.ts b/frontend/app/gallery/grid/grid.gallery.component.ts index 11f1c03..a67b4d4 100644 --- a/frontend/app/gallery/grid/grid.gallery.component.ts +++ b/frontend/app/gallery/grid/grid.gallery.component.ts @@ -15,8 +15,8 @@ import {GridRowBuilder} from "./GridRowBuilder"; import {GalleryLightboxComponent} from "../lightbox/lightbox.gallery.component"; import {GridPhoto} from "./GridPhoto"; import {GalleryPhotoComponent} from "./photo/photo.grid.gallery.component"; -import {Config} from "../../config/Config"; import {OverlayService} from "../overlay.service"; +import {Config} from "../../../../common/config/public/Config"; @Component({ selector: 'gallery-grid', diff --git a/frontend/app/gallery/grid/photo/photo.grid.gallery.component.ts b/frontend/app/gallery/grid/photo/photo.grid.gallery.component.ts index f3cee17..849ed59 100644 --- a/frontend/app/gallery/grid/photo/photo.grid.gallery.component.ts +++ b/frontend/app/gallery/grid/photo/photo.grid.gallery.component.ts @@ -1,10 +1,10 @@ -import {Component, Input, ElementRef, ViewChild, OnInit, OnDestroy} from "@angular/core"; -import {IRenderable, Dimension} from "../../../model/IRenderable"; +import {Component, ElementRef, Input, OnDestroy, OnInit, ViewChild} from "@angular/core"; +import {Dimension, IRenderable} from "../../../model/IRenderable"; import {GridPhoto} from "../GridPhoto"; import {SearchTypes} from "../../../../../common/entities/AutoCompleteItem"; import {RouterLink} from "@angular/router"; -import {Config} from "../../../config/Config"; import {Thumbnail, ThumbnailManagerService} from "../../thumnailManager.service"; +import {Config} from "../../../../../common/config/public/Config"; @Component({ selector: 'gallery-grid-photo', diff --git a/frontend/app/gallery/search/search.gallery.component.ts b/frontend/app/gallery/search/search.gallery.component.ts index 6831b72..47f81f3 100644 --- a/frontend/app/gallery/search/search.gallery.component.ts +++ b/frontend/app/gallery/search/search.gallery.component.ts @@ -1,10 +1,10 @@ import {Component} from "@angular/core"; import {AutoCompleteService} from "./autocomplete.service"; import {AutoCompleteItem, SearchTypes} from "../../../../common/entities/AutoCompleteItem"; -import {RouterLink, ActivatedRoute, Params} from "@angular/router"; +import {ActivatedRoute, Params, RouterLink} from "@angular/router"; import {Message} from "../../../../common/entities/Message"; import {GalleryService} from "../gallery.service"; -import {Config} from "../../config/Config"; +import {Config} from "../../../../common/config/public/Config"; @Component({ selector: 'gallery-search', diff --git a/frontend/app/gallery/thumnailLoader.service.ts b/frontend/app/gallery/thumnailLoader.service.ts index 538ef5e..83d38ce 100644 --- a/frontend/app/gallery/thumnailLoader.service.ts +++ b/frontend/app/gallery/thumnailLoader.service.ts @@ -1,9 +1,9 @@ import {Injectable} from "@angular/core"; -import {Config} from "../config/Config"; import {GalleryCacheService} from "./cache.gallery.service"; import {Photo} from "./Photo"; import {IconPhoto} from "./IconPhoto"; import {PhotoDTO} from "../../../common/entities/PhotoDTO"; +import {Config} from "../../../common/config/public/Config"; export enum ThumbnailLoadingPriority{ high, medium, low diff --git a/frontend/app/model/network/authentication.service.ts b/frontend/app/model/network/authentication.service.ts index 547291f..61cbbf1 100644 --- a/frontend/app/model/network/authentication.service.ts +++ b/frontend/app/model/network/authentication.service.ts @@ -6,7 +6,7 @@ import {LoginCredential} from "../../../../common/entities/LoginCredential"; import {Message} from "../../../../common/entities/Message"; import {Cookie} from "ng2-cookies"; import {ErrorCodes} from "../../../../common/entities/Error"; -import {Config} from "../../config/Config"; +import {Config} from "../../../../common/config/public/Config"; declare module ServerInject { export let user: UserDTO; diff --git a/frontend/systemjs.config.js b/frontend/systemjs.config.js index 1cd7aff..b4e4860 100644 --- a/frontend/systemjs.config.js +++ b/frontend/systemjs.config.js @@ -27,7 +27,8 @@ 'rxjs': 'npm:rxjs', '@agm/core': 'npm:@agm/core/core.umd.js', - 'ng2-cookies': 'npm:ng2-cookies/ng2-cookies' + 'ng2-cookies': 'npm:ng2-cookies/ng2-cookies', + 'typeconfig': 'npm:typeconfig' }, // packages tells the System loader how to load when no filename and/or no extension packages: { diff --git a/test/backend/unit/middlewares/uesr/AuthenticationMWs.ts b/test/backend/unit/middlewares/uesr/AuthenticationMWs.ts index ab510f0..eab2326 100644 --- a/test/backend/unit/middlewares/uesr/AuthenticationMWs.ts +++ b/test/backend/unit/middlewares/uesr/AuthenticationMWs.ts @@ -4,7 +4,7 @@ import {Error, ErrorCodes} from "../../../../../common/entities/Error"; import {UserRoles} from "../../../../../common/entities/UserDTO"; import {ObjectManagerRepository} from "../../../../../backend/model/ObjectManagerRepository"; import {UserManager} from "../../../../../backend/model/memory/UserManager"; -import {Config} from "../../../../../backend/config/Config"; +import {Config} from "../../../../../common/config/private/Config"; describe('Authentication middleware', () => {