New interface management

Optimize the pop-up interface
This commit is contained in:
2025-10-25 01:43:03 +08:00
parent 269fc897db
commit 6dad3971de
11 changed files with 1869 additions and 80 deletions

418
controllers/admin/api.go Normal file
View File

@@ -0,0 +1,418 @@
package admin
import (
"encoding/json"
"net/http"
"networkDev/database"
"networkDev/models"
"networkDev/utils"
"networkDev/utils/encrypt"
"strconv"
"strings"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/hex"
"encoding/pem"
"github.com/sirupsen/logrus"
)
// APIFragmentHandler 接口列表页面片段处理器
func APIFragmentHandler(w http.ResponseWriter, r *http.Request) {
utils.RenderTemplate(w, "apis.html", map[string]interface{}{
"Title": "接口管理",
})
}
// APIListHandler 接口列表API处理器
func APIListHandler(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
}
// 获取应用UUID参数用于按应用筛选接口
appUUID := strings.TrimSpace(r.URL.Query().Get("app_uuid"))
// 获取搜索参数
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
}
// 构建基础查询
query := db.Model(&models.API{})
// 如果指定了应用UUID则按应用筛选
if appUUID != "" {
query = query.Where("app_uuid = ?", appUUID)
}
// 如果有搜索条件,添加搜索
if search != "" {
query = query.Where("api_key LIKE ? OR app_uuid LIKE ?", "%"+search+"%", "%"+search+"%")
}
// 获取总数
var total int64
if err := query.Count(&total).Error; err != nil {
logrus.WithError(err).Error("Failed to count APIs")
http.Error(w, "Internal server error", http.StatusInternalServerError)
return
}
// 获取分页数据
var apis []models.API
offset := (page - 1) * limit
if err := query.Offset(offset).Limit(limit).Order("created_at DESC").Find(&apis).Error; err != nil {
logrus.WithError(err).Error("Failed to fetch APIs")
http.Error(w, "Internal server error", http.StatusInternalServerError)
return
}
// 获取关联的应用信息
var appUUIDs []string
for _, api := range apis {
appUUIDs = append(appUUIDs, api.AppUUID)
}
var apps []models.App
if len(appUUIDs) > 0 {
if err := db.Where("uuid IN ?", appUUIDs).Find(&apps).Error; err != nil {
logrus.WithError(err).Error("Failed to fetch related apps")
}
}
// 创建应用UUID到应用名称的映射
appMap := make(map[string]string)
for _, app := range apps {
appMap[app.UUID] = app.Name
}
// 构建响应数据
type APIResponse struct {
models.API
AppName string `json:"app_name"`
APITypeName string `json:"api_type_name"`
StatusName string `json:"status_name"`
AlgorithmNames struct {
Submit string `json:"submit"`
Return string `json:"return"`
} `json:"algorithm_names"`
}
var responseAPIs []APIResponse
for _, api := range apis {
responseAPI := APIResponse{
API: api,
AppName: appMap[api.AppUUID],
APITypeName: models.GetAPITypeName(api.APIType),
StatusName: getAPIStatusName(api.Status),
}
responseAPI.AlgorithmNames.Submit = models.GetAlgorithmName(api.SubmitAlgorithm)
responseAPI.AlgorithmNames.Return = models.GetAlgorithmName(api.ReturnAlgorithm)
responseAPIs = append(responseAPIs, responseAPI)
}
// 计算分页信息
totalPages := (total + int64(limit) - 1) / int64(limit)
response := map[string]interface{}{
"success": true,
"data": map[string]interface{}{
"apis": responseAPIs,
"total": total,
"page": page,
"limit": limit,
"total_pages": totalPages,
},
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
}
// getAPIStatusName 获取API状态名称
func getAPIStatusName(status int) string {
switch status {
case 1:
return "启用"
case 0:
return "禁用"
default:
return "未知"
}
}
// APIUpdateHandler 更新接口处理器
func APIUpdateHandler(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"`
Status int `json:"status"`
SubmitAlgorithm int `json:"submit_algorithm"`
ReturnAlgorithm int `json:"return_algorithm"`
SubmitPublicKey string `json:"submit_public_key"`
SubmitPrivateKey string `json:"submit_private_key"`
ReturnPublicKey string `json:"return_public_key"`
ReturnPrivateKey string `json:"return_private_key"`
}
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 req.Status != 0 && req.Status != 1 {
http.Error(w, "无效的状态值", http.StatusBadRequest)
return
}
if !models.IsValidAlgorithm(req.SubmitAlgorithm) || !models.IsValidAlgorithm(req.ReturnAlgorithm) {
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, "Internal server error", http.StatusInternalServerError)
return
}
// 查找并更新API记录
var api models.API
if err := db.First(&api, req.ID).Error; err != nil {
http.Error(w, "接口不存在", http.StatusNotFound)
return
}
// 更新字段(不允许修改 APIType
api.Status = req.Status
api.SubmitAlgorithm = req.SubmitAlgorithm
api.ReturnAlgorithm = req.ReturnAlgorithm
// 可选更新密钥/证书(当提供时)
if req.SubmitPublicKey != "" || req.SubmitPrivateKey != "" {
api.SubmitPublicKey = req.SubmitPublicKey
api.SubmitPrivateKey = req.SubmitPrivateKey
}
if req.ReturnPublicKey != "" || req.ReturnPrivateKey != "" {
api.ReturnPublicKey = req.ReturnPublicKey
api.ReturnPrivateKey = req.ReturnPrivateKey
}
if err := db.Save(&api).Error; err != nil {
logrus.WithError(err).Error("Failed to update API")
http.Error(w, "更新接口失败", http.StatusInternalServerError)
return
}
response := map[string]interface{}{
"success": true,
"message": "接口更新成功",
"data": api,
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
}
// APIGetAppsHandler 获取应用列表(用于接口页面的应用选择器)
func APIGetAppsHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
// 获取数据库连接
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
if err := db.Select("uuid, name").Order("created_at ASC").Find(&apps).Error; err != nil {
logrus.WithError(err).Error("Failed to fetch apps")
http.Error(w, "Internal server error", http.StatusInternalServerError)
return
}
response := map[string]interface{}{
"success": true,
"data": apps,
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
}
func APIGenerateKeysHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
var req struct {
Side string `json:"side"` // submit | return
Algorithm int `json:"algorithm"` // 与 models.Algorithm* 对应
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "Invalid JSON", http.StatusBadRequest)
return
}
if req.Side != "submit" && req.Side != "return" {
http.Error(w, "side参数必须为submit或return", http.StatusBadRequest)
return
}
if !models.IsValidAlgorithm(req.Algorithm) {
http.Error(w, "无效的算法类型", http.StatusBadRequest)
return
}
// 根据算法生成密钥/证书
result := map[string]interface{}{}
switch req.Algorithm {
case models.AlgorithmNone:
// 不加密不生成任何密钥
result["public_key"] = ""
result["private_key"] = ""
case models.AlgorithmRC4:
// 生成16字节随机密钥并返回16位十六进制大写
bytes := make([]byte, 8)
if _, err := rand.Read(bytes); err != nil {
logrus.WithError(err).Error("Failed to generate RC4 key")
http.Error(w, "生成RC4密钥失败", http.StatusInternalServerError)
return
}
result["public_key"] = ""
result["private_key"] = strings.ToUpper(hex.EncodeToString(bytes))
case models.AlgorithmRSA, models.AlgorithmRSADynamic:
// 生成RSA 2048密钥对返回PEM明文字符串
key, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
logrus.WithError(err).Error("Failed to generate RSA key pair")
http.Error(w, "生成RSA密钥失败", http.StatusInternalServerError)
return
}
privBytes := x509.MarshalPKCS1PrivateKey(key)
pubBytes := x509.MarshalPKCS1PublicKey(&key.PublicKey)
privPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: privBytes})
pubPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PUBLIC KEY", Bytes: pubBytes})
result["private_key"] = string(privPEM)
result["public_key"] = string(pubPEM)
case models.AlgorithmEasy:
// 生成易加密密钥对,返回逗号分隔的整数数组字符串
encryptKey, _, err := encrypt.GenerateEasyKey()
if err != nil {
logrus.WithError(err).Error("Failed to generate Easy encryption key")
http.Error(w, "生成易加密密钥失败", http.StatusInternalServerError)
return
}
result["public_key"] = ""
result["private_key"] = encrypt.FormatKeyAsString(encryptKey)
default:
http.Error(w, "不支持的算法类型", http.StatusBadRequest)
return
}
response := map[string]interface{}{
"success": true,
"message": "生成成功",
"data": result,
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
}
func APIResetKeyHandler(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, "Internal server error", http.StatusInternalServerError)
return
}
var api models.API
if err := db.First(&api, req.ID).Error; err != nil {
http.Error(w, "接口不存在", http.StatusNotFound)
return
}
// 生成新的16位大写十六进制密钥
bytes := make([]byte, 8)
if _, err := rand.Read(bytes); err != nil {
logrus.WithError(err).Error("Failed to generate random API key")
http.Error(w, "生成密钥失败", http.StatusInternalServerError)
return
}
newKey := strings.ToUpper(hex.EncodeToString(bytes))
if err := db.Model(&api).Update("api_key", newKey).Error; err != nil {
logrus.WithError(err).Error("Failed to update API key")
http.Error(w, "更新密钥失败", http.StatusInternalServerError)
return
}
response := map[string]interface{}{
"success": true,
"message": "接口密钥重置成功",
"data": map[string]interface{}{
"id": api.ID,
"api_key": newKey,
},
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
}

View File

@@ -88,6 +88,76 @@ func AppsListHandler(w http.ResponseWriter, r *http.Request) {
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 {
@@ -294,12 +364,73 @@ func AppCreateHandler(w http.ResponseWriter, r *http.Request) {
return
}
if err := db.Create(&app).Error; err != nil {
// 开始事务
tx := db.Begin()
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")
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")
response := map[string]interface{}{
"code": 0,
"msg": "创建成功",
@@ -408,13 +539,51 @@ func AppDeleteHandler(w http.ResponseWriter, r *http.Request) {
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
}
// 删除应用
if err := db.Delete(&models.App{}, req.ID).Error; err != nil {
if err := tx.Delete(&models.App{}, req.ID).Error; err != nil {
tx.Rollback()
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")
response := map[string]interface{}{
"code": 0,
"msg": "删除成功",
@@ -452,13 +621,59 @@ func AppsBatchDeleteHandler(w http.ResponseWriter, r *http.Request) {
return
}
// 批量删除
if err := db.Delete(&models.App{}, req.IDs).Error; err != nil {
// 开始事务
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()
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")
response := map[string]interface{}{
"code": 0,
"msg": "批量删除成功",
@@ -523,6 +738,108 @@ func AppsBatchUpdateStatusHandler(w http.ResponseWriter, r *http.Request) {
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 {