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