diff --git a/.gitignore b/.gitignore index fd59fd35..cd087199 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,7 @@ .idea/ -release PiGallery2.iml node_modules/ -typings/ +pigallery2.zip frontend/app/**/*.js frontend/app/**/*.js.map frontend/main.js diff --git a/README.md b/README.md index 41c0137e..dab5ae2e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ # PiGallery2 +[![npm version](https://badge.fury.io/js/pigallery2.svg)](https://badge.fury.io/js/pigallery2) [![Build Status](https://travis-ci.org/bpatrik/PiGallery2.svg?branch=master)](https://travis-ci.org/bpatrik/PiGallery2) [![Coverage Status](https://coveralls.io/repos/github/bpatrik/PiGallery2/badge.svg?branch=master)](https://coveralls.io/github/bpatrik/PiGallery2?branch=master) [![Heroku](https://heroku-badge.herokuapp.com/?app=pigallery2&style=flat)](https://pigallery2.herokuapp.com) diff --git a/backend/Logger.ts b/backend/Logger.ts new file mode 100644 index 00000000..7c2557d3 --- /dev/null +++ b/backend/Logger.ts @@ -0,0 +1,32 @@ +import * as winston from "winston"; + +declare module 'winston' { + interface LoggerInstance { + logFileName: string; + logFilePath: string; + } +} + + +export const Logger = new winston.Logger({ + transports: [ + new winston.transports.Console({ + level: 'silly', + handleExceptions: true, + json: false, + colorize: true, + timestamp: function () { + return (new Date()).toLocaleString(); + }, + label: "innerLabel", + 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 + }) + ], + exitOnError: false +}); \ No newline at end of file diff --git a/backend/middlewares/GalleryMWs.ts b/backend/middlewares/GalleryMWs.ts index 6c837ee1..5aa49119 100644 --- a/backend/middlewares/GalleryMWs.ts +++ b/backend/middlewares/GalleryMWs.ts @@ -10,7 +10,10 @@ 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"; + +const LOG_TAG = "[GalleryMWs]"; export class GalleryMWs { @@ -24,6 +27,7 @@ export class GalleryMWs { ObjectManagerRepository.getInstance().getGalleryManager().listDirectory(directoryName, (err, directory: DirectoryDTO) => { if (err || !directory) { + Logger.warn(LOG_TAG, "Error during listing the directory", err); console.error(err); return next(new Error(ErrorCodes.GENERAL_ERROR, err)); } diff --git a/backend/model/DiskManger.ts b/backend/model/DiskManger.ts index 18b9394a..c77b6c6d 100644 --- a/backend/model/DiskManger.ts +++ b/backend/model/DiskManger.ts @@ -10,9 +10,13 @@ import { PositionMetaData } from "../../common/entities/PhotoDTO"; import {ProjectPath} from "../ProjectPath"; +import {Logger} from "../Logger"; const Pool = require('threads').Pool; const pool = new Pool(); + +const LOG_TAG = "[DiskManager]"; + pool.run( (input: { relativeDirectoryName: string, @@ -49,13 +53,15 @@ pool.run( return new Promise((resolve: (metadata: PhotoMetadata) => void, reject) => { fs.readFile(fullPath, function (err, data) { if (err) { - return reject(err); - } else { - let exif = exif_parser.create(data).parse(); - let iptcData = iptc(data); + return reject({file: fullPath, error: err}); + } + try { - let imageSize: ImageSize = {width: exif.imageSize.width, height: exif.imageSize.height}; - let cameraData: CameraMetadata = { + const exif = exif_parser.create(data).parse(); + const iptcData = iptc(data); + + const imageSize: ImageSize = {width: exif.imageSize.width, height: exif.imageSize.height}; + const cameraData: CameraMetadata = { ISO: exif.tags.ISO, model: exif.tags.Modeol, maker: exif.tags.Make, @@ -64,14 +70,14 @@ pool.run( focalLength: exif.tags.FocalLength, lens: exif.tags.LensModel, }; - let GPS: GPSMetadata = { + const GPS: GPSMetadata = { latitude: exif.tags.GPSLatitude, longitude: exif.tags.GPSLongitude, altitude: exif.tags.GPSAltitude }; - let positionData: PositionMetaData = { + const positionData: PositionMetaData = { GPSData: GPS, country: iptcData.country_or_primary_location_name, state: iptcData.province_or_state, @@ -79,7 +85,7 @@ pool.run( }; //Decode characters to UTF8 - let decode = (s: any) => { + const decode = (s: any) => { for (let a, b, i = -1, l = (s = s.split("")).length, o = String.fromCharCode, c = "charCodeAt"; ++i < l; ((a = s[i][c](0)) & 0x80) && (s[i] = (a & 0xfc) == 0xc0 && ((b = s[i + 1][c](0)) & 0xc0) == 0x80 ? @@ -88,11 +94,12 @@ pool.run( return s.join(""); }; - let keywords: [string] = iptcData.keywords.map((s: string) => decode(s)); - let creationDate: number = iptcData.date_time.getTime(); + + const keywords: string[] = (iptcData.keywords || []).map((s: string) => decode(s)); + const creationDate: number = iptcData.date_time ? iptcData.date_time.getTime() : 0; - let metadata: PhotoMetadata = { + const metadata: PhotoMetadata = { keywords: keywords, cameraData: cameraData, positionData: positionData, @@ -100,6 +107,8 @@ pool.run( creationDate: creationDate }; return resolve(metadata); + } catch (err) { + return reject({file: fullPath, error: err}); } }); }); @@ -111,7 +120,6 @@ pool.run( directoryParent: string, absoluteDirectoryName: string }, maxPhotos: number = null, photosOnly: boolean = false): Promise => { - return new Promise((resolve, reject) => { let promises: Array> = []; let directory = { @@ -127,41 +135,49 @@ pool.run( return reject(err); } - - for (let i = 0; i < list.length; i++) { - let file = list[i]; - let fullFilePath = path.normalize(path.resolve(directoryInfo.absoluteDirectoryName, file)); - if (photosOnly == false && fs.statSync(fullFilePath).isDirectory()) { - let promise = parseDir({ - relativeDirectoryName: path.join(directoryInfo.relativeDirectoryName, path.sep), - directoryName: file, - directoryParent: path.join(directoryInfo.relativeDirectoryName, path.sep), - absoluteDirectoryName: fullFilePath - }, - 5, true - ).then((dir) => { - directory.directories.push(dir); - }); - promises.push(promise); - } else if (isImage(fullFilePath)) { + 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({ + relativeDirectoryName: path.join(directoryInfo.relativeDirectoryName, path.sep), + directoryName: file, + directoryParent: path.join(directoryInfo.relativeDirectoryName, path.sep), + absoluteDirectoryName: fullFilePath + }, + 5, true + ).then((dir) => { + directory.directories.push(dir); + }); + promises.push(promise); + } else if (isImage(fullFilePath)) { - let promise = loadPhotoMetadata(fullFilePath).then((photoMetadata) => { - directory.photos.push({name: file, directory: null, metadata: photoMetadata}); - }); + let promise = loadPhotoMetadata(fullFilePath).then((photoMetadata) => { + directory.photos.push({ + name: file, + directory: null, + metadata: photoMetadata + }); + }); - promises.push(promise); - if (maxPhotos != null && promises.length > maxPhotos) { - break; + promises.push(promise); + if (maxPhotos != null && promises.length > maxPhotos) { + break; + } } } - } - Promise.all(promises).then(() => { - return resolve(directory); - }).catch((err) => { - console.error(err); - }); + Promise.all(promises).then(() => { + return resolve(directory); + }).catch((err) => { + return reject({directoryInfo: directoryInfo, error: err}); + }); + } catch (err) { + return reject({directoryInfo: directoryInfo, error: err}); + } }); @@ -179,7 +195,7 @@ pool.run( export class DiskManager { public static scanDirectory(relativeDirectoryName: string, cb: (error: any, result: DirectoryDTO) => void) { - console.log("DiskManager: scanDirectory"); + Logger.silly(LOG_TAG, "scanDirectory"); let directoryName = path.basename(relativeDirectoryName); let directoryParent = path.join(path.dirname(relativeDirectoryName), path.sep); let absoluteDirectoryName = path.join(ProjectPath.ImageFolder, relativeDirectoryName); @@ -189,7 +205,11 @@ export class DiskManager { directoryName, directoryParent, absoluteDirectoryName - }) .on('done', (error: any, result: DirectoryDTO) => { + }).on('done', (error: any, result: DirectoryDTO) => { + if (error || !result) { + return cb(error, result); + } + let addDirs = (dir: DirectoryDTO) => { dir.photos.forEach((ph) => { ph.directory = dir; @@ -199,9 +219,8 @@ export class DiskManager { }); }; addDirs(result); - return cb(error, result); - }).on('error', (job, error) => { + }).on('error', (error) => { return cb(error, null); }); } diff --git a/backend/server.ts b/backend/server.ts index d7c53b47..256503f5 100644 --- a/backend/server.ts +++ b/backend/server.ts @@ -12,8 +12,9 @@ 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"; - +const LOG_TAG = "[server]"; export class Server { private debug: any; @@ -21,8 +22,8 @@ export class Server { private server: any; constructor() { - console.log("using config"); - console.log(Config); + Logger.info(LOG_TAG, "config:"); + Logger.info(LOG_TAG, JSON.stringify(Config, null, '\t')); this.debug = _debug("PiGallery2:server"); this.app = _express(); @@ -55,19 +56,21 @@ export class Server { // for parsing application/json this.app.use(_bodyParser.json()); - - ObjectManagerRepository.InitMySQLManagers().catch((err) => { - console.error("Erro during initailizing mysql falling back to memory DB"); - console.log(err); - Config.setDatabaseType(DatabaseType.memory); + 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); + Config.setDatabaseType(DatabaseType.memory); + ObjectManagerRepository.InitMemoryManagers(); + }); + } else { ObjectManagerRepository.InitMemoryManagers(); - }); + } if (Config.Server.thumbnail.hardwareAcceleration == true) { try { const sharp = require.resolve("sharp"); } catch (err) { - console.error("Thumbnail hardware acceleration is not possible." + + 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.hardwareAcceleration = false; @@ -114,11 +117,11 @@ export class Server { // handle specific listen error with friendly messages switch (error.code) { case 'EACCES': - console.error(bind + ' requires elevated privileges'); + Logger.error(LOG_TAG, bind + ' requires elevated privileges'); process.exit(1); break; case 'EADDRINUSE': - console.error(bind + ' is already in use'); + Logger.error(LOG_TAG, bind + ' is already in use'); process.exit(1); break; default: @@ -135,14 +138,14 @@ export class Server { const bind = typeof addr === 'string' ? 'pipe ' + addr : 'port ' + addr.port; - this.debug('Listening on ' + bind); + Logger.debug(LOG_TAG, 'Listening on ' + bind); }; } if (process.env.DEBUG) { - console.log("Running in DEBUG mode"); + Logger.debug(LOG_TAG, "Running in DEBUG mode"); } new Server(); \ No newline at end of file diff --git a/common/entities/Error.ts b/common/entities/Error.ts index 03436e9d..49cfed1e 100644 --- a/common/entities/Error.ts +++ b/common/entities/Error.ts @@ -1,22 +1,22 @@ export enum ErrorCodes{ - NOT_AUTHENTICATED, - ALREADY_AUTHENTICATED, - NOT_AUTHORISED, - CREDENTIAL_NOT_FOUND, + NOT_AUTHENTICATED = 0, + ALREADY_AUTHENTICATED = 1, + NOT_AUTHORISED = 2, + CREDENTIAL_NOT_FOUND = 3, - USER_CREATION_ERROR, + USER_CREATION_ERROR = 4, - GENERAL_ERROR, - THUMBNAIL_GENERATION_ERROR, - SERVER_ERROR, + GENERAL_ERROR = 5, + THUMBNAIL_GENERATION_ERROR = 6, + SERVER_ERROR = 7, - USER_MANAGEMENT_DISABLED + USER_MANAGEMENT_DISABLED = 8 } export class Error { - constructor(public code:ErrorCodes, public message?:String) { + constructor(public code: ErrorCodes, public message?: string) { } } \ No newline at end of file diff --git a/package.json b/package.json index f0195136..ede85800 100644 --- a/package.json +++ b/package.json @@ -50,6 +50,7 @@ "systemjs": "0.20.12", "threads": "^0.7.3", "typeorm": "0.0.11", + "winston": "^2.3.1", "zone.js": "^0.8.11" }, "devDependencies": { @@ -59,6 +60,7 @@ "@types/node": "^7.0.22", "@types/optimist": "0.0.29", "@types/sharp": "^0.17.1", + "@types/winston": "^2.3.3", "chai": "^4.0.0", "gulp": "^3.9.1", "gulp-typescript": "^3.1.7",