Conversation
… auth - Railway GraphQL client for programmatic domain add/remove/list - Middleware Priority 2: custom domain rewrite (onetree.com → /store/_cd/) - getTenantBaseUrl() for canonical tenant URLs (SEO, emails, OG tags) - DNS verification endpoint with CNAME/A record checks - Super-admin UI: domain field, DNS instructions, verify button - Clerk proxy mode via /__clerk rewrite (single DNS record per tenant) - Replaced all hardcoded budstacks.io refs with NEXT_PUBLIC_BASE_DOMAIN - Fixed robots.txt, sitemap.xml, consultation success for custom domains - PRD and E2E test plan included
…nstructions panel
- Delete lib/template-service.ts (zero imports, stale architecture) - Remove unused getFileUrlWithFallback() from lib/s3.ts - Fix type error: widen railwayDnsRecords to allow null - Update setup guide: Railway requires 2 DNS records (CNAME + TXT) - Update PRD with implementation status and lessons learned - Add custom-domain-setup-guide.md for admin/tenant onboarding
8 server-rendered pages were looking up tenants by subdomain using params.slug directly. On custom domains the middleware rewrites to /store/_cd/... so slug='_cd' which matches no tenant → 404. Switched to getCurrentTenant() which reads the x-tenant-custom-domain header set by middleware, correctly resolving the tenant on both subdomain and custom domain requests. Affected: about, contact, consultation, conditions, support, the-wire, the-wire/[postSlug], orders/[orderId]
… support All 9 store API routes were using slug-based tenant lookup (getTenantBySlug or prisma.findUnique by subdomain), which fails when custom domain middleware rewrites the path with placeholder slug "_cd". Switched to getCurrentTenant() which reads middleware-set headers and resolves correctly for all routing modes (subdomain, path-based, custom domain). Also reverted the _cd fallback hack from getTenantBySlug — proper fix, no fallbacks.
Middleware was rewriting xplaincrypto.ai/__clerk/* to /store/_cd/__clerk/* which broke Clerk auth proxy. Added passthrough so /__clerk requests reach the next.config.js rewrite to Clerk's frontend API.
The rewrite forwarded /__clerk/npm/... to clerk.accounts.dev/__clerk/npm/... but Clerk serves JS at /npm/... (no __clerk prefix). Strip the prefix when proxying so clerk.browser.js loads correctly on custom domains.
Extract reusable Clerk auth component (NavAuthButton) with dark/light variants and configurable login style (button or icon). Add to all existing navs, refactor NavHealingBuds to use shared component, and introduce NavPill — a compact centered floating capsule nav.
NavAuthButton was gating on isLoaded — if Clerk proxy fails on custom domains, isLoaded stays false and users see a skeleton forever with no way to log in. Now Login button/icon shows immediately, only swaps to avatar dropdown when Clerk confirms isSignedIn.
Clerk proxy mode requires each proxy domain registered in the Clerk Dashboard — doesn't scale for per-tenant custom domains. Removed proxyUrl so Clerk uses its standard redirect-based auth flow which works on any domain without configuration.
Split login into server component (resolves tenant, fetches branding) and client component (renders Clerk SignIn). Hides Clerk's default "Sign in to BudStack" header and replaces with tenant's business name and logo from branding table.
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (53)
📝 WalkthroughWalkthroughThis PR adds custom-domain support, switches tenant resolution from slug-based lookups to request-context Changes
Sequence DiagramsequenceDiagram
participant Admin as Super Admin UI
participant API as Verify-Domain Endpoint
participant Railway as Railway GraphQL API
participant DNS as DNS Resolver
participant DB as Prisma
Admin->>API: GET /api/super-admin/tenants/{id}/verify-domain
API->>API: load tenant, require customDomain, determine apex vs subdomain
alt Apex domain
API->>DNS: dns.resolve4(domain)
DNS-->>API: A records or ENOTFOUND
API->>API: set verification.status = verified|pending
else Subdomain
API->>DNS: dns.resolveCname(domain)
DNS-->>API: CNAME records or ENOTFOUND
API->>API: compare CNAMEs to expected Railway target
API->>Railway: (optional) fetch expected cnameTarget
Railway-->>API: cnameTarget
API->>API: set status = verified|misconfigured|pending
end
API->>DB: prisma.tenants.update(settings.domainVerification = ...)
DB-->>API: updated tenant
API-->>Admin: JSON { domain, isApex, cnameTarget, verification }
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
Poem
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Summary by CodeRabbit
New Features
Configuration
budstacks.io.Documentation