瀏覽代碼

изменена валидация env

Vadim 6 月之前
父節點
當前提交
fffa89cee2
共有 1 個文件被更改,包括 91 次插入47 次删除
  1. 91 47
      config/config.ts

+ 91 - 47
config/config.ts

@@ -1,60 +1,104 @@
 import "../src/plugins/dotenv.js";
 import { z } from "zod";
 
-// Создаем схему конфигурации с проверками и преобразованиями
-const ZConfigShema = z.object({
-  PORT: z
-    .string()
-    .min(1, "PORT must be defined")
-    .transform((val) => parseInt(val, 10)),
-  LOGS_LEVEL: z.enum(["error", "warn", "info", "verbose", "debug", "silly"]),
-  API_URL: z.string().min(1, "API_URL must be defined"),
+// Функция для обязательных строковых переменных
+const requiredString = (envVar: string, defaultValue?: string) => {
+  return z.preprocess(
+    (val) => {
+      if (typeof val !== "string" || val.trim() === "") {
+        if (defaultValue !== undefined) return defaultValue;
+        return z.NEVER;
+      }
+      return val.trim();
+    },
+    z
+      .string()
+      .min(
+        1,
+        `${envVar} не определен. Пожалуйста, задайте переменную в файле ${process.env.NODE_ENV === "production" ? ".env" : ".dev.env"} или переменных окружения.`,
+      ),
+  );
+};
+
+// Функция для обязательных числовых переменных
+const requiredNumber = (envVar: string, defaultValue?: number) => {
+  return z.preprocess(
+    (val) => {
+      if (val === undefined || val === null || val === "") {
+        if (defaultValue !== undefined) return defaultValue;
+        return z.NEVER;
+      }
+
+      // Если значение передано не строкой и не числом — ошибка
+      if (typeof val !== "string" && typeof val !== "number") {
+        return z.NEVER;
+      }
+
+      const parsed = Number(val);
+      if (!isFinite(parsed)) {
+        return z.NEVER;
+      }
+      return parsed;
+    },
+    z.number().min(1, `${envVar} должен быть числом. Например: 5432.`),
+  );
+};
+
+// Описание схемы переменных окружения
+const ConfigSchema = z.object({
+  PORT: requiredNumber("PORT", 3000),
+  LOGS_LEVEL: z
+    .enum(["error", "warn", "info", "verbose", "debug", "silly"])
+    .default("info"),
+  API_URL: requiredString("API_URL"),
 
   // DB
-  DB_USER: z.string().min(1, "DB_USER must be defined"),
-  DB_HOST: z.string().min(1, "DB_HOST must be defined"),
-  DB_PORT: z
-    .string()
-    .min(1, "DB_PORT must be defined")
-    .transform((val) => parseInt(val, 10)),
-  DB_NAME: z.string().min(1, "DB_NAME must be defined"),
-  DB_PASS: z.string().min(1, "DB_PASS must be defined"),
+  DB_USER: requiredString("DB_USER"),
+  DB_HOST: requiredString("DB_HOST"),
+  DB_PORT: requiredNumber("DB_PORT"),
+  DB_NAME: requiredString("DB_NAME"),
+  DB_PASS: requiredString("DB_PASS"),
 
   // MAIL
-  SMTP_HOST: z.string().min(1, "SMTP_HOST must be defined"),
-  SMTP_PORT: z
-    .string()
-    .min(1, "SMTP_PORT must be defined")
-    .transform((val) => parseInt(val, 10)),
-  SMTP_USER: z.string().min(1, "SMTP_USER must be defined"),
-  SMTP_PASS: z.string().min(1, "SMTP_PASS must be defined"),
+  SMTP_HOST: requiredString("SMTP_HOST"),
+  SMTP_PORT: requiredNumber("SMTP_PORT"),
+  SMTP_USER: requiredString("SMTP_USER"),
+  SMTP_PASS: requiredString("SMTP_PASS"),
 
   // AUTH
-  JWT_ACCESS_SECRET: z.string().min(1, "JWT_ACCESS_SECRET must be defined"),
-  JWT_REFRESH_SECRET: z.string().min(1, "JWT_REFRESH_SECRET must be defined"),
-  PASSWORD_MAX_TRIES: z
-    .string()
-    .min(1, "PASSWORD_MAX_TRIES must be defined")
-    .transform((val) => parseInt(val, 10)),
-  ACCESS_TOKEN_LIFETIME_MINS: z
-    .string()
-    .min(1, "ACCESS_TOKEN_LIFETIME_MINS must be defined")
-    .transform((val) => parseInt(val, 10)),
-  REFRESH_TOKEN_LIFETIME_DAYS: z
-    .string()
-    .min(1, "REFRESH_TOKEN_LIFETIME_DAYS must be defined")
-    .transform((val) => parseInt(val, 10)),
+  JWT_ACCESS_SECRET: requiredString("JWT_ACCESS_SECRET"),
+  JWT_REFRESH_SECRET: requiredString("JWT_REFRESH_SECRET"),
+  PASSWORD_MAX_TRIES: requiredNumber("PASSWORD_MAX_TRIES", 5),
+  ACCESS_TOKEN_LIFETIME_MINS: requiredNumber("ACCESS_TOKEN_LIFETIME_MINS", 15),
+  REFRESH_TOKEN_LIFETIME_DAYS: requiredNumber("REFRESH_TOKEN_LIFETIME_DAYS", 7),
 
   // CONFIRM PINS
-  CONFIRM_PIN_LIFETIME_MINS: z
-    .string()
-    .min(1, "CONFIRM_PIN_LIFETIME_MINS must be defined")
-    .transform((val) => parseInt(val, 10)),
-  CONFIRM_PIN_MAX_TRIES: z
-    .string()
-    .min(1, "CONFIRM_PIN_MAX_TRIES must be defined")
-    .transform((val) => parseInt(val, 10)),
+  CONFIRM_PIN_LIFETIME_MINS: requiredNumber("CONFIRM_PIN_LIFETIME_MINS", 10),
+  CONFIRM_PIN_MAX_TRIES: requiredNumber("CONFIRM_PIN_MAX_TRIES", 3),
 });
 
-// Парсим и валидируем переменные окружения
-export const config = ZConfigShema.parse(process.env);
+// Проверяем валидность конфигурации
+const result = ConfigSchema.safeParse(process.env);
+
+if (!result.success) {
+  console.error(
+    "❌ Невалидные переменные окружения:\n",
+    JSON.stringify(result.error.format(), null, 2),
+  );
+  process.exit(1);
+}
+
+export const config = result.data;
+
+// Экспорт типа конфигурации
+export type Config = z.infer<typeof ConfigSchema>;
+
+// Логируем успешную валидацию без чувствительных данных
+const safeConfig = {
+  ...config,
+  DB_PASS: "********",
+  SMTP_PASS: "********",
+  JWT_ACCESS_SECRET: "********",
+  JWT_REFRESH_SECRET: "********",
+};
+console.debug("✅ Валидация env прошла успешно:", safeConfig);