config/env.ts
3.1 KB · sha256:4ec1b15c8bdf321782efbc8fad6853c7856e387fb36b23fc8afbe753af049d43
import { z } from "zod";
import { readFileSync } from "fs";
import path from "path";
import fs from "fs";
import crypto from "crypto";
function loadEnv() {
try {
const envFile = fs.readFileSync(
path.join(process.cwd(), "ward", ".env"),
"utf-8",
);
const envVars: Record<string, string> = {};
envFile.split("\n").forEach((line, index) => {
const cleanLine = line.trim().replace(/\r$/, "");
if (cleanLine === "" || cleanLine.startsWith("#")) {
return;
}
const match = cleanLine.match(/^([^=]+)=(.*)$/);
if (match?.[1] && match[2] !== undefined) {
const key = match[1].trim();
let value = match[2].trim();
if (
(value.startsWith('"') && value.endsWith('"')) ||
(value.startsWith("'") && value.endsWith("'"))
) {
value = value.slice(1, -1);
}
envVars[key] = value;
}
});
return { ...process.env, ...envVars };
} catch (error) {
console.warn("No .env file found, using process.env");
return { ...process.env };
}
}
const envVars = loadEnv();
const boolish = (dflt: boolean) =>
z
.union([z.string(), z.boolean(), z.undefined()])
.transform((v): boolean => {
if (typeof v === "boolean") return v;
if (v == null) return dflt;
const s = String(v).trim().toLowerCase();
if (["1", "true", "yes", "on"].includes(s)) return true;
if (["0", "false", "no", "off"].includes(s)) return false;
return dflt;
})
.optional();
const envSchema = z.object({
NODE_ENV: z.enum(["development", "production"]).default("development"),
DATABASE_URI: z
.string()
.default("mongodb://localhost:27017/ward")
.describe("MongoDB connection string"),
CHAT_DISPLAY: boolish(true).describe(
"Cancel chat events and re-broadcast with Ward prefix/suffix formatting",
),
CHAT_FORMAT: z
.string()
.default("%ward_prefix%%player_name%%ward_suffix%<gray>:</gray> <message>")
.describe(
"MiniMessage + PAPI template for chat lines; `<message>` is the literal player message",
),
CHAT_HOVER: z
.string()
.default(
"<gray>Name: <white>%player_name%</white><newline>" +
"<gray>Group: <white>%ward_group%</white><newline>" +
"<gray>Prefix: %ward_prefix%<reset><newline>" +
"<gray>UUID: <dark_gray>%player_uuid%",
)
.describe(
"MiniMessage + PAPI hover-text template for chat lines (empty = no hover)",
),
PAPI_EXPANSION: boolish(true).describe(
"Register a PlaceholderAPI expansion exposing Ward state",
),
});
try {
envSchema.parse(envVars);
} catch (error) {
if (error instanceof z.ZodError) {
console.error(
"❌ Invalid environment variables:",
`Validation failed: ${error.issues
.map((e) => `${e.path.join(".")}: ${e.message}`)
.join(", ")}`,
);
}
}
export const env = envSchema.parse(envVars);
export type Env = z.infer<typeof envSchema>;