diff --git a/src/services/ai-kurzy/certificate-email.service.js b/src/services/ai-kurzy/certificate-email.service.js index fcefd46..e4cf9c8 100644 --- a/src/services/ai-kurzy/certificate-email.service.js +++ b/src/services/ai-kurzy/certificate-email.service.js @@ -1,5 +1,6 @@ import fs from 'fs/promises'; import path from 'path'; +import axios from 'axios'; import { db } from '../../config/database.js'; import { prilohy, registracie, ucastnici, kurzy, emailAccounts, userEmailAccounts } from '../../db/schema.js'; import { eq, and } from 'drizzle-orm'; @@ -152,6 +153,35 @@ V prípade otázok nás neváhajte kontaktovať. `.trim(); }; +/** + * Upload blob via HTTP POST to JMAP upload endpoint + */ +const uploadBlobToJmap = async (jmapConfig, fileBuffer, mimeType) => { + const jmapServer = process.env.JMAP_SERVER || 'https://mail.truemail.sk/jmap/'; + // Truemail upload URL format + const uploadUrl = `${jmapServer}upload/${jmapConfig.accountId}/`; + + logger.info(`Nahrávam súbor na JMAP server: ${uploadUrl}`); + + const response = await axios.post(uploadUrl, fileBuffer, { + auth: { + username: jmapConfig.username, + password: jmapConfig.password, + }, + headers: { + 'Content-Type': mimeType, + }, + }); + + if (!response.data?.blobId) { + logger.error('Upload nevrátil blobId', response.data); + throw new Error('Nepodarilo sa nahrať prílohu na server'); + } + + logger.info(`Súbor nahraný, blobId: ${response.data.blobId}`); + return response.data.blobId; +}; + /** * Send email with HTML content and PDF attachment via JMAP */ @@ -166,11 +196,14 @@ const sendEmailWithAttachment = async (jmapConfig, to, subject, htmlBody, textBo throw new Error('Priečinok Odoslané nebol nájdený'); } - // Read attachment file and encode to base64 + // Read attachment file const fileBuffer = await fs.readFile(attachment.path); - const base64Content = fileBuffer.toString('base64'); + const mimeType = attachment.mimeType || 'application/pdf'; - // Create email with attachment + // Upload blob via HTTP POST (this is the correct JMAP way) + const blobId = await uploadBlobToJmap(jmapConfig, fileBuffer, mimeType); + + // Create email with uploaded blob const createResponse = await jmapRequest(jmapConfig, [ [ 'Email/set', @@ -181,29 +214,36 @@ const sendEmailWithAttachment = async (jmapConfig, to, subject, htmlBody, textBo mailboxIds: { [sentMailbox.id]: true, }, - keywords: { - $draft: true, - }, from: [{ email: jmapConfig.username }], to: [{ email: to }], subject: subject, - htmlBody: [{ partId: 'html', type: 'text/html' }], - textBody: [{ partId: 'text', type: 'text/plain' }], - attachments: [ - { - blobId: null, // Will be set via bodyValues - type: attachment.mimeType || 'application/pdf', - name: attachment.filename, - disposition: 'attachment', - }, - ], + bodyStructure: { + type: 'multipart/mixed', + subParts: [ + { + type: 'multipart/alternative', + subParts: [ + { + type: 'text/plain', + partId: 'text', + }, + { + type: 'text/html', + partId: 'html', + }, + ], + }, + { + type: mimeType, + name: attachment.filename, + disposition: 'attachment', + blobId: blobId, + }, + ], + }, bodyValues: { - html: { - value: htmlBody, - }, - text: { - value: textBody, - }, + html: { value: htmlBody }, + text: { value: textBody }, }, }, }, @@ -212,148 +252,12 @@ const sendEmailWithAttachment = async (jmapConfig, to, subject, htmlBody, textBo ], ]); - // Check if we need to upload blob first (for some JMAP servers) const createResult = createResponse.methodResponses[0][1]; - - if (createResult.notCreated?.draft) { - // Try alternative approach - upload blob first, then create email - logger.info('Skúšam alternatívny prístup s nahraním prílohy'); - - // Upload blob - const uploadResponse = await jmapRequest(jmapConfig, [ - [ - 'Blob/upload', - { - accountId: jmapConfig.accountId, - create: { - attachment1: { - data: [{ 'data:asBase64': base64Content }], - type: attachment.mimeType || 'application/pdf', - }, - }, - }, - 'blob1', - ], - ]); - - const blobId = uploadResponse.methodResponses[0][1]?.created?.attachment1?.blobId; - - if (!blobId) { - // Try yet another approach - inline base64 - logger.info('Skúšam inline base64 prílohu'); - - const inlineResponse = await jmapRequest(jmapConfig, [ - [ - 'Email/set', - { - accountId: jmapConfig.accountId, - create: { - draft: { - mailboxIds: { - [sentMailbox.id]: true, - }, - keywords: { - $draft: true, - }, - from: [{ email: jmapConfig.username }], - to: [{ email: to }], - subject: subject, - bodyStructure: { - type: 'multipart/mixed', - subParts: [ - { - type: 'multipart/alternative', - subParts: [ - { - type: 'text/plain', - partId: 'text', - }, - { - type: 'text/html', - partId: 'html', - }, - ], - }, - { - type: attachment.mimeType || 'application/pdf', - name: attachment.filename, - disposition: 'attachment', - partId: 'attachment', - }, - ], - }, - bodyValues: { - html: { value: htmlBody }, - text: { value: textBody }, - attachment: { value: base64Content, isEncodingProblem: false }, - }, - }, - }, - }, - 'set2', - ], - ]); - - const inlineResult = inlineResponse.methodResponses[0][1]; - const createdEmailId = inlineResult.created?.draft?.id; - - if (!createdEmailId) { - logger.error('Nepodarilo sa vytvoriť email s prílohou', inlineResult.notCreated?.draft); - throw new Error('Nepodarilo sa vytvoriť email s prílohou'); - } - - // Submit the email - return await submitEmail(jmapConfig, createdEmailId); - } - - // Create email with uploaded blob - const emailWithBlobResponse = await jmapRequest(jmapConfig, [ - [ - 'Email/set', - { - accountId: jmapConfig.accountId, - create: { - draft: { - mailboxIds: { - [sentMailbox.id]: true, - }, - from: [{ email: jmapConfig.username }], - to: [{ email: to }], - subject: subject, - htmlBody: [{ partId: 'html', type: 'text/html' }], - textBody: [{ partId: 'text', type: 'text/plain' }], - attachments: [ - { - blobId: blobId, - type: attachment.mimeType || 'application/pdf', - name: attachment.filename, - disposition: 'attachment', - }, - ], - bodyValues: { - html: { value: htmlBody }, - text: { value: textBody }, - }, - }, - }, - }, - 'set3', - ], - ]); - - const createdEmailId = emailWithBlobResponse.methodResponses[0][1].created?.draft?.id; - - if (!createdEmailId) { - throw new Error('Nepodarilo sa vytvoriť email s prílohou'); - } - - return await submitEmail(jmapConfig, createdEmailId); - } - const createdEmailId = createResult.created?.draft?.id; if (!createdEmailId) { - throw new Error('Nepodarilo sa vytvoriť email'); + logger.error('Nepodarilo sa vytvoriť email s prílohou', createResult.notCreated?.draft); + throw new Error('Nepodarilo sa vytvoriť email s prílohou'); } return await submitEmail(jmapConfig, createdEmailId);