diff --git a/enshi_back/REST_API_stuff/requestAnswers.go b/enshi_back/REST_API_stuff/requestAnswers.go new file mode 100644 index 0000000..58bc648 --- /dev/null +++ b/enshi_back/REST_API_stuff/requestAnswers.go @@ -0,0 +1,35 @@ +package rest_api_stuff + +import ( + "net/http" + + "github.com/gin-gonic/gin" +) + +func OkAnswer(c *gin.Context, message string) { + c.IndentedJSON( + http.StatusOK, + gin.H{"message": message}, + ) +} + +func BadRequestAnswer(c *gin.Context, err error) { + c.IndentedJSON( + http.StatusBadRequest, + gin.H{"error": err.Error()}, + ) +} + +func InternalErrorAnswer(c *gin.Context, err error) { + c.IndentedJSON( + http.StatusInternalServerError, + gin.H{"error": err.Error()}, + ) +} + +func ConflictAnswer(c *gin.Context, err error) { + c.IndentedJSON( + http.StatusConflict, + gin.H{"error": err.Error()}, + ) +} diff --git a/enshi_back/db/go_queries/models.go b/enshi_back/db/go_queries/models.go index e387cb1..41a9d0c 100644 --- a/enshi_back/db/go_queries/models.go +++ b/enshi_back/db/go_queries/models.go @@ -85,9 +85,9 @@ type Tag struct { type User struct { UserID int64 `json:"user_id"` - Username string `json:"username"` - Email string `json:"email"` - Password string `json:"password"` + Username string `json:"username" validate:"required"` + Email string `json:"email" validate:"required,email"` + Password string `json:"password" validate:"required"` CreatedAt pgtype.Timestamp `json:"created_at"` IsAdmin bool `json:"is_admin"` } diff --git a/enshi_back/db/go_queries/users_queries.sql.go b/enshi_back/db/go_queries/users_queries.sql.go index 29832f1..de0e1d3 100644 --- a/enshi_back/db/go_queries/users_queries.sql.go +++ b/enshi_back/db/go_queries/users_queries.sql.go @@ -18,9 +18,9 @@ RETURNING user_id, username, email, password, created_at, is_admin type CreateUserParams struct { UserID int64 `json:"user_id"` - Username string `json:"username"` - Email string `json:"email"` - Password string `json:"password"` + Username string `json:"username" validate:"required"` + Email string `json:"email" validate:"required,email"` + Password string `json:"password" validate:"required"` } func (q *Queries) CreateUser(ctx context.Context, arg CreateUserParams) (User, error) { @@ -93,6 +93,29 @@ func (q *Queries) GetAllUsers(ctx context.Context) ([]User, error) { return items, nil } +const getUserByEmailOrNickname = `-- name: GetUserByEmailOrNickname :one +SELECT user_id, username, email, password, created_at, is_admin FROM users WHERE username = $1 OR email = $2 LIMIT 1 +` + +type GetUserByEmailOrNicknameParams struct { + Username string `json:"username" validate:"required"` + Email string `json:"email" validate:"required,email"` +} + +func (q *Queries) GetUserByEmailOrNickname(ctx context.Context, arg GetUserByEmailOrNicknameParams) (User, error) { + row := q.db.QueryRow(ctx, getUserByEmailOrNickname, arg.Username, arg.Email) + var i User + err := row.Scan( + &i.UserID, + &i.Username, + &i.Email, + &i.Password, + &i.CreatedAt, + &i.IsAdmin, + ) + return i, err +} + const getUserById = `-- name: GetUserById :one SELECT user_id, username, email, password, created_at, is_admin FROM users WHERE user_id = $1 ` @@ -137,7 +160,7 @@ RETURNING user_id, username, email, password, created_at, is_admin ` type UpdateUserPasswordHashParams struct { - Password string `json:"password"` + Password string `json:"password" validate:"required"` UserID int64 `json:"user_id"` } diff --git a/enshi_back/db/queries/users_queries.sql b/enshi_back/db/queries/users_queries.sql index 79de5da..fcf2f59 100644 --- a/enshi_back/db/queries/users_queries.sql +++ b/enshi_back/db/queries/users_queries.sql @@ -25,4 +25,7 @@ WHERE user_id=$1; -- name: DeleteUserByUsername :exec DELETE FROM public.users -WHERE username=$1; \ No newline at end of file +WHERE username=$1; + +-- name: GetUserByEmailOrNickname :one +SELECT * FROM users WHERE username = $1 OR email = $2 LIMIT 1; \ No newline at end of file diff --git a/enshi_back/db/sqlc.yml b/enshi_back/db/sqlc.yml index 638d1c9..850465f 100644 --- a/enshi_back/db/sqlc.yml +++ b/enshi_back/db/sqlc.yml @@ -10,6 +10,12 @@ sql: out: "./go_queries" sql_package: "pgx/v5" overrides: + - column: users.password + go_struct_tag: validate:"required" + - column: users.username + go_struct_tag: validate:"required" + - column: users.email + go_struct_tag: validate:"required,email" - db_type: "uuid" go_type: import: 'github.com/google/uuid' diff --git a/enshi_back/go.mod b/enshi_back/go.mod index d4efe37..82fdea1 100644 --- a/enshi_back/go.mod +++ b/enshi_back/go.mod @@ -17,7 +17,7 @@ require ( github.com/gin-gonic/gin v1.10.0 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.20.0 // indirect + github.com/go-playground/validator/v10 v10.22.1 // indirect github.com/goccy/go-json v0.10.2 // indirect github.com/golang-jwt/jwt/v5 v5.2.1 // indirect github.com/google/uuid v1.6.0 // indirect diff --git a/enshi_back/go.sum b/enshi_back/go.sum index 237fb04..2e9f5ad 100644 --- a/enshi_back/go.sum +++ b/enshi_back/go.sum @@ -21,6 +21,8 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8= github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/go-playground/validator/v10 v10.22.1 h1:40JcKH+bBNGFczGuoBYgX4I6m/i27HYW8P9FDk5PbgA= +github.com/go-playground/validator/v10 v10.22.1/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= diff --git a/enshi_back/utils/database.go b/enshi_back/utils/database.go index 9cf96eb..d75ae88 100644 --- a/enshi_back/utils/database.go +++ b/enshi_back/utils/database.go @@ -2,6 +2,7 @@ package utils import ( "context" + db_repo "enshi/db/go_queries" "fmt" "github.com/jackc/pgx/v5" @@ -11,6 +12,7 @@ import ( // Pgx connection to database var Dbx *pgxpool.Pool var Dbx_connection *pgx.Conn +var Sqlc_db = db_repo.New(Dbx) func SetupDatabase() error { @@ -27,7 +29,7 @@ func SetupDatabase() error { } // Url to connect - url := fmt.Sprintf("postgres://%v:%v@nekiiinkognito.ru:5432/enshi_db", bd_user, bd_pass) + url := fmt.Sprintf("postgres://%v:%v@nekiiinkognito.ru:5432/postgres", bd_user, bd_pass) // Connecting to database Dbx, err = pgxpool.New(context.Background(), url) diff --git a/enshi_back/utils/routesSetup.go b/enshi_back/utils/routesSetup.go index 9917dac..5b8c6f3 100644 --- a/enshi_back/utils/routesSetup.go +++ b/enshi_back/utils/routesSetup.go @@ -2,13 +2,18 @@ package utils import ( "context" + "encoding/binary" + rest_api_stuff "enshi/REST_API_stuff" db_repo "enshi/db/go_queries" + "fmt" "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) { @@ -16,6 +21,80 @@ 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()) @@ -25,6 +104,7 @@ func SetupRotes(g *gin.Engine) error { freeGroup.POST("login", login) freeGroup.GET("getCookie", testCookie) + freeGroup.POST("registerUser", RegisterUser) authGroup := g.Group("/") authGroup.Use(AuthMiddleware())