package handlers import ( "aggios-app/backend/internal/domain" "aggios-app/backend/internal/repository" "aggios-app/backend/internal/service" "encoding/json" "log" "net/http" "golang.org/x/crypto/bcrypt" ) type AgencyTemplateHandler struct { templateRepo *repository.AgencyTemplateRepository agencyService *service.AgencyService userRepo *repository.UserRepository tenantRepo *repository.TenantRepository } func NewAgencyTemplateHandler( templateRepo *repository.AgencyTemplateRepository, agencyService *service.AgencyService, userRepo *repository.UserRepository, tenantRepo *repository.TenantRepository, ) *AgencyTemplateHandler { return &AgencyTemplateHandler{ templateRepo: templateRepo, agencyService: agencyService, userRepo: userRepo, tenantRepo: tenantRepo, } } // GetTemplateBySlug - Public endpoint to get template details func (h *AgencyTemplateHandler) GetTemplateBySlug(w http.ResponseWriter, r *http.Request) { slug := r.URL.Query().Get("slug") if slug == "" { http.Error(w, "Missing slug parameter", http.StatusBadRequest) return } template, err := h.templateRepo.FindBySlug(slug) if err != nil { log.Printf("Template not found: %v", err) http.Error(w, "Template not found or expired", http.StatusNotFound) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(template) } // PublicRegisterAgency - Public endpoint for agency registration via template func (h *AgencyTemplateHandler) PublicRegisterAgency(w http.ResponseWriter, r *http.Request) { var req domain.AgencyRegistrationViaTemplate if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Invalid request body", http.StatusBadRequest) return } // 1. Validar template template, err := h.templateRepo.FindBySlug(req.TemplateSlug) if err != nil { log.Printf("Template error: %v", err) http.Error(w, "Invalid or expired template", http.StatusBadRequest) return } // 2. Validar campos obrigatórios if req.AgencyName == "" || req.Subdomain == "" || req.AdminEmail == "" || req.AdminPassword == "" { http.Error(w, "Missing required fields", http.StatusBadRequest) return } // 3. Validar senha if len(req.AdminPassword) < 8 { http.Error(w, "Password must be at least 8 characters", http.StatusBadRequest) return } // 4. Verificar se email já existe existingUser, _ := h.userRepo.FindByEmail(req.AdminEmail) if existingUser != nil { http.Error(w, "Email already registered", http.StatusConflict) return } // 5. Verificar se subdomain já existe existingTenant, _ := h.tenantRepo.FindBySubdomain(req.Subdomain) if existingTenant != nil { http.Error(w, "Subdomain already taken", http.StatusConflict) return } // 6. Hash da senha hashedPassword, err := bcrypt.GenerateFromPassword([]byte(req.AdminPassword), bcrypt.DefaultCost) if err != nil { log.Printf("Error hashing password: %v", err) http.Error(w, "Error processing password", http.StatusInternalServerError) return } // 7. Criar tenant (agência) tenant := &domain.Tenant{ Name: req.AgencyName, Domain: req.Subdomain + ".aggios.app", Subdomain: req.Subdomain, CNPJ: req.CNPJ, RazaoSocial: req.RazaoSocial, Website: req.Website, Phone: req.Phone, Description: req.Description, Industry: req.Industry, TeamSize: req.TeamSize, } // Endereço (se fornecido) if req.Address != nil { tenant.Address = req.Address["street"] tenant.Number = req.Address["number"] tenant.Complement = req.Address["complement"] tenant.Neighborhood = req.Address["neighborhood"] tenant.City = req.Address["city"] tenant.State = req.Address["state"] tenant.Zip = req.Address["cep"] } // Personalização do template if template.CustomPrimaryColor.Valid { tenant.PrimaryColor = template.CustomPrimaryColor.String } if template.CustomLogoURL.Valid { tenant.LogoURL = template.CustomLogoURL.String } if err := h.tenantRepo.Create(tenant); err != nil { log.Printf("Error creating tenant: %v", err) http.Error(w, "Error creating agency", http.StatusInternalServerError) return } // 8. Criar usuário admin da agência user := &domain.User{ Email: req.AdminEmail, Password: string(hashedPassword), Name: req.AdminName, Role: "ADMIN_AGENCIA", TenantID: &tenant.ID, } if err := h.userRepo.Create(user); err != nil { log.Printf("Error creating user: %v", err) http.Error(w, "Error creating admin user", http.StatusInternalServerError) return } // 9. Incrementar contador de uso do template if err := h.templateRepo.IncrementUsageCount(template.ID.String()); err != nil { log.Printf("Warning: failed to increment usage count: %v", err) } // 10. Preparar resposta com redirect redirectURL := template.RedirectURL.String if redirectURL == "" { redirectURL = "http://" + req.Subdomain + ".localhost/login" } response := map[string]interface{}{ "success": true, "message": template.SuccessMessage.String, "tenant_id": tenant.ID, "user_id": user.ID, "redirect_url": redirectURL, "subdomain": req.Subdomain, } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(response) } // CreateTemplate - SUPERADMIN only func (h *AgencyTemplateHandler) CreateTemplate(w http.ResponseWriter, r *http.Request) { var req domain.CreateAgencyTemplateRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Invalid request", http.StatusBadRequest) return } formFieldsJSON, _ := repository.FormFieldsToJSON(req.FormFields) modulesJSON, _ := json.Marshal(req.AvailableModules) template := &domain.AgencySignupTemplate{ Name: req.Name, Slug: req.Slug, Description: req.Description, FormFields: formFieldsJSON, AvailableModules: modulesJSON, IsActive: true, } if req.CustomPrimaryColor != "" { template.CustomPrimaryColor.Valid = true template.CustomPrimaryColor.String = req.CustomPrimaryColor } if req.CustomLogoURL != "" { template.CustomLogoURL.Valid = true template.CustomLogoURL.String = req.CustomLogoURL } if req.RedirectURL != "" { template.RedirectURL.Valid = true template.RedirectURL.String = req.RedirectURL } if req.SuccessMessage != "" { template.SuccessMessage.Valid = true template.SuccessMessage.String = req.SuccessMessage } if err := h.templateRepo.Create(template); err != nil { log.Printf("Error creating template: %v", err) http.Error(w, "Error creating template", http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(template) } // ListTemplates - SUPERADMIN only func (h *AgencyTemplateHandler) ListTemplates(w http.ResponseWriter, r *http.Request) { templates, err := h.templateRepo.List() if err != nil { http.Error(w, "Error fetching templates", http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(templates) }