Yimaru-BackEnd/internal/repository/auth.go

151 lines
3.8 KiB
Go

package repository
import (
"context"
"database/sql"
"errors"
"time"
dbgen "Yimaru-Backend/gen/db"
"Yimaru-Backend/internal/domain"
"Yimaru-Backend/internal/ports"
"Yimaru-Backend/internal/services/authentication"
"github.com/jackc/pgx/v5/pgtype"
)
// NewTokenStore returns a TokenStore implementation
func NewTokenStore(s *Store) ports.TokenStore {
return s
}
// CreateRefreshToken inserts a new refresh token into the database
func (s *Store) CreateRefreshToken(ctx context.Context, rt domain.RefreshToken) error {
rt.ExpiresAt = time.Now().Add(10 * time.Minute)
return s.queries.CreateRefreshToken(ctx, dbgen.CreateRefreshTokenParams{
UserID: rt.UserID,
Token: rt.Token,
ExpiresAt: pgtype.Timestamptz{
Time: rt.ExpiresAt,
Valid: true,
},
CreatedAt: pgtype.Timestamptz{
Time: time.Now(),
Valid: true,
},
Revoked: rt.Revoked,
})
}
// GetRefreshToken retrieves a refresh token by its token string
func (s *Store) GetRefreshToken(ctx context.Context, token string) (domain.RefreshToken, error) {
rf, err := s.queries.GetRefreshToken(ctx, token)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return domain.RefreshToken{}, authentication.ErrRefreshTokenNotFound
}
return domain.RefreshToken{}, err
}
return domain.RefreshToken{
Token: rf.Token,
UserID: rf.UserID,
CreatedAt: rf.CreatedAt.Time,
ExpiresAt: rf.ExpiresAt.Time,
Revoked: rf.Revoked,
}, nil
}
// GetRefreshTokenByUserID retrieves a refresh token for a specific user
func (s *Store) GetRefreshTokenByUserID(ctx context.Context, id int64) (domain.RefreshToken, error) {
rf, err := s.queries.GetRefreshTokenByUserID(ctx, id)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return domain.RefreshToken{}, authentication.ErrRefreshTokenNotFound
}
return domain.RefreshToken{}, err
}
return domain.RefreshToken{
Token: rf.Token,
UserID: rf.UserID,
CreatedAt: rf.CreatedAt.Time,
ExpiresAt: rf.ExpiresAt.Time,
Revoked: rf.Revoked,
}, nil
}
// RevokeRefreshToken marks a refresh token as revoked
func (s *Store) RevokeRefreshToken(ctx context.Context, token string) error {
return s.queries.RevokeRefreshToken(ctx, token)
}
// GetUserByEmailOrPhone retrieves a user by email or phone number and optional organization ID
func (s *Store) GetUserByEmailOrPhone(
ctx context.Context,
email string,
phone string,
) (domain.User, error) {
u, err := s.queries.GetUserByEmailPhone(ctx, dbgen.GetUserByEmailPhoneParams{
Email: pgtype.Text{
String: email,
Valid: email != "",
},
PhoneNumber: pgtype.Text{
String: phone,
Valid: phone != "",
},
// OrganizationID: pgtype.Int8{Int64: organizationID},
})
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return domain.User{}, authentication.ErrUserNotFound
}
return domain.User{}, err
}
var lastLogin *time.Time
if u.LastLogin.Valid {
lastLogin = &u.LastLogin.Time
}
var updatedAt *time.Time
if u.UpdatedAt.Valid {
updatedAt = &u.UpdatedAt.Time
}
return domain.User{
ID: u.ID,
FirstName: u.FirstName.String,
LastName: u.LastName.String,
// UserName: u.UserName,
Email: u.Email.String,
PhoneNumber: u.PhoneNumber.String,
Password: u.Password,
Role: domain.Role(u.Role),
Age: int(u.Age.Int32),
EducationLevel: u.EducationLevel.String,
Country: u.Country.String,
Region: u.Region.String,
EmailVerified: u.EmailVerified,
PhoneVerified: u.PhoneVerified,
Status: domain.UserStatus(u.Status),
LastLogin: lastLogin,
ProfileCompleted: u.ProfileCompleted.Bool,
ProfilePictureURL: u.ProfilePictureUrl.String,
PreferredLanguage: u.PreferredLanguage.String,
// OrganizationID: domain.ValidInt64{
// Value: u.OrganizationID.Int64,
// Valid: u.OrganizationID.Valid,
// },
CreatedAt: u.CreatedAt.Time,
UpdatedAt: updatedAt,
}, nil
}