From e8c66e65e67bbff2bb3c9b2ff51eb9acbdabb924 Mon Sep 17 00:00:00 2001 From: Philipp Dormann Date: Thu, 21 Nov 2024 18:27:45 +0100 Subject: [PATCH] wip --- bun.lockb | Bin 17861 -> 18212 bytes package.json | 1 + src/queues/email.queue.ts | 93 +++++++++++++++++++++++--------------- src/services/email.ts | 5 +- 4 files changed, 58 insertions(+), 41 deletions(-) diff --git a/bun.lockb b/bun.lockb index 0ece36a8977670e8908a841ca5ccaa2337cd7e5a..7dcebf1a7f4d392505bbe5c64b8828f879c132df 100644 GIT binary patch delta 2579 zcmd^B|8G-O6u$VPoJ~1+o4sZl1osocS!o)EhAOkb4 zF`D7`c?6+|af(C}qs9dU2?j*I2rObIgt~xYOi)pxi9scZeoohdKk*NEU(Ws9&$;K` zckX%T-un(dBey*-U-VVp*R$^Kjpp$UpIn~cO^p9gU1R$Fo1^7DdyH4LilJ3sTzI)b zq>8UM>S?^>9VWx|C?VqOI$Ba>Ich6-TSsSWOWR8EkQBlW-U=K6jHPipkjK@_-j7SH z6<{O59J2R2Uu>mOhyt+ol}jI7ykvRj0n>1AlUazHp&y&ZKeB}Ifgc01{xu*Ri+2G1 zz&XHD-~~9816)RJ(qjq0jS{dosaK9GJF5y|0fR3on|ZmzeqQ60l%DKo4xgRf_3pR+ z{y*jGQm&@W2Hmy|Bc@;^Ucxhoku69e3CSyE>BX-s4E2uC5&OqU5XHm z7-FC@W6076)&ORtK4Zvq9;}Ak#z~6PKnYV=&Z4AAmupGO(oOpfLQJKt=v0i9$O+&GQ%(`MUQNkQn>P_^uIV_(hEn8R2S=5{zHgzLlE9jo=Nu~?eTCr>) zI#ar1XbtyH#5x{K>E1zW6}RLQq*-;#C4?{xUlm%M9a3s?C}9oDPD-L{8$vq+U4qUT zLh@&d<>;0h^k^e=iwp6-?V9yM+B`y+Z&DKVdD2u}apqF98kUP_ zv#QHJN}?VjEmv1udDNVHi-wo+cS@E2qJ;)>LysYiL}9x3Mu;c~ciQeo$nzmr1lx~I z+us2338_8L)5OEZrOg?k7nKm?H796_BS8h*RYABgG7qG&8pz{nnA4d&r#FNa7e=<^ z^8XEoW;@=veV?!Ot0T9cne2M&o-1>5+fS~mxj4{R7#hFtqZ12{f7?C#wR=Ck z^VCb{``VVcwhX;{%6s!B$6$Y_XXc`=>FaXJCnBIyT+O=*G>|qbN=HVi)IBO9P4qcf zSH4Q#{D?Hu+WaV0*i||MW}!-Zlum;^XOGAnN`mzis5GS@B2`*n5T*J;m3{%sqltx4 z`T=ZfVMN;KN3cx}m1a32GM_d(qBNsOrN6-n=#HYOETlfv4*Cmq5jAO1snI*Ai%Awo zrITXCQOb6zw6{1S-DGh_X%|?#Ga@~-8*G_NCATXg%c#v2r6RXV17M>`b7N&--R_7S zOP_;vm0)Eh5jl?5mSANbtPHG@Dm_>k*mItUoIpvio>Hu=G$OtI>q{Szw8&tm7Y#eX#6oQgeUD<3ESK<&)*jU2>z=)`Wn2-JXduC%NF_i~e!;t{bwUfhJ zG5_j6!-du3A)M!#T+eXc6}&rGKOv1cA4L$t1y@0;Z&!Ny2mF-q&K`>sEVXsmj5i~n z4@D68C5j>NQ@BX=jV{PXi?``b5I!An7YRR2^LdJS@AIqcAiOa*@h(Q`s=_PEYk3gw zcOJ;5Wsp)x9t8J_U@zHw_8jLRUx>m=13qUZUn0Dh`LJOtJA~cfYsdz{<(P8AMK-^4 zK0D=s@SHqXIiwVl;iJ)J{g||l^&F`R2rr!@$x&i_JAD8xsH3O+H_-?FpB&4fMzrFY zTTfx1akRZs<#stRFC2;|wP3(%d11OD7Y2NIFu9}?RgFr$q>@09GjqSYPF)!aMR%}W z5O#Q3jG6+PY^CJ^&6)W-II^NF_shYZJ786P-O5h=0gqfpgMnFcC6!e#t}?H zjDD-ukheKO&6)YLSTJ|hz@hFFgV(K^NDpfA0>y$$mB0x4GyO1-l5$I*Q@L8P2fnX_!0Sj!u`@48y^No62;HYJ$m^5ADKJ_CRYV*(Nz@d!4`Jy2|hMUnkx=-$Y-aj`mOW^q;KzHTPc|AoGp@ delta 2457 zcmdT_YiyHM7(VA)yS3fE;AH3sFgmtw>tzK7{X_!}3MLRtVSx;8Ks6Pkguw3FzD^y*KA;DDM}kLy<=_*66+i>f1?({ikq#`RHPUH}VORjRg!ajj!o8{x zMlftVZljtLhe~p~CY=KYrCZ9C)SIHoE^_NM8KNlqUDT`7l+Pu_bRnZz5n?V@N})Bn zps@~Y7MPAkbU}SLn4g+;HOd8rV)~FwC3mVOy%bH=^qW(Jm_aw7Jdr}N)R6L13YDgX z6HH75D6g6ndNj)7hg!I+8l@>Z|sL}V_8)X@USR0?) zfzhb;7mQZN=jw6b^*oZ(C~DG-M{qwO>{X$DLr}S4qL?WpCy_f{GcLtt)j}nxN*9!G zQ!nNWc=UysOXH)-CUR$J&_pvd<3(s5x@VnB!_X`tx2nl*imIA&L8U`#NZM!^Q+4Dv zYw}f!nleLBXt9q%kAqQQgNRRwdnXxs)a85c(WrZRz5K(_NGxIcjbAPXbg zS2KYyGH3bQjg|iYuB`DtxiUJQ`59f!3dGA-8llNnmEN?5WjYOmT?d<&6P7CN%ZbqIIV#-&8$+{mBUGEK(&5~&%%odj zzkw~y3(IWk&5O`Gc`B*4u(Z-*TSVqiKl)rUj*ZAXT87?6$I*|aOnXGyDQb_<3cE^Y z?P2L4TYiM{@>SZJAC^w~1Z)tj+!2U zEK8`}iIX{TGB7tyap7bxoXizQpbvvxcc}yO3R@+eE6R{F=xR}k>_cxSS8<;4B_v=w z;`-pgjN%<8(}PH|EFI;$J<}eK5C6D3DoosH?4L;=O>%x{_H~{wO(0UA@S?FyLSx;@ z#PX{>w6UUy-t&}Yvdgm>hZZV>$fXfawltBxqS(q|#$m`I&q;y9%|X*EoL2rb4jvAy zZk~p0p-xXpIe&wYjep0X??Q8;A^8gwgm^geR*=s`I-RYsyLqFyg?PYm^5Da8O5(bK zBE|)ci=MDhW~IH3_sIJ}or$l{}Y>S%fEDc zC>S})c0Sl431v~g*OKbxVF7LR+U&^!KiE=e{_^~>qcHW}H}%2qzW2_PW#scU$t|?s zXR{|O?eBYPYcfAPa7vK@kJl4_G5W%13nVLZ!>Q+w9eM4HNhf`tN)NwFvUV3%b+n&W z7H+$*=_7~VW>41oMGMxSJ-zkZ`TKek&G*}653Te!DY10A;C~9WrY5jo?xn8+K7^=N zo$K(5WI;E!PCFf&-*rNf+pxE4k6*Bb&D6mj0=XI3?Xl3t>N07d-fHKZOUC%sw~hPN zVqISS-OqQyQ4HHUhmLpUztL_nA@#!T+6Lv3F30Y-_Edo53EO^HaOUHuhm6m-66v~p VCS?UXseh`S27?s?{>eAZe*#$Ew#)zk diff --git a/package.json b/package.json index 583df16..58cf923 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "bullmq": "5.28.1", "hono": "4.6.11", "ioredis": "5.4.1", + "nodemailer": "^6.9.16", "zod": "3.23.8" }, "devDependencies": { diff --git a/src/queues/email.queue.ts b/src/queues/email.queue.ts index a24b973..a4a4762 100644 --- a/src/queues/email.queue.ts +++ b/src/queues/email.queue.ts @@ -1,61 +1,80 @@ -import { Queue, Worker, QueueEvents } from 'bullmq' -import { EmailService } from '../services/email' -import { config } from '../config/env' -import Redis from 'ioredis' +import { createTransport } from "nodemailer"; +import { Queue, Worker, QueueEvents } from "bullmq"; +import { config } from "../config/env"; +import Redis from "ioredis"; interface EmailJob { - to: string - subject: string - html: string - text: string + to: string; + subject: string; + html: string; + text: string; } -const connection = new Redis(config.redis.url, { - maxRetriesPerRequest: null -}) +export const QUEUE_NAME = "{lfkmailqueue}"; -export const emailQueue = new Queue('email', { +const connection = new Redis(config.redis.url, { + maxRetriesPerRequest: null, +}); + +export const emailQueue = new Queue(QUEUE_NAME, { connection, defaultJobOptions: { attempts: 3, backoff: { - type: 'exponential', - delay: 1000 - } + type: "exponential", + delay: 1000, + }, + }, +}); + +const transporter = createTransport({ + host: config.smtp.host, + port: config.smtp.port, + auth: { + user: config.smtp.user, + pass: config.smtp.pass } }) const worker = new Worker( - 'email', + QUEUE_NAME, async (job) => { - const emailService = new EmailService() - await emailService.sendEmail(job.data) + // const emailService = new EmailService(); + // await emailService.sendEmail(job.data); + console.log(job.data); + await transporter.sendMail({ + from: config.email.from, + to: job.data.to, + subject: job.data.subject, + text: job.data.text, + html: job.data.html + }) }, - { + { connection, concurrency: 5, limiter: { - max: 100, - duration: 1000 * 60 // 100 emails per minute - } + max: 250, + duration: 1000 * 60, // 250 emails per minute + }, } -) +); -const queueEvents = new QueueEvents('email', { connection }) +const queueEvents = new QueueEvents(QUEUE_NAME, { connection }); -worker.on('completed', (job) => { - console.log(`Email job ${job.id} completed`) -}) +worker.on("completed", (job) => { + console.log(`Email job ${job.id} completed`); +}); -worker.on('failed', (job, error) => { - console.error(`Email job ${job?.id} failed:`, error) -}) +worker.on("failed", (job, error) => { + console.error(`Email job ${job?.id} failed:`, error); +}); -queueEvents.on('waiting', ({ jobId }) => { - console.log(`Job ${jobId} is waiting`) -}) +queueEvents.on("waiting", ({ jobId }) => { + // console.log(`Job ${jobId} is waiting`) +}); -process.on('SIGTERM', async () => { - await worker.close() - await connection.quit() -}) \ No newline at end of file +process.on("SIGTERM", async () => { + await worker.close(); + await connection.quit(); +}); diff --git a/src/services/email.ts b/src/services/email.ts index 3e45851..3353087 100644 --- a/src/services/email.ts +++ b/src/services/email.ts @@ -18,11 +18,8 @@ export class EmailService { // Add to queue instead of sending directly await emailQueue.add('send-email', email, { removeOnComplete: true, - removeOnFail: 1000 + // removeOnFail: 1000 }) - - // Log for development - await Bun.write('emails.log', JSON.stringify(email) + '\n', { append: true }) } async getQueueStatus() {