Add Groq as AI provider + new inline AI tasks, extend database to support more prompt types
This commit is contained in:
@@ -51,66 +51,64 @@ router.get('/:id', async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
// Get prompt by type (general, system, company_specific)
|
||||
// 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 prompt type
|
||||
if (!type || !['general', 'system', 'company_specific'].includes(type)) {
|
||||
return res.status(400).json({
|
||||
error: 'Valid type query parameter is required (general, system, or company_specific)'
|
||||
});
|
||||
}
|
||||
|
||||
// For company_specific type, company ID is required
|
||||
if (type === 'company_specific' && !company) {
|
||||
|
||||
// Validate type is provided
|
||||
if (!type || typeof type !== 'string' || type.trim().length === 0) {
|
||||
return res.status(400).json({
|
||||
error: 'Company ID is required for company_specific prompt type'
|
||||
error: 'Valid type query parameter is required'
|
||||
});
|
||||
}
|
||||
|
||||
// For general and system types, company should not be provided
|
||||
if ((type === 'general' || type === 'system') && company) {
|
||||
|
||||
// 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 should not be provided for general or system prompt types'
|
||||
error: 'Company ID is required for company_specific prompt types'
|
||||
});
|
||||
}
|
||||
|
||||
// Build the query based on the type
|
||||
|
||||
// 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 (type === 'company_specific') {
|
||||
if (company) {
|
||||
query = 'SELECT * FROM ai_prompts WHERE prompt_type = $1 AND company = $2';
|
||||
params = [type, company];
|
||||
params = [type.trim(), company];
|
||||
} else {
|
||||
query = 'SELECT * FROM ai_prompts WHERE prompt_type = $1';
|
||||
params = [type];
|
||||
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) {
|
||||
let errorMessage;
|
||||
if (type === 'company_specific') {
|
||||
errorMessage = `AI prompt not found for company ${company}`;
|
||||
} else {
|
||||
errorMessage = `${type.charAt(0).toUpperCase() + type.slice(1)} AI prompt not found`;
|
||||
}
|
||||
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({
|
||||
res.status(500).json({
|
||||
error: 'Failed to fetch AI prompt',
|
||||
details: error instanceof Error ? error.message : 'Unknown error'
|
||||
});
|
||||
@@ -130,27 +128,28 @@ router.post('/', async (req, res) => {
|
||||
if (!prompt_text || !prompt_type) {
|
||||
return res.status(400).json({ error: 'Prompt text and type are required' });
|
||||
}
|
||||
|
||||
// Validate prompt type
|
||||
if (!['general', 'company_specific', 'system'].includes(prompt_type)) {
|
||||
return res.status(400).json({ error: 'Prompt type must be either "general", "company_specific", or "system"' });
|
||||
}
|
||||
|
||||
// Validate company is provided for company-specific prompts
|
||||
if (prompt_type === 'company_specific' && !company) {
|
||||
return res.status(400).json({ error: 'Company is required for company-specific prompts' });
|
||||
|
||||
// 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' });
|
||||
}
|
||||
|
||||
// Validate company is not provided for general or system prompts
|
||||
if ((prompt_type === 'general' || prompt_type === 'system') && company) {
|
||||
return res.status(400).json({ error: 'Company should not be provided for general or system prompts' });
|
||||
// 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,
|
||||
@@ -160,35 +159,30 @@ router.post('/', async (req, res) => {
|
||||
RETURNING *
|
||||
`, [
|
||||
prompt_text,
|
||||
prompt_type,
|
||||
company
|
||||
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 constraint')) {
|
||||
if (error.message.includes('unique_company_prompt')) {
|
||||
return res.status(409).json({
|
||||
error: 'A prompt already exists for this company',
|
||||
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_unique_general_prompt')) {
|
||||
return res.status(409).json({
|
||||
error: 'A general prompt already exists',
|
||||
details: error.message
|
||||
});
|
||||
} else if (error.message.includes('idx_unique_system_prompt')) {
|
||||
return res.status(409).json({
|
||||
error: 'A system prompt already exists',
|
||||
} 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({
|
||||
|
||||
res.status(500).json({
|
||||
error: 'Failed to create AI prompt',
|
||||
details: error instanceof Error ? error.message : 'Unknown error'
|
||||
});
|
||||
@@ -209,73 +203,70 @@ router.put('/:id', async (req, res) => {
|
||||
if (!prompt_text || !prompt_type) {
|
||||
return res.status(400).json({ error: 'Prompt text and type are required' });
|
||||
}
|
||||
|
||||
// Validate prompt type
|
||||
if (!['general', 'company_specific', 'system'].includes(prompt_type)) {
|
||||
return res.status(400).json({ error: 'Prompt type must be either "general", "company_specific", or "system"' });
|
||||
|
||||
// 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' });
|
||||
}
|
||||
|
||||
// Validate company is provided for company-specific prompts
|
||||
if (prompt_type === 'company_specific' && !company) {
|
||||
return res.status(400).json({ error: 'Company is required for company-specific prompts' });
|
||||
|
||||
// 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' });
|
||||
}
|
||||
|
||||
// Validate company is not provided for general or system prompts
|
||||
if ((prompt_type === 'general' || prompt_type === 'system') && company) {
|
||||
return res.status(400).json({ error: 'Company should not be provided for general or system prompts' });
|
||||
|
||||
// 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
|
||||
UPDATE ai_prompts
|
||||
SET
|
||||
prompt_text = $1,
|
||||
prompt_type = $2,
|
||||
company = $3
|
||||
company = $3,
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
WHERE id = $4
|
||||
RETURNING *
|
||||
`, [
|
||||
prompt_text,
|
||||
prompt_type,
|
||||
company,
|
||||
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 constraint')) {
|
||||
if (error.message.includes('unique_company_prompt')) {
|
||||
return res.status(409).json({
|
||||
error: 'A prompt already exists for this company',
|
||||
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_unique_general_prompt')) {
|
||||
return res.status(409).json({
|
||||
error: 'A general prompt already exists',
|
||||
details: error.message
|
||||
});
|
||||
} else if (error.message.includes('idx_unique_system_prompt')) {
|
||||
return res.status(409).json({
|
||||
error: 'A system prompt already exists',
|
||||
} 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({
|
||||
|
||||
res.status(500).json({
|
||||
error: 'Failed to update AI prompt',
|
||||
details: error instanceof Error ? error.message : 'Unknown error'
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user