Harden dev environment configuration
This commit is contained in:
@@ -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
5563
backend/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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",
|
||||
|
||||
27
backend/src/config/index.js
Normal file
27
backend/src/config/index.js
Normal 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;
|
||||
@@ -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
6
backend/src/index.js
Normal 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}`);
|
||||
});
|
||||
@@ -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(
|
||||
|
||||
@@ -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' }
|
||||
);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user