feat: initial commit

This commit is contained in:
Mohamed Marrouchi
2024-09-10 10:50:11 +01:00
commit 30e5766487
879 changed files with 122820 additions and 0 deletions

View 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>;
}

View 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);
}
}

View 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);
}
}

View 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>;
}

View 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;

View 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 {}

View 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);
});
});
});

View 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
View 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;
};