Skip to content

Redesign emails and sign-in pages; add public stats and one-click unsubscribe - #939

Merged
keysersoft merged 5 commits into
mainfrom
feat/email-and-auth-redesign
Oct 7, 2026
Merged

keysersoft merged 5 commits into
mainfrom
feat/email-and-auth-redesign

Conversation

@keysersoft

Copy link
Copy Markdown
Contributor

Summary

Implements the approved email and sign-in redesign: one shared email layout for every email the backend sends, a public stats endpoint that feeds real trust numbers to emails and pages, a two-column Cloud sign-up page, and restyled MCP authorization pages. Website emails are redesigned in the website repo in parallel; the /api/email/* fallback contract is unchanged.

Changes

Public stats

  • GET /api/public/stats (no auth, throttled 30/min, Cache-Control: public) returns { githubStars, dockerPulls, workspaces, toolCalls30d, updatedAt }. Aggregates only.
  • TrustStatsService (global): GitHub stargazers_count of HelpCode-ai/anythingmcp, Docker Hub pull_count of helpcodeai/anythingmcp (5 s timeouts), count of organizations and of tool invocations in the last 30 days from the local DB. Cached for 1 h, stale-while-revalidate, single in-flight refresh, last known value kept per source.
  • Shared formatter, always rounded down: stars exact below 1,000 then hundreds with "+", downloads to the thousand, workspaces to the hundred, tool calls to 10,000. Values below their unit, or unknown, are hidden rather than shown as "0+".

Emails (settings/email-layout.ts, settings/email-templates.ts)

  • Shared layout per the approved mockup: table layout, inline styles, 600 px, Georgia headings, system sans, no web fonts, hosted PNG logos (https://anythingmcp.com/email/*.png), a trust band with live numbers and badges, a footer, a preheader and a plain-text part.
  • All emails rebuilt on it:
    • verification code
    • password reset (subject is now "Reset your AnythingMCP password")
    • invitation
    • licence key welcome (still has no callers)
    • existing-account notice
    • onboarding reminders: day 1, day 2, AI client connected
    • trial lifecycle: warn3, warn1, expired. These show the user's own connectors and successful tool calls.
    • activation reminders: connect-client, test-connector
    • win-back: discount after a week, discount after a month, help
  • User-supplied values are now escaped in all emails.
  • Self-hosted instances make no Cloud-only claims (Frankfurt, DPA, Claude Directory, Cloud tool calls and workspaces).
  • Marketing emails (onboarding, activation, win-back) get an "Unsubscribe from tips and offers" footer line and List-Unsubscribe. Trial lifecycle notices and transactional emails get neither: the cron sends trial notices regardless of the opt-out.
  • One-click unsubscribe (RFC 8058): GET/POST /api/public/unsubscribe?u=<userId>&t=<HMAC>. GET only shows a confirmation button, so link scanners cannot unsubscribe anyone; POST sets emailMarketingOptOut. The token is an HMAC derived from JWT_SECRET. The header includes List-Unsubscribe-Post: List-Unsubscribe=One-Click.
  • The old unsubscribe link pointed to /settings/profile, which does not exist. Cloud users now have an "Email preferences" toggle in Settings (/settings#email-preferences), which is also the fallback link.
  • scripts/send-email-previews.ts: --out <dir> writes every email and variant as HTML and text; --to <x@helpcode.ai> sends them with a "[Preview]" subject prefix and refuses any other domain. Also supports --self-hosted, --stats-url and --asset-base.

MCP authorization pages (auth/auth-page.ts, login.controller.ts)

  • Consent/sign-in, server picker, first-connector offer and cancelled page share one frame: client ↔ AnythingMCP tiles, " will be able to" (tools on the requested server per role, name and email, never connector secrets), "Signed in as" with Switch account, Cancel and Allow access buttons, and a trust row under the card.
  • The client logo is shown only for known redirect hosts (claude.ai/claude.com, chatgpt.com/openai.com); any other client gets a neutral initial tile. "Listed in Anthropic's Claude Directory" appears only when the code goes back to Claude, and only on Cloud.
  • Security behaviour unchanged: CSP frame-ancestors 'none', CSRF double-submit, consent and deny semantics, SSO, sign-up return. The redirect host is still shown with the "only continue if you recognise this destination" warning, now in amber for unknown hosts. Allow is still the first submit button in the markup, so Enter never cancels.
  • The requested server's name (RFC 8707) is shown only to a signed-in member of its workspace.

Sign-up / sign-in page (login/page.tsx)

  • Cloud register: two-column layout with a dark trust panel (live stat tiles, hidden when the endpoint is unavailable; badges; works-with logos; "Your 7-day trial includes"; KOCH line). On mobile the columns stack. The Cloud submit button is now "Start free trial →".
  • Sign-in and self-hosted register keep the single card, with a compact trust line underneath (no Cloud claims on self-hosted).
  • Client SVG logos added under public/logos/clients/.

Testing

  • packages/backend: npx jest. 459 suites passed, 4 skipped, 9188 tests passed.
  • New specs:
    • email-templates.spec.ts: every email and variant × cloud/self-hosted/no-stats. Checks for text and HTML parts, no {{/undefined/NaN/null, hostile names escaped, < 90 KB, only https links to anythingmcp.com, cloud.anythingmcp.com, github.com and claude.ai, unsubscribe only on marketing, images with width/height/alt, no web fonts, Cloud claims only on Cloud.
    • email.service.spec.ts: headers, signed unsubscribe link, escaping, fallback without stats.
    • trust-stats.spec.ts: rounding, caching, stale refresh, fallback, malformed answers, controller.
    • unsubscribe.controller.spec.ts: GET never unsubscribes, POST does, forged or foreign tokens are refused.
    • login.controller.spec.ts: new consent design tests. Updated deliberately: "Use a different account" → href="/auth/login?switch=1">Switch account.
  • packages/backend: npx tsc --noEmit -p tsconfig.json passes; npm run lint reports 0 errors (the unrelated files rewritten by --fix were reverted).
  • packages/frontend: npx tsc --noEmit, npm run lint (0 errors), npm run build all pass.
  • packages/frontend: npx playwright test. 119 passed, including the new signup-trust-panel.spec.ts. The Cloud sign-up button label is updated in neutral-signup-and-billing.spec.ts and billing-awareness.spec.ts.
  • Rendered every email (cloud and self-hosted), the register page (desktop and mobile) and all authorization pages to screenshots and compared them with the approved mockups.

Notes

  • The email PNG logos are served by the website (/email/*.png), which is shipping in the website repo's redesign PR. Until that is deployed, email images will not load.
  • Behaviour in both modes: the stats endpoint exists on Cloud and self-hosted (workspaces and tool calls come from the local DB). Emails and pages on self-hosted show only stars, downloads and generic badges.

GitHub stars, Docker Hub pulls, workspace count and tool calls in the
last 30 days, cached for an hour with last-known fallback per source.
Includes the shared rounding rules (always rounded down).
- email-layout.ts renders the approved design (no web fonts, hosted PNG
  logos, trust band with live numbers, footer) plus the text part
- email-templates.ts holds every email as a pure function; user-supplied
  values are now escaped in all emails
- marketing emails carry List-Unsubscribe with an RFC 8058 one-click
  endpoint (signed per user); transactional and trial notices do not
- tests render every email and variant; preview script writes or sends
  them (helpcode.ai addresses only)
Consent, server picker, first-connector offer and cancelled page share
one frame: client and AnythingMCP tiles, what the client will be able to
do, the redirect host (amber when unknown), trust row under the card.
The Claude logo and directory badge appear only when the code goes back
to claude.ai; Cloud-only claims only on Cloud. Allow stays the first
submit button so Enter never cancels.
Live numbers from /api/public/stats (tiles hidden when unavailable),
badges, supported clients and trial contents; compact trust line under
the sign-in card. Self-hosted keeps the single card without Cloud
claims. Cloud users can turn tips and offers emails on or off in
Settings.
@keysersoft
keysersoft enabled auto-merge (squash) October 7, 2026 16:21
@keysersoft
keysersoft merged commit 84878b6 into main Oct 7, 2026
13 checks passed
@keysersoft
keysersoft deleted the feat/email-and-auth-redesign branch October 7, 2026 16:25
@github-actions github-actions Bot locked and limited conversation to collaborators Oct 7, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant