diff --git a/docker-compose.yml b/docker-compose.yml index 937ce13..e76a2d3 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -503,6 +503,7 @@ services: - JWT_ACCESS_SECRET=access-secret-key-2024-tech4biz-secure_pipeline_2024 - JWT_REFRESH_SECRET=refresh-secret-key-2024-tech4biz-${POSTGRES_PASSWORD} - JWT_ACCESS_EXPIRY=24h + - JWT_ADMIN_ACCESS_EXPIRY=7d - JWT_REFRESH_EXPIRY=7d - FRONTEND_URL=* # Email Configuration diff --git a/services/api-gateway/src/server.js b/services/api-gateway/src/server.js index 0eed319..99f2dd0 100644 --- a/services/api-gateway/src/server.js +++ b/services/api-gateway/src/server.js @@ -99,6 +99,7 @@ app.use('/api/gateway', express.json({ limit: '10mb' })); app.use('/api/auth', express.json({ limit: '10mb' })); app.use('/api/templates', express.json({ limit: '10mb' })); app.use('/api/features', express.json({ limit: '10mb' })); +app.use('/api/admin', express.json({ limit: '10mb' })); app.use('/api/github', express.json({ limit: '10mb' })); app.use('/api/mockup', express.json({ limit: '10mb' })); app.use('/health', express.json({ limit: '10mb' })); diff --git a/services/template-manager/src/routes/admin-templates.js b/services/template-manager/src/routes/admin-templates.js index 50c9930..4aee752 100644 --- a/services/template-manager/src/routes/admin-templates.js +++ b/services/template-manager/src/routes/admin-templates.js @@ -213,17 +213,13 @@ router.get('/:id/features', async (req, res) => { }); } - // Get features for the template + // Get features for the template with business rules const featuresQuery = ` SELECT tf.*, - f.name, - f.description, - f.complexity, - f.business_rules, - f.technical_requirements + fbr.business_rules as stored_business_rules FROM template_features tf - LEFT JOIN features f ON tf.feature_id = f.id + LEFT JOIN feature_business_rules fbr ON CAST(tf.id AS TEXT) = fbr.feature_id WHERE tf.template_id = $1 ORDER BY tf.display_order, tf.created_at `; @@ -244,8 +240,8 @@ router.get('/:id/features', async (req, res) => { created_by_user: row.created_by_user || false, created_at: row.created_at, updated_at: row.updated_at, - business_rules: row.business_rules, - technical_requirements: row.technical_requirements + business_rules: row.stored_business_rules || row.business_rules, + technical_requirements: row.stored_business_rules || row.technical_requirements })); console.log('✅ [ADMIN-TEMPLATES] Found features:', features.length); @@ -257,11 +253,13 @@ router.get('/:id/features', async (req, res) => { }); } catch (error) { - console.error('❌ [ADMIN-TEMPLATES] Error fetching template features:', error.message); + console.error('❌ [ADMIN-TEMPLATES] Error fetching template features:', error); + console.error('❌ [ADMIN-TEMPLATES] Full error stack:', error.stack); res.status(500).json({ success: false, error: 'Failed to fetch template features', - message: error.message + message: error.message, + details: error.stack }); } }); @@ -325,6 +323,14 @@ router.put('/:templateId/features/:featureId', async (req, res) => { const updateData = req.body; console.log('✏️ [ADMIN-TEMPLATES] Updating feature:', featureId, 'in template:', templateId); + console.log('📦 [ADMIN-TEMPLATES] Raw request body:', JSON.stringify(req.body, null, 2)); + console.log('📦 [ADMIN-TEMPLATES] Request headers:', req.headers['content-type']); + console.log('📦 [ADMIN-TEMPLATES] Content-Length:', req.headers['content-length']); + console.log('📦 [ADMIN-TEMPLATES] Request method:', req.method); + console.log('📦 [ADMIN-TEMPLATES] Request URL:', req.url); + console.log('📦 [ADMIN-TEMPLATES] Update data keys:', Object.keys(updateData || {})); + console.log('📦 [ADMIN-TEMPLATES] Body type:', typeof req.body); + console.log('📦 [ADMIN-TEMPLATES] Body constructor:', req.body?.constructor?.name); // Validate template exists const template = await Template.getByIdWithFeatures(templateId); @@ -344,14 +350,44 @@ router.put('/:templateId/features/:featureId', async (req, res) => { RETURNING * `; + // Validate required fields + if (!updateData.name || updateData.name.trim() === '') { + console.error('❌ [ADMIN-TEMPLATES] Validation failed: Feature name is required'); + return res.status(400).json({ + success: false, + error: 'Validation failed', + message: 'Feature name is required', + received_data: updateData + }); + } + + console.log('📝 [ADMIN-TEMPLATES] Update data received:', JSON.stringify(updateData, null, 2)); + const result = await database.query(updateQuery, [ - updateData.name, + updateData.name.trim(), updateData.description || '', updateData.complexity || 'medium', featureId, templateId ]); + // Update or insert business rules in feature_business_rules table + if (updateData.business_rules) { + const businessRulesQuery = ` + INSERT INTO feature_business_rules (template_id, feature_id, business_rules) + VALUES ($1, $2, $3) + ON CONFLICT (template_id, feature_id) + DO UPDATE SET business_rules = $3, updated_at = NOW() + `; + + // Convert business_rules to JSON string if it's an array/object + const businessRulesData = typeof updateData.business_rules === 'string' + ? updateData.business_rules + : JSON.stringify(updateData.business_rules); + + await database.query(businessRulesQuery, [templateId, featureId, businessRulesData]); + } + if (result.rows.length === 0) { return res.status(404).json({ success: false, @@ -371,11 +407,13 @@ router.put('/:templateId/features/:featureId', async (req, res) => { }); } catch (error) { - console.error('❌ [ADMIN-TEMPLATES] Error updating feature:', error.message); + console.error('❌ [ADMIN-TEMPLATES] Error updating feature:', error); + console.error('❌ [ADMIN-TEMPLATES] Full error stack:', error.stack); res.status(500).json({ success: false, error: 'Failed to update feature', - message: error.message + message: error.message, + details: error.stack }); } }); diff --git a/services/template-manager/src/routes/templates.js b/services/template-manager/src/routes/templates.js index 721e193..6487f81 100644 --- a/services/template-manager/src/routes/templates.js +++ b/services/template-manager/src/routes/templates.js @@ -12,7 +12,7 @@ router.get('/', async (req, res) => { try { console.log('📂 Fetching all templates by category...'); const templates = await Template.getAllByCategory(); - + res.json({ success: true, data: templates, @@ -33,7 +33,7 @@ router.get('/stats', async (req, res) => { try { console.log('📊 Fetching template statistics...'); const stats = await Template.getStats(); - + res.json({ success: true, data: stats, @@ -140,28 +140,28 @@ router.get('/merged', async (req, res) => { category: req.query.category || 'all categories', search: req.query.search || 'no search query' }); - + const limit = parseInt(req.query.limit) || 10; const offset = parseInt(req.query.offset) || 0; const categoryFilter = req.query.category || null; const searchQuery = req.query.search ? req.query.search.toLowerCase() : null; console.log("req.query __", req.query) - + console.log('⚙️ [MERGED-TEMPLATES] Parsed parameters:', { limit, offset, categoryFilter, searchQuery }); - + // Get all default templates console.log('🏗️ [MERGED-TEMPLATES] Fetching default templates by category...'); const defaultTemplatesByCat = await Template.getAllByCategory(); console.log('📊 [MERGED-TEMPLATES] Default templates by category structure:', Object.keys(defaultTemplatesByCat)); - + let defaultTemplates = []; for (const cat in defaultTemplatesByCat) { const catTemplates = defaultTemplatesByCat[cat]; console.log(`📁 [MERGED-TEMPLATES] Category "${cat}": ${catTemplates.length} templates`); defaultTemplates = defaultTemplates.concat(catTemplates); } - + console.log('✅ [MERGED-TEMPLATES] Total default templates collected:', defaultTemplates.length); console.log('🔍 [MERGED-TEMPLATES] Sample default template:', defaultTemplates[0] ? { id: defaultTemplates[0].id, @@ -169,16 +169,16 @@ router.get('/merged', async (req, res) => { category: defaultTemplates[0].category, type: defaultTemplates[0].type } : 'No default templates found'); - + // Get all custom templates for the current user console.log('🎨 [MERGED-TEMPLATES] Fetching custom templates...'); console.log('🔍 [MERGED-TEMPLATES] Request userId:', req.query.userId); console.log('🔍 [MERGED-TEMPLATES] Request includeOthers:', req.query.includeOthers); - + let customTemplates = []; let userOwnCustomCount = 0; let approvedOthersCustomCount = 0; - + if (req.query.userId) { // Validate UUID v4 for userId to avoid DB errors like "invalid input syntax for type uuid" const uuidV4Regex = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i; @@ -195,7 +195,7 @@ router.get('/merged', async (req, res) => { customTemplates = await CustomTemplate.getByUserId(req.query.userId, 1000, 0); userOwnCustomCount = customTemplates.length; console.log('📈 [MERGED-TEMPLATES] ALL custom templates for THIS user:', userOwnCustomCount); - + // Optionally include ALL custom templates from other users if explicitly requested const includeOthers = String(req.query.includeOthers || '').toLowerCase() === 'true'; if (includeOthers) { @@ -216,9 +216,9 @@ router.get('/merged', async (req, res) => { approvedOthersCustomCount = customTemplates.length; console.log('📈 [MERGED-TEMPLATES] All custom templates (no user specified):', approvedOthersCustomCount); } - + console.log('📈 [MERGED-TEMPLATES] Totals → userOwn:', userOwnCustomCount, ', approvedOthers:', approvedOthersCustomCount, ', combinedCustoms:', customTemplates.length); - + if (customTemplates.length > 0) { console.log('🔍 [MERGED-TEMPLATES] Sample custom template:', { id: customTemplates[0].id, @@ -227,7 +227,7 @@ router.get('/merged', async (req, res) => { status: customTemplates[0].status }); } - + // Convert customs to standard template format and merge into flat array console.log('🔄 [MERGED-TEMPLATES] Converting custom templates to standard format...'); const convertedCustomTemplates = customTemplates.map(customTemplate => ({ @@ -249,12 +249,12 @@ router.get('/merged', async (req, res) => { business_rules: customTemplate.business_rules, technical_requirements: customTemplate.technical_requirements })); - + console.log('✅ [MERGED-TEMPLATES] Custom templates converted:', convertedCustomTemplates.length); - + let allTemplates = defaultTemplates.concat(convertedCustomTemplates); console.log('🔗 [MERGED-TEMPLATES] Combined templates total:', allTemplates.length); - + // Apply category filter if specified if (categoryFilter && categoryFilter !== 'all') { console.log(`🎯 [MERGED-TEMPLATES] Applying category filter: "${categoryFilter}"`); @@ -263,29 +263,29 @@ router.get('/merged', async (req, res) => { const afterFilter = allTemplates.length; console.log(`📊 [MERGED-TEMPLATES] Category filter result: ${beforeFilter} → ${afterFilter} templates`); } - + // Apply search filter if specified if (searchQuery) { console.log(`🔍 [MERGED-TEMPLATES] Applying search filter: "${searchQuery}"`); const beforeSearch = allTemplates.length; - allTemplates = allTemplates.filter(t => - t.title.toLowerCase().includes(searchQuery) || + allTemplates = allTemplates.filter(t => + t.title.toLowerCase().includes(searchQuery) || t.description.toLowerCase().includes(searchQuery) ); const afterSearch = allTemplates.length; console.log(`📊 [MERGED-TEMPLATES] Search filter result: ${beforeSearch} → ${afterSearch} templates`); } - + // Sort by created_at descending console.log('📅 [MERGED-TEMPLATES] Sorting templates by creation date...'); allTemplates.sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime()); console.log('✅ [MERGED-TEMPLATES] Templates sorted successfully'); - + // Paginate const total = allTemplates.length; console.log('📊 [MERGED-TEMPLATES] Final template count before pagination:', total); console.log('📄 [MERGED-TEMPLATES] Pagination parameters:', { offset, limit, total }); - + const paginatedTemplates = allTemplates.slice(offset, offset + limit); console.log('📋 [MERGED-TEMPLATES] Paginated result:', { requested: limit, @@ -293,45 +293,45 @@ router.get('/merged', async (req, res) => { startIndex: offset, endIndex: offset + paginatedTemplates.length - 1 }); - + // Add feature counts to each template console.log('🔢 [MERGED-TEMPLATES] Fetching feature counts for templates...'); - + // Separate default and custom templates for feature counting const defaultTemplateIds = paginatedTemplates.filter(t => !t.is_custom).map(t => t.id); const customTemplateIds = paginatedTemplates.filter(t => t.is_custom).map(t => t.id); - + console.log('📊 [MERGED-TEMPLATES] Template ID breakdown:', { defaultTemplates: defaultTemplateIds.length, customTemplates: customTemplateIds.length }); - + // Fetch feature counts for both types let defaultFeatureCounts = {}; let customFeatureCounts = {}; - + if (defaultTemplateIds.length > 0) { console.log('🔍 [MERGED-TEMPLATES] Fetching default template feature counts...'); defaultFeatureCounts = await Feature.countByTemplateIds(defaultTemplateIds); console.log('✅ [MERGED-TEMPLATES] Default feature counts:', Object.keys(defaultFeatureCounts).length, 'templates'); } - + if (customTemplateIds.length > 0) { console.log('🔍 [MERGED-TEMPLATES] Fetching custom template feature counts...'); customFeatureCounts = await CustomFeature.countByTemplateIds(customTemplateIds); console.log('✅ [MERGED-TEMPLATES] Custom feature counts:', Object.keys(customFeatureCounts).length, 'templates'); } - + // Add feature counts to each template const templatesWithFeatureCounts = paginatedTemplates.map(template => ({ ...template, - feature_count: template.is_custom + feature_count: template.is_custom ? (customFeatureCounts[template.id] || 0) : (defaultFeatureCounts[template.id] || 0) })); - + console.log('🎯 [MERGED-TEMPLATES] Feature counts added to all templates'); - + // Log sample of returned templates with feature counts if (templatesWithFeatureCounts.length > 0) { console.log('🔍 [MERGED-TEMPLATES] First template in result:', { @@ -341,7 +341,7 @@ router.get('/merged', async (req, res) => { is_custom: templatesWithFeatureCounts[0].is_custom, feature_count: templatesWithFeatureCounts[0].feature_count }); - + if (templatesWithFeatureCounts.length > 1) { console.log('🔍 [MERGED-TEMPLATES] Last template in result:', { id: templatesWithFeatureCounts[templatesWithFeatureCounts.length - 1].id, @@ -352,7 +352,7 @@ router.get('/merged', async (req, res) => { }); } } - + const responseData = { success: true, data: templatesWithFeatureCounts, @@ -364,7 +364,7 @@ router.get('/merged', async (req, res) => { }, message: `Found ${templatesWithFeatureCounts.length} templates (out of ${total}) with feature counts` }; - + console.log('🎉 [MERGED-TEMPLATES] Response prepared successfully:', { success: responseData.success, dataCount: responseData.data.length, @@ -376,9 +376,9 @@ router.get('/merged', async (req, res) => { is_custom: t.is_custom })) }); - + res.json(responseData); - + } catch (error) { console.error('💥 [MERGED-TEMPLATES] Critical error occurred:', error.message); console.error('📚 [MERGED-TEMPLATES] Error stack:', error.stack); @@ -387,7 +387,7 @@ router.get('/merged', async (req, res) => { code: error.code, sqlMessage: error.sqlMessage }); - + res.status(500).json({ success: false, error: 'Failed to fetch merged templates', @@ -396,14 +396,44 @@ router.get('/merged', async (req, res) => { } }); +router.get('/all-templates-without-pagination', async (req, res) => { + try { + // Fetch templates (assuming Sequelize models) + const templates = await Template.findAll({ raw: true }); + const customTemplates = await CustomTemplate.findAll({ raw: true }); + + // Merge both arrays + const allTemplates = [...(templates || []), ...(customTemplates || [])]; + + // Sort by created_at (descending) + allTemplates.sort((a, b) => { + return new Date(b.created_at) - new Date(a.created_at); + }); + + res.json({ + success: true, + data: allTemplates, + message: `Found ${allTemplates.length} templates` + }); + } catch (error) { + console.error('❌ Error fetching all templates without pagination:', error); + res.status(500).json({ + success: false, + error: 'Failed to fetch all templates without pagination', + message: error.message + }); + } +}); + + // GET /api/templates/type/:type - Get template by type router.get('/type/:type', async (req, res) => { try { const { type } = req.params; console.log(`🔍 Fetching template by type: ${type}`); - + const template = await Template.getByType(type); - + if (!template) { return res.status(404).json({ success: false, @@ -411,11 +441,11 @@ router.get('/type/:type', async (req, res) => { message: `Template with type ${type} does not exist` }); } - + // Get features for this template const features = await Feature.getByTemplateId(template.id); template.features = features; - + res.json({ success: true, data: template, @@ -432,7 +462,6 @@ router.get('/type/:type', async (req, res) => { }); - // GET /api/templates/:id - Get specific template with features (UUID constrained) router.get('/:id([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})', async (req, res) => { try { @@ -447,9 +476,9 @@ router.get('/:id([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})', message: 'id must be a valid UUID v4' }); } - + const template = await Template.getByIdWithFeatures(id); - + if (!template) { return res.status(404).json({ success: false, @@ -457,7 +486,7 @@ router.get('/:id([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})', message: `Template with ID ${id} does not exist` }); } - + res.json({ success: true, data: template, @@ -480,27 +509,27 @@ router.get('/:id([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/f try { const { id } = req.params; console.log(`🎯 Fetching features for template: ${id}`); - + // Check if template exists in either templates or custom_templates table console.log(`🔍 Searching for template ID: ${id}`); - + // First check templates table const defaultTemplateCheck = await database.query(` SELECT id, title, 'default' as template_type FROM templates WHERE id = $1 AND is_active = true `, [id]); console.log(`📊 Default templates found: ${defaultTemplateCheck.rows.length}`); - + // Then check custom_templates table const customTemplateCheck = await database.query(` SELECT id, title, 'custom' as template_type FROM custom_templates WHERE id = $1 `, [id]); console.log(`📊 Custom templates found: ${customTemplateCheck.rows.length}`); - + // Combine results const templateCheck = { rows: [...defaultTemplateCheck.rows, ...customTemplateCheck.rows] }; - + if (templateCheck.rows.length === 0) { console.log(`❌ Template not found in either table: ${id}`); return res.status(404).json({ @@ -509,12 +538,12 @@ router.get('/:id([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/f message: `Template with ID ${id} does not exist in templates or custom_templates` }); } - + console.log(`✅ Template found: ${templateCheck.rows[0].title} (${templateCheck.rows[0].template_type})`); - + // Fetch features from both tables for proper separation console.log('📋 Fetching features from both template_features and custom_features tables'); - + // Get default/suggested features from template_features table // Include aggregated business rules from feature_business_rules when available const defaultFeaturesQuery = ` @@ -542,7 +571,7 @@ router.get('/:id([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/f const defaultFeaturesResult = await database.query(defaultFeaturesQuery, [id]); const defaultFeatures = defaultFeaturesResult.rows; console.log(`📊 Found ${defaultFeatures.length} default/suggested features`); - + // Get custom features from custom_features table with business rules (if table exists) // Some environments may not have run the feature_business_rules migration yet. Probe first. const fbrExistsProbe = await database.query("SELECT to_regclass('public.feature_business_rules') AS tbl"); @@ -604,10 +633,10 @@ router.get('/:id([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/f const customFeaturesResult = await database.query(customFeaturesQuery, [id]); const customFeatures = customFeaturesResult.rows; console.log(`📊 Found ${customFeatures.length} custom features`); - + // Combine both types of features const features = [...defaultFeatures, ...customFeatures]; - + res.json({ success: true, data: features, @@ -642,7 +671,7 @@ router.post('/', async (req, res) => { } }; console.log('🏗️ Creating new template - incoming body:', JSON.stringify(debugPayload)); - + // Validate required fields const requiredFields = ['type', 'title', 'category']; for (const field of requiredFields) { @@ -676,7 +705,7 @@ router.post('/', async (req, res) => { } }); } - + // If flagged as a custom template, store in custom_templates instead if (templateData.isCustom === true || templateData.is_custom === true || templateData.source === 'custom') { try { @@ -707,7 +736,7 @@ router.post('/', async (req, res) => { } const incomingUserId = templateData.user_id || templateData.userId || (req.user && (req.user.id || req.user.user_id)) || null; - + // Check for duplicates in custom templates for this user const duplicatePayload = { type: templateData.type, @@ -715,7 +744,7 @@ router.post('/', async (req, res) => { category: templateData.category, user_id: incomingUserId }; - + console.log('[POST /api/templates - custom] duplicate payload:', duplicatePayload); const existingCustomTemplate = await CustomTemplate.checkForDuplicate(duplicatePayload); if (existingCustomTemplate) { @@ -804,7 +833,7 @@ router.post('/', async (req, res) => { }); } } - + const template = await Template.create(templateData); // Link back to custom_templates when approving from a custom @@ -835,7 +864,7 @@ router.post('/', async (req, res) => { }); } catch (error) { console.error('❌ Error creating template:', error.message); - + // Handle unique constraint violation if (error.code === '23505') { return res.status(409).json({ @@ -844,7 +873,7 @@ router.post('/', async (req, res) => { message: 'A template with this type already exists' }); } - + res.status(500).json({ success: false, error: 'Failed to create template', @@ -928,10 +957,10 @@ router.put('/:id', async (req, res) => { } // Validate allowed fields for custom templates to avoid no-op updates const allowed = [ - 'title','description','icon','category','gradient','border','text','subtext', - 'complexity','business_rules','technical_requirements','approved','usage_count', - 'status','admin_notes','admin_reviewed_at','admin_reviewed_by', - 'canonical_template_id','similarity_score','user_id' + 'title', 'description', 'icon', 'category', 'gradient', 'border', 'text', 'subtext', + 'complexity', 'business_rules', 'technical_requirements', 'approved', 'usage_count', + 'status', 'admin_notes', 'admin_reviewed_at', 'admin_reviewed_by', + 'canonical_template_id', 'similarity_score', 'user_id' ]; const providedKeys = Object.keys(updateData || {}); const updatableKeys = providedKeys.filter(k => allowed.includes(k)); @@ -967,7 +996,7 @@ router.put('/:id', async (req, res) => { console.log('📝 Updating default template...'); const updatedTemplate = await template.update(updateData); console.log('📝 Update result (default):', { updated: !!updatedTemplate }); - + res.json({ success: true, data: updatedTemplate, diff --git a/services/user-auth/env.example b/services/user-auth/env.example index f865823..8c3a53a 100644 --- a/services/user-auth/env.example +++ b/services/user-auth/env.example @@ -34,5 +34,6 @@ REDIS_PASSWORD=your_redis_password # JWT Configuration JWT_ACCESS_SECRET=your_access_secret_key JWT_REFRESH_SECRET=your_refresh_secret_key -JWT_ACCESS_EXPIRY=15m +JWT_ACCESS_EXPIRY=24h +JWT_ADMIN_ACCESS_EXPIRY=7d JWT_REFRESH_EXPIRY=7d diff --git a/services/user-auth/src/config/jwt.js b/services/user-auth/src/config/jwt.js index dfd1384..a196075 100644 --- a/services/user-auth/src/config/jwt.js +++ b/services/user-auth/src/config/jwt.js @@ -6,11 +6,13 @@ class JWTConfig { this.refreshTokenSecret = process.env.JWT_REFRESH_SECRET || 'refresh-secret-key-2024-tech4biz'; this.accessTokenExpiry = process.env.JWT_ACCESS_EXPIRY || '24h'; this.refreshTokenExpiry = process.env.JWT_REFRESH_EXPIRY || '7d'; + this.adminAccessTokenExpiry = process.env.JWT_ADMIN_ACCESS_EXPIRY || '7d'; // Extended expiry for admins } - generateAccessToken(payload) { + generateAccessToken(payload, isAdmin = false) { + const expiry = isAdmin ? this.adminAccessTokenExpiry : this.accessTokenExpiry; return jwt.sign(payload, this.accessTokenSecret, { - expiresIn: this.accessTokenExpiry, + expiresIn: expiry, issuer: 'tech4biz-auth', audience: 'tech4biz-users' }); @@ -54,13 +56,15 @@ class JWTConfig { role: user.role || 'user' }; - const accessToken = this.generateAccessToken(payload); + const isAdmin = user.role === 'admin' || user.role === 'super_admin'; + const accessToken = this.generateAccessToken(payload, isAdmin); const refreshToken = this.generateRefreshToken({ userId: user.id }); + const expiry = isAdmin ? this.adminAccessTokenExpiry : this.accessTokenExpiry; return { accessToken, refreshToken, - expiresIn: this.accessTokenExpiry + expiresIn: expiry }; }