Initial commit

This commit is contained in:
2026-05-20 16:13:58 +08:00
commit ceffbe382e
500 changed files with 157469 additions and 0 deletions
+142
View File
@@ -0,0 +1,142 @@
package auth
import (
"fmt"
"net/http"
"stat/common/enum/consts"
"stat/common/utils"
"stat/middleware/define"
"stat/models/adminMod"
"stat/service/agentSer"
"strings"
"github.com/gin-gonic/gin"
)
// gin 验证中间件
var (
// 白名单,不需要验证token的api
whiteList = []string{
"/swagger/*",
"/swagger",
"/favicon.ico",
"/api/stat/admin/login",
"/api/stat/admin/verify",
"/api/stat/agent/imgVerify",
"/api/stat/agent/login",
"/api/stat/agent/verify",
"/api/stat/billserver/callback",
"/api/stat/version/bottonclick",
"/api/stat/version/bottonlist",
"/api/stat/version/crossbottonclick",
"/api/stat/share/list",
"/api/stat/ads/get",
"/api/stat/ads/click",
"/api/stat/commodity/info",
"/api/stat/commodity/rechargeType",
"/api/stat/commodity/buy",
"/api/stat/commodity/order",
"/api/stat/version/app",
"/api/stat/userInvite/userlist",
"/api/stat/userInvite/incomelist",
"/api/stat/userInvite/district",
"/api/stat/user/districtcode",
"/api/stat/callback/rchg/bill",
"/api/stat/blocDailyStatTotal/list",
"/api/stat/adminbloc/login",
"/api/stat/channelCodeStat/list",
"/api/stat/loufeng/sync",
"/api/stat/districtMinuteStat/list",
"/api/stat/loufeng/uploadImg",
"/api/stat/loufeng/complaint", //楼凤投诉
"/api/stat/model/sync", //嫩模同步
"/api/stat/novel/sync",
"/api/stat/novel/audio/sync",
"/api/stat/file/upload",
"/api/stat/model/syncBroker",
"/api/stat/loufeng/complaint",
"/api/stat/loufeng/queryComplaint",
"/api/stat/openinstall/auth",
"/api/stat/openinstall/total",
"/api/stat/openinstall/info",
"/api/stat/openinstall/growth",
"/api/stat/openinstall/allstat",
"/api/stat/openinstall/active",
"/api/stat/activity/h5/info",
"/api/stat/activity/h5/giftinfo",
"/api/stat/activity/h5/draw",
"/api/stat/landPageConfig/getByType",
"/api/stat/media/m3u8",
"/api/stat/media/enkey",
"/api/stat/version/h5/config",
"/api/stat/version/channel/newLink",
"/api/stat/inner/botStatList",
"/api/stat/inner/appList",
"/api/stat/inner/appDownload",
"/api/stat/inner/botMonthStat",
"/api/stat/inner/sendTgMessage",
"/api/stat/activity/visit",
"/api/stat/landPage/summary/downTotal/list",
"/api/stat/web/apps/get",
"/api/stat/web/apps/click",
"/api/stat/inner/recharge/list",
"/api/stat/inner/rechargebotcmd/list",
"/api/stat/daily-stat/jtspV2",
"/api/stat/daily-stat/hour",
"/api/stat/daily-stat/rank",
"/api/stat/daily-stat/total",
}
)
func Auth(ctx *gin.Context) {
for _, w := range whiteList {
if strings.HasPrefix(ctx.Request.URL.Path, w) {
return
}
}
authToken := func() (int, error) {
checkToken := ctx.Request.Header.Get("Authorization")
if checkToken == "" {
return http.StatusBadRequest, define.NoToken
}
claims, err := define.ParseToken(checkToken)
if err != nil {
return http.StatusMovedPermanently, define.RepeatLogin
}
if claims.Type == define.Admin {
has, err := adminMod.CheckToken(ctx, checkToken)
if err != nil {
return http.StatusBadRequest, err
}
if !has {
return http.StatusMovedPermanently, define.RepeatLogin
}
} else if claims.Type == define.Channel {
//fmt.Println("", claims.Type, claims.ShowMagic)
if claims.ShowMagic > "3" {
return consts.ShowMagic, nil
}
err := agentSer.CheckToken(ctx, claims.Name, checkToken)
if err != nil {
return http.StatusBadRequest, err
}
}
ctx.Set(utils.AdminAccountKey, claims.Name)
ctx.Set(utils.AccountTypeKey, claims.Type)
return 0, nil
}
statusCode, err := authToken()
if err != nil || statusCode != 0 {
if statusCode == consts.ShowMagic {
ctx.AbortWithStatusJSON(200, gin.H{})
} else {
ctx.AbortWithStatusJSON(statusCode, gin.H{"msg": fmt.Sprintf("%+v", err)})
}
}
}
+136
View File
@@ -0,0 +1,136 @@
package cors
import (
"net/http"
"strconv"
"strings"
"github.com/gin-gonic/gin"
)
type Options struct {
Origin *[]string //传入空指针,表示允许"*", 传入空数组表示禁止跨域
Methods []string //Methods
AllowedHeaders []string //AllowHeaders
ExposedHeaders []string //exposeHeader
Credentials bool //cookie
MaxAge int64 //缓存MaxAge
PreflightContinue bool //在遇到Options的时候,继续,而不是返回请求
OptionsSuccessStatus int //Options时,返回的状态码,默认204
}
func configureOrigin(o *Options, c *gin.Context) map[string]string {
reqOrigin := c.GetHeader("Origin")
headers := make(map[string]string)
if o.Origin == nil {
headers["Access-Control-Allow-Origin"] = "*"
} else {
isAllowed := false
for _, allowed := range *o.Origin {
if allowed == reqOrigin {
isAllowed = true
break
}
}
if isAllowed {
headers["Access-Control-Allow-Origin"] = reqOrigin
} else {
headers["Access-Control-Allow-Origin"] = strconv.FormatBool(false)
}
headers["Vary"] = "Origin"
}
return headers
}
func configureMethods(o *Options) map[string]string {
headers := make(map[string]string)
if len(o.Methods) > 0 {
headers["Access-Control-Allow-Methods"] = strings.ToUpper(strings.Join(o.Methods, ","))
}
return headers
}
func configureCredentials(o *Options) map[string]string {
headers := make(map[string]string)
if o.Credentials {
headers["Access-Control-Allow-Credentials"] = strconv.FormatBool(true)
}
return headers
}
func configureAllowedHeaders(o *Options, c *gin.Context) map[string]string {
headers := make(map[string]string)
if len(o.AllowedHeaders) > 0 {
headers["Access-Control-Allow-Headers"] = strings.Join(o.AllowedHeaders, ",")
}
return headers
}
func configureExposedHeaders(o *Options) map[string]string {
var headers = make(map[string]string)
if len(o.ExposedHeaders) > 0 {
headers["Access-Control-Expose-Headers"] = strings.Join(o.ExposedHeaders, ",")
}
return headers
}
func configureMaxAge(o *Options) map[string]string {
var headers = make(map[string]string)
if o.MaxAge >= 0 {
headers["Access-Control-Max-Age"] = strconv.FormatInt(o.MaxAge, 10)
}
return headers
}
func mergeMap(m ...map[string]string) map[string]string {
r := make(map[string]string)
for _, t := range m {
for k, v := range t {
r[k] = v
}
}
return r
}
func applyHeader(c *gin.Context, h map[string]string) {
for k, v := range h {
c.Header(k, v)
}
}
//Cors 跨域处理
func Cors(o *Options) gin.HandlerFunc {
if o.OptionsSuccessStatus == 0 {
o.OptionsSuccessStatus = http.StatusNoContent
}
return func(c *gin.Context) {
method := c.Request.Method
//c.Header("Access-Control-Allow-Origin", "*")
//c.Header("Access-Control-Allow-Headers", "Content-Type,AccessToken,X-CSRF-Token,Authorization,Token,Content-Length,Etag,Content-Range,Accept-Ranges,User-Agent,Range,Bucket,Content-Disposition,Signature,X-Forwarded-For,X-Real-Ip")
if method == "OPTIONS" {
h := mergeMap(
configureOrigin(o, c),
configureCredentials(o),
configureMethods(o),
configureAllowedHeaders(o, c),
configureMaxAge(o),
configureExposedHeaders(o),
)
applyHeader(c, h)
if o.PreflightContinue {
c.Next()
} else {
c.Header("Content-Length", "0")
c.AbortWithStatus(o.OptionsSuccessStatus)
}
} else {
h := mergeMap(
configureOrigin(o, c),
configureCredentials(o),
configureExposedHeaders(o),
)
applyHeader(c, h)
c.Next()
}
}
}
+69
View File
@@ -0,0 +1,69 @@
package define
import (
"fmt"
"stat/common/log"
"stat/common/utils"
"stat/statg"
"time"
)
type ActType = string
const (
Admin ActType = "admin"
Channel ActType = "channel"
District ActType = "district"
ActTypeNone ActType = ""
)
type Claims struct {
Type ActType
Name string `json:"name"`
Role string `json:"role"`
CID string `json:"cid"`
ShowMagic string `json:"showMagic"`
}
type claimsWithExp struct {
Claims
Exp int64 `json:"exp"`
}
var (
NoToken = fmt.Errorf("no token")
RepeatLogin = fmt.Errorf("repeat login")
)
const (
WebTokenExpire = 10 * 24 * time.Hour
)
func GetTokenSecret() string {
return statg.Conf.JwtKey
}
func GenToken(claims *Claims) (string, error) {
c := claimsWithExp{Claims: *claims, Exp: time.Now().Add(WebTokenExpire).Unix()}
secret := GetTokenSecret()
args, _ := utils.JSONStruct2Map(c)
token, err := utils.CreateToken(secret, args)
if err != nil {
log.Error("auth web GenToken error", log.Any("claims", claims), log.E(err))
}
return token, err
}
func ParseToken(token string) (*claimsWithExp, error) {
secret := GetTokenSecret()
claims, err := utils.ParseToken(secret, token)
if err != nil {
return nil, err
}
c := claimsWithExp{}
err = utils.Map2JSONStruct(&c, claims)
if err != nil {
return nil, err
}
return &c, nil
}
+76
View File
@@ -0,0 +1,76 @@
package inner
import (
"bytes"
"fmt"
"io"
"net/http"
"stat/common/crypt"
"stat/common/stdLog"
"github.com/gin-gonic/gin"
)
type cWriter struct {
gin.ResponseWriter
body *bytes.Buffer
}
func (w *cWriter) Write(b []byte) (int, error) {
return w.body.Write(b)
}
func (w *cWriter) WriteString(s string) (int, error) {
return w.body.WriteString(s)
}
func Crypt(key string) gin.HandlerFunc {
return func(c *gin.Context) {
// decrypt
raw, err := c.GetRawData()
if err != nil {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{
"code": stdLog.Failure,
"msg": fmt.Sprintf("读取Body数据出错: %s", err.Error()),
})
return
}
de, err := crypt.CoreAesDecryptEx(raw, 12, key)
if err != nil {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{
"code": stdLog.Failure,
"msg": fmt.Sprintf("解密Body数据出错: %s", err.Error()),
})
return
}
c.Request.Body = io.NopCloser(bytes.NewBuffer(de))
c.Request.ContentLength = int64(len(de))
c.Request.Header.Set("Content-Type", "application/json")
oWriter := c.Writer
blw := &cWriter{body: bytes.NewBufferString(""), ResponseWriter: c.Writer}
c.Writer = blw
c.Next()
// encrypt
c.Writer = oWriter
en, err := crypt.CoreAesEncryptEx(blw.body.Bytes(), 12, key)
if err != nil {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{
"code": stdLog.Failure,
"msg": fmt.Sprintf("加密Body数据出错: %s", err.Error()),
})
return
}
_, err = c.Writer.Write(en)
if err != nil {
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{
"code": stdLog.Failure,
"msg": fmt.Sprintf("写入Body数据出错: %s", err.Error()),
})
return
}
}
}
+25
View File
@@ -0,0 +1,25 @@
package limit
import (
"fmt"
"stat/common/redis"
"stat/statg"
"time"
"github.com/gin-gonic/gin"
)
func GetKey(name string) func() string {
return func() string {
return redis.GetIpLimitDayKey(statg.Conf.Host, statg.Conf.Port) + "_" + name
}
}
func Limit(name string, capacity int64, rate int64) gin.HandlerFunc {
limiter := redis.NewTokenBucket(redis.StatRds.Client(), GetKey(name), capacity, rate, 48*time.Hour)
return func(ctx *gin.Context) {
if err := limiter.TakeTokens(ctx.ClientIP(), 1); err != nil {
ctx.AbortWithStatusJSON(200, gin.H{"msg": fmt.Sprintf("%+v", err)})
}
}
}