初始化蓝鸟文件快递柜源码与部署文档
This commit is contained in:
commit
5643d22242
70 files changed
+18669
No files matched your search
@@ -0,0 +1,157 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/pelletier/go-toml/v2"
|
||||
)
|
||||
|
||||
type Policy struct {
|
||||
AnonymousUpload bool `json:"anonymous_upload" toml:"anonymous_upload"`
|
||||
AnonymousDownload bool `json:"anonymous_download" toml:"anonymous_download"`
|
||||
MaxFileBytes int64 `json:"max_file_bytes" toml:"max_file_bytes"`
|
||||
MaxBatchBytes int64 `json:"max_batch_bytes" toml:"max_batch_bytes"`
|
||||
MaxFiles int `json:"max_files" toml:"max_files"`
|
||||
DefaultDays int `json:"default_days" toml:"default_days"`
|
||||
MaxInitialDays int `json:"max_initial_days" toml:"max_initial_days"`
|
||||
MaxLifetimeDays int `json:"max_lifetime_days" toml:"max_lifetime_days"`
|
||||
DefaultDownloads int `json:"default_downloads" toml:"default_downloads"`
|
||||
MaxDownloads int `json:"max_downloads" toml:"max_downloads"`
|
||||
RenewalDays int `json:"renewal_days" toml:"renewal_days"`
|
||||
RenewalMode string `json:"renewal_mode" toml:"renewal_mode"`
|
||||
IPDailyBytes int64 `json:"ip_daily_bytes" toml:"ip_daily_bytes"`
|
||||
IPDailyUploads int `json:"ip_daily_uploads" toml:"ip_daily_uploads"`
|
||||
SessionDailyUploads int `json:"session_daily_uploads" toml:"session_daily_uploads"`
|
||||
RequirePassword bool `json:"require_password" toml:"require_password"`
|
||||
}
|
||||
type Config struct {
|
||||
Listen string `toml:"listen"`
|
||||
PublicURL string `toml:"public_url"`
|
||||
AccessURLs []string `toml:"access_urls"`
|
||||
DataDir string `toml:"data_dir"`
|
||||
SecureCookies bool `toml:"secure_cookies"`
|
||||
AllowLANHTTP bool `toml:"allow_lan_http"`
|
||||
Encryption bool `toml:"encryption"`
|
||||
Deduplication bool `toml:"deduplication"`
|
||||
TrustedProxies []string `toml:"trusted_proxies"`
|
||||
DataBudget int64 `toml:"data_budget"`
|
||||
FileBudget int64 `toml:"file_budget"`
|
||||
MinFreeBytes int64 `toml:"min_free_bytes"`
|
||||
AuditRetentionDays int `toml:"audit_retention_days"`
|
||||
AuditMaxRecords int `toml:"audit_max_records"`
|
||||
AuditMaxBytes int64 `toml:"audit_max_bytes"`
|
||||
MaxUploads int `toml:"max_uploads"`
|
||||
MaxDownloads int `toml:"max_downloads"`
|
||||
MaxIPUploads int `toml:"max_ip_uploads"`
|
||||
MaxIPDownloads int `toml:"max_ip_downloads"`
|
||||
MaxRecords int `toml:"max_records"`
|
||||
TransferIdleSeconds int `toml:"transfer_idle_seconds"`
|
||||
MaxTransferSeconds int `toml:"max_transfer_seconds"`
|
||||
TLSCert string `toml:"tls_cert"`
|
||||
TLSKey string `toml:"tls_key"`
|
||||
Policy Policy `toml:"policy"`
|
||||
}
|
||||
|
||||
func DefaultConfig() Config {
|
||||
return Config{
|
||||
Listen: "127.0.0.1:8080", PublicURL: "http://localhost:8080", DataDir: "var", Deduplication: true,
|
||||
DataBudget: 10 << 30, FileBudget: 9 << 30, MinFreeBytes: 1 << 30, MaxUploads: 8, MaxDownloads: 16, MaxIPUploads: 2, MaxIPDownloads: 4, MaxRecords: 200000, MaxTransferSeconds: 3600, TransferIdleSeconds: 30,
|
||||
AuditRetentionDays: 30, AuditMaxRecords: 100000, AuditMaxBytes: 32 << 20,
|
||||
Policy: Policy{AnonymousUpload: true, AnonymousDownload: true, MaxFileBytes: 100 << 20, MaxBatchBytes: 500 << 20, MaxFiles: 10, DefaultDays: 1, MaxInitialDays: 7, MaxLifetimeDays: 30, DefaultDownloads: 5, MaxDownloads: 20, RenewalDays: 3, RenewalMode: "add", IPDailyBytes: 1 << 30, IPDailyUploads: 20, SessionDailyUploads: 20},
|
||||
}
|
||||
}
|
||||
func (p Policy) Validate() error {
|
||||
if p.MaxBatchBytes > 1<<40 || p.IPDailyBytes > 1<<50 || p.MaxFileBytes <= 0 || p.MaxBatchBytes <= 0 || p.MaxFiles < 1 || p.MaxFiles > 1000 || p.MaxFileBytes > p.MaxBatchBytes || p.DefaultDays < 1 || p.DefaultDays > p.MaxInitialDays || p.MaxInitialDays > p.MaxLifetimeDays || p.MaxLifetimeDays > 3650 || p.DefaultDownloads < 1 || p.DefaultDownloads > p.MaxDownloads || p.MaxDownloads > 100000 || p.RenewalDays < 0 || p.RenewalDays > p.MaxLifetimeDays || p.IPDailyBytes < 1 || p.IPDailyUploads < 1 || p.SessionDailyUploads < 1 {
|
||||
return errors.New("策略参数范围错误")
|
||||
}
|
||||
if p.RenewalMode != "add" && p.RenewalMode != "sliding" {
|
||||
return errors.New("renewal_mode 必须为 add 或 sliding")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func LoadConfig(path string) (Config, error) {
|
||||
c := DefaultConfig()
|
||||
b, e := os.ReadFile(path)
|
||||
if e != nil {
|
||||
return c, e
|
||||
}
|
||||
if e = toml.NewDecoder(bytes.NewReader(b)).DisallowUnknownFields().Decode(&c); e != nil {
|
||||
return c, e
|
||||
}
|
||||
base, e := filepath.Abs(filepath.Dir(path))
|
||||
if e != nil {
|
||||
return c, e
|
||||
}
|
||||
if !filepath.IsAbs(c.DataDir) {
|
||||
c.DataDir = filepath.Join(base, c.DataDir)
|
||||
}
|
||||
for _, ptr := range []*string{&c.TLSCert, &c.TLSKey} {
|
||||
if *ptr != "" && !filepath.IsAbs(*ptr) {
|
||||
*ptr = filepath.Join(base, *ptr)
|
||||
}
|
||||
}
|
||||
return c, c.Validate()
|
||||
}
|
||||
func (c Config) Validate() error {
|
||||
if e := c.InitialRuntimeSettings().Validate(); e != nil {
|
||||
return e
|
||||
}
|
||||
if e := c.Policy.Validate(); e != nil {
|
||||
return e
|
||||
}
|
||||
primary, e := normalizeAccessURL(c.PublicURL)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
u, _ := url.Parse(primary)
|
||||
if len(c.AccessURLs) > 16 {
|
||||
return errors.New("额外访问地址最多16个")
|
||||
}
|
||||
for _, raw := range append([]string{c.PublicURL}, c.AccessURLs...) {
|
||||
base, err := normalizeAccessURL(raw)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
entry, _ := url.Parse(base)
|
||||
ip := net.ParseIP(entry.Hostname())
|
||||
if entry.Scheme == "http" && ip != nil && ip.IsPrivate() && !ip.IsLoopback() && !c.AllowLANHTTP {
|
||||
return errors.New("内网HTTP入口须显式开启 allow_lan_http")
|
||||
}
|
||||
}
|
||||
if u.Scheme == "https" && !c.SecureCookies {
|
||||
return errors.New("HTTPS主入口须设置 secure_cookies = true")
|
||||
}
|
||||
if u.Scheme == "http" && c.SecureCookies {
|
||||
return errors.New("HTTP主入口须设置 secure_cookies = false;额外HTTPS入口自动使用安全cookie")
|
||||
}
|
||||
listenHost, port, err := net.SplitHostPort(c.Listen)
|
||||
if err != nil || port == "" {
|
||||
return errors.New("listen须包含地址和端口")
|
||||
}
|
||||
// Preserve explicit-address protection for a single local/LAN HTTP site.
|
||||
ip := net.ParseIP(u.Hostname())
|
||||
if u.Scheme == "http" && (u.Hostname() == "localhost" || ip != nil && (ip.IsPrivate() || ip.IsLoopback())) {
|
||||
bound := net.ParseIP(listenHost)
|
||||
if listenHost != "localhost" && (bound == nil || !bound.IsPrivate() && !bound.IsLoopback()) {
|
||||
return errors.New("内网HTTP须监听具体私有或回环地址")
|
||||
}
|
||||
}
|
||||
if (c.TLSCert == "") != (c.TLSKey == "") {
|
||||
return errors.New("TLS 证书和私钥需一起配置")
|
||||
}
|
||||
if c.FileBudget <= 0 || c.DataBudget-c.FileBudget < 64<<20 || c.MinFreeBytes < 0 || c.MaxUploads < 1 || c.MaxDownloads < 1 || c.MaxIPUploads < 1 || c.MaxIPDownloads < 1 || c.MaxRecords < 1 || c.MaxTransferSeconds < 1 || c.TransferIdleSeconds < 1 {
|
||||
return fmt.Errorf("容量、并发或传输配置非法")
|
||||
}
|
||||
for _, p := range c.TrustedProxies {
|
||||
if _, _, e := net.ParseCIDR(p); e != nil {
|
||||
return fmt.Errorf("非法代理网段: %s", p)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in new issue
Block a user