1
0
mirror of https://github.com/bpatrik/pigallery2.git synced 2025-07-07 00:57:28 +02:00

improving logging and fixing file reader error handling bug

This commit is contained in:
Braun Patrik
2017-06-03 22:35:47 +02:00
parent e430f0bdf6
commit 4333e41155
8 changed files with 132 additions and 72 deletions

3
.gitignore vendored
View File

@ -1,8 +1,7 @@
.idea/ .idea/
release
PiGallery2.iml PiGallery2.iml
node_modules/ node_modules/
typings/ pigallery2.zip
frontend/app/**/*.js frontend/app/**/*.js
frontend/app/**/*.js.map frontend/app/**/*.js.map
frontend/main.js frontend/main.js

View File

@ -1,4 +1,5 @@
# PiGallery2 # 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) [![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) [![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) [![Heroku](https://heroku-badge.herokuapp.com/?app=pigallery2&style=flat)](https://pigallery2.herokuapp.com)

32
backend/Logger.ts Normal file
View File

@ -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
});

View File

@ -10,7 +10,10 @@ import {SearchResultDTO} from "../../common/entities/SearchResult";
import {PhotoDTO} from "../../common/entities/PhotoDTO"; import {PhotoDTO} from "../../common/entities/PhotoDTO";
import {Config} from "../config/Config"; import {Config} from "../config/Config";
import {ProjectPath} from "../ProjectPath"; import {ProjectPath} from "../ProjectPath";
import {Logger} from "../Logger";
const LOG_TAG = "[GalleryMWs]";
export class GalleryMWs { export class GalleryMWs {
@ -24,6 +27,7 @@ export class GalleryMWs {
ObjectManagerRepository.getInstance().getGalleryManager().listDirectory(directoryName, (err, directory: DirectoryDTO) => { ObjectManagerRepository.getInstance().getGalleryManager().listDirectory(directoryName, (err, directory: DirectoryDTO) => {
if (err || !directory) { if (err || !directory) {
Logger.warn(LOG_TAG, "Error during listing the directory", err);
console.error(err); console.error(err);
return next(new Error(ErrorCodes.GENERAL_ERROR, err)); return next(new Error(ErrorCodes.GENERAL_ERROR, err));
} }

View File

@ -10,9 +10,13 @@ import {
PositionMetaData PositionMetaData
} from "../../common/entities/PhotoDTO"; } from "../../common/entities/PhotoDTO";
import {ProjectPath} from "../ProjectPath"; import {ProjectPath} from "../ProjectPath";
import {Logger} from "../Logger";
const Pool = require('threads').Pool; const Pool = require('threads').Pool;
const pool = new Pool(); const pool = new Pool();
const LOG_TAG = "[DiskManager]";
pool.run( pool.run(
(input: { (input: {
relativeDirectoryName: string, relativeDirectoryName: string,
@ -49,13 +53,15 @@ pool.run(
return new Promise<PhotoMetadata>((resolve: (metadata: PhotoMetadata) => void, reject) => { return new Promise<PhotoMetadata>((resolve: (metadata: PhotoMetadata) => void, reject) => {
fs.readFile(fullPath, function (err, data) { fs.readFile(fullPath, function (err, data) {
if (err) { if (err) {
return reject(err); return reject({file: fullPath, error: err});
} else { }
let exif = exif_parser.create(data).parse(); try {
let iptcData = iptc(data);
let imageSize: ImageSize = {width: exif.imageSize.width, height: exif.imageSize.height}; const exif = exif_parser.create(data).parse();
let cameraData: CameraMetadata = { const iptcData = iptc(data);
const imageSize: ImageSize = {width: exif.imageSize.width, height: exif.imageSize.height};
const cameraData: CameraMetadata = {
ISO: exif.tags.ISO, ISO: exif.tags.ISO,
model: exif.tags.Modeol, model: exif.tags.Modeol,
maker: exif.tags.Make, maker: exif.tags.Make,
@ -64,14 +70,14 @@ pool.run(
focalLength: exif.tags.FocalLength, focalLength: exif.tags.FocalLength,
lens: exif.tags.LensModel, lens: exif.tags.LensModel,
}; };
let GPS: GPSMetadata = { const GPS: GPSMetadata = {
latitude: exif.tags.GPSLatitude, latitude: exif.tags.GPSLatitude,
longitude: exif.tags.GPSLongitude, longitude: exif.tags.GPSLongitude,
altitude: exif.tags.GPSAltitude altitude: exif.tags.GPSAltitude
}; };
let positionData: PositionMetaData = { const positionData: PositionMetaData = {
GPSData: GPS, GPSData: GPS,
country: iptcData.country_or_primary_location_name, country: iptcData.country_or_primary_location_name,
state: iptcData.province_or_state, state: iptcData.province_or_state,
@ -79,7 +85,7 @@ pool.run(
}; };
//Decode characters to UTF8 //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; for (let a, b, i = -1, l = (s = s.split("")).length, o = String.fromCharCode, c = "charCodeAt"; ++i < l;
((a = s[i][c](0)) & 0x80) && ((a = s[i][c](0)) & 0x80) &&
(s[i] = (a & 0xfc) == 0xc0 && ((b = s[i + 1][c](0)) & 0xc0) == 0x80 ? (s[i] = (a & 0xfc) == 0xc0 && ((b = s[i + 1][c](0)) & 0xc0) == 0x80 ?
@ -88,11 +94,12 @@ pool.run(
return s.join(""); 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 = <PhotoMetadata>{ const metadata: PhotoMetadata = <PhotoMetadata>{
keywords: keywords, keywords: keywords,
cameraData: cameraData, cameraData: cameraData,
positionData: positionData, positionData: positionData,
@ -100,6 +107,8 @@ pool.run(
creationDate: creationDate creationDate: creationDate
}; };
return resolve(metadata); return resolve(metadata);
} catch (err) {
return reject({file: fullPath, error: err});
} }
}); });
}); });
@ -111,7 +120,6 @@ pool.run(
directoryParent: string, directoryParent: string,
absoluteDirectoryName: string absoluteDirectoryName: string
}, maxPhotos: number = null, photosOnly: boolean = false): Promise<DirectoryDTO> => { }, maxPhotos: number = null, photosOnly: boolean = false): Promise<DirectoryDTO> => {
return new Promise<DirectoryDTO>((resolve, reject) => { return new Promise<DirectoryDTO>((resolve, reject) => {
let promises: Array<Promise<any>> = []; let promises: Array<Promise<any>> = [];
let directory = <DirectoryDTO>{ let directory = <DirectoryDTO>{
@ -127,9 +135,10 @@ pool.run(
return reject(err); return reject(err);
} }
try {
for (let i = 0; i < list.length; i++) { for (let i = 0; i < list.length; i++) {
let file = list[i]; let file = list[i];
console.log(list[i]);
let fullFilePath = path.normalize(path.resolve(directoryInfo.absoluteDirectoryName, file)); let fullFilePath = path.normalize(path.resolve(directoryInfo.absoluteDirectoryName, file));
if (photosOnly == false && fs.statSync(fullFilePath).isDirectory()) { if (photosOnly == false && fs.statSync(fullFilePath).isDirectory()) {
let promise = parseDir({ let promise = parseDir({
@ -147,7 +156,11 @@ pool.run(
let promise = loadPhotoMetadata(fullFilePath).then((photoMetadata) => { let promise = loadPhotoMetadata(fullFilePath).then((photoMetadata) => {
directory.photos.push(<PhotoDTO>{name: file, directory: null, metadata: photoMetadata}); directory.photos.push(<PhotoDTO>{
name: file,
directory: null,
metadata: photoMetadata
});
}); });
promises.push(promise); promises.push(promise);
@ -160,8 +173,11 @@ pool.run(
Promise.all(promises).then(() => { Promise.all(promises).then(() => {
return resolve(directory); return resolve(directory);
}).catch((err) => { }).catch((err) => {
console.error(err); return reject({directoryInfo: directoryInfo, error: err});
}); });
} catch (err) {
return reject({directoryInfo: directoryInfo, error: err});
}
}); });
@ -179,7 +195,7 @@ pool.run(
export class DiskManager { export class DiskManager {
public static scanDirectory(relativeDirectoryName: string, cb: (error: any, result: DirectoryDTO) => void) { 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 directoryName = path.basename(relativeDirectoryName);
let directoryParent = path.join(path.dirname(relativeDirectoryName), path.sep); let directoryParent = path.join(path.dirname(relativeDirectoryName), path.sep);
let absoluteDirectoryName = path.join(ProjectPath.ImageFolder, relativeDirectoryName); let absoluteDirectoryName = path.join(ProjectPath.ImageFolder, relativeDirectoryName);
@ -189,7 +205,11 @@ export class DiskManager {
directoryName, directoryName,
directoryParent, directoryParent,
absoluteDirectoryName absoluteDirectoryName
}) .on('done', (error: any, result: DirectoryDTO) => { }).on('done', (error: any, result: DirectoryDTO) => {
if (error || !result) {
return cb(error, result);
}
let addDirs = (dir: DirectoryDTO) => { let addDirs = (dir: DirectoryDTO) => {
dir.photos.forEach((ph) => { dir.photos.forEach((ph) => {
ph.directory = dir; ph.directory = dir;
@ -199,9 +219,8 @@ export class DiskManager {
}); });
}; };
addDirs(result); addDirs(result);
return cb(error, result); return cb(error, result);
}).on('error', (job, error) => { }).on('error', (error) => {
return cb(error, null); return cb(error, null);
}); });
} }

View File

@ -12,8 +12,9 @@ import {SharingRouter} from "./routes/SharingRouter";
import {DatabaseType} from "../common/config/Config"; import {DatabaseType} from "../common/config/Config";
import {ObjectManagerRepository} from "./model/ObjectManagerRepository"; import {ObjectManagerRepository} from "./model/ObjectManagerRepository";
import {Config} from "./config/Config"; import {Config} from "./config/Config";
import {Logger} from "./Logger";
const LOG_TAG = "[server]";
export class Server { export class Server {
private debug: any; private debug: any;
@ -21,8 +22,8 @@ export class Server {
private server: any; private server: any;
constructor() { constructor() {
console.log("using config"); Logger.info(LOG_TAG, "config:");
console.log(Config); Logger.info(LOG_TAG, JSON.stringify(Config, null, '\t'));
this.debug = _debug("PiGallery2:server"); this.debug = _debug("PiGallery2:server");
this.app = _express(); this.app = _express();
@ -55,19 +56,21 @@ export class Server {
// for parsing application/json // for parsing application/json
this.app.use(_bodyParser.json()); this.app.use(_bodyParser.json());
if (Config.Server.database.type == DatabaseType.mysql) {
ObjectManagerRepository.InitMySQLManagers().catch((err) => { ObjectManagerRepository.InitMySQLManagers().catch((err) => {
console.error("Erro during initailizing mysql falling back to memory DB"); Logger.warn(LOG_TAG, "Error during initailizing mysql falling back to memory DB", err);
console.log(err);
Config.setDatabaseType(DatabaseType.memory); Config.setDatabaseType(DatabaseType.memory);
ObjectManagerRepository.InitMemoryManagers(); ObjectManagerRepository.InitMemoryManagers();
}); });
} else {
ObjectManagerRepository.InitMemoryManagers();
}
if (Config.Server.thumbnail.hardwareAcceleration == true) { if (Config.Server.thumbnail.hardwareAcceleration == true) {
try { try {
const sharp = require.resolve("sharp"); const sharp = require.resolve("sharp");
} catch (err) { } 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." + " 'Sharp' node module is not found." +
" Falling back to JS based thumbnail generation"); " Falling back to JS based thumbnail generation");
Config.Server.thumbnail.hardwareAcceleration = false; Config.Server.thumbnail.hardwareAcceleration = false;
@ -114,11 +117,11 @@ export class Server {
// handle specific listen error with friendly messages // handle specific listen error with friendly messages
switch (error.code) { switch (error.code) {
case 'EACCES': case 'EACCES':
console.error(bind + ' requires elevated privileges'); Logger.error(LOG_TAG, bind + ' requires elevated privileges');
process.exit(1); process.exit(1);
break; break;
case 'EADDRINUSE': case 'EADDRINUSE':
console.error(bind + ' is already in use'); Logger.error(LOG_TAG, bind + ' is already in use');
process.exit(1); process.exit(1);
break; break;
default: default:
@ -135,14 +138,14 @@ export class Server {
const bind = typeof addr === 'string' const bind = typeof addr === 'string'
? 'pipe ' + addr ? 'pipe ' + addr
: 'port ' + addr.port; : 'port ' + addr.port;
this.debug('Listening on ' + bind); Logger.debug(LOG_TAG, 'Listening on ' + bind);
}; };
} }
if (process.env.DEBUG) { if (process.env.DEBUG) {
console.log("Running in DEBUG mode"); Logger.debug(LOG_TAG, "Running in DEBUG mode");
} }
new Server(); new Server();

View File

@ -1,22 +1,22 @@
export enum ErrorCodes{ export enum ErrorCodes{
NOT_AUTHENTICATED, NOT_AUTHENTICATED = 0,
ALREADY_AUTHENTICATED, ALREADY_AUTHENTICATED = 1,
NOT_AUTHORISED, NOT_AUTHORISED = 2,
CREDENTIAL_NOT_FOUND, CREDENTIAL_NOT_FOUND = 3,
USER_CREATION_ERROR, USER_CREATION_ERROR = 4,
GENERAL_ERROR, GENERAL_ERROR = 5,
THUMBNAIL_GENERATION_ERROR, THUMBNAIL_GENERATION_ERROR = 6,
SERVER_ERROR, SERVER_ERROR = 7,
USER_MANAGEMENT_DISABLED USER_MANAGEMENT_DISABLED = 8
} }
export class Error { export class Error {
constructor(public code:ErrorCodes, public message?:String) { constructor(public code: ErrorCodes, public message?: string) {
} }
} }

View File

@ -50,6 +50,7 @@
"systemjs": "0.20.12", "systemjs": "0.20.12",
"threads": "^0.7.3", "threads": "^0.7.3",
"typeorm": "0.0.11", "typeorm": "0.0.11",
"winston": "^2.3.1",
"zone.js": "^0.8.11" "zone.js": "^0.8.11"
}, },
"devDependencies": { "devDependencies": {
@ -59,6 +60,7 @@
"@types/node": "^7.0.22", "@types/node": "^7.0.22",
"@types/optimist": "0.0.29", "@types/optimist": "0.0.29",
"@types/sharp": "^0.17.1", "@types/sharp": "^0.17.1",
"@types/winston": "^2.3.3",
"chai": "^4.0.0", "chai": "^4.0.0",
"gulp": "^3.9.1", "gulp": "^3.9.1",
"gulp-typescript": "^3.1.7", "gulp-typescript": "^3.1.7",