import "dotenv/config"; import { z } from "zod"; const configSchema = z.object({ engine: z.object({ mnemonic: z.string().min(1, "ENGINE_MNEMONIC is required"), database: z.object({ path: z.string().default("./data/xo"), fileName: z.string().default("engine.db"), }), }), syncServer: z.object({ url: z.string().default("https://sync.xo.harvmaster.com"), }), // TODO: Remove merchant - eww. merchant: z.object({ name: z.string().default("XO Snack Machine"), }), database: z.object({ path: z.string().default("data.db"), }), server: z.object({ port: z.coerce.number().default(3000), host: z.string().default("0.0.0.0"), cors: z .object({ origin: z.string().default("*"), methods: z .array(z.string()) .default(["GET", "POST", "PUT", "DELETE", "OPTIONS"]), allowedHeaders: z .array(z.string()) .default(["Content-Type", "Authorization"]), }) .partial() .prefault({}), }), }); type ConfigInput = z.input; type ConfigSchema = z.output; /** * The Config class is used to load and parse the configuration for the vending machine. */ export class Config { static fromEnv(): Config { return this.from({ engine: { mnemonic: process.env.ENGINE_MNEMONIC ?? "", database: { path: process.env.ENGINE_DATABASE_PATH, }, }, syncServer: { url: process.env.SYNC_SERVER_URL, }, // TODO: Remove merchant - eww. merchant: { name: process.env.MERCHANT_NAME, }, database: { path: process.env.DATABASE_PATH, }, server: { port: process.env.SERVER_PORT, host: process.env.SERVER_HOST, }, }); } static from(config: ConfigInput): Config { return new Config(configSchema.parse(config)); } public get syncServer() { return this.config.syncServer; } // TODO: Remove merchant - eww. public get merchant() { return this.config.merchant; } public get engine() { return this.config.engine; } public get database() { return this.config.database; } public get server() { return this.config.server; } private constructor(private readonly config: ConfigSchema) {} }