Migrate Auth/Contractor/Document/Notification/Subscription services to ctx
Backend CI / Test (push) Has been cancelled
Backend CI / Contract Tests (push) Has been cancelled
Backend CI / Build (push) Has been cancelled
Backend CI / Lint (push) Has been cancelled
Backend CI / Secret Scanning (push) Has been cancelled

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:
Trey t
2026-04-25 16:26:21 -05:00
parent 65a9aae4e5
commit e881d37de0
20 changed files with 529 additions and 522 deletions
+85 -85
View File
@@ -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)
}