You've already forked pigallery2
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:
3
.gitignore
vendored
3
.gitignore
vendored
@ -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
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
# PiGallery2
|
# PiGallery2
|
||||||
|
[](https://badge.fury.io/js/pigallery2)
|
||||||
[](https://travis-ci.org/bpatrik/PiGallery2)
|
[](https://travis-ci.org/bpatrik/PiGallery2)
|
||||||
[](https://coveralls.io/github/bpatrik/PiGallery2?branch=master)
|
[](https://coveralls.io/github/bpatrik/PiGallery2?branch=master)
|
||||||
[](https://pigallery2.herokuapp.com)
|
[](https://pigallery2.herokuapp.com)
|
||||||
|
32
backend/Logger.ts
Normal file
32
backend/Logger.ts
Normal 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
|
||||||
|
});
|
@ -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));
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -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();
|
@ -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) {
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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",
|
||||||
|
Reference in New Issue
Block a user