From 7d7f54261b1fe1db10b04d1b69ed12ec1590517e Mon Sep 17 00:00:00 2001 From: AmirMahdi Qiasvand Date: Tue, 9 Sep 2025 16:57:34 +0330 Subject: [PATCH] fix: auth flow fixed, add getOrCreate method for user --- internal/api/http/handlers/auth_handler.go | 8 ++++++- internal/api/http/setup.go | 21 ++++++++++++----- internal/app/app.go | 2 ++ internal/domain/user.go | 1 + internal/repository/storage/types/base.go | 10 +++++++- internal/repository/storage/types/user.go | 2 ++ internal/repository/storage/user_repo.go | 27 ++++++++++++++++++++-- internal/usecase/auth_service.go | 10 ++++---- 8 files changed, 65 insertions(+), 16 deletions(-) diff --git a/internal/api/http/handlers/auth_handler.go b/internal/api/http/handlers/auth_handler.go index 383e41a..4fb2d2d 100644 --- a/internal/api/http/handlers/auth_handler.go +++ b/internal/api/http/handlers/auth_handler.go @@ -73,7 +73,7 @@ func (h *AuthHandler) Authenticate(c *fiber.Ctx) error { c.Context(), req.PubKey, req.Signature, - // add chainID to cfg + //TODO: add chainID to cfg 1, c.IP(), c.Get("User-Agent"), @@ -90,3 +90,9 @@ func (h *AuthHandler) Authenticate(c *fiber.Ctx) error { ExpiresAt: userToken.ExpiresAt, }) } + +func (h *AuthHandler) HelloWorld(c *fiber.Ctx) error { + return c.Status(fiber.StatusOK).JSON(fiber.Map{ + "message": "Hello, World!", + }) +} diff --git a/internal/api/http/setup.go b/internal/api/http/setup.go index bbbee41..84b219a 100644 --- a/internal/api/http/setup.go +++ b/internal/api/http/setup.go @@ -4,6 +4,7 @@ import ( "backend/config" "backend/docs" httpHandlers "backend/internal/api/http/handlers" + "backend/internal/api/http/middlewares" "backend/internal/app" "fmt" "log" @@ -14,19 +15,27 @@ import ( ) func Run(cfg config.Server, app *app.AppContainer) { - router := fiber.New() + fiberApp := fiber.New() - api := router.Group("/api") + // Serve static files (HTML, CSS, JS) + fiberApp.Static("/", "./") + api := fiberApp.Group("/api") + // register routes here registerPublicRoutes(api, app) - docs.SwaggerInfo.Host = "" - docs.SwaggerInfo.Schemes = []string{} + docs.SwaggerInfo.Host = fmt.Sprintf("%s:%d", cfg.Host, cfg.Port) docs.SwaggerInfo.BasePath = "/api" + docs.SwaggerInfo.Schemes = []string{"http", "https"} - router.Get("/swagger/*", adaptor.HTTPHandler(httpSwagger.Handler())) + api.Get("/swagger/*", adaptor.HTTPHandler(httpSwagger.Handler())) - log.Fatal(router.Listen(fmt.Sprintf("%s:%d", cfg.Host, cfg.Port))) + api.Get("/hello", middlewares.JWTAuthMiddleware([]byte("Secret")), func(c *fiber.Ctx) error { + return c.SendString("Hello, World!") + }) + + log.Printf("Server starting on %s:%d", cfg.Host, cfg.Port) + log.Fatal(fiberApp.Listen(fmt.Sprintf("%s:%d", cfg.Host, cfg.Port))) } func registerPublicRoutes(router fiber.Router, app *app.AppContainer) { diff --git a/internal/app/app.go b/internal/app/app.go index c457e52..0e5fa2a 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -4,6 +4,7 @@ import ( "backend/config" "backend/internal/repository/cache" "backend/internal/repository/storage" + "backend/internal/repository/storage/types" "backend/internal/usecase" cachePackage "backend/pkg/cache" "backend/pkg/postgres" @@ -32,6 +33,7 @@ func NewAppContainer(cfg config.Config) (*AppContainer, error) { if err != nil { return nil, fmt.Errorf("failed to connect to database: %w", err) } + dbConn.AutoMigrate(&types.User{}) redis, err := cachePackage.NewRedis(cfg.Redis) if err != nil { diff --git a/internal/domain/user.go b/internal/domain/user.go index 06e728b..398a12d 100644 --- a/internal/domain/user.go +++ b/internal/domain/user.go @@ -28,6 +28,7 @@ type UserRepo interface { GetByID(ctx context.Context, id uuid.UUID) (*User, error) GetByPhone(ctx context.Context, phone string) (*User, error) GetByPubKey(ctx context.Context, pubKey string) (*User, error) + GetOrCreateUserByPubKey(ctx context.Context, pubKey string) (*User, error) Update(ctx context.Context, user *User) error Delete(ctx context.Context, id uuid.UUID) error } diff --git a/internal/repository/storage/types/base.go b/internal/repository/storage/types/base.go index 0f041cd..a82d061 100644 --- a/internal/repository/storage/types/base.go +++ b/internal/repository/storage/types/base.go @@ -4,11 +4,19 @@ import ( "time" "github.com/google/uuid" + "gorm.io/gorm" ) type Base struct { - ID uuid.UUID + ID uuid.UUID `gorm:"type:uuid;default:uuid_generate_v4();primaryKey"` CreatedAt time.Time UpdatedAt time.Time DeletedAt *time.Time } + +func (b *Base) BeforeCreate(tx *gorm.DB) (err error) { + if b.ID == uuid.Nil { + b.ID = uuid.New() + } + return +} diff --git a/internal/repository/storage/types/user.go b/internal/repository/storage/types/user.go index a7042de..78c97b3 100644 --- a/internal/repository/storage/types/user.go +++ b/internal/repository/storage/types/user.go @@ -27,6 +27,7 @@ func CastUserToStorage(u domain.User) *User { UpdatedAt: u.UpdatedAt, DeletedAt: u.DeletedAt, }, + PubKey: u.PubKey, Name: u.Name, LastName: u.LastName, Phone: u.PhoneNumber, @@ -40,6 +41,7 @@ func CastUserToStorage(u domain.User) *User { func CastUserToDomain(u User) *domain.User { return &domain.User{ ID: u.ID, + PubKey: u.PubKey, Name: u.Name, LastName: u.LastName, PhoneNumber: u.Phone, diff --git a/internal/repository/storage/user_repo.go b/internal/repository/storage/user_repo.go index a7d8ff1..ea42c86 100644 --- a/internal/repository/storage/user_repo.go +++ b/internal/repository/storage/user_repo.go @@ -20,8 +20,12 @@ func NewUserRepository(db *gorm.DB) domain.UserRepo { } func (r *UserRepository) Create(ctx context.Context, user *domain.User) error { - var model types.User - return r.db.WithContext(ctx).Create(&model).Error + model := types.CastUserToStorage(*user) + if err := r.db.WithContext(ctx).Create(model).Error; err != nil { + return err + } + *user = *types.CastUserToDomain(*model) + return nil } func (r *UserRepository) GetByID(ctx context.Context, id uuid.UUID) (*domain.User, error) { @@ -56,3 +60,22 @@ func (r *UserRepository) GetByPubKey(ctx context.Context, pubKey string) (*domai } return types.CastUserToDomain(user), nil } + +func (r *UserRepository) GetOrCreateUserByPubKey(ctx context.Context, pubKey string) (*domain.User, error) { + var user types.User + err := r.db.WithContext(ctx).First(&user, "pub_key = ?", pubKey).Error + if err == nil { + return types.CastUserToDomain(user), nil + } + if err != gorm.ErrRecordNotFound { + return nil, err + } + + user = types.User{ + PubKey: pubKey, + } + if err := r.db.WithContext(ctx).Create(&user).Error; err != nil { + return nil, err + } + return types.CastUserToDomain(user), nil +} diff --git a/internal/usecase/auth_service.go b/internal/usecase/auth_service.go index 275db71..77ab744 100644 --- a/internal/usecase/auth_service.go +++ b/internal/usecase/auth_service.go @@ -98,16 +98,14 @@ func (s *authService) Authenticate(ctx context.Context, pubKey string, signature return nil, fmt.Errorf("failed to delete challenge: %w", err) } - user, err := s.userRepo.GetByPubKey(ctx, pubKey) + user, err := s.userRepo.GetOrCreateUserByPubKey(ctx, pubKey) if err != nil { - return nil, err - } - if user == nil { - return nil, domain.ErrUserNotFound + return nil, fmt.Errorf("failed to get or create user: %w", err) } + user.UpdateLastLogin() if err := s.userRepo.Update(ctx, user); err != nil { - return nil, err + return nil, fmt.Errorf("failed to update user: %w", err) } expiresAt := time.Now().Add(time.Duration(s.cfg.TokenExpMinutes) * time.Minute)