Files
NetworkAuth/controllers/admin/app.go
2026-03-19 05:11:44 +08:00

1458 lines
33 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package admin
import (
"NetworkAuth/controllers"
"NetworkAuth/models"
"NetworkAuth/services"
"crypto/rand"
"encoding/base64"
"encoding/hex"
"net/http"
"strconv"
"strings"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
"github.com/sirupsen/logrus"
)
// ============================================================================
// 全局变量
// ============================================================================
var appBaseController = controllers.NewBaseController()
// ============================================================================
// 页面处理器
// ============================================================================
// AppsFragmentHandler 应用列表页面片段处理器
func AppsFragmentHandler(c *gin.Context) {
c.HTML(http.StatusOK, "apps.html", gin.H{
"Title": "应用程序",
})
}
// ============================================================================
// API处理器
// ============================================================================
// AppsListHandler 应用列表API处理器
func AppsListHandler(c *gin.Context) {
// 获取分页参数
page, _ := strconv.Atoi(c.Query("page"))
if page <= 0 {
page = 1
}
limit, _ := strconv.Atoi(c.Query("limit"))
if limit <= 0 {
limit = 10
}
// 获取搜索参数
search := strings.TrimSpace(c.Query("search"))
// 构建查询
db, ok := appBaseController.GetDB(c)
if !ok {
return
}
var apps []models.App
var total int64
query := db.Model(&models.App{})
// 如果有搜索条件
if search != "" {
// 优化:使用精确匹配提升查询性能
query = query.Where("name = ? OR uuid = ?", search, search)
}
// 获取总数
if err := query.Count(&total).Error; err != nil {
logrus.WithError(err).Error("Failed to count apps")
appBaseController.HandleInternalError(c, "获取应用总数失败", err)
return
}
// 分页查询
offset := (page - 1) * limit
if err := query.Order("created_at DESC").Offset(offset).Limit(limit).Find(&apps).Error; err != nil {
logrus.WithError(err).Error("Failed to query apps")
appBaseController.HandleInternalError(c, "查询应用列表失败", err)
return
}
// 返回结果
response := gin.H{
"code": 0,
"msg": "success",
"count": total,
"data": apps,
}
c.JSON(http.StatusOK, response)
}
// AppGetAppDataHandler 获取应用数据处理器
func AppGetAppDataHandler(c *gin.Context) {
// 获取UUID参数
uuid := c.Query("uuid")
if uuid == "" {
c.JSON(http.StatusBadRequest, gin.H{
"code": 1,
"msg": "应用UUID不能为空",
})
return
}
// 获取数据库连接
db, ok := appBaseController.GetDB(c)
if !ok {
return
}
// 查找应用
var app models.App
if err := db.Where("uuid = ?", uuid).First(&app).Error; err != nil {
logrus.WithError(err).Error("Failed to find app")
c.JSON(http.StatusNotFound, gin.H{
"code": 1,
"msg": "应用不存在",
})
return
}
// 解码base64应用数据内容
var appData string
if app.AppData != "" {
decodedBytes, err := base64.StdEncoding.DecodeString(app.AppData)
if err != nil {
logrus.WithError(err).Error("Failed to decode app data")
// 如果解码失败,返回空字符串
appData = ""
} else {
appData = string(decodedBytes)
}
}
c.JSON(http.StatusOK, gin.H{
"code": 0,
"msg": "获取成功",
"data": gin.H{
"app_data": appData,
},
})
}
// AppGetAnnouncementHandler 获取公告处理器
func AppGetAnnouncementHandler(c *gin.Context) {
// 获取UUID参数
uuid := c.Query("uuid")
if uuid == "" {
c.JSON(http.StatusBadRequest, gin.H{
"code": 1,
"msg": "应用UUID不能为空",
})
return
}
// 获取数据库连接
db, ok := appBaseController.GetDB(c)
if !ok {
return
}
// 查找应用
var app models.App
if err := db.Where("uuid = ?", uuid).First(&app).Error; err != nil {
logrus.WithError(err).Error("Failed to find app")
c.JSON(http.StatusNotFound, gin.H{
"code": 1,
"msg": "应用不存在",
})
return
}
// 解码base64公告内容
var announcement string
if app.Announcement != "" {
decodedBytes, err := base64.StdEncoding.DecodeString(app.Announcement)
if err != nil {
logrus.WithError(err).Error("Failed to decode announcement")
// 如果解码失败,返回空字符串
announcement = ""
} else {
announcement = string(decodedBytes)
}
}
c.JSON(http.StatusOK, gin.H{
"code": 0,
"msg": "获取成功",
"data": gin.H{
"announcement": announcement,
},
})
}
// AppResetSecretHandler 重置应用密钥处理器
func AppResetSecretHandler(c *gin.Context) {
var req struct {
UUID string `json:"uuid"`
}
if !appBaseController.BindJSON(c, &req) {
return
}
// 验证必填字段
if strings.TrimSpace(req.UUID) == "" {
c.JSON(http.StatusBadRequest, gin.H{
"code": 1,
"msg": "应用UUID不能为空",
})
return
}
// 获取数据库连接
db, ok := appBaseController.GetDB(c)
if !ok {
return
}
// 查找应用
var app models.App
if err := db.Where("uuid = ?", req.UUID).First(&app).Error; err != nil {
logrus.WithError(err).Error("Failed to find app")
c.JSON(http.StatusNotFound, gin.H{
"code": 1,
"msg": "应用不存在",
})
return
}
// 生成新的密钥
bytes := make([]byte, 16) // 16字节 = 32位16进制字符
rand.Read(bytes)
newSecret := strings.ToUpper(hex.EncodeToString(bytes))
// 更新密钥
if err := db.Model(&app).Update("secret", newSecret).Error; err != nil {
logrus.WithError(err).Error("Failed to update app secret")
c.JSON(http.StatusInternalServerError, gin.H{
"code": 1,
"msg": "重置密钥失败",
})
return
}
logrus.WithField("app_uuid", app.UUID).Debug("Successfully reset app secret")
c.JSON(http.StatusOK, gin.H{
"code": 0,
"msg": "重置成功",
"data": gin.H{
"secret": newSecret,
},
})
}
// AppCreateHandler 创建应用API处理器
func AppCreateHandler(c *gin.Context) {
var req struct {
Name string `json:"name"`
Version string `json:"version"`
Status int `json:"status"`
DownloadType int `json:"download_type"`
ForceUpdate int `json:"force_update"`
DownloadURL string `json:"download_url"`
}
if !appBaseController.BindJSON(c, &req) {
return
}
// 验证必填字段
if strings.TrimSpace(req.Name) == "" {
logrus.Error("App name is empty")
c.JSON(http.StatusBadRequest, gin.H{
"code": 1,
"msg": "应用名称不能为空",
})
return
}
// 设置默认值
if req.Version == "" {
req.Version = "1.0.0"
}
logrus.WithFields(logrus.Fields{
"name": req.Name,
"version": req.Version,
"status": req.Status,
"download_type": req.DownloadType,
"download_url": req.DownloadURL,
"force_update": req.ForceUpdate,
}).Debug("Received app create request")
// 创建应用
app := models.App{
Name: strings.TrimSpace(req.Name),
Version: req.Version,
Status: req.Status,
DownloadType: req.DownloadType,
DownloadURL: strings.TrimSpace(req.DownloadURL),
ForceUpdate: req.ForceUpdate,
}
// 确保UUID和Secret被设置虽然BeforeCreate钩子应该处理这些但为了保险起见
if app.UUID == "" {
app.UUID = strings.ToUpper(uuid.New().String())
}
if app.Secret == "" {
// 生成32位大写16进制随机字符
bytes := make([]byte, 16) // 16字节 = 32位16进制字符
rand.Read(bytes)
app.Secret = strings.ToUpper(hex.EncodeToString(bytes))
}
db, ok := appBaseController.GetDB(c)
if !ok {
return
}
// 开始事务
tx := db.Begin()
if tx.Error != nil {
logrus.WithError(tx.Error).Error("Failed to begin transaction")
c.JSON(http.StatusInternalServerError, gin.H{
"code": 1,
"msg": "开始事务失败",
})
return
}
defer func() {
if r := recover(); r != nil {
tx.Rollback()
}
}()
// 创建应用
if err := tx.Create(&app).Error; err != nil {
tx.Rollback()
logrus.WithError(err).Error("Failed to create app")
c.JSON(http.StatusInternalServerError, gin.H{
"code": 1,
"msg": "创建应用失败: " + err.Error(),
"error": err.Error(),
})
return
}
// 为应用创建所有默认接口
defaultAPITypes := models.GetDefaultAPITypes()
// 批量创建默认接口
for _, apiType := range defaultAPITypes {
api := models.API{
APIType: apiType,
AppUUID: app.UUID,
Status: 0, // 默认禁用
SubmitAlgorithm: models.AlgorithmNone, // 默认不加密
ReturnAlgorithm: models.AlgorithmNone, // 默认不加密
}
if err := tx.Create(&api).Error; err != nil {
tx.Rollback()
logrus.WithError(err).WithField("api_type", apiType).Error("Failed to create default API")
c.JSON(http.StatusInternalServerError, gin.H{
"code": 1,
"msg": "创建默认接口失败",
})
return
}
}
// 提交事务
if err := tx.Commit().Error; err != nil {
logrus.WithError(err).Error("Failed to commit transaction")
c.JSON(http.StatusInternalServerError, gin.H{
"code": 1,
"msg": "提交事务失败",
})
return
}
// 记录操作日志
operator := c.GetString("admin_username")
if operator == "" {
operator = "unknown"
}
operatorUUID := c.GetString("admin_uuid")
services.RecordOperationLog(
"创建应用",
operator,
operatorUUID,
"创建了应用: "+app.Name,
)
logrus.WithField("app_uuid", app.UUID).Debug("Successfully created app with default APIs")
c.JSON(http.StatusOK, gin.H{
"code": 0,
"msg": "创建成功",
"data": app,
})
}
// AppUpdateHandler 更新应用API处理器
func AppUpdateHandler(c *gin.Context) {
var req struct {
ID uint `json:"id"`
Name string `json:"name"`
Version string `json:"version"`
Status int `json:"status"`
DownloadType int `json:"download_type"`
DownloadURL string `json:"download_url"`
ForceUpdate int `json:"force_update"`
}
if !appBaseController.BindJSON(c, &req) {
return
}
// 验证必填字段
if req.ID == 0 {
c.JSON(http.StatusBadRequest, gin.H{
"code": 1,
"msg": "应用ID不能为空",
})
return
}
if strings.TrimSpace(req.Name) == "" {
c.JSON(http.StatusBadRequest, gin.H{
"code": 1,
"msg": "应用名称不能为空",
})
return
}
// 获取数据库连接
db, ok := appBaseController.GetDB(c)
if !ok {
return
}
// 查找应用
var app models.App
if err := db.First(&app, req.ID).Error; err != nil {
logrus.WithError(err).Error("Failed to find app")
c.JSON(http.StatusNotFound, gin.H{
"code": 1,
"msg": "应用不存在",
})
return
}
// 更新应用信息
app.Name = strings.TrimSpace(req.Name)
app.Version = req.Version
app.Status = req.Status
app.DownloadType = req.DownloadType
app.DownloadURL = strings.TrimSpace(req.DownloadURL)
app.ForceUpdate = req.ForceUpdate
if err := db.Save(&app).Error; err != nil {
logrus.WithError(err).Error("Failed to update app")
c.JSON(http.StatusInternalServerError, gin.H{
"code": 1,
"msg": "更新应用失败",
})
return
}
logrus.WithField("app_id", app.ID).Debug("Successfully updated app")
c.JSON(http.StatusOK, gin.H{
"code": 0,
"msg": "更新成功",
"data": app,
})
}
// AppDeleteHandler 删除应用处理器
func AppDeleteHandler(c *gin.Context) {
var req struct {
ID uint `json:"id"`
}
if !appBaseController.BindJSON(c, &req) {
return
}
// 验证必填字段
if req.ID == 0 {
c.JSON(http.StatusBadRequest, gin.H{
"code": 1,
"msg": "应用ID不能为空",
})
return
}
// 获取数据库连接
db, ok := appBaseController.GetDB(c)
if !ok {
return
}
// 查找应用
var app models.App
if err := db.First(&app, req.ID).Error; err != nil {
logrus.WithError(err).Error("Failed to find app")
c.JSON(http.StatusNotFound, gin.H{
"code": 1,
"msg": "应用不存在",
})
return
}
// 开始事务
tx := db.Begin()
if tx.Error != nil {
logrus.WithError(tx.Error).Error("Failed to begin transaction")
c.JSON(http.StatusInternalServerError, gin.H{
"code": 1,
"msg": "开始事务失败",
})
return
}
defer func() {
if r := recover(); r != nil {
tx.Rollback()
}
}()
// 删除相关的API记录
if err := tx.Where("app_uuid = ?", app.UUID).Delete(&models.API{}).Error; err != nil {
tx.Rollback()
logrus.WithError(err).Error("Failed to delete related APIs")
c.JSON(http.StatusInternalServerError, gin.H{
"code": 1,
"msg": "删除相关接口失败",
})
return
}
// 删除应用
if err := tx.Delete(&app).Error; err != nil {
tx.Rollback()
logrus.WithError(err).Error("Failed to delete app")
c.JSON(http.StatusInternalServerError, gin.H{
"code": 1,
"msg": "删除应用失败",
})
return
}
// 提交事务
if err := tx.Commit().Error; err != nil {
logrus.WithError(err).Error("Failed to commit transaction")
c.JSON(http.StatusInternalServerError, gin.H{
"code": 1,
"msg": "提交事务失败",
})
return
}
// 记录操作日志
operator := c.GetString("admin_username")
if operator == "" {
operator = "unknown"
}
operatorUUID := c.GetString("admin_uuid")
services.RecordOperationLog(
"删除应用",
operator,
operatorUUID,
"删除了应用: "+app.Name,
)
logrus.WithFields(logrus.Fields{
"app_id": app.ID,
"app_uuid": app.UUID,
}).Debug("Successfully deleted app and related APIs")
c.JSON(http.StatusOK, gin.H{
"code": 0,
"msg": "删除成功",
})
}
// AppUpdateAppDataHandler 更新应用数据处理器
func AppUpdateAppDataHandler(c *gin.Context) {
// 解析请求体
var req struct {
UUID string `json:"uuid"`
AppData string `json:"app_data"`
}
if !appBaseController.BindJSON(c, &req) {
return
}
// 验证UUID
if req.UUID == "" {
c.JSON(http.StatusBadRequest, gin.H{
"code": 1,
"msg": "应用UUID不能为空",
})
return
}
// 验证UUID格式
if _, err := uuid.Parse(req.UUID); err != nil {
logrus.WithError(err).Error("Invalid UUID format")
c.JSON(http.StatusBadRequest, gin.H{
"code": 1,
"msg": "无效的UUID格式",
})
return
}
// 获取数据库连接
db, ok := appBaseController.GetDB(c)
if !ok {
return
}
// 查找应用
var app models.App
if err := db.Where("uuid = ?", req.UUID).First(&app).Error; err != nil {
logrus.WithError(err).Error("Failed to find app")
c.JSON(http.StatusNotFound, gin.H{
"code": 1,
"msg": "应用不存在",
})
return
}
// 对应用数据内容进行base64编码
encodedAppData := base64.StdEncoding.EncodeToString([]byte(req.AppData))
// 更新应用的数据内容
if err := db.Model(&app).Update("app_data", encodedAppData).Error; err != nil {
logrus.WithError(err).Error("Failed to update app data")
c.JSON(http.StatusInternalServerError, gin.H{
"code": 1,
"msg": "更新应用数据失败",
})
return
}
logrus.WithFields(logrus.Fields{
"app_uuid": req.UUID,
"app_name": app.Name,
}).Debug("App data updated successfully")
c.JSON(http.StatusOK, gin.H{
"code": 0,
"msg": "应用数据更新成功",
})
}
// AppUpdateAnnouncementHandler 更新应用程序公告处理器
func AppUpdateAnnouncementHandler(c *gin.Context) {
// 解析请求体
var req struct {
UUID string `json:"uuid"`
Announcement string `json:"announcement"`
}
if !appBaseController.BindJSON(c, &req) {
return
}
// 验证UUID
if req.UUID == "" {
c.JSON(http.StatusBadRequest, gin.H{
"code": 1,
"msg": "应用UUID不能为空",
})
return
}
// 验证UUID格式
if _, err := uuid.Parse(req.UUID); err != nil {
logrus.WithError(err).Error("Invalid UUID format")
c.JSON(http.StatusBadRequest, gin.H{
"code": 1,
"msg": "无效的UUID格式",
})
return
}
// 获取数据库连接
db, ok := appBaseController.GetDB(c)
if !ok {
return
}
// 查找应用
var app models.App
if err := db.Where("uuid = ?", req.UUID).First(&app).Error; err != nil {
logrus.WithError(err).Error("Failed to find app")
c.JSON(http.StatusNotFound, gin.H{
"code": 1,
"msg": "应用不存在",
})
return
}
// 对公告内容进行base64编码
encodedAnnouncement := base64.StdEncoding.EncodeToString([]byte(req.Announcement))
// 更新应用的公告内容
if err := db.Model(&app).Update("announcement", encodedAnnouncement).Error; err != nil {
logrus.WithError(err).Error("Failed to update app announcement")
c.JSON(http.StatusInternalServerError, gin.H{
"code": 1,
"msg": "更新程序公告失败",
})
return
}
logrus.WithFields(logrus.Fields{
"app_uuid": req.UUID,
"app_name": app.Name,
}).Debug("App announcement updated successfully")
c.JSON(http.StatusOK, gin.H{
"code": 0,
"msg": "程序公告更新成功",
})
}
// AppGetMultiConfigHandler 获取应用多开配置处理器
func AppGetMultiConfigHandler(c *gin.Context) {
appUUID := c.Query("uuid")
if appUUID == "" {
c.JSON(http.StatusBadRequest, gin.H{
"code": 1,
"msg": "应用UUID不能为空",
})
return
}
// 验证UUID格式
if _, err := uuid.Parse(appUUID); err != nil {
logrus.WithError(err).Error("Invalid UUID format")
c.JSON(http.StatusBadRequest, gin.H{
"code": 1,
"msg": "无效的UUID格式",
})
return
}
// 获取数据库连接
db, ok := appBaseController.GetDB(c)
if !ok {
return
}
// 查找应用
var app models.App
if err := db.Where("uuid = ?", appUUID).First(&app).Error; err != nil {
logrus.WithError(err).Error("Failed to find app")
c.JSON(http.StatusNotFound, gin.H{
"code": 1,
"msg": "应用不存在",
})
return
}
c.JSON(http.StatusOK, gin.H{
"code": 0,
"msg": "获取多开配置成功",
"data": gin.H{
"login_type": app.LoginType,
"multi_open_scope": app.MultiOpenScope,
"clean_interval": app.CleanInterval,
"check_interval": app.CheckInterval,
"multi_open_count": app.MultiOpenCount,
},
})
}
// AppUpdateMultiConfigHandler 更新应用多开配置处理器
func AppUpdateMultiConfigHandler(c *gin.Context) {
// 解析请求体
var req struct {
UUID string `json:"uuid"`
LoginType int `json:"login_type"`
MultiOpenScope int `json:"multi_open_scope"`
CleanInterval int `json:"clean_interval"`
CheckInterval int `json:"check_interval"`
MultiOpenCount int `json:"multi_open_count"`
}
if !appBaseController.BindJSON(c, &req) {
return
}
// 验证UUID
if req.UUID == "" {
c.JSON(http.StatusBadRequest, gin.H{
"code": 1,
"msg": "应用UUID不能为空",
})
return
}
// 验证UUID格式
if _, err := uuid.Parse(req.UUID); err != nil {
logrus.WithError(err).Error("Invalid UUID format")
c.JSON(http.StatusBadRequest, gin.H{
"code": 1,
"msg": "无效的UUID格式",
})
return
}
// 验证参数范围
if req.LoginType < 0 || req.LoginType > 1 {
c.JSON(http.StatusBadRequest, gin.H{
"code": 1,
"msg": "登录方式参数无效",
})
return
}
if req.MultiOpenScope < 0 || req.MultiOpenScope > 2 {
c.JSON(http.StatusBadRequest, gin.H{
"code": 1,
"msg": "多开范围参数无效",
})
return
}
if req.CleanInterval < 1 {
c.JSON(http.StatusBadRequest, gin.H{
"code": 1,
"msg": "清理间隔必须大于0",
})
return
}
if req.CheckInterval < 1 {
c.JSON(http.StatusBadRequest, gin.H{
"code": 1,
"msg": "校验间隔必须大于0",
})
return
}
if req.MultiOpenCount < 1 {
c.JSON(http.StatusBadRequest, gin.H{
"code": 1,
"msg": "多开数量必须大于0",
})
return
}
// 获取数据库连接
db, ok := appBaseController.GetDB(c)
if !ok {
return
}
// 查找应用
var app models.App
if err := db.Where("uuid = ?", req.UUID).First(&app).Error; err != nil {
logrus.WithError(err).Error("Failed to find app")
c.JSON(http.StatusNotFound, gin.H{
"code": 1,
"msg": "应用不存在",
})
return
}
// 更新多开配置
updates := map[string]interface{}{
"login_type": req.LoginType,
"multi_open_scope": req.MultiOpenScope,
"clean_interval": req.CleanInterval,
"check_interval": req.CheckInterval,
"multi_open_count": req.MultiOpenCount,
}
if err := db.Model(&app).Updates(updates).Error; err != nil {
logrus.WithError(err).Error("Failed to update app multi config")
c.JSON(http.StatusInternalServerError, gin.H{
"code": 1,
"msg": "更新多开配置失败",
})
return
}
logrus.WithFields(logrus.Fields{
"app_uuid": req.UUID,
"app_name": app.Name,
}).Debug("App multi config updated successfully")
c.JSON(http.StatusOK, gin.H{
"code": 0,
"msg": "多开配置更新成功",
})
}
// AppGetBindConfigHandler 获取应用绑定配置处理器
func AppGetBindConfigHandler(c *gin.Context) {
appUUID := c.Query("uuid")
if appUUID == "" {
c.JSON(http.StatusBadRequest, gin.H{
"code": 1,
"msg": "应用UUID不能为空",
})
return
}
// 验证UUID格式
if _, err := uuid.Parse(appUUID); err != nil {
logrus.WithError(err).Error("Invalid UUID format")
c.JSON(http.StatusBadRequest, gin.H{
"code": 1,
"msg": "无效的UUID格式",
})
return
}
// 获取数据库连接
db, ok := appBaseController.GetDB(c)
if !ok {
return
}
// 查找应用
var app models.App
if err := db.Where("uuid = ?", appUUID).First(&app).Error; err != nil {
logrus.WithError(err).Error("Failed to find app")
c.JSON(http.StatusNotFound, gin.H{
"code": 1,
"msg": "应用不存在",
})
return
}
c.JSON(http.StatusOK, gin.H{
"code": 0,
"msg": "获取绑定配置成功",
"data": gin.H{
"machine_verify": app.MachineVerify,
"machine_rebind_enabled": app.MachineRebindEnabled,
"machine_rebind_limit": app.MachineRebindLimit,
"machine_free_count": app.MachineFreeCount,
"machine_rebind_count": app.MachineRebindCount,
"machine_rebind_deduct": app.MachineRebindDeduct,
"ip_verify": app.IPVerify,
"ip_rebind_enabled": app.IPRebindEnabled,
"ip_rebind_limit": app.IPRebindLimit,
"ip_free_count": app.IPFreeCount,
"ip_rebind_count": app.IPRebindCount,
"ip_rebind_deduct": app.IPRebindDeduct,
},
})
}
// AppUpdateBindConfigHandler 更新应用绑定配置处理器
func AppUpdateBindConfigHandler(c *gin.Context) {
// 解析请求体
var req struct {
UUID string `json:"uuid"`
MachineVerify int `json:"machine_verify"`
MachineRebindEnabled int `json:"machine_rebind_enabled"`
MachineRebindLimit int `json:"machine_rebind_limit"`
MachineFreeCount int `json:"machine_free_count"`
MachineRebindCount int `json:"machine_rebind_count"`
MachineRebindDeduct int `json:"machine_rebind_deduct"`
IPVerify int `json:"ip_verify"`
IPRebindEnabled int `json:"ip_rebind_enabled"`
IPRebindLimit int `json:"ip_rebind_limit"`
IPFreeCount int `json:"ip_free_count"`
IPRebindCount int `json:"ip_rebind_count"`
IPRebindDeduct int `json:"ip_rebind_deduct"`
}
if !appBaseController.BindJSON(c, &req) {
return
}
// 验证UUID
if req.UUID == "" {
c.JSON(http.StatusBadRequest, gin.H{
"code": 1,
"msg": "应用UUID不能为空",
})
return
}
// 验证UUID格式
if _, err := uuid.Parse(req.UUID); err != nil {
logrus.WithError(err).Error("Invalid UUID format")
c.JSON(http.StatusBadRequest, gin.H{
"code": 1,
"msg": "无效的UUID格式",
})
return
}
// 获取数据库连接
db, ok := appBaseController.GetDB(c)
if !ok {
return
}
// 查找应用
var app models.App
if err := db.Where("uuid = ?", req.UUID).First(&app).Error; err != nil {
logrus.WithError(err).Error("Failed to find app")
c.JSON(http.StatusNotFound, gin.H{
"code": 1,
"msg": "应用不存在",
})
return
}
// 更新绑定配置
updates := map[string]interface{}{
"machine_verify": req.MachineVerify,
"machine_rebind_enabled": req.MachineRebindEnabled,
"machine_rebind_limit": req.MachineRebindLimit,
"machine_free_count": req.MachineFreeCount,
"machine_rebind_count": req.MachineRebindCount,
"machine_rebind_deduct": req.MachineRebindDeduct,
"ip_verify": req.IPVerify,
"ip_rebind_enabled": req.IPRebindEnabled,
"ip_rebind_limit": req.IPRebindLimit,
"ip_free_count": req.IPFreeCount,
"ip_rebind_count": req.IPRebindCount,
"ip_rebind_deduct": req.IPRebindDeduct,
}
if err := db.Model(&app).Updates(updates).Error; err != nil {
logrus.WithError(err).Error("Failed to update app bind config")
c.JSON(http.StatusInternalServerError, gin.H{
"code": 1,
"msg": "更新绑定配置失败",
})
return
}
logrus.WithFields(logrus.Fields{
"app_uuid": req.UUID,
"app_name": app.Name,
}).Debug("App bind config updated successfully")
c.JSON(http.StatusOK, gin.H{
"code": 0,
"msg": "绑定配置更新成功",
})
}
// AppGetRegisterConfigHandler 获取应用注册配置处理器
func AppGetRegisterConfigHandler(c *gin.Context) {
appUUID := c.Query("uuid")
if appUUID == "" {
c.JSON(http.StatusBadRequest, gin.H{
"code": 1,
"msg": "应用UUID不能为空",
})
return
}
// 验证UUID格式
if _, err := uuid.Parse(appUUID); err != nil {
logrus.WithError(err).Error("Invalid UUID format")
c.JSON(http.StatusBadRequest, gin.H{
"code": 1,
"msg": "无效的UUID格式",
})
return
}
// 获取数据库连接
db, ok := appBaseController.GetDB(c)
if !ok {
return
}
// 查找应用
var app models.App
if err := db.Where("uuid = ?", appUUID).First(&app).Error; err != nil {
logrus.WithError(err).Error("Failed to find app")
c.JSON(http.StatusNotFound, gin.H{
"code": 1,
"msg": "应用不存在",
})
return
}
c.JSON(http.StatusOK, gin.H{
"code": 0,
"msg": "获取注册配置成功",
"data": gin.H{
"register_enabled": app.RegisterEnabled,
"register_limit_enabled": app.RegisterLimitEnabled,
"register_limit_time": app.RegisterLimitTime,
"register_count": app.RegisterCount,
"trial_enabled": app.TrialEnabled,
"trial_limit_time": app.TrialLimitTime,
"trial_duration": app.TrialDuration,
},
})
}
// AppUpdateRegisterConfigHandler 更新应用注册配置处理器
func AppUpdateRegisterConfigHandler(c *gin.Context) {
// 解析请求体
var req struct {
UUID string `json:"uuid"`
RegisterEnabled int `json:"register_enabled"`
RegisterLimitEnabled int `json:"register_limit_enabled"`
RegisterLimitTime int `json:"register_limit_time"`
RegisterCount int `json:"register_count"`
TrialEnabled int `json:"trial_enabled"`
TrialLimitTime int `json:"trial_limit_time"`
TrialDuration int `json:"trial_duration"`
}
if !appBaseController.BindJSON(c, &req) {
return
}
// 验证UUID
if req.UUID == "" {
c.JSON(http.StatusBadRequest, gin.H{
"code": 1,
"msg": "应用UUID不能为空",
})
return
}
// 验证UUID格式
if _, err := uuid.Parse(req.UUID); err != nil {
logrus.WithError(err).Error("Invalid UUID format")
c.JSON(http.StatusBadRequest, gin.H{
"code": 1,
"msg": "无效的UUID格式",
})
return
}
// 获取数据库连接
db, ok := appBaseController.GetDB(c)
if !ok {
return
}
// 查找应用
var app models.App
if err := db.Where("uuid = ?", req.UUID).First(&app).Error; err != nil {
logrus.WithError(err).Error("Failed to find app")
c.JSON(http.StatusNotFound, gin.H{
"code": 1,
"msg": "应用不存在",
})
return
}
// 更新注册配置
updates := map[string]interface{}{
"register_enabled": req.RegisterEnabled,
"register_limit_enabled": req.RegisterLimitEnabled,
"register_limit_time": req.RegisterLimitTime,
"register_count": req.RegisterCount,
"trial_enabled": req.TrialEnabled,
"trial_limit_time": req.TrialLimitTime,
"trial_duration": req.TrialDuration,
}
if err := db.Model(&app).Updates(updates).Error; err != nil {
logrus.WithError(err).Error("Failed to update app register config")
c.JSON(http.StatusInternalServerError, gin.H{
"code": 1,
"msg": "更新注册配置失败",
})
return
}
logrus.WithFields(logrus.Fields{
"app_uuid": req.UUID,
"app_name": app.Name,
}).Debug("App register config updated successfully")
c.JSON(http.StatusOK, gin.H{
"code": 0,
"msg": "注册配置更新成功",
})
}
// AppsBatchDeleteHandler 批量删除应用处理器
func AppsBatchDeleteHandler(c *gin.Context) {
var req struct {
IDs []uint `json:"ids"`
}
if !appBaseController.BindJSON(c, &req) {
return
}
if len(req.IDs) == 0 {
c.JSON(http.StatusBadRequest, gin.H{
"code": 1,
"msg": "请选择要删除的应用",
})
return
}
// 获取数据库连接
db, ok := appBaseController.GetDB(c)
if !ok {
return
}
// 开始事务
tx := db.Begin()
if tx.Error != nil {
logrus.WithError(tx.Error).Error("Failed to begin transaction")
c.JSON(http.StatusInternalServerError, gin.H{
"code": 1,
"msg": "开始事务失败",
})
return
}
defer func() {
if r := recover(); r != nil {
tx.Rollback()
}
}()
// 首先获取要删除的应用的UUID列表
var apps []models.App
if err := tx.Where("id IN ?", req.IDs).Find(&apps).Error; err != nil {
tx.Rollback()
logrus.WithError(err).Error("Failed to find apps")
c.JSON(http.StatusInternalServerError, gin.H{
"code": 1,
"msg": "查找应用失败",
})
return
}
// 提取UUID列表
var appUUIDs []string
for _, app := range apps {
appUUIDs = append(appUUIDs, app.UUID)
}
// 删除这些应用的所有相关接口
if len(appUUIDs) > 0 {
if err := tx.Where("app_uuid IN ?", appUUIDs).Delete(&models.API{}).Error; err != nil {
tx.Rollback()
logrus.WithError(err).Error("Failed to delete related APIs")
c.JSON(http.StatusInternalServerError, gin.H{
"code": 1,
"msg": "删除相关接口失败",
})
return
}
}
// 批量删除应用
if err := tx.Delete(&models.App{}, req.IDs).Error; err != nil {
tx.Rollback()
logrus.WithError(err).Error("Failed to batch delete apps")
c.JSON(http.StatusInternalServerError, gin.H{
"code": 1,
"msg": "批量删除失败",
})
return
}
// 提交事务
if err := tx.Commit().Error; err != nil {
logrus.WithError(err).Error("Failed to commit transaction")
c.JSON(http.StatusInternalServerError, gin.H{
"code": 1,
"msg": "提交事务失败",
})
return
}
logrus.WithFields(logrus.Fields{
"app_ids": req.IDs,
"app_uuids": appUUIDs,
}).Debug("Successfully batch deleted apps and related APIs")
c.JSON(http.StatusOK, gin.H{
"code": 0,
"msg": "批量删除成功",
})
}
// AppsBatchUpdateStatusHandler 批量更新应用状态处理器
func AppsBatchUpdateStatusHandler(c *gin.Context) {
var req struct {
IDs []uint `json:"ids"`
Status int `json:"status"`
}
if !appBaseController.BindJSON(c, &req) {
return
}
if len(req.IDs) == 0 {
c.JSON(http.StatusBadRequest, gin.H{
"code": 1,
"msg": "请选择要更新的应用",
})
return
}
if req.Status != 0 && req.Status != 1 {
c.JSON(http.StatusBadRequest, gin.H{
"code": 1,
"msg": "状态值无效",
})
return
}
// 获取数据库连接
db, ok := appBaseController.GetDB(c)
if !ok {
return
}
// 批量更新状态
if err := db.Model(&models.App{}).Where("id IN ?", req.IDs).Update("status", req.Status).Error; err != nil {
logrus.WithError(err).Error("Failed to batch update app status")
c.JSON(http.StatusInternalServerError, gin.H{
"code": 1,
"msg": "批量更新状态失败",
})
return
}
statusText := "禁用"
if req.Status == 1 {
statusText = "启用"
}
c.JSON(http.StatusOK, gin.H{
"code": 0,
"msg": "批量" + statusText + "成功",
})
}
// AppUpdateStatusHandler 更新单个应用状态处理器
func AppUpdateStatusHandler(c *gin.Context) {
var req struct {
ID uint `json:"id"`
Status int `json:"status"`
}
if !appBaseController.BindJSON(c, &req) {
return
}
if req.ID == 0 {
c.JSON(http.StatusBadRequest, gin.H{
"code": 1,
"msg": "应用ID不能为空",
})
return
}
if req.Status != 0 && req.Status != 1 {
c.JSON(http.StatusBadRequest, gin.H{
"code": 1,
"msg": "状态值无效",
})
return
}
// 获取数据库连接
db, ok := appBaseController.GetDB(c)
if !ok {
return
}
// 检查应用是否存在
var app models.App
if err := db.Where("id = ?", req.ID).First(&app).Error; err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"code": 1,
"msg": "应用不存在",
})
return
}
// 更新状态
if err := db.Model(&app).Update("status", req.Status).Error; err != nil {
logrus.WithError(err).Error("Failed to update app status")
c.JSON(http.StatusInternalServerError, gin.H{
"code": 1,
"msg": "更新状态失败",
})
return
}
statusText := "禁用"
if req.Status == 1 {
statusText = "启用"
}
c.JSON(http.StatusOK, gin.H{
"code": 0,
"msg": "应用" + statusText + "成功",
})
}
// AppsSimpleListHandler 简化应用列表API处理器用于下拉框选择等场景
func AppsSimpleListHandler(c *gin.Context) {
// 获取数据库连接
db, ok := appBaseController.GetDB(c)
if !ok {
return
}
// 查询所有启用的应用,只获取必要字段
var apps []struct {
ID uint `json:"id"`
UUID string `json:"uuid"`
Name string `json:"name"`
}
if err := db.Model(&models.App{}).
Select("id, uuid, name").
Where("status = ?", 1). // 只获取启用的应用
Order("name ASC").
Find(&apps).Error; err != nil {
logrus.WithError(err).Error("Failed to query simple apps list")
appBaseController.HandleInternalError(c, "获取应用列表失败", err)
return
}
// 返回结果
response := gin.H{
"code": 0,
"msg": "success",
"data": apps,
}
c.JSON(http.StatusOK, response)
}