FileCodeBox Go 重写版 v2.5.6(安全审计修复版)
Go 1.27.1 (Gin+GORM) + Vue 3 文件快传服务: - 安全审计全部修复(docs/security-audit-2026-09-05.md): bcrypt 密码哈希与自动升级、presign 直传服务端大小/内容校验、 全局请求体上限、依赖升级(govulncheck 0 命中)、janitor 后台清理、 管理端审计动作落库、/admin CORS 收紧、通知内容白名单净化、 会话默认 7 天、限流缓存故障降级、robots.txt 端点等 - 前端:取件链接复制修复(不再重复拼接提取码)、markdown 净化器加固 - Redis 支持库号(FCB_REDIS_DB / redis://…/db URL) - 文档:docs/api/* 与 openapi.yaml 同步最新行为(robots.txt、 提码 5 位起、chunk 32MiB 上限、admin 审计动作等) 验证:gofmt/go vet/go test 全绿;二进制端到端冒烟通过
This commit is contained in:
Vendored
+159
@@ -0,0 +1,159 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// memoryItem 内存缓存条目。
|
||||
type memoryItem struct {
|
||||
value string
|
||||
expiresAt time.Time // 零值表示不过期
|
||||
}
|
||||
|
||||
// MemoryCache 进程内存缓存实现(单机、无持久化)。
|
||||
type MemoryCache struct {
|
||||
mu sync.RWMutex
|
||||
items map[string]memoryItem
|
||||
done chan struct{}
|
||||
}
|
||||
|
||||
// NewMemory 构造内存缓存,并启动后台过期清理。
|
||||
func NewMemory() *MemoryCache {
|
||||
m := &MemoryCache{
|
||||
items: make(map[string]memoryItem),
|
||||
done: make(chan struct{}),
|
||||
}
|
||||
go m.gcLoop()
|
||||
return m
|
||||
}
|
||||
|
||||
// gcLoop 每分钟清理一次过期键,避免长期运行内存膨胀。
|
||||
func (m *MemoryCache) gcLoop() {
|
||||
ticker := time.NewTicker(time.Minute)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-m.done:
|
||||
return
|
||||
case now := <-ticker.C:
|
||||
m.mu.Lock()
|
||||
for k, item := range m.items {
|
||||
if !item.expiresAt.IsZero() && now.After(item.expiresAt) {
|
||||
delete(m.items, k)
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get 读取键值。
|
||||
func (m *MemoryCache) Get(_ context.Context, key string) (string, error) {
|
||||
m.mu.RLock()
|
||||
item, ok := m.items[key]
|
||||
m.mu.RUnlock()
|
||||
if !ok {
|
||||
return "", ErrNotFound
|
||||
}
|
||||
if !item.expiresAt.IsZero() && time.Now().After(item.expiresAt) {
|
||||
return "", ErrNotFound
|
||||
}
|
||||
return item.value, nil
|
||||
}
|
||||
|
||||
// Set 写入键值。
|
||||
func (m *MemoryCache) Set(_ context.Context, key, value string, ttl time.Duration) error {
|
||||
item := memoryItem{value: value}
|
||||
if ttl > 0 {
|
||||
item.expiresAt = time.Now().Add(ttl)
|
||||
}
|
||||
m.mu.Lock()
|
||||
m.items[key] = item
|
||||
m.mu.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Delete 删除键。
|
||||
func (m *MemoryCache) Delete(_ context.Context, keys ...string) error {
|
||||
m.mu.Lock()
|
||||
for _, k := range keys {
|
||||
delete(m.items, k)
|
||||
}
|
||||
m.mu.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Exists 判断键是否存在。
|
||||
func (m *MemoryCache) Exists(_ context.Context, key string) (bool, error) {
|
||||
_, err := m.Get(context.Background(), key)
|
||||
return err == nil, nil
|
||||
}
|
||||
|
||||
// Incr 原子自增;首次创建时记录窗口起点(以过期时间体现)。
|
||||
func (m *MemoryCache) Incr(_ context.Context, key string, ttl time.Duration) (int64, error) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
now := time.Now()
|
||||
item, ok := m.items[key]
|
||||
if ok && !item.expiresAt.IsZero() && now.After(item.expiresAt) {
|
||||
// 窗口已过期,重新计数
|
||||
ok = false
|
||||
}
|
||||
var n int64
|
||||
if !ok {
|
||||
n = 1
|
||||
newItem := memoryItem{value: "1"}
|
||||
if ttl > 0 {
|
||||
newItem.expiresAt = now.Add(ttl)
|
||||
}
|
||||
m.items[key] = newItem
|
||||
return n, nil
|
||||
}
|
||||
// 解析现有值
|
||||
for _, c := range item.value {
|
||||
if c < '0' || c > '9' {
|
||||
n = 0
|
||||
break
|
||||
}
|
||||
n = n*10 + int64(c-'0')
|
||||
}
|
||||
n++
|
||||
newItem := memoryItem{value: itoa(n), expiresAt: item.expiresAt}
|
||||
m.items[key] = newItem
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// Close 停止清理协程。
|
||||
func (m *MemoryCache) Close() error {
|
||||
select {
|
||||
case <-m.done:
|
||||
default:
|
||||
close(m.done)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// itoa 简单整数转字符串,避免在锁内依赖 strconv 的额外开销(数值都很小)。
|
||||
func itoa(n int64) string {
|
||||
if n == 0 {
|
||||
return "0"
|
||||
}
|
||||
var buf [20]byte
|
||||
i := len(buf)
|
||||
neg := n < 0
|
||||
if neg {
|
||||
n = -n
|
||||
}
|
||||
for n > 0 {
|
||||
i--
|
||||
buf[i] = byte('0' + n%10)
|
||||
n /= 10
|
||||
}
|
||||
if neg {
|
||||
i--
|
||||
buf[i] = '-'
|
||||
}
|
||||
return string(buf[i:])
|
||||
}
|
||||
Reference in New Issue
Block a user