385 lines
11 KiB
Go
385 lines
11 KiB
Go
package handlers
|
|
|
|
import (
|
|
"gestion/db"
|
|
"gestion/models"
|
|
"gestion/services"
|
|
"log"
|
|
"net/http"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
func TelegramWebhook(c *gin.Context) {
|
|
// Vérification du secret webhook
|
|
secret := c.GetHeader("X-Telegram-Bot-Api-Secret-Token")
|
|
if services.TelegramBot == nil || !services.TelegramBot.ValidateWebhookSecret(secret) {
|
|
c.Status(http.StatusUnauthorized)
|
|
return
|
|
}
|
|
|
|
var update models.TgUpdate
|
|
if err := c.ShouldBindJSON(&update); err != nil {
|
|
c.Status(http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
if update.Message == nil {
|
|
c.Status(http.StatusOK)
|
|
return
|
|
}
|
|
|
|
text := strings.TrimSpace(update.Message.Text)
|
|
chatID := update.Message.Chat.ID
|
|
|
|
// Commande /start <token> — liaison de compte
|
|
if token, ok := strings.CutPrefix(text, "/start "); ok {
|
|
token = strings.TrimSpace(token)
|
|
handleLinkAccount(c, token, chatID)
|
|
return
|
|
}
|
|
|
|
// Commande /start sans token — message d'accueil
|
|
if text == "/start" {
|
|
if services.TelegramBot != nil {
|
|
services.TelegramBot.SendMessage(chatID,
|
|
"👋 <b>Bienvenue !</b>\n\nPour lier votre compte, générez un token depuis l'application et envoyez <code>/start <token></code>.")
|
|
}
|
|
}
|
|
|
|
c.Status(http.StatusOK)
|
|
}
|
|
|
|
func handleLinkAccount(c *gin.Context, token string, chatID int64) {
|
|
if token == "" {
|
|
if services.TelegramBot != nil {
|
|
services.TelegramBot.SendMessage(chatID, "❌ Token manquant. Générez un nouveau token depuis l'application.")
|
|
}
|
|
c.Status(http.StatusOK)
|
|
return
|
|
}
|
|
|
|
database := c.MustGet("database").(*db.Database)
|
|
|
|
username, role, err := db.ValidateAndConsumeLinkToken(token)
|
|
if err != nil {
|
|
log.Printf("⚠️ [TELEGRAM_LINK] Token invalide: %v", err)
|
|
if services.TelegramBot != nil {
|
|
services.TelegramBot.SendMessage(chatID, "❌ Token invalide ou expiré. Générez un nouveau token depuis l'application.")
|
|
}
|
|
c.Status(http.StatusOK)
|
|
return
|
|
}
|
|
|
|
// Enregistrer le chat_id selon le rôle
|
|
var saveErr error
|
|
switch role {
|
|
case "client":
|
|
saveErr = database.SaveClientTelegramChatID(username, chatID)
|
|
default:
|
|
saveErr = database.SaveUserTelegramChatID(username, chatID)
|
|
}
|
|
|
|
if saveErr != nil {
|
|
log.Printf("❌ [TELEGRAM_LINK] Erreur sauvegarde chat_id pour %s (%s): %v", username, role, saveErr)
|
|
if services.TelegramBot != nil {
|
|
services.TelegramBot.SendMessage(chatID, "❌ Une erreur est survenue. Réessayez.")
|
|
}
|
|
c.Status(http.StatusOK)
|
|
return
|
|
}
|
|
|
|
log.Printf("✅ [TELEGRAM_LINK] Compte %s (%s) lié au chat_id %d", username, role, chatID)
|
|
|
|
if services.LBTelegram != nil && services.LBTelegram.IsConfigured() {
|
|
if err := services.LBTelegram.EnrollUser(chatID, username, role); err != nil {
|
|
log.Printf("⚠️ [LB] enrollment échoué pour %s: %v", username, err)
|
|
// fallback: confirmation directe via le bot principal
|
|
if services.TelegramBot != nil {
|
|
services.TelegramBot.SendMessage(chatID,
|
|
"✅ <b>Compte lié avec succès !</b>\n\nVous recevrez désormais vos notifications ici.")
|
|
}
|
|
}
|
|
} else if services.TelegramBot != nil {
|
|
services.TelegramBot.SendMessage(chatID,
|
|
"✅ <b>Compte lié avec succès !</b>\n\nVous recevrez désormais vos notifications ici.")
|
|
}
|
|
|
|
c.Status(http.StatusOK)
|
|
}
|
|
|
|
func GenerateClientLinkToken(c *gin.Context) {
|
|
username := c.GetString("username")
|
|
if username == "" {
|
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "Non authentifié"})
|
|
return
|
|
}
|
|
|
|
if services.TelegramBot == nil || !services.TelegramBot.IsConfigured() {
|
|
c.JSON(http.StatusServiceUnavailable, gin.H{"error": "Service Telegram non disponible"})
|
|
return
|
|
}
|
|
|
|
token, err := db.GenerateLinkToken(username, "client")
|
|
if err != nil {
|
|
log.Printf("❌ [TELEGRAM] Erreur génération token pour %s: %v", username, err)
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Erreur génération token"})
|
|
return
|
|
}
|
|
|
|
botUsername := services.TelegramBot.BotUsername
|
|
if services.LBTelegram != nil && services.LBTelegram.IsConfigured() && services.LBTelegram.Bot1Username != "" {
|
|
botUsername = services.LBTelegram.Bot1Username
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"token": token,
|
|
"link_url": "https://t.me/" + botUsername + "?start=" + token,
|
|
"message": "/start " + token,
|
|
"expires_in": 600,
|
|
})
|
|
}
|
|
|
|
func GenerateLivreurLinkToken(c *gin.Context) {
|
|
username := c.GetString("username")
|
|
if username == "" {
|
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "Non authentifié"})
|
|
return
|
|
}
|
|
|
|
if services.TelegramBot == nil || !services.TelegramBot.IsConfigured() {
|
|
c.JSON(http.StatusServiceUnavailable, gin.H{"error": "Service Telegram non disponible"})
|
|
return
|
|
}
|
|
|
|
token, err := db.GenerateLinkToken(username, "livreur")
|
|
if err != nil {
|
|
log.Printf("❌ [TELEGRAM] Erreur génération token pour livreur %s: %v", username, err)
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Erreur génération token"})
|
|
return
|
|
}
|
|
|
|
botUsername := services.TelegramBot.BotUsername
|
|
if services.LBTelegram != nil && services.LBTelegram.IsConfigured() && services.LBTelegram.Bot1Username != "" {
|
|
botUsername = services.LBTelegram.Bot1Username
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"token": token,
|
|
"link_url": "https://t.me/" + botUsername + "?start=" + token,
|
|
"message": "/start " + token,
|
|
"expires_in": 600,
|
|
})
|
|
}
|
|
|
|
func GenerateAdminLinkToken(c *gin.Context) {
|
|
username := c.GetString("username")
|
|
if username == "" {
|
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "Non authentifié"})
|
|
return
|
|
}
|
|
|
|
if services.TelegramBot == nil || !services.TelegramBot.IsConfigured() {
|
|
c.JSON(http.StatusServiceUnavailable, gin.H{"error": "Service Telegram non disponible"})
|
|
return
|
|
}
|
|
|
|
// Récupérer le rôle réel depuis la DB
|
|
database := c.MustGet("database").(*db.Database)
|
|
user, err := database.GetUserByUsername(username)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Utilisateur introuvable"})
|
|
return
|
|
}
|
|
|
|
token, err := db.GenerateLinkToken(username, user.Role)
|
|
if err != nil {
|
|
log.Printf("❌ [TELEGRAM] Erreur génération token pour admin %s: %v", username, err)
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Erreur génération token"})
|
|
return
|
|
}
|
|
|
|
botUsername := services.TelegramBot.BotUsername
|
|
if services.LBTelegram != nil && services.LBTelegram.IsConfigured() && services.LBTelegram.Bot1Username != "" {
|
|
botUsername = services.LBTelegram.Bot1Username
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"token": token,
|
|
"link_url": "https://t.me/" + botUsername + "?start=" + token,
|
|
"message": "/start " + token,
|
|
"expires_in": 600,
|
|
})
|
|
}
|
|
|
|
// GET /api/v1/telegram/status
|
|
func GetClientTelegramStatus(c *gin.Context) {
|
|
username := c.GetString("username")
|
|
if username == "" {
|
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "Non authentifié"})
|
|
return
|
|
}
|
|
|
|
database := c.MustGet("database").(*db.Database)
|
|
_, linked, err := database.GetClientTelegramChatID(username)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Erreur vérification"})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"linked": linked,
|
|
"enabled": services.TelegramBot != nil && services.TelegramBot.IsConfigured(),
|
|
})
|
|
}
|
|
|
|
// GET /api/v1/livreur/telegram/status
|
|
func GetLivreurTelegramStatus(c *gin.Context) {
|
|
username := c.GetString("username")
|
|
if username == "" {
|
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "Non authentifié"})
|
|
return
|
|
}
|
|
|
|
database := c.MustGet("database").(*db.Database)
|
|
_, linked, err := database.GetUserTelegramChatID(username)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Erreur vérification"})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"linked": linked,
|
|
"enabled": services.TelegramBot != nil && services.TelegramBot.IsConfigured(),
|
|
})
|
|
}
|
|
|
|
// ============================================
|
|
// DÉLIAISON TELEGRAM
|
|
// ============================================
|
|
|
|
// DELETE /api/v1/telegram/unlink
|
|
func UnlinkClientTelegram(c *gin.Context) {
|
|
username := c.GetString("username")
|
|
if username == "" {
|
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "Non authentifié"})
|
|
return
|
|
}
|
|
|
|
clientID := c.GetInt("client_id")
|
|
database := c.MustGet("database").(*db.Database)
|
|
|
|
if err := database.DeleteClientTelegramChatID(username); err != nil {
|
|
log.Printf("❌ [TELEGRAM_UNLINK] Erreur pour %s: %v", username, err)
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Erreur déliaison"})
|
|
return
|
|
}
|
|
|
|
// Désactiver la 2FA si Telegram est délié
|
|
if clientID > 0 {
|
|
_ = database.SetClientTwoFAEnabled(clientID, false)
|
|
}
|
|
|
|
log.Printf("✅ [TELEGRAM_UNLINK] Compte client %s délié", username)
|
|
c.JSON(http.StatusOK, gin.H{"success": true})
|
|
}
|
|
|
|
// DELETE /api/v1/livreur/telegram/unlink
|
|
func UnlinkLivreurTelegram(c *gin.Context) {
|
|
username := c.GetString("username")
|
|
if username == "" {
|
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "Non authentifié"})
|
|
return
|
|
}
|
|
|
|
database := c.MustGet("database").(*db.Database)
|
|
if err := database.DeleteUserTelegramChatID(username); err != nil {
|
|
log.Printf("❌ [TELEGRAM_UNLINK] Erreur pour %s: %v", username, err)
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Erreur déliaison"})
|
|
return
|
|
}
|
|
|
|
log.Printf("✅ [TELEGRAM_UNLINK] Compte livreur %s délié", username)
|
|
c.JSON(http.StatusOK, gin.H{"success": true})
|
|
}
|
|
|
|
// DELETE /api/v2/admin/protected/telegram/unlink
|
|
func UnlinkAdminTelegram(c *gin.Context) {
|
|
username := c.GetString("username")
|
|
if username == "" {
|
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "Non authentifié"})
|
|
return
|
|
}
|
|
|
|
database := c.MustGet("database").(*db.Database)
|
|
if err := database.DeleteUserTelegramChatID(username); err != nil {
|
|
log.Printf("❌ [TELEGRAM_UNLINK] Erreur pour %s: %v", username, err)
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Erreur déliaison"})
|
|
return
|
|
}
|
|
|
|
log.Printf("✅ [TELEGRAM_UNLINK] Compte admin %s délié", username)
|
|
c.JSON(http.StatusOK, gin.H{"success": true})
|
|
}
|
|
|
|
// ============================================
|
|
// LIAISON INTERNE (appelée par LBTelegram)
|
|
// ============================================
|
|
|
|
// POST /api/internal/telegram/link
|
|
// Appelée par LBTelegram quand Bot1 reçoit /start TOKEN.
|
|
// Valide le token, enregistre le chat_id, déclenche l'enrollment.
|
|
func InternalTelegramLink(c *gin.Context) {
|
|
secret := c.GetHeader("X-Internal-Secret")
|
|
expected := os.Getenv("BACKEND_LINK_SECRET")
|
|
if expected == "" || secret != expected {
|
|
c.Status(http.StatusUnauthorized)
|
|
return
|
|
}
|
|
|
|
var req struct {
|
|
ChatID int64 `json:"chat_id" binding:"required"`
|
|
Token string `json:"token" binding:"required"`
|
|
}
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
database := c.MustGet("database").(*db.Database)
|
|
|
|
username, role, err := db.ValidateAndConsumeLinkToken(req.Token)
|
|
if err != nil {
|
|
log.Printf("⚠️ [TELEGRAM_LINK_INTERNAL] Token invalide: %v", err)
|
|
c.Status(http.StatusUnauthorized)
|
|
return
|
|
}
|
|
|
|
var saveErr error
|
|
switch role {
|
|
case "client":
|
|
saveErr = database.SaveClientTelegramChatID(username, req.ChatID)
|
|
default:
|
|
saveErr = database.SaveUserTelegramChatID(username, req.ChatID)
|
|
}
|
|
if saveErr != nil {
|
|
log.Printf("❌ [TELEGRAM_LINK_INTERNAL] Erreur sauvegarde pour %s: %v", username, saveErr)
|
|
c.Status(http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
log.Printf("✅ [TELEGRAM_LINK_INTERNAL] Compte %s (%s) lié via Bot1 (chat_id %d)", username, role, req.ChatID)
|
|
|
|
if services.LBTelegram != nil && services.LBTelegram.IsConfigured() {
|
|
if err := services.LBTelegram.EnrollUser(req.ChatID, username, role); err != nil {
|
|
log.Printf("⚠️ [LB] enrollment échoué pour %s: %v", username, err)
|
|
c.Status(http.StatusInternalServerError)
|
|
return
|
|
}
|
|
}
|
|
|
|
c.Status(http.StatusOK)
|
|
}
|