Migrate Auth/Contractor/Document/Notification/Subscription services to ctx
Every public method on these five services now takes ctx context.Context as the first arg and routes its repo calls through .WithContext(ctx). With TaskService and ResidenceService already migrated, this means every in-process service that touches Postgres now produces a flame graph in Jaeger where the SQL spans nest under the parent HTTP request span. Endpoints now fully traced (HTTP → service → SQL): - /api/auth/login, /register, /logout, /me, /verify-email, /resend-verification - /api/auth/forgot-password, /verify-reset, /reset-password, /update-profile - /api/contractors/* (CRUD + favorite + by-residence + tasks) - /api/documents/* (CRUD + activate/deactivate + image upload/delete) - /api/notifications/* (list, count, mark-read, prefs, devices) - /api/subscription/* (status, purchase, cancel, triggers, promotions) - All previously-migrated /api/tasks/* and /api/residences/* paths Internal helpers also threaded: - TaskService.sendTaskCompletedNotification → forwards ctx - TaskService.UpdateUserTimezone → forwards ctx to NotificationService - ResidenceService.CreateResidence → forwards ctx to SubscriptionService.CheckLimit - NotificationService.registerAPNSDevice / registerGCMDevice → both take ctx ~75 method signatures, ~120 handler/test call sites updated. Tests pass green; the only failure is the pre-existing flaky TaskHandler_QuickComplete SQLite race that fails ~60% of runs on master. Step 3 of the observability plan is now genuinely complete: every API endpoint backed by a Go service emits a per-request flame graph with HTTP → service → SQL spans, plus B2/APNs/FCM/asynq spans where applicable. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -57,14 +57,14 @@ func (s *AuthService) SetNotificationRepository(notificationRepo *repositories.N
|
||||
}
|
||||
|
||||
// Login authenticates a user and returns a token
|
||||
func (s *AuthService) Login(req *requests.LoginRequest) (*responses.LoginResponse, error) {
|
||||
func (s *AuthService) Login(ctx context.Context, req *requests.LoginRequest) (*responses.LoginResponse, error) {
|
||||
// Find user by username or email
|
||||
identifier := req.Username
|
||||
if identifier == "" {
|
||||
identifier = req.Email
|
||||
}
|
||||
|
||||
user, err := s.userRepo.FindByUsernameOrEmail(identifier)
|
||||
user, err := s.userRepo.WithContext(ctx).FindByUsernameOrEmail(identifier)
|
||||
if err != nil {
|
||||
if errors.Is(err, repositories.ErrUserNotFound) {
|
||||
return nil, apperrors.Unauthorized("error.invalid_credentials")
|
||||
@@ -83,13 +83,13 @@ func (s *AuthService) Login(req *requests.LoginRequest) (*responses.LoginRespons
|
||||
}
|
||||
|
||||
// Get or create auth token
|
||||
token, err := s.userRepo.GetOrCreateToken(user.ID)
|
||||
token, err := s.userRepo.WithContext(ctx).GetOrCreateToken(user.ID)
|
||||
if err != nil {
|
||||
return nil, apperrors.Internal(err)
|
||||
}
|
||||
|
||||
// Update last login
|
||||
if err := s.userRepo.UpdateLastLogin(user.ID); err != nil {
|
||||
if err := s.userRepo.WithContext(ctx).UpdateLastLogin(user.ID); err != nil {
|
||||
// Log error but don't fail the login
|
||||
log.Warn().Err(err).Uint("user_id", user.ID).Msg("Failed to update last login")
|
||||
}
|
||||
@@ -103,9 +103,9 @@ func (s *AuthService) Login(req *requests.LoginRequest) (*responses.LoginRespons
|
||||
// Register creates a new user account.
|
||||
// F-10: User creation, profile creation, notification preferences, and confirmation code
|
||||
// are wrapped in a transaction for atomicity.
|
||||
func (s *AuthService) Register(req *requests.RegisterRequest) (*responses.RegisterResponse, string, error) {
|
||||
func (s *AuthService) Register(ctx context.Context, req *requests.RegisterRequest) (*responses.RegisterResponse, string, error) {
|
||||
// Check if username exists
|
||||
exists, err := s.userRepo.ExistsByUsername(req.Username)
|
||||
exists, err := s.userRepo.WithContext(ctx).ExistsByUsername(req.Username)
|
||||
if err != nil {
|
||||
return nil, "", apperrors.Internal(err)
|
||||
}
|
||||
@@ -114,7 +114,7 @@ func (s *AuthService) Register(req *requests.RegisterRequest) (*responses.Regist
|
||||
}
|
||||
|
||||
// Check if email exists
|
||||
exists, err = s.userRepo.ExistsByEmail(req.Email)
|
||||
exists, err = s.userRepo.WithContext(ctx).ExistsByEmail(req.Email)
|
||||
if err != nil {
|
||||
return nil, "", apperrors.Internal(err)
|
||||
}
|
||||
@@ -146,7 +146,7 @@ func (s *AuthService) Register(req *requests.RegisterRequest) (*responses.Regist
|
||||
expiresAt := time.Now().UTC().Add(s.cfg.Security.ConfirmationExpiry)
|
||||
|
||||
// Wrap user creation + profile + notification preferences + confirmation code in a transaction
|
||||
txErr := s.userRepo.Transaction(func(txRepo *repositories.UserRepository) error {
|
||||
txErr := s.userRepo.WithContext(ctx).Transaction(func(txRepo *repositories.UserRepository) error {
|
||||
// Save user
|
||||
if err := txRepo.Create(user); err != nil {
|
||||
return err
|
||||
@@ -159,7 +159,7 @@ func (s *AuthService) Register(req *requests.RegisterRequest) (*responses.Regist
|
||||
|
||||
// Create notification preferences with all options enabled
|
||||
if s.notificationRepo != nil {
|
||||
if _, err := s.notificationRepo.GetOrCreatePreferences(user.ID); err != nil {
|
||||
if _, err := s.notificationRepo.WithContext(ctx).GetOrCreatePreferences(user.ID); err != nil {
|
||||
log.Warn().Err(err).Uint("user_id", user.ID).Msg("Failed to create notification preferences during registration")
|
||||
}
|
||||
}
|
||||
@@ -176,7 +176,7 @@ func (s *AuthService) Register(req *requests.RegisterRequest) (*responses.Regist
|
||||
}
|
||||
|
||||
// Create auth token (outside transaction since token generation is idempotent)
|
||||
token, err := s.userRepo.GetOrCreateToken(user.ID)
|
||||
token, err := s.userRepo.WithContext(ctx).GetOrCreateToken(user.ID)
|
||||
if err != nil {
|
||||
return nil, "", apperrors.Internal(err)
|
||||
}
|
||||
@@ -192,7 +192,7 @@ func (s *AuthService) Register(req *requests.RegisterRequest) (*responses.Regist
|
||||
// - If token is expired (> expiryDays old), returns error (must re-login).
|
||||
// - If token is in the renewal window (> refreshDays old), generates a new token.
|
||||
// - If token is still fresh (< refreshDays old), returns the existing token (no-op).
|
||||
func (s *AuthService) RefreshToken(tokenKey string, userID uint) (*responses.RefreshTokenResponse, error) {
|
||||
func (s *AuthService) RefreshToken(ctx context.Context, tokenKey string, userID uint) (*responses.RefreshTokenResponse, error) {
|
||||
expiryDays := s.cfg.Security.TokenExpiryDays
|
||||
if expiryDays <= 0 {
|
||||
expiryDays = 90
|
||||
@@ -203,7 +203,7 @@ func (s *AuthService) RefreshToken(tokenKey string, userID uint) (*responses.Ref
|
||||
}
|
||||
|
||||
// Look up the token
|
||||
authToken, err := s.userRepo.FindTokenByKey(tokenKey)
|
||||
authToken, err := s.userRepo.WithContext(ctx).FindTokenByKey(tokenKey)
|
||||
if err != nil {
|
||||
return nil, apperrors.Unauthorized("error.invalid_token")
|
||||
}
|
||||
@@ -232,12 +232,12 @@ func (s *AuthService) RefreshToken(tokenKey string, userID uint) (*responses.Ref
|
||||
|
||||
// Token is in the renewal window — generate a new one
|
||||
// Delete the old token
|
||||
if err := s.userRepo.DeleteToken(tokenKey); err != nil {
|
||||
if err := s.userRepo.WithContext(ctx).DeleteToken(tokenKey); err != nil {
|
||||
log.Warn().Err(err).Str("token", tokenKey[:8]+"...").Msg("Failed to delete old token during refresh")
|
||||
}
|
||||
|
||||
// Create a new token
|
||||
newToken, err := s.userRepo.CreateToken(userID)
|
||||
newToken, err := s.userRepo.WithContext(ctx).CreateToken(userID)
|
||||
if err != nil {
|
||||
return nil, apperrors.Internal(err)
|
||||
}
|
||||
@@ -249,18 +249,18 @@ func (s *AuthService) RefreshToken(tokenKey string, userID uint) (*responses.Ref
|
||||
}
|
||||
|
||||
// Logout invalidates a user's token
|
||||
func (s *AuthService) Logout(token string) error {
|
||||
return s.userRepo.DeleteToken(token)
|
||||
func (s *AuthService) Logout(ctx context.Context, token string) error {
|
||||
return s.userRepo.WithContext(ctx).DeleteToken(token)
|
||||
}
|
||||
|
||||
// GetCurrentUser returns the current authenticated user with profile
|
||||
func (s *AuthService) GetCurrentUser(userID uint) (*responses.CurrentUserResponse, error) {
|
||||
user, err := s.userRepo.FindByIDWithProfile(userID)
|
||||
func (s *AuthService) GetCurrentUser(ctx context.Context, userID uint) (*responses.CurrentUserResponse, error) {
|
||||
user, err := s.userRepo.WithContext(ctx).FindByIDWithProfile(userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
authProvider, err := s.userRepo.FindAuthProvider(userID)
|
||||
authProvider, err := s.userRepo.WithContext(ctx).FindAuthProvider(userID)
|
||||
if err != nil {
|
||||
// Log but don't fail - default to "email"
|
||||
log.Warn().Err(err).Uint("user_id", userID).Msg("Failed to determine auth provider")
|
||||
@@ -275,9 +275,9 @@ func (s *AuthService) GetCurrentUser(userID uint) (*responses.CurrentUserRespons
|
||||
// For email auth users, password verification is required.
|
||||
// For social auth users, confirmation string "DELETE" is required.
|
||||
// Returns a list of file URLs that need to be deleted from disk.
|
||||
func (s *AuthService) DeleteAccount(userID uint, password, confirmation *string) ([]string, error) {
|
||||
func (s *AuthService) DeleteAccount(ctx context.Context, userID uint, password, confirmation *string) ([]string, error) {
|
||||
// Fetch user
|
||||
user, err := s.userRepo.FindByID(userID)
|
||||
user, err := s.userRepo.WithContext(ctx).FindByID(userID)
|
||||
if err != nil {
|
||||
if errors.Is(err, repositories.ErrUserNotFound) {
|
||||
return nil, apperrors.NotFound("error.user_not_found")
|
||||
@@ -286,7 +286,7 @@ func (s *AuthService) DeleteAccount(userID uint, password, confirmation *string)
|
||||
}
|
||||
|
||||
// Determine auth provider
|
||||
authProvider, err := s.userRepo.FindAuthProvider(userID)
|
||||
authProvider, err := s.userRepo.WithContext(ctx).FindAuthProvider(userID)
|
||||
if err != nil {
|
||||
return nil, apperrors.Internal(err)
|
||||
}
|
||||
@@ -308,7 +308,7 @@ func (s *AuthService) DeleteAccount(userID uint, password, confirmation *string)
|
||||
|
||||
// Start transaction and cascade delete
|
||||
var fileURLs []string
|
||||
txErr := s.userRepo.Transaction(func(txRepo *repositories.UserRepository) error {
|
||||
txErr := s.userRepo.WithContext(ctx).Transaction(func(txRepo *repositories.UserRepository) error {
|
||||
urls, err := txRepo.DeleteUserCascade(userID)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -324,15 +324,15 @@ func (s *AuthService) DeleteAccount(userID uint, password, confirmation *string)
|
||||
}
|
||||
|
||||
// UpdateProfile updates a user's profile
|
||||
func (s *AuthService) UpdateProfile(userID uint, req *requests.UpdateProfileRequest) (*responses.CurrentUserResponse, error) {
|
||||
user, err := s.userRepo.FindByID(userID)
|
||||
func (s *AuthService) UpdateProfile(ctx context.Context, userID uint, req *requests.UpdateProfileRequest) (*responses.CurrentUserResponse, error) {
|
||||
user, err := s.userRepo.WithContext(ctx).FindByID(userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Check if new email is taken (if email is being changed)
|
||||
if req.Email != nil && *req.Email != user.Email {
|
||||
exists, err := s.userRepo.ExistsByEmail(*req.Email)
|
||||
exists, err := s.userRepo.WithContext(ctx).ExistsByEmail(*req.Email)
|
||||
if err != nil {
|
||||
return nil, apperrors.Internal(err)
|
||||
}
|
||||
@@ -349,17 +349,17 @@ func (s *AuthService) UpdateProfile(userID uint, req *requests.UpdateProfileRequ
|
||||
user.LastName = *req.LastName
|
||||
}
|
||||
|
||||
if err := s.userRepo.Update(user); err != nil {
|
||||
if err := s.userRepo.WithContext(ctx).Update(user); err != nil {
|
||||
return nil, apperrors.Internal(err)
|
||||
}
|
||||
|
||||
// Reload with profile
|
||||
user, err = s.userRepo.FindByIDWithProfile(userID)
|
||||
user, err = s.userRepo.WithContext(ctx).FindByIDWithProfile(userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
authProvider, err := s.userRepo.FindAuthProvider(userID)
|
||||
authProvider, err := s.userRepo.WithContext(ctx).FindAuthProvider(userID)
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Uint("user_id", userID).Msg("Failed to determine auth provider")
|
||||
authProvider = "email"
|
||||
@@ -370,9 +370,9 @@ func (s *AuthService) UpdateProfile(userID uint, req *requests.UpdateProfileRequ
|
||||
}
|
||||
|
||||
// VerifyEmail verifies a user's email with a confirmation code
|
||||
func (s *AuthService) VerifyEmail(userID uint, code string) error {
|
||||
func (s *AuthService) VerifyEmail(ctx context.Context, userID uint, code string) error {
|
||||
// Get user profile
|
||||
profile, err := s.userRepo.GetOrCreateProfile(userID)
|
||||
profile, err := s.userRepo.WithContext(ctx).GetOrCreateProfile(userID)
|
||||
if err != nil {
|
||||
return apperrors.Internal(err)
|
||||
}
|
||||
@@ -384,14 +384,14 @@ func (s *AuthService) VerifyEmail(userID uint, code string) error {
|
||||
|
||||
// Check for test code when DEBUG_FIXED_CODES is enabled
|
||||
if s.cfg.Server.DebugFixedCodes && code == "123456" {
|
||||
if err := s.userRepo.SetProfileVerified(userID, true); err != nil {
|
||||
if err := s.userRepo.WithContext(ctx).SetProfileVerified(userID, true); err != nil {
|
||||
return apperrors.Internal(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Find and validate confirmation code
|
||||
confirmCode, err := s.userRepo.FindConfirmationCode(userID, code)
|
||||
confirmCode, err := s.userRepo.WithContext(ctx).FindConfirmationCode(userID, code)
|
||||
if err != nil {
|
||||
if errors.Is(err, repositories.ErrCodeNotFound) {
|
||||
return apperrors.BadRequest("error.invalid_verification_code")
|
||||
@@ -403,12 +403,12 @@ func (s *AuthService) VerifyEmail(userID uint, code string) error {
|
||||
}
|
||||
|
||||
// Mark code as used
|
||||
if err := s.userRepo.MarkConfirmationCodeUsed(confirmCode.ID); err != nil {
|
||||
if err := s.userRepo.WithContext(ctx).MarkConfirmationCodeUsed(confirmCode.ID); err != nil {
|
||||
return apperrors.Internal(err)
|
||||
}
|
||||
|
||||
// Set profile as verified
|
||||
if err := s.userRepo.SetProfileVerified(userID, true); err != nil {
|
||||
if err := s.userRepo.WithContext(ctx).SetProfileVerified(userID, true); err != nil {
|
||||
return apperrors.Internal(err)
|
||||
}
|
||||
|
||||
@@ -416,9 +416,9 @@ func (s *AuthService) VerifyEmail(userID uint, code string) error {
|
||||
}
|
||||
|
||||
// ResendVerificationCode creates and returns a new verification code
|
||||
func (s *AuthService) ResendVerificationCode(userID uint) (string, error) {
|
||||
func (s *AuthService) ResendVerificationCode(ctx context.Context, userID uint) (string, error) {
|
||||
// Get user profile
|
||||
profile, err := s.userRepo.GetOrCreateProfile(userID)
|
||||
profile, err := s.userRepo.WithContext(ctx).GetOrCreateProfile(userID)
|
||||
if err != nil {
|
||||
return "", apperrors.Internal(err)
|
||||
}
|
||||
@@ -437,7 +437,7 @@ func (s *AuthService) ResendVerificationCode(userID uint) (string, error) {
|
||||
}
|
||||
expiresAt := time.Now().UTC().Add(s.cfg.Security.ConfirmationExpiry)
|
||||
|
||||
if _, err := s.userRepo.CreateConfirmationCode(userID, code, expiresAt); err != nil {
|
||||
if _, err := s.userRepo.WithContext(ctx).CreateConfirmationCode(userID, code, expiresAt); err != nil {
|
||||
return "", apperrors.Internal(err)
|
||||
}
|
||||
|
||||
@@ -445,9 +445,9 @@ func (s *AuthService) ResendVerificationCode(userID uint) (string, error) {
|
||||
}
|
||||
|
||||
// ForgotPassword initiates the password reset process
|
||||
func (s *AuthService) ForgotPassword(email string) (string, *models.User, error) {
|
||||
func (s *AuthService) ForgotPassword(ctx context.Context, email string) (string, *models.User, error) {
|
||||
// Find user by email
|
||||
user, err := s.userRepo.FindByEmail(email)
|
||||
user, err := s.userRepo.WithContext(ctx).FindByEmail(email)
|
||||
if err != nil {
|
||||
if errors.Is(err, repositories.ErrUserNotFound) {
|
||||
// Don't reveal that the email doesn't exist
|
||||
@@ -457,7 +457,7 @@ func (s *AuthService) ForgotPassword(email string) (string, *models.User, error)
|
||||
}
|
||||
|
||||
// Check rate limit
|
||||
count, err := s.userRepo.CountRecentPasswordResetRequests(user.ID)
|
||||
count, err := s.userRepo.WithContext(ctx).CountRecentPasswordResetRequests(user.ID)
|
||||
if err != nil {
|
||||
return "", nil, apperrors.Internal(err)
|
||||
}
|
||||
@@ -481,7 +481,7 @@ func (s *AuthService) ForgotPassword(email string) (string, *models.User, error)
|
||||
return "", nil, apperrors.Internal(err)
|
||||
}
|
||||
|
||||
if _, err := s.userRepo.CreatePasswordResetCode(user.ID, string(codeHash), resetToken, expiresAt); err != nil {
|
||||
if _, err := s.userRepo.WithContext(ctx).CreatePasswordResetCode(user.ID, string(codeHash), resetToken, expiresAt); err != nil {
|
||||
return "", nil, apperrors.Internal(err)
|
||||
}
|
||||
|
||||
@@ -489,9 +489,9 @@ func (s *AuthService) ForgotPassword(email string) (string, *models.User, error)
|
||||
}
|
||||
|
||||
// VerifyResetCode verifies a password reset code and returns a reset token
|
||||
func (s *AuthService) VerifyResetCode(email, code string) (string, error) {
|
||||
func (s *AuthService) VerifyResetCode(ctx context.Context, email, code string) (string, error) {
|
||||
// Find the reset code
|
||||
resetCode, user, err := s.userRepo.FindPasswordResetCodeByEmail(email)
|
||||
resetCode, user, err := s.userRepo.WithContext(ctx).FindPasswordResetCodeByEmail(email)
|
||||
if err != nil {
|
||||
if errors.Is(err, repositories.ErrUserNotFound) || errors.Is(err, repositories.ErrCodeNotFound) {
|
||||
return "", apperrors.BadRequest("error.invalid_verification_code")
|
||||
@@ -507,7 +507,7 @@ func (s *AuthService) VerifyResetCode(email, code string) (string, error) {
|
||||
// Verify the code
|
||||
if !resetCode.CheckCode(code) {
|
||||
// Increment attempts
|
||||
s.userRepo.IncrementResetCodeAttempts(resetCode.ID)
|
||||
s.userRepo.WithContext(ctx).IncrementResetCodeAttempts(resetCode.ID)
|
||||
return "", apperrors.BadRequest("error.invalid_verification_code")
|
||||
}
|
||||
|
||||
@@ -528,9 +528,9 @@ func (s *AuthService) VerifyResetCode(email, code string) (string, error) {
|
||||
}
|
||||
|
||||
// ResetPassword resets the user's password using a reset token
|
||||
func (s *AuthService) ResetPassword(resetToken, newPassword string) error {
|
||||
func (s *AuthService) ResetPassword(ctx context.Context, resetToken, newPassword string) error {
|
||||
// Find the reset code by token
|
||||
resetCode, err := s.userRepo.FindPasswordResetCodeByToken(resetToken)
|
||||
resetCode, err := s.userRepo.WithContext(ctx).FindPasswordResetCodeByToken(resetToken)
|
||||
if err != nil {
|
||||
if errors.Is(err, repositories.ErrCodeNotFound) || errors.Is(err, repositories.ErrCodeExpired) {
|
||||
return apperrors.BadRequest("error.invalid_reset_token")
|
||||
@@ -539,7 +539,7 @@ func (s *AuthService) ResetPassword(resetToken, newPassword string) error {
|
||||
}
|
||||
|
||||
// Get the user
|
||||
user, err := s.userRepo.FindByID(resetCode.UserID)
|
||||
user, err := s.userRepo.WithContext(ctx).FindByID(resetCode.UserID)
|
||||
if err != nil {
|
||||
return apperrors.Internal(err)
|
||||
}
|
||||
@@ -549,18 +549,18 @@ func (s *AuthService) ResetPassword(resetToken, newPassword string) error {
|
||||
return apperrors.Internal(err)
|
||||
}
|
||||
|
||||
if err := s.userRepo.Update(user); err != nil {
|
||||
if err := s.userRepo.WithContext(ctx).Update(user); err != nil {
|
||||
return apperrors.Internal(err)
|
||||
}
|
||||
|
||||
// Mark reset code as used
|
||||
if err := s.userRepo.MarkPasswordResetCodeUsed(resetCode.ID); err != nil {
|
||||
if err := s.userRepo.WithContext(ctx).MarkPasswordResetCodeUsed(resetCode.ID); err != nil {
|
||||
// Log error but don't fail
|
||||
log.Warn().Err(err).Uint("reset_code_id", resetCode.ID).Msg("Failed to mark reset code as used")
|
||||
}
|
||||
|
||||
// Invalidate all existing tokens for this user (security measure)
|
||||
if err := s.userRepo.DeleteTokenByUserID(user.ID); err != nil {
|
||||
if err := s.userRepo.WithContext(ctx).DeleteTokenByUserID(user.ID); err != nil {
|
||||
// Log error but don't fail
|
||||
log.Warn().Err(err).Uint("user_id", user.ID).Msg("Failed to delete user tokens after password reset")
|
||||
}
|
||||
@@ -583,10 +583,10 @@ func (s *AuthService) AppleSignIn(ctx context.Context, appleAuth *AppleAuthServi
|
||||
}
|
||||
|
||||
// 2. Check if this Apple ID is already linked to an account
|
||||
existingAuth, err := s.userRepo.FindByAppleID(appleID)
|
||||
existingAuth, err := s.userRepo.WithContext(ctx).FindByAppleID(appleID)
|
||||
if err == nil && existingAuth != nil {
|
||||
// User already linked with this Apple ID - log them in
|
||||
user, err := s.userRepo.FindByIDWithProfile(existingAuth.UserID)
|
||||
user, err := s.userRepo.WithContext(ctx).FindByIDWithProfile(existingAuth.UserID)
|
||||
if err != nil {
|
||||
return nil, apperrors.Internal(err)
|
||||
}
|
||||
@@ -596,13 +596,13 @@ func (s *AuthService) AppleSignIn(ctx context.Context, appleAuth *AppleAuthServi
|
||||
}
|
||||
|
||||
// Get or create token
|
||||
token, err := s.userRepo.GetOrCreateToken(user.ID)
|
||||
token, err := s.userRepo.WithContext(ctx).GetOrCreateToken(user.ID)
|
||||
if err != nil {
|
||||
return nil, apperrors.Internal(err)
|
||||
}
|
||||
|
||||
// Update last login
|
||||
_ = s.userRepo.UpdateLastLogin(user.ID)
|
||||
_ = s.userRepo.WithContext(ctx).UpdateLastLogin(user.ID)
|
||||
|
||||
return &responses.AppleSignInResponse{
|
||||
Token: token.Key,
|
||||
@@ -614,7 +614,7 @@ func (s *AuthService) AppleSignIn(ctx context.Context, appleAuth *AppleAuthServi
|
||||
// 3. Check if email matches an existing user (for account linking)
|
||||
email := getEmailFromRequest(req.Email, claims.Email)
|
||||
if email != "" {
|
||||
existingUser, err := s.userRepo.FindByEmail(email)
|
||||
existingUser, err := s.userRepo.WithContext(ctx).FindByEmail(email)
|
||||
if err == nil && existingUser != nil {
|
||||
// S-06: Log auto-linking of social account to existing user
|
||||
log.Warn().
|
||||
@@ -630,24 +630,24 @@ func (s *AuthService) AppleSignIn(ctx context.Context, appleAuth *AppleAuthServi
|
||||
Email: email,
|
||||
IsPrivateEmail: isPrivateRelayEmail(email) || claims.IsPrivateRelayEmail(),
|
||||
}
|
||||
if err := s.userRepo.CreateAppleSocialAuth(appleAuthRecord); err != nil {
|
||||
if err := s.userRepo.WithContext(ctx).CreateAppleSocialAuth(appleAuthRecord); err != nil {
|
||||
return nil, apperrors.Internal(err)
|
||||
}
|
||||
|
||||
// Mark as verified since Apple verified the email
|
||||
_ = s.userRepo.SetProfileVerified(existingUser.ID, true)
|
||||
_ = s.userRepo.WithContext(ctx).SetProfileVerified(existingUser.ID, true)
|
||||
|
||||
// Get or create token
|
||||
token, err := s.userRepo.GetOrCreateToken(existingUser.ID)
|
||||
token, err := s.userRepo.WithContext(ctx).GetOrCreateToken(existingUser.ID)
|
||||
if err != nil {
|
||||
return nil, apperrors.Internal(err)
|
||||
}
|
||||
|
||||
// Update last login
|
||||
_ = s.userRepo.UpdateLastLogin(existingUser.ID)
|
||||
_ = s.userRepo.WithContext(ctx).UpdateLastLogin(existingUser.ID)
|
||||
|
||||
// B-08: Check error from FindByIDWithProfile
|
||||
existingUser, err = s.userRepo.FindByIDWithProfile(existingUser.ID)
|
||||
existingUser, err = s.userRepo.WithContext(ctx).FindByIDWithProfile(existingUser.ID)
|
||||
if err != nil {
|
||||
return nil, apperrors.Internal(err)
|
||||
}
|
||||
@@ -675,19 +675,19 @@ func (s *AuthService) AppleSignIn(ctx context.Context, appleAuth *AppleAuthServi
|
||||
randomPassword := generateResetToken()
|
||||
_ = user.SetPassword(randomPassword)
|
||||
|
||||
if err := s.userRepo.Create(user); err != nil {
|
||||
if err := s.userRepo.WithContext(ctx).Create(user); err != nil {
|
||||
return nil, apperrors.Internal(err)
|
||||
}
|
||||
|
||||
// Create profile (already verified since Apple verified)
|
||||
profile, _ := s.userRepo.GetOrCreateProfile(user.ID)
|
||||
profile, _ := s.userRepo.WithContext(ctx).GetOrCreateProfile(user.ID)
|
||||
if profile != nil {
|
||||
_ = s.userRepo.SetProfileVerified(user.ID, true)
|
||||
_ = s.userRepo.WithContext(ctx).SetProfileVerified(user.ID, true)
|
||||
}
|
||||
|
||||
// Create notification preferences with all options enabled
|
||||
if s.notificationRepo != nil {
|
||||
if _, err := s.notificationRepo.GetOrCreatePreferences(user.ID); err != nil {
|
||||
if _, err := s.notificationRepo.WithContext(ctx).GetOrCreatePreferences(user.ID); err != nil {
|
||||
log.Warn().Err(err).Uint("user_id", user.ID).Msg("Failed to create notification preferences for Apple Sign In user")
|
||||
}
|
||||
}
|
||||
@@ -699,18 +699,18 @@ func (s *AuthService) AppleSignIn(ctx context.Context, appleAuth *AppleAuthServi
|
||||
Email: getEmailOrDefault(email),
|
||||
IsPrivateEmail: isPrivateRelayEmail(email) || claims.IsPrivateRelayEmail(),
|
||||
}
|
||||
if err := s.userRepo.CreateAppleSocialAuth(appleAuthRecord); err != nil {
|
||||
if err := s.userRepo.WithContext(ctx).CreateAppleSocialAuth(appleAuthRecord); err != nil {
|
||||
return nil, apperrors.Internal(err)
|
||||
}
|
||||
|
||||
// Create token
|
||||
token, err := s.userRepo.GetOrCreateToken(user.ID)
|
||||
token, err := s.userRepo.WithContext(ctx).GetOrCreateToken(user.ID)
|
||||
if err != nil {
|
||||
return nil, apperrors.Internal(err)
|
||||
}
|
||||
|
||||
// B-08: Check error from FindByIDWithProfile
|
||||
user, err = s.userRepo.FindByIDWithProfile(user.ID)
|
||||
user, err = s.userRepo.WithContext(ctx).FindByIDWithProfile(user.ID)
|
||||
if err != nil {
|
||||
return nil, apperrors.Internal(err)
|
||||
}
|
||||
@@ -736,10 +736,10 @@ func (s *AuthService) GoogleSignIn(ctx context.Context, googleAuth *GoogleAuthSe
|
||||
}
|
||||
|
||||
// 2. Check if this Google ID is already linked to an account
|
||||
existingAuth, err := s.userRepo.FindByGoogleID(googleID)
|
||||
existingAuth, err := s.userRepo.WithContext(ctx).FindByGoogleID(googleID)
|
||||
if err == nil && existingAuth != nil {
|
||||
// User already linked with this Google ID - log them in
|
||||
user, err := s.userRepo.FindByIDWithProfile(existingAuth.UserID)
|
||||
user, err := s.userRepo.WithContext(ctx).FindByIDWithProfile(existingAuth.UserID)
|
||||
if err != nil {
|
||||
return nil, apperrors.Internal(err)
|
||||
}
|
||||
@@ -749,13 +749,13 @@ func (s *AuthService) GoogleSignIn(ctx context.Context, googleAuth *GoogleAuthSe
|
||||
}
|
||||
|
||||
// Get or create token
|
||||
token, err := s.userRepo.GetOrCreateToken(user.ID)
|
||||
token, err := s.userRepo.WithContext(ctx).GetOrCreateToken(user.ID)
|
||||
if err != nil {
|
||||
return nil, apperrors.Internal(err)
|
||||
}
|
||||
|
||||
// Update last login
|
||||
_ = s.userRepo.UpdateLastLogin(user.ID)
|
||||
_ = s.userRepo.WithContext(ctx).UpdateLastLogin(user.ID)
|
||||
|
||||
return &responses.GoogleSignInResponse{
|
||||
Token: token.Key,
|
||||
@@ -767,7 +767,7 @@ func (s *AuthService) GoogleSignIn(ctx context.Context, googleAuth *GoogleAuthSe
|
||||
// 3. Check if email matches an existing user (for account linking)
|
||||
email := tokenInfo.Email
|
||||
if email != "" {
|
||||
existingUser, err := s.userRepo.FindByEmail(email)
|
||||
existingUser, err := s.userRepo.WithContext(ctx).FindByEmail(email)
|
||||
if err == nil && existingUser != nil {
|
||||
// S-06: Log auto-linking of social account to existing user
|
||||
log.Warn().
|
||||
@@ -784,26 +784,26 @@ func (s *AuthService) GoogleSignIn(ctx context.Context, googleAuth *GoogleAuthSe
|
||||
Name: tokenInfo.Name,
|
||||
Picture: tokenInfo.Picture,
|
||||
}
|
||||
if err := s.userRepo.CreateGoogleSocialAuth(googleAuthRecord); err != nil {
|
||||
if err := s.userRepo.WithContext(ctx).CreateGoogleSocialAuth(googleAuthRecord); err != nil {
|
||||
return nil, apperrors.Internal(err)
|
||||
}
|
||||
|
||||
// Mark as verified since Google verified the email
|
||||
if tokenInfo.IsEmailVerified() {
|
||||
_ = s.userRepo.SetProfileVerified(existingUser.ID, true)
|
||||
_ = s.userRepo.WithContext(ctx).SetProfileVerified(existingUser.ID, true)
|
||||
}
|
||||
|
||||
// Get or create token
|
||||
token, err := s.userRepo.GetOrCreateToken(existingUser.ID)
|
||||
token, err := s.userRepo.WithContext(ctx).GetOrCreateToken(existingUser.ID)
|
||||
if err != nil {
|
||||
return nil, apperrors.Internal(err)
|
||||
}
|
||||
|
||||
// Update last login
|
||||
_ = s.userRepo.UpdateLastLogin(existingUser.ID)
|
||||
_ = s.userRepo.WithContext(ctx).UpdateLastLogin(existingUser.ID)
|
||||
|
||||
// B-08: Check error from FindByIDWithProfile
|
||||
existingUser, err = s.userRepo.FindByIDWithProfile(existingUser.ID)
|
||||
existingUser, err = s.userRepo.WithContext(ctx).FindByIDWithProfile(existingUser.ID)
|
||||
if err != nil {
|
||||
return nil, apperrors.Internal(err)
|
||||
}
|
||||
@@ -831,19 +831,19 @@ func (s *AuthService) GoogleSignIn(ctx context.Context, googleAuth *GoogleAuthSe
|
||||
randomPassword := generateResetToken()
|
||||
_ = user.SetPassword(randomPassword)
|
||||
|
||||
if err := s.userRepo.Create(user); err != nil {
|
||||
if err := s.userRepo.WithContext(ctx).Create(user); err != nil {
|
||||
return nil, apperrors.Internal(err)
|
||||
}
|
||||
|
||||
// Create profile (already verified if Google verified email)
|
||||
profile, _ := s.userRepo.GetOrCreateProfile(user.ID)
|
||||
profile, _ := s.userRepo.WithContext(ctx).GetOrCreateProfile(user.ID)
|
||||
if profile != nil && tokenInfo.IsEmailVerified() {
|
||||
_ = s.userRepo.SetProfileVerified(user.ID, true)
|
||||
_ = s.userRepo.WithContext(ctx).SetProfileVerified(user.ID, true)
|
||||
}
|
||||
|
||||
// Create notification preferences with all options enabled
|
||||
if s.notificationRepo != nil {
|
||||
if _, err := s.notificationRepo.GetOrCreatePreferences(user.ID); err != nil {
|
||||
if _, err := s.notificationRepo.WithContext(ctx).GetOrCreatePreferences(user.ID); err != nil {
|
||||
log.Warn().Err(err).Uint("user_id", user.ID).Msg("Failed to create notification preferences for Google Sign In user")
|
||||
}
|
||||
}
|
||||
@@ -856,18 +856,18 @@ func (s *AuthService) GoogleSignIn(ctx context.Context, googleAuth *GoogleAuthSe
|
||||
Name: tokenInfo.Name,
|
||||
Picture: tokenInfo.Picture,
|
||||
}
|
||||
if err := s.userRepo.CreateGoogleSocialAuth(googleAuthRecord); err != nil {
|
||||
if err := s.userRepo.WithContext(ctx).CreateGoogleSocialAuth(googleAuthRecord); err != nil {
|
||||
return nil, apperrors.Internal(err)
|
||||
}
|
||||
|
||||
// Create token
|
||||
token, err := s.userRepo.GetOrCreateToken(user.ID)
|
||||
token, err := s.userRepo.WithContext(ctx).GetOrCreateToken(user.ID)
|
||||
if err != nil {
|
||||
return nil, apperrors.Internal(err)
|
||||
}
|
||||
|
||||
// B-08: Check error from FindByIDWithProfile
|
||||
user, err = s.userRepo.FindByIDWithProfile(user.ID)
|
||||
user, err = s.userRepo.WithContext(ctx).FindByIDWithProfile(user.ID)
|
||||
if err != nil {
|
||||
return nil, apperrors.Internal(err)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user