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 {Message} from "../../common/entities/Message";
import {SharingDTO} from "../../common/entities/SharingDTO";
import {Config} from "../../common/config/private/Config";
import {PrivateConfigClass} from "../../common/config/private/PrivateConfigClass";
export class RenderingMWs {
@ -45,6 +47,13 @@ export class RenderingMWs {
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 {
if (err instanceof Error) {
let message = new Message<any>(err, null);

View File

@ -96,8 +96,8 @@ export class UserMWs {
for (let i = 0; i < result.length; i++) {
result[i].password = "";
}
req.resultPipe = result;
next();
} catch (err) {
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 {Config} from "../../../common/config/private/Config";
import {SharingEntity} from "./enitites/SharingEntity";
import {DataBaseConfig} from "../../../common/config/private/IPrivateConfig";
export class MySQLConnection {
@ -21,6 +22,7 @@ export class MySQLConnection {
if (this.connection == null) {
this.connection = await createConnection({
name: "main",
driver: {
type: "mysql",
host: Config.Server.database.mysql.host,
@ -49,23 +51,48 @@ export class MySQLConnection {
}
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));
public static async tryConnection(config: DataBaseConfig) {
const conn = await createConnection({
name: "test",
driver: {
type: "mysql",
host: config.mysql.host,
port: 3306,
username: config.mysql.username,
password: config.mysql.password,
database: config.mysql.database
},
entities: [
UserEntity,
DirectoryEntity,
PhotoMetadataEntity,
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 {UserRoles} from "../../common/entities/UserDTO";
import {RenderingMWs} from "../middlewares/RenderingMWs";
import {AdminMWs} from "../middlewares/AdminMWs";
export class AdminRouter {
public static route(app: any) {
this.addResetDB(app);
this.addIndexGallery(app);
this.addSettings(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 {
let keys = Object.keys(filter);
const keys = Object.keys(filter);
for (let i = 0; i < keys.length; i++) {
let key = keys[i];
if (object[key] !== filter[key]) {
const key = keys[i];
if (typeof filter[key] === "object") {
if (Utils.equalsFilter(object[key], filter[key]) == false) {
return false;
}
} else if (object[key] !== filter[key]) {
return false;
}
}
@ -19,7 +23,8 @@ export class Utils {
}
static concatUrls(...args: Array<string>) {
static
concatUrls(...args: Array<string>) {
let url = "";
for (let i = 0; i < args.length; i++) {
if (args[i] === "" || typeof args[i] === "undefined") continue;
@ -34,7 +39,8 @@ export class Utils {
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) => {
if (typeof targetObject[key] === "undefined") {
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) => {
if (typeof targetObject[key] === "object") {
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) => {
if (typeof sourceObject[key] === "object") {
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; }> = [];
for (let enumMember in EnumType) {
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 diff = Math.abs(number - curr);

View File

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

View File

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

View File

@ -1,11 +1,12 @@
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
*/
export class PrivateConfigClass extends PublicConfigClass {
export class PrivateConfigClass extends PublicConfigClass implements IPrivateConfig {
public Server: ServerConfig = {
port: 80,
@ -30,6 +31,7 @@ export class PrivateConfigClass extends PublicConfigClass {
},
enableThreading: true
};
private ConfigLoader: any;
public setDatabaseType(type: DatabaseType) {
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;
}
interface ClientConfig {
export interface ClientConfig {
applicationTitle: string;
iconSize: number;
thumbnailSizes: Array<number>;

View File

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

View File

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

View File

@ -16,7 +16,8 @@ export class AdminComponent implements OnInit {
}
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']);
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 {UserDTO} from "../../common/entities/UserDTO";
import {Router} from "@angular/router";
import {Config} from "../../common/config/public/Config";
import {Title} from "@angular/platform-browser";
import {NotificationService} from "./model/notification.service";
@Component({
@ -13,7 +14,11 @@ import {Title} from "@angular/platform-browser";
})
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() {

View File

@ -36,7 +36,11 @@ 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";
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()
export class GoogleMapsConfig {
@ -52,18 +56,19 @@ export class GoogleMapsConfig {
BrowserModule,
FormsModule,
HttpModule,
BrowserAnimationsModule,
appRoutes,
TypeaheadModule.forRoot(),
ToastModule.forRoot(),
ModalModule.forRoot(),
AgmCoreModule.forRoot(),
SlimLoadingBarModule.forRoot()
],
declarations: [AppComponent,
LoginComponent,
ShareLoginComponent,
AdminComponent,
GalleryComponent,
FrameComponent,
UserMangerSettingsComponent,
//Gallery
GalleryLightboxPhotoComponent,
GalleryPhotoLoadingComponent,
GalleryGridComponent,
@ -76,7 +81,10 @@ export class GoogleMapsConfig {
GalleryShareComponent,
GalleryNavigatorComponent,
GalleryPhotoComponent,
FrameComponent,
AdminComponent,
//Settings
UserMangerSettingsComponent,
DatabaseSettingsComponent,
StringifyRole],
providers: [
{provide: LAZY_MAPS_API_CONFIG, useClass: GoogleMapsConfig},
@ -88,6 +96,7 @@ export class GoogleMapsConfig {
AuthenticationService,
ThumbnailLoaderService,
ThumbnailManagerService,
NotificationService,
FullScreenService,
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-heading">
<h3 class="panel-title">User management</h3>
</div>
<div class="panel-body">
<div class="panel-heading">
<h3 class="panel-title">User management</h3>
</div>
<div class="panel-body">
<table class="table table-hover">
<thead>
<tr>
<th>Name</th>
<th>Role</th>
<th></th>
</tr>
</thead>
<tr *ngFor="let user of users">
<td>{{user.name}}</td>
<td *ngIf="canModifyUser(user)">
<select class="form-control" [(ngModel)]="user.role" (change)="updateRole(user)" required>
<option *ngFor="let repository of userRoles" [value]="repository.key">
{{repository.value}}
</option>
</select>
</td>
<td *ngIf="!canModifyUser(user)">
{{user.role | stringifyRole}}
</td>
<td>
<button *ngIf="canModifyUser(user)" (click)="deleteUser(user)"
class="btn btn-danger pull-right">
<span class="glyphicon glyphicon-trash" aria-hidden="true" aria-label="Delete"></span>
</button>
</td>
</tr>
</table>
<table class="table table-hover">
<thead>
<tr>
<th>Name</th>
<th>Role</th>
<th></th>
</tr>
</thead>
<tr *ngFor="let user of users">
<td>{{user.name}}</td>
<td *ngIf="canModifyUser(user)">
<select class="form-control" [(ngModel)]="user.role" (change)="updateRole(user)" required>
<option *ngFor="let repository of userRoles" [value]="repository.key">
{{repository.value}}
</option>
</select>
</td>
<td *ngIf="!canModifyUser(user)">
{{user.role | stringifyRole}}
</td>
<td>
<button [disabled]="!canModifyUser(user)" (click)="deleteUser(user)"
[ngClass]="canModifyUser(user)? 'btn-danger':'btn-default'"
class="btn pull-right">
<span class="glyphicon glyphicon-trash" aria-hidden="true" aria-label="Delete"></span>
</button>
</td>
</tr>
</table>
<button class="btn btn-default pull-right" data-toggle="modal" data-target="#myModal"
(click)="initNewUser()">+ Add
user
</button>
</div>
<button class="btn btn-primary pull-right"
(click)="initNewUser()">+ Add
user
</button>
</div>
</div>
<!-- Modal -->
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span
aria-hidden="true">&times;</span></button>
<h4 class="modal-title" id="myModalLabel">Modal title</h4>
</div>
<div bsModal #userModal="bs-modal" class="modal fade" id="userModal" tabindex="-1" role="dialog"
aria-labelledby="userModalLabel">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" (click)="userModal.hide()" aria-label="Close"><span
aria-hidden="true">&times;</span></button>
<h4 class="modal-title" id="userModalLabel">Add new User</h4>
</div>
<form #NewUserForm="ngForm">
<div class="modal-body">
<input type="text" class="form-control" placeholder="Username" autofocus
[(ngModel)]="newUser.name" name="name" required>
<input type="password" class="form-control" placeholder="Password"
[(ngModel)]="newUser.password" name="password" required>
<select class="form-control" [(ngModel)]="newUser.role" name="role" required>
<option *ngFor="let repository of userRoles" [value]="repository.key">{{repository.value}}
</option>
</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>
<form #NewUserForm="ngForm">
<div class="modal-body">
<input type="text" class="form-control" placeholder="Username" autofocus
[(ngModel)]="newUser.name" name="name" required>
<input type="password" class="form-control" placeholder="Password"
[(ngModel)]="newUser.password" name="password" required>
<select class="form-control" [(ngModel)]="newUser.role" name="role" required>
<option *ngFor="let repository of userRoles" [value]="repository.key">{{repository.value}}
</option>
</select>
</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>

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 {Router} from "@angular/router";
import {UserDTO, UserRoles} from "../../../../common/entities/UserDTO";
import {Utils} from "../../../../common/Utils";
import {UserManagerSettingsService} from "./usermanager.settings.service";
import {ModalDirective} from "ngx-bootstrap/modal";
@Component({
selector: 'settings-usermanager',
@ -12,7 +13,7 @@ import {UserManagerSettingsService} from "./usermanager.settings.service";
providers: [UserManagerSettingsService],
})
export class UserMangerSettingsComponent implements OnInit {
@ViewChild('userModal') public childModal: ModalDirective;
public newUser = <UserDTO>{};
public userRoles: Array<any> = [];
public users: Array<UserDTO> = [];
@ -25,7 +26,11 @@ export class UserMangerSettingsComponent implements OnInit {
this._router.navigate(['login']);
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();
}
@ -45,24 +50,25 @@ export class UserMangerSettingsComponent implements OnInit {
initNewUser() {
this.newUser = <UserDTO>{role: UserRoles.User};
this.childModal.show();
}
addNewUser() {
this._userSettings.createUser(this.newUser).then(() => {
this.getUsersList();
});
async addNewUser() {
await this._userSettings.createUser(this.newUser);
await this.getUsersList();
this.childModal.hide();
}
updateRole(user: UserDTO) {
this._userSettings.updateRole(user).then(() => {
this.getUsersList();
});
async updateRole(user: UserDTO) {
await this._userSettings.updateRole(user);
await this.getUsersList();
this.childModal.hide();
}
deleteUser(user: UserDTO) {
this._userSettings.deleteUser(user).then(() => {
this.getUsersList();
});
async deleteUser(user: UserDTO) {
await this._userSettings.deleteUser(user);
await this.getUsersList();
this.childModal.hide();
}
}

View File

@ -6,6 +6,7 @@
<title>PiGallery2</title>
<link rel="shortcut icon" href="assets/icon.png">
<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">
<script type="text/javascript" src="config_inject.js"></script>
</head>

View File

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

View File

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