- Rename billFwd* to billsNotification* for clarity - Rename rentDueNotification* to rentNotification* for consistency - Rename utilBillsProofOfPayment to billsProofOfPayment - Introduce enums for type safety: - BillsNotificationStrategy (WhenPayed, WhenAttached) - BillsNotificationStatus (Scheduled, Sent, Failed) - RentNotificationStatus (Sent, Failed) - Replace "pending" status with "scheduled" for better semantics - Fix function names to proper camelCase - Fix incorrect import path in web-app/app/lib/format.ts 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
7.8 KiB
Context
E-mail worker in workspace email-worker has the following task:
- send e-mail verification requests
- send rent due notifications
- send utility bills due notifications
The worker runs every 60 seconds (can be modified via env variable). During each run it checks which e-mails it needs to send, composes appropriate e-mail text and sends them.
Standoff strategy & Priority
For each run there's budget which limits on total number of e-mails of all types which can be sent per one worker run. This provides a stand-off so that the remote mail server is not overwhelmed. Default budget is 10, which can be configured via env variable.
Budget is reset to initial value at beginning of each run. Budget should be decremented on each attempted e-mail send - even failed ones.
E-mail verification request have priority and are sent first.
Web App
This worker is a companion to the web app in workspace web-app. It connects to the same DB and uses same data structures.
In this document we use typescript types which can be found in /home/kneecola/projects/evidencija-rezija/web-app/app/lib/db-types.ts
DB structure
MongoDB has two collections:
lokacije- described byBillingLocationtype indb-types.tsuserSettings- described byUserSettingstype indb-types.ts
Note: UserSettings shares the same userId value with BillingLocation, which can be used to find the appropriate records.
DB connection
- connect to the db the same way as in
web-appworkspace - you can re-use the same connection for processing each e-mail type - in case of failed connection break current work and try again in the next worker interval (at the beginning of each run connect, and on run end disconnect)
Sending e-mail verification requests
The process of sending e-mail verification requests is as follows:
- connect to MongoDB (see how web app in
web-appworkspace connects to the DB) - fetch all
BillingLocationsfromlokacijecollection (seelocationActions.tsinweb-appworkspace) - filter only records which satisfy the following:yearMonth.yearandyearMonth.monthequal to current year and monthtenantEmailStatusis equal toEmailStatus.Unverified
- for each record found
- check the e-mail budget counter -> if the value is 0 then exit
- compile an e-mail containing the content listed below
- send the e-mail
- if send OK set
tenantEmailStatustoEmailStatus.VerificationPending - if send failed set
tenantEmailStatustoEmailStatus.VerificationFailed
- if send OK set
- decrement the e-mail budget counter
Verification requests E-mail content
Subject: Please verify your e-mail address From rezije.app noreply@rezije.app To: BillingLocation-tenantEmail
<p>Hello ${tenantName}!</p>
<p>You have received this e-mail because your landlord <strong>${userSettings.ownerName}</strong> wants to send you rent and utility bills invoices via rezije.app</p>
<p><strong>rezije.app</strong> is an online app which helps property owners to manage expenses related to properties they lease.</p>
<p>Before the app can start sending you rent due and utility bills emails we need your verification.</p>
<p>To verify that you want to receive these notifications please click on the following link: <a href="https://rezije.app/email/verify/${shareId}">Verify</a></p>
<p>You can ignore this email if you don't want to receive notifications. You can also unsubscribe at any time using the link included in every notification email you receive.</p>
<p>Thank you!</p>
<a href="https://rezije.app" target="_blank">rezije.app</a>
Notes:
- the
shareIdis generated using algorithm found in/home/kneecola/projects/evidencija-rezija/web-app/app/lib/shareChecksum.ts - the verify link click is handled by the web app in
web-appworkspace - if
userSettings.ownerNamecannot be found replace it with an empty string
Sending rent due notifications
The process of sending rent-due e-mail notifications is as follows:
- check the e-mail budget counter -> if the value is 0 then exit
- connect to MongoDB (see how web app in
web-appworkspace connects to the DB) - fetch all
BillingLocationsfromlokacijecollection (seelocationActions.tsinweb-appworkspace) - filter only records which satisfy the following:yearMonth.yearandyearMonth.monthequal to current year and monthtenantEmailStatusis equal toEmailStatus.VerifiedrentNotificationEnabled === truerentDueDay= current day (1-31) in CET timezonerentNotificationStatus=== undefined ORrentNotificationStatus=== null
- for each record found
- check the e-mail budget counter -> if the value is 0 then exit
- compile an e-mail containing the content listed below
- send the e-mail
- if send OK set
rentNotificationStatustosent - if send failed set
rentNotificationStatustofailed
- if send OK set
- decrement the e-mail budget counter
Rent due notifications E-mail content
Subject: Rent due for ${location.tenantName} From rezije.app noreply@rezije.app Address: BillingLocation-tenantEmail
<p>Hello ${location.tenantName}!</p>
<p>Your rent for the apartment ${location.name} is due today.</p>
<p>For details and payment options please click the following link: <a href="https://rezije.app/share/rent-due/${shareId}">Rent details</a></p>
<p>Thank you!</p>
<p><a href="https://rezije.app" target="_blank">rezije.app</a></p>
<p style="font-size:.7em">If you do no longer want to receive these notifications please click the following link: <a href="https://rezije.app/email/unsubscribe/${shareId}">Unsubscribe</a></p>
Sending utility bills due notifications
The process of bills due notifications e-mail is as follows:
- check the e-mail budget counter -> if the value is 0 then exit
- connect to MongoDB (see how web app in
web-appworkspace connects to the DB) - fetch all
BillingLocationsfromlokacijecollection (seelocationActions.tsinweb-appworkspace) - filter only records which satisfy the following:yearMonth.yearandyearMonth.monthequal to current year and monthtenantEmailStatusis equal toEmailStatus.VerifiedbillsNotificationEnabled === truebillsNotificationStatus === 'scheduled'
- for each record found
- check the e-mail budget counter -> if the value is 0 then exit
- compile an e-mail containing the content listed below
- send the e-mail
- if send OK set
billsNotificationStatustosent - if send failed set
billsNotificationStatustofailed
- if send OK set
- decrement the e-mail budget counter
Utility bills due notifications E-mail content
Subject: Utility bills due for ${location.tenantName} From rezije.app noreply@rezije.app Address: BillingLocation-tenantEmail
<p>Hello ${location.tenantName}!</p>
<p>Your utility bills for the apartment ${location.name} are due today.</p>
<p>For details and payment options please click the following link: <a href="https://rezije.app/share/bills-due/${shareId}">Utility Bills</a></p>
<p>Thank you!</p>
<p><a href="https://rezije.app" target="_blank">rezije.app</a></p>
<p style="font-size:.7em">If you do no longer want to receive these notifications please click the following link: <a href="https://rezije.app/email/unsubscribe/${shareId}">Unsubscribe</a></p>
Logging
- log all errors
- log info at beginning of each worker run
- log stats at each of e-mail type processed (how many e-mails were sent of each type)
Race condition handling
Web app does not update the same fields as e-mail worker (review code & verify). Updates should be atomic and just in time. Do not batch many updates together as it might postpone the update.
E-mail provider
E-mails will be sent via Mailgun online service using its API.
Use the following API Key = d660e320e0cdeecc125d4ecd6bc5cd66-e61ae8dd-01bdd61a