Skip to content

Feat/echo v5#51

Merged
msyahidin merged 5 commits into
mainfrom
feat/echo-v5
Apr 17, 2026
Merged

Feat/echo v5#51
msyahidin merged 5 commits into
mainfrom
feat/echo-v5

Conversation

@msyahidin
Copy link
Copy Markdown
Owner

@msyahidin msyahidin commented Apr 13, 2026

  • Update go.mod: github.com/labstack/echo/v4 -> v5, remove labstack/gommon
  • Remove github.com/swaggo/echo-swagger (no v5 support); replace with
    hand-rolled Swagger UI handler using swaggo/files/v2 and swag.ReadDoc()
  • Mass import path replacement across 55 Go files (v4 -> v5)
  • Fix echo.Context interface -> *echo.Context struct pointer (v5 change)
  • Fix HTTPErrorHandler signature: func(err, ctx) -> func(ctx, err)
  • Fix NewHTTPError 3-arg calls -> 2-arg with .Wrap(err) (v5 API change)
  • Fix e.Routes() -> e.Router().Routes() (v5 API change)
  • Fix e.Shutdown() -> echo.StartConfig with GracefulTimeout (v5 API change)
  • Fix e.Debug field removed -> use config-based debug flag
  • Fix c.Response().Status/Committed -> echo.UnwrapResponse() (v5 change)
  • Fix RecoverConfig: remove LogLevel/LogErrorFunc/DisableErrorHandler fields
  • Fix gommon/random -> math/rand for RequestID fallback generation
  • Fix Validator interface: Validate(interface{}) -> Validate(any)

https://claude.ai/code/session_012HXUTcemrXNF2PbH2kSQ7m

Summary by CodeRabbit

  • Chores
    • Upgraded core web framework and multiple dependencies.
    • Modernized server start/shutdown for safer, graceful shutdowns and non-fatal shutdown logging.
  • Bug Fixes
    • Route listing generation aligned with updated router behavior.
    • Improved request handling and added additional error checks across several endpoints (more appropriate HTTP error responses).
  • Refactor
    • Updated many handlers and middleware to use the framework's updated context API.

claude added 2 commits April 12, 2026 15:21
- Update go.mod: github.com/labstack/echo/v4 -> v5, remove labstack/gommon
- Remove github.com/swaggo/echo-swagger (no v5 support); replace with
  hand-rolled Swagger UI handler using swaggo/files/v2 and swag.ReadDoc()
- Mass import path replacement across 55 Go files (v4 -> v5)
- Fix echo.Context interface -> *echo.Context struct pointer (v5 change)
- Fix HTTPErrorHandler signature: func(err, ctx) -> func(ctx, err)
- Fix NewHTTPError 3-arg calls -> 2-arg with .Wrap(err) (v5 API change)
- Fix e.Routes() -> e.Router().Routes() (v5 API change)
- Fix e.Shutdown() -> echo.StartConfig with GracefulTimeout (v5 API change)
- Fix e.Debug field removed -> use config-based debug flag
- Fix c.Response().Status/Committed -> echo.UnwrapResponse() (v5 change)
- Fix RecoverConfig: remove LogLevel/LogErrorFunc/DisableErrorHandler fields
- Fix gommon/random -> math/rand for RequestID fallback generation
- Fix Validator interface: Validate(interface{}) -> Validate(any)

https://claude.ai/code/session_012HXUTcemrXNF2PbH2kSQ7m
echo-swagger/v2 now supports Echo v5 (github.com/swaggo/echo-swagger/v2
v2.0.1). Replacing the custom swagger handler with echoSwagger.WrapHandler
also removes the last indirect pulls of echo/v4 and labstack/gommon.

https://claude.ai/code/session_012HXUTcemrXNF2PbH2kSQ7m
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 13, 2026

Warning

Rate limit exceeded

@msyahidin has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 49 minutes and 13 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 49 minutes and 13 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: cdc81df9-a241-4968-ace7-38feb5d8ce70

📥 Commits

Reviewing files that changed from the base of the PR and between 66e9733 and 5a623d1.

📒 Files selected for processing (1)
  • internal/applications/rbac/controllers/audit_controller.go
📝 Walkthrough

Walkthrough

Upgrades the web stack from Echo v4 → v5 across the codebase, converting handlers/middleware/utility APIs to pointer-based *echo.Context, updates dependencies (including echo-swagger v2), and refactors server startup/shutdown to use echo.StartConfig with graceful shutdown and a serverDone synchronization channel.

Changes

Cohort / File(s) Summary
Server lifecycle & templates
cmd/main.go, cmd/server/rest_server.go, cmd/server/web_server.go
Reworked server startup/shutdown to use echo.StartConfig / sc.Start(ctx,e), added serverDone channel and OnShutdownError logging; route listing now uses e.Router().Routes(); TemplateRenderer.Render signature and web handler closures updated to Echo v5 pointer context.
Module & config
go.mod, config/config.go
Bumped Echo to v5 and updated swagger module to v2; adjusted go directive and indirect deps; SetDebugMode no longer mutates Echo internals or uses gommon log, now emits a debug log only.
Controllers / Routes (Auth, Health, Notification, User, RBAC)
internal/applications/.../*_controller.go, *_routes.go, registry.go, tests`
Updated imports to github.com/labstack/echo/v5 and changed nearly all HTTP handler signatures from echo.Context*echo.Context. Small behavioral fixes: added error checks (user ID parsing, DTO mapping), and introduced toMapOrEmpty in audit controller.
Middleware suite
internal/middlewares/..., internal/middlewares/registry.go
Migrated middleware to Echo v5, converting handler signatures to *echo.Context. Adjusted LogValuesFunc, GzipWithConfig Skipper signature, removed custom recover/logging config, changed audit persistence to async background context, and updated request-id fallback implementation.
Error handling chain
pkg/errors/*.go, pkg/errors/registry.go
Switched to Echo v5 and pointer contexts in all handlers; response-status handling now uses echo.UnwrapResponse()/resolved response to avoid panics; Chain.EchoHandler parameter order adjusted to (ctx, err).
Auth & JWT utilities
pkg/authenticator/*.go, jwt_token.go, tests`
Authenticator, JWT extraction and middleware signatures updated to use *echo.Context. Minor error-wrapping adjustment for unauthorized HTTP errors.
Validation & translator
pkg/validator/*, mocks`
Validator interface and helpers changed to accept *echo.Context; Validate uses any for modern typing; mocks updated to match signatures.
Logger & response utils
pkg/logger/logger.go, pkg/utils/response/response_json_builder.go, tests`
Request-logging entrypoints and response builders updated to *echo.Context; status extraction now uses echo.ResolveResponseStatus/UnwrapResponse.
Versioning & routing tests
pkg/versioning/*, pkg/*_test.go
Echo imports updated to v5; no versioning logic changes. Most tests updated import/signature types to compile against v5.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant ServerMain as Main goroutine
  participant EchoServer as Echo (server goroutine)
  participant ShutdownCtx as Shutdown context

  Client->>EchoServer: HTTP requests
  ServerMain->>EchoServer: sc.Start(ctx, e) (start server goroutine)
  Note right of EchoServer: Server runs until Shutdown requested
  ServerMain->>ServerMain: wait for OS signal
  ServerMain->>ShutdownCtx: create 10s shutdown ctx
  ServerMain->>EchoServer: request graceful shutdown
  EchoServer-->>ServerMain: closes serverDone when shutdown completes
  ServerMain->>EchoServer: OnShutdownError logs (non-fatal)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰 Hopped from v4 to v5 with a cheerful twirl,
Pointers now nestle and routing heads swirl,
Shutdowns wait kindly, routes list with care,
Logs hum along, responses laid bare,
A rabbit's small cheer for each compile and whirl!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 49.23% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ 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 'Feat/echo v5' clearly describes the primary change: upgrading the Echo framework dependency from v4 to v5.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/echo-v5

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
Copy Markdown
Contributor

@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: 3

Caution

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

⚠️ Outside diff range comments (5)
internal/middlewares/rbac_audit_middleware.go (2)

107-111: ⚠️ Potential issue | 🟠 Major

Don't reuse the request context in the async audit write.

ctx comes from c.Request().Context(). For incoming server requests, that context is canceled when the handler returns, so auditRepo.Create(ctx, log) can be aborted right as the request finishes. That makes audit persistence best-effort instead of reliable. Use a detached context with its own timeout, or enqueue the record to a worker. (pkg.go.dev)

🛠️ Safer pattern
go func(log *models.AuditLog) {
	auditCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()

	if err := auditRepo.Create(auditCtx, log); err != nil {
		logger.Errorf("Failed to save audit log: %v", err)
	}
}(log)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/middlewares/rbac_audit_middleware.go` around lines 107 - 111, The
current goroutine reuses the incoming request context (ctx) which may be
canceled when the handler returns, causing auditRepo.Create(ctx, log) to be
aborted; change the async write to use a detached context with its own timeout
(e.g., context.WithTimeout(context.Background(), 5*time.Second)) or enqueue the
log to a background worker instead, and call auditRepo.Create(auditCtx, log)
inside the goroutine while ensuring the cancel function is deferred and errors
are logged via logger.Errorf; update the goroutine surrounding auditRepo.Create,
referencing auditRepo.Create, ctx, and log.

80-90: ⚠️ Potential issue | 🟠 Major

Use echo.ResolveResponseStatus to determine HTTP status code.

The current manual status resolution does not follow Echo v5's response status precedence: when a response is committed, its status is final and errors on unwind must not override it. Additionally, the code only handles *echo.HTTPError but misses other errors implementing the HTTPStatusCoder interface. Echo v5.1.0 provides ResolveResponseStatus which correctly implements all precedence rules: committed responses take priority, then response-suggested status, then error status.

🛠️ Suggested fix
-			// Add response status
-			statusCode := http.StatusOK
-			if echoResp != nil {
-				statusCode = echoResp.Status
-				if statusCode == 0 {
-					statusCode = http.StatusOK
-				}
-			}
-			if err != nil {
-				if he, ok := err.(*echo.HTTPError); ok {
-					statusCode = he.Code
-				}
-			}
+			// Add response status
+			_, statusCode := echo.ResolveResponseStatus(c.Response(), err)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/middlewares/rbac_audit_middleware.go` around lines 80 - 90, Replace
the manual status resolution logic (using statusCode, echoResp and
type-asserting *echo.HTTPError) with Echo's canonical resolver: call
echo.ResolveResponseStatus(...) passing the current Response, echoResp and err
to compute the effective status (this handles committed responses,
response-suggested status and any HTTPStatusCoder errors). Ensure the echo
package is imported and remove the custom *echo.HTTPError type assertion so the
middleware uses the returned status from echo.ResolveResponseStatus to set
statusCode.
internal/applications/user/controller/user_controller.go (2)

101-102: ⚠️ Potential issue | 🟡 Minor

Pre-existing bug: error from dtoMapper.Map is not checked.

The error returned from dtoMapper.Map on line 101 is not checked before returning success on line 102. If mapping fails, the response may contain incomplete or zero-valued data.

🐛 Suggested fix
 	err = dtoMapper.Map(&pokemonGetResponseDto, result)
+	if err != nil {
+		return response.Error(eCtx, http.StatusInternalServerError, err)
+	}
 	return response.Success(eCtx, pokemonGetResponseDto)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/applications/user/controller/user_controller.go` around lines 101 -
102, The call to dtoMapper.Map(&pokemonGetResponseDto, result) assigns an error
to err but that error is not checked before returning response.Success(eCtx,
pokemonGetResponseDto); update the code in user_controller.go to check err after
calling dtoMapper.Map (handle the error from dtoMapper.Map), and if non-nil
return an error response (e.g., response.Error or appropriate failure using
eCtx) instead of response.Success; reference dtoMapper.Map,
pokemonGetResponseDto, err, response.Success and eCtx when making the change so
the mapping failure yields a proper error response.

35-36: ⚠️ Potential issue | 🟠 Major

Pre-existing bug: error from strconv.Atoi is silently ignored.

The error from strconv.Atoi on line 35 is assigned to err, but line 36 immediately reassigns err with the result of the service call, causing the conversion error to be silently ignored. If the ID is not a valid integer, idString will be 0 and the subsequent service call may return unexpected results.

🐛 Suggested fix
 	idString, err := strconv.Atoi(userGetReq.ID)
+	if err != nil {
+		return response.Error(eCtx, http.StatusBadRequest, err)
+	}
 	user, err := c.service.GetById(eCtx.Request().Context(), uint32(idString))
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/applications/user/controller/user_controller.go` around lines 35 -
36, The strconv.Atoi call of userGetReq.ID assigns err but it gets overwritten
by c.service.GetById, so conversion errors are ignored; fix by checking the
error returned from strconv.Atoi immediately after the call (e.g., if err != nil
{ return/respond with a bad-request error referencing invalid userGetReq.ID })
before calling c.service.GetById, and avoid reusing the same err without
handling; ensure you pass the validated idString (converted to uint32) into
c.service.GetById using the existing eCtx.Request().Context().
internal/applications/rbac/controllers/policy_controller.go (1)

78-79: ⚠️ Potential issue | 🟠 Major

Pre-existing bug: potential nil pointer dereference on req.TenantID.

Line 79 dereferences *req.TenantID, but the condition on line 78 only checks req.Role != nil. If req.Role is non-nil but req.TenantID is nil, this will panic.

🐛 Suggested fix
-	} else if req.Role != nil {
-		casbinPolicies, err := c.policyService.GetPoliciesByRole(ctx.Request().Context(), *req.Role, *req.TenantID)
+	} else if req.Role != nil && req.TenantID != nil {
+		casbinPolicies, err := c.policyService.GetPoliciesByRole(ctx.Request().Context(), *req.Role, *req.TenantID)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/applications/rbac/controllers/policy_controller.go` around lines 78
- 79, The code dereferences req.TenantID when only req.Role is checked, risking
a nil pointer; update the conditional that calls policyService.GetPoliciesByRole
so it verifies req.TenantID is non-nil before using *req.TenantID (e.g., change
the branch to check both req.Role != nil && req.TenantID != nil), and if
TenantID is nil return a clear error/validation response instead of
dereferencing; locate the usage in policy_controller.go around the call to
c.policyService.GetPoliciesByRole to apply this fix.
🧹 Nitpick comments (5)
config/config.go (1)

214-215: SetDebugMode now behaves like a no-op setter; consider renaming/deprecating.

The current name implies state mutation, but this implementation only logs. A clearer API will reduce future misuse.

♻️ Suggested refactor
-func SetDebugMode(_ *echo.Echo, debug bool) {
-	logger.Debugf("Debug mode set to %v", debug)
-}
+// Deprecated: no longer mutates Echo runtime state.
+func SetDebugMode(_ *echo.Echo, debug bool) {
+	logger.Debugf("Debug mode set to %v", debug)
+}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@config/config.go` around lines 214 - 215, SetDebugMode currently only logs
the value and doesn't mutate any state; either make it actually set debug mode
on the Echo instance or rename/deprecate it to avoid confusion. Fix by one of
two minimal options: (A) implement state change inside SetDebugMode by calling
echoServer.HideBanner = !debug and/or echoServer.Debug = debug (or set your
global logger level) so the function truly sets debug on the provided
*echo.Echo, and update callers accordingly; or (B) rename the function to
LogDebugMode/DebugModeLog, add a deprecation comment to SetDebugMode, and update
all call sites to the new name so the API clearly reflects it is only logging.
Reference: the SetDebugMode function in config.go.
pkg/errors/generic_handler.go (1)

14-21: Silent failure when UnwrapResponse fails.

When echo.UnwrapResponse() returns an error, the status is not set and the handler silently returns nil. Consider logging or handling this edge case:

💡 Suggested improvement
 func (g *GenericHandler) Handle(err error, ctx *echo.Context) error {
-
 	if resp, unwrapErr := echo.UnwrapResponse(ctx.Response()); unwrapErr == nil {
 		resp.Status = 500
+	} else {
+		// Log or handle unwrap failure if needed
 	}
 	// addition code for body or header here
 	return nil
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/errors/generic_handler.go` around lines 14 - 21, The
GenericHandler.Handle currently ignores errors from
echo.UnwrapResponse(ctx.Response()), causing silent failures; update Handle
(method GenericHandler.Handle) to handle the unwrapErr path: if
echo.UnwrapResponse returns an error, set an HTTP 500 on the original
ctx.Response() (or call ctx.Response().WriteHeader(500)/SetStatus if available)
and log the unwrapErr via your logger (or return a wrapped error) instead of
returning nil, while preserving the existing success path that sets resp.Status
= 500 when unwrapErr == nil.
internal/middlewares/registry.go (1)

30-32: Recovery middleware behavior change may warrant attention.

The Echo v5 RecoverConfig no longer supports LogLevel, LogErrorFunc, or DisableErrorHandler. The previous configuration included custom stack trace logging and DisableErrorHandler: true.

With the current minimal config, panics will now be passed to the HTTPErrorHandler (default behavior) instead of being handled internally. Verify this aligns with your error handling strategy in pkg/errors/handler.go.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/middlewares/registry.go` around lines 30 - 32, Echo v5 removed
RecoverConfig options so current
middleware.RecoverWithConfig(middleware.RecoverConfig{DisablePrintStack:
!mainConfig.App().Debug}) will forward panics to the HTTPErrorHandler; update
code to either (A) implement a custom recover middleware (replace
middleware.RecoverWithConfig) that captures panics, logs stack traces when
mainConfig.App().Debug is true and writes a safe response (restoring prior
DisableErrorHandler behavior), or (B) ensure pkg/errors/handler.go's
HTTPErrorHandler properly handles panic values (inspect error types/values and
produce an appropriate response and logs). Locate
middleware.RecoverWithConfig/RecoverConfig usage in registry.go and adjust
accordingly, or modify HTTPErrorHandler in pkg/errors/handler.go to handle
panics from RecoverWithConfig.
internal/applications/auth/controller/auth_controller.go (1)

152-152: Remove debug print statement.

fmt.Println("AuthController.Me called") appears to be leftover debug code. Consider removing it or using the structured logger instead.

♻️ Suggested fix
 func (c *AuthController) Me(eCtx *echo.Context) error {
-	fmt.Println("AuthController.Me called")
 	// Get auth context from middleware
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/applications/auth/controller/auth_controller.go` at line 152, Remove
the stray debug print in the AuthController.Me handler: replace or delete the
fmt.Println("AuthController.Me called") call inside the Me method of
AuthController; if logging is desired use the existing structured logger used
elsewhere in the controller (e.g., c.logger or AuthController's logger) to emit
a debug-level message instead of fmt.Println, ensuring consistency with the
app's logging approach.
internal/applications/rbac/controllers/audit_controller.go (1)

112-113: Pre-existing risk: unchecked type assertions may panic.

The type assertions log.PolicyBefore.(map[string]interface{}) and log.PolicyAfter.(map[string]interface{}) will panic if the underlying value is nil or a different type. This is not introduced by this PR but is worth noting.

Consider using the comma-ok idiom for safety:

🛡️ Suggested defensive check
-			PolicyBefore:   log.PolicyBefore.(map[string]interface{}),
-			PolicyAfter:    log.PolicyAfter.(map[string]interface{}),
+			PolicyBefore:   toMapOrEmpty(log.PolicyBefore),
+			PolicyAfter:    toMapOrEmpty(log.PolicyAfter),

Where toMapOrEmpty is a helper that safely handles nil or type mismatch.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/applications/rbac/controllers/audit_controller.go` around lines 112
- 113, The direct type assertions on log.PolicyBefore and log.PolicyAfter can
panic if the value is nil or the wrong type; update the code that builds the
response (the lines referencing log.PolicyBefore and log.PolicyAfter) to perform
safe assertions using the comma-ok idiom or a helper like toMapOrEmpty that
returns an empty map on nil/mismatch, and then use that safe value for
PolicyBefore and PolicyAfter to prevent runtime panics.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cmd/server/web_server.go`:
- Around line 15-17: The Render method signature on TemplateRenderer is using
the old parameter order; update the method to match Echo v5 by changing func (t
*TemplateRenderer) Render(...) to func (t *TemplateRenderer) Render(c
*echo.Context, w io.Writer, templateName string, data any) error, adjust imports
to include io and use templateName and data when calling
t.templates.ExecuteTemplate (writing to w instead of http.ResponseWriter), and
ensure the context parameter is the first argument named c; keep function name
TemplateRenderer.Render and the call to t.templates.ExecuteTemplate as the
template executor to render into w.

In `@pkg/errors/bun_handler.go`:
- Around line 20-33: The handler currently unwraps the response with
echo.UnwrapResponse(ctx.Response()) and returns nil for sql.ErrNoRows and
sql.ErrTxDone even if resp == nil, which swallows the error; update the switch
arms in bun_handler.go so you only set resp.Status and return nil when resp !=
nil (keep logger.Debugf for ErrNoRows), and if resp == nil do not return nil but
return the original err (i.e., let the error propagate to fallback handlers) for
both errors checked via errors.Is(err, sql.ErrNoRows) and errors.Is(err,
sql.ErrTxDone).

In `@pkg/logger/logger.go`:
- Around line 146-149: Replace the use of echo.UnwrapResponse(...).Status (which
can be zero) with echo.ResolveResponseStatus(eCtx.Response()) when computing
statusCode in the logging path so uncommitted responses are normalized to 200;
update the code that sets the local variable statusCode (and any logic that
reads it) to call echo.ResolveResponseStatus(eCtx.Response()) instead of
echo.UnwrapResponse(eCtx.Response()).Status to avoid emitting 0 as the HTTP
status.

---

Outside diff comments:
In `@internal/applications/rbac/controllers/policy_controller.go`:
- Around line 78-79: The code dereferences req.TenantID when only req.Role is
checked, risking a nil pointer; update the conditional that calls
policyService.GetPoliciesByRole so it verifies req.TenantID is non-nil before
using *req.TenantID (e.g., change the branch to check both req.Role != nil &&
req.TenantID != nil), and if TenantID is nil return a clear error/validation
response instead of dereferencing; locate the usage in policy_controller.go
around the call to c.policyService.GetPoliciesByRole to apply this fix.

In `@internal/applications/user/controller/user_controller.go`:
- Around line 101-102: The call to dtoMapper.Map(&pokemonGetResponseDto, result)
assigns an error to err but that error is not checked before returning
response.Success(eCtx, pokemonGetResponseDto); update the code in
user_controller.go to check err after calling dtoMapper.Map (handle the error
from dtoMapper.Map), and if non-nil return an error response (e.g.,
response.Error or appropriate failure using eCtx) instead of response.Success;
reference dtoMapper.Map, pokemonGetResponseDto, err, response.Success and eCtx
when making the change so the mapping failure yields a proper error response.
- Around line 35-36: The strconv.Atoi call of userGetReq.ID assigns err but it
gets overwritten by c.service.GetById, so conversion errors are ignored; fix by
checking the error returned from strconv.Atoi immediately after the call (e.g.,
if err != nil { return/respond with a bad-request error referencing invalid
userGetReq.ID }) before calling c.service.GetById, and avoid reusing the same
err without handling; ensure you pass the validated idString (converted to
uint32) into c.service.GetById using the existing eCtx.Request().Context().

In `@internal/middlewares/rbac_audit_middleware.go`:
- Around line 107-111: The current goroutine reuses the incoming request context
(ctx) which may be canceled when the handler returns, causing
auditRepo.Create(ctx, log) to be aborted; change the async write to use a
detached context with its own timeout (e.g.,
context.WithTimeout(context.Background(), 5*time.Second)) or enqueue the log to
a background worker instead, and call auditRepo.Create(auditCtx, log) inside the
goroutine while ensuring the cancel function is deferred and errors are logged
via logger.Errorf; update the goroutine surrounding auditRepo.Create,
referencing auditRepo.Create, ctx, and log.
- Around line 80-90: Replace the manual status resolution logic (using
statusCode, echoResp and type-asserting *echo.HTTPError) with Echo's canonical
resolver: call echo.ResolveResponseStatus(...) passing the current Response,
echoResp and err to compute the effective status (this handles committed
responses, response-suggested status and any HTTPStatusCoder errors). Ensure the
echo package is imported and remove the custom *echo.HTTPError type assertion so
the middleware uses the returned status from echo.ResolveResponseStatus to set
statusCode.

---

Nitpick comments:
In `@config/config.go`:
- Around line 214-215: SetDebugMode currently only logs the value and doesn't
mutate any state; either make it actually set debug mode on the Echo instance or
rename/deprecate it to avoid confusion. Fix by one of two minimal options: (A)
implement state change inside SetDebugMode by calling echoServer.HideBanner =
!debug and/or echoServer.Debug = debug (or set your global logger level) so the
function truly sets debug on the provided *echo.Echo, and update callers
accordingly; or (B) rename the function to LogDebugMode/DebugModeLog, add a
deprecation comment to SetDebugMode, and update all call sites to the new name
so the API clearly reflects it is only logging. Reference: the SetDebugMode
function in config.go.

In `@internal/applications/auth/controller/auth_controller.go`:
- Line 152: Remove the stray debug print in the AuthController.Me handler:
replace or delete the fmt.Println("AuthController.Me called") call inside the Me
method of AuthController; if logging is desired use the existing structured
logger used elsewhere in the controller (e.g., c.logger or AuthController's
logger) to emit a debug-level message instead of fmt.Println, ensuring
consistency with the app's logging approach.

In `@internal/applications/rbac/controllers/audit_controller.go`:
- Around line 112-113: The direct type assertions on log.PolicyBefore and
log.PolicyAfter can panic if the value is nil or the wrong type; update the code
that builds the response (the lines referencing log.PolicyBefore and
log.PolicyAfter) to perform safe assertions using the comma-ok idiom or a helper
like toMapOrEmpty that returns an empty map on nil/mismatch, and then use that
safe value for PolicyBefore and PolicyAfter to prevent runtime panics.

In `@internal/middlewares/registry.go`:
- Around line 30-32: Echo v5 removed RecoverConfig options so current
middleware.RecoverWithConfig(middleware.RecoverConfig{DisablePrintStack:
!mainConfig.App().Debug}) will forward panics to the HTTPErrorHandler; update
code to either (A) implement a custom recover middleware (replace
middleware.RecoverWithConfig) that captures panics, logs stack traces when
mainConfig.App().Debug is true and writes a safe response (restoring prior
DisableErrorHandler behavior), or (B) ensure pkg/errors/handler.go's
HTTPErrorHandler properly handles panic values (inspect error types/values and
produce an appropriate response and logs). Locate
middleware.RecoverWithConfig/RecoverConfig usage in registry.go and adjust
accordingly, or modify HTTPErrorHandler in pkg/errors/handler.go to handle
panics from RecoverWithConfig.

In `@pkg/errors/generic_handler.go`:
- Around line 14-21: The GenericHandler.Handle currently ignores errors from
echo.UnwrapResponse(ctx.Response()), causing silent failures; update Handle
(method GenericHandler.Handle) to handle the unwrapErr path: if
echo.UnwrapResponse returns an error, set an HTTP 500 on the original
ctx.Response() (or call ctx.Response().WriteHeader(500)/SetStatus if available)
and log the unwrapErr via your logger (or return a wrapped error) instead of
returning nil, while preserving the existing success path that sets resp.Status
= 500 when unwrapErr == nil.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 94b0e480-4ca1-4401-a229-ab46edd01c1f

📥 Commits

Reviewing files that changed from the base of the PR and between f7d2c80 and 6d9e501.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (56)
  • cmd/main.go
  • cmd/server/rest_server.go
  • cmd/server/web_server.go
  • config/config.go
  • go.mod
  • internal/applications/auth/controller/auth_controller.go
  • internal/applications/auth/controller/auth_controller_test.go
  • internal/applications/auth/controller/auth_routes.go
  • internal/applications/auth/registry.go
  • internal/applications/auth/service/auth_service_test.go
  • internal/applications/health/controller/health_controller.go
  • internal/applications/health/registry.go
  • internal/applications/notification/controller/notification_controller.go
  • internal/applications/notification/controller/notification_routes.go
  • internal/applications/notification/registry.go
  • internal/applications/rbac/controllers/audit_controller.go
  • internal/applications/rbac/controllers/enforcement_controller.go
  • internal/applications/rbac/controllers/policy_controller.go
  • internal/applications/rbac/controllers/role_controller.go
  • internal/applications/rbac/controllers/user_role_controller.go
  • internal/applications/rbac/registry.go
  • internal/applications/user/controller/user_controller.go
  • internal/applications/user/controller/user_routes.go
  • internal/applications/user/registry.go
  • internal/middlewares/cors_middleware.go
  • internal/middlewares/log_middleware.go
  • internal/middlewares/rbac_audit_middleware.go
  • internal/middlewares/rbac_enforcement_middleware.go
  • internal/middlewares/registry.go
  • internal/middlewares/req_context_middleware.go
  • internal/middlewares/req_injector_middleware.go
  • internal/middlewares/requestid_middleware.go
  • internal/middlewares/tenant_context_middleware.go
  • internal/middlewares/timeout_middleware.go
  • internal/middlewares/validator_middleware.go
  • internal/middlewares/version_middleware.go
  • internal/middlewares/version_middleware_test.go
  • pkg/authenticator/auth.go
  • pkg/authenticator/auth_middleware.go
  • pkg/authenticator/jwt.go
  • pkg/authenticator/jwt_test.go
  • pkg/authenticator/jwt_token.go
  • pkg/errors/bun_handler.go
  • pkg/errors/echo_handler.go
  • pkg/errors/generic_handler.go
  • pkg/errors/handler.go
  • pkg/errors/oops_handler.go
  • pkg/errors/registry.go
  • pkg/logger/logger.go
  • pkg/logger/logger_test.go
  • pkg/utils/response/response_json_builder.go
  • pkg/validator/helper.go
  • pkg/validator/mocks/mock_context_validator.go
  • pkg/validator/translator.go
  • pkg/versioning/version.go
  • pkg/versioning/version_test.go

Comment thread cmd/server/web_server.go Outdated
Comment thread pkg/errors/bun_handler.go
Comment thread pkg/logger/logger.go Outdated
Inline comments:
- web_server.go: fix TemplateRenderer.Render signature to match Echo v5
  Renderer interface (c *echo.Context, w io.Writer, templateName string, data any)
- bun_handler.go: propagate error when echo.UnwrapResponse returns nil resp
  instead of silently returning nil and swallowing the error
- logger.go: use echo.ResolveResponseStatus instead of UnwrapResponse to
  normalise uncommitted responses to 200 (avoids logging status=0)

Outside diff comments:
- policy_controller.go: guard *req.TenantID dereference behind nil check to
  prevent panic when Role is set but TenantID is nil
- user_controller.go: check strconv.Atoi error before calling GetById;
  check dtoMapper.Map error in GetPokemon before returning success response
- rbac_audit_middleware.go: replace manual status code resolution with
  echo.ResolveResponseStatus(c.Response(), err); use detached
  context.WithTimeout(context.Background(), 5s) in audit goroutine to avoid
  cancellation when the request context is done

Nitpick comments:
- auth_controller.go: remove fmt.Println debug print from Me handler and
  drop now-unused fmt import
- audit_controller.go: replace direct type assertions on PolicyBefore/PolicyAfter
  with safe toMapOrEmpty helper to prevent runtime panics on nil/type mismatch
- generic_handler.go: fall back to WriteHeader(500) when echo.UnwrapResponse
  fails instead of returning nil without setting any status

https://claude.ai/code/session_012HXUTcemrXNF2PbH2kSQ7m
Copy link
Copy Markdown
Contributor

@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.

Caution

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

⚠️ Outside diff range comments (3)
internal/applications/rbac/controllers/audit_controller.go (2)

230-242: ⚠️ Potential issue | 🔴 Critical

Block path traversal in export filename handling.

Line 230 accepts req.FileName and Line 241 joins it directly into a writable path. A crafted filename (for example with ../) can escape ./exports/audit and overwrite arbitrary files.

🔐 Proposed fix
 import (
 	"fmt"
 	"net/http"
 	"os"
 	"path/filepath"
+	"strings"
 	"time"
@@
 	fileName := req.FileName
 	if fileName == "" {
 		fileName = fmt.Sprintf("audit_logs_%s.%s", time.Now().Format("20060102_150405"), req.Format)
 	}
+	if strings.Contains(fileName, "/") || strings.Contains(fileName, "\\") {
+		return response.Error(ctx, http.StatusBadRequest, echo.NewHTTPError(http.StatusBadRequest, "invalid file name"))
+	}
+	fileName = filepath.Base(fileName)
+	if fileName == "." || fileName == ".." || fileName == "" {
+		return response.Error(ctx, http.StatusBadRequest, echo.NewHTTPError(http.StatusBadRequest, "invalid file name"))
+	}
@@
 	filePath := filepath.Join(exportDir, fileName)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/applications/rbac/controllers/audit_controller.go` around lines 230
- 242, The code currently trusts req.FileName and directly joins it into
exportDir (see variables fileName and exportDir and the filepath.Join call),
which allows path traversal; fix by validating/sanitizing req.FileName before
using it: reject or sanitize inputs that are absolute paths, contain ".." or
path separators (use filepath.IsAbs and check for string.Contains any of
string(os.PathSeparator) or "/" and "\"), or better replace the name with
filepath.Base and then validate it against an allowlist regex (e.g., only
alphanumerics, dots, underscores, hyphens) and if it fails fallback to the
generated audit_logs_<timestamp>.<ext> name; only after this sanitized/validated
fileName use filepath.Join(exportDir, fileName) to create filePath.

315-316: ⚠️ Potential issue | 🔴 Critical

Avoid panic-prone type assertions in recent mutations response.

Line 315 and Line 316 directly assert map[string]interface{} from JSON-backed interface{} fields; nil or non-map values will panic at runtime.

🛠️ Proposed fix
-			PolicyBefore: log.PolicyBefore.(map[string]interface{}),
-			PolicyAfter:  log.PolicyAfter.(map[string]interface{}),
+			PolicyBefore: toMapOrEmpty(log.PolicyBefore),
+			PolicyAfter:  toMapOrEmpty(log.PolicyAfter),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/applications/rbac/controllers/audit_controller.go` around lines 315
- 316, The direct assertions PolicyBefore:
log.PolicyBefore.(map[string]interface{}) and PolicyAfter:
log.PolicyAfter.(map[string]interface{}) are panic-prone; replace them with safe
checks using the comma-ok idiom or a type switch on log.PolicyBefore and
log.PolicyAfter (e.g., attempt map[string]interface{} with ok, handle
nil/non-map by assigning nil or an empty map, and optionally log the unexpected
type). Update the code that builds the recent mutations response (the usage of
the fields PolicyBefore and PolicyAfter) to use these safe-converted values
instead of raw assertions so it never panics on non-map or nil values.
internal/applications/auth/controller/auth_controller.go (1)

56-84: ⚠️ Potential issue | 🟠 Major

Align the Swagger @Router paths with the versioned routes.

RegisterRoutesV1 mounts Register, RefreshToken, Logout, and Me on the same versioned group as Login, but these annotations drop that prefix. The generated Swagger will point consumers to URLs that do not exist.

🛠️ Proposed fix
-//	`@Router`			/auth/register [post]
+//	`@Router`			/202601/auth/register [post]
...
-//	`@Router`			/auth/refresh [post]
+//	`@Router`			/202601/auth/refresh [post]
...
-//	`@Router`			/auth/logout [post]
+//	`@Router`			/202601/auth/logout [post]
...
-//	`@Router`			/auth/me [get]
+//	`@Router`			/202601/auth/me [get]

Also applies to: 86-118, 120-136, 138-167

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/applications/auth/controller/auth_controller.go` around lines 56 -
84, Swagger `@Router` annotations for controller methods (Register, RefreshToken,
Logout, Me) don't include the API version prefix used by RegisterRoutesV1,
causing generated docs to point to nonexistent endpoints; update each method's
`@Router` line (e.g., in Register, RefreshToken, Logout, Me) to include the same
versioned path prefix used by RegisterRoutesV1 (for example change
/auth/register to /v1/auth/register or the exact prefix used by
RegisterRoutesV1) so the annotations match the actual mounted routes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@internal/applications/auth/controller/auth_controller.go`:
- Around line 56-84: Swagger `@Router` annotations for controller methods
(Register, RefreshToken, Logout, Me) don't include the API version prefix used
by RegisterRoutesV1, causing generated docs to point to nonexistent endpoints;
update each method's `@Router` line (e.g., in Register, RefreshToken, Logout, Me)
to include the same versioned path prefix used by RegisterRoutesV1 (for example
change /auth/register to /v1/auth/register or the exact prefix used by
RegisterRoutesV1) so the annotations match the actual mounted routes.

In `@internal/applications/rbac/controllers/audit_controller.go`:
- Around line 230-242: The code currently trusts req.FileName and directly joins
it into exportDir (see variables fileName and exportDir and the filepath.Join
call), which allows path traversal; fix by validating/sanitizing req.FileName
before using it: reject or sanitize inputs that are absolute paths, contain ".."
or path separators (use filepath.IsAbs and check for string.Contains any of
string(os.PathSeparator) or "/" and "\"), or better replace the name with
filepath.Base and then validate it against an allowlist regex (e.g., only
alphanumerics, dots, underscores, hyphens) and if it fails fallback to the
generated audit_logs_<timestamp>.<ext> name; only after this sanitized/validated
fileName use filepath.Join(exportDir, fileName) to create filePath.
- Around line 315-316: The direct assertions PolicyBefore:
log.PolicyBefore.(map[string]interface{}) and PolicyAfter:
log.PolicyAfter.(map[string]interface{}) are panic-prone; replace them with safe
checks using the comma-ok idiom or a type switch on log.PolicyBefore and
log.PolicyAfter (e.g., attempt map[string]interface{} with ok, handle
nil/non-map by assigning nil or an empty map, and optionally log the unexpected
type). Update the code that builds the recent mutations response (the usage of
the fields PolicyBefore and PolicyAfter) to use these safe-converted values
instead of raw assertions so it never panics on non-map or nil values.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 385416ba-9706-4e21-9413-48802f545262

📥 Commits

Reviewing files that changed from the base of the PR and between 6d9e501 and f965692.

📒 Files selected for processing (9)
  • cmd/server/web_server.go
  • internal/applications/auth/controller/auth_controller.go
  • internal/applications/rbac/controllers/audit_controller.go
  • internal/applications/rbac/controllers/policy_controller.go
  • internal/applications/user/controller/user_controller.go
  • internal/middlewares/rbac_audit_middleware.go
  • pkg/errors/bun_handler.go
  • pkg/errors/generic_handler.go
  • pkg/logger/logger.go
🚧 Files skipped from review as they are similar to previous changes (7)
  • pkg/errors/bun_handler.go
  • pkg/errors/generic_handler.go
  • cmd/server/web_server.go
  • pkg/logger/logger.go
  • internal/middlewares/rbac_audit_middleware.go
  • internal/applications/rbac/controllers/policy_controller.go
  • internal/applications/user/controller/user_controller.go

- auth_controller.go: fix @router annotations for Register, RefreshToken,
  Logout, and Me to include the /202601 version prefix, matching the actual
  mount paths from RegisterRoutesV1 (base path is /ichi-go/api per @basepath)

- audit_controller.go: fix path traversal in export filename handling;
  introduce sanitizeExportFileName that rejects absolute paths, "..",
  path separators, and non-allowlisted characters (only [a-zA-Z0-9._-]
  permitted), falling back to a timestamped default name on any violation

- audit_controller.go: replace direct type assertions on PolicyBefore/PolicyAfter
  in GetRecentMutations response loop with toMapOrEmpty to prevent runtime
  panics on nil or unexpected types

https://claude.ai/code/session_012HXUTcemrXNF2PbH2kSQ7m
Copy link
Copy Markdown
Contributor

@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: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/applications/rbac/controllers/audit_controller.go`:
- Around line 129-135: toMapOrEmpty currently hides non-map audit payloads by
returning an empty map; change it so unexpected types are preserved or surfaced
instead of collapsed. Modify toMapOrEmpty to return the original value (or a
clear wrapper like map[string]interface{}{"__raw": v}) when v is not
map[string]interface{}, and add a logged warning (via the audit logger) or
return an error to callers (update callers of toMapOrEmpty to handle
interface{}/error) so deserialization mismatches are visible rather than
silently dropped; preserve the function name (toMapOrEmpty) in the refactor so
you can locate usage sites easily.
- Around line 143-156: The fallback filename in sanitizeExportFileName currently
uses time.Now().Format with second precision causing collisions; update the
logic that sets the fallback (the fallback variable in sanitizeExportFileName)
to produce a collision-resistant name—either append higher-resolution time (e.g.
nanoseconds), a short random/UUID suffix, or reserve a unique file by using
os.CreateTemp (or similar) and return that reserved name/path to ensure two
exports cannot clash. Ensure the rest of sanitizeExportFileName still
validates/returns base when provided.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 185ff37e-29a9-4e00-918f-7e839ef5c5b6

📥 Commits

Reviewing files that changed from the base of the PR and between f965692 and 66e9733.

📒 Files selected for processing (2)
  • internal/applications/auth/controller/auth_controller.go
  • internal/applications/rbac/controllers/audit_controller.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • internal/applications/auth/controller/auth_controller.go

Comment thread internal/applications/rbac/controllers/audit_controller.go Outdated
Comment thread internal/applications/rbac/controllers/audit_controller.go
- audit_controller.go: toMapOrEmpty now returns {"__raw": v} for unexpected
  non-nil types instead of silently returning an empty map, and logs a Warnf
  so deserialization mismatches are visible; nil still maps to empty map as
  nil policy is an expected/normal case

- audit_controller.go: sanitizeExportFileName fallback name now uses
  nanosecond precision (now.Nanosecond() as 9-digit suffix) to prevent
  filename collisions when multiple exports are triggered within the same second

https://claude.ai/code/session_012HXUTcemrXNF2PbH2kSQ7m
@msyahidin msyahidin merged commit 627f47c into main Apr 17, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants