fix(sharecode): 支持分享码TTL配置并限制每MAC分享码数
- 配置增加 ShareCodeMaxPerMac 和 ShareCodeTTLMin 参数 - ShareCodeCache 支持自定义分享码有效期 TTL - 生成分享码时使用配置的 TTL 替代固定30分钟 - 共享码处理添加限制,同一MAC有效分享码数量限制 - 路由层、处理器层和任务调度中增加TTL及限额参数传递 - 同步脚本默认同步路径调整,去除默认同步Docker文件,增加docs目录同步 - 修正上传脚本拼接远程路径时尾部斜杠问题
This commit is contained in:
Vendored
+9
-8
@@ -51,7 +51,6 @@ const (
|
||||
shareImportFlushLockPref = "share:import:flush:lock:"
|
||||
shareMacIndexPrefix = "share:mac:"
|
||||
shareCodeLength = 5
|
||||
shareCodeTTL = 30 * time.Minute
|
||||
shareMacIndexTTL = 1 * time.Hour
|
||||
shareImportPendingTTL = 12 * time.Hour
|
||||
shareCodeCharset = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ"
|
||||
@@ -101,19 +100,21 @@ type ShareCodeData struct {
|
||||
}
|
||||
|
||||
type ShareCodeCache struct {
|
||||
rdb *redis.Client
|
||||
rdb *redis.Client
|
||||
codeTTL time.Duration
|
||||
}
|
||||
|
||||
func NewShareCodeCache(rdb *redis.Client) *ShareCodeCache {
|
||||
return &ShareCodeCache{rdb: rdb}
|
||||
func NewShareCodeCache(rdb *redis.Client, codeTTL time.Duration) *ShareCodeCache {
|
||||
return &ShareCodeCache{rdb: rdb, codeTTL: codeTTL}
|
||||
}
|
||||
|
||||
func ShareCodeTTL() time.Duration {
|
||||
return shareCodeTTL
|
||||
// ShareCodeTTL returns the configured share code TTL.
|
||||
func (c *ShareCodeCache) ShareCodeTTL() time.Duration {
|
||||
return c.codeTTL
|
||||
}
|
||||
|
||||
func (c *ShareCodeCache) Create(ctx context.Context, macAddr, ipAddr string, eqData []byte) (*ShareCodeData, error) {
|
||||
expireAt := time.Now().Add(shareCodeTTL)
|
||||
expireAt := time.Now().Add(c.codeTTL)
|
||||
eqJSON := string(eqData)
|
||||
|
||||
for i := 0; i < shareCodeMaxRetries; i++ {
|
||||
@@ -125,7 +126,7 @@ func (c *ShareCodeCache) Create(ctx context.Context, macAddr, ipAddr string, eqD
|
||||
key := shareCodeKey(code)
|
||||
macIdxKey := shareMacIndexKey(macAddr)
|
||||
ok, err := shareCreateScript.Run(ctx, c.rdb, []string{key, sharePendingSet, macIdxKey},
|
||||
macAddr, ipAddr, eqJSON, expireAt.Format(time.RFC3339), int(shareCodeTTL.Seconds()), code, expireAt.Unix(), int(shareMacIndexTTL.Seconds()),
|
||||
macAddr, ipAddr, eqJSON, expireAt.Format(time.RFC3339), int(c.codeTTL.Seconds()), code, expireAt.Unix(), int(shareMacIndexTTL.Seconds()),
|
||||
).Int()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("create share code in redis: %w", err)
|
||||
|
||||
+34
-18
@@ -7,15 +7,17 @@ import (
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Env string
|
||||
Host string
|
||||
Port int
|
||||
Database DatabaseConfig
|
||||
Meilisearch MeilisearchConfig
|
||||
Redis RedisConfig
|
||||
Equalize EqualizeConfig
|
||||
S3 S3Config
|
||||
EnablePersistTask bool
|
||||
Env string
|
||||
Host string
|
||||
Port int
|
||||
Database DatabaseConfig
|
||||
Meilisearch MeilisearchConfig
|
||||
Redis RedisConfig
|
||||
Equalize EqualizeConfig
|
||||
S3 S3Config
|
||||
EnablePersistTask bool
|
||||
ShareCodeMaxPerMac int
|
||||
ShareCodeTTLMin int
|
||||
}
|
||||
|
||||
func Load() (*Config, error) {
|
||||
@@ -48,15 +50,17 @@ func Load() (*Config, error) {
|
||||
s3cfg := loadS3(env)
|
||||
|
||||
return &Config{
|
||||
Env: env,
|
||||
Host: getEnv("APP_HOST", "0.0.0.0"),
|
||||
Port: port,
|
||||
Database: db,
|
||||
Meilisearch: ms,
|
||||
Redis: rd,
|
||||
Equalize: eq,
|
||||
S3: s3cfg,
|
||||
EnablePersistTask: getEnv("ENABLE_PERSIST_TASK", "false") == "true",
|
||||
Env: env,
|
||||
Host: getEnv("APP_HOST", "0.0.0.0"),
|
||||
Port: port,
|
||||
Database: db,
|
||||
Meilisearch: ms,
|
||||
Redis: rd,
|
||||
Equalize: eq,
|
||||
S3: s3cfg,
|
||||
EnablePersistTask: getEnv("ENABLE_PERSIST_TASK", "false") == "true",
|
||||
ShareCodeMaxPerMac: getEnvInt("SHARE_CODE_MAX_PER_MAC", 1),
|
||||
ShareCodeTTLMin: getEnvInt("SHARE_CODE_TTL_MIN", 30),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -70,3 +74,15 @@ func getEnv(key, fallback string) string {
|
||||
}
|
||||
return fallback
|
||||
}
|
||||
|
||||
func getEnvInt(key string, fallback int) int {
|
||||
v := os.Getenv(key)
|
||||
if v == "" {
|
||||
return fallback
|
||||
}
|
||||
n, err := strconv.Atoi(v)
|
||||
if err != nil {
|
||||
return fallback
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
@@ -12,12 +12,13 @@ import (
|
||||
)
|
||||
|
||||
type ShareCodeHandler struct {
|
||||
cache *cache.ShareCodeCache
|
||||
log *zap.Logger
|
||||
cache *cache.ShareCodeCache
|
||||
log *zap.Logger
|
||||
maxPerMac int
|
||||
}
|
||||
|
||||
func NewShareCodeHandler(shareCache *cache.ShareCodeCache, log *zap.Logger) *ShareCodeHandler {
|
||||
return &ShareCodeHandler{cache: shareCache, log: log}
|
||||
func NewShareCodeHandler(shareCache *cache.ShareCodeCache, log *zap.Logger, maxPerMac int) *ShareCodeHandler {
|
||||
return &ShareCodeHandler{cache: shareCache, log: log, maxPerMac: maxPerMac}
|
||||
}
|
||||
|
||||
// ExportShareCode 导出分享码
|
||||
@@ -69,6 +70,25 @@ func (h *ShareCodeHandler) ExportShareCode(c *gin.Context) {
|
||||
}
|
||||
|
||||
ctx := c.Request.Context()
|
||||
|
||||
// 限制同一 MAC 在有效期内的分享码数量
|
||||
existing, err := h.cache.ListByMac(ctx, mac)
|
||||
if err != nil {
|
||||
h.log.Error("check existing share codes by mac failed", zap.String("mac", mac), zap.Error(err))
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 500,
|
||||
"msg": "系统错误",
|
||||
})
|
||||
return
|
||||
}
|
||||
if len(existing) >= h.maxPerMac {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"msg": "已有未过期的分享码,请稍后再试",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
data, err := h.cache.Create(ctx, mac, clientIP, []byte(eqDataRaw))
|
||||
if err != nil {
|
||||
h.log.Error("create share code failed", zap.Error(err))
|
||||
|
||||
@@ -2,6 +2,7 @@ package router
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/luxsin/app-api/internal/cache"
|
||||
@@ -18,7 +19,7 @@ import (
|
||||
ginSwagger "github.com/swaggo/gin-swagger"
|
||||
)
|
||||
|
||||
func New(log *zap.Logger, db *sql.DB, searchClient *search.Client, rdb *redis.Client, eqCfg config.EqualizeConfig, s3 *storage.S3Storage) *gin.Engine {
|
||||
func New(log *zap.Logger, db *sql.DB, searchClient *search.Client, rdb *redis.Client, eqCfg config.EqualizeConfig, s3 *storage.S3Storage, shareCodeMaxPerMac int, shareCodeTTL time.Duration) *gin.Engine {
|
||||
r := gin.New()
|
||||
r.Use(gin.Recovery())
|
||||
r.Use(middleware.RequestID())
|
||||
@@ -36,7 +37,7 @@ func New(log *zap.Logger, db *sql.DB, searchClient *search.Client, rdb *redis.Cl
|
||||
otaRepo := repository.NewOTARepository(db)
|
||||
curveRepo := repository.NewCurveRepository(db)
|
||||
|
||||
shareCodeCache := cache.NewShareCodeCache(rdb)
|
||||
shareCodeCache := cache.NewShareCodeCache(rdb, shareCodeTTL)
|
||||
|
||||
// Handler
|
||||
health := handler.NewHealthHandler()
|
||||
@@ -47,7 +48,7 @@ func New(log *zap.Logger, db *sql.DB, searchClient *search.Client, rdb *redis.Cl
|
||||
ota := handler.NewOTAHandler(otaRepo, log)
|
||||
curve := handler.NewCurveHandler(curveRepo, curveCache, eqCfg, s3, log)
|
||||
modelCSV := handler.NewModelCSVHandler(s3, log)
|
||||
shareCode := handler.NewShareCodeHandler(shareCodeCache, log)
|
||||
shareCode := handler.NewShareCodeHandler(shareCodeCache, log, shareCodeMaxPerMac)
|
||||
|
||||
v1 := r.Group("/api/v1")
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user