Docker images / prepare-release (push) Successful in 12s
Docker images / backend-test (push) Successful in 4m9s
Docker images / frontend-image (push) Successful in 4m28s
Docker images / updater-image (push) Successful in 58s
Docker images / backend-image (push) Successful in 2m49s
Docker images / notify-webhook (push) Skipped
Docker images / publish-release (push) Successful in 11s
При старте backend накатывает схему PostgreSQL 18 и, если база пустая, один раз импортирует mikrotik.db с тома. Повторный старт не копирует данные. Бэкап в UI идёт через pg_dump. Co-authored-by: Cursor <[email protected]>
70 lines
2.3 KiB
TypeScript
70 lines
2.3 KiB
TypeScript
import { config } from "dotenv"
|
|
import { z } from "zod"
|
|
|
|
config()
|
|
|
|
function boolEnv(v: string | undefined, fallback: boolean): boolean {
|
|
if (v === undefined || v === "") return fallback
|
|
return v === "1" || v.toLowerCase() === "true"
|
|
}
|
|
|
|
const isProd = process.env.NODE_ENV === "production"
|
|
|
|
const envSchema = z.object({
|
|
DATABASE_URL: z.string().default("postgres://mmapp:[email protected]:5432/mmapp"),
|
|
DATABASE_PATH: z.string().default("./mikrotik.db"),
|
|
SQLITE_IMPORT_FULL_HISTORY: z.boolean().default(false),
|
|
PORT: z.coerce.number().int().positive().default(8000),
|
|
CORS_ORIGIN: z.string().default("http://localhost:3000"),
|
|
AUTH_REQUIRED: z.boolean().default(false),
|
|
AUTH_JWT_SECRET: z.string().default(""),
|
|
AUTH_ISSUER: z.string().default("https://auth.shnt.top"),
|
|
AUTH_PORTAL_URL: z.string().default("http://localhost:5175"),
|
|
})
|
|
|
|
const raw = {
|
|
DATABASE_URL: process.env.DATABASE_URL,
|
|
DATABASE_PATH: process.env.DATABASE_PATH,
|
|
SQLITE_IMPORT_FULL_HISTORY: boolEnv(process.env.SQLITE_IMPORT_FULL_HISTORY, false),
|
|
PORT: process.env.PORT,
|
|
CORS_ORIGIN: process.env.CORS_ORIGIN,
|
|
AUTH_REQUIRED: boolEnv(process.env.AUTH_REQUIRED, false),
|
|
AUTH_JWT_SECRET:
|
|
process.env.AUTH_JWT_SECRET?.trim() ||
|
|
process.env.JWT_SECRET?.trim() ||
|
|
(isProd ? "" : "dev-secret-change-me"),
|
|
AUTH_ISSUER:
|
|
process.env.AUTH_ISSUER?.trim() ||
|
|
process.env.ISSUER?.trim() ||
|
|
"https://auth.shnt.top",
|
|
AUTH_PORTAL_URL: (
|
|
process.env.AUTH_PORTAL_URL ??
|
|
process.env.NEXT_PUBLIC_AUTH_PORTAL_URL ??
|
|
"http://localhost:5175"
|
|
).replace(/\/$/, ""),
|
|
}
|
|
|
|
const parsed = envSchema.safeParse(raw)
|
|
|
|
if (!parsed.success) {
|
|
console.error("❌ Invalid environment variables:", parsed.error.flatten().fieldErrors)
|
|
process.exit(1)
|
|
}
|
|
|
|
if (parsed.data.AUTH_REQUIRED && parsed.data.AUTH_JWT_SECRET.length < 8) {
|
|
console.error("❌ AUTH_JWT_SECRET / JWT_SECRET required when AUTH_REQUIRED=true")
|
|
process.exit(1)
|
|
}
|
|
|
|
export const env = {
|
|
DATABASE_URL: parsed.data.DATABASE_URL,
|
|
DATABASE_PATH: parsed.data.DATABASE_PATH,
|
|
sqliteImportFullHistory: parsed.data.SQLITE_IMPORT_FULL_HISTORY,
|
|
PORT: parsed.data.PORT,
|
|
CORS_ORIGIN: parsed.data.CORS_ORIGIN,
|
|
authRequired: parsed.data.AUTH_REQUIRED,
|
|
jwtSecret: parsed.data.AUTH_JWT_SECRET || "dev-secret-change-me",
|
|
authIssuer: parsed.data.AUTH_ISSUER,
|
|
authPortalUrl: parsed.data.AUTH_PORTAL_URL,
|
|
}
|