Skip to content

Commit

Permalink
feat: add migrations and godoc comments
Browse files Browse the repository at this point in the history
  • Loading branch information
marrgancovka committed Mar 1, 2024
1 parent 4eafdce commit a1e93b9
Show file tree
Hide file tree
Showing 12 changed files with 111 additions and 117 deletions.
7 changes: 5 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,8 @@ lint:
test:
go test -race ./...

dev-compose:
docker compose -f "dev-docker-compose.yaml" up -d
dev-compose-up:
docker compose -f "dev-docker-compose.yaml" up -d

dev-compose-down:
docker compose -f "dev-docker-compose.yaml" down
12 changes: 4 additions & 8 deletions internal/models/auth.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
package models

import "github.com/satori/uuid"

type JwtPayload struct {
ID uuid.UUID
Login string
}

// UserLoginData represents user information for login and signup
type UserLoginData struct {
Login string `json:"login"`
// Login stands for users nickname
Login string `json:"login"`
// Password stands for users password
Password string `json:"password"`
}
11 changes: 7 additions & 4 deletions internal/models/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@ import (
"github.com/satori/uuid"
)

// User represents user information
type User struct {
ID uuid.UUID `json:"id"`
Login string `json:"login"`
Phone string `json:"phone"`
PasswordHash string `json:"-"`
// ID uniquely identifies the user.
ID uuid.UUID `json:"id"`
// Login is the username of the user.
Login string `json:"login"`
// PasswordHash is the hashed password of the user.
PasswordHash string `json:"-"`
}
7 changes: 7 additions & 0 deletions internal/pkg/auth/delivery/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,18 @@ import (
"time"
)

// AuthHandler handles HTTP requests for user authentication.
type AuthHandler struct {
// uc represents the usecase interface for authentication.
uc auth.AuthUsecase
}

// NewAuthHandler creates a new instance of AuthHandler.
func NewAuthHandler(uc auth.AuthUsecase) *AuthHandler {
return &AuthHandler{uc: uc}
}

// SignUp handles the request for registering a new user.
func (h *AuthHandler) SignUp(w http.ResponseWriter, r *http.Request) {
data := models.UserLoginData{}

Expand All @@ -38,6 +42,7 @@ func (h *AuthHandler) SignUp(w http.ResponseWriter, r *http.Request) {
}
}

// Login handles the request for user login.
func (h *AuthHandler) Login(w http.ResponseWriter, r *http.Request) {
data := models.UserLoginData{}
if err := utils.ReadRequestData(r, &data); err != nil {
Expand All @@ -57,6 +62,7 @@ func (h *AuthHandler) Login(w http.ResponseWriter, r *http.Request) {
}
}

// Logout handles the request for user logout.
func (h *AuthHandler) Logout(w http.ResponseWriter, r *http.Request) {
http.SetCookie(w, &http.Cookie{
Name: middleware.CookieName,
Expand All @@ -65,6 +71,7 @@ func (h *AuthHandler) Logout(w http.ResponseWriter, r *http.Request) {
})
}

// tokenCookie creates a new cookie for storing the authentication token.
func tokenCookie(name, token string, exp time.Time) *http.Cookie {
return &http.Cookie{
Name: name,
Expand Down
41 changes: 2 additions & 39 deletions internal/pkg/auth/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,52 +6,15 @@ import (
"time"
)

// AuthUsecase represents the usecase interface for authentication.
type AuthUsecase interface {
SignUp(context.Context, *models.UserLoginData) (*models.User, string, time.Time, error)
Login(context.Context, *models.UserLoginData) (*models.User, string, time.Time, error)
}

// AuthRepo represents the repository interface for authentication.
type AuthRepo interface {
CreateUser(ctx context.Context, newUser *models.User) error
CheckUser(ctx context.Context, login string, passwordHash string) (*models.User, error)
GetUserByLogin(cts context.Context, login string) (*models.User, error)
}

// func (r *User) Register(email, password string) (*User, error) {

// var err error
// passwordHash, err := bcrypt.GenerateFromPassword([]byte(password), 12)

// if err != nil {
// return &User{}, err
// }

// user := &User{
// ID: generateID(),
// CreatedAt: time.Now(),
// Email: email,
// PasswordHash: passwordHash,
// }

// user.JWTSession, _ = generateJWT(user.ID)
// _, err = r.Create(r)

// return user, err
// }

// func (r *User) GetByEmail (e,ail string) (*User, error) {

// }

// func (r *User) Create(user *User) error {
// _, err :=
// }

// func generateID() int64 {
// return 10
// }

// func generateJWT(id int64) ([]byte, error) {
// var err error
// return []byte("123"), err
// }
13 changes: 9 additions & 4 deletions internal/pkg/auth/repo/postgres.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,38 +7,43 @@ import (
"errors"
)

// AuthRepo represents a repository for authentication.
type AuthRepo struct {
db *sql.DB
}

// NewRepository creates a new instance of AuthRepo.
func NewRepository(db *sql.DB) *AuthRepo {
return &AuthRepo{db: db}
}

// CreateUser creates a new user in the database.
func (r *AuthRepo) CreateUser(ctx context.Context, user *models.User) error {
insert := `INSERT INTO users (id, login, phone, password_hash) VALUES ($1, $2, $3, $4)`
insert := `INSERT INTO users (id, login, password_hash) VALUES ($1, $2, $3, $4)`

if _, err := r.db.ExecContext(ctx, insert, user.ID, user.Login, user.Phone, user.PasswordHash); err != nil {
if _, err := r.db.ExecContext(ctx, insert, user.ID, user.Login, user.PasswordHash); err != nil {
return err
}
return nil
}

// GetUserByLogin retrieves a user from the database by their login.
func (r *AuthRepo) GetUserByLogin(ctx context.Context, login string) (*models.User, error) {
query := `SELECT * FROM users WHERE login = $1`
query := `SELECT id, login FROM users WHERE login = $1`

res := r.db.QueryRowContext(ctx, query, login)

user := &models.User{
Login: login,
}
if err := res.Scan(&user.ID, &user.Login, &user.Phone, &user.PasswordHash); err != nil {
if err := res.Scan(&user.ID, &user.Login, &user.PasswordHash); err != nil {
return nil, err
}

return user, nil
}

// CheckUser checks if the user with the given login and password hash exists in the database.
func (r *AuthRepo) CheckUser(ctx context.Context, login string, passwordHash string) (*models.User, error) {
user, err := r.GetUserByLogin(ctx, login)
if err != nil {
Expand Down
12 changes: 8 additions & 4 deletions internal/pkg/auth/usecase/usecase.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,56 +3,60 @@ package usecase
import (
"2024_1_TeaStealers/internal/models"
"2024_1_TeaStealers/internal/pkg/auth"
"2024_1_TeaStealers/internal/pkg/middleware"
"2024_1_TeaStealers/internal/pkg/jwt"
"context"
"crypto/sha1"
"encoding/hex"
"github.com/satori/uuid"
"time"
)

// AuthUsecase represents the usecase for authentication.
type AuthUsecase struct {
repo auth.AuthRepo
}

// NewAuthUsecase creates a new instance of AuthUsecase.
func NewAuthUsecase(repo auth.AuthRepo) *AuthUsecase {
return &AuthUsecase{repo: repo}
}

// SignUp handles the user registration process.
func (u *AuthUsecase) SignUp(ctx context.Context, data *models.UserLoginData) (*models.User, string, time.Time, error) {
newUser := &models.User{
ID: uuid.NewV4(),
Login: data.Login,
Phone: "",
PasswordHash: generateHashString(data.Password),
}

if err := u.repo.CreateUser(ctx, newUser); err != nil {
return nil, "", time.Now(), err
}

token, exp, err := middleware.GenerateToken(newUser)
token, exp, err := jwt.GenerateToken(newUser)
if err != nil {
return nil, "", time.Now(), err
}

return newUser, token, exp, nil
}

// Login handles the user login process.
func (u *AuthUsecase) Login(ctx context.Context, data *models.UserLoginData) (*models.User, string, time.Time, error) {
user, err := u.repo.CheckUser(ctx, data.Login, generateHashString(data.Password))
if err != nil {
return nil, "", time.Now(), err
}

token, exp, err := middleware.GenerateToken(user)
token, exp, err := jwt.GenerateToken(user)
if err != nil {
return nil, "", time.Now(), err
}

return user, token, exp, nil
}

// generateHashString returns a hash string for the given input string.
func generateHashString(s string) string {
h := sha1.New()
h.Write([]byte(s))
Expand Down
54 changes: 54 additions & 0 deletions internal/pkg/jwt/jwt.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package jwt

import (
"2024_1_TeaStealers/internal/models"
"errors"
"fmt"
"github.com/golang-jwt/jwt/v5"
"github.com/satori/uuid"
"os"
"time"
)

// GenerateToken returns a new JWT token for the given user.
func GenerateToken(user *models.User) (string, time.Time, error) {
exp := time.Now().Add(time.Hour * 24)
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
"id": user.ID,
"login": user.Login,
"exp": exp.Unix(),
})
tokenStr, err := token.SignedString([]byte(os.Getenv("JWT_SECRET")))
if err != nil {
return "", time.Now(), err
}
return tokenStr, exp, nil
}

// ParseToken parses the provided JWT token string and returns the parsed token.
func ParseToken(token string) (*jwt.Token, error) {
return jwt.Parse(token, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
return []byte(os.Getenv("JWT_SECRET")), nil
})
}

// ParseId parses the user ID from the JWT token claims.
func ParseId(claims *jwt.Token) (uuid.UUID, error) {
payloadMap, ok := claims.Claims.(jwt.MapClaims)
if !ok {
return uuid.Nil, errors.New("invalid claims")
}
idStr, ok := payloadMap["id"].(string)
if !ok {
return uuid.Nil, errors.New("incorrect id")
}
id, err := uuid.FromString(idStr)
if err != nil {
return uuid.Nil, errors.New("incorrect id")
}

return id, nil
}
Loading

0 comments on commit a1e93b9

Please sign in to comment.