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
])));
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>) {
@ -70,6 +72,8 @@ export class ExpressRouteWrapper implements IExtensionRESTRoute {
this.router[this.func](fullPaths,
...this.getAuthMWs(minRole),
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 {IObjectManager} from '../database/IObjectManager';
import {Logger} from '../../Logger';
import {IExtensionEvents, IExtensionObject, IServerExtension} from './IExtension';
import {IExtensionEvents, IExtensionObject} from './IExtension';
import {Server} from '../../server';
import {ExtensionEvent} from './ExtensionEvent';
import * as express from 'express';
@ -108,8 +108,8 @@ export class ExtensionManager implements IObjectManager {
if (fs.existsSync(packageJsonPath)) {
Logger.silly(LOG_TAG, `Running: "npm install --omit=dev" in ${extPath}`);
await exec('npm install --omit=dev' ,{
cwd:extPath
await exec('npm install --omit=dev', {
cwd: extPath
});
// eslint-disable-next-line @typescript-eslint/no-var-requires
const pkg = require(packageJsonPath);
@ -138,8 +138,9 @@ export class ExtensionManager implements IObjectManager {
const ext = require(serverExt);
if (typeof ext?.cleanUp === 'function') {
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 {createLoggerWrapper} from '../../Logger';
import * as express from 'express';
import {ExtensionMessengerHandler} from './ExtensionMessengerHandler';
export class ExtensionObject<C> implements IExtensionObject<C> {
@ -16,6 +17,7 @@ export class ExtensionObject<C> implements IExtensionObject<C> {
public readonly Logger;
public readonly events;
public readonly RESTApi;
public readonly messengers;
constructor(public readonly extensionId: string,
public readonly extensionName: string,
@ -30,6 +32,7 @@ export class ExtensionObject<C> implements IExtensionObject<C> {
this.Logger = logger;
this.events = events;
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 {ParamsDictionary} from 'express-serve-static-core';
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>;
@ -69,16 +71,18 @@ export interface IExtensionRESTRoute {
* @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 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
* @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 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 {
@ -116,9 +120,21 @@ export interface IExtensionConfig<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> {
/**
* 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,
@ -159,6 +175,13 @@ export interface IExtensionObject<C> {
* Use this to define REST calls related to the extension
*/
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);
}
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 {
if (typeof this.messengers[msgr.Name] !== 'undefined') {
throw new Error('Messenger already exist:' + msgr.Name);