backend changes
This commit is contained in:
parent
c5e62ae68b
commit
7cdc7b1165
@ -503,6 +503,7 @@ services:
|
|||||||
- JWT_ACCESS_SECRET=access-secret-key-2024-tech4biz-secure_pipeline_2024
|
- JWT_ACCESS_SECRET=access-secret-key-2024-tech4biz-secure_pipeline_2024
|
||||||
- JWT_REFRESH_SECRET=refresh-secret-key-2024-tech4biz-${POSTGRES_PASSWORD}
|
- JWT_REFRESH_SECRET=refresh-secret-key-2024-tech4biz-${POSTGRES_PASSWORD}
|
||||||
- JWT_ACCESS_EXPIRY=24h
|
- JWT_ACCESS_EXPIRY=24h
|
||||||
|
- JWT_ADMIN_ACCESS_EXPIRY=7d
|
||||||
- JWT_REFRESH_EXPIRY=7d
|
- JWT_REFRESH_EXPIRY=7d
|
||||||
- FRONTEND_URL=*
|
- FRONTEND_URL=*
|
||||||
# Email Configuration
|
# Email Configuration
|
||||||
|
|||||||
@ -99,6 +99,7 @@ app.use('/api/gateway', express.json({ limit: '10mb' }));
|
|||||||
app.use('/api/auth', express.json({ limit: '10mb' }));
|
app.use('/api/auth', express.json({ limit: '10mb' }));
|
||||||
app.use('/api/templates', express.json({ limit: '10mb' }));
|
app.use('/api/templates', express.json({ limit: '10mb' }));
|
||||||
app.use('/api/features', 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/github', express.json({ limit: '10mb' }));
|
||||||
app.use('/api/mockup', express.json({ limit: '10mb' }));
|
app.use('/api/mockup', express.json({ limit: '10mb' }));
|
||||||
app.use('/health', express.json({ limit: '10mb' }));
|
app.use('/health', express.json({ limit: '10mb' }));
|
||||||
|
|||||||
@ -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 = `
|
const featuresQuery = `
|
||||||
SELECT
|
SELECT
|
||||||
tf.*,
|
tf.*,
|
||||||
f.name,
|
fbr.business_rules as stored_business_rules
|
||||||
f.description,
|
|
||||||
f.complexity,
|
|
||||||
f.business_rules,
|
|
||||||
f.technical_requirements
|
|
||||||
FROM template_features tf
|
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
|
WHERE tf.template_id = $1
|
||||||
ORDER BY tf.display_order, tf.created_at
|
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_by_user: row.created_by_user || false,
|
||||||
created_at: row.created_at,
|
created_at: row.created_at,
|
||||||
updated_at: row.updated_at,
|
updated_at: row.updated_at,
|
||||||
business_rules: row.business_rules,
|
business_rules: row.stored_business_rules || row.business_rules,
|
||||||
technical_requirements: row.technical_requirements
|
technical_requirements: row.stored_business_rules || row.technical_requirements
|
||||||
}));
|
}));
|
||||||
|
|
||||||
console.log('✅ [ADMIN-TEMPLATES] Found features:', features.length);
|
console.log('✅ [ADMIN-TEMPLATES] Found features:', features.length);
|
||||||
@ -257,11 +253,13 @@ router.get('/:id/features', async (req, res) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
} catch (error) {
|
} 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({
|
res.status(500).json({
|
||||||
success: false,
|
success: false,
|
||||||
error: 'Failed to fetch template features',
|
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;
|
const updateData = req.body;
|
||||||
|
|
||||||
console.log('✏️ [ADMIN-TEMPLATES] Updating feature:', featureId, 'in template:', templateId);
|
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
|
// Validate template exists
|
||||||
const template = await Template.getByIdWithFeatures(templateId);
|
const template = await Template.getByIdWithFeatures(templateId);
|
||||||
@ -344,14 +350,44 @@ router.put('/:templateId/features/:featureId', async (req, res) => {
|
|||||||
RETURNING *
|
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, [
|
const result = await database.query(updateQuery, [
|
||||||
updateData.name,
|
updateData.name.trim(),
|
||||||
updateData.description || '',
|
updateData.description || '',
|
||||||
updateData.complexity || 'medium',
|
updateData.complexity || 'medium',
|
||||||
featureId,
|
featureId,
|
||||||
templateId
|
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) {
|
if (result.rows.length === 0) {
|
||||||
return res.status(404).json({
|
return res.status(404).json({
|
||||||
success: false,
|
success: false,
|
||||||
@ -371,11 +407,13 @@ router.put('/:templateId/features/:featureId', async (req, res) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
} catch (error) {
|
} 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({
|
res.status(500).json({
|
||||||
success: false,
|
success: false,
|
||||||
error: 'Failed to update feature',
|
error: 'Failed to update feature',
|
||||||
message: error.message
|
message: error.message,
|
||||||
|
details: error.stack
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@ -12,7 +12,7 @@ router.get('/', async (req, res) => {
|
|||||||
try {
|
try {
|
||||||
console.log('📂 Fetching all templates by category...');
|
console.log('📂 Fetching all templates by category...');
|
||||||
const templates = await Template.getAllByCategory();
|
const templates = await Template.getAllByCategory();
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
success: true,
|
success: true,
|
||||||
data: templates,
|
data: templates,
|
||||||
@ -33,7 +33,7 @@ router.get('/stats', async (req, res) => {
|
|||||||
try {
|
try {
|
||||||
console.log('📊 Fetching template statistics...');
|
console.log('📊 Fetching template statistics...');
|
||||||
const stats = await Template.getStats();
|
const stats = await Template.getStats();
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
success: true,
|
success: true,
|
||||||
data: stats,
|
data: stats,
|
||||||
@ -140,28 +140,28 @@ router.get('/merged', async (req, res) => {
|
|||||||
category: req.query.category || 'all categories',
|
category: req.query.category || 'all categories',
|
||||||
search: req.query.search || 'no search query'
|
search: req.query.search || 'no search query'
|
||||||
});
|
});
|
||||||
|
|
||||||
const limit = parseInt(req.query.limit) || 10;
|
const limit = parseInt(req.query.limit) || 10;
|
||||||
const offset = parseInt(req.query.offset) || 0;
|
const offset = parseInt(req.query.offset) || 0;
|
||||||
const categoryFilter = req.query.category || null;
|
const categoryFilter = req.query.category || null;
|
||||||
const searchQuery = req.query.search ? req.query.search.toLowerCase() : null;
|
const searchQuery = req.query.search ? req.query.search.toLowerCase() : null;
|
||||||
|
|
||||||
console.log("req.query __", req.query)
|
console.log("req.query __", req.query)
|
||||||
|
|
||||||
console.log('⚙️ [MERGED-TEMPLATES] Parsed parameters:', { limit, offset, categoryFilter, searchQuery });
|
console.log('⚙️ [MERGED-TEMPLATES] Parsed parameters:', { limit, offset, categoryFilter, searchQuery });
|
||||||
|
|
||||||
// Get all default templates
|
// Get all default templates
|
||||||
console.log('🏗️ [MERGED-TEMPLATES] Fetching default templates by category...');
|
console.log('🏗️ [MERGED-TEMPLATES] Fetching default templates by category...');
|
||||||
const defaultTemplatesByCat = await Template.getAllByCategory();
|
const defaultTemplatesByCat = await Template.getAllByCategory();
|
||||||
console.log('📊 [MERGED-TEMPLATES] Default templates by category structure:', Object.keys(defaultTemplatesByCat));
|
console.log('📊 [MERGED-TEMPLATES] Default templates by category structure:', Object.keys(defaultTemplatesByCat));
|
||||||
|
|
||||||
let defaultTemplates = [];
|
let defaultTemplates = [];
|
||||||
for (const cat in defaultTemplatesByCat) {
|
for (const cat in defaultTemplatesByCat) {
|
||||||
const catTemplates = defaultTemplatesByCat[cat];
|
const catTemplates = defaultTemplatesByCat[cat];
|
||||||
console.log(`📁 [MERGED-TEMPLATES] Category "${cat}": ${catTemplates.length} templates`);
|
console.log(`📁 [MERGED-TEMPLATES] Category "${cat}": ${catTemplates.length} templates`);
|
||||||
defaultTemplates = defaultTemplates.concat(catTemplates);
|
defaultTemplates = defaultTemplates.concat(catTemplates);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('✅ [MERGED-TEMPLATES] Total default templates collected:', defaultTemplates.length);
|
console.log('✅ [MERGED-TEMPLATES] Total default templates collected:', defaultTemplates.length);
|
||||||
console.log('🔍 [MERGED-TEMPLATES] Sample default template:', defaultTemplates[0] ? {
|
console.log('🔍 [MERGED-TEMPLATES] Sample default template:', defaultTemplates[0] ? {
|
||||||
id: defaultTemplates[0].id,
|
id: defaultTemplates[0].id,
|
||||||
@ -169,16 +169,16 @@ router.get('/merged', async (req, res) => {
|
|||||||
category: defaultTemplates[0].category,
|
category: defaultTemplates[0].category,
|
||||||
type: defaultTemplates[0].type
|
type: defaultTemplates[0].type
|
||||||
} : 'No default templates found');
|
} : 'No default templates found');
|
||||||
|
|
||||||
// Get all custom templates for the current user
|
// Get all custom templates for the current user
|
||||||
console.log('🎨 [MERGED-TEMPLATES] Fetching custom templates...');
|
console.log('🎨 [MERGED-TEMPLATES] Fetching custom templates...');
|
||||||
console.log('🔍 [MERGED-TEMPLATES] Request userId:', req.query.userId);
|
console.log('🔍 [MERGED-TEMPLATES] Request userId:', req.query.userId);
|
||||||
console.log('🔍 [MERGED-TEMPLATES] Request includeOthers:', req.query.includeOthers);
|
console.log('🔍 [MERGED-TEMPLATES] Request includeOthers:', req.query.includeOthers);
|
||||||
|
|
||||||
let customTemplates = [];
|
let customTemplates = [];
|
||||||
let userOwnCustomCount = 0;
|
let userOwnCustomCount = 0;
|
||||||
let approvedOthersCustomCount = 0;
|
let approvedOthersCustomCount = 0;
|
||||||
|
|
||||||
if (req.query.userId) {
|
if (req.query.userId) {
|
||||||
// Validate UUID v4 for userId to avoid DB errors like "invalid input syntax for type uuid"
|
// 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;
|
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);
|
customTemplates = await CustomTemplate.getByUserId(req.query.userId, 1000, 0);
|
||||||
userOwnCustomCount = customTemplates.length;
|
userOwnCustomCount = customTemplates.length;
|
||||||
console.log('📈 [MERGED-TEMPLATES] ALL custom templates for THIS user:', userOwnCustomCount);
|
console.log('📈 [MERGED-TEMPLATES] ALL custom templates for THIS user:', userOwnCustomCount);
|
||||||
|
|
||||||
// Optionally include ALL custom templates from other users if explicitly requested
|
// Optionally include ALL custom templates from other users if explicitly requested
|
||||||
const includeOthers = String(req.query.includeOthers || '').toLowerCase() === 'true';
|
const includeOthers = String(req.query.includeOthers || '').toLowerCase() === 'true';
|
||||||
if (includeOthers) {
|
if (includeOthers) {
|
||||||
@ -216,9 +216,9 @@ router.get('/merged', async (req, res) => {
|
|||||||
approvedOthersCustomCount = customTemplates.length;
|
approvedOthersCustomCount = customTemplates.length;
|
||||||
console.log('📈 [MERGED-TEMPLATES] All custom templates (no user specified):', approvedOthersCustomCount);
|
console.log('📈 [MERGED-TEMPLATES] All custom templates (no user specified):', approvedOthersCustomCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('📈 [MERGED-TEMPLATES] Totals → userOwn:', userOwnCustomCount, ', approvedOthers:', approvedOthersCustomCount, ', combinedCustoms:', customTemplates.length);
|
console.log('📈 [MERGED-TEMPLATES] Totals → userOwn:', userOwnCustomCount, ', approvedOthers:', approvedOthersCustomCount, ', combinedCustoms:', customTemplates.length);
|
||||||
|
|
||||||
if (customTemplates.length > 0) {
|
if (customTemplates.length > 0) {
|
||||||
console.log('🔍 [MERGED-TEMPLATES] Sample custom template:', {
|
console.log('🔍 [MERGED-TEMPLATES] Sample custom template:', {
|
||||||
id: customTemplates[0].id,
|
id: customTemplates[0].id,
|
||||||
@ -227,7 +227,7 @@ router.get('/merged', async (req, res) => {
|
|||||||
status: customTemplates[0].status
|
status: customTemplates[0].status
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert customs to standard template format and merge into flat array
|
// Convert customs to standard template format and merge into flat array
|
||||||
console.log('🔄 [MERGED-TEMPLATES] Converting custom templates to standard format...');
|
console.log('🔄 [MERGED-TEMPLATES] Converting custom templates to standard format...');
|
||||||
const convertedCustomTemplates = customTemplates.map(customTemplate => ({
|
const convertedCustomTemplates = customTemplates.map(customTemplate => ({
|
||||||
@ -249,12 +249,12 @@ router.get('/merged', async (req, res) => {
|
|||||||
business_rules: customTemplate.business_rules,
|
business_rules: customTemplate.business_rules,
|
||||||
technical_requirements: customTemplate.technical_requirements
|
technical_requirements: customTemplate.technical_requirements
|
||||||
}));
|
}));
|
||||||
|
|
||||||
console.log('✅ [MERGED-TEMPLATES] Custom templates converted:', convertedCustomTemplates.length);
|
console.log('✅ [MERGED-TEMPLATES] Custom templates converted:', convertedCustomTemplates.length);
|
||||||
|
|
||||||
let allTemplates = defaultTemplates.concat(convertedCustomTemplates);
|
let allTemplates = defaultTemplates.concat(convertedCustomTemplates);
|
||||||
console.log('🔗 [MERGED-TEMPLATES] Combined templates total:', allTemplates.length);
|
console.log('🔗 [MERGED-TEMPLATES] Combined templates total:', allTemplates.length);
|
||||||
|
|
||||||
// Apply category filter if specified
|
// Apply category filter if specified
|
||||||
if (categoryFilter && categoryFilter !== 'all') {
|
if (categoryFilter && categoryFilter !== 'all') {
|
||||||
console.log(`🎯 [MERGED-TEMPLATES] Applying category filter: "${categoryFilter}"`);
|
console.log(`🎯 [MERGED-TEMPLATES] Applying category filter: "${categoryFilter}"`);
|
||||||
@ -263,29 +263,29 @@ router.get('/merged', async (req, res) => {
|
|||||||
const afterFilter = allTemplates.length;
|
const afterFilter = allTemplates.length;
|
||||||
console.log(`📊 [MERGED-TEMPLATES] Category filter result: ${beforeFilter} → ${afterFilter} templates`);
|
console.log(`📊 [MERGED-TEMPLATES] Category filter result: ${beforeFilter} → ${afterFilter} templates`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply search filter if specified
|
// Apply search filter if specified
|
||||||
if (searchQuery) {
|
if (searchQuery) {
|
||||||
console.log(`🔍 [MERGED-TEMPLATES] Applying search filter: "${searchQuery}"`);
|
console.log(`🔍 [MERGED-TEMPLATES] Applying search filter: "${searchQuery}"`);
|
||||||
const beforeSearch = allTemplates.length;
|
const beforeSearch = allTemplates.length;
|
||||||
allTemplates = allTemplates.filter(t =>
|
allTemplates = allTemplates.filter(t =>
|
||||||
t.title.toLowerCase().includes(searchQuery) ||
|
t.title.toLowerCase().includes(searchQuery) ||
|
||||||
t.description.toLowerCase().includes(searchQuery)
|
t.description.toLowerCase().includes(searchQuery)
|
||||||
);
|
);
|
||||||
const afterSearch = allTemplates.length;
|
const afterSearch = allTemplates.length;
|
||||||
console.log(`📊 [MERGED-TEMPLATES] Search filter result: ${beforeSearch} → ${afterSearch} templates`);
|
console.log(`📊 [MERGED-TEMPLATES] Search filter result: ${beforeSearch} → ${afterSearch} templates`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort by created_at descending
|
// Sort by created_at descending
|
||||||
console.log('📅 [MERGED-TEMPLATES] Sorting templates by creation date...');
|
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());
|
allTemplates.sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime());
|
||||||
console.log('✅ [MERGED-TEMPLATES] Templates sorted successfully');
|
console.log('✅ [MERGED-TEMPLATES] Templates sorted successfully');
|
||||||
|
|
||||||
// Paginate
|
// Paginate
|
||||||
const total = allTemplates.length;
|
const total = allTemplates.length;
|
||||||
console.log('📊 [MERGED-TEMPLATES] Final template count before pagination:', total);
|
console.log('📊 [MERGED-TEMPLATES] Final template count before pagination:', total);
|
||||||
console.log('📄 [MERGED-TEMPLATES] Pagination parameters:', { offset, limit, total });
|
console.log('📄 [MERGED-TEMPLATES] Pagination parameters:', { offset, limit, total });
|
||||||
|
|
||||||
const paginatedTemplates = allTemplates.slice(offset, offset + limit);
|
const paginatedTemplates = allTemplates.slice(offset, offset + limit);
|
||||||
console.log('📋 [MERGED-TEMPLATES] Paginated result:', {
|
console.log('📋 [MERGED-TEMPLATES] Paginated result:', {
|
||||||
requested: limit,
|
requested: limit,
|
||||||
@ -293,45 +293,45 @@ router.get('/merged', async (req, res) => {
|
|||||||
startIndex: offset,
|
startIndex: offset,
|
||||||
endIndex: offset + paginatedTemplates.length - 1
|
endIndex: offset + paginatedTemplates.length - 1
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add feature counts to each template
|
// Add feature counts to each template
|
||||||
console.log('🔢 [MERGED-TEMPLATES] Fetching feature counts for templates...');
|
console.log('🔢 [MERGED-TEMPLATES] Fetching feature counts for templates...');
|
||||||
|
|
||||||
// Separate default and custom templates for feature counting
|
// Separate default and custom templates for feature counting
|
||||||
const defaultTemplateIds = paginatedTemplates.filter(t => !t.is_custom).map(t => t.id);
|
const defaultTemplateIds = paginatedTemplates.filter(t => !t.is_custom).map(t => t.id);
|
||||||
const customTemplateIds = 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:', {
|
console.log('📊 [MERGED-TEMPLATES] Template ID breakdown:', {
|
||||||
defaultTemplates: defaultTemplateIds.length,
|
defaultTemplates: defaultTemplateIds.length,
|
||||||
customTemplates: customTemplateIds.length
|
customTemplates: customTemplateIds.length
|
||||||
});
|
});
|
||||||
|
|
||||||
// Fetch feature counts for both types
|
// Fetch feature counts for both types
|
||||||
let defaultFeatureCounts = {};
|
let defaultFeatureCounts = {};
|
||||||
let customFeatureCounts = {};
|
let customFeatureCounts = {};
|
||||||
|
|
||||||
if (defaultTemplateIds.length > 0) {
|
if (defaultTemplateIds.length > 0) {
|
||||||
console.log('🔍 [MERGED-TEMPLATES] Fetching default template feature counts...');
|
console.log('🔍 [MERGED-TEMPLATES] Fetching default template feature counts...');
|
||||||
defaultFeatureCounts = await Feature.countByTemplateIds(defaultTemplateIds);
|
defaultFeatureCounts = await Feature.countByTemplateIds(defaultTemplateIds);
|
||||||
console.log('✅ [MERGED-TEMPLATES] Default feature counts:', Object.keys(defaultFeatureCounts).length, 'templates');
|
console.log('✅ [MERGED-TEMPLATES] Default feature counts:', Object.keys(defaultFeatureCounts).length, 'templates');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (customTemplateIds.length > 0) {
|
if (customTemplateIds.length > 0) {
|
||||||
console.log('🔍 [MERGED-TEMPLATES] Fetching custom template feature counts...');
|
console.log('🔍 [MERGED-TEMPLATES] Fetching custom template feature counts...');
|
||||||
customFeatureCounts = await CustomFeature.countByTemplateIds(customTemplateIds);
|
customFeatureCounts = await CustomFeature.countByTemplateIds(customTemplateIds);
|
||||||
console.log('✅ [MERGED-TEMPLATES] Custom feature counts:', Object.keys(customFeatureCounts).length, 'templates');
|
console.log('✅ [MERGED-TEMPLATES] Custom feature counts:', Object.keys(customFeatureCounts).length, 'templates');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add feature counts to each template
|
// Add feature counts to each template
|
||||||
const templatesWithFeatureCounts = paginatedTemplates.map(template => ({
|
const templatesWithFeatureCounts = paginatedTemplates.map(template => ({
|
||||||
...template,
|
...template,
|
||||||
feature_count: template.is_custom
|
feature_count: template.is_custom
|
||||||
? (customFeatureCounts[template.id] || 0)
|
? (customFeatureCounts[template.id] || 0)
|
||||||
: (defaultFeatureCounts[template.id] || 0)
|
: (defaultFeatureCounts[template.id] || 0)
|
||||||
}));
|
}));
|
||||||
|
|
||||||
console.log('🎯 [MERGED-TEMPLATES] Feature counts added to all templates');
|
console.log('🎯 [MERGED-TEMPLATES] Feature counts added to all templates');
|
||||||
|
|
||||||
// Log sample of returned templates with feature counts
|
// Log sample of returned templates with feature counts
|
||||||
if (templatesWithFeatureCounts.length > 0) {
|
if (templatesWithFeatureCounts.length > 0) {
|
||||||
console.log('🔍 [MERGED-TEMPLATES] First template in result:', {
|
console.log('🔍 [MERGED-TEMPLATES] First template in result:', {
|
||||||
@ -341,7 +341,7 @@ router.get('/merged', async (req, res) => {
|
|||||||
is_custom: templatesWithFeatureCounts[0].is_custom,
|
is_custom: templatesWithFeatureCounts[0].is_custom,
|
||||||
feature_count: templatesWithFeatureCounts[0].feature_count
|
feature_count: templatesWithFeatureCounts[0].feature_count
|
||||||
});
|
});
|
||||||
|
|
||||||
if (templatesWithFeatureCounts.length > 1) {
|
if (templatesWithFeatureCounts.length > 1) {
|
||||||
console.log('🔍 [MERGED-TEMPLATES] Last template in result:', {
|
console.log('🔍 [MERGED-TEMPLATES] Last template in result:', {
|
||||||
id: templatesWithFeatureCounts[templatesWithFeatureCounts.length - 1].id,
|
id: templatesWithFeatureCounts[templatesWithFeatureCounts.length - 1].id,
|
||||||
@ -352,7 +352,7 @@ router.get('/merged', async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const responseData = {
|
const responseData = {
|
||||||
success: true,
|
success: true,
|
||||||
data: templatesWithFeatureCounts,
|
data: templatesWithFeatureCounts,
|
||||||
@ -364,7 +364,7 @@ router.get('/merged', async (req, res) => {
|
|||||||
},
|
},
|
||||||
message: `Found ${templatesWithFeatureCounts.length} templates (out of ${total}) with feature counts`
|
message: `Found ${templatesWithFeatureCounts.length} templates (out of ${total}) with feature counts`
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log('🎉 [MERGED-TEMPLATES] Response prepared successfully:', {
|
console.log('🎉 [MERGED-TEMPLATES] Response prepared successfully:', {
|
||||||
success: responseData.success,
|
success: responseData.success,
|
||||||
dataCount: responseData.data.length,
|
dataCount: responseData.data.length,
|
||||||
@ -376,9 +376,9 @@ router.get('/merged', async (req, res) => {
|
|||||||
is_custom: t.is_custom
|
is_custom: t.is_custom
|
||||||
}))
|
}))
|
||||||
});
|
});
|
||||||
|
|
||||||
res.json(responseData);
|
res.json(responseData);
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('💥 [MERGED-TEMPLATES] Critical error occurred:', error.message);
|
console.error('💥 [MERGED-TEMPLATES] Critical error occurred:', error.message);
|
||||||
console.error('📚 [MERGED-TEMPLATES] Error stack:', error.stack);
|
console.error('📚 [MERGED-TEMPLATES] Error stack:', error.stack);
|
||||||
@ -387,7 +387,7 @@ router.get('/merged', async (req, res) => {
|
|||||||
code: error.code,
|
code: error.code,
|
||||||
sqlMessage: error.sqlMessage
|
sqlMessage: error.sqlMessage
|
||||||
});
|
});
|
||||||
|
|
||||||
res.status(500).json({
|
res.status(500).json({
|
||||||
success: false,
|
success: false,
|
||||||
error: 'Failed to fetch merged templates',
|
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
|
// GET /api/templates/type/:type - Get template by type
|
||||||
router.get('/type/:type', async (req, res) => {
|
router.get('/type/:type', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { type } = req.params;
|
const { type } = req.params;
|
||||||
console.log(`🔍 Fetching template by type: ${type}`);
|
console.log(`🔍 Fetching template by type: ${type}`);
|
||||||
|
|
||||||
const template = await Template.getByType(type);
|
const template = await Template.getByType(type);
|
||||||
|
|
||||||
if (!template) {
|
if (!template) {
|
||||||
return res.status(404).json({
|
return res.status(404).json({
|
||||||
success: false,
|
success: false,
|
||||||
@ -411,11 +441,11 @@ router.get('/type/:type', async (req, res) => {
|
|||||||
message: `Template with type ${type} does not exist`
|
message: `Template with type ${type} does not exist`
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get features for this template
|
// Get features for this template
|
||||||
const features = await Feature.getByTemplateId(template.id);
|
const features = await Feature.getByTemplateId(template.id);
|
||||||
template.features = features;
|
template.features = features;
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
success: true,
|
success: true,
|
||||||
data: template,
|
data: template,
|
||||||
@ -432,7 +462,6 @@ router.get('/type/:type', async (req, res) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// GET /api/templates/:id - Get specific template with features (UUID constrained)
|
// 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) => {
|
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 {
|
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'
|
message: 'id must be a valid UUID v4'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const template = await Template.getByIdWithFeatures(id);
|
const template = await Template.getByIdWithFeatures(id);
|
||||||
|
|
||||||
if (!template) {
|
if (!template) {
|
||||||
return res.status(404).json({
|
return res.status(404).json({
|
||||||
success: false,
|
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`
|
message: `Template with ID ${id} does not exist`
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
success: true,
|
success: true,
|
||||||
data: template,
|
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 {
|
try {
|
||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
console.log(`🎯 Fetching features for template: ${id}`);
|
console.log(`🎯 Fetching features for template: ${id}`);
|
||||||
|
|
||||||
// Check if template exists in either templates or custom_templates table
|
// Check if template exists in either templates or custom_templates table
|
||||||
console.log(`🔍 Searching for template ID: ${id}`);
|
console.log(`🔍 Searching for template ID: ${id}`);
|
||||||
|
|
||||||
// First check templates table
|
// First check templates table
|
||||||
const defaultTemplateCheck = await database.query(`
|
const defaultTemplateCheck = await database.query(`
|
||||||
SELECT id, title, 'default' as template_type FROM templates WHERE id = $1 AND is_active = true
|
SELECT id, title, 'default' as template_type FROM templates WHERE id = $1 AND is_active = true
|
||||||
`, [id]);
|
`, [id]);
|
||||||
console.log(`📊 Default templates found: ${defaultTemplateCheck.rows.length}`);
|
console.log(`📊 Default templates found: ${defaultTemplateCheck.rows.length}`);
|
||||||
|
|
||||||
// Then check custom_templates table
|
// Then check custom_templates table
|
||||||
const customTemplateCheck = await database.query(`
|
const customTemplateCheck = await database.query(`
|
||||||
SELECT id, title, 'custom' as template_type FROM custom_templates WHERE id = $1
|
SELECT id, title, 'custom' as template_type FROM custom_templates WHERE id = $1
|
||||||
`, [id]);
|
`, [id]);
|
||||||
console.log(`📊 Custom templates found: ${customTemplateCheck.rows.length}`);
|
console.log(`📊 Custom templates found: ${customTemplateCheck.rows.length}`);
|
||||||
|
|
||||||
// Combine results
|
// Combine results
|
||||||
const templateCheck = {
|
const templateCheck = {
|
||||||
rows: [...defaultTemplateCheck.rows, ...customTemplateCheck.rows]
|
rows: [...defaultTemplateCheck.rows, ...customTemplateCheck.rows]
|
||||||
};
|
};
|
||||||
|
|
||||||
if (templateCheck.rows.length === 0) {
|
if (templateCheck.rows.length === 0) {
|
||||||
console.log(`❌ Template not found in either table: ${id}`);
|
console.log(`❌ Template not found in either table: ${id}`);
|
||||||
return res.status(404).json({
|
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`
|
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})`);
|
console.log(`✅ Template found: ${templateCheck.rows[0].title} (${templateCheck.rows[0].template_type})`);
|
||||||
|
|
||||||
// Fetch features from both tables for proper separation
|
// Fetch features from both tables for proper separation
|
||||||
console.log('📋 Fetching features from both template_features and custom_features tables');
|
console.log('📋 Fetching features from both template_features and custom_features tables');
|
||||||
|
|
||||||
// Get default/suggested features from template_features table
|
// Get default/suggested features from template_features table
|
||||||
// Include aggregated business rules from feature_business_rules when available
|
// Include aggregated business rules from feature_business_rules when available
|
||||||
const defaultFeaturesQuery = `
|
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 defaultFeaturesResult = await database.query(defaultFeaturesQuery, [id]);
|
||||||
const defaultFeatures = defaultFeaturesResult.rows;
|
const defaultFeatures = defaultFeaturesResult.rows;
|
||||||
console.log(`📊 Found ${defaultFeatures.length} default/suggested features`);
|
console.log(`📊 Found ${defaultFeatures.length} default/suggested features`);
|
||||||
|
|
||||||
// Get custom features from custom_features table with business rules (if table exists)
|
// 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.
|
// 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");
|
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 customFeaturesResult = await database.query(customFeaturesQuery, [id]);
|
||||||
const customFeatures = customFeaturesResult.rows;
|
const customFeatures = customFeaturesResult.rows;
|
||||||
console.log(`📊 Found ${customFeatures.length} custom features`);
|
console.log(`📊 Found ${customFeatures.length} custom features`);
|
||||||
|
|
||||||
// Combine both types of features
|
// Combine both types of features
|
||||||
const features = [...defaultFeatures, ...customFeatures];
|
const features = [...defaultFeatures, ...customFeatures];
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
success: true,
|
success: true,
|
||||||
data: features,
|
data: features,
|
||||||
@ -642,7 +671,7 @@ router.post('/', async (req, res) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
console.log('🏗️ Creating new template - incoming body:', JSON.stringify(debugPayload));
|
console.log('🏗️ Creating new template - incoming body:', JSON.stringify(debugPayload));
|
||||||
|
|
||||||
// Validate required fields
|
// Validate required fields
|
||||||
const requiredFields = ['type', 'title', 'category'];
|
const requiredFields = ['type', 'title', 'category'];
|
||||||
for (const field of requiredFields) {
|
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 flagged as a custom template, store in custom_templates instead
|
||||||
if (templateData.isCustom === true || templateData.is_custom === true || templateData.source === 'custom') {
|
if (templateData.isCustom === true || templateData.is_custom === true || templateData.source === 'custom') {
|
||||||
try {
|
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;
|
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
|
// Check for duplicates in custom templates for this user
|
||||||
const duplicatePayload = {
|
const duplicatePayload = {
|
||||||
type: templateData.type,
|
type: templateData.type,
|
||||||
@ -715,7 +744,7 @@ router.post('/', async (req, res) => {
|
|||||||
category: templateData.category,
|
category: templateData.category,
|
||||||
user_id: incomingUserId
|
user_id: incomingUserId
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log('[POST /api/templates - custom] duplicate payload:', duplicatePayload);
|
console.log('[POST /api/templates - custom] duplicate payload:', duplicatePayload);
|
||||||
const existingCustomTemplate = await CustomTemplate.checkForDuplicate(duplicatePayload);
|
const existingCustomTemplate = await CustomTemplate.checkForDuplicate(duplicatePayload);
|
||||||
if (existingCustomTemplate) {
|
if (existingCustomTemplate) {
|
||||||
@ -804,7 +833,7 @@ router.post('/', async (req, res) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const template = await Template.create(templateData);
|
const template = await Template.create(templateData);
|
||||||
|
|
||||||
// Link back to custom_templates when approving from a custom
|
// Link back to custom_templates when approving from a custom
|
||||||
@ -835,7 +864,7 @@ router.post('/', async (req, res) => {
|
|||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('❌ Error creating template:', error.message);
|
console.error('❌ Error creating template:', error.message);
|
||||||
|
|
||||||
// Handle unique constraint violation
|
// Handle unique constraint violation
|
||||||
if (error.code === '23505') {
|
if (error.code === '23505') {
|
||||||
return res.status(409).json({
|
return res.status(409).json({
|
||||||
@ -844,7 +873,7 @@ router.post('/', async (req, res) => {
|
|||||||
message: 'A template with this type already exists'
|
message: 'A template with this type already exists'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
res.status(500).json({
|
res.status(500).json({
|
||||||
success: false,
|
success: false,
|
||||||
error: 'Failed to create template',
|
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
|
// Validate allowed fields for custom templates to avoid no-op updates
|
||||||
const allowed = [
|
const allowed = [
|
||||||
'title','description','icon','category','gradient','border','text','subtext',
|
'title', 'description', 'icon', 'category', 'gradient', 'border', 'text', 'subtext',
|
||||||
'complexity','business_rules','technical_requirements','approved','usage_count',
|
'complexity', 'business_rules', 'technical_requirements', 'approved', 'usage_count',
|
||||||
'status','admin_notes','admin_reviewed_at','admin_reviewed_by',
|
'status', 'admin_notes', 'admin_reviewed_at', 'admin_reviewed_by',
|
||||||
'canonical_template_id','similarity_score','user_id'
|
'canonical_template_id', 'similarity_score', 'user_id'
|
||||||
];
|
];
|
||||||
const providedKeys = Object.keys(updateData || {});
|
const providedKeys = Object.keys(updateData || {});
|
||||||
const updatableKeys = providedKeys.filter(k => allowed.includes(k));
|
const updatableKeys = providedKeys.filter(k => allowed.includes(k));
|
||||||
@ -967,7 +996,7 @@ router.put('/:id', async (req, res) => {
|
|||||||
console.log('📝 Updating default template...');
|
console.log('📝 Updating default template...');
|
||||||
const updatedTemplate = await template.update(updateData);
|
const updatedTemplate = await template.update(updateData);
|
||||||
console.log('📝 Update result (default):', { updated: !!updatedTemplate });
|
console.log('📝 Update result (default):', { updated: !!updatedTemplate });
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
success: true,
|
success: true,
|
||||||
data: updatedTemplate,
|
data: updatedTemplate,
|
||||||
|
|||||||
@ -34,5 +34,6 @@ REDIS_PASSWORD=your_redis_password
|
|||||||
# JWT Configuration
|
# JWT Configuration
|
||||||
JWT_ACCESS_SECRET=your_access_secret_key
|
JWT_ACCESS_SECRET=your_access_secret_key
|
||||||
JWT_REFRESH_SECRET=your_refresh_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
|
JWT_REFRESH_EXPIRY=7d
|
||||||
|
|||||||
@ -6,11 +6,13 @@ class JWTConfig {
|
|||||||
this.refreshTokenSecret = process.env.JWT_REFRESH_SECRET || 'refresh-secret-key-2024-tech4biz';
|
this.refreshTokenSecret = process.env.JWT_REFRESH_SECRET || 'refresh-secret-key-2024-tech4biz';
|
||||||
this.accessTokenExpiry = process.env.JWT_ACCESS_EXPIRY || '24h';
|
this.accessTokenExpiry = process.env.JWT_ACCESS_EXPIRY || '24h';
|
||||||
this.refreshTokenExpiry = process.env.JWT_REFRESH_EXPIRY || '7d';
|
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, {
|
return jwt.sign(payload, this.accessTokenSecret, {
|
||||||
expiresIn: this.accessTokenExpiry,
|
expiresIn: expiry,
|
||||||
issuer: 'tech4biz-auth',
|
issuer: 'tech4biz-auth',
|
||||||
audience: 'tech4biz-users'
|
audience: 'tech4biz-users'
|
||||||
});
|
});
|
||||||
@ -54,13 +56,15 @@ class JWTConfig {
|
|||||||
role: user.role || 'user'
|
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 refreshToken = this.generateRefreshToken({ userId: user.id });
|
||||||
|
const expiry = isAdmin ? this.adminAccessTokenExpiry : this.accessTokenExpiry;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
accessToken,
|
accessToken,
|
||||||
refreshToken,
|
refreshToken,
|
||||||
expiresIn: this.accessTokenExpiry
|
expiresIn: expiry
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user