Générateur de portfolios PDF Figures Libres (app Nuxt autonome)
App Nuxt 3 + Nuxt UI qui lit les projets du CMS Grav et compose des portfolios A3 paysage exportés en PDF (gabarit book_v3) : composeur WYSIWYG, templates cover/toc/projet/grille/page libre, pipeline Playwright + Ghostscript, gel du contenu à la génération, partage public opt-in. Dépôt autonome : Dockerfile (base Playwright + Ghostscript + ffmpeg + rsync) inclus. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
22
server/api/exports/[file].get.ts
Normal file
22
server/api/exports/[file].get.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { createReadStream } from 'node:fs'
|
||||
import { stat } from 'node:fs/promises'
|
||||
import { join, normalize } from 'node:path'
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const file = decodeURIComponent(getRouterParam(event, 'file') ?? '')
|
||||
const exportsDir = join(useRuntimeConfig(event).dataDir, 'exports')
|
||||
const abs = normalize(join(exportsDir, file))
|
||||
if (!abs.startsWith(normalize(exportsDir) + '/') || !abs.endsWith('.pdf')) {
|
||||
throw createError({ statusCode: 400, statusMessage: 'Fichier invalide' })
|
||||
}
|
||||
let s
|
||||
try {
|
||||
s = await stat(abs)
|
||||
} catch {
|
||||
throw createError({ statusCode: 404, statusMessage: 'Export introuvable' })
|
||||
}
|
||||
setHeader(event, 'Content-Type', 'application/pdf')
|
||||
setHeader(event, 'Content-Length', s.size)
|
||||
setHeader(event, 'Content-Disposition', `attachment; filename="${file.replace(/"/g, '')}"`)
|
||||
return sendStream(event, createReadStream(abs))
|
||||
})
|
||||
Reference in New Issue
Block a user