feat: implement administrative control panel with real-time bot actions

This commit is contained in:
syntaxbullet
2026-01-08 21:19:16 +01:00
parent 3f3a6c88e8
commit 0f6cce9b6e
14 changed files with 499 additions and 47 deletions

View File

@@ -0,0 +1,66 @@
import { describe, expect, test, mock, beforeEach, spyOn } from "bun:test";
import { actionService } from "./action.service";
import { systemEvents, EVENTS } from "@shared/lib/events";
import { dashboardService } from "@shared/modules/dashboard/dashboard.service";
describe("ActionService", () => {
beforeEach(() => {
// Clear any previous mock state
mock.restore();
});
/**
* Test Case: Command Reload
* Requirement: Emits event and records to dashboard
*/
test("reloadCommands should emit RELOAD_COMMANDS event and record dashboard event", async () => {
const emitSpy = spyOn(systemEvents, "emit");
const recordSpy = spyOn(dashboardService, "recordEvent").mockImplementation(() => Promise.resolve());
const result = await actionService.reloadCommands();
expect(result.success).toBe(true);
expect(emitSpy).toHaveBeenCalledWith(EVENTS.ACTIONS.RELOAD_COMMANDS);
expect(recordSpy).toHaveBeenCalledWith(expect.objectContaining({
type: "info",
message: "Admin: Triggered command reload"
}));
});
/**
* Test Case: Cache Clearance
* Requirement: Emits event and records to dashboard
*/
test("clearCache should emit CLEAR_CACHE event and record dashboard event", async () => {
const emitSpy = spyOn(systemEvents, "emit");
const recordSpy = spyOn(dashboardService, "recordEvent").mockImplementation(() => Promise.resolve());
const result = await actionService.clearCache();
expect(result.success).toBe(true);
expect(emitSpy).toHaveBeenCalledWith(EVENTS.ACTIONS.CLEAR_CACHE);
expect(recordSpy).toHaveBeenCalledWith(expect.objectContaining({
type: "info",
message: "Admin: Triggered cache clearance"
}));
});
/**
* Test Case: Maintenance Mode Toggle
* Requirement: Emits event with correct payload and records to dashboard with warning type
*/
test("toggleMaintenanceMode should emit MAINTENANCE_MODE event and record dashboard event", async () => {
const emitSpy = spyOn(systemEvents, "emit");
const recordSpy = spyOn(dashboardService, "recordEvent").mockImplementation(() => Promise.resolve());
const result = await actionService.toggleMaintenanceMode(true, "Test Reason");
expect(result.success).toBe(true);
expect(result.enabled).toBe(true);
expect(emitSpy).toHaveBeenCalledWith(EVENTS.ACTIONS.MAINTENANCE_MODE, { enabled: true, reason: "Test Reason" });
expect(recordSpy).toHaveBeenCalledWith(expect.objectContaining({
type: "warn",
message: "Admin: Maintenance mode ENABLED (Test Reason)"
}));
});
});

View File

@@ -0,0 +1,53 @@
import { systemEvents, EVENTS } from "@shared/lib/events";
import { dashboardService } from "@shared/modules/dashboard/dashboard.service";
/**
* Service to handle administrative actions triggered from the dashboard.
* These actions are broadcasted to the bot via the system event bus.
*/
export const actionService = {
/**
* Triggers a reload of all bot commands.
*/
reloadCommands: async () => {
systemEvents.emit(EVENTS.ACTIONS.RELOAD_COMMANDS);
await dashboardService.recordEvent({
type: "info",
message: "Admin: Triggered command reload",
icon: "♻️"
});
return { success: true, message: "Command reload triggered" };
},
/**
* Triggers a clearance of internal bot caches.
*/
clearCache: async () => {
systemEvents.emit(EVENTS.ACTIONS.CLEAR_CACHE);
await dashboardService.recordEvent({
type: "info",
message: "Admin: Triggered cache clearance",
icon: "🧹"
});
return { success: true, message: "Cache clearance triggered" };
},
/**
* Toggles maintenance mode for the bot.
*/
toggleMaintenanceMode: async (enabled: boolean, reason?: string) => {
systemEvents.emit(EVENTS.ACTIONS.MAINTENANCE_MODE, { enabled, reason });
await dashboardService.recordEvent({
type: enabled ? "warn" : "info",
message: `Admin: Maintenance mode ${enabled ? "ENABLED" : "DISABLED"}${reason ? ` (${reason})` : ""}`,
icon: "🛠️"
});
return { success: true, enabled, message: `Maintenance mode ${enabled ? "enabled" : "disabled"}` };
}
};