UNPKG

adpa-enterprise-framework-automation

Version:

Modular, standards-compliant Node.js/TypeScript automation framework for enterprise requirements, project, and data management. Provides CLI and API for BABOK v3, PMBOK 7th Edition, and DMBOK 2.0 (in progress). Production-ready Express.js API with TypeSpe

724 lines 27.8 kB
import { v4 as uuidv4 } from 'uuid'; /** * Template Management API Controller * * Implements the TypeSpec-defined template management endpoints * with full CRUD operations for document templates. */ export class TemplateController { static templateRepository; /** * Set the TemplateRepository instance after DB connection is established */ static setTemplateRepository(repo) { TemplateController.templateRepository = repo; } /** * Create a new template * POST /api/v1/templates */ static async createTemplate(req, res, next) { try { const { name, description, category, tags, templateData, isActive } = req.body; const requestId = req.headers['x-request-id'] || uuidv4(); const userId = req.user?.id || 'api-user'; // Create template using actual TemplateRepository const templateRecord = { name, description: description || `API-created template: ${name}`, category: category || 'api-created', template_type: 'api_created', ai_instructions: templateData?.aiInstructions || `Generate a ${name}`, prompt_template: templateData?.content || `# ${name}\n\n{{content}}`, generation_function: 'getAiGenericDocument', metadata: { tags: tags || [], variables: templateData?.variables || [], layout: templateData?.layout || {}, emoji: '🆕', priority: 200, source: 'api' }, version: 1, is_active: isActive !== false, is_system: false, created_by: userId }; const createdTemplate = await TemplateController.templateRepository.createTemplate(templateRecord); // Convert to API format const apiTemplate = { id: createdTemplate.id, name: createdTemplate.name, description: createdTemplate.description, category: createdTemplate.category, tags: createdTemplate.metadata?.tags || [], templateData: { content: createdTemplate.prompt_template, aiInstructions: createdTemplate.ai_instructions, variables: createdTemplate.metadata?.variables || [], layout: createdTemplate.metadata?.layout || {} }, isActive: createdTemplate.is_active, createdBy: createdTemplate.created_by, createdAt: createdTemplate.created_at, updatedAt: createdTemplate.updated_at, version: createdTemplate.version, templateType: createdTemplate.template_type }; console.log('Template created:', { requestId, templateId: createdTemplate.id, name, userId, timestamp: new Date().toISOString() }); res.status(201).json({ success: true, data: apiTemplate, timestamp: new Date().toISOString(), requestId }); } catch (error) { console.error('Error creating template:', error); next(error); } } /** * Get a template by ID * GET /api/v1/templates/:templateId */ static async getTemplate(req, res, next) { try { const { templateId } = req.params; const requestId = req.headers['x-request-id'] || uuidv4(); const userId = req.user?.id; if (!TemplateController.templateRepository) { return res.status(500).json({ success: false, error: { code: 'REPOSITORY_NOT_INITIALIZED', message: 'Template repository is not initialized. Please try again later.' }, requestId, timestamp: new Date().toISOString() }); } const template = await TemplateController.templateRepository.getTemplateById(templateId); if (template) { // Convert database template to API format const apiTemplate = { id: template.id, name: template.name, description: template.description, category: template.category, tags: template.metadata?.tags || [], templateData: { content: template.prompt_template, aiInstructions: template.ai_instructions, variables: template.metadata?.variables || [], layout: template.metadata?.layout || {} }, isActive: template.is_active, createdBy: template.created_by, createdAt: template.created_at, updatedAt: template.updated_at, version: template.version, templateType: template.template_type }; res.json({ success: true, data: apiTemplate, timestamp: new Date().toISOString(), requestId }); } else { res.status(404).json({ success: false, error: { code: 'TEMPLATE_NOT_FOUND', message: 'Template not found', timestamp: new Date().toISOString() }, requestId }); } } catch (error) { next(error); } } /** * Update a template * PUT /api/v1/templates/:templateId */ static async updateTemplate(req, res, next) { try { const { templateId } = req.params; const updates = req.body; const requestId = req.headers['x-request-id'] || uuidv4(); const userId = req.user?.id; // TODO: Implement actual template update in database console.log('Template update requested:', { requestId, templateId, updates: Object.keys(updates), userId, timestamp: new Date().toISOString() }); // Mock successful update const updatedTemplate = { id: templateId, ...updates, updatedAt: new Date().toISOString(), version: 2 }; res.json({ success: true, data: updatedTemplate, timestamp: new Date().toISOString(), requestId }); } catch (error) { next(error); } } /** * Delete a template * DELETE /api/v1/templates/:templateId */ static async deleteTemplate(req, res, next) { try { const { templateId } = req.params; const requestId = req.headers['x-request-id'] || uuidv4(); const userId = req.user?.id; // TODO: Implement actual template deletion in database console.log('Template deletion requested:', { requestId, templateId, userId, timestamp: new Date().toISOString() }); res.json({ success: true, message: 'Template deleted successfully', data: { templateId, deletedAt: new Date().toISOString() }, timestamp: new Date().toISOString(), requestId }); } catch (error) { next(error); } } /** * List templates with filtering and pagination * GET /api/v1/templates */ static async listTemplates(req, res, next) { try { const { page = 1, limit = 20, sort = 'created', order = 'desc', category, tag, search, isActive } = req.query; const requestId = req.headers['x-request-id'] || uuidv4(); if (!TemplateController.templateRepository) { return res.status(500).json({ success: false, error: { code: 'REPOSITORY_NOT_INITIALIZED', message: 'Template repository is not initialized. Please try again later.' }, requestId, timestamp: new Date().toISOString() }); } const searchQuery = { category: category, search_text: search, is_system: undefined, // Include both system and user templates limit: parseInt(limit), offset: (parseInt(page) - 1) * parseInt(limit) }; const templates = await TemplateController.templateRepository.searchTemplates(searchQuery); // Convert to API format const apiTemplates = templates.map((template) => ({ id: template.id, name: template.name, description: template.description, category: template.category, tags: template.metadata?.tags || [], isActive: template.is_active, createdAt: template.created_at, updatedAt: template.updated_at, templateType: template.template_type, version: template.version })); console.log('Template list requested:', { requestId, filters: { category, tag, search, isActive }, pagination: { page, limit, sort, order }, resultCount: apiTemplates.length, timestamp: new Date().toISOString() }); res.json({ success: true, data: apiTemplates, pagination: { page: parseInt(page), limit: parseInt(limit), total: apiTemplates.length, pages: Math.ceil(apiTemplates.length / parseInt(limit)) }, timestamp: new Date().toISOString(), requestId }); } catch (error) { next(error); } } /** * Validate a template * POST /api/v1/templates/validate */ static async validateTemplate(req, res, next) { try { const { templateData, testData } = req.body; const requestId = req.headers['x-request-id'] || uuidv4(); // TODO: Implement actual template validation logic console.log('Template validation requested:', { requestId, hasTemplateData: !!templateData, hasTestData: !!testData, timestamp: new Date().toISOString() }); // Mock validation results const validationResult = { isValid: true, errors: [], warnings: [], variables: templateData.variables || [], testResult: testData ? { success: true, renderedContent: 'Rendered template content would appear here', processingTime: '125ms' } : null }; res.json({ success: true, data: validationResult, timestamp: new Date().toISOString(), requestId }); } catch (error) { next(error); } } /** * Preview a template with sample data * POST /api/v1/templates/{templateId}/preview */ static async previewTemplate(req, res, next) { try { const { templateId } = req.params; const { sampleData } = req.body; const requestId = req.headers['x-request-id'] || uuidv4(); // TODO: Implement actual template preview logic const previewResult = { templateId, previewHtml: `<div>Preview of template ${templateId} with sample data</div>`, sampleData: sampleData || {}, generatedAt: new Date().toISOString() }; console.log('Template preview generated:', { requestId, templateId, timestamp: new Date().toISOString() }); res.json({ success: true, data: previewResult, timestamp: new Date().toISOString(), requestId }); } catch (error) { next(error); } } /** * Clone an existing template * POST /api/v1/templates/{templateId}/clone */ static async cloneTemplate(req, res, next) { try { const { templateId } = req.params; const { name, description } = req.body; const requestId = req.headers['x-request-id'] || uuidv4(); const userId = req.user?.id || 'anonymous'; // TODO: Implement actual template cloning logic const newTemplateId = uuidv4(); const createdAt = new Date().toISOString(); const clonedTemplate = { id: newTemplateId, name: name || `Copy of Template ${templateId}`, description: description || `Cloned from template ${templateId}`, originalTemplateId: templateId, category: 'general', tags: ['cloned'], templateData: {}, isActive: true, createdBy: userId, createdAt, updatedAt: createdAt, version: 1 }; console.log('Template cloned:', { requestId, originalTemplateId: templateId, newTemplateId, userId, timestamp: new Date().toISOString() }); res.status(201).json({ success: true, data: clonedTemplate, timestamp: new Date().toISOString(), requestId }); } catch (error) { next(error); } } /** * Get template usage statistics * GET /api/v1/templates/{templateId}/stats */ static async getTemplateStats(req, res, next) { try { const { templateId } = req.params; const requestId = req.headers['x-request-id'] || uuidv4(); // TODO: Implement actual template statistics retrieval const stats = { templateId, usageCount: Math.floor(Math.random() * 100), lastUsed: new Date(Date.now() - Math.floor(Math.random() * 30) * 24 * 60 * 60 * 1000).toISOString(), averageProcessingTime: Math.floor(Math.random() * 5000) + 1000, // ms successRate: 0.95, failureCount: Math.floor(Math.random() * 5), popularOutputFormats: ['pdf', 'docx', 'html'], generatedAt: new Date().toISOString() }; console.log('Template stats retrieved:', { requestId, templateId, timestamp: new Date().toISOString() }); res.json({ success: true, data: stats, timestamp: new Date().toISOString(), requestId }); } catch (error) { next(error); } } /** * Get overall template statistics (for admin dashboard) * GET /api/v1/templates/stats */ static async getOverallTemplateStats(req, res, next) { try { const requestId = req.headers['x-request-id'] || uuidv4(); if (!TemplateController.templateRepository) { return res.status(500).json({ success: false, error: { code: 'REPOSITORY_NOT_INITIALIZED', message: 'Template repository is not initialized. Please try again later.' }, requestId, timestamp: new Date().toISOString() }); } // Get all templates to calculate stats const templates = await TemplateController.templateRepository.getAllTemplates(); // Calculate statistics const totalTemplates = templates.length; const categoriesMap = new Map(); const tagsMap = new Map(); templates.forEach((template) => { // Count categories if (template.category) { categoriesMap.set(template.category, (categoriesMap.get(template.category) || 0) + 1); } // Count tags if (template.metadata?.tags && Array.isArray(template.metadata.tags)) { template.metadata.tags.forEach((tag) => { tagsMap.set(tag, (tagsMap.get(tag) || 0) + 1); }); } }); // Convert to arrays and sort const topCategories = Array.from(categoriesMap.entries()) .map(([category, count]) => ({ category, count })) .sort((a, b) => b.count - a.count); const topTags = Array.from(tagsMap.entries()) .map(([tag, count]) => ({ tag, count })) .sort((a, b) => b.count - a.count); const stats = { totalTemplates, categoriesCount: categoriesMap.size, topCategories, topTags, recentActivity: templates .filter((template) => template.updated_at) .sort((a, b) => new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime()) .slice(0, 10) .map((template) => ({ type: 'updated', templateName: template.name, timestamp: template.updated_at })) }; console.log('Overall template stats retrieved:', { requestId, totalTemplates, timestamp: new Date().toISOString() }); res.json({ success: true, data: stats, timestamp: new Date().toISOString(), requestId }); } catch (error) { console.error('Error getting template stats:', error); next(error); } } /** * Get template categories * GET /api/v1/templates/categories */ static async getTemplateCategories(req, res, next) { try { const requestId = req.headers['x-request-id'] || uuidv4(); if (!TemplateController.templateRepository) { return res.status(500).json({ success: false, error: { code: 'REPOSITORY_NOT_INITIALIZED', message: 'Template repository is not initialized. Please try again later.' }, requestId, timestamp: new Date().toISOString() }); } const templates = await TemplateController.templateRepository.getAllTemplates(); const categories = [...new Set(templates.map((t) => t.category).filter(Boolean))].sort(); res.json({ success: true, data: categories, timestamp: new Date().toISOString(), requestId }); } catch (error) { console.error('Error getting template categories:', error); next(error); } } /** * Get template tags * GET /api/v1/templates/tags */ static async getTemplateTags(req, res, next) { try { const requestId = req.headers['x-request-id'] || uuidv4(); if (!TemplateController.templateRepository) { return res.status(500).json({ success: false, error: { code: 'REPOSITORY_NOT_INITIALIZED', message: 'Template repository is not initialized. Please try again later.' }, requestId, timestamp: new Date().toISOString() }); } const templates = await TemplateController.templateRepository.getAllTemplates(); const tagsSet = new Set(); templates.forEach((template) => { if (template.metadata?.tags && Array.isArray(template.metadata.tags)) { template.metadata.tags.forEach((tag) => tagsSet.add(tag)); } }); const tags = [...tagsSet].sort(); res.json({ success: true, data: tags, timestamp: new Date().toISOString(), requestId }); } catch (error) { console.error('Error getting template tags:', error); next(error); } } /** * Bulk delete templates * POST /api/v1/templates/bulk-delete */ static async bulkDeleteTemplates(req, res, next) { try { const { ids } = req.body; const requestId = req.headers['x-request-id'] || uuidv4(); if (!Array.isArray(ids)) { return res.status(400).json({ success: false, error: 'ids must be an array', timestamp: new Date().toISOString(), requestId }); } let deleted = 0; for (const id of ids) { try { if (!TemplateController.templateRepository) { throw new Error('Template repository is not initialized.'); } await TemplateController.templateRepository.deleteTemplate(id); deleted++; } catch (error) { console.error(`Failed to delete template ${id}:`, error); } } res.json({ success: true, data: { deleted }, timestamp: new Date().toISOString(), requestId }); } catch (error) { console.error('Error bulk deleting templates:', error); next(error); } } /** * Export templates * GET /api/v1/templates/export */ static async exportTemplates(req, res, next) { try { const { ids } = req.query; const requestId = req.headers['x-request-id'] || uuidv4(); let templates; if (ids && typeof ids === 'string') { const templateIds = ids.split(','); templates = []; for (const id of templateIds) { try { if (!TemplateController.templateRepository) { throw new Error('Template repository is not initialized.'); } const template = await TemplateController.templateRepository.getTemplateById(id); if (template) templates.push(template); } catch (error) { console.error(`Failed to get template ${id}:`, error); } } } else { if (!TemplateController.templateRepository) { throw new Error('Template repository is not initialized.'); } templates = await TemplateController.templateRepository.getAllTemplates(); } res.json({ success: true, data: templates, timestamp: new Date().toISOString(), requestId }); } catch (error) { console.error('Error exporting templates:', error); next(error); } } /** * Import templates * POST /api/v1/templates/import */ static async importTemplates(req, res, next) { try { const { templates } = req.body; const requestId = req.headers['x-request-id'] || uuidv4(); const userId = req.user?.id || 'api-user'; if (!Array.isArray(templates)) { return res.status(400).json({ success: false, error: 'templates must be an array', timestamp: new Date().toISOString(), requestId }); } let imported = 0; const errors = []; for (const templateData of templates) { try { const templateRecord = { name: templateData.name, description: templateData.description || `Imported template: ${templateData.name}`, category: templateData.category || 'imported', template_type: 'imported', ai_instructions: templateData.aiInstructions || `Generate a ${templateData.name}`, prompt_template: templateData.content || `# ${templateData.name}\n\n{{content}}`, generation_function: 'getAiGenericDocument', metadata: { tags: templateData.tags || [], variables: templateData.variables || [], source: 'import' }, version: 1, is_active: true, is_system: false, created_by: userId }; if (!TemplateController.templateRepository) { throw new Error('Template repository is not initialized.'); } await TemplateController.templateRepository.createTemplate(templateRecord); imported++; } catch (error) { errors.push(`Failed to import template "${templateData.name}": ${error instanceof Error ? error.message : 'Unknown error'}`); } } res.json({ success: true, data: { imported, errors }, timestamp: new Date().toISOString(), requestId }); } catch (error) { console.error('Error importing templates:', error); next(error); } } } //# sourceMappingURL=TemplateController.js.map