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

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

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 = ` 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
}); });
} }
}); });

View File

@ -396,6 +396,36 @@ 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 {
@ -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 {

View File

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

View File

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