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:
parent
a9b06912c6
commit
8b090603b6
63
backend/middlewares/AdminMWs.ts
Normal file
63
backend/middlewares/AdminMWs.ts
Normal 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
||||||
|
@ -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));
|
||||||
}
|
}
|
||||||
|
@ -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,10 +51,38 @@ 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: {
|
||||||
|
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 userRepository = connection.getRepository(UserEntity);
|
||||||
let admins = await userRepository.find({role: UserRoles.Admin});
|
let admins = await userRepository.find({role: UserRoles.Admin});
|
||||||
if (admins.length == 0) {
|
if (admins.length == 0) {
|
||||||
@ -63,9 +93,6 @@ export class MySQLConnection {
|
|||||||
await userRepository.persist(a);
|
await userRepository.persist(a);
|
||||||
}
|
}
|
||||||
|
|
||||||
resolve();
|
|
||||||
}).catch(err => reject(err));
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -6,20 +6,25 @@ 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;
|
return false;
|
||||||
}
|
}
|
||||||
|
} else if (object[key] !== filter[key]) {
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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);
|
||||||
|
@ -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"]]);
|
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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>;
|
||||||
|
@ -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 {
|
||||||
|
@ -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>
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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() {
|
||||||
|
@ -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],
|
||||||
|
|
||||||
|
40
frontend/app/model/notification.service.ts
Normal file
40
frontend/app/model/notification.service.ts
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
@ -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) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -0,0 +1,11 @@
|
|||||||
|
.title {
|
||||||
|
margin-left: -5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-control {
|
||||||
|
margin: 5px 0;
|
||||||
|
}
|
@ -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>
|
@ -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");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
23
frontend/app/settings/database/database.settings.service.ts
Normal file
23
frontend/app/settings/database/database.settings.service.ts
Normal 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});
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
.form-control {
|
||||||
|
margin: 5px 0;
|
||||||
|
}
|
@ -25,15 +25,16 @@
|
|||||||
{{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'"
|
||||||
|
class="btn pull-right">
|
||||||
<span class="glyphicon glyphicon-trash" aria-hidden="true" aria-label="Delete"></span>
|
<span class="glyphicon glyphicon-trash" aria-hidden="true" aria-label="Delete"></span>
|
||||||
</button>
|
</button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</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>
|
||||||
@ -41,13 +42,14 @@
|
|||||||
</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"
|
||||||
|
aria-labelledby="userModalLabel">
|
||||||
<div class="modal-dialog" role="document">
|
<div class="modal-dialog" role="document">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span
|
<button type="button" class="close" (click)="userModal.hide()" aria-label="Close"><span
|
||||||
aria-hidden="true">×</span></button>
|
aria-hidden="true">×</span></button>
|
||||||
<h4 class="modal-title" id="myModalLabel">Modal title</h4>
|
<h4 class="modal-title" id="userModalLabel">Add new User</h4>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form #NewUserForm="ngForm">
|
<form #NewUserForm="ngForm">
|
||||||
@ -62,7 +64,7 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
<button type="button" class="btn btn-default" (click)="userModal.hide()">Close</button>
|
||||||
<button type="button" class="btn btn-primary" data-dismiss="modal"
|
<button type="button" class="btn btn-primary" data-dismiss="modal"
|
||||||
(click)="addNewUser()"
|
(click)="addNewUser()"
|
||||||
[disabled]="!NewUserForm.form.valid">Add User
|
[disabled]="!NewUserForm.form.valid">Add User
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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>
|
||||||
|
@ -6,3 +6,4 @@
|
|||||||
.navbar-right {
|
.navbar-right {
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user