const express = require('express'); const router = express.Router(); // Get all AI prompts router.get('/', async (req, res) => { try { const pool = req.app.locals.pool; if (!pool) { throw new Error('Database pool not initialized'); } const result = await pool.query(` SELECT * FROM ai_prompts ORDER BY prompt_type ASC, company ASC `); res.json(result.rows); } catch (error) { console.error('Error fetching AI prompts:', error); res.status(500).json({ error: 'Failed to fetch AI prompts', details: error instanceof Error ? error.message : 'Unknown error' }); } }); // Get prompt by ID router.get('/:id', async (req, res) => { try { const { id } = req.params; const pool = req.app.locals.pool; if (!pool) { throw new Error('Database pool not initialized'); } const result = await pool.query(` SELECT * FROM ai_prompts WHERE id = $1 `, [id]); if (result.rows.length === 0) { return res.status(404).json({ error: 'AI prompt not found' }); } res.json(result.rows[0]); } catch (error) { console.error('Error fetching AI prompt:', error); res.status(500).json({ error: 'Failed to fetch AI prompt', details: error instanceof Error ? error.message : 'Unknown error' }); } }); // Get prompt by type (any prompt_type value - extensible) router.get('/by-type', async (req, res) => { try { const { type, company } = req.query; const pool = req.app.locals.pool; if (!pool) { throw new Error('Database pool not initialized'); } // Validate type is provided if (!type || typeof type !== 'string' || type.trim().length === 0) { return res.status(400).json({ error: 'Valid type query parameter is required' }); } // For company_specific types, company ID is required const isCompanySpecificType = type.endsWith('_company_specific'); if (isCompanySpecificType && !company) { return res.status(400).json({ error: 'Company ID is required for company_specific prompt types' }); } // For non-company-specific types, company should not be provided if (!isCompanySpecificType && company) { return res.status(400).json({ error: 'Company ID should not be provided for non-company-specific prompt types' }); } // Build the query based on whether company is provided let query, params; if (company) { query = 'SELECT * FROM ai_prompts WHERE prompt_type = $1 AND company = $2'; params = [type.trim(), company]; } else { query = 'SELECT * FROM ai_prompts WHERE prompt_type = $1 AND company IS NULL'; params = [type.trim()]; } // Execute the query const result = await pool.query(query, params); // Check if any prompt was found if (result.rows.length === 0) { const errorMessage = company ? `AI prompt '${type}' not found for company ${company}` : `AI prompt '${type}' not found`; return res.status(404).json({ error: errorMessage }); } // Return the first matching prompt res.json(result.rows[0]); } catch (error) { console.error('Error fetching AI prompt by type:', error); res.status(500).json({ error: 'Failed to fetch AI prompt', details: error instanceof Error ? error.message : 'Unknown error' }); } }); // Create new AI prompt router.post('/', async (req, res) => { try { const { prompt_text, prompt_type, company } = req.body; // Validate required fields if (!prompt_text || !prompt_type) { return res.status(400).json({ error: 'Prompt text and type are required' }); } // Validate prompt_type is a non-empty string (no hardcoded list - extensible) if (typeof prompt_type !== 'string' || prompt_type.trim().length === 0) { return res.status(400).json({ error: 'Prompt type must be a non-empty string' }); } // For company-specific types (ending with _company_specific), require company const isCompanySpecificType = prompt_type.endsWith('_company_specific'); if (isCompanySpecificType && !company) { return res.status(400).json({ error: 'Company is required for company-specific prompt types' }); } // For non-company-specific types, company should not be provided if (!isCompanySpecificType && company) { return res.status(400).json({ error: 'Company should not be provided for non-company-specific prompt types' }); } const pool = req.app.locals.pool; if (!pool) { throw new Error('Database pool not initialized'); } const result = await pool.query(` INSERT INTO ai_prompts ( prompt_text, prompt_type, company ) VALUES ($1, $2, $3) RETURNING * `, [ prompt_text, prompt_type.trim(), company || null ]); res.status(201).json(result.rows[0]); } catch (error) { console.error('Error creating AI prompt:', error); // Check for unique constraint violations if (error instanceof Error && error.message.includes('unique')) { if (error.message.includes('idx_singleton_with_company')) { return res.status(409).json({ error: 'A prompt of this type already exists for this company', details: error.message }); } else if (error.message.includes('idx_singleton_no_company')) { return res.status(409).json({ error: 'A prompt of this type already exists', details: error.message }); } } res.status(500).json({ error: 'Failed to create AI prompt', details: error instanceof Error ? error.message : 'Unknown error' }); } }); // Update AI prompt router.put('/:id', async (req, res) => { try { const { id } = req.params; const { prompt_text, prompt_type, company } = req.body; // Validate required fields if (!prompt_text || !prompt_type) { return res.status(400).json({ error: 'Prompt text and type are required' }); } // Validate prompt_type is a non-empty string (no hardcoded list - extensible) if (typeof prompt_type !== 'string' || prompt_type.trim().length === 0) { return res.status(400).json({ error: 'Prompt type must be a non-empty string' }); } // For company-specific types, require company const isCompanySpecificType = prompt_type.endsWith('_company_specific'); if (isCompanySpecificType && !company) { return res.status(400).json({ error: 'Company is required for company-specific prompt types' }); } // For non-company-specific types, company should not be provided if (!isCompanySpecificType && company) { return res.status(400).json({ error: 'Company should not be provided for non-company-specific prompt types' }); } const pool = req.app.locals.pool; if (!pool) { throw new Error('Database pool not initialized'); } // Check if the prompt exists const checkResult = await pool.query('SELECT * FROM ai_prompts WHERE id = $1', [id]); if (checkResult.rows.length === 0) { return res.status(404).json({ error: 'AI prompt not found' }); } const result = await pool.query(` UPDATE ai_prompts SET prompt_text = $1, prompt_type = $2, company = $3, updated_at = CURRENT_TIMESTAMP WHERE id = $4 RETURNING * `, [ prompt_text, prompt_type.trim(), company || null, id ]); res.json(result.rows[0]); } catch (error) { console.error('Error updating AI prompt:', error); // Check for unique constraint violations if (error instanceof Error && error.message.includes('unique')) { if (error.message.includes('idx_singleton_with_company')) { return res.status(409).json({ error: 'A prompt of this type already exists for this company', details: error.message }); } else if (error.message.includes('idx_singleton_no_company')) { return res.status(409).json({ error: 'A prompt of this type already exists', details: error.message }); } } res.status(500).json({ error: 'Failed to update AI prompt', details: error instanceof Error ? error.message : 'Unknown error' }); } }); // Delete AI prompt router.delete('/:id', async (req, res) => { try { const { id } = req.params; const pool = req.app.locals.pool; if (!pool) { throw new Error('Database pool not initialized'); } const result = await pool.query('DELETE FROM ai_prompts WHERE id = $1 RETURNING *', [id]); if (result.rows.length === 0) { return res.status(404).json({ error: 'AI prompt not found' }); } res.json({ message: 'AI prompt deleted successfully' }); } catch (error) { console.error('Error deleting AI prompt:', error); res.status(500).json({ error: 'Failed to delete AI prompt', details: error instanceof Error ? error.message : 'Unknown error' }); } }); // Error handling middleware router.use((err, req, res, next) => { console.error('AI prompts route error:', err); res.status(500).json({ error: 'Internal server error', details: err.message }); }); module.exports = router;