Files
ToNav-go/handlers/api.go
openclaw efaf787981 feat: ToNav-go v1.0.0 - 内部服务导航系统
功能:
- 前台导航: 分类Tab切换、实时搜索、健康状态指示、响应式适配
- 后台管理: 服务/分类CRUD、系统设置、登录认证(bcrypt)
- 健康检查: 定时检测(5min)、独立检查URL、三态指示(在线/离线/未检测)
- 云端备份: WebDAV上传/下载/恢复/删除、定时自动备份、本地备份管理

技术栈: Go + Gin + GORM + SQLite
2026-02-14 05:09:23 +08:00

158 lines
4.8 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 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: "删除成功"})
}