backend changes

This commit is contained in:
Chandini 2025-09-15 14:55:50 +05:30
parent aaf7065431
commit 9c453bfe07
4 changed files with 294 additions and 78 deletions

1
Jenkinsfile vendored
View File

@ -196,6 +196,7 @@ pipeline {
docker compose ps
'
"""
}
}
}

View File

@ -8,7 +8,7 @@ services:
container_name: pipeline_postgres
environment:
POSTGRES_USER: pipeline_admin
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_PASSWORD: secure_pipeline_2024
POSTGRES_DB: dev_pipeline
volumes:
- postgres_data:/var/lib/postgresql/data
@ -26,7 +26,7 @@ services:
redis:
image: redis:7-alpine
container_name: pipeline_redis
command: redis-server --appendonly yes --requirepass ${REDIS_PASSWORD}
command: redis-server --appendonly yes --requirepass redis_secure_2024
volumes:
- redis_data:/data
ports:
@ -45,8 +45,8 @@ services:
image: mongo:7
container_name: pipeline_mongodb
environment:
MONGO_INITDB_ROOT_USERNAME: pipeline_user
MONGO_INITDB_ROOT_PASSWORD: ${MONGODB_PASSWORD}
MONGO_INITDB_ROOT_USERNAME: pipeline_admin
MONGO_INITDB_ROOT_PASSWORD: mongo_secure_2024
volumes:
- mongodb_data:/data/db
ports:
@ -62,7 +62,7 @@ services:
container_name: pipeline_rabbitmq
environment:
RABBITMQ_DEFAULT_USER: pipeline_admin
RABBITMQ_DEFAULT_PASS: ${RABBITMQ_PASSWORD}
RABBITMQ_DEFAULT_PASS: rabbit_secure_2024
volumes:
- rabbitmq_data:/var/lib/rabbitmq
- rabbitmq_logs:/var/log/rabbitmq
@ -93,12 +93,12 @@ services:
- POSTGRES_PORT=5432
- POSTGRES_DB=dev_pipeline
- POSTGRES_USER=pipeline_admin
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- POSTGRES_PASSWORD=secure_pipeline_2024
- REDIS_HOST=redis
- REDIS_PORT=6379
- REDIS_PASSWORD=${REDIS_PASSWORD}
- REDIS_PASSWORD=redis_secure_2024
- NODE_ENV=development
- DATABASE_URL=postgresql://pipeline_admin:${POSTGRES_PASSWORD}@postgres:5432/dev_pipeline
- DATABASE_URL=postgresql://pipeline_admin:secure_pipeline_2024@postgres:5432/dev_pipeline
entrypoint: ["/bin/sh", "-c", "chmod +x ./scripts/migrate-all.sh && ./scripts/migrate-all.sh"]
depends_on:
postgres:
@ -240,20 +240,20 @@ services:
- POSTGRES_PORT=5432
- POSTGRES_DB=dev_pipeline
- POSTGRES_USER=pipeline_admin
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- POSTGRES_PASSWORD=secure_pipeline_2024
# Cache and message queue
- REDIS_HOST=redis
- REDIS_PORT=6379
- REDIS_PASSWORD=${REDIS_PASSWORD}
- REDIS_PASSWORD=redis_secure_2024
- RABBITMQ_HOST=rabbitmq
- RABBITMQ_PORT=5672
- RABBITMQ_USER=pipeline_admin
- RABBITMQ_PASSWORD=${RABBITMQ_PASSWORD}
- RABBITMQ_PASSWORD=rabbit_secure_2024
# JWT configuration
- JWT_ACCESS_SECRET=access-secret-key-2024-tech4biz-secure_pipeline_2024
- JWT_SECRET=access-secret-key-2024-tech4biz-secure_pipeline_2024
# - JWT_ACCESS_SECRET=access-secret-key-2024-tech4biz-${POSTGRES_PASSWORD}
# - JWT_REFRESH_SECRET=refresh-secret-key-2024-tech4biz-${POSTGRES_PASSWORD}
- JWT_SECRET=ultra_secure_jwt_secret_2024
# - JWT_ACCESS_SECRET=access-secret-key-2024-tech4biz-secure_pipeline_2024
# - JWT_REFRESH_SECRET=refresh-secret-key-2024-tech4biz-secure_pipeline_2024
# Service URLs
- USER_AUTH_URL=http://user-auth:8011
- TEMPLATE_MANAGER_URL=http://template-manager:8009
@ -299,10 +299,10 @@ services:
- POSTGRES_PORT=5432
- POSTGRES_DB=dev_pipeline
- POSTGRES_USER=pipeline_admin
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- POSTGRES_PASSWORD=secure_pipeline_2024
- REDIS_HOST=redis
- REDIS_PORT=6379
- REDIS_PASSWORD=${REDIS_PASSWORD}
- REDIS_PASSWORD=redis_secure_2024
- MONGODB_HOST=mongodb
- MONGODB_PORT=27017
- NEO4J_URI=bolt://neo4j:7687
@ -310,7 +310,7 @@ services:
- NEO4J_PASSWORD=password
- CHROMA_HOST=chromadb
- CHROMA_PORT=8000
- REDIS_URL=redis://redis:6379
- REDIS_URL=redis://:redis_secure_2024@redis:6379
networks:
- pipeline_network
depends_on:
@ -333,10 +333,10 @@ services:
- POSTGRES_PORT=5432
- POSTGRES_DB=dev_pipeline
- POSTGRES_USER=pipeline_admin
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- POSTGRES_PASSWORD=secure_pipeline_2024
- REDIS_HOST=redis
- REDIS_PORT=6379
- REDIS_PASSWORD=${REDIS_PASSWORD}
- REDIS_PASSWORD=redis_secure_2024
networks:
- pipeline_network
depends_on:
@ -355,13 +355,13 @@ services:
environment:
- PORT=8003
- HOST=0.0.0.0
- CLAUDE_API_KEY=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
- ANTHROPIC_API_KEY=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
- CLAUDE_API_KEY=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
- ANTHROPIC_API_KEY=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
- POSTGRES_HOST=postgres
- POSTGRES_PORT=5432
- POSTGRES_DB=dev_pipeline
- POSTGRES_USER=pipeline_admin
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- POSTGRES_PASSWORD=secure_pipeline_2024
- MONGODB_HOST=mongodb
- MONGODB_PORT=27017
networks:
@ -391,13 +391,13 @@ services:
- POSTGRES_PORT=5432
- POSTGRES_DB=dev_pipeline
- POSTGRES_USER=pipeline_admin
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- POSTGRES_PASSWORD=secure_pipeline_2024
- MONGODB_HOST=mongodb
- MONGODB_PORT=27017
- REDIS_HOST=redis
- REDIS_PORT=6379
- REDIS_PASSWORD=${REDIS_PASSWORD}
- CLAUDE_API_KEY=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
- REDIS_PASSWORD=redis_secure_2024
- CLAUDE_API_KEY=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
- OPENAI_API_KEY=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
# - NEO4J_URI=bolt://neo4j:7687
# - NEO4J_USER=neo4j
@ -440,10 +440,10 @@ services:
- POSTGRES_PORT=5432
- POSTGRES_DB=dev_pipeline
- POSTGRES_USER=pipeline_admin
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- POSTGRES_PASSWORD=secure_pipeline_2024
- REDIS_HOST=redis
- REDIS_PORT=6379
- REDIS_PASSWORD=${REDIS_PASSWORD}
- REDIS_PASSWORD=redis_secure_2024
networks:
- pipeline_network
depends_on:
@ -464,13 +464,13 @@ services:
- POSTGRES_PORT=5432
- POSTGRES_DB=dev_pipeline
- POSTGRES_USER=pipeline_admin
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- POSTGRES_PASSWORD=secure_pipeline_2024
- MONGODB_HOST=mongodb
- MONGODB_PORT=27017
- RABBITMQ_HOST=rabbitmq
- RABBITMQ_PORT=5672
- RABBITMQ_USER=pipeline_admin
- RABBITMQ_PASSWORD=${RABBITMQ_PASSWORD}
- RABBITMQ_PASSWORD=rabbit_secure_2024
networks:
- pipeline_network
depends_on:
@ -496,25 +496,25 @@ services:
- POSTGRES_PORT=5432
- POSTGRES_DB=dev_pipeline
- POSTGRES_USER=pipeline_admin
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- POSTGRES_PASSWORD=secure_pipeline_2024
- REDIS_HOST=redis
- REDIS_PORT=6379
- REDIS_PASSWORD=${REDIS_PASSWORD}
- REDIS_PASSWORD=redis_secure_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-secure_pipeline_2024
- JWT_ACCESS_EXPIRY=24h
- JWT_ADMIN_ACCESS_EXPIRY=7d
- JWT_REFRESH_EXPIRY=7d
- FRONTEND_URL=*
# Email Configuration
- SMTP_HOST=${SMTP_HOST:-smtp.gmail.com}
- SMTP_PORT=${SMTP_PORT:-587}
- SMTP_SECURE=${SMTP_SECURE:-false}
- SMTP_USER=${SMTP_USER:-frontendtechbiz@gmail.com}
- SMTP_PASS=${SMTP_PASS:-oidhhjeasgzbqptq}
- SMTP_FROM=${SMTP_FROM:-frontendtechbiz@gmail.com}
- GMAIL_USER=${GMAIL_USER:-frontendtechbiz@gmail.com}
- GMAIL_APP_PASSWORD=${GMAIL_APP_PASSWORD:-oidhhjeasgzbqptq}
- SMTP_HOST=smtp.gmail.com
- SMTP_PORT=587
- SMTP_SECURE=false
- SMTP_USER=frontendtechbiz@gmail.com
- SMTP_PASS=oidhhjeasgzbqptq
- SMTP_FROM=frontendtechbiz@gmail.com
- GMAIL_USER=frontendtechbiz@gmail.com
- GMAIL_APP_PASSWORD=oidhhjeasgzbqptq
- AUTH_PUBLIC_URL=*
- TEMPLATE_MANAGER_URL=http://template-manager:8009
networks:
@ -551,10 +551,10 @@ services:
- POSTGRES_PORT=5432
- POSTGRES_DB=dev_pipeline
- POSTGRES_USER=pipeline_admin
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- POSTGRES_PASSWORD=secure_pipeline_2024
- REDIS_HOST=redis
- REDIS_PORT=6379
- REDIS_PASSWORD=${REDIS_PASSWORD}
- REDIS_PASSWORD=redis_secure_2024
- NODE_ENV=development
- JWT_ACCESS_SECRET=access-secret-key-2024-tech4biz-secure_pipeline_2024
networks:
@ -584,16 +584,16 @@ services:
environment:
- PORT=8021
- HOST=0.0.0.0
- CLAUDE_API_KEY=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
- CLAUDE_API_KEY=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
- POSTGRES_HOST=postgres
- POSTGRES_PORT=5432
- POSTGRES_DB=dev_pipeline
- POSTGRES_USER=pipeline_admin
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- POSTGRES_PASSWORD=secure_pipeline_2024
- REDIS_HOST=redis
- REDIS_PORT=6379
- REDIS_PASSWORD=${REDIS_PASSWORD}
- JWT_ACCESS_SECRET=access-secret-key-2024-tech4biz-${POSTGRES_PASSWORD}
- REDIS_PASSWORD=redis_secure_2024
- JWT_ACCESS_SECRET=access-secret-key-2024-tech4biz-secure_pipeline_2024
- USER_AUTH_SERVICE_URL=http://user-auth:8011
- FLASK_ENV=development
networks:
@ -622,10 +622,10 @@ services:
- POSTGRES_PORT=5432
- POSTGRES_DB=dev_pipeline
- POSTGRES_USER=pipeline_admin
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- POSTGRES_PASSWORD=secure_pipeline_2024
- REDIS_HOST=redis
- REDIS_PORT=6379
- REDIS_PASSWORD=${REDIS_PASSWORD}
- REDIS_PASSWORD=redis_secure_2024
- NODE_ENV=development
- GITHUB_REDIRECT_URI=*
- ATTACHED_REPOS_DIR=/tmp/attached-repos
@ -657,9 +657,9 @@ services:
environment:
- PORT=8007
- HOST=0.0.0.0
- DATABASE_URL=postgresql://pipeline_admin:${POSTGRES_PASSWORD}@postgres:5432/dev_pipeline
- CLAUDE_API_KEY=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
- REDIS_URL=redis://pipeline_redis:6379
- DATABASE_URL=postgresql://pipeline_admin:secure_pipeline_2024@postgres:5432/dev_pipeline
- CLAUDE_API_KEY=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
- REDIS_URL=redis://:redis_secure_2024@pipeline_redis:6379
- SERVICE_PORT=8007
- LOG_LEVEL=INFO
- DEFAULT_TARGET_QUALITY=0.85
@ -697,8 +697,8 @@ services:
environment:
- NODE_ENV=production
- PORT=8008
- DATABASE_URL=postgresql://pipeline_admin:${POSTGRES_PASSWORD}@postgres:5432/dev_pipeline
- REDIS_URL=redis://pipeline_redis:6379
- DATABASE_URL=postgresql://pipeline_admin:secure_pipeline_2024@postgres:5432/dev_pipeline
- REDIS_URL=redis://:redis_secure_2024@pipeline_redis:6379
- API_GATEWAY_URL=http://pipeline_api_gateway:8000
- CODE_GENERATOR_URL=http://pipeline_code_generator:8004
- SELF_IMPROVING_URL=http://pipeline_self_improving_generator:8007
@ -738,8 +738,8 @@ services:
- "5678:5678"
environment:
- N8N_BASIC_AUTH_ACTIVE=true
- N8N_BASIC_AUTH_USER=pipeline_admin
- N8N_BASIC_AUTH_PASSWORD=pipeline_n8n_2024
- N8N_BASIC_AUTH_USER=admin
- N8N_BASIC_AUTH_PASSWORD=admin_n8n_2024
- N8N_HOST=localhost
- N8N_PORT=5678
- N8N_PROTOCOL=http
@ -750,7 +750,7 @@ services:
- DB_POSTGRESDB_PORT=5432
- DB_POSTGRESDB_DATABASE=n8n
- DB_POSTGRESDB_USER=pipeline_admin
- DB_POSTGRESDB_PASSWORD=${POSTGRES_PASSWORD}
- DB_POSTGRESDB_PASSWORD=secure_pipeline_2024
volumes:
- n8n_data:/home/node/.n8n
- ./orchestration/n8n/workflows:/home/node/.n8n/workflows

View File

@ -230,31 +230,46 @@ class CustomFeature {
const updated = await CustomFeature.update(id, updates);
// If approved, ensure a mirrored entry exists/updates in template_features
// Only mirror if the template_id exists in the main templates table
if (updated && status === 'approved') {
try {
const Feature = require('./feature');
const featureId = `custom_${updated.id}`;
const existingMirror = await Feature.getByFeatureId(updated.template_id, featureId);
if (existingMirror) {
await Feature.update(existingMirror.id, {
name: updated.name,
description: updated.description,
complexity: updated.complexity,
feature_type: 'custom',
is_default: false
});
// Check if template_id exists in main templates table
const templateCheck = await database.query(
'SELECT id FROM templates WHERE id = $1 AND is_active = true',
[updated.template_id]
);
if (templateCheck.rows.length > 0) {
// Template exists in main templates table, safe to mirror
const Feature = require('./feature');
const featureId = `custom_${updated.id}`;
const existingMirror = await Feature.getByFeatureId(updated.template_id, featureId);
if (existingMirror) {
await Feature.update(existingMirror.id, {
name: updated.name,
description: updated.description,
complexity: updated.complexity,
feature_type: 'custom',
is_default: false
});
console.log('✅ Updated mirrored feature in template_features for approved custom feature');
} else {
await Feature.create({
template_id: updated.template_id,
feature_id: featureId,
name: updated.name,
description: updated.description,
feature_type: 'custom',
complexity: updated.complexity,
display_order: 999,
is_default: false,
created_by_user: true
});
console.log('✅ Created mirrored feature in template_features for approved custom feature');
}
} else {
await Feature.create({
template_id: updated.template_id,
feature_id: featureId,
name: updated.name,
description: updated.description,
feature_type: 'custom',
complexity: updated.complexity,
display_order: 999,
is_default: false,
created_by_user: true
});
// Template is likely a custom template, don't mirror to template_features
console.log(' Custom feature approved but template_id references custom template, skipping mirror to template_features');
}
} catch (mirrorErr) {
console.error('⚠️ Failed to mirror approved custom feature into template_features:', mirrorErr.message);

View File

@ -48,6 +48,206 @@ const requireAdmin = (req, res, next) => {
// Apply admin middleware to all routes
router.use(requireAdmin);
// GET /api/admin/custom-features - Proxy to existing custom features functionality
router.get('/custom-features', async (req, res) => {
try {
const limit = parseInt(req.query.limit) || 50;
const offset = parseInt(req.query.offset) || 0;
const status = req.query.status;
console.log(`Admin: Fetching custom features (status: ${status || 'all'}, limit: ${limit}, offset: ${offset})`);
let features;
if (status) {
features = await CustomFeature.getFeaturesByStatus(status, limit, offset);
} else {
features = await CustomFeature.getAllFeatures(limit, offset);
}
res.json({
success: true,
data: features,
count: features.length,
message: `Found ${features.length} custom features`
});
} catch (error) {
console.error('Error fetching custom features:', error.message);
res.status(500).json({
success: false,
error: 'Failed to fetch custom features',
message: error.message
});
}
});
// POST /api/admin/custom-features/:id/review - Review custom feature
router.post('/custom-features/:id/review', async (req, res) => {
try {
const { id } = req.params;
const { status, admin_notes, admin_reviewed_by } = req.body;
const validStatuses = ['approved', 'rejected', 'duplicate'];
if (!validStatuses.includes(status)) {
return res.status(400).json({
success: false,
error: 'Invalid status',
message: `Status must be one of: ${validStatuses.join(', ')}`
});
}
console.log(`🔍 Admin: Reviewing custom feature ${id} with status: ${status}`);
const feature = await CustomFeature.getById(id);
if (!feature) {
return res.status(404).json({
success: false,
error: 'Feature not found',
message: 'The specified feature does not exist'
});
}
const reviewData = {
status,
admin_notes,
admin_reviewed_by: admin_reviewed_by || req.user.id
};
const updatedFeature = await CustomFeature.reviewFeature(id, reviewData);
try {
await AdminNotification.notifyFeatureReviewed(id, feature.name, status);
} catch (notifError) {
console.warn('⚠️ Failed to create notification:', notifError.message);
}
res.json({
success: true,
data: updatedFeature,
message: `Feature "${feature.name}" has been ${status}`
});
} catch (error) {
console.error('❌ Error reviewing custom feature:', error.message);
res.status(500).json({
success: false,
error: 'Failed to review custom feature',
message: error.message
});
}
});
// GET /api/admin/custom-templates - Get custom templates
router.get('/custom-templates', async (req, res) => {
try {
const limit = parseInt(req.query.limit) || 50;
const offset = parseInt(req.query.offset) || 0;
const status = req.query.status;
console.log(`Admin: Fetching custom templates (status: ${status || 'all'}, limit: ${limit}, offset: ${offset})`);
let templates;
if (status) {
templates = await CustomTemplate.getTemplatesByStatus(status, limit, offset);
} else {
templates = await CustomTemplate.getAllTemplates(limit, offset);
}
res.json({
success: true,
data: templates,
count: templates.length,
message: `Found ${templates.length} custom templates`
});
} catch (error) {
console.error('Error fetching custom templates:', error.message);
res.status(500).json({
success: false,
error: 'Failed to fetch custom templates',
message: error.message
});
}
});
// POST /api/admin/custom-templates/:id/review - Review custom template
router.post('/custom-templates/:id/review', async (req, res) => {
try {
const { id } = req.params;
const { status, admin_notes, admin_reviewed_by } = req.body;
const validStatuses = ['approved', 'rejected', 'duplicate'];
if (!validStatuses.includes(status)) {
return res.status(400).json({
success: false,
error: 'Invalid status',
message: `Status must be one of: ${validStatuses.join(', ')}`
});
}
console.log(`🔍 Admin: Reviewing custom template ${id} with status: ${status}`);
const template = await CustomTemplate.getById(id);
if (!template) {
return res.status(404).json({
success: false,
error: 'Template not found',
message: 'The specified template does not exist'
});
}
const reviewData = {
status,
admin_notes,
admin_reviewed_by: admin_reviewed_by || req.user.id
};
const updatedTemplate = await CustomTemplate.reviewTemplate(id, reviewData);
try {
await AdminNotification.notifyTemplateReviewed(id, template.title, status);
} catch (notifError) {
console.warn('⚠️ Failed to create notification:', notifError.message);
}
res.json({
success: true,
data: updatedTemplate,
message: `Template "${template.title}" has been ${status}`
});
} catch (error) {
console.error('❌ Error reviewing custom template:', error.message);
res.status(500).json({
success: false,
error: 'Failed to review custom template',
message: error.message
});
}
});
// GET /api/admin/templates/stats - Get template statistics
router.get('/templates/stats', async (req, res) => {
try {
console.log('📊 Admin: Fetching template statistics...');
const stats = await CustomTemplate.getTemplateStats();
const notificationCounts = await AdminNotification.getCounts();
res.json({
success: true,
data: {
templates: stats,
notifications: notificationCounts
},
message: 'Template statistics retrieved successfully'
});
} catch (error) {
console.error('❌ Error fetching template stats:', error.message);
res.status(500).json({
success: false,
error: 'Failed to fetch template statistics',
message: error.message
});
}
});
// GET /api/admin/features/pending - Get pending features for review
router.get('/features/pending', async (req, res) => {
try {