documentacion
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"math"
|
||||
"strconv"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/sujit-baniya/fiber-boilerplate/pkg/models"
|
||||
)
|
||||
|
||||
// DocCategoriasIndex renderiza el panel de gestión de categorías de documentación.
|
||||
func DocCategoriasIndex(c *fiber.Ctx) error {
|
||||
data := fiber.Map{
|
||||
"user": c.Locals("user").(map[string]interface{}),
|
||||
"modules": c.Locals("userModules"),
|
||||
}
|
||||
if err := c.Render("docs/categorias", data, "layouts/main"); err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetDocCategorias devuelve la lista paginada de categorías en JSON.
|
||||
func GetDocCategorias(c *fiber.Ctx) error {
|
||||
page, _ := strconv.Atoi(c.Query("page", "1"))
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
search := c.Query("search", "")
|
||||
limit := 10
|
||||
offset := (page - 1) * limit
|
||||
|
||||
items, total, err := models.GetAllDocCategorias(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,
|
||||
})
|
||||
}
|
||||
|
||||
// CreateDocCategoria crea una nueva categoría global.
|
||||
func CreateDocCategoria(c *fiber.Ctx) error {
|
||||
type Req struct {
|
||||
Nombre string `json:"nombre"`
|
||||
Slug string `json:"slug"`
|
||||
Descripcion string `json:"descripcion"`
|
||||
Orden int `json:"orden"`
|
||||
}
|
||||
var req Req
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Datos inválidos"})
|
||||
}
|
||||
if req.Nombre == "" || req.Slug == "" {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Nombre y slug son obligatorios"})
|
||||
}
|
||||
item := &models.DocCategoria{
|
||||
Nombre: req.Nombre,
|
||||
Slug: req.Slug,
|
||||
Descripcion: req.Descripcion,
|
||||
Orden: req.Orden,
|
||||
}
|
||||
if err := models.CreateDocCategoria(item); err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
return c.JSON(fiber.Map{"message": "Categoría creada", "item": item})
|
||||
}
|
||||
|
||||
// UpdateDocCategoria actualiza una categoría existente.
|
||||
func UpdateDocCategoria(c *fiber.Ctx) error {
|
||||
id, err := strconv.ParseUint(c.Params("id"), 10, 32)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "ID inválido"})
|
||||
}
|
||||
type Req struct {
|
||||
Nombre string `json:"nombre"`
|
||||
Slug string `json:"slug"`
|
||||
Descripcion string `json:"descripcion"`
|
||||
Orden int `json:"orden"`
|
||||
}
|
||||
var req Req
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Datos inválidos"})
|
||||
}
|
||||
item := &models.DocCategoria{
|
||||
Nombre: req.Nombre,
|
||||
Slug: req.Slug,
|
||||
Descripcion: req.Descripcion,
|
||||
Orden: req.Orden,
|
||||
}
|
||||
item.ID = uint(id)
|
||||
if err := models.UpdateDocCategoria(item); err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
return c.JSON(fiber.Map{"message": "Categoría actualizada"})
|
||||
}
|
||||
|
||||
// DeleteDocCategoria elimina una categoría.
|
||||
func DeleteDocCategoria(c *fiber.Ctx) error {
|
||||
id, err := strconv.ParseUint(c.Params("id"), 10, 32)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "ID inválido"})
|
||||
}
|
||||
if err := models.DeleteDocCategoria(uint(id)); err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
return c.JSON(fiber.Map{"message": "Categoría eliminada"})
|
||||
}
|
||||
@@ -0,0 +1,359 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"math"
|
||||
"strconv"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/sujit-baniya/fiber-boilerplate/pkg/models"
|
||||
)
|
||||
|
||||
// DocPaginasIndex renderiza el panel de gestión de páginas de documentación.
|
||||
func DocPaginasIndex(c *fiber.Ctx) error {
|
||||
data := fiber.Map{
|
||||
"user": c.Locals("user").(map[string]interface{}),
|
||||
"modules": c.Locals("userModules"),
|
||||
}
|
||||
if err := c.Render("docs/paginas", data, "layouts/main"); err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetDocPaginas devuelve la lista paginada de páginas en JSON.
|
||||
func GetDocPaginas(c *fiber.Ctx) error {
|
||||
page, _ := strconv.Atoi(c.Query("page", "1"))
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
search := c.Query("search", "")
|
||||
limit := 10
|
||||
offset := (page - 1) * limit
|
||||
|
||||
var saasID, catID uint
|
||||
if v, err := strconv.ParseUint(c.Query("saas_id", "0"), 10, 32); err == nil {
|
||||
saasID = uint(v)
|
||||
}
|
||||
if v, err := strconv.ParseUint(c.Query("categoria_id", "0"), 10, 32); err == nil {
|
||||
catID = uint(v)
|
||||
}
|
||||
|
||||
items, total, err := models.GetAllDocPaginas(limit, offset, search, saasID, catID)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
|
||||
// Selects para el formulario
|
||||
saasItems, _ := models.GetAllSaasProductosSelect()
|
||||
categorias, _ := models.GetAllDocCategoriasSelect()
|
||||
roles, _ := models.AllRolesSelect()
|
||||
|
||||
return c.JSON(fiber.Map{
|
||||
"items": items,
|
||||
"saas": saasItems,
|
||||
"categorias": categorias,
|
||||
"roles": roles,
|
||||
"total": total,
|
||||
"totalPages": int(math.Ceil(float64(total) / float64(limit))),
|
||||
"page": page,
|
||||
"limit": limit,
|
||||
})
|
||||
}
|
||||
|
||||
// GetDocPaginaDetalle devuelve una página por ID con todos sus datos para edición.
|
||||
func GetDocPaginaDetalle(c *fiber.Ctx) error {
|
||||
id, err := strconv.ParseUint(c.Params("id"), 10, 32)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "ID inválido"})
|
||||
}
|
||||
item, err := models.GetDocPaginaByID(uint(id))
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "Página no encontrada"})
|
||||
}
|
||||
return c.JSON(item)
|
||||
}
|
||||
|
||||
// CreateDocPagina crea una nueva página de documentación.
|
||||
func CreateDocPagina(c *fiber.Ctx) error {
|
||||
type Req struct {
|
||||
SaasID uint `json:"saas_id"`
|
||||
CategoriaID uint `json:"categoria_id"`
|
||||
Titulo string `json:"titulo"`
|
||||
Slug string `json:"slug"`
|
||||
Contenido string `json:"contenido"`
|
||||
TipoContenido string `json:"tipo_contenido"`
|
||||
Visibilidad string `json:"visibilidad"`
|
||||
Orden int `json:"orden"`
|
||||
Publicado bool `json:"publicado"`
|
||||
RolIDs []uint `json:"rol_ids"`
|
||||
}
|
||||
var req Req
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Datos inválidos"})
|
||||
}
|
||||
if req.Titulo == "" || req.Slug == "" || req.SaasID == 0 || req.CategoriaID == 0 {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Título, slug, saas y categoría son obligatorios"})
|
||||
}
|
||||
if req.TipoContenido == "" {
|
||||
req.TipoContenido = "markdown"
|
||||
}
|
||||
if req.Visibilidad == "" {
|
||||
req.Visibilidad = "public"
|
||||
}
|
||||
|
||||
userMap, _ := c.Locals("user").(map[string]interface{})
|
||||
var creadoPor uint
|
||||
if userMap != nil {
|
||||
if idVal, ok := userMap["id"]; ok {
|
||||
switch v := idVal.(type) {
|
||||
case float64:
|
||||
creadoPor = uint(v)
|
||||
case uint:
|
||||
creadoPor = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
item := &models.DocPagina{
|
||||
SaasID: req.SaasID,
|
||||
CategoriaID: req.CategoriaID,
|
||||
Titulo: req.Titulo,
|
||||
Slug: req.Slug,
|
||||
Contenido: req.Contenido,
|
||||
TipoContenido: req.TipoContenido,
|
||||
Visibilidad: req.Visibilidad,
|
||||
Orden: req.Orden,
|
||||
Publicado: req.Publicado,
|
||||
CreadoPor: creadoPor,
|
||||
}
|
||||
if err := models.CreateDocPagina(item, req.RolIDs); err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
return c.JSON(fiber.Map{"message": "Página creada", "item": item})
|
||||
}
|
||||
|
||||
// UpdateDocPagina actualiza una página existente.
|
||||
func UpdateDocPagina(c *fiber.Ctx) error {
|
||||
id, err := strconv.ParseUint(c.Params("id"), 10, 32)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "ID inválido"})
|
||||
}
|
||||
type Req struct {
|
||||
SaasID uint `json:"saas_id"`
|
||||
CategoriaID uint `json:"categoria_id"`
|
||||
Titulo string `json:"titulo"`
|
||||
Slug string `json:"slug"`
|
||||
Contenido string `json:"contenido"`
|
||||
TipoContenido string `json:"tipo_contenido"`
|
||||
Visibilidad string `json:"visibilidad"`
|
||||
Orden int `json:"orden"`
|
||||
Publicado bool `json:"publicado"`
|
||||
RolIDs []uint `json:"rol_ids"`
|
||||
}
|
||||
var req Req
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Datos inválidos"})
|
||||
}
|
||||
item := &models.DocPagina{
|
||||
SaasID: req.SaasID,
|
||||
CategoriaID: req.CategoriaID,
|
||||
Titulo: req.Titulo,
|
||||
Slug: req.Slug,
|
||||
Contenido: req.Contenido,
|
||||
TipoContenido: req.TipoContenido,
|
||||
Visibilidad: req.Visibilidad,
|
||||
Orden: req.Orden,
|
||||
Publicado: req.Publicado,
|
||||
}
|
||||
item.ID = uint(id)
|
||||
if err := models.UpdateDocPagina(item, req.RolIDs); err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
return c.JSON(fiber.Map{"message": "Página actualizada"})
|
||||
}
|
||||
|
||||
// DeleteDocPagina elimina una página de documentación.
|
||||
func DeleteDocPagina(c *fiber.Ctx) error {
|
||||
id, err := strconv.ParseUint(c.Params("id"), 10, 32)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "ID inválido"})
|
||||
}
|
||||
if err := models.DeleteDocPagina(uint(id)); err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
return c.JSON(fiber.Map{"message": "Página eliminada"})
|
||||
}
|
||||
|
||||
// ─── Rutas públicas / privadas de lectura ─────────────────────────────────────
|
||||
|
||||
// DocsPublicoIndex renderiza el índice público de un SaaS (sin login).
|
||||
func DocsPublicoIndex(c *fiber.Ctx) error {
|
||||
saasSlug := c.Params("saas")
|
||||
saas, err := models.GetSaasProductoBySlug(saasSlug)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusNotFound).Render("errors/404", fiber.Map{}, "layouts/public")
|
||||
}
|
||||
|
||||
paginas, err := models.GetDocPaginasPublicas(saas.ID)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
|
||||
// Agrupar por categoría
|
||||
grupos := agruparPorCategoria(paginas)
|
||||
|
||||
return c.Render("docs/public-index", fiber.Map{
|
||||
"saas": saas,
|
||||
"grupos": grupos,
|
||||
}, "layouts/public")
|
||||
}
|
||||
|
||||
// DocsPublicaPagina renderiza una página pública individual.
|
||||
func DocsPublicaPagina(c *fiber.Ctx) error {
|
||||
saasSlug := c.Params("saas")
|
||||
slug := c.Params("slug")
|
||||
|
||||
saas, err := models.GetSaasProductoBySlug(saasSlug)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusNotFound).Render("errors/404", fiber.Map{}, "layouts/public")
|
||||
}
|
||||
|
||||
pagina, err := models.GetDocPaginaBySlug(saas.ID, slug)
|
||||
if err != nil || !pagina.Publicado {
|
||||
return c.Status(fiber.StatusNotFound).Render("errors/404", fiber.Map{}, "layouts/public")
|
||||
}
|
||||
if pagina.Visibilidad == "private" {
|
||||
return c.Status(fiber.StatusForbidden).Render("errors/403", fiber.Map{}, "layouts/public")
|
||||
}
|
||||
|
||||
// Índice lateral: páginas públicas del mismo SaaS
|
||||
todas, _ := models.GetDocPaginasPublicas(saas.ID)
|
||||
grupos := agruparPorCategoria(todas)
|
||||
|
||||
return c.Render("docs/public-page", fiber.Map{
|
||||
"saas": saas,
|
||||
"pagina": pagina,
|
||||
"grupos": grupos,
|
||||
}, "layouts/public")
|
||||
}
|
||||
|
||||
// DocsPrivadoIndex renderiza el índice de un SaaS para usuarios autenticados (incluye privadas con rol).
|
||||
func DocsPrivadoIndex(c *fiber.Ctx) error {
|
||||
saasSlug := c.Params("saas")
|
||||
saas, err := models.GetSaasProductoBySlug(saasSlug)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusNotFound).Render("errors/404", fiber.Map{}, "layouts/main")
|
||||
}
|
||||
|
||||
rolNames := getUserRolNames(c)
|
||||
paginas, err := models.GetDocPaginasParaRoles(saas.ID, rolNames)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
grupos := agruparPorCategoria(paginas)
|
||||
|
||||
return c.Render("docs/private-index", fiber.Map{
|
||||
"user": c.Locals("user"),
|
||||
"saas": saas,
|
||||
"grupos": grupos,
|
||||
}, "layouts/main")
|
||||
}
|
||||
|
||||
// DocsPrivadaPagina renderiza una página con verificación de rol.
|
||||
func DocsPrivadaPagina(c *fiber.Ctx) error {
|
||||
saasSlug := c.Params("saas")
|
||||
slug := c.Params("slug")
|
||||
|
||||
saas, err := models.GetSaasProductoBySlug(saasSlug)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusNotFound).Render("errors/404", fiber.Map{}, "layouts/main")
|
||||
}
|
||||
|
||||
pagina, err := models.GetDocPaginaBySlug(saas.ID, slug)
|
||||
if err != nil || !pagina.Publicado {
|
||||
return c.Status(fiber.StatusNotFound).Render("errors/404", fiber.Map{}, "layouts/main")
|
||||
}
|
||||
|
||||
if pagina.Visibilidad == "private" {
|
||||
// Verificar que el usuario tenga al menos un rol asignado a la página
|
||||
rolNames := getUserRolNames(c)
|
||||
if !paginaAccesible(pagina, rolNames) {
|
||||
return c.Status(fiber.StatusForbidden).Render("errors/403", fiber.Map{
|
||||
"user": c.Locals("user"),
|
||||
}, "layouts/main")
|
||||
}
|
||||
}
|
||||
|
||||
rolNames := getUserRolNames(c)
|
||||
todas, _ := models.GetDocPaginasParaRoles(saas.ID, rolNames)
|
||||
grupos := agruparPorCategoria(todas)
|
||||
|
||||
return c.Render("docs/private-page", fiber.Map{
|
||||
"user": c.Locals("user"),
|
||||
"modules": c.Locals("userModules"),
|
||||
"saas": saas,
|
||||
"pagina": pagina,
|
||||
"grupos": grupos,
|
||||
}, "layouts/main")
|
||||
}
|
||||
|
||||
// ─── helpers ──────────────────────────────────────────────────────────────────
|
||||
|
||||
type GrupoCategoria struct {
|
||||
Categoria models.DocCategoria
|
||||
Paginas []models.DocPagina
|
||||
}
|
||||
|
||||
func agruparPorCategoria(paginas []models.DocPagina) []GrupoCategoria {
|
||||
orden := []uint{}
|
||||
mapa := map[uint]*GrupoCategoria{}
|
||||
for _, p := range paginas {
|
||||
if _, ok := mapa[p.CategoriaID]; !ok {
|
||||
orden = append(orden, p.CategoriaID)
|
||||
mapa[p.CategoriaID] = &GrupoCategoria{Categoria: p.Categoria}
|
||||
}
|
||||
mapa[p.CategoriaID].Paginas = append(mapa[p.CategoriaID].Paginas, p)
|
||||
}
|
||||
var result []GrupoCategoria
|
||||
for _, id := range orden {
|
||||
result = append(result, *mapa[id])
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func getUserRolNames(c *fiber.Ctx) []string {
|
||||
userMap, ok := c.Locals("user").(map[string]interface{})
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
raw, ok := userMap["roles"]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
var names []string
|
||||
switch v := raw.(type) {
|
||||
case []string:
|
||||
names = v
|
||||
case []interface{}:
|
||||
for _, r := range v {
|
||||
if s, ok := r.(string); ok {
|
||||
names = append(names, s)
|
||||
}
|
||||
}
|
||||
}
|
||||
return names
|
||||
}
|
||||
|
||||
func paginaAccesible(p *models.DocPagina, userRoles []string) bool {
|
||||
if p.Visibilidad == "public" {
|
||||
return true
|
||||
}
|
||||
for _, pr := range p.Roles {
|
||||
for _, ur := range userRoles {
|
||||
if pr.Name == ur {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"math"
|
||||
"strconv"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/sujit-baniya/fiber-boilerplate/pkg/models"
|
||||
)
|
||||
|
||||
// SaasIndex renderiza la vista del panel de gestión de productos SaaS.
|
||||
func SaasIndex(c *fiber.Ctx) error {
|
||||
data := fiber.Map{
|
||||
"user": c.Locals("user").(map[string]interface{}),
|
||||
"modules": c.Locals("userModules"),
|
||||
}
|
||||
if err := c.Render("saas", data, "layouts/main"); err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetSaasProductos devuelve la lista paginada en JSON (para Alpine/HTMX).
|
||||
func GetSaasProductos(c *fiber.Ctx) error {
|
||||
page, _ := strconv.Atoi(c.Query("page", "1"))
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
search := c.Query("search", "")
|
||||
limit := 10
|
||||
offset := (page - 1) * limit
|
||||
|
||||
items, total, err := models.GetAllSaasProductos(limit, offset, search)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
|
||||
servicios, _ := models.GetAllServiciosSelect()
|
||||
|
||||
return c.JSON(fiber.Map{
|
||||
"items": items,
|
||||
"servicios": servicios,
|
||||
"total": total,
|
||||
"totalPages": int(math.Ceil(float64(total) / float64(limit))),
|
||||
"page": page,
|
||||
"limit": limit,
|
||||
})
|
||||
}
|
||||
|
||||
// CreateSaasProducto crea un nuevo producto SaaS.
|
||||
func CreateSaasProducto(c *fiber.Ctx) error {
|
||||
type Req struct {
|
||||
Nombre string `json:"nombre"`
|
||||
Slug string `json:"slug"`
|
||||
Descripcion string `json:"descripcion"`
|
||||
LogoURL string `json:"logo_url"`
|
||||
ServicioID *uint `json:"servicio_id"`
|
||||
Activo bool `json:"activo"`
|
||||
Orden int `json:"orden"`
|
||||
}
|
||||
var req Req
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Datos inválidos"})
|
||||
}
|
||||
if req.Nombre == "" || req.Slug == "" {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Nombre y slug son obligatorios"})
|
||||
}
|
||||
item := &models.SaasProducto{
|
||||
Nombre: req.Nombre,
|
||||
Slug: req.Slug,
|
||||
Descripcion: req.Descripcion,
|
||||
LogoURL: req.LogoURL,
|
||||
ServicioID: req.ServicioID,
|
||||
Activo: req.Activo,
|
||||
Orden: req.Orden,
|
||||
}
|
||||
if err := models.CreateSaasProducto(item); err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
return c.JSON(fiber.Map{"message": "Producto SaaS creado", "item": item})
|
||||
}
|
||||
|
||||
// UpdateSaasProducto actualiza un producto SaaS existente.
|
||||
func UpdateSaasProducto(c *fiber.Ctx) error {
|
||||
id, err := strconv.ParseUint(c.Params("id"), 10, 32)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "ID inválido"})
|
||||
}
|
||||
type Req struct {
|
||||
Nombre string `json:"nombre"`
|
||||
Slug string `json:"slug"`
|
||||
Descripcion string `json:"descripcion"`
|
||||
LogoURL string `json:"logo_url"`
|
||||
ServicioID *uint `json:"servicio_id"`
|
||||
Activo bool `json:"activo"`
|
||||
Orden int `json:"orden"`
|
||||
}
|
||||
var req Req
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Datos inválidos"})
|
||||
}
|
||||
item := &models.SaasProducto{
|
||||
Nombre: req.Nombre,
|
||||
Slug: req.Slug,
|
||||
Descripcion: req.Descripcion,
|
||||
LogoURL: req.LogoURL,
|
||||
ServicioID: req.ServicioID,
|
||||
Activo: req.Activo,
|
||||
Orden: req.Orden,
|
||||
}
|
||||
item.ID = uint(id)
|
||||
if err := models.UpdateSaasProducto(item); err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
return c.JSON(fiber.Map{"message": "Producto SaaS actualizado"})
|
||||
}
|
||||
|
||||
// DeleteSaasProducto elimina un producto SaaS.
|
||||
func DeleteSaasProducto(c *fiber.Ctx) error {
|
||||
id, err := strconv.ParseUint(c.Params("id"), 10, 32)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "ID inválido"})
|
||||
}
|
||||
if err := models.DeleteSaasProducto(uint(id)); err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
return c.JSON(fiber.Map{"message": "Producto SaaS eliminado"})
|
||||
}
|
||||
@@ -25,5 +25,7 @@ func RutasPublicas(web fiber.Router) {
|
||||
web.Get("/pago-exitoso", apiControllers.PagoExitosoPage)
|
||||
// Ruta fuera del prefijo /api para evitar que AuthApi() la intercepte
|
||||
web.Get("/pago/estado", apiControllers.PagoEstadoAPI)
|
||||
}
|
||||
// ─── Documentación pública ────────────────────────────────────────────────
|
||||
web.Get("/docs/:saas", controllers.DocsPublicoIndex)
|
||||
web.Get("/docs/:saas/:slug", controllers.DocsPublicaPagina)}
|
||||
|
||||
|
||||
@@ -144,4 +144,30 @@ func UserRoutes(app fiber.Router) {
|
||||
// Bold API (crear link, consultar estado)
|
||||
protected.Post("/pasarelas/bold/crear-link", apiControllers.BoldCreatePaymentLink)
|
||||
protected.Get("/pasarelas/bold/link/:linkID", apiControllers.BoldGetLinkStatus)
|
||||
|
||||
// ─── Productos SaaS ──────────────────────────────────────────────────────
|
||||
protected.Get("/saas", middlewares.MenuMiddleware, controllers.SaasIndex)
|
||||
protected.Get("/loadsaas", controllers.GetSaasProductos)
|
||||
protected.Post("/saas", controllers.CreateSaasProducto)
|
||||
protected.Put("/saas/:id", controllers.UpdateSaasProducto)
|
||||
protected.Delete("/saas/:id", controllers.DeleteSaasProducto)
|
||||
|
||||
// ─── Documentación: categorías globales ──────────────────────────────────
|
||||
protected.Get("/doc/categorias", middlewares.MenuMiddleware, controllers.DocCategoriasIndex)
|
||||
protected.Get("/doc/loadcategorias", controllers.GetDocCategorias)
|
||||
protected.Post("/doc/categorias", controllers.CreateDocCategoria)
|
||||
protected.Put("/doc/categorias/:id", controllers.UpdateDocCategoria)
|
||||
protected.Delete("/doc/categorias/:id", controllers.DeleteDocCategoria)
|
||||
|
||||
// ─── Documentación: páginas ───────────────────────────────────────────────
|
||||
protected.Get("/doc/paginas", middlewares.MenuMiddleware, controllers.DocPaginasIndex)
|
||||
protected.Get("/doc/loadpaginas", controllers.GetDocPaginas)
|
||||
protected.Get("/doc/paginas/:id", controllers.GetDocPaginaDetalle)
|
||||
protected.Post("/doc/paginas", controllers.CreateDocPagina)
|
||||
protected.Put("/doc/paginas/:id", controllers.UpdateDocPagina)
|
||||
protected.Delete("/doc/paginas/:id", controllers.DeleteDocPagina)
|
||||
|
||||
// ─── Documentación: lectura privada (usuario logueado) ───────────────────
|
||||
protected.Get("/docs/:saas", middlewares.MenuMiddleware, controllers.DocsPrivadoIndex)
|
||||
protected.Get("/docs/:saas/:slug", middlewares.MenuMiddleware, controllers.DocsPrivadaPagina)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user