Files
NetworkAuth/controllers/admin/app.go

1571 lines
42 KiB
Go
Raw Normal View History

2025-10-24 00:09:45 +08:00
package admin
import (
"crypto/rand"
"encoding/base64"
2025-10-24 00:09:45 +08:00
"encoding/hex"
"encoding/json"
"net/http"
"networkDev/database"
"networkDev/models"
"networkDev/utils"
"strconv"
"strings"
"github.com/google/uuid"
"github.com/sirupsen/logrus"
)
// AppsFragmentHandler 应用列表页面片段处理器
func AppsFragmentHandler(w http.ResponseWriter, r *http.Request) {
utils.RenderTemplate(w, "apps.html", map[string]interface{}{
"Title": "应用管理",
})
}
// AppsListHandler 应用列表API处理器
func AppsListHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
// 获取分页参数
page, _ := strconv.Atoi(r.URL.Query().Get("page"))
if page <= 0 {
page = 1
}
limit, _ := strconv.Atoi(r.URL.Query().Get("limit"))
if limit <= 0 {
limit = 10
}
// 获取搜索参数
search := strings.TrimSpace(r.URL.Query().Get("search"))
// 构建查询
db, err := database.GetDB()
if err != nil {
logrus.WithError(err).Error("Failed to get database connection")
http.Error(w, "Internal server error", http.StatusInternalServerError)
return
}
var apps []models.App
var total int64
query := db.Model(&models.App{})
// 如果有搜索条件
if search != "" {
query = query.Where("name LIKE ? OR uuid LIKE ?", "%"+search+"%", "%"+search+"%")
}
// 获取总数
if err := query.Count(&total).Error; err != nil {
logrus.WithError(err).Error("Failed to count apps")
http.Error(w, "Internal server error", http.StatusInternalServerError)
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")
http.Error(w, "Internal server error", http.StatusInternalServerError)
return
}
// 返回结果
response := map[string]interface{}{
"code": 0,
"msg": "success",
"count": total,
"data": apps,
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
}
// AppGetAppDataHandler 获取应用数据处理器
func AppGetAppDataHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
// 获取UUID参数
uuid := r.URL.Query().Get("uuid")
if uuid == "" {
response := map[string]interface{}{
"code": 1,
"msg": "应用UUID不能为空",
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
return
}
// 获取数据库连接
db, err := database.GetDB()
if err != nil {
logrus.WithError(err).Error("Failed to get database connection")
response := map[string]interface{}{
"code": 1,
"msg": "数据库连接失败",
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
return
}
// 查找应用
var app models.App
if err := db.Where("uuid = ?", uuid).First(&app).Error; err != nil {
logrus.WithError(err).Error("Failed to find app")
response := map[string]interface{}{
"code": 1,
"msg": "应用不存在",
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
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)
}
}
response := map[string]interface{}{
"code": 0,
"msg": "获取成功",
"data": map[string]interface{}{
"app_data": appData,
},
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
}
// AppGetAnnouncementHandler 获取应用程序公告处理器
func AppGetAnnouncementHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
// 获取UUID参数
uuid := r.URL.Query().Get("uuid")
if uuid == "" {
response := map[string]interface{}{
"code": 1,
"msg": "应用UUID不能为空",
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
return
}
// 获取数据库连接
db, err := database.GetDB()
if err != nil {
logrus.WithError(err).Error("Failed to get database connection")
response := map[string]interface{}{
"code": 1,
"msg": "数据库连接失败",
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
return
}
// 查找应用
var app models.App
if err := db.Where("uuid = ?", uuid).First(&app).Error; err != nil {
logrus.WithError(err).Error("Failed to find app")
response := map[string]interface{}{
"code": 1,
"msg": "应用不存在",
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
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)
}
}
response := map[string]interface{}{
"code": 0,
"msg": "获取成功",
"data": map[string]interface{}{
"announcement": announcement,
},
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
}
// AppResetSecretHandler 重置应用密钥API处理器
func AppResetSecretHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
var req struct {
UUID string `json:"uuid"`
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "Invalid JSON", http.StatusBadRequest)
return
}
if req.UUID == "" {
http.Error(w, "应用UUID不能为空", http.StatusBadRequest)
return
}
db, err := database.GetDB()
if err != nil {
logrus.WithError(err).Error("Failed to get database connection")
http.Error(w, "数据库连接失败", http.StatusInternalServerError)
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 by UUID")
http.Error(w, "应用不存在", http.StatusNotFound)
return
}
// 生成新的密钥
bytes := make([]byte, 16)
if _, err := rand.Read(bytes); err != nil {
logrus.WithError(err).Error("Failed to generate random secret")
http.Error(w, "生成密钥失败", http.StatusInternalServerError)
return
}
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")
http.Error(w, "更新密钥失败", http.StatusInternalServerError)
return
}
response := map[string]interface{}{
"code": 0,
"msg": "密钥重置成功",
"data": map[string]interface{}{
"uuid": app.UUID,
"secret": newSecret,
},
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
}
2025-10-24 00:09:45 +08:00
// AppCreateHandler 创建应用API处理器
func AppCreateHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
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 err := json.NewDecoder(r.Body).Decode(&req); err != nil {
logrus.WithError(err).Error("Failed to decode JSON request")
http.Error(w, "Invalid JSON", http.StatusBadRequest)
return
}
// 验证必填字段
if strings.TrimSpace(req.Name) == "" {
logrus.Error("App name is empty")
http.Error(w, "应用名称不能为空", http.StatusBadRequest)
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,
}).Info("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 == "" {
2025-10-24 01:54:45 +08:00
app.UUID = strings.ToUpper(uuid.New().String())
2025-10-24 00:09:45 +08:00
}
if app.Secret == "" {
// 生成32位大写16进制随机字符
bytes := make([]byte, 16) // 16字节 = 32位16进制字符
rand.Read(bytes)
app.Secret = strings.ToUpper(hex.EncodeToString(bytes))
}
db, err := database.GetDB()
if err != nil {
logrus.WithError(err).Error("Failed to get database connection")
http.Error(w, "数据库连接失败", http.StatusInternalServerError)
return
}
// 开始事务
tx := db.Begin()
defer func() {
if r := recover(); r != nil {
tx.Rollback()
}
}()
// 创建应用
if err := tx.Create(&app).Error; err != nil {
tx.Rollback()
2025-10-24 00:09:45 +08:00
logrus.WithError(err).Error("Failed to create app")
http.Error(w, "创建应用失败", http.StatusInternalServerError)
return
}
// 为应用创建所有默认接口
defaultAPITypes := []int{
models.APITypeGetBulletin, // 获取程序公告
models.APITypeGetUpdateUrl, // 获取更新地址
models.APITypeCheckAppVersion, // 检测最新版本
models.APITypeGetCardInfo, // 获取卡密信息
models.APITypeSingleLogin, // 卡密登录
models.APITypeUserLogin, // 用户登录
models.APITypeUserRegin, // 用户注册
models.APITypeUserRecharge, // 用户充值
models.APITypeCardRegin, // 卡密注册
models.APITypeLogOut, // 退出登录
models.APITypeGetExpired, // 获取到期时间
models.APITypeCheckUserStatus, // 检测账号状态
models.APITypeGetAppData, // 获取程序数据
models.APITypeGetVariable, // 获取变量数据
models.APITypeUpdatePwd, // 修改账号密码
models.APITypeMacChangeBind, // 机器码转绑
models.APITypeIPChangeBind, // IP转绑
models.APITypeDisableUser, // 封停用户
models.APITypeBlackUser, // 添加黑名单
models.APITypeUserDeductedTime, // 扣除时间
}
// 批量创建默认接口
for _, apiType := range defaultAPITypes {
api := models.API{
APIType: apiType,
AppUUID: app.UUID,
Status: 1, // 默认启用
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")
http.Error(w, "创建默认接口失败", http.StatusInternalServerError)
return
}
}
// 提交事务
if err := tx.Commit().Error; err != nil {
logrus.WithError(err).Error("Failed to commit transaction")
http.Error(w, "提交事务失败", http.StatusInternalServerError)
return
}
logrus.WithField("app_uuid", app.UUID).Info("Successfully created app with default APIs")
2025-10-24 00:09:45 +08:00
response := map[string]interface{}{
"code": 0,
"msg": "创建成功",
"data": app,
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
}
// AppUpdateHandler 更新应用API处理器
func AppUpdateHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
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 err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "Invalid JSON", http.StatusBadRequest)
return
}
// 验证必填字段
if req.ID == 0 {
http.Error(w, "应用ID不能为空", http.StatusBadRequest)
return
}
if strings.TrimSpace(req.Name) == "" {
http.Error(w, "应用名称不能为空", http.StatusBadRequest)
return
}
db, err := database.GetDB()
if err != nil {
logrus.WithError(err).Error("Failed to get database connection")
http.Error(w, "数据库连接失败", http.StatusInternalServerError)
return
}
// 查找应用
var app models.App
if err := db.First(&app, req.ID).Error; err != nil {
http.Error(w, "应用不存在", http.StatusNotFound)
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")
http.Error(w, "更新应用失败", http.StatusInternalServerError)
return
}
response := map[string]interface{}{
"code": 0,
"msg": "更新成功",
"data": app,
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
}
// AppDeleteHandler 删除应用API处理器
func AppDeleteHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
var req struct {
ID uint `json:"id"`
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "Invalid JSON", http.StatusBadRequest)
return
}
if req.ID == 0 {
http.Error(w, "应用ID不能为空", http.StatusBadRequest)
return
}
db, err := database.GetDB()
if err != nil {
logrus.WithError(err).Error("Failed to get database connection")
http.Error(w, "数据库连接失败", http.StatusInternalServerError)
return
}
// 开始事务
tx := db.Begin()
defer func() {
if r := recover(); r != nil {
tx.Rollback()
}
}()
// 首先获取应用信息以获取UUID
var app models.App
if err := tx.First(&app, req.ID).Error; err != nil {
tx.Rollback()
logrus.WithError(err).Error("Failed to find app")
http.Error(w, "应用不存在", http.StatusNotFound)
return
}
// 删除该应用的所有相关接口
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")
http.Error(w, "删除相关接口失败", http.StatusInternalServerError)
return
}
2025-10-24 00:09:45 +08:00
// 删除应用
if err := tx.Delete(&models.App{}, req.ID).Error; err != nil {
tx.Rollback()
2025-10-24 00:09:45 +08:00
logrus.WithError(err).Error("Failed to delete app")
http.Error(w, "删除应用失败", http.StatusInternalServerError)
return
}
// 提交事务
if err := tx.Commit().Error; err != nil {
logrus.WithError(err).Error("Failed to commit transaction")
http.Error(w, "提交事务失败", http.StatusInternalServerError)
return
}
logrus.WithFields(logrus.Fields{
"app_id": req.ID,
"app_uuid": app.UUID,
}).Info("Successfully deleted app and related APIs")
2025-10-24 00:09:45 +08:00
response := map[string]interface{}{
"code": 0,
"msg": "删除成功",
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
}
// AppsBatchDeleteHandler 批量删除应用API处理器
func AppsBatchDeleteHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
var req struct {
IDs []uint `json:"ids"`
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "Invalid JSON", http.StatusBadRequest)
return
}
if len(req.IDs) == 0 {
http.Error(w, "请选择要删除的应用", http.StatusBadRequest)
return
}
db, err := database.GetDB()
if err != nil {
logrus.WithError(err).Error("Failed to get database connection")
http.Error(w, "数据库连接失败", http.StatusInternalServerError)
return
}
// 开始事务
tx := db.Begin()
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")
http.Error(w, "查找应用失败", http.StatusInternalServerError)
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")
http.Error(w, "删除相关接口失败", http.StatusInternalServerError)
return
}
}
// 批量删除应用
if err := tx.Delete(&models.App{}, req.IDs).Error; err != nil {
tx.Rollback()
2025-10-24 00:09:45 +08:00
logrus.WithError(err).Error("Failed to batch delete apps")
http.Error(w, "批量删除失败", http.StatusInternalServerError)
return
}
// 提交事务
if err := tx.Commit().Error; err != nil {
logrus.WithError(err).Error("Failed to commit transaction")
http.Error(w, "提交事务失败", http.StatusInternalServerError)
return
}
logrus.WithFields(logrus.Fields{
"app_ids": req.IDs,
"app_uuids": appUUIDs,
}).Info("Successfully batch deleted apps and related APIs")
2025-10-24 00:09:45 +08:00
response := map[string]interface{}{
"code": 0,
"msg": "批量删除成功",
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
}
// AppsBatchUpdateStatusHandler 批量更新应用状态API处理器
func AppsBatchUpdateStatusHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
var req struct {
IDs []uint `json:"ids"`
Status int `json:"status"`
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "Invalid JSON", http.StatusBadRequest)
return
}
if len(req.IDs) == 0 {
http.Error(w, "请选择要更新的应用", http.StatusBadRequest)
return
}
if req.Status != 0 && req.Status != 1 {
http.Error(w, "状态值无效", http.StatusBadRequest)
return
}
db, err := database.GetDB()
if err != nil {
logrus.WithError(err).Error("Failed to get database connection")
http.Error(w, "数据库连接失败", http.StatusInternalServerError)
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")
http.Error(w, "批量更新状态失败", http.StatusInternalServerError)
return
}
statusText := "禁用"
if req.Status == 1 {
statusText = "启用"
}
response := map[string]interface{}{
"code": 0,
"msg": "批量" + statusText + "成功",
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
}
// AppUpdateAppDataHandler 更新应用数据处理器
func AppUpdateAppDataHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
// 解析请求体
var req struct {
UUID string `json:"uuid"`
AppData string `json:"app_data"`
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
logrus.WithError(err).Error("Failed to decode request body")
response := map[string]interface{}{
"code": 1,
"msg": "请求参数格式错误",
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
return
}
// 验证UUID
if req.UUID == "" {
response := map[string]interface{}{
"code": 1,
"msg": "应用UUID不能为空",
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
return
}
// 验证UUID格式
if _, err := uuid.Parse(req.UUID); err != nil {
logrus.WithError(err).Error("Invalid UUID format")
response := map[string]interface{}{
"code": 1,
"msg": "无效的UUID格式",
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
return
}
// 获取数据库连接
db, err := database.GetDB()
if err != nil {
logrus.WithError(err).Error("Failed to get database connection")
response := map[string]interface{}{
"code": 1,
"msg": "数据库连接失败",
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
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")
response := map[string]interface{}{
"code": 1,
"msg": "应用不存在",
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
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")
response := map[string]interface{}{
"code": 1,
"msg": "更新应用数据失败",
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
return
}
logrus.WithFields(logrus.Fields{
"app_uuid": req.UUID,
"app_name": app.Name,
}).Info("App data updated successfully")
response := map[string]interface{}{
"code": 0,
"msg": "应用数据更新成功",
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
}
// AppUpdateAnnouncementHandler 更新应用程序公告处理器
func AppUpdateAnnouncementHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
// 解析请求体
var req struct {
UUID string `json:"uuid"`
Announcement string `json:"announcement"`
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
logrus.WithError(err).Error("Failed to decode request body")
response := map[string]interface{}{
"code": 1,
"msg": "请求参数格式错误",
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
return
}
// 验证UUID
if req.UUID == "" {
response := map[string]interface{}{
"code": 1,
"msg": "应用UUID不能为空",
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
return
}
// 验证UUID格式
if _, err := uuid.Parse(req.UUID); err != nil {
logrus.WithError(err).Error("Invalid UUID format")
response := map[string]interface{}{
"code": 1,
"msg": "无效的UUID格式",
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
return
}
// 获取数据库连接
db, err := database.GetDB()
if err != nil {
logrus.WithError(err).Error("Failed to get database connection")
response := map[string]interface{}{
"code": 1,
"msg": "数据库连接失败",
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
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")
response := map[string]interface{}{
"code": 1,
"msg": "应用不存在",
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
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")
response := map[string]interface{}{
"code": 1,
"msg": "更新程序公告失败",
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
return
}
logrus.WithFields(logrus.Fields{
"app_uuid": req.UUID,
"app_name": app.Name,
}).Info("App announcement updated successfully")
response := map[string]interface{}{
"code": 0,
"msg": "程序公告更新成功",
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
}
// ... existing code ...
// AppGetMultiConfigHandler 获取应用多开配置
func AppGetMultiConfigHandler(w http.ResponseWriter, r *http.Request) {
appUUID := r.URL.Query().Get("uuid")
if appUUID == "" {
http.Error(w, "缺少应用UUID", http.StatusBadRequest)
return
}
// 验证UUID格式
if _, err := uuid.Parse(appUUID); err != nil {
http.Error(w, "无效的UUID格式", http.StatusBadRequest)
return
}
db, err := database.GetDB()
if err != nil {
logrus.WithError(err).Error("Failed to get database connection")
http.Error(w, "数据库连接失败", http.StatusInternalServerError)
return
}
var app models.App
if err := db.Where("uuid = ?", appUUID).First(&app).Error; err != nil {
logrus.WithError(err).Error("Failed to find app")
http.Error(w, "应用不存在", http.StatusNotFound)
return
}
// 返回多开配置信息
response := map[string]interface{}{
"login_type": app.LoginType,
"multi_open_scope": app.MultiOpenScope,
"clean_interval": app.CleanInterval,
"check_interval": app.CheckInterval,
"multi_open_count": app.MultiOpenCount,
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
}
// AppUpdateMultiConfigHandler 更新应用多开配置
func AppUpdateMultiConfigHandler(w http.ResponseWriter, r *http.Request) {
var req struct {
2025-10-24 08:50:45 +08:00
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 err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "Invalid JSON", http.StatusBadRequest)
return
}
// 验证UUID格式
if _, err := uuid.Parse(req.UUID); err != nil {
http.Error(w, "无效的UUID格式", http.StatusBadRequest)
return
}
// 验证参数范围
if req.LoginType < 0 || req.LoginType > 1 {
http.Error(w, "登录方式参数无效", http.StatusBadRequest)
return
}
if req.MultiOpenScope < 0 || req.MultiOpenScope > 2 {
http.Error(w, "多开范围参数无效", http.StatusBadRequest)
return
}
if req.CleanInterval < 1 {
http.Error(w, "清理间隔必须大于0", http.StatusBadRequest)
return
}
if req.CheckInterval < 1 {
http.Error(w, "校验间隔必须大于0", http.StatusBadRequest)
return
}
if req.MultiOpenCount < 1 {
http.Error(w, "多开数量必须大于0", http.StatusBadRequest)
return
}
db, err := database.GetDB()
if err != nil {
logrus.WithError(err).Error("Failed to get database connection")
http.Error(w, "数据库连接失败", http.StatusInternalServerError)
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")
http.Error(w, "应用不存在", http.StatusNotFound)
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")
http.Error(w, "更新多开配置失败", http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]string{"message": "多开配置更新成功"})
}
2025-10-24 05:09:22 +08:00
// AppGetBindConfigHandler 获取应用绑定配置
func AppGetBindConfigHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
appUUID := r.URL.Query().Get("uuid")
if appUUID == "" {
response := map[string]interface{}{
"code": 1,
"msg": "缺少应用UUID",
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
return
}
// 验证UUID格式
if _, err := uuid.Parse(appUUID); err != nil {
response := map[string]interface{}{
"code": 1,
"msg": "无效的UUID格式",
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
return
}
db, err := database.GetDB()
if err != nil {
logrus.WithError(err).Error("Failed to get database connection")
response := map[string]interface{}{
"code": 1,
"msg": "数据库连接失败",
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
return
}
var app models.App
if err := db.Where("uuid = ?", appUUID).First(&app).Error; err != nil {
logrus.WithError(err).Error("Failed to find app")
response := map[string]interface{}{
"code": 1,
"msg": "应用不存在",
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
return
}
// 返回绑定配置信息
response := map[string]interface{}{
2025-10-24 08:50:45 +08:00
"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,
2025-10-24 05:09:22 +08:00
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
}
// AppUpdateBindConfigHandler 更新应用绑定配置
func AppUpdateBindConfigHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
var req struct {
2025-10-24 08:50:45 +08:00
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"`
2025-10-24 05:09:22 +08:00
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
logrus.WithError(err).Error("Failed to decode JSON request")
response := map[string]interface{}{
"code": 1,
"msg": "无效的JSON格式",
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
return
}
// 验证UUID
if req.UUID == "" {
response := map[string]interface{}{
"code": 1,
"msg": "应用UUID不能为空",
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
return
}
if _, err := uuid.Parse(req.UUID); err != nil {
response := map[string]interface{}{
"code": 1,
"msg": "无效的UUID格式",
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
return
}
// 验证参数范围
2025-10-24 08:50:45 +08:00
if req.MachineVerify < 0 || req.MachineVerify > 1 {
2025-10-24 05:09:22 +08:00
response := map[string]interface{}{
"code": 1,
2025-10-24 08:50:45 +08:00
"msg": "机器验证参数无效",
2025-10-24 05:09:22 +08:00
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
return
}
2025-10-24 08:50:45 +08:00
if req.MachineRebindLimit < 0 || req.MachineRebindLimit > 1 {
2025-10-24 05:09:22 +08:00
response := map[string]interface{}{
"code": 1,
2025-10-24 08:50:45 +08:00
"msg": "机器重绑限制参数无效",
2025-10-24 05:09:22 +08:00
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
return
}
if req.IPVerify < 0 || req.IPVerify > 3 {
response := map[string]interface{}{
"code": 1,
"msg": "IP地址验证参数无效",
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
return
}
2025-10-24 08:50:45 +08:00
if req.IPRebindLimit < 0 || req.IPRebindLimit > 1 {
2025-10-24 05:09:22 +08:00
response := map[string]interface{}{
"code": 1,
2025-10-24 08:50:45 +08:00
"msg": "IP地址重绑限制参数无效",
2025-10-24 05:09:22 +08:00
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
return
}
2025-10-24 08:50:45 +08:00
if req.MachineRebindEnabled < 0 || req.MachineRebindEnabled > 1 {
2025-10-24 08:25:16 +08:00
response := map[string]interface{}{
"code": 1,
2025-10-24 08:50:45 +08:00
"msg": "机器重绑开关参数无效",
2025-10-24 08:25:16 +08:00
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
return
}
if req.IPRebindEnabled < 0 || req.IPRebindEnabled > 1 {
response := map[string]interface{}{
"code": 1,
"msg": "IP地址重绑开关参数无效",
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
return
}
2025-10-24 08:50:45 +08:00
if req.MachineFreeCount < 0 || req.MachineRebindCount < 0 || req.MachineRebindDeduct < 0 ||
2025-10-24 05:09:22 +08:00
req.IPFreeCount < 0 || req.IPRebindCount < 0 || req.IPRebindDeduct < 0 {
response := map[string]interface{}{
"code": 1,
"msg": "数量参数不能为负数",
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
return
}
db, err := database.GetDB()
if err != nil {
logrus.WithError(err).Error("Failed to get database connection")
response := map[string]interface{}{
"code": 1,
"msg": "数据库连接失败",
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
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")
response := map[string]interface{}{
"code": 1,
"msg": "应用不存在",
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
return
}
// 更新绑定配置
updates := map[string]interface{}{
2025-10-24 08:50:45 +08:00
"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,
2025-10-24 05:09:22 +08:00
}
if err := db.Model(&app).Updates(updates).Error; err != nil {
logrus.WithError(err).Error("Failed to update app bind config")
response := map[string]interface{}{
"code": 1,
"msg": "更新绑定配置失败",
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
return
}
response := map[string]interface{}{
"code": 0,
"msg": "绑定配置更新成功",
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
}
2025-10-24 08:25:16 +08:00
// AppGetRegisterConfigHandler 获取应用注册配置
func AppGetRegisterConfigHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
appUUID := r.URL.Query().Get("uuid")
if appUUID == "" {
response := map[string]interface{}{
"code": 1,
"msg": "缺少应用UUID",
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
return
}
// 验证UUID格式
if _, err := uuid.Parse(appUUID); err != nil {
response := map[string]interface{}{
"code": 1,
"msg": "无效的UUID格式",
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
return
}
db, err := database.GetDB()
if err != nil {
logrus.WithError(err).Error("Failed to get database connection")
response := map[string]interface{}{
"code": 1,
"msg": "数据库连接失败",
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
return
}
var app models.App
if err := db.Where("uuid = ?", appUUID).First(&app).Error; err != nil {
logrus.WithError(err).Error("Failed to find app")
response := map[string]interface{}{
"code": 1,
"msg": "应用不存在",
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
return
}
// 返回注册配置信息
response := map[string]interface{}{
"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,
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
}
// AppUpdateRegisterConfigHandler 更新应用注册配置
func AppUpdateRegisterConfigHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
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 err := json.NewDecoder(r.Body).Decode(&req); err != nil {
logrus.WithError(err).Error("Failed to decode JSON request")
response := map[string]interface{}{
"code": 1,
"msg": "无效的JSON格式",
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
return
}
// 验证UUID
if req.UUID == "" {
response := map[string]interface{}{
"code": 1,
"msg": "应用UUID不能为空",
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
return
}
if _, err := uuid.Parse(req.UUID); err != nil {
response := map[string]interface{}{
"code": 1,
"msg": "无效的UUID格式",
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
return
}
// 验证参数范围
if req.RegisterEnabled < 0 || req.RegisterEnabled > 1 {
response := map[string]interface{}{
"code": 1,
"msg": "账号注册开关参数无效",
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
return
}
if req.RegisterLimitEnabled < 0 || req.RegisterLimitEnabled > 1 {
response := map[string]interface{}{
"code": 1,
"msg": "注册限制开关参数无效",
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
return
}
if req.RegisterLimitTime < 0 || req.RegisterLimitTime > 1 {
response := map[string]interface{}{
"code": 1,
"msg": "注册限制时间参数无效",
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
return
}
if req.RegisterCount < 1 {
response := map[string]interface{}{
"code": 1,
"msg": "注册次数必须大于0",
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
return
}
if req.TrialEnabled < 0 || req.TrialEnabled > 1 {
response := map[string]interface{}{
"code": 1,
"msg": "领取试用开关参数无效",
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
return
}
if req.TrialLimitTime < 0 || req.TrialLimitTime > 1 {
response := map[string]interface{}{
"code": 1,
"msg": "试用限制时间参数无效",
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
return
}
if req.TrialDuration < 0 {
response := map[string]interface{}{
"code": 1,
"msg": "试用时间不能为负数",
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
return
}
db, err := database.GetDB()
if err != nil {
logrus.WithError(err).Error("Failed to get database connection")
response := map[string]interface{}{
"code": 1,
"msg": "数据库连接失败",
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
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")
response := map[string]interface{}{
"code": 1,
"msg": "应用不存在",
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
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")
response := map[string]interface{}{
"code": 1,
"msg": "更新注册配置失败",
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
return
}
response := map[string]interface{}{
"code": 0,
"msg": "注册配置更新成功",
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
}