Refactor file handling to prioritize internal data directories for backups and uploads; enhance error handling and logging for metadata and CSV operations.
Some checks failed
Code Analysis (JS/Vue) / analyze (push) Failing after 47s

This commit is contained in:
Torsten Schulz (local)
2026-02-11 11:42:24 +01:00
parent 0fcf6ced0e
commit 0d533710cd
15 changed files with 225 additions and 127 deletions

View File

@@ -26,9 +26,12 @@ const getDataPath = (filename) => {
}
// Multer-Konfiguration für PDF-Uploads
// Store uploads in internal data directory instead of public/
const DOCUMENTS_DIR = getDataPath('documents')
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, 'public/documents/')
cb(null, DOCUMENTS_DIR)
},
filename: (req, file, cb) => {
cb(null, 'satzung.pdf')
@@ -74,8 +77,9 @@ export default defineEventHandler(async (event) => {
})
}
try {
await fs.mkdir(path.join(process.cwd(), 'public', 'documents'), { recursive: true })
try {
// Ensure internal documents dir exists
await fs.mkdir(DOCUMENTS_DIR, { recursive: true })
// Multer-Middleware für File-Upload
await new Promise((resolve, reject) => {
@@ -133,8 +137,9 @@ export default defineEventHandler(async (event) => {
configData.seiten = {}
}
// Serve the uploaded statute via internal media proxy
configData.seiten.satzung = {
pdfUrl: '/documents/satzung.pdf',
pdfUrl: '/api/media/documents/satzung.pdf',
content: htmlContent
}

View File

@@ -45,15 +45,11 @@ export default defineEventHandler(async (event) => {
})
}
// Wichtig: In Production werden statische Dateien aus `.output/public` ausgeliefert.
// Wenn PM2 `cwd` auf das Repo-Root setzt, ist `process.cwd()` NICHT `.output`
// daher schreiben wir robust in alle sinnvollen Zielorte:
// - `.output/public/data/<file>` (damit die laufende Instanz sofort die neuen Daten liefert)
// - `public/data/<file>` (damit der nächste Build die Daten wieder übernimmt)
//
// nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal.path-join-resolve-traversal
// filename is validated against allowlist above, path traversal prevented
const cwd = process.cwd()
// Neuer Ablauf (Option B): Schreibe CSVs ausschließlich in internes Datenverzeichnis,
// damit keine direkten Schreibzugriffe auf `public/` stattfinden.
// Später kann ein kontrollierter Deploy-/Sync-Prozess die Daten aus `server/data/public-data`
// in die öffentlich ausgelieferte `public/`-Location übernehmen.
const cwd = process.cwd()
const pathExists = async (p) => {
try {
@@ -97,23 +93,15 @@ export default defineEventHandler(async (event) => {
}
}
// Preferred: das tatsächlich ausgelieferte Verzeichnis in Production
// (Nuxt/Nitro serve static aus `.output/public`)
const preferredPaths = []
if (await pathExists(path.join(cwd, '.output/public'))) {
preferredPaths.push(path.join(cwd, '.output/public/data', filename))
}
if (await pathExists(path.join(cwd, '../.output/public'))) {
preferredPaths.push(path.join(cwd, '../.output/public/data', filename))
}
// Fallbacks: Source-Public (für Persistenz bei nächstem Build) und diverse cwd-Layouts
const fallbackPaths = [
path.join(cwd, 'public/data', filename),
path.join(cwd, '../public/data', filename)
// Ziel: internes Datenverzeichnis unter `server/data/public-data` (persistente, interne Quelle)
const internalPaths = [
path.join(cwd, 'server/data/public-data', filename),
path.join(cwd, '../server/data/public-data', filename)
]
const uniquePaths = [...new Set([...preferredPaths, ...fallbackPaths])]
// Behalte legacy `.output` write nur als optionalen, nicht-standardisierten Pfad
// (wird NICHT automatisch gefordert). Hauptsächlich schreiben wir intern.
const uniquePaths = [...new Set([...internalPaths])]
const writeResults = []
const writeErrors = []
let wrotePreferred = false

View File

@@ -17,25 +17,32 @@ export default defineEventHandler(async (event) => {
const isVorstand = hasRole(currentUser, 'vorstand')
// Return users without Passwörter; Kontaktdaten nur für Vorstand
// Nur Admin oder Vorstand duerfen vollen Benutzer-Contact und Rollen sehen.
const canSeePrivate = hasAnyRole(currentUser, 'admin', 'vorstand')
const safeUsers = users.map(u => {
const migrated = migrateUserRoles({ ...u })
const roles = Array.isArray(migrated.roles) ? migrated.roles : (migrated.role ? [migrated.role] : ['mitglied'])
const email = isVorstand ? u.email : undefined
const phone = isVorstand ? (u.phone || '') : undefined
return {
id: u.id,
email,
name: u.name,
roles: roles,
role: roles[0] || 'mitglied', // Rückwärtskompatibilität
phone,
active: u.active,
created: u.created,
lastLogin: u.lastLogin
}
return canSeePrivate
? {
id: u.id,
email: u.email,
name: u.name,
roles: roles,
role: roles[0] || 'mitglied',
phone: u.phone || '',
active: u.active,
created: u.created,
lastLogin: u.lastLogin
}
: {
id: u.id,
name: u.name,
role: roles[0] || 'mitglied',
active: u.active,
lastLogin: u.lastLogin
}
})
return {