package middleware import ( "context" "log" "net/http" "strings" "aggios-app/backend/internal/repository" ) const SubdomainKey contextKey = "subdomain" // TenantDetector detects tenant from subdomain func TenantDetector(tenantRepo *repository.TenantRepository) func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Get host from X-Forwarded-Host header (set by Next.js proxy) or Host header // Priority order: X-Tenant-Subdomain (set by Next.js middleware) > X-Forwarded-Host > X-Original-Host > Host tenantSubdomain := r.Header.Get("X-Tenant-Subdomain") var host string if tenantSubdomain != "" { // Use direct subdomain from Next.js middleware host = tenantSubdomain log.Printf("TenantDetector: using X-Tenant-Subdomain = %s", tenantSubdomain) } else { // Fallback to extracting from host headers host = r.Header.Get("X-Forwarded-Host") if host == "" { host = r.Header.Get("X-Original-Host") } if host == "" { host = r.Host } log.Printf("TenantDetector: host = %s (from headers), path = %s", host, r.RequestURI) } // Extract subdomain // Examples: // - agencia-xyz.localhost -> agencia-xyz // - agencia-xyz.aggios.app -> agencia-xyz // - dash.localhost -> dash (master admin) // - localhost -> (institutional site) var subdomain string // If we got the subdomain directly from X-Tenant-Subdomain, use it if tenantSubdomain != "" { subdomain = tenantSubdomain // Remove port if present if strings.Contains(subdomain, ":") { subdomain = strings.Split(subdomain, ":")[0] } } else { // Extract from host parts := strings.Split(host, ".") if len(parts) >= 2 { // Has subdomain subdomain = parts[0] // Remove port if present if strings.Contains(subdomain, ":") { subdomain = strings.Split(subdomain, ":")[0] } } } log.Printf("TenantDetector: extracted subdomain = %s", subdomain) // Add subdomain to context ctx := context.WithValue(r.Context(), SubdomainKey, subdomain) // If subdomain is not empty and not "dash" or "api", try to find tenant if subdomain != "" && subdomain != "dash" && subdomain != "api" && subdomain != "localhost" { tenant, err := tenantRepo.FindBySubdomain(subdomain) if err == nil && tenant != nil { log.Printf("TenantDetector: found tenant %s for subdomain %s", tenant.ID.String(), subdomain) ctx = context.WithValue(ctx, TenantIDKey, tenant.ID.String()) } else { log.Printf("TenantDetector: tenant not found for subdomain %s (err=%v)", subdomain, err) } } next.ServeHTTP(w, r.WithContext(ctx)) }) } }