vish85521's picture
Upload 64 files
eeb3436 verified
Raw
History Blame Contribute Delete
6.62 kB
const fs = require("fs");
const path = require("path");
const dotenv = require("dotenv");
function loadEnvironment() {
const backendEnvPath = path.resolve(__dirname, "../../.env");
const rootEnvPath = path.resolve(__dirname, "../../../.env");
if (fs.existsSync(backendEnvPath)) {
dotenv.config({ path: backendEnvPath });
return;
}
if (fs.existsSync(rootEnvPath)) {
dotenv.config({ path: rootEnvPath });
return;
}
dotenv.config();
}
loadEnvironment();
function readBool(name, fallback) {
const raw = process.env[name];
if (raw === undefined || raw === null || raw === "") {
return fallback;
}
return ["1", "true", "yes", "on"].includes(String(raw).toLowerCase());
}
function readNumber(name, fallback) {
const raw = process.env[name];
if (raw === undefined || raw === null || raw === "") {
return fallback;
}
const parsed = Number(raw);
if (!Number.isFinite(parsed)) {
throw new Error(`Environment variable ${name} must be a valid number.`);
}
return parsed;
}
function readString(name, fallback = "") {
const raw = process.env[name];
if (raw === undefined || raw === null || raw === "") {
return fallback;
}
return String(raw);
}
function assertJwtSecret(name, value) {
if (!value || value.length < 32) {
throw new Error(
`Environment variable ${name} must be set and at least 32 characters long for JWT security.`
);
}
}
function resolveFilePath(value) {
if (!value) {
return "";
}
return path.isAbsolute(value) ? value : path.resolve(process.cwd(), value);
}
function readOptionalFile(filePathValue, label) {
if (!filePathValue) {
return undefined;
}
const absolutePath = resolveFilePath(filePathValue);
if (!fs.existsSync(absolutePath)) {
throw new Error(`${label} file does not exist: ${absolutePath}`);
}
return fs.readFileSync(absolutePath);
}
function parseBrokerUrl() {
const brokerUrl = readString("MQTT_BROKER_URL", "");
if (!brokerUrl) {
return null;
}
const url = new URL(brokerUrl);
return {
host: url.hostname,
port: Number(url.port || 8883),
username: decodeURIComponent(url.username || ""),
password: decodeURIComponent(url.password || ""),
};
}
function buildDatabaseConfig() {
const connectionString = readString("DATABASE_URL", "");
const sslEnabled = readBool("DB_SSL", false) || readBool("PGSSL", false);
const sslRejectUnauthorized = readBool("DB_SSL_REJECT_UNAUTHORIZED", true);
const sslCaPath = readString("DB_SSL_CA_PATH", "");
const base = {
max: readNumber("PGPOOL_MAX", 20),
idleTimeoutMillis: readNumber("PG_IDLE_TIMEOUT_MS", 30000),
connectionTimeoutMillis: readNumber("PG_CONNECT_TIMEOUT_MS", 15000),
};
if (sslEnabled) {
base.ssl = {
rejectUnauthorized: sslRejectUnauthorized,
ca: readOptionalFile(sslCaPath, "DB SSL CA") || undefined,
};
}
if (connectionString) {
return {
...base,
connectionString,
};
}
return {
...base,
host: readString("PGHOST", "127.0.0.1"),
port: readNumber("PGPORT", 5432),
user: readString("PGUSER", "postgres"),
password: readString("PGPASSWORD", "postgres"),
database: readString("PGDATABASE", "smart_cargo"),
};
}
const brokerFromUrl = parseBrokerUrl();
const legacyBrokerHost = readString("MQTT_BROKER", "");
const brokerHost =
readString("MQTT_BROKER_HOST", "") ||
legacyBrokerHost ||
(brokerFromUrl && brokerFromUrl.host) ||
"";
const brokerPort = readNumber(
"MQTT_BROKER_PORT",
readNumber("MQTT_PORT", (brokerFromUrl && brokerFromUrl.port) || 8883)
);
const mqttCaPath = readString("MQTT_CA_PATH", "");
const jwtAccessSecret = readString("JWT_ACCESS_SECRET", "");
const jwtRefreshSecret = readString("JWT_REFRESH_SECRET", "");
assertJwtSecret("JWT_ACCESS_SECRET", jwtAccessSecret);
assertJwtSecret("JWT_REFRESH_SECRET", jwtRefreshSecret);
const config = {
nodeEnv: readString("NODE_ENV", "development"),
logLevel: readString("LOG_LEVEL", "info"),
server: {
port: readNumber("PORT", 5000),
apiPrefix: readString("API_PREFIX", "/api"),
},
cors: {
origin: readString("CORS_ORIGIN", "*"),
},
auth: {
jwtAccessSecret,
jwtRefreshSecret,
jwtAccessExpiresIn: readString("JWT_ACCESS_EXPIRES_IN", "15m"),
jwtRefreshExpiresIn: readString("JWT_REFRESH_EXPIRES_IN", "7d"),
jwtIssuer: readString("JWT_ISSUER", "smart-cargo-backend"),
jwtAudience: readString("JWT_AUDIENCE", "smart-cargo-api"),
},
mqtt: {
enabled: Boolean(brokerHost),
host: brokerHost,
port: brokerPort,
username:
readString("MQTT_USERNAME", "") ||
readString("MQTT_USER", "") ||
(brokerFromUrl && brokerFromUrl.username) ||
"",
password: readString("MQTT_PASSWORD", "") || (brokerFromUrl && brokerFromUrl.password) || "",
topicFilter: readString(
"MQTT_TOPIC_FILTER",
"tenant/+/truck/+/container/+/telemetry"
),
clientIdPrefix: readString("MQTT_CLIENT_ID_PREFIX", "smart-cargo-backend"),
connectTimeoutMs: readNumber("MQTT_CONNECT_TIMEOUT_MS", 30000),
reconnectPeriodMs: readNumber("MQTT_RECONNECT_PERIOD_MS", 5000),
rejectUnauthorized: readBool("MQTT_REJECT_UNAUTHORIZED", true),
caPath: mqttCaPath,
ca: readOptionalFile(mqttCaPath, "MQTT CA"),
},
db: {
runMigrationsOnBoot: readBool("RUN_MIGRATIONS_ON_BOOT", false),
runSeedsOnBoot: readBool("RUN_SEEDS_ON_BOOT", false),
pg: buildDatabaseConfig(),
},
alerts: {
highTemperatureThresholdC: readNumber("ALERT_HIGH_TEMPERATURE_THRESHOLD_C", 35),
gasSpikeThresholdRaw: readNumber("ALERT_GAS_SPIKE_THRESHOLD_RAW", 1500),
autoResolveShock: readBool("ALERT_AUTO_RESOLVE_SHOCK", false),
offlineThresholdMs: readNumber("OFFLINE_THRESHOLD_MS", 30000),
},
jobs: {
offlineScanIntervalMs: readNumber("OFFLINE_SCAN_INTERVAL_MS", 5000),
},
ingest: {
queueConcurrency: readNumber("INGEST_QUEUE_CONCURRENCY", 6),
queueMaxBacklog: readNumber("INGEST_QUEUE_MAX_BACKLOG", 5000),
},
query: {
historyDefaultLimit: readNumber("HISTORY_DEFAULT_LIMIT", 240),
historyMaxLimit: readNumber("HISTORY_MAX_LIMIT", 2000),
},
ai: {
tripSummaryEnabled: readBool("AI_TRIP_SUMMARY_ENABLED", true),
geminiModel: readString("GEMINI_MODEL", "gemini-flash-lite-latest"),
geminiApiBaseUrl: readString(
"GEMINI_API_BASE_URL",
"https://generativelanguage.googleapis.com/v1beta"
),
geminiApiKeyBase64: readString("GEMINI_API_KEY_BASE64", ""),
tripSummaryTimeoutMs: readNumber("AI_TRIP_SUMMARY_TIMEOUT_MS", 15000),
},
};
module.exports = { config };