-
Notifications
You must be signed in to change notification settings - Fork 211
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
- Loading branch information
Showing
21 changed files
with
300 additions
and
176 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 changes: 110 additions & 0 deletions
110
src/backend/model/extension/ExtensionConfigTemplateLoader.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
} | ||
|
||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} | ||
} | ||
|
||
export class ExtensionConfig<C> implements IExtensionConfig<C> { | ||
public template: new() => C; | ||
|
||
constructor(private readonly extensionFolder: string) { | ||
} | ||
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 | ||
|
||
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); | ||
} 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 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; | ||
return pc; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.