// src/rules-engine/rules-engine.controller.ts import { Controller, Get, Post, Put, Delete, Patch, Param, Body, HttpException, Logger } from '@nestjs/common'; import { RulesStorageService } from './rules-storage.service'; import { RulesEngineService } from './rules-engine.service'; import type { Rule, PriorityConfig } from './types/rule.types'; import { readFileSync } from 'fs'; import { join } from 'path'; @Controller('api/rules') export class RulesEngineController { private readonly logger = new Logger(RulesEngineController.name); constructor( private readonly storage: RulesStorageService, private readonly engine: RulesEngineService, ) {} // --- Priority Config (slider UI) --- @Get('priority-config') async getPriorityConfig() { return this.storage.getPriorityConfig(); } @Put('priority-config') async updatePriorityConfig(@Body() body: PriorityConfig) { return this.storage.updatePriorityConfig(body); } // --- Rule CRUD --- @Get() async listRules() { return this.storage.getAll(); } @Get(':id') async getRule(@Param('id') id: string) { const rule = await this.storage.getById(id); if (!rule) throw new HttpException('Rule not found', 404); return rule; } @Post() async createRule(@Body() body: any) { if (!body.name || !body.trigger || !body.conditions || !body.action) { throw new HttpException('name, trigger, conditions, and action are required', 400); } return this.storage.create({ ...body, ruleType: body.ruleType ?? 'priority', enabled: body.enabled ?? true, priority: body.priority ?? 99, }); } @Put(':id') async updateRule(@Param('id') id: string, @Body() body: Partial) { const updated = await this.storage.update(id, body); if (!updated) throw new HttpException('Rule not found', 404); return updated; } @Delete(':id') async deleteRule(@Param('id') id: string) { const deleted = await this.storage.delete(id); if (!deleted) throw new HttpException('Rule not found', 404); return { status: 'ok' }; } @Patch(':id/toggle') async toggleRule(@Param('id') id: string) { const toggled = await this.storage.toggle(id); if (!toggled) throw new HttpException('Rule not found', 404); return toggled; } @Post('reorder') async reorderRules(@Body() body: { ids: string[] }) { if (!body.ids?.length) throw new HttpException('ids array required', 400); return this.storage.reorder(body.ids); } // --- Evaluation --- @Post('evaluate') async evaluate(@Body() body: { trigger: string; triggerValue: string; facts: Record }) { return this.engine.evaluate(body.trigger, body.triggerValue, body.facts); } // --- Templates --- @Get('templates/list') async listTemplates() { return [{ id: 'hospital-starter', name: 'Hospital Starter Pack', description: 'Default rules for a hospital call center', ruleCount: 7 }]; } @Post('templates/:id/apply') async applyTemplate(@Param('id') id: string) { if (id !== 'hospital-starter') throw new HttpException('Template not found', 404); let template: any; try { template = JSON.parse(readFileSync(join(__dirname, 'templates', 'hospital-starter.json'), 'utf8')); } catch { throw new HttpException('Failed to load template', 500); } // Apply priority config await this.storage.updatePriorityConfig(template.priorityConfig); // Create rules const created: Rule[] = []; for (const rule of template.rules) { const newRule = await this.storage.create(rule); created.push(newRule); } this.logger.log(`Applied hospital-starter template: ${created.length} rules + priority config`); return { status: 'ok', rulesCreated: created.length, rules: created }; } }