config.ts 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. import "../src/plugins/dotenv.js";
  2. import { z } from "zod";
  3. // Функция для обязательных строковых переменных
  4. const requiredString = (envVar: string, defaultValue?: string) => {
  5. return z.preprocess(
  6. (val) => {
  7. if (typeof val !== "string" || val.trim() === "") {
  8. if (defaultValue !== undefined) return defaultValue;
  9. return z.NEVER;
  10. }
  11. return val.trim();
  12. },
  13. z
  14. .string()
  15. .min(
  16. 1,
  17. `${envVar} не определен. Пожалуйста, задайте переменную в файле ${process.env.NODE_ENV === "production" ? ".env" : ".dev.env"} или переменных окружения.`,
  18. ),
  19. );
  20. };
  21. // Функция для обязательных числовых переменных
  22. const requiredNumber = (envVar: string, defaultValue?: number) => {
  23. return z.preprocess(
  24. (val) => {
  25. if (val === undefined || val === null || val === "") {
  26. if (defaultValue !== undefined) return defaultValue;
  27. return z.NEVER;
  28. }
  29. // Если значение передано не строкой и не числом — ошибка
  30. if (typeof val !== "string" && typeof val !== "number") {
  31. return z.NEVER;
  32. }
  33. const parsed = Number(val);
  34. if (!isFinite(parsed)) {
  35. return z.NEVER;
  36. }
  37. return parsed;
  38. },
  39. z.number().min(1, `${envVar} должен быть числом. Например: 5432.`),
  40. );
  41. };
  42. // Описание схемы переменных окружения
  43. const ConfigSchema = z.object({
  44. PORT: requiredNumber("PORT", 3000),
  45. LOGS_LEVEL: z
  46. .enum(["error", "warn", "info", "verbose", "debug", "silly"])
  47. .default("info"),
  48. API_URL: requiredString("API_URL"),
  49. // DB
  50. DB_USER: requiredString("DB_USER"),
  51. DB_HOST: requiredString("DB_HOST"),
  52. DB_PORT: requiredNumber("DB_PORT"),
  53. DB_NAME: requiredString("DB_NAME"),
  54. DB_PASS: requiredString("DB_PASS"),
  55. // MAIL
  56. SMTP_HOST: requiredString("SMTP_HOST"),
  57. SMTP_PORT: requiredNumber("SMTP_PORT"),
  58. SMTP_USER: requiredString("SMTP_USER"),
  59. SMTP_PASS: requiredString("SMTP_PASS"),
  60. // AUTH
  61. JWT_ACCESS_SECRET: requiredString("JWT_ACCESS_SECRET"),
  62. JWT_REFRESH_SECRET: requiredString("JWT_REFRESH_SECRET"),
  63. PASSWORD_MAX_TRIES: requiredNumber("PASSWORD_MAX_TRIES", 5),
  64. ACCESS_TOKEN_LIFETIME_MINS: requiredNumber("ACCESS_TOKEN_LIFETIME_MINS", 15),
  65. REFRESH_TOKEN_LIFETIME_DAYS: requiredNumber("REFRESH_TOKEN_LIFETIME_DAYS", 7),
  66. // CONFIRM PINS
  67. CONFIRM_PIN_LIFETIME_MINS: requiredNumber("CONFIRM_PIN_LIFETIME_MINS", 10),
  68. CONFIRM_PIN_MAX_TRIES: requiredNumber("CONFIRM_PIN_MAX_TRIES", 3),
  69. });
  70. // Проверяем валидность конфигурации
  71. const result = ConfigSchema.safeParse(process.env);
  72. if (!result.success) {
  73. console.error(
  74. "❌ Невалидные переменные окружения:\n",
  75. JSON.stringify(result.error.format(), null, 2),
  76. );
  77. process.exit(1);
  78. }
  79. export const config = result.data;
  80. // Экспорт типа конфигурации
  81. export type Config = z.infer<typeof ConfigSchema>;
  82. // Логируем успешную валидацию без чувствительных данных
  83. const safeConfig = {
  84. ...config,
  85. DB_PASS: "********",
  86. SMTP_PASS: "********",
  87. JWT_ACCESS_SECRET: "********",
  88. JWT_REFRESH_SECRET: "********",
  89. };
  90. console.debug("✅ Валидация env прошла успешно:", safeConfig);