up
This commit is contained in:
@@ -11,7 +11,6 @@ import (
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/sujit-baniya/fiber-boilerplate/pkg/models"
|
||||
"github.com/sujit-baniya/fiber-boilerplate/pkg/services"
|
||||
)
|
||||
|
||||
func FacturasIndex(c *fiber.Ctx) error {
|
||||
@@ -186,10 +185,6 @@ func UploadFacturaPDF(c *fiber.Ctx) error {
|
||||
if err := models.UpdateFacturaArchivo(uint(id), savePath, file.Filename); err != nil {
|
||||
return c.Status(500).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
// Notificar al cliente
|
||||
if f, err := models.GetFacturaByID(uint(id)); err == nil {
|
||||
go services.DispatchFacturaSubida(f)
|
||||
}
|
||||
return c.JSON(fiber.Map{"ok": true, "archivo": savePath})
|
||||
}
|
||||
|
||||
|
||||
@@ -2,23 +2,19 @@ package controllers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/sujit-baniya/fiber-boilerplate/pkg/auth"
|
||||
"github.com/sujit-baniya/fiber-boilerplate/pkg/models"
|
||||
"github.com/sujit-baniya/fiber-boilerplate/pkg/services"
|
||||
"github.com/sujit-baniya/fiber-boilerplate/rest/middlewares"
|
||||
)
|
||||
|
||||
// ─── Auth ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
func PortalLoginPage(c *fiber.Ctx) error {
|
||||
// Usar PortalUser (validación completa) en vez de IsPortalLoggedIn (solo key)
|
||||
// para evitar loops cuando hay sesión obsoleta pero el usuario ya no existe en DB.
|
||||
if user, err := auth.PortalUser(c); err == nil && user != nil {
|
||||
if auth.IsPortalLoggedIn(c) {
|
||||
return c.Redirect("/portal/dashboard")
|
||||
}
|
||||
return c.Render("portal/login", fiber.Map{
|
||||
@@ -31,14 +27,11 @@ func PortalLoginPost(c *fiber.Ctx) error {
|
||||
password := c.FormValue("password")
|
||||
u, err := models.CheckPortalLogin(email, password)
|
||||
if err != nil {
|
||||
log.Printf("[PORTAL LOGIN] Fallo para %q: %v", email, err)
|
||||
return c.Redirect("/portal/login?error=" + url.QueryEscape(err.Error()))
|
||||
}
|
||||
if err := auth.SetPortalSession(c, u.ID); err != nil {
|
||||
log.Printf("[PORTAL LOGIN] Error guardando sesión para usuario %d: %v", u.ID, err)
|
||||
return c.Redirect("/portal/login?error=Error+interno")
|
||||
}
|
||||
log.Printf("[PORTAL LOGIN] OK usuario %d (%s)", u.ID, email)
|
||||
return c.Redirect("/portal/dashboard")
|
||||
}
|
||||
|
||||
@@ -52,14 +45,12 @@ func PortalLogout(c *fiber.Ctx) error {
|
||||
func PortalDashboard(c *fiber.Ctx) error {
|
||||
u := middlewares.PortalUserFromLocals(c)
|
||||
if u == nil {
|
||||
log.Println("[PORTAL DASHBOARD] Usuario no en locals, redirigiendo a login")
|
||||
return c.Redirect("/portal/login")
|
||||
}
|
||||
|
||||
// Recargar con accesos
|
||||
fullUser, err := models.GetPortalUserByID(u.ID)
|
||||
if err != nil {
|
||||
log.Printf("[PORTAL DASHBOARD] Error cargando usuario %d: %v", u.ID, err)
|
||||
return c.Redirect("/portal/login")
|
||||
}
|
||||
|
||||
@@ -87,7 +78,7 @@ func PortalDashboard(c *fiber.Ctx) error {
|
||||
"portalUser": fullUser,
|
||||
"proyectos": proyectos,
|
||||
"grupos": grupos,
|
||||
"isPartner": fullUser.Rol == "partner" || (fullUser.Role != nil && fullUser.Role.EsPortalPartner),
|
||||
"isPartner": fullUser.Rol == "partner",
|
||||
}, "layouts/portal")
|
||||
}
|
||||
|
||||
@@ -229,7 +220,6 @@ func PortalCrearTicket(c *fiber.Ctx) error {
|
||||
if err := models.CreateProyectoTicket(t); err != nil {
|
||||
return c.Status(500).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
go services.DispatchTicketNuevo(t, u, proy.Nombre)
|
||||
return c.Status(201).JSON(t)
|
||||
}
|
||||
|
||||
@@ -264,7 +254,6 @@ func PortalResponderTicket(c *fiber.Ctx) error {
|
||||
if err := models.CreateTicketMensaje(msg); err != nil {
|
||||
return c.Status(500).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
go services.DispatchTicketRespuestaCliente(ticket, req.Contenido, ticket.Proyecto.Nombre)
|
||||
return c.Status(201).JSON(msg)
|
||||
}
|
||||
|
||||
@@ -335,42 +324,3 @@ func PortalDownloadFactura(c *fiber.Ctx) error {
|
||||
c.Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, f.OriginalName))
|
||||
return c.SendFile(clean)
|
||||
}
|
||||
|
||||
// ─── Notificaciones in-app del portal user ────────────────────────────────────
|
||||
|
||||
func PortalGetNotifs(c *fiber.Ctx) error {
|
||||
u := middlewares.PortalUserFromLocals(c)
|
||||
if u == nil {
|
||||
return c.Status(401).JSON(fiber.Map{"error": "no autenticado"})
|
||||
}
|
||||
soloNoLeidas := c.Query("no_leidas") == "1"
|
||||
items, err := models.GetSistemaNotifs("portal_user", u.ID, soloNoLeidas)
|
||||
if err != nil {
|
||||
return c.Status(500).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
count := models.CountUnreadNotifs("portal_user", u.ID)
|
||||
return c.JSON(fiber.Map{"items": items, "unread": count})
|
||||
}
|
||||
|
||||
func PortalMarcarNotifLeida(c *fiber.Ctx) error {
|
||||
u := middlewares.PortalUserFromLocals(c)
|
||||
if u == nil {
|
||||
return c.Status(401).JSON(fiber.Map{"error": "no autenticado"})
|
||||
}
|
||||
id, _ := c.ParamsInt("id")
|
||||
if err := models.MarcarNotifLeida(uint(id)); err != nil {
|
||||
return c.Status(500).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
return c.JSON(fiber.Map{"ok": true})
|
||||
}
|
||||
|
||||
func PortalMarcarTodasLeidas(c *fiber.Ctx) error {
|
||||
u := middlewares.PortalUserFromLocals(c)
|
||||
if u == nil {
|
||||
return c.Status(401).JSON(fiber.Map{"error": "no autenticado"})
|
||||
}
|
||||
if err := models.MarcarTodasLeidas("portal_user", u.ID); err != nil {
|
||||
return c.Status(500).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
return c.JSON(fiber.Map{"ok": true})
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/sujit-baniya/fiber-boilerplate/app"
|
||||
"github.com/sujit-baniya/fiber-boilerplate/pkg/models"
|
||||
"github.com/sujit-baniya/fiber-boilerplate/pkg/services"
|
||||
)
|
||||
|
||||
func PortalUsuariosIndex(c *fiber.Ctx) error {
|
||||
@@ -30,14 +29,12 @@ func LoadPortalUsuarios(c *fiber.Ctx) error {
|
||||
return c.Status(500).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
clientes, _, _ := models.GetAllClientes(200, 0, "")
|
||||
portalRoles, _ := models.GetPortalRoles()
|
||||
return c.JSON(fiber.Map{
|
||||
"items": items,
|
||||
"total": total,
|
||||
"totalPages": int(math.Ceil(float64(total) / float64(limit))),
|
||||
"page": page,
|
||||
"clientes": clientes,
|
||||
"portalRoles": portalRoles,
|
||||
"items": items,
|
||||
"total": total,
|
||||
"totalPages": int(math.Ceil(float64(total) / float64(limit))),
|
||||
"page": page,
|
||||
"clientes": clientes,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -47,7 +44,7 @@ func CreatePortalUsuario(c *fiber.Ctx) error {
|
||||
Email string `json:"email"`
|
||||
Password string `json:"password"`
|
||||
ClienteID *uint `json:"cliente_id"`
|
||||
RoleID *uint `json:"role_id"`
|
||||
Rol string `json:"rol"`
|
||||
Notas string `json:"notas"`
|
||||
}
|
||||
var req Req
|
||||
@@ -61,23 +58,15 @@ func CreatePortalUsuario(c *fiber.Ctx) error {
|
||||
if err != nil {
|
||||
return c.Status(500).JSON(fiber.Map{"error": "Error al hashear contraseña"})
|
||||
}
|
||||
// Derivar Rol del tipo de rol seleccionado
|
||||
rol := "cliente"
|
||||
if req.RoleID != nil {
|
||||
var role models.Roles
|
||||
if err := app.Http.Database.DB.First(&role, *req.RoleID).Error; err == nil {
|
||||
if role.EsPortalPartner {
|
||||
rol = "partner"
|
||||
}
|
||||
}
|
||||
if req.Rol == "" {
|
||||
req.Rol = "cliente"
|
||||
}
|
||||
u := &models.PortalUser{
|
||||
Nombre: req.Nombre,
|
||||
Email: strings.ToLower(strings.TrimSpace(req.Email)),
|
||||
Password: hashed,
|
||||
ClienteID: req.ClienteID,
|
||||
RoleID: req.RoleID,
|
||||
Rol: rol,
|
||||
Rol: req.Rol,
|
||||
Activo: true,
|
||||
Notas: req.Notas,
|
||||
}
|
||||
@@ -98,7 +87,7 @@ func UpdatePortalUsuario(c *fiber.Ctx) error {
|
||||
Email string `json:"email"`
|
||||
Password string `json:"password"`
|
||||
ClienteID *uint `json:"cliente_id"`
|
||||
RoleID *uint `json:"role_id"`
|
||||
Rol string `json:"rol"`
|
||||
Activo bool `json:"activo"`
|
||||
Notas string `json:"notas"`
|
||||
}
|
||||
@@ -106,22 +95,11 @@ func UpdatePortalUsuario(c *fiber.Ctx) error {
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
return c.Status(400).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
// Derivar Rol del tipo de rol seleccionado
|
||||
rol := "cliente"
|
||||
if req.RoleID != nil {
|
||||
var role models.Roles
|
||||
if err := app.Http.Database.DB.First(&role, *req.RoleID).Error; err == nil {
|
||||
if role.EsPortalPartner {
|
||||
rol = "partner"
|
||||
}
|
||||
}
|
||||
}
|
||||
u := &models.PortalUser{
|
||||
Nombre: req.Nombre,
|
||||
Email: strings.ToLower(strings.TrimSpace(req.Email)),
|
||||
ClienteID: req.ClienteID,
|
||||
RoleID: req.RoleID,
|
||||
Rol: rol,
|
||||
Rol: req.Rol,
|
||||
Activo: req.Activo,
|
||||
Notas: req.Notas,
|
||||
}
|
||||
@@ -131,7 +109,7 @@ func UpdatePortalUsuario(c *fiber.Ctx) error {
|
||||
}
|
||||
// Actualizar password solo si se envió
|
||||
if strings.TrimSpace(req.Password) != "" {
|
||||
hashed, err := app.Http.Hash.Create(req.Password)
|
||||
hashed, err := app.Http.Hash.Create(req.Password)
|
||||
if err == nil {
|
||||
_ = models.UpdatePortalUserPassword(uint(id), hashed)
|
||||
}
|
||||
@@ -173,29 +151,3 @@ func RemovePortalAcceso(c *fiber.Ctx) error {
|
||||
}
|
||||
return c.JSON(fiber.Map{"ok": true})
|
||||
}
|
||||
|
||||
// SendPortalCredentials envía las credenciales de acceso al portal al correo del usuario.
|
||||
func SendPortalCredentials(c *fiber.Ctx) error {
|
||||
id, err := strconv.ParseUint(c.Params("id"), 10, 32)
|
||||
if err != nil {
|
||||
return c.Status(400).JSON(fiber.Map{"error": "ID inválido"})
|
||||
}
|
||||
u, err := models.GetPortalUserByID(uint(id))
|
||||
if err != nil {
|
||||
return c.Status(404).JSON(fiber.Map{"error": "Usuario no encontrado"})
|
||||
}
|
||||
|
||||
type Req struct {
|
||||
Password string `json:"password"`
|
||||
}
|
||||
var req Req
|
||||
_ = c.BodyParser(&req)
|
||||
|
||||
password := req.Password
|
||||
if password == "" {
|
||||
password = "(la contraseña que configuraste)"
|
||||
}
|
||||
|
||||
services.SendPortalCredentialsEmail(u.Email, u.Nombre, password)
|
||||
return c.JSON(fiber.Map{"ok": true, "message": "Correo enviado a " + u.Email})
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ import (
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/sujit-baniya/fiber-boilerplate/pkg/models"
|
||||
"github.com/sujit-baniya/fiber-boilerplate/pkg/services"
|
||||
)
|
||||
|
||||
// ─── Admin pages ──────────────────────────────────────────────────────────────
|
||||
@@ -67,7 +66,7 @@ func LoadProyectos(c *fiber.Ctx) error {
|
||||
|
||||
func CreateProyecto(c *fiber.Ctx) error {
|
||||
type Req struct {
|
||||
ClienteID string `json:"cliente_id"`
|
||||
ClienteID uint `json:"cliente_id"`
|
||||
Nombre string `json:"nombre"`
|
||||
Slug string `json:"slug"`
|
||||
Descripcion string `json:"descripcion"`
|
||||
@@ -78,8 +77,7 @@ func CreateProyecto(c *fiber.Ctx) error {
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
return c.Status(400).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
clienteID, _ := strconv.ParseUint(req.ClienteID, 10, 64)
|
||||
if clienteID == 0 || strings.TrimSpace(req.Nombre) == "" {
|
||||
if req.ClienteID == 0 || strings.TrimSpace(req.Nombre) == "" {
|
||||
return c.Status(400).JSON(fiber.Map{"error": "cliente y nombre son requeridos"})
|
||||
}
|
||||
if req.Slug == "" {
|
||||
@@ -92,7 +90,7 @@ func CreateProyecto(c *fiber.Ctx) error {
|
||||
req.Estado = "activo"
|
||||
}
|
||||
p := &models.Proyecto{
|
||||
ClienteID: uint(clienteID),
|
||||
ClienteID: req.ClienteID,
|
||||
Nombre: req.Nombre,
|
||||
Slug: req.Slug,
|
||||
Descripcion: req.Descripcion,
|
||||
@@ -111,7 +109,7 @@ func UpdateProyecto(c *fiber.Ctx) error {
|
||||
return c.Status(400).JSON(fiber.Map{"error": "ID inválido"})
|
||||
}
|
||||
type Req struct {
|
||||
ClienteID string `json:"cliente_id"`
|
||||
ClienteID uint `json:"cliente_id"`
|
||||
Nombre string `json:"nombre"`
|
||||
Slug string `json:"slug"`
|
||||
Descripcion string `json:"descripcion"`
|
||||
@@ -123,9 +121,8 @@ func UpdateProyecto(c *fiber.Ctx) error {
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
return c.Status(400).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
updateClienteID, _ := strconv.ParseUint(req.ClienteID, 10, 64)
|
||||
p := &models.Proyecto{
|
||||
ClienteID: uint(updateClienteID),
|
||||
ClienteID: req.ClienteID,
|
||||
Nombre: req.Nombre,
|
||||
Slug: req.Slug,
|
||||
Descripcion: req.Descripcion,
|
||||
@@ -459,11 +456,6 @@ func AdminResponderTicket(c *fiber.Ctx) error {
|
||||
}
|
||||
// Cambiar estado a en_progreso si estaba abierto
|
||||
_ = models.UpdateTicketEstado(uint(ticketID), "en_progreso")
|
||||
// Notificar al portal user
|
||||
ticket, _ := models.GetTicketByID(uint(ticketID))
|
||||
if ticket != nil {
|
||||
go services.DispatchTicketRespuestaAdmin(ticket, req.Contenido, ticket.Proyecto.Nombre)
|
||||
}
|
||||
return c.Status(201).JSON(msg)
|
||||
}
|
||||
|
||||
@@ -506,21 +498,3 @@ func mimeFromHeader(fh *multipart.FileHeader) string {
|
||||
}
|
||||
return ct
|
||||
}
|
||||
|
||||
// ─── Tickets global (admin) ───────────────────────────────────────────────────
|
||||
|
||||
func TicketsAdminIndex(c *fiber.Ctx) error {
|
||||
return c.Render("tickets_admin", fiber.Map{
|
||||
"user": c.Locals("user"),
|
||||
"modules": c.Locals("userModules"),
|
||||
}, "layouts/main")
|
||||
}
|
||||
|
||||
func GetAllTicketsAdmin(c *fiber.Ctx) error {
|
||||
estado := c.Query("estado", "todos")
|
||||
items, err := models.GetAllTickets(estado)
|
||||
if err != nil {
|
||||
return c.Status(500).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
return c.JSON(items)
|
||||
}
|
||||
|
||||
@@ -7,13 +7,11 @@ import (
|
||||
)
|
||||
|
||||
// PortalAuth protege las rutas del portal de clientes.
|
||||
// Si no hay sesión válida, destruye la sesión obsoleta y redirige a /portal/login.
|
||||
// Si no hay sesión activa, redirige a /portal/login.
|
||||
func PortalAuth() fiber.Handler {
|
||||
return func(c *fiber.Ctx) error {
|
||||
user, err := auth.PortalUser(c)
|
||||
if err != nil || user == nil {
|
||||
// Limpiar sesión inválida/obsoleta para romper posibles redirect loops
|
||||
_ = auth.DestroyPortalSession(c)
|
||||
return c.Redirect("/portal/login")
|
||||
}
|
||||
c.Locals("portalUser", user)
|
||||
|
||||
@@ -29,9 +29,4 @@ func PortalRoutes(app fiber.Router) {
|
||||
// Descargas
|
||||
portal.Get("/entregables/:id/download", controllers.PortalDownloadEntregable)
|
||||
portal.Get("/facturas/:id/download", controllers.PortalDownloadFactura)
|
||||
|
||||
// Notificaciones in-app del portal user
|
||||
portal.Get("/mis-notifs", controllers.PortalGetNotifs)
|
||||
portal.Put("/mis-notifs/:id/leida", controllers.PortalMarcarNotifLeida)
|
||||
portal.Post("/mis-notifs/marcar-todas", controllers.PortalMarcarTodasLeidas)
|
||||
}
|
||||
|
||||
@@ -154,26 +154,6 @@ func UserRoutes(app fiber.Router) {
|
||||
protected.Get("/cloudflare/zones/:zone_id/ssl", controllers.GetCloudflareSSL)
|
||||
protected.Get("/cloudflare/zones/:zone_id/firewall", controllers.GetCloudflareFirewall)
|
||||
|
||||
// ─── USITE Shield ─────────────────────────────────────────────────────────
|
||||
protected.Get("/shield", middlewares.MenuMiddleware, controllers.ShieldIndex)
|
||||
protected.Get("/loadshield", controllers.LoadShieldConfig)
|
||||
protected.Post("/saveshield", controllers.SaveShieldConfig)
|
||||
protected.Get("/shield/health", controllers.ShieldHealth)
|
||||
protected.Get("/shield/extension-version", controllers.ShieldExtensionVersion)
|
||||
protected.Get("/shield/logs", controllers.ShieldLogs)
|
||||
protected.Get("/shield/logs/stats", controllers.ShieldLogStats)
|
||||
protected.Get("/shield/review-requests", controllers.ShieldReviewRequests)
|
||||
protected.Put("/shield/review-requests/:id/approve", controllers.ShieldApproveRequest)
|
||||
protected.Put("/shield/review-requests/:id/reject", controllers.ShieldRejectRequest)
|
||||
protected.Get("/shield/whitelist", controllers.ShieldWhitelist)
|
||||
protected.Post("/shield/whitelist", controllers.ShieldAddWhitelist)
|
||||
protected.Delete("/shield/whitelist/:domain", controllers.ShieldDeleteWhitelist)
|
||||
protected.Get("/shield/blacklist", controllers.ShieldBlacklist)
|
||||
protected.Post("/shield/blacklist", controllers.ShieldAddBlacklist)
|
||||
protected.Delete("/shield/blacklist/:domain", controllers.ShieldDeleteBlacklist)
|
||||
protected.Get("/shield/reputation/:domain", controllers.ShieldReputation)
|
||||
protected.Put("/shield/reputation/:domain/score", controllers.ShieldAdjustScore)
|
||||
|
||||
// ─── Pasarelas de Pago ────────────────────────────────────────────
|
||||
protected.Get("/pasarelas-pago", middlewares.MenuMiddleware, controllers.PasarelasPage)
|
||||
// Bold
|
||||
@@ -267,22 +247,6 @@ func UserRoutes(app fiber.Router) {
|
||||
protected.Put("/proyectos/:id/tickets/:ticketID/estado", controllers.UpdateTicketEstadoAdmin)
|
||||
protected.Post("/proyectos/:id/tickets/:ticketID/mensaje", controllers.AdminResponderTicket)
|
||||
|
||||
// Tickets global (todos los proyectos)
|
||||
protected.Get("/tickets", middlewares.MenuMiddleware, controllers.TicketsAdminIndex)
|
||||
protected.Get("/tickets/data", controllers.GetAllTicketsAdmin)
|
||||
protected.Put("/tickets/:ticketID/estado", controllers.UpdateTicketEstadoAdmin)
|
||||
protected.Post("/tickets/:ticketID/mensaje", controllers.AdminResponderTicket)
|
||||
|
||||
// Configuración de notificaciones
|
||||
protected.Get("/notif-config", middlewares.MenuMiddleware, controllers.NotifConfigIndex)
|
||||
protected.Get("/notif-config/data", controllers.GetNotifConfigs)
|
||||
protected.Post("/notif-config", controllers.SaveNotifConfig)
|
||||
|
||||
// Notificaciones in-app del admin (bell)
|
||||
protected.Get("/mis-notifs", controllers.GetMisNotifs)
|
||||
protected.Put("/mis-notifs/:id/leida", controllers.MarcarNotifLeida)
|
||||
protected.Post("/mis-notifs/marcar-todas", controllers.MarcarTodasLeidas)
|
||||
|
||||
protected.Get("/portal-usuarios", middlewares.MenuMiddleware, controllers.PortalUsuariosIndex)
|
||||
protected.Get("/loadportalusuarios", controllers.LoadPortalUsuarios)
|
||||
protected.Post("/portal-usuarios", controllers.CreatePortalUsuario)
|
||||
@@ -290,7 +254,6 @@ func UserRoutes(app fiber.Router) {
|
||||
protected.Delete("/portal-usuarios/:id", controllers.DeletePortalUsuario)
|
||||
protected.Post("/portal-usuarios/:id/acceso", controllers.AddPortalAcceso)
|
||||
protected.Delete("/portal-usuarios/:id/acceso/:clienteID", controllers.RemovePortalAcceso)
|
||||
protected.Post("/portal-usuarios/:id/send-credentials", controllers.SendPortalCredentials)
|
||||
|
||||
protected.Get("/facturas", middlewares.MenuMiddleware, controllers.FacturasIndex)
|
||||
protected.Get("/loadfacturas", controllers.LoadFacturas)
|
||||
|
||||
Reference in New Issue
Block a user