功能: - 前台导航: 分类Tab切换、实时搜索、健康状态指示、响应式适配 - 后台管理: 服务/分类CRUD、系统设置、登录认证(bcrypt) - 健康检查: 定时检测(5min)、独立检查URL、三态指示(在线/离线/未检测) - 云端备份: WebDAV上传/下载/恢复/删除、定时自动备份、本地备份管理 技术栈: Go + Gin + GORM + SQLite
158 lines
4.8 KiB
Go
158 lines
4.8 KiB
Go
package handlers
|
||
|
||
import (
|
||
"net/http"
|
||
"strconv"
|
||
"tonav-go/database"
|
||
"tonav-go/models"
|
||
|
||
"github.com/gin-gonic/gin"
|
||
)
|
||
|
||
// API 响应结构
|
||
type Response struct {
|
||
Success bool `json:"success"`
|
||
Message string `json:"message"`
|
||
Data interface{} `json:"data,omitempty"`
|
||
}
|
||
|
||
// GetServices 获取所有服务
|
||
func GetServices(c *gin.Context) {
|
||
var services []models.Service
|
||
database.DB.Order("category_id asc, sort_order desc").Find(&services)
|
||
c.JSON(http.StatusOK, Response{Success: true, Data: services})
|
||
}
|
||
|
||
// SaveService 创建或更新服务
|
||
func SaveService(c *gin.Context) {
|
||
var service models.Service
|
||
if err := c.ShouldBindJSON(&service); err != nil {
|
||
c.JSON(http.StatusBadRequest, Response{Success: false, Message: "请求数据格式错误: " + err.Error()})
|
||
return
|
||
}
|
||
|
||
// 验证必填字段
|
||
if service.Name == "" {
|
||
c.JSON(http.StatusBadRequest, Response{Success: false, Message: "服务名称不能为空"})
|
||
return
|
||
}
|
||
if service.URL == "" {
|
||
c.JSON(http.StatusBadRequest, Response{Success: false, Message: "服务地址不能为空"})
|
||
return
|
||
}
|
||
|
||
// 如果 URL 路径中有 id,使用路径参数
|
||
if idStr := c.Param("id"); idStr != "" {
|
||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||
if err == nil {
|
||
service.ID = uint(id)
|
||
}
|
||
}
|
||
|
||
if service.ID > 0 {
|
||
// 更新时只更新指定字段,避免覆盖 created_at 等
|
||
result := database.DB.Model(&models.Service{}).Where("id = ?", service.ID).Updates(map[string]interface{}{
|
||
"name": service.Name,
|
||
"url": service.URL,
|
||
"description": service.Description,
|
||
"icon": service.Icon,
|
||
"category_id": service.CategoryID,
|
||
"tags": service.Tags,
|
||
"is_enabled": service.IsEnabled,
|
||
"sort_order": service.SortOrder,
|
||
"health_check_url": service.HealthCheckURL,
|
||
"health_check_enabled": service.HealthCheckEnabled,
|
||
})
|
||
if result.Error != nil {
|
||
c.JSON(http.StatusInternalServerError, Response{Success: false, Message: "更新失败: " + result.Error.Error()})
|
||
return
|
||
}
|
||
} else {
|
||
if err := database.DB.Create(&service).Error; err != nil {
|
||
c.JSON(http.StatusInternalServerError, Response{Success: false, Message: "创建失败: " + err.Error()})
|
||
return
|
||
}
|
||
}
|
||
|
||
c.JSON(http.StatusOK, Response{Success: true, Message: "保存成功", Data: service})
|
||
}
|
||
|
||
// DeleteService 删除服务
|
||
func DeleteService(c *gin.Context) {
|
||
id := c.Param("id")
|
||
if id == "" {
|
||
c.JSON(http.StatusBadRequest, Response{Success: false, Message: "缺少服务ID"})
|
||
return
|
||
}
|
||
if err := database.DB.Delete(&models.Service{}, id).Error; err != nil {
|
||
c.JSON(http.StatusInternalServerError, Response{Success: false, Message: "删除失败"})
|
||
return
|
||
}
|
||
c.JSON(http.StatusOK, Response{Success: true, Message: "删除成功"})
|
||
}
|
||
|
||
// GetCategories 获取所有分类
|
||
func GetCategories(c *gin.Context) {
|
||
var categories []models.Category
|
||
database.DB.Order("sort_order desc").Find(&categories)
|
||
c.JSON(http.StatusOK, Response{Success: true, Data: categories})
|
||
}
|
||
|
||
// SaveCategory 保存分类
|
||
func SaveCategory(c *gin.Context) {
|
||
var category models.Category
|
||
if err := c.ShouldBindJSON(&category); err != nil {
|
||
c.JSON(http.StatusBadRequest, Response{Success: false, Message: "请求数据格式错误: " + err.Error()})
|
||
return
|
||
}
|
||
|
||
if category.Name == "" {
|
||
c.JSON(http.StatusBadRequest, Response{Success: false, Message: "分类名称不能为空"})
|
||
return
|
||
}
|
||
|
||
// 如果 URL 路径中有 id
|
||
if idStr := c.Param("id"); idStr != "" {
|
||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||
if err == nil {
|
||
category.ID = uint(id)
|
||
}
|
||
}
|
||
|
||
if category.ID > 0 {
|
||
result := database.DB.Model(&models.Category{}).Where("id = ?", category.ID).Updates(map[string]interface{}{
|
||
"name": category.Name,
|
||
"sort_order": category.SortOrder,
|
||
})
|
||
if result.Error != nil {
|
||
c.JSON(http.StatusInternalServerError, Response{Success: false, Message: "更新失败: " + result.Error.Error()})
|
||
return
|
||
}
|
||
} else {
|
||
if err := database.DB.Create(&category).Error; err != nil {
|
||
c.JSON(http.StatusInternalServerError, Response{Success: false, Message: "创建失败: " + err.Error()})
|
||
return
|
||
}
|
||
}
|
||
c.JSON(http.StatusOK, Response{Success: true, Message: "保存成功", Data: category})
|
||
}
|
||
|
||
// DeleteCategory 删除分类
|
||
func DeleteCategory(c *gin.Context) {
|
||
id := c.Param("id")
|
||
if id == "" {
|
||
c.JSON(http.StatusBadRequest, Response{Success: false, Message: "缺少分类ID"})
|
||
return
|
||
}
|
||
// 检查是否有服务属于该分类
|
||
var count int64
|
||
database.DB.Model(&models.Service{}).Where("category_id = ?", id).Count(&count)
|
||
if count > 0 {
|
||
c.JSON(http.StatusBadRequest, Response{Success: false, Message: "该分类下仍有服务,无法删除"})
|
||
return
|
||
}
|
||
|
||
database.DB.Delete(&models.Category{}, id)
|
||
c.JSON(http.StatusOK, Response{Success: true, Message: "删除成功"})
|
||
}
|