server

package
v0.0.0-...-399ff89 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 21, 2026 License: Apache-2.0 Imports: 48 Imported by: 0

Documentation

Index

Constants

View Source
const (
	// PermissionRead 表示只读权限。
	PermissionRead = "read"
	// PermissionWrite 表示读写权限。
	PermissionWrite = "write"
)

Variables

View Source
var ErrStorageFull = errors.New("storage quota exceeded")

ErrStorageFull 存储空间已满,拒绝写入。

Functions

func CORSMiddleware

func CORSMiddleware(cfg CORSConfig, logger *slog.Logger) func(http.Handler) http.Handler

CORSMiddleware 返回一个 HTTP 中间件,根据配置添加 CORS 头部并处理 OPTIONS 预检请求。 支持的方法:GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS。 当 AllowedOrigins 为空时直接透传,保持向后兼容。

func Checksum

func Checksum(src io.Reader) (string, error)

Checksum 计算 src 的 SHA-256 十六进制摘要。 注意:调用方负责关闭 src 如果它实现了 io.Closer(如 os.File)。 返回的 hex 字符串均为小写字符。

func FileChecksum

func FileChecksum(filename string) (string, error)

FileChecksum 计算文件的 SHA-256 十六进制摘要。 调用方需确保 filename 已通过 ValidateFilePath 校验,防止路径穿越。

func GzipMiddleware

func GzipMiddleware(logger *slog.Logger) func(http.Handler) http.Handler

GzipMiddleware 返回一个 HTTP 中间件,对客户端支持 gzip 的所有响应体进行 gzip 压缩。 注意:内容类型不限于文本;对所有 Accept-Encoding 包含 gzip 的请求均压缩。 注意:gzipResponseWriter 未实现 http.Hijacker。如果后续需要与支持劫持的 Handler(如隧道/tunnel handler) 配合使用,应重写该中间件使其在劫持场景下跳过 gzip 压缩。

func IsPathWithin

func IsPathWithin(child, parent string) bool

IsPathWithin 检查 child 路径是否在 parent 目录内(路径穿越防护)。 使用 filepath.Clean 标准化后通过前缀匹配判断,确保 parent 以分隔符结尾避免误判(如 /a/b 和 /a/bb)。 注意:

  • 此函数仅验证路径包含关系,不保证文件实际存在。
  • 不处理符号链接解析:如果 child 或 parent 包含符号链接,应在外层调用 filepath.EvalSymlinks 后再传入。

func MissingChunks

func MissingChunks(session *ChunkedUploadSession) []int

MissingChunks 返回缺失的分块索引列表。

func SaveConfig

func SaveConfig(cfg *Config, path string) error

func SetResponseLogger

func SetResponseLogger(l *slog.Logger)

SetResponseLogger 设置 sendJSONResponse 使用的日志记录器。

func ValidateFilePath

func ValidateFilePath(filename string) (string, error)

ValidateFilePath 校验并规范化用户提供的文件路径(可能包含子目录)。 返回使用平台分隔符的清洗后相对路径,或描述性错误。

规则:

  • 拒绝空字符串
  • 拒绝空字节(\x00)
  • 拒绝绝对路径(以 / 或 \ 开头)
  • filepath.Clean 规范化
  • 逐组件检查 ".."(路径穿越)
  • Windows 上检查 <>:"|?* 非法字符
  • 返回路径为 filepath.ToSlash 格式(使用 / 分隔符),适合作为 API 返回值

Types

type ACMEConfig

type ACMEConfig struct {
	Enabled    bool     `yaml:"enabled"`
	Domains    []string `yaml:"domains"`
	Email      string   `yaml:"email"`
	CacheDir   string   `yaml:"cache_dir"`
	HTTP01     bool     `yaml:"http01"`
	HTTP01Port string   `yaml:"http01_port"`
}

ACMEConfig 是 ACME 自动证书的配置。

type APIKey

type APIKey struct {
	Name       string `yaml:"name" mapstructure:"name"`
	Key        string `yaml:"key" mapstructure:"key"`
	Permission string `yaml:"permission" mapstructure:"permission"` // "read" 或 "write";空字符串默认按 "write" 处理
}

APIKey 表示一个 API 密钥及其权限。

type APIKeyConfig

type APIKeyConfig struct {
	Enabled bool     `yaml:"enabled" mapstructure:"enabled"`
	Keys    []APIKey `yaml:"keys" mapstructure:"keys"`
}

APIKeyConfig 多用户 API 密钥配置。

type ArchiveRequest

type ArchiveRequest struct {
	Files []string `json:"files"`
}

ArchiveRequest 是 POST /api/archive 的请求体。

type BatchDeleteFile

type BatchDeleteFile struct {
	Filename string `json:"filename"`
	Checksum string `json:"checksum"`
}

BatchDeleteFile 批量删除中的单条文件

type BatchDeleteRequest

type BatchDeleteRequest struct {
	Files []BatchDeleteFile `json:"files"`
}

BatchOperationRequest 批量删除请求体

type BatchOperationResult

type BatchOperationResult struct {
	Filename string `json:"filename"`
	Success  bool   `json:"success"`
	Message  string `json:"message"`
}

BatchOperationResult 批量操作单条结果

type BatchRenameOp

type BatchRenameOp struct {
	From     string `json:"from"`
	To       string `json:"to"`
	Checksum string `json:"checksum"`
}

BatchRenameOp 单条重命名操作

type BatchRenameRequest

type BatchRenameRequest struct {
	Operations []BatchRenameOp `json:"operations"`
}

BatchRenameRequest 批量重命名请求体

type BatchResponse

type BatchResponse struct {
	Results []BatchOperationResult `json:"results"`
}

BatchResponse is the JSON response for batch operations (delete, rename, etc.).

type CORSConfig

type CORSConfig struct {
	// AllowedOrigins 允许的跨域来源列表,设置 ["*"] 允许任意来源。
	// 为空时 CORS 中间件直接透传(保持向后兼容)。
	AllowedOrigins []string `yaml:"allowed_origins" mapstructure:"allowed_origins"`
	// AllowedHeaders 允许的请求头列表,为空时使用默认值。
	AllowedHeaders []string `yaml:"allowed_headers" mapstructure:"allowed_headers"`
	// MaxAge 预检请求缓存时间(秒),默认 86400。
	MaxAge int `yaml:"max_age" mapstructure:"max_age"`
}

CORSConfig 定义 CORS 跨域配置。

type ChecksumStore

type ChecksumStore struct {
	// contains filtered or unexported fields
}

ChecksumStore 在 uploads 目录下维护一个 .checksums.json 侧边文件, 持久化每个文件的 SHA-256 摘要,供 upload/download/delete 操作复用。

func NewChecksumStore

func NewChecksumStore(uploadsDir string, logger *slog.Logger) *ChecksumStore

NewChecksumStore 创建 ChecksumStore,从 uploadsDir/.checksums.json 加载已有记录。 同时清理可能由上次进程崩溃残留的 .checksums.json.tmp 文件。

func (*ChecksumStore) Delete

func (cs *ChecksumStore) Delete(filename string)

Delete 删除指定文件的 checksum 记录并持久化。

func (*ChecksumStore) DeletePrefix

func (cs *ChecksumStore) DeletePrefix(prefix string)

DeletePrefix 删除指定前缀的所有 checksum 记录(用于目录删除)。

func (*ChecksumStore) Get

func (cs *ChecksumStore) Get(filename string) (string, bool)

Get 查询指定文件的 checksum。

func (*ChecksumStore) GetAll

func (cs *ChecksumStore) GetAll() map[string]string

GetAll 返回全部 checksum 记录的副本(filename -> sha256)。

func (*ChecksumStore) Rename

func (cs *ChecksumStore) Rename(from, to string)

Rename 将一条 checksum 记录从 from 路径迁移到 to 路径并持久化。 如果 to 已存在则被覆盖(与 os.Rename 行为对齐)。 注意:save() 内部会获取 cs.mu.RLock(),因此必须在调用 save() 前释放写锁。

func (*ChecksumStore) Set

func (cs *ChecksumStore) Set(filename, checksum string)

Set 写入一条 checksum 记录并持久化到磁盘。

type ChecksumStoreIface

type ChecksumStoreIface interface {
	Get(filename string) (string, bool)
	Set(filename, checksum string)
	Delete(filename string)
	Rename(from, to string)
	DeletePrefix(prefix string)
	GetAll() map[string]string
}

ChecksumStoreIface 定义 ChecksumStore 的业务接口,方便测试替身。

type ChunkCompleteResponse

type ChunkCompleteResponse struct {
	Success      bool   `json:"success"`
	Filename     string `json:"filename,omitempty"`
	FileChecksum string `json:"file_checksum,omitempty"`
	Message      string `json:"message,omitempty"`
}

ChunkCompleteResponse 分块上传合并完成响应。

type ChunkFileLocker

type ChunkFileLocker struct {
	// contains filtered or unexported fields
}

ChunkFileLocker 管理分块文件的并发读写锁。 提取为独立导出类型,使 UploadStore 和 MockUploadStore 共享同一份真实锁定逻辑。

func NewChunkFileLocker

func NewChunkFileLocker() *ChunkFileLocker

NewChunkFileLocker 创建一个新的 ChunkFileLocker。

func (*ChunkFileLocker) DeleteLock

func (l *ChunkFileLocker) DeleteLock(uploadID string)

DeleteLock 删除指定 uploadID 的锁条目,防止内存泄漏。

func (*ChunkFileLocker) LockChunkIO

func (l *ChunkFileLocker) LockChunkIO(uploadID string) func()

LockChunkIO 获取 chunk 文件写入锁(读锁)。 uploadChunk 在写入 chunk 文件前调用,允许多个 uploadChunk 并发写入不同 chunk。

func (*ChunkFileLocker) LockChunkMerge

func (l *ChunkFileLocker) LockChunkMerge(uploadID string) func()

LockChunkMerge 获取 chunk 文件合并锁(写锁)。 mergeOneChunk 在读取 chunk 文件前调用,排他地等待所有正在写入的 chunk 完成后才允许读取, 同时阻塞新的 chunk 写入,避免读到不完整的 chunk。

type ChunkStatusResponse

type ChunkStatusResponse struct {
	Success       bool   `json:"success"`
	UploadID      string `json:"upload_id,omitempty"`
	ReceivedCount int    `json:"received_count,omitempty"`
	TotalChunks   int    `json:"total_chunks,omitempty"`
	MissingChunks []int  `json:"missing_chunks,omitempty"`
	Completed     bool   `json:"completed,omitempty"`
	FileChecksum  string `json:"file_checksum,omitempty"`
	Filename      string `json:"filename,omitempty"`
	Message       string `json:"message,omitempty"`
}

ChunkStatusResponse 分块上传状态查询响应。

type ChunkUploadResponse

type ChunkUploadResponse struct {
	Success     bool   `json:"success"`
	ChunkIndex  int    `json:"chunk_index"`
	ShouldRetry bool   `json:"should_retry,omitempty"`
	Message     string `json:"message,omitempty"`
}

ChunkUploadResponse 单块上传响应。

type ChunkedInitResponse

type ChunkedInitResponse struct {
	Success   bool   `json:"success"`
	UploadID  string `json:"upload_id,omitempty"`
	ChunkSize int64  `json:"chunk_size,omitempty"`
	Message   string `json:"message,omitempty"`
}

ChunkedInitResponse 分块上传初始化响应。

type ChunkedUploadSession

type ChunkedUploadSession struct {
	UploadID       string    `json:"upload_id"`
	Filename       string    `json:"filename"`
	TotalSize      int64     `json:"total_size"`
	ChunkSize      int64     `json:"chunk_size"`
	TotalChunks    int       `json:"total_chunks"`
	ReceivedChunks []bool    `json:"received_chunks"`
	ChunkChecksums []string  `json:"chunk_checksums"`
	FileChecksum   string    `json:"file_checksum"`
	FileModTime    int64     `json:"file_mod_time"` // UnixNano, 0 = unknown
	CreatedAt      time.Time `json:"created_at"`
	ExpiresAt      time.Time `json:"expires_at"`
	Completed      bool      `json:"completed"`
}

ChunkedUploadSession 表示一个分块上传会话。

type CloudArchiveBatchRequest

type CloudArchiveBatchRequest struct {
	TaskIDs     []string `json:"task_ids"`
	ArchiveName string   `json:"archive_name,omitempty"`
}

CloudArchiveBatchRequest 是 POST /api/cloud/archive 的请求体。

type CloudArchiveRequest

type CloudArchiveRequest struct {
	ArchiveName string `json:"archive_name,omitempty"`
}

CloudArchiveRequest 是 POST /api/cloud/tasks/{id}/archive 的请求体。

type CloudArchiveResult

type CloudArchiveResult struct {
	Success      bool     `json:"success"`
	Message      string   `json:"message,omitempty"`
	File         string   `json:"file,omitempty"`
	Size         int64    `json:"size,omitempty"`
	Checksum     string   `json:"checksum,omitempty"`
	TaskCount    int      `json:"task_count,omitempty"`
	SkippedCount int      `json:"skipped_count,omitempty"`
	SkippedTasks []string `json:"skipped_tasks,omitempty"`
}

CloudArchiveResult 是归档操作响应结构体。

type CloudBatchTaskResult

type CloudBatchTaskResult struct {
	ID       string `json:"id"`
	URL      string `json:"url"`
	Filename string `json:"filename"`
	Status   string `json:"status"`
	Error    string `json:"error,omitempty"`
}

CloudBatchTaskResult 批量下载单个任务结果。

type CloudDownloadConfig

type CloudDownloadConfig struct {
	SyncThreshold   int64         // 同步模式阈值(字节),默认 20 MiB
	MaxConcurrent   int           // 最大并发下载数,默认 3
	MaxBatchURLs    int           // 批量/组下载单次最大 URL 数,默认 100;0 使用默认值
	TaskTTL         time.Duration // 完成任务保留时间,默认 24h
	FailedTaskTTL   time.Duration // 失败任务保留时间,默认 1h
	AllowPrivate    bool          // 允许私有 IP 下载(仅测试用)
	DownloadTimeout time.Duration // 单次下载尝试整体超时,默认 30m;0 表示不限制
	IdleTimeout     time.Duration // 响应体读取空闲超时,默认 60s;0 表示不限制
	MaxRetries      int           // 失败重试次数,默认 10
	RetryDelay      time.Duration // 重试间隔,默认 10s
	Downloader      string        // 下载器名称,默认 "http"(配置 cloud_downloader 后生效)
}

CloudDownloadConfig 云端下载配置。

type CloudDownloadManager

type CloudDownloadManager struct {
	// contains filtered or unexported fields
}

CloudDownloadManager 管理云端下载任务。

func NewCloudDownloadManager

func NewCloudDownloadManager(uploadsDir string, sm *StorageManager, cs ChecksumStoreIface, logger *slog.Logger, cfg *CloudDownloadConfig) *CloudDownloadManager

NewCloudDownloadManager 创建云端下载管理器。

func (*CloudDownloadManager) CancelGroup

func (m *CloudDownloadManager) CancelGroup(groupID string) error

CancelGroup 取消组内所有 pending/downloading 任务(已完成任务跳过)。

func (*CloudDownloadManager) CancelTask

func (m *CloudDownloadManager) CancelTask(id string) error

CancelTask 取消正在进行的任务。

func (*CloudDownloadManager) Close

func (m *CloudDownloadManager) Close()

Close 停止所有后台 goroutine(flushLoop 和 cleanupExpired)并等待下载完成。 在进程退出前应调用一次。多次调用安全。 注意:优雅关闭不取消进行中的下载任务——下载 goroutine 在进程退出时自然终止, .partial 文件保留,重启后通过 recoverTasks 恢复并通过 Range 续传继续。 wg.Wait 最多等待 30 秒,超时后返回(防止下载 goroutine 卡在 I/O 上永久阻塞)。

func (*CloudDownloadManager) CreateGroup

func (m *CloudDownloadManager) CreateGroup(name string, urls []cloudfilename.Entry) (*CloudTaskGroup, error)

CreateGroup 创建下载任务组。 校验文件名冲突,创建子任务。

func (*CloudDownloadManager) CreateTask

func (m *CloudDownloadManager) CreateTask(method, url, filename string, totalSize int64) (*CloudTask, error)

CreateTask 创建云端下载任务(不启动下载)。 自动去重:相同 URL 的 pending/downloading 任务返回已有任务。

func (*CloudDownloadManager) DeleteGroup

func (m *CloudDownloadManager) DeleteGroup(groupID string) error

DeleteGroup 删除组记录及所有子任务。

func (*CloudDownloadManager) DeleteTask

func (m *CloudDownloadManager) DeleteTask(id string) error

DeleteTask 删除任务及其云端文件。

func (*CloudDownloadManager) FlushNow

func (m *CloudDownloadManager) FlushNow()

FlushNow 立即触发一次批量持久化(测试用)。

func (*CloudDownloadManager) GetGroup

func (m *CloudDownloadManager) GetGroup(id string) (*CloudTaskGroup, bool)

GetGroup 获取组详情。

func (*CloudDownloadManager) GetTask

func (m *CloudDownloadManager) GetTask(id string) (*CloudTask, bool)

GetTask 返回任务的快照(副本),避免并发修改导致 data race。

func (*CloudDownloadManager) ListGroups

func (m *CloudDownloadManager) ListGroups(status string, offset, limit int) ([]*CloudTaskGroup, int)

ListGroups 列出组,支持按 status 过滤与 offset/limit 分页。 offset<0 时不偏移;limit<=0 时返回全部(兼容现有语义)。 排序:CreatedAt 降序 + ID 降序 tie-break(同 ListTasks 注释,确定性排序)。 total 为按 status 过滤后的组总数(不受分页影响)。

func (*CloudDownloadManager) ListTasks

func (m *CloudDownloadManager) ListTasks(status string, offset, limit int) ([]*CloudTask, int)

ListTasks 列出任务,支持按 status 过滤与 offset/limit 分页。 offset<0 时不偏移;limit<=0 时返回全部(兼容现有语义)。 排序:CreatedAt 降序 + ID 降序 tie-break。ID 含随机 4 字节 hex,CreatedAt 相等时按 随机值排序,但排序本身确定(同输入同输出),分页跨页仍稳定——仅同纳秒创建的任务 顺序不代表创建序,属可接受(创建时间戳通常唯一)。 total 为按 status 过滤后的任务总数(不受分页影响)。

func (*CloudDownloadManager) ResumeGroup

func (m *CloudDownloadManager) ResumeGroup(groupID string, force bool) error

ResumeGroup 恢复组内所有失败/取消任务。

func (*CloudDownloadManager) ResumeTask

func (m *CloudDownloadManager) ResumeTask(taskID string, force bool) error

ResumeTask 恢复失败的下载任务。 force=true 时删除已有部分文件重新下载;force=false 时保留 .partial 由下载器 通过 Range 续传(不再改名成 destPath,避免续传退化为全量下载)。

func (*CloudDownloadManager) SetGroupArchiveFile

func (m *CloudDownloadManager) SetGroupArchiveFile(groupID, archiveFile string)

SetGroupArchiveFile 记录组的归档文件路径并持久化。

func (*CloudDownloadManager) SnapshotTask

func (m *CloudDownloadManager) SnapshotTask(id string) (*CloudTask, bool)

SnapshotTask 返回任务的快照(副本),避免并发修改导致 data race。

func (*CloudDownloadManager) SubmitAndStart

func (m *CloudDownloadManager) SubmitAndStart(method, url, filename string, totalSize int64, syncCtx context.Context) (*CloudTask, error)

SubmitAndStart 创建任务并立即启动下载。 仅当调用方已知 totalSize > 0 且 < syncThreshold 且 syncCtx 非 nil 时才同步执行 (在调用方 goroutine 内完成,便于小文件请求同步返回);否则始终异步。 注意:服务端 handler 提交时大小未知(传 -1),因此实际请求恒异步; 同步路径主要供调用方在已知小文件大小时使用。

func (*CloudDownloadManager) SubmitAndStartGroup

func (m *CloudDownloadManager) SubmitAndStartGroup(name string, urls []cloudfilename.Entry) (*CloudTaskGroup, error)

SubmitAndStartGroup 创建组并启动所有子任务下载。

func (*CloudDownloadManager) UpdateGroupStatus

func (m *CloudDownloadManager) UpdateGroupStatus(groupID string)

UpdateGroupStatus 根据子任务状态更新组状态(导出方法,供 handler 调用)。

type CloudMetrics

type CloudMetrics struct {
	TasksCreated    atomic.Int64 // 创建的任务总数
	TasksCompleted  atomic.Int64 // 完成的任务数
	TasksFailed     atomic.Int64 // 失败的任务数
	TasksCancelled  atomic.Int64 // 取消的任务数
	TasksRetried    atomic.Int64 // 重试的任务数
	BytesDownloaded atomic.Int64 // 云端下载总字节数
	ActiveDownloads atomic.Int64 // 当前活跃下载数
}

CloudMetrics 云端下载 Prometheus 指标。

type CloudTask

type CloudTask struct {
	ID           string    `json:"id"`
	URL          string    `json:"url"`
	Method       string    `json:"method"`     // "url" | "upload"
	Filename     string    `json:"filename"`   // 云端存储文件名
	Status       string    `json:"status"`     // pending | downloading | completed | failed | cancelled
	TotalSize    int64     `json:"total_size"` // -1 表示未知
	Downloaded   int64     `json:"downloaded"`
	Checksum     string    `json:"checksum"`
	ETag         string    `json:"etag,omitempty"`       // 服务端 ETag,用于版本标识与二次校验(可能为空)
	FileMTime    int64     `json:"file_mtime,omitempty"` // 原始文件修改时间(UnixNano),从 URL 的 Last-Modified 提取
	Error        string    `json:"error"`
	CreatedAt    time.Time `json:"created_at"`
	UpdatedAt    time.Time `json:"updated_at"`
	ExpiresAt    time.Time `json:"expires_at"`
	ReservedSize int64     `json:"-"`                  // 实际预留量,不持久化
	GroupID      string    `json:"group_id,omitempty"` // 所属组 ID(可选)
}

CloudTask 表示一个云端下载任务。

type CloudTaskGroup

type CloudTaskGroup struct {
	ID          string    `json:"id"`
	Name        string    `json:"name"`
	Status      string    `json:"status"` // downloading | completed | failed | cancelled
	TaskIDs     []string  `json:"task_ids"`
	TotalTasks  int       `json:"total_tasks"`
	Completed   int       `json:"completed"`
	Failed      int       `json:"failed"`
	Cancelled   int       `json:"cancelled"`
	Error       string    `json:"error,omitempty"`
	ArchiveFile string    `json:"archive_file,omitempty"`
	CreatedAt   time.Time `json:"created_at"`
	UpdatedAt   time.Time `json:"updated_at"`
	ExpiresAt   time.Time `json:"expires_at"`
}

CloudTaskGroup 表示一个云端下载任务组。 每个子任务仍是独立的 CloudTask(文件保存在 .__cloud__/<taskID>/ 下), 组只负责聚合元数据与组级操作(归档/取消/恢复)。

type Config

type Config struct {
	Addr       string `yaml:"addr" mapstructure:"addr"`
	UploadsDir string `yaml:"uploads_dir" mapstructure:"uploads_dir"`
	// MaxUploadBytes 已移至 internal/size.UploadBodyLimit(1 GiB 硬限制),不可配置。
	// MaxChunkUploadBytes 已移至 internal/size.DefaultChunkBodyLimit(64 MiB 硬限制),不可配置。
	ServerTimeouts ServerTimeouts  `yaml:"server_timeouts" mapstructure:"server_timeouts"`
	LogLevel       string          `yaml:"log_level" mapstructure:"log_level"`
	LogFormat      string          `yaml:"log_format" mapstructure:"log_format"`
	MaxHeaderBytes int             `yaml:"max_header_bytes" mapstructure:"max_header_bytes"`
	TunnelKey      string          `yaml:"tunnel_key" mapstructure:"tunnel_key"`
	TLS            TLSConfig       `yaml:"tls" mapstructure:"tls"`
	AuthToken      string          `yaml:"auth_token" mapstructure:"auth_token"`
	RateLimit      RateLimitConfig `yaml:"rate_limit" mapstructure:"rate_limit"`
	CORS           CORSConfig      `yaml:"cors" mapstructure:"cors"`

	// 分块上传配置
	ChunkSize        int64         `yaml:"chunk_size" mapstructure:"chunk_size"`
	UploadSessionTTL time.Duration `yaml:"upload_session_ttl" mapstructure:"upload_session_ttl"`

	// 文件版本管理(默认关闭)
	Versioning VersionConfig `yaml:"versioning" mapstructure:"versioning"`

	// API 密钥配置
	APIKeys APIKeyConfig `yaml:"api_keys" mapstructure:"api_keys"`

	// Hub 中继系统(默认关闭)
	Hub HubConfig `yaml:"hub" mapstructure:"hub"`

	// 存储空间控制
	MaxStorageBytes int64 `yaml:"max_storage_bytes" mapstructure:"max_storage_bytes"` // 存储上限(字节),0 = 不限制

	// 云端下载配置
	CloudSyncThreshold        int64         `yaml:"cloud_sync_threshold" mapstructure:"cloud_sync_threshold"`
	CloudDownloader           string        `yaml:"cloud_downloader" mapstructure:"cloud_downloader"`
	CloudTaskTTL              time.Duration `yaml:"cloud_task_ttl" mapstructure:"cloud_task_ttl"`
	CloudFailedTaskTTL        time.Duration `yaml:"cloud_failed_task_ttl" mapstructure:"cloud_failed_task_ttl"`
	CloudMaxConcurrent        int           `yaml:"cloud_max_concurrent" mapstructure:"cloud_max_concurrent"`
	CloudMaxBatchURLs         int           `yaml:"cloud_max_batch_urls" mapstructure:"cloud_max_batch_urls"`
	CloudDownloadAllowPrivate bool          `yaml:"cloud_download_allow_private" mapstructure:"cloud_download_allow_private"`
	CloudDownloadTimeout      time.Duration `yaml:"cloud_download_timeout" mapstructure:"cloud_download_timeout"`
	CloudDownloadIdleTimeout  time.Duration `yaml:"cloud_download_idle_timeout" mapstructure:"cloud_download_idle_timeout"`
	CloudMaxRetries           int           `yaml:"cloud_max_retries" mapstructure:"cloud_max_retries"`
	CloudRetryDelay           time.Duration `yaml:"cloud_retry_delay" mapstructure:"cloud_retry_delay"`
	// CloudArchiveMaxBytes 单次云归档允许的最大字节数(原始文件大小总和),0 = 不限制(仍受 max_storage_bytes 与 TryReserve 兜底)。
	CloudArchiveMaxBytes int64 `yaml:"cloud_archive_max_bytes" mapstructure:"cloud_archive_max_bytes"`
}

func Default

func Default() *Config

func LoadConfig

func LoadConfig(path string) (*Config, error)

LoadConfig 加载配置文件。路径为空或文件不存在时返回默认配置,不自动创建文件。

func LoadFromProvider

func LoadFromProvider(p provider.Provider) (*Config, error)

LoadFromProvider 从 provider.Provider 解码配置,设置默认值并校验。

func (*Config) SetDefaults

func (c *Config) SetDefaults()

SetDefaults 设置零值字段为默认值。

func (*Config) Validate

func (c *Config) Validate() error

Validate 校验配置合理性。

type DiskUsageStats

type DiskUsageStats struct {
	UploadsDir string `json:"uploads_dir"`
	TotalFiles int    `json:"total_files"`
	TotalSize  int64  `json:"total_size"`
}

DiskUsageStats 磁盘使用统计。

type Handlers

type Handlers struct {
	// contains filtered or unexported fields
}

Handlers 持有所有 HTTP handler 的依赖。

func RegisterRoutes

func RegisterRoutes(ctx context.Context, opts RegisterRoutesOpts) *Handlers

RegisterRoutes 将所有 HTTP 路由注册到 mux 上,并返回 *Handlers。 调用方应在进程退出前调用 (*Handlers).Close() 以释放后台 goroutine 与持久化资源。

func (*Handlers) Close

func (h *Handlers) Close() error

Close 释放 Handlers 持有的后台资源:停止 UploadStore 的 persist/cleanup goroutine 和 StorageManager 的定期扫描。 在进程退出前应调用一次(通常通过 defer h.Close())。多次调用是安全的。 关闭顺序:先关 uploadingFiles 清理 goroutine,再关 UploadStore(后者可能还有 uploading 操作引用其 session)。 TODO: 当前始终返回 nil;后续可收集各子组件关闭的错误,合并后返回。

func (*Handlers) Handler

func (h *Handlers) Handler() http.Handler

Handler 返回包装了 metricsMiddleware 的 HTTP handler,用于 http.Server.Handler。

func (*Handlers) MetricsHandler

func (h *Handlers) MetricsHandler(w http.ResponseWriter, r *http.Request)

MetricsHandler 返回 GET /metrics 的 HTTP handler。 使用 Prometheus 文本格式(仅标准库,无依赖)。

func (*Handlers) TunnelHandler

func (h *Handlers) TunnelHandler() http.Handler

TunnelHandler 返回隧道处理器,用于 SIGHUP 时热替换密钥。

type HubConfig

type HubConfig struct {
	Enabled    bool             `yaml:"enabled"`
	NodeID     string           `yaml:"node_id"`
	RelayToken string           `yaml:"relay_token"`
	Transports TransportConfigs `yaml:"transports"`
}

HubConfig 配置 Hub 中继系统。

type Metrics

type Metrics struct {
	RequestsTotal     atomic.Int64
	Requests2XX       atomic.Int64
	Requests4XX       atomic.Int64
	Requests5XX       atomic.Int64
	BytesUploaded     atomic.Int64
	BytesDownloaded   atomic.Int64
	ActiveConnections atomic.Int64
	FilesUploaded     atomic.Int64
	FilesDownloaded   atomic.Int64
	FilesDeleted      atomic.Int64
}

Metrics 使用 atomic 计数器收集请求统计数据。 注意:Go 1.22+ 的 atomic.Int64 自动处理对齐,无需手动对齐。

func NewMetrics

func NewMetrics() *Metrics

NewMetrics 创建并初始化 Metrics。

func (*Metrics) RecordDelete

func (m *Metrics) RecordDelete()

RecordDelete 记录删除。

func (*Metrics) RecordDownload

func (m *Metrics) RecordDownload(bytes int64)

RecordDownload 记录下载字节数和文件数。

func (*Metrics) RecordRequest

func (m *Metrics) RecordRequest(statusCode int)

RecordRequest 根据状态码记录一次请求。

func (*Metrics) RecordUpload

func (m *Metrics) RecordUpload(bytes int64)

RecordUpload 记录上传字节数和文件数。

func (*Metrics) Snapshot

func (m *Metrics) Snapshot() map[string]int64

Snapshot 返回当前所有指标的快照(用于调试和日志输出)。

type RateLimitConfig

type RateLimitConfig struct {
	Enabled  bool          `yaml:"enabled"`
	Requests int           `yaml:"requests"`
	Window   time.Duration `yaml:"window"`
}

type RateLimiter

type RateLimiter struct {
	// contains filtered or unexported fields
}

RateLimiter implements a sliding-window rate limiter using only the stdlib. Thread-safe via sync.Mutex.

当前实现为全局限流(全局单实例)+ 每 IP 令牌桶限流。 每个客户端 IP 获得 limit/10 的令牌桶配额,先检查 per-IP 令牌桶, 配额耗尽后回退到全局滑动窗口。

func NewRateLimiter

func NewRateLimiter(limit int, window time.Duration, logger *slog.Logger) *RateLimiter

NewRateLimiter creates a RateLimiter allowing up to `limit` requests per sliding `window` duration.

func (*RateLimiter) Allow

func (rl *RateLimiter) Allow() bool

Allow reports whether the current request is within the global rate limit. 不使用 per-IP 限流。

func (*RateLimiter) AllowIP

func (rl *RateLimiter) AllowIP(ip string) bool

AllowIP 检查请求是否在限流范围内,优先使用 per-IP 令牌桶, 配额耗尽后回退到全局滑动窗口。

func (*RateLimiter) Middleware

func (rl *RateLimiter) Middleware(next http.Handler) http.Handler

Middleware wraps an http.Handler with rate limiting. 使用 per-IP 令牌桶 + 全局限流。 When the limit is exceeded, it responds with 429 Too Many Requests (JSON).

type RegisterRoutesOpts

type RegisterRoutesOpts struct {
	Mux        *http.ServeMux
	CfgPtr     *atomic.Pointer[Config]
	Version    string
	BuildAt    string
	TunnelKey  []byte
	Logger     *slog.Logger
	RouteTable *hub.RouteTable
}

RegisterRoutesOpts 是 RegisterRoutes 的选项参数结构体。

type RelayHandler

type RelayHandler struct {
	// contains filtered or unexported fields
}

RelayHandler 通过 hub 路由表转发请求到目标节点。 使用 Tunnel 帧协议与目标节点的 Tunnel.Serve 通信。

func NewRelayHandler

func NewRelayHandler(rt *hub.RouteTable, logger *slog.Logger) *RelayHandler

NewRelayHandler 创建中继处理器。

func (*RelayHandler) ServeHTTP

func (h *RelayHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP 处理中继请求:解析 JSON,查找目标节点,转发 HTTP 请求。

type RelayRequest

type RelayRequest struct {
	Target     string            `json:"target"`
	Method     string            `json:"method"`
	Path       string            `json:"path"`
	Headers    map[string]string `json:"headers"`
	BodyBase64 string            `json:"body_base64"`
}

RelayRequest 是中继请求的 JSON 格式。

type RelayResponse

type RelayResponse struct {
	Status       int             `json:"status"`
	Headers      http.Header     `json:"headers"`
	Body         json.RawMessage `json:"body"`
	BodyIsBase64 bool            `json:"body_is_base64"`
	Error        string          `json:"error,omitempty"`
}

RelayResponse 是中继响应的 JSON 格式。

type RequestCounts

type RequestCounts struct {
	Total     int64 `json:"total"`
	Status2xx int64 `json:"2xx"`
	Status4xx int64 `json:"4xx"`
	Status5xx int64 `json:"5xx"`
}

RequestCounts 请求计数统计。

type ServerTimeouts

type ServerTimeouts struct {
	ReadHeader time.Duration `yaml:"read_header"`
	Read       time.Duration `yaml:"read"`
	Write      time.Duration `yaml:"write"`
	Idle       time.Duration `yaml:"idle"`
	Shutdown   time.Duration `yaml:"shutdown"`
}

type ShareCreateResponse

type ShareCreateResponse struct {
	Success      bool   `json:"success"`
	Token        string `json:"token,omitempty"`
	Filename     string `json:"filename,omitempty"`
	CreatedAt    string `json:"created_at,omitempty"`
	ExpiresAt    string `json:"expires_at,omitempty"`
	MaxDownloads int    `json:"max_downloads,omitempty"`
	OneTime      bool   `json:"one_time,omitempty"`
	Message      string `json:"message,omitempty"`
}

ShareCreateResponse 创建/撤销分享链接的响应结构体。

type ShareLink struct {
	Token        string    `json:"token"`
	Filename     string    `json:"filename"`
	AbsPath      string    `json:"-"` // 创建时解析的绝对路径
	CreatedAt    time.Time `json:"created_at"`
	ExpiresAt    time.Time `json:"expires_at"`
	MaxDownloads int       `json:"max_downloads"` // 0 = 不限
	Downloads    int       `json:"downloads"`
	OneTime      bool      `json:"one_time"`
}

ShareLink 表示一个文件分享链接。

type ShareStore

type ShareStore struct {
	// contains filtered or unexported fields
}

ShareStore 管理内存中的分享链接。

func NewShareStore

func NewShareStore(logger *slog.Logger) *ShareStore

NewShareStore 创建 ShareStore 实例。

func (*ShareStore) Consume

func (s *ShareStore) Consume(token string) *ShareLink

Consume 原子性地检查并消费一个分享链接。 返回链接信息供后续使用,如果链接无效则返回 nil。

func (*ShareStore) Create

func (s *ShareStore) Create(filename, absPath string, ttl time.Duration, maxDownloads int, oneTime bool) (*ShareLink, error)

Create 生成新的分享链接并存储。

func (*ShareStore) List

func (s *ShareStore) List() []*ShareLink

List 返回所有分享链接的副本。

func (*ShareStore) Peek

func (s *ShareStore) Peek(token string) *ShareLink

Peek 返回指定 token 的分享链接副本,不修改状态(不计数、不删除)。

func (*ShareStore) Revoke

func (s *ShareStore) Revoke(token string) error

Revoke 删除指定 token 的分享链接。链接不存在时返回 error。

func (*ShareStore) Stop

func (s *ShareStore) Stop()

Stop 停止后台清理 goroutine。等待清理 goroutine 退出后返回。

type StatsResponse

type StatsResponse struct {
	DiskUsage       DiskUsageStats `json:"disk_usage"`
	RequestCounts   RequestCounts  `json:"request_counts"`
	ActiveConns     int64          `json:"active_connections"`
	FilesUploaded   int64          `json:"files_uploaded"`
	FilesDownloaded int64          `json:"files_downloaded"`
	FilesDeleted    int64          `json:"files_deleted"`
	BytesUploaded   int64          `json:"bytes_uploaded"`
	BytesDownloaded int64          `json:"bytes_downloaded"`

	// 存储空间统计
	MaxStorageBytes  int64      `json:"max_storage_bytes"`
	StorageUsage     int64      `json:"storage_usage"`
	StorageUserFiles int64      `json:"storage_user_files"`
	StorageChunked   int64      `json:"storage_chunked"`
	StorageVersions  int64      `json:"storage_versions"`
	StorageCloud     int64      `json:"storage_cloud"`
	ScannedAt        *time.Time `json:"scanned_at"`

	// 磁盘统计
	DiskTotal int64 `json:"disk_total"`
	DiskFree  int64 `json:"disk_free"`
	DiskUsed  int64 `json:"disk_used"`
}

StatsResponse 是 GET /api/stats 的响应体。

type StorageCategory

type StorageCategory int

StorageCategory 表示存储空间分类。

const (
	CategoryUserFiles StorageCategory = iota
	CategoryChunked
	CategoryVersions
	CategoryCloud
)

type StorageManager

type StorageManager struct {
	// contains filtered or unexported fields
}

StorageManager 管理上传目录的存储空间使用情况。 通过原子计数器跟踪各分类和总使用量,支持配置上限和运行时调整。

func NewStorageManager

func NewStorageManager(dir string, maxBytes int64, _ ChecksumStoreIface, logger *slog.Logger) *StorageManager

NewStorageManager 创建存储管理器,启动时自动扫描目录统计大小。

func (*StorageManager) Clear

func (s *StorageManager) Clear()

Clear 重置所有计数器为零。仅用于测试。

func (*StorageManager) FileCount

func (s *StorageManager) FileCount() int

FileCount 返回当前已扫描的用户文件数量(不含内部目录)。 由 ScanAndRecalculate 在每次全量扫描时更新。

func (*StorageManager) LastScanTime

func (s *StorageManager) LastScanTime() *time.Time

LastScanTime 返回最近一次全量扫描完成时间。

func (*StorageManager) MaxBytes

func (s *StorageManager) MaxBytes() int64

MaxBytes 返回当前存储上限。

func (*StorageManager) Release

func (s *StorageManager) Release(size int64, cat StorageCategory)

Release 释放已占用的空间。

func (*StorageManager) ScanAndRecalculate

func (s *StorageManager) ScanAndRecalculate() error

ScanAndRecalculate 全量扫描上传目录,重新统计各分类文件大小和用户文件数量。

func (*StorageManager) SetMaxBytes

func (s *StorageManager) SetMaxBytes(n int64)

SetMaxBytes 运行时动态调整存储上限。

func (*StorageManager) Stop

func (s *StorageManager) Stop()

func (*StorageManager) TryReserve

func (s *StorageManager) TryReserve(size int64, cat StorageCategory) error

TryReserve 原子检查并预留空间。成功时累加对应分类和总使用量。 返回 ErrStorageFull 表示超出上限;maxBytes=0 时不限制。

func (*StorageManager) Usage

func (s *StorageManager) Usage() int64

Usage 返回当前总使用量。

func (*StorageManager) UsageByCategory

func (s *StorageManager) UsageByCategory() map[StorageCategory]int64

UsageByCategory 返回各分类的使用量。

type TLSConfig

type TLSConfig struct {
	Enabled  bool       `yaml:"enabled"`
	CertFile string     `yaml:"cert_file"`
	KeyFile  string     `yaml:"key_file"`
	AutoTLS  bool       `yaml:"auto_tls"`
	ClientCA string     `yaml:"client_ca"` // mTLS: CA 证书路径,非空时启用客户端证书验证
	ACME     ACMEConfig `yaml:"acme"`      // ACME 自动证书配置(可选)
}

TLSConfig 是 TLS 相关配置,支持三种证书模式:

  • CertFile + KeyFile:静态文件证书(最高优先级)
  • ACME.Enabled:ACME 自动证书
  • AutoTLS:自签证书(默认 fallback)

type TransportConfigs

type TransportConfigs struct {
	WS WSTransportConfig `yaml:"ws"` // 预留:WebSocket 传输监听配置
}

TransportConfigs 聚合所有可用的传输层配置,当前为预留扩展,暂无产品代码消费。

type TunnelUpdater

type TunnelUpdater interface {
	UpdateKey(key []byte)
}

TunnelUpdater 是隧道处理器密钥热替换接口。 cmd/sproxy 的 SIGHUP 处理流程通过此接口在运行时替换隧道密钥。

type UploadResponse

type UploadResponse struct {
	Success  bool   `json:"success"`
	Message  string `json:"message"`
	Checksum string `json:"file_checksum,omitempty"`
}

UploadResponse 是通用响应结构。

type UploadStore

type UploadStore struct {
	// contains filtered or unexported fields
}

UploadStore 管理分块上传会话的持久化与并发安全。

func MustNewUploadStore

func MustNewUploadStore(baseDir string, sessionTTL time.Duration, logger *slog.Logger) *UploadStore

MustNewUploadStore 创建 UploadStore,失败时 panic。 仅用于 handlers.go 等无法优雅处理错误的位置。

func NewUploadStore

func NewUploadStore(baseDir string, sessionTTL time.Duration, logger *slog.Logger) (*UploadStore, error)

NewUploadStore 创建并启动 UploadStore,同时从磁盘恢复已有 session。 sessionTTL 指定未完成上传会话的过期时间,默认 24h。

func (*UploadStore) AllChunksReceived

func (us *UploadStore) AllChunksReceived(uploadID string) bool

AllChunksReceived 检查是否所有分块都已接收。

func (*UploadStore) ChunkFilePath

func (us *UploadStore) ChunkFilePath(uploadID string, chunkIndex int) string

ChunkFilePath 返回指定分块的文件路径。

func (*UploadStore) CleanupSessionAfter

func (us *UploadStore) CleanupSessionAfter(uploadID string, delay time.Duration)

CleanupSessionAfter 在指定延迟后清理 session 目录。 受 UploadStore.wg 追踪,支持通过 stopCh 提前中止。

func (*UploadStore) CompleteSession

func (us *UploadStore) CompleteSession(uploadID string) error

CompleteSession 标记会话为已完成。

func (*UploadStore) CreateSession

func (us *UploadStore) CreateSession(uploadID, filename string, totalSize, chunkSize int64, totalChunks int, fileChecksum string, fileModTime int64) (*ChunkedUploadSession, error)

CreateSession 创建一个新的分块上传会话,使用客户端提供的 uploadID。

func (*UploadStore) DeleteSession

func (us *UploadStore) DeleteSession(uploadID string)

DeleteSession 删除会话目录及所有分块文件,并清理 fileLocks 条目防止内存泄漏。

func (*UploadStore) GetOrCreateSession

func (us *UploadStore) GetOrCreateSession(uploadID, filename string, totalSize, chunkSize int64, totalChunks int, fileChecksum string, fileModTime int64) (*ChunkedUploadSession, bool, error)

GetOrCreateSession 根据 uploadID 或文件名查找已有未完成的 session,或创建新 session。

func (*UploadStore) GetSession

func (us *UploadStore) GetSession(uploadID string) *ChunkedUploadSession

GetSession 返回指定 upload_id 的会话副本。

func (*UploadStore) GetSessionByFilename

func (us *UploadStore) GetSessionByFilename(filename string) *ChunkedUploadSession

GetSessionByFilename 按文件名查找未完成的 session。

func (*UploadStore) Health

func (us *UploadStore) Health() error

Health 返回 UploadStore 的健康状态。 检查后台 goroutine 是否仍在运行。

func (*UploadStore) LockChunkIO

func (us *UploadStore) LockChunkIO(uploadID string) func()

LockChunkIO 获取 chunk 文件写入锁(读锁)。 uploadChunk 在写入 chunk 文件前调用,允许多个 uploadChunk 并发写入不同 chunk。

func (*UploadStore) LockChunkMerge

func (us *UploadStore) LockChunkMerge(uploadID string) func()

LockChunkMerge 获取 chunk 文件合并锁(写锁)。 mergeOneChunk 在读取 chunk 文件前调用,排他地等待所有正在写入的 chunk 完成后才允许读取, 同时阻塞新的 chunk 写入,避免读到不完整的 chunk。

func (*UploadStore) MarkChunkReceived

func (us *UploadStore) MarkChunkReceived(uploadID string, chunkIndex int, checksum string) error

MarkChunkReceived 标记指定分块为已接收并持久化。

func (*UploadStore) SessionDir

func (us *UploadStore) SessionDir(uploadID string) string

SessionDir 返回会话目录路径。

func (*UploadStore) Stop

func (us *UploadStore) Stop()

Stop 停止后台 goroutine 并等待结束。

优雅停止流程(draining):

  1. 关闭 stopCh 通知 cleanupLoop 和 fallback goroutine 退出,同时阻止新的持久化请求
  2. 关闭 persistCh(不再接受新请求)
  3. 排空 persistCh:处理所有已入列的持久化请求
  4. 等待 wg 完成

多次调用是安全的(幂等)。

type UploadStoreIface

type UploadStoreIface interface {
	Health() error
	Stop()
	CreateSession(uploadID, filename string, totalSize, chunkSize int64, totalChunks int, fileChecksum string, fileModTime int64) (*ChunkedUploadSession, error)
	GetSession(uploadID string) *ChunkedUploadSession
	GetSessionByFilename(filename string) *ChunkedUploadSession
	MarkChunkReceived(uploadID string, chunkIndex int, checksum string) error
	AllChunksReceived(uploadID string) bool
	CompleteSession(uploadID string) error
	ChunkFilePath(uploadID string, chunkIndex int) string
	SessionDir(uploadID string) string
	DeleteSession(uploadID string)
	CleanupSessionAfter(uploadID string, delay time.Duration)
	GetOrCreateSession(uploadID, filename string, totalSize, chunkSize int64, totalChunks int, fileChecksum string, fileModTime int64) (*ChunkedUploadSession, bool, error)
	LockChunkIO(uploadID string) func()
	LockChunkMerge(uploadID string) func()
}

UploadStoreIface 定义 UploadStore 的业务接口,方便测试替身。

type VersionConfig

type VersionConfig struct {
	Enabled     bool `yaml:"enabled" mapstructure:"enabled"`
	MaxVersions int  `yaml:"max_versions" mapstructure:"max_versions"`
}

type VersionInfo

type VersionInfo struct {
	Filename  string `json:"filename"`
	VersionID int64  `json:"version_id"` // UnixNano timestamp
	Size      int64  `json:"size"`
	Checksum  string `json:"checksum,omitempty"`
	CreatedAt string `json:"created_at"`
}

VersionInfo 版本信息。

type WSTransportConfig

type WSTransportConfig struct {
	Enabled bool   `yaml:"enabled"`
	Listen  string `yaml:"listen"`
	Path    string `yaml:"path"`
}

WSTransportConfig 配置 WebSocket 传输监听,当前为预留扩展,暂无产品代码消费。

Directories

Path Synopsis
Package downloader 提供云端下载插件框架。
Package downloader 提供云端下载插件框架。

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL