1
0
mirror of https://github.com/bpatrik/pigallery2.git synced 2025-03-29 21:56:58 +02:00
pigallery2/src/backend/model/extension/ExtensionManager.ts
2024-03-02 22:18:31 +01:00

177 lines
6.0 KiB
TypeScript

import {ProjectPath} from '../../ProjectPath';
import {Config} from '../../../common/config/private/Config';
import * as fs from 'fs';
import * as path from 'path';
import {IObjectManager} from '../database/IObjectManager';
import {Logger} from '../../Logger';
import {IExtensionEvents, IExtensionObject} from './IExtension';
import {Server} from '../../server';
import {ExtensionEvent} from './ExtensionEvent';
import * as express from 'express';
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);
const LOG_TAG = '[ExtensionManager]';
export class ExtensionManager implements IObjectManager {
public static EXTENSION_API_PATH = Config.Server.apiPath + '/extension';
events: IExtensionEvents;
extObjects: { [key: string]: ExtensionObject<unknown> } = {};
router: express.Router;
constructor() {
this.initEvents();
}
public async init() {
this.extObjects = {};
this.initEvents();
if (!Config.Extensions.enabled) {
return;
}
this.router = express.Router();
Server.getInstance().app.use(ExtensionManager.EXTENSION_API_PATH, this.router);
this.loadExtensionsList();
await this.initExtensions();
}
private initEvents() {
this.events = {
gallery: {
MetadataLoader: {
loadPhotoMetadata: new ExtensionEvent(),
loadVideoMetadata: new ExtensionEvent()
},
CoverManager: {
getCoverForDirectory: new ExtensionEvent(),
getCoverForAlbum: new ExtensionEvent(),
invalidateDirectoryCovers: new ExtensionEvent(),
},
DiskManager: {
scanDirectory: new ExtensionEvent()
},
ImageRenderer: {
render: new ExtensionEvent()
}
}
};
ExtensionDecoratorObject.init(this.events);
}
public loadExtensionsList() {
Logger.debug(LOG_TAG, 'Loading extension list from ' + ProjectPath.ExtensionFolder);
if (!fs.existsSync(ProjectPath.ExtensionFolder)) {
return;
}
const extList = fs
.readdirSync(ProjectPath.ExtensionFolder)
.filter((f): boolean =>
fs.statSync(path.join(ProjectPath.ExtensionFolder, f)).isDirectory()
);
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)));
}
private createUniqueExtensionObject(name: string, folder: string): IExtensionObject<unknown> {
let id = name;
if (this.extObjects[id]) {
let i = 0;
while (this.extObjects[`${name}_${++i}`]) { /* empty */
}
id = `${name}_${++i}`;
}
if (!this.extObjects[id]) {
this.extObjects[id] = new ExtensionObject(id, name, folder, this.router, this.events);
}
return this.extObjects[id];
}
private async initExtensions() {
for (let i = 0; i < Config.Extensions.extensions.length; ++i) {
const extFolder = Config.Extensions.extensions[i].path;
let extName = extFolder;
if(Config.Extensions.extensions[i].enabled === false){
Logger.silly(LOG_TAG, `Skipping ${extFolder} initiation. Extension is disabled.`);
}
const extPath = path.join(ProjectPath.ExtensionFolder, extFolder);
const serverExtPath = path.join(extPath, 'server.js');
const packageJsonPath = path.join(extPath, 'package.json');
if (!fs.existsSync(serverExtPath)) {
Logger.silly(LOG_TAG, `Skipping ${extFolder} server initiation. server.js does not exists`);
continue;
}
if (fs.existsSync(packageJsonPath)) {
if (fs.existsSync(path.join(extPath, 'node_modules'))) {
Logger.debug(LOG_TAG, `node_modules folder exists. Skipping "npm install".`);
} else {
Logger.silly(LOG_TAG, `Running: "npm install --prefer-offline --no-audit --progress=false --omit=dev" in ${extPath}`);
await exec('npm install --no-audit --progress=false --omit=dev', {
cwd: extPath
});
}
// eslint-disable-next-line @typescript-eslint/no-var-requires
const pkg = require(packageJsonPath);
if (pkg.name) {
extName = pkg.name;
}
}
// eslint-disable-next-line @typescript-eslint/no-var-requires
const ext = require(serverExtPath);
if (typeof ext?.init === 'function') {
Logger.debug(LOG_TAG, 'Running init on extension: ' + extFolder);
await ext?.init(this.createUniqueExtensionObject(extName, extFolder));
}
}
if (Config.Extensions.cleanUpUnusedTables) {
// Clean up tables after all Extension was initialized.
await SQLConnection.removeUnusedTables();
}
}
private async cleanUpExtensions() {
for (const extObj of Object.values(this.extObjects)) {
const serverExt = path.join(extObj.folder, 'server.js');
// eslint-disable-next-line @typescript-eslint/no-var-requires
const ext = require(serverExt);
if (typeof ext?.cleanUp === 'function') {
Logger.debug(LOG_TAG, 'Running Init on extension:' + extObj.extensionName);
await ext?.cleanUp(extObj);
}
extObj.messengers.cleanUp();
}
}
public async cleanUp() {
if (!Config.Extensions.enabled) {
return;
}
this.initEvents(); // reset events
await this.cleanUpExtensions();
Server.getInstance().app.use(ExtensionManager.EXTENSION_API_PATH, express.Router());
this.extObjects = {};
}
}