603 lines
20 KiB
Go
603 lines
20 KiB
Go
package controllers
|
||
|
||
import (
|
||
"encoding/json"
|
||
"fmt"
|
||
"io"
|
||
"log"
|
||
"net/http"
|
||
"net/url"
|
||
"strconv"
|
||
"strings"
|
||
"time"
|
||
|
||
"github.com/gofiber/fiber/v2"
|
||
"github.com/sujit-baniya/fiber-boilerplate/pkg/models"
|
||
"github.com/sujit-baniya/fiber-boilerplate/pkg/services"
|
||
)
|
||
|
||
// filterQueryParam elimina un parámetro específico del querystring.
|
||
func filterQueryParam(qs, param string) string {
|
||
vals, err := url.ParseQuery(qs)
|
||
if err != nil {
|
||
return qs
|
||
}
|
||
vals.Del(param)
|
||
return vals.Encode()
|
||
}
|
||
|
||
// ─── helpers internos ────────────────────────────────────────────────────────
|
||
|
||
// coolifyDo ejecuta una petición a la API de Coolify y devuelve el body crudo.
|
||
func coolifyDo(method, endpoint string, reqBody io.Reader, contentType string, cfg *models.CoolifyConfig) ([]byte, int, error) {
|
||
base := strings.TrimRight(cfg.BaseURL, "/")
|
||
url := fmt.Sprintf("%s/api/v1%s", base, endpoint)
|
||
|
||
client := &http.Client{Timeout: 30 * time.Second}
|
||
req, err := http.NewRequest(method, url, reqBody)
|
||
if err != nil {
|
||
return nil, 0, err
|
||
}
|
||
req.Header.Set("Authorization", "Bearer "+cfg.ApiToken)
|
||
req.Header.Set("Accept", "application/json")
|
||
if contentType != "" {
|
||
req.Header.Set("Content-Type", contentType)
|
||
}
|
||
|
||
resp, err := client.Do(req)
|
||
if err != nil {
|
||
return nil, 0, err
|
||
}
|
||
defer resp.Body.Close()
|
||
body, _ := io.ReadAll(io.LimitReader(resp.Body, 1024*1024))
|
||
return body, resp.StatusCode, nil
|
||
}
|
||
|
||
// coolifyResolveConfig retorna la config a usar según ?config_id= o la activa por defecto.
|
||
func coolifyResolveConfig(c *fiber.Ctx) (*models.CoolifyConfig, error) {
|
||
if idStr := c.Query("config_id"); idStr != "" {
|
||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("config_id inválido")
|
||
}
|
||
return models.GetCoolifyConfigByID(uint(id))
|
||
}
|
||
return models.GetCoolifyConfig()
|
||
}
|
||
|
||
// coolifyProxy resuelve config, aplica qs de la request y responde al frontend.
|
||
func coolifyProxy(c *fiber.Ctx, method, endpoint string) error {
|
||
cfg, err := coolifyResolveConfig(c)
|
||
if err != nil {
|
||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Configura primero la integración Coolify"})
|
||
}
|
||
if !cfg.Activo {
|
||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "La integración Coolify está inactiva"})
|
||
}
|
||
|
||
// Filtrar config_id del querystring para no pasarlo a Coolify
|
||
qs := string(c.Request().URI().QueryString())
|
||
if qs != "" {
|
||
filtered := filterQueryParam(qs, "config_id")
|
||
if filtered != "" {
|
||
qs = filtered
|
||
} else {
|
||
qs = ""
|
||
}
|
||
}
|
||
ep := endpoint
|
||
if qs != "" {
|
||
ep = endpoint + "?" + qs
|
||
}
|
||
|
||
var bodyReader io.Reader
|
||
ct := ""
|
||
if raw := c.Body(); len(raw) > 0 {
|
||
bodyReader = strings.NewReader(string(raw))
|
||
ct = c.Get("Content-Type", "application/json")
|
||
}
|
||
|
||
body, status, err := coolifyDo(method, ep, bodyReader, ct, cfg)
|
||
if err != nil {
|
||
return c.Status(fiber.StatusBadGateway).JSON(fiber.Map{"error": err.Error()})
|
||
}
|
||
|
||
var result json.RawMessage
|
||
if err := json.Unmarshal(body, &result); err != nil {
|
||
result = json.RawMessage(fmt.Sprintf(`{"raw":%q}`, string(body)))
|
||
}
|
||
c.Status(status)
|
||
return c.JSON(result)
|
||
}
|
||
|
||
// ─── Página principal ─────────────────────────────────────────────────────────
|
||
|
||
func CoolifyIndex(c *fiber.Ctx) error {
|
||
cfgs, _ := models.GetAllCoolifyConfigs()
|
||
return c.Render("coolify", fiber.Map{
|
||
"user": c.Locals("user").(map[string]interface{}),
|
||
"modules": c.Locals("userModules"),
|
||
"configs": cfgs,
|
||
}, "layouts/main")
|
||
}
|
||
|
||
// ─── Configuración ────────────────────────────────────────────────────────────
|
||
|
||
func CoolifyGetConfig(c *fiber.Ctx) error {
|
||
cfg, err := models.GetCoolifyConfig()
|
||
if err != nil {
|
||
return c.JSON(fiber.Map{"config": nil})
|
||
}
|
||
masked := "••••••••"
|
||
if cfg.ApiToken == "" {
|
||
masked = ""
|
||
}
|
||
return c.JSON(fiber.Map{"config": fiber.Map{
|
||
"ID": cfg.ID,
|
||
"nombre": cfg.Nombre,
|
||
"base_url": cfg.BaseURL,
|
||
"api_token": masked,
|
||
"activo": cfg.Activo,
|
||
}})
|
||
}
|
||
|
||
func CoolifySaveConfig(c *fiber.Ctx) error {
|
||
type Req struct {
|
||
Nombre string `json:"nombre"`
|
||
BaseURL string `json:"base_url"`
|
||
ApiToken string `json:"api_token"`
|
||
Activo bool `json:"activo"`
|
||
}
|
||
var req Req
|
||
if err := c.BodyParser(&req); err != nil {
|
||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "body inválido"})
|
||
}
|
||
req.BaseURL = strings.TrimRight(strings.TrimSpace(req.BaseURL), "/")
|
||
if req.BaseURL == "" {
|
||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "base_url es requerido"})
|
||
}
|
||
cfg, err := models.UpsertCoolifyConfig(req.Nombre, req.BaseURL, req.ApiToken, req.Activo)
|
||
if err != nil {
|
||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||
}
|
||
return c.JSON(fiber.Map{"ok": true, "config": cfg})
|
||
}
|
||
|
||
// CoolifyTestConnection verifica conectividad con /health (no requiere auth).
|
||
func CoolifyTestConnection(c *fiber.Ctx) error {
|
||
cfg, err := models.GetCoolifyConfig()
|
||
if err != nil {
|
||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Sin config"})
|
||
}
|
||
base := strings.TrimRight(cfg.BaseURL, "/")
|
||
client := &http.Client{Timeout: 10 * time.Second}
|
||
resp, err := client.Get(base + "/api/health")
|
||
if err != nil {
|
||
return c.Status(fiber.StatusBadGateway).JSON(fiber.Map{"error": err.Error()})
|
||
}
|
||
defer resp.Body.Close()
|
||
body, _ := io.ReadAll(io.LimitReader(resp.Body, 64*1024))
|
||
var result json.RawMessage
|
||
if jsonErr := json.Unmarshal(body, &result); jsonErr != nil {
|
||
result = json.RawMessage(fmt.Sprintf(`{"raw":%q}`, string(body)))
|
||
}
|
||
c.Status(resp.StatusCode)
|
||
return c.JSON(result)
|
||
}
|
||
|
||
// ─── Applications ─────────────────────────────────────────────────────────────
|
||
|
||
func CoolifyListApplications(c *fiber.Ctx) error {
|
||
return coolifyProxy(c, http.MethodGet, "/applications")
|
||
}
|
||
|
||
func CoolifyGetApplication(c *fiber.Ctx) error {
|
||
return coolifyProxy(c, http.MethodGet, "/applications/"+c.Params("uuid"))
|
||
}
|
||
|
||
func CoolifyApplicationLogs(c *fiber.Ctx) error {
|
||
return coolifyProxy(c, http.MethodGet, "/applications/"+c.Params("uuid")+"/logs")
|
||
}
|
||
|
||
func CoolifyApplicationEnvs(c *fiber.Ctx) error {
|
||
return coolifyProxy(c, http.MethodGet, "/applications/"+c.Params("uuid")+"/envs")
|
||
}
|
||
|
||
func CoolifyApplicationEnvCreate(c *fiber.Ctx) error {
|
||
return coolifyProxy(c, http.MethodPost, "/applications/"+c.Params("uuid")+"/envs")
|
||
}
|
||
|
||
func CoolifyApplicationEnvUpdateBulk(c *fiber.Ctx) error {
|
||
return coolifyProxy(c, http.MethodPatch, "/applications/"+c.Params("uuid")+"/envs")
|
||
}
|
||
|
||
func CoolifyApplicationEnvDelete(c *fiber.Ctx) error {
|
||
return coolifyProxy(c, http.MethodDelete, "/applications/"+c.Params("uuid")+"/envs/"+c.Params("env_id"))
|
||
}
|
||
|
||
func CoolifyApplicationStart(c *fiber.Ctx) error {
|
||
return coolifyProxy(c, http.MethodGet, "/applications/"+c.Params("uuid")+"/start")
|
||
}
|
||
|
||
func CoolifyApplicationStop(c *fiber.Ctx) error {
|
||
return coolifyProxy(c, http.MethodGet, "/applications/"+c.Params("uuid")+"/stop")
|
||
}
|
||
|
||
func CoolifyApplicationRestart(c *fiber.Ctx) error {
|
||
return coolifyProxy(c, http.MethodGet, "/applications/"+c.Params("uuid")+"/restart")
|
||
}
|
||
|
||
func CoolifyApplicationDeployments(c *fiber.Ctx) error {
|
||
return coolifyProxy(c, http.MethodGet, "/applications/"+c.Params("uuid")+"/deployments")
|
||
}
|
||
|
||
func CoolifyApplicationDeploy(c *fiber.Ctx) error {
|
||
// POST /api/v1/deploy?uuid={uuid}&force=false
|
||
return coolifyProxy(c, http.MethodPost, "/deploy")
|
||
}
|
||
|
||
// ─── Servers ──────────────────────────────────────────────────────────────────
|
||
|
||
func CoolifyListServers(c *fiber.Ctx) error {
|
||
return coolifyProxy(c, http.MethodGet, "/servers")
|
||
}
|
||
|
||
func CoolifyGetServer(c *fiber.Ctx) error {
|
||
return coolifyProxy(c, http.MethodGet, "/servers/"+c.Params("uuid"))
|
||
}
|
||
|
||
func CoolifyServerResources(c *fiber.Ctx) error {
|
||
return coolifyProxy(c, http.MethodGet, "/servers/"+c.Params("uuid")+"/resources")
|
||
}
|
||
|
||
func CoolifyServerDomains(c *fiber.Ctx) error {
|
||
return coolifyProxy(c, http.MethodGet, "/servers/"+c.Params("uuid")+"/domains")
|
||
}
|
||
|
||
func CoolifyServerValidate(c *fiber.Ctx) error {
|
||
return coolifyProxy(c, http.MethodGet, "/servers/"+c.Params("uuid")+"/validate")
|
||
}
|
||
|
||
// ─── Services ─────────────────────────────────────────────────────────────────
|
||
|
||
func CoolifyListServices(c *fiber.Ctx) error {
|
||
return coolifyProxy(c, http.MethodGet, "/services")
|
||
}
|
||
|
||
func CoolifyGetService(c *fiber.Ctx) error {
|
||
return coolifyProxy(c, http.MethodGet, "/services/"+c.Params("uuid"))
|
||
}
|
||
|
||
func CoolifyServiceStart(c *fiber.Ctx) error {
|
||
return coolifyProxy(c, http.MethodGet, "/services/"+c.Params("uuid")+"/start")
|
||
}
|
||
|
||
func CoolifyServiceStop(c *fiber.Ctx) error {
|
||
return coolifyProxy(c, http.MethodGet, "/services/"+c.Params("uuid")+"/stop")
|
||
}
|
||
|
||
func CoolifyServiceRestart(c *fiber.Ctx) error {
|
||
return coolifyProxy(c, http.MethodGet, "/services/"+c.Params("uuid")+"/restart")
|
||
}
|
||
|
||
func CoolifyServiceEnvs(c *fiber.Ctx) error {
|
||
return coolifyProxy(c, http.MethodGet, "/services/"+c.Params("uuid")+"/envs")
|
||
}
|
||
|
||
func CoolifyServiceEnvUpdateBulk(c *fiber.Ctx) error {
|
||
return coolifyProxy(c, http.MethodPatch, "/services/"+c.Params("uuid")+"/envs")
|
||
}
|
||
|
||
// ─── Databases ────────────────────────────────────────────────────────────────
|
||
|
||
func CoolifyListDatabases(c *fiber.Ctx) error {
|
||
return coolifyProxy(c, http.MethodGet, "/databases")
|
||
}
|
||
|
||
func CoolifyGetDatabase(c *fiber.Ctx) error {
|
||
return coolifyProxy(c, http.MethodGet, "/databases/"+c.Params("uuid"))
|
||
}
|
||
|
||
func CoolifyDatabaseStart(c *fiber.Ctx) error {
|
||
return coolifyProxy(c, http.MethodGet, "/databases/"+c.Params("uuid")+"/start")
|
||
}
|
||
|
||
func CoolifyDatabaseStop(c *fiber.Ctx) error {
|
||
return coolifyProxy(c, http.MethodGet, "/databases/"+c.Params("uuid")+"/stop")
|
||
}
|
||
|
||
func CoolifyDatabaseRestart(c *fiber.Ctx) error {
|
||
return coolifyProxy(c, http.MethodGet, "/databases/"+c.Params("uuid")+"/restart")
|
||
}
|
||
|
||
// ─── Projects ─────────────────────────────────────────────────────────────────
|
||
|
||
func CoolifyListProjects(c *fiber.Ctx) error {
|
||
return coolifyProxy(c, http.MethodGet, "/projects")
|
||
}
|
||
|
||
func CoolifyGetProject(c *fiber.Ctx) error {
|
||
return coolifyProxy(c, http.MethodGet, "/projects/"+c.Params("uuid"))
|
||
}
|
||
|
||
func CoolifyProjectEnvironments(c *fiber.Ctx) error {
|
||
return coolifyProxy(c, http.MethodGet, "/projects/"+c.Params("uuid")+"/environments")
|
||
}
|
||
|
||
// ─── Deployments ──────────────────────────────────────────────────────────────
|
||
|
||
func CoolifyListDeployments(c *fiber.Ctx) error {
|
||
return coolifyProxy(c, http.MethodGet, "/deployments")
|
||
}
|
||
|
||
func CoolifyGetDeployment(c *fiber.Ctx) error {
|
||
return coolifyProxy(c, http.MethodGet, "/deployments/"+c.Params("uuid"))
|
||
}
|
||
|
||
// ─── Teams ────────────────────────────────────────────────────────────────────
|
||
|
||
func CoolifyListTeams(c *fiber.Ctx) error {
|
||
return coolifyProxy(c, http.MethodGet, "/teams")
|
||
}
|
||
|
||
func CoolifyCurrentTeam(c *fiber.Ctx) error {
|
||
return coolifyProxy(c, http.MethodGet, "/teams/current")
|
||
}
|
||
|
||
func CoolifyTeamMembers(c *fiber.Ctx) error {
|
||
return coolifyProxy(c, http.MethodGet, "/teams/current/members")
|
||
}
|
||
|
||
// CoolifyWebhook recibe notificaciones push de Coolify y las reenvía a Telegram.
|
||
// Configurar en Coolify → Settings → Notifications → Add Notification → Webhook
|
||
// URL: https://admin.u-site.app/webhooks/coolify/:config_id
|
||
func CoolifyWebhook(c *fiber.Ctx) error {
|
||
body := c.Body()
|
||
log.Printf("[COOLIFY_WEBHOOK] config_id=%s payload=%s", c.Params("config_id"), string(body))
|
||
|
||
if len(body) == 0 {
|
||
return c.SendStatus(fiber.StatusOK)
|
||
}
|
||
|
||
// Identificar la instancia Coolify si viene en la ruta
|
||
var instanceName string
|
||
if idStr := c.Params("config_id"); idStr != "" {
|
||
if id, err := strconv.ParseUint(idStr, 10, 32); err == nil {
|
||
if cfg, err := models.GetCoolifyConfigByID(uint(id)); err == nil {
|
||
instanceName = cfg.Nombre
|
||
}
|
||
}
|
||
}
|
||
|
||
go sendCoolifyTelegramNotif(body, instanceName)
|
||
return c.SendStatus(fiber.StatusOK)
|
||
}
|
||
|
||
// sendCoolifyTelegramNotif parsea el payload de Coolify y lo envía a Telegram.
|
||
func sendCoolifyTelegramNotif(body []byte, instanceName string) {
|
||
// Intentar parsear como JSON
|
||
var payload map[string]interface{}
|
||
isJSON := json.Unmarshal(body, &payload) == nil
|
||
|
||
// Extraer campos con múltiples nombres posibles (Coolify cambia formato según versión)
|
||
get := func(keys ...string) string {
|
||
if !isJSON {
|
||
return ""
|
||
}
|
||
for _, k := range keys {
|
||
if v, ok := payload[k]; ok {
|
||
if s, ok := v.(string); ok && s != "" {
|
||
return s
|
||
}
|
||
}
|
||
}
|
||
return ""
|
||
}
|
||
|
||
appName := get("application_name", "name", "app_name", "resource_name")
|
||
appUUID := get("application_uuid", "uuid", "resource_uuid")
|
||
status := get("status", "type", "event", "deployment_status")
|
||
message := get("message", "description", "text", "body")
|
||
fqdn := get("fqdn", "url", "application_url", "application_fqdn")
|
||
serverName := get("server_name", "server")
|
||
|
||
// Si no pudimos parsear como JSON, tratar el body entero como mensaje
|
||
if !isJSON {
|
||
message = strings.TrimSpace(string(body))
|
||
if len(message) > 500 {
|
||
message = message[:500] + "…"
|
||
}
|
||
}
|
||
|
||
// Emoji según estado
|
||
emoji := "🔔"
|
||
statusLower := strings.ToLower(status + " " + message)
|
||
switch {
|
||
case strings.Contains(statusLower, "success") || strings.Contains(statusLower, "exitoso") || strings.Contains(statusLower, "finished"):
|
||
emoji = "✅"
|
||
case strings.Contains(statusLower, "fail") || strings.Contains(statusLower, "error") || strings.Contains(statusLower, "fallo"):
|
||
emoji = "❌"
|
||
case strings.Contains(statusLower, "running") || strings.Contains(statusLower, "deploying") || strings.Contains(statusLower, "start") || strings.Contains(statusLower, "building"):
|
||
emoji = "🚀"
|
||
case strings.Contains(statusLower, "stop") || strings.Contains(statusLower, "detenido"):
|
||
emoji = "⏹"
|
||
case strings.Contains(statusLower, "restart") || strings.Contains(statusLower, "reinici"):
|
||
emoji = "🔄"
|
||
}
|
||
|
||
// Construir el mensaje
|
||
lines := []string{fmt.Sprintf("%s <b>Coolify</b>", emoji)}
|
||
if instanceName != "" {
|
||
lines = append(lines, fmt.Sprintf("Instancia: <b>%s</b>", instanceName))
|
||
}
|
||
if appName != "" {
|
||
lines = append(lines, fmt.Sprintf("App: <code>%s</code>", appName))
|
||
}
|
||
if appUUID != "" && appUUID != appName {
|
||
lines = append(lines, fmt.Sprintf("UUID: <code>%s</code>", appUUID))
|
||
}
|
||
if serverName != "" {
|
||
lines = append(lines, fmt.Sprintf("Servidor: %s", serverName))
|
||
}
|
||
if status != "" {
|
||
lines = append(lines, fmt.Sprintf("Estado: <b>%s</b>", status))
|
||
}
|
||
if message != "" && message != status {
|
||
if len(message) > 300 {
|
||
message = message[:300] + "…"
|
||
}
|
||
lines = append(lines, fmt.Sprintf("ℹ️ %s", message))
|
||
}
|
||
if fqdn != "" {
|
||
lines = append(lines, fmt.Sprintf("🌐 %s", fqdn))
|
||
}
|
||
|
||
text := strings.Join(lines, "\n")
|
||
|
||
// Obtener el bot del agente (TelegramConfig ID que tenga agent bot activo)
|
||
tgCfg, err := models.GetAgentTelegramConfig()
|
||
if err != nil {
|
||
log.Printf("[COOLIFY_WEBHOOK] Sin config Telegram para notificar: %v", err)
|
||
return
|
||
}
|
||
|
||
// Enviar a todos los chats autorizados del agente
|
||
auths, _ := models.GetAllAgentAuth()
|
||
if len(auths) == 0 {
|
||
log.Printf("[COOLIFY_WEBHOOK] Sin chats autorizados para notificar")
|
||
return
|
||
}
|
||
|
||
svc := &services.TelegramService{BotToken: tgCfg.BotToken}
|
||
for _, auth := range auths {
|
||
if !auth.Activo {
|
||
continue
|
||
}
|
||
if err := svc.SendMessage(auth.ChatID, text); err != nil {
|
||
log.Printf("[COOLIFY_WEBHOOK] Error enviando a chat %d: %v", auth.ChatID, err)
|
||
}
|
||
}
|
||
}
|
||
|
||
// ─── CRUD de instancias Coolify ───────────────────────────────────────────────
|
||
|
||
func CoolifyListConfigs(c *fiber.Ctx) error {
|
||
items, err := models.GetAllCoolifyConfigs()
|
||
if err != nil {
|
||
return c.Status(500).JSON(fiber.Map{"error": err.Error()})
|
||
}
|
||
// Ocultar tokens
|
||
type safe struct {
|
||
ID uint `json:"id"`
|
||
Nombre string `json:"nombre"`
|
||
BaseURL string `json:"base_url"`
|
||
Activo bool `json:"activo"`
|
||
}
|
||
out := make([]safe, len(items))
|
||
for i, cfg := range items {
|
||
out[i] = safe{ID: cfg.ID, Nombre: cfg.Nombre, BaseURL: cfg.BaseURL, Activo: cfg.Activo}
|
||
}
|
||
return c.JSON(fiber.Map{"items": out})
|
||
}
|
||
|
||
func CoolifyGetConfigByID(c *fiber.Ctx) error {
|
||
id, err := strconv.ParseUint(c.Params("id"), 10, 32)
|
||
if err != nil {
|
||
return c.Status(400).JSON(fiber.Map{"error": "id inválido"})
|
||
}
|
||
cfg, err := models.GetCoolifyConfigByID(uint(id))
|
||
if err != nil {
|
||
return c.Status(404).JSON(fiber.Map{"error": "instancia no encontrada"})
|
||
}
|
||
return c.JSON(fiber.Map{
|
||
"id": cfg.ID,
|
||
"nombre": cfg.Nombre,
|
||
"base_url": cfg.BaseURL,
|
||
"activo": cfg.Activo,
|
||
})
|
||
}
|
||
|
||
func CoolifyCreateConfig(c *fiber.Ctx) error {
|
||
type Req struct {
|
||
Nombre string `json:"nombre"`
|
||
BaseURL string `json:"base_url"`
|
||
ApiToken string `json:"api_token"`
|
||
Activo bool `json:"activo"`
|
||
}
|
||
var req Req
|
||
if err := c.BodyParser(&req); err != nil {
|
||
return c.Status(400).JSON(fiber.Map{"error": "body inválido"})
|
||
}
|
||
req.BaseURL = strings.TrimRight(strings.TrimSpace(req.BaseURL), "/")
|
||
if req.BaseURL == "" || req.ApiToken == "" {
|
||
return c.Status(400).JSON(fiber.Map{"error": "base_url y api_token son requeridos"})
|
||
}
|
||
cfg := &models.CoolifyConfig{Nombre: req.Nombre, BaseURL: req.BaseURL, ApiToken: req.ApiToken, Activo: req.Activo}
|
||
if err := models.CreateCoolifyConfig(cfg); err != nil {
|
||
return c.Status(500).JSON(fiber.Map{"error": err.Error()})
|
||
}
|
||
return c.Status(201).JSON(fiber.Map{"ok": true, "id": cfg.ID})
|
||
}
|
||
|
||
func CoolifyUpdateConfig(c *fiber.Ctx) error {
|
||
id, err := strconv.ParseUint(c.Params("id"), 10, 32)
|
||
if err != nil {
|
||
return c.Status(400).JSON(fiber.Map{"error": "id inválido"})
|
||
}
|
||
type Req struct {
|
||
Nombre string `json:"nombre"`
|
||
BaseURL string `json:"base_url"`
|
||
ApiToken string `json:"api_token"`
|
||
Activo bool `json:"activo"`
|
||
}
|
||
var req Req
|
||
if err := c.BodyParser(&req); err != nil {
|
||
return c.Status(400).JSON(fiber.Map{"error": "body inválido"})
|
||
}
|
||
req.BaseURL = strings.TrimRight(strings.TrimSpace(req.BaseURL), "/")
|
||
if req.BaseURL == "" {
|
||
return c.Status(400).JSON(fiber.Map{"error": "base_url es requerido"})
|
||
}
|
||
cfg, err := models.UpdateCoolifyConfig(uint(id), req.Nombre, req.BaseURL, req.ApiToken, req.Activo)
|
||
if err != nil {
|
||
return c.Status(500).JSON(fiber.Map{"error": err.Error()})
|
||
}
|
||
return c.JSON(fiber.Map{"ok": true, "id": cfg.ID})
|
||
}
|
||
|
||
func CoolifyDeleteConfig(c *fiber.Ctx) error {
|
||
id, err := strconv.ParseUint(c.Params("id"), 10, 32)
|
||
if err != nil {
|
||
return c.Status(400).JSON(fiber.Map{"error": "id inválido"})
|
||
}
|
||
if err := models.DeleteCoolifyConfig(uint(id)); err != nil {
|
||
return c.Status(500).JSON(fiber.Map{"error": err.Error()})
|
||
}
|
||
return c.JSON(fiber.Map{"ok": true})
|
||
}
|
||
|
||
func CoolifyTestConfig(c *fiber.Ctx) error {
|
||
id, err := strconv.ParseUint(c.Params("id"), 10, 32)
|
||
if err != nil {
|
||
return c.Status(400).JSON(fiber.Map{"error": "id inválido"})
|
||
}
|
||
cfg, err := models.GetCoolifyConfigByID(uint(id))
|
||
if err != nil {
|
||
return c.Status(404).JSON(fiber.Map{"error": "config no encontrada"})
|
||
}
|
||
base := strings.TrimRight(cfg.BaseURL, "/")
|
||
client := &http.Client{Timeout: 10 * time.Second}
|
||
resp, err := client.Get(base + "/api/health")
|
||
if err != nil {
|
||
return c.Status(502).JSON(fiber.Map{"error": err.Error()})
|
||
}
|
||
defer resp.Body.Close()
|
||
body, _ := io.ReadAll(io.LimitReader(resp.Body, 64*1024))
|
||
var result json.RawMessage
|
||
if jsonErr := json.Unmarshal(body, &result); jsonErr != nil {
|
||
result = json.RawMessage(fmt.Sprintf(`{"raw":%q}`, string(body)))
|
||
}
|
||
c.Status(resp.StatusCode)
|
||
return c.JSON(result)
|
||
}
|