-
Notifications
You must be signed in to change notification settings - Fork 71
fisrt_commit #176
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
fisrt_commit #176
Conversation
WalkthroughCentralizes environment variable access via a new env module, updates config files to use it, revises .env.example and README, adds env-var dependency, removes an unused TypeORM data-source, and switches server startup to use validated env values. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant Dev as Developer
participant App as Node App (startup)
participant Env as env module (env-var)
participant Cfg as Configs (Prisma/Redis/Supabase/…)
participant Srv as HTTP Server
Dev->>App: npm start
App->>Env: Load & validate required env
alt Missing/invalid env
Env-->>App: Throw startup error (message)
App-->>Dev: Exit with explicit error
else Env valid
App->>Cfg: Initialize using env.*
Cfg-->>App: Ready
App->>Srv: Listen on env.PORT
Srv-->>Dev: Server running
end
note over Env,Cfg: Centralized env access replaces scattered process.env usage
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Possibly related issues
Poem
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. ✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
src/config/winston.config.ts (1)
17-19: Create the logs directory before using file transportsWithout mkdir, file transports may fail at runtime if ./logs doesn’t exist.
-import path from "path"; +import path from "path"; +import fs from "fs"; @@ -const logsDir = path.join(process.cwd(), "logs"); +const logsDir = path.join(process.cwd(), "logs"); +fs.mkdirSync(logsDir, { recursive: true });src/config/soroban.config.ts (1)
7-14: Remove dead fallback and redundant validation; env.ts already enforces requirements
env.SOROBAN_RPC_URLandenv.SOROBAN_SERVER_SECRETare marked required inenv.ts, so the inline default and post-check are unreachable/redundant.Apply:
export const sorobanConfig = { - rpcUrl: env.SOROBAN_RPC_URL || "https://soroban-testnet.stellar.org", + rpcUrl: env.SOROBAN_RPC_URL, serverSecret: env.SOROBAN_SERVER_SECRET, }; -// Validate required environment variables -if (!sorobanConfig.serverSecret) { - throw new Error("SOROBAN_SERVER_SECRET environment variable is required"); -}If you want a default RPC URL, set it in
env.tsvia.default("https://soroban-testnet.stellar.org")and keep usage here asenv.SOROBAN_RPC_URL.
🧹 Nitpick comments (11)
package.json (1)
42-42: Pin Node engine to ≥18; env-var v7 is fine and TypeORM must stay
- env-var@^7.5.0 ships its own TS types—no need for @types/env-var.
- TypeORM is actively used in src/repository/user.repository.ts; don’t remove it.
- Add an engines field to package.json to enforce Node 18+:
@@ package.json "main": "dist/index.js", + "engines": { + "node": ">=18" + }, "scripts": {src/index.ts (2)
26-26: Fix import path to env for consistency.Use local path like other imports to avoid odd "../src" bounce.
-import { env } from "../src/config/env"; +import { env } from "./config/env";
33-35: Use centralized env for NODE_ENV too.Keep configuration sourced from env.ts.
-const PORT = env.PORT; -const ENV = process.env.NODE_ENV || "development"; +const PORT = env.PORT; +const ENV = env.NODE_ENV;src/config/prisma.ts (1)
7-14: Tweak Prisma logs by environment (optional).Reduce noise in production.
- return new PrismaClient({ - log: ["query", "info", "warn", "error"], + const logs = env.NODE_ENV === "production" ? ["warn", "error"] : ["query", "info", "warn", "error"] as const; + return new PrismaClient({ + log: logs as any, datasources: { db: { url: env.DATABASE_URL, }, }, });src/config/supabase.ts (1)
4-5: Drop unnecessary non-null assertionsenv-var already guarantees presence. The postfix ! is redundant and can hide misconfig.
-const supabaseUrl = env.SUPABASE_URL!; -const supabaseKey = env.SUPABASE_ANON_KEY!; +const supabaseUrl = env.SUPABASE_URL; +const supabaseKey = env.SUPABASE_ANON_KEY;src/config/redis.ts (1)
5-5: Remove redundant fallback; env.ts already provides the defaultKeeps a single source of truth for defaults.
- url: env.REDIS_URL || "redis://localhost:6379", + url: env.REDIS_URL,src/config/horizon.config.ts (2)
7-8: Deduplicate defaults; rely on env.ts for fallbacksDefaults already set in env.ts. Keep config lean and single-sourced.
- url: env.HORIZON_URL || "https://horizon-testnet.stellar.org", - network: env.STELLAR_NETWORK || "testnet", + url: env.HORIZON_URL, + network: env.STELLAR_NETWORK,
12-23: Optional: drop redundant validations hereenv.ts already validates presence and allowed network values; duplicating checks can drift. If you keep them, consider deriving from a shared schema.
src/config/swagger.config.ts (1)
13-16: Consider a toggle env for docs instead of strict NODE_ENV checkA flag like ENABLE_SWAGGER=true is often useful in staging.
Example change (and add ENABLE_SWAGGER to env.ts):
- if (env.NODE_ENV !== "development") { + if (env.NODE_ENV !== "development" && !env.ENABLE_SWAGGER) { console.log("⚠️ Swagger is disabled in production mode."); return; }In env.ts:
ENABLE_SWAGGER: get("ENABLE_SWAGGER").default("false").asBoolStrict(),src/config/soroban.config.ts (1)
6-9: Add an explicit type for the exported configImproves readability and editor support.
+type SorobanConfig = { rpcUrl: string; serverSecret: string }; -export const sorobanConfig = { +export const sorobanConfig: SorobanConfig = {.env.example (1)
12-13: Resolve dotenv-linter warnings: reorder keys, quote message, ensure trailing newlineAlphabetize keys within sections, quote values with spaces, add final newline.
# Supabase -SUPABASE_URL=https://your-supabase-url.supabase.co -SUPABASE_ANON_KEY=your_supabase_anon_key +SUPABASE_ANON_KEY=your_supabase_anon_key +SUPABASE_URL=https://your-supabase-url.supabase.co # Email -EMAIL_SERVICE=gmail -[email protected] -EMAIL_PASSWORD=your_email_password -BASE_URL=http://localhost:3000 +BASE_URL=http://localhost:3000 +EMAIL_PASSWORD=your_email_password +EMAIL_SERVICE=gmail +[email protected] # Rate Limiting -RATE_LIMIT_WINDOW_MS=900000 -RATE_LIMIT_MAX_REQUESTS=100 -RATE_LIMIT_MESSAGE=Too many requests, please try again later. +RATE_LIMIT_MAX_REQUESTS=100 +RATE_LIMIT_MESSAGE="Too many requests, please try again later." +RATE_LIMIT_WINDOW_MS=900000 +Also applies to: 23-26, 33-35, 37-38
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (13)
.env.example(1 hunks)package.json(1 hunks)readme.md(2 hunks)src/config/data-source.ts(0 hunks)src/config/env.ts(1 hunks)src/config/horizon.config.ts(1 hunks)src/config/prisma.ts(2 hunks)src/config/redis.ts(1 hunks)src/config/soroban.config.ts(1 hunks)src/config/supabase.ts(1 hunks)src/config/swagger.config.ts(1 hunks)src/config/winston.config.ts(2 hunks)src/index.ts(1 hunks)
💤 Files with no reviewable changes (1)
- src/config/data-source.ts
🧰 Additional context used
🧬 Code graph analysis (8)
src/config/redis.ts (1)
src/config/env.ts (1)
env(4-34)
src/config/winston.config.ts (1)
src/config/env.ts (1)
env(4-34)
src/config/horizon.config.ts (1)
src/config/env.ts (1)
env(4-34)
src/config/supabase.ts (1)
src/config/env.ts (1)
env(4-34)
src/config/swagger.config.ts (1)
src/config/env.ts (1)
env(4-34)
src/config/prisma.ts (1)
src/config/env.ts (1)
env(4-34)
src/config/soroban.config.ts (1)
src/config/env.ts (1)
env(4-34)
src/index.ts (1)
src/config/env.ts (1)
env(4-34)
🪛 dotenv-linter (3.3.0)
.env.example
[warning] 13-13: [UnorderedKey] The SUPABASE_ANON_KEY key should go before the SUPABASE_URL key
(UnorderedKey)
[warning] 25-25: [UnorderedKey] The EMAIL_PASSWORD key should go before the EMAIL_SERVICE key
(UnorderedKey)
[warning] 26-26: [UnorderedKey] The BASE_URL key should go before the EMAIL_PASSWORD key
(UnorderedKey)
[warning] 34-34: [UnorderedKey] The RATE_LIMIT_MAX_REQUESTS key should go before the RATE_LIMIT_WINDOW_MS key
(UnorderedKey)
[warning] 35-35: [UnorderedKey] The RATE_LIMIT_MESSAGE key should go before the RATE_LIMIT_WINDOW_MS key
(UnorderedKey)
[warning] 35-35: [ValueWithoutQuotes] This value needs to be surrounded in quotes
(ValueWithoutQuotes)
[warning] 38-38: [EndingBlankLine] No blank line at the end of the file
(EndingBlankLine)
🔇 Additional comments (6)
src/config/prisma.ts (2)
3-4: LGTM on centralizing DATABASE_URL via env.Consistent with the new env module.
27-29: Dev-only global prisma cache is correct.Prevents hot-reload client churn; good.
src/config/winston.config.ts (1)
3-3: Migration to env module looks goodImport shift to centralized env is correct and aligns with the PR’s objective.
src/config/supabase.ts (1)
2-2: LGTM on centralizing env accessConsistent with the new env.ts.
src/config/redis.ts (1)
1-15: No action needed: redisClient.connect() is already invoked during app startup (initializeRedis in src/index.ts).src/config/swagger.config.ts (1)
5-5: LGTM on switching to env.NODE_ENVMatches the centralized env pattern.
| # 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_SERVICE=gmail | ||
| EMAIL_USER=[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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Align example vars with validated set
.env.example includes EMAIL_SECRET and RATE_LIMIT_* which are not present in env.ts. Either add them to env.ts (with defaults/required) or remove them from the example to avoid drift.
Quick check script:
🏁 Script executed:
#!/bin/bash
# Show where these are referenced; if unused, drop from example, else add to env.ts
rg -nP -C2 '\b(EMAIL_SECRET|RATE_LIMIT_(WINDOW_MS|MAX_REQUESTS|MESSAGE))\b' src || trueLength of output: 2809
🏁 Script executed:
#!/bin/bash
find . -type f -name "env.ts"Length of output: 49
🏁 Script executed:
#!/bin/bash
cat src/config/env.tsLength of output: 1249
Sync environment variables between .env.example and env.ts
.env.example declares EMAIL_SECRET and RATE_LIMIT_WINDOW_MS/MAX_REQUESTS/MESSAGE but src/config/env.ts doesn’t include these keys. Add them to src/config/env.ts with appropriate defaults or required settings, or remove/rename them in .env.example to avoid configuration drift.
🧰 Tools
🪛 dotenv-linter (3.3.0)
[warning] 13-13: [UnorderedKey] The SUPABASE_ANON_KEY key should go before the SUPABASE_URL key
(UnorderedKey)
[warning] 25-25: [UnorderedKey] The EMAIL_PASSWORD key should go before the EMAIL_SERVICE key
(UnorderedKey)
[warning] 26-26: [UnorderedKey] The BASE_URL key should go before the EMAIL_PASSWORD key
(UnorderedKey)
[warning] 34-34: [UnorderedKey] The RATE_LIMIT_MAX_REQUESTS key should go before the RATE_LIMIT_WINDOW_MS key
(UnorderedKey)
[warning] 35-35: [UnorderedKey] The RATE_LIMIT_MESSAGE key should go before the RATE_LIMIT_WINDOW_MS key
(UnorderedKey)
[warning] 35-35: [ValueWithoutQuotes] This value needs to be surrounded in quotes
(ValueWithoutQuotes)
[warning] 38-38: [EndingBlankLine] No blank line at the end of the file
(EndingBlankLine)
🤖 Prompt for AI Agents
In .env.example lines 1-38 and src/config/env.ts, environment vars are out of
sync: .env.example defines EMAIL_SECRET and
RATE_LIMIT_WINDOW_MS/RATE_LIMIT_MAX_REQUESTS/RATE_LIMIT_MESSAGE but
src/config/env.ts does not. Update src/config/env.ts to read and export these
keys (with sensible defaults/types: RATE_LIMIT_WINDOW_MS as number (default
900000), RATE_LIMIT_MAX_REQUESTS as number (default 100), RATE_LIMIT_MESSAGE as
string (default "Too many requests, please try again later."), EMAIL_SECRET as
required or default empty string) or alternatively remove/rename those keys from
.env.example to match env.ts; ensure parsing/validation and types align with the
rest of the config module.
| ## ⚠️ 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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| ## ⚠️ 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.
| 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_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(), | ||
| }; No newline at end of file |
There was a problem hiding this comment.
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.tsto decouple callers. - Enforce enums:
–NODE_ENV→.asEnum(["development","test","production"])
–STELLAR_NETWORK→.asEnum(["testnet","mainnet"]) - Validate URLs: use
.asUrlString()forREDIS_URL. - Freeze the
envobject 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.
| 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_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_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.
| import dotenv from "dotenv"; | ||
| import { env } from "./env"; | ||
|
|
||
| dotenv.config(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
| import dotenv from "dotenv"; | ||
| import { env } from "./env"; | ||
|
|
||
| dotenv.config(); |
There was a problem hiding this comment.
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*\(' srcLength 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…anddotenv.config()lines—keep onlyimport { 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
| const isProduction = env.NODE_ENV === "production"; | ||
| const isDevelopment = env.NODE_ENV === "development"; |
There was a problem hiding this comment.
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
🚀 Volunchain Pull Request
Mark with an
xall the checkboxes that apply (like[x])📌 Type of Change
📝 Changes description
📸 Evidence (A photo is required as evidence)
⏰ Time spent breakdown
🌌 Comments
Thank you for contributing to Volunchain, we are glad that you have chosen us as your project of choice and we hope that you continue to contribute to this great project, so that together we can make our mark at the top!
Summary by CodeRabbit