mirror of
https://github.com/hexastack/hexabot
synced 2025-06-26 18:27:28 +00:00
feat: initial commit
This commit is contained in:
46
api/src/plugins/base-block-plugin.ts
Normal file
46
api/src/plugins/base-block-plugin.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright © 2024 Hexastack. All rights reserved.
|
||||
*
|
||||
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
|
||||
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
|
||||
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
|
||||
* 3. SaaS Restriction: This software, or any derivative of it, may not be used to offer a competing product or service (SaaS) without prior written consent from Hexastack. Offering the software as a service or using it in a commercial cloud environment without express permission is strictly prohibited.
|
||||
*/
|
||||
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { Block, BlockFull } from '@/chat/schemas/block.schema';
|
||||
import { Context } from '@/chat/schemas/types/context';
|
||||
import { StdOutgoingEnvelope } from '@/chat/schemas/types/message';
|
||||
|
||||
import { BasePlugin } from './base-plugin.service';
|
||||
import { PluginService } from './plugins.service';
|
||||
import {
|
||||
PluginBlockTemplate,
|
||||
PluginEffects,
|
||||
PluginSetting,
|
||||
PluginType,
|
||||
} from './types';
|
||||
|
||||
@Injectable()
|
||||
export abstract class BaseBlockPlugin extends BasePlugin {
|
||||
public readonly type: PluginType = PluginType.block;
|
||||
|
||||
constructor(id: string, pluginService: PluginService<BasePlugin>) {
|
||||
super(id, pluginService);
|
||||
}
|
||||
|
||||
title: string;
|
||||
|
||||
settings: PluginSetting[];
|
||||
|
||||
template: PluginBlockTemplate;
|
||||
|
||||
effects?: PluginEffects;
|
||||
|
||||
abstract process(
|
||||
block: Block | BlockFull,
|
||||
context: Context,
|
||||
convId?: string,
|
||||
): Promise<StdOutgoingEnvelope>;
|
||||
}
|
||||
23
api/src/plugins/base-event-plugin.ts
Normal file
23
api/src/plugins/base-event-plugin.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright © 2024 Hexastack. All rights reserved.
|
||||
*
|
||||
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
|
||||
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
|
||||
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
|
||||
* 3. SaaS Restriction: This software, or any derivative of it, may not be used to offer a competing product or service (SaaS) without prior written consent from Hexastack. Offering the software as a service or using it in a commercial cloud environment without express permission is strictly prohibited.
|
||||
*/
|
||||
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { BasePlugin } from './base-plugin.service';
|
||||
import { PluginService } from './plugins.service';
|
||||
import { PluginType } from './types';
|
||||
|
||||
@Injectable()
|
||||
export abstract class BaseEventPlugin extends BasePlugin {
|
||||
public readonly type: PluginType = PluginType.event;
|
||||
|
||||
constructor(id: string, pluginService: PluginService<BasePlugin>) {
|
||||
super(id, pluginService);
|
||||
}
|
||||
}
|
||||
27
api/src/plugins/base-plugin.service.ts
Normal file
27
api/src/plugins/base-plugin.service.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright © 2024 Hexastack. All rights reserved.
|
||||
*
|
||||
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
|
||||
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
|
||||
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
|
||||
* 3. SaaS Restriction: This software, or any derivative of it, may not be used to offer a competing product or service (SaaS) without prior written consent from Hexastack. Offering the software as a service or using it in a commercial cloud environment without express permission is strictly prohibited.
|
||||
*/
|
||||
|
||||
import { Injectable, OnModuleInit } from '@nestjs/common';
|
||||
|
||||
import { PluginService } from './plugins.service';
|
||||
import { PluginType } from './types';
|
||||
|
||||
@Injectable()
|
||||
export abstract class BasePlugin implements OnModuleInit {
|
||||
public readonly type: PluginType;
|
||||
|
||||
constructor(
|
||||
public readonly id: string,
|
||||
private pluginService: PluginService<BasePlugin>,
|
||||
) {}
|
||||
|
||||
onModuleInit() {
|
||||
this.pluginService.setPlugin(this.type, this.id, this);
|
||||
}
|
||||
}
|
||||
36
api/src/plugins/base-storage-plugin.ts
Normal file
36
api/src/plugins/base-storage-plugin.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright © 2024 Hexastack. All rights reserved.
|
||||
*
|
||||
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
|
||||
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
|
||||
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
|
||||
* 3. SaaS Restriction: This software, or any derivative of it, may not be used to offer a competing product or service (SaaS) without prior written consent from Hexastack. Offering the software as a service or using it in a commercial cloud environment without express permission is strictly prohibited.
|
||||
*/
|
||||
|
||||
import { Injectable, StreamableFile } from '@nestjs/common';
|
||||
|
||||
import { AttachmentCreateDto } from '@/attachment/dto/attachment.dto';
|
||||
import { Attachment } from '@/attachment/schemas/attachment.schema';
|
||||
|
||||
import { BasePlugin } from './base-plugin.service';
|
||||
import { PluginService } from './plugins.service';
|
||||
import { PluginType } from './types';
|
||||
|
||||
@Injectable()
|
||||
export abstract class BaseStoragePlugin extends BasePlugin {
|
||||
public readonly type: PluginType = PluginType.storage;
|
||||
|
||||
constructor(id: string, pluginService: PluginService<BasePlugin>) {
|
||||
super(id, pluginService);
|
||||
}
|
||||
|
||||
abstract fileExists(attachment: Attachment): Promise<boolean>;
|
||||
|
||||
abstract upload(file: Express.Multer.File): Promise<AttachmentCreateDto>;
|
||||
|
||||
abstract uploadAvatar(file: Express.Multer.File): Promise<any>;
|
||||
|
||||
abstract download(attachment: Attachment): Promise<StreamableFile>;
|
||||
|
||||
abstract downloadProfilePic(name: string): Promise<StreamableFile>;
|
||||
}
|
||||
18
api/src/plugins/map-types.ts
Normal file
18
api/src/plugins/map-types.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { BaseBlockPlugin } from './base-block-plugin';
|
||||
import { BaseEventPlugin } from './base-event-plugin';
|
||||
import { BasePlugin } from './base-plugin.service';
|
||||
import { BaseStoragePlugin } from './base-storage-plugin';
|
||||
import { PluginType } from './types';
|
||||
|
||||
const PLUGIN_TYPE_MAP = {
|
||||
[PluginType.event]: BaseEventPlugin,
|
||||
[PluginType.block]: BaseBlockPlugin,
|
||||
[PluginType.storage]: BaseStoragePlugin,
|
||||
};
|
||||
|
||||
export type PluginTypeMap = typeof PLUGIN_TYPE_MAP;
|
||||
|
||||
export type PluginInstance<T extends PluginType> = InstanceType<
|
||||
PluginTypeMap[T]
|
||||
> &
|
||||
BasePlugin;
|
||||
41
api/src/plugins/plugins.module.ts
Normal file
41
api/src/plugins/plugins.module.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright © 2024 Hexastack. All rights reserved.
|
||||
*
|
||||
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
|
||||
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
|
||||
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
|
||||
* 3. SaaS Restriction: This software, or any derivative of it, may not be used to offer a competing product or service (SaaS) without prior written consent from Hexastack. Offering the software as a service or using it in a commercial cloud environment without express permission is strictly prohibited.
|
||||
*/
|
||||
|
||||
import { Global, Module } from '@nestjs/common';
|
||||
import { MongooseModule } from '@nestjs/mongoose';
|
||||
import { InjectDynamicProviders } from 'nestjs-dynamic-providers';
|
||||
|
||||
import { AttachmentModule } from '@/attachment/attachment.module';
|
||||
import { AttachmentModel } from '@/attachment/schemas/attachment.schema';
|
||||
import { ChatModule } from '@/chat/chat.module';
|
||||
import { BlockModel } from '@/chat/schemas/block.schema';
|
||||
import { CmsModule } from '@/cms/cms.module';
|
||||
import { ContentTypeModel } from '@/cms/schemas/content-type.schema';
|
||||
import { ContentModel } from '@/cms/schemas/content.schema';
|
||||
|
||||
import { PluginService } from './plugins.service';
|
||||
|
||||
@InjectDynamicProviders('dist/**/*.plugin.js')
|
||||
@Global()
|
||||
@Module({
|
||||
imports: [
|
||||
MongooseModule.forFeature([
|
||||
BlockModel,
|
||||
AttachmentModel,
|
||||
ContentModel,
|
||||
ContentTypeModel,
|
||||
]),
|
||||
CmsModule,
|
||||
AttachmentModule,
|
||||
ChatModule,
|
||||
],
|
||||
providers: [PluginService],
|
||||
exports: [PluginService],
|
||||
})
|
||||
export class PluginsModule {}
|
||||
45
api/src/plugins/plugins.service.spec.ts
Normal file
45
api/src/plugins/plugins.service.spec.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright © 2024 Hexastack. All rights reserved.
|
||||
*
|
||||
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
|
||||
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
|
||||
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
|
||||
* 3. SaaS Restriction: This software, or any derivative of it, may not be used to offer a competing product or service (SaaS) without prior written consent from Hexastack. Offering the software as a service or using it in a commercial cloud environment without express permission is strictly prohibited.
|
||||
*/
|
||||
|
||||
import { Test } from '@nestjs/testing';
|
||||
|
||||
import { DummyPlugin } from '@/extensions/plugins/dummy.plugin';
|
||||
import { LoggerModule } from '@/logger/logger.module';
|
||||
|
||||
import { BaseBlockPlugin } from './base-block-plugin';
|
||||
import { PluginService } from './plugins.service';
|
||||
import { PluginType } from './types';
|
||||
|
||||
describe('PluginsService', () => {
|
||||
let pluginsService: PluginService;
|
||||
beforeAll(async () => {
|
||||
const module = await Test.createTestingModule({
|
||||
providers: [PluginService, DummyPlugin],
|
||||
imports: [LoggerModule],
|
||||
}).compile();
|
||||
pluginsService = module.get<PluginService>(PluginService);
|
||||
module.get<DummyPlugin>(DummyPlugin).onModuleInit();
|
||||
});
|
||||
afterAll(async () => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
describe('getAll', () => {
|
||||
it('should return an array of instances of base plugin', () => {
|
||||
const result = pluginsService.getAllByType(PluginType.block);
|
||||
expect(result.every((p) => p instanceof BaseBlockPlugin)).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('getPlugin', () => {
|
||||
it('should return the required plugin', () => {
|
||||
const result = pluginsService.getPlugin(PluginType.block, 'dummy');
|
||||
expect(result).toBeInstanceOf(DummyPlugin);
|
||||
});
|
||||
});
|
||||
});
|
||||
87
api/src/plugins/plugins.service.ts
Normal file
87
api/src/plugins/plugins.service.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright © 2024 Hexastack. All rights reserved.
|
||||
*
|
||||
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
|
||||
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
|
||||
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
|
||||
* 3. SaaS Restriction: This software, or any derivative of it, may not be used to offer a competing product or service (SaaS) without prior written consent from Hexastack. Offering the software as a service or using it in a commercial cloud environment without express permission is strictly prohibited.
|
||||
*/
|
||||
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { BasePlugin } from './base-plugin.service';
|
||||
import { PluginInstance } from './map-types';
|
||||
import { PluginType } from './types';
|
||||
|
||||
/**
|
||||
* @summary Service for managing and retrieving plugins.
|
||||
*
|
||||
* Plugins are dynamically loaded Nestjs providers.
|
||||
* They offer additional features to the app (example : custom blocks)
|
||||
*
|
||||
* @description
|
||||
* The `PluginService` is responsible for managing a registry of plugins that extend from a base plugin type.
|
||||
* It provides methods for adding, retrieving, and finding plugins based on a key or plugin identifier.
|
||||
* This service is generic and supports a default plugin type `BaseBlockPlugin`.
|
||||
*
|
||||
* @typeparam T - The plugin type, which extends from `BasePlugin`. By default, it uses `BaseBlockPlugin`.
|
||||
*/
|
||||
@Injectable()
|
||||
export class PluginService<T extends BasePlugin = BasePlugin> {
|
||||
/**
|
||||
* The registry of plugins, stored as a map where the first key is the type of plugin,
|
||||
* the second key is the name of the plugin and the value is a plugin of type `T`.
|
||||
*/
|
||||
private registry: Map<PluginType, Map<string, T>> = new Map(
|
||||
Object.keys(PluginType).map((t) => [t as PluginType, new Map()]),
|
||||
);
|
||||
|
||||
constructor() {}
|
||||
|
||||
/**
|
||||
* Registers a plugin with a given key.
|
||||
*
|
||||
* @param key The unique identifier for the plugin.
|
||||
* @param plugin The plugin instance to register.
|
||||
*/
|
||||
public setPlugin(type: PluginType, key: string, plugin: T) {
|
||||
const registry = this.registry.get(type);
|
||||
registry.set(key, plugin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all registered plugins as an array.
|
||||
*
|
||||
* @returns An array containing all the registered plugins.
|
||||
*/
|
||||
public getAllByType<PT extends PluginType>(type: PT): PluginInstance<PT>[] {
|
||||
const registry = this.registry.get(type);
|
||||
return Array.from(registry.values()) as PluginInstance<PT>[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a plugin based on its key.
|
||||
*
|
||||
* @param id The key used to register the plugin.
|
||||
*
|
||||
* @returns The plugin associated with the given key, or `undefined` if not found.
|
||||
*/
|
||||
public getPlugin<PT extends PluginType>(type: PT, id: string) {
|
||||
const registry = this.registry.get(type);
|
||||
const plugin = registry.get(id);
|
||||
return plugin ? (plugin as PluginInstance<PT>) : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a plugin by its internal `id` property.
|
||||
*
|
||||
* @param pluginId The unique `id` of the plugin to find.
|
||||
*
|
||||
* @returns The plugin with the matching `id`, or `undefined` if no plugin is found.
|
||||
*/
|
||||
public findPlugin<PT extends PluginType>(type: PT, pluginId: string) {
|
||||
return this.getAllByType(type).find((plugin) => {
|
||||
return plugin.id === pluginId;
|
||||
});
|
||||
}
|
||||
}
|
||||
40
api/src/plugins/types.ts
Normal file
40
api/src/plugins/types.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright © 2024 Hexastack. All rights reserved.
|
||||
*
|
||||
* Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms:
|
||||
* 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission.
|
||||
* 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file).
|
||||
* 3. SaaS Restriction: This software, or any derivative of it, may not be used to offer a competing product or service (SaaS) without prior written consent from Hexastack. Offering the software as a service or using it in a commercial cloud environment without express permission is strictly prohibited.
|
||||
*/
|
||||
|
||||
import { BlockCreateDto } from '@/chat/dto/block.dto';
|
||||
import { Block } from '@/chat/schemas/block.schema';
|
||||
import { Conversation } from '@/chat/schemas/conversation.schema';
|
||||
import { Setting } from '@/setting/schemas/setting.schema';
|
||||
|
||||
export enum PluginType {
|
||||
event = 'event',
|
||||
block = 'block',
|
||||
storage = 'storage',
|
||||
}
|
||||
|
||||
export interface CustomBlocks {}
|
||||
|
||||
type ChannelEvent = any;
|
||||
type BlockAttrs = Partial<BlockCreateDto> & { name: string };
|
||||
|
||||
export type PluginSetting = Omit<Setting, 'createdAt' | 'updatedAt'>;
|
||||
|
||||
export type PluginBlockTemplate = Omit<
|
||||
BlockAttrs,
|
||||
'message' | 'position' | 'builtin' | 'attachedBlock'
|
||||
>;
|
||||
|
||||
export type PluginEffects = {
|
||||
onStoreContextData?: (
|
||||
convo: Conversation,
|
||||
nextBlock: Block,
|
||||
event: ChannelEvent,
|
||||
captureVars: any,
|
||||
) => void;
|
||||
};
|
||||
Reference in New Issue
Block a user