From 2b306c687c749c2ab61a3cae65230a418989e199 Mon Sep 17 00:00:00 2001 From: Max Date: Sat, 9 Nov 2024 13:58:59 +0300 Subject: [PATCH] reorganized back --- enshi_back/REST_API_stuff/setCookie.go | 27 +++ enshi_back/{utils/auth.go => auth/jwt.go} | 2 +- enshi_back/db/go_queries/models.go | 1 - .../db/go_queries/profiles_queries.sql.go | 31 +-- enshi_back/db/migrations/migration1.sql | 2 +- enshi_back/db/queries/profiles_queries.sql | 6 +- .../databaseSetup.go} | 9 +- enshi_back/{utils => env}/env.go | 5 +- .../globalColorsForConsole.go} | 2 +- enshi_back/global/globalVars.go | 6 + .../{utils => hasher}/passwordHashing.go | 2 +- enshi_back/main.go | 12 +- enshi_back/middleware/authMiddleware.go | 29 +++ enshi_back/middleware/corsMiddleware.go | 20 ++ enshi_back/routes/changeUserProfile.go | 17 ++ enshi_back/routes/login.go | 74 +++++++ enshi_back/routes/registerUser.go | 121 +++++++++++ enshi_back/utils/routesSetup.go | 197 +----------------- 18 files changed, 337 insertions(+), 226 deletions(-) create mode 100644 enshi_back/REST_API_stuff/setCookie.go rename enshi_back/{utils/auth.go => auth/jwt.go} (99%) rename enshi_back/{utils/database.go => db_connection/databaseSetup.go} (79%) rename enshi_back/{utils => env}/env.go (83%) rename enshi_back/{utils/colors.go => global/globalColorsForConsole.go} (94%) create mode 100644 enshi_back/global/globalVars.go rename enshi_back/{utils => hasher}/passwordHashing.go (99%) create mode 100644 enshi_back/middleware/authMiddleware.go create mode 100644 enshi_back/middleware/corsMiddleware.go create mode 100644 enshi_back/routes/changeUserProfile.go create mode 100644 enshi_back/routes/login.go create mode 100644 enshi_back/routes/registerUser.go diff --git a/enshi_back/REST_API_stuff/setCookie.go b/enshi_back/REST_API_stuff/setCookie.go new file mode 100644 index 0000000..2d928a1 --- /dev/null +++ b/enshi_back/REST_API_stuff/setCookie.go @@ -0,0 +1,27 @@ +package rest_api_stuff + +import ( + "github.com/gin-gonic/gin" +) + +type CookieParams struct { + Name string + Value string + MaxAge int + Path string + Domain string + Secure bool + HttpOnly bool +} + +func SetCookie(c *gin.Context, params *CookieParams) { + c.SetCookie( + params.Name, + params.Value, + params.MaxAge, + params.Path, + params.Domain, + params.Secure, + params.HttpOnly, + ) +} diff --git a/enshi_back/utils/auth.go b/enshi_back/auth/jwt.go similarity index 99% rename from enshi_back/utils/auth.go rename to enshi_back/auth/jwt.go index deb1886..7c5964e 100644 --- a/enshi_back/utils/auth.go +++ b/enshi_back/auth/jwt.go @@ -1,4 +1,4 @@ -package utils +package auth import ( "fmt" diff --git a/enshi_back/db/go_queries/models.go b/enshi_back/db/go_queries/models.go index 41a9d0c..0977118 100644 --- a/enshi_back/db/go_queries/models.go +++ b/enshi_back/db/go_queries/models.go @@ -71,7 +71,6 @@ type PostVote struct { } type Profile struct { - ProfileID int64 `json:"profile_id"` UserID int64 `json:"user_id"` Bio pgtype.Text `json:"bio"` AvatarUrl pgtype.Text `json:"avatar_url"` diff --git a/enshi_back/db/go_queries/profiles_queries.sql.go b/enshi_back/db/go_queries/profiles_queries.sql.go index 728610a..946a625 100644 --- a/enshi_back/db/go_queries/profiles_queries.sql.go +++ b/enshi_back/db/go_queries/profiles_queries.sql.go @@ -15,14 +15,13 @@ const clearProfileByUserId = `-- name: ClearProfileByUserId :one UPDATE public.profiles SET bio='', avatar_url='', website_url='' WHERE user_id=$1 -RETURNING profile_id, user_id, bio, avatar_url, website_url +RETURNING user_id, bio, avatar_url, website_url ` func (q *Queries) ClearProfileByUserId(ctx context.Context, userID int64) (Profile, error) { row := q.db.QueryRow(ctx, clearProfileByUserId, userID) var i Profile err := row.Scan( - &i.ProfileID, &i.UserID, &i.Bio, &i.AvatarUrl, @@ -33,21 +32,15 @@ func (q *Queries) ClearProfileByUserId(ctx context.Context, userID int64) (Profi const createProfileForUser = `-- name: CreateProfileForUser :one INSERT INTO public.profiles -(profile_id, user_id, bio, avatar_url, website_url) -VALUES($1, $2, '', '', '') -RETURNING profile_id, user_id, bio, avatar_url, website_url +(user_id, bio, avatar_url, website_url) +VALUES($1, '', '', '') +RETURNING user_id, bio, avatar_url, website_url ` -type CreateProfileForUserParams struct { - ProfileID int64 `json:"profile_id"` - UserID int64 `json:"user_id"` -} - -func (q *Queries) CreateProfileForUser(ctx context.Context, arg CreateProfileForUserParams) (Profile, error) { - row := q.db.QueryRow(ctx, createProfileForUser, arg.ProfileID, arg.UserID) +func (q *Queries) CreateProfileForUser(ctx context.Context, userID int64) (Profile, error) { + row := q.db.QueryRow(ctx, createProfileForUser, userID) var i Profile err := row.Scan( - &i.ProfileID, &i.UserID, &i.Bio, &i.AvatarUrl, @@ -67,14 +60,13 @@ func (q *Queries) DeleteProfileByUserId(ctx context.Context, userID int64) error } const getProfileByUserId = `-- name: GetProfileByUserId :one -SELECT profile_id, user_id, bio, avatar_url, website_url FROM public.profiles WHERE user_id = $1 +SELECT user_id, bio, avatar_url, website_url FROM public.profiles WHERE user_id = $1 ` func (q *Queries) GetProfileByUserId(ctx context.Context, userID int64) (Profile, error) { row := q.db.QueryRow(ctx, getProfileByUserId, userID) var i Profile err := row.Scan( - &i.ProfileID, &i.UserID, &i.Bio, &i.AvatarUrl, @@ -86,12 +78,12 @@ func (q *Queries) GetProfileByUserId(ctx context.Context, userID int64) (Profile const updateProfileByUserId = `-- name: UpdateProfileByUserId :one UPDATE public.profiles SET bio=$2, avatar_url=$3, website_url=$4 -WHERE profile_id=$1 -RETURNING profile_id, user_id, bio, avatar_url, website_url +WHERE user_id=$1 +RETURNING user_id, bio, avatar_url, website_url ` type UpdateProfileByUserIdParams struct { - ProfileID int64 `json:"profile_id"` + UserID int64 `json:"user_id"` Bio pgtype.Text `json:"bio"` AvatarUrl pgtype.Text `json:"avatar_url"` WebsiteUrl pgtype.Text `json:"website_url"` @@ -99,14 +91,13 @@ type UpdateProfileByUserIdParams struct { func (q *Queries) UpdateProfileByUserId(ctx context.Context, arg UpdateProfileByUserIdParams) (Profile, error) { row := q.db.QueryRow(ctx, updateProfileByUserId, - arg.ProfileID, + arg.UserID, arg.Bio, arg.AvatarUrl, arg.WebsiteUrl, ) var i Profile err := row.Scan( - &i.ProfileID, &i.UserID, &i.Bio, &i.AvatarUrl, diff --git a/enshi_back/db/migrations/migration1.sql b/enshi_back/db/migrations/migration1.sql index 0414b09..a7870e0 100644 --- a/enshi_back/db/migrations/migration1.sql +++ b/enshi_back/db/migrations/migration1.sql @@ -25,6 +25,6 @@ CREATE TABLE "public"."post_tags" ("post_id" bigint NOT NULL, "tag_id" integer N -- Create "post_votes" table CREATE TABLE "public"."post_votes" ("post_id" bigint NOT NULL, "user_id" bigint NOT NULL, "vote" boolean NOT NULL, PRIMARY KEY ("post_id", "user_id"), CONSTRAINT "post_votes_post_id_fkey" FOREIGN KEY ("post_id") REFERENCES "public"."posts" ("post_id") ON UPDATE NO ACTION ON DELETE NO ACTION, CONSTRAINT "post_votes_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "public"."users" ("user_id") ON UPDATE NO ACTION ON DELETE NO ACTION); -- Create "profiles" table -CREATE TABLE "public"."profiles" ("profile_id" bigint NOT NULL, "user_id" bigint NOT NULL, "bio" text NULL, "avatar_url" character varying(255) NULL, "website_url" character varying(100) NULL, PRIMARY KEY ("profile_id"), CONSTRAINT "profiles_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "public"."users" ("user_id") ON UPDATE NO ACTION ON DELETE CASCADE); +CREATE TABLE "public"."profiles" ("user_id" bigint NOT NULL, "bio" text NULL, "avatar_url" character varying(255) NULL, "website_url" character varying(100) NULL, PRIMARY KEY ("user_id"), CONSTRAINT "profiles_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "public"."users" ("user_id") ON UPDATE NO ACTION ON DELETE CASCADE); -- Create index "profiles_user_id_idx" to table: "profiles" CREATE UNIQUE INDEX "profiles_user_id_idx" ON "public"."profiles" ("user_id"); diff --git a/enshi_back/db/queries/profiles_queries.sql b/enshi_back/db/queries/profiles_queries.sql index 0a0d3d2..087d49b 100644 --- a/enshi_back/db/queries/profiles_queries.sql +++ b/enshi_back/db/queries/profiles_queries.sql @@ -1,7 +1,7 @@ -- name: CreateProfileForUser :one INSERT INTO public.profiles -(profile_id, user_id, bio, avatar_url, website_url) -VALUES($1, $2, '', '', '') +(user_id, bio, avatar_url, website_url) +VALUES($1, '', '', '') RETURNING *; -- name: ClearProfileByUserId :one @@ -20,6 +20,6 @@ SELECT * FROM public.profiles WHERE user_id = $1; -- name: UpdateProfileByUserId :one UPDATE public.profiles SET bio=$2, avatar_url=$3, website_url=$4 -WHERE profile_id=$1 +WHERE user_id=$1 RETURNING *; diff --git a/enshi_back/utils/database.go b/enshi_back/db_connection/databaseSetup.go similarity index 79% rename from enshi_back/utils/database.go rename to enshi_back/db_connection/databaseSetup.go index d75ae88..70994ac 100644 --- a/enshi_back/utils/database.go +++ b/enshi_back/db_connection/databaseSetup.go @@ -1,8 +1,9 @@ -package utils +package db_connection import ( "context" db_repo "enshi/db/go_queries" + "enshi/env" "fmt" "github.com/jackc/pgx/v5" @@ -12,18 +13,18 @@ import ( // Pgx connection to database var Dbx *pgxpool.Pool var Dbx_connection *pgx.Conn -var Sqlc_db = db_repo.New(Dbx) +var Sqlc_query = db_repo.New(Dbx) func SetupDatabase() error { var bd_pass, bd_user string var err error - if err := LookupEnv(&bd_pass, "BD_PASSWORD"); err != nil { + if err := env.LookupEnv(&bd_pass, "BD_PASSWORD"); err != nil { fmt.Printf("%v", err) return err } - if err := LookupEnv(&bd_user, "BD_USER"); err != nil { + if err := env.LookupEnv(&bd_user, "BD_USER"); err != nil { fmt.Printf("%v", err) return err } diff --git a/enshi_back/utils/env.go b/enshi_back/env/env.go similarity index 83% rename from enshi_back/utils/env.go rename to enshi_back/env/env.go index cf82df3..b64618a 100644 --- a/enshi_back/utils/env.go +++ b/enshi_back/env/env.go @@ -1,6 +1,7 @@ -package utils +package env import ( + "enshi/auth" "fmt" "os" @@ -23,7 +24,7 @@ func LoadEnv(path string) error { return err } - if err := LookupEnv(&SecretKey, "SECRET_KEY"); err != nil { + if err := LookupEnv(&auth.SecretKey, "SECRET_KEY"); err != nil { fmt.Printf("%v\n", err) return err } diff --git a/enshi_back/utils/colors.go b/enshi_back/global/globalColorsForConsole.go similarity index 94% rename from enshi_back/utils/colors.go rename to enshi_back/global/globalColorsForConsole.go index 64febe3..1de4c21 100644 --- a/enshi_back/utils/colors.go +++ b/enshi_back/global/globalColorsForConsole.go @@ -1,4 +1,4 @@ -package utils +package global var ResetColor = "\033[0m" var RedColor = "\033[31m" diff --git a/enshi_back/global/globalVars.go b/enshi_back/global/globalVars.go new file mode 100644 index 0000000..b064789 --- /dev/null +++ b/enshi_back/global/globalVars.go @@ -0,0 +1,6 @@ +package global + +var PathForCookies = "/" +var DomainForCookies = "localhost" +var SecureForCookies = false +var HttpOnlyForCookies = false diff --git a/enshi_back/utils/passwordHashing.go b/enshi_back/hasher/passwordHashing.go similarity index 99% rename from enshi_back/utils/passwordHashing.go rename to enshi_back/hasher/passwordHashing.go index df7ebbc..5b3c4c7 100644 --- a/enshi_back/utils/passwordHashing.go +++ b/enshi_back/hasher/passwordHashing.go @@ -1,4 +1,4 @@ -package utils +package hasher import ( "bytes" diff --git a/enshi_back/main.go b/enshi_back/main.go index cda1f59..72320f0 100644 --- a/enshi_back/main.go +++ b/enshi_back/main.go @@ -3,6 +3,8 @@ package main import ( "context" db_repo "enshi/db/go_queries" + "enshi/db_connection" + "enshi/env" utils "enshi/utils" "fmt" @@ -11,17 +13,17 @@ import ( func main() { - if err := utils.LoadEnv("utils/secret.env"); err != nil { + if err := env.LoadEnv("utils/secret.env"); err != nil { fmt.Println(err.Error()) return } - if err := utils.SetupDatabase(); err != nil { + if err := db_connection.SetupDatabase(); err != nil { fmt.Println(err.Error()) return } - defer utils.Dbx.Close() - defer utils.Dbx_connection.Close(context.Background()) + defer db_connection.Dbx.Close() + defer db_connection.Dbx_connection.Close(context.Background()) router := gin.Default() if err := utils.SetupRotes(router); err != nil { @@ -30,7 +32,7 @@ func main() { } // Transaction - tx, _ := utils.Dbx.Begin(context.Background()) + tx, _ := db_connection.Dbx.Begin(context.Background()) defer tx.Rollback(context.Background()) repo := db_repo.New(tx) diff --git a/enshi_back/middleware/authMiddleware.go b/enshi_back/middleware/authMiddleware.go new file mode 100644 index 0000000..1cfc670 --- /dev/null +++ b/enshi_back/middleware/authMiddleware.go @@ -0,0 +1,29 @@ +package middleware + +import ( + "enshi/auth" + "net/http" + + "github.com/gin-gonic/gin" +) + +func AuthMiddleware() gin.HandlerFunc { + return func(c *gin.Context) { + + token := c.GetHeader("Authorization") + // tokenFromCoolies := c.Request.CookiesNamed("auth_cookie") + + claims, err := auth.ValidateToken(token) + if err != nil { + c.IndentedJSON(http.StatusUnauthorized, gin.H{"error auth": err.Error()}) + c.Abort() + return + } + + // Claims -> data stored in token + c.Set("id", claims["id"]) + c.Set("claims", claims) + c.Next() + + } +} diff --git a/enshi_back/middleware/corsMiddleware.go b/enshi_back/middleware/corsMiddleware.go new file mode 100644 index 0000000..872af6b --- /dev/null +++ b/enshi_back/middleware/corsMiddleware.go @@ -0,0 +1,20 @@ +package middleware + +import "github.com/gin-gonic/gin" + +func CORSMiddleware() gin.HandlerFunc { + return func(c *gin.Context) { + c.Writer.Header().Set("Access-Control-Allow-Origin", "http://localhost:5173") + c.Writer.Header().Set("Access-Control-Allow-Credentials", "true") + c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, authorization, Authorization, accept, origin, Cache-Control, X-Requested-With, Cookie") + c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT") + c.Writer.Header().Set("Access-Control-Expose-Headers", "Access-Token, Uid, Authorization") + + if c.Request.Method == "OPTIONS" { + c.AbortWithStatus(204) + return + } + + c.Next() + } +} diff --git a/enshi_back/routes/changeUserProfile.go b/enshi_back/routes/changeUserProfile.go new file mode 100644 index 0000000..f4fd3e5 --- /dev/null +++ b/enshi_back/routes/changeUserProfile.go @@ -0,0 +1,17 @@ +package routes + +import ( + rest_api_stuff "enshi/REST_API_stuff" + db_repo "enshi/db/go_queries" + + "github.com/gin-gonic/gin" +) + +func ChangeUserProfile(c *gin.Context) { + var userProfileParams db_repo.UpdateProfileByUserIdParams + + if err := c.BindJSON(&userProfileParams); err != nil { + rest_api_stuff.BadRequestAnswer(c, err) + } + +} diff --git a/enshi_back/routes/login.go b/enshi_back/routes/login.go new file mode 100644 index 0000000..211ee47 --- /dev/null +++ b/enshi_back/routes/login.go @@ -0,0 +1,74 @@ +package routes + +import ( + "context" + "enshi/auth" + db_repo "enshi/db/go_queries" + "enshi/db_connection" + "enshi/global" + "enshi/hasher" + "net/http" + "strconv" + "time" + + "github.com/gin-gonic/gin" +) + +func Login(c *gin.Context) { + type content struct { + Nickname string + Password string + } + + var body content + + err := c.BindJSON(&body) + if err != nil { + c.IndentedJSON(http.StatusBadRequest, gin.H{"error 1st": err.Error()}) + return + } + + repo := db_repo.New(db_connection.Dbx) + user, err := repo.GetUserByUsername(context.Background(), body.Nickname) + if err != nil { + c.IndentedJSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + password_hash, salt, err := hasher.DecodeArgon2String(user.Password) + if err != nil { + c.IndentedJSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + err = hasher.Argon2Hasher.Compare(password_hash, salt, []byte(body.Password)) + if err != nil { + c.IndentedJSON(http.StatusUnauthorized, gin.H{"error": err.Error()}) + return + } + + user_info := map[string]interface{}{ + "id": user.UserID, + "name": user.Username, + } + + token, err := auth.CreateToken(user_info) + if err != nil { + c.IndentedJSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + + cookieName := "auth_cookie" + cookieValue := "id=" + strconv.FormatInt(user_info["id"].(int64), 10) + + "_nickname=" + user_info["name"].(string) + maxAge := int(2 * time.Hour.Seconds()) // Cookie expiry time in seconds (1 hour) + path := global.PathForCookies // Cookie path + domain := global.DomainForCookies // Set domain (localhost for testing) + secure := global.SecureForCookies // Secure cookie (set to true in production with HTTPS) + httpOnly := global.HttpOnlyForCookies // HTTP only, so it can't be accessed by JavaScript + + c.Header("Authorization", token) + c.SetCookie(cookieName, cookieValue, maxAge, path, domain, secure, httpOnly) + c.IndentedJSON(http.StatusOK, gin.H{"token": token}) + +} diff --git a/enshi_back/routes/registerUser.go b/enshi_back/routes/registerUser.go new file mode 100644 index 0000000..91f5148 --- /dev/null +++ b/enshi_back/routes/registerUser.go @@ -0,0 +1,121 @@ +package routes + +import ( + "context" + "encoding/binary" + rest_api_stuff "enshi/REST_API_stuff" + "enshi/auth" + db_repo "enshi/db/go_queries" + "enshi/db_connection" + "enshi/global" + "enshi/hasher" + "fmt" + "time" + + "github.com/gin-gonic/gin" + "github.com/go-playground/validator/v10" + "github.com/google/uuid" +) + +func RegisterUser(c *gin.Context) { + var userParams db_repo.CreateUserParams + + if err := c.BindJSON(&userParams); err != nil { + rest_api_stuff.BadRequestAnswer(c, err) + return + } + + validate := validator.New(validator.WithRequiredStructEnabled()) + if err := validate.Struct(userParams); err != nil { + rest_api_stuff.BadRequestAnswer(c, err) + return + } + + query := db_repo.New(db_connection.Dbx) + sameNicknameOrEmailUser, _ := query.GetUserByEmailOrNickname( + context.Background(), + db_repo.GetUserByEmailOrNicknameParams{ + Username: userParams.Username, + Email: userParams.Email, + }, + ) + if sameNicknameOrEmailUser.Username == userParams.Username { + rest_api_stuff.ConflictAnswer( + c, + fmt.Errorf("username"), + ) + return + } else if sameNicknameOrEmailUser.Email == userParams.Email { + rest_api_stuff.ConflictAnswer( + c, + fmt.Errorf("email"), + ) + return + } + + transaction, err := db_connection.Dbx.Begin(context.Background()) + defer transaction.Rollback(context.Background()) + + if err != nil { + rest_api_stuff.InternalErrorAnswer(c, err) + return + } + + query_transaction := query.WithTx(transaction) + + passwordHashSalt, err := hasher.Argon2Hasher.HashGen([]byte(userParams.Password), []byte{}) + if err != nil { + rest_api_stuff.InternalErrorAnswer(c, err) + return + } + + userParams.Password = passwordHashSalt.StringToStore + + uuid, err := uuid.NewV7() + if err != nil { + rest_api_stuff.InternalErrorAnswer(c, err) + return + } + + userParams.UserID = -int64( + binary.BigEndian.Uint64(uuid[8:]), + ) + + if _, err := query_transaction.CreateUser(context.Background(), userParams); err != nil { + rest_api_stuff.InternalErrorAnswer(c, err) + return + } + + if _, err := query_transaction.CreateProfileForUser( + context.Background(), + userParams.UserID, + ); err != nil { + rest_api_stuff.InternalErrorAnswer(c, err) + return + } + + tokenParams := map[string]interface{}{ + "id": userParams.UserID, + "username": userParams.Username, + } + + token, err := auth.CreateToken(tokenParams) + if err != nil { + rest_api_stuff.InternalErrorAnswer(c, err) + return + } + + cookieParams := &rest_api_stuff.CookieParams{ + Name: "auth_cookie", + Value: token, + MaxAge: int(time.Hour.Seconds() * 2), + Path: global.PathForCookies, + Domain: global.DomainForCookies, + Secure: global.SecureForCookies, + HttpOnly: global.HttpOnlyForCookies, + } + + transaction.Commit(context.Background()) + rest_api_stuff.SetCookie(c, cookieParams) + rest_api_stuff.OkAnswer(c, "User has been created!") +} diff --git a/enshi_back/utils/routesSetup.go b/enshi_back/utils/routesSetup.go index 5b8c6f3..ad97142 100644 --- a/enshi_back/utils/routesSetup.go +++ b/enshi_back/utils/routesSetup.go @@ -1,19 +1,12 @@ package utils import ( - "context" - "encoding/binary" - rest_api_stuff "enshi/REST_API_stuff" - db_repo "enshi/db/go_queries" - "fmt" + "enshi/middleware" + "enshi/routes" "net/http" - "strconv" "strings" - "time" "github.com/gin-gonic/gin" - "github.com/go-playground/validator/v10" - "github.com/google/uuid" ) func testCookie(c *gin.Context) { @@ -21,192 +14,22 @@ func testCookie(c *gin.Context) { c.IndentedJSON(http.StatusOK, gin.H{"token": "SLESAR' U STASA " + strings.Split(cock, "_")[0]}) } -func RegisterUser(c *gin.Context) { - var userParams db_repo.CreateUserParams - - transaction, err := Dbx.Begin(context.Background()) - defer transaction.Rollback(context.Background()) - - if err != nil { - rest_api_stuff.InternalErrorAnswer(c, err) - return - } - - if err := c.BindJSON(&userParams); err != nil { - rest_api_stuff.BadRequestAnswer(c, err) - return - } - - validate := validator.New(validator.WithRequiredStructEnabled()) - err = validate.Struct(userParams) - if err != nil { - rest_api_stuff.BadRequestAnswer(c, err) - return - } - - query := db_repo.New(Dbx) - sameNicknameOrEmailUser, _ := query.GetUserByEmailOrNickname( - context.Background(), - db_repo.GetUserByEmailOrNicknameParams{ - Username: userParams.Username, - Email: userParams.Email, - }, - ) - if sameNicknameOrEmailUser.Username == userParams.Username { - rest_api_stuff.ConflictAnswer( - c, - fmt.Errorf("username"), - ) - return - } else if sameNicknameOrEmailUser.Email == userParams.Email { - rest_api_stuff.ConflictAnswer( - c, - fmt.Errorf("email"), - ) - return - } - - sqlc_transaction := Sqlc_db.WithTx(transaction) - - passwordHashSalt, err := Argon2Hasher.HashGen([]byte(userParams.Password), []byte{}) - if err != nil { - rest_api_stuff.InternalErrorAnswer(c, err) - return - } - - userParams.Password = passwordHashSalt.StringToStore - - uuid, err := uuid.NewV7() - if err != nil { - rest_api_stuff.InternalErrorAnswer(c, err) - return - } - - userParams.UserID = int64( - binary.BigEndian.Uint64(append(uuid[0:4], uuid[12:16]...)), - ) - - if _, err := sqlc_transaction.CreateUser(context.Background(), userParams); err != nil { - rest_api_stuff.InternalErrorAnswer(c, err) - return - } - - transaction.Commit(context.Background()) - rest_api_stuff.OkAnswer(c, "User has been created!") -} - func SetupRotes(g *gin.Engine) error { - g.Use(CORSMiddleware()) - - freeGroup := g.Group("/") + g.Use(middleware.CORSMiddleware()) // Free group routes + freeGroup := g.Group("/") - freeGroup.POST("login", login) freeGroup.GET("getCookie", testCookie) - freeGroup.POST("registerUser", RegisterUser) - authGroup := g.Group("/") - authGroup.Use(AuthMiddleware()) + freeGroup.POST("login", routes.Login) + freeGroup.POST("registerUser", routes.RegisterUser) // Auth group routes + authGroup := g.Group("/") + authGroup.Use(middleware.AuthMiddleware()) + + authGroup.POST("changeUserProfile", routes.ChangeUserProfile) return nil } - -func login(c *gin.Context) { - type content struct { - Nickname string - Password string - } - - var body content - - err := c.BindJSON(&body) - if err != nil { - c.IndentedJSON(http.StatusBadRequest, gin.H{"error 1st": err.Error()}) - return - } - - repo := db_repo.New(Dbx) - user, err := repo.GetUserByUsername(context.Background(), body.Nickname) - if err != nil { - c.IndentedJSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) - return - } - - password_hash, salt, err := DecodeArgon2String(user.Password) - if err != nil { - c.IndentedJSON(http.StatusBadRequest, gin.H{"error": err.Error()}) - return - } - - err = Argon2Hasher.Compare(password_hash, salt, []byte(body.Password)) - if err != nil { - c.IndentedJSON(http.StatusUnauthorized, gin.H{"error": err.Error()}) - return - } - - user_info := map[string]interface{}{ - "id": user.UserID, - "name": user.Username, - } - - token, err := CreateToken(user_info) - if err != nil { - c.IndentedJSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) - return - } - - cookieName := "auth_cookie" - cookieValue := "id=" + strconv.FormatInt(user_info["id"].(int64), 10) + - "_nickname=" + user_info["name"].(string) - maxAge := int(2 * time.Hour.Seconds()) // Cookie expiry time in seconds (1 hour) - path := "/" // Cookie path - domain := "localhost" // Set domain (localhost for testing) - secure := false // Secure cookie (set to true in production with HTTPS) - httpOnly := false // HTTP only, so it can't be accessed by JavaScript - // LookupEnv(&domain, "DOMAIN") - - c.Header("Authorization", token) - c.SetCookie(cookieName, cookieValue, maxAge, path, domain, secure, httpOnly) - c.IndentedJSON(http.StatusOK, gin.H{"token": token}) - -} - -func CORSMiddleware() gin.HandlerFunc { - return func(c *gin.Context) { - c.Writer.Header().Set("Access-Control-Allow-Origin", "http://localhost:5173") - c.Writer.Header().Set("Access-Control-Allow-Credentials", "true") - c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, authorization, Authorization, accept, origin, Cache-Control, X-Requested-With, Cookie") - c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT") - c.Writer.Header().Set("Access-Control-Expose-Headers", "Access-Token, Uid, Authorization") - - if c.Request.Method == "OPTIONS" { - c.AbortWithStatus(204) - return - } - - c.Next() - } -} - -func AuthMiddleware() gin.HandlerFunc { - return func(c *gin.Context) { - - token := c.GetHeader("Authorization") - - claims, err := ValidateToken(token) - if err != nil { - c.IndentedJSON(http.StatusUnauthorized, gin.H{"error auth": err.Error()}) - c.Abort() - return - } - - // Claims -> data stored in token - c.Set("id", claims["id"]) - c.Set("claims", claims) - c.Next() - - } -}