@@ -16,15 +16,17 @@ import (
1616
1717// UserHandler handles user profile and related actions.
1818type 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+ }
0 commit comments