Some checks failed
Code Analysis (JS/Vue) / analyze (push) Failing after 53s
This commit updates the version of several packages in package.json and package-lock.json, including downgrading "quill" to 2.0.2 and upgrading "devalue", "diff", "h3", "node-mock-http", "tar", and "undici" to their latest versions. Additionally, it improves the CSV file handling in the CMS by implementing a cache-busting mechanism for fetching data and enhancing error handling during file saving, ensuring more robust data management.
104 lines
3.2 KiB
JavaScript
104 lines
3.2 KiB
JavaScript
import fs from 'fs/promises'
|
||
import path from 'path'
|
||
import { getUserFromToken, hasAnyRole } from '../../utils/auth.js'
|
||
|
||
export default defineEventHandler(async (event) => {
|
||
try {
|
||
const token = getCookie(event, 'auth_token')
|
||
const currentUser = token ? await getUserFromToken(token) : null
|
||
|
||
if (!currentUser) {
|
||
throw createError({
|
||
statusCode: 401,
|
||
statusMessage: 'Nicht authentifiziert'
|
||
})
|
||
}
|
||
|
||
if (!hasAnyRole(currentUser, 'admin', 'vorstand')) {
|
||
throw createError({
|
||
statusCode: 403,
|
||
statusMessage: 'Keine Berechtigung'
|
||
})
|
||
}
|
||
|
||
const { filename, content } = await readBody(event)
|
||
|
||
if (!filename || !content) {
|
||
throw createError({
|
||
statusCode: 400,
|
||
statusMessage: 'Filename und Content sind erforderlich'
|
||
})
|
||
}
|
||
|
||
// Sicherheitsprüfung: Nur bestimmte Dateien erlauben
|
||
const allowedFiles = [
|
||
'vereinsmeisterschaften.csv',
|
||
'mannschaften.csv',
|
||
'termine.csv',
|
||
'spielplan.csv'
|
||
]
|
||
|
||
if (!allowedFiles.includes(filename)) {
|
||
throw createError({
|
||
statusCode: 403,
|
||
statusMessage: 'Datei nicht erlaubt'
|
||
})
|
||
}
|
||
|
||
// 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()
|
||
const candidatePaths = [
|
||
path.join(cwd, '.output/public/data', filename),
|
||
path.join(cwd, 'public/data', filename),
|
||
path.join(cwd, '../public/data', filename), // falls cwd z.B. `.output` oder `.output/server` ist
|
||
path.join(cwd, '../.output/public/data', filename)
|
||
]
|
||
|
||
const uniquePaths = [...new Set(candidatePaths)]
|
||
const writeResults = []
|
||
const writeErrors = []
|
||
|
||
for (const targetPath of uniquePaths) {
|
||
try {
|
||
const dataDir = path.dirname(targetPath)
|
||
await fs.mkdir(dataDir, { recursive: true })
|
||
await fs.writeFile(targetPath, content, 'utf8')
|
||
writeResults.push(targetPath)
|
||
} catch (e) {
|
||
writeErrors.push({ targetPath, error: e })
|
||
}
|
||
}
|
||
|
||
if (writeResults.length === 0) {
|
||
console.error('Konnte CSV-Datei in keinen Zielpfad schreiben:', writeErrors)
|
||
throw createError({
|
||
statusCode: 500,
|
||
statusMessage: 'Fehler beim Speichern der Datei'
|
||
})
|
||
}
|
||
|
||
return {
|
||
success: true,
|
||
message: 'Datei erfolgreich gespeichert',
|
||
writtenTo: writeResults
|
||
}
|
||
|
||
} catch (error) {
|
||
console.error('Fehler beim Speichern der CSV-Datei:', error)
|
||
if (error.statusCode) {
|
||
throw error
|
||
}
|
||
throw createError({
|
||
statusCode: 500,
|
||
statusMessage: 'Fehler beim Speichern der Datei'
|
||
})
|
||
}
|
||
})
|