283 lines
9.6 KiB
Go
283 lines
9.6 KiB
Go
package controllers
|
|
|
|
import (
|
|
"math"
|
|
"mime"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/gofiber/fiber/v2"
|
|
"github.com/sujit-baniya/fiber-boilerplate/pkg/models"
|
|
"github.com/sujit-baniya/fiber-boilerplate/pkg/services"
|
|
)
|
|
|
|
// helper: obtiene el provider OSS por ID o el activo por defecto
|
|
func getOSSProvider(c *fiber.Ctx) (services.OSSProvider, error) {
|
|
idStr := c.Query("oss_api_id", "")
|
|
if idStr != "" {
|
|
id, err := strconv.ParseUint(idStr, 10, 32)
|
|
if err == nil && id > 0 {
|
|
return services.NewOSSProviderByID(uint(id))
|
|
}
|
|
}
|
|
return services.NewOSSProviderFromDB()
|
|
}
|
|
|
|
// 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})
|
|
}
|
|
|
|
// GetActiveOssApiList devuelve todas las configuraciones activas (para selectores UI).
|
|
func GetActiveOssApiList(c *fiber.Ctx) error {
|
|
items, err := models.GetActiveOssApis()
|
|
if err != nil {
|
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
|
}
|
|
return c.JSON(fiber.Map{"data": items})
|
|
}
|
|
|
|
// 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})
|
|
}
|
|
|
|
// ─── Explorador de archivos OSS ──────────────────────────────────────────────
|
|
|
|
// OssBrowserList lista los objetos del bucket activo con soporte de carpetas virtuales.
|
|
// Query params: prefix (carpeta actual), marker (paginación), max_keys
|
|
func OssBrowserList(c *fiber.Ctx) error {
|
|
svc, err := getOSSProvider(c)
|
|
if err != nil {
|
|
return c.Status(fiber.StatusServiceUnavailable).JSON(fiber.Map{"error": "Sin configuración OSS activa: " + err.Error()})
|
|
}
|
|
|
|
prefix := c.Query("prefix", "")
|
|
marker := c.Query("marker", "")
|
|
maxKeys, _ := strconv.Atoi(c.Query("max_keys", "100"))
|
|
|
|
result, err := svc.ListObjects(prefix, marker, maxKeys)
|
|
if err != nil {
|
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
|
}
|
|
return c.JSON(result)
|
|
}
|
|
|
|
// OssBrowserSignedURL genera una URL firmada temporal (1 hora) para previsualizar o descargar un objeto.
|
|
// Query param: key (objeto)
|
|
func OssBrowserSignedURL(c *fiber.Ctx) error {
|
|
key := strings.TrimSpace(c.Query("key", ""))
|
|
if key == "" {
|
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "key es requerido"})
|
|
}
|
|
|
|
svc, err := getOSSProvider(c)
|
|
if err != nil {
|
|
return c.Status(fiber.StatusServiceUnavailable).JSON(fiber.Map{"error": "Sin configuración OSS activa: " + err.Error()})
|
|
}
|
|
|
|
url, err := svc.SignedURL(key, 3600)
|
|
if err != nil {
|
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
|
}
|
|
return c.JSON(fiber.Map{"url": url})
|
|
}
|
|
|
|
// OssBrowserDelete elimina un objeto del bucket activo.
|
|
// Body JSON: { "key": "ruta/al/archivo.jpg" }
|
|
func OssBrowserDelete(c *fiber.Ctx) error {
|
|
type Req struct {
|
|
Key string `json:"key"`
|
|
}
|
|
var req Req
|
|
if err := c.BodyParser(&req); err != nil || strings.TrimSpace(req.Key) == "" {
|
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "key es requerido"})
|
|
}
|
|
|
|
svc, err := getOSSProvider(c)
|
|
if err != nil {
|
|
return c.Status(fiber.StatusServiceUnavailable).JSON(fiber.Map{"error": "Sin configuración OSS activa: " + err.Error()})
|
|
}
|
|
|
|
if err := svc.DeleteFile(req.Key); err != nil {
|
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
|
}
|
|
return c.JSON(fiber.Map{"ok": true})
|
|
}
|
|
|
|
// OssBrowserUpload sube un archivo al bucket activo.
|
|
// Form field: file (multipart), prefix (carpeta destino opcional)
|
|
func OssBrowserUpload(c *fiber.Ctx) error {
|
|
prefix := strings.TrimSpace(c.FormValue("prefix", ""))
|
|
fh, err := c.FormFile("file")
|
|
if err != nil {
|
|
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "archivo requerido"})
|
|
}
|
|
|
|
f, err := fh.Open()
|
|
if err != nil {
|
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "error abriendo archivo"})
|
|
}
|
|
defer f.Close()
|
|
|
|
objectKey := fh.Filename
|
|
if prefix != "" {
|
|
objectKey = strings.TrimRight(prefix, "/") + "/" + fh.Filename
|
|
}
|
|
|
|
ct := mime.TypeByExtension(filepath.Ext(fh.Filename))
|
|
if ct == "" {
|
|
ct = "application/octet-stream"
|
|
}
|
|
|
|
svc, err := getOSSProvider(c)
|
|
if err != nil {
|
|
return c.Status(fiber.StatusServiceUnavailable).JSON(fiber.Map{"error": "Sin configuración OSS activa: " + err.Error()})
|
|
}
|
|
|
|
if err := svc.UploadFromReader(objectKey, ct, f); err != nil {
|
|
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
|
}
|
|
return c.JSON(fiber.Map{"ok": true, "key": objectKey})
|
|
}
|