Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 16 additions & 14 deletions casbin/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,12 @@ Casbin middleware for Fiber.

**Note: Requires Go 1.25 and above**

This middleware supports Fiber v3.

## Install
```
go get -u github.com/gofiber/fiber/v3
go get -u github.com/gofiber/contrib/casbin
go get -u github.com/gofiber/contrib/v3/casbin/v2
```
choose an adapter from [here](https://casbin.org/docs/adapters)
```
Expand All @@ -34,9 +36,9 @@ casbin.New(config ...casbin.Config) *casbin.Middleware
| ModelFilePath | `string` | Model file path | `"./model.conf"` |
| PolicyAdapter | `persist.Adapter` | Database adapter for policies | `./policy.csv` |
| Enforcer | `*casbin.Enforcer` | Custom casbin enforcer | `Middleware generated enforcer using ModelFilePath & PolicyAdapter` |
| Lookup | `func(*fiber.Ctx) string` | Look up for current subject | `""` |
| Unauthorized | `func(*fiber.Ctx) error` | Response body for unauthorized responses | `Unauthorized` |
| Forbidden | `func(*fiber.Ctx) error` | Response body for forbidden responses | `Forbidden` |
| Lookup | `func(fiber.Ctx) string` | Look up for current subject | `""` |
| Unauthorized | `func(fiber.Ctx) error` | Response body for unauthorized responses | `Unauthorized` |
| Forbidden | `func(fiber.Ctx) error` | Response body for forbidden responses | `Forbidden` |

### Examples
- [Gorm Adapter](https://github.com/svcg/-fiber_casbin_demo)
Expand All @@ -49,7 +51,7 @@ package main

import (
"github.com/gofiber/fiber/v3"
"github.com/gofiber/contrib/casbin"
"github.com/gofiber/contrib/v3/casbin/v2"
_ "github.com/go-sql-driver/mysql"
"github.com/casbin/xorm-adapter/v2"
)
Expand All @@ -60,21 +62,21 @@ func main() {
authz := casbin.New(casbin.Config{
ModelFilePath: "path/to/rbac_model.conf",
PolicyAdapter: xormadapter.NewAdapter("mysql", "root:@tcp(127.0.0.1:3306)/"),
Lookup: func(c *fiber.Ctx) string {
Lookup: func(c fiber.Ctx) string {
// fetch authenticated user subject
},
})

app.Post("/blog",
authz.RequiresPermissions([]string{"blog:create"}, casbin.WithValidationRule(casbin.MatchAllRule)),
func(c *fiber.Ctx) error {
func(c fiber.Ctx) error {
// your handler
},
)

app.Delete("/blog/:id",
authz.RequiresPermissions([]string{"blog:create", "blog:delete"}, casbin.WithValidationRule(casbin.AtLeastOneRule)),
func(c *fiber.Ctx) error {
func(c fiber.Ctx) error {
// your handler
},
)
Expand All @@ -90,7 +92,7 @@ package main

import (
"github.com/gofiber/fiber/v3"
"github.com/gofiber/contrib/casbin"
"github.com/gofiber/contrib/v3/casbin/v2"
_ "github.com/go-sql-driver/mysql"
"github.com/casbin/xorm-adapter/v2"
)
Expand All @@ -101,15 +103,15 @@ func main() {
authz := casbin.New(casbin.Config{
ModelFilePath: "path/to/rbac_model.conf",
PolicyAdapter: xormadapter.NewAdapter("mysql", "root:@tcp(127.0.0.1:3306)/"),
Lookup: func(c *fiber.Ctx) string {
Lookup: func(c fiber.Ctx) string {
// fetch authenticated user subject
},
})

// check permission with Method and Path
app.Post("/blog",
authz.RoutePermission(),
func(c *fiber.Ctx) error {
func(c fiber.Ctx) error {
// your handler
},
)
Expand All @@ -125,7 +127,7 @@ package main

import (
"github.com/gofiber/fiber/v3"
"github.com/gofiber/contrib/casbin"
"github.com/gofiber/contrib/v3/casbin/v2"
_ "github.com/go-sql-driver/mysql"
"github.com/casbin/xorm-adapter/v2"
)
Expand All @@ -136,14 +138,14 @@ func main() {
authz := casbin.New(casbin.Config{
ModelFilePath: "path/to/rbac_model.conf",
PolicyAdapter: xormadapter.NewAdapter("mysql", "root:@tcp(127.0.0.1:3306)/"),
Lookup: func(c *fiber.Ctx) string {
Lookup: func(c fiber.Ctx) string {
// fetch authenticated user subject
},
})

app.Put("/blog/:id",
authz.RequiresRoles([]string{"admin"}),
func(c *fiber.Ctx) error {
func(c fiber.Ctx) error {
// your handler
},
)
Expand Down
2 changes: 1 addition & 1 deletion casbin/go.mod
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module github.com/gofiber/contrib/casbin
module github.com/gofiber/contrib/v3/casbin/v2

go 1.25.0

Expand Down
36 changes: 19 additions & 17 deletions circuitbreaker/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,13 @@ A **Circuit Breaker** is a software design pattern used to prevent system failur
✅ **Improves system reliability** by avoiding repeated failed requests.
✅ **Reduces load on struggling services** and allows recovery.

This middleware supports Fiber v3.

## Install

```bash
go get -u github.com/gofiber/fiber/v3
go get -u github.com/gofiber/contrib/circuitbreaker
go get -u github.com/gofiber/contrib/v3/circuitbreaker/v2
```

## Signature
Expand All @@ -54,9 +56,9 @@ circuitbreaker.New(config ...circuitbreaker.Config) *circuitbreaker.Middleware
| SuccessThreshold | `int` | Number of successful requests required to close the circuit | `5` |
| HalfOpenMaxConcurrent | `int` | Max concurrent requests in half-open state | `1` |
| IsFailure | `func(error) bool` | Custom function to determine if an error is a failure | `Status >= 500` |
| OnOpen | `func(*fiber.Ctx)` | Callback function when the circuit is opened | `503 response` |
| OnClose | `func(*fiber.Ctx)` | Callback function when the circuit is closed | `Continue request` |
| OnHalfOpen | `func(*fiber.Ctx)` | Callback function when the circuit is half-open | `429 response` |
| OnOpen | `func(fiber.Ctx)` | Callback function when the circuit is opened | `503 response` |
| OnClose | `func(fiber.Ctx)` | Callback function when the circuit is closed | `Continue request` |
| OnHalfOpen | `func(fiber.Ctx)` | Callback function when the circuit is half-open | `429 response` |
Comment on lines +59 to +61
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Config callback types should return error

Examples below use func(fiber.Ctx) error; align the table to avoid confusion.

-| OnOpen | `func(fiber.Ctx)` | Callback function when the circuit is opened | `503 response` |
-| OnClose | `func(fiber.Ctx)` | Callback function when the circuit is closed | `Continue request` |
-| OnHalfOpen | `func(fiber.Ctx)` | Callback function when the circuit is half-open | `429 response` |
+| OnOpen | `func(fiber.Ctx) error` | Callback function when the circuit is opened | `503 response` |
+| OnClose | `func(fiber.Ctx) error` | Callback function when the circuit is closed | `Continue request` |
+| OnHalfOpen | `func(fiber.Ctx) error` | Callback function when the circuit is half-open | `429 response` |
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
| OnOpen | `func(fiber.Ctx)` | Callback function when the circuit is opened | `503 response` |
| OnClose | `func(fiber.Ctx)` | Callback function when the circuit is closed | `Continue request` |
| OnHalfOpen | `func(fiber.Ctx)` | Callback function when the circuit is half-open | `429 response` |
| OnOpen | `func(fiber.Ctx) error` | Callback function when the circuit is opened | `503 response` |
| OnClose | `func(fiber.Ctx) error` | Callback function when the circuit is closed | `Continue request` |
| OnHalfOpen | `func(fiber.Ctx) error` | Callback function when the circuit is half-open | `429 response` |
🤖 Prompt for AI Agents
In circuitbreaker/README.md around lines 59 to 61, the callback type signatures
in the table show func(fiber.Ctx) but should be func(fiber.Ctx) error to match
examples; update the three entries (OnOpen, OnClose, OnHalfOpen) to use
func(fiber.Ctx) error so the docs align with the examples and make a note in the
table cells if necessary that handlers must return an error to control flow.


## Circuit Breaker Usage in Fiber (Example)

Expand All @@ -73,7 +75,7 @@ package main

import (
"github.com/gofiber/fiber/v3"
"github.com/gofiber/contrib/circuitbreaker"
"github.com/gofiber/contrib/v3/circuitbreaker/v2"
)

func main() {
Expand All @@ -90,15 +92,15 @@ func main() {
app.Use(circuitbreaker.Middleware(cb))

// Sample Route
app.Get("/", func(c *fiber.Ctx) error {
app.Get("/", func(c fiber.Ctx) error {
return c.SendString("Hello, world!")
})

// Optional: Expose health check endpoint
app.Get("/health/circuit", cb.HealthHandler())

// Optional: Expose metrics about the circuit breaker:
app.Get("/metrics/circuit", func(c *fiber.Ctx) error {
app.Get("/metrics/circuit", func(c fiber.Ctx) error {
return c.JSON(cb.GetStateStats())
})

Expand All @@ -117,7 +119,7 @@ func main() {
Apply the Circuit Breaker **only to specific routes**.

```go
app.Get("/protected", circuitbreaker.Middleware(cb), func(c *fiber.Ctx) error {
app.Get("/protected", circuitbreaker.Middleware(cb), func(c fiber.Ctx) error {
return c.SendString("Protected service running")
})
```
Expand All @@ -140,22 +142,22 @@ Customize the response when the circuit **opens**.
cb := circuitbreaker.New(circuitbreaker.Config{
FailureThreshold: 3,
Timeout: 10 * time.Second,
OnOpen: func(c *fiber.Ctx) error {
OnOpen: func(c fiber.Ctx) error {
return c.Status(fiber.StatusServiceUnavailable).
JSON(fiber.Map{"error": "Circuit Open: Service unavailable"})
},
OnHalfOpen: func(c *fiber.Ctx) error {
OnHalfOpen: func(c fiber.Ctx) error {
return c.Status(fiber.StatusTooManyRequests).
JSON(fiber.Map{"error": "Circuit Half-Open: Retrying service"})
},
OnClose: func(c *fiber.Ctx) error {
OnClose: func(c fiber.Ctx) error {
return c.Status(fiber.StatusOK).
JSON(fiber.Map{"message": "Circuit Closed: Service recovered"})
},
})

// Apply to a specific route
app.Get("/custom", circuitbreaker.Middleware(cb), func(c *fiber.Ctx) error {
app.Get("/custom", circuitbreaker.Middleware(cb), func(c fiber.Ctx) error {
return c.SendString("This service is protected by a Circuit Breaker")
})
```
Expand All @@ -168,7 +170,7 @@ Use a Circuit Breaker **when calling an external API.**

```go

app.Get("/external-api", circuitbreaker.Middleware(cb), func(c *fiber.Ctx) error {
app.Get("/external-api", circuitbreaker.Middleware(cb), func(c fiber.Ctx) error {
// Simulating an external API call
resp, err := fiber.Get("https://example.com/api")
if err != nil {
Expand All @@ -192,7 +194,7 @@ cb := circuitbreaker.New(circuitbreaker.Config{
HalfOpenSemaphore: make(chan struct{}, 2), // Allow only 2 concurrent requests
})

app.Get("/half-open-limit", circuitbreaker.Middleware(cb), func(c *fiber.Ctx) error {
app.Get("/half-open-limit", circuitbreaker.Middleware(cb), func(c fiber.Ctx) error {
time.Sleep(2 * time.Second) // Simulating slow response
return c.SendString("Half-Open: Limited concurrent requests")
})
Expand All @@ -208,7 +210,7 @@ Integrate **Prometheus metrics** and **structured logging**.
cb := circuitbreaker.New(circuitbreaker.Config{
FailureThreshold: 5,
Timeout: 10 * time.Second,
OnOpen: func(c *fiber.Ctx) error {
OnOpen: func(c fiber.Ctx) error {
log.Println("Circuit Breaker Opened!")
prometheus.Inc("circuit_breaker_open_count")
return c.Status(fiber.StatusServiceUnavailable).JSON(fiber.Map{"error": "Service Down"})
Expand All @@ -227,11 +229,11 @@ Use different Circuit Breakers for different services.
dbCB := circuitbreaker.New(circuitbreaker.Config{FailureThreshold: 5, Timeout: 10 * time.Second})
apiCB := circuitbreaker.New(circuitbreaker.Config{FailureThreshold: 3, Timeout: 5 * time.Second})

app.Get("/db-service", circuitbreaker.Middleware(dbCB), func(c *fiber.Ctx) error {
app.Get("/db-service", circuitbreaker.Middleware(dbCB), func(c fiber.Ctx) error {
return c.SendString("DB service request")
})

app.Get("/api-service", circuitbreaker.Middleware(apiCB), func(c *fiber.Ctx) error {
app.Get("/api-service", circuitbreaker.Middleware(apiCB), func(c fiber.Ctx) error {
return c.SendString("External API service request")
})
```
Expand Down
2 changes: 1 addition & 1 deletion circuitbreaker/circuitbreaker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -713,7 +713,7 @@ func TestHealthHandler(t *testing.T) {
resp, err := app.Test(req)

require.NoError(t, err)
require.Equal(t, "application/json", resp.Header.Get("Content-Type"))
require.Equal(t, fiber.MIMEApplicationJSONCharsetUTF8, resp.Header.Get("Content-Type"))
})

// Clean up
Expand Down
2 changes: 1 addition & 1 deletion circuitbreaker/go.mod
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module github.com/gofiber/contrib/circuitbreaker
module github.com/gofiber/contrib/v3/circuitbreaker/v2

go 1.25.0

Expand Down
8 changes: 4 additions & 4 deletions fgprof/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@ Using fgprof to profiling your Fiber app.

```
go get -u github.com/gofiber/fiber/v3
go get -u github.com/gofiber/contrib/fgprof
go get -u github.com/gofiber/contrib/v3/fgprof/v2
```

## Config

| Property | Type | Description | Default |
|----------|---------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------|---------|
| Next | `func(c *fiber.Ctx) bool` | A function to skip this middleware when returned `true`. | `nil` |
| Next | `func(c fiber.Ctx) bool` | A function to skip this middleware when returned `true`. | `nil` |
| Prefix | `string`. | Prefix defines a URL prefix added before "/debug/fgprof". Note that it should start with (but not end with) a slash. Example: "/federated-fiber" | `""` |

## Example
Expand All @@ -38,14 +38,14 @@ package main
import (
"log"

"github.com/gofiber/contrib/fgprof"
"github.com/gofiber/contrib/v3/fgprof/v2"
"github.com/gofiber/fiber/v3"
)

func main() {
app := fiber.New()
app.Use(fgprof.New())
app.Get("/", func(c *fiber.Ctx) error {
app.Get("/", func(c fiber.Ctx) error {
return c.SendString("OK")
})
log.Fatal(app.Listen(":3000"))
Expand Down
2 changes: 1 addition & 1 deletion fgprof/fgprof_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func Test_Fgprof_Path(t *testing.T) {
app.Use(New())

// Default fgprof interval is 30 seconds
resp, err := app.Test(httptest.NewRequest("GET", "/debug/fgprof?seconds=1", nil), fiber.TestConfig{Timeout: 1500})
resp, err := app.Test(httptest.NewRequest("GET", "/debug/fgprof?seconds=0", nil))
assert.Equal(t, nil, err)
Comment on lines 54 to 58

Choose a reason for hiding this comment

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

[P1] Restore fgprof test duration to avoid timeouts

Changing Test_Fgprof_Path to call /debug/fgprof?seconds=0 while also dropping the fiber.TestConfig{Timeout: 1500} causes the handler to default back to a 30 s profiling interval (as noted in the comment) but the test still uses the default 1 s timeout in app.Test. The request will now hit a context deadline before fgprof writes a response, so the test fails and no longer verifies the middleware. Keep the shorter profiling interval or extend the test timeout.

Useful? React with 👍 / 👎.

assert.Equal(t, 200, resp.StatusCode)
Comment on lines 56 to 59
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix 400 and timeout: seconds=0 is invalid; also pass a time.Duration timeout

/debug/fgprof rejects seconds=0 with 400. Use a positive value and set fiber.TestConfig.Timeout as a proper time.Duration to avoid “empty response” due to the default/too-small timeout.

 import (
 	"io"
 	"net/http/httptest"
 	"testing"
 
 	"github.com/stretchr/testify/assert"
 
 	"github.com/gofiber/fiber/v3"
+	"time"
 )
@@
 	// Default fgprof interval is 30 seconds
-	resp, err := app.Test(httptest.NewRequest("GET", "/debug/fgprof?seconds=0", nil))
+	resp, err := app.Test(
+		httptest.NewRequest("GET", "/debug/fgprof?seconds=1", nil),
+		fiber.TestConfig{Timeout: 2 * time.Second},
+	)
 	assert.Equal(t, nil, err)
 	assert.Equal(t, 200, resp.StatusCode)

Also applies to: 3-11

🧰 Tools
🪛 GitHub Actions: Test Fgprof

[error] 59-59: go test -v -race ./...: Test_Fgprof_Path failed: Not equal: expected 200, actual 400.

🤖 Prompt for AI Agents
In fgprof/fgprof_test.go around lines 56 to 59 (and similarly lines 3 to 11),
the test passes seconds=0 (which the handler rejects with HTTP 400) and does not
set a proper time.Duration timeout for fiber.Test, causing empty
responses/timeouts; update the test to use a positive seconds value (e.g.
seconds=1 or greater) and configure fiber.TestConfig.Timeout using a
time.Duration (for example time.Second * 5) when calling app.Test so the request
has a valid parameter and enough timeout to complete.

}
Expand Down
2 changes: 1 addition & 1 deletion fgprof/go.mod
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module github.com/gofiber/contrib/fgprof
module github.com/gofiber/contrib/v3/fgprof/v2

go 1.25.0

Expand Down
16 changes: 8 additions & 8 deletions fiberi18n/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,29 +18,29 @@ This middleware supports Fiber v3.

```
go get -u github.com/gofiber/fiber/v3
go get -u github.com/gofiber/contrib/fiberi18n/v2
go get -u github.com/gofiber/contrib/v3/fiberi18n/v3
```

## Signature

| Name | Signature | Description |
|--------------|----------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------|
| New | `New(config ...*fiberi18n.Config) fiber.Handler` | Create a new fiberi18n middleware handler |
| Localize | `Localize(ctx *fiber.Ctx, params interface{}) (string, error)` | Localize returns a localized message. param is one of these type: messageID, *i18n.LocalizeConfig |
| MustLocalize | `MustLocalize(ctx *fiber.Ctx, params interface{}) string` | MustLocalize is similar to Localize, except it panics if an error happens. param is one of these type: messageID, *i18n.LocalizeConfig |
| Localize | `Localize(ctx fiber.Ctx, params interface{}) (string, error)` | Localize returns a localized message. param is one of these type: messageID, *i18n.LocalizeConfig |
| MustLocalize | `MustLocalize(ctx fiber.Ctx, params interface{}) string` | MustLocalize is similar to Localize, except it panics if an error happens. param is one of these type: messageID, *i18n.LocalizeConfig |

## Config

| Property | Type | Description | Default |
|------------------|---------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------|
| Next | `func(c *fiber.Ctx) bool` | A function to skip this middleware when returned `true`. | `nil` |
| Next | `func(c fiber.Ctx) bool` | A function to skip this middleware when returned `true`. | `nil` |
| RootPath | `string` | The i18n template folder path. | `"./example/localize"` |
| AcceptLanguages | `[]language.Tag` | A collection of languages that can be processed. | `[]language.Tag{language.Chinese, language.English}` |
| FormatBundleFile | `string` | The type of the template file. | `"yaml"` |
| DefaultLanguage | `language.Tag` | The default returned language type. | `language.English` |
| Loader | `Loader` | The implementation of the Loader interface, which defines how to read the file. We provide both os.ReadFile and embed.FS.ReadFile. | `LoaderFunc(os.ReadFile)` |
| UnmarshalFunc | `i18n.UnmarshalFunc` | The function used for decoding template files. | `yaml.Unmarshal` |
| LangHandler | `func(ctx *fiber.Ctx, defaultLang string) string` | Used to get the kind of language handled by *fiber.Ctx and defaultLang. | Retrieved from the request header `Accept-Language` or query parameter `lang`. |
| LangHandler | `func(ctx fiber.Ctx, defaultLang string) string` | Used to get the kind of language handled by fiber.Ctx and defaultLang. | Retrieved from the request header `Accept-Language` or query parameter `lang`. |

## Example

Expand All @@ -50,7 +50,7 @@ package main
import (
"log"

"github.com/gofiber/contrib/fiberi18n/v2"
"github.com/gofiber/contrib/v3/fiberi18n/v3"
"github.com/gofiber/fiber/v3"
"github.com/nicksnyder/go-i18n/v2/i18n"
"golang.org/x/text/language"
Expand All @@ -65,14 +65,14 @@ func main() {
DefaultLanguage: language.Chinese,
}),
)
app.Get("/", func(c *fiber.Ctx) error {
app.Get("/", func(c fiber.Ctx) error {
localize, err := fiberi18n.Localize(c, "welcome")
if err != nil {
return c.Status(fiber.StatusInternalServerError).SendString(err.Error())
}
return c.SendString(localize)
})
app.Get("/:name", func(ctx *fiber.Ctx) error {
app.Get("/:name", func(ctx fiber.Ctx) error {
return ctx.SendString(fiberi18n.MustLocalize(ctx, &i18n.LocalizeConfig{
MessageID: "welcomeWithName",
TemplateData: map[string]string{
Expand Down
2 changes: 1 addition & 1 deletion fiberi18n/example/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package main
import (
"log"

"github.com/gofiber/contrib/fiberi18n/v2"
"github.com/gofiber/contrib/v3/fiberi18n/v3"
"github.com/gofiber/fiber/v3"
"github.com/nicksnyder/go-i18n/v2/i18n"
"golang.org/x/text/language"
Expand Down
2 changes: 1 addition & 1 deletion fiberi18n/go.mod
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module github.com/gofiber/contrib/fiberi18n/v2
module github.com/gofiber/contrib/v3/fiberi18n/v3

go 1.25.0

Expand Down
Loading
Loading