@@ -6,13 +6,120 @@ import (
66 "github.com/Darklabel91/API_Names/models"
77 Metaphone "github.com/Darklabel91/metaphone-br"
88 "github.com/gin-gonic/gin"
9+ "github.com/golang-jwt/jwt/v5"
10+ "golang.org/x/crypto/bcrypt"
911 "net/http"
12+ "os"
1013 "sort"
14+ "strconv"
1115 "strings"
16+ "time"
1217)
1318
1419const levenshtein = 0.8
1520
21+ //Signup a new user to the database
22+ func Signup (c * gin.Context ) {
23+ //get email/pass off req body
24+ var body struct {
25+ Email string
26+ Password string
27+ }
28+
29+ if c .Bind (& body ) != nil {
30+ c .JSON (http .StatusBadRequest , gin.H {"Message" : "Failed to read body" })
31+ return
32+ }
33+
34+ //hash the password
35+ hash , err := bcrypt .GenerateFromPassword ([]byte (body .Password ), 10 )
36+ if err != nil {
37+ c .JSON (http .StatusBadRequest , gin.H {"Message" : "Failed to hash password" })
38+ return
39+ }
40+
41+ //create the user
42+ user := models.User {Email : body .Email , Password : string (hash )}
43+ result := database .Db .Create (& user )
44+
45+ if result .Error != nil {
46+ c .JSON (http .StatusBadRequest , gin.H {"Message" : "Email already registered" })
47+ return
48+ }
49+
50+ //respond
51+ c .JSON (http .StatusOK , gin.H {"Message" : "User created" , "User" : user })
52+ }
53+
54+ //Login verifies cookie session for login
55+ func Login (c * gin.Context ) {
56+ // Get the email and password from request body
57+ var body struct {
58+ Email string
59+ Password string
60+ }
61+
62+ if c .Bind (& body ) != nil {
63+ c .JSON (http .StatusBadRequest , gin.H {"Message" : "Failed to read body" })
64+ return
65+ }
66+
67+ // Look up requested user
68+ var user models.User
69+ database .Db .First (& user , "email = ?" , body .Email )
70+
71+ if user .ID == 0 {
72+ c .JSON (http .StatusBadRequest , gin.H {"Message" : "Invalid email or password" })
73+ return
74+ }
75+
76+ // Compare sent-in password with saved user password hash
77+ err := bcrypt .CompareHashAndPassword ([]byte (user .Password ), []byte (body .Password ))
78+
79+ if err != nil {
80+ c .JSON (http .StatusBadRequest , gin.H {"Message" : "Invalid email or password" })
81+ return
82+ }
83+
84+ // Generate JWT token
85+ token , err := generateJWTToken (user .ID , 1 )
86+ if err != nil {
87+ c .JSON (http .StatusInternalServerError , gin.H {"Message" : "Failed to generate token" })
88+ return
89+ }
90+
91+ // Set token as a cookie
92+ c .SetCookie ("token" , token , 60 * 60 , "/" , "" , false , true )
93+
94+ // Return success response
95+ c .JSON (http .StatusOK , gin.H {"Token" : token })
96+ }
97+
98+ //generateJWTToken generates a JWT token with a specified expiration time and user ID. It first sets the token expiration time based on the amountDays parameter passed into the function.
99+ func generateJWTToken (userID uint , amountDays time.Duration ) (string , error ) {
100+ // Set token expiration time
101+ expirationTime := time .Now ().Add (amountDays * 24 * time .Hour )
102+
103+ // Create JWT claims
104+ claims := jwt.RegisteredClaims {
105+ ExpiresAt : jwt .NewNumericDate (expirationTime ),
106+ IssuedAt : jwt .NewNumericDate (time .Now ()),
107+ Subject : strconv .Itoa (int (userID )),
108+ }
109+
110+ // Create token using claims and signing method
111+ token := jwt .NewWithClaims (jwt .SigningMethodHS256 , claims )
112+
113+ // Sign token using secret key
114+ secretKey := []byte (os .Getenv ("SECRET" ))
115+ signedToken , err := token .SignedString (secretKey )
116+ if err != nil {
117+ return "" , errors .New ("Failed to sign token" )
118+ }
119+
120+ return signedToken , nil
121+ }
122+
16123//CreateName create new name on database of type NameType
17124func CreateName (c * gin.Context ) {
18125 var name models.NameType
@@ -43,24 +150,31 @@ func GetID(c *gin.Context) {
43150//DeleteName delete name off database by id
44151func DeleteName (c * gin.Context ) {
45152 var name models.NameType
153+
46154 id := c .Params .ByName ("id" )
155+ database .Db .First (& name , id )
47156
48157 if name .ID == 0 {
49158 c .JSON (http .StatusNotFound , gin.H {"Not found" : "name id not found" })
50159 return
51160 }
52161
53162 database .Db .Delete (& name , id )
54- c .JSON (http .StatusOK , gin.H {"data " : "name data deleted" })
163+ c .JSON (http .StatusOK , gin.H {"Delete " : "name id " + id + " was deleted" })
55164}
56165
57166//UpdateName update name by id
58167func UpdateName (c * gin.Context ) {
59168 var name models.NameType
60- id := c .Param ("id" )
61169
170+ id := c .Param ("id" )
62171 database .Db .First (& name , id )
63172
173+ if name .ID == 0 {
174+ c .JSON (http .StatusNotFound , gin.H {"Not found" : "name id not found" })
175+ return
176+ }
177+
64178 if err := c .ShouldBindJSON (& name ); err != nil {
65179 c .JSON (http .StatusBadRequest , gin.H {"error" : err .Error ()})
66180 return
@@ -146,7 +260,7 @@ func SearchSimilarNames(c *gin.Context) {
146260 c .JSON (200 , r )
147261}
148262
149- /*-------ALL BELLOW USED ONLY ON searchSimilarNames -------*/
263+ /*---------- used on SearchSimilarNames --- -------*/
150264
151265//searchForAllSimilarMetaphone used in case of not finding exact metaphone match
152266func searchForAllSimilarMetaphone (mtf string ) []models.NameType {
@@ -204,8 +318,8 @@ func findCanonical(name string, matchingMetaphoneNames []models.NameType, nameVa
204318 return models.NameType {}, errors .New ("couldn't find canonical name" )
205319}
206320
207- //findNames return []models.NameVar with all similar names of searched string. For recall purpose we reduce the threshold given in 0.1 in case of empty return
208- func findNames (names []models.NameType , name string , threshold float32 ) []models.NameVar {
321+ //findNames return []models.NameLevenshtein with all similar names of searched string. For recall purpose we reduce the threshold given in 0.1 in case of empty return
322+ func findNames (names []models.NameType , name string , threshold float32 ) []models.NameLevenshtein {
209323 similarNames := findSimilarNames (name , names , threshold )
210324 //reduce the threshold given in 0.1 and search again
211325 if len (similarNames ) == 0 {
@@ -216,17 +330,17 @@ func findNames(names []models.NameType, name string, threshold float32) []models
216330}
217331
218332//findSimilarNames loop for all names given checking the similarity between words by a given threshold, called on findNames
219- func findSimilarNames (name string , names []models.NameType , threshold float32 ) []models.NameVar {
220- var similarNames []models.NameVar
333+ func findSimilarNames (name string , names []models.NameType , threshold float32 ) []models.NameLevenshtein {
334+ var similarNames []models.NameLevenshtein
221335
222336 for _ , n := range names {
223337 similarity := Metaphone .SimilarityBetweenWords (strings .ToLower (name ), strings .ToLower (n .Name ))
224338 if similarity >= threshold {
225- similarNames = append (similarNames , models.NameVar {Name : n .Name , Levenshtein : similarity })
339+ similarNames = append (similarNames , models.NameLevenshtein {Name : n .Name , Levenshtein : similarity })
226340 varWords := strings .Split (n .NameVariations , "|" )
227341 for _ , vw := range varWords {
228342 if vw != "" {
229- similarNames = append (similarNames , models.NameVar {Name : vw , Levenshtein : similarity })
343+ similarNames = append (similarNames , models.NameLevenshtein {Name : vw , Levenshtein : similarity })
230344 }
231345 }
232346 }
@@ -236,9 +350,9 @@ func findSimilarNames(name string, names []models.NameType, threshold float32) [
236350}
237351
238352//orderByLevenshtein used to sort an array by Levenshtein and len of the name
239- func orderByLevenshtein (arr []models.NameVar ) []string {
353+ func orderByLevenshtein (arr []models.NameLevenshtein ) []string {
240354 // creates copy of original array
241- sortedArr := make ([]models.NameVar , len (arr ))
355+ sortedArr := make ([]models.NameLevenshtein , len (arr ))
242356 copy (sortedArr , arr )
243357
244358 // order by func
0 commit comments