backend changes

This commit is contained in:
Chandini 2025-09-15 12:44:31 +05:30
parent c5e62ae68b
commit 7cdc7b1165
6 changed files with 163 additions and 89 deletions

View File

@ -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

View File

@ -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' }));

View File

@ -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
});
}
});

View File

@ -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,

View File

@ -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

View File

@ -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
};
}