Skip to content
Merged
Show file tree
Hide file tree
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
26 changes: 26 additions & 0 deletions backend/src/config/env.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import dotenv from "dotenv";
dotenv.config();

const requiredEnv = [
"PORT",
"MONGO_URI",
"JWT_SECRET",
"GOOGLE_CLIENT_ID",
"CLIENT_URL",
"GEMINI_API_KEY"
]

requiredEnv.forEach((key) => {
if(!process.env[key]) {
throw new Error(`Missing required environment variable: ${key}`);
}
})

export const config = {
PORT: process.env.PORT,
MONGO_URI: process.env.MONGO_URI,
JWT_SECRET: process.env.JWT_SECRET,
GOOGLE_CLIENT_ID: process.env.GOOGLE_CLIENT_ID,
CLIENT_URL: process.env.CLIENT_URL,
GEMINI_API_KEY: process.env.GEMINI_API_KEY
}
Comment on lines +13 to +26

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Materialize validated values instead of re-reading raw process.env.

This only checks truthiness, so whitespace-only values still pass startup, and config is rebuilt from raw env strings afterward. That misses the “misconfigured” part of the fail-fast contract. Validate each value once with a helper (for example, reject value.trim() === '') and export those validated results from config.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/src/config/env.ts` around lines 13 - 26, The env validation in config
env.ts only checks truthiness, so whitespace-only values can still slip through
and the exported config is rebuilt from raw process.env afterward. Update the
requiredEnv validation flow to validate each value once with a helper that
rejects empty or trim-only strings, then have config export those
validated/materialized values instead of re-reading process.env. Keep the fix
centered around requiredEnv and the config object so the fail-fast behavior is
enforced consistently.

7 changes: 4 additions & 3 deletions backend/src/controllers/authController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ import jwt from 'jsonwebtoken';
import bcrypt from 'bcryptjs';
import { prisma } from '../config/db';
import { OAuth2Client } from 'google-auth-library';
import { config } from '../config/env';

const client = new OAuth2Client(process.env.GOOGLE_CLIENT_ID);
const client = new OAuth2Client(config.GOOGLE_CLIENT_ID);

// Generate JWT
const generateToken = (id: string) => {
return jwt.sign({ id }, process.env.JWT_SECRET!, { expiresIn: '30d' });
return jwt.sign({ id }, config.JWT_SECRET!, { expiresIn: '30d' });
};

// @desc Register new user
Expand Down Expand Up @@ -97,7 +98,7 @@ export const googleAuth = async (req: Request, res: Response) => {
try {
const ticket = await client.verifyIdToken({
idToken: token,
audience: process.env.GOOGLE_CLIENT_ID
audience: config.GOOGLE_CLIENT_ID
});

const payload = ticket.getPayload();
Expand Down
3 changes: 2 additions & 1 deletion backend/src/controllers/chatController.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Request, Response } from 'express';
import { GoogleGenerativeAI } from '@google/generative-ai';
import { prisma } from '../config/db';
import { config } from '../config/env';

const SYSTEM_PROMPT = `You are AlgoBot, a friendly and expert AI tutor for Data Structures & Algorithms on the AlgoForge platform.

Expand All @@ -24,7 +25,7 @@ export const chat = async (req: Request, res: Response) => {
return res.status(400).json({ error: 'Message is required' });
}

const apiKey = process.env.GEMINI_API_KEY;
const apiKey = config.GEMINI_API_KEY;
if (!apiKey) {
return res.status(500).json({ error: 'Gemini API key not configured' });
}
Expand Down
3 changes: 2 additions & 1 deletion backend/src/middleware/authMiddleware.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Request, Response, NextFunction } from 'express';
import jwt from 'jsonwebtoken';
import { prisma } from '../config/db';
import { config } from '../config/env';

interface JwtPayload {
id: string;
Expand All @@ -24,7 +25,7 @@ const protect = async (req: Request, res: Response, next: NextFunction) => {
try {
token = req.headers.authorization.split(' ')[1];

const decoded = jwt.verify(token, process.env.JWT_SECRET!) as JwtPayload;
const decoded = jwt.verify(token, config.JWT_SECRET!) as JwtPayload;

const user = await prisma.user.findUnique({
where: { id: decoded.id },
Expand Down
3 changes: 2 additions & 1 deletion backend/src/scripts/migrate_users.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { PrismaClient } from '@prisma/client';
import dotenv from 'dotenv';
import path from 'path';
import { config } from '../config/env';

dotenv.config({ path: path.join(__dirname, '../../.env') });

const uri = process.env.MONGO_URI as string;
const uri = config.MONGO_URI as string;
Comment on lines +4 to +8

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== file list ==\n'
git ls-files backend/src/scripts/migrate_users.ts backend/src/config/env.ts

printf '\n== outlines ==\n'
ast-grep outline backend/src/scripts/migrate_users.ts --view expanded || true
ast-grep outline backend/src/config/env.ts --view expanded || true

printf '\n== migrate_users.ts ==\n'
cat -n backend/src/scripts/migrate_users.ts | sed -n '1,120p'

printf '\n== env.ts ==\n'
cat -n backend/src/config/env.ts | sed -n '1,220p'

Repository: Rishabhworkspace/AlgoForge

Length of output: 7399


Load dotenv before importing config.

backend/src/config/env.ts calls dotenv.config() and validates process.env at import time, so the dotenv.config({ path: path.join(__dirname, '../../.env') }) here runs too late to affect config.MONGO_URI. If this script depends on ../../.env, it can fail during module initialization; move dotenv bootstrapping above the config import or let the config module own the path resolution.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/src/scripts/migrate_users.ts` around lines 4 - 8, The script
initializes dotenv after importing config, so MONGO_URI is validated before the
.env file is loaded. In migrate_users.ts, move the dotenv bootstrap that uses
path.join(__dirname, '../../.env') ahead of the config import, or rely on env.ts
to load the correct file itself. Make sure the import order in migrate_users.ts
allows config.MONGO_URI to be resolved only after environment variables are
available.


async function migrate() {
console.log("Starting migration using Prisma engine to bypass DNS issues...");
Expand Down
10 changes: 3 additions & 7 deletions backend/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,18 @@ import cors from 'cors';
import connectDB from './config/db';

dotenv.config();

// server.ts — add before app.listen()
if (!process.env.JWT_SECRET) {
throw new Error('FATAL: JWT_SECRET environment variable is not set.');
}
import { config } from './config/env';

const app: Express = express();
const port = process.env.PORT || 5000;
const port = config.PORT || 5000;

// Middleware
const allowedOrigins = [
'https://algo-forge-2-0.vercel.app',
'https://algoforge-2-0.onrender.com',
'http://localhost:5173',
'http://localhost:3000',
process.env.CLIENT_URL,
config.CLIENT_URL,
].filter(Boolean) as string[];

app.use(cors({
Expand Down
Loading