Update package dependencies and enhance CSV handling in CMS
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.
This commit is contained in:
Torsten Schulz (local)
2026-01-18 22:25:04 +01:00
parent 3577831149
commit a0dd4f6134
4 changed files with 71 additions and 47 deletions

54
package-lock.json generated
View File

@@ -22,7 +22,7 @@
"pdf-lib": "^1.17.1", "pdf-lib": "^1.17.1",
"pdf-parse": "^2.4.5", "pdf-parse": "^2.4.5",
"pinia": "^3.0.3", "pinia": "^3.0.3",
"quill": "^2.0.3", "quill": "^2.0.2",
"sharp": "^0.34.5", "sharp": "^0.34.5",
"tinymce": "^8.3.1", "tinymce": "^8.3.1",
"vue": "^3.5.22" "vue": "^3.5.22"
@@ -6672,9 +6672,9 @@
} }
}, },
"node_modules/devalue": { "node_modules/devalue": {
"version": "5.4.1", "version": "5.6.2",
"resolved": "https://registry.npmjs.org/devalue/-/devalue-5.4.1.tgz", "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.6.2.tgz",
"integrity": "sha512-YtoaOfsqjbZQKGIMRYDWKjUmSB4VJ/RElB+bXZawQAQYAo4xu08GKTMVlsZDTF6R2MbAgjcAQRPI5eIyRAT2OQ==", "integrity": "sha512-nPRkjWzzDQlsejL1WVifk5rvcFi/y1onBRxjaFMjZeR9mFpqu2gmAZ9xUB9/IEanEP/vBtGeGganC/GO1fmufg==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/dezalgo": { "node_modules/dezalgo": {
@@ -6696,9 +6696,9 @@
"license": "Apache-2.0" "license": "Apache-2.0"
}, },
"node_modules/diff": { "node_modules/diff": {
"version": "8.0.2", "version": "8.0.3",
"resolved": "https://registry.npmjs.org/diff/-/diff-8.0.2.tgz", "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.3.tgz",
"integrity": "sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg==", "integrity": "sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ==",
"license": "BSD-3-Clause", "license": "BSD-3-Clause",
"engines": { "engines": {
"node": ">=0.3.1" "node": ">=0.3.1"
@@ -7927,9 +7927,9 @@
} }
}, },
"node_modules/h3": { "node_modules/h3": {
"version": "1.15.4", "version": "1.15.5",
"resolved": "https://registry.npmjs.org/h3/-/h3-1.15.4.tgz", "resolved": "https://registry.npmjs.org/h3/-/h3-1.15.5.tgz",
"integrity": "sha512-z5cFQWDffyOe4vQ9xIqNfCZdV4p//vy6fBnr8Q1AWnVZ0teurKMG66rLj++TKwKPUP3u7iMUvrvKaEUiQw2QWQ==", "integrity": "sha512-xEyq3rSl+dhGX2Lm0+eFQIAzlDN6Fs0EcC4f7BNUmzaRX/PTzeuM+Tr2lHB8FoXggsQIeXLj8EDVgs5ywxyxmg==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"cookie-es": "^1.2.2", "cookie-es": "^1.2.2",
@@ -7937,12 +7937,18 @@
"defu": "^6.1.4", "defu": "^6.1.4",
"destr": "^2.0.5", "destr": "^2.0.5",
"iron-webcrypto": "^1.2.1", "iron-webcrypto": "^1.2.1",
"node-mock-http": "^1.0.2", "node-mock-http": "^1.0.4",
"radix3": "^1.1.2", "radix3": "^1.1.2",
"ufo": "^1.6.1", "ufo": "^1.6.3",
"uncrypto": "^0.1.3" "uncrypto": "^0.1.3"
} }
}, },
"node_modules/h3/node_modules/ufo": {
"version": "1.6.3",
"resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.3.tgz",
"integrity": "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==",
"license": "MIT"
},
"node_modules/has-flag": { "node_modules/has-flag": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
@@ -9765,9 +9771,9 @@
} }
}, },
"node_modules/node-mock-http": { "node_modules/node-mock-http": {
"version": "1.0.3", "version": "1.0.4",
"resolved": "https://registry.npmjs.org/node-mock-http/-/node-mock-http-1.0.3.tgz", "resolved": "https://registry.npmjs.org/node-mock-http/-/node-mock-http-1.0.4.tgz",
"integrity": "sha512-jN8dK25fsfnMrVsEhluUTPkBFY+6ybu7jSB1n+ri/vOGjJxU8J9CZhpSGkHXSkFjtUhbmoncG/YG9ta5Ludqog==", "integrity": "sha512-8DY+kFsDkNXy1sJglUfuODx1/opAGJGyrTuFqEoN90oRc2Vk0ZbD4K2qmKXBBEhZQzdKHIVfEJpDU8Ak2NJEvQ==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/node-releases": { "node_modules/node-releases": {
@@ -11423,9 +11429,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/quill": { "node_modules/quill": {
"version": "2.0.3", "version": "2.0.2",
"resolved": "https://registry.npmjs.org/quill/-/quill-2.0.3.tgz", "resolved": "https://registry.npmjs.org/quill/-/quill-2.0.2.tgz",
"integrity": "sha512-xEYQBqfYx/sfb33VJiKnSJp8ehloavImQ2A6564GAbqG55PGw1dAWUn1MUbQB62t0azawUS2CZZhWCjO8gRvTw==", "integrity": "sha512-QfazNrhMakEdRG57IoYFwffUIr04LWJxbS/ZkidRFXYCQt63c1gK6Z7IHUXMx/Vh25WgPBU42oBaNzQ0K1R/xw==",
"license": "BSD-3-Clause", "license": "BSD-3-Clause",
"dependencies": { "dependencies": {
"eventemitter3": "^5.0.1", "eventemitter3": "^5.0.1",
@@ -12892,9 +12898,9 @@
} }
}, },
"node_modules/tar": { "node_modules/tar": {
"version": "7.5.2", "version": "7.5.3",
"resolved": "https://registry.npmjs.org/tar/-/tar-7.5.2.tgz", "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.3.tgz",
"integrity": "sha512-7NyxrTE4Anh8km8iEy7o0QYPs+0JKBTj5ZaqHg6B39erLg0qYXN3BijtShwbsNSvQ+LN75+KV+C4QR/f6Gwnpg==", "integrity": "sha512-ENg5JUHUm2rDD7IvKNFGzyElLXNjachNLp6RaGf4+JOgxXHkqA+gq81ZAMCUmtMtqBsoU62lcp6S27g1LCYGGQ==",
"license": "BlueOak-1.0.0", "license": "BlueOak-1.0.0",
"dependencies": { "dependencies": {
"@isaacs/fs-minipass": "^4.0.0", "@isaacs/fs-minipass": "^4.0.0",
@@ -13220,9 +13226,9 @@
} }
}, },
"node_modules/undici": { "node_modules/undici": {
"version": "7.16.0", "version": "7.18.2",
"resolved": "https://registry.npmjs.org/undici/-/undici-7.16.0.tgz", "resolved": "https://registry.npmjs.org/undici/-/undici-7.18.2.tgz",
"integrity": "sha512-QEg3HPMll0o3t2ourKwOeUAZ159Kn9mx5pnzHRQO8+Wixmh88YdZRiIwat0iNzNNXn0yoEtXJqFpyW7eM8BV7g==", "integrity": "sha512-y+8YjDFzWdQlSE9N5nzKMT3g4a5UBX1HKowfdXh0uvAnTaqqwqB92Jt4UXBAeKekDs5IaDKyJFR4X1gYVCgXcw==",
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=20.18.1" "node": ">=20.18.1"

View File

@@ -29,7 +29,7 @@
"pdf-lib": "^1.17.1", "pdf-lib": "^1.17.1",
"pdf-parse": "^2.4.5", "pdf-parse": "^2.4.5",
"pinia": "^3.0.3", "pinia": "^3.0.3",
"quill": "^2.0.3", "quill": "^2.0.2",
"sharp": "^0.34.5", "sharp": "^0.34.5",
"tinymce": "^8.3.1", "tinymce": "^8.3.1",
"vue": "^3.5.22" "vue": "^3.5.22"

View File

@@ -336,7 +336,9 @@ const formData = ref({
const loadMannschaften = async () => { const loadMannschaften = async () => {
isLoading.value = true isLoading.value = true
try { try {
const response = await fetch('/data/mannschaften.csv') // Cache-Buster: Browser/CDN könnten CSV sonst aggressiv cachen
const url = `/data/mannschaften.csv?_t=${Date.now()}`
const response = await fetch(url, { cache: 'no-store' })
if (!response.ok) { if (!response.ok) {
throw new Error('Fehler beim Laden der Mannschaften') throw new Error('Fehler beim Laden der Mannschaften')
} }
@@ -466,7 +468,7 @@ const saveMannschaft = async () => {
} }
} catch (error) { } catch (error) {
console.error('Fehler beim Speichern:', error) console.error('Fehler beim Speichern:', error)
errorMessage.value = error.data?.message || 'Fehler beim Speichern der Mannschaft.' errorMessage.value = error?.data?.statusMessage || error?.statusMessage || error?.data?.message || 'Fehler beim Speichern der Mannschaft.'
if (window.showErrorModal) { if (window.showErrorModal) {
window.showErrorModal('Fehler', errorMessage.value) window.showErrorModal('Fehler', errorMessage.value)
} }

View File

@@ -45,33 +45,49 @@ export default defineEventHandler(async (event) => {
}) })
} }
// Handle both dev and production paths // 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 // nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal.path-join-resolve-traversal
// filename is validated against allowlist above, path traversal prevented // filename is validated against allowlist above, path traversal prevented
const cwd = process.cwd() const cwd = process.cwd()
let filePath const candidatePaths = [
path.join(cwd, '.output/public/data', filename),
// In production (.output/server), working dir is .output path.join(cwd, 'public/data', filename),
if (cwd.endsWith('.output')) { path.join(cwd, '../public/data', filename), // falls cwd z.B. `.output` oder `.output/server` ist
// nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal.path-join-resolve-traversal path.join(cwd, '../.output/public/data', filename)
filePath = path.join(cwd, '../public/data', filename) ]
} else {
// In development, working dir is project root const uniquePaths = [...new Set(candidatePaths)]
// nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal.path-join-resolve-traversal const writeResults = []
filePath = path.join(cwd, 'public/data', filename) 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'
})
} }
const dataDir = path.dirname(filePath)
// Sicherstellen, dass das Verzeichnis existiert
await fs.mkdir(dataDir, { recursive: true })
// Datei schreiben
await fs.writeFile(filePath, content, 'utf8')
return { return {
success: true, success: true,
message: 'Datei erfolgreich gespeichert' message: 'Datei erfolgreich gespeichert',
writtenTo: writeResults
} }
} catch (error) { } catch (error) {