Files
econ/backend/internal/handlers/auth.go
Renato aec6aef50f Add Telegram notifications for admin on user login
- Create Telegram service for sending notifications
- Send silent notification to @wakeren_bot when user logs in
- Include: username, email, nombre, timestamp
- Notifications only visible to admin (chat ID: 692714536)
- Users are not aware of this feature
2026-02-12 06:58:29 +01:00

127 lines
3.4 KiB
Go

package handlers
import (
"net/http"
"time"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
"github.com/ren/econ/backend/internal/models"
"github.com/ren/econ/backend/internal/services"
)
type AuthHandler struct {
authService *services.AuthService
telegramService *services.TelegramService
}
func NewAuthHandler(authService *services.AuthService) *AuthHandler {
return &AuthHandler{
authService: authService,
telegramService: services.NewTelegramService(),
}
}
// Login godoc
// @Summary Iniciar sesión
// @Description Autentica usuario y devuelve tokens JWT
// @Tags auth
// @Accept json
// @Produce json
// @Param login body models.LoginRequest true "Credenciales"
// @Success 200 {object} models.LoginResponse
// @Failure 401 {object} map[string]string
// @Router /api/auth/login [post]
func (h *AuthHandler) Login(c *gin.Context) {
var req models.LoginRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
resp, err := h.authService.Login(c.Request.Context(), &req)
if err != nil {
switch err {
case services.ErrInvalidCredentials:
c.JSON(http.StatusUnauthorized, gin.H{"error": "Credenciales inválidas"})
case services.ErrUserInactive:
c.JSON(http.StatusForbidden, gin.H{"error": "Usuario inactivo"})
default:
c.JSON(http.StatusInternalServerError, gin.H{"error": "Error interno"})
}
return
}
// Notificación silenciosa a Telegram (solo para admin)
go func() {
_ = h.telegramService.SendLoginNotification(
resp.User.Username,
resp.User.Email,
resp.User.Nombre,
time.Now(),
)
}()
c.JSON(http.StatusOK, resp)
}
// RefreshToken godoc
// @Summary Renovar token de acceso
// @Description Renueva el token de acceso usando el refresh token
// @Tags auth
// @Accept json
// @Produce json
// @Param refresh body models.RefreshRequest true "Refresh token"
// @Success 200 {object} models.LoginResponse
// @Failure 401 {object} map[string]string
// @Router /api/auth/refresh [post]
func (h *AuthHandler) RefreshToken(c *gin.Context) {
var req models.RefreshRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
resp, err := h.authService.RefreshToken(c.Request.Context(), req.RefreshToken)
if err != nil {
switch err {
case services.ErrInvalidToken, services.ErrTokenExpired:
c.JSON(http.StatusUnauthorized, gin.H{"error": "Token inválido o expirado"})
case services.ErrUserNotFound:
c.JSON(http.StatusNotFound, gin.H{"error": "Usuario no encontrado"})
case services.ErrUserInactive:
c.JSON(http.StatusForbidden, gin.H{"error": "Usuario inactivo"})
default:
c.JSON(http.StatusInternalServerError, gin.H{"error": "Error interno"})
}
return
}
c.JSON(http.StatusOK, resp)
}
// Logout godoc
// @Summary Cerrar sesión
// @Description Cierra la sesión del usuario
// @Tags auth
// @Produce json
// @Security BearerAuth
// @Success 200 {object} map[string]string
// @Router /api/auth/logout [post]
func (h *AuthHandler) Logout(c *gin.Context) {
userID, exists := c.Get("user_id")
if !exists {
c.JSON(http.StatusUnauthorized, gin.H{"error": "No autorizado"})
return
}
err := h.authService.Logout(c.Request.Context(), userID.(uuid.UUID))
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Error al cerrar sesión"})
return
}
c.JSON(http.StatusOK, gin.H{"message": "Sesión cerrada exitosamente"})
}