初始提交:一场soul的创业实验-永平 网站与小程序

Made-with: Cursor
This commit is contained in:
卡若
2026-03-07 22:58:43 +08:00
commit b7c35a89b0
513 changed files with 89020 additions and 0 deletions

View File

@@ -0,0 +1,44 @@
package middleware
import (
"net/http"
"soul-api/internal/auth"
"soul-api/internal/config"
"github.com/gin-gonic/gin"
)
const adminClaimsKey = "admin_claims"
// AdminAuth 管理端鉴权:校验 JWTAuthorization: Bearer 或 Cookie admin_session未登录返回 401通过则设置 admin_claims 到 context
func AdminAuth() gin.HandlerFunc {
return func(c *gin.Context) {
cfg := config.Get()
if cfg == nil {
c.Next()
return
}
token := auth.GetAdminJWTFromRequest(c.Request)
claims, ok := auth.ParseAdminJWT(token, cfg.AdminSessionSecret)
if !ok {
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"success": false, "error": "未授权访问,请先登录"})
return
}
c.Set(adminClaimsKey, claims)
c.Next()
}
}
// GetAdminClaims 从 context 获取 admin claims需在 AdminAuth 之后调用)
func GetAdminClaims(c *gin.Context) *auth.AdminClaims {
v, ok := c.Get(adminClaimsKey)
if !ok || v == nil {
return nil
}
claims, ok := v.(*auth.AdminClaims)
if !ok {
return nil
}
return claims
}

View File

@@ -0,0 +1,65 @@
package middleware
import (
"net/http"
"sync"
"time"
"golang.org/x/time/rate"
"github.com/gin-gonic/gin"
)
// RateLimiter 按 IP 的限流器
type RateLimiter struct {
mu sync.Mutex
clients map[string]*rate.Limiter
r rate.Limit
b int
}
// NewRateLimiter 创建限流中间件r 每秒请求数b 突发容量
func NewRateLimiter(r rate.Limit, b int) *RateLimiter {
return &RateLimiter{
clients: make(map[string]*rate.Limiter),
r: r,
b: b,
}
}
// getLimiter 获取或创建该 key 的 limiter
func (rl *RateLimiter) getLimiter(key string) *rate.Limiter {
rl.mu.Lock()
defer rl.mu.Unlock()
if lim, ok := rl.clients[key]; ok {
return lim
}
lim := rate.NewLimiter(rl.r, rl.b)
rl.clients[key] = lim
return lim
}
// Middleware 返回 Gin 限流中间件(按客户端 IP
func (rl *RateLimiter) Middleware() gin.HandlerFunc {
return func(c *gin.Context) {
key := c.ClientIP()
lim := rl.getLimiter(key)
if !lim.Allow() {
c.AbortWithStatus(http.StatusTooManyRequests)
return
}
c.Next()
}
}
// Cleanup 定期清理过期 limiter可选避免 map 无限增长)
func (rl *RateLimiter) Cleanup(interval time.Duration) {
ticker := time.NewTicker(interval)
go func() {
for range ticker.C {
rl.mu.Lock()
rl.clients = make(map[string]*rate.Limiter)
rl.mu.Unlock()
}
}()
}

View File

@@ -0,0 +1,25 @@
package middleware
import (
"github.com/gin-gonic/gin"
"github.com/unrolled/secure"
)
// Secure 安全响应头中间件
func Secure() gin.HandlerFunc {
s := secure.New(secure.Options{
FrameDeny: true,
ContentTypeNosniff: true,
BrowserXssFilter: true,
ContentSecurityPolicy: "frame-ancestors 'none'",
ReferrerPolicy: "no-referrer",
})
return func(c *gin.Context) {
err := s.Process(c.Writer, c.Request)
if err != nil {
c.Abort()
return
}
c.Next()
}
}