Skip to content
Open
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
78 changes: 30 additions & 48 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,56 +1,38 @@
# REDIS_URL=redis://redis:6379
# REDIS_URL=redis://localhost:6379
# DATABASE_URL=postgresql://volunchain:volunchain123@db:5432/volunchain
DATABASE_URL=postgresql://volunchain:volunchain123@localhost:5434/volunchain
# Core Environment Variables
NODE_ENV=development
PORT=3000
JWT_SECRET=your-jwt-secret
JWT_SECRET=Zq0gGppr1PIqJsYbCJptI+xmgVvd3BViQUEw2nvsiBs=
DB_TYPE=postgres
# DB_HOST=db
# DB_PORT=5432
DB_HOST=localhost
DB_PORT=5434

# DIRECT_URL=DATABASE_URL=postgresql://user:password@localhost:5432/volunchain?schema=public&connection_limit=10&pool_timeout=30&idle_timeout=30
DIRECT_URL=postgresql://volunchain:volunchain123@localhost:5434/volunchain
DATABASE_CONNECTION_LIMIT=10
DATABASE_POOL_TIMEOUT=30
DATABASE_IDLE_TIMEOUT=30
DATABASE_CONNECTION_TIMEOUT=10
ENABLE_QUERY_LOGGING=true
QUERY_TIMEOUT=30000


DB_USER=volunchain
DB_PASSWORD=volunchain123
DB_NAME=volunchain
NODE_ENV="development"
BACKBLAZE_APPLICATION_KEY_ID=
BACKBLAZE_APPLICATION_KEY=
BACKBLAZE_BUCKET_ID=

RATE_LIMIT_MAX_REQUESTS=100
RATE_LIMIT_WINDOW_MINUTES=5
# Database
DATABASE_URL=postgresql://user:password@localhost:5432/volunchain

RATE_LIMIT_AUTH_MAX=10
RATE_LIMIT_AUTH_WINDOW=3
# JWT / Security
JWT_SECRET=your_jwt_secret

RATE_LIMIT_WALLET_MAX=50
RATE_LIMIT_WALLET_WINDOW=5
# Supabase
SUPABASE_URL=https://your-supabase-url.supabase.co
SUPABASE_ANON_KEY=your_supabase_anon_key

RATE_LIMIT_EMAIL_MAX=20
RATE_LIMIT_EMAIL_WINDOW=5
# Redis
REDIS_URL=redis://localhost:6379

RATE_LIMIT_DEFAULT_MAX=100
RATE_LIMIT_DEFAULT_WINDOW=5
# Stellar / Horizon
HORIZON_URL=https://horizon-testnet.stellar.org
STELLAR_NETWORK=testnet

AWS_ACCESS_KEY_ID=your-access-key-here
AWS_SECRET_ACCESS_KEY=your-secret-access-key
AWS_REGION=eu-north-1
S3_BUCKET_NAME=volunchain-certificates-test
SOROBAN_RPC_URL=your_rpc_url
SOROBAN_SERVER_SECRET=your_server_secret
# Email
EMAIL_SERVICE=gmail
[email protected]
EMAIL_PASSWORD=your_email_password
BASE_URL=http://localhost:3000

# Supabase
SUPABASE_URL=https://your-project.supabase.co
SUPABASE_ANON_KEY=your-anon-key
# Soroban
SOROBAN_RPC_URL=https://soroban-testnet.stellar.org
SOROBAN_SERVER_SECRET=your_soroban_server_secret

# Rate Limiting
RATE_LIMIT_WINDOW_MS=900000
RATE_LIMIT_MAX_REQUESTS=100
RATE_LIMIT_MESSAGE=Too many requests, please try again later.

# Email Verification
EMAIL_SECRET=your_email_verification_secret
10 changes: 10 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"cors": "^2.8.5",
"date-fns": "^4.1.0",
"dotenv": "^16.4.7",
"env-var": "^7.5.0",
"express": "^4.21.2",
"express-rate-limit": "^7.5.0",
"express-validator": "^7.2.0",
Expand Down
19 changes: 19 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

# VolunChain

**Innovating Volunteering with Blockchain οΏ½οΏ½**
Expand Down Expand Up @@ -576,3 +577,21 @@ Run wallet verification tests:
```bash
npm test -- --testPathPattern=wallet
```

## ⚠️ Environment Validation & Startup Errors

VolunChain validates all required environment variables at startup. If any required variable is missing or invalid, the application will fail to start and display a clear error message indicating which variable is missing.

**Common startup failure messages:**
- `DATABASE_URL environment variable is required`
- `JWT_SECRET environment variable is required`
- `SUPABASE_URL environment variable is required`
- `SUPABASE_ANON_KEY environment variable is required`
- `EMAIL_USER environment variable is required`
- `EMAIL_PASSWORD environment variable is required`
- `SOROBAN_SERVER_SECRET environment variable is required`

**How to fix:**
Check your `.env` file and ensure all required variables are set. See `.env.example` for a complete list of required variables and example values.

If you see a startup error, update your `.env` file and restart the application.
Comment on lines +581 to +597
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Document all required envs: missing SOROBAN_RPC_URL in failures list.

env.ts marks SOROBAN_RPC_URL as required; add it to keep docs aligned with runtime validation.

Apply:

 **Common startup failure messages:**
 - `DATABASE_URL environment variable is required`
 - `JWT_SECRET environment variable is required`
 - `SUPABASE_URL environment variable is required`
 - `SUPABASE_ANON_KEY environment variable is required`
 - `EMAIL_USER environment variable is required`
 - `EMAIL_PASSWORD environment variable is required`
+- `SOROBAN_RPC_URL environment variable is required`
 - `SOROBAN_SERVER_SECRET environment variable is required`
πŸ“ Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
## ⚠️ Environment Validation & Startup Errors
VolunChain validates all required environment variables at startup. If any required variable is missing or invalid, the application will fail to start and display a clear error message indicating which variable is missing.
**Common startup failure messages:**
- `DATABASE_URL environment variable is required`
- `JWT_SECRET environment variable is required`
- `SUPABASE_URL environment variable is required`
- `SUPABASE_ANON_KEY environment variable is required`
- `EMAIL_USER environment variable is required`
- `EMAIL_PASSWORD environment variable is required`
- `SOROBAN_SERVER_SECRET environment variable is required`
**How to fix:**
Check your `.env` file and ensure all required variables are set. See `.env.example` for a complete list of required variables and example values.
If you see a startup error, update your `.env` file and restart the application.
## ⚠️ Environment Validation & Startup Errors
VolunChain validates all required environment variables at startup. If any required variable is missing or invalid, the application will fail to start and display a clear error message indicating which variable is missing.
**Common startup failure messages:**
- `DATABASE_URL environment variable is required`
- `JWT_SECRET environment variable is required`
- `SUPABASE_URL environment variable is required`
- `SUPABASE_ANON_KEY environment variable is required`
- `EMAIL_USER environment variable is required`
- `EMAIL_PASSWORD environment variable is required`
- `SOROBAN_RPC_URL environment variable is required`
- `SOROBAN_SERVER_SECRET environment variable is required`
**How to fix:**
Check your `.env` file and ensure all required variables are set. See `.env.example` for a complete list of required variables and example values.
If you see a startup error, update your `.env` file and restart the application.
πŸ€– Prompt for AI Agents
In readme.md around lines 581 to 597, the startup failure message list omits
SOROBAN_RPC_URL even though env.ts treats it as required; update the "Common
startup failure messages" section to include a bullet `SOROBAN_RPC_URL
environment variable is required` and, if applicable, mention it in the "How to
fix" guidance (ensure .env.example shows SOROBAN_RPC_URL) so the docs match
runtime validation.

24 changes: 0 additions & 24 deletions src/config/data-source.ts

This file was deleted.

34 changes: 34 additions & 0 deletions src/config/env.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { get } from "env-var";

// Use 'get' directly instead of 'env.get'
export const env = {
NODE_ENV: get("NODE_ENV").default("development").asString(),
PORT: get("PORT").default("3000").asPortNumber(),

// Database
DATABASE_URL: get("DATABASE_URL").required().asUrlString(),

// JWT / Security
JWT_SECRET: get("JWT_SECRET").required().asString(),

// Supabase
SUPABASE_URL: get("SUPABASE_URL").required().asUrlString(),
SUPABASE_ANON_KEY: get("SUPABASE_ANON_KEY").required().asString(),

// Redis
REDIS_URL: get("REDIS_URL").default("redis://localhost:6379").asString(),

// Stellar / Horizon
HORIZON_URL: get("HORIZON_URL").default("https://horizon-testnet.stellar.org").asUrlString(),
STELLAR_NETWORK: get("STELLAR_NETWORK").default("testnet").asString(),

// Email
EMAIL_SERVICE: get("EMAIL_SERVICE").default("gmail").asString(),
EMAIL_USER: get("EMAIL_USER").required().asString(),
EMAIL_PASSWORD: get("EMAIL_PASSWORD").required().asString(),
BASE_URL: get("BASE_URL").default("http://localhost:3000").asUrlString(),

// Soroban
SOROBAN_RPC_URL: get("SOROBAN_RPC_URL").required().asUrlString(),
SOROBAN_SERVER_SECRET: get("SOROBAN_SERVER_SECRET").required().asString(),
};
Comment on lines +1 to +34
Copy link

Choose a reason for hiding this comment

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

πŸ’‘ Verification agent

🧩 Analysis chain

Make env-loading self-contained and tighten validation (enums/URL).

  • Load dotenv here to decouple callers.
  • Validate enums for NODE_ENV/STELLAR_NETWORK.
  • Validate REDIS_URL as URL.
  • Minor: freeze to avoid accidental mutation.

If STELLAR_NETWORK accepts "public" instead of "mainnet" anywhere, adjust enum accordingly. Verify:

Patch:

+import "dotenv/config";
 import { get } from "env-var";

-// Use 'get' directly instead of 'env.get'
-export const env = {
-  NODE_ENV: get("NODE_ENV").default("development").asString(),
+// Use 'get' directly instead of 'env.get'
+export const env = Object.freeze({
+  NODE_ENV: get("NODE_ENV").default("development").asEnum(["development", "test", "production"]),
   PORT: get("PORT").default("3000").asPortNumber(),

   // Database
   DATABASE_URL: get("DATABASE_URL").required().asUrlString(),

   // JWT / Security
   JWT_SECRET: get("JWT_SECRET").required().asString(),

   // Supabase
   SUPABASE_URL: get("SUPABASE_URL").required().asUrlString(),
   SUPABASE_ANON_KEY: get("SUPABASE_ANON_KEY").required().asString(),

   // Redis
-  REDIS_URL: get("REDIS_URL").default("redis://localhost:6379").asString(),
+  REDIS_URL: get("REDIS_URL").default("redis://localhost:6379").asUrlString(),

   // Stellar / Horizon
   HORIZON_URL: get("HORIZON_URL").default("https://horizon-testnet.stellar.org").asUrlString(),
-  STELLAR_NETWORK: get("STELLAR_NETWORK").default("testnet").asString(),
+  STELLAR_NETWORK: get("STELLAR_NETWORK").default("testnet").asEnum(["testnet", "mainnet"]),
@@
   SOROBAN_RPC_URL: get("SOROBAN_RPC_URL").required().asUrlString(),
   SOROBAN_SERVER_SECRET: get("SOROBAN_SERVER_SECRET").required().asString(),
-};
+} as const);

🏁 Script executed:

#!/bin/bash
rg -nP -C2 --type=ts "STELLAR_NETWORK|['\"](public|mainnet)['\"]"

Length of output: 1691


Load dotenv and tighten env validation

  • Import dotenv in src/config/env.ts to decouple callers.
  • Enforce enums:
    – NODE_ENV β†’ .asEnum(["development","test","production"])
    – STELLAR_NETWORK β†’ .asEnum(["testnet","mainnet"])
  • Validate URLs: use .asUrlString() for REDIS_URL.
  • Freeze the env object to prevent mutations.
+import "dotenv/config";
 import { get } from "env-var";

-export const env = {
+export const env = Object.freeze({
+  NODE_ENV: get("NODE_ENV").default("development").asEnum(["development", "test", "production"]),
   PORT: get("PORT").default("3000").asPortNumber(),

   // Database
   DATABASE_URL: get("DATABASE_URL").required().asUrlString(),

   // JWT / Security
   JWT_SECRET: get("JWT_SECRET").required().asString(),

   // Supabase
   SUPABASE_URL: get("SUPABASE_URL").required().asUrlString(),
   SUPABASE_ANON_KEY: get("SUPABASE_ANON_KEY").required().asString(),

   // Redis
-  REDIS_URL: get("REDIS_URL").default("redis://localhost:6379").asString(),
+  REDIS_URL: get("REDIS_URL").default("redis://localhost:6379").asUrlString(),

   // Stellar / Horizon
   HORIZON_URL: get("HORIZON_URL").default("https://horizon-testnet.stellar.org").asUrlString(),
-  STELLAR_NETWORK: get("STELLAR_NETWORK").default("testnet").asString(),
+  STELLAR_NETWORK: get("STELLAR_NETWORK").default("testnet").asEnum(["testnet", "mainnet"]),

   // Email
   EMAIL_SERVICE: get("EMAIL_SERVICE").default("gmail").asString(),
   EMAIL_USER: get("EMAIL_USER").required().asString(),
   EMAIL_PASSWORD: get("EMAIL_PASSWORD").required().asString(),
   BASE_URL: get("BASE_URL").default("http://localhost:3000").asUrlString(),

   // Soroban
   SOROBAN_RPC_URL: get("SOROBAN_RPC_URL").required().asUrlString(),
   SOROBAN_SERVER_SECRET: get("SOROBAN_SERVER_SECRET").required().asString(),
-};
+} as const);
πŸ“ Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import { get } from "env-var";
// Use 'get' directly instead of 'env.get'
export const env = {
NODE_ENV: get("NODE_ENV").default("development").asString(),
PORT: get("PORT").default("3000").asPortNumber(),
// Database
DATABASE_URL: get("DATABASE_URL").required().asUrlString(),
// JWT / Security
JWT_SECRET: get("JWT_SECRET").required().asString(),
// Supabase
SUPABASE_URL: get("SUPABASE_URL").required().asUrlString(),
SUPABASE_ANON_KEY: get("SUPABASE_ANON_KEY").required().asString(),
// Redis
REDIS_URL: get("REDIS_URL").default("redis://localhost:6379").asString(),
// Stellar / Horizon
HORIZON_URL: get("HORIZON_URL").default("https://horizon-testnet.stellar.org").asUrlString(),
STELLAR_NETWORK: get("STELLAR_NETWORK").default("testnet").asString(),
// Email
EMAIL_SERVICE: get("EMAIL_SERVICE").default("gmail").asString(),
EMAIL_USER: get("EMAIL_USER").required().asString(),
EMAIL_PASSWORD: get("EMAIL_PASSWORD").required().asString(),
BASE_URL: get("BASE_URL").default("http://localhost:3000").asUrlString(),
// Soroban
SOROBAN_RPC_URL: get("SOROBAN_RPC_URL").required().asUrlString(),
SOROBAN_SERVER_SECRET: get("SOROBAN_SERVER_SECRET").required().asString(),
};
import "dotenv/config";
import { get } from "env-var";
// Use 'get' directly instead of 'env.get'
export const env = Object.freeze({
NODE_ENV: get("NODE_ENV")
.default("development")
.asEnum(["development", "test", "production"]),
PORT: get("PORT")
.default("3000")
.asPortNumber(),
// Database
DATABASE_URL: get("DATABASE_URL")
.required()
.asUrlString(),
// JWT / Security
JWT_SECRET: get("JWT_SECRET")
.required()
.asString(),
// Supabase
SUPABASE_URL: get("SUPABASE_URL")
.required()
.asUrlString(),
SUPABASE_ANON_KEY: get("SUPABASE_ANON_KEY")
.required()
.asString(),
// Redis
REDIS_URL: get("REDIS_URL")
.default("redis://localhost:6379")
.asUrlString(),
// Stellar / Horizon
HORIZON_URL: get("HORIZON_URL")
.default("https://horizon-testnet.stellar.org")
.asUrlString(),
STELLAR_NETWORK: get("STELLAR_NETWORK")
.default("testnet")
.asEnum(["testnet", "mainnet"]),
// Email
EMAIL_SERVICE: get("EMAIL_SERVICE")
.default("gmail")
.asString(),
EMAIL_USER: get("EMAIL_USER")
.required()
.asString(),
EMAIL_PASSWORD: get("EMAIL_PASSWORD")
.required()
.asString(),
BASE_URL: get("BASE_URL")
.default("http://localhost:3000")
.asUrlString(),
// Soroban
SOROBAN_RPC_URL: get("SOROBAN_RPC_URL")
.required()
.asUrlString(),
SOROBAN_SERVER_SECRET: get("SOROBAN_SERVER_SECRET")
.required()
.asString(),
} as const);
πŸ€– Prompt for AI Agents
In src/config/env.ts around lines 1 to 34, import and call dotenv.config() at
the top so environment variables are loaded within this module (decoupling
callers); replace NODE_ENV get chain with
.asEnum(["development","test","production"]) and STELLAR_NETWORK with
.asEnum(["testnet","mainnet"]); change REDIS_URL to use .asUrlString() instead
of .asString(); and after constructing the env object call Object.freeze(env) to
prevent runtime mutations.

5 changes: 3 additions & 2 deletions src/config/horizon.config.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import dotenv from "dotenv";
import { env } from "./env";

dotenv.config();
Comment on lines 1 to 4
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Bug: dotenv loads after env import due to ESM import order

env.ts evaluates before dotenv.config() here, so .env values won’t be visible to env. Move dotenv loading into env.ts and remove it here.

-import dotenv from "dotenv";
-import { env } from "./env";
-
-dotenv.config();
+import { env } from "./env";

Then in src/config/env.ts (outside this hunk), ensure:

import "dotenv/config"; // load .env before reading variables
πŸ€– Prompt for AI Agents
In src/config/horizon.config.ts around lines 1 to 4, dotenv is being configured
after importing ./env which causes env.ts to evaluate before .env is loaded;
remove the dotenv.config() import from this file and move the dotenv
initialization into src/config/env.ts so environment variables are available
when env.ts runs; specifically, in src/config/env.ts add a top-level import
(e.g., import "dotenv/config") or call dotenv.config() before any process.env
reads, and then update horizon.config.ts to only import { env } from "./env"
without configuring dotenv locally.


export const horizonConfig = {
url: process.env.HORIZON_URL || "https://horizon-testnet.stellar.org",
network: process.env.STELLAR_NETWORK || "testnet",
url: env.HORIZON_URL || "https://horizon-testnet.stellar.org",
network: env.STELLAR_NETWORK || "testnet",
timeout: 30000, // 30 seconds timeout for API calls
};

Expand Down
5 changes: 3 additions & 2 deletions src/config/prisma.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { PrismaClient } from "@prisma/client";
import { DatabaseMonitor } from "../utils/db-monitor";
import { env } from "./env";

// Configure Prisma Client with connection pooling
const prismaClientSingleton = () => {
return new PrismaClient({
log: ["query", "info", "warn", "error"],
datasources: {
db: {
url: process.env.DATABASE_URL,
url: env.DATABASE_URL,
},
},
});
Expand All @@ -23,7 +24,7 @@ const prisma = globalThis.prisma ?? prismaClientSingleton();
// Initialize database monitor
export const dbMonitor = new DatabaseMonitor(prisma);

if (process.env.NODE_ENV !== "production") {
if (env.NODE_ENV !== "production") {
globalThis.prisma = prisma;
}

Expand Down
3 changes: 2 additions & 1 deletion src/config/redis.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { createClient, RedisClientType } from "redis";
import { env } from "./env";

const redisClient = createClient({
url: process.env.REDIS_URL || "redis://localhost:6379",
url: env.REDIS_URL || "redis://localhost:6379",
}) as RedisClientType & {
incr: (key: string) => Promise<number>;
expire: (key: string, seconds: number) => Promise<number>;
Expand Down
5 changes: 3 additions & 2 deletions src/config/soroban.config.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import dotenv from "dotenv";
import { env } from "./env";

dotenv.config();
Comment on lines 1 to 4
Copy link

Choose a reason for hiding this comment

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

πŸ’‘ Verification agent

🧩 Analysis chain

dotenv.config() runs after env import β€” order-of-operations bug

env.ts is imported before dotenv.config() runs, so variables may be read before .env is loaded. Centralize dotenv initialization in src/config/env.ts and remove it here.

Apply this diff here:

-import dotenv from "dotenv";
-import { env } from "./env";
-
-dotenv.config();
+import { env } from "./env";

And at the very top of src/config/env.ts:

import dotenv from "dotenv";
dotenv.config();

Verify no stray initializations remain:


🏁 Script executed:

#!/bin/bash
rg -nP -C2 'dotenv\.config\s*\(' src

Length of output: 1730


Centralize dotenv initialization in src/config/env.ts and remove all other calls

  • At the top of src/config/env.ts, add:
    import dotenv from "dotenv";
    dotenv.config();
  • In src/config/soroban.config.ts and src/config/horizon.config.ts, remove the import dotenv… and dotenv.config() linesβ€”keep only import { env } from "./env";.
  • Remove all dotenv.config() calls (and their imports) from:
    β€’ src/scripts/sorobanDemo.ts
    β€’ src/shared/infrastructure/utils/setup-s3-bucket.ts
    β€’ src/modules/metrics/application/services/MetricsService.ts
    β€’ src/modules/auth/utils/email.utils.ts
  • Ensure your application’s entry points import env (or β€œ./config/env”) before using any environment variables.
  • Verify no stray initializations remain:
    rg -nP 'dotenv\.config\s*\(' src


export const sorobanConfig = {
rpcUrl: process.env.SOROBAN_RPC_URL || "https://soroban-testnet.stellar.org",
serverSecret: process.env.SOROBAN_SERVER_SECRET,
rpcUrl: env.SOROBAN_RPC_URL || "https://soroban-testnet.stellar.org",
serverSecret: env.SOROBAN_SERVER_SECRET,
};

// Validate required environment variables
Expand Down
5 changes: 3 additions & 2 deletions src/config/supabase.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { createClient } from '@supabase/supabase-js';
import { env } from "./env";

const supabaseUrl = process.env.SUPABASE_URL!;
const supabaseKey = process.env.SUPABASE_ANON_KEY!;
const supabaseUrl = env.SUPABASE_URL!;
const supabaseKey = env.SUPABASE_ANON_KEY!;

export const supabase = createClient(supabaseUrl, supabaseKey);
3 changes: 2 additions & 1 deletion src/config/swagger.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ import swaggerUi from "swagger-ui-express";
import YAML from "yaml";
import { Express } from "express";
import fs from "fs";
import { env } from "./env";

export class SwaggerConfig {
private static swaggerDocument = YAML.parse(
fs.readFileSync("./openapi.yaml", "utf8")
);

static setup(app: Express): void {
if (process.env.NODE_ENV !== "development") {
if (env.NODE_ENV !== "development") {
console.log("⚠️ Swagger is disabled in production mode.");
return;
}
Expand Down
5 changes: 3 additions & 2 deletions src/config/winston.config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import winston from "winston";
import path from "path";
import { env } from "./env";

const { combine, timestamp, errors, json, printf } = winston.format;

Expand All @@ -17,8 +18,8 @@ const consoleFormat = printf(
const logsDir = path.join(process.cwd(), "logs");

const createLogger = () => {
const isProduction = process.env.NODE_ENV === "production";
const isDevelopment = process.env.NODE_ENV === "development";
const isProduction = env.NODE_ENV === "production";
const isDevelopment = env.NODE_ENV === "development";
Comment on lines +21 to +22
Copy link

Choose a reason for hiding this comment

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

πŸ› οΈ Refactor suggestion

Unify config source and add LOG_LEVEL to env module

Now that NODE_ENV is read from env, LOG_LEVEL should be too for consistency and testability.

Apply within this file:

-  const isProduction = env.NODE_ENV === "production";
-  const isDevelopment = env.NODE_ENV === "development";
+  const isProduction = env.NODE_ENV === "production";
+  const isDevelopment = env.NODE_ENV === "development";
@@
-    level: process.env.LOG_LEVEL || (isProduction ? "info" : "debug"),
+    level: env.LOG_LEVEL || (isProduction ? "info" : "debug"),

And in src/config/env.ts (outside this hunk), add:

import "dotenv/config"; // ensure .env is loaded once, early
export const env = {
  // ...
  LOG_LEVEL: get("LOG_LEVEL").default("info").asEnum(["error","warn","info","http","verbose","debug","silly"]),
};

Also applies to: 74-75


const transports: winston.transport[] = [];

Expand Down
3 changes: 2 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,14 @@ import organizationRoutes from "./routes/OrganizationRoutes";
import messageRoutes from "./modules/messaging/routes/messaging.routes";
import testRoutes from "./routes/testRoutes";
import { Logger } from "./utils/logger";
import { env } from "../src/config/env";

const globalLogger = new Logger("VolunChain");
import fs from "fs";
import path from "path";

const app = express();
const PORT = process.env.PORT || 3000;
const PORT = env.PORT;
const ENV = process.env.NODE_ENV || "development";

// Ensure logs directory exists
Expand Down