2026-01-05 11:24:23 -05:00
|
|
|
package handlers
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"net/http"
|
|
|
|
|
"strconv"
|
|
|
|
|
|
|
|
|
|
"nebula/views"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func RegisterRoutes(mux *http.ServeMux) {
|
|
|
|
|
mux.HandleFunc("GET /", handleWelcome)
|
|
|
|
|
mux.HandleFunc("GET /welcome", handleWelcome)
|
|
|
|
|
mux.HandleFunc("GET /welcome/step/{step}", handleWelcomeStep)
|
2026-01-05 13:34:44 -05:00
|
|
|
|
|
|
|
|
mux.HandleFunc("GET /register", handleRegister)
|
|
|
|
|
mux.HandleFunc("GET /register/step/{step}", handleRegisterStep)
|
|
|
|
|
mux.HandleFunc("GET /register/capabilities", handleRegisterCapabilities)
|
|
|
|
|
mux.HandleFunc("POST /register/verify-code", handleRegisterVerifyCode)
|
2026-01-05 13:44:02 -05:00
|
|
|
|
|
|
|
|
mux.HandleFunc("GET /login", handleLogin)
|
|
|
|
|
mux.HandleFunc("GET /login/step/{step}", handleLoginStep)
|
|
|
|
|
mux.HandleFunc("GET /login/qr-status", handleLoginQRStatus)
|
2026-01-05 13:57:11 -05:00
|
|
|
|
|
|
|
|
mux.HandleFunc("GET /authorize", handleAuthorize)
|
|
|
|
|
mux.HandleFunc("POST /authorize/approve", handleAuthorizeApprove)
|
|
|
|
|
mux.HandleFunc("POST /authorize/deny", handleAuthorizeDeny)
|
|
|
|
|
|
|
|
|
|
mux.HandleFunc("GET /dashboard", handleDashboard)
|
2026-01-05 11:24:23 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// handleWelcome renders the full welcome page at step 1
|
|
|
|
|
func handleWelcome(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
views.WelcomePage(1).Render(r.Context(), w)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// handleWelcomeStep handles HTMX partial updates for step navigation
|
|
|
|
|
func handleWelcomeStep(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
stepStr := r.PathValue("step")
|
|
|
|
|
step, err := strconv.Atoi(stepStr)
|
|
|
|
|
if err != nil || step < 1 || step > 3 {
|
|
|
|
|
step = 1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check if this is an HTMX request
|
|
|
|
|
if r.Header.Get("HX-Request") == "true" {
|
2026-01-05 13:28:41 -05:00
|
|
|
// Return step content with OOB stepper update (HTMX 4 pattern)
|
|
|
|
|
views.WelcomeStepWithStepper(step).Render(r.Context(), w)
|
2026-01-05 11:24:23 -05:00
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
views.WelcomePage(step).Render(r.Context(), w)
|
|
|
|
|
}
|
2026-01-05 13:34:44 -05:00
|
|
|
|
|
|
|
|
func handleRegister(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
state := views.RegisterState{Step: 1}
|
|
|
|
|
views.RegisterPage(state).Render(r.Context(), w)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func handleRegisterStep(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
stepStr := r.PathValue("step")
|
|
|
|
|
step, err := strconv.Atoi(stepStr)
|
|
|
|
|
if err != nil || step < 1 || step > 3 {
|
|
|
|
|
step = 1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
method := r.URL.Query().Get("method")
|
|
|
|
|
if method == "" {
|
|
|
|
|
method = "passkey"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
state := views.RegisterState{Step: step, Method: method}
|
|
|
|
|
|
|
|
|
|
if r.Header.Get("HX-Request") == "true" {
|
|
|
|
|
views.RegisterStepWithStepper(state).Render(r.Context(), w)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
views.RegisterPage(state).Render(r.Context(), w)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func handleRegisterCapabilities(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
caps := views.DeviceCapabilities{
|
|
|
|
|
Platform: true,
|
|
|
|
|
CrossPlatform: true,
|
|
|
|
|
Conditional: true,
|
|
|
|
|
}
|
|
|
|
|
views.CapabilitiesResult(caps).Render(r.Context(), w)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func handleRegisterVerifyCode(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
state := views.RegisterState{Step: 3}
|
|
|
|
|
if r.Header.Get("HX-Request") == "true" {
|
|
|
|
|
views.RegisterStepWithStepper(state).Render(r.Context(), w)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
views.RegisterPage(state).Render(r.Context(), w)
|
|
|
|
|
}
|
2026-01-05 13:44:02 -05:00
|
|
|
|
|
|
|
|
func handleLogin(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
state := views.LoginState{Step: "1"}
|
|
|
|
|
views.LoginPage(state).Render(r.Context(), w)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func handleLoginStep(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
step := r.PathValue("step")
|
|
|
|
|
if step == "" {
|
|
|
|
|
step = "1"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
state := views.LoginState{Step: step}
|
|
|
|
|
|
|
|
|
|
if r.Header.Get("HX-Request") == "true" {
|
|
|
|
|
views.LoginStepWithOOB(state).Render(r.Context(), w)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
views.LoginPage(state).Render(r.Context(), w)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var qrPollCount = 0
|
|
|
|
|
|
|
|
|
|
func handleLoginQRStatus(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
qrPollCount++
|
|
|
|
|
if qrPollCount >= 3 {
|
|
|
|
|
qrPollCount = 0
|
|
|
|
|
views.QRStatusSuccess().Render(r.Context(), w)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
views.QRStatusWaiting().Render(r.Context(), w)
|
|
|
|
|
}
|
2026-01-05 13:57:11 -05:00
|
|
|
|
|
|
|
|
func handleAuthorize(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
reqType := r.URL.Query().Get("type")
|
|
|
|
|
if reqType == "" {
|
|
|
|
|
reqType = "connect"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
req := views.AuthRequest{
|
|
|
|
|
Type: reqType,
|
|
|
|
|
App: views.AppInfo{
|
|
|
|
|
Name: "Uniswap",
|
|
|
|
|
Domain: "app.uniswap.org",
|
|
|
|
|
LogoIcon: "cube",
|
|
|
|
|
Verified: true,
|
|
|
|
|
},
|
|
|
|
|
Wallet: views.WalletInfo{
|
|
|
|
|
Name: "Main Wallet",
|
|
|
|
|
Address: "sonr1x9f...7k2m",
|
|
|
|
|
Balance: "1,234.56 SNR",
|
|
|
|
|
},
|
|
|
|
|
Message: `Welcome to Uniswap!
|
|
|
|
|
|
|
|
|
|
Click to sign in and accept the Uniswap Terms of Service.
|
|
|
|
|
|
|
|
|
|
This request will not trigger a blockchain transaction or cost any gas fees.
|
|
|
|
|
|
|
|
|
|
Wallet address:
|
|
|
|
|
sonr1x9f4h2k8m3n5p7q2r4s6t8v0w3x5y7z9a1b3c5d7k2m
|
|
|
|
|
|
|
|
|
|
Nonce: 8f4a2b1c`,
|
|
|
|
|
MessageHex: "0x57656c636f6d6520746f20556e697377617021...",
|
|
|
|
|
Transaction: &views.TxDetails{
|
|
|
|
|
Type: "Swap",
|
|
|
|
|
FromToken: views.TokenAmount{
|
|
|
|
|
Symbol: "ETH",
|
|
|
|
|
Amount: "100.00",
|
|
|
|
|
USD: "$234,567.00",
|
|
|
|
|
Initials: "E",
|
|
|
|
|
},
|
|
|
|
|
ToToken: views.TokenAmount{
|
|
|
|
|
Symbol: "USDC",
|
|
|
|
|
Amount: "125,000",
|
|
|
|
|
USD: "$125,000.00",
|
|
|
|
|
Initials: "U",
|
|
|
|
|
},
|
|
|
|
|
Network: "Sonr Mainnet",
|
|
|
|
|
NetworkFee: "~$0.12",
|
|
|
|
|
MaxFee: "$0.26",
|
|
|
|
|
Slippage: "0.5%",
|
|
|
|
|
Contract: "0x7a25...3f8b",
|
|
|
|
|
Function: "swapExactTokensForTokens()",
|
|
|
|
|
RawData: "0x38ed1739\n0000000000000000000000000000000000000056bc75e2d63100000...",
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
views.AuthorizePage(req).Render(r.Context(), w)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func handleAuthorizeApprove(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
r.ParseForm()
|
|
|
|
|
actionType := r.FormValue("type")
|
|
|
|
|
if actionType == "" {
|
|
|
|
|
actionType = "connect"
|
|
|
|
|
}
|
|
|
|
|
views.AuthResultSuccess(actionType).Render(r.Context(), w)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func handleAuthorizeDeny(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
views.AuthResultDenied().Render(r.Context(), w)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func handleDashboard(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
tab := r.URL.Query().Get("tab")
|
|
|
|
|
if tab == "" {
|
|
|
|
|
tab = "accounts"
|
|
|
|
|
}
|
|
|
|
|
data := views.DefaultDashboardData()
|
|
|
|
|
views.DashboardPage(data, tab).Render(r.Context(), w)
|
|
|
|
|
}
|