diff --git a/api/src/channel/channel.service.ts b/api/src/channel/channel.service.ts index 00c987c00..3ddf04082 100644 --- a/api/src/channel/channel.service.ts +++ b/api/src/channel/channel.service.ts @@ -6,13 +6,18 @@ * 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). */ -import { Injectable, UnauthorizedException } from '@nestjs/common'; +import { + Injectable, + OnApplicationBootstrap, + UnauthorizedException, +} from '@nestjs/common'; import { Request, Response } from 'express'; import { SubscriberService } from '@/chat/services/subscriber.service'; 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 { SettingService } from '@/setting/services/setting.service'; import { getSessionStore } from '@/utils/constants/session-store'; import { SocketGet, @@ -27,14 +32,42 @@ import ChannelHandler from './lib/Handler'; import { ChannelName } from './types'; @Injectable() -export class ChannelService { +export class ChannelService implements OnApplicationBootstrap { private registry: Map> = new Map(); constructor( private readonly logger: LoggerService, + private readonly settingService: SettingService, private readonly subscriberService: SubscriberService, ) {} + async onApplicationBootstrap(): Promise { + await this.cleanup(); + } + + /** + * Cleanups the unregisterd channels from settings. + * + */ + async cleanup(): Promise { + const activePlugins = this.getAll().map((handler) => + handler.getNamespace(), + ); + + const channelSettings = (await this.settingService.getAllGroups()).filter( + (group) => group.split('_').pop() === 'channel', + ) as HyphenToUnderscore[]; + + const orphanSettings = channelSettings.filter( + (group) => !activePlugins.includes(group), + ); + + for (const group of orphanSettings) { + this.logger.log(`Deleting orphaned settings for ${group}...`); + await this.settingService.deleteGroup(group); + } + } + /** * Registers a channel with a specific handler. * diff --git a/api/src/extra/index.ts b/api/src/extra/index.ts index 1c6f0b7d0..d773e1360 100644 --- a/api/src/extra/index.ts +++ b/api/src/extra/index.ts @@ -1,18 +1,9 @@ /* - * Copyright © 2025 Hexastack. All rights reserved. + * 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). */ -import { Module } from '@nestjs/common'; - -import { CleanupService } from './cleanup.service'; - -@Module({ - providers: [CleanupService], -}) -class CleanupModule {} - -export default [CleanupModule]; +export default []; diff --git a/api/src/helper/helper.service.ts b/api/src/helper/helper.service.ts index db0a88008..285784923 100644 --- a/api/src/helper/helper.service.ts +++ b/api/src/helper/helper.service.ts @@ -6,7 +6,11 @@ * 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). */ -import { Injectable, InternalServerErrorException } from '@nestjs/common'; +import { + Injectable, + InternalServerErrorException, + OnApplicationBootstrap, +} from '@nestjs/common'; import { LoggerService } from '@/logger/logger.service'; import { SettingService } from '@/setting/services/setting.service'; @@ -15,7 +19,7 @@ import BaseHelper from './lib/base-helper'; import { HelperName, HelperRegistry, HelperType, TypeOfHelper } from './types'; @Injectable() -export class HelperService { +export class HelperService implements OnApplicationBootstrap { private registry: HelperRegistry = new Map(); constructor( @@ -28,6 +32,33 @@ export class HelperService { }); } + async onApplicationBootstrap(): Promise { + await this.cleanup(); + } + + /** + * Cleanups the unregisterd helpers from settings. + * + */ + async cleanup(): Promise { + const activeHelpers = this.getAll().map((handler) => + handler.getNamespace(), + ); + + const helperSettings = (await this.settingService.getAllGroups()).filter( + (group) => group.split('_').pop() === 'helper', + ) as HyphenToUnderscore[]; + + const orphanSettings = helperSettings.filter( + (group) => !activeHelpers.includes(group), + ); + + for (const group of orphanSettings) { + this.logger.log(`Deleting orphaned settings for ${group}...`); + await this.settingService.deleteGroup(group); + } + } + /** * Registers a helper. * diff --git a/api/src/plugins/plugins.service.ts b/api/src/plugins/plugins.service.ts index 8a2667c94..51edc39e7 100644 --- a/api/src/plugins/plugins.service.ts +++ b/api/src/plugins/plugins.service.ts @@ -6,7 +6,14 @@ * 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). */ -import { Injectable, InternalServerErrorException } from '@nestjs/common'; +import { + Injectable, + InternalServerErrorException, + OnApplicationBootstrap, +} from '@nestjs/common'; + +import { LoggerService } from '@/logger/logger.service'; +import { SettingService } from '@/setting/services/setting.service'; import { BasePlugin } from './base-plugin.service'; import { PluginInstance } from './map-types'; @@ -26,7 +33,9 @@ import { PluginName, PluginType } from './types'; * @typeparam T - The plugin type, which extends from `BasePlugin`. By default, it uses `BaseBlockPlugin`. */ @Injectable() -export class PluginService { +export class PluginService + implements OnApplicationBootstrap +{ /** * 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`. @@ -35,7 +44,37 @@ export class PluginService { Object.keys(PluginType).map((t) => [t as PluginType, new Map()]), ); - constructor() {} + constructor( + private readonly settingService: SettingService, + private readonly logger: LoggerService, + ) {} + + async onApplicationBootstrap(): Promise { + await this.cleanup(); + } + + /** + * Cleanups the unregisterd plugins from settings. + * + */ + async cleanup(): Promise { + const activePlugins = this.getAll().map((handler) => + handler.getNamespace(), + ); + + const pluginSettings = (await this.settingService.getAllGroups()).filter( + (group) => group.split('_').pop() === 'plugin', + ) as HyphenToUnderscore[]; + + const orphanSettings = pluginSettings.filter( + (group) => !activePlugins.includes(group), + ); + + for (const group of orphanSettings) { + this.logger.log(`Deleting orphaned settings for ${group}...`); + await this.settingService.deleteGroup(group); + } + } /** * Registers a plugin with a given name.