1
0
mirror of https://github.com/xuthus83/pigallery2.git synced 2025-01-14 14:43:17 +08:00

implementing improved error handling

This commit is contained in:
Braun Patrik 2017-07-09 14:23:50 +02:00
parent d591204740
commit 34c508073f
21 changed files with 213 additions and 53 deletions

View File

@ -37,8 +37,7 @@ export class AdminMWs {
return next();
} catch (err) {
Logger.warn(LOG_TAG, "Error saving database settings", err);
return next(new Error(ErrorCodes.SETTINGS_ERROR, err));
return next(new Error(ErrorCodes.SETTINGS_ERROR, "Error saving database settings", err));
}
}
@ -56,8 +55,7 @@ export class AdminMWs {
}
return next();
} catch (err) {
Logger.warn(LOG_TAG, "Error saving database settings", err);
return next(new Error(ErrorCodes.SETTINGS_ERROR, err));
return next(new Error(ErrorCodes.SETTINGS_ERROR, "Error saving database settings", err));
}
}
}

View File

@ -8,7 +8,6 @@ import {SearchTypes} from "../../common/entities/AutoCompleteItem";
import {ContentWrapper} from "../../common/entities/ConentWrapper";
import {PhotoDTO} from "../../common/entities/PhotoDTO";
import {ProjectPath} from "../ProjectPath";
import {Logger} from "../Logger";
import {Config} from "../../common/config/private/Config";
import {UserDTO} from "../../common/entities/UserDTO";
@ -38,9 +37,7 @@ export class GalleryMWs {
return next();
} catch (err) {
Logger.warn(LOG_TAG, "Error during listing the directory", err);
console.error(err);
return next(new Error(ErrorCodes.GENERAL_ERROR, err));
return next(new Error(ErrorCodes.GENERAL_ERROR, "Error during listing the directory", err));
}
}
@ -111,10 +108,7 @@ export class GalleryMWs {
req.resultPipe = new ContentWrapper(null, result);
return next();
} catch (err) {
Logger.warn(LOG_TAG, "Error during searching", err);
console.error(err);
return next(new Error(ErrorCodes.GENERAL_ERROR, err));
return next(new Error(ErrorCodes.GENERAL_ERROR, "Error during searching", err));
}
}
@ -134,9 +128,7 @@ export class GalleryMWs {
req.resultPipe = new ContentWrapper(null, result);
return next();
} catch (err) {
Logger.warn(LOG_TAG, "Error during searching", err);
console.error(err);
return next(new Error(ErrorCodes.GENERAL_ERROR, err));
return next(new Error(ErrorCodes.GENERAL_ERROR, "Error during searching", err));
}
}
@ -152,9 +144,7 @@ export class GalleryMWs {
req.resultPipe = await ObjectManagerRepository.getInstance().SearchManager.autocomplete(req.params.text);
return next();
} catch (err) {
Logger.warn(LOG_TAG, "Error during searching", err);
console.error(err);
return next(new Error(ErrorCodes.GENERAL_ERROR, err));
return next(new Error(ErrorCodes.GENERAL_ERROR, "Error during searching", err));
}
}

View File

@ -0,0 +1,22 @@
import {NextFunction, Request, Response} from "express";
import {UserRoles} from "../../common/entities/UserDTO";
import {NotificationManager} from "../model/NotifocationManager";
const LOG_TAG = "[NotificationMWs]";
export class NotificationMWs {
public static list(req: Request, res: Response, next: NextFunction) {
if (req.session.user.role >= UserRoles.Admin) {
req.resultPipe = NotificationManager.notifications;
} else if (NotificationManager.notifications.length > 0) {
req.resultPipe = NotificationManager.HasNotification;
} else {
req.resultPipe = [];
}
return next();
}
}

View File

@ -5,6 +5,8 @@ import {Message} from "../../common/entities/Message";
import {SharingDTO} from "../../common/entities/SharingDTO";
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";
export class RenderingMWs {
@ -55,10 +57,15 @@ 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);
}
let message = new Message<any>(err, null);
return res.json(message);
}
NotificationManager.error("unknown server error", err);
return next(err);
}

View File

@ -1,7 +1,6 @@
import {NextFunction, Request, Response} from "express";
import {CreateSharingDTO, SharingDTO} from "../../common/entities/SharingDTO";
import {ObjectManagerRepository} from "../model/ObjectManagerRepository";
import {Logger} from "../Logger";
import {Error, ErrorCodes} from "../../common/entities/Error";
const LOG_TAG = "[SharingMWs]";
@ -27,9 +26,7 @@ export class SharingMWs {
return next();
} catch (err) {
Logger.warn(LOG_TAG, "Error during retrieving sharing link", err);
console.error(err);
return next(new Error(ErrorCodes.GENERAL_ERROR, err));
return next(new Error(ErrorCodes.GENERAL_ERROR, "Error during retrieving sharing link", err));
}
}
@ -71,9 +68,7 @@ export class SharingMWs {
return next();
} catch (err) {
Logger.warn(LOG_TAG, "Error during creating sharing link", err);
console.error(err);
return next(new Error(ErrorCodes.GENERAL_ERROR, err));
return next(new Error(ErrorCodes.GENERAL_ERROR, "Error during creating sharing link", err));
}
}
@ -100,9 +95,7 @@ export class SharingMWs {
return next();
} catch (err) {
Logger.warn(LOG_TAG, "Error during creating sharing link", err);
console.error(err);
return next(new Error(ErrorCodes.GENERAL_ERROR, err));
return next(new Error(ErrorCodes.GENERAL_ERROR, "Error during creating sharing link", err));
}
}

View File

@ -162,8 +162,7 @@ export class ThumbnailGeneratorMWs {
return next();
}
} catch (error) {
console.log(error);
return next(new Error(ErrorCodes.THUMBNAIL_GENERATION_ERROR, error));
return next(new Error(ErrorCodes.THUMBNAIL_GENERATION_ERROR, "Error during generating thumbnail", error));
}
}

View File

@ -44,8 +44,7 @@ export class AuthenticationMWs {
return next();
}
} catch (err) {
console.error(err);
return next(new Error(ErrorCodes.CREDENTIAL_NOT_FOUND));
return next(new Error(ErrorCodes.CREDENTIAL_NOT_FOUND, null, err));
}
if (typeof req.session.user === 'undefined') {
return next(new Error(ErrorCodes.NOT_AUTHENTICATED));
@ -109,11 +108,9 @@ export class AuthenticationMWs {
return next();
}
} catch (err) {
console.error(err);
return next(new Error(ErrorCodes.CREDENTIAL_NOT_FOUND));
return next(new Error(ErrorCodes.CREDENTIAL_NOT_FOUND, null, err));
}
console.error(err);
return next(new Error(ErrorCodes.CREDENTIAL_NOT_FOUND));
}
@ -128,7 +125,7 @@ export class AuthenticationMWs {
}
//not enough parameter
if ((!req.query.sk && !req.params.sharingKey)) {
return next(new Error(ErrorCodes.INPUT_ERROR));
return next(new Error(ErrorCodes.INPUT_ERROR, "no sharing key provided"));
}
try {
@ -151,7 +148,7 @@ export class AuthenticationMWs {
return next();
} catch (err) {
return next(new Error(ErrorCodes.GENERAL_ERROR));
return next(new Error(ErrorCodes.GENERAL_ERROR, null, err));
}
}

View File

@ -22,7 +22,7 @@ export class UserMWs {
return next();
} catch (err) {
return next(new Error(ErrorCodes.GENERAL_ERROR));
return next(new Error(ErrorCodes.GENERAL_ERROR, null, err));
}
}
@ -40,7 +40,7 @@ export class UserMWs {
return next();
} catch (err) {
return next(new Error(ErrorCodes.USER_CREATION_ERROR));
return next(new Error(ErrorCodes.USER_CREATION_ERROR, null, err));
}
@ -60,7 +60,7 @@ export class UserMWs {
return next();
} catch (err) {
return next(new Error(ErrorCodes.GENERAL_ERROR));
return next(new Error(ErrorCodes.GENERAL_ERROR, null, err));
}
@ -80,7 +80,7 @@ export class UserMWs {
return next();
} catch (err) {
return next(new Error(ErrorCodes.GENERAL_ERROR));
return next(new Error(ErrorCodes.GENERAL_ERROR, null, err));
}
}
@ -99,7 +99,7 @@ export class UserMWs {
req.resultPipe = result;
next();
} catch (err) {
return next(new Error(ErrorCodes.GENERAL_ERROR));
return next(new Error(ErrorCodes.GENERAL_ERROR, null, err));
}
}

View File

@ -0,0 +1,28 @@
import {NotificationDTO, NotificationType} from "../../common/entities/NotificationDTO";
export class NotificationManager {
public static notifications: NotificationDTO[] = [];
public static HasNotification: NotificationDTO[] =
[
{
type: NotificationType.info,
message: "There are unhandled server notification. Login as Administrator to handle them."
}
];
public static error(message: string, details?: any) {
NotificationManager.notifications.push({
type: NotificationType.error,
message: message,
details: details
});
}
public static warning(message: string, details?: any) {
NotificationManager.notifications.push({
type: NotificationType.warning,
message: message,
details: details
});
}
}

View File

@ -19,10 +19,9 @@ export class ErrorRouter {
private static addGenericHandler(app) {
app.use((err: any, req: Request, res: Response, next: Function) => {
//Flush out the stack to the console
Logger.error(err);
next(new Error(ErrorCodes.SERVER_ERROR, "Unknown server side error"));
Logger.error("Unexpected error:", err);
next(new Error(ErrorCodes.SERVER_ERROR, "Unknown server side error", err));
},
RenderingMWs.renderError
);

View File

@ -1,6 +1,9 @@
import {NextFunction, Request, Response} from "express";
import {Logger} from "../Logger";
/**
* Adds logging to express
*/
export class LoggerRouter {
public static route(app: any) {

View File

@ -0,0 +1,24 @@
import {UserRoles} from "../../common/entities/UserDTO";
import {AuthenticationMWs} from "../middlewares/user/AuthenticationMWs";
import {RenderingMWs} from "../middlewares/RenderingMWs";
import {NotificationMWs} from "../middlewares/NotificationMWs";
import Request = Express.Request;
import Response = Express.Response;
export class NotificationRouter {
public static route(app: any) {
this.addGetNotifications(app);
}
private static addGetNotifications(app) {
app.get("/api/notifications",
AuthenticationMWs.authenticate,
AuthenticationMWs.authorise(UserRoles.Guest),
NotificationMWs.list,
RenderingMWs.renderResult
);
};
}

View File

@ -16,6 +16,8 @@ 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";
const LOG_TAG = "[server]";
export class Server {
@ -72,6 +74,7 @@ export class Server {
GalleryRouter.route(this.app);
SharingRouter.route(this.app);
AdminRouter.route(this.app);
NotificationRouter.route(this.app);
ErrorRouter.route(this.app);
@ -99,6 +102,8 @@ export class Server {
} 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();
}
@ -112,6 +117,9 @@ export class Server {
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." +
@ -128,6 +136,9 @@ export class Server {
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." +
@ -136,6 +147,9 @@ export class Server {
});
} 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." +
@ -147,12 +161,14 @@ export class Server {
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;
}

View File

@ -21,6 +21,6 @@ export enum ErrorCodes{
}
export class Error {
constructor(public code: ErrorCodes, public message?: string) {
constructor(public code: ErrorCodes, public message?: string, public details?: any) {
}
}

View File

@ -0,0 +1,9 @@
export enum NotificationType{
error, warning, info
}
export interface NotificationDTO {
type: NotificationType;
message: string;
details?: any;
}

View File

@ -1,6 +1,27 @@
<app-frame>
<div body class="container">
<settings-usermanager *ngIf="userManagementEnable"></settings-usermanager>
<settings-database></settings-database>
<div body class="container">
<div class="panel panel-default" *ngIf="notificationService.notifications.length>0">
<div class="panel-heading">
<h3 class="panel-title">Server notifications</h3>
</div>
<div class="panel-body">
<ng-container *ngFor="let notification of notificationService.notifications">
<div class="alert alert-{{NotificationType[notification.type]}}" role="alert">
{{notification.message}}
<br *ngIf="notification.details"/>
{{notification.details | json}}
</div>
</ng-container>
</div>
<div class="panel-footer">
To dismiss these notifications, restart the server.
</div>
</div>
<settings-usermanager *ngIf="userManagementEnable"></settings-usermanager>
<settings-database></settings-database>
</div>
</app-frame>

View File

@ -3,6 +3,8 @@ import {AuthenticationService} from "../model/network/authentication.service";
import {Router} from "@angular/router";
import {UserRoles} from "../../../common/entities/UserDTO";
import {Config} from "../../../common/config/public/Config";
import {NotificationService} from "../model/notification.service";
import {NotificationType} from "../../../common/entities/NotificationDTO";
@Component({
selector: 'admin',
templateUrl: './admin.component.html',
@ -11,8 +13,13 @@ import {Config} from "../../../common/config/public/Config";
export class AdminComponent implements OnInit {
userManagementEnable: boolean = false;
constructor(private _authService: AuthenticationService, private _router: Router) {
NotificationType: any;
constructor(private _authService: AuthenticationService,
private _router: Router,
public notificationService: NotificationService) {
this.userManagementEnable = Config.Client.authenticationRequired;
this.NotificationType = NotificationType;
}
ngOnInit() {

View File

@ -38,3 +38,9 @@ ng2-slim-loading-bar {
display: none;
}
}
.badge {
margin-left: -5px;
padding: 0;
background-color: transparent;
}

View File

@ -29,7 +29,9 @@
<li *ngIf="isAdmin()">
<a style="cursor: pointer"
class="admin-link"
[routerLink]="['/admin']"><span class="glyphicon glyphicon-wrench" aria-hidden="true"></span>
[routerLink]="['/admin']">
<span class="glyphicon glyphicon-wrench" aria-hidden="true"></span>
<span *ngIf="notificationService.notifications.length>0" class="badge">{{notificationService.notifications.length}}</span>
</a>
</li>
<li *ngIf="authenticationRequired">

View File

@ -4,6 +4,7 @@ import {AuthenticationService} from "../model/network/authentication.service";
import {UserDTO, UserRoles} from "../../../common/entities/UserDTO";
import {Config} from "../../../common/config/public/Config";
import {BehaviorSubject} from "rxjs/BehaviorSubject";
import {NotificationService} from "../model/notification.service";
@Component({
selector: 'app-frame',
@ -19,7 +20,8 @@ export class FrameComponent {
public title: string;
isIn: boolean = false;
constructor(private _authService: AuthenticationService) {
constructor(private _authService: AuthenticationService,
public notificationService: NotificationService) {
this.user = this._authService.user;
this.authenticationRequired = Config.Client.authenticationRequired;
this.title = Config.Client.applicationTitle;

View File

@ -1,5 +1,9 @@
import {Injectable, ViewContainerRef} from "@angular/core";
import {ToastsManager} from "ng2-toastr/ng2-toastr";
import {NetworkService} from "./network/network.service";
import {AuthenticationService} from "./network/authentication.service";
import {NotificationDTO, NotificationType} from "../../../common/entities/NotificationDTO";
import {UserDTO} from "../../../common/entities/UserDTO";
@Injectable()
export class NotificationService {
@ -8,9 +12,42 @@ export class NotificationService {
positionClass: "toast-top-center",
animate: "flyLeft"
};
notifications: NotificationDTO[] = [];
lastUser: UserDTO = null;
constructor(private _toastr: ToastsManager) {
constructor(private _toastr: ToastsManager,
private _networkService: NetworkService,
private _authService: AuthenticationService) {
this._authService.user.subscribe(() => {
if (this._authService.isAuthenticated() &&
(!this.lastUser ||
this.lastUser.id != this._authService.user.value.id)) {
this.getServerNotifications();
}
this.lastUser = this._authService.user.value;
});
}
async getServerNotifications() {
this.notifications = await this._networkService.getJson<NotificationDTO[]>("/notifications");
this.notifications.forEach((noti) => {
let msg = noti.message;
if (noti.details) {
msg += " Details: " + JSON.stringify(noti.details);
}
switch (noti.type) {
case NotificationType.error:
this.error(msg, "Server error");
break;
case NotificationType.warning:
this.warning(msg, "Server error");
break;
case NotificationType.info:
this.info(msg, "Server info");
break;
}
})
}
setRootViewContainerRef(vcr: ViewContainerRef) {