Files
omnex/control-plane/api/internal/profile/handler.go
T
Xor290 e19fa4e82c
ci-web / test (push) Canceled after 0s
ci-api / test (push) Successful in 26m5s
chore: build
2026-08-18 22:06:23 +02:00

229 lines
6.5 KiB
Go

package profile
import (
"net/http"
"github.com/gin-gonic/gin"
"github.com/omnex/control-plane/api/internal/alerts"
"github.com/omnex/control-plane/api/internal/auth"
)
type Handler struct {
store Store
}
func NewHandler(store Store) *Handler {
return &Handler{store: store}
}
type newUsername struct {
Username string `json:"username" binding:"required,min=2,max=120"`
}
type newPassword struct {
Password string `json:"password" binding:"required,min=8"`
}
type newTelegram struct {
Telegram string `json:"telegram" binding:"required,min=3,max=64"`
}
type alertSettingsRequest struct {
DiscordWebhookURL string `json:"discord_webhook_url" binding:"omitempty,max=500,url"`
TelegramBotToken string `json:"telegram_bot_token" binding:"omitempty,max=200"`
TelegramChatID string `json:"telegram_chat_id" binding:"omitempty,max=64"`
}
type alertSettingsResponse struct {
DiscordWebhookURL string `json:"discord_webhook_url"`
TelegramBotToken string `json:"telegram_bot_token"`
TelegramChatID string `json:"telegram_chat_id"`
}
func toAlertSettingsResponse(user auth.User) alertSettingsResponse {
deref := func(s *string) string {
if s == nil {
return ""
}
return *s
}
return alertSettingsResponse{
DiscordWebhookURL: deref(user.AlertDiscordWebhookURL),
TelegramBotToken: deref(user.AlertTelegramBotToken),
TelegramChatID: deref(user.AlertTelegramChatID),
}
}
func (h *Handler) UpdateUsernameById(c *gin.Context) {
var u newUsername
if err := c.ShouldBindJSON(&u); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "requête invalide"})
return
}
p := auth.PrincipalFrom(c)
if p == nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "non authentifié"})
return
}
user, err := h.store.UpdateUsername(p.UserID, u.Username)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "erreur serveur"})
return
}
c.JSON(http.StatusOK, user)
}
func (h *Handler) UpdatePasswordById(c *gin.Context) {
var u newPassword
if err := c.ShouldBindJSON(&u); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "requête invalide"})
return
}
p := auth.PrincipalFrom(c)
if p == nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "non authentifié"})
return
}
hash, err := auth.HashPassword(u.Password)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "erreur serveur"})
return
}
user, err := h.store.UpdatePassword(p.UserID, hash)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "requête invalide"})
return
}
c.JSON(http.StatusOK, user)
}
func (h *Handler) GetTelegramById(c *gin.Context) {
p := auth.PrincipalFrom(c)
if p == nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "non authentifié"})
return
}
user, err := h.store.GetTelegram(p.UserID)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "requête invalide"})
return
}
c.JSON(http.StatusOK, gin.H{"telegram": user.Telegram})
}
func (h *Handler) SetTelegramById(c *gin.Context) {
var t newTelegram
if err := c.ShouldBindJSON(&t); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "requête invalide"})
return
}
p := auth.PrincipalFrom(c)
if p == nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "non authentifié"})
return
}
user, err := h.store.SetTelegram(p.UserID, t.Telegram)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "erreur serveur"})
return
}
c.JSON(http.StatusOK, gin.H{"telegram": user.Telegram})
}
// GetAlerts : réglages d'alerte monitoring (webhook Discord / bot Telegram)
// de l'admin authentifié — chacun configure les siens, rien de partagé.
func (h *Handler) GetAlerts(c *gin.Context) {
p := auth.PrincipalFrom(c)
if p == nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "non authentifié"})
return
}
user, err := h.store.GetAlertSettings(p.UserID)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "requête invalide"})
return
}
c.JSON(http.StatusOK, toAlertSettingsResponse(user))
}
// SetAlerts : enregistre les réglages d'alerte de l'admin authentifié. Un
// champ vide désactive le canal correspondant.
func (h *Handler) SetAlerts(c *gin.Context) {
var req alertSettingsRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "requête invalide"})
return
}
p := auth.PrincipalFrom(c)
if p == nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "non authentifié"})
return
}
user, err := h.store.SetAlertSettings(p.UserID, req.DiscordWebhookURL, req.TelegramBotToken, req.TelegramChatID)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "erreur serveur"})
return
}
c.JSON(http.StatusOK, toAlertSettingsResponse(user))
}
type testChannelResult struct {
OK bool `json:"ok"`
Error string `json:"error,omitempty"`
}
type testAlertsResponse struct {
Discord *testChannelResult `json:"discord,omitempty"`
Telegram *testChannelResult `json:"telegram,omitempty"`
}
const testAlertMessage = "🔔 Test Omnex — si vous recevez ce message, ce canal d'alerte est bien configuré."
// TestAlerts : envoie un message de test aux canaux fournis dans la requête,
// sans les persister — permet de vérifier la config (webhook Discord valide,
// bot Telegram démarré, chat_id correct...) avant d'enregistrer, ou de
// vérifier des réglages déjà enregistrés sans les ressaisir.
func (h *Handler) TestAlerts(c *gin.Context) {
var req alertSettingsRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "requête invalide"})
return
}
p := auth.PrincipalFrom(c)
if p == nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "non authentifié"})
return
}
if req.DiscordWebhookURL == "" && (req.TelegramBotToken == "" || req.TelegramChatID == "") {
c.JSON(http.StatusBadRequest, gin.H{"error": "aucun canal renseigné"})
return
}
var res testAlertsResponse
if req.DiscordWebhookURL != "" {
n := &alerts.DiscordNotifier{WebhookURL: req.DiscordWebhookURL}
if err := n.Notify(c.Request.Context(), testAlertMessage); err != nil {
res.Discord = &testChannelResult{Error: err.Error()}
} else {
res.Discord = &testChannelResult{OK: true}
}
}
if req.TelegramBotToken != "" && req.TelegramChatID != "" {
n := &alerts.TelegramNotifier{BotToken: req.TelegramBotToken, ChatID: req.TelegramChatID}
if err := n.Notify(c.Request.Context(), testAlertMessage); err != nil {
res.Telegram = &testChannelResult{Error: err.Error()}
} else {
res.Telegram = &testChannelResult{OK: true}
}
}
c.JSON(http.StatusOK, res)
}