Files
2026-09-30 21:54:01 +00:00

58 lines
2.0 KiB
JavaScript

// Build-Protokolle je Projekt: upload-data/preview-logs/<projectId>/<runFile>, maskiert, letzte 5
import crypto from 'node:crypto'
import fs from 'node:fs/promises'
import path from 'node:path'
import { config } from '../config.js'
import { maskLog } from './previewEnv.js'
const KEEP = 5
const MAX_LINES = 300
const PROJECT_RE = /^[A-Za-z0-9][A-Za-z0-9._-]{0,35}$/
const FILE_RE = /^\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}-\d{3}Z-[0-9a-f]{6}\.log$/
const projectDir = (projectId) => {
if (!PROJECT_RE.test(String(projectId || '')) || String(projectId).includes('..')) {
throw new Error('Ungültige Projekt-ID')
}
return path.join(config.previewRunner.logDir, projectId)
}
export async function saveRunLog(projectId, lines, env) {
const dir = projectDir(projectId)
await fs.mkdir(dir, { recursive: true })
const stamp = new Date().toISOString().replace(/[:.]/g, '-')
const runFile = `${stamp}-${crypto.randomBytes(3).toString('hex')}.log`
const text = maskLog((Array.isArray(lines) ? lines : []).slice(-MAX_LINES).join('\n'), env)
await fs.writeFile(path.join(dir, runFile), text + '\n', 'utf8')
const all = (await fs.readdir(dir)).filter((f) => FILE_RE.test(f)).sort()
for (const old of all.slice(0, Math.max(0, all.length - KEEP))) {
await fs.rm(path.join(dir, old), { force: true })
}
return runFile
}
export const isRunFile = (name) => FILE_RE.test(String(name || ''))
// Neueste zuerst
export async function listRunLogs(projectId) {
let files
try { files = await fs.readdir(projectDir(projectId)) } catch { return [] }
return files
.filter((f) => FILE_RE.test(f))
.sort()
.reverse()
.map((runFile) => ({
runFile,
at: runFile.slice(0, 24).replace(/^(.{13})-(.{2})-(.{2})-/, '$1:$2:$3.'),
}))
}
export async function readRunLog(projectId, runFile) {
if (!FILE_RE.test(String(runFile || ''))) return null
try {
return await fs.readFile(path.join(projectDir(projectId), runFile), 'utf8')
} catch {
return null
}
}