Harden dev environment configuration
Some checks failed
CI / Backend Tests (push) Has been cancelled
CI / Frontend Tests (push) Has been cancelled
CI / Build Docker Images (push) Has been cancelled

This commit is contained in:
2025-10-16 17:48:26 -05:00
parent 75c7430d01
commit 9355d5c7c2
20 changed files with 26341 additions and 42 deletions

View File

@@ -3,10 +3,13 @@ FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
RUN npm ci
COPY . .
ENV HOST=0.0.0.0 \
PORT=3001
EXPOSE 3001
CMD ["npm", "run", "dev"]

5563
backend/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -4,8 +4,8 @@
"description": "Backend for MerchantsOfHope-SupplyANdDemandPortal recruiter workflow SAAS",
"main": "src/server.js",
"scripts": {
"start": "node src/server.js",
"dev": "nodemon src/server.js",
"start": "node src/index.js",
"dev": "nodemon src/index.js",
"test": "jest",
"test:watch": "jest --watch",
"migrate": "node src/database/migrate.js",

View File

@@ -0,0 +1,27 @@
const dotenv = require('dotenv');
dotenv.config();
const optionalNumber = (value, fallback) => {
const parsed = parseInt(value, 10);
return Number.isFinite(parsed) ? parsed : fallback;
};
const config = {
env: process.env.NODE_ENV || 'development',
host: process.env.HOST || process.env.BACKEND_HOST || '0.0.0.0',
port: optionalNumber(process.env.PORT || process.env.BACKEND_PORT, 3001),
databaseUrl: process.env.DATABASE_URL,
jwtSecret: process.env.JWT_SECRET,
corsOrigin: process.env.CORS_ORIGIN || '*',
logLevel: process.env.LOG_LEVEL || 'info'
};
const required = ['databaseUrl', 'jwtSecret'];
const missingKeys = required.filter((key) => !config[key]);
if (missingKeys.length > 0) {
throw new Error(`Missing required environment variables: ${missingKeys.join(', ')}`);
}
module.exports = config;

View File

@@ -1,14 +1,16 @@
const { Pool } = require('pg');
require('dotenv').config();
const config = require('../config');
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
ssl: process.env.NODE_ENV === 'production' ? { rejectUnauthorized: false } : false
connectionString: config.databaseUrl,
ssl: config.env === 'production' ? { rejectUnauthorized: false } : false
});
// Test database connection
pool.on('connect', () => {
console.log('Connected to MerchantsOfHope-SupplyANdDemandPortal database');
if (config.logLevel !== 'silent') {
console.log('Connected to MerchantsOfHope-SupplyANdDemandPortal database');
}
});
pool.on('error', (err) => {

6
backend/src/index.js Normal file
View File

@@ -0,0 +1,6 @@
const app = require('./server');
const config = require('./config');
app.listen(config.port, config.host, () => {
console.log(`MerchantsOfHope-SupplyANdDemandPortal backend server running on ${config.host}:${config.port}`);
});

View File

@@ -1,5 +1,6 @@
const jwt = require('jsonwebtoken');
const pool = require('../database/connection');
const config = require('../config');
const authenticateToken = async (req, res, next) => {
const authHeader = req.headers['authorization'];
@@ -10,7 +11,7 @@ const authenticateToken = async (req, res, next) => {
}
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
const decoded = jwt.verify(token, config.jwtSecret);
// Get user details from database
const userResult = await pool.query(

View File

@@ -4,6 +4,7 @@ const jwt = require('jsonwebtoken');
const { body, validationResult } = require('express-validator');
const pool = require('../database/connection');
const { authenticateToken } = require('../middleware/auth');
const config = require('../config');
const router = express.Router();
@@ -47,7 +48,7 @@ router.post('/register', [
// Generate JWT token
const token = jwt.sign(
{ userId: user.id, email: user.email, role: user.role },
process.env.JWT_SECRET,
config.jwtSecret,
{ expiresIn: '24h' }
);
@@ -106,7 +107,7 @@ router.post('/login', [
// Generate JWT token
const token = jwt.sign(
{ userId: user.id, email: user.email, role: user.role },
process.env.JWT_SECRET,
config.jwtSecret,
{ expiresIn: '24h' }
);

View File

@@ -2,7 +2,7 @@ const express = require('express');
const cors = require('cors');
const helmet = require('helmet');
const morgan = require('morgan');
require('dotenv').config();
const config = require('./config');
const authRoutes = require('./routes/auth');
const userRoutes = require('./routes/users');
@@ -13,16 +13,19 @@ const applicationRoutes = require('./routes/applications');
const resumeRoutes = require('./routes/resumes');
const app = express();
const PORT = process.env.PORT || 3001;
// Middleware
app.disable('x-powered-by');
const corsOrigins = config.corsOrigin === '*'
? undefined
: config.corsOrigin.split(',').map((origin) => origin.trim());
app.use(helmet());
app.use(cors());
app.use(morgan('combined'));
app.use(cors(corsOrigins ? { origin: corsOrigins, credentials: true } : {}));
app.use(morgan(config.env === 'production' ? 'combined' : 'dev'));
app.use(express.json({ limit: '10mb' }));
app.use(express.urlencoded({ extended: true }));
// Routes
app.use('/api/auth', authRoutes);
app.use('/api/users', userRoutes);
app.use('/api/employers', employerRoutes);
@@ -31,27 +34,21 @@ app.use('/api/jobs', jobRoutes);
app.use('/api/applications', applicationRoutes);
app.use('/api/resumes', resumeRoutes);
// Health check
app.get('/api/health', (req, res) => {
res.json({ status: 'OK', timestamp: new Date().toISOString() });
});
// Error handling middleware
// eslint-disable-next-line no-unused-vars
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({
res.status(500).json({
error: 'Something went wrong!',
message: process.env.NODE_ENV === 'development' ? err.message : 'Internal server error'
message: config.env === 'development' ? err.message : 'Internal server error'
});
});
// 404 handler
app.use('*', (req, res) => {
res.status(404).json({ error: 'Route not found' });
});
app.listen(PORT, () => {
console.log(`MerchantsOfHope-SupplyANdDemandPortal backend server running on port ${PORT}`);
});
module.exports = app;