package controllers import ( "fmt" "os" "path/filepath" "strconv" "strings" "github.com/gofiber/fiber/v2" "github.com/sujit-baniya/fiber-boilerplate/pkg/models" ) // ─── Admin: vista principal ─────────────────────────────────────────────────── func PartnerRecursosIndex(c *fiber.Ctx) error { return c.Render("partner_recursos", fiber.Map{ "user": c.Locals("user"), "modules": c.Locals("userModules"), }, "layouts/main") } // ─── Recursos ───────────────────────────────────────────────────────────────── func LoadPartnerRecursos(c *fiber.Ctx) error { recursos, err := models.GetAllPartnerRecursos(false) if err != nil { return c.Status(500).JSON(fiber.Map{"error": err.Error()}) } return c.JSON(fiber.Map{"recursos": recursos}) } func CreatePartnerRecurso(c *fiber.Ctx) error { type Req struct { Nombre string `json:"nombre"` Descripcion string `json:"descripcion"` Categoria string `json:"categoria"` Enlace string `json:"enlace"` Activo bool `json:"activo"` } var req Req if err := c.BodyParser(&req); err != nil { return c.Status(400).JSON(fiber.Map{"error": "Datos inválidos"}) } r := &models.PartnerRecurso{ Nombre: req.Nombre, Descripcion: req.Descripcion, Categoria: req.Categoria, Enlace: req.Enlace, Activo: req.Activo, } if err := models.CreatePartnerRecurso(r); err != nil { return c.Status(500).JSON(fiber.Map{"error": err.Error()}) } return c.Status(201).JSON(r) } func UpdatePartnerRecurso(c *fiber.Ctx) error { id, _ := strconv.ParseUint(c.Params("id"), 10, 32) type Req struct { Nombre string `json:"nombre"` Descripcion string `json:"descripcion"` Categoria string `json:"categoria"` Enlace string `json:"enlace"` Activo bool `json:"activo"` } var req Req if err := c.BodyParser(&req); err != nil { return c.Status(400).JSON(fiber.Map{"error": "Datos inválidos"}) } r, err := models.GetPartnerRecursoByID(uint(id)) if err != nil { return c.Status(404).JSON(fiber.Map{"error": "No encontrado"}) } r.Nombre = req.Nombre r.Descripcion = req.Descripcion r.Categoria = req.Categoria r.Enlace = req.Enlace r.Activo = req.Activo if err := models.UpdatePartnerRecurso(r); err != nil { return c.Status(500).JSON(fiber.Map{"error": err.Error()}) } return c.JSON(r) } func DeletePartnerRecurso(c *fiber.Ctx) error { id, _ := strconv.ParseUint(c.Params("id"), 10, 32) if err := models.DeletePartnerRecurso(uint(id)); err != nil { return c.Status(500).JSON(fiber.Map{"error": err.Error()}) } return c.JSON(fiber.Map{"ok": true}) } func UploadPartnerRecursoArchivo(c *fiber.Ctx) error { id, _ := strconv.ParseUint(c.Params("id"), 10, 32) file, err := c.FormFile("archivo") if err != nil { return c.Status(400).JSON(fiber.Map{"error": "Archivo requerido"}) } if file.Size > 50*1024*1024 { return c.Status(400).JSON(fiber.Map{"error": "Máximo 50MB"}) } dir := "uploads/partner_recursos" if err := os.MkdirAll(dir, 0755); err != nil { return c.Status(500).JSON(fiber.Map{"error": "Error al crear directorio"}) } safeFile := safeFilenameProyecto(file.Filename) savePath := filepath.Join(dir, fmt.Sprintf("%d_%s", id, safeFile)) // Validar que la ruta quede dentro de uploads/ clean := filepath.Clean(savePath) if !strings.HasPrefix(clean, "uploads/") { return c.Status(400).JSON(fiber.Map{"error": "Ruta inválida"}) } if err := c.SaveFile(file, savePath); err != nil { return c.Status(500).JSON(fiber.Map{"error": "Error al guardar archivo"}) } r, err := models.GetPartnerRecursoByID(uint(id)) if err != nil { return c.Status(404).JSON(fiber.Map{"error": "Recurso no encontrado"}) } r.Archivo = savePath r.NombreOrig = file.Filename if err := models.UpdatePartnerRecurso(r); err != nil { return c.Status(500).JSON(fiber.Map{"error": err.Error()}) } return c.JSON(fiber.Map{"ok": true, "archivo": savePath, "nombre_orig": file.Filename}) } func DownloadPartnerRecurso(c *fiber.Ctx) error { id, _ := strconv.ParseUint(c.Params("id"), 10, 32) r, err := models.GetPartnerRecursoByID(uint(id)) if err != nil || r.Archivo == "" { return c.Status(404).JSON(fiber.Map{"error": "Archivo no encontrado"}) } clean := filepath.Clean(r.Archivo) if !strings.HasPrefix(clean, "uploads/") { return c.Status(403).JSON(fiber.Map{"error": "Acceso denegado"}) } nombre := r.NombreOrig if nombre == "" { nombre = filepath.Base(r.Archivo) } c.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, nombre)) return c.SendFile(clean) } // ─── Comunicados ────────────────────────────────────────────────────────────── func LoadPartnerComunicados(c *fiber.Ctx) error { items, err := models.GetAllPartnerComunicados(false) if err != nil { return c.Status(500).JSON(fiber.Map{"error": err.Error()}) } return c.JSON(fiber.Map{"comunicados": items}) } func CreatePartnerComunicado(c *fiber.Ctx) error { type Req struct { Titulo string `json:"titulo"` Contenido string `json:"contenido"` Activo bool `json:"activo"` } var req Req if err := c.BodyParser(&req); err != nil { return c.Status(400).JSON(fiber.Map{"error": "Datos inválidos"}) } item := &models.PartnerComunicado{ Titulo: req.Titulo, Contenido: req.Contenido, Activo: req.Activo, } if err := models.CreatePartnerComunicado(item); err != nil { return c.Status(500).JSON(fiber.Map{"error": err.Error()}) } return c.Status(201).JSON(item) } func UpdatePartnerComunicado(c *fiber.Ctx) error { id, _ := strconv.ParseUint(c.Params("id"), 10, 32) type Req struct { Titulo string `json:"titulo"` Contenido string `json:"contenido"` Activo bool `json:"activo"` } var req Req if err := c.BodyParser(&req); err != nil { return c.Status(400).JSON(fiber.Map{"error": "Datos inválidos"}) } item, err := models.GetPartnerComunicadoByID(uint(id)) if err != nil { return c.Status(404).JSON(fiber.Map{"error": "No encontrado"}) } item.Titulo = req.Titulo item.Contenido = req.Contenido item.Activo = req.Activo if err := models.UpdatePartnerComunicado(item); err != nil { return c.Status(500).JSON(fiber.Map{"error": err.Error()}) } return c.JSON(item) } func DeletePartnerComunicado(c *fiber.Ctx) error { id, _ := strconv.ParseUint(c.Params("id"), 10, 32) if err := models.DeletePartnerComunicado(uint(id)); err != nil { return c.Status(500).JSON(fiber.Map{"error": err.Error()}) } return c.JSON(fiber.Map{"ok": true}) } func UploadPartnerComunicadoArchivo(c *fiber.Ctx) error { id, _ := strconv.ParseUint(c.Params("id"), 10, 32) file, err := c.FormFile("archivo") if err != nil { return c.Status(400).JSON(fiber.Map{"error": "Archivo requerido"}) } if file.Size > 50*1024*1024 { return c.Status(400).JSON(fiber.Map{"error": "Máximo 50MB"}) } dir := "uploads/partner_comunicados" if err := os.MkdirAll(dir, 0755); err != nil { return c.Status(500).JSON(fiber.Map{"error": "Error al crear directorio"}) } safeFile := safeFilenameProyecto(file.Filename) savePath := filepath.Join(dir, fmt.Sprintf("%d_%s", id, safeFile)) clean := filepath.Clean(savePath) if !strings.HasPrefix(clean, "uploads/") { return c.Status(400).JSON(fiber.Map{"error": "Ruta inválida"}) } if err := c.SaveFile(file, savePath); err != nil { return c.Status(500).JSON(fiber.Map{"error": "Error al guardar archivo"}) } item, err := models.GetPartnerComunicadoByID(uint(id)) if err != nil { return c.Status(404).JSON(fiber.Map{"error": "Comunicado no encontrado"}) } item.Archivo = savePath item.NombreOrig = file.Filename if err := models.UpdatePartnerComunicado(item); err != nil { return c.Status(500).JSON(fiber.Map{"error": err.Error()}) } return c.JSON(fiber.Map{"ok": true, "archivo": savePath, "nombre_orig": file.Filename}) } func DownloadPartnerComunicadoArchivo(c *fiber.Ctx) error { id, _ := strconv.ParseUint(c.Params("id"), 10, 32) item, err := models.GetPartnerComunicadoByID(uint(id)) if err != nil || item.Archivo == "" { return c.Status(404).JSON(fiber.Map{"error": "Archivo no encontrado"}) } clean := filepath.Clean(item.Archivo) if !strings.HasPrefix(clean, "uploads/") { return c.Status(403).JSON(fiber.Map{"error": "Acceso denegado"}) } nombre := item.NombreOrig if nombre == "" { nombre = filepath.Base(item.Archivo) } c.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, nombre)) return c.SendFile(clean) } // ─── Portal: acceso para partners ──────────────────────────────────────────── func PortalPartnerRecursos(c *fiber.Ctx) error { recursos, _ := models.GetAllPartnerRecursos(true) comunicados, _ := models.GetAllPartnerComunicados(true) return c.JSON(fiber.Map{ "recursos": recursos, "comunicados": comunicados, }) } func PortalDownloadPartnerRecurso(c *fiber.Ctx) error { id, _ := strconv.ParseUint(c.Params("id"), 10, 32) r, err := models.GetPartnerRecursoByID(uint(id)) if err != nil || r.Archivo == "" || !r.Activo { return c.Status(404).JSON(fiber.Map{"error": "Archivo no encontrado"}) } clean := filepath.Clean(r.Archivo) if !strings.HasPrefix(clean, "uploads/") { return c.Status(403).JSON(fiber.Map{"error": "Acceso denegado"}) } nombre := r.NombreOrig if nombre == "" { nombre = filepath.Base(r.Archivo) } c.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, nombre)) return c.SendFile(clean) }