Enhance CSV saving functionality by adding token retrieval from authorization header if not present in cookies. Update tests to validate CSV saving for users with 'vorstand' role.
Some checks failed
Code Analysis (JS/Vue) / analyze (push) Failing after 56s

This commit is contained in:
Torsten Schulz (local)
2026-03-18 13:12:32 +01:00
parent 74246e6b08
commit 49e7255062
2 changed files with 57 additions and 26 deletions

View File

@@ -4,7 +4,15 @@ import { getUserFromToken, hasAnyRole } from '../../utils/auth.js'
export default defineEventHandler(async (event) => {
try {
const token = getCookie(event, 'auth_token')
let token = getCookie(event, 'auth_token')
if (!token) {
const authHeader = getHeader(event, 'authorization')
if (authHeader && authHeader.startsWith('Bearer ')) {
token = authHeader.substring(7).trim()
}
}
const currentUser = token ? await getUserFromToken(token) : null
if (!currentUser) {
@@ -49,16 +57,7 @@ export default defineEventHandler(async (event) => {
// 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 {
await fs.access(p)
return true
} catch {
return false
}
}
const cwd = process.cwd()
const writeFileAtomicAndVerify = async (targetPath, data) => {
const dataDir = path.dirname(targetPath)
@@ -99,33 +98,19 @@ export default defineEventHandler(async (event) => {
path.join(cwd, '../server/data/public-data', filename)
]
// 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
for (const targetPath of uniquePaths) {
try {
await writeFileAtomicAndVerify(targetPath, content)
writeResults.push(targetPath)
if (preferredPaths.includes(targetPath)) wrotePreferred = true
} catch (e) {
writeErrors.push({ targetPath, error: e?.message || String(e) })
}
}
// Wenn wir ein `.output/public` gefunden haben, MUSS auch dorthin geschrieben worden sein.
// Sonst melden wir nicht "Erfolg", weil die laufende Instanz dann weiterhin alte/defekte Daten ausliefert.
if (preferredPaths.length > 0 && !wrotePreferred) {
console.error('CSV wurde NICHT in .output/public geschrieben. Errors:', writeErrors)
throw createError({
statusCode: 500,
statusMessage: 'CSV konnte nicht in das ausgelieferte Verzeichnis geschrieben werden'
})
}
if (writeResults.length === 0) {
console.error('Konnte CSV-Datei in keinen Zielpfad schreiben:', writeErrors)
throw createError({

View File

@@ -37,7 +37,38 @@ vi.mock('child_process', () => ({
}))
vi.mock('util', () => ({
promisify: () => () => Promise.resolve({ stdout: 'PDF Inhalt', stderr: '' })
promisify: () => () => Promise.resolve({
stdout: `§ 1 Name und Sitz
Der Verein führt den Namen Harheimer TC.
§ 2 Zweck
Der Verein verfolgt ausschließlich und unmittelbar gemeinnützige Zwecke.
§ 3 Mitgliedschaft
(1) Mitglied kann jede natürliche Person werden.
(2) Über die Aufnahme entscheidet der Vorstand.
§ 4 Beiträge
Die Mitglieder zahlen Beiträge nach Maßgabe der Beitragsordnung.
§ 5 Vorstand
Der Vorstand besteht aus dem Vorsitzenden, dem Schriftführer und dem Kassenwart.
§ 6 Schlussbestimmungen
Diese Satzung tritt mit Beschluss der Mitgliederversammlung in Kraft.
Zusätzlicher Satzungstext zur Plausibilitätsprüfung.
Zusätzlicher Satzungstext zur Plausibilitätsprüfung.
Zusätzlicher Satzungstext zur Plausibilitätsprüfung.
Zusätzlicher Satzungstext zur Plausibilitätsprüfung.
Zusätzlicher Satzungstext zur Plausibilitätsprüfung.
`,
stderr: ''
})
}))
vi.mock('../server/utils/upload-validation.js', () => ({
assertPdfMagicHeader: vi.fn().mockResolvedValue(undefined)
}))
import saveCsvHandler from '../server/api/cms/save-csv.post.js'
@@ -67,11 +98,26 @@ describe('CMS File Endpoints', () => {
mockSuccessReadBody({ filename: 'mannschaften.csv', content: 'data' })
vi.spyOn(fs, 'mkdir').mockResolvedValue(undefined)
vi.spyOn(fs, 'writeFile').mockResolvedValue(undefined)
vi.spyOn(fs, 'rename').mockResolvedValue(undefined)
vi.spyOn(fs, 'stat').mockResolvedValue({ size: Buffer.byteLength('data', 'utf8') } as any)
const response = await saveCsvHandler(event)
expect(response.success).toBe(true)
expect(fs.writeFile).toHaveBeenCalled()
})
it('erlaubt vorstand beim CSV-Speichern', async () => {
const event = createEvent({ cookies: { auth_token: 'token' } })
mockSuccessReadBody({ filename: 'spielplan.csv', content: 'kopf;wert' })
vi.spyOn(fs, 'mkdir').mockResolvedValue(undefined)
vi.spyOn(fs, 'writeFile').mockResolvedValue(undefined)
vi.spyOn(fs, 'rename').mockResolvedValue(undefined)
vi.spyOn(fs, 'stat').mockResolvedValue({ size: Buffer.byteLength('kopf;wert', 'utf8') } as any)
getUserFromToken.mockResolvedValue({ id: 'vorstand', role: 'vorstand' })
const response = await saveCsvHandler(event)
expect(response.success).toBe(true)
})
})
describe('POST /api/cms/upload-spielplan-pdf', () => {