1
0
mirror of https://github.com/bpatrik/pigallery2.git synced 2024-12-23 01:27:14 +02:00

Add messenger extensibility to extensions #753

This commit is contained in:
Patrik J. Braun 2023-11-19 01:43:10 +01:00
parent 50b8f7a81d
commit ebb9886d4b
7 changed files with 93 additions and 9 deletions

View File

@ -62,7 +62,9 @@ export class ExpressRouteWrapper implements IExtensionRESTRoute {
}, },
RenderingMWs.renderResult RenderingMWs.renderResult
]))); ])));
this.extLogger.silly(`Listening on ${this.func} ${ExtensionManager.EXTENSION_API_PATH}${fullPaths}`); const p = ExtensionManager.EXTENSION_API_PATH + fullPaths;
this.extLogger.silly(`Listening on ${this.func} ${p}`);
return p;
} }
public rawMiddleware(paths: string[], minRole: UserRoles, mw: (req: Request, res: Response, next: NextFunction) => void | Promise<void>) { public rawMiddleware(paths: string[], minRole: UserRoles, mw: (req: Request, res: Response, next: NextFunction) => void | Promise<void>) {
@ -70,6 +72,8 @@ export class ExpressRouteWrapper implements IExtensionRESTRoute {
this.router[this.func](fullPaths, this.router[this.func](fullPaths,
...this.getAuthMWs(minRole), ...this.getAuthMWs(minRole),
mw); mw);
this.extLogger.silly(`Listening on ${this.func} ${ExtensionManager.EXTENSION_API_PATH}${fullPaths}`); const p = ExtensionManager.EXTENSION_API_PATH + fullPaths;
this.extLogger.silly(`Listening on ${this.func} ${p}`);
return p;
} }
} }

View File

@ -4,7 +4,7 @@ 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 {Logger} from '../../Logger'; import {Logger} from '../../Logger';
import {IExtensionEvents, IExtensionObject, IServerExtension} from './IExtension'; import {IExtensionEvents, IExtensionObject} from './IExtension';
import {Server} from '../../server'; import {Server} from '../../server';
import {ExtensionEvent} from './ExtensionEvent'; import {ExtensionEvent} from './ExtensionEvent';
import * as express from 'express'; import * as express from 'express';
@ -108,8 +108,8 @@ export class ExtensionManager implements IObjectManager {
if (fs.existsSync(packageJsonPath)) { if (fs.existsSync(packageJsonPath)) {
Logger.silly(LOG_TAG, `Running: "npm install --omit=dev" in ${extPath}`); Logger.silly(LOG_TAG, `Running: "npm install --omit=dev" in ${extPath}`);
await exec('npm install --omit=dev' ,{ await exec('npm install --omit=dev', {
cwd:extPath cwd: extPath
}); });
// eslint-disable-next-line @typescript-eslint/no-var-requires // eslint-disable-next-line @typescript-eslint/no-var-requires
const pkg = require(packageJsonPath); const pkg = require(packageJsonPath);
@ -138,8 +138,9 @@ export class ExtensionManager implements IObjectManager {
const ext = require(serverExt); const ext = require(serverExt);
if (typeof ext?.cleanUp === 'function') { if (typeof ext?.cleanUp === 'function') {
Logger.debug(LOG_TAG, 'Running Init on extension:' + extObj.extensionName); Logger.debug(LOG_TAG, 'Running Init on extension:' + extObj.extensionName);
await ext?.cleanUp(ext); await ext?.cleanUp(extObj);
} }
extObj.messengers.cleanUp();
} }
} }

View File

@ -0,0 +1,31 @@
import {IExtensionMessengers} from './IExtension';
import {DynamicConfig} from '../../../common/entities/DynamicConfig';
import {MediaDTOWithThPath, Messenger} from '../messenger/Messenger';
import {ExtensionMessenger} from '../messenger/ExtensionMessenger';
import {MessengerRepository} from '../messenger/MessengerRepository';
import {ILogger} from '../../Logger';
export class ExtensionMessengerHandler implements IExtensionMessengers {
messengers: Messenger[] = [];
constructor(private readonly extLogger: ILogger) {
}
addMessenger<C extends Record<string, unknown>>(name: string, config: DynamicConfig[], callbacks: {
sendMedia: (config: C, media: MediaDTOWithThPath[]) => Promise<void>
}): void {
this.extLogger.silly('Adding new Messenger:', name);
const em = new ExtensionMessenger(name, config, callbacks);
this.messengers.push(em);
MessengerRepository.Instance.register(em);
}
cleanUp() {
this.extLogger.silly('Removing Messenger');
this.messengers.forEach(m => MessengerRepository.Instance.remove(m));
}
}

View File

@ -6,6 +6,7 @@ import {ProjectPath} from '../../ProjectPath';
import {ExpressRouterWrapper} from './ExpressRouterWrapper'; import {ExpressRouterWrapper} from './ExpressRouterWrapper';
import {createLoggerWrapper} from '../../Logger'; import {createLoggerWrapper} from '../../Logger';
import * as express from 'express'; import * as express from 'express';
import {ExtensionMessengerHandler} from './ExtensionMessengerHandler';
export class ExtensionObject<C> implements IExtensionObject<C> { export class ExtensionObject<C> implements IExtensionObject<C> {
@ -16,6 +17,7 @@ export class ExtensionObject<C> implements IExtensionObject<C> {
public readonly Logger; public readonly Logger;
public readonly events; public readonly events;
public readonly RESTApi; public readonly RESTApi;
public readonly messengers;
constructor(public readonly extensionId: string, constructor(public readonly extensionId: string,
public readonly extensionName: string, public readonly extensionName: string,
@ -30,6 +32,7 @@ export class ExtensionObject<C> implements IExtensionObject<C> {
this.Logger = logger; this.Logger = logger;
this.events = events; this.events = events;
this.RESTApi = new ExpressRouterWrapper(extensionRouter, extensionId, logger); this.RESTApi = new ExpressRouterWrapper(extensionRouter, extensionId, logger);
this.messengers = new ExtensionMessengerHandler(logger);
} }
} }

View File

@ -7,6 +7,8 @@ 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} from 'typeorm'; import {Connection} from 'typeorm';
import {DynamicConfig} from '../../../common/entities/DynamicConfig';
import {MediaDTOWithThPath} from '../messenger/Messenger';
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>;
@ -69,16 +71,18 @@ export interface IExtensionRESTRoute {
* @param paths RESTapi path, relative to the extension base endpoint * @param paths RESTapi path, relative to the extension base endpoint
* @param minRole set to null to omit auer check (ie make the endpoint public) * @param minRole set to null to omit auer check (ie make the endpoint public)
* @param cb function callback * @param cb function callback
* @return newly added REST api path
*/ */
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): string;
/** /**
* Exposes a standard expressjs middleware * Exposes a standard expressjs middleware
* @param paths RESTapi path, relative to the extension base endpoint * @param paths RESTapi path, relative to the extension base endpoint
* @param minRole set to null to omit auer check (ie make the endpoint public) * @param minRole set to null to omit auer check (ie make the endpoint public)
* @param mw expressjs middleware * @param mw expressjs middleware
* @return newly added REST api path
*/ */
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>): string;
} }
export interface IExtensionRESTApi { export interface IExtensionRESTApi {
@ -116,9 +120,21 @@ export interface IExtensionConfig<C> {
getConfig(): C; getConfig(): C;
} }
export interface IExtensionMessengers {
/**
* Adds a new messenger that the user can select e.g.: for sending top pick photos
* @param name Name of the messenger (also used as id)
* @param config config metadata for this messenger
* @param callbacks messenger logic
*/
addMessenger<C extends Record<string, unknown> = Record<string, unknown>>(name: string, config: DynamicConfig[], callbacks: {
sendMedia: (config: C, media: MediaDTOWithThPath[]) => Promise<void>
}): void;
}
export interface IExtensionObject<C> { export interface IExtensionObject<C> {
/** /**
* ID of the extension that is internally used. By default the name and ID matches if there is no collision. * ID of the extension that is internally used. By default, the name and ID matches if there is no collision.
*/ */
extensionId: string, extensionId: string,
@ -159,6 +175,13 @@ export interface IExtensionObject<C> {
* Use this to define REST calls related to the extension * Use this to define REST calls related to the extension
*/ */
RESTApi: IExtensionRESTApi; RESTApi: IExtensionRESTApi;
/**
* Object to manipulate messengers.
* Messengers are used to send messages (like emails) from the app.
* One type of message is a list of selected photos.
*/
messengers: IExtensionMessengers;
} }

View File

@ -0,0 +1,15 @@
import {MediaDTOWithThPath, Messenger} from './Messenger';
import {DynamicConfig} from '../../../common/entities/DynamicConfig';
export class ExtensionMessenger<C extends Record<string, unknown> = Record<string, unknown>> extends Messenger<C> {
constructor(public readonly Name: string,
public readonly ConfigTemplate: DynamicConfig[],
private readonly callbacks: { sendMedia: (config: C, media: MediaDTOWithThPath[]) => Promise<void> }) {
super();
}
protected sendMedia(config: C, media: MediaDTOWithThPath[]): Promise<void> {
return this.callbacks.sendMedia(config, media);
}
}

View File

@ -18,6 +18,13 @@ export class MessengerRepository {
return Object.values(this.messengers); return Object.values(this.messengers);
} }
remove(m: Messenger<Record<string, unknown>>): void {
if (!this.messengers[m.Name]) {
throw new Error('Messenger does not exist:' + m.Name);
}
delete this.messengers[m.Name];
}
register(msgr: Messenger): void { register(msgr: Messenger): void {
if (typeof this.messengers[msgr.Name] !== 'undefined') { if (typeof this.messengers[msgr.Name] !== 'undefined') {
throw new Error('Messenger already exist:' + msgr.Name); throw new Error('Messenger already exist:' + msgr.Name);