backend changes

This commit is contained in:
Chandini 2025-09-12 17:47:53 +05:30
parent 49a19447dc
commit c5e62ae68b
8 changed files with 190 additions and 73 deletions

View File

@ -231,8 +231,8 @@ services:
- NODE_ENV=development
- PORT=8000
- HOST=0.0.0.0
- FRONTEND_URL=http://localhost:3001 # Make sure this matches your frontend URL
- CORS_ORIGINS=http://localhost:3001 # Add this line
- FRONTEND_URL=* # Allow all URLs
- CORS_ORIGINS=* # Allow all URLs
- CORS_METHODS=GET,POST,PUT,DELETE,PATCH,OPTIONS # Add this line
- CORS_CREDENTIALS=true # Add this line
# Database connections
@ -500,11 +500,11 @@ services:
- REDIS_HOST=redis
- REDIS_PORT=6379
- REDIS_PASSWORD=${REDIS_PASSWORD}
- JWT_ACCESS_SECRET=access-secret-key-2024-tech4biz-${POSTGRES_PASSWORD}
- 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_REFRESH_EXPIRY=7d
- FRONTEND_URL=http://localhost:3001
- FRONTEND_URL=*
# Email Configuration
- SMTP_HOST=${SMTP_HOST:-smtp.gmail.com}
- SMTP_PORT=${SMTP_PORT:-587}
@ -514,7 +514,7 @@ services:
- SMTP_FROM=${SMTP_FROM:-frontendtechbiz@gmail.com}
- GMAIL_USER=${GMAIL_USER:-frontendtechbiz@gmail.com}
- GMAIL_APP_PASSWORD=${GMAIL_APP_PASSWORD:-oidhhjeasgzbqptq}
- AUTH_PUBLIC_URL=http://localhost:3001
- AUTH_PUBLIC_URL=*
- TEMPLATE_MANAGER_URL=http://template-manager:8009
networks:
- pipeline_network
@ -616,7 +616,7 @@ services:
environment:
- PORT=8012
- HOST=0.0.0.0
- FRONTEND_URL=http://localhost:3000
- FRONTEND_URL=*
- POSTGRES_HOST=postgres
- POSTGRES_PORT=5432
- POSTGRES_DB=dev_pipeline
@ -626,7 +626,7 @@ services:
- REDIS_PORT=6379
- REDIS_PASSWORD=${REDIS_PASSWORD}
- NODE_ENV=development
- GITHUB_REDIRECT_URI=http://localhost:8012/api/github/auth/github/callback
- GITHUB_REDIRECT_URI=*
- ATTACHED_REPOS_DIR=/tmp/attached-repos
- SESSION_SECRET=git-integration-secret-key-2024
volumes:

View File

@ -2,22 +2,8 @@ const cors = require('cors');
const corsMiddleware = cors({
origin: function (origin, callback) {
// Allow requests from your frontend and other services
const allowedOrigins = [
'http://localhost:3001', // Frontend (CodeNuk)
'http://localhost:3000', // Alternative frontend port
'http://localhost:8008', // Dashboard service
'http://localhost:8000', // API Gateway
process.env.CORS_ORIGIN,
process.env.FRONTEND_URL
].filter(Boolean);
// Allow requests with no origin (mobile apps, etc.) or from allowed origins
if (!origin || allowedOrigins.indexOf(origin) !== -1) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}
// Allow all origins
callback(null, true);
},
methods: process.env.CORS_METHODS?.split(',') || ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
credentials: process.env.CORS_CREDENTIALS === 'true' || true,

View File

@ -26,19 +26,23 @@ const websocketRouter = require('./routes/websocketRouter');
const app = express();
// Apply CORS middleware before other middleware
app.use(corsMiddleware);
// Ensure CORS preflight (OPTIONS) requests are handled globally before any proxies
app.options('*', corsMiddleware);
// Force explicit ACAO for credentialed requests (avoid downstream "*")
app.use((req, res, next) => {
const origin = req.headers.origin || '*';
res.setHeader('Access-Control-Allow-Origin', origin);
res.setHeader('Vary', 'Origin');
res.setHeader('Access-Control-Allow-Credentials', 'true');
next();
});
const server = http.createServer(app);
const PORT = process.env.PORT || 8000;
// Initialize Socket.IO with CORS
const io = socketIo(server, {
cors: {
origin: [
'http://localhost:3001', // Frontend (CodeNuk)
'http://localhost:3000', // Alternative frontend port
'http://localhost:8008', // Dashboard service
'http://localhost:8000', // API Gateway
process.env.FRONTEND_URL
].filter(Boolean),
origin: "*",
credentials: true,
methods: ['GET', 'POST']
},
@ -64,6 +68,12 @@ const serviceTargets = {
AI_MOCKUP_URL: process.env.AI_MOCKUP_URL || 'http://localhost:8021',
};
// Log service targets for debugging
console.log('🔧 Service Targets Configuration:');
Object.entries(serviceTargets).forEach(([name, url]) => {
console.log(` ${name}: ${url}`);
});
// ========================================
// MIDDLEWARE SETUP
// ========================================
@ -158,6 +168,32 @@ app.get('/health', (req, res) => {
// Service health monitoring routes
app.use('/health', healthRouter.router);
// Auth service health check endpoint
app.get('/api/auth/health', async (req, res) => {
const authServiceUrl = serviceTargets.USER_AUTH_URL;
const targetUrl = `${authServiceUrl}/health`;
try {
console.log(`🔍 [AUTH HEALTH] Checking: ${targetUrl}`);
const response = await axios.get(targetUrl, { timeout: 5000 });
res.json({
success: true,
auth_service: 'healthy',
target_url: targetUrl,
response: response.data
});
} catch (error) {
console.error(`❌ [AUTH HEALTH] Error:`, error.message);
res.status(502).json({
success: false,
auth_service: 'unhealthy',
target_url: targetUrl,
error: error.message,
code: error.code
});
}
});
// WebSocket connection handling
const websocketHandlers = websocketAuth(io);
@ -165,7 +201,10 @@ const websocketHandlers = websocketAuth(io);
console.log('🔧 Registering /api/auth proxy route...');
app.use('/api/auth', (req, res, next) => {
const authServiceUrl = serviceTargets.USER_AUTH_URL;
console.log(`🔥 [AUTH PROXY] ${req.method} ${req.originalUrl}${authServiceUrl}${req.originalUrl}`);
const targetUrl = `${authServiceUrl}${req.originalUrl}`;
console.log(`🔥 [AUTH PROXY] ${req.method} ${req.originalUrl}${targetUrl}`);
console.log(`🔍 [AUTH PROXY] Service URL: ${authServiceUrl}`);
console.log(`🔍 [AUTH PROXY] Full target: ${targetUrl}`);
// Set response timeout to prevent hanging
res.setTimeout(15000, () => {
@ -177,15 +216,19 @@ app.use('/api/auth', (req, res, next) => {
const options = {
method: req.method,
url: `${authServiceUrl}${req.originalUrl}`,
url: targetUrl,
headers: {
'Content-Type': 'application/json',
'User-Agent': 'API-Gateway/1.0',
'Connection': 'keep-alive',
// Forward Authorization header so protected auth-admin routes work
'Authorization': req.headers.authorization
'Authorization': req.headers.authorization,
// Forward all relevant headers
'X-Forwarded-For': req.ip,
'X-Forwarded-Proto': req.protocol,
'X-Forwarded-Host': req.get('host')
},
timeout: 8000,
timeout: 10000,
validateStatus: () => true,
maxRedirects: 0
};
@ -196,23 +239,46 @@ app.use('/api/auth', (req, res, next) => {
console.log(`📦 [AUTH PROXY] Request body:`, JSON.stringify(req.body));
}
console.log(`🚀 [AUTH PROXY] Making request to: ${targetUrl}`);
axios(options)
.then(response => {
console.log(`✅ [AUTH PROXY] Response: ${response.status} for ${req.method} ${req.originalUrl}`);
console.log(`📊 [AUTH PROXY] Response headers:`, response.headers);
if (!res.headersSent) {
// Forward response headers except CORS; gateway controls CORS
Object.keys(response.headers).forEach(key => {
const k = key.toLowerCase();
if (k === 'content-encoding' || k === 'transfer-encoding') return;
if (k.startsWith('access-control-')) return; // strip downstream CORS
res.setHeader(key, response.headers[key]);
});
// Set gateway CORS headers explicitly (support credentials)
const origin = req.headers.origin || '*';
res.removeHeader('Access-Control-Allow-Origin');
res.removeHeader('access-control-allow-origin');
res.setHeader('Access-Control-Allow-Origin', origin);
res.setHeader('Vary', 'Origin');
res.setHeader('Access-Control-Allow-Credentials', 'true');
res.setHeader('Access-Control-Expose-Headers', 'Content-Length, X-Total-Count, X-Gateway-Request-ID, X-Gateway-Timestamp, X-Forwarded-By, X-Forwarded-For, X-Forwarded-Proto, X-Forwarded-Host');
res.status(response.status).json(response.data);
}
})
.catch(error => {
console.error(`❌ [AUTH PROXY ERROR]:`, error.message);
console.error(`❌ [AUTH PROXY ERROR CODE]:`, error.code);
console.error(`❌ [AUTH PROXY ERROR STACK]:`, error.stack);
if (!res.headersSent) {
if (error.response) {
console.log(`📊 [AUTH PROXY] Error response status: ${error.response.status}`);
console.log(`📊 [AUTH PROXY] Error response data:`, error.response.data);
res.status(error.response.status).json(error.response.data);
} else {
res.status(502).json({
error: 'Auth service unavailable',
message: error.code || error.message,
service: 'user-auth'
service: 'user-auth',
target_url: targetUrl,
details: process.env.NODE_ENV === 'development' ? error.stack : undefined
});
}
}

View File

@ -22,11 +22,7 @@ const app = express();
const server = http.createServer(app);
const io = new Server(server, {
cors: {
origin: [
'http://localhost:3001', // Frontend (CodeNuk)
'http://localhost:3000', // Alternative frontend port
process.env.FRONTEND_URL
].filter(Boolean),
origin: "*",
methods: ["GET", "POST"],
credentials: true
}
@ -36,12 +32,7 @@ const PORT = process.env.PORT || 8009;
// Middleware
app.use(helmet());
app.use(cors({
origin: [
'http://localhost:3001', // Frontend (CodeNuk)
'http://localhost:3000', // Alternative frontend port
'http://localhost:8000', // API Gateway
process.env.FRONTEND_URL
].filter(Boolean),
origin: "*",
credentials: true,
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
allowedHeaders: ['Content-Type', 'Authorization', 'X-User-ID', 'X-User-Role']

View File

@ -162,7 +162,7 @@ router.get('/type/:type', async (req, res) => {
}
});
// GET /api/features/:id - Get specific default feature
// GET /api/features/:id - Get specific default feature (with aggregated business rules if present)
router.get('/:id', async (req, res) => {
try {
const { id } = req.params;
@ -178,11 +178,22 @@ router.get('/:id', async (req, res) => {
});
}
res.json({
success: true,
data: feature,
message: `Feature '${feature.name}' retrieved successfully`
});
// Try to fetch aggregated business rules for this feature from feature_business_rules using both keys
try {
const rulesQuery = `
SELECT business_rules
FROM feature_business_rules
WHERE template_id = $1 AND (feature_id = $2 OR feature_id = $3)
LIMIT 1
`
const rulesResult = await database.query(rulesQuery, [feature.template_id, String(id), feature.feature_id])
const additional = rulesResult.rows?.[0]?.business_rules || null
const payload = { ...feature, additional_business_rules: additional }
return res.json({ success: true, data: payload, message: `Feature '${feature.name}' retrieved successfully` })
} catch (rulesErr) {
console.warn('⚠️ Failed to fetch aggregated rules for feature:', rulesErr.message)
return res.json({ success: true, data: feature, message: `Feature '${feature.name}' retrieved successfully` })
}
} catch (error) {
console.error('❌ Error fetching feature:', error.message);
res.status(500).json({
@ -522,8 +533,63 @@ router.post('/custom', async (req, res) => {
router.get('/templates/:templateId/features', async (req, res) => {
try {
const { templateId } = req.params;
const defaults = await Feature.getByTemplateId(templateId);
const customs = await CustomFeature.getByTemplateId(templateId);
// Include aggregated rules for default/suggested features
const defaultsQuery = `
SELECT
tf.*,
fbr.business_rules AS additional_business_rules
FROM template_features tf
LEFT JOIN feature_business_rules fbr
ON tf.template_id = fbr.template_id
AND (
fbr.feature_id = (tf.id::text)
OR fbr.feature_id = tf.feature_id
)
WHERE tf.template_id = $1
ORDER BY
CASE tf.feature_type
WHEN 'essential' THEN 1
WHEN 'suggested' THEN 2
WHEN 'custom' THEN 3
END,
tf.display_order,
tf.usage_count DESC,
tf.name
`;
const defaultsResult = await database.query(defaultsQuery, [templateId]);
const defaults = defaultsResult.rows;
// Fetch custom features with joined business rules like in templates.js
const customFeaturesQuery = `
SELECT
cf.id,
cf.template_id,
cf.name,
cf.description,
cf.complexity,
cf.business_rules,
cf.technical_requirements,
'custom' as feature_type,
cf.created_at,
cf.updated_at,
cf.status,
cf.approved,
cf.usage_count,
0 as user_rating,
false as is_default,
true as created_by_user,
fbr.business_rules as additional_business_rules
FROM custom_features cf
LEFT JOIN feature_business_rules fbr
ON cf.template_id = fbr.template_id
AND (
fbr.feature_id = (cf.id::text)
OR fbr.feature_id = ('custom_' || cf.id::text)
)
WHERE cf.template_id = $1
ORDER BY cf.created_at DESC
`;
const customsResult = await database.query(customFeaturesQuery, [templateId]);
const customs = customsResult.rows;
// Map custom model to template-like shape
const customAsTemplate = customs.map(cf => ({
id: cf.id,

View File

@ -516,7 +516,31 @@ router.get('/:id([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/f
console.log('📋 Fetching features from both template_features and custom_features tables');
// Get default/suggested features from template_features table
const defaultFeatures = await Feature.getByTemplateId(id);
// Include aggregated business rules from feature_business_rules when available
const defaultFeaturesQuery = `
SELECT
tf.*,
fbr.business_rules AS additional_business_rules
FROM template_features tf
LEFT JOIN feature_business_rules fbr
ON tf.template_id = fbr.template_id
AND (
fbr.feature_id = (tf.id::text)
OR fbr.feature_id = tf.feature_id
)
WHERE tf.template_id = $1
ORDER BY
CASE tf.feature_type
WHEN 'essential' THEN 1
WHEN 'suggested' THEN 2
WHEN 'custom' THEN 3
END,
tf.display_order,
tf.usage_count DESC,
tf.name
`;
const defaultFeaturesResult = await database.query(defaultFeaturesQuery, [id]);
const defaultFeatures = defaultFeaturesResult.rows;
console.log(`📊 Found ${defaultFeatures.length} default/suggested features`);
// Get custom features from custom_features table with business rules (if table exists)

View File

@ -41,23 +41,7 @@ app.use(securityHeaders);
// CORS configuration
const corsOptions = {
origin: function (origin, callback) {
// Allow requests from your web-dashboard and other services
const allowedOrigins = [
'http://localhost:3001', // Frontend (CodeNuk)
'http://localhost:3000', // Alternative frontend port
'http://localhost:8008', // Dashboard service
'http://localhost:8000', // API Gateway
process.env.FRONTEND_URL
].filter(Boolean);
// Allow requests with no origin (mobile apps, etc.)
if (!origin || allowedOrigins.indexOf(origin) !== -1) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}
},
origin: "*",
credentials: true, // Allow cookies
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
allowedHeaders: ['Content-Type', 'Authorization', 'X-Session-Token', 'X-Platform', 'X-App-Version']

View File

@ -2,7 +2,7 @@ const jwt = require('jsonwebtoken');
class JWTConfig {
constructor() {
this.accessTokenSecret = process.env.JWT_ACCESS_SECRET || 'access-secret-key-2024-tech4biz';
this.accessTokenSecret = process.env.JWT_ACCESS_SECRET || 'access-secret-key-2024-tech4biz-secure_pipeline_2024';
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';