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_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
|
||||
|
||||
@ -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' }));
|
||||
|
||||
@ -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
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@ -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
|
||||
router.get('/type/:type', async (req, res) => {
|
||||
try {
|
||||
@ -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 {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user