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

implementing database settings

This commit is contained in:
Braun Patrik 2017-07-08 12:43:42 +02:00
parent a9b06912c6
commit 8b090603b6
27 changed files with 513 additions and 155 deletions

View File

@ -0,0 +1,63 @@
import {NextFunction, Request, Response} from "express";
import {Error, ErrorCodes} from "../../common/entities/Error";
import {ObjectManagerRepository} from "../model/ObjectManagerRepository";
import {Logger} from "../Logger";
import {MySQLConnection} from "../model/mysql/MySQLConnection";
import {DataBaseConfig, DatabaseType} from "../../common/config/private/IPrivateConfig";
import {Config} from "../../common/config/private/Config";
const LOG_TAG = "[AdminMWs]";
export class AdminMWs {
public static async updateDatabaseSettings(req: Request, res: Response, next: NextFunction) {
if ((typeof req.body === 'undefined') || (typeof req.body.databaseSettings === 'undefined')) {
return next(new Error(ErrorCodes.INPUT_ERROR, "databaseSettings is needed"));
}
const databaseSettings = <DataBaseConfig>req.body.databaseSettings;
try {
if (Config.Server.database.type == DatabaseType.mysql) {
await MySQLConnection.tryConnection(databaseSettings);
}
Config.Server.database = databaseSettings;
Config.save();
Logger.info(LOG_TAG, "new config:");
Logger.info(LOG_TAG, JSON.stringify(Config, null, '\t'));
ObjectManagerRepository.reset();
if (Config.Server.database.type == DatabaseType.mysql) {
await ObjectManagerRepository.InitMySQLManagers();
} else {
await ObjectManagerRepository.InitMemoryManagers();
}
return next();
} catch (err) {
Logger.warn(LOG_TAG, "Error saving database settings", err);
return next(new Error(ErrorCodes.SETTINGS_ERROR, err));
}
}
public static async testDatabaseSettings(req: Request, res: Response, next: NextFunction) {
if ((typeof req.body === 'undefined') || (typeof req.body.databaseSettings === 'undefined')) {
return next(new Error(ErrorCodes.INPUT_ERROR, "databaseSettings is needed"));
}
const databaseSettings = <DataBaseConfig>req.body.databaseSettings;
try {
if (Config.Server.database.type == DatabaseType.mysql) {
await MySQLConnection.tryConnection(databaseSettings);
}
return next();
} catch (err) {
Logger.warn(LOG_TAG, "Error saving database settings", err);
return next(new Error(ErrorCodes.SETTINGS_ERROR, err));
}
}
}

View File

@ -3,6 +3,8 @@ import {Error, ErrorCodes} from "../../common/entities/Error";
import {Utils} from "../../common/Utils"; import {Utils} from "../../common/Utils";
import {Message} from "../../common/entities/Message"; import {Message} from "../../common/entities/Message";
import {SharingDTO} from "../../common/entities/SharingDTO"; import {SharingDTO} from "../../common/entities/SharingDTO";
import {Config} from "../../common/config/private/Config";
import {PrivateConfigClass} from "../../common/config/private/PrivateConfigClass";
export class RenderingMWs { export class RenderingMWs {
@ -45,6 +47,13 @@ export class RenderingMWs {
res.json(message); res.json(message);
} }
public static renderConfig(req: Request, res: Response, next: NextFunction) {
let message = new Message<PrivateConfigClass>(null, Config);
res.json(message);
}
public static renderError(err: any, req: Request, res: Response, next: NextFunction): any { public static renderError(err: any, req: Request, res: Response, next: NextFunction): any {
if (err instanceof Error) { if (err instanceof Error) {
let message = new Message<any>(err, null); let message = new Message<any>(err, null);

View File

@ -96,8 +96,8 @@ export class UserMWs {
for (let i = 0; i < result.length; i++) { for (let i = 0; i < result.length; i++) {
result[i].password = ""; result[i].password = "";
} }
req.resultPipe = result; req.resultPipe = result;
next();
} catch (err) { } catch (err) {
return next(new Error(ErrorCodes.GENERAL_ERROR)); return next(new Error(ErrorCodes.GENERAL_ERROR));
} }

View File

@ -6,6 +6,7 @@ import {PhotoEntity, PhotoMetadataEntity} from "./enitites/PhotoEntity";
import {DirectoryEntity} from "./enitites/DirectoryEntity"; import {DirectoryEntity} from "./enitites/DirectoryEntity";
import {Config} from "../../../common/config/private/Config"; import {Config} from "../../../common/config/private/Config";
import {SharingEntity} from "./enitites/SharingEntity"; import {SharingEntity} from "./enitites/SharingEntity";
import {DataBaseConfig} from "../../../common/config/private/IPrivateConfig";
export class MySQLConnection { export class MySQLConnection {
@ -21,6 +22,7 @@ export class MySQLConnection {
if (this.connection == null) { if (this.connection == null) {
this.connection = await createConnection({ this.connection = await createConnection({
name: "main",
driver: { driver: {
type: "mysql", type: "mysql",
host: Config.Server.database.mysql.host, host: Config.Server.database.mysql.host,
@ -49,23 +51,48 @@ export class MySQLConnection {
} }
public static init(): Promise<void> { public static async tryConnection(config: DataBaseConfig) {
return new Promise<void>((resolve, reject) => { const conn = await createConnection({
this.getConnection().then(async connection => { name: "test",
driver: {
let userRepository = connection.getRepository(UserEntity); type: "mysql",
let admins = await userRepository.find({role: UserRoles.Admin}); host: config.mysql.host,
if (admins.length == 0) { port: 3306,
let a = new UserEntity(); username: config.mysql.username,
a.name = "admin"; password: config.mysql.password,
a.password = "admin"; database: config.mysql.database
a.role = UserRoles.Admin; },
await userRepository.persist(a); entities: [
} UserEntity,
DirectoryEntity,
resolve(); PhotoMetadataEntity,
}).catch(err => reject(err)); PhotoEntity,
SharingEntity
],
autoSchemaSync: true,
logging: {
logQueries: true,
logOnlyFailedQueries: true,
logFailedQueryError: true,
logSchemaCreation: true
}
}); });
await conn.close();
return true;
}
public static async init(): Promise<void> {
const connection = await this.getConnection();
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);
}
} }

View File

@ -1,11 +1,14 @@
import {AuthenticationMWs} from "../middlewares/user/AuthenticationMWs"; import {AuthenticationMWs} from "../middlewares/user/AuthenticationMWs";
import {UserRoles} from "../../common/entities/UserDTO"; import {UserRoles} from "../../common/entities/UserDTO";
import {RenderingMWs} from "../middlewares/RenderingMWs";
import {AdminMWs} from "../middlewares/AdminMWs";
export class AdminRouter { export class AdminRouter {
public static route(app: any) { public static route(app: any) {
this.addResetDB(app); this.addResetDB(app);
this.addIndexGallery(app); this.addIndexGallery(app);
this.addSettings(app);
} }
private static addResetDB(app) { private static addResetDB(app) {
@ -24,5 +27,28 @@ export class AdminRouter {
); );
}; };
private static addSettings(app) {
app.get("/api/settings",
AuthenticationMWs.authenticate,
AuthenticationMWs.authorise(UserRoles.Admin),
RenderingMWs.renderConfig
);
app.put("/api/settings/database",
AuthenticationMWs.authenticate,
AuthenticationMWs.authorise(UserRoles.Admin),
AdminMWs.updateDatabaseSettings,
RenderingMWs.renderOK
);
app.post("/api/settings/test/database",
AuthenticationMWs.authenticate,
AuthenticationMWs.authorise(UserRoles.Admin),
AdminMWs.testDatabaseSettings,
RenderingMWs.renderOK
);
};
} }

View File

@ -6,12 +6,16 @@ export class Utils {
} }
static equalsFilter(object: any, filter: any): boolean { static equalsFilter(object: any, filter: any): boolean {
const keys = Object.keys(filter);
let keys = Object.keys(filter);
for (let i = 0; i < keys.length; i++) { for (let i = 0; i < keys.length; i++) {
let key = keys[i]; const key = keys[i];
if (object[key] !== filter[key]) { if (typeof filter[key] === "object") {
if (Utils.equalsFilter(object[key], filter[key]) == false) {
return false;
}
} else if (object[key] !== filter[key]) {
return false; return false;
} }
} }
@ -19,7 +23,8 @@ export class Utils {
} }
static concatUrls(...args: Array<string>) { static
concatUrls(...args: Array<string>) {
let url = ""; let url = "";
for (let i = 0; i < args.length; i++) { for (let i = 0; i < args.length; i++) {
if (args[i] === "" || typeof args[i] === "undefined") continue; if (args[i] === "" || typeof args[i] === "undefined") continue;
@ -34,7 +39,8 @@ export class Utils {
return url.substring(0, url.length - 1); return url.substring(0, url.length - 1);
} }
public static updateKeys(targetObject: any, sourceObject: any) { public static
updateKeys(targetObject: any, sourceObject: any) {
Object.keys(sourceObject).forEach((key) => { Object.keys(sourceObject).forEach((key) => {
if (typeof targetObject[key] === "undefined") { if (typeof targetObject[key] === "undefined") {
return; return;
@ -47,7 +53,8 @@ export class Utils {
}); });
} }
public static setKeys(targetObject: any, sourceObject: any) { public static
setKeys(targetObject: any, sourceObject: any) {
Object.keys(sourceObject).forEach((key) => { Object.keys(sourceObject).forEach((key) => {
if (typeof targetObject[key] === "object") { if (typeof targetObject[key] === "object") {
Utils.setKeys(targetObject[key], sourceObject[key]); Utils.setKeys(targetObject[key], sourceObject[key]);
@ -57,7 +64,8 @@ export class Utils {
}); });
} }
public static setKeysForced(targetObject: any, sourceObject: any) { public static
setKeysForced(targetObject: any, sourceObject: any) {
Object.keys(sourceObject).forEach((key) => { Object.keys(sourceObject).forEach((key) => {
if (typeof sourceObject[key] === "object") { if (typeof sourceObject[key] === "object") {
if (typeof targetObject[key] === "undefined") { if (typeof targetObject[key] === "undefined") {
@ -70,7 +78,11 @@ export class Utils {
}); });
} }
public static enumToArray(EnumType: any): Array<{ key: number; value: string; }> { public static
enumToArray(EnumType: any): Array<{
key: number;
value: string;
}> {
let arr: Array<{ key: number; value: string; }> = []; let arr: Array<{ key: number; value: string; }> = [];
for (let enumMember in EnumType) { for (let enumMember in EnumType) {
if (!EnumType.hasOwnProperty(enumMember)) { if (!EnumType.hasOwnProperty(enumMember)) {
@ -85,7 +97,8 @@ export class Utils {
} }
public static findClosest(number: number, arr: Array<number>) { public static
findClosest(number: number, arr: Array<number>) {
let curr = arr[0]; let curr = arr[0];
let diff = Math.abs(number - curr); let diff = Math.abs(number - curr);

View File

@ -1,11 +1,5 @@
import * as path from "path";
import {PrivateConfigClass} from "./PrivateConfigClass"; import {PrivateConfigClass} from "./PrivateConfigClass";
import {ConfigLoader} from "typeconfig";
export let Config = new PrivateConfigClass(); export let Config = new PrivateConfigClass();
Config.load();
ConfigLoader.loadBackendConfig(Config,
path.join(__dirname, './../../../config.json'),
[["PORT", "Server-port"]]);

View File

@ -1,3 +1,4 @@
import {ClientConfig} from "../public/ConfigClass";
export enum DatabaseType{ export enum DatabaseType{
memory = 0, mysql = 1 memory = 0, mysql = 1
} }
@ -37,3 +38,7 @@ export interface ServerConfig {
enableThreading: boolean; enableThreading: boolean;
sharing: SharingConfig; sharing: SharingConfig;
} }
export interface IPrivateConfig {
Server: ServerConfig;
Client: ClientConfig;
}

View File

@ -1,11 +1,12 @@
import {PublicConfigClass} from "../public/ConfigClass"; import {PublicConfigClass} from "../public/ConfigClass";
import {DatabaseType, ServerConfig, ThumbnailProcessingLib} from "./IPrivateConfig"; import {DatabaseType, IPrivateConfig, ServerConfig, ThumbnailProcessingLib} from "./IPrivateConfig";
import * as path from "path";
import {ConfigLoader} from "typeconfig";
/** /**
* This configuration will be only at backend * This configuration will be only at backend
*/ */
export class PrivateConfigClass extends PublicConfigClass { export class PrivateConfigClass extends PublicConfigClass implements IPrivateConfig {
public Server: ServerConfig = { public Server: ServerConfig = {
port: 80, port: 80,
@ -30,6 +31,7 @@ export class PrivateConfigClass extends PublicConfigClass {
}, },
enableThreading: true enableThreading: true
}; };
private ConfigLoader: any;
public setDatabaseType(type: DatabaseType) { public setDatabaseType(type: DatabaseType) {
this.Server.database.type = type; this.Server.database.type = type;
@ -40,5 +42,15 @@ export class PrivateConfigClass extends PublicConfigClass {
} }
} }
public load() {
ConfigLoader.loadBackendConfig(this,
path.join(__dirname, './../../../config.json'),
[["PORT", "Server-port"]]);
}
public save() {
ConfigLoader.saveConfigFile(path.join(__dirname, './../../../config.json'), this);
}
} }

View File

@ -9,7 +9,7 @@ interface SharingConfig {
passwordProtected: boolean; passwordProtected: boolean;
} }
interface ClientConfig { export interface ClientConfig {
applicationTitle: string; applicationTitle: string;
iconSize: number; iconSize: number;
thumbnailSizes: Array<number>; thumbnailSizes: Array<number>;

View File

@ -15,8 +15,9 @@ export enum ErrorCodes{
USER_MANAGEMENT_DISABLED = 9, USER_MANAGEMENT_DISABLED = 9,
INPUT_ERROR = 10 INPUT_ERROR = 10,
SETTINGS_ERROR = 11
} }
export class Error { export class Error {

View File

@ -1,5 +1,6 @@
<app-frame> <app-frame>
<div body class="container"> <div body class="container">
<settings-usermanager *ngIf="userManagementEnable"></settings-usermanager> <settings-usermanager *ngIf="userManagementEnable"></settings-usermanager>
<settings-database></settings-database>
</div> </div>
</app-frame> </app-frame>

View File

@ -16,7 +16,8 @@ export class AdminComponent implements OnInit {
} }
ngOnInit() { ngOnInit() {
if (!this._authService.isAuthenticated() || this._authService.user.value.role < UserRoles.Admin) { if (!this._authService.isAuthenticated()
|| this._authService.user.value.role < UserRoles.Admin) {
this._router.navigate(['login']); this._router.navigate(['login']);
return; return;
} }

View File

@ -1,9 +1,10 @@
import {Component, OnInit} from "@angular/core"; import {Component, OnInit, ViewContainerRef} from "@angular/core";
import {AuthenticationService} from "./model/network/authentication.service"; import {AuthenticationService} from "./model/network/authentication.service";
import {UserDTO} from "../../common/entities/UserDTO"; import {UserDTO} from "../../common/entities/UserDTO";
import {Router} from "@angular/router"; import {Router} from "@angular/router";
import {Config} from "../../common/config/public/Config"; import {Config} from "../../common/config/public/Config";
import {Title} from "@angular/platform-browser"; import {Title} from "@angular/platform-browser";
import {NotificationService} from "./model/notification.service";
@Component({ @Component({
@ -13,7 +14,11 @@ import {Title} from "@angular/platform-browser";
}) })
export class AppComponent implements OnInit { export class AppComponent implements OnInit {
constructor(private _router: Router, private _authenticationService: AuthenticationService, private _title: Title) { constructor(private _router: Router,
private _authenticationService: AuthenticationService,
private _title: Title, vcr: ViewContainerRef,
notificatin: NotificationService) {
notificatin.setRootViewContainerRef(vcr);
} }
ngOnInit() { ngOnInit() {

View File

@ -36,7 +36,11 @@ import {SlimLoadingBarModule} from "ng2-slim-loading-bar";
import {GalleryShareComponent} from "./gallery/share/share.gallery.component"; import {GalleryShareComponent} from "./gallery/share/share.gallery.component";
import {ShareLoginComponent} from "./sharelogin/share-login.component"; import {ShareLoginComponent} from "./sharelogin/share-login.component";
import {ShareService} from "./gallery/share.service"; import {ShareService} from "./gallery/share.service";
import {TypeaheadModule} from "ngx-bootstrap"; import {ModalModule} from "ngx-bootstrap/modal";
import {DatabaseSettingsComponent} from "./settings/database/database.settings.component";
import {ToastModule} from "ng2-toastr/ng2-toastr";
import {BrowserAnimationsModule} from "@angular/platform-browser/animations";
import {NotificationService} from "./model/notification.service";
@Injectable() @Injectable()
export class GoogleMapsConfig { export class GoogleMapsConfig {
@ -52,18 +56,19 @@ export class GoogleMapsConfig {
BrowserModule, BrowserModule,
FormsModule, FormsModule,
HttpModule, HttpModule,
BrowserAnimationsModule,
appRoutes, appRoutes,
TypeaheadModule.forRoot(), ToastModule.forRoot(),
ModalModule.forRoot(),
AgmCoreModule.forRoot(), AgmCoreModule.forRoot(),
SlimLoadingBarModule.forRoot() SlimLoadingBarModule.forRoot()
], ],
declarations: [AppComponent, declarations: [AppComponent,
LoginComponent, LoginComponent,
ShareLoginComponent, ShareLoginComponent,
AdminComponent,
GalleryComponent, GalleryComponent,
FrameComponent, FrameComponent,
UserMangerSettingsComponent, //Gallery
GalleryLightboxPhotoComponent, GalleryLightboxPhotoComponent,
GalleryPhotoLoadingComponent, GalleryPhotoLoadingComponent,
GalleryGridComponent, GalleryGridComponent,
@ -76,7 +81,10 @@ export class GoogleMapsConfig {
GalleryShareComponent, GalleryShareComponent,
GalleryNavigatorComponent, GalleryNavigatorComponent,
GalleryPhotoComponent, GalleryPhotoComponent,
FrameComponent, AdminComponent,
//Settings
UserMangerSettingsComponent,
DatabaseSettingsComponent,
StringifyRole], StringifyRole],
providers: [ providers: [
{provide: LAZY_MAPS_API_CONFIG, useClass: GoogleMapsConfig}, {provide: LAZY_MAPS_API_CONFIG, useClass: GoogleMapsConfig},
@ -88,6 +96,7 @@ export class GoogleMapsConfig {
AuthenticationService, AuthenticationService,
ThumbnailLoaderService, ThumbnailLoaderService,
ThumbnailManagerService, ThumbnailManagerService,
NotificationService,
FullScreenService, FullScreenService,
OverlayService], OverlayService],

View File

@ -0,0 +1,40 @@
import {Injectable, ViewContainerRef} from "@angular/core";
import {ToastsManager} from "ng2-toastr/ng2-toastr";
@Injectable()
export class NotificationService {
options = {
positionClass: "toast-top-center",
animate: "flyLeft"
};
constructor(private _toastr: ToastsManager) {
}
setRootViewContainerRef(vcr: ViewContainerRef) {
this._toastr.setRootViewContainerRef(vcr);
}
success(text, title = null) {
this._toastr.success(text, title, this.options);
}
error(text, title?) {
this._toastr.error(text, title, this.options);
}
warning(text, title?) {
this._toastr.warning(text, title, this.options);
}
info(text, title = null) {
this._toastr.info(text, title, this.options);
}
get Toastr(): ToastsManager {
return this._toastr;
}
}

View File

@ -1,27 +0,0 @@
import {Injectable} from "@angular/core";
@Injectable()
export class NotificationService {
constructor() {
}
public showException(message: string) {
}
public showError(message: string) {
}
public showWarn(message: string) {
}
public showInfo(message: string) {
}
}

View File

@ -0,0 +1,11 @@
.title {
margin-left: -5px;
}
.btn {
margin-left: 10px;
}
.form-control {
margin: 5px 0;
}

View File

@ -0,0 +1,40 @@
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">Database settings</h3>
</div>
<div class="panel-body">
<div [hidden]="!error" class="alert alert-danger" role="alert"><strong>Error: </strong>{{error}}</div>
<form #settingsForm="ngForm">
<p class="title">Type:</p>
<select class="form-control" [(ngModel)]="settings.type" name="type" required>
<option *ngFor="let type of types" [value]="type.key">{{type.value}}
</option>
</select>
<ng-container *ngIf="settings.type == DatabaseType.mysql">
<p class="title">MySQL settings:</p>
<input type="text" class="form-control" placeholder="Host" autofocus
[(ngModel)]="settings.mysql.host" name="host" required>
<input type="text" class="form-control" placeholder="Database" autofocus
[(ngModel)]="settings.mysql.database" name="database" required>
<input type="text" class="form-control" placeholder="Username"
[(ngModel)]="settings.mysql.username" name="username">
<input type="password" class="form-control" placeholder="Password"
[(ngModel)]="settings.mysql.password" name="password">
</ng-container>
</form>
<button class="btn btn-primary pull-right"
*ngIf="tested==false"
[disabled]="!settingsForm.form.valid || !changed"
(click)="test()">Test
</button>
<button class="btn btn-success pull-right"
*ngIf="tested==true"
[disabled]="!settingsForm.form.valid || !changed"
(click)="save()">Save
</button>
<button class="btn btn-default pull-right"
(click)="reset()">Reset
</button>
</div>
</div>

View File

@ -0,0 +1,90 @@
import {Component, OnInit, ViewChild} from "@angular/core";
import {AuthenticationService} from "../../model/network/authentication.service";
import {Router} from "@angular/router";
import {UserRoles} from "../../../../common/entities/UserDTO";
import {DatabaseSettingsService} from "./database.settings.service";
import {DataBaseConfig, DatabaseType} from "../../../../common/config/private/IPrivateConfig";
import {Utils} from "../../../../common/Utils";
import {Error} from "../../../../common/entities/Error";
import {NotificationService} from "../../model/notification.service";
@Component({
selector: 'settings-database',
templateUrl: './database.settings.component.html',
styleUrls: ['./database.settings.component.css'],
providers: [DatabaseSettingsService],
})
export class DatabaseSettingsComponent implements OnInit {
@ViewChild('settingsForm') form;
public settings: DataBaseConfig = <DataBaseConfig> {
type: DatabaseType.memory,
mysql: {}
};
private original: DataBaseConfig;
public types: Array<any> = [];
public DatabaseType: any;
public tested = false;
public error: string = null;
public changed: boolean = false;
constructor(private _authService: AuthenticationService,
private _router: Router,
private _dbSettings: DatabaseSettingsService,
private notification: NotificationService) {
this.original = Utils.clone(this.settings);
}
ngOnInit() {
if (!this._authService.isAuthenticated() ||
this._authService.user.value.role < UserRoles.Admin) {
this._router.navigate(['login']);
return;
}
this.types = Utils
.enumToArray(DatabaseType);
this.DatabaseType = DatabaseType;
this.getSettings();
this.form.valueChanges.subscribe((data) => {
this.changed = !Utils.equalsFilter(this.settings, this.original);
this.tested = false;
});
}
private async getSettings() {
const s = await this._dbSettings.getSettings();
this.original = Utils.clone(s);
this.settings = s;
this.tested = false;
this.changed = false;
}
public reset() {
this.getSettings();
}
public async test() {
try {
await this._dbSettings.testSettings(this.settings);
this.tested = true;
} catch (err) {
if (err.message)
this.error = (<Error>err).message;
}
}
public async save() {
if (typeof this.settings.type == "undefined" || !this.tested) {
return;
}
await this._dbSettings.updateSettings(this.settings);
await this.getSettings();
this.notification.success('Database settings saved', "Success");
}
}

View File

@ -0,0 +1,23 @@
import {Injectable} from "@angular/core";
import {NetworkService} from "../../model/network/network.service";
import {DataBaseConfig, IPrivateConfig} from "../../../../common/config/private/IPrivateConfig";
@Injectable()
export class DatabaseSettingsService {
constructor(private _networkService: NetworkService) {
}
public async getSettings(): Promise<DataBaseConfig> {
return (await <Promise<IPrivateConfig>>this._networkService.getJson("/settings")).Server.database;
}
public updateSettings(settings): Promise<void> {
return this._networkService.putJson("/settings/database", {databaseSettings: settings});
}
public testSettings(settings): Promise<void> {
return this._networkService.postJson("/settings/test/database", {databaseSettings: settings});
}
}

View File

@ -0,0 +1,3 @@
.form-control {
margin: 5px 0;
}

View File

@ -1,74 +1,76 @@
<div class="panel panel-default"> <div class="panel panel-default">
<div class="panel-heading"> <div class="panel-heading">
<h3 class="panel-title">User management</h3> <h3 class="panel-title">User management</h3>
</div> </div>
<div class="panel-body"> <div class="panel-body">
<table class="table table-hover"> <table class="table table-hover">
<thead> <thead>
<tr> <tr>
<th>Name</th> <th>Name</th>
<th>Role</th> <th>Role</th>
<th></th> <th></th>
</tr> </tr>
</thead> </thead>
<tr *ngFor="let user of users"> <tr *ngFor="let user of users">
<td>{{user.name}}</td> <td>{{user.name}}</td>
<td *ngIf="canModifyUser(user)"> <td *ngIf="canModifyUser(user)">
<select class="form-control" [(ngModel)]="user.role" (change)="updateRole(user)" required> <select class="form-control" [(ngModel)]="user.role" (change)="updateRole(user)" required>
<option *ngFor="let repository of userRoles" [value]="repository.key"> <option *ngFor="let repository of userRoles" [value]="repository.key">
{{repository.value}} {{repository.value}}
</option> </option>
</select> </select>
</td> </td>
<td *ngIf="!canModifyUser(user)"> <td *ngIf="!canModifyUser(user)">
{{user.role | stringifyRole}} {{user.role | stringifyRole}}
</td> </td>
<td> <td>
<button *ngIf="canModifyUser(user)" (click)="deleteUser(user)" <button [disabled]="!canModifyUser(user)" (click)="deleteUser(user)"
class="btn btn-danger pull-right"> [ngClass]="canModifyUser(user)? 'btn-danger':'btn-default'"
<span class="glyphicon glyphicon-trash" aria-hidden="true" aria-label="Delete"></span> class="btn pull-right">
</button> <span class="glyphicon glyphicon-trash" aria-hidden="true" aria-label="Delete"></span>
</td> </button>
</tr> </td>
</table> </tr>
</table>
<button class="btn btn-default pull-right" data-toggle="modal" data-target="#myModal" <button class="btn btn-primary pull-right"
(click)="initNewUser()">+ Add (click)="initNewUser()">+ Add
user user
</button> </button>
</div> </div>
</div> </div>
<!-- Modal --> <!-- Modal -->
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel"> <div bsModal #userModal="bs-modal" class="modal fade" id="userModal" tabindex="-1" role="dialog"
<div class="modal-dialog" role="document"> aria-labelledby="userModalLabel">
<div class="modal-content"> <div class="modal-dialog" role="document">
<div class="modal-header"> <div class="modal-content">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span <div class="modal-header">
aria-hidden="true">&times;</span></button> <button type="button" class="close" (click)="userModal.hide()" aria-label="Close"><span
<h4 class="modal-title" id="myModalLabel">Modal title</h4> aria-hidden="true">&times;</span></button>
</div> <h4 class="modal-title" id="userModalLabel">Add new User</h4>
</div>
<form #NewUserForm="ngForm"> <form #NewUserForm="ngForm">
<div class="modal-body"> <div class="modal-body">
<input type="text" class="form-control" placeholder="Username" autofocus <input type="text" class="form-control" placeholder="Username" autofocus
[(ngModel)]="newUser.name" name="name" required> [(ngModel)]="newUser.name" name="name" required>
<input type="password" class="form-control" placeholder="Password" <input type="password" class="form-control" placeholder="Password"
[(ngModel)]="newUser.password" name="password" required> [(ngModel)]="newUser.password" name="password" required>
<select class="form-control" [(ngModel)]="newUser.role" name="role" required> <select class="form-control" [(ngModel)]="newUser.role" name="role" required>
<option *ngFor="let repository of userRoles" [value]="repository.key">{{repository.value}} <option *ngFor="let repository of userRoles" [value]="repository.key">{{repository.value}}
</option> </option>
</select> </select>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary" data-dismiss="modal"
(click)="addNewUser()"
[disabled]="!NewUserForm.form.valid">Add User
</button>
</div>
</form>
</div> </div>
<div class="modal-footer">
<button type="button" class="btn btn-default" (click)="userModal.hide()">Close</button>
<button type="button" class="btn btn-primary" data-dismiss="modal"
(click)="addNewUser()"
[disabled]="!NewUserForm.form.valid">Add User
</button>
</div>
</form>
</div> </div>
</div>
</div> </div>

View File

@ -1,9 +1,10 @@
import {Component, OnInit} from "@angular/core"; import {Component, OnInit, ViewChild} from "@angular/core";
import {AuthenticationService} from "../../model/network/authentication.service"; import {AuthenticationService} from "../../model/network/authentication.service";
import {Router} from "@angular/router"; import {Router} from "@angular/router";
import {UserDTO, UserRoles} from "../../../../common/entities/UserDTO"; import {UserDTO, UserRoles} from "../../../../common/entities/UserDTO";
import {Utils} from "../../../../common/Utils"; import {Utils} from "../../../../common/Utils";
import {UserManagerSettingsService} from "./usermanager.settings.service"; import {UserManagerSettingsService} from "./usermanager.settings.service";
import {ModalDirective} from "ngx-bootstrap/modal";
@Component({ @Component({
selector: 'settings-usermanager', selector: 'settings-usermanager',
@ -12,7 +13,7 @@ import {UserManagerSettingsService} from "./usermanager.settings.service";
providers: [UserManagerSettingsService], providers: [UserManagerSettingsService],
}) })
export class UserMangerSettingsComponent implements OnInit { export class UserMangerSettingsComponent implements OnInit {
@ViewChild('userModal') public childModal: ModalDirective;
public newUser = <UserDTO>{}; public newUser = <UserDTO>{};
public userRoles: Array<any> = []; public userRoles: Array<any> = [];
public users: Array<UserDTO> = []; public users: Array<UserDTO> = [];
@ -25,7 +26,11 @@ export class UserMangerSettingsComponent implements OnInit {
this._router.navigate(['login']); this._router.navigate(['login']);
return; return;
} }
this.userRoles = Utils.enumToArray(UserRoles).filter(r => r.key <= this._authService.user.value.role); this.userRoles = Utils
.enumToArray(UserRoles)
.filter(r => r.key <= this._authService.user.value.role)
.sort((a, b) => a.key - b.key);
this.getUsersList(); this.getUsersList();
} }
@ -45,24 +50,25 @@ export class UserMangerSettingsComponent implements OnInit {
initNewUser() { initNewUser() {
this.newUser = <UserDTO>{role: UserRoles.User}; this.newUser = <UserDTO>{role: UserRoles.User};
this.childModal.show();
} }
addNewUser() { async addNewUser() {
this._userSettings.createUser(this.newUser).then(() => { await this._userSettings.createUser(this.newUser);
this.getUsersList(); await this.getUsersList();
}); this.childModal.hide();
} }
updateRole(user: UserDTO) { async updateRole(user: UserDTO) {
this._userSettings.updateRole(user).then(() => { await this._userSettings.updateRole(user);
this.getUsersList(); await this.getUsersList();
}); this.childModal.hide();
} }
deleteUser(user: UserDTO) { async deleteUser(user: UserDTO) {
this._userSettings.deleteUser(user).then(() => { await this._userSettings.deleteUser(user);
this.getUsersList(); await this.getUsersList();
}); this.childModal.hide();
} }
} }

View File

@ -6,6 +6,7 @@
<title>PiGallery2</title> <title>PiGallery2</title>
<link rel="shortcut icon" href="assets/icon.png"> <link rel="shortcut icon" href="assets/icon.png">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<link href="../node_modules/ng2-toastr/bundles/ng2-toastr.min.css" rel="stylesheet"/>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"> <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<script type="text/javascript" src="config_inject.js"></script> <script type="text/javascript" src="config_inject.js"></script>
</head> </head>

View File

@ -6,3 +6,4 @@
.navbar-right { .navbar-right {
margin-right: 0; margin-right: 0;
} }

View File

@ -35,12 +35,13 @@
"mysql": "^2.13.0", "mysql": "^2.13.0",
"node-iptc": "^1.0.4", "node-iptc": "^1.0.4",
"reflect-metadata": "^0.1.10", "reflect-metadata": "^0.1.10",
"typeconfig": "^1.0.1", "typeconfig": "^1.0.3",
"typeorm": "0.0.11", "typeorm": "0.0.11",
"winston": "^2.3.1" "winston": "^2.3.1"
}, },
"devDependencies": { "devDependencies": {
"@agm/core": "^1.0.0-beta.0", "@agm/core": "^1.0.0-beta.0",
"@angular/animations": "^4.2.6",
"@angular/cli": "1.2.0", "@angular/cli": "1.2.0",
"@angular/common": "~4.2.5", "@angular/common": "~4.2.5",
"@angular/compiler": "~4.2.5", "@angular/compiler": "~4.2.5",
@ -82,6 +83,7 @@
"mocha": "^3.4.2", "mocha": "^3.4.2",
"ng2-cookies": "^1.0.12", "ng2-cookies": "^1.0.12",
"ng2-slim-loading-bar": "^4.0.0", "ng2-slim-loading-bar": "^4.0.0",
"ng2-toastr": "^4.1.2",
"ngx-bootstrap": "^1.7.1", "ngx-bootstrap": "^1.7.1",
"phantomjs-prebuilt": "^2.1.14", "phantomjs-prebuilt": "^2.1.14",
"protractor": "^5.1.2", "protractor": "^5.1.2",