2017-05-08 23:07:07 +02:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2021-04-16 14:55:19 +02:00
|
|
|
"crypto/tls"
|
2021-04-21 11:37:38 +02:00
|
|
|
"embed"
|
2019-09-05 00:44:02 +02:00
|
|
|
"fmt"
|
2021-04-21 11:37:38 +02:00
|
|
|
"io/fs"
|
2017-05-08 23:07:07 +02:00
|
|
|
"log"
|
|
|
|
"net/http"
|
|
|
|
"os"
|
2022-08-04 22:30:04 +02:00
|
|
|
"time"
|
2017-05-08 23:07:07 +02:00
|
|
|
|
2021-12-30 12:09:42 +01:00
|
|
|
"github.com/cloudlena/adapters/logging"
|
2022-03-24 08:46:21 +01:00
|
|
|
"github.com/cloudlena/s3manager/internal/app/s3manager"
|
2022-03-23 10:10:30 +01:00
|
|
|
"github.com/gorilla/mux"
|
2021-08-05 11:44:40 +02:00
|
|
|
"github.com/minio/minio-go/v7"
|
|
|
|
"github.com/minio/minio-go/v7/pkg/credentials"
|
2022-03-23 10:10:30 +01:00
|
|
|
"github.com/spf13/viper"
|
2017-05-08 23:07:07 +02:00
|
|
|
)
|
|
|
|
|
2021-04-21 11:37:38 +02:00
|
|
|
//go:embed web/template
|
|
|
|
var templateFS embed.FS
|
|
|
|
|
2022-05-01 16:20:41 +02:00
|
|
|
//go:embed web/static
|
2022-04-30 12:33:46 +02:00
|
|
|
var staticFS embed.FS
|
|
|
|
|
2023-07-28 15:05:14 +02:00
|
|
|
type configuration struct {
|
|
|
|
Endpoint string
|
|
|
|
UseIam bool
|
|
|
|
IamEndpoint string
|
|
|
|
AccessKeyID string
|
|
|
|
SecretAccessKey string
|
|
|
|
Region string
|
|
|
|
AllowDelete bool
|
|
|
|
ForceDownload bool
|
|
|
|
UseSSL bool
|
|
|
|
SkipSSLVerification bool
|
2023-09-28 12:04:37 +02:00
|
|
|
SignatureType string
|
2023-07-28 15:05:14 +02:00
|
|
|
ListRecursive bool
|
|
|
|
Port string
|
|
|
|
Timeout int32
|
|
|
|
SseType string
|
|
|
|
SseKey string
|
|
|
|
}
|
|
|
|
|
|
|
|
func parseConfiguration() configuration {
|
2022-03-24 08:46:21 +01:00
|
|
|
var accessKeyID, secretAccessKey, iamEndpoint string
|
2022-03-23 10:46:09 +01:00
|
|
|
|
2022-03-23 10:10:30 +01:00
|
|
|
viper.AutomaticEnv()
|
|
|
|
|
|
|
|
viper.SetDefault("ENDPOINT", "s3.amazonaws.com")
|
|
|
|
endpoint := viper.GetString("ENDPOINT")
|
|
|
|
|
2022-03-23 10:46:09 +01:00
|
|
|
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")
|
|
|
|
}
|
2022-03-23 10:10:30 +01:00
|
|
|
|
2022-03-23 10:46:09 +01:00
|
|
|
secretAccessKey = viper.GetString("SECRET_ACCESS_KEY")
|
|
|
|
if len(secretAccessKey) == 0 {
|
|
|
|
log.Fatal("please provide SECRET_ACCESS_KEY")
|
|
|
|
}
|
2018-11-30 23:09:00 +01:00
|
|
|
}
|
2022-03-23 10:10:30 +01:00
|
|
|
|
|
|
|
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")
|
|
|
|
|
2023-09-28 12:04:37 +02:00
|
|
|
viper.SetDefault("SIGNATURE_TYPE", "V4")
|
|
|
|
signatureType := viper.GetString("SIGNATURE_TYPE")
|
2023-09-28 10:21:28 +02:00
|
|
|
|
2022-03-23 10:10:30 +01:00
|
|
|
listRecursive := viper.GetBool("LIST_RECURSIVE")
|
|
|
|
|
|
|
|
viper.SetDefault("PORT", "8080")
|
|
|
|
port := viper.GetString("PORT")
|
2019-09-05 00:44:02 +02:00
|
|
|
|
2023-07-28 15:05:14 +02:00
|
|
|
viper.SetDefault("TIMEOUT", 600)
|
|
|
|
timeout := viper.GetInt32("TIMEOUT")
|
|
|
|
|
2023-02-09 12:05:14 +01:00
|
|
|
viper.SetDefault("SSE_TYPE", "")
|
2023-07-28 15:05:14 +02:00
|
|
|
sseType := viper.GetString("SSE_TYPE")
|
2023-09-28 10:53:17 +02:00
|
|
|
|
2023-02-09 12:05:14 +01:00
|
|
|
viper.SetDefault("SSE_KEY", "")
|
2023-07-28 15:05:14 +02:00
|
|
|
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,
|
2023-09-28 12:04:37 +02:00
|
|
|
SignatureType: signatureType,
|
2023-07-28 15:05:14 +02:00
|
|
|
ListRecursive: listRecursive,
|
|
|
|
Port: port,
|
|
|
|
Timeout: timeout,
|
|
|
|
SseType: sseType,
|
|
|
|
SseKey: sseKey,
|
|
|
|
}
|
|
|
|
}
|
2023-02-09 12:05:14 +01:00
|
|
|
|
2023-07-28 15:05:14 +02:00
|
|
|
func main() {
|
|
|
|
configuration := parseConfiguration()
|
2023-02-09 12:05:14 +01:00
|
|
|
|
2023-07-28 15:05:14 +02:00
|
|
|
sseType := s3manager.SSEType{Type: configuration.SseType, Key: configuration.SseKey}
|
|
|
|
serverTimeout := time.Duration(configuration.Timeout) * time.Second
|
2023-07-27 18:59:39 +02:00
|
|
|
|
2021-04-21 11:37:38 +02:00
|
|
|
// Set up templates
|
|
|
|
templates, err := fs.Sub(templateFS, "web/template")
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
2022-04-30 12:34:24 +02:00
|
|
|
// Set up statics
|
2022-05-01 16:20:41 +02:00
|
|
|
statics, err := fs.Sub(staticFS, "web/static")
|
2022-04-30 12:33:46 +02:00
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
}
|
2018-05-31 16:10:41 +02:00
|
|
|
|
2017-05-08 23:07:07 +02:00
|
|
|
// Set up S3 client
|
2021-08-05 11:44:40 +02:00
|
|
|
opts := &minio.Options{
|
2023-07-28 15:05:14 +02:00
|
|
|
Secure: configuration.UseSSL,
|
2021-08-05 11:44:40 +02:00
|
|
|
}
|
2023-07-28 15:05:14 +02:00
|
|
|
if configuration.UseIam {
|
|
|
|
opts.Creds = credentials.NewIAM(configuration.IamEndpoint)
|
2022-03-23 10:46:09 +01:00
|
|
|
} else {
|
2023-09-28 12:04:37 +02:00
|
|
|
var signatureType credentials.SignatureType
|
2023-09-28 11:51:16 +02:00
|
|
|
|
2023-09-28 12:04:37 +02:00
|
|
|
switch configuration.SignatureType {
|
2023-09-28 10:53:17 +02:00
|
|
|
case "V2":
|
|
|
|
signatureType = credentials.SignatureV2
|
|
|
|
case "V4":
|
|
|
|
signatureType = credentials.SignatureV4
|
|
|
|
case "V4Streaming":
|
|
|
|
signatureType = credentials.SignatureV4Streaming
|
|
|
|
case "Anonymous":
|
|
|
|
signatureType = credentials.SignatureAnonymous
|
|
|
|
default:
|
|
|
|
log.Fatalf("Invalid SIGNATURE_TYPE: %s", configuration.SignatureType)
|
2023-09-28 10:21:28 +02:00
|
|
|
}
|
2023-09-28 11:51:16 +02:00
|
|
|
|
2023-09-28 12:04:37 +02:00
|
|
|
opts.Creds = credentials.NewStatic(configuration.AccessKeyID, configuration.SecretAccessKey, "", signatureType)
|
2022-03-23 10:46:09 +01:00
|
|
|
}
|
|
|
|
|
2023-07-28 15:05:14 +02:00
|
|
|
if configuration.Region != "" {
|
|
|
|
opts.Region = configuration.Region
|
2017-05-08 23:07:07 +02:00
|
|
|
}
|
2023-07-28 15:05:14 +02:00
|
|
|
if configuration.UseSSL && configuration.SkipSSLVerification {
|
2021-08-05 11:44:40 +02:00
|
|
|
opts.Transport = &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}} //nolint:gosec
|
|
|
|
}
|
2023-07-28 15:05:14 +02:00
|
|
|
s3, err := minio.New(configuration.Endpoint, opts)
|
2021-08-05 11:44:40 +02:00
|
|
|
if err != nil {
|
|
|
|
log.Fatalln(fmt.Errorf("error creating s3 client: %w", err))
|
2021-04-16 14:55:19 +02:00
|
|
|
}
|
2017-05-08 23:07:07 +02:00
|
|
|
|
|
|
|
// Set up router
|
2022-03-23 10:10:30 +01:00
|
|
|
r := mux.NewRouter()
|
|
|
|
r.Handle("/", http.RedirectHandler("/buckets", http.StatusPermanentRedirect)).Methods(http.MethodGet)
|
2022-04-30 12:33:46 +02:00
|
|
|
r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.FS(statics)))).Methods(http.MethodGet)
|
2023-07-28 15:05:14 +02:00
|
|
|
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)
|
2022-03-23 10:10:30 +01:00
|
|
|
r.Handle("/api/buckets", s3manager.HandleCreateBucket(s3)).Methods(http.MethodPost)
|
2023-07-28 15:05:14 +02:00
|
|
|
if configuration.AllowDelete {
|
2022-03-23 10:10:30 +01:00
|
|
|
r.Handle("/api/buckets/{bucketName}", s3manager.HandleDeleteBucket(s3)).Methods(http.MethodDelete)
|
|
|
|
}
|
2023-02-09 12:05:14 +01:00
|
|
|
r.Handle("/api/buckets/{bucketName}/objects", s3manager.HandleCreateObject(s3, sseType)).Methods(http.MethodPost)
|
2023-08-20 14:03:02 +02:00
|
|
|
r.Handle("/api/buckets/{bucketName}/objects/{objectName:.*}/url", s3manager.HandleGenerateUrl(s3)).Methods(http.MethodGet)
|
2023-07-28 15:05:14 +02:00
|
|
|
r.Handle("/api/buckets/{bucketName}/objects/{objectName:.*}", s3manager.HandleGetObject(s3, configuration.ForceDownload)).Methods(http.MethodGet)
|
|
|
|
if configuration.AllowDelete {
|
2022-03-23 10:10:30 +01:00
|
|
|
r.Handle("/api/buckets/{bucketName}/objects/{objectName:.*}", s3manager.HandleDeleteObject(s3)).Methods(http.MethodDelete)
|
|
|
|
}
|
2018-05-31 16:10:41 +02:00
|
|
|
|
|
|
|
lr := logging.Handler(os.Stdout)(r)
|
2022-08-04 22:30:04 +02:00
|
|
|
srv := &http.Server{
|
2023-07-28 15:05:14 +02:00
|
|
|
Addr: ":" + configuration.Port,
|
2022-08-04 22:30:04 +02:00
|
|
|
Handler: lr,
|
|
|
|
ReadTimeout: serverTimeout,
|
|
|
|
WriteTimeout: serverTimeout,
|
|
|
|
}
|
|
|
|
log.Fatal(srv.ListenAndServe())
|
2017-05-08 23:07:07 +02:00
|
|
|
}
|