fix: resolve critical issues and improve task management
- Fix path traversal vulnerability in downloadVideo handler by adding download directory whitelist validation - Add graceful shutdown with signal handling for task persistence - Fix division by zero panic in progressBar.percent() when total <= 0 - Add GetDB() function that returns error instead of using log.Fatal - Change deleteTask to only remove database records, preserve downloaded files - Add paused status for interrupted tasks on shutdown Co-Authored-By: Claude
This commit is contained in:
+53
-9
@@ -1,15 +1,20 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"embed"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"bilidown/internal/logger"
|
||||
"bilidown/internal/router"
|
||||
"bilidown/internal/task"
|
||||
"bilidown/internal/util"
|
||||
|
||||
_ "modernc.org/sqlite"
|
||||
@@ -21,11 +26,12 @@ var staticFiles embed.FS
|
||||
const (
|
||||
HTTP_PORT = 8098 // HTTP 服务器端口
|
||||
HTTP_HOST = "" // HTTP 服务器主机
|
||||
VERSION = "v2.1.1" // 软件版本号
|
||||
VERSION = "v2.1.2" // 软件版本号
|
||||
)
|
||||
|
||||
var urlLocal = fmt.Sprintf("http://127.0.0.1:%d", HTTP_PORT)
|
||||
var ffmpegAvailable bool
|
||||
var server *http.Server
|
||||
|
||||
func main() {
|
||||
ffmpegAvailable = checkFFmpeg()
|
||||
@@ -33,7 +39,14 @@ func main() {
|
||||
mustInitTables()
|
||||
mustRunServer()
|
||||
logger.ServerStarted(HTTP_PORT, VERSION)
|
||||
select {} // 保持运行
|
||||
|
||||
// 等待中断信号,实现优雅退出
|
||||
quit := make(chan os.Signal, 1)
|
||||
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
|
||||
<-quit
|
||||
|
||||
logger.Info("正在关闭服务...")
|
||||
gracefulShutdown()
|
||||
}
|
||||
|
||||
// checkFFmpeg 检测 ffmpeg 的安装情况,返回是否可用
|
||||
@@ -59,14 +72,44 @@ func mustRunServer() {
|
||||
})
|
||||
http.Handle("/api/", http.StripPrefix("/api", apiRouter))
|
||||
// 启动 HTTP 服务器
|
||||
server = &http.Server{
|
||||
Addr: fmt.Sprintf("%s:%d", HTTP_HOST, HTTP_PORT),
|
||||
Handler: nil,
|
||||
}
|
||||
go func() {
|
||||
err := http.ListenAndServe(fmt.Sprintf("%s:%d", HTTP_HOST, HTTP_PORT), nil)
|
||||
if err != nil {
|
||||
log.Fatal("http.ListenAndServe:", err)
|
||||
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||
logger.Fatal("http.ListenAndServe: " + err.Error())
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// gracefulShutdown 优雅退出:保存任务状态并关闭服务
|
||||
func gracefulShutdown() {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// 等待活跃任务完成或超时
|
||||
task.GlobalTaskMux.Lock()
|
||||
activeCount := len(task.GlobalTaskList)
|
||||
task.GlobalTaskMux.Unlock()
|
||||
|
||||
if activeCount > 0 {
|
||||
logger.Infof("等待 %d 个活跃任务完成(最多10秒)...", activeCount)
|
||||
// 标记所有运行中的任务为暂停状态
|
||||
task.MarkAllTasksPaused()
|
||||
// 等待下载信号量释放
|
||||
task.GlobalDownloadSem.Wait()
|
||||
task.GlobalMergeSem.Wait()
|
||||
}
|
||||
|
||||
// 关闭 HTTP 服务器
|
||||
if err := server.Shutdown(ctx); err != nil {
|
||||
logger.Error("服务器关闭失败: " + err.Error())
|
||||
}
|
||||
|
||||
logger.Info("服务已关闭")
|
||||
}
|
||||
|
||||
// mustInitTables 初始化数据表
|
||||
func mustInitTables() {
|
||||
db := util.MustGetDB()
|
||||
@@ -129,17 +172,18 @@ func addMissingColumns(db *sql.DB) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// initHistoryTask 将上一次程序运行时未完成的任务状态变为 error
|
||||
// initHistoryTask 将上一次程序运行时未完成的任务状态变为 paused,支持恢复
|
||||
func initHistoryTask(db *sql.DB) error {
|
||||
util.SqliteLock.Lock()
|
||||
result, err := db.Exec(`UPDATE "task" SET "status" = 'error' WHERE "status" IN ('waiting', 'running')`)
|
||||
// 将 waiting 和 running 状态改为 paused,而非 error
|
||||
result, err := db.Exec(`UPDATE "task" SET "status" = 'paused' WHERE "status" IN ('waiting', 'running')`)
|
||||
util.SqliteLock.Unlock()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rowsAffected, _ := result.RowsAffected()
|
||||
if rowsAffected > 0 {
|
||||
logger.Infof("重置 %d 个未完成任务状态为 error", rowsAffected)
|
||||
logger.Infof("发现 %d 个未完成任务,状态已设为 paused", rowsAffected)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user