s3manager-web/main.go
2023-09-28 10:21:28 +02:00

189 lines
5.6 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
SkipSignature 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")
viper.SetDefault("SKIP_SIGNATURE", false)
skipSignature := viper.GetBool("SKIP_SIGNATURE")
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,
SkipSignature: skipSignature,
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 {
if (configuration.SkipSignature) {
opts.Creds = credentials.NewStatic(configuration.AccessKeyID, configuration.SecretAccessKey, "", SignatureType.SignatureAnonymous)
} 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())
}