import fs from 'fs' import path from 'path' function uniqueCandidates(candidates) { return [...new Set(candidates.filter(Boolean))] } function hasServerDataDir(root) { const normalizedRoot = String(root || '').replace(/\/+$/, '') return fs.existsSync(`${normalizedRoot}/server/data`) } export function resolveProjectRoot() { const envRoot = process.env.APP_ROOT ? process.env.APP_ROOT.trim() : '' const cwd = process.cwd() const parent = path.resolve(cwd, '..') const candidates = uniqueCandidates([envRoot, cwd, parent]) for (const root of candidates) { if (hasServerDataDir(root)) return root } return cwd } export function getProjectPath(...segments) { return path.join(resolveProjectRoot(), ...segments) } export function getServerDataPath(...segments) { // Validate segments: only allow simple filenames/dirnames (no path separators) const SEGMENT_RE = /^[a-zA-Z0-9._-]+$/ for (const s of segments) { if (!SEGMENT_RE.test(String(s || ''))) { throw new Error(`Invalid data path segment: ${String(s)}`) } } const dataDir = getProjectPath('server', 'data') const candidate = path.join(dataDir, ...segments) const resolved = path.resolve(candidate) const resolvedDataDir = path.resolve(dataDir) if (!resolved.startsWith(resolvedDataDir + path.sep) && resolved !== resolvedDataDir) { throw new Error('Resolved data path is outside server/data') } return resolved }