From 9aefa5d08fa454576297fdf6c8a5703881e6b86e Mon Sep 17 00:00:00 2001 From: florianfederspiel Date: Sun, 28 Sep 2025 17:44:31 +0200 Subject: [PATCH] Added E-Mail Sending Features --- src/routes/emailAsUser.ts | 170 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 162 insertions(+), 8 deletions(-) diff --git a/src/routes/emailAsUser.ts b/src/routes/emailAsUser.ts index 9755e45..6977ede 100644 --- a/src/routes/emailAsUser.ts +++ b/src/routes/emailAsUser.ts @@ -1,11 +1,115 @@ -import { FastifyInstance } from "fastify"; -import {createInvoicePDF} from "../utils/pdf"; -import {useNextNumberRangeNumber} from "../utils/functions"; -import {sendMailAsUser} from "../utils/emailengine"; -import {subtle} from "node:crypto"; +import nodemailer from "nodemailer" +import { FastifyInstance } from "fastify"; +import {sendMailAsUser} from "../utils/emailengine"; +import {encrypt, decrypt} from "../utils/crypt" +import {secrets} from "../utils/secrets"; export default async function emailAsUserRoutes(server: FastifyInstance) { - server.post("/emailasuser/send", async (req, reply) => { + + // Create E-Mail Account + server.post("/email/accounts/", async (req, reply) => { + if (!req.user?.tenant_id) { + return reply.code(400).send({ error: "No tenant selected" }); + } + + const body = req.body as Record; + + let createData = { + user_id: req.user.user_id, + email_encrypted: encrypt(body.email), + password_encrypted: encrypt(body.password), + tenant_id: req.user.tenant_id, + smtp_host_encrypted: encrypt(body.smtp_host), + smtp_port: body.smtp_port, + smtp_ssl: body.smtp_ssl, + type: "mail", + imap_host_encrypted: encrypt(body.imap_host), + imap_port: body.imap_port, + imap_ssl: body.imap_ssl, + } + + + const { data, error } = await server.supabase + .from("user_credentials") + .insert(createData) + .select("*") + .single(); + + if (error) { + return reply.code(400).send({ error: error.message }); + } + + return data; + }); + + server.get("/email/accounts/:id?", async (req, reply) => { + if (!req.user?.tenant_id) { + return reply.code(400).send({ error: "No tenant selected" }); + } + + const { id } = req.params as { id: string }; + + if(id) { + let returnData = {} + // @ts-ignore + const { data, error } = await server.supabase + .from("user_credentials") + .select("id, email_encrypted, smtp_host_encrypted, smtp_port, smtp_ssl, imap_host_encrypted, imap_port, imap_ssl, user_id, tenant_id") + .eq("id", id) + .eq("tenant_id", req.user.tenant_id) + .eq("type", "mail") + .single(); + + if (error || !data) { + return reply.code(404).send({ error: "Not found" }); + } else { + Object.keys(data).forEach((key) => { + if(key.includes("encrypted")){ + returnData[key.substring(0,key.length-10)] = decrypt(data[key]) + } else { + returnData[key] = data[key] + } + }) + } + } else { + + const { data, error } = await server.supabase + .from("user_credentials") + .select("id, email_encrypted, user_id, tenant_id") + .eq("tenant_id", req.user.tenant_id) + .eq("type", "mail") + + let accounts = [] + data.forEach(item => { + let temp = {} + Object.keys(item).forEach((key) => { + if(key.includes("encrypted")){ + temp[key.substring(0,key.length-10)] = decrypt(item[key]) + } else { + temp[key] = item[key] + } + }) + accounts.push(temp) + }) + + + + return accounts + } + + + + + + return returnData; + }); + + + + + + + server.post("/email/send", async (req, reply) => { const body = req.body as { to: string cc?: string @@ -18,10 +122,60 @@ export default async function emailAsUserRoutes(server: FastifyInstance) { } try { - reply.send(await sendMailAsUser(body.to,body.subject,body.html,body.text,body.account,body.cc,body.bcc,body.attachments)) + + let accountData = {} + // @ts-ignore + const { data, error } = await server.supabase + .from("user_credentials") + .select("id, email_encrypted,password_encrypted, smtp_host_encrypted, smtp_port, smtp_ssl, user_id, tenant_id") + .eq("id", body.account) + .eq("tenant_id", req.user.tenant_id) + .eq("type", "mail") + .single(); + + if (error || !data) { + return reply.code(404).send({ error: "Not found" }); + } else { + Object.keys(data).forEach((key) => { + if(key.includes("encrypted")){ + accountData[key.substring(0,key.length-10)] = decrypt(data[key]) + } else { + accountData[key] = data[key] + } + }) + } + + + const transporter = nodemailer.createTransport({ + host: accountData.smtp_host, + port: accountData.smtp_port, + secure: accountData.smtp_ssl, + auth: { + user: accountData.email, + pass: accountData.password, + }, + }) + + const info = await transporter.sendMail({ + from: accountData.email, + to: body.to, + cc: body.cc ? body.cc : undefined, + bcc: body.bcc ? body.bcc : undefined, + subject: body.subject, + html: body.html ? body.html : undefined, + text: body.text, + attachments: body.attachments ? body.attachments : undefined, + }) + + + if(info.response.includes("OK")){ + reply.send({success: true}) + }{ + reply.status(500) + } + } catch (err) { - console.log(err) reply.code(500).send({ error: "Failed to send E-Mail as User" }) } })