253 lines
6.6 KiB
Go
253 lines
6.6 KiB
Go
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)
|
|
if err := cleanOldRemoteBackups(client, 5); err != nil {
|
|
log.Printf("云端旧备份清理失败: %v", err)
|
|
}
|
|
|
|
c.JSON(http.StatusOK, Response{Success: true, Message: "备份成功: " + remoteName})
|
|
}
|
|
|
|
// cleanOldRemoteBackups 清理 WebDAV 上的旧备份,仅保留最近 keep 份。
|
|
func cleanOldRemoteBackups(client *config.WebDAVClient, keep int) error {
|
|
if keep < 1 {
|
|
keep = 1
|
|
}
|
|
files, err := client.List()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if len(files) <= keep {
|
|
return nil
|
|
}
|
|
var firstErr error
|
|
for _, file := range files[keep:] {
|
|
if err := client.Delete(file.Name); err != nil {
|
|
if firstErr == nil {
|
|
firstErr = fmt.Errorf("删除 %s: %w", file.Name, err)
|
|
}
|
|
continue
|
|
}
|
|
log.Printf("已清理云端旧备份: %s", file.Name)
|
|
}
|
|
return firstErr
|
|
}
|
|
|
|
// 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)
|
|
if err := cleanOldRemoteBackups(client, 5); err != nil {
|
|
log.Printf("云端旧备份清理失败: %v", err)
|
|
}
|
|
log.Printf("自动备份成功: %s", remoteName)
|
|
}
|