Trials were anonymous — a 14-day trial you cannot talk to. trials gains an email column (idempotent ALTER), POST /api/the-nook/trial-email attaches the optional first-launch prompt's answer, and the address lands in a Brevo list when NOOK_TRIAL_LIST_ID is set. The lifecycle emails follow: a D+11 trial expiry reminder with a buy link, a purchase thank-you from the Stripe webhook, and scheduled testimonial (D+3) and review (D+14) asks via the daily /api/the-nook/lifecycle cron. Rows are marked before sending so retries never double-send; trial addresses that already bought get no buy reminder; the endpoint requires a Bearer secret (CRON_SECRET / NOOK_LIFECYCLE_SECRET) and fails closed when unconfigured — an open trigger would email real users. All sends are best-effort and logged, never thrown. Tests: trial-email (3) and lifecycle (6) suites; full run 539 pass. Issues: FRE-10, FRE-11
63 lines
1.8 KiB
JSON
63 lines
1.8 KiB
JSON
{
|
|
"rewrites": [
|
|
{
|
|
"source": "/api/(.*)",
|
|
"has": [{ "type": "host", "value": "gaze.freno.me" }],
|
|
"destination": "/api/$1"
|
|
},
|
|
{
|
|
"source": "/api/(.*)",
|
|
"has": [{ "type": "host", "value": "inputhalo.freno.me" }],
|
|
"destination": "/api/$1"
|
|
},
|
|
{
|
|
"source": "/api/(.*)",
|
|
"has": [{ "type": "host", "value": "nessa.freno.me" }],
|
|
"destination": "/api/$1"
|
|
},
|
|
{
|
|
"source": "/api/(.*)",
|
|
"has": [{ "type": "host", "value": "lineage.freno.me" }],
|
|
"destination": "/api/$1"
|
|
},
|
|
{ "source": "/(.*)", "has": [{ "type": "host", "value": "nessa.freno.me" }], "destination": "/nessa/$1" },
|
|
{ "source": "/(.*)", "has": [{ "type": "host", "value": "lineage.freno.me" }], "destination": "/lineage/$1" },
|
|
{ "source": "/(.*)", "has": [{ "type": "host", "value": "gaze.freno.me" }], "destination": "/gaze/$1" },
|
|
{ "source": "/(.*)", "has": [{ "type": "host", "value": "inputhalo.freno.me" }], "destination": "/inputhalo/$1" },
|
|
{ "source": "/api/(.*)", "has": [{ "type": "host", "value": "nook.freno.me" }], "destination": "/api/$1" },
|
|
{ "source": "/(.*)", "has": [{ "type": "host", "value": "nook.freno.me" }], "destination": "/nook/$1" }
|
|
],
|
|
"headers": [
|
|
{
|
|
"source": "/",
|
|
"headers": [
|
|
{
|
|
"key": "Cache-Control",
|
|
"value": "public, max-age=0, must-revalidate"
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"source": "/(.*)",
|
|
"headers": [
|
|
{
|
|
"key": "Cache-Control",
|
|
"value": "public, max-age=0, must-revalidate"
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"source": "/_build/(.*)",
|
|
"headers": [
|
|
{
|
|
"key": "Cache-Control",
|
|
"value": "public, max-age=31536000, immutable"
|
|
}
|
|
]
|
|
}
|
|
],
|
|
"crons": [
|
|
{ "path": "/api/the-nook/lifecycle", "schedule": "0 13 * * *" }
|
|
]
|
|
}
|