e5fa0ae36c
第 2 层网关持久层落地,遵守 sundynix_ 表名前缀 + AutoMigrate 约定。 - store: GORM(NamingStrategy 前缀 sundynix_/单数) → User=sundynix_user, Task=sundynix_task 启动 AutoMigrate;go-redis/v9 滑动窗口限流(Incr+Expire,按 IP) - 优雅降级:连不上库则 warn 继续(不 fatal),保证无 Docker 的 make demo 仍跑通 - handler: SubmitTask 持久化任务(best-effort),Billing 真实读库返回 tasks_submitted - main: OpenPostgres/OpenRedis 读 POSTGRES_DSN/REDIS_ADDR 环境变量 - 验证: 4 模块 build ✓;e2e 3 测试 PASS;live 双路径(真实库持久化 + 坏DSN降级)实测通过 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
35 lines
917 B
Go
35 lines
917 B
Go
// Package middleware 提供 Guardrail 与限流等接入层中间件。
|
|
package middleware
|
|
|
|
import (
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
"github.com/sundynix/sundynix-gateway/internal/store"
|
|
)
|
|
|
|
// Guardrail 实现 Harness 的输入/输出护栏(敏感词、注入、配额校验等)。
|
|
func Guardrail() gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
// TODO: 输入护栏校验
|
|
c.Next()
|
|
// TODO: 输出护栏校验
|
|
}
|
|
}
|
|
|
|
// RateLimit 基于 Redis 的会话级限流(按客户端 IP,每分钟上限)。
|
|
// Redis 降级时 Allow 始终放行,不阻断业务。
|
|
func RateLimit(cache *store.Redis) gin.HandlerFunc {
|
|
const perMinute = 120
|
|
return func(c *gin.Context) {
|
|
ok, _ := cache.Allow(c.Request.Context(), c.ClientIP(), perMinute, time.Minute)
|
|
if !ok {
|
|
c.AbortWithStatusJSON(http.StatusTooManyRequests, gin.H{"error": "rate limit exceeded"})
|
|
return
|
|
}
|
|
c.Next()
|
|
}
|
|
}
|