Skip to content

Commit f16f265

Browse files
committed
Vérification vérification tel avec Whatsapp
1 parent 02e3482 commit f16f265

File tree

10 files changed

+453
-86
lines changed

10 files changed

+453
-86
lines changed
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
-- +goose Up
2+
-- +goose StatementBegin
3+
ALTER TABLE newf
4+
ADD COLUMN phone_number_verified BOOLEAN NOT NULL DEFAULT FALSE,
5+
ADD COLUMN phone_number_verification_code VARCHAR(6),
6+
ADD COLUMN phone_number_verification_code_expiration TIMESTAMP DEFAULT (CURRENT_TIMESTAMP + INTERVAL '10 minutes');
7+
-- +goose StatementEnd
8+
9+
-- +goose Down
10+
-- +goose StatementBegin
11+
ALTER TABLE newf
12+
DROP COLUMN phone_number_verified,
13+
DROP COLUMN phone_number_verification_code,
14+
DROP COLUMN phone_number_verification_code_expiration;
15+
-- +goose StatementEnd

handlers/user/user_handlers.go

Lines changed: 243 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,17 @@ import (
1616

1717
// UserHandler handles user profile and related actions.
1818
type UserHandler struct {
19-
DB *sql.DB
20-
NotifService *services.NotificationService // Inject if needed for notification handlers
19+
DB *sql.DB
20+
NotifService *services.NotificationService // Inject if needed for notification handlers
21+
PhoneVerificationService *services.PhoneVerificationService // Service for phone verification
2122
}
2223

2324
// NewUserHandler creates a new UserHandler.
24-
func NewUserHandler(db *sql.DB, notifService *services.NotificationService) *UserHandler {
25+
func NewUserHandler(db *sql.DB, notifService *services.NotificationService, phoneVerificationService *services.PhoneVerificationService) *UserHandler {
2526
return &UserHandler{
26-
DB: db,
27-
NotifService: notifService,
27+
DB: db,
28+
NotifService: notifService,
29+
PhoneVerificationService: phoneVerificationService,
2830
}
2931
}
3032

@@ -172,8 +174,55 @@ func (h *UserHandler) UpdateNewf(c *fiber.Ctx) error {
172174
if req.LastName != "" {
173175
updateFields["last_name"] = req.LastName
174176
}
177+
// cas spécial: si le numéro de téléphone change, on vérifie le numéro avec Whatsapp
175178
if req.PhoneNumber != "" {
176-
// Add validation for phone number format if needed
179+
// il FAUT le préfixe international pour WA
180+
if !strings.HasPrefix(req.PhoneNumber, "+") {
181+
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid phone number"})
182+
}
183+
184+
var currentPhoneNumber sql.NullString
185+
phoneQuery := `SELECT phone_number FROM newf WHERE email = $1`
186+
err := h.DB.QueryRow(phoneQuery, email).Scan(&currentPhoneNumber)
187+
if err != nil {
188+
utils.LogMessage(utils.LevelError, "Failed to fetch current phone number")
189+
utils.LogLineKeyValue(utils.LevelError, "Error", err)
190+
} else if !currentPhoneNumber.Valid || currentPhoneNumber.String != req.PhoneNumber {
191+
utils.LogMessage(utils.LevelInfo, "Phone number changed, sending verification code")
192+
193+
// faut la langue pour wassap
194+
languageCode, langErr := utils.GetLanguageCode(h.DB, email)
195+
if langErr != nil {
196+
utils.LogMessage(utils.LevelError, "Failed to get language code")
197+
utils.LogLineKeyValue(utils.LevelError, "Error", langErr)
198+
utils.LogFooter()
199+
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Failed to get language code"})
200+
}
201+
202+
verificationCode, err := h.PhoneVerificationService.SendPhoneVerificationCode(req.PhoneNumber, languageCode)
203+
204+
if err != nil {
205+
utils.LogMessage(utils.LevelError, "Failed to send phone verification code")
206+
utils.LogLineKeyValue(utils.LevelError, "Error", err)
207+
} else {
208+
expirationTime := time.Now().Add(10 * time.Minute)
209+
updateVerifQuery := `
210+
UPDATE newf
211+
SET phone_number_verification_code = $1,
212+
phone_number_verification_code_expiration = $2,
213+
phone_number_verified = false
214+
WHERE email = $3
215+
`
216+
_, err := h.DB.Exec(updateVerifQuery, verificationCode, expirationTime, email)
217+
if err != nil {
218+
utils.LogMessage(utils.LevelError, "Failed to update verification code in database")
219+
utils.LogLineKeyValue(utils.LevelError, "Error", err)
220+
}
221+
}
222+
}
223+
224+
// on update tout de suite (une fois qu'on est sûr que le code est parti),
225+
// mais il ne faut PAS afficher ce numéro dans l'appli car pas encore vérifié
177226
updateFields["phone_number"] = req.PhoneNumber
178227
}
179228
if req.GraduationYear != 0 {
@@ -269,6 +318,34 @@ func (h *UserHandler) UpdateNewf(c *fiber.Ctx) error {
269318
utils.LogLineKeyValue(utils.LevelInfo, "Fields Updated", updateFields) // Log only keys for brevity/privacy
270319
utils.LogFooter()
271320

321+
// Vérifier si une vérification de téléphone est nécessaire
322+
var phoneVerificationRequired bool
323+
var phoneNumber string
324+
if req.PhoneNumber != "" {
325+
// Vérifier si le numéro a changé et n'est pas encore vérifié
326+
verifQuery := `
327+
SELECT phone_number, phone_number_verified
328+
FROM newf
329+
WHERE email = $1
330+
`
331+
err := h.DB.QueryRow(verifQuery, email).Scan(&phoneNumber, &phoneVerificationRequired)
332+
if err != nil {
333+
utils.LogMessage(utils.LevelError, "Failed to check phone verification status")
334+
} else {
335+
phoneVerificationRequired = !phoneVerificationRequired
336+
}
337+
}
338+
339+
// Retourner une réponse différente si une vérification est nécessaire
340+
if phoneVerificationRequired && req.PhoneNumber != "" {
341+
return c.Status(fiber.StatusOK).JSON(fiber.Map{
342+
"message": "Profile updated successfully. Phone number verification required.",
343+
"phone_verification_required": true,
344+
"phone_number": req.PhoneNumber,
345+
"expires_in": "10 minutes",
346+
})
347+
}
348+
272349
return c.SendStatus(fiber.StatusOK)
273350
}
274351

@@ -587,3 +664,163 @@ func (h *UserHandler) SendNotification(c *fiber.Ctx) error {
587664
// It needs to be implemented fully to handle parsing, target resolution, etc.
588665
return h.NotifService.SendNotification(c)
589666
}
667+
668+
// whatsapp
669+
func (h *UserHandler) VerifyPhoneNumber(c *fiber.Ctx) error {
670+
email := c.Locals("email").(string)
671+
var req struct {
672+
VerificationCode string `json:"verification_code"`
673+
}
674+
675+
utils.LogHeader("📱 Verifying Phone Number (avec whatsapp)")
676+
utils.LogLineKeyValue(utils.LevelInfo, "User", email)
677+
678+
if err := c.BodyParser(&req); err != nil {
679+
utils.LogMessage(utils.LevelError, "Failed to parse request body")
680+
utils.LogLineKeyValue(utils.LevelError, "Error", err)
681+
utils.LogFooter()
682+
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request format"})
683+
}
684+
685+
if req.VerificationCode == "" {
686+
utils.LogMessage(utils.LevelWarn, "Missing verification code")
687+
utils.LogFooter()
688+
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Verification code is required"})
689+
}
690+
691+
var storedCode, expirationTime string
692+
var phoneNumber string
693+
query := `
694+
SELECT phone_number_verification_code,
695+
phone_number_verification_code_expiration,
696+
phone_number
697+
FROM newf
698+
WHERE email = $1
699+
`
700+
701+
err := h.DB.QueryRow(query, email).Scan(&storedCode, &expirationTime, &phoneNumber)
702+
if err != nil {
703+
if err == sql.ErrNoRows {
704+
utils.LogMessage(utils.LevelWarn, "User not found???")
705+
utils.LogFooter()
706+
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "User not found"})
707+
}
708+
utils.LogMessage(utils.LevelError, "Failed to fetch verification data")
709+
utils.LogLineKeyValue(utils.LevelError, "Error", err)
710+
utils.LogFooter()
711+
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Failed to fetch verification data"})
712+
}
713+
714+
if !h.PhoneVerificationService.ValidateVerificationCode(storedCode, req.VerificationCode, expirationTime) {
715+
utils.LogMessage(utils.LevelWarn, "Invalid or expired verification code")
716+
utils.LogFooter()
717+
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid or expired verification code"})
718+
}
719+
720+
// marquer comme verified
721+
updateQuery := `
722+
UPDATE newf
723+
SET phone_number_verified = true,
724+
phone_number_verification_code = NULL,
725+
phone_number_verification_code_expiration = NULL
726+
WHERE email = $1
727+
`
728+
729+
_, err = h.DB.Exec(updateQuery, email)
730+
if err != nil {
731+
utils.LogMessage(utils.LevelError, "Failed to update phone verification status")
732+
utils.LogLineKeyValue(utils.LevelError, "Error", err)
733+
utils.LogFooter()
734+
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Failed to verify phone number"})
735+
}
736+
737+
utils.LogMessage(utils.LevelInfo, "Phone number verified successfully")
738+
utils.LogLineKeyValue(utils.LevelInfo, "Phone", phoneNumber)
739+
utils.LogFooter()
740+
741+
return c.JSON(fiber.Map{
742+
"message": "Phone number verified successfully",
743+
"phone_number": phoneNumber,
744+
"verified": true,
745+
})
746+
}
747+
748+
// ResendPhoneVerificationCode renvoie un nouveau code de vérification
749+
func (h *UserHandler) ResendPhoneVerificationCode(c *fiber.Ctx) error {
750+
email := c.Locals("email").(string)
751+
752+
utils.LogHeader("📱 Resend Phone Verification Code")
753+
utils.LogLineKeyValue(utils.LevelInfo, "User", email)
754+
755+
// Récupérer le numéro de téléphone et la langue de l'utilisateur
756+
languageCode, langErr := utils.GetLanguageCode(h.DB, email)
757+
if langErr != nil {
758+
utils.LogMessage(utils.LevelError, "Failed to get language code")
759+
utils.LogLineKeyValue(utils.LevelError, "Error", langErr)
760+
utils.LogFooter()
761+
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Failed to get language code"})
762+
}
763+
764+
var phoneNumber string
765+
query := `
766+
SELECT n.phone_number
767+
FROM newf n
768+
WHERE n.email = $1
769+
`
770+
771+
err := h.DB.QueryRow(query, email).Scan(&phoneNumber)
772+
if err != nil {
773+
if err == sql.ErrNoRows {
774+
utils.LogMessage(utils.LevelWarn, "User not found")
775+
utils.LogFooter()
776+
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "User not found"})
777+
}
778+
utils.LogMessage(utils.LevelError, "Failed to fetch user data")
779+
utils.LogLineKeyValue(utils.LevelError, "Error", err)
780+
utils.LogFooter()
781+
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Failed to fetch user data"})
782+
}
783+
784+
if phoneNumber == "" {
785+
utils.LogMessage(utils.LevelWarn, "No phone number found for user")
786+
utils.LogFooter()
787+
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "No phone number found"})
788+
}
789+
790+
// Envoyer un nouveau code de vérification
791+
verificationCode, err := h.PhoneVerificationService.SendPhoneVerificationCode(phoneNumber, languageCode)
792+
if err != nil {
793+
utils.LogMessage(utils.LevelError, "Failed to send verification code")
794+
utils.LogLineKeyValue(utils.LevelError, "Error", err)
795+
utils.LogFooter()
796+
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Failed to send verification code"})
797+
}
798+
799+
// Mettre à jour le code et l'expiration dans la base de données
800+
expirationTime := time.Now().Add(10 * time.Minute)
801+
updateQuery := `
802+
UPDATE newf
803+
SET phone_number_verification_code = $1,
804+
phone_number_verification_code_expiration = $2,
805+
phone_number_verified = false
806+
WHERE email = $3
807+
`
808+
809+
_, err = h.DB.Exec(updateQuery, verificationCode, expirationTime, email)
810+
if err != nil {
811+
utils.LogMessage(utils.LevelError, "Failed to update verification code in database")
812+
utils.LogLineKeyValue(utils.LevelError, "Error", err)
813+
utils.LogFooter()
814+
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Failed to save verification code"})
815+
}
816+
817+
utils.LogMessage(utils.LevelInfo, "Verification code resent successfully")
818+
utils.LogLineKeyValue(utils.LevelInfo, "Phone", phoneNumber)
819+
utils.LogFooter()
820+
821+
return c.JSON(fiber.Map{
822+
"message": "Verification code sent successfully",
823+
"phone_number": phoneNumber,
824+
"expires_in": "10 minutes",
825+
})
826+
}

handlers/whatsapp.go

Lines changed: 0 additions & 46 deletions
This file was deleted.

main.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,8 @@ func main() {
105105
log.Fatalf("💥 Failed to create WhatsApp Service: %v", err)
106106
}
107107

108-
// pour tester temporairemetn
109-
whatsappHandler := handlers.NewWhatsAppHandler(whatsappService)
108+
// Initialize Phone Verification Service
109+
phoneVerificationService := services.NewPhoneVerificationService(whatsappService)
110110

111111
appScheduler := scheduler.NewScheduler(restHandler)
112112
appScheduler.StartAll()
@@ -164,7 +164,7 @@ func main() {
164164

165165
// API Group --- NEW ROUTES
166166
routes.SetupAuthRoutes(app, db, jwtSecret, notificationService, emailService, discordService)
167-
routes.SetupUserRoutes(app, db, notificationService)
167+
routes.SetupUserRoutes(app, db, notificationService, phoneVerificationService)
168168
routes.SetupTraqRoutes(app, db)
169169
routes.SetupFileRoutes(app, db, r2Service)
170170
routes.SetupRestaurantRoutes(app, restHandler)
@@ -177,7 +177,6 @@ func main() {
177177
routes.SetupEventRoutes(app, eventHandler)
178178
routes.SetupReservationRoutes(app, db)
179179
routes.SetupBassineRoutes(app, db)
180-
routes.SetupWhatsAppRoutes(app, whatsappHandler)
181180

182181
app.Get("/health", func(c *fiber.Ctx) error {
183182
return c.SendString("OK")

0 commit comments

Comments
 (0)