Skip to content

Commit

Permalink
Switch to Fiber and HTMX
Browse files Browse the repository at this point in the history
  • Loading branch information
cloudlena committed Nov 15, 2023
1 parent 97e3723 commit 5db5699
Show file tree
Hide file tree
Showing 40 changed files with 1,781 additions and 591 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build:

.PHONY: run
run:
go run
go run ./...

.PHONY: lint
lint:
Expand Down
19 changes: 10 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
[![Go Report Card](https://goreportcard.com/badge/github.com/cloudlena/s3manager)](https://goreportcard.com/report/github.com/cloudlena/s3manager)
[![Build Status](https://github.com/cloudlena/s3manager/actions/workflows/main.yml/badge.svg)](https://github.com/cloudlena/s3manager/actions)

A Web GUI written in Go to manage S3 buckets from any provider.
A Web GUI to manage S3 buckets from any provider.

![Screenshot](https://raw.githubusercontent.com/cloudlena/s3manager/master/screenshot.png)

:rocket: Powered by [Fiber](https://gofiber.io/) and [HTMX](https://htmx.org/)

## Features

- List all buckets in your account
Expand All @@ -29,28 +31,27 @@ The application can be configured with the following environment variables:
- `USE_SSL`: Whether your S3 server uses SSL or not (defaults to `true`)
- `SKIP_SSL_VERIFICATION`: Whether the HTTP client should skip SSL verification (defaults to `false`)
- `SIGNATURE_TYPE`: The signature type to be used (defaults to `V4`; valid values are `V2, V4, V4Streaming, Anonymous`)
- `PORT`: The port the s3manager app should listen on (defaults to `8080`)
- `PORT`: The port the `s3manager` app should listen on (defaults to `8080`)
- `ALLOW_DELETE`: Enable buttons to delete objects (defaults to `true`)
- `FORCE_DOWNLOAD`: Add response headers for object downloading instead of opening in a new tab (defaults to `true`)
- `LIST_RECURSIVE`: List all objects in buckets recursively (defaults to `false`)
- `USE_IAM`: Use IAM role instead of key pair (defaults to `false`)
- `IAM_ENDPOINT`: Endpoint for IAM role retrieving (Can be blank for AWS)
- `SSE_TYPE`: Specified server side encrpytion (defaults blank) Valid values can be `SSE`, `KMS`, `SSE-C` all others values don't enable the SSE
- `SSE_TYPE`: Specified server side encryption (defaults blank) Valid values can be `SSE`, `KMS`, `SSE-C` all others values don't enable the SSE
- `SSE_KEY`: The key needed for SSE method (only for `KMS` and `SSE-C`)
- `TIMEOUT`: The read and write timout in seconds (default to `600` - 10 minutes)
- `TIMEOUT`: The read and write timeout in seconds (default to `600` - 10 minutes)

### Build and Run Locally
### Run Locally

1. Run `make build`
1. Execute the created binary and visit <http://localhost:8080>
1. Run `make run`

### Run Container image

1. Run `docker run -p 8080:8080 -e 'ACCESS_KEY_ID=XXX' -e 'SECRET_ACCESS_KEY=xxx' cloudlena/s3manager`

### Deploy to Kubernetes

You can deploy s3manager to a Kubernetes cluster using the [Helm chart](https://github.com/sergeyshevch/s3manager-helm).
You can deploy `s3manager` to a Kubernetes cluster using the [Helm chart](https://github.com/sergeyshevch/s3manager-helm).

## Development

Expand All @@ -70,7 +71,7 @@ The image is available on [Docker Hub](https://hub.docker.com/r/cloudlena/s3mana

### Run Locally for Testing

There is an example [docker-compose.yml](https://github.com/cloudlena/s3manager/blob/master/docker-compose.yml) file that spins up an S3 service and the s3manager. You can try it by issuing the following command:
There is an example [docker-compose.yml](https://github.com/cloudlena/s3manager/blob/master/docker-compose.yml) file that spins up an S3 service and the `s3manager`. You can try it by issuing the following command:

```shell
$ docker-compose up
Expand Down
13 changes: 12 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,36 @@ module github.com/cloudlena/s3manager
go 1.21.3

require (
github.com/cloudlena/adapters v0.0.0-20231026063741-fb9c991fff9e
github.com/gofiber/fiber/v2 v2.51.0
github.com/gofiber/template/html/v2 v2.0.5
github.com/gorilla/mux v1.8.1
github.com/matryer/is v1.4.1
github.com/minio/minio-go/v7 v7.0.63
github.com/spf13/viper v1.17.0
)

require (
github.com/andybalholm/brotli v1.0.6 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/gofiber/template v1.8.2 // indirect
github.com/gofiber/utils v1.1.0 // indirect
github.com/google/uuid v1.4.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.17.2 // indirect
github.com/klauspost/cpuid/v2 v2.2.6 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/minio/md5-simd v1.1.2 // indirect
github.com/minio/sha256-simd v1.0.1 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
github.com/rivo/uniseg v0.4.4 // indirect
github.com/rs/xid v1.5.0 // indirect
github.com/sagikazarmark/locafero v0.3.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
Expand All @@ -34,6 +42,9 @@ require (
github.com/spf13/cast v1.5.1 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasthttp v1.51.0 // indirect
github.com/valyala/tcplisten v1.0.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/crypto v0.15.0 // indirect
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect
Expand Down
30 changes: 28 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,13 @@ cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3f
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI=
github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudlena/adapters v0.0.0-20231026063741-fb9c991fff9e h1:ivHfzlfxOt0tNXk3iDXz2EEnRG0fZGBwQ+prrouTAP4=
github.com/cloudlena/adapters v0.0.0-20231026063741-fb9c991fff9e/go.mod h1:bf4uqsP0magG7Imj4SH0236acfqt8ZEz+XohCDmZ7Pw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
Expand All @@ -67,6 +67,14 @@ github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyT
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/gofiber/fiber/v2 v2.51.0 h1:JNACcZy5e2tGApWB2QrRpenTWn0fq0hkFm6k0C86gKQ=
github.com/gofiber/fiber/v2 v2.51.0/go.mod h1:xaQRZQJGqnKOQnbQw+ltvku3/h8QxvNi8o6JiJ7Ll0U=
github.com/gofiber/template v1.8.2 h1:PIv9s/7Uq6m+Fm2MDNd20pAFFKt5wWs7ZBd8iV9pWwk=
github.com/gofiber/template v1.8.2/go.mod h1:bs/2n0pSNPOkRa5VJ8zTIvedcI/lEYxzV3+YPXdBvq8=
github.com/gofiber/template/html/v2 v2.0.5 h1:BKLJ6Qr940NjntbGmpO3zVa4nFNGDCi/IfUiDB9OC20=
github.com/gofiber/template/html/v2 v2.0.5/go.mod h1:RCF14eLeQDCSUPp0IGc2wbSSDv6yt+V54XB/+Unz+LM=
github.com/gofiber/utils v1.1.0 h1:vdEBpn7AzIUJRhe+CiTOJdUcTg4Q9RK+pEa0KPbLdrM=
github.com/gofiber/utils v1.1.0/go.mod h1:poZpsnhBykfnY1Mc0KeEa6mSHrS3dV0+oBWyeQmb2e0=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
Expand Down Expand Up @@ -156,6 +164,13 @@ github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0V
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/matryer/is v1.4.1 h1:55ehd8zaGABKLXQUe2awZ99BD/PTc2ls+KV/dXphgEQ=
github.com/matryer/is v1.4.1/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
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=
Expand All @@ -177,6 +192,9 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
Expand Down Expand Up @@ -211,6 +229,12 @@ github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcU
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.51.0 h1:8b30A5JlZ6C7AS81RsWjYMQmrZG6feChmgAolCl1SqA=
github.com/valyala/fasthttp v1.51.0/go.mod h1:oI2XroL+lI7vdXyYoQk03bXBThfFl2cVdIA3Xl7cH8g=
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
Expand Down Expand Up @@ -356,7 +380,9 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
Expand Down
19 changes: 19 additions & 0 deletions internal/s3manager/handle_bucket_list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package s3manager

import (
"fmt"

"github.com/gofiber/fiber/v2"
)

// HandleBucketList renders all buckets as an HTML list.
func (s *S3Manager) HandleBucketList(c *fiber.Ctx) error {
buckets, err := s.s3.ListBuckets(c.Context())
if err != nil {
return fmt.Errorf("error listing buckets: %w", err)
}

return c.Render("bucket-list", fiber.Map{
"Buckets": buckets,
})
}
80 changes: 80 additions & 0 deletions internal/s3manager/handle_bucket_list_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package s3manager_test

import (
"context"
"errors"
"io"
"net/http"
"strings"
"testing"

"github.com/cloudlena/s3manager/internal/s3manager"
"github.com/cloudlena/s3manager/internal/s3manager/mocks"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/template/html/v2"
"github.com/matryer/is"
"github.com/minio/minio-go/v7"
)

func TestHandleBucketList(t *testing.T) {
t.Parallel()

cases := []struct {
it string
listBucketsVal []minio.BucketInfo
listBucketsErr error
expectedStatusCode int
expectedBodyContains string
}{
{
it: "renders a list of buckets",
listBucketsVal: []minio.BucketInfo{{Name: "BUCKET-NAME"}},
expectedStatusCode: http.StatusOK,
expectedBodyContains: "BUCKET-NAME",
},
{
it: "renders placeholder if no buckets",
expectedStatusCode: http.StatusOK,
expectedBodyContains: "No buckets yet",
},
{
it: "returns error if there is an S3 error",
listBucketsErr: errors.New("mocked s3 error"),
expectedStatusCode: http.StatusInternalServerError,
expectedBodyContains: "mocked s3 error",
},
}

for _, tc := range cases {
tc := tc
t.Run(tc.it, func(t *testing.T) {
t.Parallel()
is := is.New(t)

s3 := &mocks.S3Mock{
ListBucketsFunc: func(ctx context.Context) ([]minio.BucketInfo, error) {
return tc.listBucketsVal, tc.listBucketsErr
},
}
server := s3manager.New(s3, true, "", "")

engine := html.New("../../views", ".html.gotmpl")
app := fiber.New(fiber.Config{
Views: engine,
})
app.Get("/bucket-list", server.HandleBucketList)

req, err := http.NewRequest(fiber.MethodGet, "/bucket-list", nil)
is.NoErr(err)

resp, err := app.Test(req)
is.NoErr(err)
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
is.NoErr(err)

is.Equal(resp.StatusCode, tc.expectedStatusCode) // status code
is.True(strings.Contains(string(body), tc.expectedBodyContains)) // body
})
}
}
10 changes: 10 additions & 0 deletions internal/s3manager/handle_buckets_view.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package s3manager

import (
"github.com/gofiber/fiber/v2"
)

// HandleBucketsView renders all buckets on an HTML page.
func (s *S3Manager) HandleBucketsView(c *fiber.Ctx) error {
return c.Render("buckets", fiber.Map{})
}
57 changes: 57 additions & 0 deletions internal/s3manager/handle_buckets_view_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package s3manager_test

import (
"io"
"net/http"
"strings"
"testing"

"github.com/cloudlena/s3manager/internal/s3manager"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/template/html/v2"
"github.com/matryer/is"
)

func TestHandleBucketsView(t *testing.T) {
t.Parallel()

cases := []struct {
it string
expectedStatusCode int
expectedBodyContains string
}{
{
it: "renders the buckets page",
expectedStatusCode: http.StatusOK,
expectedBodyContains: "S3 Manager",
},
}

for _, tc := range cases {
tc := tc
t.Run(tc.it, func(t *testing.T) {
t.Parallel()
is := is.New(t)

server := s3manager.New(nil, true, "", "")

engine := html.New("../../views", ".html.gotmpl")
app := fiber.New(fiber.Config{
Views: engine,
})
app.Get("/buckets", server.HandleBucketsView)

req, err := http.NewRequest(fiber.MethodGet, "/buckets", nil)
is.NoErr(err)

resp, err := app.Test(req)
is.NoErr(err)
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
is.NoErr(err)

is.Equal(resp.StatusCode, tc.expectedStatusCode) // status code
is.True(strings.Contains(string(body), tc.expectedBodyContains)) // body
})
}
}
25 changes: 25 additions & 0 deletions internal/s3manager/handle_create_bucket.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package s3manager

import (
"fmt"
"strings"

"github.com/gofiber/fiber/v2"
"github.com/minio/minio-go/v7"
)

// HandleCreateBucket creates a new bucket.
func (s *S3Manager) HandleCreateBucket(c *fiber.Ctx) error {
name := c.FormValue("name")
if strings.TrimSpace(name) == "" {
return fiber.NewError(fiber.StatusBadRequest, "name is required")
}

err := s.s3.MakeBucket(c.Context(), name, minio.MakeBucketOptions{})
if err != nil {
return fmt.Errorf("error making bucket: %w", err)
}

c.Response().Header.Set("HX-Trigger", "bucketListChanged")
return c.SendStatus(fiber.StatusCreated)
}
Loading

0 comments on commit 5db5699

Please sign in to comment.