docs: add Echo v5 migration guide and remove outdated infrastructure plan
- Add comprehensive Echo v5 migration guide (ECHO_V5_MIGRATION.md) - Documents all API changes and type signature updates - Provides step-by-step fixes for deprecated middleware - Includes middleware pattern examples for v5 - Documents WebSocket fix for v5 compatibility - Includes verification and rollback plans - Remove outdated infrastructure enhancement plan (3488 lines) - Legacy plan is no longer relevant after Echo v5 migration - Consolidates documentation into single migration guide
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,338 @@
|
||||
# Echo v5 Migration Plan
|
||||
|
||||
## Overview
|
||||
|
||||
This document outlines the steps required to migrate from Echo v4 to Echo v5, including API changes, type signature updates, and middleware modifications.
|
||||
|
||||
## Errors Identified
|
||||
|
||||
### 1. Deprecated Middleware
|
||||
|
||||
**Error:** `echomiddleware.Logger undefined`
|
||||
|
||||
**Solution:** Replace `echomiddleware.Logger()` with `echomiddleware.RequestLogger()`
|
||||
|
||||
**Files affected:**
|
||||
- `cmd/server/main.go` (line 142)
|
||||
- `internal/router/router.go` (line 144)
|
||||
|
||||
### 2. Removed API Methods
|
||||
|
||||
**Error:** `a.echo.Close undefined (type *echo.Echo has no field or method Close)`
|
||||
|
||||
**Solution:** Remove the `echo.Close()` call as v5 uses different graceful shutdown mechanism
|
||||
|
||||
**Files affected:**
|
||||
- `internal/app/app.go` (line 86)
|
||||
|
||||
### 3. Type Signature & Response API Changes
|
||||
|
||||
**Error:** Multiple type mismatches and Response API changes in Echo v5
|
||||
|
||||
**Root Cause:** Echo v5 uses `*echo.Context` (pointer) for handlers but some code uses `echo.Context` (value). Response wrapper API also changed.
|
||||
|
||||
**Solution:**
|
||||
- Update all middleware to use `*echo.Context`
|
||||
- Update helper functions to accept `*echo.Context`
|
||||
- Fix Response wrapper usage for Echo v5 API
|
||||
|
||||
**Files affected:**
|
||||
- `internal/middleware/device_auth.go` (lines 38, 170, 212)
|
||||
- `internal/middleware/error_handler.go` (lines 44, 69, 82, 84, 87, 89)
|
||||
- `internal/middleware/rate_limiter.go` (line 102)
|
||||
- `internal/middleware/request_tracing.go` (lines 48, 58, 60)
|
||||
- `internal/middleware/security.go` (line 14)
|
||||
|
||||
---
|
||||
|
||||
## Step-by-Step Fixes
|
||||
|
||||
### Step 1: Fix Deprecated Logger Middleware
|
||||
|
||||
#### File: `cmd/server/main.go`
|
||||
|
||||
**Line 142:**
|
||||
```go
|
||||
// BEFORE:
|
||||
e.Use(echomiddleware.Logger())
|
||||
|
||||
// AFTER:
|
||||
e.Use(echomiddleware.RequestLogger())
|
||||
```
|
||||
|
||||
#### File: `internal/router/router.go`
|
||||
|
||||
**Line ~144:**
|
||||
```go
|
||||
// BEFORE:
|
||||
e.Use(echomiddleware.Logger())
|
||||
|
||||
// AFTER:
|
||||
e.Use(echomiddleware.RequestLogger())
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 2: Fix Removed API Methods
|
||||
|
||||
#### File: `internal/app/app.go`
|
||||
|
||||
**Lines 26-32 - Initialize server field:**
|
||||
```go
|
||||
// REPLACE lines 26-32 with:
|
||||
func New(echo *echo.Echo) *App {
|
||||
return &App{
|
||||
echo: echo,
|
||||
server: nil, // Will be set in StartServer()
|
||||
shutdownTimeout: 30 * time.Second,
|
||||
shutdownDone: make(chan struct{}),
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Add StartServer method after New() (after line 32):**
|
||||
```go
|
||||
// StartServer creates HTTP server and starts listening
|
||||
func (a *App) StartServer(addr string) error {
|
||||
a.server = &http.Server{
|
||||
Addr: addr,
|
||||
Handler: a.echo,
|
||||
}
|
||||
|
||||
// Start HTTP server in background
|
||||
go func() {
|
||||
if err := a.server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||
log.Fatalf("Server failed to start: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
**Lines 82-94 - Replace goroutine in Shutdown():**
|
||||
```go
|
||||
// REPLACE lines 82-94 with:
|
||||
// Stop accepting new connections and shutdown HTTP server
|
||||
log.Println("Stopping HTTP server...")
|
||||
if a.server != nil {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), a.shutdownTimeout)
|
||||
defer cancel()
|
||||
|
||||
if err := a.server.Shutdown(ctx); err != nil {
|
||||
log.Printf("Error stopping HTTP server: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
log.Println("All services stopped")
|
||||
```
|
||||
|
||||
#### File: `cmd/server/main.go`
|
||||
|
||||
**Lines 197-214 - Replace server startup:**
|
||||
```go
|
||||
// REPLACE lines 197-214 with:
|
||||
// ========================================================================
|
||||
// START SERVER (managed by app lifecycle)
|
||||
// ========================================================================
|
||||
|
||||
log.Printf("Starting server on port %s", cfg.ServerPort)
|
||||
|
||||
// Start HTTP server
|
||||
if err := application.StartServer(":" + cfg.ServerPort); err != nil {
|
||||
log.Fatalf("Failed to start server: %v", err)
|
||||
}
|
||||
|
||||
// Start application lifecycle (blocks until shutdown signal)
|
||||
if err := application.Start(); err != nil {
|
||||
log.Fatalf("Application error: %v", err)
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 3: Fix Middleware Type Signatures
|
||||
|
||||
#### Pattern for Echo v5 Middleware
|
||||
|
||||
**Echo v5 Middleware Pattern:**
|
||||
```go
|
||||
func MiddlewareFunc(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c *echo.Context) error { // ← POINTER (required in v5)
|
||||
// ... middleware logic
|
||||
return next(c) // ← c is already a pointer, no & needed
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### File: `internal/middleware/device_auth.go`
|
||||
|
||||
**Line 38:**
|
||||
```go
|
||||
// BEFORE:
|
||||
return func(c echo.Context) error {
|
||||
|
||||
// AFTER:
|
||||
return func(c *echo.Context) error {
|
||||
```
|
||||
|
||||
**Line 170:**
|
||||
```go
|
||||
// BEFORE:
|
||||
return func(c echo.Context) error {
|
||||
|
||||
// AFTER:
|
||||
return func(c *echo.Context) error {
|
||||
```
|
||||
|
||||
**Line 212:**
|
||||
```go
|
||||
// BEFORE:
|
||||
return func(c echo.Context) error {
|
||||
|
||||
// AFTER:
|
||||
return func(c *echo.Context) error {
|
||||
```
|
||||
|
||||
#### File: `internal/middleware/error_handler.go`
|
||||
|
||||
**Line 44:**
|
||||
```go
|
||||
// BEFORE:
|
||||
func RespondWithError(c echo.Context, code int, message string, err error) error {
|
||||
|
||||
// AFTER:
|
||||
func RespondWithError(c *echo.Context, code int, message string, err error) error {
|
||||
```
|
||||
|
||||
**Line 69:**
|
||||
```go
|
||||
// BEFORE:
|
||||
func RespondWithHTTPError(c echo.Context, httpErr *HTTPError) error {
|
||||
|
||||
// AFTER:
|
||||
func RespondWithHTTPError(c *echo.Context, httpErr *HTTPError) error {
|
||||
```
|
||||
|
||||
**Line 82:**
|
||||
```go
|
||||
// BEFORE:
|
||||
func WrapHandler(fn func(c echo.Context) error) echo.HandlerFunc {
|
||||
return func(c *echo.Context) error {
|
||||
err := fn(c)
|
||||
|
||||
// AFTER:
|
||||
func WrapHandler(fn func(*echo.Context) error) echo.HandlerFunc {
|
||||
return func(c *echo.Context) error {
|
||||
err := fn(c) // c is already *echo.Context
|
||||
```
|
||||
|
||||
**Lines 84, 87, 89:** No change - calls to helpers will now work with updated signatures
|
||||
|
||||
#### File: `internal/middleware/rate_limiter.go`
|
||||
|
||||
**Line 102:**
|
||||
```go
|
||||
// BEFORE:
|
||||
return func(c echo.Context) error {
|
||||
|
||||
// AFTER:
|
||||
return func(c *echo.Context) error {
|
||||
```
|
||||
|
||||
#### File: `internal/middleware/request_tracing.go`
|
||||
|
||||
**Line 48:**
|
||||
```go
|
||||
// BEFORE:
|
||||
return func(c echo.Context) error {
|
||||
|
||||
// AFTER:
|
||||
return func(c *echo.Context) error {
|
||||
```
|
||||
|
||||
**Lines 57-61:**
|
||||
```go
|
||||
// BEFORE:
|
||||
recorder := &responseWriter{
|
||||
ResponseWriter: c.Response().Writer,
|
||||
}
|
||||
c.Response().Writer = recorder
|
||||
|
||||
// AFTER:
|
||||
recorder := &responseWriter{
|
||||
ResponseWriter: *c.Response(), // Dereference: *echo.Response -> http.ResponseWriter
|
||||
}
|
||||
```
|
||||
|
||||
**Note:** `c.Response().Status` (line 108) should work - Status field exists in Echo v5
|
||||
|
||||
#### File: `internal/middleware/security.go`
|
||||
|
||||
**Line 14:**
|
||||
```go
|
||||
// BEFORE:
|
||||
return func(c echo.Context) error {
|
||||
|
||||
// AFTER:
|
||||
return func(c *echo.Context) error {
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## WebSocket Fix
|
||||
|
||||
After Echo v5 migration, the WebSocket handler should now work natively:
|
||||
|
||||
### File: `internal/handlers/websocket.go`
|
||||
|
||||
The existing code should now work:
|
||||
```go
|
||||
ws, err := upgrader.Upgrade(c.Response(), c.Request(), nil)
|
||||
```
|
||||
|
||||
This previously failed with "response does not implement http.Hijacker" but Echo v5's Response now supports the `rwUnwrapper` interface needed for WebSocket upgrades.
|
||||
|
||||
---
|
||||
|
||||
## Verification
|
||||
|
||||
After making all changes:
|
||||
|
||||
1. **Build the project:**
|
||||
```bash
|
||||
go build ./cmd/server
|
||||
```
|
||||
|
||||
2. **Run tests:**
|
||||
```bash
|
||||
go test ./cmd/server/tests/... -v
|
||||
```
|
||||
|
||||
3. **Test WebSocket manually:**
|
||||
- Get JWT token: `curl -X POST http://localhost:8765/api/auth/login -H "Content-Type: application/json" -d '{"login":"testuser@tests.bookhoard.internal","password":"Test@Pass123!"}' | jq -r '.access_token'`
|
||||
- Connect in browser console: `new WebSocket('ws://localhost:8765/ws/sync?token=YOUR_TOKEN')`
|
||||
|
||||
---
|
||||
|
||||
## Rollback Plan
|
||||
|
||||
If Echo v5 migration fails:
|
||||
|
||||
1. Revert go.mod changes:
|
||||
```go
|
||||
// Change back to v4:
|
||||
github.com/labstack/echo/v4 v4.15.1
|
||||
github.com/labstack/echo-jwt/v4 v4.4.0
|
||||
```
|
||||
|
||||
2. Restore import statements in all modified files
|
||||
3. Revert all code changes
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
|
||||
- [Echo v5 Changelog](https://github.com/labstack/echo/blob/master/CHANGELOG.md)
|
||||
- [Echo v5 Middleware Documentation](https://echo.labstack.com/docs/middleware)
|
||||
- [Echo WebSocket Cookbook](https://echo.labstack.com/docs/cookbook/websocket)
|
||||
Reference in New Issue
Block a user