106 lines
3.8 KiB
TypeScript
106 lines
3.8 KiB
TypeScript
// modules/helpdesk/helpdesk.inbound.email.ts
|
||
import { FastifyPluginAsync } from 'fastify'
|
||
import { createConversation } from '../modules/helpdesk/helpdesk.conversation.service.js'
|
||
import { addMessage } from '../modules/helpdesk/helpdesk.message.service.js'
|
||
import { getOrCreateContact } from '../modules/helpdesk/helpdesk.contact.service.js'
|
||
import { findCustomerOrContactByEmailOrDomain } from "../utils/helpers";
|
||
import { eq } from "drizzle-orm";
|
||
import { helpdesk_conversations, helpdesk_messages } from "../../db/schema";
|
||
|
||
// -------------------------------------------------------------
|
||
// 📧 Interne M2M-Route für eingehende E-Mails
|
||
// -------------------------------------------------------------
|
||
|
||
const helpdeskInboundEmailRoutes: FastifyPluginAsync = async (server) => {
|
||
server.post('/helpdesk/inbound-email', async (req, res) => {
|
||
|
||
const {
|
||
tenant_id,
|
||
channel_id,
|
||
from,
|
||
subject,
|
||
text,
|
||
message_id,
|
||
in_reply_to,
|
||
} = req.body as {
|
||
tenant_id: number
|
||
channel_id: string
|
||
from: {address: string, name: string}
|
||
subject: string
|
||
text: string
|
||
message_id: string
|
||
in_reply_to: string
|
||
}
|
||
|
||
if (!tenant_id || !from?.address || !text) {
|
||
return res.status(400).send({ error: 'Invalid payload' })
|
||
}
|
||
|
||
server.log.info(`[InboundEmail] Neue Mail von ${from.address} für Tenant ${tenant_id}`)
|
||
|
||
// 1️⃣ Kunde & Kontakt ermitteln
|
||
const { customer, contact: contactPerson } =
|
||
(await findCustomerOrContactByEmailOrDomain(server, from.address, tenant_id)) || {}
|
||
|
||
// 2️⃣ Kontakt anlegen oder laden
|
||
const contact = await getOrCreateContact(server, tenant_id, {
|
||
email: from.address,
|
||
display_name: from.name || from.address,
|
||
customer_id: customer,
|
||
contact_id: contactPerson,
|
||
})
|
||
|
||
// 3️⃣ Konversation anhand In-Reply-To suchen
|
||
let conversationId: string | null = null
|
||
if (in_reply_to) {
|
||
const msg = await server.db
|
||
.select({ conversationId: helpdesk_messages.conversationId })
|
||
.from(helpdesk_messages)
|
||
.where(eq(helpdesk_messages.externalMessageId, in_reply_to))
|
||
.limit(1)
|
||
conversationId = msg[0]?.conversationId || null
|
||
}
|
||
|
||
// 4️⃣ Neue Konversation anlegen falls keine existiert
|
||
let conversation
|
||
if (!conversationId) {
|
||
conversation = await createConversation(server, {
|
||
tenant_id,
|
||
contact,
|
||
channel_instance_id: channel_id,
|
||
subject: subject || '(kein Betreff)',
|
||
customer_id: customer,
|
||
contact_person_id: contactPerson,
|
||
})
|
||
conversationId = conversation.id
|
||
} else {
|
||
const rows = await server.db
|
||
.select()
|
||
.from(helpdesk_conversations)
|
||
.where(eq(helpdesk_conversations.id, conversationId))
|
||
.limit(1)
|
||
conversation = rows[0]
|
||
}
|
||
|
||
// 5️⃣ Nachricht speichern
|
||
await addMessage(server, {
|
||
tenant_id,
|
||
conversation_id: conversationId,
|
||
direction: 'incoming',
|
||
payload: { type: 'text', text },
|
||
external_message_id: message_id,
|
||
raw_meta: { source: 'email' },
|
||
})
|
||
|
||
server.log.info(`[InboundEmail] Ticket ${conversationId} gespeichert`)
|
||
|
||
return res.status(201).send({
|
||
success: true,
|
||
conversation_id: conversationId,
|
||
ticket_number: conversation?.ticket_number || conversation?.ticketNumber,
|
||
})
|
||
})
|
||
}
|
||
|
||
export default helpdeskInboundEmailRoutes
|