import path from 'node:path'; import dotenv from 'dotenv'; import { z } from 'zod'; dotenv.config({ override: true, quiet: true }); const envSchema = z.object({ PORT: z.coerce.number().default(3100), APP_TIME_ZONE: z.string().default('Asia/Seoul'), DB_TIME_ZONE: z.string().default('Asia/Seoul'), DB_CLIENT: z.string().default('pg'), DB_HOST: z.string().default('localhost'), DB_PORT: z.coerce.number().default(5432), DB_NAME: z.string().default('work_db'), DB_USER: z.string().default('work_user'), DB_PASSWORD: z.string().default('change-me'), DB_SSL: z .string() .default('false') .transform((value) => value === 'true'), PLAN_WORKER_ENABLED: z .string() .default('true') .transform((value) => value === 'true'), PLAN_WORKER_INTERVAL_MS: z.coerce.number().default(10000), PLAN_WORKER_ID: z.string().optional(), PLAN_GIT_REPO_PATH: z.string().default('/workspace/repo'), PLAN_MAIN_PROJECT_REPO_PATH: z.string().optional(), PLAN_RELEASE_BRANCH: z.string().default('release'), PLAN_MAIN_BRANCH: z.string().default('main'), PLAN_GIT_USER_NAME: z.string().default('how2ice'), PLAN_GIT_USER_EMAIL: z.string().default('how2ice@naver.com'), PLAN_CODEX_RUNNER_PATH: z.string().default('/workspace/repo-scripts/run-plan-codex-once.mjs'), PLAN_CODEX_ENABLED: z .string() .default('true') .transform((value) => value === 'true'), PLAN_LOCAL_MAIN_MODE: z .string() .default('true') .transform((value) => value === 'true'), PLAN_CODEX_BIN: z.string().default('codex'), PLAN_CODEX_TEMPLATE_HOME: z.string().optional(), PLAN_PREVIEW_BASE_URL: z.string().optional(), PLAN_PREVIEW_URL_TEMPLATE: z.string().optional(), IOS_NOTIFICATION_ENABLED: z .string() .default('false') .transform((value) => value === 'true'), WEB_PUSH_ENABLED: z .string() .default('false') .transform((value) => value === 'true'), WEB_PUSH_VAPID_PUBLIC_KEY: z.string().optional(), WEB_PUSH_VAPID_PRIVATE_KEY: z.string().optional(), WEB_PUSH_SUBJECT: z.string().default('mailto:how2ice@naver.com'), APNS_KEY_ID: z.string().optional(), APNS_TEAM_ID: z.string().optional(), APNS_BUNDLE_ID: z.string().optional(), APNS_PRIVATE_KEY: z.string().optional(), APNS_PRIVATE_KEY_PATH: z.string().optional(), APNS_PRODUCTION: z .string() .default('false') .transform((value) => value === 'true'), SERVER_COMMAND_ACCESS_TOKEN: z.string().default('usr_7f3a9c2d8e1b4a6f'), SERVER_COMMAND_API_BASE_URL: z.string().optional(), SERVER_COMMAND_API_ACCESS_TOKEN: z.string().optional(), SERVER_COMMAND_API_RESTART_PATH_TEMPLATE: z.string().default('/api/server-commands/{key}/actions/restart'), SERVER_COMMAND_PROJECT_ROOT: z.string().default(path.resolve(process.cwd(), '../../..')), SERVER_COMMAND_MAIN_PROJECT_ROOT: z.string().default('/workspace/main-project'), SERVER_COMMAND_TEST_URL: z.string().default('https://preview.sm-home.cloud/'), SERVER_COMMAND_REL_URL: z.string().default('https://rel.sm-home.cloud/'), SERVER_COMMAND_PROD_URL: z.string().default('https://sm-home.cloud/'), SERVER_COMMAND_WORK_SERVER_URL: z.string().default('http://127.0.0.1:3100/health'), SERVER_COMMAND_RUNNER_URL: z.string().default('http://host.docker.internal:3211/health'), SERVER_COMMAND_RUNNER_ACCESS_TOKEN: z.string().default('local-server-command-runner'), SERVER_COMMAND_RUNNER_HEARTBEAT_FILE: z.string().optional(), SERVER_COMMAND_TEST_SERVICE: z.string().default('app'), SERVER_COMMAND_REL_SERVICE: z.string().default('release-app'), SERVER_COMMAND_PROD_SERVICE: z.string().default('prod-app'), SERVER_COMMAND_WORK_SERVER_SERVICE: z.string().default('work-server'), WORK_SERVER_DIST_DIR: z.string().default('dist'), }); function parseEnv() { dotenv.config({ override: true, quiet: true }); const parsedEnv = envSchema.parse(process.env); return { ...parsedEnv, PLAN_MAIN_PROJECT_REPO_PATH: parsedEnv.PLAN_MAIN_PROJECT_REPO_PATH ?? parsedEnv.PLAN_GIT_REPO_PATH, }; } export function getEnv() { const parsedEnv = parseEnv(); if (!process.env.TZ?.trim()) { process.env.TZ = parsedEnv.APP_TIME_ZONE; } return parsedEnv; } export const env = getEnv();