Merge pull request #3352 from bluewave-labs/fix/env-var-validation
Fix/env var validation
This commit is contained in:
5 files changed
+95
-23
No files matched your search
@@ -0,0 +1,26 @@
|
||||
# ==============================================
|
||||
# Checkmate Server Configuration
|
||||
# ==============================================
|
||||
# Copy this file to .env and fill in your values
|
||||
|
||||
# Server Configuration
|
||||
# --------------------
|
||||
NODE_ENV=development
|
||||
LOG_LEVEL=debug
|
||||
|
||||
# Database
|
||||
# --------
|
||||
DB_CONNECTION_STRING=mongodb://localhost:27017/uptime_db
|
||||
|
||||
# JWT Authentication
|
||||
# ------------------
|
||||
JWT_SECRET=my_secret
|
||||
TOKEN_TTL=99d
|
||||
|
||||
# Client Configuration
|
||||
# --------------------
|
||||
CLIENT_HOST=http://localhost:5173
|
||||
|
||||
# Optional Configuration
|
||||
# ----------------------
|
||||
ORIGIN=localhost
|
||||
+6
-2
@@ -2,6 +2,7 @@ import { initializeServices } from "./config/services.js";
|
||||
import { initializeControllers } from "./config/controllers.js";
|
||||
import { createApp } from "./app.js";
|
||||
import { initShutdownListener } from "./shutdown.js";
|
||||
import { validateEnv } from "./validation/envValidation.js";
|
||||
import { fileURLToPath } from "url";
|
||||
import path from "path";
|
||||
import fs from "fs";
|
||||
@@ -15,6 +16,9 @@ const SERVICE_NAME = "Server";
|
||||
let logger: ILogger;
|
||||
|
||||
const startApp = async () => {
|
||||
// Validate environment variables first
|
||||
const env = validateEnv();
|
||||
|
||||
// FE path
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
@@ -23,7 +27,7 @@ const startApp = async () => {
|
||||
|
||||
// Create services
|
||||
const settingsRepository = new MongoSettingsRepository();
|
||||
const settingsService = new SettingsService(settingsRepository);
|
||||
const settingsService = new SettingsService(settingsRepository, env);
|
||||
|
||||
const envSettings = settingsService.loadSettings();
|
||||
|
||||
@@ -46,7 +50,7 @@ const startApp = async () => {
|
||||
openApiSpec,
|
||||
});
|
||||
|
||||
const port = envSettings.port || 52345;
|
||||
const port = 52345;
|
||||
const server = app.listen(port, () => {
|
||||
logger.info({ message: `Server started on port:${port}` });
|
||||
});
|
||||
|
||||
@@ -1,29 +1,17 @@
|
||||
import { ISettingsRepository } from "@/repositories/index.js";
|
||||
import { Settings } from "@/types/index.js";
|
||||
import { AppError } from "@/utils/AppError.js";
|
||||
import { ValidatedEnv } from "@/validation/envValidation.js";
|
||||
|
||||
const SERVICE_NAME = "SettingsService";
|
||||
|
||||
export type EnvConfig = {
|
||||
jwtSecret: string | undefined;
|
||||
jwtTTL: string | undefined;
|
||||
systemEmailHost: string | undefined;
|
||||
nodeEnv: string | undefined;
|
||||
logLevel: string | undefined;
|
||||
clientHost: string | undefined;
|
||||
dbConnectionString: string | undefined;
|
||||
port: string | undefined;
|
||||
};
|
||||
|
||||
const envConfig: EnvConfig = {
|
||||
jwtSecret: process.env.JWT_SECRET,
|
||||
jwtTTL: process.env.TOKEN_TTL,
|
||||
systemEmailHost: process.env.SYSTEM_EMAIL_HOST,
|
||||
nodeEnv: process.env.NODE_ENV,
|
||||
logLevel: process.env.LOG_LEVEL,
|
||||
clientHost: process.env.CLIENT_HOST,
|
||||
dbConnectionString: process.env.DB_CONNECTION_STRING,
|
||||
port: process.env.PORT,
|
||||
jwtSecret: string;
|
||||
jwtTTL: string;
|
||||
nodeEnv: string;
|
||||
logLevel: string;
|
||||
clientHost: string;
|
||||
dbConnectionString: string;
|
||||
};
|
||||
|
||||
export interface ISettingsService {
|
||||
@@ -38,9 +26,16 @@ class SettingsService implements ISettingsService {
|
||||
private settings: EnvConfig;
|
||||
private settingsRepository: ISettingsRepository;
|
||||
|
||||
constructor(settingsRepository: ISettingsRepository) {
|
||||
constructor(settingsRepository: ISettingsRepository, env: ValidatedEnv) {
|
||||
this.settingsRepository = settingsRepository;
|
||||
this.settings = { ...envConfig };
|
||||
this.settings = {
|
||||
jwtSecret: env.JWT_SECRET,
|
||||
jwtTTL: env.TOKEN_TTL,
|
||||
nodeEnv: env.NODE_ENV,
|
||||
logLevel: env.LOG_LEVEL,
|
||||
clientHost: env.CLIENT_HOST,
|
||||
dbConnectionString: env.DB_CONNECTION_STRING,
|
||||
};
|
||||
}
|
||||
|
||||
get serviceName() {
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
import { z } from "zod";
|
||||
|
||||
const envSchema = z.object({
|
||||
// Server Configuration
|
||||
NODE_ENV: z.enum(["development", "production", "test"]).default("development"),
|
||||
LOG_LEVEL: z.enum(["error", "warn", "info", "debug"]).default("debug"),
|
||||
|
||||
// Database
|
||||
DB_CONNECTION_STRING: z.string().min(1, "Database connection string is required"),
|
||||
|
||||
// JWT Authentication
|
||||
JWT_SECRET: z.string().min(1, "JWT_SECRET is required"),
|
||||
TOKEN_TTL: z.string().default("99d"),
|
||||
|
||||
// Client Configuration
|
||||
CLIENT_HOST: z.string().url("CLIENT_HOST must be a valid URL"),
|
||||
|
||||
// Optional
|
||||
ORIGIN: z.string().optional(),
|
||||
});
|
||||
|
||||
export type ValidatedEnv = z.infer<typeof envSchema>;
|
||||
|
||||
export const validateEnv = (): ValidatedEnv => {
|
||||
const result = envSchema.safeParse(process.env);
|
||||
|
||||
if (!result.success) {
|
||||
console.error("Environment validation failed:");
|
||||
console.error("");
|
||||
|
||||
const errors = result.error.format();
|
||||
for (const [key, value] of Object.entries(errors)) {
|
||||
if (key !== "_errors" && value && typeof value === "object" && "_errors" in value) {
|
||||
console.error(` ${key}: ${value._errors.join(", ")}`);
|
||||
}
|
||||
}
|
||||
|
||||
console.error("Please check your .env file and ensure all required variables are set.");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log("Environment variables validated successfully");
|
||||
return result.data;
|
||||
};
|
||||
@@ -8,6 +8,9 @@
|
||||
// Shared utilities
|
||||
export * from "./shared.js";
|
||||
|
||||
// Environment validation
|
||||
export * from "./envValidation.js";
|
||||
|
||||
// Domain-specific validations
|
||||
export * from "./authValidation.js";
|
||||
export * from "./monitorValidation.js";
|
||||
|
||||
Reference in new issue
Block a user