From 7a374340c1e43b253d00242f0b3ea706b716882f Mon Sep 17 00:00:00 2001 From: Lizandro Guarnizo <77708265+lizandrogd@users.noreply.github.com> Date: Tue, 26 May 2026 20:54:10 -0500 Subject: [PATCH] up --- pkg/models/oss_api.go | 10 + resources/views/oss_api.html | 352 +++++++++++++++++++++++++ rest/controllers/oss_api_controller.go | 159 +++++++++++ rest/routes/user.go | 7 + seed_integraciones.sql | 15 +- 5 files changed, 541 insertions(+), 2 deletions(-) create mode 100644 resources/views/oss_api.html create mode 100644 rest/controllers/oss_api_controller.go diff --git a/pkg/models/oss_api.go b/pkg/models/oss_api.go index 286f741..3e88fba 100755 --- a/pkg/models/oss_api.go +++ b/pkg/models/oss_api.go @@ -74,6 +74,16 @@ func DeleteOssApi(OssApi *OssApi) error { return nil } +// GetOssApiByID obtiene un registro por ID +func GetOssApiByID(id uint) (*OssApi, error) { + var item OssApi + err := app.Http.Database.DB.First(&item, id).Error + if err != nil { + return nil, err + } + return &item, nil +} + // GetLastActiveOssApi obtiene el último registro activo func GetLastActiveOssApi() (*OssApi, error) { var ossConfig OssApi diff --git a/resources/views/oss_api.html b/resources/views/oss_api.html new file mode 100644 index 0000000..bc62823 --- /dev/null +++ b/resources/views/oss_api.html @@ -0,0 +1,352 @@ + +
+ + +
+ Cargando... +
+ +
+ + +
+
+

Alibaba Cloud OSS

+

Gestiona las configuraciones de acceso a Alibaba Cloud Object Storage Service (OSS). La configuración activa es usada por todos los servicios de almacenamiento.

+
+ +
+ + +
+ + +
+ + +
+ + +
+ + + + + + + + + + + + + + + +
NombreEndpointBucketRegiónEstadoAcciones
+
+ + +
+ +
+ + +
+
+
+ + +
+
+
+

+ +
+
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+
+ + +
+
+ + +
+
+ +
+ + +
+ +
+ + Configuración activa +
+ +

+ +
+ + +
+
+
+
+ + +
+
+ + + +

¿Eliminar configuración?

+

Esta acción no se puede deshacer.

+
+ + +
+
+
+
+ + diff --git a/rest/controllers/oss_api_controller.go b/rest/controllers/oss_api_controller.go new file mode 100644 index 0000000..8d30178 --- /dev/null +++ b/rest/controllers/oss_api_controller.go @@ -0,0 +1,159 @@ +package controllers + +import ( + "math" + "strconv" + "strings" + + "github.com/gofiber/fiber/v2" + "github.com/sujit-baniya/fiber-boilerplate/pkg/models" +) + +// OssApiIndex renderiza la vista del panel de gestión de Alibaba Cloud OSS. +func OssApiIndex(c *fiber.Ctx) error { + data := fiber.Map{ + "user": c.Locals("user").(map[string]interface{}), + "modules": c.Locals("userModules"), + } + return c.Render("oss_api", data, "layouts/main") +} + +// GetOssApiConfigs devuelve la lista paginada de configuraciones en JSON. +func GetOssApiConfigs(c *fiber.Ctx) error { + page, _ := strconv.Atoi(c.Query("page", "1")) + if page < 1 { + page = 1 + } + limit := 20 + offset := (page - 1) * limit + search := c.Query("search", "") + + items, total, err := models.GetAllOssApi(limit, offset, search) + if err != nil { + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()}) + } + + return c.JSON(fiber.Map{ + "items": items, + "total": total, + "totalPages": int(math.Ceil(float64(total) / float64(limit))), + "page": page, + "limit": limit, + }) +} + +// CreateOssApiConfig crea una nueva configuración de Alibaba Cloud OSS. +func CreateOssApiConfig(c *fiber.Ctx) error { + type Req struct { + Name string `json:"name"` + Endpoint string `json:"endpoint"` + AccessKeyID string `json:"access_key_id"` + AccessKeySecret string `json:"access_key_secret"` + BucketName string `json:"bucket_name"` + Region string `json:"region"` + IsActive bool `json:"is_active"` + Notes string `json:"notes"` + } + var req Req + if err := c.BodyParser(&req); err != nil { + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "body inválido"}) + } + if strings.TrimSpace(req.Name) == "" { + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "name es requerido"}) + } + if strings.TrimSpace(req.Endpoint) == "" { + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "endpoint es requerido"}) + } + if strings.TrimSpace(req.AccessKeyID) == "" { + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "access_key_id es requerido"}) + } + if strings.TrimSpace(req.AccessKeySecret) == "" { + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "access_key_secret es requerido"}) + } + if strings.TrimSpace(req.BucketName) == "" { + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "bucket_name es requerido"}) + } + + item := models.OssApi{ + Name: strings.TrimSpace(req.Name), + Endpoint: strings.TrimSpace(req.Endpoint), + AccessKeyID: strings.TrimSpace(req.AccessKeyID), + AccessKeySecret: strings.TrimSpace(req.AccessKeySecret), + BucketName: strings.TrimSpace(req.BucketName), + Region: strings.TrimSpace(req.Region), + IsActive: req.IsActive, + Notes: strings.TrimSpace(req.Notes), + } + if err := models.CreateOssApi(&item); err != nil { + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()}) + } + return c.Status(fiber.StatusCreated).JSON(fiber.Map{"ok": true, "item": item}) +} + +// UpdateOssApiConfig actualiza una configuración existente de Alibaba Cloud OSS. +func UpdateOssApiConfig(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 64) + if err != nil { + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "id inválido"}) + } + + existing, err := models.GetOssApiByID(uint(id)) + if err != nil { + return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "configuración no encontrada"}) + } + + type Req struct { + Name string `json:"name"` + Endpoint string `json:"endpoint"` + AccessKeyID string `json:"access_key_id"` + AccessKeySecret string `json:"access_key_secret"` + BucketName string `json:"bucket_name"` + Region string `json:"region"` + IsActive bool `json:"is_active"` + Notes string `json:"notes"` + } + var req Req + if err := c.BodyParser(&req); err != nil { + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "body inválido"}) + } + if strings.TrimSpace(req.Name) == "" { + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "name es requerido"}) + } + + existing.Name = strings.TrimSpace(req.Name) + existing.Endpoint = strings.TrimSpace(req.Endpoint) + existing.BucketName = strings.TrimSpace(req.BucketName) + existing.Region = strings.TrimSpace(req.Region) + existing.IsActive = req.IsActive + existing.Notes = strings.TrimSpace(req.Notes) + // Solo actualizar secrets si se envían valores no vacíos + if strings.TrimSpace(req.AccessKeyID) != "" { + existing.AccessKeyID = strings.TrimSpace(req.AccessKeyID) + } + if strings.TrimSpace(req.AccessKeySecret) != "" { + existing.AccessKeySecret = strings.TrimSpace(req.AccessKeySecret) + } + + if err := models.UpdateOssApi(existing); err != nil { + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()}) + } + return c.JSON(fiber.Map{"ok": true, "item": existing}) +} + +// DeleteOssApiConfig elimina una configuración de Alibaba Cloud OSS. +func DeleteOssApiConfig(c *fiber.Ctx) error { + id, err := strconv.ParseUint(c.Params("id"), 10, 64) + if err != nil { + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "id inválido"}) + } + + item, err := models.GetOssApiByID(uint(id)) + if err != nil { + return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "configuración no encontrada"}) + } + + if err := models.DeleteOssApi(item); err != nil { + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()}) + } + return c.JSON(fiber.Map{"ok": true}) +} diff --git a/rest/routes/user.go b/rest/routes/user.go index 99955be..cf10c5f 100755 --- a/rest/routes/user.go +++ b/rest/routes/user.go @@ -271,6 +271,13 @@ func UserRoutes(app fiber.Router) { protected.Get("/saas-api/logs", middlewares.MenuMiddleware, controllers.SaasDispatchLogIndex) protected.Get("/loadsaasdispatchlogs", controllers.GetSaasDispatchLogs) + // ─── Alibaba Cloud OSS API ──────────────────────────────────────────────── + protected.Get("/oss-api", middlewares.MenuMiddleware, controllers.OssApiIndex) + protected.Get("/loadossapi", controllers.GetOssApiConfigs) + protected.Post("/oss-api", controllers.CreateOssApiConfig) + protected.Put("/oss-api/:id", controllers.UpdateOssApiConfig) + protected.Delete("/oss-api/:id", controllers.DeleteOssApiConfig) + // ─── VCard API (integración Admin Laravel) ──────────────────────────────── protected.Get("/vcard-api", middlewares.MenuMiddleware, controllers.VcardApiIndex) protected.Post("/vcard-api/login", controllers.VcardApiLogin) diff --git a/seed_integraciones.sql b/seed_integraciones.sql index 0df9789..f80194f 100644 --- a/seed_integraciones.sql +++ b/seed_integraciones.sql @@ -46,12 +46,23 @@ WHERE NOT EXISTS ( SELECT 1 FROM submodules WHERE url = '/app/vcard-api' AND deleted_at IS NULL ); --- 5. Asignar todos los submódulos de Integraciones a TODOS los roles (ignorar duplicados) +-- 5. Insertar submódulo Alibaba Cloud OSS si no existe +INSERT INTO submodules (title, description, url, module_id, modified_at, created_at, updated_at) +SELECT 'Alibaba Cloud OSS', + 'Gestión de configuraciones de acceso a Alibaba Cloud Object Storage Service (OSS): credenciales, buckets y regiones', + '/app/oss-api', + (SELECT id FROM modules WHERE title = 'Integraciones' AND deleted_at IS NULL LIMIT 1), + NOW(), NOW(), NOW() +WHERE NOT EXISTS ( + SELECT 1 FROM submodules WHERE url = '/app/oss-api' AND deleted_at IS NULL +); + +-- 6. Asignar todos los submódulos de Integraciones a TODOS los roles (ignorar duplicados) INSERT INTO roles_submodules (role_id, submodule_id) SELECT r.id, s.id FROM roles r CROSS JOIN submodules s -WHERE s.url IN ('/app/hostinger', '/app/cloudflare', '/app/vcard-api') +WHERE s.url IN ('/app/hostinger', '/app/cloudflare', '/app/vcard-api', '/app/oss-api') AND r.deleted_at IS NULL AND s.deleted_at IS NULL ON CONFLICT DO NOTHING;