Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 68 additions & 32 deletions backend/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,70 +3,106 @@ require('dotenv').config();
const express = require('express');
const cors = require('cors');
const mongoose = require('mongoose');
const helmet = require('helmet');
const rateLimit = require('express-rate-limit');
const mongoSanitize = require('express-mongo-sanitize');
const xss = require('xss-clean');

const seedDB = require('./seed/productSeeds');
const syncWeaviate = require('./sync/syncWeaviate');
const productRoutes = require('./routes/products');
const checkoutRoutes = require('./routes/checkout');
const authRoutes = require('./routes/auth');
const { swaggerUi, swaggerSpec, setupSwaggerUi, setupSwaggerJson } = require('./docs/swagger');
const searchRoutes = require('./routes/search');
const { setupSwaggerUi, setupSwaggerJson } = require('./docs/swagger');

// Validate required environment variables
const requiredEnv = ['MONGO_URI', 'JWT_SECRET'];
requiredEnv.forEach(key => {
if (!process.env[key]) {
console.error(`❌ Missing required env var: ${key}`);
process.exit(1);
}
});

// Create Express App
const app = express();
const PORT = process.env.PORT || 8000;

// Database Connection + Seed + Weaviate Sync + Server Start
// Security middleware
app.disable('x-powered-by');
app.use(helmet());

const corsOptions = {
origin: process.env.CORS_ORIGIN?.split(',') || [],
Comment on lines +36 to +37
Copy link

Copilot AI Sep 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CORS origin configuration allows an empty array as fallback, which would block all origins. Consider using a secure default like ['http://localhost:3000'] for development or require CORS_ORIGIN to be explicitly set in production.

Suggested change
const corsOptions = {
origin: process.env.CORS_ORIGIN?.split(',') || [],
let allowedOrigins;
if (process.env.NODE_ENV === 'production') {
if (!process.env.CORS_ORIGIN) {
console.error('❌ Missing required env var: CORS_ORIGIN in production');
process.exit(1);
}
allowedOrigins = process.env.CORS_ORIGIN.split(',');
} else {
allowedOrigins = process.env.CORS_ORIGIN
? process.env.CORS_ORIGIN.split(',')
: ['http://localhost:3000'];
}
const corsOptions = {
origin: allowedOrigins,

Copilot uses AI. Check for mistakes.
methods: ['GET', 'POST', 'PUT', 'DELETE'],
credentials: true,
};
app.use(cors(corsOptions));

app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(mongoSanitize());
app.use(xss());

// Rate limiter for auth routes
const authLimiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 10,
Comment on lines +50 to +51
Copy link

Copilot AI Sep 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The rate limiting configuration uses magic numbers. Consider moving these values to environment variables (e.g., AUTH_RATE_LIMIT_WINDOW_MS, AUTH_RATE_LIMIT_MAX) for better configurability across different environments.

Suggested change
windowMs: 15 * 60 * 1000,
max: 10,
windowMs: parseInt(process.env.AUTH_RATE_LIMIT_WINDOW_MS, 10) || 15 * 60 * 1000,
max: parseInt(process.env.AUTH_RATE_LIMIT_MAX, 10) || 10,

Copilot uses AI. Check for mistakes.
message: 'Too many login attempts, try again later.',
});
app.use('/api/auth', authLimiter, authRoutes);

// Routes
app.use('/api/products', productRoutes);
app.use('/api/checkout', checkoutRoutes);
app.use('/api/search', searchRoutes);

// Swagger docs (disable in production unless needed)
if (process.env.NODE_ENV !== 'production') {
app.get('/', (req, res) => res.redirect('/api-docs'));
setupSwaggerJson(app);
setupSwaggerUi(app);
}

// Error handling
app.use((err, req, res, next) => {
console.error(err.stack);
Copy link

Copilot AI Sep 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error handler logs the full error stack to console, which could expose sensitive information in production logs. Consider logging less detailed information in production environments by checking NODE_ENV.

Suggested change
console.error(err.stack);
if (process.env.NODE_ENV !== 'production') {
console.error(err.stack);
} else {
console.error('Error:', err.message);
}

Copilot uses AI. Check for mistakes.
res.status(500).json({ message: 'Something went wrong' });
});

// Database Connection + Optional Seed + Sync + Server Start
mongoose
.connect(process.env.MONGO_URI, {
useNewUrlParser: true,
useUnifiedTopology: true,
})
.then(async () => {
console.log('MongoDB Connected');
console.log('MongoDB Connected');

// 1. Seed the database
try {
await seedDB();
console.log('🪴 Database seeded');
} catch (err) {
console.error('❌ Seeding error:', err);
if (process.env.NODE_ENV === 'development') {
try {
await seedDB();
console.log('🪴 Database seeded');
} catch (err) {
console.error('❌ Seeding error:', err);
}
}

// 2. Sync with Weaviate
try {
await syncWeaviate();
console.log('✅ Weaviate synced');
} catch (err) {
console.error('❌ Weaviate sync error:', err);
}

// 3. Start Express server
app.listen(PORT, '0.0.0.0', () => {
console.log(`Server ready on port ${PORT}.`);
console.log(`🚀 Server ready on port ${PORT}`);
});
})
.catch(err => {
console.error('❌ MongoDB connection error:', err);
process.exit(1);
});

// Middleware
app.use(cors());
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

// Redirect root to /api-docs
app.get('/', (req, res) => {
res.redirect('/api-docs');
});

// Setup Swagger UI with customized title
setupSwaggerJson(app); // serves /api-docs/swagger.json
setupSwaggerUi(app);

// Routes
app.use('/api/products', productRoutes);
app.use('/api/checkout', checkoutRoutes);
app.use('/api/search', require('./routes/search'));
app.use('/api/auth', authRoutes);

module.exports = app;