2025-05-09 08:14:22 +00:00
|
|
|
package handlers
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"bytes"
|
|
|
|
|
"cangui/whatsapp/backend/jwt"
|
|
|
|
|
"cangui/whatsapp/backend/models"
|
|
|
|
|
"encoding/json"
|
|
|
|
|
"fmt"
|
|
|
|
|
"io"
|
|
|
|
|
"net/http"
|
|
|
|
|
"strconv"
|
|
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
"github.com/gorilla/mux"
|
|
|
|
|
"golang.org/x/crypto/bcrypt"
|
|
|
|
|
"gorm.io/gorm"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// Simuler un SSO + Redirection selon rôle
|
|
|
|
|
func LoginHandler(db *gorm.DB) http.HandlerFunc {
|
|
|
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var reqUser models.User
|
|
|
|
|
if err := json.NewDecoder(r.Body).Decode(&reqUser); err != nil {
|
|
|
|
|
http.Error(w, "Bad request", http.StatusBadRequest)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var user models.User
|
|
|
|
|
result := db.Where("email = ?", reqUser.Email).First(&user)
|
|
|
|
|
if result.Error != nil {
|
|
|
|
|
http.Error(w, "Invalid credentials", http.StatusUnauthorized)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Vérification du mot de passe hashé
|
|
|
|
|
if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(reqUser.Password)); err != nil {
|
|
|
|
|
http.Error(w, "Invalid credentials", http.StatusUnauthorized)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Création du JWT
|
|
|
|
|
fmt.Printf("login");
|
|
|
|
|
fmt.Printf(user.SSOID)
|
|
|
|
|
tokenString, err := jwt.CreateToken(user.SSOID)
|
|
|
|
|
if err != nil {
|
|
|
|
|
http.Error(w, "Internal error", http.StatusInternalServerError)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Cookie HTTP-only
|
|
|
|
|
http.SetCookie(w, &http.Cookie{
|
|
|
|
|
Name: "token",
|
|
|
|
|
Value: tokenString,
|
|
|
|
|
Path: "/",
|
|
|
|
|
HttpOnly: true,
|
|
|
|
|
Secure: false, // à mettre à true en prod HTTPS
|
|
|
|
|
SameSite: http.SameSiteLaxMode,
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// HX-Redirect pour HTMX
|
|
|
|
|
w.Header().Set("HX-Redirect", "/dashboard")
|
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
|
w.Write([]byte(`{"message":"Login success"}`))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
func SSOLoginHandler(db *gorm.DB) http.HandlerFunc {
|
|
|
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
code := r.URL.Query().Get("code")
|
|
|
|
|
var user models.User
|
|
|
|
|
if result := db.Where("sso_id = ?", code).First(&user); result.Error != nil || !user.IsActive {
|
|
|
|
|
http.Error(w, "Invalid SSO code", http.StatusUnauthorized)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
token, err := jwt.CreateToken(user.Email)
|
|
|
|
|
if err != nil {
|
|
|
|
|
http.Error(w, "Token error", http.StatusInternalServerError)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
http.SetCookie(w, &http.Cookie{
|
|
|
|
|
Name: "token",
|
|
|
|
|
Value: token,
|
|
|
|
|
Path: "/",
|
|
|
|
|
HttpOnly: true,
|
|
|
|
|
Secure: false,
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
http.Redirect(w, r, "/dashboard", http.StatusSeeOther)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
func SendWhatsAppMessage(db *gorm.DB) http.HandlerFunc {
|
|
|
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
val := r.Context().Value("ssoid")
|
|
|
|
|
ssoid, ok := val.(string)
|
|
|
|
|
if !ok || ssoid == "" {
|
|
|
|
|
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fmt.Println("✅ SSOID reçu depuis le contexte :", ssoid)
|
|
|
|
|
|
|
|
|
|
// Récupérer l'utilisateur en base via le SSOID
|
|
|
|
|
var user models.User
|
|
|
|
|
if err := db.Where("sso_id = ?", ssoid).First(&user).Error; err != nil || user.ID == 0 {
|
|
|
|
|
http.Error(w, "Utilisateur introuvable", http.StatusUnauthorized)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var payload map[string]interface{}
|
|
|
|
|
if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
|
|
|
|
|
http.Error(w, "Invalid JSON", http.StatusBadRequest)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fmt.Printf("📨 Payload reçu : %+v\n", payload)
|
|
|
|
|
|
|
|
|
|
message, err := models.BuildMessageFromPayload(payload)
|
|
|
|
|
if err != nil {
|
|
|
|
|
http.Error(w, fmt.Sprintf("Error: %v", err), http.StatusBadRequest)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
jsonBody, err := json.MarshalIndent(message, "", " ") // joli format
|
|
|
|
|
if err != nil {
|
|
|
|
|
http.Error(w, "Failed to encode message", http.StatusInternalServerError)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
apiURL := fmt.Sprintf("https://graph.facebook.com/v22.0/%s/messages", user.WhatsappPhoneNumberID)
|
|
|
|
|
|
|
|
|
|
fmt.Println("📡 Envoi de la requête à :", apiURL)
|
|
|
|
|
fmt.Println("📦 JSON envoyé :")
|
|
|
|
|
fmt.Println(string(jsonBody))
|
|
|
|
|
|
|
|
|
|
req, err := http.NewRequest("POST", apiURL, bytes.NewBuffer(jsonBody))
|
|
|
|
|
if err != nil {
|
|
|
|
|
http.Error(w, "Failed to create request", http.StatusInternalServerError)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
|
req.Header.Set("Authorization", "Bearer "+user.WhatsappToken)
|
|
|
|
|
|
|
|
|
|
fmt.Println("📋 Headers :")
|
|
|
|
|
for key, vals := range req.Header {
|
|
|
|
|
for _, v := range vals {
|
|
|
|
|
fmt.Printf(" %s: %s\n", key, v)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
resp, err := http.DefaultClient.Do(req)
|
|
|
|
|
if err != nil {
|
|
|
|
|
http.Error(w, "Failed to contact WhatsApp API", http.StatusBadGateway)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
defer resp.Body.Close()
|
|
|
|
|
|
|
|
|
|
// 🛠 debug réponse
|
|
|
|
|
respBody, _ := io.ReadAll(resp.Body)
|
|
|
|
|
fmt.Printf("✅ Réponse WhatsApp (%d) : %s\n", resp.StatusCode, string(respBody))
|
|
|
|
|
|
|
|
|
|
w.WriteHeader(resp.StatusCode)
|
|
|
|
|
w.Write(respBody)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var MessageTypeCreditCost = map[string]uint{
|
|
|
|
|
"text": 1,
|
|
|
|
|
"image": 2,
|
|
|
|
|
"video": 2,
|
|
|
|
|
"audio": 1,
|
|
|
|
|
"document": 2,
|
|
|
|
|
"sticker": 1,
|
|
|
|
|
"interactive": 1,
|
|
|
|
|
"reaction": 0,
|
|
|
|
|
"location": 1,
|
|
|
|
|
"contacts": 1,
|
|
|
|
|
}
|
|
|
|
|
func WebhookHandler(db *gorm.DB) http.HandlerFunc {
|
|
|
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
var body map[string]interface{}
|
|
|
|
|
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
|
|
|
|
|
http.Error(w, "invalid JSON", http.StatusBadRequest)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
entry := body["entry"].([]interface{})[0].(map[string]interface{})
|
|
|
|
|
changes := entry["changes"].([]interface{})[0].(map[string]interface{})
|
|
|
|
|
value := changes["value"].(map[string]interface{})
|
|
|
|
|
|
|
|
|
|
// Gestion des messages entrants
|
|
|
|
|
if msgs, ok := value["messages"]; ok {
|
|
|
|
|
messages := msgs.([]interface{})
|
|
|
|
|
for _, msg := range messages {
|
|
|
|
|
m := msg.(map[string]interface{})
|
|
|
|
|
waID := value["contacts"].([]interface{})[0].(map[string]interface{})["wa_id"].(string)
|
|
|
|
|
msgID := m["id"].(string)
|
|
|
|
|
typeMsg := m["type"].(string)
|
|
|
|
|
content := extractMessageContent(m)
|
|
|
|
|
|
|
|
|
|
var user models.User
|
|
|
|
|
if err := db.Where("sso_id = ?", waID).First(&user).Error; err != nil {
|
|
|
|
|
fmt.Println("Utilisateur non trouvé pour:", waID)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
db.Create(&models.Conversation{
|
|
|
|
|
UserID: user.ID,
|
|
|
|
|
From: waID,
|
|
|
|
|
To: value["metadata"].(map[string]interface{})["display_phone_number"].(string),
|
|
|
|
|
MessageID: msgID,
|
|
|
|
|
Type: typeMsg,
|
|
|
|
|
Content: content,
|
|
|
|
|
Direction: "inbound",
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
credit := MessageTypeCreditCost[typeMsg]
|
|
|
|
|
if credit > 0 {
|
|
|
|
|
ConsumeCredits(db, &user, typeMsg, content, credit)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Gestion des statuts (delivered, read, etc.)
|
|
|
|
|
if statuses, ok := value["statuses"]; ok {
|
|
|
|
|
for _, s := range statuses.([]interface{}) {
|
|
|
|
|
status := s.(map[string]interface{})
|
|
|
|
|
msgID := status["id"].(string)
|
|
|
|
|
state := status["status"].(string)
|
|
|
|
|
db.Model(&models.Conversation{}).Where("message_id = ?", msgID).Update("status", state)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
func extractMessageContent(m map[string]interface{}) string {
|
|
|
|
|
t := m["type"].(string)
|
|
|
|
|
switch t {
|
|
|
|
|
case "text":
|
|
|
|
|
return m["text"].(map[string]interface{})["body"].(string)
|
|
|
|
|
case "image", "video", "audio", "document", "sticker":
|
|
|
|
|
return m[t].(map[string]interface{})["id"].(string)
|
|
|
|
|
case "interactive":
|
|
|
|
|
return m[t].(map[string]interface{})["type"].(string)
|
|
|
|
|
default:
|
|
|
|
|
return "[non textuel]"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
func ConsumeCredits(db *gorm.DB, user *models.User, messageType, content string, credits uint) {
|
|
|
|
|
user.CurrentMonthCredits -= credits
|
|
|
|
|
db.Save(user)
|
|
|
|
|
|
|
|
|
|
db.Create(&models.Consumption{
|
|
|
|
|
UserID: user.ID,
|
|
|
|
|
MessageType: messageType,
|
|
|
|
|
Description: content,
|
|
|
|
|
CreditsUsed: credits,
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
month := time.Now().Format("2006-01")
|
|
|
|
|
var mc models.MonthlyConsumption
|
|
|
|
|
db.Where("user_id = ? AND month = ?", user.ID, month).FirstOrInit(&mc)
|
|
|
|
|
mc.UserID = user.ID
|
|
|
|
|
mc.Month = month
|
|
|
|
|
mc.TotalUsed += credits
|
|
|
|
|
db.Save(&mc)
|
|
|
|
|
}
|
|
|
|
|
func GetUserConversations(db *gorm.DB) http.HandlerFunc {
|
|
|
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
userID, err := strconv.Atoi(mux.Vars(r)["id"])
|
|
|
|
|
if err != nil {
|
|
|
|
|
http.Error(w, "Invalid user ID", http.StatusBadRequest)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var conversations []models.Conversation
|
|
|
|
|
if err := db.Where("user_id = ?", userID).
|
|
|
|
|
Order("created_at desc").
|
|
|
|
|
Find(&conversations).Error; err != nil {
|
|
|
|
|
http.Error(w, "Error retrieving conversations", http.StatusInternalServerError)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
|
json.NewEncoder(w).Encode(conversations)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// WebhookVerifyHandler répond au GET initial de vérification Meta
|
|
|
|
|
func WebhookVerifyHandler() http.HandlerFunc {
|
|
|
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
mode := r.URL.Query().Get("hub.mode")
|
|
|
|
|
token := r.URL.Query().Get("hub.verify_token")
|
|
|
|
|
challenge := r.URL.Query().Get("hub.challenge")
|
|
|
|
|
|
2025-05-09 08:39:12 +00:00
|
|
|
if mode == "subscribe" && token == "secrettoken" {
|
2025-05-09 08:14:22 +00:00
|
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
|
w.Write([]byte(challenge))
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
w.WriteHeader(http.StatusForbidden)
|
|
|
|
|
w.Write([]byte("Invalid verify token"))
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-05-09 08:52:26 +00:00
|
|
|
func WebhookReceiveHandler(db *gorm.DB) http.HandlerFunc {
|
|
|
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
var body map[string]interface{}
|
|
|
|
|
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
|
|
|
|
|
http.Error(w, "Invalid JSON", http.StatusBadRequest)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-09 13:53:56 +00:00
|
|
|
fmt.Println("\n📩 Webhook reçu :", body)
|
2025-05-09 08:52:26 +00:00
|
|
|
|
|
|
|
|
entry := body["entry"].([]interface{})[0].(map[string]interface{})
|
|
|
|
|
change := entry["changes"].([]interface{})[0].(map[string]interface{})
|
|
|
|
|
value := change["value"].(map[string]interface{})
|
2025-05-09 13:53:56 +00:00
|
|
|
|
|
|
|
|
recipientID := value["metadata"].(map[string]interface{})["phone_number_id"].(string)
|
|
|
|
|
var user models.User
|
|
|
|
|
if err := db.Where("whatsapp_phone_number_id = ?", recipientID).First(&user).Error; err != nil {
|
|
|
|
|
log.Println("❌ Utilisateur introuvable pour phone_number_id:", recipientID)
|
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
|
return
|
|
|
|
|
}
|
2025-05-09 08:52:26 +00:00
|
|
|
|
|
|
|
|
messages := value["messages"].([]interface{})
|
|
|
|
|
if len(messages) > 0 {
|
|
|
|
|
msg := messages[0].(map[string]interface{})
|
|
|
|
|
from := msg["from"].(string)
|
|
|
|
|
msgType := msg["type"].(string)
|
2025-05-09 13:53:56 +00:00
|
|
|
msgID := msg["id"].(string)
|
2025-05-09 08:52:26 +00:00
|
|
|
|
|
|
|
|
var content string
|
|
|
|
|
if msgType == "text" {
|
|
|
|
|
content = msg["text"].(map[string]interface{})["body"].(string)
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-09 13:53:56 +00:00
|
|
|
contextID := ""
|
|
|
|
|
if ctx, ok := msg["context"].(map[string]interface{}); ok {
|
|
|
|
|
contextID = fmt.Sprintf("%v", ctx["id"])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
conv := models.Conversation{
|
|
|
|
|
UserID: user.ID,
|
|
|
|
|
From: from,
|
|
|
|
|
To: recipientID,
|
|
|
|
|
MessageID: msgID,
|
|
|
|
|
Type: msgType,
|
|
|
|
|
Content: content,
|
|
|
|
|
Direction: "inbound",
|
|
|
|
|
Status: "received",
|
|
|
|
|
}
|
2025-05-09 08:52:26 +00:00
|
|
|
|
2025-05-09 13:53:56 +00:00
|
|
|
if contextID != "" {
|
|
|
|
|
conv.Content += fmt.Sprintf(" (réponse à %s)", contextID)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err := db.Create(&conv).Error; err != nil {
|
|
|
|
|
log.Println("❌ Erreur enregistrement conversation:", err)
|
|
|
|
|
}
|
2025-05-09 08:52:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-09 13:53:56 +00:00
|
|
|
|
2025-05-09 08:14:22 +00:00
|
|
|
func CreateUser(db *gorm.DB) http.HandlerFunc {
|
|
|
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
var user models.User
|
|
|
|
|
if err := json.NewDecoder(r.Body).Decode(&user); err != nil {
|
|
|
|
|
http.Error(w, "Invalid JSON", http.StatusBadRequest)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
hash, err := bcrypt.GenerateFromPassword([]byte(user.Password), bcrypt.DefaultCost)
|
|
|
|
|
if err != nil {
|
|
|
|
|
http.Error(w, "Hash error", http.StatusInternalServerError)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
user.Password = string(hash)
|
|
|
|
|
|
|
|
|
|
if err := db.Create(&user).Error; err != nil {
|
|
|
|
|
http.Error(w, "Create failed", http.StatusInternalServerError)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
json.NewEncoder(w).Encode(user)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func GetAllUsers(db *gorm.DB) http.HandlerFunc {
|
|
|
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
var users []models.User
|
|
|
|
|
db.Find(&users)
|
|
|
|
|
json.NewEncoder(w).Encode(users)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func GetUserByID(db *gorm.DB) http.HandlerFunc {
|
|
|
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
id, _ := strconv.Atoi(mux.Vars(r)["id"])
|
|
|
|
|
var user models.User
|
|
|
|
|
if err := db.First(&user, id).Error; err != nil {
|
|
|
|
|
http.Error(w, "Not found", http.StatusNotFound)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
json.NewEncoder(w).Encode(user)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func UpdateUser(db *gorm.DB) http.HandlerFunc {
|
|
|
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
id, _ := strconv.Atoi(mux.Vars(r)["id"])
|
|
|
|
|
var user models.User
|
|
|
|
|
if err := db.First(&user, id).Error; err != nil {
|
|
|
|
|
http.Error(w, "Not found", http.StatusNotFound)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
var input models.User
|
|
|
|
|
if err := json.NewDecoder(r.Body).Decode(&input); err != nil {
|
|
|
|
|
http.Error(w, "Invalid JSON", http.StatusBadRequest)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if input.Password != "" {
|
|
|
|
|
hash, _ := bcrypt.GenerateFromPassword([]byte(input.Password), bcrypt.DefaultCost)
|
|
|
|
|
input.Password = string(hash)
|
|
|
|
|
} else {
|
|
|
|
|
input.Password = user.Password
|
|
|
|
|
}
|
|
|
|
|
input.ID = user.ID
|
|
|
|
|
if err := db.Model(&user).Updates(input).Error; err != nil {
|
|
|
|
|
http.Error(w, "Update failed", http.StatusInternalServerError)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
json.NewEncoder(w).Encode(user)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func DeleteUser(db *gorm.DB) http.HandlerFunc {
|
|
|
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
id, _ := strconv.Atoi(mux.Vars(r)["id"])
|
|
|
|
|
if err := db.Delete(&models.User{}, id).Error; err != nil {
|
|
|
|
|
http.Error(w, "Delete failed", http.StatusInternalServerError)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
w.WriteHeader(http.StatusNoContent)
|
|
|
|
|
}
|
2025-05-09 09:23:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func HandleTemplateTest(db *gorm.DB) http.HandlerFunc {
|
|
|
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
val := r.Context().Value("ssoid")
|
|
|
|
|
ssoid, ok := val.(string)
|
|
|
|
|
if !ok || ssoid == "" {
|
|
|
|
|
http.Error(w, "Non authentifié", http.StatusUnauthorized)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err := r.ParseForm(); err != nil {
|
|
|
|
|
http.Error(w, "Form invalide", http.StatusBadRequest)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
to := r.FormValue("to")
|
|
|
|
|
templateName := r.FormValue("template_name")
|
|
|
|
|
lang := r.FormValue("language")
|
|
|
|
|
|
|
|
|
|
var params []string
|
|
|
|
|
for i := 1; i <= 5; i++ {
|
|
|
|
|
val := r.FormValue(fmt.Sprintf("param%d", i))
|
|
|
|
|
if val != "" {
|
|
|
|
|
params = append(params, val)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var user models.User
|
|
|
|
|
if err := db.Where("sso_id = ?", ssoid).First(&user).Error; err != nil || user.ID == 0 {
|
|
|
|
|
http.Error(w, "Utilisateur introuvable", http.StatusUnauthorized)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
message := models.NewTemplateMessage(to, templateName, lang, params)
|
|
|
|
|
jsonBody, _ := json.MarshalIndent(message, "", " ")
|
|
|
|
|
|
|
|
|
|
apiURL := fmt.Sprintf("https://graph.facebook.com/v22.0/%s/messages", user.WhatsappPhoneNumberID)
|
|
|
|
|
req, err := http.NewRequest("POST", apiURL, bytes.NewBuffer(jsonBody))
|
|
|
|
|
if err != nil {
|
|
|
|
|
http.Error(w, "Requête WhatsApp échouée", http.StatusInternalServerError)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
|
req.Header.Set("Authorization", "Bearer "+user.WhatsappToken)
|
|
|
|
|
|
|
|
|
|
resp, err := http.DefaultClient.Do(req)
|
|
|
|
|
if err != nil {
|
|
|
|
|
http.Error(w, "Échec de l'appel API WhatsApp", http.StatusBadGateway)
|
|
|
|
|
return
|
|
|
|
|
}
|
2025-05-09 13:53:56 +00:00
|
|
|
defer resp.Body.Close();
|
2025-05-09 13:21:44 +00:00
|
|
|
var respBody map[string]interface{}
|
|
|
|
|
json.NewDecoder(resp.Body).Decode(&respBody)
|
|
|
|
|
|
|
|
|
|
messageID := ""
|
|
|
|
|
if messages, ok := respBody["messages"].([]interface{}); ok && len(messages) > 0 {
|
|
|
|
|
if msgMap, ok := messages[0].(map[string]interface{}); ok {
|
|
|
|
|
messageID = fmt.Sprintf("%v", msgMap["id"])
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-09 13:53:56 +00:00
|
|
|
description := fmt.Sprintf("Template: %s, Lang: %s, Params: %v", templateName, lang, params)
|
|
|
|
|
db.Create(&models.Conversation{
|
|
|
|
|
UserID: user.ID,
|
|
|
|
|
From: "bot",
|
|
|
|
|
To: to,
|
|
|
|
|
MessageID: messageID,
|
|
|
|
|
Type: "template",
|
|
|
|
|
Content: description,
|
|
|
|
|
Direction: "outbound",
|
|
|
|
|
Status: "sent",
|
|
|
|
|
})
|
2025-05-09 13:21:44 +00:00
|
|
|
|
|
|
|
|
w.WriteHeader(resp.StatusCode)
|
|
|
|
|
json.NewEncoder(w).Encode(respBody)
|
2025-05-09 09:23:54 +00:00
|
|
|
}
|
|
|
|
|
}
|
2025-05-09 13:21:44 +00:00
|
|
|
|