mirror of
https://github.com/DullJZ/s3-balance.git
synced 2026-06-27 05:51:21 +08:00
154 lines
4.7 KiB
Go
154 lines
4.7 KiB
Go
package api
|
||
|
||
import (
|
||
"sync/atomic"
|
||
|
||
"github.com/DullJZ/s3-balance/internal/balancer"
|
||
"github.com/DullJZ/s3-balance/internal/bucket"
|
||
"github.com/DullJZ/s3-balance/internal/config"
|
||
"github.com/DullJZ/s3-balance/internal/metrics"
|
||
"github.com/DullJZ/s3-balance/internal/middleware"
|
||
"github.com/DullJZ/s3-balance/internal/storage"
|
||
"github.com/DullJZ/s3-balance/pkg/presigner"
|
||
"github.com/gorilla/mux"
|
||
)
|
||
|
||
// S3Handler S3兼容的API处理器
|
||
type S3Handler struct {
|
||
bucketManager *bucket.Manager
|
||
balancer *balancer.Balancer
|
||
presigner *presigner.Presigner
|
||
storage *storage.Service
|
||
metrics *metrics.Metrics
|
||
settings atomic.Value
|
||
}
|
||
|
||
type handlerSettings struct {
|
||
accessKey string
|
||
secretKey string
|
||
proxyMode bool
|
||
authRequired bool
|
||
virtualHost bool
|
||
signatureHost string
|
||
}
|
||
|
||
// NewS3Handler 创建新的S3兼容API处理器
|
||
func NewS3Handler(
|
||
bucketManager *bucket.Manager,
|
||
balancer *balancer.Balancer,
|
||
presigner *presigner.Presigner,
|
||
storage *storage.Service,
|
||
accessKey string,
|
||
secretKey string,
|
||
metrics *metrics.Metrics,
|
||
proxyMode bool,
|
||
authRequired bool,
|
||
virtualHost bool,
|
||
signatureHost string,
|
||
|
||
) *S3Handler {
|
||
handler := &S3Handler{
|
||
bucketManager: bucketManager,
|
||
balancer: balancer,
|
||
presigner: presigner,
|
||
storage: storage,
|
||
metrics: metrics,
|
||
}
|
||
handler.initSettings(accessKey, secretKey, proxyMode, authRequired, virtualHost, signatureHost)
|
||
return handler
|
||
}
|
||
|
||
func (h *S3Handler) initSettings(accessKey, secretKey string, proxyMode, authRequired, virtualHost bool, signatureHost string) {
|
||
h.settings.Store(handlerSettings{
|
||
accessKey: accessKey,
|
||
secretKey: secretKey,
|
||
proxyMode: proxyMode,
|
||
authRequired: authRequired,
|
||
virtualHost: virtualHost,
|
||
signatureHost: signatureHost,
|
||
})
|
||
}
|
||
|
||
// RegisterS3Routes 注册S3兼容的路由
|
||
func (h *S3Handler) RegisterS3Routes(router *mux.Router) {
|
||
// 公共路由(不需要认证)
|
||
router.HandleFunc("/", h.handleListBuckets).Methods("GET")
|
||
|
||
// 带认证/虚拟主机的路由
|
||
protected := router.NewRoute().PathPrefix("/{bucket}").Subrouter()
|
||
// 注意:不使用 StrictSlash(true) 以避免 301 重定向,兼容WinSCP
|
||
|
||
// Bucket operations
|
||
protected.HandleFunc("", h.handleBucketOperations).Methods("GET", "HEAD", "PUT", "DELETE")
|
||
protected.HandleFunc("/", h.handleBucketOperations).Methods("GET", "HEAD", "PUT", "DELETE")
|
||
|
||
// Multipart upload operations - must be registered before generic object operations
|
||
protected.HandleFunc("/{key:.*}", h.handleUploadPart).Methods("PUT").Queries("partNumber", "{partNumber:[0-9]+}", "uploadId", "{uploadId}")
|
||
protected.HandleFunc("/{key:.*}", h.handleMultipartUpload).Methods("POST").Queries("uploads", "")
|
||
protected.HandleFunc("/{key:.*}", h.handleListMultipartUploads).Methods("GET").Queries("uploads", "")
|
||
protected.HandleFunc("/{key:.*}", h.handleListMultipartParts).Methods("GET").Queries("uploadId", "{uploadId}")
|
||
protected.HandleFunc("/{key:.*}", h.handleCompleteMultipartUpload).Methods("POST").Queries("uploadId", "{uploadId}")
|
||
protected.HandleFunc("/{key:.*}", h.handleAbortMultipartUpload).Methods("DELETE").Queries("uploadId", "{uploadId}")
|
||
|
||
// Object operations - must be registered after multipart operations to avoid conflicts
|
||
protected.HandleFunc("/{key:.*}", h.handleObjectOperations).Methods("GET", "HEAD", "PUT", "DELETE")
|
||
|
||
// 添加中间件
|
||
protected.Use(h.accessLogMiddleware)
|
||
protected.Use(middleware.VirtualHost(middleware.VirtualHostConfig{
|
||
Enabled: h.virtualHostEnabled,
|
||
BucketExists: func(name string) bool {
|
||
_, ok := h.bucketManager.GetBucket(name)
|
||
return ok
|
||
},
|
||
}))
|
||
protected.Use(middleware.S3Signature(middleware.S3SignatureConfig{
|
||
Required: h.authRequired,
|
||
Credentials: h.credentials,
|
||
OnError: h.handleAuthError,
|
||
SignatureHost: h.signatureHost,
|
||
}))
|
||
}
|
||
|
||
func (h *S3Handler) loadSettings() handlerSettings {
|
||
if v := h.settings.Load(); v != nil {
|
||
return v.(handlerSettings)
|
||
}
|
||
return handlerSettings{}
|
||
}
|
||
|
||
func (h *S3Handler) virtualHostEnabled() bool {
|
||
return h.loadSettings().virtualHost
|
||
}
|
||
|
||
func (h *S3Handler) authRequired() bool {
|
||
return h.loadSettings().authRequired
|
||
}
|
||
|
||
func (h *S3Handler) credentials() (string, string) {
|
||
s := h.loadSettings()
|
||
return s.accessKey, s.secretKey
|
||
}
|
||
|
||
func (h *S3Handler) proxyModeEnabled() bool {
|
||
return h.loadSettings().proxyMode
|
||
}
|
||
|
||
func (h *S3Handler) signatureHost() string {
|
||
return h.loadSettings().signatureHost
|
||
}
|
||
|
||
func (h *S3Handler) UpdateS3APIConfig(cfg *config.S3APIConfig) {
|
||
if cfg == nil {
|
||
return
|
||
}
|
||
h.settings.Store(handlerSettings{
|
||
accessKey: cfg.AccessKey,
|
||
secretKey: cfg.SecretKey,
|
||
proxyMode: cfg.ProxyMode,
|
||
authRequired: cfg.AuthRequired,
|
||
virtualHost: cfg.VirtualHost,
|
||
signatureHost: cfg.Host,
|
||
})
|
||
}
|