Files
evidencija-rezija/sprints/email-worker.md
Knee Cola 1e83172491 refactor: rename share URL path from location to bills-due
- Renamed directory: app/[locale]/share/location -> app/[locale]/share/bills-due
- Updated all URL references in web-app (billActions, locationActions, ViewBillCard)
- Updated all URL references in email-worker templates (4 email templates)
- Updated documentation in sprints/email-worker.md

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-31 11:27:50 +01:00

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 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:

  • 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.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 tenantEmailStatus to EmailStatus.VerificationPending
      • if send failed set tenantEmailStatus to EmailStatus.VerificationFailed
    • 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 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

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-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) 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
      • 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

<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/bills-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-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
    • 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 billFwdStatus to sent
      • if send failed set billFwdStatus to failed
    • 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