s3manager-web/main.go
2023-08-20 16:21:04 +02:00

179 lines
5.2 KiB
Go

package main
import (
"crypto/tls"
"embed"
"fmt"
"io/fs"
"log"
"net/http"
"os"
"time"
"github.com/cloudlena/adapters/logging"
"github.com/cloudlena/s3manager/internal/app/s3manager"
"github.com/gorilla/mux"
"github.com/minio/minio-go/v7"
"github.com/minio/minio-go/v7/pkg/credentials"
"github.com/spf13/viper"
)
//go:embed web/template
var templateFS embed.FS
//go:embed web/static
var staticFS embed.FS
type configuration struct {
Endpoint string
UseIam bool
IamEndpoint string
AccessKeyID string
SecretAccessKey string
Region string
AllowDelete bool
ForceDownload bool
UseSSL bool
SkipSSLVerification bool
ListRecursive bool
Port string
Timeout int32
SseType string
SseKey string
}
func parseConfiguration() configuration {
var accessKeyID, secretAccessKey, iamEndpoint string
viper.AutomaticEnv()
viper.SetDefault("ENDPOINT", "s3.amazonaws.com")
endpoint := viper.GetString("ENDPOINT")
useIam := viper.GetBool("USE_IAM")
if useIam {
iamEndpoint = viper.GetString("IAM_ENDPOINT")
} else {
accessKeyID = viper.GetString("ACCESS_KEY_ID")
if len(accessKeyID) == 0 {
log.Fatal("please provide ACCESS_KEY_ID")
}
secretAccessKey = viper.GetString("SECRET_ACCESS_KEY")
if len(secretAccessKey) == 0 {
log.Fatal("please provide SECRET_ACCESS_KEY")
}
}
region := viper.GetString("REGION")
viper.SetDefault("ALLOW_DELETE", true)
allowDelete := viper.GetBool("ALLOW_DELETE")
viper.SetDefault("FORCE_DOWNLOAD", true)
forceDownload := viper.GetBool("FORCE_DOWNLOAD")
viper.SetDefault("USE_SSL", true)
useSSL := viper.GetBool("USE_SSL")
viper.SetDefault("SKIP_SSL_VERIFICATION", false)
skipSSLVerification := viper.GetBool("SKIP_SSL_VERIFICATION")
listRecursive := viper.GetBool("LIST_RECURSIVE")
viper.SetDefault("PORT", "8080")
port := viper.GetString("PORT")
viper.SetDefault("TIMEOUT", 600)
timeout := viper.GetInt32("TIMEOUT")
viper.SetDefault("SSE_TYPE", "")
sseType := viper.GetString("SSE_TYPE")
viper.SetDefault("SSE_KEY", "")
sseKey := viper.GetString("SSE_KEY")
return configuration{
Endpoint: endpoint,
UseIam: useIam,
IamEndpoint: iamEndpoint,
AccessKeyID: accessKeyID,
SecretAccessKey: secretAccessKey,
Region: region,
AllowDelete: allowDelete,
ForceDownload: forceDownload,
UseSSL: useSSL,
SkipSSLVerification: skipSSLVerification,
ListRecursive: listRecursive,
Port: port,
Timeout: timeout,
SseType: sseType,
SseKey: sseKey,
}
}
func main() {
configuration := parseConfiguration()
sseType := s3manager.SSEType{Type: configuration.SseType, Key: configuration.SseKey}
serverTimeout := time.Duration(configuration.Timeout) * time.Second
// Set up templates
templates, err := fs.Sub(templateFS, "web/template")
if err != nil {
log.Fatal(err)
}
// Set up statics
statics, err := fs.Sub(staticFS, "web/static")
if err != nil {
log.Fatal(err)
}
// Set up S3 client
opts := &minio.Options{
Secure: configuration.UseSSL,
}
if configuration.UseIam {
opts.Creds = credentials.NewIAM(configuration.IamEndpoint)
} else {
opts.Creds = credentials.NewStaticV4(configuration.AccessKeyID, configuration.SecretAccessKey, "")
}
if configuration.Region != "" {
opts.Region = configuration.Region
}
if configuration.UseSSL && configuration.SkipSSLVerification {
opts.Transport = &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}} //nolint:gosec
}
s3, err := minio.New(configuration.Endpoint, opts)
if err != nil {
log.Fatalln(fmt.Errorf("error creating s3 client: %w", err))
}
// Set up router
r := mux.NewRouter()
r.Handle("/", http.RedirectHandler("/buckets", http.StatusPermanentRedirect)).Methods(http.MethodGet)
r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.FS(statics)))).Methods(http.MethodGet)
r.Handle("/buckets", s3manager.HandleBucketsView(s3, templates, configuration.AllowDelete)).Methods(http.MethodGet)
r.PathPrefix("/buckets/").Handler(s3manager.HandleBucketView(s3, templates, configuration.AllowDelete, configuration.ListRecursive)).Methods(http.MethodGet)
r.Handle("/api/buckets", s3manager.HandleCreateBucket(s3)).Methods(http.MethodPost)
if configuration.AllowDelete {
r.Handle("/api/buckets/{bucketName}", s3manager.HandleDeleteBucket(s3)).Methods(http.MethodDelete)
}
r.Handle("/api/buckets/{bucketName}/objects", s3manager.HandleCreateObject(s3, sseType)).Methods(http.MethodPost)
r.Handle("/api/buckets/{bucketName}/objects/{objectName:.*}/url", s3manager.HandleGenerateUrl(s3)).Methods(http.MethodGet)
r.Handle("/api/buckets/{bucketName}/objects/{objectName:.*}", s3manager.HandleGetObject(s3, configuration.ForceDownload)).Methods(http.MethodGet)
if configuration.AllowDelete {
r.Handle("/api/buckets/{bucketName}/objects/{objectName:.*}", s3manager.HandleDeleteObject(s3)).Methods(http.MethodDelete)
}
lr := logging.Handler(os.Stdout)(r)
srv := &http.Server{
Addr: ":" + configuration.Port,
Handler: lr,
ReadTimeout: serverTimeout,
WriteTimeout: serverTimeout,
}
log.Fatal(srv.ListenAndServe())
}