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:
@@ -1,17 +1,19 @@
|
|||||||
# Context
|
# 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 rent due notifications
|
||||||
- send utility bills due notifications
|
- send utility bills due notifications
|
||||||
|
|
||||||
The worker runs every 60 seconds (can be modified via env variable).
|
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
|
## 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
|
## 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
|
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:
|
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
|
-> if the value is 0 then exit
|
||||||
- compile an e-mail containing the content listed below
|
- compile an e-mail containing the content listed below
|
||||||
- send the e-mail
|
- 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
|
- 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
|
```html
|
||||||
<p>Hello ${tenantName}!</p>
|
<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>
|
<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>
|
<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
|
# 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)
|
- 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:
|
- 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
|
- `yearMonth.year` and `yearMonth.month` equal to current year and month
|
||||||
|
- `tenantEmailStatus` is equal to `EmailStatus.Verified`
|
||||||
- `rentDueNotificationEnabled === true`
|
- `rentDueNotificationEnabled === true`
|
||||||
- `rentDueDay` = current day (1-31)
|
- `rentDueDay` = current day (1-31) in CET timezone
|
||||||
- `rentDueNotificationStatus` === undefined OR `rentDueNotificationStatus` === null
|
- `rentDueNotificationStatus` === undefined OR `rentDueNotificationStatus` === null
|
||||||
- for each record found
|
- for each record found
|
||||||
- check the e-mail budget counter
|
- check the e-mail budget counter
|
||||||
-> if the value is 0 then exit
|
-> if the value is 0 then exit
|
||||||
- compile an e-mail containing the content listed below
|
- compile an e-mail containing the content listed below
|
||||||
- send the e-mail
|
- 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
|
- decrement the e-mail budget counter
|
||||||
|
|
||||||
### Rent due notifications E-mail content
|
### Rent due notifications E-mail content
|
||||||
|
|
||||||
|
Subject: Rent due for ${location.tenantName}
|
||||||
|
From rezije.app <noreply@rezije.app>
|
||||||
|
Address: BillingLocation-tenantEmail
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<p>Hello ${location.tenantName}!</p>
|
<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><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
|
# 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
|
- check the e-mail budget counter
|
||||||
-> if the value is 0 then exit
|
-> if the value is 0 then exit
|
||||||
- connect to MongoDB (see how web app in `web-app` workspace connects to the DB)
|
- 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:
|
- 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
|
- `yearMonth.year` and `yearMonth.month` equal to current year and month
|
||||||
|
- `tenantEmailStatus` is equal to `EmailStatus.Verified`
|
||||||
- `billFwdEnabled === true`
|
- `billFwdEnabled === true`
|
||||||
- `billFwdStatus === 'pending'`
|
- `billFwdStatus === 'pending'`
|
||||||
- for each record found
|
- 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
|
-> if the value is 0 then exit
|
||||||
- compile an e-mail containing the content listed below
|
- compile an e-mail containing the content listed below
|
||||||
- send the e-mail
|
- 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
|
- 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
|
```html
|
||||||
<p>Hello ${location.tenantName}!</p>
|
<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>
|
<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><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.
|
||||||
@@ -41,6 +41,8 @@ export enum EmailStatus {
|
|||||||
Unverified = "unverified",
|
Unverified = "unverified",
|
||||||
/** Email is not yet verified - a verification request has been sent */
|
/** Email is not yet verified - a verification request has been sent */
|
||||||
VerificationPending = "verification-pending",
|
VerificationPending = "verification-pending",
|
||||||
|
/** sending of verification email failed */
|
||||||
|
VerificationFailed = "verification-failed",
|
||||||
/** Email is verified and is in good standing: emails are being successfully delivered */
|
/** Email is verified and is in good standing: emails are being successfully delivered */
|
||||||
Verified = "verified",
|
Verified = "verified",
|
||||||
/** Recepient has unsubscribed from receiving emails via link - no further emails will be sent */
|
/** Recepient has unsubscribed from receiving emails via link - no further emails will be sent */
|
||||||
|
|||||||
Reference in New Issue
Block a user