- Add modules: ai, audit, billing-usage, biometrics, branches, dashboard, feature-flags, invoices, mcp, mobile, notifications, partners, payment-terminals, products, profiles, purchases, reports, sales, storage, warehouses, webhooks, whatsapp - Add controllers, DTOs, entities, and services for each module - Add shared services and utilities Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
382 lines
12 KiB
TypeScript
382 lines
12 KiB
TypeScript
import { Request, Response, NextFunction, Router } from 'express';
|
|
import { AIService, ConversationFilters } from '../services/ai.service';
|
|
|
|
export class AIController {
|
|
public router: Router;
|
|
|
|
constructor(private readonly aiService: AIService) {
|
|
this.router = Router();
|
|
this.initializeRoutes();
|
|
}
|
|
|
|
private initializeRoutes(): void {
|
|
// Models
|
|
this.router.get('/models', this.findAllModels.bind(this));
|
|
this.router.get('/models/:id', this.findModel.bind(this));
|
|
this.router.get('/models/code/:code', this.findModelByCode.bind(this));
|
|
this.router.get('/models/provider/:provider', this.findModelsByProvider.bind(this));
|
|
this.router.get('/models/type/:type', this.findModelsByType.bind(this));
|
|
|
|
// Prompts
|
|
this.router.get('/prompts', this.findAllPrompts.bind(this));
|
|
this.router.get('/prompts/:id', this.findPrompt.bind(this));
|
|
this.router.get('/prompts/code/:code', this.findPromptByCode.bind(this));
|
|
this.router.post('/prompts', this.createPrompt.bind(this));
|
|
this.router.patch('/prompts/:id', this.updatePrompt.bind(this));
|
|
|
|
// Conversations
|
|
this.router.get('/conversations', this.findConversations.bind(this));
|
|
this.router.get('/conversations/user/:userId', this.findUserConversations.bind(this));
|
|
this.router.get('/conversations/:id', this.findConversation.bind(this));
|
|
this.router.post('/conversations', this.createConversation.bind(this));
|
|
this.router.patch('/conversations/:id', this.updateConversation.bind(this));
|
|
this.router.post('/conversations/:id/archive', this.archiveConversation.bind(this));
|
|
|
|
// Messages
|
|
this.router.get('/conversations/:conversationId/messages', this.findMessages.bind(this));
|
|
this.router.post('/conversations/:conversationId/messages', this.addMessage.bind(this));
|
|
this.router.get('/conversations/:conversationId/tokens', this.getConversationTokenCount.bind(this));
|
|
|
|
// Usage & Quotas
|
|
this.router.post('/usage', this.logUsage.bind(this));
|
|
this.router.get('/usage/stats', this.getUsageStats.bind(this));
|
|
this.router.get('/quotas', this.getTenantQuota.bind(this));
|
|
this.router.patch('/quotas', this.updateTenantQuota.bind(this));
|
|
this.router.get('/quotas/check', this.checkQuotaAvailable.bind(this));
|
|
}
|
|
|
|
// ============================================
|
|
// MODELS
|
|
// ============================================
|
|
|
|
private async findAllModels(req: Request, res: Response, next: NextFunction): Promise<void> {
|
|
try {
|
|
const models = await this.aiService.findAllModels();
|
|
res.json({ data: models, total: models.length });
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
}
|
|
|
|
private async findModel(req: Request, res: Response, next: NextFunction): Promise<void> {
|
|
try {
|
|
const { id } = req.params;
|
|
const model = await this.aiService.findModel(id);
|
|
|
|
if (!model) {
|
|
res.status(404).json({ error: 'Model not found' });
|
|
return;
|
|
}
|
|
|
|
res.json({ data: model });
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
}
|
|
|
|
private async findModelByCode(req: Request, res: Response, next: NextFunction): Promise<void> {
|
|
try {
|
|
const { code } = req.params;
|
|
const model = await this.aiService.findModelByCode(code);
|
|
|
|
if (!model) {
|
|
res.status(404).json({ error: 'Model not found' });
|
|
return;
|
|
}
|
|
|
|
res.json({ data: model });
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
}
|
|
|
|
private async findModelsByProvider(req: Request, res: Response, next: NextFunction): Promise<void> {
|
|
try {
|
|
const { provider } = req.params;
|
|
const models = await this.aiService.findModelsByProvider(provider);
|
|
res.json({ data: models, total: models.length });
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
}
|
|
|
|
private async findModelsByType(req: Request, res: Response, next: NextFunction): Promise<void> {
|
|
try {
|
|
const { type } = req.params;
|
|
const models = await this.aiService.findModelsByType(type);
|
|
res.json({ data: models, total: models.length });
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
}
|
|
|
|
// ============================================
|
|
// PROMPTS
|
|
// ============================================
|
|
|
|
private async findAllPrompts(req: Request, res: Response, next: NextFunction): Promise<void> {
|
|
try {
|
|
const tenantId = req.headers['x-tenant-id'] as string;
|
|
const prompts = await this.aiService.findAllPrompts(tenantId);
|
|
res.json({ data: prompts, total: prompts.length });
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
}
|
|
|
|
private async findPrompt(req: Request, res: Response, next: NextFunction): Promise<void> {
|
|
try {
|
|
const { id } = req.params;
|
|
const prompt = await this.aiService.findPrompt(id);
|
|
|
|
if (!prompt) {
|
|
res.status(404).json({ error: 'Prompt not found' });
|
|
return;
|
|
}
|
|
|
|
res.json({ data: prompt });
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
}
|
|
|
|
private async findPromptByCode(req: Request, res: Response, next: NextFunction): Promise<void> {
|
|
try {
|
|
const { code } = req.params;
|
|
const tenantId = req.headers['x-tenant-id'] as string;
|
|
const prompt = await this.aiService.findPromptByCode(code, tenantId);
|
|
|
|
if (!prompt) {
|
|
res.status(404).json({ error: 'Prompt not found' });
|
|
return;
|
|
}
|
|
|
|
// Increment usage count
|
|
await this.aiService.incrementPromptUsage(prompt.id);
|
|
|
|
res.json({ data: prompt });
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
}
|
|
|
|
private async createPrompt(req: Request, res: Response, next: NextFunction): Promise<void> {
|
|
try {
|
|
const tenantId = req.headers['x-tenant-id'] as string;
|
|
const userId = req.headers['x-user-id'] as string;
|
|
|
|
const prompt = await this.aiService.createPrompt(tenantId, req.body, userId);
|
|
res.status(201).json({ data: prompt });
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
}
|
|
|
|
private async updatePrompt(req: Request, res: Response, next: NextFunction): Promise<void> {
|
|
try {
|
|
const { id } = req.params;
|
|
const userId = req.headers['x-user-id'] as string;
|
|
|
|
const prompt = await this.aiService.updatePrompt(id, req.body, userId);
|
|
|
|
if (!prompt) {
|
|
res.status(404).json({ error: 'Prompt not found' });
|
|
return;
|
|
}
|
|
|
|
res.json({ data: prompt });
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
}
|
|
|
|
// ============================================
|
|
// CONVERSATIONS
|
|
// ============================================
|
|
|
|
private async findConversations(req: Request, res: Response, next: NextFunction): Promise<void> {
|
|
try {
|
|
const tenantId = req.headers['x-tenant-id'] as string;
|
|
const filters: ConversationFilters = {
|
|
userId: req.query.userId as string,
|
|
modelId: req.query.modelId as string,
|
|
status: req.query.status as string,
|
|
};
|
|
|
|
if (req.query.startDate) filters.startDate = new Date(req.query.startDate as string);
|
|
if (req.query.endDate) filters.endDate = new Date(req.query.endDate as string);
|
|
|
|
const limit = parseInt(req.query.limit as string) || 50;
|
|
|
|
const conversations = await this.aiService.findConversations(tenantId, filters, limit);
|
|
res.json({ data: conversations, total: conversations.length });
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
}
|
|
|
|
private async findUserConversations(req: Request, res: Response, next: NextFunction): Promise<void> {
|
|
try {
|
|
const tenantId = req.headers['x-tenant-id'] as string;
|
|
const { userId } = req.params;
|
|
const limit = parseInt(req.query.limit as string) || 20;
|
|
|
|
const conversations = await this.aiService.findUserConversations(tenantId, userId, limit);
|
|
res.json({ data: conversations, total: conversations.length });
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
}
|
|
|
|
private async findConversation(req: Request, res: Response, next: NextFunction): Promise<void> {
|
|
try {
|
|
const { id } = req.params;
|
|
const conversation = await this.aiService.findConversation(id);
|
|
|
|
if (!conversation) {
|
|
res.status(404).json({ error: 'Conversation not found' });
|
|
return;
|
|
}
|
|
|
|
res.json({ data: conversation });
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
}
|
|
|
|
private async createConversation(req: Request, res: Response, next: NextFunction): Promise<void> {
|
|
try {
|
|
const tenantId = req.headers['x-tenant-id'] as string;
|
|
const userId = req.headers['x-user-id'] as string;
|
|
|
|
const conversation = await this.aiService.createConversation(tenantId, userId, req.body);
|
|
res.status(201).json({ data: conversation });
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
}
|
|
|
|
private async updateConversation(req: Request, res: Response, next: NextFunction): Promise<void> {
|
|
try {
|
|
const { id } = req.params;
|
|
const conversation = await this.aiService.updateConversation(id, req.body);
|
|
|
|
if (!conversation) {
|
|
res.status(404).json({ error: 'Conversation not found' });
|
|
return;
|
|
}
|
|
|
|
res.json({ data: conversation });
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
}
|
|
|
|
private async archiveConversation(req: Request, res: Response, next: NextFunction): Promise<void> {
|
|
try {
|
|
const { id } = req.params;
|
|
const archived = await this.aiService.archiveConversation(id);
|
|
|
|
if (!archived) {
|
|
res.status(404).json({ error: 'Conversation not found' });
|
|
return;
|
|
}
|
|
|
|
res.json({ data: { success: true } });
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
}
|
|
|
|
// ============================================
|
|
// MESSAGES
|
|
// ============================================
|
|
|
|
private async findMessages(req: Request, res: Response, next: NextFunction): Promise<void> {
|
|
try {
|
|
const { conversationId } = req.params;
|
|
const messages = await this.aiService.findMessages(conversationId);
|
|
res.json({ data: messages, total: messages.length });
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
}
|
|
|
|
private async addMessage(req: Request, res: Response, next: NextFunction): Promise<void> {
|
|
try {
|
|
const { conversationId } = req.params;
|
|
const message = await this.aiService.addMessage(conversationId, req.body);
|
|
res.status(201).json({ data: message });
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
}
|
|
|
|
private async getConversationTokenCount(req: Request, res: Response, next: NextFunction): Promise<void> {
|
|
try {
|
|
const { conversationId } = req.params;
|
|
const tokenCount = await this.aiService.getConversationTokenCount(conversationId);
|
|
res.json({ data: { tokenCount } });
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
}
|
|
|
|
// ============================================
|
|
// USAGE & QUOTAS
|
|
// ============================================
|
|
|
|
private async logUsage(req: Request, res: Response, next: NextFunction): Promise<void> {
|
|
try {
|
|
const tenantId = req.headers['x-tenant-id'] as string;
|
|
const log = await this.aiService.logUsage(tenantId, req.body);
|
|
res.status(201).json({ data: log });
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
}
|
|
|
|
private async getUsageStats(req: Request, res: Response, next: NextFunction): Promise<void> {
|
|
try {
|
|
const tenantId = req.headers['x-tenant-id'] as string;
|
|
const startDate = new Date(req.query.startDate as string || Date.now() - 30 * 24 * 60 * 60 * 1000);
|
|
const endDate = new Date(req.query.endDate as string || Date.now());
|
|
|
|
const stats = await this.aiService.getUsageStats(tenantId, startDate, endDate);
|
|
res.json({ data: stats });
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
}
|
|
|
|
private async getTenantQuota(req: Request, res: Response, next: NextFunction): Promise<void> {
|
|
try {
|
|
const tenantId = req.headers['x-tenant-id'] as string;
|
|
const quota = await this.aiService.getTenantQuota(tenantId);
|
|
res.json({ data: quota });
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
}
|
|
|
|
private async updateTenantQuota(req: Request, res: Response, next: NextFunction): Promise<void> {
|
|
try {
|
|
const tenantId = req.headers['x-tenant-id'] as string;
|
|
const quota = await this.aiService.updateTenantQuota(tenantId, req.body);
|
|
res.json({ data: quota });
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
}
|
|
|
|
private async checkQuotaAvailable(req: Request, res: Response, next: NextFunction): Promise<void> {
|
|
try {
|
|
const tenantId = req.headers['x-tenant-id'] as string;
|
|
const result = await this.aiService.checkQuotaAvailable(tenantId);
|
|
res.json({ data: result });
|
|
} catch (error) {
|
|
next(error);
|
|
}
|
|
}
|
|
}
|