docs: improve email-worker specification and add VerificationFailed status

- Fix spelling and grammar errors throughout email-worker.md
- Add DB structure and connection sections
- Add error handling for email send failures
- Add email subjects, sender information, and delivery details
- Add logging requirements section
- Add race condition handling guidelines
- Add email provider specification (Mailgun)
- Add EmailStatus.VerificationFailed enum value for failed verification emails

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Knee Cola
2025-12-30 12:16:30 +01:00
parent f8291f9f7b
commit 4a6896c910
2 changed files with 71 additions and 19 deletions

View File

@@ -1,17 +1,19 @@
# Context
E-mail worker in workspace `email-server` has the following task:
E-mail worker in workspace `email-worker` has the following task:
- send e-mail verficiation requests
- 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 approrpate e-mail text and sends them.
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 which can be sent in one go. 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.
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.
E-mail verficiation request have priority and are sent first.
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
@@ -19,7 +21,19 @@ This worker is a companion to the web app in workspace `web-app`. It connects to
In this document we use typescript types which can be found in /home/kneecola/projects/evidencija-rezija/web-app/app/lib/db-types.ts
## Sending e-mail verficiation requests
### DB structure
MongoDB has two collections:
* `lokacije` - described by `BillingLocation` type in `db-types.ts`
* `userSettings` - described by `UserSettings` type in `db-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-app` workspace - 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:
@@ -32,17 +46,22 @@ The process of sending e-mail verification requests is as follows:
-> if the value is 0 then exit
- compile an e-mail containing the content listed below
- send the e-mail
- set `tenantEmailStatus` to `EmailStatus.VerificationPending`
- if send OK set `tenantEmailStatus` to `EmailStatus.VerificationPending`
- if send failed set `tenantEmailStatus` to `EmailStatus.VerificationFailed`
- decrement the e-mail budget counter
### Verficiation requests E-mail content
### Verification requests E-mail content
Subject: Please verify your e-mail address
From rezije.app <noreply@rezije.app>
To: BillingLocation-tenantEmail
```html
<p>Hello ${tenantName}!</p>
<p>You have received this e-mail because your landloard <strong>${userSettings.ownerName}</strong> wants to send you rent and utility bills invoices via rezije.app</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 proprty owners to manage expenses related to properties they lease.</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>
@@ -55,7 +74,11 @@ The process of sending e-mail verification requests is as follows:
<a href="https://rezije.app" target="_blank">rezije.app</a>
```
The `shareId` is generated using algorithm found in `/home/kneecola/projects/evidencija-rezija/web-app/app/lib/shareChecksum.ts`.
Notes:
* the `shareId` is 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-app` workspace
* if `userSettings.ownerName` cannot be found replace it with an empty string
# Sending rent due notifications
@@ -66,19 +89,25 @@ The process of sending rent-due e-mail notifications is as follows:
- connect to MongoDB (see how web app in `web-app` workspace connects to the DB)
- fetch all `BillingLocations` from `lokacije` collection (see `locationActions.ts` in `web-app` workspace) - filter only records which satisfy the following:
- `yearMonth.year` and `yearMonth.month` equal to current year and month
- `tenantEmailStatus` is equal to `EmailStatus.Verified`
- `rentDueNotificationEnabled === true`
- `rentDueDay` = current day (1-31)
- `rentDueDay` = current day (1-31) in CET timezone
- `rentDueNotificationStatus` === undefined OR `rentDueNotificationStatus` === 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
- set `rentDueNotificationStatus` to `sent`
- if send OK set `rentDueNotificationStatus` to `sent`
- if send failed set `rentDueNotificationStatus` to `failed`
- 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
```html
<p>Hello ${location.tenantName}!</p>
@@ -90,18 +119,19 @@ The process of sending rent-due e-mail notifications is as follows:
<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}">Rent details</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 sending rent-due e-mail notifications is as follows:
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-app` workspace connects to the DB)
- fetch all `BillingLocations` from `lokacije` collection (see `locationActions.ts` in `web-app` workspace) - filter only records which satisfy the following:
- `yearMonth.year` and `yearMonth.month` equal to current year and month
- `tenantEmailStatus` is equal to `EmailStatus.Verified`
- `billFwdEnabled === true`
- `billFwdStatus === 'pending'`
- for each record found
@@ -109,15 +139,20 @@ The process of sending rent-due e-mail notifications is as follows:
-> if the value is 0 then exit
- compile an e-mail containing the content listed below
- send the e-mail
- set `billFwdStatus` to `sent`
- if send OK set `billFwdStatus` to `sent`
- if send failed set `billFwdStatus` to `failed`
- decrement the e-mail budget counter
### Verficiation requests E-mail content
### Utility bills due notifications E-mail content
Subject: Utility bills due for ${location.tenantName}
From rezije.app <noreply@rezije.app>
Address: BillingLocation-tenantEmail
```html
<p>Hello ${location.tenantName}!</p>
<p>Your utitlity bills for the apartment ${location.name} are due today.</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/location/bills/${shareId}">Utility Bills</a></p>
@@ -125,5 +160,20 @@ The process of sending rent-due e-mail notifications is as follows:
<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}">Rent details</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.

View File

@@ -41,6 +41,8 @@ export enum EmailStatus {
Unverified = "unverified",
/** Email is not yet verified - a verification request has been sent */
VerificationPending = "verification-pending",
/** sending of verification email failed */
VerificationFailed = "verification-failed",
/** Email is verified and is in good standing: emails are being successfully delivered */
Verified = "verified",
/** Recepient has unsubscribed from receiving emails via link - no further emails will be sent */