реструктурирован код

This commit is contained in:
Sergey Chubaryan 2024-07-22 22:52:43 +03:00
parent 39a79d956b
commit 5da8a4754d
13 changed files with 162 additions and 141 deletions

33
main.go
View File

@ -1,7 +1,12 @@
package main
import (
"backend/src"
"backend/src/handlers"
"backend/src/middleware"
"backend/src/models"
"backend/src/repo"
"backend/src/services"
"backend/src/utils"
"crypto/rand"
"crypto/rsa"
@ -30,27 +35,31 @@ func main() {
panic(err)
}
jwtUtil := src.NewJwtUtil(key)
passwordUtil := src.NewPasswordUtil()
db := src.NewDB(sqlDb)
userService := src.NewUserService(src.UserServiceDeps{
jwtUtil := utils.NewJwtUtil(key)
passwordUtil := utils.NewPasswordUtil()
userRepo := repo.NewUserRepo(sqlDb)
userCache := repo.NewCacheInmem[string, models.UserDTO](60 * 60)
userService := services.NewUserService(
services.UserServiceDeps{
Jwt: jwtUtil,
Password: passwordUtil,
Db: db,
Cache: src.NewCacheInmem[string, src.UserDTO](60 * 60),
})
UserRepo: userRepo,
UserCache: userCache,
},
)
r := gin.New()
r.Use(gin.Logger())
r.Use(gin.Recovery())
userGroup := r.Group("/user")
userGroup.POST("/create", src.NewUserCreateHandler(userService))
userGroup.POST("/login", src.NewUserLoginHandler(userService))
userGroup.POST("/create", handlers.NewUserCreateHandler(userService))
userGroup.POST("/login", handlers.NewUserLoginHandler(userService))
dummyGroup := r.Group("/dummy")
dummyGroup.Use(src.NewAuthMiddleware(userService))
dummyGroup.GET("/", src.NewDummyHandler())
dummyGroup.Use(middleware.NewAuthMiddleware(userService))
dummyGroup.GET("/", handlers.NewDummyHandler())
r.Run(":8080")
}

View File

@ -1,70 +0,0 @@
package src
import (
"context"
"database/sql"
"errors"
)
type DB interface {
CreateUser(ctx context.Context, dto UserDTO) (*UserDTO, error)
GetUserById(ctx context.Context, id string) (*UserDTO, error)
GetUserByLogin(ctx context.Context, login string) (*UserDTO, error)
}
func NewDB(db *sql.DB) DB {
return &dbImpl{db}
}
type dbImpl struct {
db *sql.DB
}
func (d *dbImpl) CreateUser(ctx context.Context, dto UserDTO) (*UserDTO, error) {
query := `insert into users (login, secret, name) values ($1, $2, $3) returning id;`
row := d.db.QueryRowContext(ctx, query, dto.Login, dto.Secret, dto.Name)
id := ""
if err := row.Scan(&id); err != nil {
return nil, err
}
return &UserDTO{
Id: id,
Login: dto.Login,
Secret: dto.Secret,
Name: dto.Name,
}, nil
}
func (d *dbImpl) GetUserById(ctx context.Context, id string) (*UserDTO, error) {
query := `select id, login, secret, name from users where id = $1;`
row := d.db.QueryRowContext(ctx, query, id)
dto := &UserDTO{}
err := row.Scan(&dto.Id, &dto.Login, &dto.Secret, &dto.Name)
if err == nil {
return dto, nil
}
if errors.Is(err, sql.ErrNoRows) {
return nil, nil
}
return nil, err
}
func (d *dbImpl) GetUserByLogin(ctx context.Context, login string) (*UserDTO, error) {
query := `select id, login, secret, name from users where login = $1;`
row := d.db.QueryRowContext(ctx, query, login)
dto := &UserDTO{}
err := row.Scan(&dto.Id, &dto.Login, &dto.Secret, &dto.Name)
if err == nil {
return dto, nil
}
if errors.Is(err, sql.ErrNoRows) {
return nil, nil
}
return nil, err
}

View File

@ -1,4 +1,4 @@
package src
package handlers
import "github.com/gin-gonic/gin"

View File

@ -1,6 +1,7 @@
package src
package handlers
import (
"backend/src/services"
"encoding/json"
"github.com/gin-gonic/gin"
@ -18,7 +19,7 @@ type createUserOutput struct {
Name string `json:"name"`
}
func NewUserCreateHandler(userService UserService) gin.HandlerFunc {
func NewUserCreateHandler(userService services.UserService) gin.HandlerFunc {
return func(ctx *gin.Context) {
params := createUserInput{}
if err := ctx.ShouldBindJSON(&params); err != nil {
@ -26,12 +27,12 @@ func NewUserCreateHandler(userService UserService) gin.HandlerFunc {
return
}
dto, err := userService.CreateUser(ctx, UserCreateParams{
dto, err := userService.CreateUser(ctx, services.UserCreateParams{
Login: params.Login,
Password: params.Password,
Name: params.Name,
})
if err == ErrUserExists || err == ErrUserBadPassword {
if err == services.ErrUserExists || err == services.ErrUserBadPassword {
ctx.Data(400, "plain/text", []byte(err.Error()))
return
}

View File

@ -1,6 +1,7 @@
package src
package handlers
import (
"backend/src/services"
"encoding/json"
"github.com/gin-gonic/gin"
@ -15,7 +16,7 @@ type loginUserOutput struct {
Token string `json:"token"`
}
func NewUserLoginHandler(userService UserService) gin.HandlerFunc {
func NewUserLoginHandler(userService services.UserService) gin.HandlerFunc {
return func(ctx *gin.Context) {
params := loginUserInput{}
if err := ctx.ShouldBindJSON(&params); err != nil {
@ -24,7 +25,7 @@ func NewUserLoginHandler(userService UserService) gin.HandlerFunc {
}
token, err := userService.AuthenticateUser(ctx, params.Login, params.Password)
if err == ErrUserNotExists || err == ErrUserWrongPassword {
if err == services.ErrUserNotExists || err == services.ErrUserWrongPassword {
ctx.AbortWithError(400, err)
return
}

View File

@ -1,12 +1,13 @@
package src
package middleware
import (
"backend/src/services"
"fmt"
"github.com/gin-gonic/gin"
)
func NewAuthMiddleware(userService UserService) gin.HandlerFunc {
func NewAuthMiddleware(userService services.UserService) gin.HandlerFunc {
return func(ctx *gin.Context) {
token := ctx.GetHeader("X-Auth")
if token == "" {
@ -15,7 +16,7 @@ func NewAuthMiddleware(userService UserService) gin.HandlerFunc {
}
user, err := userService.ValidateToken(ctx, token)
if err == ErrUserWrongToken || err == ErrUserNotExists {
if err == services.ErrUserWrongToken || err == services.ErrUserNotExists {
ctx.AbortWithError(403, err)
return
}

View File

@ -1,15 +0,0 @@
package src
type UserDTO struct {
Id string
Login string
Secret string
Name string
}
type UserDAO struct {
Id string `json:"id"`
Login string `json:"login"`
Secret string `json:"secret"`
Name string `json:"name"`
}

8
src/models/user.go Normal file
View File

@ -0,0 +1,8 @@
package models
type UserDTO struct {
Id string
Login string
Secret string
Name string
}

View File

@ -1,4 +1,4 @@
package src
package repo
import (
"sync"

78
src/repo/user_repo.go Normal file
View File

@ -0,0 +1,78 @@
package repo
import (
"backend/src/models"
"context"
"database/sql"
"errors"
)
// type userDAO struct {
// Id string `json:"id"`
// Login string `json:"login"`
// Secret string `json:"secret"`
// Name string `json:"name"`
// }
type UserRepo interface {
CreateUser(ctx context.Context, dto models.UserDTO) (*models.UserDTO, error)
GetUserById(ctx context.Context, id string) (*models.UserDTO, error)
GetUserByLogin(ctx context.Context, login string) (*models.UserDTO, error)
}
func NewUserRepo(db *sql.DB) UserRepo {
return &userRepo{db}
}
type userRepo struct {
db *sql.DB
}
func (u *userRepo) CreateUser(ctx context.Context, dto models.UserDTO) (*models.UserDTO, error) {
query := `insert into users (login, secret, name) values ($1, $2, $3) returning id;`
row := u.db.QueryRowContext(ctx, query, dto.Login, dto.Secret, dto.Name)
id := ""
if err := row.Scan(&id); err != nil {
return nil, err
}
return &models.UserDTO{
Id: id,
Login: dto.Login,
Secret: dto.Secret,
Name: dto.Name,
}, nil
}
func (u *userRepo) GetUserById(ctx context.Context, id string) (*models.UserDTO, error) {
query := `select id, login, secret, name from users where id = $1;`
row := u.db.QueryRowContext(ctx, query, id)
dto := &models.UserDTO{}
err := row.Scan(&dto.Id, &dto.Login, &dto.Secret, &dto.Name)
if err == nil {
return dto, nil
}
if errors.Is(err, sql.ErrNoRows) {
return nil, nil
}
return nil, err
}
func (u *userRepo) GetUserByLogin(ctx context.Context, login string) (*models.UserDTO, error) {
query := `select id, login, secret, name from users where login = $1;`
row := u.db.QueryRowContext(ctx, query, login)
dto := &models.UserDTO{}
err := row.Scan(&dto.Id, &dto.Login, &dto.Secret, &dto.Name)
if err == nil {
return dto, nil
}
if errors.Is(err, sql.ErrNoRows) {
return nil, nil
}
return nil, err
}

View File

@ -1,6 +1,9 @@
package src
package services
import (
"backend/src/models"
"backend/src/repo"
"backend/src/utils"
"context"
"fmt"
)
@ -15,9 +18,9 @@ var (
)
type UserService interface {
CreateUser(ctx context.Context, params UserCreateParams) (*UserDTO, error)
CreateUser(ctx context.Context, params UserCreateParams) (*models.UserDTO, error)
AuthenticateUser(ctx context.Context, login, password string) (string, error)
ValidateToken(ctx context.Context, tokenStr string) (*UserDTO, error)
ValidateToken(ctx context.Context, tokenStr string) (*models.UserDTO, error)
}
func NewUserService(deps UserServiceDeps) UserService {
@ -25,10 +28,10 @@ func NewUserService(deps UserServiceDeps) UserService {
}
type UserServiceDeps struct {
Db DB
Jwt JwtUtil
Password PasswordUtil
Cache Cache[string, UserDTO]
Jwt utils.JwtUtil
Password utils.PasswordUtil
UserRepo repo.UserRepo
UserCache repo.Cache[string, models.UserDTO]
}
type userService struct {
@ -41,8 +44,8 @@ type UserCreateParams struct {
Name string
}
func (u *userService) CreateUser(ctx context.Context, params UserCreateParams) (*UserDTO, error) {
exisitngUser, err := u.deps.Db.GetUserByLogin(ctx, params.Login)
func (u *userService) CreateUser(ctx context.Context, params UserCreateParams) (*models.UserDTO, error) {
exisitngUser, err := u.deps.UserRepo.GetUserByLogin(ctx, params.Login)
if err != nil {
return nil, err
}
@ -59,24 +62,24 @@ func (u *userService) CreateUser(ctx context.Context, params UserCreateParams) (
return nil, err
}
user := UserDTO{
user := models.UserDTO{
Login: params.Login,
Secret: string(secret),
Name: params.Name,
}
result, err := u.deps.Db.CreateUser(ctx, user)
result, err := u.deps.UserRepo.CreateUser(ctx, user)
if err != nil {
return nil, err
}
u.deps.Cache.Set(result.Id, *result, -1)
u.deps.UserCache.Set(result.Id, *result, -1)
return result, nil
}
func (u *userService) AuthenticateUser(ctx context.Context, login, password string) (string, error) {
user, err := u.deps.Db.GetUserByLogin(ctx, login)
user, err := u.deps.UserRepo.GetUserByLogin(ctx, login)
if err != nil {
return "", err
}
@ -88,22 +91,23 @@ func (u *userService) AuthenticateUser(ctx context.Context, login, password stri
return "", ErrUserWrongPassword
}
jwt, err := u.deps.Jwt.Create(*user)
payload := utils.JwtPayload{UserId: user.Id}
jwt, err := u.deps.Jwt.Create(payload)
if err != nil {
return "", err
}
u.deps.Cache.Set(user.Id, *user, -1)
u.deps.UserCache.Set(user.Id, *user, -1)
return jwt, nil
}
func (u *userService) getUserById(ctx context.Context, userId string) (*UserDTO, error) {
if user, ok := u.deps.Cache.Get(userId); ok {
func (u *userService) getUserById(ctx context.Context, userId string) (*models.UserDTO, error) {
if user, ok := u.deps.UserCache.Get(userId); ok {
return &user, nil
}
user, err := u.deps.Db.GetUserById(ctx, userId)
user, err := u.deps.UserRepo.GetUserById(ctx, userId)
if err != nil {
return nil, err
}
@ -111,12 +115,12 @@ func (u *userService) getUserById(ctx context.Context, userId string) (*UserDTO,
return nil, ErrUserNotExists
}
u.deps.Cache.Set(user.Id, *user, -1)
u.deps.UserCache.Set(user.Id, *user, -1)
return user, nil
}
func (u *userService) ValidateToken(ctx context.Context, tokenStr string) (*UserDTO, error) {
func (u *userService) ValidateToken(ctx context.Context, tokenStr string) (*models.UserDTO, error) {
payload, err := u.deps.Jwt.Parse(tokenStr)
if err != nil {
return nil, ErrUserWrongToken

View File

@ -1,4 +1,4 @@
package src
package utils
import (
"crypto/rsa"
@ -8,12 +8,16 @@ import (
)
type JwtPayload struct {
jwt.RegisteredClaims
UserId string `json:"userId"`
}
type jwtClaims struct {
jwt.RegisteredClaims
JwtPayload
}
type JwtUtil interface {
Create(user UserDTO) (string, error)
Create(payload JwtPayload) (string, error)
Parse(tokenStr string) (JwtPayload, error)
}
@ -27,9 +31,9 @@ type jwtUtil struct {
privateKey *rsa.PrivateKey
}
func (j *jwtUtil) Create(user UserDTO) (string, error) {
payload := &JwtPayload{UserId: user.Id}
token := jwt.NewWithClaims(jwt.SigningMethodRS256, payload)
func (j *jwtUtil) Create(payload JwtPayload) (string, error) {
claims := &jwtClaims{JwtPayload: payload}
token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
tokenStr, err := token.SignedString(j.privateKey)
if err != nil {
return "", err
@ -38,15 +42,15 @@ func (j *jwtUtil) Create(user UserDTO) (string, error) {
}
func (j *jwtUtil) Parse(tokenStr string) (JwtPayload, error) {
token, err := jwt.ParseWithClaims(tokenStr, &JwtPayload{}, func(t *jwt.Token) (interface{}, error) {
token, err := jwt.ParseWithClaims(tokenStr, &jwtClaims{}, func(t *jwt.Token) (interface{}, error) {
return &j.privateKey.PublicKey, nil
})
if err != nil {
return JwtPayload{}, err
}
if payload, ok := token.Claims.(*JwtPayload); ok {
return *payload, nil
if claims, ok := token.Claims.(*jwtClaims); ok {
return claims.JwtPayload, nil
}
return JwtPayload{}, fmt.Errorf("cant get payload")

View File

@ -1,4 +1,4 @@
package src
package utils
import (
"fmt"