Skip to content

Conversation

@capcom6
Copy link
Member

@capcom6 capcom6 commented Nov 25, 2025

Summary by CodeRabbit

  • New Features

    • Added a dedicated One-Time Password (OTP) authentication system with generation, validation, expiry and retry handling.
  • Configuration

    • Added OTP settings with configurable TTL (expiry) and retry attempts.
  • Authentication

    • Authentication flows updated to use the new OTP service for code generation and validation.
  • Changes

    • API example updated to use Basic authorization header instead of Bearer.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Nov 25, 2025

Walkthrough

Adds a new OTP module (config, domain, storage, service, FX wiring), moves one-time-code generation/validation from the auth module into that service, updates auth/handler/middleware signatures to accept contexts and user pointers, and adjusts the mobile API auth header example.

Changes

Cohort / File(s) Summary
OTP package
internal/sms-gateway/otp/config.go, internal/sms-gateway/otp/domain.go, internal/sms-gateway/otp/errors.go, internal/sms-gateway/otp/service.go, internal/sms-gateway/otp/storage.go, internal/sms-gateway/otp/module.go
New OTP module: Config (TTL, Retries) with Validate; Code domain type; error tokens; Service with Generate(ctx,userID) and Validate(ctx,code); Storage wrapper around cache; FX module wiring and logger.
Configuration & DI
configs/config.example.yml, internal/config/config.go, internal/config/module.go, internal/sms-gateway/app.go
Added otp config block to example YAML; added OTP field and defaults to top-level Config; provider mapping cfg.OTP → otp.Config registered; OTP FX module included in app wiring.
Auth module refactor
internal/sms-gateway/modules/auth/service.go, internal/sms-gateway/modules/auth/types.go, internal/sms-gateway/modules/auth/module.go
Removed in-file OneTimeCode, cache and lifecycle hooks; added otpSvc dependency; GenerateUserCode(ctx,userID) and AuthorizeUserByCode(ctx,code) now delegate to otp.Service; module no longer starts service via lifecycle hooks.
Handlers & middlewares
internal/sms-gateway/handlers/mobile.go, internal/sms-gateway/handlers/middlewares/userauth/userauth.go, internal/sms-gateway/handlers/middlewares/jwtauth/jwtauth.go
Updated calls to pass context to GenerateUserCode/AuthorizeUserByCode; SetUser/GetUser now use *users.User (pointer) and callers updated accordingly; JWT middleware passes pointer.
API spec
api/mobile.http
Switched Authorization example for GET /user/code from Bearer {{mobileToken}} to Basic {{credentials}}; original Bearer line retained as a comment.

Sequence Diagram(s)

sequenceDiagram
    actor Client
    participant MobileHandler as Mobile Handler
    participant AuthService as Auth Service
    participant OTPService as OTP Service
    participant Storage as Storage/Cache

    rect rgb(200,220,255)
    Note over MobileHandler,OTPService: Generate code flow
    Client->>MobileHandler: POST /user/code (Basic auth)
    MobileHandler->>AuthService: GenerateUserCode(ctx, userID)
    AuthService->>OTPService: Generate(ctx, userID)
    OTPService->>OTPService: generate 6-digit random code
    OTPService->>Storage: SetOrFail(ctx, code, userID, validUntil)
    Storage-->>OTPService: success / error
    OTPService-->>AuthService: Code{Code, ValidUntil} / error
    AuthService-->>MobileHandler: code / error
    MobileHandler-->>Client: response
    end

    rect rgb(220,200,255)
    Note over MobileHandler,OTPService: Validate code flow
    Client->>MobileHandler: authenticate with code
    MobileHandler->>AuthService: AuthorizeUserByCode(ctx, code)
    AuthService->>OTPService: Validate(ctx, code)
    OTPService->>Storage: GetAndDelete(ctx, code)
    Storage-->>OTPService: userID / error
    OTPService-->>AuthService: userID / error
    AuthService-->>MobileHandler: authenticated user / error
    MobileHandler-->>Client: token / error
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Pay attention to: crypto/rand usage and retry logic in otp/service.go
  • Storage wrapping and error conversions in otp/storage.go
  • Context propagation and updated function signatures across auth and handlers
  • DI wiring in internal/config/module.go and internal/sms-gateway/app.go

Possibly related PRs

  • #130 — Modifies mobile one-time-code flows; likely antecedent to extracting code logic into OTP service.
  • #117 — Changes userauth middleware signatures and usages, overlapping pointer/type updates.
  • #191 — Also updates userauth middleware handling for pointer-based users and SetUser/GetUser signatures.

Suggested labels

ready

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 46.15% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and accurately summarizes the main change: extracting OTP functionality from the auth module into a separate, dedicated package with its own configuration, service, storage, and module wiring.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch otp/introduce-package

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a0ea59b and 3c25b21.

📒 Files selected for processing (17)
  • api/mobile.http (1 hunks)
  • configs/config.example.yml (1 hunks)
  • internal/config/config.go (3 hunks)
  • internal/config/module.go (2 hunks)
  • internal/sms-gateway/app.go (2 hunks)
  • internal/sms-gateway/handlers/middlewares/jwtauth/jwtauth.go (1 hunks)
  • internal/sms-gateway/handlers/middlewares/userauth/userauth.go (3 hunks)
  • internal/sms-gateway/handlers/mobile.go (1 hunks)
  • internal/sms-gateway/modules/auth/module.go (0 hunks)
  • internal/sms-gateway/modules/auth/service.go (3 hunks)
  • internal/sms-gateway/modules/auth/types.go (0 hunks)
  • internal/sms-gateway/otp/config.go (1 hunks)
  • internal/sms-gateway/otp/domain.go (1 hunks)
  • internal/sms-gateway/otp/errors.go (1 hunks)
  • internal/sms-gateway/otp/module.go (1 hunks)
  • internal/sms-gateway/otp/service.go (1 hunks)
  • internal/sms-gateway/otp/storage.go (1 hunks)
💤 Files with no reviewable changes (2)
  • internal/sms-gateway/modules/auth/types.go
  • internal/sms-gateway/modules/auth/module.go
🚧 Files skipped from review as they are similar to previous changes (6)
  • internal/sms-gateway/app.go
  • internal/sms-gateway/otp/config.go
  • internal/config/module.go
  • api/mobile.http
  • internal/sms-gateway/handlers/middlewares/jwtauth/jwtauth.go
  • configs/config.example.yml
🧰 Additional context used
🧬 Code graph analysis (3)
internal/sms-gateway/handlers/middlewares/userauth/userauth.go (1)
internal/sms-gateway/users/domain.go (1)
  • User (5-10)
internal/sms-gateway/otp/service.go (6)
internal/config/config.go (1)
  • Config (12-23)
internal/sms-gateway/otp/config.go (1)
  • Config (9-12)
internal/sms-gateway/otp/storage.go (1)
  • Storage (11-13)
internal/sms-gateway/otp/errors.go (1)
  • ErrInitFailed (9-9)
internal/sms-gateway/otp/domain.go (1)
  • Code (6-9)
pkg/cache/options.go (1)
  • WithValidUntil (70-74)
internal/sms-gateway/modules/auth/service.go (1)
internal/sms-gateway/otp/domain.go (1)
  • Code (6-9)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: E2E
  • GitHub Check: Benchmark
  • GitHub Check: Analyze (go)
🔇 Additional comments (16)
internal/config/config.go (3)

22-22: LGTM!

The OTP field addition to the Config struct is well-placed and follows the established patterns for YAML tagging and inline comments.


94-97: LGTM!

The OTP struct definition is well-designed:

  • uint16 for TTL provides sufficient range (up to ~18 hours) for OTP expiration
  • uint8 for Retries allows ample retry attempts (up to 255)
  • YAML and envconfig tags follow established patterns

135-138: LGTM!

The default values are sensible and follow industry standards:

  • 300 seconds (5 minutes) is a typical OTP expiration time
  • 3 retries provides a good balance between usability and security
internal/sms-gateway/otp/domain.go (1)

1-9: LGTM! Clean domain definition.

The OTP domain type is well-defined with appropriate fields for code and expiration tracking.

internal/sms-gateway/otp/errors.go (1)

1-10: LGTM! Error definitions follow conventions.

The error variables are well-documented and appropriately scoped for configuration validation and initialization failures.

internal/sms-gateway/handlers/mobile.go (1)

201-212: LGTM! Context properly propagated.

The update to pass c.Context() to GenerateUserCode correctly enables context-aware code generation, supporting timeouts and cancellation.

internal/sms-gateway/otp/module.go (1)

9-22: LGTM! FX module wiring is correct.

The module properly encapsulates the cache and storage as private dependencies while exposing the OTP service publicly. The use of fx.Private at line 18 correctly scopes the cache and storage to the module, allowing NewService to consume them while keeping them hidden from other modules.

internal/sms-gateway/handlers/middlewares/userauth/userauth.go (3)

49-58: LGTM! Pointer-based user handling is consistent.

The change to pass user (a pointer) directly to SetUser aligns with the updated signature and maintains consistency across authentication flows.


66-86: LGTM! Context propagation and pointer handling are correct.

The middleware now properly propagates the request context to AuthorizeUserByCode and consistently uses pointer-based user objects.


88-110: LGTM! Pointer-based helpers are correctly implemented.

The SetUser and GetUser functions now consistently work with *users.User, with the type assertion and return logic properly updated to handle pointers.

internal/sms-gateway/modules/auth/service.go (3)

22-52: LGTM! OTP service properly integrated.

The auth service now correctly depends on the OTP service, with the constructor accepting and wiring the otpSvc dependency.


54-62: LGTM! Context properly propagated to OTP service.

The method correctly delegates code generation to the OTP service while propagating the caller's context for cancellation and timeout support.


111-124: LGTM! Code validation properly delegated to OTP service.

The method correctly validates the OTP code using the context-aware OTP service and returns the user pointer, maintaining consistency with the middleware's type expectations.

internal/sms-gateway/otp/storage.go (1)

1-41: LGTM! Storage wrapper is well-implemented.

The storage abstraction provides a clean string-based API over the cache interface with appropriate error wrapping for context. The implementation correctly delegates to the underlying cache while maintaining consistency.

internal/sms-gateway/otp/service.go (2)

22-44: LGTM! Constructor properly validates dependencies.

The constructor performs appropriate validation of the configuration and ensures required dependencies (storage, logger) are non-nil before initializing the service.


88-98: LGTM! Validation correctly delegates to storage.

The Validate method appropriately delegates to GetAndDelete, ensuring one-time use semantics by atomically retrieving and removing the code.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (5)
configs/config.example.yml (1)

41-43: Consider clarifying the "retries" comment.

The comment "otp generation retries" could be ambiguous. Does this refer to:

  • Retries for generating a unique OTP code (collision handling), or
  • Maximum validation attempts allowed per code?

Based on typical OTP implementations, this likely controls code generation collision handling, but a clearer comment would help users configure this correctly.

 otp: # otp config
   ttl: 300 # otp ttl in seconds [OTP__TTL]
-  retries: 3 # otp generation retries [OTP__RETRIES]
+  retries: 3 # otp code generation retries (collision handling) [OTP__RETRIES]
internal/sms-gateway/otp/errors.go (1)

1-8: Consider adding doc comments for exported error variables

ErrInvalidConfig and ErrInitFailed are exported but undocumented. Adding short comments will help tooling and make their intended usage (config validation vs. initialization) clearer.

-var (
-	ErrInvalidConfig = errors.New("invalid config")
-	ErrInitFailed    = errors.New("initialization failed")
-)
+var (
+	// ErrInvalidConfig indicates that the OTP configuration is invalid.
+	ErrInvalidConfig = errors.New("invalid config")
+
+	// ErrInitFailed indicates that the OTP service failed to initialize.
+	ErrInitFailed = errors.New("initialization failed")
+)
internal/config/config.go (1)

20-21: OTP config integration looks good; consider clarifying TTL units

The new OTP block on Config and its defaults (TTL=300, Retries=3) are consistent with the downstream otp.Config mapping and validation. To avoid confusion, you may want to clarify that OTP.TTL is in seconds, in line with other *Seconds fields:

-type OTP struct {
-	TTL     uint16 `yaml:"ttl" envconfig:"OTP__TTL"`
-	Retries uint8  `yaml:"retries" envconfig:"OTP__RETRIES"`
-}
+type OTP struct {
+	// TTL specifies OTP lifetime in seconds.
+	TTL     uint16 `yaml:"ttl" envconfig:"OTP__TTL"`
+	Retries uint8  `yaml:"retries" envconfig:"OTP__RETRIES"`
+}

Otherwise, the wiring into defaultConfig and the main Config struct looks solid.

Also applies to: 89-93, 128-131

internal/sms-gateway/otp/storage.go (1)

1-33: String-focused storage wrapper is fine for OTP use

The Storage wrapper correctly adapts the byte-based cache API to string values for OTP codes (SetOrFail, GetAndDelete, Cleanup all delegate cleanly). Only very minor nit is the receiver name cc (could be s or st), but that’s purely cosmetic.

internal/sms-gateway/modules/auth/service.go (1)

49-49: Consider handling the error from nanoid.Standard.

The error from nanoid.Standard(21) is silently ignored. While it's unlikely to fail with a valid length of 21, ignoring errors can mask issues during initialization. Consider logging or panicking on error since this is a critical initialization step.

-	idgen, _ := nanoid.Standard(21)
+	idgen, err := nanoid.Standard(21)
+	if err != nil {
+		panic(fmt.Sprintf("failed to initialize nanoid generator: %v", err))
+	}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7ebb0c7 and 68967a7.

📒 Files selected for processing (16)
  • api/mobile.http (1 hunks)
  • configs/config.example.yml (1 hunks)
  • internal/config/config.go (3 hunks)
  • internal/config/module.go (2 hunks)
  • internal/sms-gateway/app.go (2 hunks)
  • internal/sms-gateway/handlers/middlewares/userauth/userauth.go (1 hunks)
  • internal/sms-gateway/handlers/mobile.go (1 hunks)
  • internal/sms-gateway/modules/auth/module.go (1 hunks)
  • internal/sms-gateway/modules/auth/service.go (3 hunks)
  • internal/sms-gateway/modules/auth/types.go (0 hunks)
  • internal/sms-gateway/otp/config.go (1 hunks)
  • internal/sms-gateway/otp/domain.go (1 hunks)
  • internal/sms-gateway/otp/errors.go (1 hunks)
  • internal/sms-gateway/otp/module.go (1 hunks)
  • internal/sms-gateway/otp/service.go (1 hunks)
  • internal/sms-gateway/otp/storage.go (1 hunks)
💤 Files with no reviewable changes (1)
  • internal/sms-gateway/modules/auth/types.go
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-09-10T11:11:43.462Z
Learnt from: capcom6
Repo: android-sms-gateway/server PR: 178
File: pkg/cache/memory.go:59-74
Timestamp: 2025-09-10T11:11:43.462Z
Learning: In pkg/cache/memory.go, the cleanup method removes expired items first, then executes the provided callback function. This means methods like Drain that use cleanup don't need additional expiration filtering since expired items are already removed before the callback runs.

Applied to files:

  • internal/sms-gateway/modules/auth/service.go
📚 Learning: 2025-09-10T23:45:45.502Z
Learnt from: capcom6
Repo: android-sms-gateway/server PR: 178
File: internal/sms-gateway/online/module.go:14-16
Timestamp: 2025-09-10T23:45:45.502Z
Learning: fx.Decorate in uber-go/fx creates module-scoped decorated dependencies, not global modifications. Using fx.Decorate(func(log *zap.Logger) *zap.Logger { return log.Named("module_name") }) is a correct pattern for creating module-specific loggers that are scoped only to that module's dependency graph.

Applied to files:

  • internal/sms-gateway/modules/auth/module.go
🧬 Code graph analysis (8)
internal/sms-gateway/otp/errors.go (1)
internal/sms-gateway/modules/auth/service.go (1)
  • New (41-65)
internal/config/module.go (2)
internal/config/config.go (2)
  • Config (10-21)
  • OTP (89-92)
internal/sms-gateway/otp/config.go (1)
  • Config (8-11)
internal/sms-gateway/otp/module.go (3)
internal/sms-gateway/cache/factory.go (1)
  • Factory (16-18)
internal/sms-gateway/otp/storage.go (1)
  • NewStorage (15-17)
internal/sms-gateway/otp/service.go (1)
  • NewService (21-35)
internal/sms-gateway/modules/auth/service.go (4)
internal/sms-gateway/otp/service.go (1)
  • Service (13-19)
internal/sms-gateway/models/models.go (1)
  • User (17-23)
internal/sms-gateway/otp/config.go (1)
  • Config (8-11)
internal/sms-gateway/otp/domain.go (1)
  • Code (6-9)
internal/sms-gateway/modules/auth/module.go (1)
internal/sms-gateway/modules/auth/service.go (1)
  • New (41-65)
internal/sms-gateway/otp/service.go (6)
internal/config/config.go (1)
  • Config (10-21)
internal/sms-gateway/otp/config.go (1)
  • Config (8-11)
internal/sms-gateway/otp/storage.go (1)
  • Storage (10-12)
internal/sms-gateway/otp/errors.go (1)
  • ErrInitFailed (7-7)
internal/sms-gateway/otp/domain.go (1)
  • Code (6-9)
pkg/cache/options.go (1)
  • WithValidUntil (34-38)
internal/sms-gateway/otp/storage.go (1)
internal/config/config.go (1)
  • Cache (81-83)
internal/sms-gateway/app.go (2)
internal/config/module.go (1)
  • Module (23-134)
internal/sms-gateway/otp/module.go (1)
  • Module (9-19)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: E2E
  • GitHub Check: Benchmark
  • GitHub Check: Test
  • GitHub Check: Analyze (go)
🔇 Additional comments (12)
api/mobile.http (1)

11-13: LGTM!

The authorization change from Bearer to Basic for the /user/code endpoint aligns correctly with the OTP-based authentication flow. Basic auth with user credentials is the appropriate mechanism for requesting a one-time code, while Bearer tokens are used for subsequent authenticated requests.

internal/config/module.go (1)

128-133: LGTM!

The OTP config provider follows the established pattern in this module. The conversions from uint16 to time.Duration and uint8 to int are safe and consistent with how other time-based configurations are handled (e.g., push.Config, messages.Config).

internal/sms-gateway/modules/auth/module.go (1)

15-16: LGTM!

The provider ordering with newRepository before New (service constructor) is clean and reflects the dependency hierarchy. While fx handles actual initialization order based on the dependency graph, this explicit ordering improves readability. Based on learnings, the fx.Decorate pattern for module-scoped loggers is correctly applied here.

internal/sms-gateway/otp/domain.go (1)

5-9: LGTM!

Clean domain model for OTP codes. The struct appropriately exposes fields for cross-package use.

Minor consideration: the Code field name within a Code type leads to code.Code access patterns. An alternative like Value could be considered, but this is a stylistic preference and the current naming is acceptable.

internal/sms-gateway/handlers/mobile.go (1)

178-188: LGTM!

Good improvement passing c.Context() to GenerateUserCode. This enables:

  • Request cancellation propagation
  • Deadline/timeout handling
  • Distributed tracing support

The response mapping from *otp.Code to the DTO is clean and correct.

internal/sms-gateway/handlers/middlewares/userauth/userauth.go (1)

64-76: Context-aware auth call in NewCode looks correct

Passing c.Context() into AuthorizeUserByCode keeps cancellation/timeouts tied to the HTTP request and matches the updated auth service signature. No further changes needed here.

internal/sms-gateway/app.go (1)

22-58: OTP module wiring into Fx graph is consistent

Importing otp and adding otp.Module() to the main Module keeps OTP concerns nicely isolated while making the service injectable (e.g., into auth). Given otp.NewService can fail on invalid config, just make sure you’re happy with “fail-fast on startup” behavior if OTP config is misconfigured.

internal/sms-gateway/otp/module.go (1)

1-19: Fx wiring is idiomatic; just confirm cache type alignment

The OTP Fx module looks clean: named logger, a dedicated cache namespace via factory.New("otp"), NewStorage chained off that cache, and NewService provided separately, all marked fx.Private so internals don’t leak out of the module.

One thing to double-check: Module() uses internal/sms-gateway/cache.Factory while NewStorage depends on github.com/android-sms-gateway/server/pkg/cache.Cache. As long as internal/sms-gateway/cache.Cache is an alias or the same concrete type as pkg/cache.Cache, Fx will resolve this correctly. If they’re distinct types, NewStorage will never get its dependency.

If you haven’t already, it’s worth confirming those two Cache types are aligned.

internal/sms-gateway/otp/config.go (1)

1-23: OTP Config validation is straightforward and correct

Config.Validate cleanly enforces TTL > 0 and Retries > 0, wrapping ErrInvalidConfig so callers can distinguish config issues while still having a readable message. This fits well with the NewService constructor flow.

internal/sms-gateway/otp/service.go (1)

21-66: The for range over integer syntax is valid in Go 1.22+; no fix needed.

The repository uses Go 1.24.3, which supports "range over integers"—a feature introduced in Go 1.22 (released February 2024). The syntax for range s.cfg.Retries is correct and will compile as expected. No changes are required.

Likely an incorrect or invalid review comment.

internal/sms-gateway/modules/auth/service.go (2)

26-39: LGTM!

The Service struct is well-organized with clear separation of dependencies. The addition of otpSvc for delegating OTP operations is appropriate.


161-174: LGTM!

The refactored method correctly propagates context to otpSvc.Validate, has proper error wrapping, and the pointer return type allows for cleaner nil returns on error.

@capcom6 capcom6 force-pushed the otp/introduce-package branch from bf02218 to 22d772b Compare November 25, 2025 09:12
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
internal/sms-gateway/modules/auth/service.go (1)

26-39: Service wiring and caching look good; consider removing unused idgen

The updated Service struct and New constructor correctly wire in users, otpSvc, devicesSvc, onlineSvc, and logger, and initialize usersCache with a 1h TTL, which fits the existing auth caching pattern.

However, idgen is now initialized but no longer used anywhere in this file after the OTP refactor. Unless you plan to reintroduce ID-based auth codes, consider removing the idgen field and its initialization to keep the service lean.

Also applies to: 41-64

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 68967a7 and 22d772b.

📒 Files selected for processing (16)
  • api/mobile.http (1 hunks)
  • configs/config.example.yml (1 hunks)
  • internal/config/config.go (3 hunks)
  • internal/config/module.go (2 hunks)
  • internal/sms-gateway/app.go (2 hunks)
  • internal/sms-gateway/handlers/middlewares/userauth/userauth.go (1 hunks)
  • internal/sms-gateway/handlers/mobile.go (1 hunks)
  • internal/sms-gateway/modules/auth/module.go (1 hunks)
  • internal/sms-gateway/modules/auth/service.go (3 hunks)
  • internal/sms-gateway/modules/auth/types.go (0 hunks)
  • internal/sms-gateway/otp/config.go (1 hunks)
  • internal/sms-gateway/otp/domain.go (1 hunks)
  • internal/sms-gateway/otp/errors.go (1 hunks)
  • internal/sms-gateway/otp/module.go (1 hunks)
  • internal/sms-gateway/otp/service.go (1 hunks)
  • internal/sms-gateway/otp/storage.go (1 hunks)
💤 Files with no reviewable changes (1)
  • internal/sms-gateway/modules/auth/types.go
🚧 Files skipped from review as they are similar to previous changes (8)
  • internal/sms-gateway/otp/domain.go
  • internal/sms-gateway/handlers/mobile.go
  • internal/sms-gateway/otp/errors.go
  • internal/config/module.go
  • internal/sms-gateway/otp/storage.go
  • internal/sms-gateway/otp/module.go
  • internal/sms-gateway/otp/service.go
  • api/mobile.http
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-09-10T11:11:43.462Z
Learnt from: capcom6
Repo: android-sms-gateway/server PR: 178
File: pkg/cache/memory.go:59-74
Timestamp: 2025-09-10T11:11:43.462Z
Learning: In pkg/cache/memory.go, the cleanup method removes expired items first, then executes the provided callback function. This means methods like Drain that use cleanup don't need additional expiration filtering since expired items are already removed before the callback runs.

Applied to files:

  • internal/sms-gateway/modules/auth/service.go
📚 Learning: 2025-09-10T23:45:45.502Z
Learnt from: capcom6
Repo: android-sms-gateway/server PR: 178
File: internal/sms-gateway/online/module.go:14-16
Timestamp: 2025-09-10T23:45:45.502Z
Learning: fx.Decorate in uber-go/fx creates module-scoped decorated dependencies, not global modifications. Using fx.Decorate(func(log *zap.Logger) *zap.Logger { return log.Named("module_name") }) is a correct pattern for creating module-specific loggers that are scoped only to that module's dependency graph.

Applied to files:

  • internal/sms-gateway/modules/auth/module.go
🧬 Code graph analysis (3)
internal/sms-gateway/modules/auth/service.go (4)
internal/sms-gateway/otp/service.go (1)
  • Service (13-19)
internal/sms-gateway/cache/factory.go (1)
  • Cache (14-14)
internal/sms-gateway/otp/config.go (1)
  • Config (9-12)
internal/sms-gateway/otp/domain.go (1)
  • Code (6-9)
internal/sms-gateway/modules/auth/module.go (1)
internal/sms-gateway/modules/auth/service.go (1)
  • New (41-65)
internal/sms-gateway/otp/config.go (2)
internal/worker/config/types.go (1)
  • Duration (11-11)
internal/sms-gateway/otp/errors.go (1)
  • ErrInvalidConfig (7-7)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: E2E
  • GitHub Check: Benchmark
  • GitHub Check: Analyze (go)
🔇 Additional comments (7)
internal/sms-gateway/otp/config.go (1)

8-23: OTP Config and validation look consistent and robust

Config models TTL and retries cleanly, and Validate correctly enforces both > 0 while preserving ErrInvalidConfig via %w, which is useful for classification and wrapping.

configs/config.example.yml (1)

41-43: OTP example config matches Go config and defaults

The new otp block (ttl=300, retries=3) matches Config.OTP and defaultConfig, and the comments/env var hints are consistent with the struct tags.

internal/sms-gateway/modules/auth/module.go (1)

16-16: Auth module wiring remains correct with the new constructor

Keeping fx.Provide(New) here cleanly wires the updated New constructor (now depending on the OTP service and other deps) while preserving the existing module structure.

internal/sms-gateway/app.go (1)

22-22: OTP module is correctly wired into the application module graph

Importing internal/sms-gateway/otp and adding otp.Module() to the top-level Module cleanly integrates the new OTP subsystem without disturbing existing modules.

Also applies to: 57-57

internal/sms-gateway/handlers/middlewares/userauth/userauth.go (1)

75-82: Context-aware user authorization for Code header looks good

Passing c.Context() into AuthorizeUserByCode enables proper context propagation (cancellation, deadlines, tracing) through the auth/OTP stack while preserving the middleware flow.

internal/config/config.go (1)

20-21: OTP config struct and defaults integrate cleanly

Adding OTP to Config, defining OTP with ttl/retries fields, and initializing sensible defaults (300, 3) keeps the configuration surface consistent with the example YAML and the OTP package’s expectations.

Also applies to: 89-92, 128-131

internal/sms-gateway/modules/auth/service.go (1)

67-75: GenerateUserCode delegation to OTP service is straightforward

GenerateUserCode now simply delegates to s.otpSvc.Generate and wraps errors with contextual information, which keeps OTP concerns encapsulated in the OTP service and keeps the auth service thin.

@capcom6 capcom6 force-pushed the otp/introduce-package branch from 22d772b to c460972 Compare November 25, 2025 11:06
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
internal/sms-gateway/modules/auth/service.go (1)

156-169: Normalize error returns in AuthorizeUserByCode

To keep the contract simple (user is zero value whenever err != nil), consider returning models.User{} in the GetByID failure path as well, mirroring the validation error branch.

 func (s *Service) AuthorizeUserByCode(ctx context.Context, code string) (models.User, error) {
 	userID, err := s.otpSvc.Validate(ctx, code)
 	if err != nil {
 		return models.User{}, fmt.Errorf("failed to validate code: %w", err)
 	}

 	user, err := s.users.GetByID(userID)
 	if err != nil {
-		return user, fmt.Errorf("failed to get user: %w", err)
+		return models.User{}, fmt.Errorf("failed to get user: %w", err)
 	}

 	return user, nil
 }
🧹 Nitpick comments (1)
internal/sms-gateway/otp/storage.go (1)

20-33: Consider renaming receiver for consistency.

The receiver name cc suggests "CodesCache" but the type is now Storage. For consistency, consider using s as the receiver name.

Apply this diff if you'd like to align the receiver name with the type:

-func (cc *Storage) SetOrFail(ctx context.Context, key string, value string, opts ...cache.Option) error {
-	return cc.cache.SetOrFail(ctx, key, []byte(value), opts...)
+func (s *Storage) SetOrFail(ctx context.Context, key string, value string, opts ...cache.Option) error {
+	return s.cache.SetOrFail(ctx, key, []byte(value), opts...)
 }
 
-func (cc *Storage) GetAndDelete(ctx context.Context, key string) (string, error) {
-	data, err := cc.cache.GetAndDelete(ctx, key)
+func (s *Storage) GetAndDelete(ctx context.Context, key string) (string, error) {
+	data, err := s.cache.GetAndDelete(ctx, key)
 	return string(data), err
 }
 
-func (cc *Storage) Cleanup(ctx context.Context) error {
-	return cc.cache.Cleanup(ctx)
+func (s *Storage) Cleanup(ctx context.Context) error {
+	return s.cache.Cleanup(ctx)
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 22d772b and c460972.

📒 Files selected for processing (16)
  • api/mobile.http (1 hunks)
  • configs/config.example.yml (1 hunks)
  • internal/config/config.go (3 hunks)
  • internal/config/module.go (2 hunks)
  • internal/sms-gateway/app.go (2 hunks)
  • internal/sms-gateway/handlers/middlewares/userauth/userauth.go (1 hunks)
  • internal/sms-gateway/handlers/mobile.go (1 hunks)
  • internal/sms-gateway/modules/auth/module.go (1 hunks)
  • internal/sms-gateway/modules/auth/service.go (3 hunks)
  • internal/sms-gateway/modules/auth/types.go (0 hunks)
  • internal/sms-gateway/otp/config.go (1 hunks)
  • internal/sms-gateway/otp/domain.go (1 hunks)
  • internal/sms-gateway/otp/errors.go (1 hunks)
  • internal/sms-gateway/otp/module.go (1 hunks)
  • internal/sms-gateway/otp/service.go (1 hunks)
  • internal/sms-gateway/otp/storage.go (1 hunks)
💤 Files with no reviewable changes (1)
  • internal/sms-gateway/modules/auth/types.go
🚧 Files skipped from review as they are similar to previous changes (12)
  • internal/config/module.go
  • internal/sms-gateway/otp/errors.go
  • configs/config.example.yml
  • internal/sms-gateway/otp/module.go
  • internal/sms-gateway/handlers/mobile.go
  • internal/config/config.go
  • internal/sms-gateway/handlers/middlewares/userauth/userauth.go
  • api/mobile.http
  • internal/sms-gateway/app.go
  • internal/sms-gateway/modules/auth/module.go
  • internal/sms-gateway/otp/domain.go
  • internal/sms-gateway/otp/service.go
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-10T11:11:43.462Z
Learnt from: capcom6
Repo: android-sms-gateway/server PR: 178
File: pkg/cache/memory.go:59-74
Timestamp: 2025-09-10T11:11:43.462Z
Learning: In pkg/cache/memory.go, the cleanup method removes expired items first, then executes the provided callback function. This means methods like Drain that use cleanup don't need additional expiration filtering since expired items are already removed before the callback runs.

Applied to files:

  • internal/sms-gateway/modules/auth/service.go
🧬 Code graph analysis (2)
internal/sms-gateway/otp/storage.go (1)
internal/config/config.go (1)
  • Cache (81-83)
internal/sms-gateway/otp/config.go (2)
internal/config/config.go (1)
  • Config (10-21)
internal/sms-gateway/otp/errors.go (1)
  • ErrInvalidConfig (7-7)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: E2E
  • GitHub Check: Benchmark
  • GitHub Check: Test
  • GitHub Check: Analyze (go)
🔇 Additional comments (3)
internal/sms-gateway/otp/config.go (1)

8-24: LGTM! Clean validation logic.

The Config struct and validation method are well-implemented. The validation checks ensure both TTL and Retries are positive values, with clear error messages wrapped appropriately.

internal/sms-gateway/modules/auth/service.go (2)

14-18: Dependency injection and cache setup look consistent

The new otpSvc dependency and users *repository are wired cleanly through Service and New, and usersCache is initialized with a 1h TTL that matches the hourly Run/clean loop. Given the cache.Cleanup semantics (expired entries are dropped before callbacks), this simple Cleanup() call is sufficient and you don’t need extra expiry handling in callers. Based on learnings, this looks correct.

If not already done, just confirm that Service.Run is actually started in the app wiring so the hourly cleanup runs in production.

Also applies to: 25-37, 39-60


62-69: Context is now correctly propagated to OTP generation

GenerateUserCode now passes the incoming ctx to s.otpSvc.Generate, instead of using a background context, which fixes cancellation/timeout/tracing propagation through the OTP path. Implementation and error wrapping look good.

@capcom6 capcom6 force-pushed the otp/introduce-package branch from c460972 to f48971a Compare November 25, 2025 12:39
@capcom6 capcom6 added the ready label Nov 25, 2025
@capcom6 capcom6 force-pushed the otp/introduce-package branch from f48971a to b7b8354 Compare November 28, 2025 02:03
@capcom6 capcom6 removed the ready label Nov 28, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

♻️ Duplicate comments (1)
internal/sms-gateway/otp/storage.go (1)

14-14: Update the stale comment reference.

The comment references "CodesCache" but the function is named "NewStorage".

-// NewStorage creates a new CodesCache with the underlying cache
+// NewStorage creates a new Storage with the underlying cache.
🧹 Nitpick comments (1)
internal/sms-gateway/otp/service.go (1)

57-66: Extract magic numbers to named constants.

The values 3 (bytes for random data) and 16, 8 (bit shifts) are flagged by the linter. Consider defining constants for clarity.

+const (
+	codeByteLen = 3  // 3 bytes = 24 bits of randomness
+	codeModulo  = 1000000
+)
+
 func (s *Service) Generate(ctx context.Context, userID string) (*Code, error) {
 	var code string
 	var err error
 
-	b := make([]byte, 3)
+	b := make([]byte, codeByteLen)
 	validUntil := time.Now().Add(s.cfg.TTL)
 
 	for range s.cfg.Retries {
 		if _, err = rand.Read(b); err != nil {
 			s.logger.Warn("failed to read random bytes", zap.Error(err))
 			continue
 		}
-		num := (int(b[0]) << 16) | (int(b[1]) << 8) | int(b[2])
-		code = fmt.Sprintf("%06d", num%1000000)
+		num := int(b[0])<<16 | int(b[1])<<8 | int(b[2])
+		code = fmt.Sprintf("%06d", num%codeModulo)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f48971a and b7b8354.

📒 Files selected for processing (16)
  • api/mobile.http (1 hunks)
  • configs/config.example.yml (1 hunks)
  • internal/config/config.go (3 hunks)
  • internal/config/module.go (2 hunks)
  • internal/sms-gateway/app.go (2 hunks)
  • internal/sms-gateway/handlers/middlewares/userauth/userauth.go (1 hunks)
  • internal/sms-gateway/handlers/mobile.go (1 hunks)
  • internal/sms-gateway/modules/auth/module.go (1 hunks)
  • internal/sms-gateway/modules/auth/service.go (3 hunks)
  • internal/sms-gateway/modules/auth/types.go (0 hunks)
  • internal/sms-gateway/otp/config.go (1 hunks)
  • internal/sms-gateway/otp/domain.go (1 hunks)
  • internal/sms-gateway/otp/errors.go (1 hunks)
  • internal/sms-gateway/otp/module.go (1 hunks)
  • internal/sms-gateway/otp/service.go (1 hunks)
  • internal/sms-gateway/otp/storage.go (1 hunks)
💤 Files with no reviewable changes (1)
  • internal/sms-gateway/modules/auth/types.go
🚧 Files skipped from review as they are similar to previous changes (4)
  • internal/sms-gateway/handlers/middlewares/userauth/userauth.go
  • internal/sms-gateway/otp/errors.go
  • internal/sms-gateway/app.go
  • internal/sms-gateway/otp/module.go
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-09-10T11:11:43.462Z
Learnt from: capcom6
Repo: android-sms-gateway/server PR: 178
File: pkg/cache/memory.go:59-74
Timestamp: 2025-09-10T11:11:43.462Z
Learning: In pkg/cache/memory.go, the cleanup method removes expired items first, then executes the provided callback function. This means methods like Drain that use cleanup don't need additional expiration filtering since expired items are already removed before the callback runs.

Applied to files:

  • internal/sms-gateway/modules/auth/service.go
📚 Learning: 2025-09-10T23:45:45.502Z
Learnt from: capcom6
Repo: android-sms-gateway/server PR: 178
File: internal/sms-gateway/online/module.go:14-16
Timestamp: 2025-09-10T23:45:45.502Z
Learning: fx.Decorate in uber-go/fx creates module-scoped decorated dependencies, not global modifications. Using fx.Decorate(func(log *zap.Logger) *zap.Logger { return log.Named("module_name") }) is a correct pattern for creating module-specific loggers that are scoped only to that module's dependency graph.

Applied to files:

  • internal/sms-gateway/modules/auth/module.go
🧬 Code graph analysis (6)
internal/config/module.go (2)
internal/config/config.go (2)
  • Config (10-21)
  • OTP (90-93)
internal/sms-gateway/otp/config.go (1)
  • Config (9-12)
internal/sms-gateway/modules/auth/service.go (3)
internal/sms-gateway/otp/service.go (1)
  • Service (13-19)
internal/sms-gateway/otp/config.go (1)
  • Config (9-12)
internal/sms-gateway/otp/domain.go (1)
  • Code (6-9)
internal/sms-gateway/otp/service.go (5)
internal/sms-gateway/otp/config.go (1)
  • Config (9-12)
internal/sms-gateway/otp/storage.go (1)
  • Storage (10-12)
internal/sms-gateway/otp/errors.go (1)
  • ErrInitFailed (9-9)
internal/sms-gateway/otp/domain.go (1)
  • Code (6-9)
pkg/cache/options.go (1)
  • WithValidUntil (35-39)
internal/sms-gateway/modules/auth/module.go (1)
internal/sms-gateway/modules/auth/service.go (1)
  • New (35-56)
internal/sms-gateway/otp/config.go (2)
internal/config/config.go (1)
  • Config (10-21)
internal/sms-gateway/otp/errors.go (1)
  • ErrInvalidConfig (7-7)
internal/sms-gateway/otp/storage.go (1)
internal/config/config.go (1)
  • Cache (81-83)
🪛 GitHub Check: Lint
internal/config/config.go

[failure] 91-91:
File is not properly formatted (golines)

internal/sms-gateway/otp/service.go

[failure] 65-65:
Magic number: 16, in detected (mnd)


[failure] 57-57:
Magic number: 3, in detected (mnd)

internal/sms-gateway/otp/config.go

[failure] 10-10:
File is not properly formatted (golines)

internal/sms-gateway/otp/domain.go

[failure] 5-5:
Comment should end in a period (godot)

internal/sms-gateway/otp/storage.go

[failure] 30-30:
Comment should end in a period (godot)


[failure] 24-24:
Comment should end in a period (godot)


[failure] 19-19:
Comment should end in a period (godot)


[failure] 14-14:
Comment should end in a period (godot)


[failure] 9-9:
Comment should end in a period (godot)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: E2E
  • GitHub Check: Benchmark
  • GitHub Check: Analyze (go)
🔇 Additional comments (16)
api/mobile.http (1)

11-13: LGTM! Auth scheme change aligns with OTP flow.

The switch from Bearer token to Basic authentication for the /user/code endpoint is appropriate, as users need to authenticate with credentials to obtain an OTP code. The commented line preserves context for the change.

internal/sms-gateway/modules/auth/module.go (1)

16-17: Provider reordering is functionally safe but unnecessary.

While the reordering of fx.Provide(New) after fx.Provide(newRepository, fx.Private) doesn't introduce any issues, Uber FX automatically resolves dependencies regardless of provider declaration order. The dependency graph is built at runtime, so this change has no functional impact.

internal/sms-gateway/otp/domain.go (1)

6-9: LGTM! Clean domain model.

The Code struct is well-designed as a simple data container for OTP codes with clear field semantics.

internal/sms-gateway/handlers/mobile.go (1)

181-192: LGTM! Context propagation follows best practices.

Adding c.Context() as the first parameter to GenerateUserCode enables proper request cancellation, timeout handling, and distributed tracing. This is a best practice for context-aware operations.

configs/config.example.yml (1)

41-43: LGTM! Sensible OTP configuration defaults.

The OTP configuration values are appropriate:

  • TTL of 300 seconds (5 minutes) is standard for OTP validity
  • 3 retries adequately handles code generation collisions
  • Clear documentation with environment variable mappings
internal/config/config.go (2)

20-20: LGTM! OTP field properly integrated into Config.

The OTP field is correctly positioned with other service configurations and has clear documentation.


132-135: LGTM! Sensible OTP defaults.

The default values (300s TTL, 3 retries) are appropriate and consistent with the example configuration.

internal/sms-gateway/otp/config.go (1)

14-24: LGTM! Solid validation logic.

The Validate() method correctly ensures both TTL and Retries are positive values with clear error messages and proper error wrapping.

internal/config/module.go (2)

14-14: LGTM! Import properly positioned.

The OTP package import is correctly placed with other internal imports and is used in the provider below.


140-145: LGTM! Clean type conversion and wiring.

The provider correctly bridges the global configuration to the OTP-specific config, with safe type conversions from uint16/uint8 to time.Duration/int. The pattern is consistent with other config providers in this module.

internal/sms-gateway/modules/auth/service.go (3)

35-56: Clean constructor refactoring.

The constructor signature is now explicit with all dependencies, and the initialization is clear and well-organized.


58-66: Clean delegation to OTP service.

The GenerateUserCode method now properly forwards the context to the OTP service and returns the domain type directly.


147-160: I'll help you verify the return type consistency between AuthorizeUserByCode and the middleware's GetUser function. Let me search for the GetUser implementation and check the type assertion.
<function_calls>

#!/bin/bash

Search for GetUser function in middleware-related files

rg -n -A10 'func GetUser' --type=go


</function_calls>
<function_calls>

#!/bin/bash

Also search for GetUser in all Go files to be thorough

rg -n 'GetUser' --type=go -B2 -A10 | head -100


</function_calls>

Let me also check the models.User type definition:
<function_calls>

#!/bin/bash

Find models package and User type definition

fd -e go | xargs grep -l "type User struct" | head -5


</function_calls>

Let me look at the auth service file more broadly:
<function_calls>

#!/bin/bash

Check the auth service file structure and related middleware

cat internal/sms-gateway/modules/auth/service.go | head -50


</function_calls>

<function_calls>

#!/bin/bash

Search for middleware files that might contain GetUser

fd -type f -name 'middleware' --type=go


</function_calls>

internal/sms-gateway/otp/service.go (3)

21-43: Well-structured constructor with proper validation.

The constructor validates configuration, storage, and logger dependencies appropriately, returning clear initialization errors.


84-94: Clean validation implementation.

The Validate method correctly retrieves and atomically deletes the code, preventing replay attacks.


53-82: I'll verify the review comment by investigating whether the configuration validates Retries > 0 and whether the edge case is a real issue.
<function_calls>


#!/bin/bash

Find Config struct and Validate() method in the same package/file

fd -e go internal/sms-gateway/otp/ | head -20


</function_calls>
<function_calls>


#!/bin/bash

Search for Config struct definition and Validate method

rg -n "type.Config\sstruct|func.*Validate" internal/sms-gateway/otp/ -A 5


</function_calls>

@github-actions
Copy link

github-actions bot commented Nov 28, 2025

🤖 Pull request artifacts

Platform File
🐳 Docker GitHub Container Registry
🍎 Darwin arm64 server_Darwin_arm64.tar.gz
🍎 Darwin x86_64 server_Darwin_x86_64.tar.gz
🐧 Linux arm64 server_Linux_arm64.tar.gz
🐧 Linux i386 server_Linux_i386.tar.gz
🐧 Linux x86_64 server_Linux_x86_64.tar.gz
🪟 Windows arm64 server_Windows_arm64.zip
🪟 Windows i386 server_Windows_i386.zip
🪟 Windows x86_64 server_Windows_x86_64.zip

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
internal/sms-gateway/otp/storage.go (1)

15-18: Fix stale constructor doc comment ("CodesCache" vs Storage).

The doc comment still refers to "CodesCache" while the function/type are NewStorage/Storage. This is a leftover from refactoring and can confuse readers and tooling.

-// NewStorage creates a new CodesCache with the underlying cache.
+// NewStorage creates a new Storage with the underlying cache.
 func NewStorage(c cache.Cache) *Storage {
 	return &Storage{cache: c}
 }
🧹 Nitpick comments (2)
internal/sms-gateway/otp/storage.go (1)

29-33: Consider adding contextual error wrapping in GetAndDelete.

GetAndDelete currently returns the raw cache error, while SetOrFail and Cleanup wrap errors with contextual messages. For consistency and easier debugging, you might wrap the error similarly (using %w so callers can still use errors.Is).

 func (s *Storage) GetAndDelete(ctx context.Context, key string) (string, error) {
-	data, err := s.cache.GetAndDelete(ctx, key)
-	return string(data), err
+	data, err := s.cache.GetAndDelete(ctx, key)
+	if err != nil {
+		return "", fmt.Errorf("failed to get and delete cache item: %w", err)
+	}
+	return string(data), nil
 }
internal/config/config.go (1)

90-93: TTL/Retries scalar types are fine; consider future headroom (optional)

Using uint16 for TTL (seconds) and uint8 for Retries is perfectly reasonable for typical OTP lifetimes and retry counts. If you ever foresee significantly longer TTLs or want to align with other duration‑style config (e.g., using a common duration wrapper or a wider integer), you could switch TTL to uint32 or a shared duration type, but that’s purely a future‑proofing tweak.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b7b8354 and d712680.

📒 Files selected for processing (6)
  • internal/config/config.go (3 hunks)
  • internal/sms-gateway/otp/config.go (1 hunks)
  • internal/sms-gateway/otp/domain.go (1 hunks)
  • internal/sms-gateway/otp/module.go (1 hunks)
  • internal/sms-gateway/otp/service.go (1 hunks)
  • internal/sms-gateway/otp/storage.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • internal/sms-gateway/otp/module.go
  • internal/sms-gateway/otp/service.go
  • internal/sms-gateway/otp/domain.go
🧰 Additional context used
🧬 Code graph analysis (2)
internal/sms-gateway/otp/storage.go (1)
internal/config/config.go (1)
  • Cache (81-83)
internal/sms-gateway/otp/config.go (3)
internal/worker/config/types.go (1)
  • Duration (11-11)
internal/config/config.go (1)
  • Config (10-21)
internal/sms-gateway/otp/errors.go (1)
  • ErrInvalidConfig (7-7)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: goreleaser
  • GitHub Check: Benchmark
🔇 Additional comments (3)
internal/config/config.go (2)

20-20: OTP section wiring into global Config looks consistent

Adding OTP OTP 'yaml:"otp"' follows the existing pattern for other top‑level config sections and keeps the YAML surface clear; I don’t see any issues with how this is introduced.


132-135: OTP defaults and configuration are properly documented and in sync

Verification confirms that configs/config.example.yml (lines 41-43) correctly documents the OTP section with matching default values (ttl: 300 and retries: 3), yaml field tags align with the struct definition, and the top-level layout matches the Config structure. No divergence between code and documentation was found.

internal/sms-gateway/otp/config.go (1)

1-24: OTP Config and validation are well-structured

Using time.Duration for TTL and int for Retries at the service level, plus a Validate() that enforces both are > 0 and wraps ErrInvalidConfig, is a clean and type‑safe design. This should integrate nicely with the wiring from the global config.

@capcom6 capcom6 added the ready label Nov 28, 2025
@capcom6 capcom6 force-pushed the otp/introduce-package branch 2 times, most recently from 2c0ebe5 to 28bffef Compare November 28, 2025 23:56
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
internal/sms-gateway/modules/auth/service.go (1)

147-160: Inconsistent error return on line 156.

When s.users.GetByID(userID) fails, the code returns user (line 156) which may be a zero-value or partially initialized, rather than returning nil for consistency with the error on line 151.

 	user, err := s.users.GetByID(userID)
 	if err != nil {
-		return user, fmt.Errorf("failed to get user: %w", err)
+		return nil, fmt.Errorf("failed to get user: %w", err)
 	}
🧹 Nitpick comments (2)
internal/sms-gateway/otp/config.go (1)

14-24: Consider accumulating validation errors.

The current implementation returns on the first validation failure. For a better developer experience during configuration, consider collecting all validation errors before returning.

+import "errors"
+
 func (c Config) Validate() error {
+	var errs []error
+
 	if c.TTL <= 0 {
-		return fmt.Errorf("%w: TTL must be greater than 0", ErrInvalidConfig)
+		errs = append(errs, fmt.Errorf("TTL must be greater than 0"))
 	}
 
 	if c.Retries <= 0 {
-		return fmt.Errorf("%w: retries must be greater than 0", ErrInvalidConfig)
+		errs = append(errs, fmt.Errorf("retries must be greater than 0"))
 	}
 
+	if len(errs) > 0 {
+		return fmt.Errorf("%w: %w", ErrInvalidConfig, errors.Join(errs...))
+	}
+
 	return nil
 }
internal/sms-gateway/otp/service.go (1)

14-44: Align NewService docs with actual storage type

The NewService comment calls the second parameter “a storage interface”, but the field and parameter are *Storage (a concrete struct). Either update the comment to drop “interface” or change the type to an interface if that’s the intended abstraction.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d712680 and 28bffef.

📒 Files selected for processing (16)
  • api/mobile.http (1 hunks)
  • configs/config.example.yml (1 hunks)
  • internal/config/config.go (3 hunks)
  • internal/config/module.go (2 hunks)
  • internal/sms-gateway/app.go (2 hunks)
  • internal/sms-gateway/handlers/middlewares/userauth/userauth.go (1 hunks)
  • internal/sms-gateway/handlers/mobile.go (1 hunks)
  • internal/sms-gateway/modules/auth/module.go (1 hunks)
  • internal/sms-gateway/modules/auth/service.go (3 hunks)
  • internal/sms-gateway/modules/auth/types.go (0 hunks)
  • internal/sms-gateway/otp/config.go (1 hunks)
  • internal/sms-gateway/otp/domain.go (1 hunks)
  • internal/sms-gateway/otp/errors.go (1 hunks)
  • internal/sms-gateway/otp/module.go (1 hunks)
  • internal/sms-gateway/otp/service.go (1 hunks)
  • internal/sms-gateway/otp/storage.go (1 hunks)
💤 Files with no reviewable changes (1)
  • internal/sms-gateway/modules/auth/types.go
🚧 Files skipped from review as they are similar to previous changes (8)
  • internal/sms-gateway/otp/module.go
  • api/mobile.http
  • internal/config/config.go
  • internal/sms-gateway/otp/storage.go
  • internal/config/module.go
  • internal/sms-gateway/handlers/middlewares/userauth/userauth.go
  • configs/config.example.yml
  • internal/sms-gateway/handlers/mobile.go
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-09-10T11:11:43.462Z
Learnt from: capcom6
Repo: android-sms-gateway/server PR: 178
File: pkg/cache/memory.go:59-74
Timestamp: 2025-09-10T11:11:43.462Z
Learning: In pkg/cache/memory.go, the cleanup method removes expired items first, then executes the provided callback function. This means methods like Drain that use cleanup don't need additional expiration filtering since expired items are already removed before the callback runs.

Applied to files:

  • internal/sms-gateway/modules/auth/service.go
📚 Learning: 2025-09-10T23:45:45.502Z
Learnt from: capcom6
Repo: android-sms-gateway/server PR: 178
File: internal/sms-gateway/online/module.go:14-16
Timestamp: 2025-09-10T23:45:45.502Z
Learning: fx.Decorate in uber-go/fx creates module-scoped decorated dependencies, not global modifications. Using fx.Decorate(func(log *zap.Logger) *zap.Logger { return log.Named("module_name") }) is a correct pattern for creating module-specific loggers that are scoped only to that module's dependency graph.

Applied to files:

  • internal/sms-gateway/modules/auth/module.go
🧬 Code graph analysis (5)
internal/sms-gateway/app.go (2)
internal/config/module.go (1)
  • Module (24-147)
internal/sms-gateway/otp/module.go (1)
  • Module (9-22)
internal/sms-gateway/otp/errors.go (1)
internal/sms-gateway/modules/auth/service.go (1)
  • New (35-56)
internal/sms-gateway/otp/config.go (1)
internal/sms-gateway/otp/errors.go (1)
  • ErrInvalidConfig (7-7)
internal/sms-gateway/otp/service.go (6)
internal/config/config.go (1)
  • Config (10-21)
internal/sms-gateway/otp/config.go (1)
  • Config (9-12)
internal/sms-gateway/otp/storage.go (1)
  • Storage (11-13)
internal/sms-gateway/otp/errors.go (1)
  • ErrInitFailed (9-9)
internal/sms-gateway/otp/domain.go (1)
  • Code (6-9)
pkg/cache/options.go (1)
  • WithValidUntil (35-39)
internal/sms-gateway/modules/auth/module.go (1)
internal/sms-gateway/modules/auth/service.go (1)
  • New (35-56)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Benchmark
  • GitHub Check: E2E
  • GitHub Check: Analyze (go)
🔇 Additional comments (9)
internal/sms-gateway/otp/domain.go (1)

1-9: LGTM!

The Code domain type is well-structured with appropriate field types. Using time.Time for ValidUntil provides proper time handling semantics.

internal/sms-gateway/modules/auth/module.go (1)

16-17: LGTM!

The reordering of fx.Provide(New) after fx.Provide(newRepository, fx.Private) doesn't affect dependency resolution since fx resolves based on types. The *Service can now be injected into other modules if needed.

internal/sms-gateway/app.go (1)

22-22: LGTM!

The OTP module is correctly imported and wired into the fx application lifecycle. The dependency graph between auth.Module() and otp.Module() will be properly resolved by fx regardless of declaration order.

Also applies to: 57-58

internal/sms-gateway/otp/config.go (1)

8-12: Config struct design is sound.

The use of time.Duration for TTL provides type safety, and the struct tags are properly aligned. The validation logic correctly enforces positive values.

internal/sms-gateway/modules/auth/service.go (2)

35-56: LGTM!

The constructor properly wires the new *otp.Service dependency and initializes all fields correctly. The delegation to the OTP service for code generation cleanly separates concerns.


58-66: LGTM!

The GenerateUserCode method correctly delegates to otpSvc.Generate with proper context propagation and error wrapping.

internal/sms-gateway/otp/errors.go (1)

5-10: LGTM! Both sentinel errors are properly used.

The error definitions follow Go idioms with correct godoc comments. ErrInvalidConfig and ErrInitFailed are both actively used for wrapping configuration and initialization validation errors in the OTP module (service.go, lines 36 and 40).

internal/sms-gateway/otp/service.go (2)

95-105: Validate wrapper looks correct; confirm ErrKeyNotFound contract

Validate is a thin pass-through to storage.GetAndDelete, which is good for keeping behavior centralized. Just ensure Storage.GetAndDelete actually returns ErrKeyNotFound for missing/expired codes so the doc comment here stays accurate.


54-93: Config validation already guarantees Retries > 0; the review concern is addressed

The codebase already prevents the scenario described in the review comment. Config.Validate() in internal/sms-gateway/otp/config.go (lines 19–20) explicitly checks that Retries > 0 and returns an error if violated. Since NewService() calls cfg.Validate() before creating the Service, the Generate() function can never be called with s.cfg.Retries <= 0. The Go version requirement is satisfied (go.mod declares go 1.24.3, which fully supports range-over-int syntax).

No changes needed; the invariants are already properly enforced.

@capcom6 capcom6 force-pushed the otp/introduce-package branch from 28bffef to 4d57117 Compare November 29, 2025 01:08
@github-actions github-actions bot removed the ready label Nov 29, 2025
@capcom6 capcom6 force-pushed the otp/introduce-package branch from 4d57117 to b374055 Compare November 29, 2025 07:29
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (2)
internal/config/config.go (1)

90-93: OTP configuration looks reasonable.

The defaults (TTL=300s, Retries=3) are sensible for OTP use cases—5-minute expiry provides usability while limiting attack window, and 3 retries mitigate brute-force attempts. The type choices (uint16, uint8) are appropriate for these value ranges.

Note: A previous review flagged a line-length formatting issue on line 91 per golines rules. Please ensure the formatter has been run.

internal/sms-gateway/otp/storage.go (1)

15-17: Fix stale doc comment wording for NewStorage.

The comment still says “creates a new CodesCache” while the function returns *Storage. Updating the wording will keep the doc comment accurate and avoid confusion.

-// NewStorage creates a new CodesCache with the underlying cache.
+// NewStorage creates a new Storage with the underlying cache.
🧹 Nitpick comments (1)
internal/sms-gateway/modules/auth/service.go (1)

147-160: Verify AuthorizeUserByCode return type matches what middleware expects.

AuthorizeUserByCode returns *models.User and fetches via s.users.GetByID(userID), which is consistent with how AuthorizeUser works. However, earlier middleware (GetUser in userauth.go) historically asserted a models.User value from c.Locals, which caused a panic when a *models.User was stored for code-based auth.

Please double-check that:

  • The middleware storing the user from AuthorizeUserByCode and
  • GetUser’s type assertion

are now aligned on either models.User or *models.User so the type is consistent across Basic and OTP flows. If GetUser still expects a value, consider switching this method to return models.User (by value) as previously suggested.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4d57117 and b374055.

📒 Files selected for processing (16)
  • api/mobile.http (1 hunks)
  • configs/config.example.yml (1 hunks)
  • internal/config/config.go (3 hunks)
  • internal/config/module.go (2 hunks)
  • internal/sms-gateway/app.go (2 hunks)
  • internal/sms-gateway/handlers/middlewares/userauth/userauth.go (1 hunks)
  • internal/sms-gateway/handlers/mobile.go (1 hunks)
  • internal/sms-gateway/modules/auth/module.go (1 hunks)
  • internal/sms-gateway/modules/auth/service.go (3 hunks)
  • internal/sms-gateway/modules/auth/types.go (0 hunks)
  • internal/sms-gateway/otp/config.go (1 hunks)
  • internal/sms-gateway/otp/domain.go (1 hunks)
  • internal/sms-gateway/otp/errors.go (1 hunks)
  • internal/sms-gateway/otp/module.go (1 hunks)
  • internal/sms-gateway/otp/service.go (1 hunks)
  • internal/sms-gateway/otp/storage.go (1 hunks)
💤 Files with no reviewable changes (1)
  • internal/sms-gateway/modules/auth/types.go
🚧 Files skipped from review as they are similar to previous changes (7)
  • internal/sms-gateway/app.go
  • internal/sms-gateway/otp/config.go
  • api/mobile.http
  • internal/sms-gateway/otp/errors.go
  • configs/config.example.yml
  • internal/sms-gateway/otp/service.go
  • internal/sms-gateway/otp/domain.go
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-09-10T11:11:43.462Z
Learnt from: capcom6
Repo: android-sms-gateway/server PR: 178
File: pkg/cache/memory.go:59-74
Timestamp: 2025-09-10T11:11:43.462Z
Learning: In pkg/cache/memory.go, the cleanup method removes expired items first, then executes the provided callback function. This means methods like Drain that use cleanup don't need additional expiration filtering since expired items are already removed before the callback runs.

Applied to files:

  • internal/sms-gateway/modules/auth/service.go
📚 Learning: 2025-09-10T23:45:45.502Z
Learnt from: capcom6
Repo: android-sms-gateway/server PR: 178
File: internal/sms-gateway/online/module.go:14-16
Timestamp: 2025-09-10T23:45:45.502Z
Learning: fx.Decorate in uber-go/fx creates module-scoped decorated dependencies, not global modifications. Using fx.Decorate(func(log *zap.Logger) *zap.Logger { return log.Named("module_name") }) is a correct pattern for creating module-specific loggers that are scoped only to that module's dependency graph.

Applied to files:

  • internal/sms-gateway/modules/auth/module.go
🧬 Code graph analysis (5)
internal/config/module.go (2)
internal/config/config.go (2)
  • Config (10-21)
  • OTP (90-93)
internal/sms-gateway/otp/config.go (1)
  • Config (9-12)
internal/sms-gateway/otp/module.go (6)
internal/config/module.go (1)
  • Module (24-147)
internal/sms-gateway/app.go (1)
  • Module (34-60)
internal/sms-gateway/cache/factory.go (1)
  • Factory (16-18)
internal/config/config.go (1)
  • Cache (81-83)
internal/sms-gateway/otp/storage.go (1)
  • NewStorage (16-18)
internal/sms-gateway/otp/service.go (1)
  • NewService (30-44)
internal/sms-gateway/otp/storage.go (1)
internal/config/config.go (1)
  • Cache (81-83)
internal/sms-gateway/modules/auth/service.go (3)
internal/sms-gateway/otp/service.go (1)
  • Service (14-20)
internal/sms-gateway/otp/config.go (1)
  • Config (9-12)
internal/sms-gateway/otp/domain.go (1)
  • Code (6-9)
internal/sms-gateway/modules/auth/module.go (1)
internal/sms-gateway/modules/auth/service.go (1)
  • New (35-56)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Benchmark
  • GitHub Check: E2E
  • GitHub Check: Analyze (go)
🔇 Additional comments (9)
internal/sms-gateway/handlers/mobile.go (1)

181-192: LGTM! Proper context propagation.

Passing c.Context() to GenerateUserCode correctly threads the request context through to the OTP service layer, enabling proper cancellation and tracing support.

internal/sms-gateway/otp/module.go (1)

9-22: Well-structured fx module with proper scoping.

The module correctly:

  • Uses logger.WithNamedLogger("otp") for a module-scoped logger
  • Applies fx.Private to the cache factory result and NewStorage, preventing them from leaking into the global dependency graph
  • Exposes only NewService publicly for consumption by other modules like auth

This follows good fx module encapsulation patterns.

internal/sms-gateway/modules/auth/module.go (1)

10-31: LGTM! Clean module structure.

The reordering of providers is cosmetic (fx resolves by type, not order). The New function now receives *otp.Service as a dependency (per the updated signature in service.go), which fx will inject from the newly added otp.Module(). Based on learnings, the fx.Decorate pattern for the named logger correctly creates module-scoped decorated dependencies.

internal/sms-gateway/handlers/middlewares/userauth/userauth.go (1)

64-84: LGTM! Context correctly threaded to authorization.

Passing c.Context() to AuthorizeUserByCode properly propagates the request context through the middleware to the OTP validation logic, enabling cancellation support and request-scoped tracing.

internal/config/config.go (1)

132-135: Default OTP values properly initialized.

The defaults align with the OTP struct definition and provide sensible out-of-the-box behavior.

internal/config/module.go (1)

14-14: OTP config wiring and type conversion look correct.

The new fx.Provide cleanly maps Config.OTP to otp.Config, converting TTL from seconds (uint16) to time.Duration and widening Retries (uint8) to int. This matches existing time conversion patterns in this module; no issues from a correctness or DI perspective.

Also applies to: 140-145

internal/sms-gateway/otp/storage.go (1)

10-13: String-oriented cache wrapper and cleanup behavior look good.

Wrapping cache.Cache for string values via SetOrFail/GetAndDelete and delegating Cleanup with additional error context is straightforward and consistent with existing cache semantics; Cleanup’s doc (“removes expired items”) matches the underlying behavior noted in pkg/cache/memory.go.

Based on learnings, Cleanup already removes expired entries before any further processing.

Also applies to: 20-41

internal/sms-gateway/modules/auth/service.go (2)

12-12: Constructor wiring of otpSvc and other deps looks consistent.

Adding otpSvc *otp.Service to Service and switching New to an explicit-parameter constructor keeps dependencies clear and matches how other services are injected; usersCache initialization is preserved. No functional issues from this change.

Also applies to: 28-33, 35-56


58-66: GenerateUserCode now correctly delegates to OTP service with caller context.

GenerateUserCode forwards the provided ctx into s.otpSvc.Generate and wraps errors with context, eliminating the previous context.Background() issue and centralizing code generation in the OTP module. The signature and behavior look sound.

@capcom6 capcom6 force-pushed the otp/introduce-package branch 2 times, most recently from d5148f6 to d5337ba Compare November 29, 2025 23:53
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
internal/sms-gateway/otp/storage.go (1)

15-15: Update stale comment reference.

The comment references "CodesCache" but the function is named NewStorage.

Apply this diff to fix the comment:

-// NewStorage creates a new CodesCache with the underlying cache.
+// NewStorage creates a new Storage with the underlying cache.
🧹 Nitpick comments (1)
api/mobile.http (1)

12-13: Basic auth for /user/code looks correct; optionally clarify the commented Bearer header

Switching /user/code to Authorization: Basic {{credentials}} aligns with an OTP-style “pre-token” flow, and keeping the old Bearer form as a comment is useful for reference. To avoid confusion later, you might add a short note to the commented Bearer line (e.g., # ... (when already authenticated with a mobile token)) so it’s obvious in which scenario each header should be used.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d5148f6 and d5337ba.

📒 Files selected for processing (16)
  • api/mobile.http (1 hunks)
  • configs/config.example.yml (1 hunks)
  • internal/config/config.go (3 hunks)
  • internal/config/module.go (2 hunks)
  • internal/sms-gateway/app.go (2 hunks)
  • internal/sms-gateway/handlers/middlewares/userauth/userauth.go (2 hunks)
  • internal/sms-gateway/handlers/mobile.go (1 hunks)
  • internal/sms-gateway/modules/auth/module.go (1 hunks)
  • internal/sms-gateway/modules/auth/service.go (3 hunks)
  • internal/sms-gateway/modules/auth/types.go (0 hunks)
  • internal/sms-gateway/otp/config.go (1 hunks)
  • internal/sms-gateway/otp/domain.go (1 hunks)
  • internal/sms-gateway/otp/errors.go (1 hunks)
  • internal/sms-gateway/otp/module.go (1 hunks)
  • internal/sms-gateway/otp/service.go (1 hunks)
  • internal/sms-gateway/otp/storage.go (1 hunks)
💤 Files with no reviewable changes (1)
  • internal/sms-gateway/modules/auth/types.go
🚧 Files skipped from review as they are similar to previous changes (8)
  • internal/config/module.go
  • internal/sms-gateway/app.go
  • internal/sms-gateway/otp/config.go
  • configs/config.example.yml
  • internal/config/config.go
  • internal/sms-gateway/otp/service.go
  • internal/sms-gateway/modules/auth/module.go
  • internal/sms-gateway/otp/domain.go
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-10T11:11:43.462Z
Learnt from: capcom6
Repo: android-sms-gateway/server PR: 178
File: pkg/cache/memory.go:59-74
Timestamp: 2025-09-10T11:11:43.462Z
Learning: In pkg/cache/memory.go, the cleanup method removes expired items first, then executes the provided callback function. This means methods like Drain that use cleanup don't need additional expiration filtering since expired items are already removed before the callback runs.

Applied to files:

  • internal/sms-gateway/modules/auth/service.go
🧬 Code graph analysis (5)
internal/sms-gateway/modules/auth/service.go (4)
internal/sms-gateway/otp/service.go (1)
  • Service (14-20)
internal/config/config.go (1)
  • Config (10-20)
internal/sms-gateway/otp/config.go (1)
  • Config (9-12)
internal/sms-gateway/otp/domain.go (1)
  • Code (6-9)
internal/sms-gateway/otp/module.go (7)
internal/config/module.go (1)
  • Module (24-143)
internal/sms-gateway/app.go (1)
  • Module (34-60)
internal/sms-gateway/cache/factory.go (1)
  • Factory (16-18)
internal/config/config.go (1)
  • Cache (76-78)
internal/sms-gateway/modules/auth/service.go (1)
  • New (35-56)
internal/sms-gateway/otp/storage.go (1)
  • NewStorage (16-18)
internal/sms-gateway/otp/service.go (1)
  • NewService (30-44)
internal/sms-gateway/otp/storage.go (1)
internal/config/config.go (1)
  • Cache (76-78)
internal/sms-gateway/handlers/middlewares/userauth/userauth.go (1)
internal/sms-gateway/models/models.go (1)
  • User (18-24)
internal/sms-gateway/otp/errors.go (1)
internal/sms-gateway/modules/auth/service.go (1)
  • New (35-56)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: E2E
  • GitHub Check: Benchmark
  • GitHub Check: Analyze (go)
🔇 Additional comments (11)
internal/sms-gateway/handlers/middlewares/userauth/userauth.go (2)

53-53: LGTM: Clean refactor with proper context propagation.

The introduction of the SetUser helper consolidates user storage logic, and passing c.Context() to AuthorizeUserByCode correctly propagates request context for cancellation and timeout support.

Also applies to: 75-75, 80-80


86-88: LGTM: Helper function is type-safe and well-designed.

The exported SetUser function provides a consistent interface for storing user data in Fiber locals, maintaining type compatibility with GetUser.

internal/sms-gateway/modules/auth/service.go (3)

35-56: LGTM: Constructor properly wired with OTP service dependency.

The updated constructor cleanly integrates the OTP service dependency following dependency injection best practices.


58-66: LGTM: Context propagation correctly implemented.

The method properly passes the caller's context to the OTP service, enabling cancellation, timeout, and tracing as intended.


148-160: LGTM: OTP-based authorization correctly implemented.

The method properly validates codes via the OTP service with context propagation, retrieves the user, and maintains type consistency with the middleware layer.

internal/sms-gateway/handlers/mobile.go (1)

181-192: LGTM: Handler correctly propagates context to OTP service.

The handler properly passes the Fiber context to GenerateUserCode, ensuring request-scoped cancellation and timeout behavior.

internal/sms-gateway/otp/module.go (1)

9-22: LGTM: Module wiring follows fx best practices.

The OTP module correctly:

  • Registers a scoped logger for observability
  • Uses fx.Private to encapsulate cache/storage implementation details
  • Exposes only the Service for dependency injection
internal/sms-gateway/otp/errors.go (1)

5-10: LGTM: Error tokens follow Go conventions.

The sentinel errors provide clear, package-level error types for configuration and initialization failures, enabling proper error handling with errors.Is.

internal/sms-gateway/otp/storage.go (3)

10-13: LGTM: Storage wrapper provides clean abstraction.

The Storage struct appropriately encapsulates the cache interface for string-based operations.


20-27: LGTM: Method correctly wraps cache with error context.

The SetOrFail method properly converts string values to bytes and provides meaningful error wrapping.


29-33: LGTM: Cleanup and retrieval methods are well-implemented.

Both GetAndDelete and Cleanup correctly delegate to the underlying cache with appropriate type conversions and error handling.

Also applies to: 35-41

@capcom6 capcom6 added the ready label Nov 30, 2025
@capcom6 capcom6 force-pushed the otp/introduce-package branch from d5337ba to 6cae7c4 Compare December 2, 2025 23:44
@github-actions github-actions bot removed the ready label Dec 2, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
internal/sms-gateway/otp/storage.go (1)

15-18: Update NewStorage doc comment to match the type name

The doc comment still mentions “CodesCache” while the function and type are named NewStorage/Storage, which can be confusing and contradicts the earlier review guidance.

Consider updating it to something like:

// NewStorage creates a new Storage with the underlying cache.
🧹 Nitpick comments (1)
internal/sms-gateway/otp/service.go (1)

70-77: Minor: modulo bias in code generation.

With 3 bytes (24 bits = 16,777,216 values) and num % 1,000,000, codes 0–777,215 have 17 chances each while 777,216–999,999 have only 16. This creates ~6% higher probability for lower codes.

For OTP purposes this is likely acceptable, but if uniformity matters, consider rejection sampling or using 4 bytes.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d5337ba and 6cae7c4.

📒 Files selected for processing (16)
  • api/mobile.http (1 hunks)
  • configs/config.example.yml (1 hunks)
  • internal/config/config.go (3 hunks)
  • internal/config/module.go (2 hunks)
  • internal/sms-gateway/app.go (2 hunks)
  • internal/sms-gateway/handlers/middlewares/userauth/userauth.go (2 hunks)
  • internal/sms-gateway/handlers/mobile.go (1 hunks)
  • internal/sms-gateway/modules/auth/module.go (1 hunks)
  • internal/sms-gateway/modules/auth/service.go (3 hunks)
  • internal/sms-gateway/modules/auth/types.go (0 hunks)
  • internal/sms-gateway/otp/config.go (1 hunks)
  • internal/sms-gateway/otp/domain.go (1 hunks)
  • internal/sms-gateway/otp/errors.go (1 hunks)
  • internal/sms-gateway/otp/module.go (1 hunks)
  • internal/sms-gateway/otp/service.go (1 hunks)
  • internal/sms-gateway/otp/storage.go (1 hunks)
💤 Files with no reviewable changes (1)
  • internal/sms-gateway/modules/auth/types.go
🚧 Files skipped from review as they are similar to previous changes (5)
  • internal/sms-gateway/otp/config.go
  • configs/config.example.yml
  • internal/config/module.go
  • internal/sms-gateway/otp/module.go
  • api/mobile.http
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-09-10T23:45:45.502Z
Learnt from: capcom6
Repo: android-sms-gateway/server PR: 178
File: internal/sms-gateway/online/module.go:14-16
Timestamp: 2025-09-10T23:45:45.502Z
Learning: fx.Decorate in uber-go/fx creates module-scoped decorated dependencies, not global modifications. Using fx.Decorate(func(log *zap.Logger) *zap.Logger { return log.Named("module_name") }) is a correct pattern for creating module-specific loggers that are scoped only to that module's dependency graph.

Applied to files:

  • internal/sms-gateway/modules/auth/module.go
📚 Learning: 2025-09-10T11:11:43.462Z
Learnt from: capcom6
Repo: android-sms-gateway/server PR: 178
File: pkg/cache/memory.go:59-74
Timestamp: 2025-09-10T11:11:43.462Z
Learning: In pkg/cache/memory.go, the cleanup method removes expired items first, then executes the provided callback function. This means methods like Drain that use cleanup don't need additional expiration filtering since expired items are already removed before the callback runs.

Applied to files:

  • internal/sms-gateway/modules/auth/service.go
🧬 Code graph analysis (5)
internal/sms-gateway/otp/errors.go (1)
internal/sms-gateway/modules/auth/service.go (1)
  • New (35-56)
internal/sms-gateway/modules/auth/module.go (1)
internal/sms-gateway/modules/auth/service.go (1)
  • New (35-56)
internal/sms-gateway/modules/auth/service.go (3)
internal/sms-gateway/otp/service.go (1)
  • Service (14-20)
internal/sms-gateway/otp/config.go (1)
  • Config (9-12)
internal/sms-gateway/otp/domain.go (1)
  • Code (6-9)
internal/sms-gateway/otp/service.go (5)
internal/sms-gateway/otp/config.go (1)
  • Config (9-12)
internal/sms-gateway/otp/storage.go (1)
  • Storage (11-13)
internal/sms-gateway/otp/errors.go (1)
  • ErrInitFailed (9-9)
internal/sms-gateway/otp/domain.go (1)
  • Code (6-9)
pkg/cache/options.go (1)
  • WithValidUntil (35-39)
internal/sms-gateway/handlers/middlewares/userauth/userauth.go (1)
internal/sms-gateway/models/models.go (1)
  • User (18-24)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: E2E
  • GitHub Check: Benchmark
  • GitHub Check: Test
  • GitHub Check: Analyze (go)
🔇 Additional comments (13)
internal/sms-gateway/modules/auth/module.go (1)

13-18: Auth module wiring and logger decoration look solid

The scoped logger via fx.Decorate and the ordering of fx.Provide(newRepository, fx.Private) before fx.Provide(New) are consistent with the established fx patterns and keep the auth module nicely self-contained. Based on learnings, this matches the recommended module-specific logger setup.

internal/sms-gateway/otp/domain.go (1)

5-9: OTP Code domain model is clear and minimal

Code cleanly captures the value plus expiry (ValidUntil) and aligns with how it’s consumed by the OTP/auth services and handlers.

internal/config/config.go (1)

19-20: OTP config struct and defaults are consistent with existing patterns

The new OTP block on Config, its YAML/env tags, and the default values (TTL=300, Retries=3) follow the same conventions as other config sections and provide sensible defaults for the OTP module.

Also applies to: 85-88, 122-125

internal/sms-gateway/otp/errors.go (1)

5-9: Error sentinel definitions are appropriate

ErrInvalidConfig and ErrInitFailed give clear, reusable markers for higher-level wrapping and errors.Is checks across the OTP config/service.

internal/sms-gateway/app.go (1)

22-22: OTP module wiring into the app looks correct

Importing otp and registering otp.Module() alongside the other fx modules keeps the OTP subsystem integrated with existing config/cache/logging without changing the server startup flow.

Also applies to: 58-58

internal/sms-gateway/handlers/mobile.go (1)

181-191: The review's concern about a type mismatch is unfounded. In Fiber v2, c.Context() returns context.Context (not *fasthttp.RequestCtx), making it fully compatible with GenerateUserCode's parameter type. The code is correct as written.

internal/sms-gateway/modules/auth/service.go (4)

35-56: Clean dependency injection pattern.

The refactored constructor properly accepts all explicit dependencies, improving testability and making the dependency graph clearer. The initialization of usersCache internally is appropriate since it's an implementation detail.


58-66: OTP integration looks correct.

The delegation to otpSvc.Generate properly propagates the context (addressing the earlier feedback). The error wrapping provides good context for debugging.


147-160: Code-based authorization properly integrated.

The method correctly delegates OTP validation to the dedicated service and retrieves the user by the validated ID. The context propagation and error wrapping are appropriate.


205-206: Simplified cleanup reflects architecture change.

Since OTP code expiration is now managed internally by the OTP storage layer with TTL-based eviction, removing codesCache cleanup here is correct.

internal/sms-gateway/otp/service.go (3)

30-44: Good defensive initialization checks.

The constructor properly validates the configuration and ensures required dependencies are non-nil before constructing the service.


95-104: LGTM!

The validation method cleanly delegates to storage for atomic get-and-delete, ensuring one-time use semantics.


54-93: The edge case is already prevented by validation.

The concern about Retries = 0 returning an empty code is not valid. Config.Validate() in internal/sms-gateway/otp/config.go (lines 19–20) already enforces that Retries must be greater than 0:

if c.Retries <= 0 {
    return fmt.Errorf("%w: retries must be greater than 0", ErrInvalidConfig)
}

Since NewService() calls cfg.Validate() and returns an error if validation fails, an invalid config with Retries <= 0 cannot proceed to the Generate() method.

Regarding the err = nil assignment on line 31: While technically redundant (since a successful SetOrFail() already returns nil), it's a stylistic choice and not a correctness issue.

Likely an incorrect or invalid review comment.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
internal/sms-gateway/otp/service.go (1)

30-43: Unify NewService error wrapping for config vs storage/logger

cfg.Validate() errors are returned raw, while storage/logger nil errors are wrapped with ErrInitFailed. If callers key off ErrInitFailed to detect “service failed to initialize”, config issues will be treated differently.

Consider wrapping config errors the same way:

 func NewService(cfg Config, storage *Storage, logger *zap.Logger) (*Service, error) {
-	if err := cfg.Validate(); err != nil {
-		return nil, err
-	}
+	if err := cfg.Validate(); err != nil {
+		return nil, fmt.Errorf("%w: invalid config: %w", ErrInitFailed, err)
+	}

This keeps all initialization failures discoverable via errors.Is(err, ErrInitFailed). Based on relevant_code_snippets for ErrInitFailed in internal/sms-gateway/otp/errors.go.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6cae7c4 and 454eb1e.

📒 Files selected for processing (1)
  • internal/sms-gateway/otp/service.go (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
internal/sms-gateway/otp/service.go (5)
internal/sms-gateway/otp/config.go (1)
  • Config (9-12)
internal/sms-gateway/otp/storage.go (1)
  • Storage (11-13)
internal/sms-gateway/otp/errors.go (1)
  • ErrInitFailed (9-9)
internal/sms-gateway/otp/domain.go (1)
  • Code (6-9)
pkg/cache/options.go (1)
  • WithValidUntil (35-39)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: E2E
  • GitHub Check: Test
  • GitHub Check: Benchmark
  • GitHub Check: Analyze (go)
🔇 Additional comments (2)
internal/sms-gateway/otp/service.go (2)

96-98: Validate delegating to storage looks appropriate

This thin wrapper around storage.GetAndDelete matches the documented behavior (lookup + delete) and keeps the OTP validity semantics centralized in the storage layer. No issues from my side here.


54-85: The code is valid; for range over integers is supported in Go 1.22+

The project uses Go 1.24.3, which supports for range over integer values as a valid language feature (introduced in Go 1.22). The code at line 62 will compile and run correctly.

Additionally, Config.Validate() already enforces that Retries > 0, so the validation concern is already addressed—the loop will never run with zero or negative retries.

The only remaining suggestion is the optional micro-optimization to hoist big.NewInt(maxValue) outside the loop to avoid reallocation on each iteration.

Likely an incorrect or invalid review comment.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
internal/sms-gateway/handlers/middlewares/userauth/userauth.go (1)

86-88: Optional: align comments with the actual locals key name

Since setUser is now the single point writing localsUser ("user") into the context, consider updating the NewBasic/NewCode doc comments that mention the key “LocalsUser” to either cite "user" directly or reference localsUser for clarity and to avoid confusion.

For example:

-// user in the request's Locals under the key LocalsUser. If the header is invalid,
+// user in the request's Locals under the key "user". If the header is invalid,

(and similarly for NewCode).

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 454eb1e and 5b42514.

📒 Files selected for processing (1)
  • internal/sms-gateway/handlers/middlewares/userauth/userauth.go (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
internal/sms-gateway/handlers/middlewares/userauth/userauth.go (1)
internal/sms-gateway/models/models.go (1)
  • User (18-24)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: goreleaser
🔇 Additional comments (2)
internal/sms-gateway/handlers/middlewares/userauth/userauth.go (2)

53-53: Good centralization of user locals handling via setUser

Delegating to setUser(c, user) removes duplication and keeps the locals key usage in one place; this also nicely addresses the earlier concern about an exported helper by keeping the helper package‑private.


75-80: All AuthorizeUserByCode call sites already use the updated signature consistently

The codebase contains only one call site for AuthorizeUserByCode (at line 75 in userauth.go), and it correctly passes c.Context() as the first argument, matching the function signature defined in ./internal/sms-gateway/modules/auth/service.go:148. No inconsistencies or outdated call conventions exist.

@capcom6 capcom6 force-pushed the otp/introduce-package branch from 5b42514 to 3aff6b6 Compare December 3, 2025 07:36
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
internal/sms-gateway/otp/storage.go (1)

15-18: Fix stale comment reference.

The comment still references "CodesCache" but the function is named "NewStorage".

Apply this diff:

-// NewStorage creates a new CodesCache with the underlying cache.
+// NewStorage creates a new Storage with the underlying cache.
🧹 Nitpick comments (2)
api/mobile.http (1)

11-13: Clarify Basic vs Bearer for /user/code example

Switching /user/code to Authorization: Basic {{credentials}} and keeping the old Bearer form commented out makes sense for the new flow, but it’s a bit ambiguous for consumers of this file. If Bearer is now deprecated for this endpoint, consider either removing the commented line or annotating it (e.g. # Legacy Bearer example) so it’s clear which scheme is actually supported.

internal/sms-gateway/otp/storage.go (1)

29-33: Consider consistent error wrapping.

SetOrFail and Cleanup wrap errors with context, but GetAndDelete returns the raw error. For consistency in error messages and debugging:

 // GetAndDelete retrieves and deletes a string value.
 func (s *Storage) GetAndDelete(ctx context.Context, key string) (string, error) {
 	data, err := s.cache.GetAndDelete(ctx, key)
+	if err != nil {
+		return "", fmt.Errorf("failed to get and delete cache item: %w", err)
+	}
-	return string(data), err
+	return string(data), nil
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5b42514 and 3aff6b6.

📒 Files selected for processing (16)
  • api/mobile.http (1 hunks)
  • configs/config.example.yml (1 hunks)
  • internal/config/config.go (3 hunks)
  • internal/config/module.go (2 hunks)
  • internal/sms-gateway/app.go (2 hunks)
  • internal/sms-gateway/handlers/middlewares/userauth/userauth.go (2 hunks)
  • internal/sms-gateway/handlers/mobile.go (1 hunks)
  • internal/sms-gateway/modules/auth/module.go (1 hunks)
  • internal/sms-gateway/modules/auth/service.go (3 hunks)
  • internal/sms-gateway/modules/auth/types.go (0 hunks)
  • internal/sms-gateway/otp/config.go (1 hunks)
  • internal/sms-gateway/otp/domain.go (1 hunks)
  • internal/sms-gateway/otp/errors.go (1 hunks)
  • internal/sms-gateway/otp/module.go (1 hunks)
  • internal/sms-gateway/otp/service.go (1 hunks)
  • internal/sms-gateway/otp/storage.go (1 hunks)
💤 Files with no reviewable changes (1)
  • internal/sms-gateway/modules/auth/types.go
🚧 Files skipped from review as they are similar to previous changes (4)
  • configs/config.example.yml
  • internal/sms-gateway/otp/module.go
  • internal/sms-gateway/otp/domain.go
  • internal/config/module.go
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-09-10T23:45:45.502Z
Learnt from: capcom6
Repo: android-sms-gateway/server PR: 178
File: internal/sms-gateway/online/module.go:14-16
Timestamp: 2025-09-10T23:45:45.502Z
Learning: fx.Decorate in uber-go/fx creates module-scoped decorated dependencies, not global modifications. Using fx.Decorate(func(log *zap.Logger) *zap.Logger { return log.Named("module_name") }) is a correct pattern for creating module-specific loggers that are scoped only to that module's dependency graph.

Applied to files:

  • internal/sms-gateway/modules/auth/module.go
📚 Learning: 2025-09-10T11:11:43.462Z
Learnt from: capcom6
Repo: android-sms-gateway/server PR: 178
File: pkg/cache/memory.go:59-74
Timestamp: 2025-09-10T11:11:43.462Z
Learning: In pkg/cache/memory.go, the cleanup method removes expired items first, then executes the provided callback function. This means methods like Drain that use cleanup don't need additional expiration filtering since expired items are already removed before the callback runs.

Applied to files:

  • internal/sms-gateway/modules/auth/service.go
🧬 Code graph analysis (8)
internal/sms-gateway/otp/errors.go (1)
internal/sms-gateway/modules/auth/service.go (1)
  • New (35-56)
internal/sms-gateway/handlers/middlewares/userauth/userauth.go (1)
internal/sms-gateway/models/models.go (1)
  • User (18-24)
internal/sms-gateway/otp/storage.go (1)
internal/config/config.go (1)
  • Cache (76-78)
internal/sms-gateway/modules/auth/module.go (1)
internal/sms-gateway/modules/auth/service.go (1)
  • New (35-56)
internal/sms-gateway/otp/config.go (3)
internal/worker/config/types.go (1)
  • Duration (11-11)
internal/config/config.go (1)
  • Config (10-20)
internal/sms-gateway/otp/errors.go (1)
  • ErrInvalidConfig (7-7)
internal/sms-gateway/otp/service.go (7)
internal/config/config.go (1)
  • Config (10-20)
internal/sms-gateway/modules/auth/service.go (2)
  • Config (17-20)
  • Service (22-33)
internal/sms-gateway/otp/config.go (1)
  • Config (9-12)
internal/sms-gateway/otp/storage.go (1)
  • Storage (11-13)
internal/sms-gateway/otp/errors.go (1)
  • ErrInitFailed (9-9)
internal/sms-gateway/otp/domain.go (1)
  • Code (6-9)
pkg/cache/options.go (1)
  • WithValidUntil (35-39)
internal/sms-gateway/modules/auth/service.go (3)
internal/sms-gateway/otp/service.go (1)
  • Service (14-20)
internal/sms-gateway/otp/config.go (1)
  • Config (9-12)
internal/sms-gateway/otp/domain.go (1)
  • Code (6-9)
internal/sms-gateway/app.go (2)
internal/config/module.go (1)
  • Module (24-143)
internal/sms-gateway/otp/module.go (1)
  • Module (9-22)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Benchmark
  • GitHub Check: Test
  • GitHub Check: Lint
  • GitHub Check: E2E
  • GitHub Check: Analyze (go)
🔇 Additional comments (16)
internal/sms-gateway/modules/auth/module.go (1)

16-17: LGTM!

The reordering to list the private newRepository provider before New improves readability and reflects the dependency order. Based on learnings, the fx.Decorate pattern for module-scoped loggers is correct.

internal/config/config.go (2)

85-88: LGTM!

The OTP struct definition follows the existing configuration patterns in the codebase with appropriate YAML and envconfig tags. The choice of uint16 for TTL and uint8 for Retries provides reasonable bounds.


122-125: Sensible defaults.

TTL of 300 seconds (5 minutes) and 3 retries are reasonable defaults for OTP codes, balancing security and usability.

internal/sms-gateway/handlers/mobile.go (1)

181-186: LGTM!

Proper context propagation from the Fiber request to the service layer. This enables cancellation and timeout handling throughout the OTP generation flow.

internal/sms-gateway/app.go (2)

22-22: LGTM!

Import correctly added for the new OTP package.


58-58: LGTM!

The OTP module is correctly wired into the application's dependency injection graph. The module's private providers (cache, storage) and public service will be properly scoped within the fx module.

internal/sms-gateway/handlers/middlewares/userauth/userauth.go (2)

53-53: LGTM! Clean refactor with the setUser helper.

The extraction of setUser eliminates duplication between the Basic and Code authentication paths. Making it package-private (lowercase) is appropriate since it's only used within this package.

Also applies to: 80-80, 86-88


75-75: LGTM! Context propagation is correct.

Passing c.Context() to AuthorizeUserByCode properly propagates cancellation, timeouts, and tracing through the OTP validation flow.

internal/sms-gateway/otp/errors.go (1)

1-10: LGTM! Standard error sentinel pattern.

The error definitions follow Go conventions with clear doc comments and will integrate well with the config validation and service initialization flows.

internal/sms-gateway/otp/config.go (1)

8-24: LGTM! Config validation is sound.

The Validate method correctly enforces positive values for both TTL and Retries, with properly wrapped error messages. Using time.Duration for TTL provides better type safety than raw integers.

internal/sms-gateway/modules/auth/service.go (3)

35-56: LGTM! Constructor explicitly wires dependencies.

The updated New function signature makes dependencies explicit, which improves testability and aligns with dependency injection best practices. The transition from in-memory code handling to the OTP service is clean.


59-66: LGTM! Context propagation is correct.

GenerateUserCode now properly uses the provided ctx instead of context.Background(), ensuring cancellation and timeouts propagate through the OTP generation flow.


148-160: LGTM! OTP-based authorization flow is correct.

The method properly delegates validation to otpSvc.Validate(ctx, code) and retrieves the user by ID. Error handling and context propagation are correct.

internal/sms-gateway/otp/service.go (3)

22-44: LGTM! Constructor validates dependencies thoroughly.

The constructor properly validates the config and checks for nil dependencies before initializing the service. Error handling with ErrInitFailed wrapping is appropriate.


54-86: LGTM! Code generation and retry logic are sound.

The implementation correctly:

  • Generates 6-digit codes (0-999999) using cryptographically secure randomness
  • Computes validUntil once before retries for consistency (avoiding race conditions where different attempts might have different expiration times)
  • Retries on both random generation and storage failures
  • Logs warnings without exposing codes in logs (good security practice)

88-98: LGTM! Validation delegates cleanly to storage.

The Validate method correctly delegates to storage.GetAndDelete, which atomically retrieves and removes the code, preventing reuse.

@capcom6 capcom6 added the ready label Dec 3, 2025
@capcom6 capcom6 force-pushed the otp/introduce-package branch from 3aff6b6 to 218da98 Compare December 10, 2025 02:41
@github-actions github-actions bot removed the ready label Dec 10, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
internal/sms-gateway/handlers/middlewares/userauth/userauth.go (1)

48-53: Fix user storage/retrieval mismatch in Locals (this currently breaks all userauth flows).

SetUser now stores a *users.User in Locals, but GetUser still asserts users.User by value:

  • SetUser: c.Locals(localsUser, user) where user is *users.User.
  • GetUser: c.Locals(localsUser).(users.User).

This type mismatch means the assertion always fails, so GetUser returns nil, HasUser is always false, and WithUser will respond with 401 even when SetUser was called. Handlers like mobile.postDevice that rely on userauth.GetUser(c) will treat authenticated users as anonymous and create new accounts instead of attaching devices.

Update GetUser to work with the pointer stored by SetUser and (optionally) add a doc comment:

-// NewBasic returns a middleware that optionally performs HTTP Basic authentication.
+// NewBasic returns a middleware that optionally performs HTTP Basic authentication.
@@
-		user, err := usersSvc.Login(c.Context(), username, password)
+		user, err := usersSvc.Login(c.Context(), username, password)
@@
-		SetUser(c, user)
+		SetUser(c, user)
@@
-		user, err := authSvc.AuthorizeUserByCode(c.Context(), code)
+		user, err := authSvc.AuthorizeUserByCode(c.Context(), code)
@@
-func SetUser(c *fiber.Ctx, user *users.User) {
-	c.Locals(localsUser, user)
-}
+// SetUser stores the given user in the request's Locals.
+func SetUser(c *fiber.Ctx, user *users.User) {
+	c.Locals(localsUser, user)
+}
@@
-func GetUser(c *fiber.Ctx) *users.User {
-	user, ok := c.Locals(localsUser).(users.User)
-	if !ok {
-		return nil
-	}
-
-	return &user
-}
+func GetUser(c *fiber.Ctx) *users.User {
+	user, ok := c.Locals(localsUser).(*users.User)
+	if !ok {
+		return nil
+	}
+
+	return user
+}

You may also want to tweak the GetUser comment to say it returns a *users.User directly, instead of a value re-wrapped as a pointer.

Also applies to: 75-81, 86-108

🧹 Nitpick comments (2)
internal/sms-gateway/otp/storage.go (1)

10-41: Update NewStorage doc comment to reflect the actual type.

The implementation is sound, but the comment on NewStorage still mentions CodesCache, which looks like a leftover from refactoring and can confuse future readers.

-// NewStorage creates a new CodesCache with the underlying cache.
+// NewStorage creates a new Storage with the underlying cache.

You may also consider returning an empty string when GetAndDelete returns an error (instead of string(data)), but current call sites appear to ignore the value on non-nil errors, so this is optional.

internal/sms-gateway/modules/auth/service.go (1)

126-144: clean is now a no-op while Run still schedules it hourly.

Run continues to tick and call s.clean(ctx) every hour, but clean has an empty body. Previously this hook was used to clean OTP-related cache entries; now there appears to be no cleanup wired for OTP storage.

Consider either:

  • Delegating to an OTP-level cleanup (e.g., exposing a Cleanup/Run on the OTP service or wiring Storage.Cleanup somewhere), or
  • Removing Run/clean from auth if cleanup is handled entirely inside the cache or OTP module.

This avoids confusing, effectively dead scheduling code and ensures expired OTP entries are actually reclaimed. Based on learnings, cache.Cleanup is the intended place to evict expired items.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3aff6b6 and 218da98.

📒 Files selected for processing (16)
  • api/mobile.http (1 hunks)
  • configs/config.example.yml (1 hunks)
  • internal/config/config.go (3 hunks)
  • internal/config/module.go (2 hunks)
  • internal/sms-gateway/app.go (2 hunks)
  • internal/sms-gateway/handlers/middlewares/jwtauth/jwtauth.go (1 hunks)
  • internal/sms-gateway/handlers/middlewares/userauth/userauth.go (2 hunks)
  • internal/sms-gateway/handlers/mobile.go (1 hunks)
  • internal/sms-gateway/modules/auth/service.go (4 hunks)
  • internal/sms-gateway/modules/auth/types.go (0 hunks)
  • internal/sms-gateway/otp/config.go (1 hunks)
  • internal/sms-gateway/otp/domain.go (1 hunks)
  • internal/sms-gateway/otp/errors.go (1 hunks)
  • internal/sms-gateway/otp/module.go (1 hunks)
  • internal/sms-gateway/otp/service.go (1 hunks)
  • internal/sms-gateway/otp/storage.go (1 hunks)
💤 Files with no reviewable changes (1)
  • internal/sms-gateway/modules/auth/types.go
🚧 Files skipped from review as they are similar to previous changes (7)
  • internal/sms-gateway/app.go
  • internal/sms-gateway/otp/module.go
  • configs/config.example.yml
  • internal/config/module.go
  • internal/sms-gateway/otp/domain.go
  • internal/config/config.go
  • api/mobile.http
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-10T11:11:43.462Z
Learnt from: capcom6
Repo: android-sms-gateway/server PR: 178
File: pkg/cache/memory.go:59-74
Timestamp: 2025-09-10T11:11:43.462Z
Learning: In pkg/cache/memory.go, the cleanup method removes expired items first, then executes the provided callback function. This means methods like Drain that use cleanup don't need additional expiration filtering since expired items are already removed before the callback runs.

Applied to files:

  • internal/sms-gateway/modules/auth/service.go
🧬 Code graph analysis (6)
internal/sms-gateway/otp/errors.go (1)
internal/sms-gateway/modules/auth/service.go (1)
  • New (33-52)
internal/sms-gateway/otp/storage.go (1)
internal/config/config.go (1)
  • Cache (79-81)
internal/sms-gateway/otp/service.go (5)
internal/sms-gateway/otp/config.go (1)
  • Config (9-12)
internal/sms-gateway/otp/storage.go (1)
  • Storage (11-13)
internal/sms-gateway/otp/errors.go (1)
  • ErrInitFailed (9-9)
internal/sms-gateway/otp/domain.go (1)
  • Code (6-9)
pkg/cache/options.go (1)
  • WithValidUntil (70-74)
internal/sms-gateway/otp/config.go (2)
internal/config/config.go (1)
  • Config (12-23)
internal/sms-gateway/otp/errors.go (1)
  • ErrInvalidConfig (7-7)
internal/sms-gateway/handlers/middlewares/userauth/userauth.go (1)
internal/sms-gateway/users/domain.go (1)
  • User (5-10)
internal/sms-gateway/handlers/middlewares/jwtauth/jwtauth.go (1)
internal/sms-gateway/handlers/middlewares/userauth/userauth.go (1)
  • SetUser (86-88)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: E2E
  • GitHub Check: Benchmark
  • GitHub Check: Analyze (go)
🔇 Additional comments (9)
internal/sms-gateway/modules/auth/service.go (3)

22-52: otpSvc wiring and constructor changes look good.

Injecting *otp.Service into Service and threading it through New is consistent with how other dependencies are handled and keeps auth focused on orchestration rather than OTP implementation.


55-62: GenerateUserCode now properly delegates to otpSvc with context.

Using ctx in s.otpSvc.Generate(ctx, userID) fixes the earlier context propagation issue and cleanly separates OTP generation from auth.


111-124: AuthorizeUserByCode integration with otpSvc is consistent.

Delegating validation to s.otpSvc.Validate(ctx, code) and then loading the user via usersSvc.GetByUsername is coherent with how the rest of the module models user identity; error wrapping is also clear.

internal/sms-gateway/handlers/mobile.go (1)

200-212: Context propagation to GenerateUserCode is correct.

Passing c.Context() into GenerateUserCode aligns this handler with the new auth API and preserves cancellation/timeout semantics.

internal/sms-gateway/handlers/middlewares/jwtauth/jwtauth.go (1)

29-39: JWT middleware now aligns with pointer-based SetUser.

Using userauth.SetUser(c, user) with *users.User from GetByUsername correctly matches the updated SetUser signature; no further changes needed here.

internal/sms-gateway/otp/errors.go (1)

1-10: Error sentinel definitions are clear and conventional.

ErrInvalidConfig and ErrInitFailed are well-named, documented, and suitable for use with errors.Is in callers; no changes needed.

internal/sms-gateway/otp/config.go (1)

8-24: OTP Config and validation look solid.

Using time.Duration for TTL and validating both TTL and Retries against ErrInvalidConfig gives clear, type-safe configuration errors; no adjustments needed.

internal/sms-gateway/otp/service.go (2)

30-44: NewService initialization guardrails look correct.

Config validation, plus explicit nil checks for storage and logger with ErrInitFailed, provide clear, early failures for misconfiguration; this constructor looks good as is.


88-98: Validate is a thin, appropriate wrapper over storage.

Delegating validation to s.storage.GetAndDelete(ctx, code) matches the intended behavior of one-time codes and keeps logic centralized in the storage/cache layer.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
internal/sms-gateway/handlers/middlewares/userauth/userauth.go (1)

88-90: Add doc comment to exported SetUser function.

The exported SetUser function still lacks a doc comment. As noted in a previous review, either add a doc comment (e.g., // SetUser stores the user in the request's Locals.) or make it unexported by renaming to setUser.

🧹 Nitpick comments (1)
internal/sms-gateway/handlers/middlewares/userauth/userauth.go (1)

132-140: Consider updating handler signature to use pointer for consistency.

The WithUser decorator's handler signature still expects users.User by value (line 132), while the rest of the codebase now uses *users.User pointers. Consider updating the signature to func(*users.User, *fiber.Ctx) error and removing the dereference on line 139 for consistency with the pointer-based approach.

This may be intentional to provide handlers with an immutable copy, but pointer consistency would align better with the current refactoring direction.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 57bbcce and a0ea59b.

📒 Files selected for processing (1)
  • internal/sms-gateway/handlers/middlewares/userauth/userauth.go (3 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
internal/sms-gateway/handlers/middlewares/userauth/userauth.go (1)
internal/sms-gateway/users/domain.go (1)
  • User (5-10)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: E2E
  • GitHub Check: Benchmark
  • GitHub Check: Analyze (go)
🔇 Additional comments (2)
internal/sms-gateway/handlers/middlewares/userauth/userauth.go (2)

54-54: LGTM! Context-aware authentication and consistent pointer handling.

The changes correctly:

  • Pass request context to AuthorizeUserByCode (line 77), enabling proper propagation of deadlines and cancellation signals
  • Use the returned *users.User pointers directly with SetUser

Also applies to: 77-77, 82-82


104-109: LGTM! Correct pointer type assertion and return.

The logic correctly asserts to *users.User and returns the pointer directly, matching the updated SetUser signature that stores pointers in Locals.

@capcom6 capcom6 force-pushed the otp/introduce-package branch from a0ea59b to 3c25b21 Compare December 10, 2025 23:26
@capcom6 capcom6 added ready deployed The PR is deployed on staging labels Dec 11, 2025
@capcom6 capcom6 merged commit aa68c69 into master Dec 12, 2025
10 checks passed
@capcom6 capcom6 deleted the otp/introduce-package branch December 12, 2025 23:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

deployed The PR is deployed on staging ready

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants