up
This commit is contained in:
@@ -93,6 +93,8 @@ func Migrate() {
|
||||
&models.PartnerComunicado{},
|
||||
// Integración VCard API Admin (Laravel)
|
||||
&models.VcardApiConfig{},
|
||||
// Integración Coolify
|
||||
&models.CoolifyConfig{},
|
||||
); err != nil {
|
||||
log.Fatalf("Error during main migration: %v", err)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"github.com/sujit-baniya/fiber-boilerplate/app"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// CoolifyConfig almacena las credenciales para la API REST de Coolify.
|
||||
// Coolify expone su API en {base_url}/api/v1 con Bearer token.
|
||||
type CoolifyConfig struct {
|
||||
gorm.Model
|
||||
Nombre string `json:"nombre" gorm:"column:nombre;not null"`
|
||||
BaseURL string `json:"base_url" gorm:"column:base_url;type:text;not null"`
|
||||
ApiToken string `json:"api_token" gorm:"column:api_token;type:text;not null"`
|
||||
Activo bool `json:"activo" gorm:"column:activo;default:true"`
|
||||
}
|
||||
|
||||
func (CoolifyConfig) TableName() string { return "coolify_configs" }
|
||||
|
||||
func GetCoolifyConfig() (*CoolifyConfig, error) {
|
||||
var cfg CoolifyConfig
|
||||
if err := app.Http.Database.DB.First(&cfg).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &cfg, nil
|
||||
}
|
||||
|
||||
func UpsertCoolifyConfig(nombre, baseURL, apiToken string, activo bool) (*CoolifyConfig, error) {
|
||||
var cfg CoolifyConfig
|
||||
err := app.Http.Database.DB.First(&cfg).Error
|
||||
if err != nil {
|
||||
// No existe → crear
|
||||
cfg = CoolifyConfig{
|
||||
Nombre: nombre,
|
||||
BaseURL: baseURL,
|
||||
ApiToken: apiToken,
|
||||
Activo: activo,
|
||||
}
|
||||
if createErr := app.Http.Database.DB.Create(&cfg).Error; createErr != nil {
|
||||
return nil, createErr
|
||||
}
|
||||
return &cfg, nil
|
||||
}
|
||||
cfg.Nombre = nombre
|
||||
cfg.BaseURL = baseURL
|
||||
if apiToken != "" {
|
||||
cfg.ApiToken = apiToken
|
||||
}
|
||||
cfg.Activo = activo
|
||||
if err := app.Http.Database.DB.Save(&cfg).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &cfg, nil
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,313 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/sujit-baniya/fiber-boilerplate/pkg/models"
|
||||
)
|
||||
|
||||
// ─── 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
|
||||
}
|
||||
|
||||
// coolifyProxy resuelve config, aplica qs de la request y responde al frontend.
|
||||
func coolifyProxy(c *fiber.Ctx, method, endpoint string) error {
|
||||
cfg, err := models.GetCoolifyConfig()
|
||||
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"})
|
||||
}
|
||||
|
||||
ep := endpoint
|
||||
if qs := string(c.Request().URI().QueryString()); 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 {
|
||||
cfg, _ := models.GetCoolifyConfig()
|
||||
return c.Render("coolify", fiber.Map{
|
||||
"user": c.Locals("user").(map[string]interface{}),
|
||||
"modules": c.Locals("userModules"),
|
||||
"config": cfg,
|
||||
}, "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")
|
||||
}
|
||||
@@ -15,6 +15,7 @@ func RunSeed(c *fiber.Ctx) error {
|
||||
if err := seedModulo("Integraciones", "Conexión con servicios externos: Hostinger, Cloudflare y más", []seedEntry{
|
||||
{"Hostinger", "Panel de VPS, dominios, hosting y DNS de Hostinger", "/app/hostinger"},
|
||||
{"Cloudflare", "Gestión de zonas, DNS, SSL y firewall en Cloudflare", "/app/cloudflare"},
|
||||
{"Coolify", "Gestión de aplicaciones, servicios, bases de datos y deployments con Coolify", "/app/coolify"},
|
||||
{"Pasarelas de Pago", "Configuración de Bold, dLocal y otras pasarelas", "/app/pasarelas-pago"},
|
||||
}); err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
|
||||
@@ -163,6 +163,56 @@ func UserRoutes(app fiber.Router) {
|
||||
protected.Get("/cloudflare/zones/:zone_id/ssl", controllers.GetCloudflareSSL)
|
||||
protected.Get("/cloudflare/zones/:zone_id/firewall", controllers.GetCloudflareFirewall)
|
||||
|
||||
// ─── Coolify API ──────────────────────────────────────────────────
|
||||
protected.Get("/coolify", middlewares.MenuMiddleware, controllers.CoolifyIndex)
|
||||
protected.Get("/coolify/config", controllers.CoolifyGetConfig)
|
||||
protected.Post("/coolify/config", controllers.CoolifySaveConfig)
|
||||
protected.Get("/coolify/health", controllers.CoolifyTestConnection)
|
||||
// Applications
|
||||
protected.Get("/coolify/apps", controllers.CoolifyListApplications)
|
||||
protected.Get("/coolify/apps/:uuid", controllers.CoolifyGetApplication)
|
||||
protected.Get("/coolify/apps/:uuid/logs", controllers.CoolifyApplicationLogs)
|
||||
protected.Get("/coolify/apps/:uuid/envs", controllers.CoolifyApplicationEnvs)
|
||||
protected.Post("/coolify/apps/:uuid/envs", controllers.CoolifyApplicationEnvCreate)
|
||||
protected.Patch("/coolify/apps/:uuid/envs", controllers.CoolifyApplicationEnvUpdateBulk)
|
||||
protected.Delete("/coolify/apps/:uuid/envs/:env_id", controllers.CoolifyApplicationEnvDelete)
|
||||
protected.Get("/coolify/apps/:uuid/start", controllers.CoolifyApplicationStart)
|
||||
protected.Get("/coolify/apps/:uuid/stop", controllers.CoolifyApplicationStop)
|
||||
protected.Get("/coolify/apps/:uuid/restart", controllers.CoolifyApplicationRestart)
|
||||
protected.Get("/coolify/apps/:uuid/deployments", controllers.CoolifyApplicationDeployments)
|
||||
protected.Post("/coolify/apps/:uuid/deploy", controllers.CoolifyApplicationDeploy)
|
||||
// Servers
|
||||
protected.Get("/coolify/servers", controllers.CoolifyListServers)
|
||||
protected.Get("/coolify/servers/:uuid", controllers.CoolifyGetServer)
|
||||
protected.Get("/coolify/servers/:uuid/resources", controllers.CoolifyServerResources)
|
||||
protected.Get("/coolify/servers/:uuid/domains", controllers.CoolifyServerDomains)
|
||||
protected.Get("/coolify/servers/:uuid/validate", controllers.CoolifyServerValidate)
|
||||
// Services
|
||||
protected.Get("/coolify/services", controllers.CoolifyListServices)
|
||||
protected.Get("/coolify/services/:uuid", controllers.CoolifyGetService)
|
||||
protected.Get("/coolify/services/:uuid/start", controllers.CoolifyServiceStart)
|
||||
protected.Get("/coolify/services/:uuid/stop", controllers.CoolifyServiceStop)
|
||||
protected.Get("/coolify/services/:uuid/restart", controllers.CoolifyServiceRestart)
|
||||
protected.Get("/coolify/services/:uuid/envs", controllers.CoolifyServiceEnvs)
|
||||
protected.Patch("/coolify/services/:uuid/envs", controllers.CoolifyServiceEnvUpdateBulk)
|
||||
// Databases
|
||||
protected.Get("/coolify/databases", controllers.CoolifyListDatabases)
|
||||
protected.Get("/coolify/databases/:uuid", controllers.CoolifyGetDatabase)
|
||||
protected.Get("/coolify/databases/:uuid/start", controllers.CoolifyDatabaseStart)
|
||||
protected.Get("/coolify/databases/:uuid/stop", controllers.CoolifyDatabaseStop)
|
||||
protected.Get("/coolify/databases/:uuid/restart", controllers.CoolifyDatabaseRestart)
|
||||
// Projects
|
||||
protected.Get("/coolify/projects", controllers.CoolifyListProjects)
|
||||
protected.Get("/coolify/projects/:uuid", controllers.CoolifyGetProject)
|
||||
protected.Get("/coolify/projects/:uuid/environments", controllers.CoolifyProjectEnvironments)
|
||||
// Deployments
|
||||
protected.Get("/coolify/deployments", controllers.CoolifyListDeployments)
|
||||
protected.Get("/coolify/deployments/:uuid", controllers.CoolifyGetDeployment)
|
||||
// Team
|
||||
protected.Get("/coolify/team", controllers.CoolifyCurrentTeam)
|
||||
protected.Get("/coolify/team/members", controllers.CoolifyTeamMembers)
|
||||
protected.Get("/coolify/teams", controllers.CoolifyListTeams)
|
||||
|
||||
// ─── Pasarelas de Pago ────────────────────────────────────────────
|
||||
protected.Get("/pasarelas-pago", middlewares.MenuMiddleware, controllers.PasarelasPage)
|
||||
// Bold
|
||||
|
||||
Reference in New Issue
Block a user