Skip to content

Template editor#35

Merged
AutomatosAI merged 15 commits intomainfrom
template-editor
Apr 9, 2026
Merged

Template editor#35
AutomatosAI merged 15 commits intomainfrom
template-editor

Conversation

@AutomatosAI
Copy link
Copy Markdown
Owner

@AutomatosAI AutomatosAI commented Apr 9, 2026

Summary by CodeRabbit

  • New Features

    • Added custom domain support with DNS verification workflow for tenant storefronts.
    • Integrated authentication controls (login, account management, role-based navigation) into navigation components.
  • Configuration

    • Made base domain suffix configurable via environment variable; defaults to budstacks.io.
  • Documentation

    • Added comprehensive guides for custom domain setup, testing, and technical requirements.

Gerard161-Site and others added 15 commits April 9, 2026 13:35
… 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
- 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.
@railway-app railway-app bot temporarily deployed to budstack-saas / Development April 9, 2026 18:52 Inactive
@AutomatosAI AutomatosAI merged commit 40cf219 into main Apr 9, 2026
1 check was pending
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 9, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c245d00f-7639-4e39-a88b-c02963eab337

📥 Commits

Reviewing files that changed from the base of the PR and between 498c193 and 3fb42ec.

📒 Files selected for processing (53)
  • .gitignore
  • nextjs_space/app/api/store/[slug]/cart/add/route.ts
  • nextjs_space/app/api/store/[slug]/cart/clear/route.ts
  • nextjs_space/app/api/store/[slug]/cart/remove/route.ts
  • nextjs_space/app/api/store/[slug]/cart/route.ts
  • nextjs_space/app/api/store/[slug]/orders/[orderId]/route.ts
  • nextjs_space/app/api/store/[slug]/orders/route.ts
  • nextjs_space/app/api/store/[slug]/orders/submit/route.ts
  • nextjs_space/app/api/store/[slug]/products/route.ts
  • nextjs_space/app/api/super-admin/tenants/[id]/route.ts
  • nextjs_space/app/api/super-admin/tenants/[id]/verify-domain/route.ts
  • nextjs_space/app/layout.tsx
  • nextjs_space/app/onboarding/onboarding-form.tsx
  • nextjs_space/app/store/[slug]/about/page.tsx
  • nextjs_space/app/store/[slug]/conditions/page.tsx
  • nextjs_space/app/store/[slug]/consultation/page.tsx
  • nextjs_space/app/store/[slug]/consultation/success/page.tsx
  • nextjs_space/app/store/[slug]/contact/page.tsx
  • nextjs_space/app/store/[slug]/login/login-form.tsx
  • nextjs_space/app/store/[slug]/login/page.tsx
  • nextjs_space/app/store/[slug]/orders/[orderId]/page.tsx
  • nextjs_space/app/store/[slug]/page.tsx
  • nextjs_space/app/store/[slug]/robots.txt/route.ts
  • nextjs_space/app/store/[slug]/sitemap.xml/route.ts
  • nextjs_space/app/store/[slug]/support/page.tsx
  • nextjs_space/app/store/[slug]/the-wire/[postSlug]/page.tsx
  • nextjs_space/app/store/[slug]/the-wire/page.tsx
  • nextjs_space/app/super-admin/analytics/page.tsx
  • nextjs_space/app/super-admin/tenants/[id]/tenant-edit-form.tsx
  • nextjs_space/app/tenant-admin/seo/page.tsx
  • nextjs_space/app/tenant-admin/settings/settings-form.tsx
  • nextjs_space/components/sections/navigation/NavAuthButton.tsx
  • nextjs_space/components/sections/navigation/NavDark.tsx
  • nextjs_space/components/sections/navigation/NavFull.tsx
  • nextjs_space/components/sections/navigation/NavHealingBuds.tsx
  • nextjs_space/components/sections/navigation/NavMinimal.tsx
  • nextjs_space/components/sections/navigation/NavPill.tsx
  • nextjs_space/components/sections/navigation/NavTransparent.tsx
  • nextjs_space/emails/order-confirmation.tsx
  • nextjs_space/emails/tenant-welcome.tsx
  • nextjs_space/lib/railway-api.ts
  • nextjs_space/lib/s3.ts
  • nextjs_space/lib/section-registry.ts
  • nextjs_space/lib/section-schemas.ts
  • nextjs_space/lib/template-service.ts
  • nextjs_space/lib/tenant-utils.ts
  • nextjs_space/lib/tenant.ts
  • nextjs_space/middleware.ts
  • nextjs_space/next.config.js
  • nextjs_space/scripts/seed-order-template.ts
  • tasks/custom-domain-setup-guide.md
  • tasks/custom-domain-test-plan.md
  • tasks/prd-custom-domains.md

📝 Walkthrough

Walkthrough

This PR adds custom-domain support, switches tenant resolution from slug-based lookups to request-context getCurrentTenant(), integrates a Railway GraphQL client for domain provisioning, adds DNS verification endpoints and UI, updates middleware/rewrites for custom domains and Clerk proxying, and introduces new navigation/auth components and related UI changes.

Changes

Cohort / File(s) Summary
Tenant resolution migration
nextjs_space/app/api/store/[slug]/cart/*, nextjs_space/app/api/store/[slug]/orders/*, nextjs_space/app/store/[slug]/*, nextjs_space/app/store/[slug]/**
Replaced slug-based prisma.tenants.findUnique / getTenantBySlug usage with getCurrentTenant() across API routes and pages; updated control flow to depend on request-context tenant.
Custom domain provisioning & verification
nextjs_space/lib/railway-api.ts, nextjs_space/app/api/super-admin/tenants/[id]/route.ts, nextjs_space/app/api/super-admin/tenants/[id]/verify-domain/route.ts
Added Railway GraphQL client and domain operations; implemented Railway provisioning/removal in tenant PATCH/DELETE; added DNS verification endpoint with A/CNAME/TXT checks and persisted verification state into tenant.settings.
Middleware, rewrites & tenant utils
nextjs_space/middleware.ts, nextjs_space/next.config.js, nextjs_space/lib/tenant.ts, nextjs_space/lib/tenant-utils.ts
Middleware now rewrites custom-domain storefront requests to /store/_cd*, sets tenant headers, and preserves API/auth/Clerk routing exceptions; added getTenantBaseUrl() and improved host parsing for custom-domain detection; added Clerk proxy rewrite.
Navigation & auth components
nextjs_space/components/sections/navigation/NavAuthButton.tsx, .../NavPill.tsx, .../Nav*.tsx
Introduced NavAuthButton (Clerk-aware auth UX) and new NavPill component; integrated auth button into multiple nav variants and updated nav behavior and registrations.
Login flow & server-side branding
nextjs_space/app/store/[slug]/login/login-form.tsx, .../login/page.tsx
Added reusable TenantLoginForm client component; converted login page to async server component and fetches tenant/branding server-side via getCurrentTenant() and Prisma.
Emails & templates adjustments
nextjs_space/emails/*.tsx, nextjs_space/scripts/seed-order-template.ts
Email templates now accept optional storeUrl/customDomain and build URLs using NEXT_PUBLIC_BASE_DOMAIN fallback instead of hardcoded .budstacks.io.
Tenant admin UI & DNS UX
nextjs_space/app/super-admin/tenants/[id]/tenant-edit-form.tsx, nextjs_space/app/tenant-admin/seo/page.tsx
Added DNS verification UI, copy-to-clipboard, status badges, and merged Railway metadata into tenant settings; replaced local base URL logic with getTenantBaseUrl().
Sitemap / robots / SEO base URL
nextjs_space/app/store/[slug]/sitemap.xml/route.ts, .../robots.txt/route.ts, .../page.tsx
Removed slug param from handlers, use getCurrentTenant(); fetch products/posts by tenantId; centralized base URL via getTenantBaseUrl().
Removals & refactors
nextjs_space/lib/template-service.ts, nextjs_space/lib/s3.ts, .gitignore
Deleted template-service.ts (tenant template lifecycle), removed getFileUrlWithFallback from S3 utils, and added graphify-out/ to .gitignore.
Section registry & schemas
nextjs_space/lib/section-registry.ts, nextjs_space/lib/section-schemas.ts
Registered NavPill in section registry and added NavPill to NAV_STYLES; updated some nav descriptions.
Docs & test plans
tasks/custom-domain-setup-guide.md, tasks/custom-domain-test-plan.md, tasks/prd-custom-domains.md
Added setup guide, E2E test plan, and PRD for custom-domain support and operational procedures.
Minor/UI wide domain fallback changes
nextjs_space/app/onboarding/onboarding-form.tsx, .../super-admin/analytics/page.tsx, .../tenant-admin/settings/settings-form.tsx, emails/*
Replaced hardcoded .budstacks.io UI text with `.{process.env.NEXT_PUBLIC_BASE_DOMAIN

Sequence Diagram

sequenceDiagram
    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 }
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

codex

Poem

🐰 Hooray — a rabbit hops on rails and DNS,

I stitch domains with tiny, twitchy lens,
From slug to header, tenant finds its place,
Now stores wear names of their own webspace,
I thump my paws — hooray for custom-domain wins!

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch template-editor

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@AutomatosAI AutomatosAI deleted the template-editor branch April 16, 2026 20:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants