Skip to content

Commit

Permalink
feat: stand-alone port s3 server (AlistGo#6242)
Browse files Browse the repository at this point in the history
* feat: single port s3 server

* fix: unable to PUT files if not in root dir
  • Loading branch information
itsHenry35 authored and EtherealAO committed Apr 12, 2024
1 parent f007672 commit 7eea203
Show file tree
Hide file tree
Showing 9 changed files with 70 additions and 21 deletions.
21 changes: 21 additions & 0 deletions cmd/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,27 @@ the address is defined in config file`,
}
}()
}
s3r := gin.New()
s3r.Use(gin.LoggerWithWriter(log.StandardLogger().Out), gin.RecoveryWithWriter(log.StandardLogger().Out))
server.InitS3(s3r)
if conf.Conf.S3.Port != -1 {
s3Base := fmt.Sprintf("%s:%d", conf.Conf.Scheme.Address, conf.Conf.S3.Port)
utils.Log.Infof("start S3 server @ %s", s3Base)
go func() {
var err error
if conf.Conf.S3.SSL {
httpsSrv = &http.Server{Addr: s3Base, Handler: s3r}
err = httpsSrv.ListenAndServeTLS(conf.Conf.Scheme.CertFile, conf.Conf.Scheme.KeyFile)
}
if !conf.Conf.S3.SSL {
httpSrv = &http.Server{Addr: s3Base, Handler: s3r}
err = httpSrv.ListenAndServe()
}
if err != nil && !errors.Is(err, http.ErrServerClosed) {
utils.Log.Fatalf("failed to start s3 server: %s", err.Error())
}
}()
}
// Wait for interrupt signal to gracefully shutdown the server with
// a timeout of 1 second.
quit := make(chan os.Signal, 1)
Expand Down
1 change: 0 additions & 1 deletion internal/bootstrap/data/setting.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,6 @@ func InitialSettings() []model.SettingItem {
{Key: conf.LdapLoginTips, Value: "login with ldap", Type: conf.TypeString, Group: model.LDAP, Flag: model.PUBLIC},

//s3 settings
{Key: conf.S3Enabled, Value: "false", Type: conf.TypeBool, Group: model.S3, Flag: model.PRIVATE},
{Key: conf.S3AccessKeyId, Value: "", Type: conf.TypeString, Group: model.S3, Flag: model.PRIVATE},
{Key: conf.S3SecretAccessKey, Value: "", Type: conf.TypeString, Group: model.S3, Flag: model.PRIVATE},
{Key: conf.S3Buckets, Value: "[]", Type: conf.TypeString, Group: model.S3, Flag: model.PRIVATE},
Expand Down
15 changes: 14 additions & 1 deletion internal/conf/config.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package conf

import (
"path/filepath"

"github.com/alist-org/alist/v3/cmd/flags"
"github.com/alist-org/alist/v3/pkg/utils/random"
"path/filepath"
)

type Database struct {
Expand Down Expand Up @@ -63,6 +64,12 @@ type Cors struct {
AllowHeaders []string `json:"allow_headers" env:"ALLOW_HEADERS"`
}

type S3 struct {
Enable bool `json:"enable" env:"ENABLE"`
Port int `json:"port" env:"PORT"`
SSL bool `json:"ssl" env:"SSL"`
}

type Config struct {
Force bool `json:"force" env:"FORCE"`
SiteURL string `json:"site_url" env:"SITE_URL"`
Expand All @@ -81,6 +88,7 @@ type Config struct {
TlsInsecureSkipVerify bool `json:"tls_insecure_skip_verify" env:"TLS_INSECURE_SKIP_VERIFY"`
Tasks TasksConfig `json:"tasks" envPrefix:"TASKS_"`
Cors Cors `json:"cors" envPrefix:"CORS_"`
S3 S3 `json:"s3" envPrefix:"S3_"`
}

func DefaultConfig() *Config {
Expand Down Expand Up @@ -142,5 +150,10 @@ func DefaultConfig() *Config {
AllowMethods: []string{"*"},
AllowHeaders: []string{"*"},
},
S3: S3{
Enable: false,
Port: 5246,
SSL: false,
},
}
}
3 changes: 1 addition & 2 deletions internal/conf/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,9 @@ const (
LdapLoginTips = "ldap_login_tips"

//s3
S3Enabled = "s3_enabled"
S3Buckets = "s3_buckets"
S3AccessKeyId = "s3_access_key_id"
S3SecretAccessKey = "s3_secret_access_key"
S3Buckets = "s3_buckets"

// qbittorrent
QbittorrentUrl = "qbittorrent_url"
Expand Down
5 changes: 5 additions & 0 deletions server/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,3 +171,8 @@ func Cors(r *gin.Engine) {
config.AllowMethods = conf.Conf.Cors.AllowMethods
r.Use(cors.New(config))
}

func InitS3(e *gin.Engine) {
Cors(e)
S3Server(e.Group("/"))
}
22 changes: 19 additions & 3 deletions server/s3.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,40 @@ import (
"strings"

"github.com/alist-org/alist/v3/internal/conf"
"github.com/alist-org/alist/v3/internal/setting"
"github.com/alist-org/alist/v3/server/common"
"github.com/alist-org/alist/v3/server/s3"
"github.com/gin-gonic/gin"
)

func S3(g *gin.RouterGroup) {
if !setting.GetBool(conf.S3Enabled) {
if !conf.Conf.S3.Enable {
g.Any("/*path", func(c *gin.Context) {
common.ErrorStrResp(c, "S3 server is not enabled", 403)
})
return
}
h, _ := s3.NewServer(context.Background(), []string{setting.GetStr(conf.S3AccessKeyId) + "," + setting.GetStr(conf.S3SecretAccessKey)})
if conf.Conf.S3.Port != -1 {
g.Any("/*path", func(c *gin.Context) {
common.ErrorStrResp(c, "S3 server bound to single port", 403)
})
return
}
h, _ := s3.NewServer(context.Background())

g.Any("/*path", func(c *gin.Context) {
adjustedPath := strings.TrimPrefix(c.Request.URL.Path, path.Join(conf.URL.Path, "/s3"))
c.Request.URL.Path = adjustedPath
gin.WrapH(h)(c)
})
}

func S3Server(g *gin.RouterGroup) {
if !conf.Conf.S3.Enable {
g.Any("/*path", func(c *gin.Context) {
common.ErrorStrResp(c, "S3 server is not enabled", 403)
})
return
}
h, _ := s3.NewServer(context.Background())
g.Any("/*path", gin.WrapH(h))
}
2 changes: 1 addition & 1 deletion server/s3/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ func (b *s3Backend) PutObject(
Mimetype: meta["Content-Type"],
}

err = fs.PutDirectly(ctx, path.Dir(reqPath), stream)
err = fs.PutDirectly(ctx, reqPath, stream)
if err != nil {
return result, err
}
Expand Down
4 changes: 2 additions & 2 deletions server/s3/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@ import (
)

// Make a new S3 Server to serve the remote
func NewServer(ctx context.Context, authpair []string) (h http.Handler, err error) {
func NewServer(ctx context.Context) (h http.Handler, err error) {
var newLogger logger
faker := gofakes3.New(
newBackend(),
// gofakes3.WithHostBucket(!opt.pathBucketMode),
gofakes3.WithLogger(newLogger),
gofakes3.WithRequestID(rand.Uint64()),
gofakes3.WithoutVersioning(),
gofakes3.WithV4Auth(authlistResolver(authpair)),
gofakes3.WithV4Auth(authlistResolver()),
gofakes3.WithIntegrityCheck(true), // Check Content-MD5 if supplied
)

Expand Down
18 changes: 7 additions & 11 deletions server/s3/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ package s3
import (
"context"
"encoding/json"
"fmt"
"strings"

"github.com/Mikubill/gofakes3"
Expand All @@ -15,7 +14,6 @@ import (
"github.com/alist-org/alist/v3/internal/model"
"github.com/alist-org/alist/v3/internal/op"
"github.com/alist-org/alist/v3/internal/setting"
"github.com/alist-org/alist/v3/pkg/utils"
)

type Bucket struct {
Expand Down Expand Up @@ -150,15 +148,13 @@ func prefixParser(p *gofakes3.Prefix) (path, remaining string) {
// }
// }

func authlistResolver(list []string) map[string]string {
authList := make(map[string]string)
for _, v := range list {
parts := strings.Split(v, ",")
if len(parts) != 2 {
utils.Log.Infof(fmt.Sprintf("Ignored: invalid auth pair %s", v))
continue
}
authList[parts[0]] = parts[1]
func authlistResolver() map[string]string {
s3accesskeyid := setting.GetStr(conf.S3AccessKeyId)
s3secretaccesskey := setting.GetStr(conf.S3SecretAccessKey)
if s3accesskeyid == "" && s3secretaccesskey == "" {
return nil
}
authList := make(map[string]string)
authList[s3accesskeyid] = s3secretaccesskey
return authList
}

0 comments on commit 7eea203

Please sign in to comment.