2026-04-20 21:35:15 +08:00
|
|
|
import { availableParallelism } from "node:os";
|
2026-03-22 02:46:34 +08:00
|
|
|
import { z } from "zod";
|
|
|
|
|
|
|
|
|
|
const envSchema = z.object({
|
2026-03-23 16:38:27 +08:00
|
|
|
PORT: z.coerce.number().default(1349),
|
2026-03-22 02:46:34 +08:00
|
|
|
AUTH_ENABLED: z
|
|
|
|
|
.enum(["true", "false"])
|
2026-03-23 11:46:45 +08:00
|
|
|
.default("true")
|
2026-03-22 02:46:34 +08:00
|
|
|
.transform((v) => v === "true"),
|
|
|
|
|
DEFAULT_USERNAME: z.string().default("admin"),
|
|
|
|
|
DEFAULT_PASSWORD: z.string().default("admin"),
|
2026-03-28 11:19:09 +08:00
|
|
|
SKIP_MUST_CHANGE_PASSWORD: z
|
|
|
|
|
.enum(["true", "false"])
|
|
|
|
|
.default("false")
|
|
|
|
|
.transform((v) => v === "true"),
|
2026-03-22 02:46:34 +08:00
|
|
|
STORAGE_MODE: z.enum(["local", "s3"]).default("local"),
|
2026-04-20 21:35:15 +08:00
|
|
|
FILE_MAX_AGE_HOURS: z.coerce.number().default(72),
|
|
|
|
|
CLEANUP_INTERVAL_MINUTES: z.coerce.number().default(60),
|
|
|
|
|
MAX_UPLOAD_SIZE_MB: z.coerce.number().default(0),
|
|
|
|
|
MAX_BATCH_SIZE: z.coerce.number().default(0),
|
|
|
|
|
CONCURRENT_JOBS: z.coerce.number().default(0),
|
|
|
|
|
MAX_MEGAPIXELS: z.coerce.number().default(0),
|
|
|
|
|
RATE_LIMIT_PER_MIN: z.coerce.number().default(0),
|
2026-04-24 18:02:21 +08:00
|
|
|
DB_PATH: z.string().default("./data/snapotter.db"),
|
2026-03-25 01:13:29 +08:00
|
|
|
FILES_STORAGE_PATH: z.string().default("./data/files"),
|
2026-03-22 02:46:34 +08:00
|
|
|
WORKSPACE_PATH: z.string().default("./tmp/workspace"),
|
2026-04-25 22:39:18 +08:00
|
|
|
DEFAULT_THEME: z.enum(["light", "dark", "system"]).default("light"),
|
2026-03-22 02:46:34 +08:00
|
|
|
DEFAULT_LOCALE: z.string().default("en"),
|
2026-03-23 11:46:45 +08:00
|
|
|
CORS_ORIGIN: z.string().default(""),
|
2026-04-20 21:35:15 +08:00
|
|
|
MAX_USERS: z.coerce.number().default(0),
|
2026-04-11 14:47:30 +08:00
|
|
|
LOG_LEVEL: z.enum(["fatal", "error", "warn", "info", "debug", "trace"]).default("info"),
|
2026-04-20 21:35:15 +08:00
|
|
|
MAX_WORKER_THREADS: z.coerce.number().default(0),
|
|
|
|
|
PROCESSING_TIMEOUT_S: z.coerce.number().default(0),
|
|
|
|
|
MAX_PIPELINE_STEPS: z.coerce.number().default(0),
|
|
|
|
|
MAX_CANVAS_PIXELS: z.coerce.number().default(0),
|
|
|
|
|
MAX_SVG_SIZE_MB: z.coerce.number().default(0),
|
|
|
|
|
MAX_SPLIT_GRID: z.coerce.number().default(100),
|
|
|
|
|
MAX_PDF_PAGES: z.coerce.number().default(0),
|
|
|
|
|
SESSION_DURATION_HOURS: z.coerce.number().default(168),
|
2026-04-24 22:43:14 +08:00
|
|
|
LOGIN_ATTEMPT_LIMIT: z.coerce.number().default(500),
|
2026-04-21 10:19:08 +08:00
|
|
|
TRUST_PROXY: z
|
|
|
|
|
.enum(["true", "false"])
|
|
|
|
|
.default("true")
|
|
|
|
|
.transform((v) => v === "true"),
|
2026-04-22 19:00:15 +08:00
|
|
|
ANALYTICS_ENABLED: z
|
|
|
|
|
.enum(["true", "false"])
|
|
|
|
|
.default("true")
|
|
|
|
|
.transform((v) => v === "true"),
|
|
|
|
|
ANALYTICS_SAMPLE_RATE: z.coerce.number().min(0).max(1).default(1.0),
|
2026-04-23 00:29:22 +08:00
|
|
|
POSTHOG_API_KEY: z.string().default("phc_CVHjGivwWVzh76M5EjijTwP5LpiqWie3EbCzXU7w2Smy"),
|
2026-04-22 19:00:15 +08:00
|
|
|
POSTHOG_HOST: z.string().default("https://us.i.posthog.com"),
|
2026-04-23 00:29:22 +08:00
|
|
|
SENTRY_DSN: z
|
|
|
|
|
.string()
|
|
|
|
|
.default(
|
|
|
|
|
"https://2fd53fc3b3fdc59d02cac044a4f90b71@o4511263372738560.ingest.us.sentry.io/4511264620085248",
|
|
|
|
|
),
|
2026-03-22 02:46:34 +08:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
export type Env = z.infer<typeof envSchema>;
|
|
|
|
|
|
|
|
|
|
export function loadEnv(): Env {
|
|
|
|
|
return envSchema.parse(process.env);
|
|
|
|
|
}
|
2026-04-20 21:35:15 +08:00
|
|
|
|
|
|
|
|
export function resolveConcurrency(env: Env): number {
|
|
|
|
|
if (env.CONCURRENT_JOBS > 0) return env.CONCURRENT_JOBS;
|
|
|
|
|
return Math.max(2, availableParallelism() - 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function resolveWorkerThreads(env: Env): number {
|
|
|
|
|
if (env.MAX_WORKER_THREADS > 0) return env.MAX_WORKER_THREADS;
|
|
|
|
|
return Math.max(2, availableParallelism() - 1);
|
|
|
|
|
}
|