You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
171 lines
4.8 KiB
171 lines
4.8 KiB
package api
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/golang-jwt/jwt/v4"
|
|
)
|
|
|
|
// 需要记录操作日志的重要接口(增删改类操作)
|
|
var auditableAPIMap = map[string]bool{
|
|
// 登录
|
|
"api_login": false,
|
|
// 管理员管理
|
|
"api_addadminuser": true,
|
|
"api_deladminuser": true,
|
|
"api_updateadminuser": true,
|
|
// 配置管理
|
|
"api_addconfig": true,
|
|
"api_updateconfig": true,
|
|
"api_delconfig": true,
|
|
// 智能体
|
|
"api_addagent": true,
|
|
"api_updateagent": true,
|
|
"api_delagent": true,
|
|
// 产品管理
|
|
"api_addproduct": true,
|
|
"api_updateproduct": true,
|
|
"api_delproduct": true,
|
|
"api_addproductversion": true,
|
|
"api_delproductversion": true,
|
|
// 品牌商 / 生产管理
|
|
"api_createauthcodes": true,
|
|
"api_revokeproductionbatch": true,
|
|
"api_disableauthcodes": true,
|
|
"api_restfactorydevics": true,
|
|
"api_resetlicensestatus": true,
|
|
// 商品管理
|
|
"api_addgoods": true,
|
|
"api_updategoods": true,
|
|
"api_delgoods": true,
|
|
// 渠道包
|
|
"api_addchannelapp": true,
|
|
"api_uploadchannelapp": true,
|
|
"api_delchannelapp": true,
|
|
// MCP
|
|
"api_addmcpservers": true,
|
|
"api_updatemcpservers": true,
|
|
"api_delmcpservers": true,
|
|
// 模板
|
|
"api_addtemplates": true,
|
|
"api_updatetemplates": true,
|
|
"api_deltemplate": true,
|
|
// 唤醒词
|
|
"api_addwakeupvoice": true,
|
|
"api_updatewakeupvoice": true,
|
|
"api_delwakeupvoice": true,
|
|
// 用户管理
|
|
"api_updateuser": true,
|
|
// 邮件
|
|
"api_sendsmtpemail": true,
|
|
// 资源分发
|
|
"api_dispatchresource": true,
|
|
"api_grantadminresource": true, // 超管→后台账号 资源点赠送
|
|
// 统计
|
|
"api_rebuildstats": true, // 全量重建统计
|
|
}
|
|
|
|
// 设置跨域
|
|
func cors() gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
method := c.Request.Method
|
|
origin := c.Request.Header.Get("Origin")
|
|
if origin != "" {
|
|
c.Header("Access-Control-Allow-Origin", "*") // 可将将 * 替换为指定的域名
|
|
c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, UPDATE")
|
|
c.Header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization")
|
|
c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Cache-Control, Content-Language, Content-Type")
|
|
c.Header("Access-Control-Allow-Credentials", "true")
|
|
}
|
|
if method == "OPTIONS" {
|
|
c.AbortWithStatus(http.StatusNoContent)
|
|
}
|
|
c.Next()
|
|
}
|
|
}
|
|
|
|
func parseToken(tokenString string, secretKey []byte) (*jwt.RegisteredClaims, error) {
|
|
parsedToken, err := jwt.ParseWithClaims(tokenString, &jwt.RegisteredClaims{}, func(token *jwt.Token) (interface{}, error) {
|
|
// Ensure the token method conforms to "SigningMethodHMAC"
|
|
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
|
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
|
|
}
|
|
return secretKey, nil
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Validate the token and extract the claims
|
|
if claims, ok := parsedToken.Claims.(*jwt.RegisteredClaims); ok && parsedToken.Valid {
|
|
return claims, nil
|
|
} else {
|
|
return nil, fmt.Errorf("invalid token")
|
|
}
|
|
}
|
|
|
|
// 基准日期
|
|
var baseDate = time.Date(2025, 1, 1, 0, 0, 0, 0, time.UTC)
|
|
|
|
// createCode 将三个整数拼接成一个15位的十五进制字符串
|
|
func createCode(devicetype int, factoryid int, timeday int, number int) string {
|
|
// 转换并格式化channel为4位十五进制
|
|
deviceTsype := formatToHex(devicetype, 1)
|
|
// 转换并格式化group为4位十五进制
|
|
factoryId := formatToHex(factoryid, 2)
|
|
// 转换并格式化number为7位十五进制
|
|
timeDay := formatToHex(timeday, 4)
|
|
// 转换并格式化number为7位十五进制
|
|
numBer := formatToHex(number, 4)
|
|
return deviceTsype + factoryId + timeDay + numBer
|
|
}
|
|
|
|
// ConvertRelativeDaysToDate 将相对天数转换回实际日期
|
|
func ConvertRelativeDaysToDate(relativeDays uint16) uint32 {
|
|
baseDate := uint32(20250101)
|
|
baseYear := baseDate / 10000
|
|
baseMonth := (baseDate % 10000) / 100
|
|
baseDay := baseDate % 100
|
|
|
|
// 简化计算:假设每月30天
|
|
totalDays := baseYear*365 + baseMonth*30 + baseDay + uint32(relativeDays)
|
|
|
|
// 转换回年月日格式(简化版本)
|
|
year := totalDays / 365
|
|
remainingDays := totalDays % 365
|
|
month := remainingDays / 30
|
|
day := remainingDays % 30
|
|
|
|
if month == 0 {
|
|
month = 1
|
|
}
|
|
if day == 0 {
|
|
day = 1
|
|
}
|
|
|
|
return year*10000 + month*100 + day
|
|
}
|
|
|
|
// formatToHex 将整数转换为指定长度的十六进制字符串
|
|
func formatToHex(num, length int) string {
|
|
if num < 0 {
|
|
num = 0 // 处理负数情况
|
|
}
|
|
|
|
// 使用标准库转换为十六进制
|
|
hexStr := fmt.Sprintf("%x", num)
|
|
|
|
// 补零至指定长度
|
|
hexStr = strings.ToUpper(fmt.Sprintf("%0*s", length, hexStr))
|
|
|
|
// 截取指定长度(处理溢出情况)
|
|
if len(hexStr) > length {
|
|
hexStr = hexStr[len(hexStr)-length:]
|
|
}
|
|
|
|
return hexStr
|
|
}
|
|
|