181 lines
5.0 KiB
Go
181 lines
5.0 KiB
Go
package cmd
|
||
|
||
import (
|
||
"os"
|
||
"sort"
|
||
"time"
|
||
|
||
"github.com/evil7/hostsync/core"
|
||
"github.com/evil7/hostsync/utils"
|
||
"github.com/robfig/cron/v3"
|
||
"github.com/spf13/cobra"
|
||
)
|
||
|
||
// cronCmd 定时任务命令
|
||
var cronCmd = &cobra.Command{
|
||
Use: "cron [blockName] [cronExpression]",
|
||
Short: "管理定时任务", Long: `管理块的定时任务配置。使用6字段cron表达式格式,支持秒级精度。
|
||
|
||
cron表达式格式: 秒 分 时 日 月 星期
|
||
|
||
示例:
|
||
hostsync cron # 列出所有定时任务(默认)
|
||
hostsync cron list # 列出所有定时任务
|
||
hostsync cron github "0 0 0 * * *" # 设置每日午夜更新
|
||
hostsync cron github "*/30 * * * * *" # 每30秒更新一次
|
||
hostsync cron github # 清除定时任务
|
||
|
||
常用cron表达式:
|
||
0 0 0 * * * # 每天午夜
|
||
0 0 */4 * * * # 每4小时
|
||
0 0 9 * * 1 # 每周一上午9点
|
||
0 */10 * * * * # 每10分钟
|
||
*/30 * * * * * # 每30秒
|
||
0 */5 * * * * # 每5分钟
|
||
0 0 12 * * * # 每天中午12点`,
|
||
Args: cobra.RangeArgs(0, 2), // 修改为允许0-2个参数
|
||
Run: runCron,
|
||
}
|
||
|
||
func runCron(cmd *cobra.Command, args []string) {
|
||
// 如果没有参数或第一个参数是"list",显示定时任务列表
|
||
if len(args) == 0 || (len(args) == 1 && args[0] == "list") {
|
||
listCronJobs()
|
||
return
|
||
}
|
||
|
||
blockName := args[0]
|
||
if !utils.ValidateBlockName(blockName) {
|
||
utils.LogError("无效的块名称: %s", blockName)
|
||
utils.LogError("块名称只能包含字母、数字、下划线和连字符")
|
||
os.Exit(1)
|
||
}
|
||
|
||
// 如果有两个参数,先验证cron表达式(不需要管理员权限)
|
||
if len(args) == 2 {
|
||
cronExpr := args[1]
|
||
cronManager := core.NewCronManager()
|
||
if err := cronManager.ValidateCronExpression(cronExpr); err != nil {
|
||
utils.LogError("无效的cron表达式: %v", err)
|
||
utils.LogInfo("cron表达式格式: 秒 分 时 日 月 星期")
|
||
utils.LogInfo("常用示例:")
|
||
utils.LogInfo(" \"0 0 0 * * *\" - 每日午夜")
|
||
utils.LogInfo(" \"0 */30 * * * *\" - 每30分钟")
|
||
utils.LogInfo(" \"*/30 * * * * *\" - 每30秒")
|
||
utils.LogInfo("更多信息: https://pkg.go.dev/github.com/robfig/cron")
|
||
os.Exit(1)
|
||
}
|
||
}
|
||
|
||
// 验证通过后,检查管理员权限
|
||
utils.CheckAdmin()
|
||
|
||
hm := core.NewHostsManager()
|
||
if err := hm.Load(); err != nil {
|
||
utils.LogError("加载hosts文件失败: %v", err)
|
||
os.Exit(1)
|
||
}
|
||
|
||
block := hm.GetBlock(blockName)
|
||
if block == nil {
|
||
utils.LogWarning("块不存在: %s", blockName)
|
||
os.Exit(1)
|
||
}
|
||
|
||
if len(args) == 1 {
|
||
// 清除定时任务
|
||
clearCronJob(hm, blockName)
|
||
} else {
|
||
// 设置定时任务
|
||
cronExpr := args[1]
|
||
setCronJob(hm, blockName, cronExpr)
|
||
}
|
||
}
|
||
|
||
func setCronJob(hm *core.HostsManager, blockName, cronExpr string) {
|
||
// cron表达式已经在上层验证过了,这里直接设置
|
||
block := hm.GetBlock(blockName)
|
||
block.CronJob = cronExpr
|
||
|
||
if err := hm.Save(); err != nil {
|
||
utils.LogError("保存配置失败: %v", err)
|
||
os.Exit(1)
|
||
}
|
||
|
||
utils.LogSuccess("已设置定时任务: %s (%s)", blockName, cronExpr)
|
||
}
|
||
|
||
func clearCronJob(hm *core.HostsManager, blockName string) {
|
||
block := hm.GetBlock(blockName)
|
||
if block.CronJob == "" {
|
||
utils.LogInfo("块 %s 没有设置定时任务", blockName)
|
||
return
|
||
}
|
||
|
||
block.CronJob = ""
|
||
|
||
if err := hm.Save(); err != nil {
|
||
utils.LogError("保存配置失败: %v", err)
|
||
os.Exit(1)
|
||
}
|
||
|
||
utils.LogSuccess("已清除定时任务: %s", blockName)
|
||
}
|
||
|
||
func listCronJobs() {
|
||
hm := core.NewHostsManager()
|
||
if err := hm.Load(); err != nil {
|
||
utils.LogError("加载hosts文件失败: %v", err)
|
||
os.Exit(1)
|
||
}
|
||
|
||
// 收集有定时任务的块名称
|
||
var cronBlockNames []string
|
||
for name, block := range hm.Blocks {
|
||
if block.CronJob != "" {
|
||
cronBlockNames = append(cronBlockNames, name)
|
||
}
|
||
}
|
||
if len(cronBlockNames) == 0 {
|
||
utils.LogInfo("没有找到任何定时任务")
|
||
return
|
||
}
|
||
|
||
// 对块名称进行排序,保持与 list 命令一致
|
||
sort.Strings(cronBlockNames)
|
||
|
||
utils.LogResult("定时任务列表 (%d 个)\n", len(cronBlockNames))
|
||
|
||
headers := []string{"块名称", "Cron表达式", "最后更新", "下次执行"}
|
||
var rows [][]string
|
||
|
||
for _, name := range cronBlockNames {
|
||
block := hm.Blocks[name]
|
||
lastUpdate := "从未更新"
|
||
if !block.UpdateAt.IsZero() {
|
||
lastUpdate = block.UpdateAt.Format("2006-01-02 15:04")
|
||
}
|
||
|
||
// 计算下次执行时间(简化版本)
|
||
nextRun := calculateNextRun(block.CronJob)
|
||
rows = append(rows, []string{block.Name, block.CronJob, lastUpdate, nextRun})
|
||
}
|
||
// 设置合适的列宽
|
||
columnWidths := []int{12, 20, 17, 20}
|
||
utils.FormatTable(headers, rows, columnWidths)
|
||
}
|
||
|
||
// calculateNextRun 计算下次执行时间
|
||
func calculateNextRun(cronExpr string) string {
|
||
// 使用robfig/cron解析器解析6字段cron表达式
|
||
parser := cron.NewParser(cron.Second | cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow | cron.Descriptor)
|
||
schedule, err := parser.Parse(cronExpr)
|
||
if err != nil {
|
||
return "解析错误"
|
||
}
|
||
|
||
// 计算下次执行时间
|
||
nextTime := schedule.Next(time.Now())
|
||
return nextTime.Format("2006-01-02 15:04:05")
|
||
}
|