import {execFile} from "node:child_process" import folderMap from "#server/config/backup-folders.json"; export const REMOTE_HOST = process.env.BACKUPS_REMOTE_HOST export const REMOTE_ROOT = process.env.BACKUPS_REMOTE_ROOT || "/home/malio-b/backups" export const FOLDER_MAP = folderMap as Record export const isSafeFolder = (value: string) => /^[a-zA-Z0-9._-]+$/.test(value) export const isSafeFile = (value: string) => /^[a-zA-Z0-9._-]+$/.test(value) export const shellQuote = (value: string) => `'${value.replace(/'/g, `'\\''`)}'` export function runSsh(command: string): Promise { if (!REMOTE_HOST) { return Promise.reject(new Error("BACKUPS_REMOTE_HOST is not configured")) } return new Promise((resolve, reject) => { execFile( "ssh", ["-o", "BatchMode=yes", "-o", "ConnectTimeout=5", REMOTE_HOST, command], {maxBuffer: 10 * 1024 * 1024}, (error, stdout, stderr) => { if (error) { reject(stderr || error.message) return } resolve(stdout) } ) }) } async function remoteDirExists(remoteDir: string): Promise { const output = await runSsh(`[ -d ${shellQuote(remoteDir)} ] && echo yes || echo no`) return output.trim() === "yes" } export async function resolveFolderRemoteDir(folderName: string): Promise { const mapped = FOLDER_MAP[folderName] if (mapped) { return `${REMOTE_ROOT}/${mapped}` } const direct = `${REMOTE_ROOT}/${folderName}` if (await remoteDirExists(direct)) { return direct } const nested = `${REMOTE_ROOT}/bdd-recette/${folderName}` if (await remoteDirExists(nested)) { return nested } return null }