diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index be5e9233..975c9586 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -68,6 +68,12 @@ updates:
- "๐ค Dependencies"
schedule:
interval: "daily"
+ - package-ecosystem: "gomod"
+ directory: "/minio/" # Location of package manifests
+ labels:
+ - "๐ค Dependencies"
+ schedule:
+ interval: "daily"
- package-ecosystem: "gomod"
directory: "/mongodb/" # Location of package manifests
labels:
diff --git a/.github/release-drafter-minio.yml b/.github/release-drafter-minio.yml
new file mode 100644
index 00000000..39f0e75e
--- /dev/null
+++ b/.github/release-drafter-minio.yml
@@ -0,0 +1,50 @@
+name-template: 'Minio - v$RESOLVED_VERSION'
+tag-template: 'minio/v$RESOLVED_VERSION'
+tag-prefix: minio/v
+include-paths:
+ - minio
+categories:
+ - title: 'โ Breaking Changes'
+ labels:
+ - 'โ BreakingChange'
+ - title: '๐ New'
+ labels:
+ - 'โ๏ธ Feature'
+ - title: '๐งน Updates'
+ labels:
+ - '๐งน Updates'
+ - '๐ค Dependencies'
+ - title: '๐ Fixes'
+ labels:
+ - 'โข๏ธ Bug'
+ - title: '๐ Documentation'
+ labels:
+ - '๐ Documentation'
+change-template: '- $TITLE (#$NUMBER)'
+change-title-escapes: '\<*_&' # You can add # and @ to disable mentions, and add ` to disable code blocks.
+exclude-contributors:
+ - dependabot
+ - dependabot[bot]
+version-resolver:
+ major:
+ labels:
+ - 'major'
+ - 'โ BreakingChange'
+ minor:
+ labels:
+ - 'minor'
+ - 'โ๏ธ Feature'
+ patch:
+ labels:
+ - 'patch'
+ - '๐ Documentation'
+ - 'โข๏ธ Bug'
+ - '๐ค Dependencies'
+ - '๐งน Updates'
+ default: patch
+template: |
+ $CHANGES
+
+ **Full Changelog**: https://github.com/$OWNER/$REPOSITORY/compare/$PREVIOUS_TAG...minio/v$RESOLVED_VERSION
+
+ Thank you $CONTRIBUTORS for making this update possible.
diff --git a/.github/workflows/gosec.yml b/.github/workflows/gosec.yml
index 25929f03..db587765 100644
--- a/.github/workflows/gosec.yml
+++ b/.github/workflows/gosec.yml
@@ -72,6 +72,10 @@ jobs:
working-directory: ./memory
run: gosec ./...
# -----
+ - name: Run Gosec (minio)
+ working-directory: ./minio
+ run: gosec ./...
+ # -----
- name: Run Gosec (mongodb)
working-directory: ./mongodb
run: gosec ./...
diff --git a/.github/workflows/release-drafter-minio.yml b/.github/workflows/release-drafter-minio.yml
new file mode 100644
index 00000000..6cddd599
--- /dev/null
+++ b/.github/workflows/release-drafter-minio.yml
@@ -0,0 +1,19 @@
+name: Release Drafter Minio
+on:
+ push:
+ # branches to consider in the event; optional, defaults to all
+ branches:
+ - master
+ - main
+ paths:
+ - 'minio/**'
+jobs:
+ draft_release_minio:
+ runs-on: ubuntu-latest
+ timeout-minutes: 30
+ steps:
+ - uses: release-drafter/release-drafter@v5
+ with:
+ config-name: release-drafter-minio.yml
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/test-minio.yml b/.github/workflows/test-minio.yml
new file mode 100644
index 00000000..dd3b03ed
--- /dev/null
+++ b/.github/workflows/test-minio.yml
@@ -0,0 +1,33 @@
+on:
+ push:
+ branches:
+ - master
+ - main
+ paths:
+ - 'minio/**'
+ pull_request:
+ paths:
+ - 'minio/**'
+name: "Tests Minio"
+jobs:
+ Tests:
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ go-version:
+ - 1.19.x
+ - 1.20.x
+ - 1.21.x
+ steps:
+ - name: Install MinIO
+ run: |
+ docker run -d --restart always -p 9000:9000 --name storage-minio -e MINIO_ROOT_USER='minio-user' -e MINIO_ROOT_PASSWORD='minio-password' minio/minio server /data
+
+ - name: Fetch Repository
+ uses: actions/checkout@v3
+ - name: Install Go
+ uses: actions/setup-go@v4
+ with:
+ go-version: '${{ matrix.go-version }}'
+ - name: Run Test
+ run: cd ./minio && go test ./... -v -race
diff --git a/README.md b/README.md
index 402621fd..8416e603 100644
--- a/README.md
+++ b/README.md
@@ -60,6 +60,7 @@ type Storage interface {
- [Etcd](./etcd/README.md)
- [Memcache](./memcache/README.md)
- [Memory](./memory/README.md)
+- [Minio](./minio/README.md)
- [MongoDB](./mongodb/README.md)
- [MSSQL](./mssql/README.md)
- [MySQL](./mysql/README.md)
diff --git a/minio/README.md b/minio/README.md
new file mode 100644
index 00000000..7fd0adf8
--- /dev/null
+++ b/minio/README.md
@@ -0,0 +1,126 @@
+# Minio
+
+A Minio storage driver using [minio/minio-go](https://github.com/minio/minio-go).
+
+**Note: Requires Go 1.19 and above**
+
+### Table of Contents
+- [Signatures](#signatures)
+- [Installation](#installation)
+- [Examples](#examples)
+- [Config](#config)
+- [Default Config](#default-config)
+
+### Signatures
+```go
+func New(config ...Config) Storage
+func (s *Storage) Get(key string) ([]byte, error)
+func (s *Storage) Set(key string, val []byte, exp time.Duration) error
+func (s *Storage) Delete(key string) error
+func (s *Storage) Reset() error
+func (s *Storage) Close() error
+func (s *Storage) CheckBucket() error
+func (s *Storage) CreateBucket() error
+func (s *Storage) RemoveBucket() error
+func (s *Storage) Conn() *minio.Client
+```
+### Installation
+Install the Minio implementation:
+```bash
+go get github.com/gofiber/storage/minio
+```
+And then run minio on Docker
+```bash
+docker run -d --restart always -p 9000:9000 -p 9001:9001 --name storage-minio --volume=minio:/var/lib/minio -e MINIO_ROOT_USER='minio-user' -e MINIO_ROOT_PASSWORD='minio-password' minio/minio server --console-address ":9001" /var/lib/minio
+```
+
+### Examples
+Import the storage package.
+```go
+import "github.com/gofiber/storage/minio"
+```
+
+You can use the following possibilities to create a storage:
+```go
+// Initialize default config
+store := minio.New()
+
+// Initialize custom config
+store := minio.New(minio.Config{
+ Bucket: "fiber-bucket",
+ Endpoint: "localhost:9000",
+ Credentials: Credentials{
+ accessKeyID: "minio-user",
+ secretAccessKey: "minio-password",
+ },
+})
+```
+
+### Config
+```go
+// Config defines the config for storage.
+type Config struct {
+ // Bucket
+ // Default fiber-bucket
+ Bucket string
+
+ // Endpoint is a host name or an IP address
+ Endpoint string
+
+ // Region Set this value to override region cache
+ // Optional
+ Region string
+
+ // Token Set this value to provide x-amz-security-token (AWS S3 specific)
+ // Optional, Default is false
+ Token string
+
+ // Secure If set to true, https is used instead of http.
+ // Default is false
+ Secure bool
+
+ // Reset clears any existing keys in existing Bucket
+ // Optional. Default is false
+ Reset bool
+
+ // Credentials Minio access key and Minio secret key.
+ // Need to be defined
+ Credentials Credentials
+
+ // GetObjectOptions Options for GET requests specifying additional options like encryption, If-Match
+ GetObjectOptions minio.GetObjectOptions
+
+ // PutObjectOptions
+ // Allows user to set optional custom metadata, content headers, encryption keys and number of threads for multipart upload operation.
+ PutObjectOptions minio.PutObjectOptions
+
+ // ListObjectsOptions Options per to list objects
+ ListObjectsOptions minio.ListObjectsOptions
+
+ // RemoveObjectOptions Allows user to set options
+ RemoveObjectOptions minio.RemoveObjectOptions
+}
+```
+
+### Default Config
+The default configuration lacks Bucket, Region, and Endpoint which are all required and must be overwritten:
+```go
+// ConfigDefault is the default config
+var ConfigDefault = Config{
+ Bucket: "fiber-bucket",
+ Endpoint: "",
+ Region: "",
+ Token: "",
+ Secure: false,
+ Reset: false,
+ Credentials: Credentials{},
+ GetObjectOptions: minio.GetObjectOptions{},
+ PutObjectOptions: minio.PutObjectOptions{},
+ ListObjectsOptions: minio.ListObjectsOptions{},
+ RemoveObjectOptions: minio.RemoveObjectOptions{},
+}
+type Credentials struct {
+ AccessKeyID string
+ SecretAccessKey string
+}
+```
diff --git a/minio/config.go b/minio/config.go
new file mode 100644
index 00000000..d1976d7c
--- /dev/null
+++ b/minio/config.go
@@ -0,0 +1,88 @@
+package minio
+
+import (
+ "github.com/minio/minio-go/v7"
+)
+
+// Config defines the config for storage.
+type Config struct {
+ // Bucket
+ // Default fiber-bucket
+ Bucket string
+
+ // Endpoint is a host name or an IP address
+ Endpoint string
+
+ // Region Set this value to override region cache
+ // Optional
+ Region string
+
+ // Token Set this value to provide x-amz-security-token (AWS S3 specific)
+ // Optional, Default is false
+ Token string
+
+ // Secure If set to true, https is used instead of http.
+ // Default is false
+ Secure bool
+
+ // Reset clears any existing keys in existing Bucket
+ // Optional. Default is false
+ Reset bool
+
+ // Credentials Minio access key and Minio secret key.
+ // Need to be defined
+ Credentials Credentials
+
+ // GetObjectOptions Options for GET requests specifying additional options like encryption, If-Match
+ GetObjectOptions minio.GetObjectOptions
+
+ // PutObjectOptions
+ // Allows user to set optional custom metadata, content headers, encryption keys and number of threads for multipart upload operation.
+ PutObjectOptions minio.PutObjectOptions
+
+ // ListObjectsOptions Options per to list objects
+ ListObjectsOptions minio.ListObjectsOptions
+
+ // RemoveObjectOptions Allows user to set options
+ RemoveObjectOptions minio.RemoveObjectOptions
+}
+
+type Credentials struct {
+ // AccessKeyID is like user-id that uniquely identifies your account.
+ AccessKeyID string
+ // SecretAccessKey is the password to your account.
+ SecretAccessKey string
+}
+
+// ConfigDefault is the default config
+var ConfigDefault = Config{
+ Bucket: "fiber-bucket",
+ Endpoint: "",
+ Region: "",
+ Token: "",
+ Secure: false,
+ Reset: false,
+ Credentials: Credentials{},
+ GetObjectOptions: minio.GetObjectOptions{},
+ PutObjectOptions: minio.PutObjectOptions{},
+ ListObjectsOptions: minio.ListObjectsOptions{},
+ RemoveObjectOptions: minio.RemoveObjectOptions{},
+}
+
+// Helper function to set default values
+func configDefault(config ...Config) Config {
+ // Return default config if nothing provided
+ if len(config) < 1 {
+ return ConfigDefault
+ }
+
+ // Override default config
+ cfg := config[0]
+
+ // Set default values
+ if cfg.Bucket == "" {
+ cfg.Bucket = ConfigDefault.Bucket
+ }
+
+ return cfg
+}
diff --git a/minio/go.mod b/minio/go.mod
new file mode 100644
index 00000000..2e86fc8b
--- /dev/null
+++ b/minio/go.mod
@@ -0,0 +1,31 @@
+module github.com/gofiber/storage/minio
+
+go 1.19
+
+require (
+ github.com/minio/minio-go/v7 v7.0.63
+ github.com/stretchr/testify v1.8.4
+ github.com/valyala/bytebufferpool v1.0.0
+)
+
+require (
+ github.com/davecgh/go-spew v1.1.1 // indirect
+ github.com/dustin/go-humanize v1.0.1 // indirect
+ github.com/google/uuid v1.3.1 // indirect
+ github.com/json-iterator/go v1.1.12 // indirect
+ github.com/klauspost/compress v1.16.7 // indirect
+ github.com/klauspost/cpuid/v2 v2.2.5 // indirect
+ github.com/minio/md5-simd v1.1.2 // indirect
+ github.com/minio/sha256-simd v1.0.1 // indirect
+ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
+ github.com/modern-go/reflect2 v1.0.2 // indirect
+ github.com/pmezard/go-difflib v1.0.0 // indirect
+ github.com/rs/xid v1.5.0 // indirect
+ github.com/sirupsen/logrus v1.9.3 // indirect
+ golang.org/x/crypto v0.12.0 // indirect
+ golang.org/x/net v0.14.0 // indirect
+ golang.org/x/sys v0.11.0 // indirect
+ golang.org/x/text v0.12.0 // indirect
+ gopkg.in/ini.v1 v1.67.0 // indirect
+ gopkg.in/yaml.v3 v3.0.1 // indirect
+)
diff --git a/minio/go.sum b/minio/go.sum
new file mode 100644
index 00000000..63183408
--- /dev/null
+++ b/minio/go.sum
@@ -0,0 +1,56 @@
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
+github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
+github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
+github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
+github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I=
+github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
+github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
+github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg=
+github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
+github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
+github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
+github.com/minio/minio-go/v7 v7.0.63 h1:GbZ2oCvaUdgT5640WJOpyDhhDxvknAJU2/T3yurwcbQ=
+github.com/minio/minio-go/v7 v7.0.63/go.mod h1:Q6X7Qjb7WMhvG65qKf4gUgA5XaiSox74kR1uAEjxRS4=
+github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM=
+github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
+github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc=
+github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
+github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
+github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
+github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
+github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
+github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
+golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk=
+golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
+golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14=
+golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
+golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
+golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc=
+golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
+gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/minio/minio.go b/minio/minio.go
new file mode 100644
index 00000000..ebf65847
--- /dev/null
+++ b/minio/minio.go
@@ -0,0 +1,171 @@
+package minio
+
+import (
+ "bytes"
+ "context"
+ "errors"
+ "log"
+ "net/http"
+ "time"
+
+ "github.com/minio/minio-go/v7"
+ "github.com/minio/minio-go/v7/pkg/credentials"
+ "github.com/valyala/bytebufferpool"
+)
+
+// Storage interface that is implemented by storage providers
+type Storage struct {
+ minio *minio.Client
+ cfg Config
+ ctx context.Context
+}
+
+// New creates a new storage
+func New(config ...Config) *Storage {
+
+ // Set default config
+ cfg := configDefault(config...)
+
+ // Minio instance
+ minioClient, err := minio.New(cfg.Endpoint, &minio.Options{
+ Creds: credentials.NewStaticV4(cfg.Credentials.AccessKeyID, cfg.Credentials.SecretAccessKey, cfg.Token),
+ Secure: cfg.Secure,
+ Region: cfg.Region,
+ })
+ if err != nil {
+ panic(err)
+ }
+
+ storage := &Storage{minio: minioClient, cfg: cfg, ctx: context.Background()}
+
+ // Reset all entries if set to true
+ if cfg.Reset {
+ if err = storage.Reset(); err != nil {
+ panic(err)
+ }
+ }
+
+ // check bucket
+ err = storage.CheckBucket()
+ if err != nil {
+ // create bucket
+ err = storage.CreateBucket()
+ if err != nil {
+ panic(err)
+ }
+ }
+
+ return storage
+}
+
+// Get value by key
+func (s *Storage) Get(key string) ([]byte, error) {
+
+ if len(key) <= 0 {
+ return nil, errors.New("the key value is required")
+ }
+
+ // get object
+ object, err := s.minio.GetObject(s.ctx, s.cfg.Bucket, key, s.cfg.GetObjectOptions)
+ if err != nil {
+ return nil, err
+ }
+
+ // convert to byte
+ bb := bytebufferpool.Get()
+ defer bytebufferpool.Put(bb)
+ _, err = bb.ReadFrom(object)
+ if err != nil {
+ return nil, err
+ }
+ return bb.Bytes(), nil
+}
+
+// Set key with value
+func (s *Storage) Set(key string, val []byte, exp time.Duration) error {
+
+ if len(key) <= 0 {
+ return errors.New("the key value is required")
+ }
+
+ // create Reader
+ file := bytes.NewReader(val)
+
+ // set content type
+ s.cfg.PutObjectOptions.ContentType = http.DetectContentType(val)
+
+ // put object
+ _, err := s.minio.PutObject(s.ctx, s.cfg.Bucket, key, file, file.Size(), s.cfg.PutObjectOptions)
+
+ return err
+}
+
+// Delete entry by key
+func (s *Storage) Delete(key string) error {
+
+ if len(key) <= 0 {
+ return errors.New("the key value is required")
+ }
+
+ // remove
+ err := s.minio.RemoveObject(s.ctx, s.cfg.Bucket, key, s.cfg.RemoveObjectOptions)
+
+ return err
+}
+
+// Reset all entries, including unexpired
+func (s *Storage) Reset() error {
+
+ objectsCh := make(chan minio.ObjectInfo)
+
+ // Send object names that are needed to be removed to objectsCh
+ go func() {
+ defer close(objectsCh)
+ // List all objects from a bucket-name with a matching prefix.
+ for object := range s.minio.ListObjects(s.ctx, s.cfg.Bucket, s.cfg.ListObjectsOptions) {
+ if object.Err != nil {
+ log.Println(object.Err)
+ }
+ objectsCh <- object
+ }
+ }()
+
+ opts := minio.RemoveObjectsOptions{
+ GovernanceBypass: true,
+ }
+
+ for err := range s.minio.RemoveObjects(s.ctx, s.cfg.Bucket, objectsCh, opts) {
+ log.Println("Error detected during deletion: ", err)
+ }
+
+ return nil
+}
+
+// Close the storage
+func (s *Storage) Close() error {
+ return nil
+}
+
+// CheckBucket Check to see if bucket already exists
+func (s *Storage) CheckBucket() error {
+ exists, err := s.minio.BucketExists(s.ctx, s.cfg.Bucket)
+ if !exists || err != nil {
+ return errors.New("the specified bucket does not exist")
+ }
+ return nil
+}
+
+// CreateBucket Bucket not found so Make a new bucket
+func (s *Storage) CreateBucket() error {
+ return s.minio.MakeBucket(s.ctx, s.cfg.Bucket, minio.MakeBucketOptions{Region: s.cfg.Region})
+}
+
+// RemoveBucket Bucket remove if bucket is empty
+func (s *Storage) RemoveBucket() error {
+ return s.minio.RemoveBucket(s.ctx, s.cfg.Bucket)
+}
+
+// Conn Return minio client
+func (s *Storage) Conn() *minio.Client {
+ return s.minio
+}
diff --git a/minio/minio_test.go b/minio/minio_test.go
new file mode 100644
index 00000000..07fb0724
--- /dev/null
+++ b/minio/minio_test.go
@@ -0,0 +1,184 @@
+package minio
+
+import (
+ "strconv"
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/require"
+)
+
+var testStore = New(
+ Config{
+ Bucket: "fiber-bucket",
+ Endpoint: "localhost:9000",
+ Credentials: Credentials{
+ AccessKeyID: "minio-user",
+ SecretAccessKey: "minio-password",
+ },
+ },
+)
+
+func Test_Get(t *testing.T) {
+ var (
+ key = "john"
+ val = []byte("doe")
+ )
+
+ err := testStore.Set(key, val, 0)
+ require.NoError(t, err)
+
+ result, err := testStore.Get(key)
+ require.NoError(t, err)
+ require.Equal(t, val, result)
+
+ result, err = testStore.Get("doe")
+ require.Error(t, err)
+ require.Zero(t, len(result))
+}
+
+func Test_Get_Empty_Key(t *testing.T) {
+ var (
+ key = ""
+ )
+
+ _, err := testStore.Get(key)
+ require.Error(t, err)
+ require.EqualError(t, err, "the key value is required")
+
+}
+
+func Test_Get_Not_Exists_Key(t *testing.T) {
+ var (
+ key = "not-exists"
+ )
+
+ _, err := testStore.Get(key)
+ require.Error(t, err)
+ require.EqualError(t, err, "The specified key does not exist.")
+
+}
+
+func Test_Get_Not_Exists_Bucket(t *testing.T) {
+ var (
+ key = "john"
+ )
+
+ // random bucket name
+ testStore.cfg.Bucket = strconv.FormatInt(time.Now().UnixMicro(), 10)
+
+ result, err := testStore.Get(key)
+ require.Error(t, err)
+ require.Zero(t, len(result))
+ require.EqualError(t, err, "The specified bucket does not exist")
+
+ testStore.cfg.Bucket = "fiber-bucket"
+
+}
+
+func Test_Set(t *testing.T) {
+ var (
+ key = "john"
+ val = []byte("doe")
+ )
+
+ err := testStore.Set(key, val, 0)
+ require.NoError(t, err)
+}
+
+func Test_Set_Empty_Key(t *testing.T) {
+ var (
+ key = ""
+ val = []byte("doe")
+ )
+
+ err := testStore.Set(key, val, 0)
+
+ require.Error(t, err)
+ require.EqualError(t, err, "the key value is required")
+
+}
+
+func Test_Set_Not_Exists_Bucket(t *testing.T) {
+ var (
+ key = "john"
+ val = []byte("doe")
+ )
+
+ // random bucket name
+ testStore.cfg.Bucket = strconv.FormatInt(time.Now().UnixMicro(), 10)
+
+ err := testStore.Set(key, val, 0)
+ require.Error(t, err)
+ require.EqualError(t, err, "The specified bucket does not exist")
+
+ testStore.cfg.Bucket = "fiber-bucket"
+}
+
+func Test_Delete(t *testing.T) {
+ var (
+ key = "john"
+ val = []byte("doe")
+ )
+
+ err := testStore.Set(key, val, 0)
+ require.NoError(t, err)
+
+ err = testStore.Delete(key)
+ require.NoError(t, err)
+
+}
+
+func Test_Delete_Empty_Key(t *testing.T) {
+ var (
+ key = ""
+ val = []byte("doe")
+ )
+
+ err := testStore.Set(key, val, 0)
+
+ require.Error(t, err)
+ require.EqualError(t, err, "the key value is required")
+
+}
+
+func Test_Delete_Not_Exists_Bucket(t *testing.T) {
+ var (
+ key = "john"
+ )
+
+ // random bucket name
+ testStore.cfg.Bucket = strconv.FormatInt(time.Now().UnixMicro(), 10)
+
+ err := testStore.Delete(key)
+
+ require.Error(t, err)
+ require.EqualError(t, err, "The specified bucket does not exist")
+
+ testStore.cfg.Bucket = "fiber-bucket"
+
+}
+
+func Test_Reset(t *testing.T) {
+ var (
+ val = []byte("doe")
+ )
+
+ err := testStore.Set("john1", val, 0)
+ require.NoError(t, err)
+
+ err = testStore.Set("john2", val, 0)
+ require.NoError(t, err)
+
+ err = testStore.Reset()
+ require.NoError(t, err)
+
+ result, err := testStore.Get("john1")
+ require.Error(t, err)
+ require.Zero(t, len(result))
+
+}
+
+func Test_Close(t *testing.T) {
+ require.NoError(t, testStore.Close())
+}