package handlers import ( "context" "encoding/json" "fmt" "time" "git.optclblast.xyz/draincloud/draincloud-core/internal/common" "git.optclblast.xyz/draincloud/draincloud-core/internal/domain" "git.optclblast.xyz/draincloud/draincloud-core/internal/handler" "git.optclblast.xyz/draincloud/draincloud-core/internal/logger" "git.optclblast.xyz/draincloud/draincloud-core/internal/storage" "git.optclblast.xyz/draincloud/draincloud-core/internal/storage/models" "github.com/google/uuid" "golang.org/x/crypto/bcrypt" ) type RegisterHandler struct { *handler.BaseHandler authStorage storage.AuthStorage } func NewRegisterHandler( authStorage storage.AuthStorage, ) *RegisterHandler { h := &RegisterHandler{ authStorage: authStorage, BaseHandler: handler.New(). WithName("registerv1"). WithRequiredResolveParams(), } h.WithProcessFunc(h.process) return h } func (h *RegisterHandler) process(ctx context.Context, req *common.Request, w handler.Writer) error { regReq := new(domain.RegisterRequest) if err := json.Unmarshal(req.Body, regReq); err != nil { return err } resp, err := h.register(ctx, regReq, w) if err != nil { return fmt.Errorf("failed to register user: %w", err) } w.Write(ctx, resp) return nil } func (d *RegisterHandler) register( ctx context.Context, req *domain.RegisterRequest, w handler.Writer, ) (*domain.RegisterResponse, error) { if err := validateLoginAndPassword(req.Login, req.Password); err != nil { return nil, fmt.Errorf("invalid creds: %w", err) } passwordHash, err := bcrypt.GenerateFromPassword([]byte(req.Password), 10) if err != nil { logger.Error(ctx, "[register] failed to generate password hash", logger.Err(err)) return nil, fmt.Errorf("failed to generate password hash: %w", err) } userID, err := uuid.NewV7() if err != nil { return nil, fmt.Errorf("failed to generate user id: %w", err) } user := &models.User{ ID: userID, Username: req.Login, Login: req.Login, PasswordHash: passwordHash, } err = d.authStorage.AddUser(ctx, userID, user.Login, user.Username, user.PasswordHash) if err != nil { return nil, fmt.Errorf("failed to add new user: %w", err) } sessionCreatedAt := time.Now() sessionExpiredAt := sessionCreatedAt.Add(time.Hour * 24 * 7) sessionToken, err := generateSessionToken(100) if err != nil { return nil, fmt.Errorf("failed to generate a session token: %w", err) } w.SetCookie(sessionTokenCookie, sessionToken, int(sessionExpiredAt.Sub(sessionCreatedAt).Seconds()), "_path", "_domain", true, true) csrfToken, err := generateSessionToken(100) if err != nil { return nil, fmt.Errorf("failed to generate a csrf token: %w", err) } w.SetCookie(csrfTokenCookie, csrfToken, int(sessionExpiredAt.Sub(sessionCreatedAt).Seconds()), "_path", "_domain", true, false) sessionID, err := uuid.NewV7() if err != nil { return nil, fmt.Errorf("failed to generate session id: %w", err) } if _, err = d.authStorage.AddSession(ctx, &models.Session{ ID: sessionID, SessionToken: sessionToken, CsrfToken: csrfToken, UserID: user.ID, CreatedAt: sessionCreatedAt, ExpiredAt: sessionExpiredAt, }); err != nil { return nil, fmt.Errorf("failed to save session: %w", err) } return &domain.RegisterResponse{ Ok: true, }, nil }