Initial commit: 网络验证平台
This commit is contained in:
@@ -0,0 +1,427 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"verification-platform-backend/internal/database"
|
||||
"verification-platform-backend/internal/model"
|
||||
"verification-platform-backend/internal/service"
|
||||
"verification-platform-backend/pkg/jwt"
|
||||
"verification-platform-backend/pkg/response"
|
||||
|
||||
"github.com/dop251/goja"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func SetupDynamicRoutes(r *gin.RouterGroup) {
|
||||
dynamicCode := r.Group("/dynamic-code")
|
||||
{
|
||||
dynamicCode.POST("/:key/execute", handleExecuteDynamicCode)
|
||||
}
|
||||
}
|
||||
|
||||
func handleExecuteDynamicCode(c *gin.Context) {
|
||||
var token string
|
||||
authHeader := c.GetHeader("Authorization")
|
||||
if authHeader != "" {
|
||||
parts := strings.SplitN(authHeader, " ", 2)
|
||||
if len(parts) == 2 && parts[0] == "Bearer" {
|
||||
token = parts[1]
|
||||
}
|
||||
}
|
||||
|
||||
if token == "" {
|
||||
token = c.Query("token")
|
||||
}
|
||||
|
||||
if token == "" {
|
||||
response.Error(c, http.StatusUnauthorized, "Authorization header is required")
|
||||
return
|
||||
}
|
||||
|
||||
claims, err := jwt.ParseToken(token)
|
||||
if err != nil {
|
||||
response.Error(c, http.StatusUnauthorized, "Invalid token")
|
||||
return
|
||||
}
|
||||
|
||||
if time.Now().Unix() > claims.ExpiresAt.Unix() {
|
||||
response.Error(c, http.StatusUnauthorized, "Token expired")
|
||||
return
|
||||
}
|
||||
|
||||
c.Set("user_id", claims.UserID)
|
||||
c.Set("username", claims.Username)
|
||||
c.Set("role", claims.Role)
|
||||
|
||||
appKey := c.Param("appKey")
|
||||
key := c.Param("key")
|
||||
|
||||
var app model.Application
|
||||
if err := database.DB.Where("app_key = ?", appKey).First(&app).Error; err != nil {
|
||||
response.Error(c, 404, "应用不存在")
|
||||
return
|
||||
}
|
||||
|
||||
if service.GetApplicationDisabledStatus(app.ID) {
|
||||
response.Error(c, 403, "该应用已被禁用")
|
||||
return
|
||||
}
|
||||
|
||||
var appUser model.AppUser
|
||||
if err := database.DB.Where("id = ? AND application_id = ?", claims.UserID, app.ID).First(&appUser).Error; err != nil {
|
||||
response.Error(c, http.StatusForbidden, "无权访问该应用的动态代码")
|
||||
return
|
||||
}
|
||||
|
||||
var dynamicCode model.DynamicCode
|
||||
if err := database.DB.Where("application_id = ? AND key = ?", app.ID, key).First(&dynamicCode).Error; err != nil {
|
||||
response.Error(c, 404, "动态代码不存在")
|
||||
return
|
||||
}
|
||||
|
||||
if dynamicCode.Status != "active" {
|
||||
response.Error(c, 400, "动态代码未启用")
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
Params map[string]interface{} `json:"params"`
|
||||
UserID *uint `json:"user_id,omitempty"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.Error(c, 400, "参数错误")
|
||||
return
|
||||
}
|
||||
|
||||
startTime := time.Now()
|
||||
|
||||
vm := goja.New()
|
||||
|
||||
appData := map[string]interface{}{
|
||||
"id": app.ID,
|
||||
"name": app.Name,
|
||||
"description": app.Description,
|
||||
"app_key": app.AppKey,
|
||||
"status": app.Status,
|
||||
"billing_type": app.BillingType,
|
||||
"deduction_mode": app.DeductionMode,
|
||||
"deduction_type": app.DeductionType,
|
||||
"deduction_interval": app.DeductionInterval,
|
||||
"deduction_unit": app.DeductionUnit,
|
||||
"deduction_amount": app.DeductionAmount,
|
||||
"enable_trial": app.EnableTrial,
|
||||
"trial_balance": app.TrialBalance,
|
||||
"trial_days": app.TrialDays,
|
||||
"enable_free_period": app.EnableFreePeriod,
|
||||
"free_period_type": app.FreePeriodType,
|
||||
"free_period_start": app.FreePeriodStart,
|
||||
"free_period_end": app.FreePeriodEnd,
|
||||
"free_period_weekdays": app.FreePeriodWeekdays,
|
||||
"free_period_start_time": app.FreePeriodStartTime,
|
||||
"free_period_end_time": app.FreePeriodEndTime,
|
||||
"max_devices": app.MaxDevices,
|
||||
"bind_type": app.BindType,
|
||||
"multi_open": app.MultiOpen,
|
||||
"multi_open_mode": app.MultiOpenMode,
|
||||
"max_instances": app.MaxInstances,
|
||||
"login_policy": app.LoginPolicy,
|
||||
"max_attempts": app.MaxAttempts,
|
||||
"lock_duration": app.LockDuration,
|
||||
"heartbeat_interval": app.HeartbeatInterval,
|
||||
"heartbeat_timeout": app.HeartbeatTimeout,
|
||||
"change_limit": app.ChangeLimit,
|
||||
"change_interval": app.ChangeInterval,
|
||||
"change_exceed_action": app.ChangeExceedAction,
|
||||
"change_deduct_amount": app.ChangeDeductAmount,
|
||||
}
|
||||
if err := vm.Set("app", appData); err != nil {
|
||||
response.Error(c, 500, "应用数据设置失败")
|
||||
return
|
||||
}
|
||||
|
||||
var constants []model.CloudConstant
|
||||
database.DB.Where("app_id = ? AND status = ?", app.ID, "active").Find(&constants)
|
||||
constantsData := make(map[string]interface{})
|
||||
for _, c := range constants {
|
||||
constantsData[c.Key] = c.Value
|
||||
}
|
||||
if err := vm.Set("constants", constantsData); err != nil {
|
||||
response.Error(c, 500, "云端常量设置失败")
|
||||
return
|
||||
}
|
||||
|
||||
var cloudVariables []model.CloudVariable
|
||||
database.DB.Where("app_id = ? AND status = ?", app.ID, "active").Find(&cloudVariables)
|
||||
appVariables := make(map[string]interface{})
|
||||
for _, v := range cloudVariables {
|
||||
appVariables[v.Key] = v.DefaultValue
|
||||
}
|
||||
if err := vm.Set("appVariables", appVariables); err != nil {
|
||||
response.Error(c, 500, "云端变量设置失败")
|
||||
return
|
||||
}
|
||||
|
||||
userData := map[string]interface{}{}
|
||||
subscription := map[string]interface{}{}
|
||||
userVariables := map[string]interface{}{}
|
||||
devices := []map[string]interface{}{}
|
||||
|
||||
if req.UserID != nil {
|
||||
var user model.AppUser
|
||||
if err := database.DB.Where("id = ? AND application_id = ?", *req.UserID, app.ID).First(&user).Error; err == nil {
|
||||
userData = map[string]interface{}{
|
||||
"id": user.ID,
|
||||
"username": user.Username,
|
||||
"email": user.Email,
|
||||
"status": user.Status,
|
||||
"device_id": user.DeviceID,
|
||||
"avatar": user.Avatar,
|
||||
"created_at": user.CreatedAt,
|
||||
"last_login_at": user.LastLoginAt,
|
||||
}
|
||||
|
||||
subscription = map[string]interface{}{
|
||||
"balance": user.Balance,
|
||||
"is_trial_user": user.IsTrialUser,
|
||||
"trial_start_at": user.TrialStartAt,
|
||||
"trial_end_at": user.TrialEndAt,
|
||||
"expiry_at": user.ExpiryAt,
|
||||
"is_expired": user.ExpiryAt != nil && user.ExpiryAt.Before(time.Now()),
|
||||
"is_lifetime": user.Balance == -1,
|
||||
"days_remaining": calculateDaysRemaining(user.ExpiryAt, user.Balance),
|
||||
}
|
||||
|
||||
var userDevices []model.UserDevice
|
||||
database.DB.Where("user_id = ? AND application_id = ?", user.ID, app.ID).Find(&userDevices)
|
||||
for _, d := range userDevices {
|
||||
devices = append(devices, map[string]interface{}{
|
||||
"id": d.ID,
|
||||
"device_id": d.DeviceID,
|
||||
"device_name": d.DeviceName,
|
||||
"device_type": d.DeviceType,
|
||||
"status": d.Status,
|
||||
"created_at": d.CreatedAt,
|
||||
})
|
||||
}
|
||||
|
||||
var userVars []model.UserVariable
|
||||
database.DB.Where("user_id = ? AND app_id = ?", user.ID, app.ID).Find(&userVars)
|
||||
for _, v := range userVars {
|
||||
userVariables[v.VarName] = v.VarValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := vm.Set("user", userData); err != nil {
|
||||
response.Error(c, 500, "用户数据设置失败")
|
||||
return
|
||||
}
|
||||
if err := vm.Set("subscription", subscription); err != nil {
|
||||
response.Error(c, 500, "订阅数据设置失败")
|
||||
return
|
||||
}
|
||||
if err := vm.Set("userVariables", userVariables); err != nil {
|
||||
response.Error(c, 500, "用户变量设置失败")
|
||||
return
|
||||
}
|
||||
if err := vm.Set("devices", devices); err != nil {
|
||||
response.Error(c, 500, "设备数据设置失败")
|
||||
return
|
||||
}
|
||||
|
||||
for k, v := range req.Params {
|
||||
if err := vm.Set(k, v); err != nil {
|
||||
response.Error(c, 500, "参数设置失败")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if err := vm.Set("params", req.Params); err != nil {
|
||||
response.Error(c, 500, "参数设置失败")
|
||||
return
|
||||
}
|
||||
|
||||
value, err := vm.RunString("(function() { " + dynamicCode.Code + " })()")
|
||||
if err != nil {
|
||||
response.Error(c, 400, "代码执行错误: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
executionTime := time.Since(startTime).Milliseconds()
|
||||
|
||||
result := value.Export()
|
||||
|
||||
if actionMap, ok := result.(map[string]interface{}); ok {
|
||||
if action, hasAction := actionMap["action"]; hasAction {
|
||||
switch action {
|
||||
case "extend_time":
|
||||
if err := handleExtendTime(app.ID, actionMap); err != nil {
|
||||
response.Error(c, 500, "执行加时操作失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
case "deduct_points":
|
||||
if err := handleDeductPoints(app.ID, actionMap); err != nil {
|
||||
response.Error(c, 500, "执行扣点操作失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
case "update_user_variable":
|
||||
appID := app.ID
|
||||
if err := handleUpdateUserVariable(&appID, req.UserID, actionMap); err != nil {
|
||||
response.Error(c, 500, "更新用户变量失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
case "update_app_variable":
|
||||
if err := handleUpdateAppVariable(app.ID, actionMap); err != nil {
|
||||
response.Error(c, 500, "更新应用变量失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
response.Success(c, gin.H{
|
||||
"result": result,
|
||||
"execution_time": executionTime,
|
||||
})
|
||||
}
|
||||
|
||||
func calculateDaysRemaining(expiryAt *time.Time, balance float64) int {
|
||||
if balance == -1 {
|
||||
return -1
|
||||
}
|
||||
if expiryAt == nil {
|
||||
return 0
|
||||
}
|
||||
remaining := int(time.Until(*expiryAt).Hours() / 24)
|
||||
if remaining < 0 {
|
||||
return 0
|
||||
}
|
||||
return remaining
|
||||
}
|
||||
|
||||
func handleExtendTime(appID uint, actionMap map[string]interface{}) error {
|
||||
userID, ok := actionMap["user_id"].(float64)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
days, ok := actionMap["days"].(float64)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
var user model.AppUser
|
||||
if err := database.DB.Where("id = ? AND application_id = ?", uint(userID), appID).First(&user).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if user.Balance == -1 {
|
||||
return nil
|
||||
}
|
||||
|
||||
user.Balance += float64(days)
|
||||
|
||||
return database.DB.Save(&user).Error
|
||||
}
|
||||
|
||||
func handleDeductPoints(appID uint, actionMap map[string]interface{}) error {
|
||||
userID, ok := actionMap["user_id"].(float64)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
points, ok := actionMap["points"].(float64)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
var user model.AppUser
|
||||
if err := database.DB.Where("id = ? AND application_id = ?", uint(userID), appID).First(&user).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if user.Balance == -1 {
|
||||
return nil
|
||||
}
|
||||
|
||||
user.Balance -= points
|
||||
if user.Balance < 0 {
|
||||
user.Balance = 0
|
||||
}
|
||||
|
||||
return database.DB.Save(&user).Error
|
||||
}
|
||||
|
||||
func handleUpdateUserVariable(appID *uint, userID *uint, actionMap map[string]interface{}) error {
|
||||
if userID == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
varName, ok := actionMap["name"].(string)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
varValue, ok := actionMap["value"].(string)
|
||||
if !ok {
|
||||
if v, ok := actionMap["value"]; ok {
|
||||
varValue = toString(v)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
var userVar model.UserVariable
|
||||
if err := database.DB.Where("user_id = ? AND app_id = ? AND var_name = ?", *userID, appID, varName).First(&userVar).Error; err != nil {
|
||||
userVar = model.UserVariable{
|
||||
UserID: *userID,
|
||||
AppID: *appID,
|
||||
VarName: varName,
|
||||
VarValue: varValue,
|
||||
}
|
||||
return database.DB.Create(&userVar).Error
|
||||
}
|
||||
|
||||
userVar.VarValue = varValue
|
||||
return database.DB.Save(&userVar).Error
|
||||
}
|
||||
|
||||
func handleUpdateAppVariable(appID uint, actionMap map[string]interface{}) error {
|
||||
varName, ok := actionMap["name"].(string)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
varValue, ok := actionMap["value"].(string)
|
||||
if !ok {
|
||||
if v, ok := actionMap["value"]; ok {
|
||||
varValue = toString(v)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
var appVar model.CloudVariable
|
||||
if err := database.DB.Where("app_id = ? AND key = ?", appID, varName).First(&appVar).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
appVar.DefaultValue = varValue
|
||||
return database.DB.Save(&appVar).Error
|
||||
}
|
||||
|
||||
func toString(v interface{}) string {
|
||||
switch val := v.(type) {
|
||||
case string:
|
||||
return val
|
||||
case float64:
|
||||
return string(rune(int(val)))
|
||||
case int:
|
||||
return string(rune(val))
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user