Promote synvya-staging to synvya (production)#42
Merged
alejandro-runner merged 72 commits intosynvyafrom Apr 29, 2026
Merged
Conversation
Allow authenticated users to create their first team
Fix tenant-scoped team membership test setup
After verifying their email, users were landing on the Keycast dashboard instead of being redirected back to the originating client app (e.g. account.synvya.com). This adds an optional redirect_uri parameter to registration that is stored and returned as redirect_to after verification, leveraging the existing frontend redirect logic. Closes #12 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Redirect to client app after email verification
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implements email-based team invitations so restaurant admins can invite team members by email instead of requiring Nostr public keys. Existing users are added immediately; new users receive an invitation email with a token-based accept flow. New endpoints: - POST /teams/:id/invite (instant add or send invitation) - GET /teams/:id/invitations (list invitations) - DELETE /teams/:id/invitations/:id (revoke) - GET /invitations/preview?token= (public preview) - POST /invitations/accept (accept with auth) Also fixes cloudbuild.yaml migration glob to pick up date-based migration filenames (20260413... pattern). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add team invite by email
Sync the spec with what was actually shipped: partial unique index instead of plain UNIQUE constraint, lowercase role default, separate CORS routers, and 404 (not 410) for expired tokens. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds isSynvyaManaged detection to the register page and a proper success screen to the reset-password flow, matching the Synvya-branded login style already used in verify-email and reset-password forms. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds a light Synvya theme variant to /support-admin and surfaces per-user team memberships with their team-owned restaurant keys and NIP-46 authorizations. The theme activates only on auth.synvya.com / auth.staging.synvya.com via the existing getLoginUrl() switch, so diVine deployments are unchanged. Backend: - New GET /api/admin/user-teams?pubkey=<hex> (support-admin only, tenant-scoped) returning teams → restaurant_keys → authorizations with label, bunker pubkey, relays, connected/expires/created timestamps. Frontend: - Support-admin page dual-modes via isSynvyaManaged; Synvya variant uses a white admin surface with Synvya logo header. - New "Teams & Restaurants" section (Synvya-only) lazy-loads user-teams on expand and groups authorizations under their restaurant key, with visual accents for Synvya Server (24/7) vs Synvya Client (interactive) labels and a "shared across the team" hint reflecting the Keycast boundary: authorizations are team-scoped, not user-scoped. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add Synvya-themed support-admin user detail view
Replace inline 9-tuple sqlx::query_as with a dedicated FromRow struct (AuthorizationRow) to satisfy clippy::type_complexity under -D warnings. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fix clippy type_complexity in get_user_teams
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Apply cargo fmt to admin get_user_teams handler
Adds `revoked_at` + `revoked_reason` columns to the `authorizations` table, mirrors the existing `oauth_authorizations` pattern. The signer daemon now skips revoked rows on lazy load, the repository filters them by default, and a new support-admin endpoint marks rows inactive without hard-deleting and losing forensic history. - Migration adds columns + partial index on active rows. - AuthorizationRepository: filter revoked in find_by_stored_key/all_ids; add find_by_stored_key_with_revoked and revoke(tenant_id, id, reason). - Signer daemon: load_single_authorization skips revoked team rows (matches OAuth path). - New endpoint: POST /api/admin/authorizations/:id/revoke (support admin, tenant-scoped). Notifies signer via existing AuthorizationCommand::Remove channel so the in-memory handler is tombstoned immediately. Closes #18 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat: soft-delete for team authorizations (#18)
Groups authorizations by label in the Synvya support-admin user detail view so a restaurant key with many rows (e.g. several repeated logins producing `Synvya Client (staging)` authorizations) renders as one card per label with an "N older" disclosure instead of a flat scroll. - Group by label (empty labels bucket as `(no label)`). - Newest-first inside each group; groups sorted by most-recent row. - Show newest inline; click "N older" to expand the rest. - Color accent (green for Synvya Server, blue for Synvya Client) moves to the group header. - Row count badge on the group header. - Only active in Synvya-gated view (isSynvyaManaged); diVine deployments keep the existing flat rendering. Per #19 out-of-scope: no backend-shape change, no dedupe-on-create. Revoked-row handling deferred to a follow-up now that #18 has landed. Closes #19 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat(support-admin): collapse same-label authorizations (#19)
Closes the UI gap left by #18: support admins can now revoke an authorization directly from /support-admin (Synvya variant) via a per-card Revoke button that prompts for an optional reason and calls POST /api/admin/authorizations/:id/revoke. - Backend: expose `revoked_at` and `revoked_reason` on AdminAuthorization (and the underlying sqlx FromRow struct + SELECT in get_user_teams). - Frontend: hide revoked authorizations by default; per-restaurant-key "Show revoked (N)" toggle surfaces them with tombstone styling (opacity, strikethrough label, revoked pill, reason tooltip). - Revoke button appears on active authorizations only; during the request the button shows "Revoking…" and is disabled. On success we reload the user's teams so the UI reflects the new revoked state. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fix(cors): allow credentialed requests on /api/invitations/preview
Lets the invitation landing page render the team's kind-0 profile (display name + avatar) by pubkey and show the inviter's email instead of a truncated pubkey. Closes #29. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…mail feat(invitations): add team_key_pubkey and invited_by_email to preview
Synvya deployments run keycast with DISABLE_WEB_UI=true and redirect unknown routes to www.synvya.com. That swallowed the /verify-email SvelteKit route so invited users landed on the marketing site instead of verifying their email. Allowlist /verify-email (plus SvelteKit /_app static assets) in the disabled-UI branch, and add PASSWORD_RESET_BASE_URL so the reset link can point at account.{staging.,}synvya.com where Synvya hosts its own reset form that POSTs to /api/auth/reset-password. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
fix(email): route /verify-email past DISABLE_WEB_UI; split password reset URL
change diVine to Synvya svelte tooling introduced formatting changes to .svelte pages
keycast/ is running locally now.
────────────
Summary [ 25.980s] 368 tests run: 368 passed, 11 skipped
Rework the team-invitation email so it mirrors the accept-invite page: centred Synvya wordmark, "Team Invitation" header, white card with the team's avatar and display name, inviter + recipient + expiry, and a prominent Accept Invitation button. - New api/src/nostr_profile.rs fetches kind-0 metadata (display_name, picture) from BUNKER_RELAYS with a 3s timeout; returns None on any failure so the email still sends with the DB handle as fallback. - Prefer inviter email over the pubkey-prefix fallback when the admin has no display_name/username, so recipients see "staging@…" instead of "f7ebfcf1…". - Team display name + avatar in the email now match what the accept page shows, because both derive from the same kind-0 record. - HTML-escape all interpolated values and restrict avatar URLs to http/https. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
feat(invitations): card-style invite email with kind-0 team profile
Fixes formatting in teams.rs, email_service.rs, and nostr_profile.rs flagged by the `cargo fmt --all -- --check` CI step on synvya-staging. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
style: apply cargo fmt
Align the three remaining transactional emails (verification, password reset, account claim) with the visual language of the new team-invite email: centred Synvya wordmark header, centred heading + intro, rounded pill button, copy-link fallback, muted footer note. Done via a small shared helper `basic_email_html` that takes heading, intro, CTA label, CTA URL, and footer note. Keeps the emails minimal — no card, no stylised subject — since these flows have no entity to present and overly stylised reset/verify emails can read as phishing. All interpolated values are HTML-escaped. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Claim emails now render the same card layout as team invites when the
preloaded account has kind-0 metadata: avatar, display name (restaurant
name), and "Claim your login for {email}". Falls back to the plain
single-CTA layout when kind-0 is unavailable.
- Extend send_claim_email signature with account_display_name and
account_picture Options; threaded through all three EmailSender impls
and the EmailService facade.
- claim_email_html: branches to card layout when either field is set;
otherwise delegates to basic_email_html. HTML-escapes interpolated
values; avatar URL restricted to http/https via safe_http_url.
- admin.rs batch claim handler: best-effort kind-0 fetch from
BUNKER_RELAYS for each user_pubkey (3s timeout, handled entirely in
fetch_profile_metadata) before sending. Silent fallback on failure so
batch throughput degrades gracefully.
Password-reset and verification emails intentionally left on the plain
layout — no entity to present, and over-stylised reset mail reads as
phishing.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
feat(email): unify verify / reset / claim email layouts
Collapse a two-line statement onto one line to satisfy rustfmt's width heuristics — the preceding line ran one char longer than cargo fmt's breakpoint, leaving the two lines inconsistent. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
chore(fmt): apply cargo fmt to admin.rs claim-email call site
Murali dev diVine-> synvya; enable local host build and test
bring in latest feature changes from staging
Tests pass Summary [ 26.036s] 373 tests run: 373 passed, 11 skipped
Murali dev
Test: api/tests/common/mod.rs#L46 called `Iterator::last` on a `DoubleEndedIterator`; this will needlessly iterate the entire iterator
fix ci/cd test issue
keep order intact
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Promotes ~70 commits of accumulated staging work to production. The
synvyabranch is currently 1 commit ahead (docs-only: ALB/WAF architecture notes) and 71 commits behind staging.Major changes included
DISABLE_WEB_UI/api/invitations/preview, logout CORS fix, staging-environment secure_cookies handling, localhost session cookies, account/server originsDISABLE_WEB_UIfor Synvya deployments,DISABLE_EMAILSonly when explicitly set, Docker cache pruning before deploys, port mapping updatesTeamUserresponses,team_key_pubkeyandinvited_by_emailin invitation previewtype_complexityfix inget_user_teamsDiverging commit on
synvya46b11c4 Document dedicated Synvya ALBs and WAF layout— docs-only, will merge cleanly.Test plan
DISABLE_WEB_UIstill hides web UI on production deployment