Skip to content

Conversation

@capcom6
Copy link
Member

@capcom6 capcom6 commented Nov 25, 2025

Summary by CodeRabbit

  • New Features

    • OTP system added: generation, validation, storage, service, and app wiring with configurable TTL and retries.
  • Changes

    • Mobile user-code endpoint now uses Basic credentials instead of Bearer.
    • Authentication and code-generation flows now use the new OTP service and propagate request context.
  • Documentation

    • Configuration example updated to include OTP settings (ttl, retries).

✏️ 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 subsystem (config, domain, errors, storage, service, fx module), replaces in-memory one-time-code handling in auth with the OTP service and context-aware APIs, wires OTP into DI and app modules, updates mobile API example to Basic auth, and adds a setUser helper in middleware.

Changes

Cohort / File(s) Change Summary
OTP module
internal/sms-gateway/otp/config.go, internal/sms-gateway/otp/domain.go, internal/sms-gateway/otp/errors.go, internal/sms-gateway/otp/module.go, internal/sms-gateway/otp/service.go, internal/sms-gateway/otp/storage.go
New otp package: Config (TTL, Retries) with Validate, Code domain type, exported error tokens, Storage wrapping cache.Cache, Service with Generate(ctx,userID) and Validate(ctx,code), and fx module wiring.
Config integration
configs/config.example.yml, internal/config/config.go, internal/config/module.go
Added otp block to example YAML; added OTP field on top-level Config with defaults TTL=300 and Retries=3; added fx provider mapping top-level Configotp.Config (seconds → time.Duration).
Auth service 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 one-time-code types/caching; auth now depends on otp.Service; signatures changed to GenerateUserCode(ctx,...) (*otp.Code, error) and AuthorizeUserByCode(ctx,code) (*models.User, error); codes cache removed; provider ordering adjusted.
Handlers & middleware
internal/sms-gateway/handlers/mobile.go, internal/sms-gateway/handlers/middlewares/userauth/userauth.go
Call sites updated to pass context.Context into Generate/Authorize methods; middleware now uses internal setUser(c, user) to set user in Fiber locals; AuthorizeUserByCode invoked with Fiber context in one path.
App wiring & API sample
internal/sms-gateway/app.go, api/mobile.http
Registered otp.Module() in FX modules; updated example api/mobile.http Authorization header from Bearer {{mobileToken}} to Basic {{credentials}} (previous Bearer left commented).

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Handler as Mobile Handler
    participant AuthSvc as Auth Service
    participant OTPSvc as OTP Service
    participant Cache as Cache Storage

    rect rgb(240,248,255)
    Note over Client,Cache: OTP Generation Flow
    Client->>Handler: POST /user/code
    Handler->>AuthSvc: GenerateUserCode(ctx, userID)
    AuthSvc->>OTPSvc: Generate(ctx, userID)
    OTPSvc->>OTPSvc: generate 6-digit code (crypto/rand)
    OTPSvc->>Cache: SetOrFail(ctx, code, userID, TTL)
    Cache-->>OTPSvc: stored / error
    OTPSvc-->>AuthSvc: Code{Code, ValidUntil}
    AuthSvc-->>Handler: Code
    Handler-->>Client: 200 OK + Code
    end

    rect rgb(240,255,240)
    Note over Client,Cache: OTP Validation Flow
    Client->>Handler: GET /user/code (Basic auth)
    Handler->>AuthSvc: AuthorizeUserByCode(ctx, code)
    AuthSvc->>OTPSvc: Validate(ctx, code)
    OTPSvc->>Cache: GetAndDelete(ctx, code)
    Cache-->>OTPSvc: userID (if valid) / error
    OTPSvc-->>AuthSvc: userID
    AuthSvc->>AuthSvc: load user by userID
    AuthSvc-->>Handler: User
    Handler-->>Client: 200 OK + User
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

  • Files/areas needing extra attention:
    • internal/sms-gateway/modules/auth/service.go — signature changes, DI changes, removal of codes cache, updated control flow.
    • internal/sms-gateway/otp/service.go — randomness, retry loop, TTL and error handling, logging.
    • internal/sms-gateway/otp/storage.go — cache API usage and byte/string conversions.
    • DI wiring: internal/config/module.go and internal/sms-gateway/app.go.
    • Middleware changes: internal/sms-gateway/handlers/middlewares/userauth/userauth.go and handlers invoking new signatures.

Possibly related PRs

Suggested labels

ready

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 45.45% 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 accurately describes the main change: extracting OTP logic into a separate package from the auth module, which is the primary refactoring across all modified files.
✨ 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

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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants