Help us shape the future of Go! Take the 2026 Go Developer Survey

gate

package
v0.32.41 Latest Latest
Warning

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

Go to latest
Published: Oct 31, 2026 License: Apache-2.0 Imports: 13 Imported by: 0

Documentation

Overview

Package gate 上传/下载准入闸门。

服务端强制执行 upload.open_upload / upload.require_login / download.require_login (此前三者只透传给前端做 UI 裁剪,后端不校验,形同虚设)。 判定规则:

  • open_upload=false:拒绝全部匿名上传(登录用户不受影响)
  • upload.require_login=true:拒绝匿名上传(含 chunk/presign 等纯匿名通道)
  • download.require_login=true:拒绝匿名取件/下载

pickup.go 取件闸门:过期 → 管控 → 就绪 → 密码/防爆破锁定 的统一判定。

历史:同一条「取件前置校验」在四处各写一份且规格互不一致—— share/select、share/download、anonymous.Retrieve 的锁定在 handler 层 (scope 各为 pickup/download/anon,计数互不相通,同一口令在四个入口 各有独立爆破预算),preview 的锁定在域层且用 FormatLockKey(不做取件码 大小写折叠,存在变体绕过锁定漏洞)。2026-10-10 收口于此:

  • 单一 scope "pickup":四入口共享 (IP, code) 失败计数;
  • 统一 LookupLockKey(折叠开启时大小写变体同键);
  • 判定顺序、锁定检查、失败记账、成功清零只此一份。

域/handler 侧职责不变:把 model.FileCode 映射为 PickupState(pkg 层不得 import repo,架构守卫规则 1),把 PickupVerdict 映射为各自的错误类型与 HTTP 响应形态(select 空密码提示不计数、preview 空密码计数等入口差异经 MissingCountsFailure 表达)。

quota.go 匿名上传 per-IP 日配额(治理 2026-10-03)。

此前配额只对登录用户生效(share.CreateShare → CheckQuota),匿名分片/直传 完全绕过——刷爆存储的主通道。本文件以"自然日"为窗口对匿名来源 IP 计数 (次数 + 字节双维度,任一超限即拒):

  • Redis 可用:INCR + 当日剩余秒数 EXPIRE(多实例共享计数)
  • 无 Redis:进程内 map + 日期轮转(单实例语义,重启清零)

Index

Constants

View Source
const PickupScope = "pickup"

PickupScope 统一锁定 scope:取件/下载/预览/匿名取件四入口共享失败计数。

Variables

View Source
var ErrLoginRequired = newGateErr(errcode.CodeUnauthorized, "该操作需要登录后使用")

ErrLoginRequired 需要登录才能执行该操作。

View Source
var ErrPresignDisabled = newGateErr(errcode.CodePresignDisabled, "匿名直传未开放,文件将改用普通通道上传")

ErrPresignDisabled 匿名直传已被管理员关闭(download.presign_policy=disabled/authenticated)。

View Source
var ErrUploadDisabled = newGateErr(errcode.CodeUploadDisabled, "管理员已关闭匿名上传功能")

ErrUploadDisabled 上传已被管理员关闭(匿名通道)。

Functions

func CheckAnonymousQuota

func CheckAnonymousQuota(ctx context.Context, ip string, addBytes int64) error

CheckAnonymousQuota 匿名上传配额检查并计数(仅对匿名请求调用)。 超限返回 *GateError(业务码 10014);不限或登录请求恒通过。

func CheckDownloadLogin

func CheckDownloadLogin(userID *uint) error

CheckDownloadLogin 取件/下载登录要求(download.require_login)。

func CheckPresignPolicy

func CheckPresignPolicy(userID *uint) error

CheckPresignPolicy 直传策略闸门(download.presign_policy 三档):

  • everyone:所有人可直传
  • authenticated:仅登录用户(匿名 Init 返回 10015,前端回退分片中转)
  • disabled:完全关闭直传(登录用户也拒绝——密钥轮换/通道故障时的总闸)

func CheckUploadAllowed

func CheckUploadAllowed(userID *uint) error

CheckUploadAllowed 匿名上传总开关(upload.open_upload)。 登录用户(userID != nil)不受该开关限制。

func CheckUploadLogin

func CheckUploadLogin(userID *uint) error

CheckUploadLogin 上传登录要求(upload.require_login)。

func CheckUploadSizeCap added in v0.26.0

func CheckUploadSizeCap(userID *uint, totalSize, capSize int64) error

CheckUploadSizeCap 登录用户单次上传总大小预检(capSize<=0 = 不限;userID nil 跳过)。 分片 init / 多文件绑定按申报大小提前调用,把超限拦截在传输与合并落盘之前。

func ErrUploadSizeCap added in v0.26.0

func ErrUploadSizeCap(capBytes int64) error

ErrUploadSizeCap 单用户单次上传总大小超过上限(用户级 max_upload_size 优先, 回退系统默认 user.user_upload_size;匿名用户不受此限,走匿名日配额)。 必须携带 CodeTooLarge 业务码:2026-10-19 事故中 share 域以普通 error 返回, NewTypedError 兜底 CodeInternal,前端显示「服务器内部错误」掩盖真实原因, 用户在 450MB×2 整轮分片传输全部白费后才见到报错。

func PickupLockKey added in v0.15.1

func PickupLockKey(clientIP, code string) string

PickupLockKey 统一锁定键(scope 固定 "pickup";取件码折叠开启时大小写 变体同键,与 LookupLockKey 语义一致)。handler 预检与枚举记账用同一键。

func PickupLockedRemain added in v0.15.1

func PickupLockedRemain(ctx context.Context, clientIP, code string) (int, bool)

PickupLockedRemain 查询统一键的剩余锁定秒数(handler 快速 429 预检用; 未锁定返回 (0, false))。

func QuotaLimits

func QuotaLimits() (count int64, bytes int64)

QuotaLimits 当前生效的匿名日配额(0 = 不限)。

func RecordPickupFailure added in v0.15.1

func RecordPickupFailure(ctx context.Context, clientIP, code string)

RecordPickupFailure 在统一键上记一次失败(供「码不存在」等到达不了 CheckPickup 的路径做枚举防护记账,语义与旧 handler 层记账一致)。

func SetQuotaRedis

func SetQuotaRedis(rdb *redis.Client)

SetQuotaRedis 注入 Redis(bootstrap 调用;nil = 内存模式)。 nil 归一化:typed-nil 接口会骗过内存模式守卫(!= nil 判真)。

Types

type GateError

type GateError struct {
	Code int
	Msg  string
}

GateError 携带业务码的闸门拒绝错误,handler 侧按 ErrCode 透传给前端。

func (*GateError) ErrCode

func (e *GateError) ErrCode() int

ErrCode 实现 app 侧 typed error 约定(同 user.QuotaExceededError)。

func (*GateError) Error

func (e *GateError) Error() string

type PickupCheck added in v0.15.1

type PickupCheck struct {
	ClientIP      string // 可信代理解析后的取件方 IP(锁定键维度)
	Code          string // 取件入口使用的码(分享码或取件码,锁定键维度)
	Password      string
	AuthedByToken bool // 持有效下载令牌视为已认证,跳过密码段(share/download 通道)
	// MissingCountsFailure 空密码是否按失败记账:preview/anonymous/download
	// 为 true(对齐各自历史行为),select 为 false(空密码是"需要密码"提示态)。
	MissingCountsFailure bool
}

PickupCheck 判定入参(HTTP 关注点由适配层提取后传入)。

type PickupDecision added in v0.15.1

type PickupDecision struct {
	Verdict       PickupVerdict
	BlockedStatus string // Verdict==PickupBlocked 时有效
	LockRemain    int    // Verdict==PickupLocked 时有效(秒)
}

PickupDecision CheckPickup 的完整判定。

func CheckPickup added in v0.15.1

func CheckPickup(ctx context.Context, st PickupState, req PickupCheck) PickupDecision

CheckPickup 统一取件闸门。判定顺序固定:过期 → 管控 → 就绪 → 锁定 → 密码;RequireAuth=false 或 AuthedByToken=true 时密码段整体跳过。 密码正确即在统一键清零失败计数;密码错误(含脏数据空哈希)记账一次。

type PickupState added in v0.15.1

type PickupState struct {
	// Expired **只表示时间过期**(次数耗尽在交付面单独判定)。
	// ⚠️ 2026-10-26 审计订正:旧注释写「时间或次数已耗尽」⇒ 会让人照着把
	// 次数语义**合流回这个闸门** ⇒ 刚修好的 20004「超过取件次数」/
	// 20007「取件码已用完」会被压回 404「不存在/已过期」(2026-10-24 那轮
	// 踩过的坑)。⇒ 次数耗尽由 model.IsCountExhausted() 判定,并在**交付面**
	// 返回独立错误码,**不在本闸门判**。
	Expired       bool   // 时间过期(不含次数耗尽)
	Blocked       bool   // 管控拒绝态(禁用/待审核)
	BlockedStatus string // 原始状态串(blocked/pending_review),Blocked=true 时有效
	NotReady      bool   // 登记占位未回填物理文件(匿名取件通道语义)
	RequireAuth   bool   // 密码保护
	PasswordHash  string // bcrypt 哈希(RequireAuth=true 而哈希空 = 历史脏数据,按拒绝处理)
}

PickupState 取件目标分享的状态快照(域侧从 model.FileCode 映射; Blocked 由 IsBlockedShare() 判定,BlockedStatus 仅用于错误映射透传)。

type PickupVerdict added in v0.15.1

type PickupVerdict int

PickupVerdict 闸门判定结果。

const (
	PickupAllowed         PickupVerdict = iota // 放行
	PickupUnavailable                          // 不存在/已过期(各面自行统一 404 语义)
	PickupBlocked                              // 管控拒绝态(携带 BlockedStatus)
	PickupNotReady                             // 未完成上传登记
	PickupLocked                               // 防爆破锁定态(携带 LockRemain 秒)
	PickupPasswordMissing                      // 需要密码但未提供(未记账,入口自行决定提示/计数)
	PickupPasswordWrong                        // 密码错误(已在统一键记账,达阈值触发锁定)
)

Jump to

Keyboard shortcuts

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