package com.android.trisolarisserver.component import org.apache.pdfbox.pdmodel.PDDocument import org.apache.pdfbox.pdmodel.PDPage import org.apache.pdfbox.pdmodel.PDPageContentStream import org.apache.pdfbox.pdmodel.common.PDRectangle import org.apache.pdfbox.pdmodel.font.PDType1Font import org.springframework.beans.factory.annotation.Value import org.springframework.stereotype.Component import java.nio.file.Files import java.nio.file.Path import java.nio.file.Paths import java.time.OffsetDateTime import java.util.UUID @Component class EmailStorage( @Value("\${storage.emails.root:/home/androidlover5842/docs/emails}") private val rootPath: String ) { fun storePdf(propertyId: UUID?, messageId: String?, subject: String?, body: String): String { val dir = if (propertyId != null) { Paths.get(rootPath, propertyId.toString()) } else { Paths.get(rootPath, "unassigned") } Files.createDirectories(dir) val safeName = (messageId ?: UUID.randomUUID().toString()).replace(Regex("[^A-Za-z0-9._-]"), "_") val fileName = "${safeName}_${OffsetDateTime.now().toEpochSecond()}.pdf" val path = dir.resolve(fileName).normalize() val tmp = dir.resolve("${fileName}.tmp").normalize() val document = PDDocument() val page = PDPage(PDRectangle.LETTER) document.addPage(page) val header = "Subject: ${subject ?: ""}\n\n" writeText(document, page, header + body) document.save(tmp.toFile()) document.close() atomicMove(tmp, path) return path.toString() } private fun writeText(document: PDDocument, firstPage: PDPage, text: String) { var page = firstPage var y = 730f val marginX = 50f val lineHeight = 14f val maxLines = 48 var linesOnPage = 0 var content = PDPageContentStream(document, page) content.beginText() content.setFont(PDType1Font.HELVETICA, 11f) content.newLineAtOffset(marginX, y) fun newLine() { content.newLineAtOffset(0f, -lineHeight) y -= lineHeight linesOnPage++ if (linesOnPage >= maxLines) { content.endText() content.close() page = PDPage(PDRectangle.LETTER) document.addPage(page) content = PDPageContentStream(document, page) content.beginText() content.setFont(PDType1Font.HELVETICA, 11f) y = 730f linesOnPage = 0 content.newLineAtOffset(marginX, y) } } val lines = text.split("\n") for (line in lines) { val chunks = line.chunked(90) for (chunk in chunks) { content.showText(chunk) newLine() } } content.endText() content.close() } fun storeEml(propertyId: UUID?, messageId: String?, rawBytes: ByteArray): String { val dir = if (propertyId != null) { Paths.get(rootPath, propertyId.toString(), "raw") } else { Paths.get(rootPath, "unassigned", "raw") } Files.createDirectories(dir) val safeName = (messageId ?: UUID.randomUUID().toString()).replace(Regex("[^A-Za-z0-9._-]"), "_") val fileName = "${safeName}_${OffsetDateTime.now().toEpochSecond()}.eml" val path = dir.resolve(fileName).normalize() val tmp = dir.resolve("${fileName}.tmp").normalize() Files.write(tmp, rawBytes) atomicMove(tmp, path) return path.toString() } fun storeUploadedPdf(propertyId: UUID, originalName: String?, bytes: ByteArray): String { val dir = Paths.get(rootPath, propertyId.toString(), "manual") Files.createDirectories(dir) val safeName = (originalName ?: UUID.randomUUID().toString()).replace(Regex("[^A-Za-z0-9._-]"), "_") val fileName = "${safeName}_${OffsetDateTime.now().toEpochSecond()}.pdf" val path = dir.resolve(fileName).normalize() val tmp = dir.resolve("${fileName}.tmp").normalize() Files.write(tmp, bytes) atomicMove(tmp, path) return path.toString() } private fun atomicMove(tmp: Path, target: Path) { try { Files.move(tmp, target, java.nio.file.StandardCopyOption.ATOMIC_MOVE, java.nio.file.StandardCopyOption.REPLACE_EXISTING) } catch (_: Exception) { Files.move(tmp, target, java.nio.file.StandardCopyOption.REPLACE_EXISTING) } } }