/** * ───────────────────────────────────────────── * SPB Drive — Personal Cloud Drive * ───────────────────────────────────────────── * Author : Simon-Pierre Boucher * Contact : contact@spboucher.ai * File : src/config.mjs * Purpose : zod-validated environment configuration + data-dir layout * License : MIT © Simon-Pierre Boucher * ───────────────────────────────────────────── */ import { mkdirSync } from 'node:fs'; import path from 'node:path'; import { z } from 'zod'; const schema = z.object({ SPBDRIVE_DATA_DIR: z.string().default('/srv/drive'), SPBDRIVE_PORT: z.coerce.number().int().min(1).max(65535).default(7430), SPBDRIVE_HOST: z.string().default('127.0.0.1'), SPBDRIVE_PUBLIC_URL: z.string().url().default('https://drive.spboucher.ai'), SPBDRIVE_LOG_LEVEL: z.enum(['silent', 'fatal', 'error', 'warn', 'info', 'debug', 'trace']).default('info'), SPBDRIVE_TRUST_PROXY: z .string() .default('true') .transform((v) => v === 'true' || v === '1'), /** Read once at first boot to seed the login password; ignored afterwards. */ SPBDRIVE_BOOTSTRAP_PASSWORD: z.string().optional(), }); const env = schema.parse(process.env); const dataDir = path.resolve(env.SPBDRIVE_DATA_DIR); /** Resolved, validated application configuration (frozen). */ export const config = Object.freeze({ port: env.SPBDRIVE_PORT, host: env.SPBDRIVE_HOST, publicUrl: env.SPBDRIVE_PUBLIC_URL.replace(/\/$/, ''), logLevel: env.SPBDRIVE_LOG_LEVEL, trustProxy: env.SPBDRIVE_TRUST_PROXY, bootstrapPassword: env.SPBDRIVE_BOOTSTRAP_PASSWORD ?? null, dataDir, filesDir: path.join(dataDir, 'files'), dbDir: path.join(dataDir, 'db'), cacheDir: path.join(dataDir, 'cache'), uploadsDir: path.join(dataDir, 'cache', 'uploads'), logsDir: path.join(dataDir, 'logs'), dbFile: path.join(dataDir, 'db', 'drive.sqlite'), authFile: path.join(dataDir, 'auth.json'), keysFile: path.join(dataDir, 'keys.json'), chunkSize: 8 * 1024 * 1024, sessionTtlMs: 24 * 60 * 60 * 1000, rememberTtlMs: 30 * 24 * 60 * 60 * 1000, trashRetentionDays: 30, loginMaxFailures: 5, loginLockoutMs: 15 * 60 * 1000, }); /** Create every runtime directory (idempotent, restrictive perms). */ export function ensureDataDirs() { for (const dir of [ config.dataDir, config.filesDir, config.dbDir, config.cacheDir, config.uploadsDir, config.logsDir, path.join(config.cacheDir, 'thumbs'), path.join(config.cacheDir, 'transcode'), path.join(config.cacheDir, 'office'), path.join(config.cacheDir, 'peaks'), path.join(config.cacheDir, 'text'), path.join(config.cacheDir, 'tmp'), ]) { mkdirSync(dir, { recursive: true, mode: 0o700 }); } }