Files
harheimertc/server/utils/pdf-generator-service.js
2025-12-20 14:49:57 +01:00

107 lines
3.0 KiB
JavaScript

/**
* PDF Generator Service - Main service for PDF generation
* Clean Code: Facade Pattern, Single Responsibility
*/
import { PDFDocument } from 'pdf-lib'
import fs from 'fs/promises'
import path from 'path'
import { fillPdfForm } from './pdf-form-filler.js'
/**
* PDF Generation Result
*/
export class PDFGenerationResult {
constructor(success, pdfBuffer, filename, error = null) {
this.success = success
this.pdfBuffer = pdfBuffer
this.filename = filename
this.error = error
}
}
/**
* PDF Generator Service
*/
export class PDFGeneratorService {
constructor() {
this.templatePath = path.join(process.cwd(), 'server', 'templates', 'mitgliedschaft-fillable.pdf')
this.fallbackTemplatePath = path.join(process.cwd(), 'server', 'templates', 'Aufnahmeantrag 2025.pdf')
}
/**
* Generates PDF from template with form data
* @param {Object} data - Form data
* @returns {Promise<PDFGenerationResult>} Generation result
*/
async generateFromTemplate(data) {
try {
const templatePath = await this.getTemplatePath()
const templateBytes = await fs.readFile(templatePath)
const pdfDoc = await PDFDocument.load(templateBytes)
const form = pdfDoc.getForm()
// Fill form fields
await fillPdfForm(pdfDoc, form, data)
// Don't flatten form to keep fields editable
// form.flatten() makes fields non-editable
// Generate filename
const filename = this.generateFilename(data)
// Save PDF
const pdfBytes = await pdfDoc.save()
return new PDFGenerationResult(true, Buffer.from(pdfBytes), filename)
} catch (error) {
console.error('Template PDF generation failed:', error.message)
return new PDFGenerationResult(false, null, null, error.message)
}
}
/**
* Gets the appropriate template path
* @returns {Promise<string>} Template path
*/
async getTemplatePath() {
try {
await fs.access(this.templatePath)
return this.templatePath
} catch (error) {
try {
await fs.access(this.fallbackTemplatePath)
return this.fallbackTemplatePath
} catch (fallbackError) {
throw new Error('No PDF template found')
}
}
}
/**
* Generates filename for PDF
* @param {Object} data - Form data
* @returns {string} Filename
*/
generateFilename(data) {
const timestamp = Date.now()
const name = `${data.nachname || 'Unbekannt'}_${data.vorname || 'Unbekannt'}`
return `beitrittserklärung_${timestamp}.pdf`
}
/**
* Saves PDF to file system
* @param {Buffer} pdfBuffer - PDF buffer
* @param {string} filename - Filename
* @param {string} uploadDir - Upload directory
* @returns {Promise<string>} File path
*/
async savePDF(pdfBuffer, filename, uploadDir) {
// nosemgrep: javascript.lang.security.audit.path-traversal.path-join-resolve-traversal.path-join-resolve-traversal
const filePath = path.join(uploadDir, filename)
await fs.writeFile(filePath, pdfBuffer)
return filePath
}
}