Skip to content

Commit

Permalink
Merge pull request #257 from Hexastack/feat/refactor-extension
Browse files Browse the repository at this point in the history
feat: refactor extensions as npm packages (be brave 1)
  • Loading branch information
marrouchi authored Oct 23, 2024
2 parents 4cf5c57 + ab35a77 commit 16c4852
Show file tree
Hide file tree
Showing 117 changed files with 941 additions and 957 deletions.
1 change: 1 addition & 0 deletions api/.dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ test
*.mock.ts
__mock__
__test__
.hexabot
1 change: 1 addition & 0 deletions api/.gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.hexabot/
node_modules/
dist/
coverage/
Expand Down
8 changes: 0 additions & 8 deletions api/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,6 @@ WORKDIR /app

COPY package*.json ./

COPY merge-extensions-deps.js ./

COPY src/extensions ./src/extensions

COPY patches ./patches

RUN npm ci
Expand All @@ -22,8 +18,6 @@ WORKDIR /app

COPY package*.json ./

COPY --from=builder /app/merge-extensions-deps.js ./

COPY --from=builder /app/src/extensions ./src/extensions

COPY --from=builder /app/patches ./patches
Expand All @@ -44,8 +38,6 @@ WORKDIR /app

COPY package*.json ./

COPY --from=builder /app/merge-extensions-deps.js ./

COPY --from=builder /app/src/extensions ./src/extensions

COPY --from=builder /app/patches ./patches
Expand Down
80 changes: 0 additions & 80 deletions api/merge-extensions-deps.js

This file was deleted.

15 changes: 10 additions & 5 deletions api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,19 @@
"author": "Hexastack",
"license": "AGPL-3.0-only",
"scripts": {
"preinstall": "node merge-extensions-deps.js",
"postinstall": "patch-package",
"build": "nest build",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"build:clean": "rm -rf src/.hexabot",
"build:channels": "mkdir -p src/.hexabot/channels && find node_modules/ -name 'hexabot-channel-*' -exec cp -R {} src/.hexabot/channels/ \\;",
"build:helpers": "mkdir -p src/.hexabot/helpers && find node_modules/ -name 'hexabot-helper-*' -exec cp -R {} src/.hexabot/helpers/ \\;",
"build:plugins": "mkdir -p src/.hexabot/plugins && find node_modules/ -name 'hexabot-plugin-*' -exec cp -R {} src/.hexabot/plugins/ \\;",
"build:extensions": "npm run build:channels && npm run build:helpers && npm run build:plugins",
"build:prepare": "npm run build:clean && npm run build:extensions",
"build": "npm run build:prepare && nest build",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\" \"libs/**/*.ts\"",
"start": "nest start",
"doc": "npx @compodoc/compodoc --hideGenerator -p tsconfig.doc.json -s -r 9003 -w",
"start:dev": "nest start --watch",
"start:debug": "nest start --debug 0.0.0.0:9229 --watch",
"start:dev": "npm run build:prepare && nest start --watch",
"start:debug": "npm run build:prepare && nest start --debug 0.0.0.0:9229 --watch",
"start:prod": "node dist/main",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\"",
"lint:fix": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
Expand Down
4 changes: 2 additions & 2 deletions api/src/attachment/services/attachment.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,13 +108,13 @@ export class AttachmentService extends BaseService<Attachment> {
await this.getStoragePlugin().uploadAvatar(picture);
this.logger.log(
`Profile picture uploaded successfully to ${
this.getStoragePlugin().id
this.getStoragePlugin().name
}`,
);
} catch (err) {
this.logger.error(
`Error while uploading profile picture to ${
this.getStoragePlugin().id
this.getStoragePlugin().name
}`,
err,
);
Expand Down
2 changes: 1 addition & 1 deletion api/src/channel/channel.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export class ChannelController {
getChannels(): { name: string }[] {
return this.channelService.getAll().map((handler) => {
return {
name: handler.getChannel(),
name: handler.getName(),
};
});
}
Expand Down
6 changes: 4 additions & 2 deletions api/src/channel/channel.middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*/

import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
import { NextFunction, Request, Response } from 'express';

import { ChannelService } from './channel.service';

Expand All @@ -20,7 +20,9 @@ export class ChannelMiddleware implements NestMiddleware {
try {
const [_, path, channelName] = req.path.split('/');
if (path === 'webhook' && channelName) {
const channel = this.channelService.getChannelHandler(channelName);
const channel = this.channelService.getChannelHandler(
`${channelName}-channel`,
);
if (channel) {
return await channel.middleware(req, res, next);
}
Expand Down
15 changes: 13 additions & 2 deletions api/src/channel/channel.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@
*/

import { HttpModule } from '@nestjs/axios';
import { MiddlewareConsumer, Module, RequestMethod } from '@nestjs/common';
import {
Global,
MiddlewareConsumer,
Module,
RequestMethod,
} from '@nestjs/common';
import { InjectDynamicProviders } from 'nestjs-dynamic-providers';

import { AttachmentModule } from '@/attachment/attachment.module';
Expand All @@ -23,7 +28,13 @@ export interface ChannelModuleOptions {
folder: string;
}

@InjectDynamicProviders('dist/extensions/**/*.channel.js')
@Global()
@InjectDynamicProviders(
// Core & under dev channels
'dist/extensions/**/*.channel.js',
// Installed channels via npm
'dist/.hexabot/channels/**/*.channel.js',
)
@Module({
controllers: [WebhookController, ChannelController],
providers: [ChannelService],
Expand Down
47 changes: 24 additions & 23 deletions api/src/channel/channel.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import { Injectable, UnauthorizedException } from '@nestjs/common';
import { Request, Response } from 'express';

import { SubscriberService } from '@/chat/services/subscriber.service';
import { LIVE_CHAT_TEST_CHANNEL_NAME } from '@/extensions/channels/live-chat-tester/settings';
import { OFFLINE_CHANNEL_NAME } from '@/extensions/channels/offline/settings';
import { CONSOLE_CHANNEL_NAME } from '@/extensions/channels/console/settings';
import { WEB_CHANNEL_NAME } from '@/extensions/channels/web/settings';
import { LoggerService } from '@/logger/logger.service';
import {
SocketGet,
Expand All @@ -23,10 +23,11 @@ import { SocketRequest } from '@/websocket/utils/socket-request';
import { SocketResponse } from '@/websocket/utils/socket-response';

import ChannelHandler from './lib/Handler';
import { ChannelName } from './types';

@Injectable()
export class ChannelService {
private registry: Map<string, ChannelHandler<string>> = new Map();
private registry: Map<string, ChannelHandler<ChannelName>> = new Map();

constructor(
private readonly logger: LoggerService,
Expand All @@ -40,7 +41,7 @@ export class ChannelService {
* @param channel - The channel handler associated with the channel name.
* @typeParam C The channel handler's type that extends `ChannelHandler`.
*/
public setChannel<T extends string, C extends ChannelHandler<T>>(
public setChannel<T extends ChannelName, C extends ChannelHandler<T>>(
name: T,
channel: C,
) {
Expand All @@ -62,19 +63,19 @@ export class ChannelService {
* @param name - The name of the channel to find.
* @returns The channel handler associated with the specified name, or undefined if the channel is not found.
*/
public findChannel(name: string) {
public findChannel(name: ChannelName) {
return this.getAll().find((c) => {
return c.getChannel() === name;
return c.getName() === name;
});
}

/**
* Retrieves the appropriate channel handler based on the channel name.
*
* @param channelName - The name of the channel (messenger, offline, ...).
* @param channelName - The name of the channel (messenger, web, ...).
* @returns The handler for the specified channel.
*/
public getChannelHandler<T extends string, C extends ChannelHandler<T>>(
public getChannelHandler<T extends ChannelName, C extends ChannelHandler<T>>(
name: T,
): C {
const handler = this.registry.get(name);
Expand All @@ -93,42 +94,42 @@ export class ChannelService {
* @returns A promise that resolves when the handler has processed the request.
*/
async handle(channel: string, req: Request, res: Response): Promise<void> {
const handler = this.getChannelHandler(channel);
const handler = this.getChannelHandler(`${channel}-channel`);
handler.handle(req, res);
}

/**
* Handles a websocket request for the offline channel.
* Handles a websocket request for the web channel.
*
* @param req - The websocket request object.
* @param res - The websocket response object.
*/
@SocketGet(`/webhook/${OFFLINE_CHANNEL_NAME}/`)
@SocketPost(`/webhook/${OFFLINE_CHANNEL_NAME}/`)
handleWebsocketForOffline(
@SocketGet(`/webhook/${WEB_CHANNEL_NAME}/`)
@SocketPost(`/webhook/${WEB_CHANNEL_NAME}/`)
handleWebsocketForWebChannel(
@SocketReq() req: SocketRequest,
@SocketRes() res: SocketResponse,
) {
this.logger.log('Channel notification (Offline Socket) : ', req.method);
const handler = this.getChannelHandler(OFFLINE_CHANNEL_NAME);
this.logger.log('Channel notification (Web Socket) : ', req.method);
const handler = this.getChannelHandler(WEB_CHANNEL_NAME);
return handler.handle(req, res);
}

/**
* Handles a websocket request for the live chat tester channel.
* Handles a websocket request for the admin chat console channel.
* It considers the user as a subscriber.
*
* @param req - The websocket request object.
* @param res - The websocket response object.
*/
@SocketGet(`/webhook/${LIVE_CHAT_TEST_CHANNEL_NAME}/`)
@SocketPost(`/webhook/${LIVE_CHAT_TEST_CHANNEL_NAME}/`)
async handleWebsocketForLiveChatTester(
@SocketGet(`/webhook/${CONSOLE_CHANNEL_NAME}/`)
@SocketPost(`/webhook/${CONSOLE_CHANNEL_NAME}/`)
async handleWebsocketForAdminChatConsole(
@SocketReq() req: SocketRequest,
@SocketRes() res: SocketResponse,
) {
this.logger.log(
'Channel notification (Live Chat Tester Socket) : ',
'Channel notification (Admin Chat Console Socket) : ',
req.method,
);

Expand Down Expand Up @@ -157,21 +158,21 @@ export class ChannelService {
country: '',
labels: [],
channel: {
name: LIVE_CHAT_TEST_CHANNEL_NAME,
name: CONSOLE_CHANNEL_NAME,
isSocket: true,
},
},
);

// Update session (end user is both a user + subscriber)
req.session.offline = {
req.session.web = {
profile: testSubscriber,
isSocket: true,
messageQueue: [],
polling: false,
};

const handler = this.getChannelHandler(LIVE_CHAT_TEST_CHANNEL_NAME);
const handler = this.getChannelHandler(CONSOLE_CHANNEL_NAME);
return handler.handle(req, res);
}
}
2 changes: 1 addition & 1 deletion api/src/channel/lib/EventWrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export default abstract class EventWrapper<
toString() {
return JSON.stringify(
{
handler: this._handler.getChannel(),
handler: this._handler.getName(),
channelData: this.getChannelData(),
sender: this.getSender(),
recipient: this.getRecipientForeignId(),
Expand Down
Loading

0 comments on commit 16c4852

Please sign in to comment.