package controllers import ( "time" "github.com/gofiber/fiber/v2" "github.com/sujit-baniya/fiber-boilerplate/pkg/models" "github.com/sujit-baniya/fiber-boilerplate/pkg/services" ) // ─── Configuración ──────────────────────────────────────────────────────────── // HostingerConfigPage renderiza la vista de gestión de Hostinger. func HostingerConfigPage(c *fiber.Ctx) error { cfg, _ := models.GetHostingerConfig() if err := c.Render("hostinger", fiber.Map{ "Title": "Hostinger API", "Config": cfg, "user": c.Locals("user"), "modules": c.Locals("userModules"), }, "layouts/main"); err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()}) } return nil } // SaveHostingerConfig guarda o actualiza el token de Hostinger. func SaveHostingerConfig(c *fiber.Ctx) error { type body struct { ID uint `json:"id" form:"id"` Token string `json:"token" form:"token"` Nota string `json:"nota" form:"nota"` } var b body if err := c.BodyParser(&b); err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "body inválido"}) } if b.Token == "" { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "token requerido"}) } cfg := models.HostingerConfig{ Nota: b.Nota, Token: b.Token, } cfg.ID = b.ID if err := models.SaveHostingerConfig(cfg); err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()}) } return c.JSON(fiber.Map{"message": "Configuración guardada"}) } // ─── Helpers ───────────────────────────────────────────────────────────────── func hostingerClient() (*services.HostingerClient, error) { cfg, err := models.GetHostingerConfig() if err != nil { return nil, err } return services.NewHostingerClient(cfg.Token), nil } // ─── Endpoints de datos ─────────────────────────────────────────────────────── // GetHostingerVPS devuelve la lista de VPS/VMs. func GetHostingerVPS(c *fiber.Ctx) error { client, err := hostingerClient() if err != nil { return c.Status(fiber.StatusFailedDependency).JSON(fiber.Map{ "error": "No se encontró configuración activa de Hostinger", }) } data, err := client.GetVPSList() if err != nil { return c.Status(fiber.StatusUnprocessableEntity).JSON(fiber.Map{"error": err.Error()}) } return c.JSON(fiber.Map{"data": data}) } // GetHostingerDomains devuelve el portafolio de dominios. func GetHostingerDomains(c *fiber.Ctx) error { client, err := hostingerClient() if err != nil { return c.Status(fiber.StatusFailedDependency).JSON(fiber.Map{ "error": "No se encontró configuración activa de Hostinger", }) } data, err := client.GetDomains() if err != nil { return c.Status(fiber.StatusUnprocessableEntity).JSON(fiber.Map{"error": err.Error()}) } return c.JSON(fiber.Map{"data": data}) } // GetHostingerDNS devuelve los registros DNS y los nameservers de un dominio (:domain). func GetHostingerDNS(c *fiber.Ctx) error { domain := c.Params("domain") if domain == "" { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "dominio requerido"}) } client, err := hostingerClient() if err != nil { return c.Status(fiber.StatusFailedDependency).JSON(fiber.Map{ "error": "No se encontró configuración activa de Hostinger", }) } records, err := client.GetDNSRecords(domain) if err != nil { return c.Status(fiber.StatusUnprocessableEntity).JSON(fiber.Map{"error": err.Error()}) } // Nameservers son opcionales: si falla (ej. VPS hostnames no están en portafolio) se ignora. ns, _ := client.GetDomainNameServers(domain) return c.JSON(fiber.Map{"data": records, "name_servers": ns, "domain": domain}) } // GetHostingerOrders devuelve las órdenes de facturación. func GetHostingerOrders(c *fiber.Ctx) error { client, err := hostingerClient() if err != nil { return c.Status(fiber.StatusFailedDependency).JSON(fiber.Map{ "error": "No se encontró configuración activa de Hostinger", }) } data, err := client.GetOrders() if err != nil { return c.Status(fiber.StatusUnprocessableEntity).JSON(fiber.Map{"error": err.Error()}) } return c.JSON(fiber.Map{"data": data}) } // GetHostingerHosting devuelve las cuentas de hosting. func GetHostingerHosting(c *fiber.Ctx) error { client, err := hostingerClient() if err != nil { return c.Status(fiber.StatusFailedDependency).JSON(fiber.Map{ "error": "No se encontró configuración activa de Hostinger", }) } data, err := client.GetHostingAccounts() if err != nil { return c.Status(fiber.StatusUnprocessableEntity).JSON(fiber.Map{"error": err.Error()}) } return c.JSON(fiber.Map{"data": data}) } // ─── DNS: escritura ────────────────────────────────────────────────────────── // UpdateHostingerDNS agrega o actualiza registros DNS. func UpdateHostingerDNS(c *fiber.Ctx) error { domain := c.Params("domain") if domain == "" { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "dominio requerido"}) } var body struct { Overwrite bool `json:"overwrite"` Zone []services.HostingerDNSZoneInput `json:"zone"` } if err := c.BodyParser(&body); err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "body inválido"}) } client, err := hostingerClient() if err != nil { return c.Status(fiber.StatusFailedDependency).JSON(fiber.Map{"error": "No se encontró configuración activa de Hostinger"}) } if err := client.UpdateDNSZone(domain, body.Overwrite, body.Zone); err != nil { return c.Status(fiber.StatusUnprocessableEntity).JSON(fiber.Map{"error": err.Error()}) } return c.JSON(fiber.Map{"message": "DNS actualizado"}) } // ResetHostingerDNS resetea la zona DNS de un dominio. func ResetHostingerDNS(c *fiber.Ctx) error { domain := c.Params("domain") if domain == "" { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "dominio requerido"}) } client, err := hostingerClient() if err != nil { return c.Status(fiber.StatusFailedDependency).JSON(fiber.Map{"error": "No se encontró configuración activa de Hostinger"}) } if err := client.ResetDNS(domain); err != nil { return c.Status(fiber.StatusUnprocessableEntity).JSON(fiber.Map{"error": err.Error()}) } return c.JSON(fiber.Map{"message": "Zona DNS reseteada"}) } // ─── Domains: escritura ────────────────────────────────────────────────────── // UpdateHostingerNameservers actualiza los nameservers de un dominio. func UpdateHostingerNameservers(c *fiber.Ctx) error { domain := c.Params("domain") var body struct { NS1 string `json:"ns1"` NS2 string `json:"ns2"` NS3 string `json:"ns3"` NS4 string `json:"ns4"` } if err := c.BodyParser(&body); err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "body inválido"}) } client, err := hostingerClient() if err != nil { return c.Status(fiber.StatusFailedDependency).JSON(fiber.Map{"error": "No se encontró configuración activa de Hostinger"}) } if err := client.UpdateNameservers(domain, body.NS1, body.NS2, body.NS3, body.NS4); err != nil { return c.Status(fiber.StatusUnprocessableEntity).JSON(fiber.Map{"error": err.Error()}) } return c.JSON(fiber.Map{"message": "Nameservers actualizados"}) } // EnableHostingerDomainLock activa el bloqueo del dominio. func EnableHostingerDomainLock(c *fiber.Ctx) error { return hostingerDomainToggle(c, "domain-lock", true) } // DisableHostingerDomainLock desactiva el bloqueo del dominio. func DisableHostingerDomainLock(c *fiber.Ctx) error { return hostingerDomainToggle(c, "domain-lock", false) } // EnableHostingerPrivacy activa la protección WHOIS. func EnableHostingerPrivacy(c *fiber.Ctx) error { return hostingerDomainToggle(c, "privacy", true) } // DisableHostingerPrivacy desactiva la protección WHOIS. func DisableHostingerPrivacy(c *fiber.Ctx) error { return hostingerDomainToggle(c, "privacy", false) } func hostingerDomainToggle(c *fiber.Ctx, feature string, enable bool) error { domain := c.Params("domain") client, err := hostingerClient() if err != nil { return c.Status(fiber.StatusFailedDependency).JSON(fiber.Map{"error": "No se encontró configuración activa de Hostinger"}) } var e error switch feature { case "domain-lock": e = client.SetDomainLock(domain, enable) case "privacy": e = client.SetPrivacyProtection(domain, enable) } if e != nil { return c.Status(fiber.StatusUnprocessableEntity).JSON(fiber.Map{"error": e.Error()}) } return c.JSON(fiber.Map{"message": "Operación realizada"}) } // ─── VPS: acciones ─────────────────────────────────────────────────────────── // StartHostingerVPS arranca una VM. func StartHostingerVPS(c *fiber.Ctx) error { return vpsAction(c, "start") } // StopHostingerVPS apaga una VM. func StopHostingerVPS(c *fiber.Ctx) error { return vpsAction(c, "stop") } // RestartHostingerVPS reinicia una VM. func RestartHostingerVPS(c *fiber.Ctx) error { return vpsAction(c, "restart") } func vpsAction(c *fiber.Ctx, action string) error { id, err := c.ParamsInt("id") if err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "id inválido"}) } client, errC := hostingerClient() if errC != nil { return c.Status(fiber.StatusFailedDependency).JSON(fiber.Map{"error": "No se encontró configuración activa de Hostinger"}) } if err := client.VPSAction(id, action); err != nil { return c.Status(fiber.StatusUnprocessableEntity).JSON(fiber.Map{"error": err.Error()}) } return c.JSON(fiber.Map{"message": action + " enviado"}) } // SetHostingerVPSRootPassword cambia la contraseña root de una VM. func SetHostingerVPSRootPassword(c *fiber.Ctx) error { id, err := c.ParamsInt("id") if err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "id inválido"}) } var body struct { Password string `json:"password"` } if err := c.BodyParser(&body); err != nil || body.Password == "" { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "password requerido"}) } client, errC := hostingerClient() if errC != nil { return c.Status(fiber.StatusFailedDependency).JSON(fiber.Map{"error": "No se encontró configuración activa de Hostinger"}) } if err := client.SetVPSRootPassword(id, body.Password); err != nil { return c.Status(fiber.StatusUnprocessableEntity).JSON(fiber.Map{"error": err.Error()}) } return c.JSON(fiber.Map{"message": "Contraseña actualizada"}) } // SetHostingerVPSHostname cambia el hostname de una VM. func SetHostingerVPSHostname(c *fiber.Ctx) error { id, err := c.ParamsInt("id") if err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "id inválido"}) } var body struct { Hostname string `json:"hostname"` } if err := c.BodyParser(&body); err != nil || body.Hostname == "" { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "hostname requerido"}) } client, errC := hostingerClient() if errC != nil { return c.Status(fiber.StatusFailedDependency).JSON(fiber.Map{"error": "No se encontró configuración activa de Hostinger"}) } if err := client.SetVPSHostname(id, body.Hostname); err != nil { return c.Status(fiber.StatusUnprocessableEntity).JSON(fiber.Map{"error": err.Error()}) } return c.JSON(fiber.Map{"message": "Hostname actualizado"}) } // GetHostingerVPSMetrics retorna las métricas de una VM. // Acepta query params date_from y date_to (RFC3339). Por defecto: últimas 24 h. func GetHostingerVPSMetrics(c *fiber.Ctx) error { id, err := c.ParamsInt("id") if err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "id inválido"}) } client, errC := hostingerClient() if errC != nil { return c.Status(fiber.StatusFailedDependency).JSON(fiber.Map{"error": "No se encontró configuración activa de Hostinger"}) } now := time.Now().UTC() dateFrom := c.Query("date_from", now.Add(-24*time.Hour).Format(time.RFC3339)) dateTo := c.Query("date_to", now.Format(time.RFC3339)) data, err := client.GetVPSMetrics(id, dateFrom, dateTo) if err != nil { return c.Status(fiber.StatusUnprocessableEntity).JSON(fiber.Map{"error": err.Error()}) } return c.JSON(data) } // GetHostingerVPSBackups retorna los backups de una VM. func GetHostingerVPSBackups(c *fiber.Ctx) error { id, err := c.ParamsInt("id") if err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "id inválido"}) } client, errC := hostingerClient() if errC != nil { return c.Status(fiber.StatusFailedDependency).JSON(fiber.Map{"error": "No se encontró configuración activa de Hostinger"}) } data, err := client.GetVPSBackups(id) if err != nil { return c.Status(fiber.StatusUnprocessableEntity).JSON(fiber.Map{"error": err.Error()}) } return c.JSON(data) } // ─── Billing: acciones ─────────────────────────────────────────────────────── // EnableHostingerAutoRenewal activa la autorenovación de una suscripción. func EnableHostingerAutoRenewal(c *fiber.Ctx) error { return hostingerAutoRenewalToggle(c, true) } // DisableHostingerAutoRenewal desactiva la autorenovación de una suscripción. func DisableHostingerAutoRenewal(c *fiber.Ctx) error { return hostingerAutoRenewalToggle(c, false) } func hostingerAutoRenewalToggle(c *fiber.Ctx, enable bool) error { subID := c.Params("id") if subID == "" { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "id requerido"}) } client, err := hostingerClient() if err != nil { return c.Status(fiber.StatusFailedDependency).JSON(fiber.Map{"error": "No se encontró configuración activa de Hostinger"}) } sub, err := client.ToggleAutoRenewal(subID, enable) if err != nil { return c.Status(fiber.StatusUnprocessableEntity).JSON(fiber.Map{"error": err.Error()}) } return c.JSON(fiber.Map{"data": sub}) }