mirror of
https://github.com/bpatrik/pigallery2.git
synced 2025-01-02 03:37:54 +02:00
Add basic configuring options #753
This commit is contained in:
parent
4b215c1e57
commit
75d277040d
@ -9,6 +9,7 @@ import {SharingDTO} from '../../common/entities/SharingDTO';
|
|||||||
import {Utils} from '../../common/Utils';
|
import {Utils} from '../../common/Utils';
|
||||||
import {LoggerRouter} from '../routes/LoggerRouter';
|
import {LoggerRouter} from '../routes/LoggerRouter';
|
||||||
import {TAGS} from '../../common/config/public/ClientConfig';
|
import {TAGS} from '../../common/config/public/ClientConfig';
|
||||||
|
import {ExtensionConfigWrapper} from '../model/extension/ExtensionConfigWrapper';
|
||||||
|
|
||||||
const forcedDebug = process.env['NODE_ENV'] === 'debug';
|
const forcedDebug = process.env['NODE_ENV'] === 'debug';
|
||||||
|
|
||||||
@ -107,7 +108,7 @@ export class RenderingMWs {
|
|||||||
req: Request,
|
req: Request,
|
||||||
res: Response
|
res: Response
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const originalConf = await Config.original();
|
const originalConf = await ExtensionConfigWrapper.original();
|
||||||
// These are sensitive information, do not send to the client side
|
// These are sensitive information, do not send to the client side
|
||||||
originalConf.Server.sessionSecret = null;
|
originalConf.Server.sessionSecret = null;
|
||||||
const message = new Message<PrivateConfigClass>(
|
const message = new Message<PrivateConfigClass>(
|
||||||
|
@ -6,6 +6,7 @@ import {ConfigDiagnostics} from '../../model/diagnostics/ConfigDiagnostics';
|
|||||||
import {ConfigClassBuilder} from '../../../../node_modules/typeconfig/node';
|
import {ConfigClassBuilder} from '../../../../node_modules/typeconfig/node';
|
||||||
import {TAGS} from '../../../common/config/public/ClientConfig';
|
import {TAGS} from '../../../common/config/public/ClientConfig';
|
||||||
import {ObjectManagers} from '../../model/ObjectManagers';
|
import {ObjectManagers} from '../../model/ObjectManagers';
|
||||||
|
import {ExtensionConfigWrapper} from '../../model/extension/ExtensionConfigWrapper';
|
||||||
|
|
||||||
const LOG_TAG = '[SettingsMWs]';
|
const LOG_TAG = '[SettingsMWs]';
|
||||||
|
|
||||||
@ -28,7 +29,7 @@ export class SettingsMWs {
|
|||||||
try {
|
try {
|
||||||
let settings = req.body.settings; // Top level settings JSON
|
let settings = req.body.settings; // Top level settings JSON
|
||||||
const settingsPath: string = req.body.settingsPath; // Name of the top level settings
|
const settingsPath: string = req.body.settingsPath; // Name of the top level settings
|
||||||
const transformer = await Config.original();
|
const transformer = await ExtensionConfigWrapper.original();
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
transformer[settingsPath] = settings;
|
transformer[settingsPath] = settings;
|
||||||
@ -37,7 +38,7 @@ export class SettingsMWs {
|
|||||||
settings = ConfigClassBuilder.attachPrivateInterface(transformer[settingsPath]).toJSON({
|
settings = ConfigClassBuilder.attachPrivateInterface(transformer[settingsPath]).toJSON({
|
||||||
skipTags: {secret: true} as TAGS
|
skipTags: {secret: true} as TAGS
|
||||||
});
|
});
|
||||||
const original = await Config.original();
|
const original = await ExtensionConfigWrapper.original();
|
||||||
// only updating explicitly set config (not saving config set by the diagnostics)
|
// only updating explicitly set config (not saving config set by the diagnostics)
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
|
50
src/backend/model/extension/ExtensionConfigWrapper.ts
Normal file
50
src/backend/model/extension/ExtensionConfigWrapper.ts
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import {IConfigClass} from '../../../../node_modules/typeconfig/common';
|
||||||
|
import {Config, PrivateConfigClass} from '../../../common/config/private/Config';
|
||||||
|
import {ConfigClassBuilder} from '../../../../node_modules/typeconfig/node';
|
||||||
|
import {IExtensionConfig} from './IExtension';
|
||||||
|
import {Utils} from '../../../common/Utils';
|
||||||
|
import {ObjectManagers} from '../ObjectManagers';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wraps to original config and makes sure all extension related config is loaded
|
||||||
|
*/
|
||||||
|
export class ExtensionConfigWrapper {
|
||||||
|
static async original(): Promise<PrivateConfigClass & IConfigClass> {
|
||||||
|
const pc = ConfigClassBuilder.attachPrivateInterface(new PrivateConfigClass());
|
||||||
|
try {
|
||||||
|
await pc.load();
|
||||||
|
for (const ext of Object.values(ObjectManagers.getInstance().ExtensionManager.extObjects)) {
|
||||||
|
ext.config.loadToConfig(ConfigClassBuilder.attachPrivateInterface(pc));
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Error during loading original config. Reverting to defaults.');
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
return pc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ExtensionConfig<C> implements IExtensionConfig<C> {
|
||||||
|
public template: new() => C;
|
||||||
|
|
||||||
|
constructor(private readonly extensionId: string) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public getConfig(): C {
|
||||||
|
return Config.Extensions.configs[this.extensionId] as C;
|
||||||
|
}
|
||||||
|
|
||||||
|
public setTemplate(template: new() => C): void {
|
||||||
|
this.template = template;
|
||||||
|
this.loadToConfig(Config);
|
||||||
|
}
|
||||||
|
|
||||||
|
loadToConfig(config: PrivateConfigClass) {
|
||||||
|
if (!this.template) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const conf = ConfigClassBuilder.attachPrivateInterface(new this.template());
|
||||||
|
conf.__loadJSONObject(Utils.clone(config.Extensions.configs[this.extensionId] || {}));
|
||||||
|
config.Extensions.configs[this.extensionId] = conf;
|
||||||
|
}
|
||||||
|
}
|
@ -3,15 +3,13 @@ import {Config} from '../../../common/config/private/Config';
|
|||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import {IObjectManager} from '../database/IObjectManager';
|
import {IObjectManager} from '../database/IObjectManager';
|
||||||
import {createLoggerWrapper, Logger} from '../../Logger';
|
import {Logger} from '../../Logger';
|
||||||
import {IExtensionEvents, IExtensionObject, IServerExtension} from './IExtension';
|
import {IExtensionEvents, IExtensionObject, IServerExtension} from './IExtension';
|
||||||
import {Server} from '../../server';
|
import {Server} from '../../server';
|
||||||
import {ExtensionEvent} from './ExtensionEvent';
|
import {ExtensionEvent} from './ExtensionEvent';
|
||||||
import {ExpressRouterWrapper} from './ExpressRouterWrapper';
|
|
||||||
import * as express from 'express';
|
import * as express from 'express';
|
||||||
import {ExtensionApp} from './ExtensionApp';
|
|
||||||
import {ExtensionDB} from './ExtensionDB';
|
|
||||||
import {SQLConnection} from '../database/SQLConnection';
|
import {SQLConnection} from '../database/SQLConnection';
|
||||||
|
import {ExtensionObject} from './ExtensionObject';
|
||||||
|
|
||||||
const LOG_TAG = '[ExtensionManager]';
|
const LOG_TAG = '[ExtensionManager]';
|
||||||
|
|
||||||
@ -20,7 +18,7 @@ export class ExtensionManager implements IObjectManager {
|
|||||||
public static EXTENSION_API_PATH = Config.Server.apiPath + '/extension';
|
public static EXTENSION_API_PATH = Config.Server.apiPath + '/extension';
|
||||||
|
|
||||||
events: IExtensionEvents;
|
events: IExtensionEvents;
|
||||||
extObjects: { [key: string]: IExtensionObject } = {};
|
extObjects: { [key: string]: ExtensionObject<unknown> } = {};
|
||||||
router: express.Router;
|
router: express.Router;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
@ -65,15 +63,15 @@ export class ExtensionManager implements IObjectManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Config.Extensions.list = fs
|
Config.Extensions.list = fs
|
||||||
.readdirSync(ProjectPath.ExtensionFolder)
|
.readdirSync(ProjectPath.ExtensionFolder)
|
||||||
.filter((f): boolean =>
|
.filter((f): boolean =>
|
||||||
fs.statSync(path.join(ProjectPath.ExtensionFolder, f)).isDirectory()
|
fs.statSync(path.join(ProjectPath.ExtensionFolder, f)).isDirectory()
|
||||||
);
|
);
|
||||||
Config.Extensions.list.sort();
|
Config.Extensions.list.sort();
|
||||||
Logger.debug(LOG_TAG, 'Extensions found ', JSON.stringify(Config.Extensions.list));
|
Logger.debug(LOG_TAG, 'Extensions found ', JSON.stringify(Config.Extensions.list));
|
||||||
}
|
}
|
||||||
|
|
||||||
private async callServerFN(fn: (ext: IServerExtension, extName: string) => Promise<void>) {
|
private async callServerFN(fn: (ext: IServerExtension<unknown>, extName: string) => Promise<void>) {
|
||||||
for (let i = 0; i < Config.Extensions.list.length; ++i) {
|
for (let i = 0; i < Config.Extensions.list.length; ++i) {
|
||||||
const extName = Config.Extensions.list[i];
|
const extName = Config.Extensions.list[i];
|
||||||
const extPath = path.join(ProjectPath.ExtensionFolder, extName);
|
const extPath = path.join(ProjectPath.ExtensionFolder, extName);
|
||||||
@ -88,17 +86,9 @@ export class ExtensionManager implements IObjectManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private createExtensionObject(name: string): IExtensionObject {
|
private createExtensionObject(name: string): IExtensionObject<unknown> {
|
||||||
if (!this.extObjects[name]) {
|
if (!this.extObjects[name]) {
|
||||||
const logger = createLoggerWrapper(`[Extension][${name}]`);
|
this.extObjects[name] = new ExtensionObject(name, this.router, this.events);
|
||||||
this.extObjects[name] = {
|
|
||||||
_app: new ExtensionApp(),
|
|
||||||
db: new ExtensionDB(logger),
|
|
||||||
paths: ProjectPath,
|
|
||||||
Logger: logger,
|
|
||||||
events: this.events,
|
|
||||||
RESTApi: new ExpressRouterWrapper(this.router, name, logger)
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
return this.extObjects[name];
|
return this.extObjects[name];
|
||||||
}
|
}
|
||||||
|
31
src/backend/model/extension/ExtensionObject.ts
Normal file
31
src/backend/model/extension/ExtensionObject.ts
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import {IExtensionEvents, IExtensionObject} from './IExtension';
|
||||||
|
import {ExtensionApp} from './ExtensionApp';
|
||||||
|
import {ExtensionConfig} from './ExtensionConfigWrapper';
|
||||||
|
import {ExtensionDB} from './ExtensionDB';
|
||||||
|
import {ProjectPath} from '../../ProjectPath';
|
||||||
|
import {ExpressRouterWrapper} from './ExpressRouterWrapper';
|
||||||
|
import {createLoggerWrapper} from '../../Logger';
|
||||||
|
import * as express from 'express';
|
||||||
|
|
||||||
|
export class ExtensionObject<C> implements IExtensionObject<C> {
|
||||||
|
|
||||||
|
public readonly _app;
|
||||||
|
public readonly config;
|
||||||
|
public readonly db;
|
||||||
|
public readonly paths;
|
||||||
|
public readonly Logger;
|
||||||
|
public readonly events;
|
||||||
|
public readonly RESTApi;
|
||||||
|
|
||||||
|
constructor(public readonly extensionId: string, extensionRouter: express.Router, events: IExtensionEvents) {
|
||||||
|
const logger = createLoggerWrapper(`[Extension][${extensionId}]`);
|
||||||
|
this._app = new ExtensionApp();
|
||||||
|
this.config = new ExtensionConfig<C>(extensionId);
|
||||||
|
this.db = new ExtensionDB(logger);
|
||||||
|
this.paths = ProjectPath;
|
||||||
|
this.Logger = logger;
|
||||||
|
this.events = events;
|
||||||
|
this.RESTApi = new ExpressRouterWrapper(extensionRouter, extensionId, logger);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -6,7 +6,7 @@ import {ProjectPathClass} from '../../ProjectPath';
|
|||||||
import {ILogger} from '../../Logger';
|
import {ILogger} from '../../Logger';
|
||||||
import {UserDTO, UserRoles} from '../../../common/entities/UserDTO';
|
import {UserDTO, UserRoles} from '../../../common/entities/UserDTO';
|
||||||
import {ParamsDictionary} from 'express-serve-static-core';
|
import {ParamsDictionary} from 'express-serve-static-core';
|
||||||
import {Connection, EntitySchema} from 'typeorm';
|
import {Connection} from 'typeorm';
|
||||||
|
|
||||||
|
|
||||||
export type IExtensionBeforeEventHandler<I, O> = (input: { inputs: I }, event: { stopPropagation: boolean }) => Promise<{ inputs: I } | O>;
|
export type IExtensionBeforeEventHandler<I, O> = (input: { inputs: I }, event: { stopPropagation: boolean }) => Promise<{ inputs: I } | O>;
|
||||||
@ -66,17 +66,17 @@ export interface IExtensionApp {
|
|||||||
export interface IExtensionRESTRoute {
|
export interface IExtensionRESTRoute {
|
||||||
/**
|
/**
|
||||||
* Sends a pigallery2 standard JSON object with payload or error message back to the client.
|
* Sends a pigallery2 standard JSON object with payload or error message back to the client.
|
||||||
* @param paths
|
* @param paths RESTapi path, relative to the extension base endpoint
|
||||||
* @param minRole
|
* @param minRole set to null to omit auer check (ie make the endpoint public)
|
||||||
* @param cb
|
* @param cb function callback
|
||||||
*/
|
*/
|
||||||
jsonResponse(paths: string[], minRole: UserRoles, cb: (params?: ParamsDictionary, body?: any, user?: UserDTO) => Promise<unknown> | unknown): void;
|
jsonResponse(paths: string[], minRole: UserRoles, cb: (params?: ParamsDictionary, body?: any, user?: UserDTO) => Promise<unknown> | unknown): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exposes a standard expressjs middleware
|
* Exposes a standard expressjs middleware
|
||||||
* @param paths
|
* @param paths RESTapi path, relative to the extension base endpoint
|
||||||
* @param minRole
|
* @param minRole set to null to omit auer check (ie make the endpoint public)
|
||||||
* @param mw
|
* @param mw expressjs middleware
|
||||||
*/
|
*/
|
||||||
rawMiddleware(paths: string[], minRole: UserRoles, mw: (req: Request, res: Response, next: NextFunction) => void | Promise<void>): void;
|
rawMiddleware(paths: string[], minRole: UserRoles, mw: (req: Request, res: Response, next: NextFunction) => void | Promise<void>): void;
|
||||||
}
|
}
|
||||||
@ -110,13 +110,24 @@ export interface IExtensionDB {
|
|||||||
_getAllTables(): Function[];
|
_getAllTables(): Function[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IExtensionObject {
|
export interface IExtensionConfig<C> {
|
||||||
|
setTemplate(template: new() => C): void;
|
||||||
|
|
||||||
|
getConfig(): C;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IExtensionObject<C> {
|
||||||
/**
|
/**
|
||||||
* Inner functionality of the app. Use this with caution.
|
* Inner functionality of the app. Use this with caution.
|
||||||
* If you want to go deeper than the standard exposed APIs, you can try doing so here.
|
* If you want to go deeper than the standard exposed APIs, you can try doing so here.
|
||||||
*/
|
*/
|
||||||
_app: IExtensionApp;
|
_app: IExtensionApp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create extension related configuration
|
||||||
|
*/
|
||||||
|
config: IExtensionConfig<C>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create new SQL tables and access SQL connection
|
* Create new SQL tables and access SQL connection
|
||||||
*/
|
*/
|
||||||
@ -144,12 +155,12 @@ export interface IExtensionObject {
|
|||||||
/**
|
/**
|
||||||
* Extension interface. All extension is expected to implement and export these methods
|
* Extension interface. All extension is expected to implement and export these methods
|
||||||
*/
|
*/
|
||||||
export interface IServerExtension {
|
export interface IServerExtension<C> {
|
||||||
/**
|
/**
|
||||||
* Extension init function. Extension should at minimum expose this function.
|
* Extension init function. Extension should at minimum expose this function.
|
||||||
* @param extension
|
* @param extension
|
||||||
*/
|
*/
|
||||||
init(extension: IExtensionObject): Promise<void>;
|
init(extension: IExtensionObject<C>): Promise<void>;
|
||||||
|
|
||||||
cleanUp?: (extension: IExtensionObject) => Promise<void>;
|
cleanUp?: (extension: IExtensionObject<C>) => Promise<void>;
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ const upTime = new Date().toISOString();
|
|||||||
// TODO: Refactor Config to be injectable globally.
|
// TODO: Refactor Config to be injectable globally.
|
||||||
// This is a bad habit to let the Config know if its in a testing env.
|
// This is a bad habit to let the Config know if its in a testing env.
|
||||||
const isTesting = process.env['NODE_ENV'] == true || ['afterEach', 'after', 'beforeEach', 'before', 'describe', 'it']
|
const isTesting = process.env['NODE_ENV'] == true || ['afterEach', 'after', 'beforeEach', 'before', 'describe', 'it']
|
||||||
.every((fn) => (global as any)[fn] instanceof Function);
|
.every((fn) => (global as any)[fn] instanceof Function);
|
||||||
|
|
||||||
@ConfigClass<IConfigClass<TAGS> & ServerConfig>({
|
@ConfigClass<IConfigClass<TAGS> & ServerConfig>({
|
||||||
configPath: path.join(__dirname, !isTesting ? './../../../../config.json' : './../../../../test/backend/tmp/config.json'),
|
configPath: path.join(__dirname, !isTesting ? './../../../../config.json' : './../../../../test/backend/tmp/config.json'),
|
||||||
@ -76,30 +76,20 @@ export class PrivateConfigClass extends ServerConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.Environment.appVersion =
|
this.Environment.appVersion =
|
||||||
require('../../../../package.json').version;
|
require('../../../../package.json').version;
|
||||||
this.Environment.buildTime =
|
this.Environment.buildTime =
|
||||||
require('../../../../package.json').buildTime;
|
require('../../../../package.json').buildTime;
|
||||||
this.Environment.buildCommitHash =
|
this.Environment.buildCommitHash =
|
||||||
require('../../../../package.json').buildCommitHash;
|
require('../../../../package.json').buildCommitHash;
|
||||||
this.Environment.upTime = upTime;
|
this.Environment.upTime = upTime;
|
||||||
this.Environment.isDocker = !!process.env.PI_DOCKER;
|
this.Environment.isDocker = !!process.env.PI_DOCKER;
|
||||||
}
|
}
|
||||||
|
|
||||||
async original(): Promise<PrivateConfigClass & IConfigClass> {
|
|
||||||
const pc = ConfigClassBuilder.attachPrivateInterface(new PrivateConfigClass());
|
|
||||||
try {
|
|
||||||
await pc.load();
|
|
||||||
} catch (e) {
|
|
||||||
console.error('Error during loading original config. Reverting to defaults.');
|
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
return pc;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Config = ConfigClassBuilder.attachInterface(
|
export const Config = ConfigClassBuilder.attachInterface(
|
||||||
new PrivateConfigClass()
|
new PrivateConfigClass()
|
||||||
);
|
);
|
||||||
try {
|
try {
|
||||||
Config.loadSync();
|
Config.loadSync();
|
||||||
|
@ -1014,12 +1014,14 @@ export class ServerServiceConfig extends ClientServiceConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@SubConfigClass<TAGS>({softReadonly: true})
|
@SubConfigClass<TAGS>({softReadonly: true})
|
||||||
export class ServerExtensionsConfig {
|
export class ServerExtensionsConfig {
|
||||||
@ConfigProperty({volatile: true})
|
@ConfigProperty({volatile: true})
|
||||||
list: string[] = [];
|
list: string[] = [];
|
||||||
|
|
||||||
|
@ConfigProperty({type: 'object'})
|
||||||
|
configs: Record<string, unknown> = {};
|
||||||
|
|
||||||
@ConfigProperty({
|
@ConfigProperty({
|
||||||
tags: {
|
tags: {
|
||||||
name: $localize`Clean up unused tables`,
|
name: $localize`Clean up unused tables`,
|
||||||
|
@ -59,7 +59,7 @@ interface IState {
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class SettingsEntryComponent
|
export class SettingsEntryComponent
|
||||||
implements ControlValueAccessor, Validator, OnChanges {
|
implements ControlValueAccessor, Validator, OnChanges {
|
||||||
name: string;
|
name: string;
|
||||||
required: boolean;
|
required: boolean;
|
||||||
dockerWarning: boolean;
|
dockerWarning: boolean;
|
||||||
@ -79,7 +79,10 @@ export class SettingsEntryComponent
|
|||||||
public arrayType: string;
|
public arrayType: string;
|
||||||
public uiType: string;
|
public uiType: string;
|
||||||
newThemeModalRef: any;
|
newThemeModalRef: any;
|
||||||
iconModal: { ref?: any, error?: string };
|
iconModal: {
|
||||||
|
ref?: any,
|
||||||
|
error?: string
|
||||||
|
};
|
||||||
@Input() noChangeDetection = false;
|
@Input() noChangeDetection = false;
|
||||||
public readonly ConfigStyle = ConfigStyle;
|
public readonly ConfigStyle = ConfigStyle;
|
||||||
protected readonly SortByTypes = SortByTypes;
|
protected readonly SortByTypes = SortByTypes;
|
||||||
@ -101,9 +104,9 @@ export class SettingsEntryComponent
|
|||||||
for (let i = 0; i < this.state.value?.length; ++i) {
|
for (let i = 0; i < this.state.value?.length; ++i) {
|
||||||
for (const k of Object.keys(this.state.value[i].__state)) {
|
for (const k of Object.keys(this.state.value[i].__state)) {
|
||||||
if (!Utils.equalsFilter(
|
if (!Utils.equalsFilter(
|
||||||
this.state.value[i]?.__state[k]?.value,
|
this.state.value[i]?.__state[k]?.value,
|
||||||
this.state.default[i] ? this.state.default[i][k] : undefined,
|
this.state.default[i] ? this.state.default[i][k] : undefined,
|
||||||
['default', '__propPath', '__created', '__prototype', '__rootConfig'])) {
|
['default', '__propPath', '__created', '__prototype', '__rootConfig'])) {
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -129,7 +132,7 @@ export class SettingsEntryComponent
|
|||||||
get defaultStr(): string {
|
get defaultStr(): string {
|
||||||
if (this.type === 'SearchQuery') {
|
if (this.type === 'SearchQuery') {
|
||||||
return (
|
return (
|
||||||
'\'' + this.searchQueryParserService.stringify(this.state.default) + '\''
|
'\'' + this.searchQueryParserService.stringify(this.state.default) + '\''
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,8 +146,8 @@ export class SettingsEntryComponent
|
|||||||
|
|
||||||
get StringValue(): string {
|
get StringValue(): string {
|
||||||
if (
|
if (
|
||||||
this.state.type === 'array' &&
|
this.state.type === 'array' &&
|
||||||
(this.state.arrayType === 'string' || this.isNumberArray)
|
(this.state.arrayType === 'string' || this.isNumberArray)
|
||||||
) {
|
) {
|
||||||
return (this.state.value || []).join(';');
|
return (this.state.value || []).join(';');
|
||||||
}
|
}
|
||||||
@ -162,8 +165,8 @@ export class SettingsEntryComponent
|
|||||||
|
|
||||||
set StringValue(value: string) {
|
set StringValue(value: string) {
|
||||||
if (
|
if (
|
||||||
this.state.type === 'array' &&
|
this.state.type === 'array' &&
|
||||||
(this.state.arrayType === 'string' || this.isNumberArray)
|
(this.state.arrayType === 'string' || this.isNumberArray)
|
||||||
) {
|
) {
|
||||||
value = value.replace(new RegExp(',', 'g'), ';');
|
value = value.replace(new RegExp(',', 'g'), ';');
|
||||||
if (!this.allowSpaces) {
|
if (!this.allowSpaces) {
|
||||||
@ -172,14 +175,14 @@ export class SettingsEntryComponent
|
|||||||
this.state.value = value.split(';').filter((v: string) => v !== '');
|
this.state.value = value.split(';').filter((v: string) => v !== '');
|
||||||
if (this.isNumberArray) {
|
if (this.isNumberArray) {
|
||||||
this.state.value = this.state.value
|
this.state.value = this.state.value
|
||||||
.map((v: string) => parseFloat(v))
|
.map((v: string) => parseFloat(v))
|
||||||
.filter((v: number) => !isNaN(v));
|
.filter((v: number) => !isNaN(v));
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof this.state.value === 'object') {
|
if (typeof this.state.value === 'object') {
|
||||||
this.state.value = JSON.parse(value);
|
this.state.value = JSON.parse(value);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.state.value = value;
|
this.state.value = value;
|
||||||
@ -194,11 +197,13 @@ export class SettingsEntryComponent
|
|||||||
key: 'default',
|
key: 'default',
|
||||||
value: $localize`default`
|
value: $localize`default`
|
||||||
}, ...(this.state.rootConfig as any).__state.availableThemes.value
|
}, ...(this.state.rootConfig as any).__state.availableThemes.value
|
||||||
.map((th: ThemeConfig) => ({key: th.name, value: th.name}))];
|
.map((th: ThemeConfig) => ({key: th.name, value: th.name}))];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
get SelectedThemeSettings(): { theme: string } {
|
get SelectedThemeSettings(): {
|
||||||
|
theme: string
|
||||||
|
} {
|
||||||
return (this.state.value as ThemeConfig[]).find(th => th.name === (this.state.rootConfig as any).__state.selectedTheme.value) || {theme: 'N/A'};
|
return (this.state.value as ThemeConfig[]).find(th => th.name === (this.state.rootConfig as any).__state.selectedTheme.value) || {theme: 'N/A'};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -241,16 +246,16 @@ export class SettingsEntryComponent
|
|||||||
this.uiType = CustomSettingsEntries.getFullName(this.state);
|
this.uiType = CustomSettingsEntries.getFullName(this.state);
|
||||||
}
|
}
|
||||||
if (!this.state.isEnumType &&
|
if (!this.state.isEnumType &&
|
||||||
!this.state.isEnumArrayType &&
|
!this.state.isEnumArrayType &&
|
||||||
this.type !== 'boolean' &&
|
this.type !== 'boolean' &&
|
||||||
this.type !== 'SearchQuery' &&
|
this.type !== 'SearchQuery' &&
|
||||||
!CustomSettingsEntries.iS(this.state) &&
|
!CustomSettingsEntries.iS(this.state) &&
|
||||||
this.arrayType !== 'MapLayers' &&
|
this.arrayType !== 'MapLayers' &&
|
||||||
this.arrayType !== 'NavigationLinkConfig' &&
|
this.arrayType !== 'NavigationLinkConfig' &&
|
||||||
this.arrayType !== 'MapPathGroupConfig' &&
|
this.arrayType !== 'MapPathGroupConfig' &&
|
||||||
this.arrayType !== 'MapPathGroupThemeConfig' &&
|
this.arrayType !== 'MapPathGroupThemeConfig' &&
|
||||||
this.arrayType !== 'JobScheduleConfig' &&
|
this.arrayType !== 'JobScheduleConfig' &&
|
||||||
this.arrayType !== 'UserConfig') {
|
this.arrayType !== 'UserConfig') {
|
||||||
this.uiType = 'StringInput';
|
this.uiType = 'StringInput';
|
||||||
}
|
}
|
||||||
if (this.type === this.state.tags?.uiType) {
|
if (this.type === this.state.tags?.uiType) {
|
||||||
@ -273,18 +278,18 @@ export class SettingsEntryComponent
|
|||||||
this.name = this.state?.tags?.name;
|
this.name = this.state?.tags?.name;
|
||||||
if (this.name) {
|
if (this.name) {
|
||||||
this.idName =
|
this.idName =
|
||||||
this.GUID + this.name.toLowerCase().replace(new RegExp(' ', 'gm'), '-');
|
this.GUID + this.name.toLowerCase().replace(new RegExp(' ', 'gm'), '-');
|
||||||
}
|
}
|
||||||
this.isNumberArray =
|
this.isNumberArray =
|
||||||
this.state.arrayType === 'unsignedInt' ||
|
this.state.arrayType === 'unsignedInt' ||
|
||||||
this.state.arrayType === 'integer' ||
|
this.state.arrayType === 'integer' ||
|
||||||
this.state.arrayType === 'float' ||
|
this.state.arrayType === 'float' ||
|
||||||
this.state.arrayType === 'positiveFloat';
|
this.state.arrayType === 'positiveFloat';
|
||||||
this.isNumber =
|
this.isNumber =
|
||||||
this.state.type === 'unsignedInt' ||
|
this.state.type === 'unsignedInt' ||
|
||||||
this.state.type === 'integer' ||
|
this.state.type === 'integer' ||
|
||||||
this.state.type === 'float' ||
|
this.state.type === 'float' ||
|
||||||
this.state.type === 'positiveFloat';
|
this.state.type === 'positiveFloat';
|
||||||
|
|
||||||
|
|
||||||
if (this.isNumber) {
|
if (this.isNumber) {
|
||||||
@ -306,11 +311,16 @@ export class SettingsEntryComponent
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getOptionsView(state: IState & { optionsView?: { key: number | string; value: string | number }[] }) {
|
getOptionsView(state: IState & {
|
||||||
|
optionsView?: {
|
||||||
|
key: number | string;
|
||||||
|
value: string | number
|
||||||
|
}[]
|
||||||
|
}) {
|
||||||
if (!state.optionsView) {
|
if (!state.optionsView) {
|
||||||
const eClass = state.isEnumType
|
const eClass = state.isEnumType
|
||||||
? state.type
|
? state.type
|
||||||
: state.arrayType;
|
: state.arrayType;
|
||||||
if (state.tags?.uiOptions) {
|
if (state.tags?.uiOptions) {
|
||||||
state.optionsView = state.tags?.uiOptions.map(o => ({
|
state.optionsView = state.tags?.uiOptions.map(o => ({
|
||||||
key: o,
|
key: o,
|
||||||
@ -325,11 +335,11 @@ export class SettingsEntryComponent
|
|||||||
|
|
||||||
validate(): ValidationErrors {
|
validate(): ValidationErrors {
|
||||||
if (
|
if (
|
||||||
!this.required ||
|
!this.required ||
|
||||||
(this.state &&
|
(this.state &&
|
||||||
typeof this.state.value !== 'undefined' &&
|
typeof this.state.value !== 'undefined' &&
|
||||||
this.state.value !== null &&
|
this.state.value !== null &&
|
||||||
this.state.value !== '')
|
this.state.value !== '')
|
||||||
) {
|
) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -386,8 +396,8 @@ export class SettingsEntryComponent
|
|||||||
|
|
||||||
removeLayer(layer: MapLayers): void {
|
removeLayer(layer: MapLayers): void {
|
||||||
this.state.value.splice(
|
this.state.value.splice(
|
||||||
this.state.value.indexOf(layer),
|
this.state.value.indexOf(layer),
|
||||||
1
|
1
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -395,7 +405,7 @@ export class SettingsEntryComponent
|
|||||||
addNewTheme(): void {
|
addNewTheme(): void {
|
||||||
const availableThemes = (this.state.rootConfig as any).__state.availableThemes;
|
const availableThemes = (this.state.rootConfig as any).__state.availableThemes;
|
||||||
if (!this.newThemeName ||
|
if (!this.newThemeName ||
|
||||||
(availableThemes.value as ThemeConfig[]).find(th => th.name === this.newThemeName)) {
|
(availableThemes.value as ThemeConfig[]).find(th => th.name === this.newThemeName)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.state.value = this.newThemeName;
|
this.state.value = this.newThemeName;
|
||||||
|
@ -7,6 +7,7 @@ import {ProjectPath} from '../../../../../src/backend/ProjectPath';
|
|||||||
import {TAGS} from '../../../../../src/common/config/public/ClientConfig';
|
import {TAGS} from '../../../../../src/common/config/public/ClientConfig';
|
||||||
import {ObjectManagers} from '../../../../../src/backend/model/ObjectManagers';
|
import {ObjectManagers} from '../../../../../src/backend/model/ObjectManagers';
|
||||||
import {UserRoles} from '../../../../../src/common/entities/UserDTO';
|
import {UserRoles} from '../../../../../src/common/entities/UserDTO';
|
||||||
|
import {ExtensionConfigWrapper} from '../../../../../src/backend/model/extension/ExtensionConfigWrapper';
|
||||||
|
|
||||||
process.env.NODE_ENV = 'test';
|
process.env.NODE_ENV = 'test';
|
||||||
const chai: any = require('chai');
|
const chai: any = require('chai');
|
||||||
@ -34,7 +35,7 @@ describe('SettingsRouter', () => {
|
|||||||
it('it should GET the settings', async () => {
|
it('it should GET the settings', async () => {
|
||||||
Config.Users.authenticationRequired = false;
|
Config.Users.authenticationRequired = false;
|
||||||
Config.Users.unAuthenticatedUserRole = UserRoles.Admin;
|
Config.Users.unAuthenticatedUserRole = UserRoles.Admin;
|
||||||
const originalSettings = await Config.original();
|
const originalSettings = await ExtensionConfigWrapper.original();
|
||||||
const srv = new Server();
|
const srv = new Server();
|
||||||
await srv.onStarted.wait();
|
await srv.onStarted.wait();
|
||||||
const result = await chai.request(srv.App)
|
const result = await chai.request(srv.App)
|
||||||
|
@ -9,6 +9,7 @@ import {UserRoles} from '../../../../../src/common/entities/UserDTO';
|
|||||||
import {ConfigClassBuilder} from '../../../../../node_modules/typeconfig/node';
|
import {ConfigClassBuilder} from '../../../../../node_modules/typeconfig/node';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
import {ExtensionConfigWrapper} from '../../../../../src/backend/model/extension/ExtensionConfigWrapper';
|
||||||
|
|
||||||
|
|
||||||
declare const describe: any;
|
declare const describe: any;
|
||||||
@ -74,7 +75,7 @@ describe('Settings middleware', () => {
|
|||||||
expect(Config.Users.enforcedUsers.length).to.be.equal(1);
|
expect(Config.Users.enforcedUsers.length).to.be.equal(1);
|
||||||
expect(Config.Users.enforcedUsers[0].name).to.be.equal('Apple');
|
expect(Config.Users.enforcedUsers[0].name).to.be.equal('Apple');
|
||||||
expect(Config.Users.enforcedUsers.length).to.be.equal(1);
|
expect(Config.Users.enforcedUsers.length).to.be.equal(1);
|
||||||
Config.original().then((cfg) => {
|
ExtensionConfigWrapper.original().then((cfg) => {
|
||||||
try {
|
try {
|
||||||
expect(cfg.Users.enforcedUsers.length).to.be.equal(1);
|
expect(cfg.Users.enforcedUsers.length).to.be.equal(1);
|
||||||
expect(cfg.Users.enforcedUsers[0].name).to.be.equal('Apple');
|
expect(cfg.Users.enforcedUsers[0].name).to.be.equal('Apple');
|
||||||
|
Loading…
Reference in New Issue
Block a user