import { PutObjectCommand } from "@aws-sdk/client-s3" import { s3 } from "./s3" import { secrets } from "./secrets" // Drizzle schema import { files } from "../../db/schema" import { eq } from "drizzle-orm" import { FastifyInstance } from "fastify" import { storeExtractedTextForFile } from "./documentText" export const saveFile = async ( server: FastifyInstance, tenant: number, messageId: string | number | null, // Typ angepasst (oft null bei manueller Gen) attachment: any, // Kann File, Buffer oder Mailparser-Objekt sein folder: string | null, type: string | null, other: Record = {} ) => { try { const { filename: providedFilename, filesize: _providedFilesize, mimeType: providedMimeType, ...dbFields } = other // --------------------------------------------------- // 1️⃣ FILE ENTRY ANLEGEN // --------------------------------------------------- const insertRes = await server.db .insert(files) .values({ tenant, folder, type, ...dbFields }) .returning() const created = insertRes?.[0] if (!created) { console.error("File creation failed (no row returned)") return null } // Name ermitteln (Fallback Logik) // Wenn attachment ein Buffer ist, muss der Name in 'other' stehen oder generiert werden const filename = attachment.filename || providedFilename || `${created.id}.pdf` // --------------------------------------------------- // 2️⃣ BODY & CONTENT TYPE ERMITTELN // --------------------------------------------------- let body: Buffer | Uint8Array | string let contentType = providedMimeType || "application/octet-stream" if (Buffer.isBuffer(attachment)) { // FALL 1: RAW BUFFER (von finishManualGeneration) body = attachment // ContentType wurde oben schon über 'type' Parameter gesetzt (z.B. application/pdf) } else if (typeof File !== "undefined" && attachment instanceof File) { // FALL 2: BROWSER FILE body = Buffer.from(await attachment.arrayBuffer()) contentType = attachment.type || contentType } else if (attachment.content) { // FALL 3: MAILPARSER OBJECT body = attachment.content contentType = attachment.contentType || contentType } else { console.error("saveFile: Unknown attachment format") return null } // --------------------------------------------------- // 3️⃣ S3 UPLOAD // --------------------------------------------------- const key = `${tenant}/filesbyid/${created.id}/${filename}` await s3.send( new PutObjectCommand({ Bucket: secrets.S3_BUCKET, Key: key, Body: body, ContentType: contentType, ContentLength: body.length // <--- WICHTIG: Behebt den AWS Fehler }) ) // --------------------------------------------------- // 4️⃣ PATH IN DB SETZEN // --------------------------------------------------- await server.db .update(files) .set({ path: key, mimeType: contentType, name: filename, size: body.length }) .where(eq(files.id, created.id)) await storeExtractedTextForFile( server, created.id, Buffer.isBuffer(body) ? body : Buffer.from(body), contentType, filename ) console.log(`File saved: ${key}`) return { id: created.id, key } } catch (err) { console.error("saveFile error:", err) return null } }