[READ-ONLY] Mirror of https://github.com/openstatusHQ/openstatus. 馃珫 Status page with uptime monitoring & API monitoring as code 馃珫 openstatus.dev
bun drizzle-orm monitoring monitoring-as-code nextjs observability on-call open-source shadcn-ui status-page statuspage synthetic-monitoring tinybird turso uptime uptime-checker uptime-monitor

feat: status page unsubscription (#1736) master

* feat(db): add unsubscribedAt field to page_subscriber schema Add unsubscribedAt timestamp field to support email unsubscribe feature. This enables tracking when subscribers opt out of status page notifications. - Add unsubscribedAt field to page_subscriber table schema - Create migration 0053 to ALTER TABLE with new column - Update drizzle journal and snapshot files Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(api): add unsubscribe mutation to statusPage router Add public unsubscribe mutation that allows page subscribers to unsubscribe from status page notifications. The mutation validates that the token exists, the subscription is verified, and the user hasn't already unsubscribed before setting the unsubscribedAt timestamp. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(api): add getSubscriberByToken query to statusPage router Add public query to retrieve subscriber info for the unsubscribe confirmation page. Returns page name and masked email for valid subscribers, or null if not found or already unsubscribed. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(server): add RFC 8058 one-click unsubscribe endpoint Add public POST endpoint at /public/unsubscribe/:token for email clients that support one-click unsubscribe (RFC 8058). The endpoint validates the token, checks subscriber status, and sets the unsubscribedAt timestamp. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(emails): add unsubscribe link to status report emails - Add `unsubscribeUrl` optional prop to StatusReportSchema - Add footer section with unsubscribe link (conditionally rendered) - Style footer with small, muted text for visual hierarchy - Update preview props with sample unsubscribe URL - Remove old TODO comment Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(emails): add RFC 8058 List-Unsubscribe headers to status report emails - Update sendStatusReportUpdate to accept subscribers with tokens - Add List-Unsubscribe and List-Unsubscribe-Post headers per RFC 8058 - Pass unsubscribeUrl to StatusReportEmail template for each subscriber - Update all callers to pass subscriber tokens instead of just emails - Filter out subscribers with null tokens for type safety Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(server): filter out unsubscribed users from email notifications Add isNull(pageSubscriber.unsubscribedAt) to subscriber queries in statusReports/post.ts and statusReportUpdates/post.ts to ensure unsubscribed users no longer receive status report emails. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(status-page): add unsubscribe confirmation page Add a user-facing unsubscribe confirmation page at /[domain]/unsubscribe/[token] that allows subscribers to confirm their unsubscription from status page notifications. Features: - Loading state while fetching subscriber info - Error state for invalid/expired tokens - Confirmation view with masked email and page name - Success state after unsubscription Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(dashboard): add subscriber status column to table Add a Status column to the subscribers data table that displays: - "Active" badge for verified subscribers - "Pending" badge for unverified subscribers - "Unsubscribed {date}" badge for users who have unsubscribed Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(api): add re-subscription flow for unsubscribed users When an unsubscribed user attempts to subscribe again: - Clear unsubscribedAt field - Reset acceptedAt to require re-verification - Regenerate token for security - Update expiresAt for new verification window Also handles pending (unverified) re-subscription by regenerating token. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(api): add unit tests for unsubscribe API endpoints Add comprehensive unit tests for getSubscriberByToken query and unsubscribe mutation in statusPage router: - Test valid token returns masked email and page name - Test non-existent token returns null/undefined - Test already unsubscribed user returns null - Test email masking logic with various edge cases - Test unsubscribe mutation success and error scenarios - Test UUID token format validation Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test: add integration tests for email headers and subscriber filtering - Add email client integration tests for List-Unsubscribe headers - Add subscriber filtering integration tests for email queries - Test RFC 8058 compliance headers (List-Unsubscribe-Post) - Test email body contains unsubscribe link with proper styling - Test unsubscribed users are excluded from notification queries Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test(api): add E2E tests for full unsubscribe flow Add comprehensive end-to-end integration tests covering the complete unsubscribe user journey: subscribe -> verify -> receive email -> unsubscribe. Tests include: - Full subscribe/verify/unsubscribe flow with proper state transitions - Confirmation page displaying correct page name and masked email - Timestamp tracking when user clicks confirm unsubscribe - Email recipient filtering to exclude unsubscribed users - Re-subscription flow after unsubscribe - Invalid token handling Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore: remove ralph prd and progress files * fix: format * fix: valid subscribers * fix: sqlite precision time * refactor: update List-Unsubscribe to redirect to status page Change the email unsubscribe URL from the API endpoint (/public/unsubscribe) to the status page's unsubscribe page. This allows users to see a proper unsubscribe confirmation UI instead of a direct API response. - Remove List-Unsubscribe-Post header (no longer using one-click POST) - Update List-Unsubscribe to use status page URL - Add pageSlug and customDomain params to sendStatusReportUpdate - Update all call sites to pass page slug/domain info - Update integration tests for new URL format Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * Update packages/emails/src/client.tsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * fix: email header --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>


+5039 -61
26 changed files