feat: ToNav-go v1.0.0 - 内部服务导航系统
功能: - 前台导航: 分类Tab切换、实时搜索、健康状态指示、响应式适配 - 后台管理: 服务/分类CRUD、系统设置、登录认证(bcrypt) - 健康检查: 定时检测(5min)、独立检查URL、三态指示(在线/离线/未检测) - 云端备份: WebDAV上传/下载/恢复/删除、定时自动备份、本地备份管理 技术栈: Go + Gin + GORM + SQLite
This commit is contained in:
221
handlers/settings.go
Normal file
221
handlers/settings.go
Normal file
@@ -0,0 +1,221 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"time"
|
||||
"tonav-go/database"
|
||||
"tonav-go/models"
|
||||
config "tonav-go/utils"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// GetSettings 获取设置
|
||||
func GetSettings(c *gin.Context) {
|
||||
var settings []models.Setting
|
||||
database.DB.Find(&settings)
|
||||
|
||||
res := make(map[string]string)
|
||||
for _, s := range settings {
|
||||
res[s.Key] = s.Value
|
||||
}
|
||||
c.JSON(http.StatusOK, res)
|
||||
}
|
||||
|
||||
// SaveSettings 保存设置
|
||||
func SaveSettings(c *gin.Context) {
|
||||
var input map[string]string
|
||||
if err := c.ShouldBindJSON(&input); err != nil {
|
||||
c.JSON(http.StatusBadRequest, Response{Success: false, Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
for k, v := range input {
|
||||
database.DB.Save(&models.Setting{Key: k, Value: v})
|
||||
}
|
||||
c.JSON(http.StatusOK, Response{Success: true, Message: "设置已保存"})
|
||||
}
|
||||
|
||||
// getWebDAVClient 从数据库配置构建 WebDAV 客户端
|
||||
func getWebDAVClient() (*config.WebDAVClient, error) {
|
||||
var url, user, pass models.Setting
|
||||
database.DB.Where("key = ?", "webdav_url").First(&url)
|
||||
database.DB.Where("key = ?", "webdav_user").First(&user)
|
||||
database.DB.Where("key = ?", "webdav_password").First(&pass)
|
||||
|
||||
if url.Value == "" {
|
||||
return nil, fmt.Errorf("未配置 WebDAV URL")
|
||||
}
|
||||
return config.NewWebDAVClient(url.Value, user.Value, pass.Value), nil
|
||||
}
|
||||
|
||||
// RunCloudBackup 执行云端备份
|
||||
func RunCloudBackup(c *gin.Context) {
|
||||
client, err := getWebDAVClient()
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, Response{Success: false, Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// 获取数据库路径
|
||||
cfg := config.LoadConfig()
|
||||
backupPath, err := config.CreateBackup(cfg.DBPath)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, Response{Success: false, Message: "本地备份失败: " + err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// 上传时只用文件名,不带路径
|
||||
remoteName := filepath.Base(backupPath)
|
||||
err = client.Upload(backupPath, remoteName)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, Response{Success: false, Message: "云端上传失败: " + err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// 清理本地旧备份,保留最近5份
|
||||
config.CleanOldBackups(5)
|
||||
|
||||
c.JSON(http.StatusOK, Response{Success: true, Message: "备份成功: " + remoteName})
|
||||
}
|
||||
|
||||
// ListCloudBackups 列出云端备份
|
||||
func ListCloudBackups(c *gin.Context) {
|
||||
client, err := getWebDAVClient()
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{"files": []interface{}{}})
|
||||
return
|
||||
}
|
||||
|
||||
files, err := client.List()
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"files": files})
|
||||
}
|
||||
|
||||
// DeleteCloudBackup 删除云端备份
|
||||
func DeleteCloudBackup(c *gin.Context) {
|
||||
name := c.Query("name")
|
||||
if name == "" {
|
||||
c.JSON(http.StatusBadRequest, Response{Success: false, Message: "缺少备份文件名"})
|
||||
return
|
||||
}
|
||||
|
||||
client, err := getWebDAVClient()
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, Response{Success: false, Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if err := client.Delete(name); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, Response{Success: false, Message: "删除失败: " + err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, Response{Success: true, Message: "已删除: " + name})
|
||||
}
|
||||
|
||||
// RestoreCloudBackup 从云端备份恢复
|
||||
func RestoreCloudBackup(c *gin.Context) {
|
||||
name := c.Query("name")
|
||||
if name == "" {
|
||||
c.JSON(http.StatusBadRequest, Response{Success: false, Message: "缺少备份文件名"})
|
||||
return
|
||||
}
|
||||
|
||||
client, err := getWebDAVClient()
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, Response{Success: false, Message: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
cfg := config.LoadConfig()
|
||||
|
||||
// 恢复前先备份当前数据库
|
||||
preBackup, err := config.CreateBackup(cfg.DBPath)
|
||||
if err != nil {
|
||||
log.Printf("恢复前备份失败: %v", err)
|
||||
} else {
|
||||
log.Printf("恢复前已备份当前数据库: %s", preBackup)
|
||||
}
|
||||
|
||||
// 下载云端备份到临时文件
|
||||
tmpPath := "backups/restore_tmp.db"
|
||||
if err := client.Download(name, tmpPath); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, Response{Success: false, Message: "下载备份失败: " + err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// 关闭当前数据库连接
|
||||
sqlDB, err := database.DB.DB()
|
||||
if err == nil {
|
||||
sqlDB.Close()
|
||||
}
|
||||
|
||||
// 用下载的备份替换当前数据库
|
||||
if err := config.ReplaceDB(tmpPath, cfg.DBPath); err != nil {
|
||||
// 尝试重新连接原数据库
|
||||
database.InitDB(cfg.DBPath)
|
||||
c.JSON(http.StatusInternalServerError, Response{Success: false, Message: "替换数据库失败: " + err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// 重新初始化数据库连接
|
||||
database.InitDB(cfg.DBPath)
|
||||
|
||||
c.JSON(http.StatusOK, Response{Success: true, Message: fmt.Sprintf("已从 %s 恢复,恢复前备份: %s", name, filepath.Base(preBackup))})
|
||||
}
|
||||
|
||||
// StartAutoBackup 启动定时自动备份
|
||||
func StartAutoBackup() {
|
||||
go func() {
|
||||
for {
|
||||
// 计算距离下一个凌晨3点的时间
|
||||
now := time.Now()
|
||||
next := time.Date(now.Year(), now.Month(), now.Day(), 3, 0, 0, 0, now.Location())
|
||||
if next.Before(now) {
|
||||
next = next.Add(24 * time.Hour)
|
||||
}
|
||||
timer := time.NewTimer(next.Sub(now))
|
||||
<-timer.C
|
||||
|
||||
// 检查是否启用了自动备份
|
||||
var setting models.Setting
|
||||
database.DB.Where("key = ?", "auto_backup").First(&setting)
|
||||
if setting.Value != "true" {
|
||||
continue
|
||||
}
|
||||
|
||||
log.Println("开始执行自动备份...")
|
||||
doAutoBackup()
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func doAutoBackup() {
|
||||
client, err := getWebDAVClient()
|
||||
if err != nil {
|
||||
log.Printf("自动备份失败(WebDAV未配置): %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
cfg := config.LoadConfig()
|
||||
backupPath, err := config.CreateBackup(cfg.DBPath)
|
||||
if err != nil {
|
||||
log.Printf("自动备份失败(本地备份): %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
remoteName := filepath.Base(backupPath)
|
||||
if err := client.Upload(backupPath, remoteName); err != nil {
|
||||
log.Printf("自动备份失败(上传): %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
config.CleanOldBackups(5)
|
||||
log.Printf("自动备份成功: %s", remoteName)
|
||||
}
|
||||
Reference in New Issue
Block a user