mirror of
https://github.com/bpatrik/pigallery2.git
synced 2024-12-25 02:04:15 +02:00
implementing sharing
This commit is contained in:
parent
b499f0b5ac
commit
eaa3c15825
12
README.md
12
README.md
@ -1,11 +1,11 @@
|
||||
# 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)
|
||||
[![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)
|
||||
[![Code Climate](https://codeclimate.com/github/bpatrik/PiGallery2/badges/gpa.svg)](https://codeclimate.com/github/bpatrik/PiGallery2)
|
||||
[![Dependency Status](https://david-dm.org/bpatrik/PiGallery2.svg)](https://david-dm.org/bpatrik/PiGallery2)
|
||||
[![devDependency Status](https://david-dm.org/bpatrik/PiGallery2/dev-status.svg)](https://david-dm.org/bpatrik/PiGallery2#info=devDependencies)
|
||||
[![Code Climate](https://codeclimate.com/github/bpatrik/pigallery2/badges/gpa.svg)](https://codeclimate.com/github/bpatrik/pigallery2)
|
||||
[![Dependency Status](https://david-dm.org/bpatrik/pigallery2.svg)](https://david-dm.org/bpatrik/pigallery2)
|
||||
[![devDependency Status](https://david-dm.org/bpatrik/pigallery2/dev-status.svg)](https://david-dm.org/bpatrik/pigallery2#info=devDependencies)
|
||||
|
||||
This is a directory-first photo gallery website, optimised for running on low resource servers (especially on raspberry pi)
|
||||
|
||||
@ -36,7 +36,7 @@ Full node install description: https://raspberrypi.stackexchange.com/questions/4
|
||||
### Install PiGallery2
|
||||
```bash
|
||||
cd ~
|
||||
wget https://github.com/bpatrik/PiGallery2/archive/1.0.0-beta.0.tar.gz
|
||||
wget https://github.com/bpatrik/pigallery2/archive/1.0.0-beta.0.tar.gz
|
||||
tar -xzvf 1.0.0-beta.0.tar.gz
|
||||
cd PiGallery2-1.0.0-beta.0
|
||||
npm install
|
||||
|
@ -5,6 +5,7 @@ class ProjectPathClass {
|
||||
public Root: string;
|
||||
public ImageFolder: string;
|
||||
public ThumbnailFolder: string;
|
||||
public FrontendFolder: string;
|
||||
|
||||
isAbsolutePath(pathStr: string) {
|
||||
return path.resolve(pathStr) === path.normalize(pathStr);
|
||||
@ -18,6 +19,7 @@ class ProjectPathClass {
|
||||
this.Root = path.join(__dirname, "/../");
|
||||
this.ImageFolder = this.isAbsolutePath(Config.Server.imagesFolder) ? Config.Server.imagesFolder : path.join(this.Root, Config.Server.imagesFolder);
|
||||
this.ThumbnailFolder = this.isAbsolutePath(Config.Server.thumbnail.folder) ? Config.Server.thumbnail.folder : path.join(this.Root, Config.Server.thumbnail.folder);
|
||||
this.FrontendFolder = path.join(this.Root, 'dist')
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,13 +11,14 @@ import {PhotoDTO} from "../../common/entities/PhotoDTO";
|
||||
import {ProjectPath} from "../ProjectPath";
|
||||
import {Logger} from "../Logger";
|
||||
import {Config} from "../../common/config/private/Config";
|
||||
import {UserUtil} from "../../common/entities/UserDTO";
|
||||
|
||||
|
||||
const LOG_TAG = "[GalleryMWs]";
|
||||
export class GalleryMWs {
|
||||
|
||||
|
||||
public static listDirectory(req: Request, res: Response, next: NextFunction) {
|
||||
public static async listDirectory(req: Request, res: Response, next: NextFunction) {
|
||||
let directoryName = req.params.directory || "/";
|
||||
let absoluteDirectoryName = path.join(ProjectPath.ImageFolder, directoryName);
|
||||
|
||||
@ -25,17 +26,23 @@ export class GalleryMWs {
|
||||
return next();
|
||||
}
|
||||
|
||||
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));
|
||||
try {
|
||||
|
||||
const directory = await ObjectManagerRepository.getInstance().GalleryManager.listDirectory(directoryName);
|
||||
if (req.session.user.permissions &&
|
||||
req.session.user.permissions.length > 0 &&
|
||||
req.session.user.permissions[0] != "/") {
|
||||
directory.directories = directory.directories.filter(d =>
|
||||
UserUtil.isDirectoryAvailable(d, req.session.user.permissions));
|
||||
}
|
||||
|
||||
req.resultPipe = new ContentWrapper(directory, null);
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -99,7 +106,7 @@ export class GalleryMWs {
|
||||
type = parseInt(req.query.type);
|
||||
}
|
||||
|
||||
ObjectManagerRepository.getInstance().getSearchManager().search(req.params.text, type, (err, result: SearchResultDTO) => {
|
||||
ObjectManagerRepository.getInstance().SearchManager.search(req.params.text, type, (err, result: SearchResultDTO) => {
|
||||
if (err || !result) {
|
||||
return next(new Error(ErrorCodes.GENERAL_ERROR, err));
|
||||
}
|
||||
@ -119,7 +126,7 @@ export class GalleryMWs {
|
||||
}
|
||||
|
||||
|
||||
ObjectManagerRepository.getInstance().getSearchManager().instantSearch(req.params.text, (err, result: SearchResultDTO) => {
|
||||
ObjectManagerRepository.getInstance().SearchManager.instantSearch(req.params.text, (err, result: SearchResultDTO) => {
|
||||
if (err || !result) {
|
||||
return next(new Error(ErrorCodes.GENERAL_ERROR, err));
|
||||
}
|
||||
@ -136,7 +143,7 @@ export class GalleryMWs {
|
||||
return next();
|
||||
}
|
||||
|
||||
ObjectManagerRepository.getInstance().getSearchManager().autocomplete(req.params.text, (err, items: Array<AutoCompleteItem>) => {
|
||||
ObjectManagerRepository.getInstance().SearchManager.autocomplete(req.params.text, (err, items: Array<AutoCompleteItem>) => {
|
||||
if (err || !items) {
|
||||
return next(new Error(ErrorCodes.GENERAL_ERROR, err));
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ import {NextFunction, Request, Response} from "express";
|
||||
import {Error, ErrorCodes} from "../../common/entities/Error";
|
||||
import {Utils} from "../../common/Utils";
|
||||
import {Message} from "../../common/entities/Message";
|
||||
import {SharingDTO} from "../../common/entities/SharingDTO";
|
||||
|
||||
export class RenderingMWs {
|
||||
|
||||
@ -15,14 +16,23 @@ export class RenderingMWs {
|
||||
|
||||
public static renderSessionUser(req: Request, res: Response, next: NextFunction) {
|
||||
if (!(req.session.user)) {
|
||||
return next(new Error(ErrorCodes.GENERAL_ERROR));
|
||||
return next(new Error(ErrorCodes.GENERAL_ERROR, "User not exists"));
|
||||
}
|
||||
|
||||
let user = Utils.clone(req.session.user);
|
||||
const user = Utils.clone(req.session.user);
|
||||
delete user.password;
|
||||
RenderingMWs.renderMessage(res, user);
|
||||
}
|
||||
|
||||
public static renderSharing(req: Request, res: Response, next: NextFunction) {
|
||||
if (!req.resultPipe)
|
||||
return next();
|
||||
|
||||
const sharing = Utils.clone<SharingDTO>(req.resultPipe);
|
||||
delete sharing.password;
|
||||
RenderingMWs.renderMessage(res, sharing);
|
||||
}
|
||||
|
||||
public static renderFile(req: Request, res: Response, next: NextFunction) {
|
||||
if (!req.resultPipe)
|
||||
return next();
|
||||
|
109
backend/middlewares/SharingMWs.ts
Normal file
109
backend/middlewares/SharingMWs.ts
Normal file
@ -0,0 +1,109 @@
|
||||
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]";
|
||||
export class SharingMWs {
|
||||
|
||||
|
||||
private static generateKey(): string {
|
||||
function s4() {
|
||||
return Math.floor((1 + Math.random()) * 0x10000)
|
||||
.toString(16)
|
||||
.substring(1);
|
||||
}
|
||||
|
||||
return s4() + s4();
|
||||
}
|
||||
|
||||
|
||||
public static async getSharing(req: Request, res: Response, next: NextFunction) {
|
||||
const sharingKey = req.params.sharingKey;
|
||||
|
||||
try {
|
||||
req.resultPipe = await ObjectManagerRepository.getInstance().SharingManager.findOne({sharingKey: sharingKey});
|
||||
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));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static async createSharing(req: Request, res: Response, next: NextFunction) {
|
||||
if ((typeof req.body === 'undefined') || (typeof req.body.createSharing === 'undefined')) {
|
||||
return next(new Error(ErrorCodes.INPUT_ERROR, "createSharing filed is missing"));
|
||||
}
|
||||
const createSharing: CreateSharingDTO = req.body.createSharing;
|
||||
let sharingKey = SharingMWs.generateKey();
|
||||
|
||||
//create one not yet used
|
||||
|
||||
while (true) {
|
||||
try {
|
||||
await ObjectManagerRepository.getInstance().SharingManager.findOne({sharingKey: sharingKey});
|
||||
sharingKey = this.generateKey();
|
||||
} catch (err) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const directoryName = req.params.directory || "/";
|
||||
let sharing: SharingDTO = {
|
||||
id: null,
|
||||
sharingKey: sharingKey,
|
||||
path: directoryName,
|
||||
password: createSharing.password,
|
||||
creator: req.session.user,
|
||||
expires: Date.now() + createSharing.valid,
|
||||
includeSubfolders: createSharing.includeSubfolders,
|
||||
timeStamp: Date.now()
|
||||
};
|
||||
|
||||
try {
|
||||
|
||||
req.resultPipe = await ObjectManagerRepository.getInstance().SharingManager.createSharing(sharing);
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
public static async updateSharing(req: Request, res: Response, next: NextFunction) {
|
||||
if ((typeof req.body === 'undefined') || (typeof req.body.updateSharing === 'undefined')) {
|
||||
return next(new Error(ErrorCodes.INPUT_ERROR, "updateSharing filed is missing"));
|
||||
}
|
||||
const updateSharing: CreateSharingDTO = req.body.updateSharing;
|
||||
const directoryName = req.params.directory || "/";
|
||||
let sharing: SharingDTO = {
|
||||
id: updateSharing.id,
|
||||
path: directoryName,
|
||||
sharingKey: "",
|
||||
password: updateSharing.password,
|
||||
creator: req.session.user,
|
||||
expires: Date.now() + updateSharing.valid,
|
||||
includeSubfolders: updateSharing.includeSubfolders,
|
||||
timeStamp: Date.now()
|
||||
};
|
||||
|
||||
try {
|
||||
|
||||
req.resultPipe = await ObjectManagerRepository.getInstance().SharingManager.updateSharing(sharing);
|
||||
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));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
import {Metadata, SharpInstance} from "@types/sharp";
|
||||
import {Dimensions, State} from "@types/gm";
|
||||
import {Metadata, SharpInstance} from "sharp";
|
||||
import {Dimensions, State} from "gm";
|
||||
import {Logger} from "../../Logger";
|
||||
import {Error, ErrorCodes} from "../../../common/entities/Error";
|
||||
|
||||
@ -15,7 +15,6 @@ export module ThumbnailRenderers {
|
||||
}
|
||||
|
||||
export const jimp = (input: RendererInput, done) => {
|
||||
|
||||
//generate thumbnail
|
||||
const Jimp = require("jimp");
|
||||
Jimp.read(input.imagePath).then((image) => {
|
||||
@ -53,7 +52,6 @@ export module ThumbnailRenderers {
|
||||
};
|
||||
|
||||
export const sharp = (input: RendererInput, done) => {
|
||||
|
||||
//generate thumbnail
|
||||
const sharp = require("sharp");
|
||||
|
||||
@ -99,11 +97,13 @@ export module ThumbnailRenderers {
|
||||
}).catch(function (err) {
|
||||
// const Error = require(input.__dirname + "/../../../common/entities/Error").Error;
|
||||
// const ErrorCodes = require(input.__dirname + "/../../../common/entities/Error").ErrorCodes;
|
||||
console.error(err);
|
||||
return done(new Error(ErrorCodes.GENERAL_ERROR, err));
|
||||
});
|
||||
} catch (err) {
|
||||
// const Error = require(input.__dirname + "/../../../common/entities/Error").Error;
|
||||
// const ErrorCodes = require(input.__dirname + "/../../../common/entities/Error").ErrorCodes;
|
||||
console.error(err);
|
||||
return done(new Error(ErrorCodes.GENERAL_ERROR, err));
|
||||
}
|
||||
});
|
||||
@ -112,7 +112,6 @@ export module ThumbnailRenderers {
|
||||
|
||||
|
||||
export const gm = (input: RendererInput, done) => {
|
||||
|
||||
//generate thumbnail
|
||||
const gm = require("gm");
|
||||
|
||||
|
@ -139,7 +139,7 @@ export class ThumbnailGeneratorMWs {
|
||||
|
||||
|
||||
private static generateImage(imagePath: string, size: number, makeSquare: boolean, req: Request, res: Response, next: NextFunction) {
|
||||
|
||||
ThumbnailGeneratorMWs.init();
|
||||
//generate thumbnail path
|
||||
let thPath = path.join(ProjectPath.ThumbnailFolder, ThumbnailGeneratorMWs.generateThumbnailName(imagePath, size));
|
||||
|
||||
@ -189,4 +189,3 @@ export class ThumbnailGeneratorMWs {
|
||||
}
|
||||
}
|
||||
|
||||
ThumbnailGeneratorMWs.init();
|
||||
|
@ -1,67 +1,124 @@
|
||||
///<reference path="../customtypings/ExtendedRequest.d.ts"/>
|
||||
import {NextFunction, Request, Response} from "express";
|
||||
import {Error, ErrorCodes} from "../../../common/entities/Error";
|
||||
import {UserDTO, UserRoles} from "../../../common/entities/UserDTO";
|
||||
import {UserDTO, UserRoles, UserUtil} from "../../../common/entities/UserDTO";
|
||||
import {ObjectManagerRepository} from "../../model/ObjectManagerRepository";
|
||||
import {Config} from "../../../common/config/private/Config";
|
||||
|
||||
export class AuthenticationMWs {
|
||||
|
||||
public static authenticate(req: Request, res: Response, next: NextFunction) {
|
||||
if (Config.Client.authenticationRequired === false) {
|
||||
req.session.user = <UserDTO>{name: "", role: UserRoles.Admin};
|
||||
private static async getSharingUser(req: Request) {
|
||||
if (Config.Client.Sharing.enabled === true &&
|
||||
Config.Client.Sharing.passwordProtected === false &&
|
||||
(!!req.query.sk || !!req.params.sharingKey)) {
|
||||
const sharing = await ObjectManagerRepository.getInstance().SharingManager.findOne({
|
||||
sharingKey: req.query.sk || req.params.sharingKey,
|
||||
});
|
||||
if (!sharing) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return next();
|
||||
}
|
||||
if (typeof req.session.user === 'undefined') {
|
||||
return next(new Error(ErrorCodes.NOT_AUTHENTICATED));
|
||||
}
|
||||
let path = sharing.path;
|
||||
if (sharing.includeSubfolders == true) {
|
||||
path += "*";
|
||||
}
|
||||
return <UserDTO>{name: "Guest", role: UserRoles.Guest, permissions: [path]};
|
||||
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static async authenticate(req: Request, res: Response, next: NextFunction) {
|
||||
|
||||
if (Config.Client.authenticationRequired === false) {
|
||||
req.session.user = <UserDTO>{name: "", role: UserRoles.Admin};
|
||||
return next();
|
||||
}
|
||||
try {
|
||||
const user = await AuthenticationMWs.getSharingUser(req);
|
||||
if (!!user) {
|
||||
req.session.user = user;
|
||||
return next();
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
return next(new Error(ErrorCodes.CREDENTIAL_NOT_FOUND));
|
||||
}
|
||||
if (typeof req.session.user === 'undefined') {
|
||||
return next(new Error(ErrorCodes.NOT_AUTHENTICATED));
|
||||
}
|
||||
return next();
|
||||
}
|
||||
|
||||
public static authorise(role: UserRoles) {
|
||||
return (req: Request, res: Response, next: NextFunction) => {
|
||||
if (req.session.user.role < role) {
|
||||
return next(new Error(ErrorCodes.NOT_AUTHORISED));
|
||||
}
|
||||
return next();
|
||||
};
|
||||
}
|
||||
|
||||
public static authoriseDirectory(req: Request, res: Response, next: NextFunction) {
|
||||
if (req.session.user.permissions == null ||
|
||||
req.session.user.permissions.length == 0 ||
|
||||
req.session.user.permissions[0] == "/") {
|
||||
return next();
|
||||
}
|
||||
|
||||
public static authorise(role: UserRoles) {
|
||||
return (req: Request, res: Response, next: NextFunction) => {
|
||||
if (req.session.user.role < role) {
|
||||
return next(new Error(ErrorCodes.NOT_AUTHORISED));
|
||||
}
|
||||
return next();
|
||||
};
|
||||
}
|
||||
const directoryName = req.params.directory || "/";
|
||||
if (UserUtil.isPathAvailable(directoryName, req.session.user.permissions) == true) {
|
||||
return next();
|
||||
|
||||
public static inverseAuthenticate(req: Request, res: Response, next: NextFunction) {
|
||||
if (typeof req.session.user !== 'undefined') {
|
||||
return next(new Error(ErrorCodes.ALREADY_AUTHENTICATED));
|
||||
}
|
||||
return next(new Error(ErrorCodes.PERMISSION_DENIED));
|
||||
}
|
||||
|
||||
public static inverseAuthenticate(req: Request, res: Response, next: NextFunction) {
|
||||
if (typeof req.session.user !== 'undefined') {
|
||||
return next(new Error(ErrorCodes.ALREADY_AUTHENTICATED));
|
||||
}
|
||||
return next();
|
||||
}
|
||||
|
||||
public static async login(req: Request, res: Response, next: NextFunction) {
|
||||
|
||||
//not enough parameter
|
||||
if ((typeof req.body === 'undefined') || (typeof req.body.loginCredential === 'undefined') || (typeof req.body.loginCredential.username === 'undefined') ||
|
||||
(typeof req.body.loginCredential.password === 'undefined')) {
|
||||
return next(new Error(ErrorCodes.INPUT_ERROR));
|
||||
}
|
||||
try {
|
||||
//lets find the user
|
||||
req.session.user = await ObjectManagerRepository.getInstance().UserManager.findOne({
|
||||
name: req.body.loginCredential.username,
|
||||
password: req.body.loginCredential.password
|
||||
});
|
||||
return next();
|
||||
|
||||
} catch (err) {
|
||||
//if its a shared link, login as guest
|
||||
try {
|
||||
const user = await AuthenticationMWs.getSharingUser(req);
|
||||
if (user) {
|
||||
req.session.user = user;
|
||||
return next();
|
||||
}
|
||||
return next();
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
return next(new Error(ErrorCodes.CREDENTIAL_NOT_FOUND));
|
||||
}
|
||||
|
||||
console.error(err);
|
||||
return next(new Error(ErrorCodes.CREDENTIAL_NOT_FOUND));
|
||||
}
|
||||
|
||||
public static login(req: Request, res: Response, next: NextFunction) {
|
||||
//not enough parameter
|
||||
if ((typeof req.body === 'undefined') || (typeof req.body.loginCredential === 'undefined') || (typeof req.body.loginCredential.username === 'undefined') ||
|
||||
(typeof req.body.loginCredential.password === 'undefined')) {
|
||||
return next();
|
||||
}
|
||||
|
||||
//lets find the user
|
||||
ObjectManagerRepository.getInstance().getUserManager().findOne({
|
||||
name: req.body.loginCredential.username,
|
||||
password: req.body.loginCredential.password
|
||||
}, (err, result) => {
|
||||
if ((err) || (!result)) {
|
||||
console.error(err);
|
||||
return next(new Error(ErrorCodes.CREDENTIAL_NOT_FOUND));
|
||||
}
|
||||
}
|
||||
|
||||
public static logout(req: Request, res: Response, next: NextFunction) {
|
||||
delete req.session.user;
|
||||
return next();
|
||||
}
|
||||
|
||||
req.session.user = result;
|
||||
|
||||
return next();
|
||||
});
|
||||
}
|
||||
|
||||
public static logout(req: Request, res: Response, next: NextFunction) {
|
||||
delete req.session.user;
|
||||
return next();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,106 +1,107 @@
|
||||
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 {Utils} from "../../../common/Utils";
|
||||
import {Config} from "../../../common/config/private/Config";
|
||||
|
||||
export class UserMWs {
|
||||
|
||||
public static changePassword(req:Request, res:Response, next:NextFunction) {
|
||||
if (Config.Client.authenticationRequired === false) {
|
||||
return next(new Error(ErrorCodes.USER_MANAGEMENT_DISABLED));
|
||||
}
|
||||
if ((typeof req.body === 'undefined') || (typeof req.body.userModReq === 'undefined')
|
||||
|| (typeof req.body.userModReq.id === 'undefined')
|
||||
|| (typeof req.body.userModReq.oldPassword === 'undefined')
|
||||
|| (typeof req.body.userModReq.newPassword === 'undefined')) {
|
||||
return next();
|
||||
}
|
||||
public static async changePassword(req: Request, res: Response, next: NextFunction) {
|
||||
if (Config.Client.authenticationRequired === false) {
|
||||
return next(new Error(ErrorCodes.USER_MANAGEMENT_DISABLED));
|
||||
}
|
||||
if ((typeof req.body === 'undefined') || (typeof req.body.userModReq === 'undefined')
|
||||
|| (typeof req.body.userModReq.id === 'undefined')
|
||||
|| (typeof req.body.userModReq.oldPassword === 'undefined')
|
||||
|| (typeof req.body.userModReq.newPassword === 'undefined')) {
|
||||
return next();
|
||||
}
|
||||
|
||||
ObjectManagerRepository.getInstance().getUserManager().changePassword(req.body.userModReq, (err, result) => {
|
||||
if ((err) || (!result)) {
|
||||
return next(new Error(ErrorCodes.GENERAL_ERROR));
|
||||
}
|
||||
try {
|
||||
await ObjectManagerRepository.getInstance().UserManager.changePassword(req.body.userModReq);
|
||||
return next();
|
||||
|
||||
return next();
|
||||
});
|
||||
} catch (err) {
|
||||
return next(new Error(ErrorCodes.GENERAL_ERROR));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static async createUser(req: Request, res: Response, next: NextFunction) {
|
||||
if (Config.Client.authenticationRequired === false) {
|
||||
return next(new Error(ErrorCodes.USER_MANAGEMENT_DISABLED));
|
||||
}
|
||||
if ((typeof req.body === 'undefined') || (typeof req.body.newUser === 'undefined')) {
|
||||
return next();
|
||||
}
|
||||
|
||||
try {
|
||||
await ObjectManagerRepository.getInstance().UserManager.createUser(req.body.newUser);
|
||||
return next();
|
||||
|
||||
} catch (err) {
|
||||
return next(new Error(ErrorCodes.USER_CREATION_ERROR));
|
||||
}
|
||||
|
||||
|
||||
public static createUser(req:Request, res:Response, next:NextFunction) {
|
||||
if (Config.Client.authenticationRequired === false) {
|
||||
return next(new Error(ErrorCodes.USER_MANAGEMENT_DISABLED));
|
||||
}
|
||||
if ((typeof req.body === 'undefined') || (typeof req.body.newUser === 'undefined')) {
|
||||
return next();
|
||||
}
|
||||
|
||||
ObjectManagerRepository.getInstance().getUserManager().createUser(req.body.newUser, (err, result) => {
|
||||
if ((err) || (!result)) {
|
||||
return next(new Error(ErrorCodes.USER_CREATION_ERROR));
|
||||
}
|
||||
|
||||
return next();
|
||||
});
|
||||
}
|
||||
|
||||
public static async deleteUser(req: Request, res: Response, next: NextFunction) {
|
||||
if (Config.Client.authenticationRequired === false) {
|
||||
return next(new Error(ErrorCodes.USER_MANAGEMENT_DISABLED));
|
||||
}
|
||||
|
||||
public static deleteUser(req:Request, res:Response, next:NextFunction) {
|
||||
if (Config.Client.authenticationRequired === false) {
|
||||
return next(new Error(ErrorCodes.USER_MANAGEMENT_DISABLED));
|
||||
}
|
||||
if ((typeof req.params === 'undefined') || (typeof req.params.id === 'undefined')) {
|
||||
return next();
|
||||
}
|
||||
|
||||
ObjectManagerRepository.getInstance().getUserManager().deleteUser(req.params.id, (err, result) => {
|
||||
if ((err) || (!result)) {
|
||||
return next(new Error(ErrorCodes.GENERAL_ERROR));
|
||||
}
|
||||
|
||||
|
||||
return next();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public static changeRole(req:Request, res:Response, next:NextFunction) {
|
||||
if (Config.Client.authenticationRequired === false) {
|
||||
return next(new Error(ErrorCodes.USER_MANAGEMENT_DISABLED));
|
||||
}
|
||||
if ((typeof req.params === 'undefined') || (typeof req.params.id === 'undefined')
|
||||
|| (typeof req.body === 'undefined') || (typeof req.body.newRole === 'undefined')) {
|
||||
return next();
|
||||
}
|
||||
|
||||
ObjectManagerRepository.getInstance().getUserManager().changeRole(req.params.id, req.body.newRole, (err) => {
|
||||
if (err) {
|
||||
return next(new Error(ErrorCodes.GENERAL_ERROR));
|
||||
}
|
||||
|
||||
return next();
|
||||
});
|
||||
if ((typeof req.params === 'undefined') || (typeof req.params.id === 'undefined')) {
|
||||
return next();
|
||||
}
|
||||
|
||||
|
||||
public static listUsers(req:Request, res:Response, next:NextFunction) {
|
||||
if (Config.Client.authenticationRequired === false) {
|
||||
return next(new Error(ErrorCodes.USER_MANAGEMENT_DISABLED));
|
||||
}
|
||||
ObjectManagerRepository.getInstance().getUserManager().find({}, (err, result: Array<UserDTO>) => {
|
||||
if ((err) || (!result)) {
|
||||
return next(new Error(ErrorCodes.GENERAL_ERROR));
|
||||
}
|
||||
result = Utils.clone(result);
|
||||
for (let i = 0; i < result.length; i++) {
|
||||
result[i].password = "";
|
||||
}
|
||||
try {
|
||||
await ObjectManagerRepository.getInstance().UserManager.deleteUser(req.params.id);
|
||||
return next();
|
||||
|
||||
req.resultPipe = result;
|
||||
return next();
|
||||
});
|
||||
} catch (err) {
|
||||
return next(new Error(ErrorCodes.GENERAL_ERROR));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public static async changeRole(req: Request, res: Response, next: NextFunction) {
|
||||
if (Config.Client.authenticationRequired === false) {
|
||||
return next(new Error(ErrorCodes.USER_MANAGEMENT_DISABLED));
|
||||
}
|
||||
if ((typeof req.params === 'undefined') || (typeof req.params.id === 'undefined')
|
||||
|| (typeof req.body === 'undefined') || (typeof req.body.newRole === 'undefined')) {
|
||||
return next();
|
||||
}
|
||||
|
||||
try {
|
||||
await ObjectManagerRepository.getInstance().UserManager.changeRole(req.params.id, req.body.newRole);
|
||||
return next();
|
||||
|
||||
} catch (err) {
|
||||
return next(new Error(ErrorCodes.GENERAL_ERROR));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static async listUsers(req: Request, res: Response, next: NextFunction) {
|
||||
if (Config.Client.authenticationRequired === false) {
|
||||
return next(new Error(ErrorCodes.USER_MANAGEMENT_DISABLED));
|
||||
}
|
||||
|
||||
try {
|
||||
let result = await ObjectManagerRepository.getInstance().UserManager.find({});
|
||||
result = Utils.clone(result);
|
||||
for (let i = 0; i < result.length; i++) {
|
||||
result[i].password = "";
|
||||
}
|
||||
|
||||
req.resultPipe = result;
|
||||
} catch (err) {
|
||||
return next(new Error(ErrorCodes.GENERAL_ERROR));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -5,51 +5,52 @@ import {ObjectManagerRepository} from "../../model/ObjectManagerRepository";
|
||||
|
||||
export class UserRequestConstrainsMWs {
|
||||
|
||||
public static forceSelfRequest(req:Request, res:Response, next:NextFunction) {
|
||||
if ((typeof req.params === 'undefined') || (typeof req.params.id === 'undefined')) {
|
||||
return next();
|
||||
}
|
||||
if (req.session.user.id !== req.params.id) {
|
||||
return next(new Error(ErrorCodes.NOT_AUTHORISED));
|
||||
}
|
||||
|
||||
return next();
|
||||
public static forceSelfRequest(req: Request, res: Response, next: NextFunction) {
|
||||
if ((typeof req.params === 'undefined') || (typeof req.params.id === 'undefined')) {
|
||||
return next();
|
||||
}
|
||||
if (req.session.user.id !== req.params.id) {
|
||||
return next(new Error(ErrorCodes.NOT_AUTHORISED));
|
||||
}
|
||||
|
||||
return next();
|
||||
}
|
||||
|
||||
public static notSelfRequest(req:Request, res:Response, next:NextFunction) {
|
||||
if ((typeof req.params === 'undefined') || (typeof req.params.id === 'undefined')) {
|
||||
return next();
|
||||
}
|
||||
|
||||
if (req.session.user.id === req.params.id) {
|
||||
return next(new Error(ErrorCodes.NOT_AUTHORISED));
|
||||
}
|
||||
|
||||
return next();
|
||||
public static notSelfRequest(req: Request, res: Response, next: NextFunction) {
|
||||
if ((typeof req.params === 'undefined') || (typeof req.params.id === 'undefined')) {
|
||||
return next();
|
||||
}
|
||||
|
||||
public static notSelfRequestOr2Admins(req:Request, res:Response, next:NextFunction) {
|
||||
if ((typeof req.params === 'undefined') || (typeof req.params.id === 'undefined')) {
|
||||
return next();
|
||||
}
|
||||
|
||||
if (req.session.user.id !== req.params.id) {
|
||||
return next();
|
||||
}
|
||||
//TODO: fix it!
|
||||
ObjectManagerRepository.getInstance().getUserManager().find({minRole: UserRoles.Admin}, (err, result) => {
|
||||
if ((err) || (!result)) {
|
||||
return next(new Error(ErrorCodes.GENERAL_ERROR));
|
||||
}
|
||||
if (result.length <= 1) {
|
||||
return next(new Error(ErrorCodes.GENERAL_ERROR));
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
return next();
|
||||
if (req.session.user.id === req.params.id) {
|
||||
return next(new Error(ErrorCodes.NOT_AUTHORISED));
|
||||
}
|
||||
|
||||
return next();
|
||||
}
|
||||
|
||||
}
|
||||
public static async notSelfRequestOr2Admins(req: Request, res: Response, next: NextFunction) {
|
||||
if ((typeof req.params === 'undefined') || (typeof req.params.id === 'undefined')) {
|
||||
return next();
|
||||
}
|
||||
|
||||
if (req.session.user.id !== req.params.id) {
|
||||
return next();
|
||||
}
|
||||
|
||||
//TODO: fix it!
|
||||
try {
|
||||
const result = await ObjectManagerRepository.getInstance().UserManager.find({minRole: UserRoles.Admin});
|
||||
if (result.length <= 1) {
|
||||
return next(new Error(ErrorCodes.GENERAL_ERROR));
|
||||
}
|
||||
return next();
|
||||
|
||||
} catch (err) {
|
||||
return next(new Error(ErrorCodes.GENERAL_ERROR));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -15,50 +15,52 @@ const LOG_TAG = "[DiskManager]";
|
||||
pool.run(diskManagerTask);
|
||||
|
||||
export class DiskManager {
|
||||
public static scanDirectory(relativeDirectoryName: string, cb: (error: any, result: DirectoryDTO) => void) {
|
||||
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);
|
||||
public static scanDirectory(relativeDirectoryName: string): Promise<DirectoryDTO> {
|
||||
return new Promise((resolve, reject) => {
|
||||
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);
|
||||
|
||||
let input = <DiskManagerTask.PoolInput>{
|
||||
relativeDirectoryName,
|
||||
directoryName,
|
||||
directoryParent,
|
||||
absoluteDirectoryName
|
||||
};
|
||||
|
||||
let done = (error: any, result: DirectoryDTO) => {
|
||||
if (error || !result) {
|
||||
return cb(error, result);
|
||||
}
|
||||
|
||||
let addDirs = (dir: DirectoryDTO) => {
|
||||
dir.photos.forEach((ph) => {
|
||||
ph.directory = dir;
|
||||
});
|
||||
dir.directories.forEach((d) => {
|
||||
addDirs(d);
|
||||
});
|
||||
let input = <DiskManagerTask.PoolInput>{
|
||||
relativeDirectoryName,
|
||||
directoryName,
|
||||
directoryParent,
|
||||
absoluteDirectoryName
|
||||
};
|
||||
addDirs(result);
|
||||
return cb(error, result);
|
||||
};
|
||||
|
||||
let error = (error) => {
|
||||
return cb(error, null);
|
||||
};
|
||||
let done = (error: any, result: DirectoryDTO) => {
|
||||
if (error || !result) {
|
||||
return reject(error);
|
||||
}
|
||||
|
||||
let addDirs = (dir: DirectoryDTO) => {
|
||||
dir.photos.forEach((ph) => {
|
||||
ph.directory = dir;
|
||||
});
|
||||
dir.directories.forEach((d) => {
|
||||
addDirs(d);
|
||||
});
|
||||
};
|
||||
addDirs(result);
|
||||
return resolve(result);
|
||||
};
|
||||
|
||||
let error = (error) => {
|
||||
return reject(error);
|
||||
};
|
||||
|
||||
|
||||
if (Config.Server.enableThreading == true) {
|
||||
pool.send(input).on('done', done).on('error', error);
|
||||
} else {
|
||||
try {
|
||||
diskManagerTask(input, done);
|
||||
} catch (err) {
|
||||
error(err);
|
||||
if (Config.Server.enableThreading == true) {
|
||||
pool.send(input).on('done', done).on('error', error);
|
||||
} else {
|
||||
try {
|
||||
diskManagerTask(input, done);
|
||||
} catch (err) {
|
||||
error(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2,12 +2,15 @@ import {IUserManager} from "./interfaces/IUserManager";
|
||||
import {IGalleryManager} from "./interfaces/IGalleryManager";
|
||||
import {ISearchManager} from "./interfaces/ISearchManager";
|
||||
import {MySQLConnection} from "./mysql/MySQLConnection";
|
||||
import {ISharingManager} from "./interfaces/ISharingManager";
|
||||
import {Logger} from "../Logger";
|
||||
|
||||
export class ObjectManagerRepository {
|
||||
|
||||
private _galleryManager: IGalleryManager;
|
||||
private _userManager: IUserManager;
|
||||
private _searchManager: ISearchManager;
|
||||
private _sharingManager: ISharingManager;
|
||||
private static _instance: ObjectManagerRepository = null;
|
||||
|
||||
|
||||
@ -15,24 +18,24 @@ export class ObjectManagerRepository {
|
||||
const GalleryManager = require("./memory/GalleryManager").GalleryManager;
|
||||
const UserManager = require("./memory/UserManager").UserManager;
|
||||
const SearchManager = require("./memory/SearchManager").SearchManager;
|
||||
ObjectManagerRepository.getInstance().setGalleryManager(new GalleryManager());
|
||||
ObjectManagerRepository.getInstance().setUserManager(new UserManager());
|
||||
ObjectManagerRepository.getInstance().setSearchManager(new SearchManager());
|
||||
const SharingManager = require("./memory/SharingManager").SharingManager;
|
||||
ObjectManagerRepository.getInstance().GalleryManager = new GalleryManager();
|
||||
ObjectManagerRepository.getInstance().UserManager = new UserManager();
|
||||
ObjectManagerRepository.getInstance().SearchManager = new SearchManager();
|
||||
ObjectManagerRepository.getInstance().SharingManager = new SharingManager();
|
||||
}
|
||||
|
||||
public static InitMySQLManagers(): Promise<boolean> {
|
||||
return new Promise<boolean>((resolve, reject) => {
|
||||
MySQLConnection.init().then(() => {
|
||||
const GalleryManager = require("./mysql/GalleryManager").GalleryManager;
|
||||
const UserManager = require("./mysql/UserManager").UserManager;
|
||||
const SearchManager = require("./mysql/SearchManager").SearchManager;
|
||||
ObjectManagerRepository.getInstance().setGalleryManager(new GalleryManager());
|
||||
ObjectManagerRepository.getInstance().setUserManager(new UserManager());
|
||||
ObjectManagerRepository.getInstance().setSearchManager(new SearchManager());
|
||||
console.log("MySQL DB inited");
|
||||
resolve(true);
|
||||
}).catch(err => reject(err));
|
||||
});
|
||||
public static async InitMySQLManagers() {
|
||||
await MySQLConnection.init();
|
||||
const GalleryManager = require("./mysql/GalleryManager").GalleryManager;
|
||||
const UserManager = require("./mysql/UserManager").UserManager;
|
||||
const SearchManager = require("./mysql/SearchManager").SearchManager;
|
||||
const SharingManager = require("./mysql/SharingManager").SharingManager;
|
||||
ObjectManagerRepository.getInstance().GalleryManager = new GalleryManager();
|
||||
ObjectManagerRepository.getInstance().UserManager = new UserManager();
|
||||
ObjectManagerRepository.getInstance().SearchManager = new SearchManager();
|
||||
ObjectManagerRepository.getInstance().SharingManager = new SharingManager();
|
||||
Logger.debug("MySQL DB inited");
|
||||
}
|
||||
|
||||
public static getInstance() {
|
||||
@ -47,28 +50,36 @@ export class ObjectManagerRepository {
|
||||
}
|
||||
|
||||
|
||||
getGalleryManager(): IGalleryManager {
|
||||
get GalleryManager(): IGalleryManager {
|
||||
return this._galleryManager;
|
||||
}
|
||||
|
||||
setGalleryManager(value: IGalleryManager) {
|
||||
set GalleryManager(value: IGalleryManager) {
|
||||
this._galleryManager = value;
|
||||
}
|
||||
|
||||
getUserManager(): IUserManager {
|
||||
get UserManager(): IUserManager {
|
||||
return this._userManager;
|
||||
}
|
||||
|
||||
setUserManager(value: IUserManager) {
|
||||
set UserManager(value: IUserManager) {
|
||||
this._userManager = value;
|
||||
}
|
||||
|
||||
getSearchManager(): ISearchManager {
|
||||
get SearchManager(): ISearchManager {
|
||||
return this._searchManager;
|
||||
}
|
||||
|
||||
setSearchManager(value: ISearchManager) {
|
||||
set SearchManager(value: ISearchManager) {
|
||||
this._searchManager = value;
|
||||
}
|
||||
|
||||
get SharingManager(): ISharingManager {
|
||||
return this._sharingManager;
|
||||
}
|
||||
|
||||
set SharingManager(value: ISharingManager) {
|
||||
this._sharingManager = value;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import {DirectoryDTO} from "../../../common/entities/DirectoryDTO";
|
||||
export interface IGalleryManager {
|
||||
listDirectory(relativeDirectoryName: string, cb: (error: any, result: DirectoryDTO) => void): void;
|
||||
}
|
||||
listDirectory(relativeDirectoryName: string): Promise<DirectoryDTO>;
|
||||
}
|
||||
|
6
backend/model/interfaces/ISharingManager.ts
Normal file
6
backend/model/interfaces/ISharingManager.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import {SharingDTO} from "../../../common/entities/SharingDTO";
|
||||
export interface ISharingManager {
|
||||
findOne(filter: any): Promise<SharingDTO>;
|
||||
createSharing(sharing: SharingDTO): Promise<SharingDTO>;
|
||||
updateSharing(sharing: SharingDTO): Promise<SharingDTO>;
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
import {UserDTO, UserRoles} from "../../../common/entities/UserDTO";
|
||||
export interface IUserManager {
|
||||
findOne(filter: any, cb: (error: any, result: UserDTO) => void): void;
|
||||
find(filter: any, cb: (error: any, result: Array<UserDTO>) => void): void;
|
||||
createUser(user: UserDTO, cb: (error: any, result: UserDTO) => void): void;
|
||||
deleteUser(id: number, cb: (error: any, result: string) => void): void;
|
||||
changeRole(id: number, newRole: UserRoles, cb: (error: any) => void): void;
|
||||
changePassword(request: any, cb: (error: any, result: string) => void): void;
|
||||
}
|
||||
findOne(filter: any): Promise<UserDTO>;
|
||||
find(filter: any): Promise<UserDTO[]>;
|
||||
createUser(user: UserDTO): Promise<UserDTO>;
|
||||
deleteUser(id: number): Promise<UserDTO>;
|
||||
changeRole(id: number, newRole: UserRoles): Promise<UserDTO>;
|
||||
changePassword(request: any): Promise<void>;
|
||||
}
|
||||
|
@ -4,10 +4,8 @@ import {DiskManager} from "../DiskManger";
|
||||
|
||||
export class GalleryManager implements IGalleryManager {
|
||||
|
||||
public listDirectory(relativeDirectoryName: string): Promise<DirectoryDTO> {
|
||||
return DiskManager.scanDirectory(relativeDirectoryName);
|
||||
}
|
||||
|
||||
public listDirectory(relativeDirectoryName: string, cb: (error: any, result: DirectoryDTO) => void) {
|
||||
return DiskManager.scanDirectory(relativeDirectoryName, cb);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
19
backend/model/memory/SharingManager.ts
Normal file
19
backend/model/memory/SharingManager.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import {ISharingManager} from "../interfaces/ISharingManager";
|
||||
import {SharingDTO} from "../../../common/entities/SharingDTO";
|
||||
|
||||
export class SharingManager implements ISharingManager {
|
||||
|
||||
findOne(filter: any): Promise<SharingDTO> {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
createSharing(sharing: SharingDTO): Promise<SharingDTO> {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
updateSharing(sharing: SharingDTO): Promise<SharingDTO> {
|
||||
throw new Error("not implemented");
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -8,91 +8,87 @@ import * as path from "path";
|
||||
|
||||
|
||||
export class UserManager implements IUserManager {
|
||||
private db: any = null;
|
||||
private db: any = null;
|
||||
|
||||
generateId(): string {
|
||||
function s4() {
|
||||
return Math.floor((1 + Math.random()) * 0x10000)
|
||||
.toString(16)
|
||||
.substring(1);
|
||||
}
|
||||
|
||||
return s4() + s4() + s4() + s4();
|
||||
generateId(): string {
|
||||
function s4() {
|
||||
return Math.floor((1 + Math.random()) * 0x10000)
|
||||
.toString(16)
|
||||
.substring(1);
|
||||
}
|
||||
|
||||
constructor() {
|
||||
this.db = flatfile.sync(path.join(ProjectPath.Root, 'users.db'));
|
||||
return s4() + s4() + s4() + s4();
|
||||
}
|
||||
|
||||
if (!this.db.has("idCounter")) {
|
||||
console.log("creating counter");
|
||||
this.db.put("idCounter", 1);
|
||||
}
|
||||
|
||||
if (!this.db.has("users")) {
|
||||
this.db.put("users", []);
|
||||
//TODO: remove defaults
|
||||
this.createUser(<UserDTO>{name: "developer", password: "developer", role: UserRoles.Developer});
|
||||
this.createUser(<UserDTO>{name: "admin", password: "admin", role: UserRoles.Admin});
|
||||
this.createUser(<UserDTO>{name: "user", password: "user", role: UserRoles.User});
|
||||
this.createUser(<UserDTO>{name: "guest", password: "guest", role: UserRoles.Guest});
|
||||
}
|
||||
constructor() {
|
||||
this.db = flatfile.sync(path.join(ProjectPath.Root, 'users.db'));
|
||||
|
||||
if (!this.db.has("idCounter")) {
|
||||
console.log("creating counter");
|
||||
this.db.put("idCounter", 1);
|
||||
}
|
||||
|
||||
if (!this.db.has("users")) {
|
||||
this.db.put("users", []);
|
||||
//TODO: remove defaults
|
||||
this.createUser(<UserDTO>{name: "developer", password: "developer", role: UserRoles.Developer});
|
||||
this.createUser(<UserDTO>{name: "admin", password: "admin", role: UserRoles.Admin});
|
||||
this.createUser(<UserDTO>{name: "user", password: "user", role: UserRoles.User});
|
||||
this.createUser(<UserDTO>{name: "guest", password: "guest", role: UserRoles.Guest});
|
||||
}
|
||||
|
||||
|
||||
public findOne(filter: any, cb: (error: any, result: UserDTO) => void) {
|
||||
this.find(filter, (error, result: Array<UserDTO>) => {
|
||||
if (error) {
|
||||
return cb(error, null);
|
||||
}
|
||||
if (result.length == 0) {
|
||||
return cb("UserDTO not found", null);
|
||||
}
|
||||
return cb(null, result[0]);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
public async findOne(filter: any) {
|
||||
const result = await this.find(filter);
|
||||
|
||||
if (result.length == 0) {
|
||||
throw "UserDTO not found";
|
||||
}
|
||||
return result[0];
|
||||
}
|
||||
|
||||
public find(filter: any, cb: (error: any, result: Array<UserDTO>) => void) {
|
||||
public async find(filter: any) {
|
||||
return this.db.get("users").filter((u: UserDTO) => Utils.equalsFilter(u, filter));
|
||||
}
|
||||
|
||||
let users = this.db.get("users").filter((u: UserDTO) => Utils.equalsFilter(u, filter));
|
||||
public async createUser(user: UserDTO) {
|
||||
user.id = parseInt(this.db.get("idCounter")) + 1;
|
||||
this.db.put("idCounter", user.id);
|
||||
let users = this.db.get("users");
|
||||
users.push(user);
|
||||
this.db.put("users", users);
|
||||
|
||||
return cb(null, users);
|
||||
return user;
|
||||
}
|
||||
|
||||
public deleteUser(id: number) {
|
||||
let deleted = this.db.get("users").filter((u: UserDTO) => u.id == id);
|
||||
let users = this.db.get("users").filter((u: UserDTO) => u.id != id);
|
||||
this.db.put("users", users);
|
||||
if (deleted.length > 0) {
|
||||
return deleted[0];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public createUser(user: UserDTO, cb: (error: any, result: UserDTO) => void = (e, r) => {
|
||||
}) {
|
||||
user.id = parseInt(this.db.get("idCounter")) + 1;
|
||||
this.db.put("idCounter", user.id);
|
||||
let users = this.db.get("users");
|
||||
users.push(user);
|
||||
public async changeRole(id: number, newRole: UserRoles): Promise<UserDTO> {
|
||||
|
||||
let users: Array<UserDTO> = this.db.get("users");
|
||||
|
||||
for (let i = 0; i < users.length; i++) {
|
||||
if (users[i].id == id) {
|
||||
users[i].role = newRole;
|
||||
this.db.put("users", users);
|
||||
return cb(null, user);
|
||||
return users[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public deleteUser(id: number, cb: (error: any) => void) {
|
||||
let users = this.db.get("users").filter((u: UserDTO) => u.id != id);
|
||||
this.db.put("users", users);
|
||||
return cb(null);
|
||||
}
|
||||
public async changePassword(request: any) {
|
||||
throw new Error("not implemented"); //TODO: implement
|
||||
}
|
||||
|
||||
public changeRole(id: number, newRole: UserRoles, cb: (error: any, result: string) => void) {
|
||||
|
||||
let users: Array<UserDTO> = this.db.get("users");
|
||||
|
||||
for (let i = 0; i < users.length; i++) {
|
||||
if (users[i].id == id) {
|
||||
users[i].role = newRole;
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.db.put("users", users);
|
||||
}
|
||||
|
||||
public changePassword(request: any, cb: (error: any, result: string) => void) {
|
||||
throw new Error("not implemented"); //TODO: implement
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -11,158 +11,154 @@ import {ProjectPath} from "../../ProjectPath";
|
||||
export class GalleryManager implements IGalleryManager {
|
||||
|
||||
|
||||
public listDirectory(relativeDirectoryName, cb: (error: any, result: DirectoryDTO) => void) {
|
||||
relativeDirectoryName = path.normalize(path.join("." + path.sep, relativeDirectoryName));
|
||||
let directoryName = path.basename(relativeDirectoryName);
|
||||
let directoryParent = path.join(path.dirname(relativeDirectoryName), path.sep);
|
||||
console.log("GalleryManager:listDirectory");
|
||||
console.log(directoryName, directoryParent, path.dirname(relativeDirectoryName), ProjectPath.normalizeRelative(path.dirname(relativeDirectoryName)));
|
||||
MySQLConnection.getConnection().then(async connection => {
|
||||
public async listDirectory(relativeDirectoryName): Promise<DirectoryDTO> {
|
||||
relativeDirectoryName = path.normalize(path.join("." + path.sep, relativeDirectoryName));
|
||||
const directoryName = path.basename(relativeDirectoryName);
|
||||
const directoryParent = path.join(path.dirname(relativeDirectoryName), path.sep);
|
||||
console.log("GalleryManager:listDirectory");
|
||||
console.log(directoryName, directoryParent, path.dirname(relativeDirectoryName), ProjectPath.normalizeRelative(path.dirname(relativeDirectoryName)));
|
||||
const connection = await MySQLConnection.getConnection();
|
||||
let dir = await connection
|
||||
.getRepository(DirectoryEntity)
|
||||
.createQueryBuilder("directory")
|
||||
.where("directory.name = :name AND directory.path = :path", {
|
||||
name: directoryName,
|
||||
path: directoryParent
|
||||
})
|
||||
.leftJoinAndSelect("directory.directories", "directories")
|
||||
.leftJoinAndSelect("directory.photos", "photos")
|
||||
.getOne();
|
||||
|
||||
let dir = await connection
|
||||
.getRepository(DirectoryEntity)
|
||||
.createQueryBuilder("directory")
|
||||
.where("directory.name = :name AND directory.path = :path", {
|
||||
name: directoryName,
|
||||
path: directoryParent
|
||||
})
|
||||
.leftJoinAndSelect("directory.directories", "directories")
|
||||
.leftJoinAndSelect("directory.photos", "photos")
|
||||
.getOne();
|
||||
if (dir && dir.scanned == true) {
|
||||
if (dir.photos) {
|
||||
for (let i = 0; i < dir.photos.length; i++) {
|
||||
dir.photos[i].directory = dir;
|
||||
dir.photos[i].metadata.keywords = <any>JSON.parse(<any>dir.photos[i].metadata.keywords);
|
||||
dir.photos[i].metadata.cameraData = <any>JSON.parse(<any>dir.photos[i].metadata.cameraData);
|
||||
dir.photos[i].metadata.positionData = <any>JSON.parse(<any>dir.photos[i].metadata.positionData);
|
||||
dir.photos[i].metadata.size = <any>JSON.parse(<any>dir.photos[i].metadata.size);
|
||||
dir.photos[i].readyThumbnails = [];
|
||||
dir.photos[i].readyIcon = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (dir && dir.scanned == true) {
|
||||
if (dir.photos) {
|
||||
for (let i = 0; i < dir.photos.length; i++) {
|
||||
dir.photos[i].directory = dir;
|
||||
dir.photos[i].metadata.keywords = <any>JSON.parse(<any>dir.photos[i].metadata.keywords);
|
||||
dir.photos[i].metadata.cameraData = <any>JSON.parse(<any>dir.photos[i].metadata.cameraData);
|
||||
dir.photos[i].metadata.positionData = <any>JSON.parse(<any>dir.photos[i].metadata.positionData);
|
||||
dir.photos[i].metadata.size = <any>JSON.parse(<any>dir.photos[i].metadata.size);
|
||||
dir.photos[i].readyThumbnails = [];
|
||||
dir.photos[i].readyIcon = false;
|
||||
}
|
||||
}
|
||||
//on the fly updating
|
||||
this.indexDirectory(relativeDirectoryName).catch((err) => {
|
||||
|
||||
cb(null, dir); //WARNING: only on the fly indexing should happen after this point
|
||||
console.error(err);
|
||||
});
|
||||
|
||||
//on the fly updating
|
||||
return this.indexDirectory(relativeDirectoryName, (err, res) => {
|
||||
if (!!err || !res) {
|
||||
console.error(err);
|
||||
}
|
||||
});
|
||||
}
|
||||
return this.indexDirectory(relativeDirectoryName, cb);
|
||||
|
||||
|
||||
}).catch((error) => {
|
||||
return cb(error, null);
|
||||
});
|
||||
return dir;
|
||||
|
||||
|
||||
}
|
||||
return this.indexDirectory(relativeDirectoryName);
|
||||
|
||||
public indexDirectory(relativeDirectoryName, cb: (error: any, result: DirectoryDTO) => void) {
|
||||
DiskManager.scanDirectory(relativeDirectoryName, (err, scannedDirectory) => {
|
||||
if (!!err || !scannedDirectory) {
|
||||
return cb(err, null);
|
||||
|
||||
}
|
||||
|
||||
public indexDirectory(relativeDirectoryName): Promise<DirectoryDTO> {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
const scannedDirectory = await DiskManager.scanDirectory(relativeDirectoryName);
|
||||
|
||||
|
||||
const connection = await MySQLConnection.getConnection();
|
||||
|
||||
//returning with the result
|
||||
scannedDirectory.photos.forEach(p => p.readyThumbnails = []);
|
||||
resolve(scannedDirectory);
|
||||
|
||||
//saving to db
|
||||
let directoryRepository = connection.getRepository(DirectoryEntity);
|
||||
let photosRepository = connection.getRepository(PhotoEntity);
|
||||
|
||||
|
||||
let parentDir = await directoryRepository.createQueryBuilder("directory")
|
||||
.where("directory.name = :name AND directory.path = :path", {
|
||||
name: scannedDirectory.name,
|
||||
path: scannedDirectory.path
|
||||
}).getOne();
|
||||
|
||||
if (!!parentDir) {
|
||||
parentDir.scanned = true;
|
||||
parentDir.lastUpdate = Date.now();
|
||||
parentDir = await directoryRepository.persist(parentDir);
|
||||
} else {
|
||||
(<DirectoryEntity>scannedDirectory).scanned = true;
|
||||
parentDir = await directoryRepository.persist(<DirectoryEntity>scannedDirectory);
|
||||
}
|
||||
|
||||
|
||||
for (let i = 0; i < scannedDirectory.directories.length; i++) {
|
||||
//TODO: simplify algorithm
|
||||
let dir = await directoryRepository.createQueryBuilder("directory")
|
||||
.where("directory.name = :name AND directory.path = :path", {
|
||||
name: scannedDirectory.directories[i].name,
|
||||
path: scannedDirectory.directories[i].path
|
||||
}).getOne();
|
||||
|
||||
if (dir) {
|
||||
dir.parent = parentDir;
|
||||
await directoryRepository.persist(dir);
|
||||
} else {
|
||||
scannedDirectory.directories[i].parent = parentDir;
|
||||
(<DirectoryEntity>scannedDirectory.directories[i]).scanned = false;
|
||||
await directoryRepository.persist(<DirectoryEntity>scannedDirectory.directories[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let indexedPhotos = await photosRepository.createQueryBuilder("photo")
|
||||
.where("photo.directory = :dir", {
|
||||
dir: parentDir.id
|
||||
}).getMany();
|
||||
|
||||
|
||||
let photosToSave = [];
|
||||
for (let i = 0; i < scannedDirectory.photos.length; i++) {
|
||||
let photo = null;
|
||||
for (let j = 0; j < indexedPhotos.length; j++) {
|
||||
if (indexedPhotos[j].name == scannedDirectory.photos[i].name) {
|
||||
photo = indexedPhotos[j];
|
||||
indexedPhotos.splice(j, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (photo == null) {
|
||||
scannedDirectory.photos[i].directory = null;
|
||||
photo = Utils.clone(scannedDirectory.photos[i]);
|
||||
scannedDirectory.photos[i].directory = scannedDirectory;
|
||||
photo.directory = parentDir;
|
||||
}
|
||||
|
||||
MySQLConnection.getConnection().then(async connection => {
|
||||
//typeorm not supports recursive embended: TODO:fix it
|
||||
let keyStr = <any>JSON.stringify(scannedDirectory.photos[i].metadata.keywords);
|
||||
let camStr = <any>JSON.stringify(scannedDirectory.photos[i].metadata.cameraData);
|
||||
let posStr = <any>JSON.stringify(scannedDirectory.photos[i].metadata.positionData);
|
||||
let sizeStr = <any>JSON.stringify(scannedDirectory.photos[i].metadata.size);
|
||||
|
||||
//returning with the result
|
||||
scannedDirectory.photos.forEach(p => p.readyThumbnails = []);
|
||||
cb(null, scannedDirectory);
|
||||
if (photo.metadata.keywords != keyStr ||
|
||||
photo.metadata.cameraData != camStr ||
|
||||
photo.metadata.positionData != posStr ||
|
||||
photo.metadata.size != sizeStr) {
|
||||
|
||||
//saving to db
|
||||
let directoryRepository = connection.getRepository(DirectoryEntity);
|
||||
let photosRepository = connection.getRepository(PhotoEntity);
|
||||
photo.metadata.keywords = keyStr;
|
||||
photo.metadata.cameraData = camStr;
|
||||
photo.metadata.positionData = posStr;
|
||||
photo.metadata.size = sizeStr;
|
||||
photosToSave.push(photo);
|
||||
}
|
||||
}
|
||||
await photosRepository.persist(photosToSave);
|
||||
await photosRepository.remove(indexedPhotos);
|
||||
|
||||
|
||||
let parentDir = await directoryRepository.createQueryBuilder("directory")
|
||||
.where("directory.name = :name AND directory.path = :path", {
|
||||
name: scannedDirectory.name,
|
||||
path: scannedDirectory.path
|
||||
}).getOne();
|
||||
} catch (error) {
|
||||
return reject(error);
|
||||
|
||||
if (!!parentDir) {
|
||||
parentDir.scanned = true;
|
||||
parentDir.lastUpdate = Date.now();
|
||||
parentDir = await directoryRepository.persist(parentDir);
|
||||
} else {
|
||||
(<DirectoryEntity>scannedDirectory).scanned = true;
|
||||
parentDir = await directoryRepository.persist(<DirectoryEntity>scannedDirectory);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
for (let i = 0; i < scannedDirectory.directories.length; i++) {
|
||||
//TODO: simplify algorithm
|
||||
let dir = await directoryRepository.createQueryBuilder("directory")
|
||||
.where("directory.name = :name AND directory.path = :path", {
|
||||
name: scannedDirectory.directories[i].name,
|
||||
path: scannedDirectory.directories[i].path
|
||||
}).getOne();
|
||||
|
||||
if (dir) {
|
||||
dir.parent = parentDir;
|
||||
await directoryRepository.persist(dir);
|
||||
} else {
|
||||
scannedDirectory.directories[i].parent = parentDir;
|
||||
(<DirectoryEntity>scannedDirectory.directories[i]).scanned = false;
|
||||
await directoryRepository.persist(<DirectoryEntity>scannedDirectory.directories[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let indexedPhotos = await photosRepository.createQueryBuilder("photo")
|
||||
.where("photo.directory = :dir", {
|
||||
dir: parentDir.id
|
||||
}).getMany();
|
||||
|
||||
|
||||
let photosToSave = [];
|
||||
for (let i = 0; i < scannedDirectory.photos.length; i++) {
|
||||
let photo = null;
|
||||
for (let j = 0; j < indexedPhotos.length; j++) {
|
||||
if (indexedPhotos[j].name == scannedDirectory.photos[i].name) {
|
||||
photo = indexedPhotos[j];
|
||||
indexedPhotos.splice(j, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (photo == null) {
|
||||
scannedDirectory.photos[i].directory = null;
|
||||
photo = Utils.clone(scannedDirectory.photos[i]);
|
||||
scannedDirectory.photos[i].directory = scannedDirectory;
|
||||
photo.directory = parentDir;
|
||||
}
|
||||
|
||||
//typeorm not supports recursive embended: TODO:fix it
|
||||
let keyStr = <any>JSON.stringify(scannedDirectory.photos[i].metadata.keywords);
|
||||
let camStr = <any>JSON.stringify(scannedDirectory.photos[i].metadata.cameraData);
|
||||
let posStr = <any>JSON.stringify(scannedDirectory.photos[i].metadata.positionData);
|
||||
let sizeStr = <any>JSON.stringify(scannedDirectory.photos[i].metadata.size);
|
||||
|
||||
if (photo.metadata.keywords != keyStr ||
|
||||
photo.metadata.cameraData != camStr ||
|
||||
photo.metadata.positionData != posStr ||
|
||||
photo.metadata.size != sizeStr) {
|
||||
|
||||
photo.metadata.keywords = keyStr;
|
||||
photo.metadata.cameraData = camStr;
|
||||
photo.metadata.positionData = posStr;
|
||||
photo.metadata.size = sizeStr;
|
||||
photosToSave.push(photo);
|
||||
}
|
||||
}
|
||||
await photosRepository.persist(photosToSave);
|
||||
await photosRepository.remove(indexedPhotos);
|
||||
|
||||
|
||||
}).catch((error) => {
|
||||
return cb(error, null);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -5,72 +5,68 @@ import {UserRoles} from "../../../common/entities/UserDTO";
|
||||
import {PhotoEntity, PhotoMetadataEntity} from "./enitites/PhotoEntity";
|
||||
import {DirectoryEntity} from "./enitites/DirectoryEntity";
|
||||
import {Config} from "../../../common/config/private/Config";
|
||||
import {SharingEntity} from "./enitites/SharingEntity";
|
||||
|
||||
|
||||
export class MySQLConnection {
|
||||
|
||||
constructor() {
|
||||
constructor() {
|
||||
|
||||
|
||||
}
|
||||
|
||||
private static connection: Connection = null;
|
||||
|
||||
public static async getConnection(): Promise<Connection> {
|
||||
|
||||
if (this.connection == null) {
|
||||
this.connection = await createConnection({
|
||||
driver: {
|
||||
type: "mysql",
|
||||
host: Config.Server.database.mysql.host,
|
||||
port: 3306,
|
||||
username: Config.Server.database.mysql.username,
|
||||
password: Config.Server.database.mysql.password,
|
||||
database: Config.Server.database.mysql.database
|
||||
},
|
||||
entities: [
|
||||
UserEntity,
|
||||
DirectoryEntity,
|
||||
PhotoMetadataEntity,
|
||||
PhotoEntity,
|
||||
SharingEntity
|
||||
],
|
||||
autoSchemaSync: true,
|
||||
logging: {
|
||||
logQueries: true,
|
||||
logOnlyFailedQueries: true,
|
||||
logFailedQueryError: true,
|
||||
logSchemaCreation: true
|
||||
}
|
||||
});
|
||||
}
|
||||
return this.connection;
|
||||
|
||||
private static connection: Connection = null;
|
||||
}
|
||||
|
||||
public static getConnection(): Promise<Connection> {
|
||||
return new Promise<Connection>((resolve, reject) => {
|
||||
public static init(): Promise<void> {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
this.getConnection().then(async connection => {
|
||||
|
||||
if (this.connection != null) {
|
||||
return resolve(this.connection);
|
||||
}
|
||||
let userRepository = connection.getRepository(UserEntity);
|
||||
let admins = await userRepository.find({role: UserRoles.Admin});
|
||||
if (admins.length == 0) {
|
||||
let a = new UserEntity();
|
||||
a.name = "admin";
|
||||
a.password = "admin";
|
||||
a.role = UserRoles.Admin;
|
||||
await userRepository.persist(a);
|
||||
}
|
||||
|
||||
createConnection({
|
||||
driver: {
|
||||
type: "mysql",
|
||||
host: Config.Server.database.mysql.host,
|
||||
port: 3306,
|
||||
username: Config.Server.database.mysql.username,
|
||||
password: Config.Server.database.mysql.password,
|
||||
database: Config.Server.database.mysql.database
|
||||
},
|
||||
entities: [
|
||||
UserEntity,
|
||||
DirectoryEntity,
|
||||
PhotoMetadataEntity,
|
||||
PhotoEntity
|
||||
],
|
||||
autoSchemaSync: true,
|
||||
logging: {
|
||||
logQueries: true,
|
||||
logOnlyFailedQueries: true,
|
||||
logFailedQueryError: true,
|
||||
logSchemaCreation: true
|
||||
}
|
||||
}).then((conn) => {
|
||||
this.connection = conn;
|
||||
return resolve(this.connection);
|
||||
|
||||
}).catch(err => reject(err));
|
||||
});
|
||||
}
|
||||
|
||||
public static init(): Promise<void> {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
this.getConnection().then(async connection => {
|
||||
|
||||
let userRepository = connection.getRepository(UserEntity);
|
||||
let admins = await userRepository.find({role: UserRoles.Admin});
|
||||
if (admins.length == 0) {
|
||||
let a = new UserEntity();
|
||||
a.name = "admin";
|
||||
a.password = "admin";
|
||||
a.role = UserRoles.Admin;
|
||||
await userRepository.persist(a);
|
||||
}
|
||||
|
||||
resolve();
|
||||
}).catch(err => reject(err));
|
||||
});
|
||||
}
|
||||
resolve();
|
||||
}).catch(err => reject(err));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
54
backend/model/mysql/SharingManager.ts
Normal file
54
backend/model/mysql/SharingManager.ts
Normal file
@ -0,0 +1,54 @@
|
||||
import {ISharingManager} from "../interfaces/ISharingManager";
|
||||
import {SharingDTO} from "../../../common/entities/SharingDTO";
|
||||
import {MySQLConnection} from "./MySQLConnection";
|
||||
import {SharingEntity} from "./enitites/SharingEntity";
|
||||
import {Config} from "../../../common/config/private/Config";
|
||||
|
||||
export class SharingManager implements ISharingManager {
|
||||
|
||||
private async removeExpiredLink() {
|
||||
const connection = await MySQLConnection.getConnection();
|
||||
return connection
|
||||
.getRepository(SharingEntity)
|
||||
.createQueryBuilder("share")
|
||||
.where("expires < :now", {now: Date.now()})
|
||||
.delete()
|
||||
.execute();
|
||||
}
|
||||
|
||||
async findOne(filter: any): Promise<SharingDTO> {
|
||||
await this.removeExpiredLink();
|
||||
const connection = await MySQLConnection.getConnection();
|
||||
return await connection.getRepository(SharingEntity).findOne(filter);
|
||||
}
|
||||
|
||||
async createSharing(sharing: SharingDTO): Promise<SharingDTO> {
|
||||
await this.removeExpiredLink();
|
||||
const connection = await MySQLConnection.getConnection();
|
||||
return await connection.getRepository(SharingEntity).persist(sharing);
|
||||
|
||||
|
||||
}
|
||||
|
||||
async updateSharing(inSharing: SharingDTO): Promise<SharingDTO> {
|
||||
const connection = await MySQLConnection.getConnection();
|
||||
|
||||
let sharing = await connection.getRepository(SharingEntity).findOne({
|
||||
id: inSharing.id,
|
||||
creator: inSharing.creator.id,
|
||||
path: inSharing.path
|
||||
});
|
||||
|
||||
if (sharing.timeStamp < Date.now() - Config.Server.sharing.updateTimeout) {
|
||||
throw "Sharing is locked, can't update anymore"
|
||||
}
|
||||
|
||||
sharing.password = inSharing.password;
|
||||
sharing.includeSubfolders = inSharing.includeSubfolders;
|
||||
sharing.expires = inSharing.expires;
|
||||
|
||||
return await connection.getRepository(SharingEntity).persist(sharing);
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -5,79 +5,56 @@ import {MySQLConnection} from "./MySQLConnection";
|
||||
|
||||
export class UserManager implements IUserManager {
|
||||
|
||||
constructor() {
|
||||
constructor() {
|
||||
}
|
||||
|
||||
|
||||
public async findOne(filter: any) {
|
||||
const connection = await MySQLConnection.getConnection();
|
||||
const user = (await connection.getRepository(UserEntity).findOne(filter));
|
||||
if (user.permissions && user.permissions != null) {
|
||||
user.permissions = <any>JSON.parse(<any>user.permissions);
|
||||
}
|
||||
return user;
|
||||
|
||||
};
|
||||
|
||||
public findOne(filter: any, cb: (error: any, result: UserDTO) => void) {
|
||||
MySQLConnection.getConnection().then(async connection => {
|
||||
|
||||
let userRepository = connection.getRepository(UserEntity);
|
||||
return cb(null, await userRepository.findOne(filter));
|
||||
|
||||
}).catch((error) => {
|
||||
return cb(error, null);
|
||||
});
|
||||
public async find(filter: any) {
|
||||
const connection = await MySQLConnection.getConnection();
|
||||
return (await connection.getRepository(UserEntity).find(filter)).map(user => {
|
||||
if (user.permissions && user.permissions != null) {
|
||||
user.permissions = <any>JSON.parse(<any>user.permissions);
|
||||
}
|
||||
return user;
|
||||
});
|
||||
}
|
||||
|
||||
public async createUser(user: UserDTO) {
|
||||
const connection = await MySQLConnection.getConnection();
|
||||
if (user.permissions && user.permissions != null) {
|
||||
user.permissions = <any>JSON.stringify(<any>user.permissions);
|
||||
}
|
||||
return await connection.getRepository(UserEntity).persist(user);
|
||||
}
|
||||
|
||||
public find(filter: any, cb: (error: any, result: Array<UserDTO>) => void) {
|
||||
MySQLConnection.getConnection().then(async connection => {
|
||||
public async deleteUser(id: number) {
|
||||
const connection = await MySQLConnection.getConnection();
|
||||
const user = await connection.getRepository(UserEntity).findOne({id: id});
|
||||
return await connection.getRepository(UserEntity).remove(user);
|
||||
}
|
||||
|
||||
let userRepository = connection.getRepository(UserEntity);
|
||||
return cb(null, await userRepository.find(filter));
|
||||
public async changeRole(id: number, newRole: UserRoles) {
|
||||
|
||||
}).catch((error) => {
|
||||
return cb(error, null);
|
||||
});
|
||||
}
|
||||
const connection = await MySQLConnection.getConnection();
|
||||
let userRepository = connection.getRepository(UserEntity);
|
||||
const user = await userRepository.findOne({id: id});
|
||||
user.role = newRole;
|
||||
return await userRepository.persist(user);
|
||||
|
||||
public createUser(user: UserDTO, cb: (error: any, result: UserDTO) => void = (e, r) => {
|
||||
}) {
|
||||
MySQLConnection.getConnection().then(connection => {
|
||||
}
|
||||
|
||||
let userRepository = connection.getRepository(UserEntity);
|
||||
userRepository.persist(user).then(u => cb(null, u)).catch(err => cb(err, null));
|
||||
public async changePassword(request: any) {
|
||||
throw new Error("not implemented"); //TODO: implement
|
||||
}
|
||||
|
||||
}).catch((error) => {
|
||||
return cb(error, null);
|
||||
});
|
||||
}
|
||||
|
||||
public deleteUser(id: number, cb: (error: any) => void) {
|
||||
MySQLConnection.getConnection().then(connection => {
|
||||
|
||||
|
||||
let userRepository = connection.getRepository(UserEntity);
|
||||
userRepository.findOne({id: id}).then((user) => {
|
||||
userRepository.remove(user).catch(err => cb(err));
|
||||
}).catch(err => cb(err));
|
||||
|
||||
|
||||
}).catch((error) => {
|
||||
return cb(error);
|
||||
});
|
||||
}
|
||||
|
||||
public changeRole(id: number, newRole: UserRoles, cb: (error: any, result: string) => void) {
|
||||
|
||||
MySQLConnection.getConnection().then(async connection => {
|
||||
|
||||
|
||||
let userRepository = connection.getRepository(UserEntity);
|
||||
let user = await userRepository.findOne({id: id});
|
||||
user.role = newRole;
|
||||
await userRepository.persist(user);
|
||||
return cb(null, "ok");
|
||||
|
||||
|
||||
}).catch((error) => {
|
||||
return cb(error, null);
|
||||
});
|
||||
}
|
||||
|
||||
public changePassword(request: any, cb: (error: any, result: string) => void) {
|
||||
throw new Error("not implemented"); //TODO: implement
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
31
backend/model/mysql/enitites/SharingEntity.ts
Normal file
31
backend/model/mysql/enitites/SharingEntity.ts
Normal file
@ -0,0 +1,31 @@
|
||||
import {Column, EmbeddableEntity, Embedded, Entity, ManyToOne, PrimaryGeneratedColumn} from "typeorm";
|
||||
import {SharingDTO} from "../../../../common/entities/SharingDTO";
|
||||
import {UserEntity} from "./UserEntity";
|
||||
import {UserDTO} from "../../../../common/entities/UserDTO";
|
||||
|
||||
@Entity()
|
||||
export class SharingEntity implements SharingDTO {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column("string")
|
||||
sharingKey: string;
|
||||
|
||||
@Column("string")
|
||||
path: string;
|
||||
|
||||
@Column("string", {nullable: true})
|
||||
password: string;
|
||||
|
||||
@Column("number")
|
||||
expires: number;
|
||||
|
||||
@Column("number")
|
||||
timeStamp: number;
|
||||
|
||||
@Column("boolean")
|
||||
includeSubfolders: boolean;
|
||||
|
||||
@ManyToOne(type => UserEntity)
|
||||
creator: UserDTO;
|
||||
}
|
@ -1,23 +1,26 @@
|
||||
import {UserDTO, UserRoles} from "../../../../common/entities/UserDTO";
|
||||
import {Entity, Column, PrimaryGeneratedColumn} from "typeorm";
|
||||
import {Column, Entity, PrimaryGeneratedColumn} from "typeorm";
|
||||
|
||||
@Entity()
|
||||
export class UserEntity implements UserDTO {
|
||||
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column({
|
||||
length: 500
|
||||
})
|
||||
name: string;
|
||||
@Column({
|
||||
length: 500
|
||||
})
|
||||
name: string;
|
||||
|
||||
@Column({
|
||||
length: 500
|
||||
})
|
||||
password: string;
|
||||
@Column({
|
||||
length: 500
|
||||
})
|
||||
password: string;
|
||||
|
||||
@Column("int")
|
||||
role: UserRoles;
|
||||
@Column("int")
|
||||
role: UserRoles;
|
||||
|
||||
}
|
||||
@Column("string")
|
||||
permissions: string[];
|
||||
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ export class GalleryRouter {
|
||||
private static addDirectoryList(app) {
|
||||
app.get(["/api/gallery/content/:directory(*)", "/api/gallery/", "/api/gallery//"],
|
||||
AuthenticationMWs.authenticate,
|
||||
AuthenticationMWs.authoriseDirectory,
|
||||
GalleryMWs.listDirectory,
|
||||
ThumbnailGeneratorMWs.addThumbnailInformation,
|
||||
GalleryMWs.removeCyclicDirectoryReferences,
|
||||
|
@ -3,6 +3,7 @@ import {NextFunction, Request, Response} from "express";
|
||||
import * as _path from "path";
|
||||
import {Utils} from "../../common/Utils";
|
||||
import {Config} from "../../common/config/private/Config";
|
||||
import {ProjectPath} from "../ProjectPath";
|
||||
|
||||
export class PublicRouter {
|
||||
|
||||
@ -24,11 +25,11 @@ export class PublicRouter {
|
||||
app.get('/config_inject.js', (req: Request, res: Response) => {
|
||||
res.render(_path.resolve(__dirname, './../../dist/config_inject.ejs'), res.tpl);
|
||||
});
|
||||
app.get(['/', '/login', "/gallery*", "/admin", "/search*"], (req: Request, res: Response) => {
|
||||
app.get(['/', '/login', "/gallery*", "/share*", "/admin", "/search*"], (req: Request, res: Response) => {
|
||||
res.sendFile(_path.resolve(__dirname, './../../dist/index.html'));
|
||||
});
|
||||
|
||||
app.use(_express.static(_path.resolve(__dirname, './../../dist')));
|
||||
app.use(_express.static(ProjectPath.FrontendFolder));
|
||||
app.use('/node_modules', _express.static(_path.resolve(__dirname, './../../node_modules')));
|
||||
app.use('/common', _express.static(_path.resolve(__dirname, './../../common')));
|
||||
|
||||
|
@ -1,26 +1,40 @@
|
||||
import {AuthenticationMWs} from "../middlewares/user/AuthenticationMWs";
|
||||
import {UserRoles} from "../../common/entities/UserDTO";
|
||||
import {RenderingMWs} from "../middlewares/RenderingMWs";
|
||||
import {SharingMWs} from "../middlewares/SharingMWs";
|
||||
|
||||
export class SharingRouter {
|
||||
public static route(app: any) {
|
||||
|
||||
this.addGetSharing(app);
|
||||
this.addCreateSharing(app);
|
||||
this.addUpdateSharing(app);
|
||||
}
|
||||
|
||||
private static addGetSharing(app) {
|
||||
app.get("/api/share/:directory",
|
||||
app.get("/api/share/:sharingKey",
|
||||
AuthenticationMWs.authenticate,
|
||||
AuthenticationMWs.authorise(UserRoles.User)
|
||||
//TODO: implement
|
||||
AuthenticationMWs.authorise(UserRoles.Guest),
|
||||
SharingMWs.getSharing,
|
||||
RenderingMWs.renderSharing
|
||||
);
|
||||
};
|
||||
|
||||
private static addCreateSharing(app) {
|
||||
app.post(["/api/share/:directory(*)", "/api/share/", "/api/share//"],
|
||||
AuthenticationMWs.authenticate,
|
||||
AuthenticationMWs.authorise(UserRoles.User),
|
||||
SharingMWs.createSharing,
|
||||
RenderingMWs.renderSharing
|
||||
);
|
||||
};
|
||||
|
||||
private static addUpdateSharing(app) {
|
||||
app.post("/api/share/:directory",
|
||||
app.put(["/api/share/:directory(*)", "/api/share/", "/api/share//"],
|
||||
AuthenticationMWs.authenticate,
|
||||
AuthenticationMWs.authorise(UserRoles.User)
|
||||
//TODO: implement
|
||||
AuthenticationMWs.authorise(UserRoles.User),
|
||||
SharingMWs.updateSharing,
|
||||
RenderingMWs.renderSharing
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -13,6 +13,7 @@ import {Logger} from "./Logger";
|
||||
import {Config} from "../common/config/private/Config";
|
||||
import {DatabaseType, ThumbnailProcessingLib} from "../common/config/private/IPrivateConfig";
|
||||
import {LoggerRouter} from "./routes/LoggerRouter";
|
||||
import {ProjectPath} from "./ProjectPath";
|
||||
|
||||
const LOG_TAG = "[server]";
|
||||
export class Server {
|
||||
@ -88,7 +89,11 @@ export class Server {
|
||||
if (Config.Server.thumbnail.processingLibrary == ThumbnailProcessingLib.gm) {
|
||||
try {
|
||||
const gm = require("gm");
|
||||
gm(1, 1).stream((err) => {
|
||||
gm(ProjectPath.FrontendFolder + "/assets/icon.png").size((err, value) => {
|
||||
console.log(err, value);
|
||||
if (!err) {
|
||||
return;
|
||||
}
|
||||
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." +
|
||||
|
@ -26,11 +26,14 @@ export interface ThumbnailConfig {
|
||||
processingLibrary: ThumbnailProcessingLib;
|
||||
qualityPriority: boolean;
|
||||
}
|
||||
|
||||
export interface SharingConfig {
|
||||
updateTimeout: number;
|
||||
}
|
||||
export interface ServerConfig {
|
||||
port: number;
|
||||
imagesFolder: string;
|
||||
thumbnail: ThumbnailConfig;
|
||||
database: DataBaseConfig;
|
||||
enableThreading: boolean;
|
||||
sharing: SharingConfig;
|
||||
}
|
||||
|
@ -25,6 +25,9 @@ export class PrivateConfigClass extends PublicConfigClass {
|
||||
|
||||
}
|
||||
},
|
||||
sharing: {
|
||||
updateTimeout: 1000 * 60 * 5
|
||||
},
|
||||
enableThreading: true
|
||||
};
|
||||
|
||||
|
@ -4,10 +4,16 @@ interface SearchConfig {
|
||||
autocompleteEnabled: boolean
|
||||
}
|
||||
|
||||
interface SharingConfig {
|
||||
enabled: boolean;
|
||||
passwordProtected: boolean;
|
||||
}
|
||||
|
||||
interface ClientConfig {
|
||||
iconSize: number;
|
||||
thumbnailSizes: Array<number>;
|
||||
Search: SearchConfig;
|
||||
Sharing: SharingConfig;
|
||||
concurrentThumbnailGenerations: number;
|
||||
enableCache: boolean;
|
||||
enableOnScrollRendering: boolean;
|
||||
@ -29,6 +35,10 @@ export class PublicConfigClass {
|
||||
instantSearchEnabled: true,
|
||||
autocompleteEnabled: true
|
||||
},
|
||||
Sharing: {
|
||||
enabled: true,
|
||||
passwordProtected: true
|
||||
},
|
||||
concurrentThumbnailGenerations: 1,
|
||||
enableCache: false,
|
||||
enableOnScrollRendering: true,
|
||||
|
@ -2,17 +2,20 @@ export enum ErrorCodes{
|
||||
NOT_AUTHENTICATED = 0,
|
||||
ALREADY_AUTHENTICATED = 1,
|
||||
NOT_AUTHORISED = 2,
|
||||
CREDENTIAL_NOT_FOUND = 3,
|
||||
PERMISSION_DENIED = 3,
|
||||
CREDENTIAL_NOT_FOUND = 4,
|
||||
|
||||
|
||||
USER_CREATION_ERROR = 4,
|
||||
USER_CREATION_ERROR = 5,
|
||||
|
||||
|
||||
GENERAL_ERROR = 5,
|
||||
THUMBNAIL_GENERATION_ERROR = 6,
|
||||
SERVER_ERROR = 7,
|
||||
GENERAL_ERROR = 6,
|
||||
THUMBNAIL_GENERATION_ERROR = 7,
|
||||
SERVER_ERROR = 8,
|
||||
|
||||
USER_MANAGEMENT_DISABLED = 8
|
||||
USER_MANAGEMENT_DISABLED = 9,
|
||||
|
||||
INPUT_ERROR = 10
|
||||
|
||||
}
|
||||
|
||||
|
19
common/entities/SharingDTO.ts
Normal file
19
common/entities/SharingDTO.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import {UserDTO} from "./UserDTO";
|
||||
|
||||
export interface SharingDTO {
|
||||
id: number;
|
||||
path: string;
|
||||
sharingKey: string;
|
||||
password: string;
|
||||
expires: number;
|
||||
timeStamp: number;
|
||||
includeSubfolders: boolean;
|
||||
creator: UserDTO;
|
||||
}
|
||||
|
||||
export interface CreateSharingDTO {
|
||||
id?: number;
|
||||
password: string;
|
||||
valid: number;
|
||||
includeSubfolders: boolean;
|
||||
}
|
@ -1,5 +1,8 @@
|
||||
import {DirectoryDTO} from "./DirectoryDTO";
|
||||
import {Utils} from "../Utils";
|
||||
export enum UserRoles{
|
||||
Guest = 1,
|
||||
Guest = 0,
|
||||
TrustedGuest = 1,
|
||||
User = 2,
|
||||
Admin = 3,
|
||||
Developer = 4,
|
||||
@ -11,4 +14,33 @@ export interface UserDTO {
|
||||
name: string;
|
||||
password: string;
|
||||
role: UserRoles;
|
||||
permissions: string[]; //user can only see these permissions. if ends with *, its recursive
|
||||
}
|
||||
|
||||
export module UserUtil {
|
||||
|
||||
export const isPathAvailable = (path: string, permissions: string[]): boolean => {
|
||||
if (permissions == null || permissions.length == 0 || permissions[0] == "/") {
|
||||
return true;
|
||||
}
|
||||
for (let i = 0; i < permissions.length; i++) {
|
||||
let permission = permissions[i];
|
||||
if (permission[permission.length - 1] == "*") {
|
||||
permission = permission.slice(0, -1);
|
||||
if (path.startsWith(permission)) {
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
if (path == permission) {
|
||||
return true
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
export const isDirectoryAvailable = (direcotry: DirectoryDTO, permissions: string[]): boolean => {
|
||||
return isPathAvailable(Utils.concatUrls(direcotry.path, direcotry.name), permissions);
|
||||
};
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ export class AdminComponent implements OnInit {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
if (!this._authService.isAuthenticated() || this._authService.getUser().role < UserRoles.Admin) {
|
||||
if (!this._authService.isAuthenticated() || this._authService.user.value.role < UserRoles.Admin) {
|
||||
this._router.navigate(['login']);
|
||||
return;
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ export class AppComponent implements OnInit {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this._authenticationService.OnUserChanged.on((user: UserDTO) => {
|
||||
this._authenticationService.user.subscribe((user: UserDTO) => {
|
||||
if (user != null) {
|
||||
if (this._router.isActive('login', true)) {
|
||||
console.log("routing");
|
||||
|
@ -33,6 +33,9 @@ import {OverlayService} from "./gallery/overlay.service";
|
||||
import {Config} from "../../common/config/public/Config";
|
||||
import {LAZY_MAPS_API_CONFIG} from "@agm/core/services";
|
||||
import {SlimLoadingBarModule} from "ng2-slim-loading-bar";
|
||||
import {GalleryShareComponent} from "./gallery/share/share.gallery.component";
|
||||
import {ShareLoginComponent} from "./sharelogin/share-login.component";
|
||||
import {ShareService} from "./gallery/share.service";
|
||||
|
||||
|
||||
@Injectable()
|
||||
@ -55,6 +58,7 @@ export class GoogleMapsConfig {
|
||||
],
|
||||
declarations: [AppComponent,
|
||||
LoginComponent,
|
||||
ShareLoginComponent,
|
||||
AdminComponent,
|
||||
GalleryComponent,
|
||||
FrameComponent,
|
||||
@ -68,6 +72,7 @@ export class GoogleMapsConfig {
|
||||
GalleryMapLightboxComponent,
|
||||
FrameComponent,
|
||||
GallerySearchComponent,
|
||||
GalleryShareComponent,
|
||||
GalleryNavigatorComponent,
|
||||
GalleryPhotoComponent,
|
||||
FrameComponent,
|
||||
@ -75,6 +80,7 @@ export class GoogleMapsConfig {
|
||||
providers: [
|
||||
{provide: LAZY_MAPS_API_CONFIG, useClass: GoogleMapsConfig},
|
||||
NetworkService,
|
||||
ShareService,
|
||||
UserService,
|
||||
GalleryCacheService,
|
||||
GalleryService,
|
||||
|
@ -3,12 +3,17 @@ import {RouterModule, Routes} from "@angular/router";
|
||||
import {LoginComponent} from "./login/login.component";
|
||||
import {GalleryComponent} from "./gallery/gallery.component";
|
||||
import {AdminComponent} from "./admin/admin.component";
|
||||
import {ShareLoginComponent} from "./sharelogin/share-login.component";
|
||||
|
||||
const ROUTES: Routes = [
|
||||
{
|
||||
path: 'login',
|
||||
component: LoginComponent
|
||||
},
|
||||
{
|
||||
path: 'shareLogin',
|
||||
component: ShareLoginComponent
|
||||
},
|
||||
{
|
||||
path: 'admin',
|
||||
component: AdminComponent
|
||||
@ -25,6 +30,10 @@ const ROUTES: Routes = [
|
||||
path: 'search/:searchText',
|
||||
component: GalleryComponent
|
||||
},
|
||||
{
|
||||
path: 'share/:sharingKey',
|
||||
component: GalleryComponent
|
||||
},
|
||||
{path: '', redirectTo: '/login', pathMatch: 'full'}
|
||||
];
|
||||
|
||||
|
@ -16,9 +16,10 @@
|
||||
<li class="active"><a [routerLink]="['/gallery','/']">Gallery</a></li>
|
||||
<li><a [routerLink]="['/admin']">Admin</a></li>
|
||||
</ul>
|
||||
|
||||
<ul class="nav navbar-nav navbar-right" *ngIf="authenticationRequired">
|
||||
<li>
|
||||
<p class="navbar-text" *ngIf="user">{{user.name}}</p>
|
||||
<p class="navbar-text" *ngIf="user.value">{{user.value.name}}</p>
|
||||
</li>
|
||||
<li>
|
||||
<a style="cursor: pointer" (click)="logout()">Logout</a>
|
||||
|
@ -3,6 +3,7 @@ import {RouterLink} from "@angular/router";
|
||||
import {AuthenticationService} from "../model/network/authentication.service";
|
||||
import {UserDTO} from "../../../common/entities/UserDTO";
|
||||
import {Config} from "../../../common/config/public/Config";
|
||||
import {BehaviorSubject} from "rxjs/BehaviorSubject";
|
||||
|
||||
@Component({
|
||||
selector: 'app-frame',
|
||||
@ -13,11 +14,11 @@ import {Config} from "../../../common/config/public/Config";
|
||||
})
|
||||
export class FrameComponent {
|
||||
|
||||
user: UserDTO;
|
||||
user: BehaviorSubject<UserDTO>;
|
||||
authenticationRequired: boolean = false;
|
||||
|
||||
constructor(private _authService: AuthenticationService) {
|
||||
this.user = this._authService.getUser();
|
||||
this.user = this._authService.user;
|
||||
this.authenticationRequired = Config.Client.authenticationRequired;
|
||||
}
|
||||
|
||||
|
@ -1,21 +1,21 @@
|
||||
<a #dirContainer class="button btn btn-default" [routerLink]="['/gallery', getDirectoryPath()]"
|
||||
[queryParams]="_shareService.isSharing() ? { sk: _shareService.getSharingKey() }:{}"
|
||||
style="display: inline-block;">
|
||||
|
||||
|
||||
<div class="photo-container">
|
||||
<div class="photo" *ngIf="thumbnail && thumbnail.Available"
|
||||
[style.background-image]="getSanitizedThUrl()"></div>
|
||||
|
||||
<div class="photo-container">
|
||||
<div class="photo" *ngIf="thumbnail && thumbnail.Available"
|
||||
[style.background-image]="getSanitizedThUrl()"></div>
|
||||
|
||||
<span *ngIf="!thumbnail || !thumbnail.Available" class="glyphicon glyphicon-folder-open no-image"
|
||||
aria-hidden="true">
|
||||
<span *ngIf="!thumbnail || !thumbnail.Available" class="glyphicon glyphicon-folder-open no-image"
|
||||
aria-hidden="true">
|
||||
|
||||
</span>
|
||||
</div>
|
||||
<!--Info box -->
|
||||
<div #info class="info">
|
||||
<div class="photo-name">{{directory.name}}</div>
|
||||
</div>
|
||||
<!--Info box -->
|
||||
<div #info class="info">
|
||||
<div class="photo-name">{{directory.name}}</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
|
@ -5,6 +5,7 @@ import {RouterLink} from "@angular/router";
|
||||
import {Utils} from "../../../../common/Utils";
|
||||
import {Photo} from "../Photo";
|
||||
import {Thumbnail, ThumbnailManagerService} from "../thumnailManager.service";
|
||||
import {ShareService} from "../share.service";
|
||||
|
||||
@Component({
|
||||
selector: 'gallery-directory',
|
||||
@ -17,7 +18,10 @@ export class GalleryDirectoryComponent implements OnInit, OnDestroy {
|
||||
@ViewChild("dirContainer") container: ElementRef;
|
||||
thumbnail: Thumbnail = null;
|
||||
|
||||
constructor(private thumbnailService: ThumbnailManagerService, private _sanitizer: DomSanitizer) {
|
||||
constructor(private thumbnailService: ThumbnailManagerService,
|
||||
private _sanitizer: DomSanitizer,
|
||||
public _shareService: ShareService) {
|
||||
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
|
@ -2,6 +2,7 @@
|
||||
<app-frame>
|
||||
|
||||
<div navbar>
|
||||
<gallery-share *ngIf="showShare"></gallery-share>
|
||||
<gallery-search #search *ngIf="showSearchBar"></gallery-search>
|
||||
</div>
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import {Component, OnInit, ViewChild} from "@angular/core";
|
||||
import {Component, OnDestroy, OnInit, ViewChild} from "@angular/core";
|
||||
import {AuthenticationService} from "../model/network/authentication.service";
|
||||
import {ActivatedRoute, Params, Router} from "@angular/router";
|
||||
import {GalleryService} from "./gallery.service";
|
||||
@ -8,87 +8,119 @@ import {SearchTypes} from "../../../common/entities/AutoCompleteItem";
|
||||
import {Config} from "../../../common/config/public/Config";
|
||||
import {DirectoryDTO} from "../../../common/entities/DirectoryDTO";
|
||||
import {SearchResultDTO} from "../../../common/entities/SearchResult";
|
||||
import {ShareService} from "./share.service";
|
||||
|
||||
@Component({
|
||||
selector: 'gallery',
|
||||
templateUrl: './gallery.component.html',
|
||||
styleUrls: ['./gallery.component.css']
|
||||
})
|
||||
export class GalleryComponent implements OnInit {
|
||||
export class GalleryComponent implements OnInit, OnDestroy {
|
||||
|
||||
@ViewChild(GallerySearchComponent) search: GallerySearchComponent;
|
||||
@ViewChild(GalleryGridComponent) grid: GalleryGridComponent;
|
||||
|
||||
public showSearchBar: boolean = true;
|
||||
public showShare: boolean = true;
|
||||
public directories: DirectoryDTO[] = [];
|
||||
public isPhotoWithLocation = false;
|
||||
private subscription = {
|
||||
content: null,
|
||||
route: null
|
||||
};
|
||||
|
||||
constructor(public _galleryService: GalleryService,
|
||||
private _authService: AuthenticationService,
|
||||
private _router: Router,
|
||||
private shareService: ShareService,
|
||||
private _route: ActivatedRoute) {
|
||||
|
||||
this.showSearchBar = Config.Client.Search.searchEnabled;
|
||||
this.showShare = Config.Client.Sharing.enabled;
|
||||
}
|
||||
|
||||
|
||||
ngOnInit() {
|
||||
if (!this._authService.isAuthenticated()) {
|
||||
this._router.navigate(['login']);
|
||||
if (!this._authService.isAuthenticated() &&
|
||||
(!this.shareService.isSharing() ||
|
||||
(this.shareService.isSharing() && Config.Client.Sharing.passwordProtected == true))
|
||||
) {
|
||||
if (this.shareService.isSharing()) {
|
||||
this._router.navigate(['shareLogin']);
|
||||
} else {
|
||||
this._router.navigate(['login']);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
this._galleryService.content.subscribe((content) => {
|
||||
const dirSorter = (a: DirectoryDTO, b: DirectoryDTO) => {
|
||||
return a.name.localeCompare(b.name);
|
||||
};
|
||||
|
||||
const tmp = <DirectoryDTO | SearchResultDTO>(content.searchResult || content.directory || {
|
||||
directories: [],
|
||||
photos: []
|
||||
});
|
||||
this.directories = tmp.directories.sort(dirSorter);
|
||||
this.isPhotoWithLocation = false;
|
||||
for (let i = 0; i < tmp.photos.length; i++) {
|
||||
if (tmp.photos[i].metadata &&
|
||||
tmp.photos[i].metadata.positionData &&
|
||||
tmp.photos[i].metadata.positionData.GPSData &&
|
||||
tmp.photos[i].metadata.positionData.GPSData.longitude
|
||||
) {
|
||||
this.isPhotoWithLocation = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this._route.params
|
||||
.subscribe((params: Params) => {
|
||||
let searchText = params['searchText'];
|
||||
if (searchText && searchText != "") {
|
||||
console.log("searching");
|
||||
let typeString = params['type'];
|
||||
|
||||
if (typeString && typeString != "") {
|
||||
console.log("with type");
|
||||
let type: SearchTypes = <any>SearchTypes[typeString];
|
||||
this._galleryService.search(searchText, type);
|
||||
return;
|
||||
}
|
||||
|
||||
this._galleryService.search(searchText);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
let directoryName = params['directory'];
|
||||
directoryName = directoryName ? directoryName : "";
|
||||
|
||||
this._galleryService.getDirectory(directoryName);
|
||||
|
||||
});
|
||||
|
||||
this.subscription.content = this._galleryService.content.subscribe(this.onContentChange);
|
||||
this.subscription.route = this._route.params.subscribe(this.onRoute);
|
||||
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
if (this.subscription.content !== null) {
|
||||
this.subscription.content.unsubscribe();
|
||||
}
|
||||
if (this.subscription.route !== null) {
|
||||
this.subscription.route.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
private onContentChange = (content) => {
|
||||
const dirSorter = (a: DirectoryDTO, b: DirectoryDTO) => {
|
||||
return a.name.localeCompare(b.name);
|
||||
};
|
||||
|
||||
const tmp = <DirectoryDTO | SearchResultDTO>(content.searchResult || content.directory || {
|
||||
directories: [],
|
||||
photos: []
|
||||
});
|
||||
this.directories = tmp.directories.sort(dirSorter);
|
||||
this.isPhotoWithLocation = false;
|
||||
for (let i = 0; i < tmp.photos.length; i++) {
|
||||
if (tmp.photos[i].metadata &&
|
||||
tmp.photos[i].metadata.positionData &&
|
||||
tmp.photos[i].metadata.positionData.GPSData &&
|
||||
tmp.photos[i].metadata.positionData.GPSData.longitude
|
||||
) {
|
||||
this.isPhotoWithLocation = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private onRoute = async (params: Params) => {
|
||||
console.log("onRoute", params);
|
||||
const searchText = params['searchText'];
|
||||
if (searchText && searchText != "") {
|
||||
console.log("searching");
|
||||
let typeString = params['type'];
|
||||
|
||||
if (typeString && typeString != "") {
|
||||
console.log("with type");
|
||||
let type: SearchTypes = <any>SearchTypes[typeString];
|
||||
this._galleryService.search(searchText, type);
|
||||
return;
|
||||
}
|
||||
|
||||
this._galleryService.search(searchText);
|
||||
return;
|
||||
}
|
||||
|
||||
if (params['sharingKey'] && params['sharingKey'] != "") {
|
||||
const sharing = await this._galleryService.getSharing(this.shareService.getSharingKey());
|
||||
this._router.navigate(['/gallery', sharing.path], {queryParams: {sk: this.shareService.getSharingKey()}});
|
||||
return;
|
||||
}
|
||||
|
||||
let directoryName = params['directory'];
|
||||
directoryName = directoryName || "";
|
||||
|
||||
this._galleryService.getDirectory(directoryName);
|
||||
|
||||
};
|
||||
|
||||
|
||||
onLightboxLastElement() {
|
||||
this.grid.renderARow();
|
||||
|
@ -1,12 +1,14 @@
|
||||
import {Injectable} from "@angular/core";
|
||||
import {NetworkService} from "../model/network/network.service";
|
||||
import {Message} from "../../../common/entities/Message";
|
||||
import {ContentWrapper} from "../../../common/entities/ConentWrapper";
|
||||
import {PhotoDTO} from "../../../common/entities/PhotoDTO";
|
||||
import {DirectoryDTO} from "../../../common/entities/DirectoryDTO";
|
||||
import {SearchTypes} from "../../../common/entities/AutoCompleteItem";
|
||||
import {GalleryCacheService} from "./cache.gallery.service";
|
||||
import {BehaviorSubject} from "rxjs/BehaviorSubject";
|
||||
import {SharingDTO} from "../../../common/entities/SharingDTO";
|
||||
import {Config} from "../../../common/config/public/Config";
|
||||
import {ShareService} from "./share.service";
|
||||
|
||||
@Injectable()
|
||||
export class GalleryService {
|
||||
@ -15,7 +17,9 @@ export class GalleryService {
|
||||
private lastDirectory: DirectoryDTO;
|
||||
private searchId: any;
|
||||
|
||||
constructor(private networkService: NetworkService, private galleryCacheService: GalleryCacheService) {
|
||||
constructor(private networkService: NetworkService,
|
||||
private galleryCacheService: GalleryCacheService,
|
||||
private _shareService: ShareService) {
|
||||
this.content = new BehaviorSubject<ContentWrapper>(new ContentWrapper());
|
||||
}
|
||||
|
||||
@ -23,7 +27,7 @@ export class GalleryService {
|
||||
directory: null
|
||||
};
|
||||
|
||||
public async getDirectory(directoryName: string): Promise<Message<ContentWrapper>> {
|
||||
public async getDirectory(directoryName: string): Promise<ContentWrapper> {
|
||||
const content = new ContentWrapper();
|
||||
|
||||
content.directory = this.galleryCacheService.getDirectory(directoryName);
|
||||
@ -32,70 +36,71 @@ export class GalleryService {
|
||||
this.content.next(content);
|
||||
this.lastRequest.directory = directoryName;
|
||||
|
||||
let message: Message<ContentWrapper> = await this.networkService.getJson<Message<ContentWrapper>>("/gallery/content/" + directoryName);
|
||||
|
||||
if (!message.error && message.result) {
|
||||
|
||||
this.galleryCacheService.setDirectory(message.result.directory); //save it before adding references
|
||||
|
||||
if (this.lastRequest.directory != directoryName) {
|
||||
return;
|
||||
let cw: ContentWrapper = null;
|
||||
if (Config.Client.Sharing.enabled == true) {
|
||||
if (this._shareService.isSharing()) {
|
||||
cw = await this.networkService.getJson<ContentWrapper>("/gallery/content/" + directoryName + "?sk=" + this._shareService.getSharingKey());
|
||||
}
|
||||
|
||||
//Add references
|
||||
let addDir = (dir: DirectoryDTO) => {
|
||||
dir.photos.forEach((photo: PhotoDTO) => {
|
||||
photo.directory = dir;
|
||||
});
|
||||
|
||||
dir.directories.forEach((directory: DirectoryDTO) => {
|
||||
addDir(directory);
|
||||
directory.parent = dir;
|
||||
});
|
||||
|
||||
|
||||
};
|
||||
addDir(message.result.directory);
|
||||
|
||||
|
||||
this.lastDirectory = message.result.directory;
|
||||
this.content.next(message.result);
|
||||
}
|
||||
if (cw == null) {
|
||||
cw = await this.networkService.getJson<ContentWrapper>("/gallery/content/" + directoryName);
|
||||
}
|
||||
|
||||
return message;
|
||||
this.galleryCacheService.setDirectory(cw.directory); //save it before adding references
|
||||
|
||||
if (this.lastRequest.directory != directoryName) {
|
||||
return;
|
||||
}
|
||||
|
||||
//Add references
|
||||
let addDir = (dir: DirectoryDTO) => {
|
||||
dir.photos.forEach((photo: PhotoDTO) => {
|
||||
photo.directory = dir;
|
||||
});
|
||||
|
||||
dir.directories.forEach((directory: DirectoryDTO) => {
|
||||
addDir(directory);
|
||||
directory.parent = dir;
|
||||
});
|
||||
|
||||
|
||||
};
|
||||
addDir(cw.directory);
|
||||
|
||||
|
||||
this.lastDirectory = cw.directory;
|
||||
this.content.next(cw);
|
||||
|
||||
|
||||
return cw;
|
||||
|
||||
}
|
||||
|
||||
//TODO: cache
|
||||
public search(text: string, type?: SearchTypes): Promise<Message<ContentWrapper>> {
|
||||
public async search(text: string, type?: SearchTypes): Promise<ContentWrapper> {
|
||||
clearTimeout(this.searchId);
|
||||
if (text === null || text === '') {
|
||||
return Promise.resolve(new Message(null, null));
|
||||
return null
|
||||
}
|
||||
|
||||
let queryString = "/search/" + text;
|
||||
if (type) {
|
||||
queryString += "?type=" + type;
|
||||
}
|
||||
|
||||
return this.networkService.getJson(queryString).then(
|
||||
(message: Message<ContentWrapper>) => {
|
||||
if (!message.error && message.result) {
|
||||
this.content.next(message.result);
|
||||
}
|
||||
return message;
|
||||
});
|
||||
const cw: ContentWrapper = await this.networkService.getJson<ContentWrapper>(queryString);
|
||||
this.content.next(cw);
|
||||
return cw;
|
||||
}
|
||||
|
||||
//TODO: cache (together with normal search)
|
||||
public instantSearch(text: string): Promise<Message<ContentWrapper>> {
|
||||
public async instantSearch(text: string): Promise<ContentWrapper> {
|
||||
if (text === null || text === '') {
|
||||
const content = new ContentWrapper();
|
||||
content.directory = this.lastDirectory;
|
||||
content.searchResult = null;
|
||||
this.content.next(content);
|
||||
clearTimeout(this.searchId);
|
||||
return Promise.resolve(new Message(null, null));
|
||||
return null
|
||||
}
|
||||
|
||||
if (this.searchId != null) {
|
||||
@ -107,14 +112,14 @@ export class GalleryService {
|
||||
this.searchId = null;
|
||||
}, 3000); //TODO: set timeout to config
|
||||
|
||||
return this.networkService.getJson("/instant-search/" + text).then(
|
||||
(message: Message<ContentWrapper>) => {
|
||||
if (!message.error && message.result) {
|
||||
this.content.next(message.result);
|
||||
}
|
||||
return message;
|
||||
});
|
||||
const cw = await this.networkService.getJson<ContentWrapper>("/instant-search/" + text);
|
||||
this.content.next(cw);
|
||||
return cw;
|
||||
|
||||
}
|
||||
|
||||
public async getSharing(sharingKey: string): Promise<SharingDTO> {
|
||||
return this.networkService.getJson<SharingDTO>("/share/" + sharingKey);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
<ol id="directory-path" class="breadcrumb">
|
||||
<li *ngFor="let path of routes">
|
||||
<a *ngIf="path.route" [routerLink]="['/gallery',path.route]">{{path.name}}</a>
|
||||
<span *ngIf="!path.route">{{path.name}}</span>
|
||||
</li>
|
||||
<li *ngFor="let path of routes">
|
||||
<a *ngIf="path.route" [routerLink]="['/gallery',path.route]"
|
||||
[queryParams]="_shareService.isSharing() ? { sk: _shareService.getSharingKey() }:{}">{{path.name}}</a>
|
||||
<span *ngIf="!path.route">{{path.name}}</span>
|
||||
</li>
|
||||
|
||||
</ol>
|
||||
|
@ -1,6 +1,9 @@
|
||||
import {Component, Input, OnChanges} from "@angular/core";
|
||||
import {DirectoryDTO} from "../../../../common/entities/DirectoryDTO";
|
||||
import {RouterLink} from "@angular/router";
|
||||
import {UserUtil} from "../../../../common/entities/UserDTO";
|
||||
import {AuthenticationService} from "../../model/network/authentication.service";
|
||||
import {ShareService} from "../share.service";
|
||||
|
||||
@Component({
|
||||
selector: 'gallery-navbar',
|
||||
@ -10,9 +13,10 @@ import {RouterLink} from "@angular/router";
|
||||
export class GalleryNavigatorComponent implements OnChanges {
|
||||
@Input() directory: DirectoryDTO;
|
||||
|
||||
routes: Array<any> = [];
|
||||
routes: Array<NavigatorPath> = [];
|
||||
|
||||
constructor() {
|
||||
constructor(private _authService: AuthenticationService,
|
||||
public _shareService: ShareService) {
|
||||
}
|
||||
|
||||
|
||||
@ -38,24 +42,24 @@ export class GalleryNavigatorComponent implements OnChanges {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let arr: any = [];
|
||||
const user = this._authService.user.value;
|
||||
let arr: NavigatorPath[] = [];
|
||||
|
||||
//create root link
|
||||
if (dirs.length == 0) {
|
||||
arr.push({name: "Images", route: null});
|
||||
} else {
|
||||
arr.push({name: "Images", route: "/"});
|
||||
arr.push({name: "Images", route: UserUtil.isPathAvailable("/", user.permissions) ? "/" : null});
|
||||
|
||||
}
|
||||
|
||||
//create rest navigation
|
||||
dirs.forEach((name, index) => {
|
||||
let route = dirs.slice(0, dirs.indexOf(name) + 1).join("/");
|
||||
const route = dirs.slice(0, dirs.indexOf(name) + 1).join("/");
|
||||
if (dirs.length - 1 == index) {
|
||||
arr.push({name: name, route: null});
|
||||
} else {
|
||||
arr.push({name: name, route: route});
|
||||
arr.push({name: name, route: UserUtil.isPathAvailable(route, user.permissions) ? route : null});
|
||||
}
|
||||
});
|
||||
|
||||
@ -68,3 +72,8 @@ export class GalleryNavigatorComponent implements OnChanges {
|
||||
|
||||
}
|
||||
|
||||
interface NavigatorPath {
|
||||
name: string;
|
||||
route: string;
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
import {Injectable} from "@angular/core";
|
||||
import {NetworkService} from "../../model/network/network.service";
|
||||
import {AutoCompleteItem} from "../../../../common/entities/AutoCompleteItem";
|
||||
import {Message} from "../../../../common/entities/Message";
|
||||
|
||||
@Injectable()
|
||||
export class AutoCompleteService {
|
||||
@ -10,8 +9,8 @@ export class AutoCompleteService {
|
||||
constructor(private _networkService: NetworkService) {
|
||||
}
|
||||
|
||||
public autoComplete(text: string): Promise<Message<Array<AutoCompleteItem>>> {
|
||||
return this._networkService.getJson("/autocomplete/" + text);
|
||||
public autoComplete(text: string): Promise<Array<AutoCompleteItem>> {
|
||||
return this._networkService.getJson<Array<AutoCompleteItem>>("/autocomplete/" + text);
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
<div class="col-sm-4 col-md-5 pull-right">
|
||||
<div class="col-sm-3 col-md-4 pull-right">
|
||||
<form class="navbar-form" role="search" #SearchForm="ngForm">
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" placeholder="Search" (keyup)="onSearchChange($event)"
|
||||
|
@ -2,7 +2,6 @@ import {Component} from "@angular/core";
|
||||
import {AutoCompleteService} from "./autocomplete.service";
|
||||
import {AutoCompleteItem, SearchTypes} from "../../../../common/entities/AutoCompleteItem";
|
||||
import {ActivatedRoute, Params, RouterLink} from "@angular/router";
|
||||
import {Message} from "../../../../common/entities/Message";
|
||||
import {GalleryService} from "../gallery.service";
|
||||
import {Config} from "../../../../common/config/public/Config";
|
||||
|
||||
@ -23,20 +22,27 @@ export class GallerySearchComponent {
|
||||
|
||||
SearchTypes: any = [];
|
||||
|
||||
private subscription = null;
|
||||
|
||||
constructor(private _autoCompleteService: AutoCompleteService,
|
||||
private _galleryService: GalleryService,
|
||||
private _route: ActivatedRoute) {
|
||||
|
||||
this.SearchTypes = SearchTypes;
|
||||
|
||||
this._route.params
|
||||
.subscribe((params: Params) => {
|
||||
let searchText = params['searchText'];
|
||||
if (searchText && searchText != "") {
|
||||
this.searchText = searchText;
|
||||
}
|
||||
this.subscription = this._route.params.subscribe((params: Params) => {
|
||||
let searchText = params['searchText'];
|
||||
if (searchText && searchText != "") {
|
||||
this.searchText = searchText;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
ngOnDestroy() {
|
||||
if (this.subscription !== null) {
|
||||
this.subscription.unsubscribe()
|
||||
}
|
||||
}
|
||||
|
||||
onSearchChange(event: KeyboardEvent) {
|
||||
@ -87,19 +93,19 @@ export class GallerySearchComponent {
|
||||
this.autoCompleteItems = [];
|
||||
}
|
||||
|
||||
private autocomplete(searchText: string) {
|
||||
private async autocomplete(searchText: string) {
|
||||
if (!Config.Client.Search.autocompleteEnabled) {
|
||||
return
|
||||
}
|
||||
|
||||
if (searchText.trim().length > 0) {
|
||||
this._autoCompleteService.autoComplete(searchText).then((message: Message<Array<AutoCompleteItem>>) => {
|
||||
if (message.error) {
|
||||
//TODO: implement
|
||||
console.error(message.error);
|
||||
return;
|
||||
}
|
||||
this.showSuggestions(message.result, searchText);
|
||||
});
|
||||
try {
|
||||
const items = await this._autoCompleteService.autoComplete(searchText);
|
||||
this.showSuggestions(items, searchText);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
|
||||
} else {
|
||||
this.emptyAutoComplete();
|
||||
}
|
||||
|
74
frontend/app/gallery/share.service.ts
Normal file
74
frontend/app/gallery/share.service.ts
Normal file
@ -0,0 +1,74 @@
|
||||
import {Injectable} from "@angular/core";
|
||||
import {NetworkService} from "../model/network/network.service";
|
||||
import {CreateSharingDTO, SharingDTO} from "../../../common/entities/SharingDTO";
|
||||
import {Router, RoutesRecognized} from "@angular/router";
|
||||
|
||||
@Injectable()
|
||||
export class ShareService {
|
||||
|
||||
param = null;
|
||||
queryParam = null;
|
||||
sharingKey = null;
|
||||
inited = false;
|
||||
public ReadyPR: Promise<void>;
|
||||
private resolve;
|
||||
|
||||
|
||||
constructor(private _networkService: NetworkService, private router: Router) {
|
||||
|
||||
this.ReadyPR = new Promise((resolve) => {
|
||||
if (this.inited == true) {
|
||||
return resolve();
|
||||
}
|
||||
this.resolve = resolve;
|
||||
});
|
||||
|
||||
this.router.events.subscribe(val => {
|
||||
if (val instanceof RoutesRecognized) {
|
||||
this.param = val.state.root.firstChild.params["sharingKey"] || null;
|
||||
this.queryParam = val.state.root.firstChild.queryParams["sk"] || null;
|
||||
this.sharingKey = this.param || this.queryParam;
|
||||
if (this.resolve) {
|
||||
this.resolve();
|
||||
console.log("resolving", this.sharingKey);
|
||||
this.inited = true;
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
public wait(): Promise<void> {
|
||||
return this.ReadyPR;
|
||||
}
|
||||
|
||||
public getSharing(dir: string, includeSubfolders: boolean, valid: number): Promise<SharingDTO> {
|
||||
return this._networkService.postJson("/share/" + dir, {
|
||||
createSharing: <CreateSharingDTO>{
|
||||
includeSubfolders: includeSubfolders,
|
||||
valid: valid
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public updateSharing(dir: string, sharingId: number, includeSubfolders: boolean, valid: number): Promise<SharingDTO> {
|
||||
return this._networkService.putJson("/share/" + dir, {
|
||||
updateSharing: <CreateSharingDTO>{
|
||||
id: sharingId,
|
||||
includeSubfolders: includeSubfolders,
|
||||
valid: valid
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public getSharingKey() {
|
||||
return this.sharingKey;
|
||||
}
|
||||
|
||||
public isSharing(): boolean {
|
||||
return this.sharingKey != null;
|
||||
|
||||
}
|
||||
}
|
4
frontend/app/gallery/share/share.gallery.component.css
Normal file
4
frontend/app/gallery/share/share.gallery.component.css
Normal file
@ -0,0 +1,4 @@
|
||||
.modal {
|
||||
z-index: 9999;
|
||||
}
|
||||
|
73
frontend/app/gallery/share/share.gallery.component.html
Normal file
73
frontend/app/gallery/share/share.gallery.component.html
Normal file
@ -0,0 +1,73 @@
|
||||
<div class="col-sm-2 col-md-2 pull-right">
|
||||
<button id="shareButton" class="btn btn-default navbar-btn btn-link" type="button"
|
||||
data-toggle="modal" data-target="#shareModal" [disabled]="!enabled" (click)="get()">
|
||||
<span class="glyphicon glyphicon-share-alt"></span>
|
||||
Share
|
||||
</button>
|
||||
</div>
|
||||
<!-- sharing Modal-->
|
||||
<div class="modal fade" id="shareModal" tabindex="-1" role="dialog" aria-labelledby="shareModalLabel"
|
||||
data-backdrop="false"
|
||||
aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span
|
||||
aria-hidden="true">×</span></button>
|
||||
<h4 class="modal-title" id="shareModalLabel">Share</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="row">
|
||||
<div class="col-sm-10">
|
||||
<input id="shareLink" name="shareLink" placeholder="link" class="form-control input-md" type="text"
|
||||
[ngModel]="url">
|
||||
</div>
|
||||
<div class="col-sm-2 pull-right">
|
||||
<button id="copyButton" name="copyButton" data-clipboard-target="shareLink" class="btn btn-primary">Copy
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<hr/>
|
||||
<div class="form-horizontal">
|
||||
<div class="form-group" style="padding: 0 15px 0 15px;">
|
||||
|
||||
<div style="display: inline;">
|
||||
<label class="control-label">sharing:</label>
|
||||
<div class="form-control-static" id="sharingPath">{{currentDir}}</div>
|
||||
</div>
|
||||
|
||||
<label class="checkbox pull-right">
|
||||
<input id="recursiveShareBox" type="checkbox" (change)="update()" [(ngModel)]="input.includeSubfolders"
|
||||
checked="true" value="remember-me"> Include subfolders
|
||||
</label>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-4">
|
||||
Valid:
|
||||
<p id="sliderText"></p>
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<input id="shareSlider" data-slider-id='shareSlider' [(ngModel)]="input.valid.amount" (change)="update()"
|
||||
name="validAmount"
|
||||
type="number" min="0" step="1"/>
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<select class="form-control" [(ngModel)]="input.valid.type" (change)="update()" name="validType" required>
|
||||
<option *ngFor="let repository of validityTypes" [value]="repository.key">{{repository.value}}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-2 col-sm-push-10">
|
||||
<button id="updatebutton" name="updatebutton" class="btn btn-primary">Update</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
86
frontend/app/gallery/share/share.gallery.component.ts
Normal file
86
frontend/app/gallery/share/share.gallery.component.ts
Normal file
@ -0,0 +1,86 @@
|
||||
import {Component, OnDestroy, OnInit} from "@angular/core";
|
||||
import {Utils} from "../../../../common/Utils";
|
||||
import {ShareService} from "../share.service";
|
||||
import {GalleryService} from "../gallery.service";
|
||||
import {ContentWrapper} from "../../../../common/entities/ConentWrapper";
|
||||
import {SharingDTO} from "../../../../common/entities/SharingDTO";
|
||||
|
||||
@Component({
|
||||
selector: 'gallery-share',
|
||||
templateUrl: './share.gallery.component.html',
|
||||
styleUrls: ['./share.gallery.component.css'],
|
||||
})
|
||||
export class GalleryShareComponent implements OnInit, OnDestroy {
|
||||
|
||||
enabled: boolean = true;
|
||||
url: string = "";
|
||||
|
||||
input = {
|
||||
includeSubfolders: true,
|
||||
valid: {
|
||||
amount: 30,
|
||||
type: ValidityTypes.Days
|
||||
}
|
||||
};
|
||||
validityTypes = [];
|
||||
currentDir: string = "";
|
||||
sharing: SharingDTO;
|
||||
contentSubscription = null;
|
||||
|
||||
constructor(private _sharingService: ShareService, public _galleryService: GalleryService) {
|
||||
this.validityTypes = Utils.enumToArray(ValidityTypes);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
ngOnInit() {
|
||||
this.contentSubscription = this._galleryService.content.subscribe((content: ContentWrapper) => {
|
||||
this.enabled = !!content.directory;
|
||||
if (!this.enabled) {
|
||||
return;
|
||||
}
|
||||
this.currentDir = Utils.concatUrls(content.directory.path, content.directory.name);
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
if (this.contentSubscription !== null) {
|
||||
this.contentSubscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
calcValidity() {
|
||||
switch (parseInt(this.input.valid.type.toString())) {
|
||||
case ValidityTypes.Minutes:
|
||||
return this.input.valid.amount * 1000 * 60;
|
||||
case ValidityTypes.Hours:
|
||||
return this.input.valid.amount * 1000 * 60 * 60;
|
||||
case ValidityTypes.Days:
|
||||
return this.input.valid.amount * 1000 * 60 * 60 * 24;
|
||||
case ValidityTypes.Months:
|
||||
return this.input.valid.amount * 1000 * 60 * 60 * 24 * 30;
|
||||
}
|
||||
throw "unknown type: " + this.input.valid.type;
|
||||
}
|
||||
|
||||
async update() {
|
||||
this.url = "loading..";
|
||||
this.sharing = await this._sharingService.updateSharing(this.currentDir, this.sharing.id, this.input.includeSubfolders, this.calcValidity());
|
||||
console.log(this.sharing);
|
||||
this.url = location.origin + "/share/" + this.sharing.sharingKey
|
||||
}
|
||||
|
||||
async get() {
|
||||
this.url = "loading..";
|
||||
this.sharing = await this._sharingService.getSharing(this.currentDir, this.input.includeSubfolders, this.calcValidity());
|
||||
console.log(this.sharing);
|
||||
this.url = location.origin + "/share/" + this.sharing.sharingKey
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
export enum ValidityTypes{
|
||||
Minutes = 0, Hours = 1, Days = 2, Months = 3
|
||||
}
|
@ -2,8 +2,6 @@ import {Component, OnInit} from "@angular/core";
|
||||
import {LoginCredential} from "../../../common/entities/LoginCredential";
|
||||
import {AuthenticationService} from "../model/network/authentication.service";
|
||||
import {Router} from "@angular/router";
|
||||
import {Message} from "../../../common/entities/Message";
|
||||
import {UserDTO} from "../../../common/entities/UserDTO";
|
||||
import {ErrorCodes} from "../../../common/entities/Error";
|
||||
|
||||
@Component({
|
||||
@ -25,15 +23,17 @@ export class LoginComponent implements OnInit {
|
||||
}
|
||||
}
|
||||
|
||||
onLogin() {
|
||||
async onLogin() {
|
||||
this.loginError = null;
|
||||
this._authService.login(this.loginCredential).then((message: Message<UserDTO>) => {
|
||||
if (message.error) {
|
||||
if (message.error.code === ErrorCodes.CREDENTIAL_NOT_FOUND) {
|
||||
this.loginError = "Wrong username or password";
|
||||
}
|
||||
|
||||
try {
|
||||
await this._authService.login(this.loginCredential);
|
||||
|
||||
} catch (error) {
|
||||
if (error && error.code === ErrorCodes.CREDENTIAL_NOT_FOUND) {
|
||||
this.loginError = "Wrong username or password";
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,8 @@
|
||||
import {Injectable} from "@angular/core";
|
||||
import {UserDTO, UserRoles} from "../../../../common/entities/UserDTO";
|
||||
import {Event} from "../../../../common/event/Event";
|
||||
import {BehaviorSubject} from "rxjs/BehaviorSubject";
|
||||
import {UserService} from "./user.service";
|
||||
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 "../../../../common/config/public/Config";
|
||||
@ -15,49 +14,45 @@ declare module ServerInject {
|
||||
@Injectable()
|
||||
export class AuthenticationService {
|
||||
|
||||
private _user: UserDTO = null;
|
||||
public OnUserChanged: Event<UserDTO>;
|
||||
public user: BehaviorSubject<UserDTO>;
|
||||
|
||||
constructor(private _userService: UserService) {
|
||||
this.OnUserChanged = new Event();
|
||||
this.user = new BehaviorSubject(null);
|
||||
|
||||
//picking up session..
|
||||
if (this.isAuthenticated() == false && Cookie.get('pigallery2-session') != null) {
|
||||
if (typeof ServerInject !== "undefined" && typeof ServerInject.user !== "undefined") {
|
||||
this.setUser(ServerInject.user);
|
||||
this.user.next(ServerInject.user);
|
||||
}
|
||||
this.getSessionUser();
|
||||
} else {
|
||||
this.OnUserChanged.trigger(this._user);
|
||||
if (Config.Client.authenticationRequired === false) {
|
||||
this.user.next(<UserDTO>{name: "", password: "", role: UserRoles.Admin});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private getSessionUser() {
|
||||
this._userService.getSessionUser().then((message: Message<UserDTO>) => {
|
||||
if (message.error) {
|
||||
console.log(message.error);
|
||||
} else {
|
||||
this._user = message.result;
|
||||
this.OnUserChanged.trigger(this._user);
|
||||
}
|
||||
});
|
||||
private async getSessionUser(): Promise<void> {
|
||||
try {
|
||||
this.user.next(await this._userService.getSessionUser());
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private setUser(user: UserDTO) {
|
||||
this._user = user;
|
||||
this.OnUserChanged.trigger(this._user);
|
||||
}
|
||||
|
||||
public login(credential: LoginCredential) {
|
||||
return this._userService.login(credential).then((message: Message<UserDTO>) => {
|
||||
if (message.error) {
|
||||
console.log(ErrorCodes[message.error.code] + ", message: ", message.error.message);
|
||||
} else {
|
||||
this.setUser(message.result);
|
||||
public async login(credential: LoginCredential): Promise<UserDTO> {
|
||||
try {
|
||||
const user = await this._userService.login(credential);
|
||||
this.user.next(user);
|
||||
return user;
|
||||
} catch (error) {
|
||||
if (typeof error.code !== "undefined") {
|
||||
console.log(ErrorCodes[error.code] + ", message: ", error.message);
|
||||
}
|
||||
return message;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -65,19 +60,14 @@ export class AuthenticationService {
|
||||
if (Config.Client.authenticationRequired === false) {
|
||||
return true;
|
||||
}
|
||||
return !!(this._user && this._user != null);
|
||||
return !!(this.user.value && this.user.value != null);
|
||||
}
|
||||
|
||||
public getUser() {
|
||||
if (Config.Client.authenticationRequired === false) {
|
||||
return <UserDTO>{name: "", password: "", role: UserRoles.Admin};
|
||||
}
|
||||
return this._user;
|
||||
}
|
||||
|
||||
|
||||
public logout() {
|
||||
this._userService.logout();
|
||||
this.setUser(null);
|
||||
this.user.next(null);
|
||||
}
|
||||
|
||||
|
||||
|
@ -138,7 +138,7 @@ describe('NetworkService Fail tests', () => {
|
||||
|
||||
it('should call GET with error', inject([NetworkService], (networkService) => {
|
||||
|
||||
networkService.getJson(testUrl).then((res: Message<any>) => {
|
||||
networkService.getJson(testUrl).then((res: any) => {
|
||||
expect(res).toBe(null);
|
||||
}).catch((err) => {
|
||||
expect(err).toBe(testError);
|
||||
@ -148,7 +148,7 @@ describe('NetworkService Fail tests', () => {
|
||||
|
||||
it('should call POST with error', inject([NetworkService, MockBackend], (networkService) => {
|
||||
|
||||
networkService.postJson(testUrl, testData).then((res: Message<any>) => {
|
||||
networkService.postJson(testUrl, testData).then((res: any) => {
|
||||
expect(res).toBe(null);
|
||||
}).catch((err) => {
|
||||
expect(err).toBe(testError);
|
||||
@ -158,7 +158,7 @@ describe('NetworkService Fail tests', () => {
|
||||
|
||||
it('should call PUT with error', inject([NetworkService, MockBackend], (networkService) => {
|
||||
|
||||
networkService.putJson(testUrl, testData).then((res: Message<any>) => {
|
||||
networkService.putJson(testUrl, testData).then((res: any) => {
|
||||
expect(res).toBe(null);
|
||||
}).catch((err) => {
|
||||
expect(err).toBe(testError);
|
||||
@ -170,7 +170,7 @@ describe('NetworkService Fail tests', () => {
|
||||
|
||||
it('should call DELETE with error', inject([NetworkService, MockBackend], (networkService) => {
|
||||
|
||||
networkService.deleteJson(testUrl).then((res: Message<any>) => {
|
||||
networkService.deleteJson(testUrl).then((res: any) => {
|
||||
expect(res).toBe(null);
|
||||
}).catch((err) => {
|
||||
expect(err).toBe(testError);
|
||||
|
@ -3,6 +3,7 @@ import {Headers, Http, RequestOptions} from "@angular/http";
|
||||
import {Message} from "../../../../common/entities/Message";
|
||||
import {SlimLoadingBarService} from "ng2-slim-loading-bar";
|
||||
import "rxjs/Rx";
|
||||
import {ErrorCodes} from "../../../../common/entities/Error";
|
||||
|
||||
@Injectable()
|
||||
export class NetworkService {
|
||||
@ -22,9 +23,16 @@ export class NetworkService {
|
||||
this.slimLoadingBarService.visible = false;
|
||||
});
|
||||
|
||||
const process = (res: any) => {
|
||||
const process = (data: any): T => {
|
||||
this.slimLoadingBarService.complete();
|
||||
return <Message<any>> res.json();
|
||||
let res = <Message<any>> data.json();
|
||||
if (!!res.error) {
|
||||
if (res.error.code) {
|
||||
res.error['title'] = ErrorCodes[res.error.code];
|
||||
}
|
||||
throw res.error;
|
||||
}
|
||||
return res.result;
|
||||
};
|
||||
|
||||
const err = (err) => {
|
||||
@ -62,9 +70,9 @@ export class NetworkService {
|
||||
}
|
||||
|
||||
private static handleError(error: any) {
|
||||
// TODO: in a real world app do smthing better
|
||||
// TODO: in a real world app do something better
|
||||
// instead of just logging it to the console
|
||||
console.error(error);
|
||||
return Promise.reject(error.message || error.json().error || 'Server error');
|
||||
return Promise.reject(error.message || error || 'Server error');
|
||||
}
|
||||
}
|
||||
|
@ -2,26 +2,34 @@ import {Injectable} from "@angular/core";
|
||||
import {LoginCredential} from "../../../../common/entities/LoginCredential";
|
||||
import {NetworkService} from "./network.service";
|
||||
import {UserDTO} from "../../../../common/entities/UserDTO";
|
||||
import {Message} from "../../../../common/entities/Message";
|
||||
import {Config} from "../../../../common/config/public/Config";
|
||||
import {ShareService} from "../../gallery/share.service";
|
||||
|
||||
@Injectable()
|
||||
export class UserService {
|
||||
|
||||
|
||||
constructor(private _networkService: NetworkService) {
|
||||
constructor(private _networkService: NetworkService,
|
||||
private _shareService: ShareService) {
|
||||
}
|
||||
|
||||
public logout(): Promise<Message<string>> {
|
||||
public logout(): Promise<string> {
|
||||
console.log("call logout");
|
||||
return this._networkService.postJson("/user/logout");
|
||||
}
|
||||
|
||||
public login(credential: LoginCredential): Promise<Message<UserDTO>> {
|
||||
public login(credential: LoginCredential): Promise<UserDTO> {
|
||||
return this._networkService.postJson("/user/login", {"loginCredential": credential});
|
||||
}
|
||||
|
||||
public getSessionUser(): Promise<Message<UserDTO>> {
|
||||
return this._networkService.getJson("/user/login");
|
||||
public async getSessionUser(): Promise<UserDTO> {
|
||||
await this._shareService.wait();
|
||||
if (Config.Client.Sharing.enabled == true) {
|
||||
if (this._shareService.isSharing()) {
|
||||
return this._networkService.getJson<UserDTO>("/user/login?sk=" + this._shareService.getSharingKey());
|
||||
}
|
||||
}
|
||||
return this._networkService.getJson<UserDTO>("/user/login");
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ import {AuthenticationService} from "../../model/network/authentication.service"
|
||||
import {Router} from "@angular/router";
|
||||
import {UserDTO, UserRoles} from "../../../../common/entities/UserDTO";
|
||||
import {Utils} from "../../../../common/Utils";
|
||||
import {Message} from "../../../../common/entities/Message";
|
||||
import {UserManagerSettingsService} from "./usermanager.settings.service";
|
||||
|
||||
@Component({
|
||||
@ -22,23 +21,21 @@ export class UserMangerSettingsComponent implements OnInit {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
if (!this._authService.isAuthenticated() || this._authService.getUser().role < UserRoles.Admin) {
|
||||
if (!this._authService.isAuthenticated() || this._authService.user.value.role < UserRoles.Admin) {
|
||||
this._router.navigate(['login']);
|
||||
return;
|
||||
}
|
||||
this.userRoles = Utils.enumToArray(UserRoles).filter(r => r.key <= this._authService.getUser().role);
|
||||
this.userRoles = Utils.enumToArray(UserRoles).filter(r => r.key <= this._authService.user.value.role);
|
||||
this.getUsersList();
|
||||
}
|
||||
|
||||
private getUsersList() {
|
||||
this._userSettings.getUsers().then((result: Message<Array<UserDTO>>) => {
|
||||
this.users = result.result;
|
||||
});
|
||||
private async getUsersList() {
|
||||
this.users = await this._userSettings.getUsers();
|
||||
}
|
||||
|
||||
|
||||
canModifyUser(user: UserDTO): boolean {
|
||||
let currentUser = this._authService.getUser();
|
||||
let currentUser = this._authService.user.value;
|
||||
if (!currentUser) {
|
||||
return false;
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
import {Injectable} from "@angular/core";
|
||||
import {UserDTO} from "../../../../common/entities/UserDTO";
|
||||
import {NetworkService} from "../../model/network/network.service";
|
||||
import {Message} from "../../../../common/entities/Message";
|
||||
|
||||
@Injectable()
|
||||
export class UserManagerSettingsService {
|
||||
@ -10,21 +9,21 @@ export class UserManagerSettingsService {
|
||||
constructor(private _networkService: NetworkService) {
|
||||
}
|
||||
|
||||
public createUser(user: UserDTO): Promise<Message<string>> {
|
||||
public createUser(user: UserDTO): Promise<string> {
|
||||
return this._networkService.putJson("/user", {newUser: user});
|
||||
}
|
||||
|
||||
|
||||
public getUsers(): Promise<Message<Array<UserDTO>>> {
|
||||
public getUsers(): Promise<Array<UserDTO>> {
|
||||
return this._networkService.getJson("/user/list");
|
||||
}
|
||||
|
||||
|
||||
public deleteUser(user: UserDTO) {
|
||||
public deleteUser(user: UserDTO): Promise<void> {
|
||||
return this._networkService.deleteJson("/user/" + user.id);
|
||||
}
|
||||
|
||||
public updateRole(user: UserDTO) {
|
||||
public updateRole(user: UserDTO): Promise<void> {
|
||||
return this._networkService.postJson("/user/" + user.id + "/role", {newRole: user.role});
|
||||
}
|
||||
}
|
||||
|
3
frontend/app/sharelogin/share-login.component.css
Normal file
3
frontend/app/sharelogin/share-login.component.css
Normal file
@ -0,0 +1,3 @@
|
||||
body {
|
||||
background: #eee;
|
||||
}
|
15
frontend/app/sharelogin/share-login.component.html
Normal file
15
frontend/app/sharelogin/share-login.component.html
Normal file
@ -0,0 +1,15 @@
|
||||
<div class="container">
|
||||
<div class="col-sm-offset-3 col-sm-6 col-lg-4 col-lg-offset-4">
|
||||
<form class="form-signin" #LoginForm="ngForm">
|
||||
<h2 class="form-signin-heading">The link is password protected</h2>
|
||||
<div *ngIf="loginError">
|
||||
{{loginError}}
|
||||
</div>
|
||||
<input type="password" class="form-control" placeholder="Password"
|
||||
[(ngModel)]="loginCredential.password" name="password" required>
|
||||
<br/>
|
||||
<button class="btn btn-lg btn-primary btn-block" [disabled]="!LoginForm.form.valid" (click)="onLogin()">OK
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div> <!-- /container -->
|
39
frontend/app/sharelogin/share-login.component.ts
Normal file
39
frontend/app/sharelogin/share-login.component.ts
Normal file
@ -0,0 +1,39 @@
|
||||
import {Component, OnInit} from "@angular/core";
|
||||
import {LoginCredential} from "../../../common/entities/LoginCredential";
|
||||
import {AuthenticationService} from "../model/network/authentication.service";
|
||||
import {Router} from "@angular/router";
|
||||
import {ErrorCodes} from "../../../common/entities/Error";
|
||||
|
||||
@Component({
|
||||
selector: 'share-login',
|
||||
templateUrl: './share-login.component.html',
|
||||
styleUrls: ['./share-login.component.css'],
|
||||
})
|
||||
export class ShareLoginComponent implements OnInit {
|
||||
loginCredential: LoginCredential;
|
||||
loginError: any = null;
|
||||
|
||||
constructor(private _authService: AuthenticationService, private _router: Router) {
|
||||
this.loginCredential = new LoginCredential();
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
if (this._authService.isAuthenticated()) {
|
||||
this._router.navigate(['gallery', "/"]);
|
||||
}
|
||||
}
|
||||
|
||||
async onLogin() {
|
||||
this.loginError = null;
|
||||
|
||||
try {
|
||||
await this._authService.login(this.loginCredential);
|
||||
|
||||
} catch (error) {
|
||||
if (error && error.code === ErrorCodes.CREDENTIAL_NOT_FOUND) {
|
||||
this.loginError = "Wrong username or password";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
51
package.json
51
package.json
@ -6,6 +6,7 @@
|
||||
"homepage": "https://github.com/bpatrik/PiGallery2",
|
||||
"license": "MIT",
|
||||
"main": "./backend/server.js",
|
||||
"bin": "./backend/server.js",
|
||||
"scripts": {
|
||||
"build": "ng build",
|
||||
"pretest": "tsc",
|
||||
@ -23,50 +24,48 @@
|
||||
"url": "https://github.com/bpatrik/PiGallery2/issues"
|
||||
},
|
||||
"dependencies": {
|
||||
"@agm/core": "^1.0.0-beta.0",
|
||||
"@angular/common": "~4.2.4",
|
||||
"@angular/compiler": "~4.2.4",
|
||||
"@angular/core": "~4.2.4",
|
||||
"@angular/forms": "~4.2.4",
|
||||
"@angular/http": "~4.2.4",
|
||||
"@angular/platform-browser": "~4.2.4",
|
||||
"@angular/platform-browser-dynamic": "~4.2.4",
|
||||
"@angular/router": "~4.2.4",
|
||||
"body-parser": "^1.17.2",
|
||||
"core-js": "^2.4.1",
|
||||
"ejs": "^2.5.6",
|
||||
"exif-parser": "^0.1.9",
|
||||
"express": "^4.15.3",
|
||||
"express-session": "^1.15.3",
|
||||
"flat-file-db": "^1.0.0",
|
||||
"intl": "^1.2.5",
|
||||
"jimp": "^0.2.28",
|
||||
"mime": "^1.3.6",
|
||||
"mocha": "^3.4.2",
|
||||
"mysql": "^2.13.0",
|
||||
"ng2-cookies": "^1.0.12",
|
||||
"ng2-slim-loading-bar": "^4.0.0",
|
||||
"node-iptc": "^1.0.4",
|
||||
"reflect-metadata": "^0.1.10",
|
||||
"rxjs": "^5.4.1",
|
||||
"systemjs": "0.20.14",
|
||||
"threads": "^0.8.1",
|
||||
"typeconfig": "^1.0.1",
|
||||
"typeorm": "0.0.11",
|
||||
"winston": "^2.3.1",
|
||||
"zone.js": "^0.8.12"
|
||||
"winston": "^2.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular/cli": "1.1.3",
|
||||
"@angular/compiler-cli": "^4.2.4",
|
||||
"@angular/language-service": "^4.2.4",
|
||||
"@angular/cli": "1.2.0",
|
||||
"@angular/compiler-cli": "^4.2.5",
|
||||
"@angular/language-service": "^4.2.5",
|
||||
"@types/express": "^4.0.36",
|
||||
"@types/express-session": "1.15.0",
|
||||
"@types/gm": "^1.17.31",
|
||||
"@types/jasmine": "^2.5.53",
|
||||
"@types/node": "^8.0.4",
|
||||
"@types/node": "^8.0.7",
|
||||
"@types/sharp": "^0.17.2",
|
||||
"ng2-cookies": "^1.0.12",
|
||||
"ng2-slim-loading-bar": "^4.0.0",
|
||||
"intl": "^1.2.5",
|
||||
"core-js": "^2.4.1",
|
||||
"zone.js": "^0.8.12",
|
||||
"rxjs": "^5.4.1",
|
||||
"@types/winston": "^2.3.3",
|
||||
"@agm/core": "^1.0.0-beta.0",
|
||||
"@angular/common": "~4.2.5",
|
||||
"@angular/compiler": "~4.2.5",
|
||||
"@angular/core": "~4.2.5",
|
||||
"@angular/forms": "~4.2.5",
|
||||
"@angular/http": "~4.2.5",
|
||||
"@angular/platform-browser": "~4.2.5",
|
||||
"@angular/platform-browser-dynamic": "~4.2.5",
|
||||
"@angular/router": "~4.2.5",
|
||||
"chai": "^4.0.2",
|
||||
"codelyzer": "~3.1.1",
|
||||
"ejs-loader": "^0.3.0",
|
||||
@ -83,20 +82,20 @@
|
||||
"karma-phantomjs-launcher": "^1.0.4",
|
||||
"karma-remap-istanbul": "^0.6.0",
|
||||
"karma-systemjs": "^0.16.0",
|
||||
"merge2": "^1.0.3",
|
||||
"merge2": "^1.1.0",
|
||||
"mocha": "^3.4.2",
|
||||
"phantomjs-prebuilt": "^2.1.14",
|
||||
"protractor": "^5.1.2",
|
||||
"remap-istanbul": "^0.9.5",
|
||||
"rimraf": "^2.6.1",
|
||||
"run-sequence": "^1.2.2",
|
||||
"run-sequence": "^2.0.0",
|
||||
"ts-helpers": "^1.1.2",
|
||||
"ts-node": "~3.1.0",
|
||||
"tslint": "^5.4.3",
|
||||
"typescript": "^2.3.4"
|
||||
"typescript": "^2.4.1"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"gm": "^1.23.0",
|
||||
"sharp": "^0.18.1"
|
||||
"sharp": "^0.18.2"
|
||||
}
|
||||
}
|
||||
|
@ -9,222 +9,222 @@ import {Config} from "../../../../../common/config/private/Config";
|
||||
|
||||
describe('Authentication middleware', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
ObjectManagerRepository.reset();
|
||||
});
|
||||
|
||||
describe('authenticate', () => {
|
||||
it('should call next on authenticated', (done) => {
|
||||
let req: any = {
|
||||
session: {
|
||||
user: "A user"
|
||||
}
|
||||
};
|
||||
let next: any = (err) => {
|
||||
expect(err).to.be.undefined;
|
||||
done();
|
||||
};
|
||||
AuthenticationMWs.authenticate(req, null, next);
|
||||
|
||||
});
|
||||
|
||||
it('should call next with error on not authenticated', (done) => {
|
||||
let req: any = {
|
||||
session: {}
|
||||
};
|
||||
Config.Client.authenticationRequired = true;
|
||||
let res: any = {};
|
||||
let next: any = (err: Error) => {
|
||||
expect(err).not.to.be.undefined;
|
||||
expect(err.code).to.be.eql(ErrorCodes.NOT_AUTHENTICATED);
|
||||
done();
|
||||
};
|
||||
AuthenticationMWs.authenticate(req, null, next);
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
describe('inverseAuthenticate', () => {
|
||||
|
||||
it('should call next with error on authenticated', (done) => {
|
||||
let req: any = {
|
||||
session: {}
|
||||
};
|
||||
let res: any = {};
|
||||
let next: any = (err) => {
|
||||
expect(err).to.be.undefined;
|
||||
done();
|
||||
};
|
||||
AuthenticationMWs.inverseAuthenticate(req, null, next);
|
||||
|
||||
});
|
||||
|
||||
|
||||
it('should call next error on authenticated', (done) => {
|
||||
let req: any = {
|
||||
session: {
|
||||
user: "A user"
|
||||
}
|
||||
};
|
||||
let res: any = {};
|
||||
let next: any = (err: Error) => {
|
||||
expect(err).not.to.be.undefined;
|
||||
expect(err.code).to.be.eql(ErrorCodes.ALREADY_AUTHENTICATED);
|
||||
done();
|
||||
};
|
||||
AuthenticationMWs.inverseAuthenticate(req, null, next);
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
describe('authorise', () => {
|
||||
it('should call next on authorised', (done) => {
|
||||
let req: any = {
|
||||
session: {
|
||||
user: {
|
||||
role: UserRoles.Guest
|
||||
}
|
||||
}
|
||||
};
|
||||
let next: any = (err) => {
|
||||
expect(err).to.be.undefined;
|
||||
done();
|
||||
};
|
||||
AuthenticationMWs.authorise(UserRoles.Guest)(req, null, next);
|
||||
|
||||
});
|
||||
|
||||
it('should call next with error on not authorised', (done) => {
|
||||
let req: any = {
|
||||
session: {
|
||||
user: {
|
||||
role: UserRoles.Guest
|
||||
}
|
||||
}
|
||||
};
|
||||
let next: any = (err: Error) => {
|
||||
expect(err).not.to.be.undefined;
|
||||
expect(err.code).to.be.eql(ErrorCodes.NOT_AUTHORISED);
|
||||
done();
|
||||
};
|
||||
AuthenticationMWs.authorise(UserRoles.Developer)(req, null, next);
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
describe('login', () => {
|
||||
beforeEach(() => {
|
||||
ObjectManagerRepository.reset();
|
||||
ObjectManagerRepository.reset();
|
||||
});
|
||||
|
||||
describe('authenticate', () => {
|
||||
it('should call next on authenticated', (done) => {
|
||||
let req:any = {
|
||||
session: {
|
||||
user: "A user"
|
||||
}
|
||||
};
|
||||
let next:any = (err) => {
|
||||
expect(err).to.be.undefined;
|
||||
done();
|
||||
};
|
||||
AuthenticationMWs.authenticate(req, null, next);
|
||||
describe('should call next on missing...', () => {
|
||||
it('body', (done) => {
|
||||
let req: any = {};
|
||||
let next: any = (err) => {
|
||||
expect(err).to.be.undefined;
|
||||
done();
|
||||
};
|
||||
AuthenticationMWs.login(req, null, next);
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
it('should call next with error on not authenticated', (done) => {
|
||||
let req:any = {
|
||||
session: {}
|
||||
};
|
||||
Config.Client.authenticationRequired = true;
|
||||
let res:any = {};
|
||||
let next:any = (err:Error) => {
|
||||
expect(err).not.to.be.undefined;
|
||||
expect(err.code).to.be.eql(ErrorCodes.NOT_AUTHENTICATED);
|
||||
done();
|
||||
};
|
||||
AuthenticationMWs.authenticate(req, null, next);
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
describe('inverseAuthenticate', () => {
|
||||
|
||||
it('should call next with error on authenticated', (done) => {
|
||||
let req:any = {
|
||||
session: {}
|
||||
};
|
||||
let res:any = {};
|
||||
let next:any = (err) => {
|
||||
expect(err).to.be.undefined;
|
||||
done();
|
||||
};
|
||||
AuthenticationMWs.inverseAuthenticate(req, null, next);
|
||||
|
||||
});
|
||||
it('loginCredential', (done) => {
|
||||
let req: any = {
|
||||
body: {}
|
||||
};
|
||||
let next: any = (err) => {
|
||||
expect(err).to.be.undefined;
|
||||
done();
|
||||
};
|
||||
AuthenticationMWs.login(req, null, next);
|
||||
|
||||
|
||||
it('should call next error on authenticated', (done) => {
|
||||
let req:any = {
|
||||
session: {
|
||||
user: "A user"
|
||||
}
|
||||
};
|
||||
let res:any = {};
|
||||
let next:any = (err:Error) => {
|
||||
expect(err).not.to.be.undefined;
|
||||
expect(err.code).to.be.eql(ErrorCodes.ALREADY_AUTHENTICATED);
|
||||
done();
|
||||
};
|
||||
AuthenticationMWs.inverseAuthenticate(req, null, next);
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
describe('authorise', () => {
|
||||
it('should call next on authorised', (done) => {
|
||||
let req:any = {
|
||||
session: {
|
||||
user: {
|
||||
role: UserRoles.Guest
|
||||
}
|
||||
}
|
||||
};
|
||||
let next:any = (err) => {
|
||||
expect(err).to.be.undefined;
|
||||
done();
|
||||
};
|
||||
AuthenticationMWs.authorise(UserRoles.Guest)(req, null, next);
|
||||
|
||||
});
|
||||
|
||||
it('should call next with error on not authorised', (done) => {
|
||||
let req:any = {
|
||||
session: {
|
||||
user: {
|
||||
role: UserRoles.Guest
|
||||
}
|
||||
}
|
||||
};
|
||||
let next:any = (err:Error) => {
|
||||
expect(err).not.to.be.undefined;
|
||||
expect(err.code).to.be.eql(ErrorCodes.NOT_AUTHORISED);
|
||||
done();
|
||||
};
|
||||
AuthenticationMWs.authorise(UserRoles.Developer)(req, null, next);
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
describe('login', () => {
|
||||
beforeEach(() => {
|
||||
ObjectManagerRepository.reset();
|
||||
});
|
||||
|
||||
describe('should call next on missing...', () => {
|
||||
it('body', (done) => {
|
||||
let req:any = {};
|
||||
let next:any = (err) => {
|
||||
expect(err).to.be.undefined;
|
||||
done();
|
||||
};
|
||||
AuthenticationMWs.login(req, null, next);
|
||||
|
||||
});
|
||||
|
||||
it('loginCredential', (done) => {
|
||||
let req:any = {
|
||||
body: {}
|
||||
};
|
||||
let next:any = (err) => {
|
||||
expect(err).to.be.undefined;
|
||||
done();
|
||||
};
|
||||
AuthenticationMWs.login(req, null, next);
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
it('loginCredential content', (done) => {
|
||||
let req: any = {
|
||||
body: {loginCredential: {}}
|
||||
};
|
||||
let next: any = (err) => {
|
||||
expect(err).to.be.undefined;
|
||||
done();
|
||||
};
|
||||
AuthenticationMWs.login(req, null, next);
|
||||
|
||||
|
||||
it('loginCredential content', (done) => {
|
||||
let req:any = {
|
||||
body: {loginCredential: {}}
|
||||
};
|
||||
let next:any = (err) => {
|
||||
expect(err).to.be.undefined;
|
||||
done();
|
||||
};
|
||||
AuthenticationMWs.login(req, null, next);
|
||||
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
it('should call next with error on not finding user', (done) => {
|
||||
let req:any = {
|
||||
body: {
|
||||
loginCredential: {
|
||||
username: "aa",
|
||||
password: "bb"
|
||||
}
|
||||
}
|
||||
};
|
||||
let next:any = (err:Error) => {
|
||||
expect(err).not.to.be.undefined;
|
||||
expect(err.code).to.be.eql(ErrorCodes.CREDENTIAL_NOT_FOUND);
|
||||
done();
|
||||
};
|
||||
ObjectManagerRepository.getInstance().setUserManager(<UserManager>{
|
||||
findOne: (filter, cb) => {
|
||||
cb(null, null);
|
||||
}
|
||||
});
|
||||
AuthenticationMWs.login(req, null, next);
|
||||
|
||||
|
||||
});
|
||||
|
||||
it('should call next with user on the session on finding user', (done) => {
|
||||
let req:any = {
|
||||
session: {},
|
||||
body: {
|
||||
loginCredential: {
|
||||
username: "aa",
|
||||
password: "bb"
|
||||
}
|
||||
}
|
||||
};
|
||||
let next:any = (err:Error) => {
|
||||
expect(err).to.be.undefined;
|
||||
expect(req.session.user).to.be.eql("test user");
|
||||
done();
|
||||
};
|
||||
ObjectManagerRepository.getInstance().setUserManager(<UserManager>{
|
||||
findOne: (filter, cb:any) => {
|
||||
cb(null, "test user");
|
||||
}
|
||||
});
|
||||
AuthenticationMWs.login(req, null, next);
|
||||
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('logout', () => {
|
||||
it('should call next on logout', (done) => {
|
||||
let req:any = {
|
||||
session: {
|
||||
user: {
|
||||
role: UserRoles.Guest
|
||||
}
|
||||
}
|
||||
};
|
||||
let next:any = (err) => {
|
||||
expect(err).to.be.undefined;
|
||||
expect(req.session.user).to.be.undefined;
|
||||
done();
|
||||
};
|
||||
AuthenticationMWs.logout(req, null, next);
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
it('should call next with error on not finding user', (done) => {
|
||||
let req: any = {
|
||||
body: {
|
||||
loginCredential: {
|
||||
username: "aa",
|
||||
password: "bb"
|
||||
}
|
||||
}
|
||||
};
|
||||
let next: any = (err: Error) => {
|
||||
expect(err).not.to.be.undefined;
|
||||
expect(err.code).to.be.eql(ErrorCodes.CREDENTIAL_NOT_FOUND);
|
||||
done();
|
||||
};
|
||||
ObjectManagerRepository.getInstance().UserManager = <UserManager>{
|
||||
findOne: (filter, cb) => {
|
||||
cb(null, null);
|
||||
}
|
||||
};
|
||||
AuthenticationMWs.login(req, null, next);
|
||||
|
||||
|
||||
});
|
||||
|
||||
it('should call next with user on the session on finding user', (done) => {
|
||||
let req: any = {
|
||||
session: {},
|
||||
body: {
|
||||
loginCredential: {
|
||||
username: "aa",
|
||||
password: "bb"
|
||||
}
|
||||
}
|
||||
};
|
||||
let next: any = (err: Error) => {
|
||||
expect(err).to.be.undefined;
|
||||
expect(req.session.user).to.be.eql("test user");
|
||||
done();
|
||||
};
|
||||
ObjectManagerRepository.getInstance().UserManager = <UserManager>{
|
||||
findOne: (filter, cb: any) => {
|
||||
cb(null, "test user");
|
||||
}
|
||||
};
|
||||
AuthenticationMWs.login(req, null, next);
|
||||
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('logout', () => {
|
||||
it('should call next on logout', (done) => {
|
||||
let req: any = {
|
||||
session: {
|
||||
user: {
|
||||
role: UserRoles.Guest
|
||||
}
|
||||
}
|
||||
};
|
||||
let next: any = (err) => {
|
||||
expect(err).to.be.undefined;
|
||||
expect(req.session.user).to.be.undefined;
|
||||
done();
|
||||
};
|
||||
AuthenticationMWs.logout(req, null, next);
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -1,6 +1,7 @@
|
||||
{
|
||||
"compileOnSave": true,
|
||||
"compilerOptions": {
|
||||
"skipLibCheck": true,
|
||||
"sourceMap": true,
|
||||
"declaration": false,
|
||||
"moduleResolution": "node",
|
||||
|
Loading…
Reference in New Issue
Block a user