Skip to content

Commit

Permalink
WIP: /signup work correctly
Browse files Browse the repository at this point in the history
  • Loading branch information
AnnHarvard committed Sep 28, 2024
1 parent eb2e19c commit bc023ea
Show file tree
Hide file tree
Showing 8 changed files with 287 additions and 1 deletion.
11 changes: 10 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1 +1,10 @@
module TripAdvisor
module 2024_2_ThereWillBeName

go 1.23.1

require (
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/lib/pq v1.10.9
)

require golang.org/x/crypto v0.27.0 // indirect
6 changes: 6 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
10 changes: 10 additions & 0 deletions internal/models/user.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package models

import "time"

type User struct {
ID int64 `json:"id" db:"id"`
Email string `json:"email" db:"email"`
Password string `json:"-" db:"password"`
CreatedAt time.Time `json:"created_at" db:"created_at"`
}
84 changes: 84 additions & 0 deletions internal/pkg/auth/delivery/http/handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package http

import (
"context"
"encoding/json"
"net/http"
"2024_2_ThereWillBeName/internal/models"
"2024_2_ThereWillBeName/internal/pkg/auth"
"2024_2_ThereWillBeName/internal/pkg/jwt"
)

type Handler struct {
usecase auth.AuthUsecase
jwt *jwt.JWT
}

func NewHandler(usecase auth.AuthUsecase, jwt *jwt.JWT) *Handler {
return &Handler{
usecase: usecase,
jwt: jwt,
}
}

func (h *Handler) SignUp(w http.ResponseWriter, r *http.Request) {
var user models.User
if err := json.NewDecoder(r.Body).Decode(&user); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}

if err := h.usecase.SignUp(context.Background(), user); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

w.WriteHeader(http.StatusCreated)
}

func (h *Handler) Login(w http.ResponseWriter, r *http.Request) {
var credentials struct {
Email string `json:"email"`
Password string `json:"password"`
}
if err := json.NewDecoder(r.Body).Decode(&credentials); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}

token, err := h.usecase.Login(context.Background(), credentials.Email, credentials.Password)
if err != nil {
http.Error(w, err.Error(), http.StatusUnauthorized)
return
}

http.SetCookie(w, &http.Cookie{
Name: "token",
Value: token,
Path: "/",
HttpOnly: true,
Secure: true,
})

w.WriteHeader(http.StatusOK)
}

func (h *Handler) Middleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
cookie, err := r.Cookie("token")
if err != nil {
http.Error(w, "Cookie not found", http.StatusUnauthorized)
return
}

_, err = h.jwt.ParseToken(cookie.Value)
if err != nil {
http.Error(w, "Invalid token", http.StatusUnauthorized)
return
}

//логика для работы с claims

next.ServeHTTP(w, r)
})
}
20 changes: 20 additions & 0 deletions internal/pkg/auth/interfaces.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package auth

import (
"context"
"2024_2_ThereWillBeName/internal/models"
)

type AuthUsecase interface {
SignUp(ctx context.Context, user models.User) error
Login(ctx context.Context, email, password string) (string, error) // Возвращает JWT токен
Logout(ctx context.Context, token string) error
}

type AuthRepo interface {
CreateUser(ctx context.Context, user models.User) error
GetUserByEmail(ctx context.Context, email string) (models.User, error)
UpdateUser(ctx context.Context, user models.User) error
DeleteUser(ctx context.Context, id string) error
GetUsers(ctx context.Context, count, offset int64) ([]models.User, error)
}
70 changes: 70 additions & 0 deletions internal/pkg/auth/repo/auth_repository.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package repo

import (
"context"
"database/sql"
"2024_2_ThereWillBeName/internal/models"
_ "github.com/lib/pq"
"time"
)

type RepositoryImpl struct {
db *sql.DB
}

func NewRepository(db *sql.DB) *RepositoryImpl {
return &RepositoryImpl{db: db}
}

func (r *RepositoryImpl) CreateUser(ctx context.Context, user models.User) error {
user.CreatedAt = time.Now()
query := "INSERT INTO users (email, password, created_at) VALUES ($1, $2, NOW())"
_, err := r.db.ExecContext(ctx, query, user.Email, user.Password, user.CreatedAt)
return err
}

func (r *RepositoryImpl) GetUserByEmail(ctx context.Context, email string) (models.User, error) {
var user models.User
query := "SELECT id, email, password, created_at FROM users WHERE email = $1"
row := r.db.QueryRowContext(ctx, query, email)
fmt.Println("1")

Check failure on line 30 in internal/pkg/auth/repo/auth_repository.go

View workflow job for this annotation

GitHub Actions / linters

undefined: fmt

Check failure on line 30 in internal/pkg/auth/repo/auth_repository.go

View workflow job for this annotation

GitHub Actions / linters

undefined: fmt
err := row.Scan(&user.ID, &user.Email, &user.Password, &user.CreatedAt)
if err != nil {
if err == sql.ErrNoRows {
return models.User{}, fmt.Errorf("user not found with email: %s", email)

Check failure on line 34 in internal/pkg/auth/repo/auth_repository.go

View workflow job for this annotation

GitHub Actions / linters

undefined: fmt (typecheck)

Check failure on line 34 in internal/pkg/auth/repo/auth_repository.go

View workflow job for this annotation

GitHub Actions / linters

undefined: fmt) (typecheck)
}
return models.User{}, err
}
return user, nil
}

func (r *RepositoryImpl) UpdateUser(ctx context.Context, user models.User) error {
query := "UPDATE users SET email = $1, password = $2 WHERE id = $3"
_, err := r.db.ExecContext(ctx, query, user.Email, user.Password, user.ID)
return err
}

func (r *RepositoryImpl) DeleteUser(ctx context.Context, id string) error {
query := "DELETE FROM users WHERE id = $1"
_, err := r.db.ExecContext(ctx, query, id)
return err
}

func (r *RepositoryImpl) GetUsers(ctx context.Context, count, offset int64) ([]models.User, error) {
query := "SELECT id, email, password, created_at FROM users LIMIT $1 OFFSET $2"
rows, err := r.db.QueryContext(ctx, query, count, offset)
if err != nil {
return nil, err
}
defer rows.Close()

var users []models.User
for rows.Next() {
var user models.User
if err := rows.Scan(&user.ID, &user.Email, &user.Password, &user.CreatedAt); err != nil {
return nil, err
}
users = append(users, user)
}
return users, nil
}
45 changes: 45 additions & 0 deletions internal/pkg/auth/usecase/auth_usecase.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package usecase

import (
"context"
"2024_2_ThereWillBeName/internal/models"
"2024_2_ThereWillBeName/internal/pkg/auth"
"2024_2_ThereWillBeName/internal/pkg/jwt"
"golang.org/x/crypto/bcrypt"
)

type AuthUsecaseImpl struct {
repo auth.AuthRepo
jwt *jwt.JWT
}

func NewAuthUsecase(repo auth.AuthRepo, jwt *jwt.JWT) *AuthUsecaseImpl {
return &AuthUsecaseImpl{
repo: repo,
jwt: jwt,
}
}

func (a *AuthUsecaseImpl) SignUp(ctx context.Context, user models.User) error {
hashedPassword, _ := bcrypt.GenerateFromPassword([]byte(user.Password), bcrypt.DefaultCost)
user.Password = string(hashedPassword)
return a.repo.CreateUser(ctx, user)
}

func (a *AuthUsecaseImpl) Login(ctx context.Context, email, password string) (string, error) {
user, err := a.repo.GetUserByEmail(ctx, email)
if err != nil {
return "", err
}

if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password)); err != nil {
return "", err
}

return a.jwt.GenerateToken(uint(user.ID), user.Email)
}

func (a *AuthUsecaseImpl) Logout(ctx context.Context, token string) error {

return nil
}
42 changes: 42 additions & 0 deletions internal/pkg/jwt/jwt.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package jwt

import (
"github.com/dgrijalva/jwt-go"
"time"
"fmt"
)

type JWT struct {
secret []byte
}

func NewJWT(secret string) *JWT {
return &JWT{
secret: []byte(secret),
}
}

func (j *JWT) GenerateToken(userID uint, email string) (string, error) {
claims := jwt.MapClaims{
"id": userID,
"email": email,
"exp": time.Now().Add(time.Hour * 24).Unix(),
}

token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString(j.secret)
}

func (j *JWT) ParseToken(token string) (map[string]interface{}, error) {
parsedToken, err := 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 j.secret, nil
})

if claims, ok := parsedToken.Claims.(jwt.MapClaims); ok && parsedToken.Valid {
return claims, nil
}
return nil, err
}

0 comments on commit bc023ea

Please sign in to comment.