mirror of
https://github.com/bpatrik/pigallery2.git
synced 2025-01-02 03:37:54 +02:00
parent
f551509fee
commit
d4d8dcfcdb
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "pigallery2-extension-kit",
|
||||
"version": "2.0.3-edge4",
|
||||
"version": "2.0.3-edge6",
|
||||
"description": "Interfaces for developing extensions for pigallery2",
|
||||
"author": "Patrik J. Braun",
|
||||
"homepage": "https://github.com/bpatrik/pigallery2",
|
||||
|
@ -12,7 +12,7 @@ import * as child_process from 'child_process';
|
||||
// @ts-ignore
|
||||
import * as jeditor from 'gulp-json-editor';
|
||||
import {XLIFF} from 'xlf-google-translate';
|
||||
import {PrivateConfigClass} from './src/common/config/private/Config';
|
||||
import {PrivateConfigClass} from './src/common/config/private/PrivateConfigClass';
|
||||
import {ConfigClassBuilder} from 'typeconfig/src/decorators/builders/ConfigClassBuilder';
|
||||
|
||||
const execPr = util.promisify(child_process.exec);
|
||||
|
@ -1,7 +1,7 @@
|
||||
import {NextFunction, Request, Response} from 'express';
|
||||
import {ErrorCodes, ErrorDTO} from '../../common/entities/Error';
|
||||
import {Message} from '../../common/entities/Message';
|
||||
import {Config, PrivateConfigClass} from '../../common/config/private/Config';
|
||||
import {PrivateConfigClass} from '../../common/config/private/PrivateConfigClass';
|
||||
import {UserDTO, UserRoles} from '../../common/entities/UserDTO';
|
||||
import {NotificationManager} from '../model/NotifocationManager';
|
||||
import {Logger} from '../Logger';
|
||||
|
@ -51,8 +51,8 @@ export class SettingsMWs {
|
||||
await ConfigDiagnostics.runDiagnostics();
|
||||
// restart all schedule timers. In case they have changed
|
||||
ObjectManagers.getInstance().JobManager.runSchedules();
|
||||
Logger.info(LOG_TAG, 'new config:');
|
||||
Logger.info(LOG_TAG, JSON.stringify(Config.toJSON({attachDescription: false}), null, '\t'));
|
||||
// Logger.info(LOG_TAG, 'new config:');
|
||||
// Logger.info(LOG_TAG, JSON.stringify(Config.toJSON({attachDescription: false}), null, '\t'));
|
||||
return next();
|
||||
} catch (err) {
|
||||
if (err instanceof Error) {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import {Config, PrivateConfigClass} from '../../../common/config/private/Config';
|
||||
import {PrivateConfigClass} from '../../../common/config/private/PrivateConfigClass';
|
||||
import {Logger} from '../../Logger';
|
||||
import {NotificationManager} from '../NotifocationManager';
|
||||
import {SQLConnection} from '../database/SQLConnection';
|
||||
@ -27,8 +27,8 @@ import {SearchQueryParser} from '../../../common/SearchQueryParser';
|
||||
import {SearchQueryTypes, TextSearch,} from '../../../common/entities/SearchQueryDTO';
|
||||
import {Utils} from '../../../common/Utils';
|
||||
import {JobRepository} from '../jobs/JobRepository';
|
||||
import {ExtensionConfig} from '../extension/ExtensionConfigWrapper';
|
||||
import {ConfigClassBuilder} from '../../../../node_modules/typeconfig/node';
|
||||
import { Config } from '../../../common/config/private/Config';
|
||||
|
||||
const LOG_TAG = '[ConfigDiagnostics]';
|
||||
|
||||
|
18
src/backend/model/extension/ExtensionConfig.ts
Normal file
18
src/backend/model/extension/ExtensionConfig.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import {IExtensionConfig} from './IExtension';
|
||||
import {Config} from '../../../common/config/private/Config';
|
||||
|
||||
export class ExtensionConfig<C> implements IExtensionConfig<C> {
|
||||
|
||||
constructor(private readonly extensionFolder: string) {
|
||||
}
|
||||
|
||||
|
||||
public getConfig(): C {
|
||||
const c = (Config.Extensions.extensions || [])
|
||||
.find(e => e.path === this.extensionFolder);
|
||||
|
||||
return c?.configs as C;
|
||||
}
|
||||
|
||||
|
||||
}
|
110
src/backend/model/extension/ExtensionConfigTemplateLoader.ts
Normal file
110
src/backend/model/extension/ExtensionConfigTemplateLoader.ts
Normal file
@ -0,0 +1,110 @@
|
||||
import {PrivateConfigClass} from '../../../common/config/private/PrivateConfigClass';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import {ServerExtensionsEntryConfig} from '../../../common/config/private/subconfigs/ServerExtensionsConfig';
|
||||
|
||||
|
||||
const LOG_TAG = '[ExtensionConfigTemplateLoader]';
|
||||
|
||||
/**
|
||||
* This class decouples the extension management and the config.
|
||||
* It helps to solve the "chicken and the egg" which should load first:
|
||||
* Config or the extension as they have a circular dependency
|
||||
*/
|
||||
export class ExtensionConfigTemplateLoader {
|
||||
|
||||
private static instance: ExtensionConfigTemplateLoader;
|
||||
private extensionsFolder: string;
|
||||
|
||||
private loaded = false;
|
||||
private extensionList: string[] = [];
|
||||
private extensionTemplates: { folder: string, template?: { new(): unknown } }[] = [];
|
||||
|
||||
public static get Instance() {
|
||||
if (!this.instance) {
|
||||
this.instance = new ExtensionConfigTemplateLoader();
|
||||
}
|
||||
|
||||
return this.instance;
|
||||
}
|
||||
|
||||
|
||||
init(extensionsFolder: string) {
|
||||
this.extensionsFolder = extensionsFolder;
|
||||
}
|
||||
|
||||
public loadExtensionTemplates(config: PrivateConfigClass) {
|
||||
if (!this.extensionsFolder) {
|
||||
throw new Error('Unknown extensions folder.');
|
||||
}
|
||||
// already loaded
|
||||
if (!this.loaded) {
|
||||
|
||||
this.extensionList = (fs
|
||||
.readdirSync(this.extensionsFolder))
|
||||
.filter((f): boolean =>
|
||||
fs.statSync(path.join(this.extensionsFolder, f)).isDirectory()
|
||||
);
|
||||
this.extensionList.sort();
|
||||
|
||||
this.extensionTemplates = [];
|
||||
for (let i = 0; i < this.extensionList.length; ++i) {
|
||||
const extFolder = this.extensionList[i];
|
||||
const extPath = path.join(this.extensionsFolder, extFolder);
|
||||
const serverExtPath = path.join(extPath, 'server.js');
|
||||
if (!fs.existsSync(serverExtPath)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const ext = require(serverExtPath);
|
||||
if (typeof ext?.initConfig === 'function') {
|
||||
ext?.initConfig({
|
||||
setConfigTemplate: (template: { new(): unknown }): void => {
|
||||
this.extensionTemplates.push({folder: extFolder, template: template});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
//also create basic config extensions that do not have any
|
||||
this.extensionTemplates.push({folder: extFolder});
|
||||
}
|
||||
}
|
||||
this.loaded = true;
|
||||
}
|
||||
|
||||
this.setTemplatesToConfig(config);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private setTemplatesToConfig(config: PrivateConfigClass) {
|
||||
if (!this.extensionTemplates) {
|
||||
return;
|
||||
}
|
||||
|
||||
const ePaths = this.extensionTemplates.map(et => et.folder);
|
||||
// delete not existing extensions
|
||||
config.Extensions.extensions = config.Extensions.extensions
|
||||
.filter(ec => ePaths.indexOf(ec.path) !== -1);
|
||||
|
||||
|
||||
for (let i = 0; i < this.extensionTemplates.length; ++i) {
|
||||
const ext = this.extensionTemplates[i];
|
||||
|
||||
let c = (config.Extensions.extensions || [])
|
||||
.find(e => e.path === ext.folder);
|
||||
|
||||
// set the new structure with the new def values
|
||||
if (!c) {
|
||||
c = new ServerExtensionsEntryConfig(ext.folder);
|
||||
if (ext.template) {
|
||||
c.configs= new ext.template()
|
||||
}
|
||||
config.Extensions.extensions.push(c);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -1,64 +1,49 @@
|
||||
import {IConfigClass} from 'typeconfig/common';
|
||||
import {Config, PrivateConfigClass} from '../../../common/config/private/Config';
|
||||
import {PrivateConfigClass} from '../../../common/config/private/PrivateConfigClass';
|
||||
import {ConfigClassBuilder} from 'typeconfig/node';
|
||||
import {IExtensionConfig} from './IExtension';
|
||||
import {ObjectManagers} from '../ObjectManagers';
|
||||
import {ServerExtensionsEntryConfig} from '../../../common/config/private/subconfigs/ServerExtensionsConfig';
|
||||
import {ExtensionConfigTemplateLoader} from './ExtensionConfigTemplateLoader';
|
||||
import {NotificationManager} from '../NotifocationManager';
|
||||
|
||||
|
||||
const LOG_TAG = '[ExtensionConfigWrapper]';
|
||||
|
||||
/**
|
||||
* Wraps to original config and makes sure all extension related config is loaded
|
||||
*/
|
||||
export class ExtensionConfigWrapper {
|
||||
static async original(): Promise<PrivateConfigClass & IConfigClass> {
|
||||
|
||||
static async original(showError = false): Promise<PrivateConfigClass & IConfigClass> {
|
||||
const pc = ConfigClassBuilder.attachPrivateInterface(new PrivateConfigClass());
|
||||
ExtensionConfigTemplateLoader.Instance.loadExtensionTemplates(pc);
|
||||
try {
|
||||
await pc.load(); // loading the basic configs but we do not know the extension config hierarchy yet
|
||||
if (ObjectManagers.isReady()) {
|
||||
for (const ext of Object.values(ObjectManagers.getInstance().ExtensionManager.extObjects)) {
|
||||
ext.config.loadToConfig(ConfigClassBuilder.attachPrivateInterface(pc));
|
||||
}
|
||||
}
|
||||
await pc.load(); // loading the extension related configs
|
||||
await pc.load(); // loading the basic configs, but we do not know the extension config hierarchy yet
|
||||
|
||||
} catch (e) {
|
||||
console.error('Error during loading original config. Reverting to defaults.');
|
||||
console.error(e);
|
||||
if(showError){
|
||||
console.error(LOG_TAG,'Error during loading config. Reverting to defaults.');
|
||||
console.error(LOG_TAG,'This is most likely due to: 1) you added a bad configuration in the server.json OR 2) The configuration changed in the latest release.');
|
||||
console.error(e);
|
||||
NotificationManager.error('Can\'t load config. Reverting to default. This is most likely due to: 1) you added a bad configuration in the server.json OR 2) The configuration changed in the latest release.', (e.toString ? e.toString() : JSON.stringify(e)));
|
||||
}
|
||||
}
|
||||
return pc;
|
||||
}
|
||||
|
||||
|
||||
static originalSync(showError = false): PrivateConfigClass & IConfigClass {
|
||||
const pc = ConfigClassBuilder.attachPrivateInterface(new PrivateConfigClass());
|
||||
ExtensionConfigTemplateLoader.Instance.loadExtensionTemplates(pc);
|
||||
try {
|
||||
pc.loadSync(); // loading the basic configs, but we do not know the extension config hierarchy yet
|
||||
|
||||
} catch (e) {
|
||||
if(showError){
|
||||
console.error(LOG_TAG,'Error during loading config. Reverting to defaults.');
|
||||
console.error(LOG_TAG,'This is most likely due to: 1) you added a bad configuration in the server.json OR 2) The configuration changed in the latest release.');
|
||||
console.error(e);
|
||||
NotificationManager.error('Ca\'nt load config. Reverting to default. This is most likely due to: 1) you added a bad configuration in the server.json OR 2) The configuration changed in the latest release.', (e.toString ? e.toString() : JSON.stringify(e)));
|
||||
}
|
||||
}
|
||||
return pc;
|
||||
}
|
||||
}
|
||||
|
||||
export class ExtensionConfig<C> implements IExtensionConfig<C> {
|
||||
public template: new() => C;
|
||||
|
||||
constructor(private readonly extensionFolder: string) {
|
||||
}
|
||||
|
||||
private findConfig(config: PrivateConfigClass): ServerExtensionsEntryConfig {
|
||||
let c = (config.Extensions.extensions || []).find(e => e.path === this.extensionFolder);
|
||||
if (!c) {
|
||||
c = new ServerExtensionsEntryConfig(this.extensionFolder);
|
||||
config.Extensions.extensions.push(c);
|
||||
}
|
||||
return c;
|
||||
|
||||
}
|
||||
|
||||
public getConfig(): C {
|
||||
return this.findConfig(Config).configs as C;
|
||||
}
|
||||
|
||||
public setTemplate(template: new() => C): void {
|
||||
this.template = template;
|
||||
this.loadToConfig(Config);
|
||||
}
|
||||
|
||||
loadToConfig(config: PrivateConfigClass) {
|
||||
if (!this.template) {
|
||||
return;
|
||||
}
|
||||
|
||||
const confTemplate = ConfigClassBuilder.attachPrivateInterface(new this.template());
|
||||
const extConf = this.findConfig(config);
|
||||
extConf.configs = confTemplate;
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,6 @@ import {SQLConnection} from '../database/SQLConnection';
|
||||
import {ExtensionObject} from './ExtensionObject';
|
||||
import {ExtensionDecoratorObject} from './ExtensionDecorator';
|
||||
import * as util from 'util';
|
||||
import {ServerExtensionsEntryConfig} from '../../../common/config/private/subconfigs/ServerExtensionsConfig';
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const exec = util.promisify(require('child_process').exec);
|
||||
|
||||
@ -80,15 +79,8 @@ export class ExtensionManager implements IObjectManager {
|
||||
);
|
||||
extList.sort();
|
||||
|
||||
// delete not existing extensions
|
||||
Config.Extensions.extensions = Config.Extensions.extensions.filter(ec => extList.indexOf(ec.path) !== -1);
|
||||
|
||||
// Add new extensions
|
||||
const ePaths = Config.Extensions.extensions.map(ec => ec.path);
|
||||
extList.filter(ep => ePaths.indexOf(ep) === -1).forEach(ep =>
|
||||
Config.Extensions.extensions.push(new ServerExtensionsEntryConfig(ep)));
|
||||
|
||||
Logger.debug(LOG_TAG, 'Extensions found ', JSON.stringify(Config.Extensions.extensions.map(ec => ec.path)));
|
||||
Logger.debug(LOG_TAG, 'Extensions found: ', JSON.stringify(Config.Extensions.extensions.map(ec => ec.path)));
|
||||
}
|
||||
|
||||
private createUniqueExtensionObject(name: string, folder: string): IExtensionObject<unknown> {
|
||||
@ -111,7 +103,7 @@ export class ExtensionManager implements IObjectManager {
|
||||
const extFolder = Config.Extensions.extensions[i].path;
|
||||
let extName = extFolder;
|
||||
|
||||
if(Config.Extensions.extensions[i].enabled === false){
|
||||
if (Config.Extensions.extensions[i].enabled === false) {
|
||||
Logger.silly(LOG_TAG, `Skipping ${extFolder} initiation. Extension is disabled.`);
|
||||
}
|
||||
const extPath = path.join(ProjectPath.ExtensionFolder, extFolder);
|
||||
|
@ -1,12 +1,12 @@
|
||||
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';
|
||||
import {ExtensionMessengerHandler} from './ExtensionMessengerHandler';
|
||||
import {ExtensionConfig} from './ExtensionConfig';
|
||||
|
||||
export class ExtensionObject<C> implements IExtensionObject<C> {
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import * as express from 'express';
|
||||
import {NextFunction, Request, Response} from 'express';
|
||||
import {PrivateConfigClass} from '../../../common/config/private/Config';
|
||||
import {PrivateConfigClass} from '../../../common/config/private/PrivateConfigClass';
|
||||
import {ObjectManagers} from '../ObjectManagers';
|
||||
import {ProjectPathClass} from '../../ProjectPath';
|
||||
import {ILogger} from '../../Logger';
|
||||
@ -141,8 +141,6 @@ export interface IExtensionDB {
|
||||
}
|
||||
|
||||
export interface IExtensionConfig<C> {
|
||||
setTemplate(template: new() => C): void;
|
||||
|
||||
getConfig(): C;
|
||||
}
|
||||
|
||||
@ -210,11 +208,25 @@ export interface IExtensionObject<C = void> {
|
||||
messengers: IExtensionMessengers;
|
||||
}
|
||||
|
||||
export interface IExtensionConfigInit<C> {
|
||||
/**
|
||||
* Sets the config tempalte class
|
||||
* @param template
|
||||
*/
|
||||
setConfigTemplate(template: new() => C): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extension interface. All extension is expected to implement and export these methods
|
||||
*/
|
||||
export interface IServerExtension<C> {
|
||||
|
||||
/**
|
||||
* This function can be called any time. It should only set the config template class
|
||||
* @param extension
|
||||
*/
|
||||
initConfig(extension: IExtensionConfigInit<C>): void;
|
||||
|
||||
/**
|
||||
* Extension init function. Extension should at minimum expose this function.
|
||||
* @param extension
|
||||
|
@ -1,101 +1,13 @@
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
import {ServerConfig} from './PrivateConfig';
|
||||
import * as crypto from 'crypto';
|
||||
import {ExtensionConfigWrapper} from '../../../backend/model/extension/ExtensionConfigWrapper';
|
||||
import {PrivateConfigClass} from './PrivateConfigClass';
|
||||
import {ConfigClassBuilder} from 'typeconfig/node';
|
||||
import {ExtensionConfigTemplateLoader} from '../../../backend/model/extension/ExtensionConfigTemplateLoader';
|
||||
import * as path from 'path';
|
||||
import {ConfigClass, ConfigClassBuilder} from 'typeconfig/node';
|
||||
import {IConfigClass} from 'typeconfig/common';
|
||||
import {PasswordHelper} from '../../../backend/model/PasswordHelper';
|
||||
import {TAGS} from '../public/ClientConfig';
|
||||
import {NotificationManager} from '../../../backend/model/NotifocationManager';
|
||||
|
||||
declare const process: any;
|
||||
const upTime = new Date().toISOString();
|
||||
// TODO: Refactor Config to be injectable globally.
|
||||
// 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']
|
||||
.every((fn) => (global as any)[fn] instanceof Function);
|
||||
|
||||
@ConfigClass<IConfigClass<TAGS> & ServerConfig>({
|
||||
configPath: path.join(__dirname, !isTesting ? './../../../../config.json' : './../../../../test/backend/tmp/config.json'),
|
||||
crateConfigPathIfNotExists: isTesting,
|
||||
saveIfNotExist: true,
|
||||
attachDescription: true,
|
||||
enumsAsString: true,
|
||||
softReadonly: true,
|
||||
cli: {
|
||||
enable: {
|
||||
configPath: true,
|
||||
attachState: true,
|
||||
attachDescription: true,
|
||||
rewriteCLIConfig: true,
|
||||
rewriteENVConfig: true,
|
||||
enumsAsString: true,
|
||||
saveIfNotExist: true,
|
||||
exitOnConfig: true,
|
||||
},
|
||||
defaults: {
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
onLoadedSync: async (config) => {
|
||||
let changed = false;
|
||||
for (let i = 0; i < config.Users.enforcedUsers.length; ++i) {
|
||||
const uc = config.Users.enforcedUsers[i];
|
||||
|
||||
// encrypt password and save back to the config
|
||||
if (uc.password) {
|
||||
if (!uc.encryptedPassword) {
|
||||
uc.encryptedPassword = PasswordHelper.cryptPassword(uc.password);
|
||||
}
|
||||
uc.password = '';
|
||||
changed = true;
|
||||
}
|
||||
if (!uc.encrypted) {
|
||||
uc.encrypted = !!uc.encryptedPassword;
|
||||
changed = true;
|
||||
}
|
||||
if (!uc.encrypted && !uc.password) {
|
||||
throw new Error('Password error for enforced user: ' + uc.name);
|
||||
}
|
||||
}
|
||||
if (changed) {
|
||||
config.saveSync();
|
||||
}
|
||||
}
|
||||
})
|
||||
export class PrivateConfigClass extends ServerConfig {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
if (!this.Server.sessionSecret || this.Server.sessionSecret.length === 0) {
|
||||
this.Server.sessionSecret = [
|
||||
crypto.randomBytes(256).toString('hex'),
|
||||
crypto.randomBytes(256).toString('hex'),
|
||||
crypto.randomBytes(256).toString('hex'),
|
||||
];
|
||||
}
|
||||
|
||||
this.Environment.appVersion =
|
||||
require('../../../../package.json').version;
|
||||
this.Environment.buildTime =
|
||||
require('../../../../package.json').buildTime;
|
||||
this.Environment.buildCommitHash =
|
||||
require('../../../../package.json').buildCommitHash;
|
||||
this.Environment.upTime = upTime;
|
||||
this.Environment.isDocker = !!process.env.PI_DOCKER;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
export const Config = ConfigClassBuilder.attachInterface(
|
||||
new PrivateConfigClass()
|
||||
);
|
||||
const pre = ConfigClassBuilder.attachPrivateInterface(new PrivateConfigClass());
|
||||
try {
|
||||
Config.loadSync();
|
||||
} catch (e) {
|
||||
console.error('Error during loading config. Reverting to defaults.');
|
||||
console.error('This is most likely due to: 1) you added a bad configuration in the server.json OR 2) The configuration changed in the latest release.');
|
||||
console.error(e);
|
||||
NotificationManager.error('Cant load config. Reverting to default. This is most likely due to: 1) you added a bad configuration in the server.json OR 2) The configuration changed in the latest release.', (e.toString ? e.toString() : JSON.stringify(e)));
|
||||
}
|
||||
pre.loadSync();
|
||||
} catch (e) { /* empty */ }
|
||||
ExtensionConfigTemplateLoader.Instance.init(path.join(__dirname, '/../../../../', pre.Extensions.folder));
|
||||
|
||||
export const Config = ExtensionConfigWrapper.originalSync(true);
|
||||
|
89
src/common/config/private/PrivateConfigClass.ts
Normal file
89
src/common/config/private/PrivateConfigClass.ts
Normal file
@ -0,0 +1,89 @@
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
import {ServerConfig} from './PrivateConfig';
|
||||
import * as crypto from 'crypto';
|
||||
import * as path from 'path';
|
||||
import {ConfigClass, ConfigClassBuilder} from 'typeconfig/node';
|
||||
import {IConfigClass} from 'typeconfig/common';
|
||||
import {PasswordHelper} from '../../../backend/model/PasswordHelper';
|
||||
import {TAGS} from '../public/ClientConfig';
|
||||
import {NotificationManager} from '../../../backend/model/NotifocationManager';
|
||||
|
||||
declare const process: any;
|
||||
const upTime = new Date().toISOString();
|
||||
// TODO: Refactor Config to be injectable globally.
|
||||
// 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']
|
||||
.every((fn) => (global as any)[fn] instanceof Function);
|
||||
|
||||
@ConfigClass<IConfigClass<TAGS> & ServerConfig>({
|
||||
configPath: path.join(__dirname, !isTesting ? './../../../../config.json' : './../../../../test/backend/tmp/config.json'),
|
||||
crateConfigPathIfNotExists: isTesting,
|
||||
saveIfNotExist: true,
|
||||
attachDescription: true,
|
||||
enumsAsString: true,
|
||||
softReadonly: true,
|
||||
cli: {
|
||||
enable: {
|
||||
configPath: true,
|
||||
attachState: true,
|
||||
attachDescription: true,
|
||||
rewriteCLIConfig: true,
|
||||
rewriteENVConfig: true,
|
||||
enumsAsString: true,
|
||||
saveIfNotExist: true,
|
||||
exitOnConfig: true,
|
||||
},
|
||||
defaults: {
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
onLoadedSync: async (config) => {
|
||||
let changed = false;
|
||||
for (let i = 0; i < config.Users.enforcedUsers.length; ++i) {
|
||||
const uc = config.Users.enforcedUsers[i];
|
||||
|
||||
// encrypt password and save back to the config
|
||||
if (uc.password) {
|
||||
if (!uc.encryptedPassword) {
|
||||
uc.encryptedPassword = PasswordHelper.cryptPassword(uc.password);
|
||||
}
|
||||
uc.password = '';
|
||||
changed = true;
|
||||
}
|
||||
if (!uc.encrypted) {
|
||||
uc.encrypted = !!uc.encryptedPassword;
|
||||
changed = true;
|
||||
}
|
||||
if (!uc.encrypted && !uc.password) {
|
||||
throw new Error('Password error for enforced user: ' + uc.name);
|
||||
}
|
||||
}
|
||||
if (changed) {
|
||||
config.saveSync();
|
||||
}
|
||||
}
|
||||
})
|
||||
export class PrivateConfigClass extends ServerConfig {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
if (!this.Server.sessionSecret || this.Server.sessionSecret.length === 0) {
|
||||
this.Server.sessionSecret = [
|
||||
crypto.randomBytes(256).toString('hex'),
|
||||
crypto.randomBytes(256).toString('hex'),
|
||||
crypto.randomBytes(256).toString('hex'),
|
||||
];
|
||||
}
|
||||
|
||||
this.Environment.appVersion =
|
||||
require('../../../../package.json').version;
|
||||
this.Environment.buildTime =
|
||||
require('../../../../package.json').buildTime;
|
||||
this.Environment.buildCommitHash =
|
||||
require('../../../../package.json').buildCommitHash;
|
||||
this.Environment.upTime = upTime;
|
||||
this.Environment.isDocker = !!process.env.PI_DOCKER;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
import {ConfigProperty, SubConfigClass} from 'typeconfig/common';
|
||||
import {ClientExtensionsConfig, ConfigPriority, TAGS} from '../../public/ClientConfig';
|
||||
import {GenericConfigType} from 'typeconfig/src/GenericConfigType';
|
||||
|
||||
declare let $localize: (s: TemplateStringsArray) => string;
|
||||
|
||||
if (typeof $localize === 'undefined') {
|
||||
|
@ -107,7 +107,7 @@
|
||||
[ngModel]="rStates?.value.__state[ck]">
|
||||
</app-settings-entry>
|
||||
<!-- Config entries --->
|
||||
<ng-container *ngIf="isExpandableConfig(rStates.value.__state[ck])">
|
||||
<ng-container *ngIf="isExpandableConfig(rStates.value.__state[ck]) && rStates.value.__state[ck].value">
|
||||
<!-- Sub category with header and menu item -->
|
||||
<div class="card mt-2 mb-2" *ngIf="topLevel && rStates?.value.__state[ck].tags?.uiIcon"
|
||||
[id]="ConfigPath+'.'+ck">
|
||||
|
@ -220,12 +220,12 @@ export class TemplateComponent implements OnInit, OnChanges, OnDestroy, ISetting
|
||||
st.original = Utils.clone(st.value);
|
||||
}
|
||||
|
||||
if (st.isConfigType) {
|
||||
if (st.isConfigType && st.value) {
|
||||
for (const k of Object.keys(st.value.__state)) {
|
||||
instrument(st.value.__state[k], st);
|
||||
}
|
||||
}
|
||||
if (st.isConfigArrayType) {
|
||||
if (st.isConfigArrayType && st.value) {
|
||||
for (let i = 0; i < st.value?.length; ++i) {
|
||||
for (const k of Object.keys(st.value[i].__state)) {
|
||||
instrument(st.value[i].__state[k], st);
|
||||
@ -317,6 +317,9 @@ export class TemplateComponent implements OnInit, OnChanges, OnDestroy, ISetting
|
||||
if (states.keys) {
|
||||
return states.keys;
|
||||
}
|
||||
if (!states.value) {
|
||||
return [];
|
||||
}
|
||||
const s = states.value.__state;
|
||||
const keys = Object.keys(s).sort((a, b) => {
|
||||
if ((this.isExpandableConfig(s[a]) || s[a].isConfigArrayType) !== (this.isExpandableConfig(s[b]) || s[b].isConfigArrayType)) {
|
||||
|
@ -20,11 +20,14 @@ import {
|
||||
import {DirectoryBaseDTO, DirectoryPathDTO} from '../src/common/entities/DirectoryDTO';
|
||||
import {FileDTO} from '../src/common/entities/FileDTO';
|
||||
import {DiskManager} from '../src/backend/model/fileaccess/DiskManager';
|
||||
import * as path from 'path';
|
||||
|
||||
export class TestHelper {
|
||||
|
||||
static creationCounter = 0;
|
||||
|
||||
public static readonly TMP_DIR= path.join(__dirname, './tmp');
|
||||
|
||||
public static getDirectoryEntry(parent: DirectoryBaseDTO = null, name = 'wars dir'): DirectoryEntity {
|
||||
|
||||
const dir = new DirectoryEntity();
|
||||
|
@ -47,7 +47,6 @@ export class DBTestHelper {
|
||||
mysql: process.env.TEST_MYSQL !== 'false'
|
||||
};
|
||||
public static readonly savedDescribe = savedDescribe;
|
||||
public tempDir: string;
|
||||
public readonly testGalleyEntities: {
|
||||
dir: ParentDirectoryDTO,
|
||||
subDir: SubDirectoryDTO,
|
||||
@ -80,7 +79,6 @@ export class DBTestHelper {
|
||||
};
|
||||
|
||||
constructor(public dbType: DatabaseType) {
|
||||
this.tempDir = path.join(__dirname, './tmp');
|
||||
}
|
||||
|
||||
static describe(settingsOverride: {
|
||||
@ -206,7 +204,7 @@ export class DBTestHelper {
|
||||
await ObjectManagers.reset();
|
||||
Config.Database.type = DatabaseType.mysql;
|
||||
Config.Database.mysql.database = 'pigallery2_test';
|
||||
await fs.promises.rm(this.tempDir, {recursive: true, force: true});
|
||||
await fs.promises.rm(TestHelper.TMP_DIR, {recursive: true, force: true});
|
||||
const conn = await SQLConnection.getConnection();
|
||||
await conn.query('DROP DATABASE IF EXISTS ' + conn.options.database);
|
||||
await SQLConnection.close();
|
||||
@ -225,10 +223,10 @@ export class DBTestHelper {
|
||||
private async clearUpSQLite(): Promise<void> {
|
||||
Logger.debug(LOG_TAG, 'clearing up sqlite');
|
||||
Config.Database.type = DatabaseType.sqlite;
|
||||
Config.Database.dbFolder = this.tempDir;
|
||||
Config.Database.dbFolder = TestHelper.TMP_DIR;
|
||||
ProjectPath.reset();
|
||||
await ObjectManagers.reset();
|
||||
await fs.promises.rm(this.tempDir, {recursive: true, force: true});
|
||||
await fs.promises.rm(TestHelper.TMP_DIR, {recursive: true, force: true});
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import {SuperAgentStatic} from 'superagent';
|
||||
import {ProjectPath} from '../../../../src/backend/ProjectPath';
|
||||
import {DBTestHelper} from '../../DBTestHelper';
|
||||
import {ReIndexingSensitivity} from '../../../../src/common/config/private/PrivateConfig';
|
||||
import {TestHelper} from '../../../TestHelper';
|
||||
|
||||
|
||||
process.env.NODE_ENV = 'test';
|
||||
@ -25,14 +26,13 @@ describe = DBTestHelper.describe({sqlite: true});
|
||||
describe('GalleryRouter', (sqlHelper: DBTestHelper) => {
|
||||
describe = tmpDescribe;
|
||||
|
||||
const tempDir = sqlHelper.tempDir;
|
||||
let server: Server;
|
||||
const setUp = async () => {
|
||||
await sqlHelper.initDB();
|
||||
Config.Users.authenticationRequired = false;
|
||||
Config.Media.Video.enabled = true;
|
||||
Config.Media.folder = path.join(__dirname, '../../assets');
|
||||
Config.Media.tempFolder = tempDir;
|
||||
Config.Media.tempFolder = TestHelper.TMP_DIR;
|
||||
ProjectPath.reset();
|
||||
server = new Server();
|
||||
await server.onStarted.wait();
|
||||
|
@ -19,6 +19,7 @@ describe('SettingsRouter', () => {
|
||||
|
||||
const tempDir = path.join(__dirname, '../../tmp');
|
||||
beforeEach(async () => {
|
||||
await ObjectManagers.reset();
|
||||
await fs.promises.rm(tempDir, {recursive: true, force: true});
|
||||
Config.Database.type = DatabaseType.sqlite;
|
||||
Config.Database.dbFolder = tempDir;
|
||||
|
@ -177,10 +177,10 @@ describe('IndexingManager', (sqlHelper: DBTestHelper) => {
|
||||
|
||||
expect(selected?.media?.length)
|
||||
.to.be.greaterThan(0);
|
||||
if (!fs.existsSync(sqlHelper.tempDir)) {
|
||||
fs.mkdirSync(sqlHelper.tempDir);
|
||||
if (!fs.existsSync(TestHelper.TMP_DIR)) {
|
||||
fs.mkdirSync(TestHelper.TMP_DIR);
|
||||
}
|
||||
const tmpDir = path.join(sqlHelper.tempDir, '/rnd5sdf_emptyDir');
|
||||
const tmpDir = path.join(TestHelper.TMP_DIR, '/rnd5sdf_emptyDir');
|
||||
fs.mkdirSync(tmpDir);
|
||||
ProjectPath.ImageFolder = tmpDir;
|
||||
let notFailed = false;
|
||||
|
Loading…
Reference in New Issue
Block a user