feat: unlimited by default — env vars default to 0 (unlimited)

Add 10 new configurable env vars (MAX_WORKER_THREADS, PROCESSING_TIMEOUT_S,
MAX_PIPELINE_STEPS, MAX_CANVAS_PIXELS, MAX_SVG_SIZE_MB, MAX_LOGO_SIZE_KB,
MAX_SPLIT_GRID, MAX_PDF_PAGES, SESSION_DURATION_HOURS, LOGIN_ATTEMPT_LIMIT).

Change existing defaults: MAX_UPLOAD_SIZE_MB=0, MAX_BATCH_SIZE=0,
MAX_MEGAPIXELS=0, RATE_LIMIT_PER_MIN=0, MAX_USERS=0, CONCURRENT_JOBS=0.

Zero means unlimited/auto-detect throughout the application.
This commit is contained in:
ashim-hq
2026-04-20 21:35:15 +08:00
parent 37277e5c09
commit 7daf2377f0
5 changed files with 97 additions and 26 deletions
+29 -8
View File
@@ -1,3 +1,4 @@
import { availableParallelism } from "node:os";
import { z } from "zod";
const envSchema = z.object({
@@ -13,13 +14,13 @@ const envSchema = z.object({
.default("false")
.transform((v) => v === "true"),
STORAGE_MODE: z.enum(["local", "s3"]).default("local"),
FILE_MAX_AGE_HOURS: z.coerce.number().default(24),
CLEANUP_INTERVAL_MINUTES: z.coerce.number().default(30),
MAX_UPLOAD_SIZE_MB: z.coerce.number().default(100),
MAX_BATCH_SIZE: z.coerce.number().default(200),
CONCURRENT_JOBS: z.coerce.number().default(3),
MAX_MEGAPIXELS: z.coerce.number().default(100),
RATE_LIMIT_PER_MIN: z.coerce.number().default(100),
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),
DB_PATH: z.string().default("./data/ashim.db"),
FILES_STORAGE_PATH: z.string().default("./data/files"),
WORKSPACE_PATH: z.string().default("./tmp/workspace"),
@@ -27,8 +28,18 @@ const envSchema = z.object({
DEFAULT_LOCALE: z.string().default("en"),
APP_NAME: z.string().default("ashim"),
CORS_ORIGIN: z.string().default(""),
MAX_USERS: z.coerce.number().default(5),
MAX_USERS: z.coerce.number().default(0),
LOG_LEVEL: z.enum(["fatal", "error", "warn", "info", "debug", "trace"]).default("info"),
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_LOGO_SIZE_KB: z.coerce.number().default(2048),
MAX_SPLIT_GRID: z.coerce.number().default(100),
MAX_PDF_PAGES: z.coerce.number().default(0),
SESSION_DURATION_HOURS: z.coerce.number().default(168),
LOGIN_ATTEMPT_LIMIT: z.coerce.number().default(10),
});
export type Env = z.infer<typeof envSchema>;
@@ -36,3 +47,13 @@ export type Env = z.infer<typeof envSchema>;
export function loadEnv(): Env {
return envSchema.parse(process.env);
}
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);
}