diff --git a/services/template-manager/src/models/custom_template.js b/services/template-manager/src/models/custom_template.js index e23742c..da633c2 100644 --- a/services/template-manager/src/models/custom_template.js +++ b/services/template-manager/src/models/custom_template.js @@ -39,9 +39,11 @@ class CustomTemplate { // Check for duplicate custom templates based on title, type, category, and user_id static async checkForDuplicate(templateData) { + const normalizedTitle = (templateData.title || '').toLowerCase(); console.log('[CustomTemplate.checkForDuplicate] Checking for duplicates:', { type: templateData.type, title: templateData.title, + normalizedTitle, category: templateData.category, user_id: templateData.user_id }); @@ -58,24 +60,53 @@ class CustomTemplate { return typeResult.rows[0]; } - // Check for same title + category for same user + // Check for same title for same user (category-agnostic) if (templateData.user_id) { const titleQuery = ` SELECT id, title, type, category, user_id FROM custom_templates - WHERE LOWER(title) = LOWER($1) AND category = $2 AND user_id = $3 + WHERE LOWER(title) = LOWER($1) AND user_id = $2 `; - const titleResult = await database.query(titleQuery, [ - templateData.title, - templateData.category, - templateData.user_id - ]); + const titleParams = [templateData.title, templateData.user_id]; + console.log('[CustomTemplate.checkForDuplicate] title check params:', titleParams); + const titleResult = await database.query(titleQuery, titleParams); if (titleResult.rows.length > 0) { - console.log('[CustomTemplate.checkForDuplicate] Found duplicate by title+category+user:', titleResult.rows[0]); + const row = titleResult.rows[0]; + const titleMatch = (row.title || '').toLowerCase() === normalizedTitle; + console.log('[CustomTemplate.checkForDuplicate] Found duplicate by title+user:', { + id: row.id, + title: row.title, + type: row.type, + category: row.category, + user_id: row.user_id, + titleMatch + }); return titleResult.rows[0]; } } + + // Also check if main templates already have the same title (case-insensitive) + const mainTitleQuery = ` + SELECT id, title, type, category FROM templates + WHERE is_active = true AND LOWER(title) = LOWER($1) + LIMIT 1 + `; + const mainTitleParams = [templateData.title]; + console.log('[CustomTemplate.checkForDuplicate] main title check params:', mainTitleParams); + const mainTitleResult = await database.query(mainTitleQuery, mainTitleParams); + if (mainTitleResult.rows.length > 0) { + const row = mainTitleResult.rows[0]; + const titleMatch = (row.title || '').toLowerCase() === normalizedTitle; + console.log('[CustomTemplate.checkForDuplicate] Found duplicate title in main templates:', { + id: row.id, + title: row.title, + type: row.type, + category: row.category, + titleMatch + }); + return mainTitleResult.rows[0]; + } console.log('[CustomTemplate.checkForDuplicate] No duplicates found'); return null; diff --git a/services/template-manager/src/models/template.js b/services/template-manager/src/models/template.js index f7303e1..563b9e4 100644 --- a/services/template-manager/src/models/template.js +++ b/services/template-manager/src/models/template.js @@ -87,23 +87,54 @@ class Template { // Check for duplicate templates based on title, type, and category static async checkForDuplicate(templateData) { + const normalizedTitle = (templateData.title || '').toLowerCase(); + const incomingType = templateData.type; + console.log('[Template.checkForDuplicate] input:', { + type: incomingType, + title: templateData.title, + normalizedTitle + }); const query = ` SELECT id, title, type, category FROM templates WHERE is_active = true AND ( type = $1 OR - (LOWER(title) = LOWER($2) AND category = $3) + LOWER(title) = LOWER($2) ) `; - const result = await database.query(query, [ - templateData.type, - templateData.title, - templateData.category - ]); + const params = [incomingType, templateData.title]; + console.log('[Template.checkForDuplicate] query params:', params); + const result = await database.query(query, params); + if (result.rows.length > 0) { + const row = result.rows[0]; + const titleMatch = (row.title || '').toLowerCase() === normalizedTitle; + const typeMatch = (row.type || '') === incomingType; + console.log('[Template.checkForDuplicate] found duplicate:', { + id: row.id, + title: row.title, + type: row.type, + category: row.category, + titleMatch, + typeMatch + }); + } else { + console.log('[Template.checkForDuplicate] no duplicates found'); + } return result.rows.length > 0 ? result.rows[0] : null; } + // Get a template by title (case-insensitive) + static async getByTitle(title) { + const query = ` + SELECT * FROM templates + WHERE is_active = true AND LOWER(title) = LOWER($1) + LIMIT 1 + `; + const result = await database.query(query, [title]); + return result.rows.length > 0 ? new Template(result.rows[0]) : null; + } + // Create new template static async create(templateData) { const id = uuidv4(); diff --git a/services/template-manager/src/routes/templates.js b/services/template-manager/src/routes/templates.js index 33077a5..418a707 100644 --- a/services/template-manager/src/routes/templates.js +++ b/services/template-manager/src/routes/templates.js @@ -562,7 +562,17 @@ router.get('/:id([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/f router.post('/', async (req, res) => { try { const templateData = req.body; - console.log('🏗️ Creating new template - incoming body:', JSON.stringify(templateData)); + const debugPayload = { + raw: templateData, + normalized: { + title: (templateData.title || '').toLowerCase(), + type: templateData.type, + category: templateData.category, + isCustom: templateData.isCustom ?? templateData.is_custom ?? false, + user_id: templateData.user_id || templateData.userId || null + } + }; + console.log('🏗️ Creating new template - incoming body:', JSON.stringify(debugPayload)); // Validate required fields const requiredFields = ['type', 'title', 'category']; @@ -579,10 +589,16 @@ router.post('/', async (req, res) => { // Check for duplicates in regular templates first const existingTemplate = await Template.checkForDuplicate(templateData); if (existingTemplate) { + const isTitleDuplicate = (existingTemplate.title || '').toLowerCase() === (templateData.title || '').toLowerCase(); + const isTypeDuplicate = (existingTemplate.type || '') === (templateData.type || ''); + console.log('[POST /api/templates] duplicate detected in main templates:', { existingTemplate, isTitleDuplicate, isTypeDuplicate }); + const message = isTitleDuplicate + ? `A template with this name already exists: "${existingTemplate.title}"` + : `A template with this type already exists: "${existingTemplate.title}" (type: ${existingTemplate.type})`; return res.status(409).json({ success: false, - error: 'Duplicate template detected', - message: `A template with similar characteristics already exists: "${existingTemplate.title}" (type: ${existingTemplate.type})`, + error: isTitleDuplicate ? 'Template name already exists' : 'Template type already exists', + message, existing_template: { id: existingTemplate.id, title: existingTemplate.title, @@ -631,12 +647,19 @@ router.post('/', async (req, res) => { user_id: incomingUserId }; + console.log('[POST /api/templates - custom] duplicate payload:', duplicatePayload); const existingCustomTemplate = await CustomTemplate.checkForDuplicate(duplicatePayload); if (existingCustomTemplate) { + const isTitleDuplicate = (existingCustomTemplate.title || '').toLowerCase() === (templateData.title || '').toLowerCase(); + const isTypeDuplicate = (existingCustomTemplate.type || '') === (templateData.type || ''); + console.log('[POST /api/templates - custom] duplicate detected in custom/main:', { existingCustomTemplate, isTitleDuplicate, isTypeDuplicate }); + const message = isTitleDuplicate + ? `You already have a template with this name: "${existingCustomTemplate.title}"` + : `You already have a template with this type: "${existingCustomTemplate.title}" (type: ${existingCustomTemplate.type})`; return res.status(409).json({ success: false, - error: 'Duplicate custom template detected', - message: `You already have a similar template: "${existingCustomTemplate.title}" (type: ${existingCustomTemplate.type})`, + error: isTitleDuplicate ? 'Template name already exists' : 'Template type already exists', + message, existing_template: { id: existingCustomTemplate.id, title: existingCustomTemplate.title,