Component architecture
- Reorganise src/lib/components into pages/, sections/, ui/ subdirectories and
  update all imports across routes and tests.

Owner admin dashboard (+2k lines)
- Scheduled welcome-pack emails: durable queue with cancel/reschedule and a
  background sender loop (SCHEDULED_CHECK_INTERVAL_SECONDS, default 60s).
- Custom welcome-pack subject line, preview recipients, and client BCC.
- Add-client, edit client profile, and reset-onboarding flows.
- "View as client" onboarding preview (owner impersonation, dry-run submit).
- Tabbed owner welcome route (/owner/welcome/[[tab]]).

MYOB integration
- New mail_api/myob.py: create new clients as MYOB AccountRight customer
  contacts. Disabled until all MYOB_* env vars are set (no-op otherwise).

Onboarding
- New "Does your dog resource guard?" Yes/No question in the Behaviour step.
- Persist vetAddress, flea/tick, and pet-insurance fields server-side.

Misc
- vite config, new hooks.ts, responsive CSS tweaks, deploy/docker config,
  mail-api README and start-dev.ps1.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-17 21:51:29 +12:00
co-authored by Claude Opus 4.8
parent 7d5b72e1d3
commit 0e88e7bdac
82 changed files with 4543 additions and 914 deletions
+22
View File
@@ -0,0 +1,22 @@
# Mail API local dev
Use the project virtualenv, not the global Python install. The local environment already resolves compatible `pydantic` and `pydantic-core` versions together.
## Start on Windows PowerShell
```powershell
cd C:\Users\mattc\gw-svelte\gw-svelte\mail-api
.\start-dev.ps1
```
## First-time setup
```powershell
cd C:\Users\mattc\gw-svelte\gw-svelte\mail-api
py -3.14 -m venv .venv
.\.venv\Scripts\python.exe -m pip install -r requirements.txt
```
## If you see a `pydantic-core` mismatch
You are almost certainly running against a global interpreter or a stale environment. Reinstall into the project virtualenv and start the app with `.\start-dev.ps1`.
Binary file not shown.
Binary file not shown.
+72 -72
View File
@@ -207,12 +207,12 @@
"lifecycle": {
"status": "archived",
"reason": "Imported from legacy Gravity Forms data",
"changedAt": "2026-05-24T05:45:33.160Z",
"changedAt": "2026-06-15T21:24:42.302Z",
"changedBy": "legacy-import"
},
"legacy": {
"source": "gravity-forms-csv",
"importedAt": "2026-05-24T05:45:33.160Z",
"importedAt": "2026-06-15T21:24:42.302Z",
"onboardingEntryIds": [
"27"
],
@@ -432,12 +432,12 @@
"lifecycle": {
"status": "archived",
"reason": "Imported from legacy Gravity Forms data",
"changedAt": "2026-05-24T05:45:33.160Z",
"changedAt": "2026-06-15T21:24:42.302Z",
"changedBy": "legacy-import"
},
"legacy": {
"source": "gravity-forms-csv",
"importedAt": "2026-05-24T05:45:33.160Z",
"importedAt": "2026-06-15T21:24:42.302Z",
"onboardingEntryIds": [
"45"
],
@@ -657,12 +657,12 @@
"lifecycle": {
"status": "archived",
"reason": "Imported from legacy Gravity Forms data",
"changedAt": "2026-05-24T05:45:33.160Z",
"changedAt": "2026-06-15T21:24:42.302Z",
"changedBy": "legacy-import"
},
"legacy": {
"source": "gravity-forms-csv",
"importedAt": "2026-05-24T05:45:33.160Z",
"importedAt": "2026-06-15T21:24:42.302Z",
"onboardingEntryIds": [
"95"
],
@@ -882,12 +882,12 @@
"lifecycle": {
"status": "archived",
"reason": "Imported from legacy Gravity Forms data",
"changedAt": "2026-05-24T05:45:33.160Z",
"changedAt": "2026-06-15T21:24:42.302Z",
"changedBy": "legacy-import"
},
"legacy": {
"source": "gravity-forms-csv",
"importedAt": "2026-05-24T05:45:33.160Z",
"importedAt": "2026-06-15T21:24:42.302Z",
"onboardingEntryIds": [
"103"
],
@@ -1107,12 +1107,12 @@
"lifecycle": {
"status": "archived",
"reason": "Imported from legacy Gravity Forms data",
"changedAt": "2026-05-24T05:45:33.160Z",
"changedAt": "2026-06-15T21:24:42.302Z",
"changedBy": "legacy-import"
},
"legacy": {
"source": "gravity-forms-csv",
"importedAt": "2026-05-24T05:45:33.160Z",
"importedAt": "2026-06-15T21:24:42.302Z",
"onboardingEntryIds": [
"39"
],
@@ -1332,12 +1332,12 @@
"lifecycle": {
"status": "archived",
"reason": "Imported from legacy Gravity Forms data",
"changedAt": "2026-05-24T05:45:33.160Z",
"changedAt": "2026-06-15T21:24:42.302Z",
"changedBy": "legacy-import"
},
"legacy": {
"source": "gravity-forms-csv",
"importedAt": "2026-05-24T05:45:33.160Z",
"importedAt": "2026-06-15T21:24:42.302Z",
"onboardingEntryIds": [
"41"
],
@@ -1557,12 +1557,12 @@
"lifecycle": {
"status": "archived",
"reason": "Imported from legacy Gravity Forms data",
"changedAt": "2026-05-24T05:45:33.160Z",
"changedAt": "2026-06-15T21:24:42.302Z",
"changedBy": "legacy-import"
},
"legacy": {
"source": "gravity-forms-csv",
"importedAt": "2026-05-24T05:45:33.160Z",
"importedAt": "2026-06-15T21:24:42.302Z",
"onboardingEntryIds": [
"36"
],
@@ -1782,12 +1782,12 @@
"lifecycle": {
"status": "archived",
"reason": "Imported from legacy Gravity Forms data",
"changedAt": "2026-05-24T05:45:33.160Z",
"changedAt": "2026-06-15T21:24:42.302Z",
"changedBy": "legacy-import"
},
"legacy": {
"source": "gravity-forms-csv",
"importedAt": "2026-05-24T05:45:33.160Z",
"importedAt": "2026-06-15T21:24:42.302Z",
"onboardingEntryIds": [
"83"
],
@@ -2007,12 +2007,12 @@
"lifecycle": {
"status": "archived",
"reason": "Imported from legacy Gravity Forms data",
"changedAt": "2026-05-24T05:45:33.160Z",
"changedAt": "2026-06-15T21:24:42.302Z",
"changedBy": "legacy-import"
},
"legacy": {
"source": "gravity-forms-csv",
"importedAt": "2026-05-24T05:45:33.160Z",
"importedAt": "2026-06-15T21:24:42.302Z",
"onboardingEntryIds": [
"76",
"49"
@@ -2233,12 +2233,12 @@
"lifecycle": {
"status": "archived",
"reason": "Imported from legacy Gravity Forms data",
"changedAt": "2026-05-24T05:45:33.160Z",
"changedAt": "2026-06-15T21:24:42.302Z",
"changedBy": "legacy-import"
},
"legacy": {
"source": "gravity-forms-csv",
"importedAt": "2026-05-24T05:45:33.160Z",
"importedAt": "2026-06-15T21:24:42.302Z",
"onboardingEntryIds": [
"99"
],
@@ -2458,12 +2458,12 @@
"lifecycle": {
"status": "archived",
"reason": "Imported from legacy Gravity Forms data",
"changedAt": "2026-05-24T05:45:33.160Z",
"changedAt": "2026-06-15T21:24:42.302Z",
"changedBy": "legacy-import"
},
"legacy": {
"source": "gravity-forms-csv",
"importedAt": "2026-05-24T05:45:33.160Z",
"importedAt": "2026-06-15T21:24:42.302Z",
"onboardingEntryIds": [
"84"
],
@@ -2683,12 +2683,12 @@
"lifecycle": {
"status": "archived",
"reason": "Imported from legacy Gravity Forms data",
"changedAt": "2026-05-24T05:45:33.160Z",
"changedAt": "2026-06-15T21:24:42.302Z",
"changedBy": "legacy-import"
},
"legacy": {
"source": "gravity-forms-csv",
"importedAt": "2026-05-24T05:45:33.160Z",
"importedAt": "2026-06-15T21:24:42.302Z",
"onboardingEntryIds": [
"51"
],
@@ -2908,12 +2908,12 @@
"lifecycle": {
"status": "archived",
"reason": "Imported from legacy Gravity Forms data",
"changedAt": "2026-05-24T05:45:33.160Z",
"changedAt": "2026-06-15T21:24:42.302Z",
"changedBy": "legacy-import"
},
"legacy": {
"source": "gravity-forms-csv",
"importedAt": "2026-05-24T05:45:33.160Z",
"importedAt": "2026-06-15T21:24:42.302Z",
"onboardingEntryIds": [
"87"
],
@@ -3134,12 +3134,12 @@
"lifecycle": {
"status": "archived",
"reason": "Imported from legacy Gravity Forms data",
"changedAt": "2026-05-24T05:45:33.160Z",
"changedAt": "2026-06-15T21:24:42.302Z",
"changedBy": "legacy-import"
},
"legacy": {
"source": "gravity-forms-csv",
"importedAt": "2026-05-24T05:45:33.160Z",
"importedAt": "2026-06-15T21:24:42.302Z",
"onboardingEntryIds": [
"88"
],
@@ -3359,12 +3359,12 @@
"lifecycle": {
"status": "archived",
"reason": "Imported from legacy Gravity Forms data",
"changedAt": "2026-05-24T05:45:33.160Z",
"changedAt": "2026-06-15T21:24:42.302Z",
"changedBy": "legacy-import"
},
"legacy": {
"source": "gravity-forms-csv",
"importedAt": "2026-05-24T05:45:33.160Z",
"importedAt": "2026-06-15T21:24:42.302Z",
"onboardingEntryIds": [
"34"
],
@@ -3584,12 +3584,12 @@
"lifecycle": {
"status": "archived",
"reason": "Imported from legacy Gravity Forms data",
"changedAt": "2026-05-24T05:45:33.160Z",
"changedAt": "2026-06-15T21:24:42.302Z",
"changedBy": "legacy-import"
},
"legacy": {
"source": "gravity-forms-csv",
"importedAt": "2026-05-24T05:45:33.160Z",
"importedAt": "2026-06-15T21:24:42.302Z",
"onboardingEntryIds": [
"61",
"47"
@@ -3811,12 +3811,12 @@
"lifecycle": {
"status": "archived",
"reason": "Imported from legacy Gravity Forms data",
"changedAt": "2026-05-24T05:45:33.160Z",
"changedAt": "2026-06-15T21:24:42.302Z",
"changedBy": "legacy-import"
},
"legacy": {
"source": "gravity-forms-csv",
"importedAt": "2026-05-24T05:45:33.160Z",
"importedAt": "2026-06-15T21:24:42.302Z",
"onboardingEntryIds": [
"57"
],
@@ -4036,12 +4036,12 @@
"lifecycle": {
"status": "archived",
"reason": "Imported from legacy Gravity Forms data",
"changedAt": "2026-05-24T05:45:33.160Z",
"changedAt": "2026-06-15T21:24:42.302Z",
"changedBy": "legacy-import"
},
"legacy": {
"source": "gravity-forms-csv",
"importedAt": "2026-05-24T05:45:33.160Z",
"importedAt": "2026-06-15T21:24:42.302Z",
"onboardingEntryIds": [
"32"
],
@@ -4261,12 +4261,12 @@
"lifecycle": {
"status": "archived",
"reason": "Imported from legacy Gravity Forms data",
"changedAt": "2026-05-24T05:45:33.160Z",
"changedAt": "2026-06-15T21:24:42.302Z",
"changedBy": "legacy-import"
},
"legacy": {
"source": "gravity-forms-csv",
"importedAt": "2026-05-24T05:45:33.160Z",
"importedAt": "2026-06-15T21:24:42.302Z",
"onboardingEntryIds": [
"22"
],
@@ -4486,12 +4486,12 @@
"lifecycle": {
"status": "archived",
"reason": "Imported from legacy Gravity Forms data",
"changedAt": "2026-05-24T05:45:33.160Z",
"changedAt": "2026-06-15T21:24:42.302Z",
"changedBy": "legacy-import"
},
"legacy": {
"source": "gravity-forms-csv",
"importedAt": "2026-05-24T05:45:33.160Z",
"importedAt": "2026-06-15T21:24:42.302Z",
"onboardingEntryIds": [
"64"
],
@@ -4711,12 +4711,12 @@
"lifecycle": {
"status": "archived",
"reason": "Imported from legacy Gravity Forms data",
"changedAt": "2026-05-24T05:45:33.160Z",
"changedAt": "2026-06-15T21:24:42.302Z",
"changedBy": "legacy-import"
},
"legacy": {
"source": "gravity-forms-csv",
"importedAt": "2026-05-24T05:45:33.160Z",
"importedAt": "2026-06-15T21:24:42.302Z",
"onboardingEntryIds": [
"74"
],
@@ -4936,12 +4936,12 @@
"lifecycle": {
"status": "archived",
"reason": "Imported from legacy Gravity Forms data",
"changedAt": "2026-05-24T05:45:33.160Z",
"changedAt": "2026-06-15T21:24:42.302Z",
"changedBy": "legacy-import"
},
"legacy": {
"source": "gravity-forms-csv",
"importedAt": "2026-05-24T05:45:33.160Z",
"importedAt": "2026-06-15T21:24:42.302Z",
"onboardingEntryIds": [
"56"
],
@@ -5161,12 +5161,12 @@
"lifecycle": {
"status": "archived",
"reason": "Imported from legacy Gravity Forms data",
"changedAt": "2026-05-24T05:45:33.160Z",
"changedAt": "2026-06-15T21:24:42.302Z",
"changedBy": "legacy-import"
},
"legacy": {
"source": "gravity-forms-csv",
"importedAt": "2026-05-24T05:45:33.160Z",
"importedAt": "2026-06-15T21:24:42.302Z",
"onboardingEntryIds": [
"116",
"93"
@@ -5387,12 +5387,12 @@
"lifecycle": {
"status": "archived",
"reason": "Imported from legacy Gravity Forms data",
"changedAt": "2026-05-24T05:45:33.160Z",
"changedAt": "2026-06-15T21:24:42.302Z",
"changedBy": "legacy-import"
},
"legacy": {
"source": "gravity-forms-csv",
"importedAt": "2026-05-24T05:45:33.160Z",
"importedAt": "2026-06-15T21:24:42.302Z",
"onboardingEntryIds": [
"59"
],
@@ -5612,12 +5612,12 @@
"lifecycle": {
"status": "archived",
"reason": "Imported from legacy Gravity Forms data",
"changedAt": "2026-05-24T05:45:33.160Z",
"changedAt": "2026-06-15T21:24:42.302Z",
"changedBy": "legacy-import"
},
"legacy": {
"source": "gravity-forms-csv",
"importedAt": "2026-05-24T05:45:33.160Z",
"importedAt": "2026-06-15T21:24:42.302Z",
"onboardingEntryIds": [
"30"
],
@@ -5837,12 +5837,12 @@
"lifecycle": {
"status": "archived",
"reason": "Imported from legacy Gravity Forms data",
"changedAt": "2026-05-24T05:45:33.160Z",
"changedAt": "2026-06-15T21:24:42.302Z",
"changedBy": "legacy-import"
},
"legacy": {
"source": "gravity-forms-csv",
"importedAt": "2026-05-24T05:45:33.160Z",
"importedAt": "2026-06-15T21:24:42.302Z",
"onboardingEntryIds": [
"53"
],
@@ -6062,12 +6062,12 @@
"lifecycle": {
"status": "archived",
"reason": "Imported from legacy Gravity Forms data",
"changedAt": "2026-05-24T05:45:33.160Z",
"changedAt": "2026-06-15T21:24:42.302Z",
"changedBy": "legacy-import"
},
"legacy": {
"source": "gravity-forms-csv",
"importedAt": "2026-05-24T05:45:33.160Z",
"importedAt": "2026-06-15T21:24:42.302Z",
"onboardingEntryIds": [
"107"
],
@@ -6287,12 +6287,12 @@
"lifecycle": {
"status": "archived",
"reason": "Imported from legacy Gravity Forms data",
"changedAt": "2026-05-24T05:45:33.160Z",
"changedAt": "2026-06-15T21:24:42.302Z",
"changedBy": "legacy-import"
},
"legacy": {
"source": "gravity-forms-csv",
"importedAt": "2026-05-24T05:45:33.160Z",
"importedAt": "2026-06-15T21:24:42.302Z",
"onboardingEntryIds": [
"24"
],
@@ -6512,12 +6512,12 @@
"lifecycle": {
"status": "archived",
"reason": "Imported from legacy Gravity Forms data",
"changedAt": "2026-05-24T05:45:33.160Z",
"changedAt": "2026-06-15T21:24:42.302Z",
"changedBy": "legacy-import"
},
"legacy": {
"source": "gravity-forms-csv",
"importedAt": "2026-05-24T05:45:33.160Z",
"importedAt": "2026-06-15T21:24:42.302Z",
"onboardingEntryIds": [
"43"
],
@@ -6737,12 +6737,12 @@
"lifecycle": {
"status": "archived",
"reason": "Imported from legacy Gravity Forms data",
"changedAt": "2026-05-24T05:45:33.160Z",
"changedAt": "2026-06-15T21:24:42.302Z",
"changedBy": "legacy-import"
},
"legacy": {
"source": "gravity-forms-csv",
"importedAt": "2026-05-24T05:45:33.160Z",
"importedAt": "2026-06-15T21:24:42.302Z",
"onboardingEntryIds": [
"97"
],
@@ -6962,12 +6962,12 @@
"lifecycle": {
"status": "archived",
"reason": "Imported from legacy Gravity Forms data",
"changedAt": "2026-05-24T05:45:33.160Z",
"changedAt": "2026-06-15T21:24:42.302Z",
"changedBy": "legacy-import"
},
"legacy": {
"source": "gravity-forms-csv",
"importedAt": "2026-05-24T05:45:33.160Z",
"importedAt": "2026-06-15T21:24:42.302Z",
"onboardingEntryIds": [
"90"
],
@@ -7188,12 +7188,12 @@
"lifecycle": {
"status": "archived",
"reason": "Imported from legacy Gravity Forms data",
"changedAt": "2026-05-24T05:45:33.160Z",
"changedAt": "2026-06-15T21:24:42.302Z",
"changedBy": "legacy-import"
},
"legacy": {
"source": "gravity-forms-csv",
"importedAt": "2026-05-24T05:45:33.160Z",
"importedAt": "2026-06-15T21:24:42.302Z",
"onboardingEntryIds": [
"105"
],
@@ -7413,12 +7413,12 @@
"lifecycle": {
"status": "archived",
"reason": "Imported from legacy Gravity Forms data",
"changedAt": "2026-05-24T05:45:33.160Z",
"changedAt": "2026-06-15T21:24:42.302Z",
"changedBy": "legacy-import"
},
"legacy": {
"source": "gravity-forms-csv",
"importedAt": "2026-05-24T05:45:33.160Z",
"importedAt": "2026-06-15T21:24:42.302Z",
"onboardingEntryIds": [
"101"
],
@@ -7638,12 +7638,12 @@
"lifecycle": {
"status": "archived",
"reason": "Imported from legacy Gravity Forms data",
"changedAt": "2026-05-24T05:45:33.160Z",
"changedAt": "2026-06-15T21:24:42.302Z",
"changedBy": "legacy-import"
},
"legacy": {
"source": "gravity-forms-csv",
"importedAt": "2026-05-24T05:45:33.160Z",
"importedAt": "2026-06-15T21:24:42.302Z",
"onboardingEntryIds": [
"78"
],
@@ -7664,12 +7664,12 @@
"lifecycle": {
"status": "archived",
"reason": "Imported from legacy Gravity Forms data",
"changedAt": "2026-05-24T05:45:33.160Z",
"changedAt": "2026-06-15T21:24:42.302Z",
"changedBy": "legacy-import"
},
"legacy": {
"source": "gravity-forms-csv",
"importedAt": "2026-05-24T05:45:33.160Z",
"importedAt": "2026-06-15T21:24:42.302Z",
"onboardingEntryIds": [],
"contractEntryIds": [
"29"
@@ -7887,12 +7887,12 @@
"lifecycle": {
"status": "archived",
"reason": "Imported from legacy Gravity Forms data",
"changedAt": "2026-05-24T05:45:33.160Z",
"changedAt": "2026-06-15T21:24:42.302Z",
"changedBy": "legacy-import"
},
"legacy": {
"source": "gravity-forms-csv",
"importedAt": "2026-05-24T05:45:33.160Z",
"importedAt": "2026-06-15T21:24:42.302Z",
"onboardingEntryIds": [
"80"
],
+654
View File
@@ -1437,3 +1437,657 @@ resend.exceptions.ResendError: API key is invalid
19/05/2026 23:32:40 New Zealand Standard Time WARNING mail-api: Startup smoke: postgres disabled — activity/submissions will NOT be recorded
19/05/2026 23:32:40 New Zealand Standard Time INFO mail-api: Startup test email skipped: OWNER_BCC is not set to a real address
19/05/2026 23:37:58 New Zealand Standard Time INFO mail-api: [e8c74fa0] GET /auth/verify → 401 (2ms)
15/06/2026 20:01:52 New Zealand Standard Time INFO mail-api: Logging initialised → console=INFO, file=logs\mail-api.log (DEBUG, rotating)
15/06/2026 20:01:52 New Zealand Standard Time INFO mail-api: Mail API config: version='unknown' timezone='system-default' from='GoodWalk <info@goodwalk.co.nz>' reply_to='info@goodwalk.co.nz' owner='info@goodwalk.co.nz' cp_admins=['info@goodwalk.co.nz'] owner_bcc='' client_bcc='' general_enquiries=False max_attempts=3 form_min=1s form_max=7200s rate_window=900s per_ip=50 per_email=50 min_interval=1s send_timeout=20s
15/06/2026 20:01:52 New Zealand Standard Time INFO mail-api: Auth: loaded 3 allowed email(s)
15/06/2026 20:01:53 New Zealand Standard Time ERROR mail-api: Startup smoke: WeasyPrint UNAVAILABLE — PDF attachments will be skipped (No module named 'weasyprint')
15/06/2026 20:01:53 New Zealand Standard Time WARNING mail-api: Startup smoke: postgres disabled — activity/submissions will NOT be recorded
15/06/2026 20:01:53 New Zealand Standard Time INFO mail-api: Startup test email skipped: OWNER_BCC is not set to a real address
15/06/2026 20:05:19 New Zealand Standard Time INFO mail-api: [ae1b0f7e] POST /auth/request-code → 400 (0ms)
15/06/2026 20:06:13 New Zealand Standard Time INFO mail-api: [faf603f0] POST /auth/request-code → 400 (0ms)
15/06/2026 20:06:14 New Zealand Standard Time INFO mail-api: [dd8ad9b9] POST /auth/request-code → 400 (0ms)
15/06/2026 20:06:14 New Zealand Standard Time INFO mail-api: [3fb682e8] POST /auth/request-code → 400 (0ms)
15/06/2026 20:06:14 New Zealand Standard Time INFO mail-api: [734a08d2] POST /auth/request-code → 400 (0ms)
15/06/2026 20:06:14 New Zealand Standard Time INFO mail-api: [658d0d93] POST /auth/request-code → 400 (0ms)
15/06/2026 20:06:15 New Zealand Standard Time INFO mail-api: [a66b3c9f] POST /auth/request-code → 400 (0ms)
15/06/2026 20:06:15 New Zealand Standard Time INFO mail-api: [28307070] POST /auth/request-code → 400 (0ms)
15/06/2026 20:06:29 New Zealand Standard Time INFO mail-api: Logging initialised → console=INFO, file=logs\mail-api.log (DEBUG, rotating)
15/06/2026 20:06:29 New Zealand Standard Time INFO mail-api: Mail API config: version='unknown' timezone='system-default' from='GoodWalk <info@goodwalk.co.nz>' reply_to='info@goodwalk.co.nz' owner='info@goodwalk.co.nz' cp_admins=['info@goodwalk.co.nz'] owner_bcc='' client_bcc='' general_enquiries=False max_attempts=3 form_min=1s form_max=7200s rate_window=900s per_ip=50 per_email=50 min_interval=1s send_timeout=20s
15/06/2026 20:06:29 New Zealand Standard Time INFO mail-api: Auth: loaded 3 allowed email(s)
15/06/2026 20:06:29 New Zealand Standard Time ERROR mail-api: Startup smoke: WeasyPrint UNAVAILABLE — PDF attachments will be skipped (No module named 'weasyprint')
15/06/2026 20:06:29 New Zealand Standard Time WARNING mail-api: Startup smoke: postgres disabled — activity/submissions will NOT be recorded
15/06/2026 20:06:29 New Zealand Standard Time INFO mail-api: Startup test email skipped: OWNER_BCC is not set to a real address
15/06/2026 20:06:36 New Zealand Standard Time INFO mail-api: Logging initialised → console=INFO, file=logs\mail-api.log (DEBUG, rotating)
15/06/2026 20:06:37 New Zealand Standard Time CRITICAL mail-api: Startup aborted: missing env vars: ['RESEND_API_KEY', 'OWNER_EMAIL']
15/06/2026 20:06:55 New Zealand Standard Time INFO mail-api: [78c69f47] POST /auth/request-code → 400 (0ms)
15/06/2026 20:07:00 New Zealand Standard Time INFO mail-api: Logging initialised → console=INFO, file=logs\mail-api.log (DEBUG, rotating)
15/06/2026 20:07:00 New Zealand Standard Time INFO mail-api: Mail API config: version='unknown' timezone='system-default' from='GoodWalk <info@goodwalk.co.nz>' reply_to='info@goodwalk.co.nz' owner='info@goodwalk.co.nz' cp_admins=['info@goodwalk.co.nz'] owner_bcc='' client_bcc='' general_enquiries=False max_attempts=3 form_min=1s form_max=7200s rate_window=900s per_ip=50 per_email=50 min_interval=1s send_timeout=20s
15/06/2026 20:07:00 New Zealand Standard Time INFO mail-api: Auth: loaded 3 allowed email(s)
15/06/2026 20:07:00 New Zealand Standard Time ERROR mail-api: Startup smoke: WeasyPrint UNAVAILABLE — PDF attachments will be skipped (No module named 'weasyprint')
15/06/2026 20:07:00 New Zealand Standard Time WARNING mail-api: Startup smoke: postgres disabled — activity/submissions will NOT be recorded
15/06/2026 20:07:00 New Zealand Standard Time INFO mail-api: Startup test email skipped: OWNER_BCC is not set to a real address
15/06/2026 20:07:02 New Zealand Standard Time INFO mail-api: [ccfc7849] POST /auth/request-code → 400 (0ms)
15/06/2026 20:08:31 New Zealand Standard Time INFO mail-api: Logging initialised → console=INFO, file=logs\mail-api.log (DEBUG, rotating)
15/06/2026 20:08:31 New Zealand Standard Time INFO mail-api: Mail API config: version='unknown' timezone='system-default' from='GoodWalk <info@goodwalk.co.nz>' reply_to='info@goodwalk.co.nz' owner='info@goodwalk.co.nz' cp_admins=['info@goodwalk.co.nz'] owner_bcc='' client_bcc='' general_enquiries=False max_attempts=3 form_min=1s form_max=7200s rate_window=900s per_ip=50 per_email=50 min_interval=1s send_timeout=20s
15/06/2026 20:08:31 New Zealand Standard Time INFO mail-api: Auth: loaded 3 allowed email(s)
15/06/2026 20:08:31 New Zealand Standard Time ERROR mail-api: Startup smoke: WeasyPrint UNAVAILABLE — PDF attachments will be skipped (No module named 'weasyprint')
15/06/2026 20:08:31 New Zealand Standard Time WARNING mail-api: Startup smoke: postgres disabled — activity/submissions will NOT be recorded
15/06/2026 20:08:31 New Zealand Standard Time INFO mail-api: Startup test email skipped: OWNER_BCC is not set to a real address
15/06/2026 20:08:36 New Zealand Standard Time INFO mail-api: Logging initialised → console=INFO, file=logs\mail-api.log (DEBUG, rotating)
15/06/2026 20:08:36 New Zealand Standard Time INFO mail-api: Mail API config: version='unknown' timezone='system-default' from='GoodWalk <info@goodwalk.co.nz>' reply_to='info@goodwalk.co.nz' owner='info@goodwalk.co.nz' cp_admins=['info@goodwalk.co.nz'] owner_bcc='' client_bcc='' general_enquiries=False max_attempts=3 form_min=1s form_max=7200s rate_window=900s per_ip=50 per_email=50 min_interval=1s send_timeout=20s
15/06/2026 20:08:36 New Zealand Standard Time INFO mail-api: Auth: loaded 3 allowed email(s)
15/06/2026 20:08:36 New Zealand Standard Time ERROR mail-api: Startup smoke: WeasyPrint UNAVAILABLE — PDF attachments will be skipped (No module named 'weasyprint')
15/06/2026 20:08:36 New Zealand Standard Time WARNING mail-api: Startup smoke: postgres disabled — activity/submissions will NOT be recorded
15/06/2026 20:08:36 New Zealand Standard Time INFO mail-api: Startup test email skipped: OWNER_BCC is not set to a real address
15/06/2026 20:08:40 New Zealand Standard Time INFO mail-api: [b2f2b0fc] POST /auth/request-code → 400 (0ms)
15/06/2026 20:09:25 New Zealand Standard Time ERROR mail-api: [0f10f6ec] POST /auth/request-code crashed after 3ms
Traceback (most recent call last):
File "C:\Users\mattc\gw-svelte\gw-svelte\mail-api\main.py", line 579, in _request_logging_middleware
response = await call_next(request)
^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\mattc\gw-svelte\gw-svelte\mail-api\.venv\Lib\site-packages\starlette\middleware\base.py", line 168, in call_next
raise app_exc from app_exc.__cause__ or app_exc.__context__
File "C:\Users\mattc\gw-svelte\gw-svelte\mail-api\.venv\Lib\site-packages\starlette\middleware\base.py", line 144, in coro
await self.app(scope, receive_or_disconnect, send_no_error)
File "C:\Users\mattc\gw-svelte\gw-svelte\mail-api\.venv\Lib\site-packages\starlette\middleware\cors.py", line 88, in __call__
await self.app(scope, receive, send)
File "C:\Users\mattc\gw-svelte\gw-svelte\mail-api\.venv\Lib\site-packages\starlette\middleware\trustedhost.py", line 51, in __call__
await self.app(scope, receive, send)
File "C:\Users\mattc\gw-svelte\gw-svelte\mail-api\main.py", line 546, in __call__
await self.app(scope, _wrapped_receive, send)
File "C:\Users\mattc\gw-svelte\gw-svelte\mail-api\.venv\Lib\site-packages\starlette\middleware\exceptions.py", line 63, in __call__
await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
File "C:\Users\mattc\gw-svelte\gw-svelte\mail-api\.venv\Lib\site-packages\starlette\_exception_handler.py", line 53, in wrapped_app
raise exc
File "C:\Users\mattc\gw-svelte\gw-svelte\mail-api\.venv\Lib\site-packages\starlette\_exception_handler.py", line 42, in wrapped_app
await app(scope, receive, sender)
File "C:\Users\mattc\gw-svelte\gw-svelte\mail-api\.venv\Lib\site-packages\fastapi\middleware\asyncexitstack.py", line 18, in __call__
await self.app(scope, receive, send)
File "C:\Users\mattc\gw-svelte\gw-svelte\mail-api\.venv\Lib\site-packages\starlette\routing.py", line 660, in __call__
await self.middleware_stack(scope, receive, send)
File "C:\Users\mattc\gw-svelte\gw-svelte\mail-api\.venv\Lib\site-packages\starlette\routing.py", line 680, in app
await route.handle(scope, receive, send)
File "C:\Users\mattc\gw-svelte\gw-svelte\mail-api\.venv\Lib\site-packages\starlette\routing.py", line 276, in handle
await self.app(scope, receive, send)
File "C:\Users\mattc\gw-svelte\gw-svelte\mail-api\.venv\Lib\site-packages\fastapi\routing.py", line 134, in app
await wrap_app_handling_exceptions(app, request)(scope, receive, send)
File "C:\Users\mattc\gw-svelte\gw-svelte\mail-api\.venv\Lib\site-packages\starlette\_exception_handler.py", line 53, in wrapped_app
raise exc
File "C:\Users\mattc\gw-svelte\gw-svelte\mail-api\.venv\Lib\site-packages\starlette\_exception_handler.py", line 42, in wrapped_app
await app(scope, receive, sender)
File "C:\Users\mattc\gw-svelte\gw-svelte\mail-api\.venv\Lib\site-packages\fastapi\routing.py", line 120, in app
response = await f(request)
^^^^^^^^^^^^^^^^
File "C:\Users\mattc\gw-svelte\gw-svelte\mail-api\.venv\Lib\site-packages\fastapi\routing.py", line 674, in app
raw_response = await run_endpoint_function(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
...<3 lines>...
)
^
File "C:\Users\mattc\gw-svelte\gw-svelte\mail-api\.venv\Lib\site-packages\fastapi\routing.py", line 328, in run_endpoint_function
return await dependant.call(**values)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\mattc\gw-svelte\gw-svelte\mail-api\main.py", line 2474, in auth_request_code
body = await request.json()
^^^^^^^^^^^^^^^^^^^^
File "C:\Users\mattc\gw-svelte\gw-svelte\mail-api\.venv\Lib\site-packages\starlette\requests.py", line 264, in json
self._json = json.loads(body)
~~~~~~~~~~^^^^^^
File "C:\Users\mattc\AppData\Local\Programs\Python\Python314\Lib\json\__init__.py", line 352, in loads
return _default_decoder.decode(s)
~~~~~~~~~~~~~~~~~~~~~~~^^^
File "C:\Users\mattc\AppData\Local\Programs\Python\Python314\Lib\json\decoder.py", line 345, in decode
obj, end = self.raw_decode(s, idx=_w(s, 0).end())
~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\mattc\AppData\Local\Programs\Python\Python314\Lib\json\decoder.py", line 361, in raw_decode
obj, end = self.scan_once(s, idx)
~~~~~~~~~~~~~~^^^^^^^^
json.decoder.JSONDecodeError: Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
15/06/2026 20:09:38 New Zealand Standard Time ERROR mail-api: [251e2d22] POST /auth/request-code crashed after 1ms
Traceback (most recent call last):
File "C:\Users\mattc\gw-svelte\gw-svelte\mail-api\main.py", line 579, in _request_logging_middleware
response = await call_next(request)
^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\mattc\gw-svelte\gw-svelte\mail-api\.venv\Lib\site-packages\starlette\middleware\base.py", line 168, in call_next
raise app_exc from app_exc.__cause__ or app_exc.__context__
File "C:\Users\mattc\gw-svelte\gw-svelte\mail-api\.venv\Lib\site-packages\starlette\middleware\base.py", line 144, in coro
await self.app(scope, receive_or_disconnect, send_no_error)
File "C:\Users\mattc\gw-svelte\gw-svelte\mail-api\.venv\Lib\site-packages\starlette\middleware\cors.py", line 88, in __call__
await self.app(scope, receive, send)
File "C:\Users\mattc\gw-svelte\gw-svelte\mail-api\.venv\Lib\site-packages\starlette\middleware\trustedhost.py", line 51, in __call__
await self.app(scope, receive, send)
File "C:\Users\mattc\gw-svelte\gw-svelte\mail-api\main.py", line 546, in __call__
await self.app(scope, _wrapped_receive, send)
File "C:\Users\mattc\gw-svelte\gw-svelte\mail-api\.venv\Lib\site-packages\starlette\middleware\exceptions.py", line 63, in __call__
await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
File "C:\Users\mattc\gw-svelte\gw-svelte\mail-api\.venv\Lib\site-packages\starlette\_exception_handler.py", line 53, in wrapped_app
raise exc
File "C:\Users\mattc\gw-svelte\gw-svelte\mail-api\.venv\Lib\site-packages\starlette\_exception_handler.py", line 42, in wrapped_app
await app(scope, receive, sender)
File "C:\Users\mattc\gw-svelte\gw-svelte\mail-api\.venv\Lib\site-packages\fastapi\middleware\asyncexitstack.py", line 18, in __call__
await self.app(scope, receive, send)
File "C:\Users\mattc\gw-svelte\gw-svelte\mail-api\.venv\Lib\site-packages\starlette\routing.py", line 660, in __call__
await self.middleware_stack(scope, receive, send)
File "C:\Users\mattc\gw-svelte\gw-svelte\mail-api\.venv\Lib\site-packages\starlette\routing.py", line 680, in app
await route.handle(scope, receive, send)
File "C:\Users\mattc\gw-svelte\gw-svelte\mail-api\.venv\Lib\site-packages\starlette\routing.py", line 276, in handle
await self.app(scope, receive, send)
File "C:\Users\mattc\gw-svelte\gw-svelte\mail-api\.venv\Lib\site-packages\fastapi\routing.py", line 134, in app
await wrap_app_handling_exceptions(app, request)(scope, receive, send)
File "C:\Users\mattc\gw-svelte\gw-svelte\mail-api\.venv\Lib\site-packages\starlette\_exception_handler.py", line 53, in wrapped_app
raise exc
File "C:\Users\mattc\gw-svelte\gw-svelte\mail-api\.venv\Lib\site-packages\starlette\_exception_handler.py", line 42, in wrapped_app
await app(scope, receive, sender)
File "C:\Users\mattc\gw-svelte\gw-svelte\mail-api\.venv\Lib\site-packages\fastapi\routing.py", line 120, in app
response = await f(request)
^^^^^^^^^^^^^^^^
File "C:\Users\mattc\gw-svelte\gw-svelte\mail-api\.venv\Lib\site-packages\fastapi\routing.py", line 674, in app
raw_response = await run_endpoint_function(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
...<3 lines>...
)
^
File "C:\Users\mattc\gw-svelte\gw-svelte\mail-api\.venv\Lib\site-packages\fastapi\routing.py", line 328, in run_endpoint_function
return await dependant.call(**values)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\mattc\gw-svelte\gw-svelte\mail-api\main.py", line 2474, in auth_request_code
body = await request.json()
^^^^^^^^^^^^^^^^^^^^
File "C:\Users\mattc\gw-svelte\gw-svelte\mail-api\.venv\Lib\site-packages\starlette\requests.py", line 264, in json
self._json = json.loads(body)
~~~~~~~~~~^^^^^^
File "C:\Users\mattc\AppData\Local\Programs\Python\Python314\Lib\json\__init__.py", line 352, in loads
return _default_decoder.decode(s)
~~~~~~~~~~~~~~~~~~~~~~~^^^
File "C:\Users\mattc\AppData\Local\Programs\Python\Python314\Lib\json\decoder.py", line 345, in decode
obj, end = self.raw_decode(s, idx=_w(s, 0).end())
~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\mattc\AppData\Local\Programs\Python\Python314\Lib\json\decoder.py", line 361, in raw_decode
obj, end = self.scan_once(s, idx)
~~~~~~~~~~~~~~^^^^^^^^
json.decoder.JSONDecodeError: Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
15/06/2026 20:09:53 New Zealand Standard Time INFO mail-api: Logging initialised → console=INFO, file=logs\mail-api.log (DEBUG, rotating)
15/06/2026 20:09:53 New Zealand Standard Time INFO mail-api: Mail API config: version='unknown' timezone='system-default' from='GoodWalk <info@goodwalk.co.nz>' reply_to='info@goodwalk.co.nz' owner='info@goodwalk.co.nz' cp_admins=['info@goodwalk.co.nz'] owner_bcc='' client_bcc='' general_enquiries=False max_attempts=3 form_min=1s form_max=7200s rate_window=900s per_ip=50 per_email=50 min_interval=1s send_timeout=20s
15/06/2026 20:09:53 New Zealand Standard Time INFO mail-api: Auth: loaded 3 allowed email(s)
15/06/2026 20:09:53 New Zealand Standard Time ERROR mail-api: Startup smoke: WeasyPrint UNAVAILABLE — PDF attachments will be skipped (No module named 'weasyprint')
15/06/2026 20:09:53 New Zealand Standard Time WARNING mail-api: Startup smoke: postgres disabled — activity/submissions will NOT be recorded
15/06/2026 20:09:53 New Zealand Standard Time INFO mail-api: Startup test email skipped: OWNER_BCC is not set to a real address
15/06/2026 20:10:15 New Zealand Standard Time INFO mail-api: Logging initialised → console=INFO, file=logs\mail-api.log (DEBUG, rotating)
15/06/2026 20:10:15 New Zealand Standard Time INFO mail-api: Mail API config: version='unknown' timezone='system-default' from='GoodWalk <info@goodwalk.co.nz>' reply_to='info@goodwalk.co.nz' owner='info@goodwalk.co.nz' cp_admins=['info@goodwalk.co.nz'] owner_bcc='' client_bcc='' general_enquiries=False max_attempts=3 form_min=1s form_max=7200s rate_window=900s per_ip=50 per_email=50 min_interval=1s send_timeout=20s
15/06/2026 20:10:15 New Zealand Standard Time INFO mail-api: Auth: loaded 3 allowed email(s)
15/06/2026 20:10:15 New Zealand Standard Time ERROR mail-api: Startup smoke: WeasyPrint UNAVAILABLE — PDF attachments will be skipped (No module named 'weasyprint')
15/06/2026 20:10:15 New Zealand Standard Time WARNING mail-api: Startup smoke: postgres disabled — activity/submissions will NOT be recorded
15/06/2026 20:10:15 New Zealand Standard Time INFO mail-api: Startup test email skipped: OWNER_BCC is not set to a real address
15/06/2026 20:10:20 New Zealand Standard Time INFO mail-api: [72b111d2] POST /auth/request-code → 400 (0ms)
15/06/2026 20:11:27 New Zealand Standard Time INFO mail-api: Logging initialised → console=INFO, file=logs\mail-api.log (DEBUG, rotating)
15/06/2026 20:11:27 New Zealand Standard Time INFO mail-api: Mail API config: version='unknown' timezone='system-default' from='GoodWalk <info@goodwalk.co.nz>' reply_to='info@goodwalk.co.nz' owner='info@goodwalk.co.nz' cp_admins=['info@goodwalk.co.nz'] owner_bcc='' client_bcc='' general_enquiries=False max_attempts=3 form_min=1s form_max=7200s rate_window=900s per_ip=50 per_email=50 min_interval=1s send_timeout=20s
15/06/2026 20:11:27 New Zealand Standard Time INFO mail-api: Auth: loaded 3 allowed email(s)
15/06/2026 20:11:27 New Zealand Standard Time ERROR mail-api: Startup smoke: WeasyPrint UNAVAILABLE — PDF attachments will be skipped (No module named 'weasyprint')
15/06/2026 20:11:27 New Zealand Standard Time WARNING mail-api: Startup smoke: postgres disabled — activity/submissions will NOT be recorded
15/06/2026 20:11:27 New Zealand Standard Time INFO mail-api: Startup test email skipped: OWNER_BCC is not set to a real address
15/06/2026 20:11:38 New Zealand Standard Time INFO mail-api: [6c351fe3] POST /auth/request-code → 400 (0ms)
15/06/2026 20:11:45 New Zealand Standard Time INFO mail-api: [912374c3] POST /auth/request-code → 400 (0ms)
15/06/2026 20:11:50 New Zealand Standard Time INFO mail-api: [ca16d00f] POST /auth/request-code → 400 (0ms)
15/06/2026 20:15:10 New Zealand Standard Time INFO mail-api: [90835023] auth: code issued for email=info@goodwalk.co.nz
15/06/2026 20:15:10 New Zealand Standard Time WARNING mail-api: [DEV] auth code for info@goodwalk.co.nz: 956187
15/06/2026 20:15:10 New Zealand Standard Time INFO mail-api: [90835023] POST /auth/request-code → 200 (11ms)
15/06/2026 20:15:18 New Zealand Standard Time INFO mail-api: [b5f581f9] auth: session created for email=info@goodwalk.co.nz
15/06/2026 20:15:18 New Zealand Standard Time INFO mail-api: [b5f581f9] POST /auth/verify-code → 200 (2ms)
15/06/2026 20:15:18 New Zealand Standard Time INFO mail-api: [9d58963f] GET /auth/verify → 200 (1ms)
15/06/2026 20:15:18 New Zealand Standard Time INFO mail-api: [6efc3cae] GET /owner/pending-onboarding → 200 (1ms)
15/06/2026 20:15:18 New Zealand Standard Time INFO mail-api: [f8fd2577] GET /owner/all-clients → 200 (2ms)
15/06/2026 20:15:18 New Zealand Standard Time INFO mail-api: [bd38d327] GET /owner/birthdays → 200 (11ms)
15/06/2026 20:15:20 New Zealand Standard Time INFO mail-api: [59319824] GET /auth/verify → 200 (0ms)
15/06/2026 20:15:20 New Zealand Standard Time INFO mail-api: [bb2fa1e3] GET /owner/pending-onboarding → 200 (1ms)
15/06/2026 20:15:20 New Zealand Standard Time INFO mail-api: [a7d68e07] GET /owner/all-clients → 200 (2ms)
15/06/2026 20:15:20 New Zealand Standard Time INFO mail-api: [3f420d48] GET /owner/birthdays → 200 (2ms)
15/06/2026 20:15:24 New Zealand Standard Time INFO mail-api: [9dee7759] GET /auth/verify → 200 (0ms)
15/06/2026 20:15:24 New Zealand Standard Time INFO mail-api: [bff1d27d] GET /owner/pending-onboarding → 200 (0ms)
15/06/2026 20:15:24 New Zealand Standard Time INFO mail-api: [573299f0] GET /owner/birthdays → 200 (1ms)
15/06/2026 20:15:24 New Zealand Standard Time INFO mail-api: [c1b2cbca] GET /owner/all-clients → 200 (1ms)
15/06/2026 20:15:31 New Zealand Standard Time INFO mail-api: [8ca00ff2] GET /owner/message-templates → 200 (1ms)
15/06/2026 20:15:32 New Zealand Standard Time INFO mail-api: [5d67ac5b] POST /owner/render-message → 200 (3ms)
15/06/2026 20:16:25 New Zealand Standard Time INFO mail-api: [a3d3d2d9] GET /owner/activity → 200 (1ms)
15/06/2026 20:17:24 New Zealand Standard Time INFO mail-api: [62384e2c] GET /owner/activity → 200 (0ms)
15/06/2026 20:17:46 New Zealand Standard Time INFO mail-api: [be53083f] GET /owner/activity → 200 (0ms)
15/06/2026 20:18:38 New Zealand Standard Time INFO mail-api: [5f777ef7] GET /owner/activity → 200 (0ms)
15/06/2026 20:19:19 New Zealand Standard Time INFO mail-api: [739f25e5] GET /owner/client-enquiry → 200 (8ms)
15/06/2026 20:19:28 New Zealand Standard Time INFO mail-api: [e70d718b] GET /owner/activity → 200 (1ms)
15/06/2026 20:20:46 New Zealand Standard Time INFO mail-api: [4b64a4ec] GET /owner/activity → 200 (0ms)
15/06/2026 20:21:06 New Zealand Standard Time INFO mail-api: [5206327c] GET /auth/verify → 200 (0ms)
15/06/2026 20:21:06 New Zealand Standard Time INFO mail-api: [0520fe2c] GET /owner/pending-onboarding → 200 (0ms)
15/06/2026 20:21:06 New Zealand Standard Time INFO mail-api: [659bc15b] GET /owner/all-clients → 200 (1ms)
15/06/2026 20:21:06 New Zealand Standard Time INFO mail-api: [b367235f] GET /owner/birthdays → 200 (1ms)
15/06/2026 20:27:58 New Zealand Standard Time INFO mail-api: Logging initialised → console=INFO, file=logs\mail-api.log (DEBUG, rotating)
15/06/2026 20:27:58 New Zealand Standard Time INFO mail-api: Mail API config: version='unknown' timezone='system-default' from='GoodWalk <info@goodwalk.co.nz>' reply_to='info@goodwalk.co.nz' owner='info@goodwalk.co.nz' cp_admins=['info@goodwalk.co.nz'] owner_bcc='' client_bcc='' general_enquiries=False max_attempts=3 form_min=1s form_max=7200s rate_window=900s per_ip=50 per_email=50 min_interval=1s send_timeout=20s
15/06/2026 20:27:58 New Zealand Standard Time INFO mail-api: Auth: loaded 3 allowed email(s)
15/06/2026 20:27:58 New Zealand Standard Time ERROR mail-api: Startup smoke: WeasyPrint UNAVAILABLE — PDF attachments will be skipped (No module named 'weasyprint')
15/06/2026 20:27:58 New Zealand Standard Time WARNING mail-api: Startup smoke: postgres disabled — activity/submissions will NOT be recorded
15/06/2026 20:27:58 New Zealand Standard Time INFO mail-api: Startup test email skipped: OWNER_BCC is not set to a real address
15/06/2026 20:28:06 New Zealand Standard Time INFO mail-api: [bc4610f2] GET /owner/message-templates → 401 (2ms)
15/06/2026 20:28:07 New Zealand Standard Time INFO mail-api: [0169b9e6] GET /owner/activity → 401 (1ms)
15/06/2026 20:30:00 New Zealand Standard Time INFO mail-api: [87be64e8] GET /owner/message-templates → 401 (0ms)
15/06/2026 20:30:01 New Zealand Standard Time INFO mail-api: [2822c7c6] GET /owner/activity → 401 (0ms)
15/06/2026 20:30:09 New Zealand Standard Time INFO mail-api: [f61a5c74] GET /auth/verify → 401 (1ms)
15/06/2026 20:31:51 New Zealand Standard Time INFO mail-api: [ee1b53c2] auth: code issued for email=info@goodwalk.co.nz
15/06/2026 20:31:51 New Zealand Standard Time WARNING mail-api: [DEV] auth code for info@goodwalk.co.nz: 546348
15/06/2026 20:31:51 New Zealand Standard Time INFO mail-api: [ee1b53c2] POST /auth/request-code → 200 (2ms)
15/06/2026 20:32:01 New Zealand Standard Time INFO mail-api: [ac46d159] auth: session created for email=info@goodwalk.co.nz
15/06/2026 20:32:01 New Zealand Standard Time INFO mail-api: [ac46d159] POST /auth/verify-code → 200 (1ms)
15/06/2026 20:32:01 New Zealand Standard Time INFO mail-api: [60c7fd10] GET /auth/verify → 200 (0ms)
15/06/2026 20:32:01 New Zealand Standard Time INFO mail-api: [abfa833f] GET /owner/all-clients → 200 (2ms)
15/06/2026 20:32:01 New Zealand Standard Time INFO mail-api: [71044bf5] GET /owner/pending-onboarding → 200 (3ms)
15/06/2026 20:32:01 New Zealand Standard Time INFO mail-api: [2ec29972] GET /owner/birthdays → 200 (6ms)
15/06/2026 20:32:03 New Zealand Standard Time INFO mail-api: [6c0a3799] GET /owner/client-onboarding-view → 200 (1ms)
15/06/2026 20:32:10 New Zealand Standard Time INFO mail-api: [64cdfdf6] GET /auth/verify → 200 (0ms)
15/06/2026 20:32:10 New Zealand Standard Time INFO mail-api: [b8478985] GET /owner/pending-onboarding → 200 (1ms)
15/06/2026 20:32:10 New Zealand Standard Time INFO mail-api: [d5ec31df] GET /owner/all-clients → 200 (1ms)
15/06/2026 20:32:10 New Zealand Standard Time INFO mail-api: [0ebeb971] GET /owner/birthdays → 200 (1ms)
15/06/2026 20:32:11 New Zealand Standard Time INFO mail-api: [3b2f4ee0] GET /owner/client-onboarding-view → 200 (0ms)
15/06/2026 20:32:28 New Zealand Standard Time INFO mail-api: [fdc6a9fa] GET /owner/client-onboarding-view → 200 (0ms)
15/06/2026 20:32:53 New Zealand Standard Time INFO mail-api: [07b910be] GET /auth/verify → 200 (0ms)
15/06/2026 20:32:53 New Zealand Standard Time INFO mail-api: [eb7646b6] GET /owner/pending-onboarding → 200 (1ms)
15/06/2026 20:32:53 New Zealand Standard Time INFO mail-api: [e8b1a226] GET /owner/all-clients → 200 (1ms)
15/06/2026 20:32:53 New Zealand Standard Time INFO mail-api: [882b2a08] GET /owner/birthdays → 200 (0ms)
15/06/2026 20:33:23 New Zealand Standard Time INFO mail-api: [783d78c3] GET /auth/verify → 200 (0ms)
15/06/2026 20:33:23 New Zealand Standard Time INFO mail-api: [e8b66a3c] GET /owner/pending-onboarding → 200 (1ms)
15/06/2026 20:33:23 New Zealand Standard Time INFO mail-api: [020c91d9] GET /owner/all-clients → 200 (1ms)
15/06/2026 20:33:23 New Zealand Standard Time INFO mail-api: [91679cb8] GET /owner/birthdays → 200 (1ms)
15/06/2026 20:33:28 New Zealand Standard Time INFO mail-api: [e764f088] GET /owner/client-enquiry → 200 (1ms)
15/06/2026 20:33:29 New Zealand Standard Time INFO mail-api: [5089b29e] GET /owner/client-onboarding-view → 200 (0ms)
15/06/2026 20:33:34 New Zealand Standard Time INFO mail-api: [2c30ae12] GET /owner/message-templates → 200 (1ms)
15/06/2026 20:33:34 New Zealand Standard Time INFO mail-api: [2f086a39] POST /owner/render-message → 200 (2ms)
15/06/2026 20:33:36 New Zealand Standard Time INFO mail-api: [c383d4c2] GET /owner/activity → 200 (0ms)
15/06/2026 20:33:46 New Zealand Standard Time INFO mail-api: [8ce6215d] GET /auth/verify → 200 (0ms)
15/06/2026 20:33:46 New Zealand Standard Time INFO mail-api: [77dd8afc] GET /owner/all-clients → 200 (1ms)
15/06/2026 20:33:46 New Zealand Standard Time INFO mail-api: [5be1fa50] GET /owner/pending-onboarding → 200 (1ms)
15/06/2026 20:33:46 New Zealand Standard Time INFO mail-api: [69163ab8] GET /owner/birthdays → 200 (1ms)
15/06/2026 20:34:03 New Zealand Standard Time INFO mail-api: [ef3c519f] GET /owner/client-enquiry → 200 (0ms)
15/06/2026 20:34:05 New Zealand Standard Time INFO mail-api: [d547ff1d] GET /owner/client-onboarding-view → 200 (0ms)
15/06/2026 20:36:48 New Zealand Standard Time INFO mail-api: [0d37e5dc] GET /owner/client-enquiry → 200 (0ms)
15/06/2026 20:36:50 New Zealand Standard Time INFO mail-api: [24d3194d] GET /owner/client-onboarding-view → 200 (0ms)
15/06/2026 20:37:56 New Zealand Standard Time INFO mail-api: [1145dc87] GET /owner/message-templates → 200 (1ms)
15/06/2026 20:37:56 New Zealand Standard Time INFO mail-api: [681ad27d] POST /owner/render-message → 200 (1ms)
15/06/2026 20:37:57 New Zealand Standard Time INFO mail-api: [99d12925] GET /owner/activity → 200 (0ms)
15/06/2026 20:38:16 New Zealand Standard Time INFO mail-api: [823c2f81] GET /owner/client-onboarding-view → 200 (0ms)
15/06/2026 20:38:22 New Zealand Standard Time INFO mail-api: [1ee7e7fc] GET /owner/client-onboarding-view → 200 (1ms)
15/06/2026 20:38:37 New Zealand Standard Time INFO mail-api: [f3eed04b] GET /auth/verify → 200 (0ms)
15/06/2026 20:38:37 New Zealand Standard Time INFO mail-api: [2fcb1297] GET /owner/pending-onboarding → 200 (1ms)
15/06/2026 20:38:37 New Zealand Standard Time INFO mail-api: [f8c4996f] GET /owner/birthdays → 200 (2ms)
15/06/2026 20:38:37 New Zealand Standard Time INFO mail-api: [99f8e950] GET /owner/all-clients → 200 (2ms)
15/06/2026 20:38:41 New Zealand Standard Time INFO mail-api: [9da1e72a] GET /auth/verify → 200 (0ms)
15/06/2026 20:38:41 New Zealand Standard Time INFO mail-api: [697cad6a] GET /owner/pending-onboarding → 200 (1ms)
15/06/2026 20:38:41 New Zealand Standard Time INFO mail-api: [b5c6c30b] GET /owner/all-clients → 200 (1ms)
15/06/2026 20:38:41 New Zealand Standard Time INFO mail-api: [1c67730b] GET /owner/birthdays → 200 (0ms)
15/06/2026 20:38:50 New Zealand Standard Time INFO mail-api: [4b3db25d] GET /auth/verify → 200 (0ms)
15/06/2026 20:38:50 New Zealand Standard Time INFO mail-api: [b3feec86] GET /owner/pending-onboarding → 200 (1ms)
15/06/2026 20:38:50 New Zealand Standard Time INFO mail-api: [f47f7889] GET /owner/all-clients → 200 (1ms)
15/06/2026 20:38:50 New Zealand Standard Time INFO mail-api: [f15da397] GET /owner/birthdays → 200 (1ms)
15/06/2026 20:38:55 New Zealand Standard Time INFO mail-api: [65cae4c6] GET /auth/verify → 200 (1ms)
15/06/2026 20:38:55 New Zealand Standard Time INFO mail-api: [21ab02ac] GET /owner/pending-onboarding → 200 (1ms)
15/06/2026 20:38:55 New Zealand Standard Time INFO mail-api: [5a7c1435] GET /owner/all-clients → 200 (1ms)
15/06/2026 20:38:55 New Zealand Standard Time INFO mail-api: [e64d71b6] GET /owner/birthdays → 200 (2ms)
15/06/2026 20:39:01 New Zealand Standard Time INFO mail-api: [251265ca] GET /auth/verify → 200 (0ms)
15/06/2026 20:39:01 New Zealand Standard Time INFO mail-api: [40b2e542] GET /owner/pending-onboarding → 200 (1ms)
15/06/2026 20:39:01 New Zealand Standard Time INFO mail-api: [c9af86f0] GET /owner/birthdays → 200 (1ms)
15/06/2026 20:39:01 New Zealand Standard Time INFO mail-api: [e9c26722] GET /owner/all-clients → 200 (1ms)
15/06/2026 20:39:13 New Zealand Standard Time INFO mail-api: [a2afb433] GET /auth/verify → 200 (0ms)
15/06/2026 20:39:13 New Zealand Standard Time INFO mail-api: [195ded67] GET /owner/pending-onboarding → 200 (1ms)
15/06/2026 20:39:13 New Zealand Standard Time INFO mail-api: [212985b9] GET /owner/all-clients → 200 (1ms)
15/06/2026 20:39:13 New Zealand Standard Time INFO mail-api: [9cdd4476] GET /owner/birthdays → 200 (1ms)
15/06/2026 20:39:23 New Zealand Standard Time INFO mail-api: [0ba57c08] GET /auth/verify → 200 (0ms)
15/06/2026 20:39:23 New Zealand Standard Time INFO mail-api: [6eec0e7f] GET /owner/pending-onboarding → 200 (1ms)
15/06/2026 20:39:23 New Zealand Standard Time INFO mail-api: [f12a42d6] GET /owner/all-clients → 200 (1ms)
15/06/2026 20:39:23 New Zealand Standard Time INFO mail-api: [99a199e6] GET /owner/birthdays → 200 (0ms)
15/06/2026 20:39:39 New Zealand Standard Time INFO mail-api: [0a1bd987] GET /auth/verify → 200 (0ms)
15/06/2026 20:39:39 New Zealand Standard Time INFO mail-api: [261ccb15] GET /owner/pending-onboarding → 200 (0ms)
15/06/2026 20:39:39 New Zealand Standard Time INFO mail-api: [ef0c4698] GET /owner/all-clients → 200 (1ms)
15/06/2026 20:39:39 New Zealand Standard Time INFO mail-api: [819915a2] GET /owner/birthdays → 200 (1ms)
15/06/2026 20:39:57 New Zealand Standard Time INFO mail-api: [7f594e14] GET /owner/message-templates → 200 (1ms)
15/06/2026 20:39:57 New Zealand Standard Time INFO mail-api: [99e918ff] POST /owner/render-message → 200 (1ms)
15/06/2026 20:39:57 New Zealand Standard Time INFO mail-api: [c387447a] GET /owner/activity → 200 (0ms)
15/06/2026 20:40:03 New Zealand Standard Time INFO mail-api: [e225bdd8] GET /owner/activity → 200 (0ms)
15/06/2026 20:41:27 New Zealand Standard Time INFO mail-api: [fea1334d] GET /owner/activity → 200 (0ms)
15/06/2026 20:41:35 New Zealand Standard Time INFO mail-api: [0476565e] GET /auth/verify → 200 (0ms)
15/06/2026 20:41:35 New Zealand Standard Time INFO mail-api: [637f7274] GET /owner/pending-onboarding → 200 (1ms)
15/06/2026 20:41:35 New Zealand Standard Time INFO mail-api: [e2f2deb3] GET /owner/all-clients → 200 (1ms)
15/06/2026 20:41:35 New Zealand Standard Time INFO mail-api: [ada6cda9] GET /owner/birthdays → 200 (1ms)
15/06/2026 20:41:40 New Zealand Standard Time INFO mail-api: [8ed96de9] GET /owner/message-templates → 200 (1ms)
15/06/2026 20:41:40 New Zealand Standard Time INFO mail-api: [99ebfb37] POST /owner/render-message → 200 (1ms)
15/06/2026 20:41:43 New Zealand Standard Time INFO mail-api: [054a22e5] GET /owner/client-onboarding-view → 200 (1ms)
15/06/2026 20:41:50 New Zealand Standard Time INFO mail-api: [0331ffb5] GET /auth/verify → 200 (0ms)
15/06/2026 20:41:50 New Zealand Standard Time INFO mail-api: [0bfa7c1d] GET /owner/pending-onboarding → 200 (1ms)
15/06/2026 20:41:50 New Zealand Standard Time INFO mail-api: [7bff5a04] GET /owner/birthdays → 200 (1ms)
15/06/2026 20:41:50 New Zealand Standard Time INFO mail-api: [b3b90343] GET /owner/all-clients → 200 (2ms)
15/06/2026 20:41:55 New Zealand Standard Time INFO mail-api: [e7e42f0d] GET /owner/client-onboarding-view → 200 (0ms)
15/06/2026 20:42:09 New Zealand Standard Time INFO mail-api: [06c17820] GET /owner/client-onboarding-view → 200 (0ms)
15/06/2026 20:42:27 New Zealand Standard Time INFO mail-api: [fb40d429] GET /auth/verify → 200 (1ms)
15/06/2026 20:42:27 New Zealand Standard Time INFO mail-api: [93be80d1] GET /owner/pending-onboarding → 200 (1ms)
15/06/2026 20:42:27 New Zealand Standard Time INFO mail-api: [aacd6e15] GET /owner/all-clients → 200 (1ms)
15/06/2026 20:42:27 New Zealand Standard Time INFO mail-api: [1f59b652] GET /owner/birthdays → 200 (2ms)
15/06/2026 20:42:35 New Zealand Standard Time INFO mail-api: [f63506a6] GET /owner/message-templates → 200 (1ms)
15/06/2026 20:42:35 New Zealand Standard Time INFO mail-api: [388261bd] POST /owner/render-message → 200 (1ms)
15/06/2026 20:42:36 New Zealand Standard Time INFO mail-api: [b360a242] GET /owner/activity → 200 (1ms)
15/06/2026 20:42:41 New Zealand Standard Time INFO mail-api: [158bdb8d] GET /owner/client-enquiry → 200 (0ms)
15/06/2026 20:48:29 New Zealand Standard Time INFO mail-api: Logging initialised → console=INFO, file=logs\mail-api.log (DEBUG, rotating)
15/06/2026 20:48:29 New Zealand Standard Time INFO mail-api: Mail API config: version='unknown' timezone='system-default' from='GoodWalk <info@goodwalk.co.nz>' reply_to='info@goodwalk.co.nz' owner='info@goodwalk.co.nz' cp_admins=['info@goodwalk.co.nz'] owner_bcc='' client_bcc='' general_enquiries=False max_attempts=3 form_min=1s form_max=7200s rate_window=900s per_ip=50 per_email=50 min_interval=1s send_timeout=20s
15/06/2026 20:48:29 New Zealand Standard Time INFO mail-api: Auth: loaded 3 allowed email(s)
15/06/2026 20:48:30 New Zealand Standard Time ERROR mail-api: Startup smoke: WeasyPrint UNAVAILABLE — PDF attachments will be skipped (No module named 'weasyprint')
15/06/2026 20:48:30 New Zealand Standard Time WARNING mail-api: Startup smoke: postgres disabled — activity/submissions will NOT be recorded
15/06/2026 20:48:30 New Zealand Standard Time INFO mail-api: Startup test email skipped: OWNER_BCC is not set to a real address
15/06/2026 20:49:48 New Zealand Standard Time INFO mail-api: [545f781f] GET /auth/verify → 401 (2ms)
15/06/2026 20:52:31 New Zealand Standard Time INFO mail-api: [7ac75d9d] auth: code issued for email=info@goodwalk.co.nz
15/06/2026 20:52:31 New Zealand Standard Time WARNING mail-api: [DEV] auth code for info@goodwalk.co.nz: 718390
15/06/2026 20:52:31 New Zealand Standard Time INFO mail-api: [7ac75d9d] POST /auth/request-code → 200 (2ms)
15/06/2026 20:52:38 New Zealand Standard Time INFO mail-api: [48f12e62] auth: session created for email=info@goodwalk.co.nz
15/06/2026 20:52:38 New Zealand Standard Time INFO mail-api: [48f12e62] POST /auth/verify-code → 200 (2ms)
15/06/2026 20:52:38 New Zealand Standard Time INFO mail-api: [6057c23b] GET /auth/verify → 200 (0ms)
15/06/2026 20:52:38 New Zealand Standard Time INFO mail-api: [96abc272] GET /owner/pending-onboarding → 200 (2ms)
15/06/2026 20:52:38 New Zealand Standard Time INFO mail-api: [92cdf868] GET /owner/all-clients → 200 (2ms)
15/06/2026 20:52:38 New Zealand Standard Time INFO mail-api: [5fb5fdce] GET /owner/birthdays → 200 (12ms)
15/06/2026 20:52:50 New Zealand Standard Time INFO mail-api: [12d01bfe] GET /auth/verify → 200 (1ms)
15/06/2026 20:52:50 New Zealand Standard Time INFO mail-api: [3841ff56] GET /owner/pending-onboarding → 200 (1ms)
15/06/2026 20:52:50 New Zealand Standard Time INFO mail-api: [f920aade] GET /owner/all-clients → 200 (1ms)
15/06/2026 20:52:50 New Zealand Standard Time INFO mail-api: [7852a9d2] GET /owner/birthdays → 200 (1ms)
15/06/2026 20:53:03 New Zealand Standard Time INFO mail-api: [230b4b2b] GET /auth/verify → 200 (0ms)
15/06/2026 20:53:03 New Zealand Standard Time INFO mail-api: [4ba82776] GET /owner/pending-onboarding → 200 (1ms)
15/06/2026 20:53:03 New Zealand Standard Time INFO mail-api: [6fefad3f] GET /owner/all-clients → 200 (2ms)
15/06/2026 20:53:03 New Zealand Standard Time INFO mail-api: [acb28200] GET /owner/birthdays → 200 (2ms)
15/06/2026 20:53:22 New Zealand Standard Time INFO mail-api: [9839fe15] GET /owner/message-templates → 200 (1ms)
15/06/2026 20:53:22 New Zealand Standard Time INFO mail-api: [26e12e0e] POST /owner/render-message → 200 (1ms)
15/06/2026 20:53:23 New Zealand Standard Time INFO mail-api: [a4614459] GET /owner/activity → 200 (1ms)
15/06/2026 21:06:24 New Zealand Standard Time INFO mail-api: Logging initialised → console=INFO, file=logs\mail-api.log (DEBUG, rotating)
15/06/2026 21:06:24 New Zealand Standard Time INFO mail-api: Mail API config: version='unknown' timezone='system-default' from='GoodWalk <info@goodwalk.co.nz>' reply_to='info@goodwalk.co.nz' owner='info@goodwalk.co.nz' cp_admins=['info@goodwalk.co.nz'] owner_bcc='' client_bcc='' general_enquiries=False max_attempts=3 form_min=1s form_max=7200s rate_window=900s per_ip=50 per_email=50 min_interval=1s send_timeout=20s
15/06/2026 21:06:24 New Zealand Standard Time INFO mail-api: Auth: loaded 3 allowed email(s)
15/06/2026 21:06:24 New Zealand Standard Time ERROR mail-api: Startup smoke: WeasyPrint UNAVAILABLE — PDF attachments will be skipped (No module named 'weasyprint')
15/06/2026 21:06:24 New Zealand Standard Time WARNING mail-api: Startup smoke: postgres disabled — activity/submissions will NOT be recorded
15/06/2026 21:06:24 New Zealand Standard Time INFO mail-api: Startup test email skipped: OWNER_BCC is not set to a real address
15/06/2026 21:44:37 New Zealand Standard Time INFO mail-api: Logging initialised → console=INFO, file=logs\mail-api.log (DEBUG, rotating)
15/06/2026 21:44:37 New Zealand Standard Time INFO mail-api: Mail API config: version='unknown' timezone='system-default' from='GoodWalk <info@goodwalk.co.nz>' reply_to='info@goodwalk.co.nz' owner='info@goodwalk.co.nz' cp_admins=['info@goodwalk.co.nz'] owner_bcc='' client_bcc='' general_enquiries=False max_attempts=3 form_min=1s form_max=7200s rate_window=900s per_ip=50 per_email=50 min_interval=1s send_timeout=20s
15/06/2026 21:44:37 New Zealand Standard Time INFO mail-api: Auth: loaded 3 allowed email(s)
15/06/2026 21:44:37 New Zealand Standard Time ERROR mail-api: Startup smoke: WeasyPrint UNAVAILABLE — PDF attachments will be skipped (No module named 'weasyprint')
15/06/2026 21:44:37 New Zealand Standard Time WARNING mail-api: Startup smoke: postgres disabled — activity/submissions will NOT be recorded
15/06/2026 21:44:37 New Zealand Standard Time INFO mail-api: Startup test email skipped: OWNER_BCC is not set to a real address
15/06/2026 21:44:45 New Zealand Standard Time INFO mail-api: Logging initialised → console=INFO, file=logs\mail-api.log (DEBUG, rotating)
15/06/2026 21:44:45 New Zealand Standard Time INFO mail-api: Mail API config: version='unknown' timezone='system-default' from='GoodWalk <info@goodwalk.co.nz>' reply_to='info@goodwalk.co.nz' owner='info@goodwalk.co.nz' cp_admins=['info@goodwalk.co.nz'] owner_bcc='' client_bcc='' general_enquiries=False max_attempts=3 form_min=1s form_max=7200s rate_window=900s per_ip=50 per_email=50 min_interval=1s send_timeout=20s
15/06/2026 21:44:45 New Zealand Standard Time INFO mail-api: Auth: loaded 3 allowed email(s)
15/06/2026 21:44:45 New Zealand Standard Time ERROR mail-api: Startup smoke: WeasyPrint UNAVAILABLE — PDF attachments will be skipped (No module named 'weasyprint')
15/06/2026 21:44:45 New Zealand Standard Time WARNING mail-api: Startup smoke: postgres disabled — activity/submissions will NOT be recorded
15/06/2026 21:44:45 New Zealand Standard Time INFO mail-api: Startup test email skipped: OWNER_BCC is not set to a real address
15/06/2026 21:44:56 New Zealand Standard Time INFO mail-api: Logging initialised → console=INFO, file=logs\mail-api.log (DEBUG, rotating)
15/06/2026 21:44:56 New Zealand Standard Time INFO mail-api: Mail API config: version='unknown' timezone='system-default' from='GoodWalk <info@goodwalk.co.nz>' reply_to='info@goodwalk.co.nz' owner='info@goodwalk.co.nz' cp_admins=['info@goodwalk.co.nz'] owner_bcc='' client_bcc='' general_enquiries=False max_attempts=3 form_min=1s form_max=7200s rate_window=900s per_ip=50 per_email=50 min_interval=1s send_timeout=20s
15/06/2026 21:44:56 New Zealand Standard Time INFO mail-api: Auth: loaded 3 allowed email(s)
15/06/2026 21:44:56 New Zealand Standard Time ERROR mail-api: Startup smoke: WeasyPrint UNAVAILABLE — PDF attachments will be skipped (No module named 'weasyprint')
15/06/2026 21:44:56 New Zealand Standard Time WARNING mail-api: Startup smoke: postgres disabled — activity/submissions will NOT be recorded
15/06/2026 21:44:56 New Zealand Standard Time INFO mail-api: Startup test email skipped: OWNER_BCC is not set to a real address
15/06/2026 21:47:36 New Zealand Standard Time INFO mail-api: [68b31f4e] GET /auth/verify → 401 (2ms)
15/06/2026 21:47:40 New Zealand Standard Time INFO mail-api: [b6c5eb6f] auth: code issued for email=info@goodwalk.co.nz
15/06/2026 21:47:40 New Zealand Standard Time WARNING mail-api: [DEV] auth code for info@goodwalk.co.nz: 719493
15/06/2026 21:47:40 New Zealand Standard Time INFO mail-api: [b6c5eb6f] POST /auth/request-code → 200 (2ms)
15/06/2026 21:47:53 New Zealand Standard Time INFO mail-api: [d53657f2] auth: session created for email=info@goodwalk.co.nz
15/06/2026 21:47:53 New Zealand Standard Time INFO mail-api: [d53657f2] POST /auth/verify-code → 200 (1ms)
15/06/2026 21:47:53 New Zealand Standard Time INFO mail-api: [f183b75a] GET /auth/verify → 200 (0ms)
15/06/2026 21:47:53 New Zealand Standard Time INFO mail-api: [cb914009] GET /owner/all-clients → 200 (2ms)
15/06/2026 21:47:53 New Zealand Standard Time INFO mail-api: [e4eeae45] GET /owner/pending-onboarding → 200 (2ms)
15/06/2026 21:47:53 New Zealand Standard Time INFO mail-api: [05e1416f] GET /owner/birthdays → 200 (9ms)
15/06/2026 21:47:58 New Zealand Standard Time INFO mail-api: [bc6b815a] GET /owner/client-onboarding-view → 200 (8ms)
15/06/2026 21:49:47 New Zealand Standard Time INFO mail-api: Logging initialised → console=INFO, file=logs\mail-api.log (DEBUG, rotating)
15/06/2026 21:49:47 New Zealand Standard Time INFO mail-api: Mail API config: version='unknown' timezone='system-default' from='GoodWalk <info@goodwalk.co.nz>' reply_to='info@goodwalk.co.nz' owner='info@goodwalk.co.nz' cp_admins=['info@goodwalk.co.nz'] owner_bcc='' client_bcc='' general_enquiries=False max_attempts=3 form_min=1s form_max=7200s rate_window=900s per_ip=50 per_email=50 min_interval=1s send_timeout=20s
15/06/2026 21:49:47 New Zealand Standard Time INFO mail-api: Auth: loaded 3 allowed email(s)
15/06/2026 21:49:47 New Zealand Standard Time ERROR mail-api: Startup smoke: WeasyPrint UNAVAILABLE — PDF attachments will be skipped (No module named 'weasyprint')
15/06/2026 21:49:47 New Zealand Standard Time WARNING mail-api: Startup smoke: postgres disabled — activity/submissions will NOT be recorded
15/06/2026 21:49:47 New Zealand Standard Time INFO mail-api: Startup test email skipped: OWNER_BCC is not set to a real address
15/06/2026 21:50:00 New Zealand Standard Time INFO mail-api: Logging initialised → console=INFO, file=logs\mail-api.log (DEBUG, rotating)
15/06/2026 21:50:00 New Zealand Standard Time INFO mail-api: Mail API config: version='unknown' timezone='system-default' from='GoodWalk <info@goodwalk.co.nz>' reply_to='info@goodwalk.co.nz' owner='info@goodwalk.co.nz' cp_admins=['info@goodwalk.co.nz'] owner_bcc='' client_bcc='' general_enquiries=False max_attempts=3 form_min=1s form_max=7200s rate_window=900s per_ip=50 per_email=50 min_interval=1s send_timeout=20s
15/06/2026 21:50:00 New Zealand Standard Time INFO mail-api: Auth: loaded 3 allowed email(s)
15/06/2026 21:50:00 New Zealand Standard Time ERROR mail-api: Startup smoke: WeasyPrint UNAVAILABLE — PDF attachments will be skipped (No module named 'weasyprint')
15/06/2026 21:50:00 New Zealand Standard Time WARNING mail-api: Startup smoke: postgres disabled — activity/submissions will NOT be recorded
15/06/2026 21:50:00 New Zealand Standard Time INFO mail-api: Startup test email skipped: OWNER_BCC is not set to a real address
15/06/2026 21:52:22 New Zealand Standard Time INFO mail-api: [d28300fe] GET /owner/message-templates → 401 (3ms)
15/06/2026 21:52:23 New Zealand Standard Time INFO mail-api: [0ea10897] GET /owner/activity → 401 (1ms)
15/06/2026 21:52:24 New Zealand Standard Time INFO mail-api: [82beeffb] GET /owner/message-templates → 401 (0ms)
15/06/2026 21:52:28 New Zealand Standard Time INFO mail-api: [d4e90d6f] GET /auth/verify → 401 (1ms)
16/06/2026 06:29:29 New Zealand Standard Time INFO mail-api: Logging initialised → console=INFO, file=logs\mail-api.log (DEBUG, rotating)
16/06/2026 06:29:29 New Zealand Standard Time INFO mail-api: Mail API config: version='unknown' timezone='system-default' from='GoodWalk <info@goodwalk.co.nz>' reply_to='info@goodwalk.co.nz' owner='info@goodwalk.co.nz' cp_admins=['info@goodwalk.co.nz'] owner_bcc='' client_bcc='' general_enquiries=False max_attempts=3 form_min=1s form_max=7200s rate_window=900s per_ip=50 per_email=50 min_interval=1s send_timeout=20s
16/06/2026 06:29:29 New Zealand Standard Time INFO mail-api: Auth: loaded 3 allowed email(s)
16/06/2026 06:29:29 New Zealand Standard Time ERROR mail-api: Startup smoke: WeasyPrint UNAVAILABLE — PDF attachments will be skipped (No module named 'weasyprint')
16/06/2026 06:29:29 New Zealand Standard Time WARNING mail-api: Startup smoke: postgres disabled — activity/submissions will NOT be recorded
16/06/2026 06:29:29 New Zealand Standard Time INFO mail-api: Startup test email skipped: OWNER_BCC is not set to a real address
16/06/2026 06:30:42 New Zealand Standard Time INFO mail-api: Logging initialised -> console=INFO, file=logs\mail-api.log (DEBUG, rotating)
16/06/2026 06:30:42 New Zealand Standard Time INFO mail-api: Mail API config: version='unknown' timezone='system-default' from='GoodWalk <info@goodwalk.co.nz>' reply_to='info@goodwalk.co.nz' owner='info@goodwalk.co.nz' cp_admins=['info@goodwalk.co.nz'] owner_bcc='' client_bcc='' general_enquiries=False max_attempts=3 form_min=1s form_max=7200s rate_window=900s per_ip=50 per_email=50 min_interval=1s send_timeout=20s
16/06/2026 06:30:42 New Zealand Standard Time INFO mail-api: Auth: loaded 3 allowed email(s)
16/06/2026 06:30:42 New Zealand Standard Time ERROR mail-api: Startup smoke: WeasyPrint UNAVAILABLE — PDF attachments will be skipped (No module named 'weasyprint')
16/06/2026 06:30:42 New Zealand Standard Time WARNING mail-api: Startup smoke: postgres disabled — activity/submissions will NOT be recorded
16/06/2026 06:30:42 New Zealand Standard Time INFO mail-api: Startup test email skipped: OWNER_BCC is not set to a real address
16/06/2026 06:31:28 New Zealand Standard Time INFO mail-api: Logging initialised -> console=INFO, file=logs\mail-api.log (DEBUG, rotating)
16/06/2026 06:31:28 New Zealand Standard Time INFO mail-api: Mail API config: version='unknown' timezone='system-default' from='GoodWalk <info@goodwalk.co.nz>' reply_to='info@goodwalk.co.nz' owner='info@goodwalk.co.nz' cp_admins=['info@goodwalk.co.nz'] owner_bcc='' client_bcc='' general_enquiries=False max_attempts=3 form_min=1s form_max=7200s rate_window=900s per_ip=50 per_email=50 min_interval=1s send_timeout=20s
16/06/2026 06:31:28 New Zealand Standard Time INFO mail-api: Auth: loaded 3 allowed email(s)
16/06/2026 06:31:28 New Zealand Standard Time ERROR mail-api: Startup smoke: WeasyPrint UNAVAILABLE — PDF attachments will be skipped (No module named 'weasyprint')
16/06/2026 06:31:28 New Zealand Standard Time WARNING mail-api: Startup smoke: postgres disabled — activity/submissions will NOT be recorded
16/06/2026 06:31:28 New Zealand Standard Time INFO mail-api: Startup test email skipped: OWNER_BCC is not set to a real address
16/06/2026 06:38:14 New Zealand Standard Time INFO mail-api: [48563882] auth: code issued for email=info@goodwalk.co.nz
16/06/2026 06:38:14 New Zealand Standard Time WARNING mail-api: [DEV] auth code for info@goodwalk.co.nz: 926365
16/06/2026 06:38:14 New Zealand Standard Time INFO mail-api: [48563882] POST /auth/request-code → 200 (3ms)
16/06/2026 06:38:25 New Zealand Standard Time INFO mail-api: [5664ed1d] auth: session created for email=info@goodwalk.co.nz
16/06/2026 06:38:25 New Zealand Standard Time INFO mail-api: [5664ed1d] POST /auth/verify-code → 200 (3ms)
16/06/2026 06:38:25 New Zealand Standard Time INFO mail-api: [0bb03468] GET /auth/verify → 200 (1ms)
16/06/2026 06:38:25 New Zealand Standard Time INFO mail-api: [6f38622c] GET /owner/pending-onboarding → 200 (2ms)
16/06/2026 06:38:25 New Zealand Standard Time INFO mail-api: [3d0c8bcf] GET /owner/all-clients → 200 (10ms)
16/06/2026 06:38:25 New Zealand Standard Time INFO mail-api: [0b3eb923] GET /owner/birthdays → 200 (11ms)
16/06/2026 06:38:49 New Zealand Standard Time INFO mail-api: [1ea3d806] GET /owner/message-templates → 200 (1ms)
16/06/2026 06:38:49 New Zealand Standard Time INFO mail-api: [88793213] POST /owner/render-message → 200 (4ms)
16/06/2026 06:38:51 New Zealand Standard Time INFO mail-api: [fb1a9f6e] GET /owner/activity → 200 (1ms)
16/06/2026 06:40:29 New Zealand Standard Time INFO mail-api: [31e4240c] GET /owner/client-enquiry → 200 (2ms)
16/06/2026 06:41:47 New Zealand Standard Time INFO mail-api: [121f9f98] GET /owner/activity → 200 (0ms)
16/06/2026 06:44:02 New Zealand Standard Time INFO mail-api: [5a77951c] GET /auth/verify → 200 (0ms)
16/06/2026 06:44:02 New Zealand Standard Time INFO mail-api: [737112c8] GET /owner/pending-onboarding → 200 (1ms)
16/06/2026 06:44:02 New Zealand Standard Time INFO mail-api: [d9a6813f] GET /owner/all-clients → 200 (1ms)
16/06/2026 06:44:02 New Zealand Standard Time INFO mail-api: [46903a39] GET /owner/birthdays → 200 (2ms)
16/06/2026 06:45:04 New Zealand Standard Time INFO mail-api: [a241498f] GET /auth/verify → 200 (0ms)
16/06/2026 06:45:04 New Zealand Standard Time INFO mail-api: [a306e4a4] GET /owner/pending-onboarding → 200 (1ms)
16/06/2026 06:45:04 New Zealand Standard Time INFO mail-api: [6e060204] GET /owner/all-clients → 200 (1ms)
16/06/2026 06:45:04 New Zealand Standard Time INFO mail-api: [58ffe4c7] GET /owner/birthdays → 200 (3ms)
16/06/2026 06:45:17 New Zealand Standard Time INFO mail-api: [56c5f41a] GET /auth/verify → 200 (0ms)
16/06/2026 06:45:17 New Zealand Standard Time INFO mail-api: [f49f41b6] GET /owner/pending-onboarding → 200 (1ms)
16/06/2026 06:45:17 New Zealand Standard Time INFO mail-api: [bd4fd420] GET /owner/all-clients → 200 (1ms)
16/06/2026 06:45:17 New Zealand Standard Time INFO mail-api: [d4b0ada0] GET /owner/birthdays → 200 (2ms)
16/06/2026 06:50:33 New Zealand Standard Time INFO mail-api: [e2163ae1] GET /owner/client-enquiry → 200 (0ms)
16/06/2026 06:51:46 New Zealand Standard Time INFO mail-api: [16d4acbc] GET /owner/message-templates → 200 (1ms)
16/06/2026 06:51:46 New Zealand Standard Time INFO mail-api: [049712df] POST /owner/render-message → 200 (1ms)
16/06/2026 06:51:47 New Zealand Standard Time INFO mail-api: [ff1f1f13] GET /owner/activity → 200 (0ms)
16/06/2026 06:52:05 New Zealand Standard Time INFO mail-api: [e3758e0f] GET /owner/all-clients → 200 (1ms)
16/06/2026 06:52:51 New Zealand Standard Time INFO mail-api: [80d0c427] GET /owner/activity → 200 (0ms)
16/06/2026 06:54:39 New Zealand Standard Time INFO mail-api: [ae3c4fd3] GET /owner/activity → 200 (1ms)
16/06/2026 06:55:26 New Zealand Standard Time INFO mail-api: [d6b285f1] GET /owner/all-clients → 200 (1ms)
16/06/2026 06:55:30 New Zealand Standard Time INFO mail-api: [24446f12] GET /owner/client-enquiry → 200 (0ms)
16/06/2026 06:55:43 New Zealand Standard Time INFO mail-api: [f47fdfc6] GET /owner/activity → 200 (0ms)
16/06/2026 06:56:04 New Zealand Standard Time INFO mail-api: [ef4b6c2a] GET /owner/client-enquiry → 200 (0ms)
16/06/2026 06:56:21 New Zealand Standard Time INFO mail-api: [59ef0c42] GET /owner/activity → 200 (1ms)
16/06/2026 06:57:43 New Zealand Standard Time INFO mail-api: [ce3133ef] GET /auth/verify → 200 (0ms)
16/06/2026 06:57:43 New Zealand Standard Time INFO mail-api: [c6a5dfeb] GET /owner/all-clients → 200 (1ms)
16/06/2026 06:57:43 New Zealand Standard Time INFO mail-api: [de7d24a0] GET /owner/pending-onboarding → 200 (1ms)
16/06/2026 06:57:43 New Zealand Standard Time INFO mail-api: [bf7f5696] GET /owner/birthdays → 200 (0ms)
16/06/2026 06:57:46 New Zealand Standard Time INFO mail-api: [08a9d0a6] GET /owner/message-templates → 200 (1ms)
16/06/2026 06:57:46 New Zealand Standard Time INFO mail-api: [9388927c] POST /owner/render-message → 200 (1ms)
16/06/2026 07:00:13 New Zealand Standard Time INFO mail-api: [39b85a36] GET /owner/activity → 200 (0ms)
16/06/2026 07:02:06 New Zealand Standard Time INFO mail-api: [2fbeef76] GET /owner/all-clients → 200 (1ms)
16/06/2026 07:06:11 New Zealand Standard Time INFO mail-api: [1c8a494a] GET /owner/activity → 200 (1ms)
16/06/2026 07:06:42 New Zealand Standard Time INFO mail-api: [41ddcc81] GET /owner/activity → 200 (0ms)
16/06/2026 07:13:10 New Zealand Standard Time INFO mail-api: [93328ec4] GET /owner/all-clients → 200 (1ms)
16/06/2026 07:14:02 New Zealand Standard Time INFO mail-api: Logging initialised -> console=INFO, file=logs\mail-api.log (DEBUG, rotating)
16/06/2026 07:14:02 New Zealand Standard Time INFO mail-api: Mail API config: version='unknown' timezone='system-default' from='GoodWalk <info@goodwalk.co.nz>' reply_to='info@goodwalk.co.nz' owner='info@goodwalk.co.nz' cp_admins=['info@goodwalk.co.nz'] owner_bcc='' client_bcc='' general_enquiries=False max_attempts=3 form_min=1s form_max=7200s rate_window=900s per_ip=50 per_email=50 min_interval=1s send_timeout=20s
16/06/2026 07:14:02 New Zealand Standard Time INFO mail-api: Auth: loaded 3 allowed email(s)
16/06/2026 07:14:02 New Zealand Standard Time ERROR mail-api: Startup smoke: WeasyPrint UNAVAILABLE — PDF attachments will be skipped (No module named 'weasyprint')
16/06/2026 07:14:02 New Zealand Standard Time WARNING mail-api: Startup smoke: postgres disabled — activity/submissions will NOT be recorded
16/06/2026 07:14:02 New Zealand Standard Time INFO mail-api: Startup test email skipped: OWNER_BCC is not set to a real address
16/06/2026 07:14:08 New Zealand Standard Time INFO mail-api: Logging initialised -> console=INFO, file=logs\mail-api.log (DEBUG, rotating)
16/06/2026 07:14:08 New Zealand Standard Time INFO mail-api: Mail API config: version='unknown' timezone='system-default' from='GoodWalk <info@goodwalk.co.nz>' reply_to='info@goodwalk.co.nz' owner='info@goodwalk.co.nz' cp_admins=['info@goodwalk.co.nz'] owner_bcc='' client_bcc='' general_enquiries=False max_attempts=3 form_min=1s form_max=7200s rate_window=900s per_ip=50 per_email=50 min_interval=1s send_timeout=20s
16/06/2026 07:14:08 New Zealand Standard Time INFO mail-api: Auth: loaded 3 allowed email(s)
16/06/2026 07:14:08 New Zealand Standard Time ERROR mail-api: Startup smoke: WeasyPrint UNAVAILABLE — PDF attachments will be skipped (No module named 'weasyprint')
16/06/2026 07:14:08 New Zealand Standard Time WARNING mail-api: Startup smoke: postgres disabled — activity/submissions will NOT be recorded
16/06/2026 07:14:08 New Zealand Standard Time INFO mail-api: Startup test email skipped: OWNER_BCC is not set to a real address
16/06/2026 07:14:25 New Zealand Standard Time INFO mail-api: Logging initialised -> console=INFO, file=logs\mail-api.log (DEBUG, rotating)
16/06/2026 07:14:25 New Zealand Standard Time INFO mail-api: Mail API config: version='unknown' timezone='system-default' from='GoodWalk <info@goodwalk.co.nz>' reply_to='info@goodwalk.co.nz' owner='info@goodwalk.co.nz' cp_admins=['info@goodwalk.co.nz'] owner_bcc='' client_bcc='' general_enquiries=False max_attempts=3 form_min=1s form_max=7200s rate_window=900s per_ip=50 per_email=50 min_interval=1s send_timeout=20s
16/06/2026 07:14:31 New Zealand Standard Time INFO mail-api: Logging initialised -> console=INFO, file=logs\mail-api.log (DEBUG, rotating)
16/06/2026 07:14:31 New Zealand Standard Time INFO mail-api: Mail API config: version='unknown' timezone='system-default' from='GoodWalk <info@goodwalk.co.nz>' reply_to='info@goodwalk.co.nz' owner='info@goodwalk.co.nz' cp_admins=['info@goodwalk.co.nz'] owner_bcc='' client_bcc='' general_enquiries=False max_attempts=3 form_min=1s form_max=7200s rate_window=900s per_ip=50 per_email=50 min_interval=1s send_timeout=20s
16/06/2026 07:14:31 New Zealand Standard Time INFO mail-api: Auth: loaded 3 allowed email(s)
16/06/2026 07:14:31 New Zealand Standard Time ERROR mail-api: Startup smoke: WeasyPrint UNAVAILABLE — PDF attachments will be skipped (No module named 'weasyprint')
16/06/2026 07:14:31 New Zealand Standard Time WARNING mail-api: Startup smoke: postgres disabled — activity/submissions will NOT be recorded
16/06/2026 07:14:31 New Zealand Standard Time INFO mail-api: Startup test email skipped: OWNER_BCC is not set to a real address
16/06/2026 07:14:46 New Zealand Standard Time INFO mail-api: Logging initialised -> console=INFO, file=logs\mail-api.log (DEBUG, rotating)
16/06/2026 07:14:46 New Zealand Standard Time INFO mail-api: Mail API config: version='unknown' timezone='system-default' from='GoodWalk <info@goodwalk.co.nz>' reply_to='info@goodwalk.co.nz' owner='info@goodwalk.co.nz' cp_admins=['info@goodwalk.co.nz'] owner_bcc='' client_bcc='' general_enquiries=False max_attempts=3 form_min=1s form_max=7200s rate_window=900s per_ip=50 per_email=50 min_interval=1s send_timeout=20s
16/06/2026 07:14:46 New Zealand Standard Time INFO mail-api: Auth: loaded 3 allowed email(s)
16/06/2026 07:14:46 New Zealand Standard Time ERROR mail-api: Startup smoke: WeasyPrint UNAVAILABLE — PDF attachments will be skipped (No module named 'weasyprint')
16/06/2026 07:14:46 New Zealand Standard Time WARNING mail-api: Startup smoke: postgres disabled — activity/submissions will NOT be recorded
16/06/2026 07:14:46 New Zealand Standard Time INFO mail-api: Startup test email skipped: OWNER_BCC is not set to a real address
16/06/2026 07:14:59 New Zealand Standard Time INFO mail-api: Logging initialised -> console=INFO, file=logs\mail-api.log (DEBUG, rotating)
16/06/2026 07:14:59 New Zealand Standard Time INFO mail-api: Mail API config: version='unknown' timezone='system-default' from='GoodWalk <info@goodwalk.co.nz>' reply_to='info@goodwalk.co.nz' owner='info@goodwalk.co.nz' cp_admins=['info@goodwalk.co.nz'] owner_bcc='' client_bcc='' general_enquiries=False max_attempts=3 form_min=1s form_max=7200s rate_window=900s per_ip=50 per_email=50 min_interval=1s send_timeout=20s
16/06/2026 07:14:59 New Zealand Standard Time INFO mail-api: Auth: loaded 3 allowed email(s)
16/06/2026 07:14:59 New Zealand Standard Time ERROR mail-api: Startup smoke: WeasyPrint UNAVAILABLE — PDF attachments will be skipped (No module named 'weasyprint')
16/06/2026 07:14:59 New Zealand Standard Time WARNING mail-api: Startup smoke: postgres disabled — activity/submissions will NOT be recorded
16/06/2026 07:14:59 New Zealand Standard Time INFO mail-api: Startup test email skipped: OWNER_BCC is not set to a real address
16/06/2026 07:15:06 New Zealand Standard Time INFO mail-api: Logging initialised -> console=INFO, file=logs\mail-api.log (DEBUG, rotating)
16/06/2026 07:15:06 New Zealand Standard Time INFO mail-api: Mail API config: version='unknown' timezone='system-default' from='GoodWalk <info@goodwalk.co.nz>' reply_to='info@goodwalk.co.nz' owner='info@goodwalk.co.nz' cp_admins=['info@goodwalk.co.nz'] owner_bcc='' client_bcc='' general_enquiries=False max_attempts=3 form_min=1s form_max=7200s rate_window=900s per_ip=50 per_email=50 min_interval=1s send_timeout=20s
16/06/2026 07:15:06 New Zealand Standard Time INFO mail-api: Auth: loaded 3 allowed email(s)
16/06/2026 07:15:06 New Zealand Standard Time ERROR mail-api: Startup smoke: WeasyPrint UNAVAILABLE — PDF attachments will be skipped (No module named 'weasyprint')
16/06/2026 07:15:06 New Zealand Standard Time WARNING mail-api: Startup smoke: postgres disabled — activity/submissions will NOT be recorded
16/06/2026 07:15:06 New Zealand Standard Time INFO mail-api: Startup test email skipped: OWNER_BCC is not set to a real address
16/06/2026 07:15:24 New Zealand Standard Time INFO mail-api: Logging initialised -> console=INFO, file=logs\mail-api.log (DEBUG, rotating)
16/06/2026 07:15:24 New Zealand Standard Time INFO mail-api: Mail API config: version='unknown' timezone='system-default' from='GoodWalk <info@goodwalk.co.nz>' reply_to='info@goodwalk.co.nz' owner='info@goodwalk.co.nz' cp_admins=['info@goodwalk.co.nz'] owner_bcc='' client_bcc='' general_enquiries=False max_attempts=3 form_min=1s form_max=7200s rate_window=900s per_ip=50 per_email=50 min_interval=1s send_timeout=20s
16/06/2026 07:15:24 New Zealand Standard Time INFO mail-api: Auth: loaded 3 allowed email(s)
16/06/2026 07:15:24 New Zealand Standard Time ERROR mail-api: Startup smoke: WeasyPrint UNAVAILABLE — PDF attachments will be skipped (No module named 'weasyprint')
16/06/2026 07:15:24 New Zealand Standard Time WARNING mail-api: Startup smoke: postgres disabled — activity/submissions will NOT be recorded
16/06/2026 07:15:24 New Zealand Standard Time INFO mail-api: Startup test email skipped: OWNER_BCC is not set to a real address
16/06/2026 07:15:50 New Zealand Standard Time INFO mail-api: [825c3ae9] GET /auth/verify → 401 (2ms)
16/06/2026 07:15:54 New Zealand Standard Time INFO mail-api: [d2197895] auth: code issued for email=info@goodwalk.co.nz
16/06/2026 07:15:54 New Zealand Standard Time WARNING mail-api: [DEV] auth code for info@goodwalk.co.nz: 269988
16/06/2026 07:15:54 New Zealand Standard Time INFO mail-api: [d2197895] POST /auth/request-code → 200 (2ms)
16/06/2026 07:16:05 New Zealand Standard Time INFO mail-api: [f3567591] auth: session created for email=info@goodwalk.co.nz
16/06/2026 07:16:05 New Zealand Standard Time INFO mail-api: [f3567591] POST /auth/verify-code → 200 (2ms)
16/06/2026 07:16:05 New Zealand Standard Time INFO mail-api: [f568d6aa] GET /auth/verify → 200 (0ms)
16/06/2026 07:16:05 New Zealand Standard Time INFO mail-api: [15422225] GET /owner/pending-onboarding → 200 (2ms)
16/06/2026 07:16:05 New Zealand Standard Time INFO mail-api: [560b7831] GET /owner/all-clients → 200 (5ms)
16/06/2026 07:16:05 New Zealand Standard Time INFO mail-api: [b7db7b94] GET /owner/birthdays → 200 (5ms)
16/06/2026 07:16:20 New Zealand Standard Time INFO mail-api: [b1f1abfd] GET /owner/all-clients → 200 (1ms)
16/06/2026 07:16:46 New Zealand Standard Time INFO mail-api: [c80114ed] GET /owner/all-clients → 200 (1ms)
16/06/2026 07:16:57 New Zealand Standard Time INFO mail-api: [f47002dd] GET /owner/message-templates → 200 (1ms)
16/06/2026 07:16:57 New Zealand Standard Time INFO mail-api: [949a4211] POST /owner/render-message → 200 (2ms)
16/06/2026 07:17:07 New Zealand Standard Time INFO mail-api: Logging initialised -> console=INFO, file=logs\mail-api.log (DEBUG, rotating)
16/06/2026 07:17:07 New Zealand Standard Time INFO mail-api: Mail API config: version='unknown' timezone='system-default' from='GoodWalk <info@goodwalk.co.nz>' reply_to='info@goodwalk.co.nz' owner='info@goodwalk.co.nz' cp_admins=['info@goodwalk.co.nz'] owner_bcc='' client_bcc='' general_enquiries=False max_attempts=3 form_min=1s form_max=7200s rate_window=900s per_ip=50 per_email=50 min_interval=1s send_timeout=20s
16/06/2026 07:17:07 New Zealand Standard Time INFO mail-api: Auth: loaded 3 allowed email(s)
16/06/2026 07:17:07 New Zealand Standard Time ERROR mail-api: Startup smoke: WeasyPrint UNAVAILABLE — PDF attachments will be skipped (No module named 'weasyprint')
16/06/2026 07:17:07 New Zealand Standard Time WARNING mail-api: Startup smoke: postgres disabled — activity/submissions will NOT be recorded
16/06/2026 07:17:07 New Zealand Standard Time INFO mail-api: Startup test email skipped: OWNER_BCC is not set to a real address
16/06/2026 07:17:13 New Zealand Standard Time INFO mail-api: Logging initialised -> console=INFO, file=logs\mail-api.log (DEBUG, rotating)
16/06/2026 07:17:13 New Zealand Standard Time INFO mail-api: Mail API config: version='unknown' timezone='system-default' from='GoodWalk <info@goodwalk.co.nz>' reply_to='info@goodwalk.co.nz' owner='info@goodwalk.co.nz' cp_admins=['info@goodwalk.co.nz'] owner_bcc='' client_bcc='' general_enquiries=False max_attempts=3 form_min=1s form_max=7200s rate_window=900s per_ip=50 per_email=50 min_interval=1s send_timeout=20s
16/06/2026 07:17:13 New Zealand Standard Time INFO mail-api: Auth: loaded 3 allowed email(s)
16/06/2026 07:17:13 New Zealand Standard Time ERROR mail-api: Startup smoke: WeasyPrint UNAVAILABLE — PDF attachments will be skipped (No module named 'weasyprint')
16/06/2026 07:17:13 New Zealand Standard Time WARNING mail-api: Startup smoke: postgres disabled — activity/submissions will NOT be recorded
16/06/2026 07:17:13 New Zealand Standard Time INFO mail-api: Startup test email skipped: OWNER_BCC is not set to a real address
16/06/2026 07:17:20 New Zealand Standard Time INFO mail-api: Logging initialised -> console=INFO, file=logs\mail-api.log (DEBUG, rotating)
16/06/2026 07:17:20 New Zealand Standard Time INFO mail-api: Mail API config: version='unknown' timezone='system-default' from='GoodWalk <info@goodwalk.co.nz>' reply_to='info@goodwalk.co.nz' owner='info@goodwalk.co.nz' cp_admins=['info@goodwalk.co.nz'] owner_bcc='' client_bcc='' general_enquiries=False max_attempts=3 form_min=1s form_max=7200s rate_window=900s per_ip=50 per_email=50 min_interval=1s send_timeout=20s
16/06/2026 07:17:20 New Zealand Standard Time INFO mail-api: Auth: loaded 3 allowed email(s)
16/06/2026 07:17:20 New Zealand Standard Time ERROR mail-api: Startup smoke: WeasyPrint UNAVAILABLE — PDF attachments will be skipped (No module named 'weasyprint')
16/06/2026 07:17:20 New Zealand Standard Time WARNING mail-api: Startup smoke: postgres disabled — activity/submissions will NOT be recorded
16/06/2026 07:17:20 New Zealand Standard Time INFO mail-api: Startup test email skipped: OWNER_BCC is not set to a real address
16/06/2026 07:17:27 New Zealand Standard Time INFO mail-api: Logging initialised -> console=INFO, file=logs\mail-api.log (DEBUG, rotating)
16/06/2026 07:17:27 New Zealand Standard Time INFO mail-api: Mail API config: version='unknown' timezone='system-default' from='GoodWalk <info@goodwalk.co.nz>' reply_to='info@goodwalk.co.nz' owner='info@goodwalk.co.nz' cp_admins=['info@goodwalk.co.nz'] owner_bcc='' client_bcc='' general_enquiries=False max_attempts=3 form_min=1s form_max=7200s rate_window=900s per_ip=50 per_email=50 min_interval=1s send_timeout=20s
16/06/2026 07:17:27 New Zealand Standard Time INFO mail-api: Auth: loaded 3 allowed email(s)
16/06/2026 07:17:27 New Zealand Standard Time ERROR mail-api: Startup smoke: WeasyPrint UNAVAILABLE — PDF attachments will be skipped (No module named 'weasyprint')
16/06/2026 07:17:27 New Zealand Standard Time WARNING mail-api: Startup smoke: postgres disabled — activity/submissions will NOT be recorded
16/06/2026 07:17:27 New Zealand Standard Time INFO mail-api: Startup test email skipped: OWNER_BCC is not set to a real address
16/06/2026 07:18:01 New Zealand Standard Time INFO mail-api: [8cdf225d] GET /auth/verify → 401 (2ms)
16/06/2026 07:18:05 New Zealand Standard Time INFO mail-api: [cbe1d0d9] auth: unknown email=info@goodwalk.co.z ip=127.0.0.1
16/06/2026 07:18:05 New Zealand Standard Time WARNING mail-api: [cbe1d0d9] auth: failure ip=127.0.0.1 reason='unknown_email' total_in_window=1
16/06/2026 07:18:05 New Zealand Standard Time INFO mail-api: [cbe1d0d9] POST /auth/request-code → 403 (2ms)
16/06/2026 07:18:08 New Zealand Standard Time INFO mail-api: [008f2e1a] auth: code issued for email=info@goodwalk.co.nz
16/06/2026 07:18:08 New Zealand Standard Time WARNING mail-api: [DEV] auth code for info@goodwalk.co.nz: 952701
16/06/2026 07:18:08 New Zealand Standard Time INFO mail-api: [008f2e1a] POST /auth/request-code → 200 (1ms)
16/06/2026 07:18:14 New Zealand Standard Time INFO mail-api: Logging initialised -> console=INFO, file=logs\mail-api.log (DEBUG, rotating)
16/06/2026 07:18:14 New Zealand Standard Time INFO mail-api: Mail API config: version='unknown' timezone='system-default' from='GoodWalk <info@goodwalk.co.nz>' reply_to='info@goodwalk.co.nz' owner='info@goodwalk.co.nz' cp_admins=['info@goodwalk.co.nz'] owner_bcc='' client_bcc='' general_enquiries=False max_attempts=3 form_min=1s form_max=7200s rate_window=900s per_ip=50 per_email=50 min_interval=1s send_timeout=20s
16/06/2026 07:18:14 New Zealand Standard Time INFO mail-api: Auth: loaded 3 allowed email(s)
16/06/2026 07:18:14 New Zealand Standard Time ERROR mail-api: Startup smoke: WeasyPrint UNAVAILABLE — PDF attachments will be skipped (No module named 'weasyprint')
16/06/2026 07:18:14 New Zealand Standard Time WARNING mail-api: Startup smoke: postgres disabled — activity/submissions will NOT be recorded
16/06/2026 07:18:14 New Zealand Standard Time INFO mail-api: Startup test email skipped: OWNER_BCC is not set to a real address
16/06/2026 07:18:20 New Zealand Standard Time INFO mail-api: [369f9969] auth: code issued for email=info@goodwalk.co.nz
16/06/2026 07:18:20 New Zealand Standard Time WARNING mail-api: [DEV] auth code for info@goodwalk.co.nz: 943074
16/06/2026 07:18:20 New Zealand Standard Time INFO mail-api: [369f9969] POST /auth/request-code → 200 (3ms)
16/06/2026 07:18:31 New Zealand Standard Time INFO mail-api: [ceb1fc90] auth: code issued for email=info@goodwalk.co.nz
16/06/2026 07:18:31 New Zealand Standard Time WARNING mail-api: [DEV] auth code for info@goodwalk.co.nz: 261983
16/06/2026 07:18:31 New Zealand Standard Time INFO mail-api: [ceb1fc90] POST /auth/request-code → 200 (1ms)
16/06/2026 07:25:18 New Zealand Standard Time INFO mail-api: Logging initialised -> console=INFO, file=logs\mail-api.log (DEBUG, rotating)
16/06/2026 07:25:18 New Zealand Standard Time INFO mail-api: Mail API config: version='unknown' timezone='system-default' from='GoodWalk <info@goodwalk.co.nz>' reply_to='info@goodwalk.co.nz' owner='info@goodwalk.co.nz' cp_admins=['info@goodwalk.co.nz'] owner_bcc='' client_bcc='' general_enquiries=False max_attempts=3 form_min=1s form_max=7200s rate_window=900s per_ip=50 per_email=50 min_interval=1s send_timeout=20s
16/06/2026 07:25:18 New Zealand Standard Time INFO mail-api: Auth: loaded 3 allowed email(s)
16/06/2026 07:25:18 New Zealand Standard Time ERROR mail-api: Startup smoke: WeasyPrint UNAVAILABLE — PDF attachments will be skipped (No module named 'weasyprint')
16/06/2026 07:25:18 New Zealand Standard Time WARNING mail-api: Startup smoke: postgres disabled — activity/submissions will NOT be recorded
16/06/2026 07:25:18 New Zealand Standard Time INFO mail-api: Startup test email skipped: OWNER_BCC is not set to a real address
16/06/2026 07:25:20 New Zealand Standard Time INFO mail-api: Logging initialised -> console=INFO, file=logs\mail-api.log (DEBUG, rotating)
16/06/2026 07:25:20 New Zealand Standard Time INFO mail-api: Mail API config: version='unknown' timezone='system-default' from='GoodWalk <info@goodwalk.co.nz>' reply_to='info@goodwalk.co.nz' owner='info@goodwalk.co.nz' cp_admins=['info@goodwalk.co.nz'] owner_bcc='' client_bcc='' general_enquiries=False max_attempts=3 form_min=1s form_max=7200s rate_window=900s per_ip=50 per_email=50 min_interval=1s send_timeout=20s
16/06/2026 07:25:20 New Zealand Standard Time INFO mail-api: Auth: loaded 3 allowed email(s)
16/06/2026 07:25:20 New Zealand Standard Time ERROR mail-api: Startup smoke: WeasyPrint UNAVAILABLE — PDF attachments will be skipped (No module named 'weasyprint')
16/06/2026 07:25:20 New Zealand Standard Time WARNING mail-api: Startup smoke: postgres disabled — activity/submissions will NOT be recorded
16/06/2026 07:25:20 New Zealand Standard Time INFO mail-api: Startup test email skipped: OWNER_BCC is not set to a real address
16/06/2026 07:25:28 New Zealand Standard Time INFO mail-api: [098d12dc] auth: code issued for email=info@goodwalk.co.nz
16/06/2026 07:25:28 New Zealand Standard Time WARNING mail-api: [DEV] auth code for info@goodwalk.co.nz: 496267
16/06/2026 07:25:28 New Zealand Standard Time INFO mail-api: [098d12dc] POST /auth/request-code → 200 (4ms)
16/06/2026 07:25:35 New Zealand Standard Time INFO mail-api: [8eb8c518] auth: session created for email=info@goodwalk.co.nz
16/06/2026 07:25:35 New Zealand Standard Time INFO mail-api: [8eb8c518] POST /auth/verify-code → 200 (2ms)
16/06/2026 07:25:35 New Zealand Standard Time INFO mail-api: [b4bcbfbd] GET /auth/verify → 200 (1ms)
16/06/2026 07:25:35 New Zealand Standard Time INFO mail-api: [37f1c7a9] GET /owner/pending-onboarding → 200 (2ms)
16/06/2026 07:25:35 New Zealand Standard Time INFO mail-api: [0daa6a9c] GET /owner/all-clients → 200 (7ms)
16/06/2026 07:25:35 New Zealand Standard Time INFO mail-api: [40680736] GET /owner/birthdays → 200 (7ms)
16/06/2026 07:25:37 New Zealand Standard Time INFO mail-api: [e9db9842] POST /owner/render-welcome-pack → 200 (14ms)
16/06/2026 07:25:44 New Zealand Standard Time INFO mail-api: [13fbb03a] POST /owner/render-welcome-pack → 200 (1ms)
16/06/2026 07:25:49 New Zealand Standard Time INFO mail-api: [fb8e73e3] POST /owner/render-welcome-pack → 200 (1ms)
16/06/2026 07:25:51 New Zealand Standard Time INFO mail-api: [22db529c] POST /owner/render-welcome-pack → 200 (1ms)
16/06/2026 07:26:13 New Zealand Standard Time INFO mail-api: [806f9017] GET /owner/scheduled-emails → 200 (2ms)
16/06/2026 07:26:15 New Zealand Standard Time INFO mail-api: [2864b4ca] GET /owner/message-templates → 200 (1ms)
16/06/2026 07:26:15 New Zealand Standard Time INFO mail-api: [f133698c] POST /owner/render-message → 200 (3ms)
16/06/2026 07:47:45 New Zealand Standard Time INFO mail-api: [11b1a1dd] POST /owner/render-welcome-pack → 200 (1ms)
16/06/2026 07:48:20 New Zealand Standard Time INFO mail-api: [03817871] GET /auth/verify → 200 (0ms)
16/06/2026 07:48:20 New Zealand Standard Time INFO mail-api: [1b5b54f8] GET /owner/pending-onboarding → 200 (1ms)
16/06/2026 07:48:20 New Zealand Standard Time INFO mail-api: [d0736f04] GET /owner/all-clients → 200 (1ms)
16/06/2026 07:48:20 New Zealand Standard Time INFO mail-api: [4919bec5] GET /owner/birthdays → 200 (1ms)
16/06/2026 07:48:38 New Zealand Standard Time INFO mail-api: [3c6b1f63] GET /auth/verify → 200 (0ms)
16/06/2026 07:48:38 New Zealand Standard Time INFO mail-api: [c1ea7cf1] GET /owner/pending-onboarding → 200 (1ms)
16/06/2026 07:48:38 New Zealand Standard Time INFO mail-api: [a3a06d7a] GET /owner/all-clients → 200 (1ms)
16/06/2026 07:48:38 New Zealand Standard Time INFO mail-api: [c9a5683b] GET /owner/birthdays → 200 (2ms)
16/06/2026 07:51:48 New Zealand Standard Time INFO mail-api: Logging initialised -> console=INFO, file=logs\mail-api.log (DEBUG, rotating)
16/06/2026 07:51:48 New Zealand Standard Time INFO mail-api: Mail API config: version='unknown' timezone='system-default' from='GoodWalk <info@goodwalk.co.nz>' reply_to='info@goodwalk.co.nz' owner='info@goodwalk.co.nz' cp_admins=['info@goodwalk.co.nz'] owner_bcc='' client_bcc='' general_enquiries=False max_attempts=3 form_min=1s form_max=7200s rate_window=900s per_ip=50 per_email=50 min_interval=1s send_timeout=20s
16/06/2026 07:51:48 New Zealand Standard Time INFO mail-api: Auth: loaded 3 allowed email(s)
16/06/2026 07:51:48 New Zealand Standard Time ERROR mail-api: Startup smoke: WeasyPrint UNAVAILABLE — PDF attachments will be skipped (No module named 'weasyprint')
16/06/2026 07:51:48 New Zealand Standard Time WARNING mail-api: Startup smoke: postgres disabled — activity/submissions will NOT be recorded
16/06/2026 07:51:48 New Zealand Standard Time INFO mail-api: Startup test email skipped: OWNER_BCC is not set to a real address
16/06/2026 07:51:52 New Zealand Standard Time INFO mail-api: Logging initialised -> console=INFO, file=logs\mail-api.log (DEBUG, rotating)
16/06/2026 07:51:52 New Zealand Standard Time INFO mail-api: Mail API config: version='unknown' timezone='system-default' from='GoodWalk <info@goodwalk.co.nz>' reply_to='info@goodwalk.co.nz' owner='info@goodwalk.co.nz' cp_admins=['info@goodwalk.co.nz'] owner_bcc='' client_bcc='' general_enquiries=False max_attempts=3 form_min=1s form_max=7200s rate_window=900s per_ip=50 per_email=50 min_interval=1s send_timeout=20s
16/06/2026 07:51:53 New Zealand Standard Time INFO mail-api: Auth: loaded 3 allowed email(s)
16/06/2026 07:51:53 New Zealand Standard Time ERROR mail-api: Startup smoke: WeasyPrint UNAVAILABLE — PDF attachments will be skipped (No module named 'weasyprint')
16/06/2026 07:51:53 New Zealand Standard Time WARNING mail-api: Startup smoke: postgres disabled — activity/submissions will NOT be recorded
16/06/2026 07:51:53 New Zealand Standard Time INFO mail-api: Startup test email skipped: OWNER_BCC is not set to a real address
16/06/2026 07:51:56 New Zealand Standard Time INFO mail-api: Logging initialised -> console=INFO, file=logs\mail-api.log (DEBUG, rotating)
16/06/2026 07:51:56 New Zealand Standard Time INFO mail-api: Mail API config: version='unknown' timezone='system-default' from='GoodWalk <info@goodwalk.co.nz>' reply_to='info@goodwalk.co.nz' owner='info@goodwalk.co.nz' cp_admins=['info@goodwalk.co.nz'] owner_bcc='' client_bcc='' general_enquiries=False max_attempts=3 form_min=1s form_max=7200s rate_window=900s per_ip=50 per_email=50 min_interval=1s send_timeout=20s
16/06/2026 07:51:56 New Zealand Standard Time INFO mail-api: Auth: loaded 3 allowed email(s)
16/06/2026 07:51:56 New Zealand Standard Time ERROR mail-api: Startup smoke: WeasyPrint UNAVAILABLE — PDF attachments will be skipped (No module named 'weasyprint')
16/06/2026 07:51:56 New Zealand Standard Time WARNING mail-api: Startup smoke: postgres disabled — activity/submissions will NOT be recorded
16/06/2026 07:51:56 New Zealand Standard Time INFO mail-api: Startup test email skipped: OWNER_BCC is not set to a real address
16/06/2026 07:52:00 New Zealand Standard Time INFO mail-api: Logging initialised -> console=INFO, file=logs\mail-api.log (DEBUG, rotating)
16/06/2026 07:52:00 New Zealand Standard Time INFO mail-api: Mail API config: version='unknown' timezone='system-default' from='GoodWalk <info@goodwalk.co.nz>' reply_to='info@goodwalk.co.nz' owner='info@goodwalk.co.nz' cp_admins=['info@goodwalk.co.nz'] owner_bcc='' client_bcc='' general_enquiries=False max_attempts=3 form_min=1s form_max=7200s rate_window=900s per_ip=50 per_email=50 min_interval=1s send_timeout=20s
16/06/2026 07:52:00 New Zealand Standard Time INFO mail-api: Auth: loaded 3 allowed email(s)
16/06/2026 07:52:00 New Zealand Standard Time ERROR mail-api: Startup smoke: WeasyPrint UNAVAILABLE — PDF attachments will be skipped (No module named 'weasyprint')
16/06/2026 07:52:00 New Zealand Standard Time WARNING mail-api: Startup smoke: postgres disabled — activity/submissions will NOT be recorded
16/06/2026 07:52:00 New Zealand Standard Time INFO mail-api: Startup test email skipped: OWNER_BCC is not set to a real address
16/06/2026 07:52:54 New Zealand Standard Time INFO mail-api: [01084365] GET /owner/client-onboarding-view → 401 (3ms)
16/06/2026 07:52:55 New Zealand Standard Time INFO mail-api: [117b9eb3] GET /auth/verify → 401 (1ms)
16/06/2026 07:52:59 New Zealand Standard Time INFO mail-api: [bee5ac03] auth: code issued for email=info@goodwalk.co.nz
16/06/2026 07:52:59 New Zealand Standard Time WARNING mail-api: [DEV] auth code for info@goodwalk.co.nz: 948798
16/06/2026 07:52:59 New Zealand Standard Time INFO mail-api: [bee5ac03] POST /auth/request-code → 200 (2ms)
16/06/2026 07:53:09 New Zealand Standard Time INFO mail-api: [d782d75d] auth: session created for email=info@goodwalk.co.nz
16/06/2026 07:53:09 New Zealand Standard Time INFO mail-api: [d782d75d] POST /auth/verify-code → 200 (3ms)
16/06/2026 07:53:09 New Zealand Standard Time INFO mail-api: [a8c2db4a] GET /auth/verify → 200 (0ms)
16/06/2026 07:53:09 New Zealand Standard Time INFO mail-api: [a2bc9b29] GET /owner/pending-onboarding → 200 (9ms)
16/06/2026 07:53:09 New Zealand Standard Time INFO mail-api: [110111bd] GET /owner/birthdays → 200 (9ms)
16/06/2026 07:53:09 New Zealand Standard Time INFO mail-api: [2fe1e0d1] GET /owner/all-clients → 200 (10ms)
16/06/2026 07:53:12 New Zealand Standard Time INFO mail-api: [3f755a79] GET /owner/client-onboarding-view → 200 (0ms)
17/06/2026 07:22:07 New Zealand Standard Time INFO mail-api: Logging initialised -> console=INFO, file=logs\mail-api.log (DEBUG, rotating)
17/06/2026 07:22:07 New Zealand Standard Time INFO mail-api: Mail API config: version='unknown' timezone='system-default' from='GoodWalk <info@goodwalk.co.nz>' reply_to='info@goodwalk.co.nz' owner='dev@localhost' cp_admins=['dev@localhost'] owner_bcc='' client_bcc='' general_enquiries=False max_attempts=3 form_min=1s form_max=7200s rate_window=900s per_ip=50 per_email=50 min_interval=1s send_timeout=20s
17/06/2026 07:22:07 New Zealand Standard Time INFO mail-api: MYOB integration: disabled (set MYOB_* env vars to enable)
Binary file not shown.
Binary file not shown.
Binary file not shown.
+31 -1
View File
@@ -48,7 +48,7 @@ def setup_logging() -> logging.Logger:
root.addHandler(rotating)
log = logging.getLogger("mail-api")
log.info("Logging initialised console=INFO, file=%s (DEBUG, rotating)", log_file)
log.info("Logging initialised -> console=INFO, file=%s (DEBUG, rotating)", log_file)
return log
@@ -185,6 +185,10 @@ AUTH_IP_MAX_FAILURES = max(3, int(os.environ.get("AUTH_IP_MAX_FAILURES", "10")))
AUTH_IP_FAILURE_WINDOW = max(60, int(os.environ.get("AUTH_IP_FAILURE_WINDOW", "600")))
AUTH_IP_BLOCK_DURATION = max(60, int(os.environ.get("AUTH_IP_BLOCK_DURATION", "3600")))
BIRTHDAY_CHECK_INTERVAL_SECONDS = max(3600, int(os.environ.get("BIRTHDAY_CHECK_INTERVAL_SECONDS", str(12 * 3600))))
# How often the scheduled-email worker wakes to dispatch any sends that have
# come due. Scheduled welcome emails need finer granularity than the birthday
# sweep, so this defaults to one minute (floored at 15s).
SCHEDULED_CHECK_INTERVAL_SECONDS = max(15, int(os.environ.get("SCHEDULED_CHECK_INTERVAL_SECONDS", "60")))
def _split_csv_env(name: str, default: str) -> tuple[str, ...]:
@@ -211,6 +215,10 @@ _DATA_DIR = Path(os.environ.get("DATA_DIR", "data"))
ALLOWED_EMAILS_FILE = _DATA_DIR / "allowed_emails.json"
CLIENT_PROFILES_FILE = _DATA_DIR / "client_profiles.json"
DRAFTS_FILE = _DATA_DIR / "drafts.json"
# Durable queue of owner-scheduled emails (welcome packs queued for later send).
# Mirrors the client_profiles persistence model: postgres admin_kv is canonical
# in production, with this JSON file as the dev/local fallback and seed source.
SCHEDULED_EMAILS_FILE = _DATA_DIR / "scheduled_emails.json"
# Legacy seed lives OUTSIDE the data volume — it's shipped in the image so a
# fresh deploy always carries the same baked-in copy. On boot the mail-api
# merges this dict into _client_profiles, adding any emails that aren't
@@ -221,6 +229,27 @@ LEGACY_SEED_FILE = Path(
LOGO_URL = "https://www.goodwalk.co.nz/images/goodwalk-auckland-dog-walking-logo.png"
# ── MYOB integration ───────────────────────────────────────────────────────────
# Optional: create new clients as customer contacts in MYOB AccountRight Live.
# The integration stays disabled until every required value is present, so an
# unconfigured environment behaves exactly as before.
#
# MYOB_API_KEY / MYOB_API_SECRET the developer app's OAuth client id/secret
# MYOB_REFRESH_TOKEN long-lived token used to mint access tokens
# MYOB_COMPANY_FILE_URI base API URI of the target company file,
# e.g. https://api.myob.com/accountright/<guid>
# MYOB_CF_USERNAME / MYOB_CF_PASSWORD company-file login (password may be blank)
# MYOB_TOKEN_URL OAuth token endpoint (override only for tests)
MYOB_API_KEY = (os.environ.get("MYOB_API_KEY") or "").strip()
MYOB_API_SECRET = (os.environ.get("MYOB_API_SECRET") or "").strip()
MYOB_REFRESH_TOKEN = (os.environ.get("MYOB_REFRESH_TOKEN") or "").strip()
MYOB_COMPANY_FILE_URI = (os.environ.get("MYOB_COMPANY_FILE_URI") or "").strip().rstrip("/")
MYOB_CF_USERNAME = (os.environ.get("MYOB_CF_USERNAME") or "Administrator").strip()
MYOB_CF_PASSWORD = os.environ.get("MYOB_CF_PASSWORD", "")
MYOB_TOKEN_URL = (os.environ.get("MYOB_TOKEN_URL") or "https://secure.myob.com/oauth2/v1/authorize").strip()
MYOB_HTTP_TIMEOUT_SECONDS = max(5, int(os.environ.get("MYOB_HTTP_TIMEOUT_SECONDS", "20")))
MYOB_ENABLED = bool(MYOB_API_KEY and MYOB_API_SECRET and MYOB_REFRESH_TOKEN and MYOB_COMPANY_FILE_URI)
# ── Legacy module constants (kept for compatibility with the existing main.py) ──
OWNER_EMAIL = settings.owner_email
@@ -268,3 +297,4 @@ logger.info(
RATE_LIMIT_MIN_INTERVAL_SECONDS,
EMAIL_SEND_TIMEOUT_SECONDS,
)
logger.info("MYOB integration: %s", "enabled" if MYOB_ENABLED else "disabled (set MYOB_* env vars to enable)")
+59
View File
@@ -41,9 +41,13 @@ class OnboardingSubmission(BaseSubmission):
medicalNotes: str = ""
accessInstructions: str = ""
vetName: str
vetAddress: str
vetPhone: str
emergencyContactName: str
emergencyContactPhone: str
regularFleaTickTreatment: str = ""
petInsurance: str = ""
petInsuranceOwnerExpenseAccepted: bool = False
councilRegistrationConfirmed: bool = False
vaccinationsConfirmed: bool = False
emergencyVetConsent: bool = False
@@ -54,15 +58,33 @@ class OnboardingSubmission(BaseSubmission):
class WelcomePackEmailRequest(BaseModel):
email: EmailStr
# Owner-set subject line. Defaults to "Goodwalk Onboarding - <Dog Name>" when
# blank (resolved server-side so scheduled sends keep a sensible subject too).
subject: str = ""
serviceType: str
priceDetails: str
startDate: str
preview: bool = False
previewRecipients: list[EmailStr] = []
# ISO 8601 local datetime (e.g. "2026-06-20T14:30"). When set on a non-preview
# request, the welcome email is queued for reliable delivery at that time
# instead of being sent immediately.
scheduledFor: str | None = None
class ScheduledEmailCancelRequest(BaseModel):
id: str
class ScheduledEmailRescheduleRequest(BaseModel):
id: str
scheduledFor: str
class BirthdayEmailRequest(BaseModel):
email: EmailStr
preview: bool = False
previewRecipients: list[EmailStr] = []
class BirthdayAutoSendRequest(BaseModel):
@@ -81,6 +103,42 @@ class ClientStatusUpdate(BaseModel):
reason: str = ""
class ClientProfileUpdate(BaseModel):
email: EmailStr
nextEmail: EmailStr
fullName: str
phone: str = ""
address: str = ""
dogName: str
class ResetOnboardingRequest(BaseModel):
"""Owner-initiated reset: keep the client's saved details but mark their
onboarding incomplete so they can sign in and complete the new form
(used for clients imported from the legacy Gravity Forms data)."""
email: EmailStr
class NewClientRequest(BaseModel):
"""Owner-initiated client creation (e.g. leads from Instagram / Facebook
that never came through the public enquiry form). Registers the email so
the client can access the onboarding form, and seeds a profile."""
email: EmailStr
fullName: str
phone: str = ""
address: str = ""
dogName: str = ""
dogBreed: str = ""
# Where the client came from, e.g. "Instagram", "Facebook", "Referral".
source: str = ""
# When true (and the MYOB integration is configured), also create the client
# as a customer contact in MYOB. Non-fatal: a MYOB failure never blocks the
# local client from being created.
createInMyob: bool = False
class ContractSubmission(BaseSubmission):
address: str
dogName: str
@@ -126,3 +184,4 @@ class SendMessageRequest(BaseModel):
fontId: str = "system"
recipients: list[EmailStr] = []
preview: bool = False
previewRecipients: list[EmailStr] = []
+212
View File
@@ -0,0 +1,212 @@
"""MYOB AccountRight Live integration — create customer contacts.
Used when the owner ticks "Also create in MYOB" while adding a new client in the
control panel. The integration is optional: when the required environment
variables are not set, :func:`is_configured` returns ``False`` and the caller
skips MYOB entirely (the local client is still created).
Auth model (MYOB OAuth 2.0):
* A developer key/secret (``client_id`` / ``client_secret``) identifies this
app to MYOB.
* A long-lived refresh token mints short-lived bearer access tokens. We cache
the access token in memory and refresh it shortly before it expires.
* Each AccountRight company file lives at its own base URI and is gated by a
company-file token: ``base64("username:password")`` sent as the
``x-myobapi-cftoken`` header (password may be blank).
All HTTP calls are synchronous (``requests``); the async app invokes the public
helpers via ``asyncio.to_thread`` so the event loop is never blocked.
"""
from __future__ import annotations
import base64
import threading
import time
import requests
from mail_api.config import (
MYOB_API_KEY,
MYOB_API_SECRET,
MYOB_CF_PASSWORD,
MYOB_CF_USERNAME,
MYOB_COMPANY_FILE_URI,
MYOB_ENABLED,
MYOB_HTTP_TIMEOUT_SECONDS,
MYOB_REFRESH_TOKEN,
MYOB_TOKEN_URL,
logger,
)
class MyobError(Exception):
"""Raised when a MYOB API call cannot be completed."""
# Access tokens are short-lived (MYOB returns ~20 min). Cache the current token
# and its expiry so back-to-back client creations reuse a single refresh.
_token_lock = threading.Lock()
_cached_token: str = ""
_cached_token_expires_at: float = 0.0
# Refresh a little early so a token never expires mid-request.
_TOKEN_EXPIRY_SKEW_SECONDS = 60
def is_configured() -> bool:
"""True when every credential needed to talk to MYOB is present."""
return MYOB_ENABLED
def _cf_token() -> str:
raw = f"{MYOB_CF_USERNAME}:{MYOB_CF_PASSWORD}".encode("utf-8")
return base64.b64encode(raw).decode("ascii")
def _refresh_access_token() -> str:
"""Exchange the stored refresh token for a fresh access token.
Caches the result in module state. Raises :class:`MyobError` on failure.
"""
global _cached_token, _cached_token_expires_at
try:
resp = requests.post(
MYOB_TOKEN_URL,
data={
"client_id": MYOB_API_KEY,
"client_secret": MYOB_API_SECRET,
"grant_type": "refresh_token",
"refresh_token": MYOB_REFRESH_TOKEN,
},
headers={"Content-Type": "application/x-www-form-urlencoded"},
timeout=MYOB_HTTP_TIMEOUT_SECONDS,
)
except requests.RequestException as exc:
raise MyobError(f"Could not reach the MYOB token service: {exc}") from exc
if resp.status_code != 200:
raise MyobError(
f"MYOB token refresh failed (HTTP {resp.status_code}). "
"Check MYOB_API_KEY / MYOB_API_SECRET / MYOB_REFRESH_TOKEN."
)
try:
payload = resp.json()
except ValueError as exc:
raise MyobError("MYOB token service returned an unreadable response.") from exc
token = str(payload.get("access_token") or "").strip()
if not token:
raise MyobError("MYOB token service did not return an access token.")
try:
expires_in = int(payload.get("expires_in", 1200))
except (TypeError, ValueError):
expires_in = 1200
_cached_token = token
_cached_token_expires_at = time.monotonic() + max(0, expires_in - _TOKEN_EXPIRY_SKEW_SECONDS)
logger.info("MYOB: access token refreshed (valid ~%ds)", expires_in)
return token
def _access_token() -> str:
with _token_lock:
if _cached_token and time.monotonic() < _cached_token_expires_at:
return _cached_token
return _refresh_access_token()
def _api_headers() -> dict[str, str]:
return {
"Authorization": f"Bearer {_access_token()}",
"x-myobapi-key": MYOB_API_KEY,
"x-myobapi-version": "v2",
"x-myobapi-cftoken": _cf_token(),
"Content-Type": "application/json",
"Accept": "application/json",
}
def _split_name(full_name: str) -> tuple[str, str]:
"""Best-effort split of a single owner name into first/last for MYOB."""
parts = full_name.split()
if not parts:
return ("", "")
if len(parts) == 1:
return (parts[0], "")
return (parts[0], " ".join(parts[1:]))
def create_customer(
*,
email: str,
full_name: str,
phone: str = "",
street: str = "",
source: str = "",
) -> dict[str, str]:
"""Create an individual customer contact in MYOB.
Returns ``{"uid": ..., "uri": ...}`` parsed from the ``Location`` header MYOB
returns on a successful create. Raises :class:`MyobError` on any failure so
the caller can record it without aborting the local client creation.
"""
if not is_configured():
raise MyobError("MYOB integration is not configured.")
first_name, last_name = _split_name(full_name)
body = {
"IsIndividual": True,
"FirstName": first_name,
"LastName": last_name,
"IsActive": True,
"Addresses": [
{
"Location": 1,
"Email": email,
"Phone1": phone,
"Street": street,
}
],
}
if source:
body["Notes"] = f"Added via Goodwalk control panel — source: {source}"
url = f"{MYOB_COMPANY_FILE_URI}/Contact/Customer"
try:
resp = requests.post(
url,
json=body,
headers=_api_headers(),
timeout=MYOB_HTTP_TIMEOUT_SECONDS,
)
except requests.RequestException as exc:
raise MyobError(f"Could not reach MYOB: {exc}") from exc
# 200/201 both indicate success depending on API version; MYOB returns the
# new record's URI in the Location header.
if resp.status_code not in (200, 201):
detail = _error_detail(resp)
raise MyobError(f"MYOB rejected the customer (HTTP {resp.status_code}): {detail}")
location = resp.headers.get("Location", "")
uid = location.rstrip("/").rsplit("/", 1)[-1] if location else ""
logger.info("MYOB: created customer uid=%s for %s", uid or "?", email)
return {"uid": uid, "uri": location}
def _error_detail(resp: requests.Response) -> str:
"""Extract a human-readable error message from a MYOB error response."""
try:
data = resp.json()
except ValueError:
return (resp.text or "").strip()[:300] or "no detail provided"
errors = data.get("Errors") if isinstance(data, dict) else None
if isinstance(errors, list) and errors:
messages = [str(e.get("Message", "")).strip() for e in errors if isinstance(e, dict)]
joined = "; ".join(m for m in messages if m)
if joined:
return joined
return str(data)[:300]
+913 -69
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -4,3 +4,4 @@ resend>=2.0
pydantic[email]>=2.10
asyncpg>=0.30
weasyprint>=63
requests>=2.32
+15
View File
@@ -0,0 +1,15 @@
$ErrorActionPreference = 'Stop'
Set-Location $PSScriptRoot
$env:DEV_MODE = '1'
if (-not $env:OWNER_EMAIL) {
$env:OWNER_EMAIL = 'info@goodwalk.co.nz'
}
$python = Join-Path $PSScriptRoot '.venv\Scripts\python.exe'
if (-not (Test-Path $python)) {
throw "Missing virtualenv interpreter at $python. Create it first with: py -3.14 -m venv .venv"
}
& $python -m uvicorn main:app --host 127.0.0.1 --port 8000 --reload