From aada1c6a4e417b13ef3d2042866802f7aabc71dd Mon Sep 17 00:00:00 2001 From: James Date: Wed, 8 Apr 2026 12:11:05 -0400 Subject: [PATCH] clavitor.ai: oauth, onboarding flow, admin, templates, css Co-Authored-By: Claude Opus 4.6 (1M context) --- clavitor.ai/admin/main.go | 239 ++++- clavitor.ai/admin/main_test.go | 896 ++++++++++++++++++ clavitor.ai/admin/paddle_checkout.go | 123 ++- clavitor.ai/admin/price_utils.go | 256 +++++ clavitor.ai/admin/scripts/fetch_rates.go | 99 ++ clavitor.ai/admin/sync.go | 53 +- clavitor.ai/clavitor.css | 69 +- clavitor.ai/main.go | 87 ++ clavitor.ai/oauth.go | 293 ++++++ clavitor.ai/onboarding.go | 322 +++++++ clavitor.ai/templates/base.tmpl | 92 +- clavitor.ai/templates/claude-code.tmpl | 16 +- clavitor.ai/templates/codex.tmpl | 4 +- clavitor.ai/templates/cookies.tmpl | 67 ++ clavitor.ai/templates/dpa.tmpl | 104 ++ clavitor.ai/templates/footer.tmpl | 65 +- clavitor.ai/templates/for-consumer.tmpl | 10 +- clavitor.ai/templates/for-enterprise.tmpl | 4 +- clavitor.ai/templates/for-mme.tmpl | 4 +- clavitor.ai/templates/for-smb.tmpl | 16 +- clavitor.ai/templates/glass.tmpl | 4 +- clavitor.ai/templates/hosted.tmpl | 10 +- clavitor.ai/templates/index.tmpl | 4 +- clavitor.ai/templates/integrations.tmpl | 2 +- .../templates/onboarding-checkout.tmpl | 842 ++++++++++++++++ clavitor.ai/templates/onboarding-details.tmpl | 480 ++++++++++ clavitor.ai/templates/onboarding-done.tmpl | 539 +++++++++++ clavitor.ai/templates/onboarding-login.tmpl | 478 ++++++++++ clavitor.ai/templates/onboarding-plan.tmpl | 68 ++ clavitor.ai/templates/onboarding-product.tmpl | 702 ++++++++++++++ clavitor.ai/templates/onboarding-profile.tmpl | 85 ++ clavitor.ai/templates/onboarding-terms.tmpl | 537 +++++++++++ clavitor.ai/templates/openclaw-cn.tmpl | 12 +- clavitor.ai/templates/openclaw.tmpl | 12 +- clavitor.ai/templates/pricing-new.tmpl | 153 --- clavitor.ai/templates/pricing.tmpl | 20 +- clavitor.ai/templates/privacy.tmpl | 22 +- clavitor.ai/templates/signup.tmpl | 80 +- clavitor.ai/templates/subprocessors.tmpl | 100 ++ clavitor.ai/templates/terms.tmpl | 63 +- clavitor.ai/templates/upgrade.tmpl | 2 +- clavitor.ai/tlw.go | 309 ++++++ 42 files changed, 6976 insertions(+), 367 deletions(-) create mode 100644 clavitor.ai/admin/main_test.go create mode 100644 clavitor.ai/admin/price_utils.go create mode 100644 clavitor.ai/admin/scripts/fetch_rates.go create mode 100644 clavitor.ai/oauth.go create mode 100644 clavitor.ai/onboarding.go create mode 100644 clavitor.ai/templates/cookies.tmpl create mode 100644 clavitor.ai/templates/dpa.tmpl create mode 100644 clavitor.ai/templates/onboarding-checkout.tmpl create mode 100644 clavitor.ai/templates/onboarding-details.tmpl create mode 100644 clavitor.ai/templates/onboarding-done.tmpl create mode 100644 clavitor.ai/templates/onboarding-login.tmpl create mode 100644 clavitor.ai/templates/onboarding-plan.tmpl create mode 100644 clavitor.ai/templates/onboarding-product.tmpl create mode 100644 clavitor.ai/templates/onboarding-profile.tmpl create mode 100644 clavitor.ai/templates/onboarding-terms.tmpl delete mode 100644 clavitor.ai/templates/pricing-new.tmpl create mode 100644 clavitor.ai/templates/subprocessors.tmpl create mode 100644 clavitor.ai/tlw.go diff --git a/clavitor.ai/admin/main.go b/clavitor.ai/admin/main.go index 0d97cf6..5a784dd 100644 --- a/clavitor.ai/admin/main.go +++ b/clavitor.ai/admin/main.go @@ -17,6 +17,7 @@ import ( ) var db *sql.DB +var isSandboxMode bool func main() { var err error @@ -26,6 +27,14 @@ func main() { } defer db.Close() + // Detect sandbox mode by IP + isSandboxMode = !isProductionServer() + if isSandboxMode { + fmt.Println("🧪 SANDBOX MODE DETECTED (non-production IP)") + } else { + fmt.Println("🏭 PRODUCTION MODE (running on clavitor.ai)") + } + initDB() r := chi.NewRouter() @@ -114,9 +123,9 @@ func handleDynamicHome(w http.ResponseWriter, r *http.Request) { html := ` -Clavitor Dynamic Admin +Clavitor Dynamic Admin{{if .IsSandbox}} [SANDBOX]{{end}} +{{if .IsSandbox}}
🧪 SANDBOX MODE Test environment - not production
{{end}} -

Clavitor Corporate Admin

-

Auto-generated from SQLite schema • 95% Paddle + 5% Extensions

+

Clavitor Corporate Admin{{if .IsSandbox}} [SANDBOX]{{end}}

+

Auto-generated from SQLite schema • 95% Paddle + 5% Extensions{{if .IsSandbox}} • TEST ENVIRONMENT{{end}}

{{range .Cards}} @@ -152,6 +174,20 @@ h1 { margin-bottom: 10px; }
{{end}} + + +
+

💰 USD Pricing (from database)

+
+{{range .Prices}} +
+

{{.ProductName}}

+
{{.Amount}} {{.Currency}}
+
+{{end}} +
+
+ ` @@ -167,10 +203,56 @@ h1 { margin-bottom: 10px; } cards = append(cards, Card{Name: t, Count: count}) } + // Fetch USD prices from database (Paddle JSON format) + type PriceInfo struct { + ProductName string + Amount string + Currency string + } + var prices []PriceInfo + rows, err := db.Query(` + SELECT p.name, pr.unit_price + FROM products p + JOIN prices pr ON p.id = pr.product_id + WHERE pr.unit_price LIKE '%USD%' + ORDER BY p.name + `) + if err == nil { + defer rows.Close() + for rows.Next() { + var name, unitPrice string + rows.Scan(&name, &unitPrice) + // Parse Paddle JSON: {"amount": "1200", "currency_code": "USD"} + amount := "0" + currency := "USD" + if idx := strings.Index(unitPrice, `"amount":`); idx >= 0 { + after := unitPrice[idx+9:] + if q1 := strings.Index(after, `"`); q1 >= 0 { + valStart := q1 + 1 + if q2 := strings.Index(after[valStart:], `"`); q2 >= 0 { + amount = after[valStart : valStart+q2] + } + } + } + if idx := strings.Index(unitPrice, `"currency_code":`); idx >= 0 { + after := unitPrice[idx+17:] + if q1 := strings.Index(after, `"`); q1 >= 0 { + valStart := q1 + 1 + if q2 := strings.Index(after[valStart:], `"`); q2 >= 0 { + currency = after[valStart : valStart+q2] + } + } + } + prices = append(prices, PriceInfo{ProductName: name, Amount: amount, Currency: currency}) + } + } + tmpl := template.Must(template.New("home").Parse(html)) tmpl.Execute(w, map[string]interface{}{ - "Tables": tables, - "Cards": cards, + "Tables": tables, + "Cards": cards, + "Prices": prices, + "IsSandbox": isSandboxMode, }) } @@ -222,9 +304,9 @@ func handleDynamicList(w http.ResponseWriter, r *http.Request) { htmlTemplate := ` -{{.Table}} - List +{{.Table}} - List{{if .IsSandbox}} [SANDBOX]{{end}} +{{if .IsSandbox}}
🧪 SANDBOX MODE - Test environment
{{end}}