2026-08-29 14:55:42 +02:00
|
|
|
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs'
|
|
|
|
|
import path from 'node:path'
|
|
|
|
|
import crypto from 'node:crypto'
|
|
|
|
|
|
2026-08-29 14:43:44 +02:00
|
|
|
export interface Config {
|
|
|
|
|
dataDir: string
|
|
|
|
|
artworkDir: string
|
2026-09-03 20:43:33 +02:00
|
|
|
backupsDir: string
|
2026-08-29 14:43:44 +02:00
|
|
|
dbPath: string
|
|
|
|
|
port: number
|
|
|
|
|
sessionSecret: string
|
|
|
|
|
}
|
2026-08-29 14:55:42 +02:00
|
|
|
|
|
|
|
|
export function loadConfig(env: Record<string, string | undefined> = process.env): Config {
|
|
|
|
|
const dataDir = env.DATA_DIR ?? path.resolve('data')
|
|
|
|
|
mkdirSync(dataDir, { recursive: true })
|
|
|
|
|
const artworkDir = path.join(dataDir, 'artwork-cache')
|
|
|
|
|
mkdirSync(artworkDir, { recursive: true })
|
2026-09-03 20:43:33 +02:00
|
|
|
const backupsDir = path.join(dataDir, 'backups')
|
|
|
|
|
mkdirSync(backupsDir, { recursive: true })
|
2026-08-29 14:55:42 +02:00
|
|
|
return {
|
|
|
|
|
dataDir,
|
|
|
|
|
artworkDir,
|
2026-09-03 20:43:33 +02:00
|
|
|
backupsDir,
|
2026-08-29 14:55:42 +02:00
|
|
|
dbPath: path.join(dataDir, 'record-shop.db'),
|
|
|
|
|
port: Number(env.PORT ?? 3000),
|
|
|
|
|
sessionSecret: getOrCreateSecret(dataDir),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getOrCreateSecret(dataDir: string): string {
|
|
|
|
|
const secretPath = path.join(dataDir, 'session-secret')
|
|
|
|
|
try {
|
|
|
|
|
const existing = readFileSync(secretPath, 'utf8').trim()
|
|
|
|
|
if (existing) return existing
|
|
|
|
|
} catch {
|
|
|
|
|
// first boot — create below
|
|
|
|
|
}
|
|
|
|
|
const secret = crypto.randomBytes(32).toString('hex')
|
|
|
|
|
writeFileSync(secretPath, secret, { mode: 0o600 })
|
|
|
|
|
return secret
|
|
|
|
|
}
|