diff --git a/contrib_versioned_docs/version-hcaptcha_v0.x.x/README.md b/contrib_versioned_docs/version-hcaptcha_v0.x.x/README.md new file mode 100644 index 00000000000..9608633168e --- /dev/null +++ b/contrib_versioned_docs/version-hcaptcha_v0.x.x/README.md @@ -0,0 +1,37 @@ +--- +title: 👋 Welcome +sidebar_position: 1 +--- + +
+ Fiber + Fiber +
+ +[![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) +![Test](https://github.com/gofiber/contrib/workflows/Tests/badge.svg) +![Security](https://github.com/gofiber/contrib/workflows/Security/badge.svg) +![Linter](https://github.com/gofiber/contrib/workflows/Linter/badge.svg) + +Repository for third party middlewares with dependencies. + +
+ +## 📑 Middleware Implementations + +* [Casbin](./casbin/README.md) +* [Fgprof](./fgprof/README.md) +* [Fiberi18n](./fiberi18n/README.md) +* [Fibersentry](./fibersentry/README.md) +* [Fiberzap](./fiberzap/README.md) +* [Fiberzerolog](./fiberzerolog/README.md) +* [HCaptcha](./hcaptcha/README.md) +* [JWT](./jwt/README.md) +* [Loadshed](./loadshed/README.md) +* [NewRelic](./fibernewrelic/README.md) +* [Open Policy Agent](./opafiber/README.md) +* [Otelfiber (OpenTelemetry)](./otelfiber/README.md) +* [Paseto](./paseto/README.md) +* [Socket.io](./socketio/README.md) +* [Swagger](./swagger/README.md) +* [Websocket](./websocket/README.md) diff --git a/contrib_versioned_docs/version-hcaptcha_v0.x.x/casbin/README.md b/contrib_versioned_docs/version-hcaptcha_v0.x.x/casbin/README.md new file mode 100644 index 00000000000..6462cc0294a --- /dev/null +++ b/contrib_versioned_docs/version-hcaptcha_v0.x.x/casbin/README.md @@ -0,0 +1,155 @@ +--- +id: casbin +--- + +# Casbin + +![Release](https://img.shields.io/github/v/tag/gofiber/contrib?filter=casbin*) +[![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) +![Test](https://github.com/gofiber/contrib/workflows/Tests/badge.svg) +![Security](https://github.com/gofiber/contrib/workflows/Security/badge.svg) +![Linter](https://github.com/gofiber/contrib/workflows/Linter/badge.svg) + +Casbin middleware for Fiber. + +**Note: Requires Go 1.18 and above** + +## Install +``` +go get -u github.com/gofiber/fiber/v2 +go get -u github.com/gofiber/contrib/casbin +``` +choose an adapter from [here](https://casbin.org/docs/en/adapters) +``` +go get -u github.com/casbin/xorm-adapter +``` + +## Signature +```go +casbin.New(config ...casbin.Config) *casbin.Middleware +``` + +## Config + +| Property | Type | Description | Default | +|:--------------|:--------------------------|:-----------------------------------------|:--------------------------------------------------------------------| +| 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` | + +### Examples +- [Gorm Adapter](https://github.com/svcg/-fiber_casbin_demo) +- [File Adapter](https://github.com/gofiber/contrib/casbin/tree/master/example) + +## CustomPermission + +```go +package main + +import ( + "github.com/gofiber/fiber/v2" + "github.com/gofiber/contrib/casbin" + _ "github.com/go-sql-driver/mysql" + "github.com/casbin/xorm-adapter/v2" +) + +func main() { + app := fiber.New() + + 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 { + // fetch authenticated user subject + }, + }) + + app.Post("/blog", + authz.RequiresPermissions([]string{"blog:create"}, casbin.WithValidationRule(casbin.MatchAllRule)), + 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 { + // your handler + }, + ) + + app.Listen(":8080") +} +``` + +## RoutePermission + +```go +package main + +import ( + "github.com/gofiber/fiber/v2" + "github.com/gofiber/contrib/casbin" + _ "github.com/go-sql-driver/mysql" + "github.com/casbin/xorm-adapter/v2" +) + +func main() { + app := fiber.New() + + 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 { + // fetch authenticated user subject + }, + }) + + // check permission with Method and Path + app.Post("/blog", + authz.RoutePermission(), + func(c *fiber.Ctx) error { + // your handler + }, + ) + + app.Listen(":8080") +} +``` + +## RoleAuthorization + +```go +package main + +import ( + "github.com/gofiber/fiber/v2" + "github.com/gofiber/contrib/casbin" + _ "github.com/go-sql-driver/mysql" + "github.com/casbin/xorm-adapter/v2" +) + +func main() { + app := fiber.New() + + 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 { + // fetch authenticated user subject + }, + }) + + app.Put("/blog/:id", + authz.RequiresRoles([]string{"admin"}), + func(c *fiber.Ctx) error { + // your handler + }, + ) + + app.Listen(":8080") +} +``` diff --git a/contrib_versioned_docs/version-hcaptcha_v0.x.x/fgprof/README.md b/contrib_versioned_docs/version-hcaptcha_v0.x.x/fgprof/README.md new file mode 100644 index 00000000000..74a87f1f259 --- /dev/null +++ b/contrib_versioned_docs/version-hcaptcha_v0.x.x/fgprof/README.md @@ -0,0 +1,59 @@ +--- +id: fgprof +--- + +# Fgprof + +![Release](https://img.shields.io/github/v/tag/gofiber/contrib?filter=fgprof*) +[![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) +![Test](https://github.com/gofiber/contrib/workflows/Tests/badge.svg) +![Security](https://github.com/gofiber/contrib/workflows/Security/badge.svg) +![Linter](https://github.com/gofiber/contrib/workflows/Linter/badge.svg) + +[fgprof](https://github.com/felixge/fgprof) support for Fiber. + +**Note: Requires Go 1.19 and above** + +## Install + +This middleware supports Fiber v2. + +Using fgprof to profiling your Fiber app. + +``` +go get -u github.com/gofiber/fiber/v2 +go get -u github.com/gofiber/contrib/fgprof +``` + +## Config + +| Property | Type | Description | Default | +|----------|---------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------|---------| +| 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 + +```go +package main + +import ( + "log" + + "github.com/gofiber/contrib/fgprof" + "github.com/gofiber/fiber/v2" +) + +func main() { + app := fiber.New() + app.Use(fgprof.New()) + app.Get("/", func(c *fiber.Ctx) error { + return c.SendString("OK") + }) + log.Fatal(app.Listen(":3000")) +} +``` + +```bash +go tool pprof -http=:8080 http://localhost:3000/debug/fgprof +``` diff --git a/contrib_versioned_docs/version-hcaptcha_v0.x.x/fiberi18n/README.md b/contrib_versioned_docs/version-hcaptcha_v0.x.x/fiberi18n/README.md new file mode 100644 index 00000000000..6085548ab1b --- /dev/null +++ b/contrib_versioned_docs/version-hcaptcha_v0.x.x/fiberi18n/README.md @@ -0,0 +1,88 @@ +--- +id: fiberi18n +--- + +# Fiberi18n + +![Release](https://img.shields.io/github/v/tag/gofiber/contrib?filter=fiberi18n*) +[![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) +![Test](https://github.com/gofiber/contrib/workflows/Tests/badge.svg) +![Security](https://github.com/gofiber/contrib/workflows/Security/badge.svg) +![Linter](https://github.com/gofiber/contrib/workflows/Linter/badge.svg) + +[go-i18n](https://github.com/nicksnyder/go-i18n) support for Fiber. + +**Note: Requires Go 1.18 and above** + +## Install + +This middleware supports Fiber v2. + +``` +go get -u github.com/gofiber/fiber/v2 +go get -u github.com/gofiber/contrib/fiberi18n/v2 +``` + +## 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 | + +## Config + +| Property | Type | Description | Default | +|------------------|---------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------| +| 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`. | + +## Example + +```go +package main + +import ( + "log" + + "github.com/gofiber/contrib/fiberi18n/v2" + "github.com/gofiber/fiber/v2" + "github.com/nicksnyder/go-i18n/v2/i18n" + "golang.org/x/text/language" +) + +func main() { + app := fiber.New() + app.Use( + fiberi18n.New(&fiberi18n.Config{ + RootPath: "./example/localize", + AcceptLanguages: []language.Tag{language.Chinese, language.English}, + DefaultLanguage: language.Chinese, + }), + ) + 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 { + return ctx.SendString(fiberi18n.MustLocalize(ctx, &i18n.LocalizeConfig{ + MessageID: "welcomeWithName", + TemplateData: map[string]string{ + "name": ctx.Params("name"), + }, + })) + }) + log.Fatal(app.Listen(":3000")) +} +``` + diff --git a/contrib_versioned_docs/version-hcaptcha_v0.x.x/fibernewrelic/README.md b/contrib_versioned_docs/version-hcaptcha_v0.x.x/fibernewrelic/README.md new file mode 100644 index 00000000000..28f954ce983 --- /dev/null +++ b/contrib_versioned_docs/version-hcaptcha_v0.x.x/fibernewrelic/README.md @@ -0,0 +1,112 @@ +--- +id: fibernewrelic +--- + +# Fibernewrelic + +![Release](https://img.shields.io/github/v/tag/gofiber/contrib?filter=fibernewrelic*) +[![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) +![Test](https://github.com/gofiber/contrib/workflows/Tests/badge.svg) +![Security](https://github.com/gofiber/contrib/workflows/Security/badge.svg) +![Linter](https://github.com/gofiber/contrib/workflows/Linter/badge.svg) + +[NewRelic](https://github.com/newrelic/go-agent) support for Fiber. + +**Note: Requires Go 1.18 and above** + +## Install + +``` +go get -u github.com/gofiber/fiber/v2 +go get -u github.com/gofiber/contrib/fibernewrelic +``` + +## Signature + +```go +fibernewrelic.New(config fibernewrelic.Config) fiber.Handler +``` + +## Config + +| Property | Type | Description | Default | +|:------------------|:-----------------|:---------------------------------------|:---------------| +| License | `string` | Required - New Relic License Key | `""` | +| AppName | `string` | New Relic Application Name | `fiber-api` | +| Enabled | `bool` | Enable/Disable New Relic | `false` | +| ~~TransportType~~ | ~~`string`~~ | ~~Can be HTTP or HTTPS~~ (Deprecated) | ~~`"HTTP"`~~ | +| Application | `Application` | Existing New Relic App | `nil` | +| ErrorStatusCodeHandler | `func(c *fiber.Ctx, err error) int` | If you want to change newrelic status code, you can use it. | `DefaultErrorStatusCodeHandler` | + +## Usage + +```go +package main + +import ( + "github.com/gofiber/fiber/v2" + "github.com/gofiber/contrib/fibernewrelic" +) + +func main() { + app := fiber.New() + + app.Get("/", func(ctx *fiber.Ctx) error { + return ctx.SendStatus(200) + }) + + cfg := fibernewrelic.Config{ + License: "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", + AppName: "MyCustomApi", + Enabled: true, + } + + app.Use(fibernewrelic.New(cfg)) + + app.Listen(":8080") +} +``` + +## Usage with existing New Relic application + +```go +package main + +import ( + "github.com/gofiber/fiber/v2" + "github.com/gofiber/contrib/fibernewrelic" + "github.com/newrelic/go-agent/v3/newrelic" +) + +func main() { + newrelicApp, err := newrelic.NewApplication( + newrelic.ConfigAppName("MyCustomApi"), + newrelic.ConfigLicense("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"), + newrelic.ConfigEnabled(true), + ) + + app := fiber.New() + + app.Get("/", func(ctx *fiber.Ctx) error { + return ctx.SendStatus(200) + }) + + app.Get("/foo", func(ctx *fiber.Ctx) error { + txn := newrelic.FromContext(ctx) + segment := txn.StartSegment("foo segment") + defer segment.End() + + // do foo + + return nil + }) + + cfg := fibernewrelic.Config{ + Application: newrelicApp, + } + + app.Use(fibernewrelic.New(cfg)) + + app.Listen(":8080") +} +``` diff --git a/contrib_versioned_docs/version-hcaptcha_v0.x.x/fibersentry/README.md b/contrib_versioned_docs/version-hcaptcha_v0.x.x/fibersentry/README.md new file mode 100644 index 00000000000..6291441d9e3 --- /dev/null +++ b/contrib_versioned_docs/version-hcaptcha_v0.x.x/fibersentry/README.md @@ -0,0 +1,126 @@ +--- +id: fibersentry +--- + +# Fibersentry + +![Release](https://img.shields.io/github/v/tag/gofiber/contrib?filter=fibersentry*) +[![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) +![Test](https://github.com/gofiber/contrib/workflows/Tests/badge.svg) +![Security](https://github.com/gofiber/contrib/workflows/Security/badge.svg) +![Linter](https://github.com/gofiber/contrib/workflows/Linter/badge.svg) + +[Sentry](https://sentry.io/) support for Fiber. + +**Note: Requires Go 1.18 and above** + +## Install + +This middleware supports Fiber v2. + +``` +go get -u github.com/gofiber/fiber/v2 +go get -u github.com/gofiber/contrib/fibersentry +go get -u github.com/getsentry/sentry-go +``` + +## Signature + +``` +fibersentry.New(config ...fibersentry.Config) fiber.Handler +``` + +## Config + +| Property | Type | Description | Default | +|:----------------|:----------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:------------------| +| Repanic | `bool` | Repanic configures whether Sentry should repanic after recovery. Set to true, if [Recover](https://github.com/gofiber/fiber/tree/master/middleware/recover) middleware is used. | `false` | +| WaitForDelivery | `bool` | WaitForDelivery configures whether you want to block the request before moving forward with the response. If [Recover](https://github.com/gofiber/fiber/tree/master/middleware/recover) middleware is used, it's safe to either skip this option or set it to false. | `false` | +| Timeout | `time.Duration` | Timeout for the event delivery requests. | `time.Second * 2` | + +## Usage + +`fibersentry` attaches an instance of `*sentry.Hub` (https://godoc.org/github.com/getsentry/sentry-go#Hub) to the request's context, which makes it available throughout the rest of the request's lifetime. +You can access it by using the `fibersentry.GetHubFromContext()` method on the context itself in any of your proceeding middleware and routes. +And it should be used instead of the global `sentry.CaptureMessage`, `sentry.CaptureException`, or any other calls, as it keeps the separation of data between the requests. + +**Keep in mind that `*sentry.Hub` won't be available in middleware attached before to `fibersentry`!** + +```go +package main + +import ( + "fmt" + "log" + + "github.com/getsentry/sentry-go" + "github.com/gofiber/contrib/fibersentry" + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/utils" +) + +func main() { + _ = sentry.Init(sentry.ClientOptions{ + Dsn: "", + BeforeSend: func(event *sentry.Event, hint *sentry.EventHint) *sentry.Event { + if hint.Context != nil { + if c, ok := hint.Context.Value(sentry.RequestContextKey).(*fiber.Ctx); ok { + // You have access to the original Context if it panicked + fmt.Println(utils.ImmutableString(c.Hostname())) + } + } + fmt.Println(event) + return event + }, + Debug: true, + AttachStacktrace: true, + }) + + app := fiber.New() + + app.Use(fibersentry.New(fibersentry.Config{ + Repanic: true, + WaitForDelivery: true, + })) + + enhanceSentryEvent := func(c *fiber.Ctx) error { + if hub := fibersentry.GetHubFromContext(c); hub != nil { + hub.Scope().SetTag("someRandomTag", "maybeYouNeedIt") + } + return c.Next() + } + + app.All("/foo", enhanceSentryEvent, func(c *fiber.Ctx) error { + panic("y tho") + }) + + app.All("/", func(c *fiber.Ctx) error { + if hub := fibersentry.GetHubFromContext(c); hub != nil { + hub.WithScope(func(scope *sentry.Scope) { + scope.SetExtra("unwantedQuery", "someQueryDataMaybe") + hub.CaptureMessage("User provided unwanted query string, but we recovered just fine") + }) + } + return c.SendStatus(fiber.StatusOK) + }) + + log.Fatal(app.Listen(":3000")) +} +``` + +## Accessing Context in `BeforeSend` callback + +```go +sentry.Init(sentry.ClientOptions{ + Dsn: "your-public-dsn", + BeforeSend: func(event *sentry.Event, hint *sentry.EventHint) *sentry.Event { + if hint.Context != nil { + if c, ok := hint.Context.Value(sentry.RequestContextKey).(*fiber.Ctx); ok { + // You have access to the original Context if it panicked + fmt.Println(c.Hostname()) + } + } + return event + }, +}) +``` diff --git a/contrib_versioned_docs/version-hcaptcha_v0.x.x/fiberzap/README.md b/contrib_versioned_docs/version-hcaptcha_v0.x.x/fiberzap/README.md new file mode 100644 index 00000000000..8cbf80517b6 --- /dev/null +++ b/contrib_versioned_docs/version-hcaptcha_v0.x.x/fiberzap/README.md @@ -0,0 +1,120 @@ +--- +id: fiberzap +--- + +# Fiberzap + +![Release](https://img.shields.io/github/v/tag/gofiber/contrib?filter=fiberzap*) +[![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) +![Test](https://github.com/gofiber/contrib/workflows/Tests/badge.svg) +![Security](https://github.com/gofiber/contrib/workflows/Security/badge.svg) +![Linter](https://github.com/gofiber/contrib/workflows/Linter/badge.svg) + +[Zap](https://github.com/uber-go/zap) logging support for Fiber. + +**Note: Requires Go 1.19 and above** + +## Install + +This middleware supports Fiber v2. + +``` +go get -u github.com/gofiber/fiber/v2 +go get -u github.com/gofiber/contrib/fiberzap/v2 +go get -u go.uber.org/zap +``` + +### Signature + +```go +fiberzap.New(config ...fiberzap.Config) fiber.Handler +``` + +### Config + +| Property | Type | Description | Default | +| :--------- | :------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------- | +| Next | `func(*Ctx) bool` | Define a function to skip this middleware when returned true | `nil` | +| Logger | `*zap.Logger` | Add custom zap logger. | `zap.NewDevelopment()` | +| Fields | `[]string` | Add fields what you want see. | `[]string{"latency", "status", "method", "url"}` | +| FieldsFunc | `[]zap.Field` | Define a function to add custom fields. | `nil` | +| Messages | `[]string` | Custom response messages. | `[]string{"Server error", "Client error", "Success"}` | +| Levels | `[]zapcore.Level` | Custom response levels. | `[]zapcore.Level{zapcore.ErrorLevel, zapcore.WarnLevel, zapcore.InfoLevel}` | +| SkipURIs | `[]string` | Skip logging these URI. | `[]string{}` | +| GetResBody | func(c \*fiber.Ctx) []byte | Define a function to get response body when return non-nil.
eg: When use compress middleware, resBody is unreadable. you can set GetResBody func to get readable resBody. | `nil` | + +### Example + +```go +package main + +import ( + "log" + + "github.com/gofiber/fiber/v2" + "github.com/gofiber/contrib/fiberzap/v2" + "go.uber.org/zap" +) + +func main() { + app := fiber.New() + logger, _ := zap.NewProduction() + + app.Use(fiberzap.New(fiberzap.Config{ + Logger: logger, + })) + + app.Get("/", func (c *fiber.Ctx) error { + return c.SendString("Hello, World!") + }) + + log.Fatal(app.Listen(":3000")) +} +``` + +## NewLogger + +### Signature + +```go +fiberzap.NewLogger(config ...fiberzap.LoggerConfig) *fiberzap.LoggerConfig +``` + +### LoggerConfig + +| Property | Type | Description | Default | +| :---------- | :------------- | :------------------------------------------------------------------------------------------------------- | :----------------------------- | +| CoreConfigs | `[]CoreConfig` | Define Config for zapcore | `fiberzap.LoggerConfigDefault` | +| SetLogger | `*zap.Logger` | Add custom zap logger. if not nil, `ZapOptions`, `CoreConfigs`, `SetLevel`, `SetOutput` will be ignored. | `nil` | +| ExtraKeys | `[]string` | Allow users log extra values from context. | `[]string{}` | +| ZapOptions | `[]zap.Option` | Allow users to configure the zap.Option supplied by zap. | `[]zap.Option{}` | + +### Example + +```go +package main + +import ( + "context" + "github.com/gofiber/contrib/fiberzap/v2" + "github.com/gofiber/fiber/v2" + "github.com/gofiber/fiber/v2/log" +) + +func main() { + app := fiber.New() + log.SetLogger(fiberzap.NewLogger(fiberzap.LoggerConfig{ + ExtraKeys: []string{"request_id"}, + })) + app.Use(func(c *fiber.Ctx) error { + ctx := context.WithValue(c.UserContext(), "request_id", "123") + c.SetUserContext(ctx) + return c.Next() + }) + app.Get("/", func(c *fiber.Ctx) error { + log.WithContext(c.UserContext()).Info("Hello, World!") + return c.SendString("Hello, World!") + }) + log.Fatal(app.Listen(":3000")) +} +``` diff --git a/contrib_versioned_docs/version-hcaptcha_v0.x.x/fiberzerolog/README.md b/contrib_versioned_docs/version-hcaptcha_v0.x.x/fiberzerolog/README.md new file mode 100644 index 00000000000..46e7f5a5612 --- /dev/null +++ b/contrib_versioned_docs/version-hcaptcha_v0.x.x/fiberzerolog/README.md @@ -0,0 +1,74 @@ +--- +id: fiberzerolog +--- + +# Fiberzerolog + +![Release](https://img.shields.io/github/v/tag/gofiber/contrib?filter=fiberzerolog*) +[![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) +![Test](https://github.com/gofiber/contrib/workflows/Tests/badge.svg) +![Security](https://github.com/gofiber/contrib/workflows/Security/badge.svg) +![Linter](https://github.com/gofiber/contrib/workflows/Linter/badge.svg) + +[Zerolog](https://zerolog.io/) logging support for Fiber. + +**Note: Requires Go 1.18 and above** + +## Install + +This middleware supports Fiber v2. + +```sh +go get -u github.com/gofiber/fiber/v2 +go get -u github.com/gofiber/contrib/fiberzerolog +go get -u github.com/rs/zerolog/log +``` + +## Signature + +```go +fiberzerolog.New(config ...fiberzerolog.Config) fiber.Handler +``` + +## Config + +| Property | Type | Description | Default | +|:--------------|:-------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:----------------------------------------------------------------------------| +| Next | `func(*Ctx) bool` | Define a function to skip this middleware when returned true | `nil` | +| Logger | `*zerolog.Logger` | Add custom zerolog logger. | `zerolog.New(os.Stderr).With().Timestamp().Logger()` | +| GetLogger | `func(*fiber.Ctx) zerolog.Logger` | Get custom zerolog logger, if it's defined the returned logger will replace the `Logger` value. | `nil` | +| Fields | `[]string` | Add fields what you want see. | `[]string{"latency", "status", "method", "url", "error"}` | +| WrapHeaders | bool | Wrap headers to dictionary.
If false: `{"method":"POST", "header-key":"header value"}`
If true: `{"method":"POST", "reqHeaders": {"header-key":"header value"}}` | `false` | +| FieldsSnakeCase | bool | Use snake case for fields: FieldResBody, FieldQueryParams, FieldBytesReceived, FieldBytesSent, FieldRequestId, FieldReqHeaders, FieldResHeaders.
If false: `{"method":"POST", "resBody":"v", "queryParams":"v"}`
If true: `{"method":"POST", "res_body":"v", "query_params":"v"}` | `false` | +| Messages | `[]string` | Custom response messages. | `[]string{"Server error", "Client error", "Success"}` | +| Levels | `[]zerolog.Level` | Custom response levels. | `[]zerolog.Level{zerolog.ErrorLevel, zerolog.WarnLevel, zerolog.InfoLevel}` | +| SkipURIs | `[]string` | Skip logging these URI. | `[]string{}` | +| GetResBody | func(c *fiber.Ctx) []byte | Define a function to get response body when return non-nil.
eg: When use compress middleware, resBody is unreadable. you can set GetResBody func to get readable resBody. | `nil` | +## Example + +```go +package main + +import ( + "github.com/gofiber/fiber/v2" + "github.com/gofiber/contrib/fiberzerolog" + "github.com/rs/zerolog" +) + +func main() { + app := fiber.New() + logger := zerolog.New(os.Stderr).With().Timestamp().Logger() + + app.Use(fiberzerolog.New(fiberzerolog.Config{ + Logger: &logger, + })) + + app.Get("/", func (c *fiber.Ctx) error { + return c.SendString("Hello, World!") + }) + + if err := app.Listen(":3000"); err != nil { + logger.Fatal().Err(err).Msg("Fiber app error") + } +} +``` diff --git a/contrib_versioned_docs/version-hcaptcha_v0.x.x/hcaptcha/README.md b/contrib_versioned_docs/version-hcaptcha_v0.x.x/hcaptcha/README.md new file mode 100644 index 00000000000..952956e0168 --- /dev/null +++ b/contrib_versioned_docs/version-hcaptcha_v0.x.x/hcaptcha/README.md @@ -0,0 +1,83 @@ +--- +id: hcaptcha +--- + +# HCaptcha + +![Release](https://img.shields.io/github/v/tag/gofiber/contrib?filter=hcaptcha*) +[![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) +![Test](https://github.com/gofiber/contrib/workflows/Tests/badge.svg) +![Security](https://github.com/gofiber/contrib/workflows/Security/badge.svg) +![Linter](https://github.com/gofiber/contrib/workflows/Linter/badge.svg) + +A simple [HCaptcha](https://hcaptcha.com) middleware to prevent bot attacks. + +:::note + +Requires Go **1.21** and above + +::: + +## Install + +:::caution + +This middleware only supports Fiber **v3**. + +::: + +```shell +go get -u github.com/gofiber/fiber/v3 +go get -u github.com/gofiber/contrib/hcaptcha +``` + +## Signature + +```go +hcaptcha.New(config hcaptcha.Config) fiber.Handler +``` + +## Config + +| Property | Type | Description | Default | +|:----------------|:----------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------------------------------------| +| SecretKey | `string` | The secret key you obtained from the HCaptcha admin panel. This field must not be empty. | `""` | +| ResponseKeyFunc | `func(fiber.Ctx) (string, error)` | ResponseKeyFunc should return the token that captcha provides upon successful solving. By default, it gets the token from the body by parsing a JSON request and returns the `hcaptcha_token` field. | `hcaptcha.DefaultResponseKeyFunc` | +| SiteVerifyURL | `string` | This property specifies the API resource used for token authentication. | `https://api.hcaptcha.com/siteverify` | + +## Example + +```go +package main + +import ( + "github.com/gofiber/contrib/hcaptcha" + "github.com/gofiber/fiber/v3" + "log" +) + +const ( + TestSecretKey = "0x0000000000000000000000000000000000000000" + TestSiteKey = "20000000-ffff-ffff-ffff-000000000002" +) + +func main() { + app := fiber.New() + captcha := hcaptcha.New(hcaptcha.Config{ + // Must set the secret key + SecretKey: TestSecretKey, + }) + + app.Get("/api/", func(c fiber.Ctx) error { + return c.JSON(fiber.Map{ + "hcaptcha_site_key": TestSiteKey, + }) + }) + + app.Post("/api/robots-excluded", func(c fiber.Ctx) error { + return c.SendString("You are not a robot") + }, captcha) + + log.Fatal(app.Listen(":3000")) +} +``` diff --git a/contrib_versioned_docs/version-hcaptcha_v0.x.x/jwt/README.md b/contrib_versioned_docs/version-hcaptcha_v0.x.x/jwt/README.md new file mode 100644 index 00000000000..49c7e9ed49d --- /dev/null +++ b/contrib_versioned_docs/version-hcaptcha_v0.x.x/jwt/README.md @@ -0,0 +1,300 @@ +--- +id: jwt +--- + +# JWT + +![Release](https://img.shields.io/github/v/tag/gofiber/contrib?filter=jwt*) +[![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) +![Test](https://github.com/gofiber/contrib/workflows/Tests/badge.svg) +![Security](https://github.com/gofiber/contrib/workflows/Security/badge.svg) +![Linter](https://github.com/gofiber/contrib/workflows/Linter/badge.svg) + +JWT returns a JSON Web Token (JWT) auth middleware. +For valid token, it sets the user in Ctx.Locals and calls next handler. +For invalid token, it returns "401 - Unauthorized" error. +For missing token, it returns "400 - Bad Request" error. + +Special thanks and credits to [Echo](https://echo.labstack.com/middleware/jwt) + +**Note: Requires Go 1.19 and above** + +## Install + +This middleware supports Fiber v1 & v2, install accordingly. + +``` +go get -u github.com/gofiber/fiber/v2 +go get -u github.com/gofiber/contrib/jwt +go get -u github.com/golang-jwt/jwt/v5 +``` + +## Signature +```go +jwtware.New(config ...jwtware.Config) func(*fiber.Ctx) error +``` + +## Config + +| Property | Type | Description | Default | +|:---------------|:--------------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------|:-----------------------------| +| Filter | `func(*fiber.Ctx) bool` | Defines a function to skip middleware | `nil` | +| SuccessHandler | `func(*fiber.Ctx) error` | SuccessHandler defines a function which is executed for a valid token. | `nil` | +| ErrorHandler | `func(*fiber.Ctx, error) error` | ErrorHandler defines a function which is executed for an invalid token. | `401 Invalid or expired JWT` | +| SigningKey | `interface{}` | Signing key to validate token. Used as fallback if SigningKeys has length 0. | `nil` | +| SigningKeys | `map[string]interface{}` | Map of signing keys to validate token with kid field usage. | `nil` | +| ContextKey | `string` | Context key to store user information from the token into context. | `"user"` | +| Claims | `jwt.Claim` | Claims are extendable claims data defining token content. | `jwt.MapClaims{}` | +| TokenLookup | `string` | TokenLookup is a string in the form of `:` that is used | `"header:Authorization"` | +| AuthScheme | `string` | AuthScheme to be used in the Authorization header. The default value (`"Bearer"`) will only be used in conjuction with the default `TokenLookup` value. | `"Bearer"` | +| KeyFunc | `func() jwt.Keyfunc` | KeyFunc defines a user-defined function that supplies the public key for a token validation. | `jwtKeyFunc` | +| JWKSetURLs | `[]string` | A slice of unique JSON Web Key (JWK) Set URLs to used to parse JWTs. | `nil` | + + +## HS256 Example +```go +package main + +import ( + "time" + + "github.com/gofiber/fiber/v2" + + jwtware "github.com/gofiber/contrib/jwt" + "github.com/golang-jwt/jwt/v5" +) + +func main() { + app := fiber.New() + + // Login route + app.Post("/login", login) + + // Unauthenticated route + app.Get("/", accessible) + + // JWT Middleware + app.Use(jwtware.New(jwtware.Config{ + SigningKey: jwtware.SigningKey{Key: []byte("secret")}, + })) + + // Restricted Routes + app.Get("/restricted", restricted) + + app.Listen(":3000") +} + +func login(c *fiber.Ctx) error { + user := c.FormValue("user") + pass := c.FormValue("pass") + + // Throws Unauthorized error + if user != "john" || pass != "doe" { + return c.SendStatus(fiber.StatusUnauthorized) + } + + // Create the Claims + claims := jwt.MapClaims{ + "name": "John Doe", + "admin": true, + "exp": time.Now().Add(time.Hour * 72).Unix(), + } + + // Create token + token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) + + // Generate encoded token and send it as response. + t, err := token.SignedString([]byte("secret")) + if err != nil { + return c.SendStatus(fiber.StatusInternalServerError) + } + + return c.JSON(fiber.Map{"token": t}) +} + +func accessible(c *fiber.Ctx) error { + return c.SendString("Accessible") +} + +func restricted(c *fiber.Ctx) error { + user := c.Locals("user").(*jwt.Token) + claims := user.Claims.(jwt.MapClaims) + name := claims["name"].(string) + return c.SendString("Welcome " + name) +} + +``` + +## HS256 Test +_Login using username and password to retrieve a token._ +``` +curl --data "user=john&pass=doe" http://localhost:3000/login +``` +_Response_ +```json +{ + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0NjE5NTcxMzZ9.RB3arc4-OyzASAaUhC2W3ReWaXAt_z2Fd3BN4aWTgEY" +} +``` + + +_Request a restricted resource using the token in Authorization request header._ +``` +curl localhost:3000/restricted -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0NjE5NTcxMzZ9.RB3arc4-OyzASAaUhC2W3ReWaXAt_z2Fd3BN4aWTgEY" +``` +_Response_ +``` +Welcome John Doe +``` + +## RS256 Example +```go +package main + +import ( + "crypto/rand" + "crypto/rsa" + "log" + "time" + + "github.com/gofiber/fiber/v2" + + "github.com/golang-jwt/jwt/v5" + + jwtware "github.com/gofiber/contrib/jwt" +) + +var ( + // Obviously, this is just a test example. Do not do this in production. + // In production, you would have the private key and public key pair generated + // in advance. NEVER add a private key to any GitHub repo. + privateKey *rsa.PrivateKey +) + +func main() { + app := fiber.New() + + // Just as a demo, generate a new private/public key pair on each run. See note above. + rng := rand.Reader + var err error + privateKey, err = rsa.GenerateKey(rng, 2048) + if err != nil { + log.Fatalf("rsa.GenerateKey: %v", err) + } + + // Login route + app.Post("/login", login) + + // Unauthenticated route + app.Get("/", accessible) + + // JWT Middleware + app.Use(jwtware.New(jwtware.Config{ + SigningKey: jwtware.SigningKey{ + JWTAlg: jwtware.RS256, + Key: privateKey.Public(), + }, + })) + + // Restricted Routes + app.Get("/restricted", restricted) + + app.Listen(":3000") +} + +func login(c *fiber.Ctx) error { + user := c.FormValue("user") + pass := c.FormValue("pass") + + // Throws Unauthorized error + if user != "john" || pass != "doe" { + return c.SendStatus(fiber.StatusUnauthorized) + } + + // Create the Claims + claims := jwt.MapClaims{ + "name": "John Doe", + "admin": true, + "exp": time.Now().Add(time.Hour * 72).Unix(), + } + + // Create token + token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims) + + // Generate encoded token and send it as response. + t, err := token.SignedString(privateKey) + if err != nil { + log.Printf("token.SignedString: %v", err) + return c.SendStatus(fiber.StatusInternalServerError) + } + + return c.JSON(fiber.Map{"token": t}) +} + +func accessible(c *fiber.Ctx) error { + return c.SendString("Accessible") +} + +func restricted(c *fiber.Ctx) error { + user := c.Locals("user").(*jwt.Token) + claims := user.Claims.(jwt.MapClaims) + name := claims["name"].(string) + return c.SendString("Welcome " + name) +} +``` + +## RS256 Test +The RS256 is actually identical to the HS256 test above. + +## JWK Set Test +The tests are identical to basic `JWT` tests above, with exception that `JWKSetURLs` to valid public keys collection in JSON Web Key (JWK) Set format should be supplied. See [RFC 7517](https://www.rfc-editor.org/rfc/rfc7517). + +## Custom KeyFunc example + +KeyFunc defines a user-defined function that supplies the public key for a token validation. +The function shall take care of verifying the signing algorithm and selecting the proper key. +A user-defined KeyFunc can be useful if tokens are issued by an external party. + +When a user-defined KeyFunc is provided, SigningKey, SigningKeys, and SigningMethod are ignored. +This is one of the three options to provide a token validation key. +The order of precedence is a user-defined KeyFunc, SigningKeys and SigningKey. +Required if neither SigningKeys nor SigningKey is provided. +Default to an internal implementation verifying the signing algorithm and selecting the proper key. + +```go +package main + +import ( + "fmt" + "github.com/gofiber/fiber/v2" + + jwtware "github.com/gofiber/contrib/jwt" + "github.com/golang-jwt/jwt/v5" +) + +func main() { + app := fiber.New() + + app.Use(jwtware.New(jwtware.Config{ + KeyFunc: customKeyFunc(), + })) + + app.Get("/ok", func(c *fiber.Ctx) error { + return c.SendString("OK") + }) +} + +func customKeyFunc() jwt.Keyfunc { + return func(t *jwt.Token) (interface{}, error) { + // Always check the signing method + if t.Method.Alg() != jwtware.HS256 { + return nil, fmt.Errorf("Unexpected jwt signing method=%v", t.Header["alg"]) + } + + // TODO custom implementation of loading signing key like from a database + signingKey := "secret" + + return []byte(signingKey), nil + } +} +``` diff --git a/contrib_versioned_docs/version-hcaptcha_v0.x.x/loadshed/README.md b/contrib_versioned_docs/version-hcaptcha_v0.x.x/loadshed/README.md new file mode 100644 index 00000000000..be0b0a8a515 --- /dev/null +++ b/contrib_versioned_docs/version-hcaptcha_v0.x.x/loadshed/README.md @@ -0,0 +1,121 @@ +--- +id: loadshed +--- + +# LoadShed + +![Release](https://img.shields.io/github/v/tag/gofiber/contrib?filter=loadshed*) +[![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) +![Test](https://github.com/gofiber/contrib/workflows/Tests/badge.svg) +![Security](https://github.com/gofiber/contrib/workflows/Security/badge.svg) +![Linter](https://github.com/gofiber/contrib/workflows/Linter/badge.svg) + +The LoadShed middleware for [Fiber](https://github.com/gofiber/fiber) is designed to help manage server load by shedding requests based on certain load criteria. + +**Note: Requires Go 1.19 and above** + +## Install + +This middleware supports Fiber v2 + +``` +go get -u github.com/gofiber/fiber/v2 +go get -u github.com/gofiber/contrib/loadshed +``` + +## Signatures + +```go +loadshed.New(config ...loadshed.Config) fiber.Handler +``` + +## Examples + +To use the LoadShed middleware in your Fiber application, import it and apply it to your Fiber app. Here's an example: + +```go +package main + +import ( + "github.com/gofiber/fiber/v2" + loadshed "github.com/gofiber/contrib/loadshed" +) + +func main() { + app := fiber.New() + + // Configure and use LoadShed middleware + app.Use(loadshed.New(loadshed.Config{ + Criteria: &loadshed.CPULoadCriteria{ + LowerThreshold: 0.75, // Set your own lower threshold + UpperThreshold: 0.90, // Set your own upper threshold + Interval: 10 * time.Second, + Getter: &loadshed.DefaultCPUPercentGetter{}, + }, + })) + + app.Get("/", func(c *fiber.Ctx) error { + return c.SendString("Welcome!") + }) + + app.Listen(":3000") +} +``` + +## Config + +The LoadShed middleware in Fiber offers various configuration options to tailor the load shedding behavior according to the needs of your application. + +| Property | Type | Description | Default | +| :------- | :---------------------- | :--------------------------------------------------- | :---------------------- | +| Next | `func(*fiber.Ctx) bool` | Function to skip this middleware when returned true. | `nil` | +| Criteria | `LoadCriteria` | Interface for defining load shedding criteria. | `&CPULoadCriteria{...}` | + +## LoadCriteria + +LoadCriteria is an interface in the LoadShed middleware that defines the criteria for determining when to shed load in the system. Different implementations of this interface can use various metrics and algorithms to decide when and how to shed incoming requests to maintain system performance. + +### CPULoadCriteria + +`CPULoadCriteria` is an implementation of the `LoadCriteria` interface, using CPU load as the metric for determining whether to shed requests. + +#### Properties + +| Property | Type | Description | +| :------------- | :----------------- | :------------------------------------------------------------------------------------------------------------------------------------ | +| LowerThreshold | `float64` | The lower CPU usage threshold as a fraction (0.0 to 1.0). Requests are considered for shedding when CPU usage exceeds this threshold. | +| UpperThreshold | `float64` | The upper CPU usage threshold as a fraction (0.0 to 1.0). All requests are shed when CPU usage exceeds this threshold. | +| Interval | `time.Duration` | The time interval over which the CPU usage is averaged for decision making. | +| Getter | `CPUPercentGetter` | Interface to retrieve CPU usage percentages. | + +#### How It Works + +`CPULoadCriteria` determines the load on the system based on CPU usage and decides whether to shed incoming requests. It operates on the following principles: + +- **CPU Usage Measurement**: It measures the CPU usage over a specified interval. +- **Thresholds**: Utilizes `LowerThreshold` and `UpperThreshold` values to decide when to start shedding requests. +- **Proportional Rejection Probability**: + - **Below `LowerThreshold`**: No requests are rejected, as the system is considered under acceptable load. + - **Between `LowerThreshold` and `UpperThreshold`**: The probability of rejecting a request increases as the CPU usage approaches the `UpperThreshold`. This is calculated using the formula: + ```plaintext + rejectionProbability := (cpuUsage - LowerThreshold*100) / (UpperThreshold - LowerThreshold) + ``` + - **Above `UpperThreshold`**: All requests are rejected to prevent system overload. + +This mechanism ensures that the system can adaptively manage its load, maintaining stability and performance under varying traffic conditions. + +## Default Config + +This is the default configuration for `LoadCriteria` in the LoadShed middleware. + +```go +var ConfigDefault = Config{ + Next: nil, + Criteria: &CPULoadCriteria{ + LowerThreshold: 0.90, // 90% CPU usage as the start point for considering shedding + UpperThreshold: 0.95, // 95% CPU usage as the point where all requests are shed + Interval: 10 * time.Second, // CPU usage is averaged over 10 seconds + Getter: &DefaultCPUPercentGetter{}, // Default method for getting CPU usage + }, +} +``` diff --git a/contrib_versioned_docs/version-hcaptcha_v0.x.x/opafiber/README.md b/contrib_versioned_docs/version-hcaptcha_v0.x.x/opafiber/README.md new file mode 100644 index 00000000000..03e4b2f0fbb --- /dev/null +++ b/contrib_versioned_docs/version-hcaptcha_v0.x.x/opafiber/README.md @@ -0,0 +1,109 @@ +--- +id: opafiber +--- + +# Opafiber + +![Release](https://img.shields.io/github/v/tag/gofiber/contrib?filter=opafiber*) +[![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) +![Test](https://github.com/gofiber/contrib/workflows/Tests/badge.svg) +![Security](https://github.com/gofiber/contrib/workflows/Security/badge.svg) +![Linter](https://github.com/gofiber/contrib/workflows/Linter/badge.svg) + +[Open Policy Agent](https://github.com/open-policy-agent/opa) support for Fiber. + +**Note: Requires Go 1.19 and above** + +## Install + +``` +go get -u github.com/gofiber/fiber/v2 +go get -u github.com/gofiber/contrib/opafiber/v2 +``` + +## Signature + +```go +opafiber.New(config opafiber.Config) fiber.Handler + +``` + +## Config + +| Property | Type | Description | Default | +|:----------------------|:--------------------|:-------------------------------------------------------------|:--------------------------------------------------------------------| +| RegoQuery | `string` | Required - Rego query | - | +| RegoPolicy | `io.Reader` | Required - Rego policy | - | +| IncludeQueryString | `bool` | Include query string as input to rego policy | `false` | +| DeniedStatusCode | `int` | Http status code to return when policy denies request | `400` | +| DeniedResponseMessage | `string` | Http response body text to return when policy denies request | `""` | +| IncludeHeaders | `[]string` | Include headers as input to rego policy | - | +| InputCreationMethod | `InputCreationFunc` | Use your own function to provide input for OPA | `func defaultInput(ctx *fiber.Ctx) (map[string]interface{}, error)` | + +## Types + +```go +type InputCreationFunc func(c *fiber.Ctx) (map[string]interface{}, error) +``` + +## Usage + +OPA Fiber middleware sends the following example data to the policy engine as input: + +```json +{ + "method": "GET", + "path": "/somePath", + "query": { + "name": ["John Doe"] + }, + "headers": { + "Accept": "application/json", + "Content-Type": "application/json" + } +} +``` + +```go +package main + +import ( + "github.com/gofiber/fiber/v2" + "github.com/gofiber/contrib/opafiber/v2" +) + +func main() { + app := fiber.New() + module := ` +package example.authz + +default allow := false + +allow { + input.method == "GET" +} +` + + cfg := opafiber.Config{ + RegoQuery: "data.example.authz.allow", + RegoPolicy: bytes.NewBufferString(module), + IncludeQueryString: true, + DeniedStatusCode: fiber.StatusForbidden, + DeniedResponseMessage: "status forbidden", + IncludeHeaders: []string{"Authorization"}, + InputCreationMethod: func (ctx *fiber.Ctx) (map[string]interface{}, error) { + return map[string]interface{}{ + "method": ctx.Method(), + "path": ctx.Path(), + }, nil + }, + } + app.Use(opafiber.New(cfg)) + + app.Get("/", func(ctx *fiber.Ctx) error { + return ctx.SendStatus(200) + }) + + app.Listen(":8080") +} +``` diff --git a/contrib_versioned_docs/version-hcaptcha_v0.x.x/otelfiber/README.md b/contrib_versioned_docs/version-hcaptcha_v0.x.x/otelfiber/README.md new file mode 100644 index 00000000000..5d3a215b4b4 --- /dev/null +++ b/contrib_versioned_docs/version-hcaptcha_v0.x.x/otelfiber/README.md @@ -0,0 +1,133 @@ +--- +id: otelfiber +--- + +# Otelfiber + +![Release](https://img.shields.io/github/v/tag/gofiber/contrib?filter=otelfiber*) +[![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) +![Test](https://github.com/gofiber/contrib/workflows/Tests/badge.svg) +![Security](https://github.com/gofiber/contrib/workflows/Security/badge.svg) +![Linter](https://github.com/gofiber/contrib/workflows/Linter/badge.svg) + +[OpenTelemetry](https://opentelemetry.io/) support for Fiber. + +Can be found on [OpenTelemetry Registry](https://opentelemetry.io/registry/instrumentation-go-fiber/). + +**Note: Requires Go 1.19 and above** + +## Install + +This middleware supports Fiber v2. + +``` +go get -u github.com/gofiber/contrib/otelfiber/v2 +``` + +## Signature + +```go +otelfiber.Middleware(opts ...otelfiber.Option) fiber.Handler +``` + +## Config +You can configure the middleware using functional parameters + + +| Function | Argument Type | Description | Default | +| :------------------ | :-------------------------------- | :--------------------------------------------------------------------------------- | :-------------------------------------------------------------------- | +| `WithNext` | `func(*fiber.Ctx) bool` | Define a function to skip this middleware when returned true .| nil | +| `WithTracerProvider` | `oteltrace.TracerProvider` | Specifies a tracer provider to use for creating a tracer. | nil - the global tracer provider is used | +| `WithMeterProvider` | `otelmetric.MeterProvider` | Specifies a meter provider to use for reporting. | nil - the global meter provider is used | +| `WithPort` | `int` | Specifies the value to use when setting the `net.host.port` attribute on metrics/spans. | Defaults to (`80` for `http`, `443` for `https`) | +| `WithPropagators` | `propagation.TextMapPropagator` | Specifies propagators to use for extracting information from the HTTP requests. | If none are specified, global ones will be used | +| `WithServerName` | `string` | Specifies the value to use when setting the `http.server_name` attribute on metrics/spans. | - | +| `WithSpanNameFormatter` | `func(*fiber.Ctx) string` | Takes a function that will be called on every request and the returned string will become the span Name. | Default formatter returns the route pathRaw | +| `WithCustomAttributes` | `func(*fiber.Ctx) []attribute.KeyValue` | Define a function to add custom attributes to the span. | nil | +| `WithCollectClientIP` | `bool` | Specifies whether to collect the client's IP address from the request. | true | + +## Usage + +Please refer to [example](./example) + +## Example + +```go +package main + +import ( + "context" + "errors" + "log" + + "go.opentelemetry.io/otel/sdk/resource" + + "github.com/gofiber/fiber/v2" + + "github.com/gofiber/contrib/otelfiber" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + stdout "go.opentelemetry.io/otel/exporters/stdout/stdouttrace" + + //"go.opentelemetry.io/otel/exporters/jaeger" + "go.opentelemetry.io/otel/propagation" + sdktrace "go.opentelemetry.io/otel/sdk/trace" + semconv "go.opentelemetry.io/otel/semconv/v1.4.0" + oteltrace "go.opentelemetry.io/otel/trace" +) + +var tracer = otel.Tracer("fiber-server") + +func main() { + tp := initTracer() + defer func() { + if err := tp.Shutdown(context.Background()); err != nil { + log.Printf("Error shutting down tracer provider: %v", err) + } + }() + + app := fiber.New() + + app.Use(otelfiber.Middleware()) + + app.Get("/error", func(ctx *fiber.Ctx) error { + return errors.New("abc") + }) + + app.Get("/users/:id", func(c *fiber.Ctx) error { + id := c.Params("id") + name := getUser(c.UserContext(), id) + return c.JSON(fiber.Map{"id": id, name: name}) + }) + + log.Fatal(app.Listen(":3000")) +} + +func initTracer() *sdktrace.TracerProvider { + exporter, err := stdout.New(stdout.WithPrettyPrint()) + if err != nil { + log.Fatal(err) + } + tp := sdktrace.NewTracerProvider( + sdktrace.WithSampler(sdktrace.AlwaysSample()), + sdktrace.WithBatcher(exporter), + sdktrace.WithResource( + resource.NewWithAttributes( + semconv.SchemaURL, + semconv.ServiceNameKey.String("my-service"), + )), + ) + otel.SetTracerProvider(tp) + otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(propagation.TraceContext{}, propagation.Baggage{})) + return tp +} + +func getUser(ctx context.Context, id string) string { + _, span := tracer.Start(ctx, "getUser", oteltrace.WithAttributes(attribute.String("id", id))) + defer span.End() + if id == "123" { + return "otelfiber tester" + } + return "unknown" +} +``` diff --git a/contrib_versioned_docs/version-hcaptcha_v0.x.x/otelfiber/example/README.md b/contrib_versioned_docs/version-hcaptcha_v0.x.x/otelfiber/example/README.md new file mode 100644 index 00000000000..e8029e451c7 --- /dev/null +++ b/contrib_versioned_docs/version-hcaptcha_v0.x.x/otelfiber/example/README.md @@ -0,0 +1,32 @@ +--- +id: otelfiber-example +--- + +# Example + +An HTTP server using gofiber fiber and instrumentation. The server has a +`/users/:id` endpoint. The server generates span information to +`stdout`. + +These instructions expect you have +[docker-compose](https://docs.docker.com/compose/) installed. + +Bring up the `fiber-server` and `fiber-client` services to run the +example: + +```sh +docker-compose up --detach fiber-server fiber-client +``` + +The `fiber-client` service sends just one HTTP request to `fiber-server` +and then exits. View the span generated by `fiber-server` in the logs: + +```sh +docker-compose logs fiber-server +``` + +Shut down the services when you are finished with the example: + +```sh +docker-compose down +``` diff --git a/contrib_versioned_docs/version-hcaptcha_v0.x.x/paseto/README.md b/contrib_versioned_docs/version-hcaptcha_v0.x.x/paseto/README.md new file mode 100644 index 00000000000..3f473032b16 --- /dev/null +++ b/contrib_versioned_docs/version-hcaptcha_v0.x.x/paseto/README.md @@ -0,0 +1,385 @@ +--- +id: paseto +--- + +# Paseto + +![Release](https://img.shields.io/github/v/tag/gofiber/contrib?filter=paseto*) +[![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) +![Test](https://github.com/gofiber/contrib/workflows/Tests/badge.svg) +![Security](https://github.com/gofiber/contrib/workflows/Security/badge.svg) +![Linter](https://github.com/gofiber/contrib/workflows/Linter/badge.svg) + +PASETO returns a Web Token (PASETO) auth middleware. + +- For valid token, it sets the payload data in Ctx.Locals and calls next handler. +- For invalid token, it returns "401 - Unauthorized" error. +- For missing token, it returns "400 - BadRequest" error. + +**Note: Requires Go 1.18 and above** + +## Install + +This middleware supports Fiber v2. + +``` +go get -u github.com/gofiber/fiber/v2 +go get -u github.com/gofiber/contrib/paseto +go get -u github.com/o1egl/paseto +``` + +## Signature + +```go +pasetoware.New(config ...pasetoware.Config) func(*fiber.Ctx) error +``` + +## Config + +| Property | Type | Description | Default | +|:---------------|:--------------------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------------------------------| +| Next | `func(*Ctx) bool` | Defines a function to skip middleware | `nil` | +| SuccessHandler | `func(*fiber.Ctx) error` | SuccessHandler defines a function which is executed for a valid token. | `c.Next()` | +| ErrorHandler | `func(*fiber.Ctx, error) error` | ErrorHandler defines a function which is executed for an invalid token. | `401 Invalid or expired PASETO` | +| Validate | `PayloadValidator` | Defines a function to validate if payload is valid. Optional. In case payload used is created using `CreateToken` function. If token is created using another function, this function must be provided. | `nil` | +| SymmetricKey | `[]byte` | Secret key to encrypt token. If present the middleware will generate local tokens. | `nil` | +| PrivateKey | `ed25519.PrivateKey` | Secret key to sign the tokens. If present (along with its `PublicKey`) the middleware will generate public tokens. | `nil` +| PublicKey | `crypto.PublicKey` | Public key to verify the tokens. If present (along with `PrivateKey`) the middleware will generate public tokens. | `nil` +| ContextKey | `string` | Context key to store user information from the token into context. | `"auth-token"` | +| TokenLookup | `[2]string` | TokenLookup is a string slice with size 2, that is used to extract token from the request | `["header","Authorization"]` | + +## Instructions + +When using this middleware, and creating a token for authentication, you can use the function pasetoware.CreateToken, +that will create a token, encrypt or sign it and returns the PASETO token. + +Passing a `SymmetricKey` in the Config results in a local (encrypted) token, while passing a `PublicKey` +and `PrivateKey` results in a public (signed) token. + +In case you want to use your own data structure, is needed to provide the `Validate` function in `paseware.Config`, that +will return the data stored in the token, and a error. + +## Examples + +Below have a list of some examples that can help you start to use this middleware. In case of any additional example +that doesn't show here, please take a look at the test file. + +### SymmetricKey + +```go +package main + +import ( + "time" + + "github.com/gofiber/fiber/v2" + "github.com/o1egl/paseto" + + pasetoware "github.com/gofiber/contrib/paseto" +) + +const secretSymmetricKey = "symmetric-secret-key (size = 32)" + +func main() { + + app := fiber.New() + + // Login route + app.Post("/login", login) + + // Unauthenticated route + app.Get("/", accessible) + + // Paseto Middleware with local (encrypted) token + apiGroup := app.Group("api", pasetoware.New(pasetoware.Config{ + SymmetricKey: []byte(secretSymmetricKey), + TokenPrefix: "Bearer", + })) + + // Restricted Routes + apiGroup.Get("/restricted", restricted) + + err := app.Listen(":8088") + if err != nil { + return + } +} + +func login(c *fiber.Ctx) error { + user := c.FormValue("user") + pass := c.FormValue("pass") + + // Throws Unauthorized error + if user != "john" || pass != "doe" { + return c.SendStatus(fiber.StatusUnauthorized) + } + + // Create token and encrypt it + encryptedToken, err := pasetoware.CreateToken([]byte(secretSymmetricKey), user, 12*time.Hour, pasetoware.PurposeLocal) + if err != nil { + return c.SendStatus(fiber.StatusInternalServerError) + } + + return c.JSON(fiber.Map{"token": encryptedToken}) +} + +func accessible(c *fiber.Ctx) error { + return c.SendString("Accessible") +} + +func restricted(c *fiber.Ctx) error { + payload := c.Locals(pasetoware.DefaultContextKey).(string) + return c.SendString("Welcome " + payload) +} + +``` + +#### Test it + +_Login using username and password to retrieve a token._ + +``` +curl --data "user=john&pass=doe" http://localhost:8088/login +``` + +_Response_ + +```json +{ + "token": "v2.local.eY7o9YAJ7Uqyo0JdyfHXKVARj3HgBhqIHckPgNIJOU6u489CXYL6bpOXbEtTB_nNM7nTFpcRVi7YAtJToxbxkkraHmE39pqjnBgkca-URgE-jhZGuhGu7ablmK-8tVoe5iY8mQqWFuJHAznTASUHh4AG55AMUcIALi6pEG28lAgVfw2azvnvbg4JOVZnjutcOVswd-ErsAuGtuEZkTmX7BfaLaO9ZvEX9cHahYPajuRjwU2TQrcpqITg-eYMNA1NuO8OVdnGf0mkUk6ElJUTZqhx4CSSylNXr7IlOwzTbUotEDAQTcNP7IRZI3VfpnRgnmtnZ5s.bnVsbAY" +} +``` + +_Request a restricted resource using the token in Authorization request header._ + +``` +curl localhost:8088/api/restricted -H "Authorization: Bearer v2.local.eY7o9YAJ7Uqyo0JdyfHXKVARj3HgBhqIHckPgNIJOU6u489CXYL6bpOXbEtTB_nNM7nTFpcRVi7YAtJToxbxkkraHmE39pqjnBgkca-URgE-jhZGuhGu7ablmK-8tVoe5iY8mQqWFuJHAznTASUHh4AG55AMUcIALi6pEG28lAgVfw2azvnvbg4JOVZnjutcOVswd-ErsAuGtuEZkTmX7BfaLaO9ZvEX9cHahYPajuRjwU2TQrcpqITg-eYMNA1NuO8OVdnGf0mkUk6ElJUTZqhx4CSSylNXr7IlOwzTbUotEDAQTcNP7IRZI3VfpnRgnmtnZ5s.bnVsbA" +``` + +_Response_ + +``` +Welcome john +``` + +### SymmetricKey + Custom Validator callback + +```go +package main + +import ( + "encoding/json" + "time" + + "github.com/o1egl/paseto" + + pasetoware "github.com/gofiber/contrib/paseto" +) + +const secretSymmetricKey = "symmetric-secret-key (size = 32)" + +type customPayloadStruct struct { + Name string `json:"name"` + ExpiresAt time.Time `json:"expiresAt"` +} + +func main() { + + app := fiber.New() + + // Login route + app.Post("/login", login) + + // Unauthenticated route + app.Get("/", accessible) + + // Paseto Middleware with local (encrypted) token + apiGroup := app.Group("api", pasetoware.New(pasetoware.Config{ + SymmetricKey: []byte(secretSymmetricKey), + TokenPrefix: "Bearer", + Validate: func(decrypted []byte) (any, error) { + var payload customPayloadStruct + err := json.Unmarshal(decrypted, &payload) + return payload, err + }, + })) + + // Restricted Routes + apiGroup.Get("/restricted", restricted) + + err := app.Listen(":8088") + if err != nil { + return + } +} + +func login(c *fiber.Ctx) error { + user := c.FormValue("user") + pass := c.FormValue("pass") + + // Throws Unauthorized error + if user != "john" || pass != "doe" { + return c.SendStatus(fiber.StatusUnauthorized) + } + + // Create the payload + payload := customPayloadStruct{ + Name: "John Doe", + ExpiresAt: time.Now().Add(12 * time.Hour), + } + + // Create token and encrypt it + encryptedToken, err := paseto.NewV2().Encrypt([]byte(secretSymmetricKey), payload, nil) + if err != nil { + return c.SendStatus(fiber.StatusInternalServerError) + } + + return c.JSON(fiber.Map{"token": encryptedToken}) +} + +func accessible(c *fiber.Ctx) error { + return c.SendString("Accessible") +} + +func restricted(c *fiber.Ctx) error { + payload := c.Locals(pasetoware.DefaultContextKey).(customPayloadStruct) + return c.SendString("Welcome " + payload.Name) +} + +``` + +#### Test it + +_Login using username and password to retrieve a token._ + +``` +curl --data "user=john&pass=doe" http://localhost:8088/login +``` + +_Response_ + +```json +{ + "token": "v2.local.OSnDEMUndq8JpRdCD8yX-mr-Z0-Mi85Jw0ftxseiNLCbRc44Mxl5dnn-SV9Qew1n9Y44wXZwm_FG279cILJk7lYc_B_IoMCRBudJE7qMgctkD9UBM-ZRZgCX9ekJh3S1Oo6Erp7bO-omPra5.bnVsbA" +} +``` + +_Request a restricted resource using the token in Authorization request header._ + +``` +curl localhost:8088/api/restricted -H "Authorization: Bearer v2.local.OSnDEMUndq8JpRdCD8yX-mr-Z0-Mi85Jw0ftxseiNLCbRc44Mxl5dnn-SV9Qew1n9Y44wXZwm_FG279cILJk7lYc_B_IoMCRBudJE7qMgctkD9UBM-ZRZgCX9ekJh3S1Oo6Erp7bO-omPra5.bnVsbA" +``` + +_Response_ + +``` +Welcome John Doe +``` + +### PublicPrivate Key + +```go +package main + +import ( + "crypto/ed25519" + "encoding/hex" + "time" + + "github.com/gofiber/fiber/v2" + + pasetoware "github.com/gofiber/contrib/paseto" +) + +const privateKeySeed = "e9c67fe2433aa4110caf029eba70df2c822cad226b6300ead3dcae443ac3810f" + +var seed, _ = hex.DecodeString(privateKeySeed) +var privateKey = ed25519.NewKeyFromSeed(seed) + +type customPayloadStruct struct { + Name string `json:"name"` + ExpiresAt time.Time `json:"expiresAt"` +} + +func main() { + + app := fiber.New() + + // Login route + app.Post("/login", login) + + // Unauthenticated route + app.Get("/", accessible) + + // Paseto Middleware with local (encrypted) token + apiGroup := app.Group("api", pasetoware.New(pasetoware.Config{ + TokenPrefix: "Bearer", + PrivateKey: privateKey, + PublicKey: privateKey.Public(), + })) + + // Restricted Routes + apiGroup.Get("/restricted", restricted) + + err := app.Listen(":8088") + if err != nil { + return + } +} + +func login(c *fiber.Ctx) error { + user := c.FormValue("user") + pass := c.FormValue("pass") + + // Throws Unauthorized error + if user != "john" || pass != "doe" { + return c.SendStatus(fiber.StatusUnauthorized) + } + + // Create token and encrypt it + encryptedToken, err := pasetoware.CreateToken(privateKey, user, 12*time.Hour, pasetoware.PurposePublic) + if err != nil { + return c.SendStatus(fiber.StatusInternalServerError) + } + + return c.JSON(fiber.Map{"token": encryptedToken}) +} + +func accessible(c *fiber.Ctx) error { + return c.SendString("Accessible") +} + +func restricted(c *fiber.Ctx) error { + payload := c.Locals(pasetoware.DefaultContextKey).(string) + return c.SendString("Welcome " + payload) +} + +``` + +#### Test it + +_Login using username and password to retrieve a token._ + +``` +curl --data "user=john&pass=doe" http://localhost:8088/login +``` + +_Response_ + +```json +{ + "token": "v2.public.eyJhdWQiOiJnb2ZpYmVyLmdvcGhlcnMiLCJkYXRhIjoiam9obiIsImV4cCI6IjIwMjMtMDctMTNUMDg6NDk6MzctMDM6MDAiLCJpYXQiOiIyMDIzLTA3LTEyVDIwOjQ5OjM3LTAzOjAwIiwianRpIjoiMjIzYjM0MjQtNWNkZS00NDFhLWJiZWEtZjBjYWFhYTdiYWFlIiwibmJmIjoiMjAyMy0wNy0xMlQyMDo0OTozNy0wMzowMCIsInN1YiI6InVzZXItdG9rZW4ifWiqK_yg0eJbIs2hnup4NuBYg7v4lxh33zEhEljsH7QUaZXAdtbCPK7cN-NSfSxrw68owwgo-dOlPrD7lc5M_AU.bnVsbA" +} +``` + +_Request a restricted resource using the token in Authorization request header._ + +``` +curl localhost:8088/api/restricted -H "Authorization: Bearer v2.public.eyJhdWQiOiJnb2ZpYmVyLmdvcGhlcnMiLCJkYXRhIjoiam9obiIsImV4cCI6IjIwMjMtMDctMTNUMDg6NDk6MzctMDM6MDAiLCJpYXQiOiIyMDIzLTA3LTEyVDIwOjQ5OjM3LTAzOjAwIiwianRpIjoiMjIzYjM0MjQtNWNkZS00NDFhLWJiZWEtZjBjYWFhYTdiYWFlIiwibmJmIjoiMjAyMy0wNy0xMlQyMDo0OTozNy0wMzowMCIsInN1YiI6InVzZXItdG9rZW4ifWiqK_yg0eJbIs2hnup4NuBYg7v4lxh33zEhEljsH7QUaZXAdtbCPK7cN-NSfSxrw68owwgo-dOlPrD7lc5M_AU.bnVsbA" +``` + +_Response_ + +``` +Welcome John Doe +``` diff --git a/contrib_versioned_docs/version-hcaptcha_v0.x.x/socketio/README.md b/contrib_versioned_docs/version-hcaptcha_v0.x.x/socketio/README.md new file mode 100644 index 00000000000..976495c19d9 --- /dev/null +++ b/contrib_versioned_docs/version-hcaptcha_v0.x.x/socketio/README.md @@ -0,0 +1,235 @@ +id: socketio +--- + +# Socket.io + +![Release](https://img.shields.io/github/v/tag/gofiber/contrib?filter=socketio*) +![Test](https://github.com/gofiber/contrib/workflows/Tests/badge.svg) +![Security](https://github.com/gofiber/contrib/workflows/Security/badge.svg) +![Linter](https://github.com/gofiber/contrib/workflows/Linter/badge.svg) + +WebSocket wrapper for [Fiber](https://github.com/gofiber/fiber) with events support and inspired by [Socket.io](https://github.com/socketio/socket.io) + +**Note: Requires Go 1.20 and above** + +## Install + +``` +go get -u github.com/gofiber/fiber/v2 +go get -u github.com/gofiber/contrib/socketio +``` + +## Signatures + +```go +// Initialize new socketio in the callback this will +// execute a callback that expects kws *Websocket Object +func New(callback func(kws *Websocket)) func(*fiber.Ctx) error +``` + +```go +// Add listener callback for an event into the listeners list +func On(event string, callback func(payload *EventPayload)) +``` + +```go +// Emit the message to a specific socket uuids list +// Ignores all errors +func EmitToList(uuids []string, message []byte) +``` + +```go +// Emit to a specific socket connection +func EmitTo(uuid string, message []byte) error +``` + +```go +// Broadcast to all the active connections +// except avoid broadcasting the message to itself +func Broadcast(message []byte) +``` + +```go +// Fire custom event on all connections +func Fire(event string, data []byte) +``` + +## Example + +```go +package main + +import ( + "encoding/json" + "fmt" + "log" + + "github.com/gofiber/contrib/socketio" + "github.com/gofiber/contrib/websocket" + "github.com/gofiber/fiber/v2" +) + +// MessageObject Basic chat message object +type MessageObject struct { + Data string `json:"data"` + From string `json:"from"` + Event string `json:"event"` + To string `json:"to"` +} + +func main() { + + // The key for the map is message.to + clients := make(map[string]string) + + // Start a new Fiber application + app := fiber.New() + + // Setup the middleware to retrieve the data sent in first GET request + app.Use(func(c *fiber.Ctx) error { + // IsWebSocketUpgrade returns true if the client + // requested upgrade to the WebSocket protocol. + if websocket.IsWebSocketUpgrade(c) { + c.Locals("allowed", true) + return c.Next() + } + return fiber.ErrUpgradeRequired + }) + + // Multiple event handling supported + socketio.On(socketio.EventConnect, func(ep *socketio.EventPayload) { + fmt.Println(fmt.Sprintf("Connection event 1 - User: %s", ep.Kws.GetStringAttribute("user_id"))) + }) + + // Custom event handling supported + socketio.On("CUSTOM_EVENT", func(ep *socketio.EventPayload) { + fmt.Println(fmt.Sprintf("Custom event - User: %s", ep.Kws.GetStringAttribute("user_id"))) + // ---> + + // DO YOUR BUSINESS HERE + + // ---> + }) + + // On message event + socketio.On(socketio.EventMessage, func(ep *socketio.EventPayload) { + + fmt.Println(fmt.Sprintf("Message event - User: %s - Message: %s", ep.Kws.GetStringAttribute("user_id"), string(ep.Data))) + + message := MessageObject{} + + // Unmarshal the json message + // { + // "from": "", + // "to": "", + // "event": "CUSTOM_EVENT", + // "data": "hello" + //} + err := json.Unmarshal(ep.Data, &message) + if err != nil { + fmt.Println(err) + return + } + + // Fire custom event based on some + // business logic + if message.Event != "" { + ep.Kws.Fire(message.Event, []byte(message.Data)) + } + + // Emit the message directly to specified user + err = ep.Kws.EmitTo(clients[message.To], ep.Data, socketio.TextMessage) + if err != nil { + fmt.Println(err) + } + }) + + // On disconnect event + socketio.On(socketio.EventDisconnect, func(ep *socketio.EventPayload) { + // Remove the user from the local clients + delete(clients, ep.Kws.GetStringAttribute("user_id")) + fmt.Println(fmt.Sprintf("Disconnection event - User: %s", ep.Kws.GetStringAttribute("user_id"))) + }) + + // On close event + // This event is called when the server disconnects the user actively with .Close() method + socketio.On(socketio.EventClose, func(ep *socketio.EventPayload) { + // Remove the user from the local clients + delete(clients, ep.Kws.GetStringAttribute("user_id")) + fmt.Println(fmt.Sprintf("Close event - User: %s", ep.Kws.GetStringAttribute("user_id"))) + }) + + // On error event + socketio.On(socketio.EventError, func(ep *socketio.EventPayload) { + fmt.Println(fmt.Sprintf("Error event - User: %s", ep.Kws.GetStringAttribute("user_id"))) + }) + + app.Get("/ws/:id", socketio.New(func(kws *socketio.Websocket) { + + // Retrieve the user id from endpoint + userId := kws.Params("id") + + // Add the connection to the list of the connected clients + // The UUID is generated randomly and is the key that allow + // socketio to manage Emit/EmitTo/Broadcast + clients[userId] = kws.UUID + + // Every websocket connection has an optional session key => value storage + kws.SetAttribute("user_id", userId) + + //Broadcast to all the connected users the newcomer + kws.Broadcast([]byte(fmt.Sprintf("New user connected: %s and UUID: %s", userId, kws.UUID)), true, socketio.TextMessage) + //Write welcome message + kws.Emit([]byte(fmt.Sprintf("Hello user: %s with UUID: %s", userId, kws.UUID)), socketio.TextMessage) + })) + + log.Fatal(app.Listen(":3000")) +} + +``` + +--- + +## Supported events + +| Const | Event | Description | +|:----------------|:-------------|:-----------------------------------------------------------------------------------------------------------------------------------------------------------| +| EventMessage | `message` | Fired when a Text/Binary message is received | +| EventPing | `ping` | [More details here](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_servers#Pings_and_Pongs_The_Heartbeat_of_WebSockets) | +| EventPong | `pong` | Refer to ping description | +| EventDisconnect | `disconnect` | Fired on disconnection. The error provided in disconnection event as defined in RFC 6455, section 11.7. | +| EventConnect | `connect` | Fired on first connection | +| EventClose | `close` | Fired when the connection is actively closed from the server. Different from client disconnection | +| EventError | `error` | Fired when some error appears useful also for debugging websockets | + +## Event Payload object + +| Variable | Type | Description | +|:-----------------|:--------------------|:--------------------------------------------------------------------------------| +| Kws | `*Websocket` | The connection object | +| Name | `string` | The name of the event | +| SocketUUID | `string` | Unique connection UUID | +| SocketAttributes | `map[string]string` | Optional websocket attributes | +| Error | `error` | (optional) Fired from disconnection or error events | +| Data | `[]byte` | Data used on Message and on Error event, contains the payload for custom events | + +## Socket instance functions + +| Name | Type | Description | +|:-------------|:---------|:----------------------------------------------------------------------------------| +| SetAttribute | `void` | Set a specific attribute for the specific socket connection | +| GetUUID | `string` | Get socket connection UUID | +| SetUUID | `error` | Set socket connection UUID | +| GetAttribute | `string` | Get a specific attribute from the socket attributes | +| EmitToList | `void` | Emit the message to a specific socket uuids list | +| EmitTo | `error` | Emit to a specific socket connection | +| Broadcast | `void` | Broadcast to all the active connections except broadcasting the message to itself | +| Fire | `void` | Fire custom event | +| Emit | `void` | Emit/Write the message into the given connection | +| Close | `void` | Actively close the connection from the server | + +**Note: the FastHTTP connection can be accessed directly from the instance** + +```go +kws.Conn +``` diff --git a/contrib_versioned_docs/version-hcaptcha_v0.x.x/swagger/README.md b/contrib_versioned_docs/version-hcaptcha_v0.x.x/swagger/README.md new file mode 100644 index 00000000000..b928f4f555e --- /dev/null +++ b/contrib_versioned_docs/version-hcaptcha_v0.x.x/swagger/README.md @@ -0,0 +1,132 @@ +--- +id: swagger +title: Swagger +--- + +# Swagger + +![Release](https://img.shields.io/github/v/tag/gofiber/contrib?filter=swagger*) +[![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) +![Test](https://github.com/gofiber/contrib/workflows/Tests/badge.svg) +![Security](https://github.com/gofiber/contrib/workflows/Security/badge.svg) +![Linter](https://github.com/gofiber/contrib/workflows/Linter/badge.svg) + +Swagger middleware for [Fiber](https://github.com/gofiber/fiber). The middleware handles Swagger UI. + +**Note: Requires Go 1.18 and above** + +### Table of Contents +- [Signatures](#signatures) +- [Installation](#installation) +- [Examples](#examples) +- [Config](#config) +- [Default Config](#default-config) + +### Signatures +```go +func New(config ...swagger.Config) fiber.Handler +``` + +### Installation +Swagger is tested on the latests [Go versions](https://golang.org/dl/) with support for modules. So make sure to initialize one first if you didn't do that yet: +```bash +go mod init github.com// +``` +And then install the swagger middleware: +```bash +go get github.com/gofiber/contrib/swagger +``` + +### Examples +Import the middleware package +```go +import ( + "github.com/gofiber/fiber/v2" + "github.com/gofiber/contrib/swagger" +) +``` + +Using the default config: +```go +app.Use(swagger.New()) +``` + +Using a custom config: +```go +cfg := swagger.Config{ + BasePath: "/", + FilePath: "./docs/swagger.json", + Path: "swagger", + Title: "Swagger API Docs", +} + +app.Use(swagger.New(cfg)) +``` + +Using multiple instances of Swagger: +```go +// Create Swagger middleware for v1 +// +// Swagger will be available at: /api/v1/docs +app.Use(swagger.New(swagger.Config{ + BasePath: "/api/v1/", + FilePath: "./docs/v1/swagger.json", + Path: "docs", +})) + +// Create Swagger middleware for v2 +// +// Swagger will be available at: /api/v2/docs +app.Use(swagger.New(swagger.Config{ + BasePath: "/api/v2/", + FilePath: "./docs/v2/swagger.json", + Path: "docs", +})) +``` + +### Config +```go +type Config struct { + // Next defines a function to skip this middleware when returned true. + // + // Optional. Default: nil + Next func(c *fiber.Ctx) bool + + // BasePath for the UI path + // + // Optional. Default: / + BasePath string + + // FilePath for the swagger.json or swagger.yaml file + // + // Optional. Default: ./swagger.json + FilePath string + + // Path combines with BasePath for the full UI path + // + // Optional. Default: docs + Path string + + // Title for the documentation site + // + // Optional. Default: Fiber API documentation + Title string + + // CacheAge defines the max-age for the Cache-Control header in seconds. + // + // Optional. Default: 3600 (1 hour) + CacheAge int +} +``` + +### Default Config +```go +var ConfigDefault = Config{ + Next: nil, + BasePath: "/", + FilePath: "./swagger.json", + Path: "docs", + Title: "Fiber API documentation", + CacheAge: 3600, // Default to 1 hour +} +``` diff --git a/contrib_versioned_docs/version-hcaptcha_v0.x.x/websocket/README.md b/contrib_versioned_docs/version-hcaptcha_v0.x.x/websocket/README.md new file mode 100644 index 00000000000..3c7214ab0ba --- /dev/null +++ b/contrib_versioned_docs/version-hcaptcha_v0.x.x/websocket/README.md @@ -0,0 +1,145 @@ +--- +id: websocket +--- + +# Websocket + +![Release](https://img.shields.io/github/v/tag/gofiber/contrib?filter=websocket*) +[![Discord](https://img.shields.io/discord/704680098577514527?style=flat&label=%F0%9F%92%AC%20discord&color=00ACD7)](https://gofiber.io/discord) +![Test](https://github.com/gofiber/contrib/workflows/Tests/badge.svg) +![Security](https://github.com/gofiber/contrib/workflows/Security/badge.svg) +![Linter](https://github.com/gofiber/contrib/workflows/Linter/badge.svg) + +Based on [Fasthttp WebSocket](https://github.com/fasthttp/websocket) for [Fiber](https://github.com/gofiber/fiber) with available `*fiber.Ctx` methods like [Locals](http://docs.gofiber.io/ctx#locals), [Params](http://docs.gofiber.io/ctx#params), [Query](http://docs.gofiber.io/ctx#query) and [Cookies](http://docs.gofiber.io/ctx#cookies). + +**Note: Requires Go 1.18 and above** + +## Install + +``` +go get -u github.com/gofiber/fiber/v2 +go get -u github.com/gofiber/contrib/websocket +``` + +## Signatures +```go +func New(handler func(*websocket.Conn), config ...websocket.Config) fiber.Handler { +``` + +## Config + +| Property | Type | Description | Default | +|:--------------------|:----------------------------|:------------------------------------------------------------------------------------------------------------------------------|:-----------------------| +| Filter | `func(*fiber.Ctx) bool` | Defines a function to skip middleware. | `nil` | +| HandshakeTimeout | `time.Duration` | HandshakeTimeout specifies the duration for the handshake to complete. | `0` (No timeout) | +| Subprotocols | `[]string` | Subprotocols specifies the client's requested subprotocols. | `nil` | +| Origins | `[]string` | Allowed Origins based on the Origin header. If empty, everything is allowed. | `nil` | +| ReadBufferSize | `int` | ReadBufferSize specifies the I/O buffer size in bytes for incoming messages. | `0` (Use default size) | +| WriteBufferSize | `int` | WriteBufferSize specifies the I/O buffer size in bytes for outgoing messages. | `0` (Use default size) | +| WriteBufferPool | `websocket.BufferPool` | WriteBufferPool is a pool of buffers for write operations. | `nil` | +| EnableCompression | `bool` | EnableCompression specifies if the client should attempt to negotiate per message compression (RFC 7692). | `false` | +| RecoverHandler | `func(*websocket.Conn) void` | RecoverHandler is a panic handler function that recovers from panics. | `defaultRecover` | + + +## Example + +```go +package main + +import ( + "log" + + "github.com/gofiber/fiber/v2" + "github.com/gofiber/contrib/websocket" +) + +func main() { + app := fiber.New() + + app.Use("/ws", func(c *fiber.Ctx) error { + // IsWebSocketUpgrade returns true if the client + // requested upgrade to the WebSocket protocol. + if websocket.IsWebSocketUpgrade(c) { + c.Locals("allowed", true) + return c.Next() + } + return fiber.ErrUpgradeRequired + }) + + app.Get("/ws/:id", websocket.New(func(c *websocket.Conn) { + // c.Locals is added to the *websocket.Conn + log.Println(c.Locals("allowed")) // true + log.Println(c.Params("id")) // 123 + log.Println(c.Query("v")) // 1.0 + log.Println(c.Cookies("session")) // "" + + // websocket.Conn bindings https://pkg.go.dev/github.com/fasthttp/websocket?tab=doc#pkg-index + var ( + mt int + msg []byte + err error + ) + for { + if mt, msg, err = c.ReadMessage(); err != nil { + log.Println("read:", err) + break + } + log.Printf("recv: %s", msg) + + if err = c.WriteMessage(mt, msg); err != nil { + log.Println("write:", err) + break + } + } + + })) + + log.Fatal(app.Listen(":3000")) + // Access the websocket server: ws://localhost:3000/ws/123?v=1.0 + // https://www.websocket.org/echo.html +} + +``` + +## Note with cache middleware + +If you get the error `websocket: bad handshake` when using the [cache middleware](https://github.com/gofiber/fiber/tree/master/middleware/cache), please use `config.Next` to skip websocket path. + +```go +app := fiber.New() +app.Use(cache.New(cache.Config{ + Next: func(c *fiber.Ctx) bool { + return strings.Contains(c.Route().Path, "/ws") + }, +})) + +app.Get("/ws/:id", websocket.New(func(c *websocket.Conn) {})) +``` + +## Note with recover middleware + +For internal implementation reasons, currently recover middleware is not work with websocket middleware, please use `config.RecoverHandler` to add recover handler to websocket endpoints. +By default, config `RecoverHandler` is recovers from panic and writes stack trace to stderr, also returns a response that contains panic message in **error** field. + + +```go +app := fiber.New() + +app.Use(cache.New(cache.Config{ + Next: func(c *fiber.Ctx) bool { + return strings.Contains(c.Route().Path, "/ws") + }, +})) + +cfg := Config{ + RecoverHandler: func(conn *Conn) { + if err := recover(); err != nil { + conn.WriteJSON(fiber.Map{"customError": "error occurred"}) + } + }, +} + +app.Get("/ws/:id", websocket.New(func(c *websocket.Conn) {}, cfg)) + + +``` \ No newline at end of file diff --git a/contrib_versioned_sidebars/version-hcaptcha_v0.x.x-sidebars.json b/contrib_versioned_sidebars/version-hcaptcha_v0.x.x-sidebars.json new file mode 100644 index 00000000000..caea0c03ba6 --- /dev/null +++ b/contrib_versioned_sidebars/version-hcaptcha_v0.x.x-sidebars.json @@ -0,0 +1,8 @@ +{ + "tutorialSidebar": [ + { + "type": "autogenerated", + "dirName": "." + } + ] +} diff --git a/contrib_versions.json b/contrib_versions.json index 527280416a6..dee9c8eebd3 100644 --- a/contrib_versions.json +++ b/contrib_versions.json @@ -9,6 +9,7 @@ "opafiber_v1.x.x", "loadshed_v0.x.x", "jwt_v1.x.x", + "hcaptcha_v0.x.x", "fiberzerolog_v1.x.x", "fiberzerolog_v0.x.x", "fiberzap_v2.x.x",