Repository navigation
Fix super-admin access without requiring the DB migration - #5
Merged
Merged
Conversation
Drop the Free tier from the pricing page and make the Monthly/Yearly toggle functional, defaulting to Yearly on load. Pricing cards now show the selected billing period's price and pass it through to signup. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DHvvccxiTVzkDBugo4VbfD
The full app and admin were already Supabase-backed; the only remaining static datasets were the marketing blog and the hardcoded social-proof figures. This replaces both with real, database-backed data. Blog: - Add blog_posts table (migration 0010) with public-read RLS and the 9 existing posts seeded idempotently; writes are service-role only. - Rewrite lib/blog/posts.ts as an async Supabase data-access layer with the same BlogPost shape, degrading to empty on DB errors. - Update the /blog routes to await the helpers and revalidate hourly so content edits surface without a redeploy. Marketing figures: - Add public_stats() RPC (migration 0011): anon-callable, SECURITY DEFINER, returns only non-identifying aggregate counts. - Replace the hardcoded '10,000+ families' on the landing, pricing, and features/how-it-works pages with the real family count, via a cached server fetcher (lib/marketing/stats.ts) and client-safe formatters. Add the new table + function to database.types.ts. Production build and typecheck pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DHvvccxiTVzkDBugo4VbfD
Daniel.Hughen@gmail.com already lands on /admin: he is seeded in super_admins (0008) and both login paths route super admins there. This rebuilds the dashboard itself and fills the one dead nav link. Admin Dashboard (/admin): - Stat cards (families, active users, subscriptions, monthly revenue) with real new-this-month context. - System Status rail from real health probes (DB/storage) plus email/AI configuration readiness; System Health gauge derived from them. - User-growth sparkline (30d new profiles), storage-usage gauge (summed document bytes), recent activity (audit logs), top families by members. - Subscription donut by plan, revenue bars (new MRR, 6 months), support overview, and quick-action links — all from Supabase. Supporting: - New support_tickets table (0012) with service-role-only access; the contact form now persists each message as a ticket so the Support Overview is real. - Add /admin/reports (Reports & Analytics) — previously linked in the sidebar but missing — wired to Supabase growth/revenue/activity data. - Extract shared SVG chart primitives to components/admin/charts.tsx. - Add checkAI() health probe. Typecheck, lint, and a full production build pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DHvvccxiTVzkDBugo4VbfD
…pdates-lwv4d9 # Conflicts: # app/(marketing)/pricing/page.tsx # app/(marketing)/pricing/pricing-content.tsx
Audit found every existing admin tab already fully Supabase-wired (real data + mutations, no mock). This elevates them to world-class by adding the capabilities the reference design implied: - Support Tickets (/admin/support): manage contact-form submissions through their lifecycle (open/pending/resolved/closed) with search, status filters, and inline status control — wired to support_tickets. - Audit Logs (/admin/audit): filterable, paginated, append-only record of sensitive actions across all families and the site admin. - Security tab: new Accounts table with real ban/unban and password-reset actions via the Supabase Auth admin API (self-ban guarded, audit-logged). - New server actions: adminSetUserBanAction, adminSendPasswordResetAction, adminUpdateTicketStatusAction. - Add Support Tickets + Audit Logs to the admin sidebar; dashboard Support Overview and Recent Activities now link to them. Typecheck, lint, and a full production build pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DHvvccxiTVzkDBugo4VbfD
Replace the single 'Switch to Parent Role' link in the admin shell with two clearly-labeled entries (dropdown menu and sidebar): - Go to Parent Dashboard → /dashboard/briefing (Daily Briefing) - Go to Family Dashboard → /dashboard (family overview) Both target real routes gated by the dashboard layout; a super admin without a family rides the existing /onboarding redirect. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DHvvccxiTVzkDBugo4VbfD
One paste-ready, idempotent file (0008 super-admin seed, 0010 blog, 0011 public_stats, 0012 support_tickets) for applying the pending migrations via the Supabase SQL Editor or psql. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DHvvccxiTVzkDBugo4VbfD
Root cause: super-admin detection relied solely on the super_admins table (migration 0008). Until that migration is applied to the database, is_super_admin() returns false, so Daniel was routed to /dashboard (then /onboarding) instead of /admin. Fix: add a code/env super-admin allowlist (lib/constants/super-admins.ts) with daniel.hughen@gmail.com built in, plus an optional SUPER_ADMIN_EMAILS env var. isSuperAdmin() now checks this allowlist before the DB RPC, so site-admin access and the post-login redirect work on deploy with no SQL. - isSuperAdmin() (admin + dashboard layouts) is now allowlist-aware. - Post-login redirect resolves server-side via resolveLandingPathAction() so the env/code allowlist is honored (login form + OAuth callback). The DB RPC remains the source of truth when present; this is additive. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DHvvccxiTVzkDBugo4VbfD
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
NewWorldVenture
marked this pull request as ready for review
June 20, 2026 02:19
NewWorldVenture
pushed a commit
that referenced
this pull request
Jul 4, 2026
Closes the roadmap #5 gap — "Build a persistent family knowledge graph" had no backing table. Adds the durable facts store the family looks up again and again. - Migration 0123_family_facts.sql: family-scoped RLS table (category, label, value, notes, member_id nullable = family-level, is_pinned) + updated_at trigger + indexes. Validated on PG16: RLS, 4 policies, category check constraint, idempotent. - lib/memory/facts.ts: pure, tested (8 cases) — categories, factMatches, filterFacts (member/category/search, pinned-first then newest), groupByCategory. - components/modules/knowledge-base-module.tsx at /dashboard/knowledge: search + member/category filter chips, grouped cards, add/edit, pin, copy value, delete. 100% Supabase via useRealtimeQuery. - Types for family_facts; nav entry (Family AI OS, Brain icon); todo.md + AGENT_HANDOFF updated. The family-knowledge-graph view can now read this store. Verified: tsc, eslint, vitest (1637), next build (/dashboard/knowledge in manifest). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qnr3AA4xBTeaJ4Lrm41o7J
NewWorldVenture
pushed a commit
that referenced
this pull request
Jul 5, 2026
…n't missed Friction #5 (push notifications) — the web-push delivery path already existed (lib/server/push.ts, push_devices, service worker, subscribe routes, settings toggle) and imminentMomentNotices was wired into generateFamilyNotifications, but pushes were only dispatched by the once-daily /api/cron/notifications run, which also owns the email digest. Same cadence for both meant a moment or reminder added after the daily run for later that day was missed until the next day — after it mattered. Add /api/cron/push-scan (every 2h) that regenerates notifications and dispatches pending pushes only — no email, so the digest stays daily. Safe to overlap the daily cron: generation dedups by related_id, push is gated on pushed_at and email on sent_at, so nothing double-sends. Live delivery still degrades honestly when VAPID/FCM keys are absent (skips, never fakes success). Also reconcile FRICTION_BACKLOG.md: #2/#4/#7/#10 were shipped but still read "Todo"; marked done with their landing files. Update AGENT_HANDOFF.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VJs1Px4GTVtA1jmzDZ4ShU
NewWorldVenture
added a commit
that referenced
this pull request
Jul 5, 2026
…n't missed (#226) Friction #5 (push notifications) — the web-push delivery path already existed (lib/server/push.ts, push_devices, service worker, subscribe routes, settings toggle) and imminentMomentNotices was wired into generateFamilyNotifications, but pushes were only dispatched by the once-daily /api/cron/notifications run, which also owns the email digest. Same cadence for both meant a moment or reminder added after the daily run for later that day was missed until the next day — after it mattered. Add /api/cron/push-scan (every 2h) that regenerates notifications and dispatches pending pushes only — no email, so the digest stays daily. Safe to overlap the daily cron: generation dedups by related_id, push is gated on pushed_at and email on sent_at, so nothing double-sends. Live delivery still degrades honestly when VAPID/FCM keys are absent (skips, never fakes success). Also reconcile FRICTION_BACKLOG.md: #2/#4/#7/#10 were shipped but still read "Todo"; marked done with their landing files. Update AGENT_HANDOFF.md. Claude-Session: https://claude.ai/code/session_01VJs1Px4GTVtA1jmzDZ4ShU Co-authored-by: Claude <noreply@anthropic.com>
NewWorldVenture
pushed a commit
that referenced
this pull request
Jul 5, 2026
…apshot Operating Layer slice 2 (pillar #5): a calm end-of-day "what changed" recap that diffs today's Family Operating Index snapshot against the prior one — reusing the state vector slice 1 established rather than re-querying. - lib/operating-index/summary.ts (pure, 8 tests): summarizeChange(current, prior) -> headline + composite delta + improved/declined dimensions (>=5-pt moves) + resolved/emerged suggestions (by id). Leads with cleared items, stays silent on a flat day, handles the first-reading case. - server: reads the prior full snapshot (dimensions + suggestions) and returns change: ChangeSummary alongside the index. - FOI page: a "Since yesterday" card (headline + cleared/new/+-dimension chips). summarizeChange is exported so the existing Command Center / Daily Briefing surfaces can adopt it. Zero contended files touched. Verified: tsc, eslint, 1669 vitest, next build (route registered). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VJs1Px4GTVtA1jmzDZ4ShU
NewWorldVenture
added a commit
that referenced
this pull request
Jul 5, 2026
…apshot (#228) Operating Layer slice 2 (pillar #5): a calm end-of-day "what changed" recap that diffs today's Family Operating Index snapshot against the prior one — reusing the state vector slice 1 established rather than re-querying. - lib/operating-index/summary.ts (pure, 8 tests): summarizeChange(current, prior) -> headline + composite delta + improved/declined dimensions (>=5-pt moves) + resolved/emerged suggestions (by id). Leads with cleared items, stays silent on a flat day, handles the first-reading case. - server: reads the prior full snapshot (dimensions + suggestions) and returns change: ChangeSummary alongside the index. - FOI page: a "Since yesterday" card (headline + cleared/new/+-dimension chips). summarizeChange is exported so the existing Command Center / Daily Briefing surfaces can adopt it. Zero contended files touched. Verified: tsc, eslint, 1669 vitest, next build (route registered). Claude-Session: https://claude.ai/code/session_01VJs1Px4GTVtA1jmzDZ4ShU Co-authored-by: Claude <noreply@anthropic.com>
NewWorldVenture
pushed a commit
that referenced
this pull request
Jul 5, 2026
Closes tracker #5 — summarizeChange was rendered on the Operating Index page but not in the Command Center. The Command Center now loads the same FOI change summary (loadOperatingIndex returns `change`) and shows a "Since yesterday" recap card (headline + cleared/new/±dimension chips), so the evening "what changed" view lives where families check their week. Additive, null-safe (hidden on first reading). Verified: tsc, eslint, vitest (1712), next build. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qnr3AA4xBTeaJ4Lrm41o7J
NewWorldVenture
pushed a commit
that referenced
this pull request
Jul 5, 2026
One paste-ready, idempotent seed per pillar (targets a family by email; each inserts >=500 records so every surface can be fully tested). Validated on PG16 (counts stable across re-runs): - seed_pillar1_orchestrator.sql — 500 calendar events (conflicts, overload, missing-info) + 12 open polls → the five orchestrator questions. - seed_pillar2_twin.sql — 500 events + teams/classes/routines/budgets → the Digital Twin's linked model + simulation. - seed_pillar3_playbook.sql — 500 suggested playbook facts (Save/Dismiss). - seed_pillar4_outcomes.sql — 200 events + 150 overdue to-dos + 150 open grocery items + a birthday → lights up every Outcomes badge. - seed_pillar5_command_center.sql— 500 daily Operating Index snapshots; yesterday carries open suggestions so the "Since yesterday" recap shows cleared items. Idempotent via per-seed sentinels / ON CONFLICT / upsert — never clobbers real data, re-run is stable. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qnr3AA4xBTeaJ4Lrm41o7J
NewWorldVenture
pushed a commit
that referenced
this pull request
Jul 6, 2026
Finish north-star pillar #5 (Family Command Center): the Operating Index evening "what changed since yesterday" narrative now appears wherever a family checks in, not just on the Operating Index page. - NEW shared components/operating-index/change-recap.tsx — a server-safe presentational card that renders a ChangeSummary (headline, composite move, cleared/new suggestions, dimension shifts). Calm by default: silent on the first reading. - The Family Operating Index page now reuses it (removed the duplicated inline card + its now-unused icon imports). - /dashboard/command-center: new section diffing the two latest snapshots (summarizeChange) and rendering the recap under the header. - /dashboard/briefing: the Evening tab shows the recap via a new optional `recap` prop threaded into EveningContent (server page reads the two latest snapshots and passes the rendered card in). - supabase/seed_operating_index_all_families.sql: seeds yesterday+today snapshots across ALL families (deliberately different) so the recap renders a real narrative everywhere; idempotent upsert on (family_id, as_of_date). Verified: tsc, eslint, vitest (1699), next build; migration 0125 + seed validated on PG16 (6 rows across 3 families, second run idempotent). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Mz4QW1Ti45CpqKabFHYmPb
NewWorldVenture
pushed a commit
that referenced
this pull request
Jul 12, 2026
Deepens industry-first #5 (household relationship CRM) into a real surface at /dashboard/contacts/[id], linked from every contact card. - lib/contacts/timeline.ts: pure timeline merge (logged visits/calls/messages/ gifts/favors/notes + inbox communications + birthdays) and relationship health — last touch vs. the relationship's natural cadence (median gap) → fresh/due/overdue with a plain-language reconnect nudge. 8 tests. - 0170_contact_interactions: family-scoped touches (kind CHECK, occurred_on, gift amounts). RLS via is_family_member, updated_at trigger. - Mobile-first module: health card, log-a-touch composer (kind chips, date, amount, note), railed timeline with per-entry delete. - seed_contact_interactions.sql: 500 rows across <=25 contacts (auto-creates 8 starter contacts if none), varied cadences; in SEED_ALL.sql. - Docs: PENDING_PROD_MIGRATIONS (0170) + AGENT_HANDOFF session state. PG16-verified: migration idempotent x2, seed 500 x2, 4 RLS policies, trigger. tsc/eslint/build green; full vitest suite 2390 passing. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VJBpzPzPrBbMzYFSVCQaTM
NewWorldVenture
pushed a commit
that referenced
this pull request
Jul 12, 2026
Deepens industry-first #5 (household relationship CRM) into a real surface at /dashboard/contacts/[id], linked from every contact card. - lib/contacts/timeline.ts: pure timeline merge (logged visits/calls/messages/ gifts/favors/notes + inbox communications + birthdays) and relationship health — last touch vs. the relationship's natural cadence (median gap) → fresh/due/overdue with a plain-language reconnect nudge. 8 tests. - 0170_contact_interactions: family-scoped touches (kind CHECK, occurred_on, gift amounts). RLS via is_family_member, updated_at trigger. - Mobile-first module: health card, log-a-touch composer (kind chips, date, amount, note), railed timeline with per-entry delete. - seed_contact_interactions.sql: 500 rows across <=25 contacts (auto-creates 8 starter contacts if none), varied cadences; in SEED_ALL.sql. - Docs: PENDING_PROD_MIGRATIONS (0170) + AGENT_HANDOFF session state. PG16-verified: migration idempotent x2, seed 500 x2, 4 RLS policies, trigger. tsc/eslint/build green; full vitest suite 2390 passing. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VJBpzPzPrBbMzYFSVCQaTM
NewWorldVenture
added a commit
that referenced
this pull request
Jul 12, 2026
Deepens industry-first #5 (household relationship CRM) into a real surface at /dashboard/contacts/[id], linked from every contact card. - lib/contacts/timeline.ts: pure timeline merge (logged visits/calls/messages/ gifts/favors/notes + inbox communications + birthdays) and relationship health — last touch vs. the relationship's natural cadence (median gap) → fresh/due/overdue with a plain-language reconnect nudge. 8 tests. - 0170_contact_interactions: family-scoped touches (kind CHECK, occurred_on, gift amounts). RLS via is_family_member, updated_at trigger. - Mobile-first module: health card, log-a-touch composer (kind chips, date, amount, note), railed timeline with per-entry delete. - seed_contact_interactions.sql: 500 rows across <=25 contacts (auto-creates 8 starter contacts if none), varied cadences; in SEED_ALL.sql. - Docs: PENDING_PROD_MIGRATIONS (0170) + AGENT_HANDOFF session state. PG16-verified: migration idempotent x2, seed 500 x2, 4 RLS policies, trigger. tsc/eslint/build green; full vitest suite 2390 passing. Claude-Session: https://claude.ai/code/session_01VJBpzPzPrBbMzYFSVCQaTM Co-authored-by: Claude <noreply@anthropic.com>
NewWorldVenture
pushed a commit
that referenced
this pull request
Jul 12, 2026
…andoff - SEED_ALL.sql: the owner's re-upload (979deb8, which usefully guarded family_apps with to_regclass) was generated from a pre-#308 copy and dropped the seed_paperwork section — re-append it (47 seeds; PG16-verified x2, 500 rows, idempotent against the shipped 0169_paperwork_items schema). - AI Front Desk: add a "Paperwork Inbox — Bubaly reads it for you" card beside the outbound-calls card (the paper side of the front desk; complements the Communications Hub entry #308 added). No global-nav change. - AGENT_HANDOFF.md: coordination note — the Fable lane independently built the same Paperwork Inbox and lost the push race; #308 is canonical, the duplicate is parked on local branch paperwork-fable-build. Next: #5 CRM timelines, #1/#3 autonomous execution loop. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018G7EyLjpFGBb9xJiGovim5
NewWorldVenture
pushed a commit
that referenced
this pull request
Jul 12, 2026
Deep audit: the Issuing pipeline was already sound (real-time authorization with atomic reserve against the child's Spend bucket, capture/release, idempotent money webhook, capability resolver, honest 3-mode Cards UI). This pass fixes what was genuinely missing: - tests/stripe-issuing-auth.test.ts: the auth decision was documented as "unit-tested separately" but no test existed. 7 tests: approve within balance, decline on inactive/frozen/blocked-category/insufficient, exact-balance edge, status-outranks-balance, unknown-category safety, precheck parity. - Secure PAN reveal (was promised in issuing.ts, never built): prepareCardRevealAction + createCardRevealAction (manager-gated, family-scoped, capability-gated, audited to wallet_audit_logs) and CardRevealModal using Stripe.js v9 Issuing display Elements — nonce → ephemeral key → number/expiry/CVC rendered in Stripe-hosted iframes; the PAN never touches our servers. Reveal button on every card row. - child-detail-view: stale "Coming soon · real-time balance check" on the virtual card art → links to /wallet/cards with honest live-gate copy. - .env.example: STRIPE_MONEY_WEBHOOK_SECRET documented (the money webhook reads it; was entirely undocumented). - Docs: todo.md build-out record + AGENT_HANDOFF block with the owner flip-live checklist (enable Issuing, feature flags, second webhook). Verified: 2411 tests green (7 new), tsc/eslint clean.
NewWorldVenture
pushed a commit
that referenced
this pull request
Sep 20, 2026
…r half Merge #6 with main (13 conflicts). The governing rule, as in merge #5: where the two sessions fixed different halves of one defect class, keep both halves; where one is strictly better, take it and say why. CONFLICTS - contact-center sms + voice/transcription: took main's urgent-delivery RECEIPT wholesale. It closes C1-S7-04 more strongly than my `filed.inserted` boolean did — the receipt claims the dispatch (compare-and-set into phase `dispatching`) before it can send, so it has no read-then-act race. - kid-login-form: kept main's `disabled` guard AND this branch's `focus-ring` class. `.focus-ring` is now self-scoped to `:focus-visible` (C2-B01), so main's `focus-visible:` prefix was a redundant leftover. - voice-module: kept main's staleness re-checks and `if (sb)` guard, this branch's catch-block ordering (report to the user BEFORE the history write, never downstream of a call that fails for the same reason), and the extracted `lib/voice/history.ts` over main's duplicate local helper. - concierge: restored main's prompt line order (which its test pins) and kept this branch's untrusted-content fences. The inner `safeContactText` is load-bearing: `fenceUntrustedBlock` bounds with a plain `.slice()`, which would cut a surrogate pair; bounding scalar-safely first makes it a no-op. - guardian/marketing tests + status.md + finalaudit.md: unioned. finalaudit.md now carries both registers, verified mechanically at 1,572 IDs with nothing lost from either side. TWO THINGS `git checkout --theirs` SILENTLY COST, AND ONE LESSON Taking main's `lib/server/push.ts` after verifying that ONE of my fixes to it survived was not the same as verifying the FILE survived. It dropped: - the SSRF re-check before each web-push send (C3-S5-03). The helper still existed; nothing called it. Restored, and main's two push tests now stub it the way this branch's already did. - the retry window. main retried on any failure with no age bound at all, so a permanently dead endpoint held the row pending and re-buzzed the healthy devices on every scan, for ever, with nothing in the logs saying so. Restored as a union: main's broader retry CONDITION, bounded by age — and bounded for genuine failures only, since a `skipped` row is missing provider configuration, which gets repaired, unlike a dead endpoint. GUARDS RE-POINTED AT BEHAVIOUR, NOT SHAPE Nine of this branch's guards went red against main's rewrites. Each was re-pointed at what it actually protects, and each re-pointing was proved red by mutation before being trusted: - native-push: main replaced legacy FCM with v1 + provider-specific APNs. The guard pinned the legacy literals. Now asserts the behaviours, including that a bare 404 must never prune a registration. - every-server-action-reaches-auth: main added five pre-auth actions. Rather than widen a blanket allow-list past a password-change endpoint, each is listed WITH the credential check it must still reach (CREDENTIAL_GATED), so "pre-auth" cannot quietly become "unchecked". - social-tokens: main's X token store is the service-role gateway this guard asks for, not a breach of it — exempted, but pinned to `createServiceClient` plus its own `requireUserContext`/`requireSocialPermission`. Two of this branch's vacuity guards (C4-S5-01, C4-S5-02) caught eleven real instances in main's tests — helper names asserted without a call site, and ordering assertions on bare `indexOf`, which `-1` satisfies most convincingly when the statement is gone. All fixed; none went red on conversion, so no needle was actually missing. One of my own assertions failed to kill its mutation because it matched a different `else result.failed++` further down, and another passed vacuously on an EMPTY slice because `at()` searches from the start of the file. Both tightened. GATE tsc clean. 16,904/16,907 tests pass across 1,343 files. Lint 0 errors, 3 pre-existing warnings. The 3 remaining failures are two files BLOCKED on this container running Node 22.22.2 against the repo's `.nvmrc` 24.21.0, which main bumped; CI resolves Node from `.nvmrc`, and nvm cannot fetch 24 here. Not claimed as passing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01776xJyhVe8xJQvTGeNfT9T
This branch was successfully deployed
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.
Root cause
Daniel wasn't landing on the Admin Dashboard because super-admin detection depended entirely on the
super_adminsDB table (migration0008). That migration isn't applied to production yet, sois_super_admin()returnsfalse— and the post-login redirect sent Daniel to/dashboard(then/onboardingif he has no family), never/admin. The admin-layout gate would also have bounced him.Fix (works on deploy, no SQL required)
Add a code/env super-admin allowlist that's checked alongside the database:
lib/constants/super-admins.ts—daniel.hughen@gmail.combuilt in, plus an optionalSUPER_ADMIN_EMAILSenv var (comma-separated) for adding others without a DB change. Case-insensitive.isSuperAdmin()now checks the allowlist before the DB RPC, so the admin-layout gate and the dashboard "Site Admin" link work immediately.resolveLandingPathAction()(used by both the password login form and the OAuth callback), so the env/code allowlist is honored — not just the DB.The DB-backed
is_super_admin()RPC remains the source of truth when present; this is purely additive (never a downgrade).Result
Across all three entry paths — password login, Google OAuth, and direct navigation to
/admin— Daniel is recognized as super admin and lands on the Admin Dashboard, with no database migration needed. Applying migration0008later changes nothing for him; it just also seeds the DB.tsc, ESLint, and a fullnext buildpass.🤖 Generated with Claude Code
Generated by Claude Code