refactor: extract path logic to path.go and run go fmt on codebase

This commit is contained in:
duorameng
2026-05-22 14:52:51 +08:00
parent b6fd889661
commit 4c283a60d5
55 changed files with 409 additions and 420 deletions
+15 -15
View File
@@ -137,21 +137,21 @@ type TaskResult struct {
}
type Agent struct {
config *Config
configFile string
machineID string
scheduler *executor.Scheduler
cronManager *executor.CronManager
tasks map[string]*AgentTask // 本地任务缓存,用于执行 lookup
lastTaskCount int
mu sync.RWMutex
client *http.Client
wsConn *websocket.Conn
wsMu sync.Mutex
stopCh chan struct{}
wsStopCh chan struct{} // 用于停止当前 WebSocket 相关的 goroutine
taskLogs map[string][]string // 记录最近的日志行,用于失败显示
logMu sync.Mutex // taskLogs 的锁
config *Config
configFile string
machineID string
scheduler *executor.Scheduler
cronManager *executor.CronManager
tasks map[string]*AgentTask // 本地任务缓存,用于执行 lookup
lastTaskCount int
mu sync.RWMutex
client *http.Client
wsConn *websocket.Conn
wsMu sync.Mutex
stopCh chan struct{}
wsStopCh chan struct{} // 用于停止当前 WebSocket 相关的 goroutine
taskLogs map[string][]string // 记录最近的日志行,用于失败显示
logMu sync.Mutex // taskLogs 的锁
schedulerStarted bool // 调度器是否已经启动
}
+1 -76
View File
@@ -1,81 +1,6 @@
package constant
import (
"os"
"path/filepath"
"time"
)
var (
// ConfigPath 配置文件路径
ConfigPath string
// DataDir 数据目录
DataDir string
// DefaultDBPath 默认数据库路径
DefaultDBPath string
// WebDistDir 前端构建目录
WebDistDir string
// ScriptsWorkDir 脚本工作目录
ScriptsWorkDir string
)
func init() {
rootDir := ResolveAppRootDir()
ConfigPath = filepath.Clean(filepath.Join(rootDir, "configs", "config.ini"))
DataDir = filepath.Clean(filepath.Join(rootDir, "data"))
DefaultDBPath = filepath.Clean(filepath.Join(rootDir, "data", "baihu.db"))
WebDistDir = filepath.Clean(filepath.Join(rootDir, "web", "dist"))
ScriptsWorkDir = filepath.Clean(filepath.Join(rootDir, "data", "scripts"))
}
// ResolveAppRootDir 获取应用程序的绝对根目录路径。
func ResolveAppRootDir() string {
// 1. 检查当前工作目录(CWD)及其上级目录
if cwd, err := os.Getwd(); err == nil {
dir := cwd
for {
if _, err := os.Stat(filepath.Join(dir, "configs", "config.ini")); err == nil {
return dir
}
if _, err := os.Stat(filepath.Join(dir, "go.mod")); err == nil {
return dir
}
parent := filepath.Dir(dir)
if parent == dir {
break
}
dir = parent
}
}
// 2. 检查当前可执行文件路径及其上级目录
if exe, err := os.Executable(); err == nil {
dir := filepath.Dir(exe)
for {
if _, err := os.Stat(filepath.Join(dir, "configs", "config.ini")); err == nil {
return dir
}
if _, err := os.Stat(filepath.Join(dir, "go.mod")); err == nil {
return dir
}
parent := filepath.Dir(dir)
if parent == dir {
break
}
dir = parent
}
}
// 3. 兜底回退到当前工作目录
if cwd, err := os.Getwd(); err == nil {
return cwd
}
return "."
}
import "time"
const (
+77
View File
@@ -0,0 +1,77 @@
package constant
import (
"os"
"path/filepath"
)
var (
// ConfigPath 配置文件路径
ConfigPath string
// DataDir 数据目录
DataDir string
// DefaultDBPath 默认数据库路径
DefaultDBPath string
// WebDistDir 前端构建目录
WebDistDir string
// ScriptsWorkDir 脚本工作目录
ScriptsWorkDir string
)
func init() {
rootDir := ResolveAppRootDir()
ConfigPath = filepath.Clean(filepath.Join(rootDir, "configs", "config.ini"))
DataDir = filepath.Clean(filepath.Join(rootDir, "data"))
DefaultDBPath = filepath.Clean(filepath.Join(rootDir, "data", "baihu.db"))
WebDistDir = filepath.Clean(filepath.Join(rootDir, "web", "dist"))
ScriptsWorkDir = filepath.Clean(filepath.Join(rootDir, "data", "scripts"))
}
// ResolveAppRootDir 获取应用程序的绝对根目录路径。
func ResolveAppRootDir() string {
// 1. 检查当前工作目录(CWD)及其上级目录
if cwd, err := os.Getwd(); err == nil {
dir := cwd
for {
if _, err := os.Stat(filepath.Join(dir, "configs", "config.ini")); err == nil {
return dir
}
if _, err := os.Stat(filepath.Join(dir, "go.mod")); err == nil {
return dir
}
parent := filepath.Dir(dir)
if parent == dir {
break
}
dir = parent
}
}
// 2. 检查当前可执行文件路径及其上级目录
if exe, err := os.Executable(); err == nil {
dir := filepath.Dir(exe)
for {
if _, err := os.Stat(filepath.Join(dir, "configs", "config.ini")); err == nil {
return dir
}
if _, err := os.Stat(filepath.Join(dir, "go.mod")); err == nil {
return dir
}
parent := filepath.Dir(dir)
if parent == dir {
break
}
dir = parent
}
}
// 3. 兜底回退到当前工作目录
if cwd, err := os.Getwd(); err == nil {
return cwd
}
return "."
}
-1
View File
@@ -1,7 +1,6 @@
package controllers
import (
"strconv"
"sync"
"time"
+5 -5
View File
@@ -42,11 +42,11 @@ func (ec *EnvController) CreateEnvVar(c *gin.Context) {
userID := c.GetString("userID")
var req struct {
Name string `json:"name" binding:"required"`
Value string `json:"value" binding:"required"`
Remark string `json:"remark"`
Type string `json:"type"`
Hidden *bool `json:"hidden"`
Name string `json:"name" binding:"required"`
Value string `json:"value" binding:"required"`
Remark string `json:"remark"`
Type string `json:"type"`
Hidden *bool `json:"hidden"`
Enabled *bool `json:"enabled"`
}
-1
View File
@@ -1,7 +1,6 @@
package controllers
import (
"github.com/engigu/baihu-panel/internal/database"
"github.com/engigu/baihu-panel/internal/models"
"github.com/engigu/baihu-panel/internal/models/vo"
+1
View File
@@ -76,6 +76,7 @@ func (c *MiseController) VerifyCommand(ctx *gin.Context) {
}
utils.Success(ctx, gin.H{"command": cmd})
}
// UseGlobal 设置全局默认版本
func (c *MiseController) UseGlobal(ctx *gin.Context) {
var req struct {
@@ -1,7 +1,6 @@
package controllers
import (
"github.com/engigu/baihu-panel/internal/models/vo"
"github.com/engigu/baihu-panel/internal/services"
"github.com/engigu/baihu-panel/internal/utils"
+42 -43
View File
@@ -6,12 +6,12 @@ import (
"strings"
"github.com/engigu/baihu-panel/internal/constant"
"github.com/engigu/baihu-panel/internal/logger"
"github.com/engigu/baihu-panel/internal/models"
"github.com/engigu/baihu-panel/internal/models/vo"
"github.com/engigu/baihu-panel/internal/services"
"github.com/engigu/baihu-panel/internal/services/tasks"
"github.com/engigu/baihu-panel/internal/utils"
"github.com/engigu/baihu-panel/internal/logger"
"github.com/gin-gonic/gin"
"os"
@@ -59,26 +59,26 @@ func resolveWorkDir(workDir string) string {
func (tc *TaskController) CreateTask(c *gin.Context) {
var req struct {
Name string `json:"name" binding:"required"`
Remark string `json:"remark"`
Command string `json:"command"`
PreCommand string `json:"pre_command"`
PostCommand string `json:"post_command"`
Tags string `json:"tags"`
Type string `json:"type"`
Config string `json:"config"`
Schedule string `json:"schedule"`
Timeout int `json:"timeout"`
WorkDir string `json:"work_dir"`
CleanConfig string `json:"clean_config"`
Envs string `json:"envs"`
Languages models.TaskLanguages `json:"languages"`
AgentID *string `json:"agent_id"`
TriggerType string `json:"trigger_type"`
RetryCount int `json:"retry_count"`
RetryInterval int `json:"retry_interval"`
RandomRange int `json:"random_range"`
PinType string `json:"pin_type"`
Name string `json:"name" binding:"required"`
Remark string `json:"remark"`
Command string `json:"command"`
PreCommand string `json:"pre_command"`
PostCommand string `json:"post_command"`
Tags string `json:"tags"`
Type string `json:"type"`
Config string `json:"config"`
Schedule string `json:"schedule"`
Timeout int `json:"timeout"`
WorkDir string `json:"work_dir"`
CleanConfig string `json:"clean_config"`
Envs string `json:"envs"`
Languages models.TaskLanguages `json:"languages"`
AgentID *string `json:"agent_id"`
TriggerType string `json:"trigger_type"`
RetryCount int `json:"retry_count"`
RetryInterval int `json:"retry_interval"`
RandomRange int `json:"random_range"`
PinType string `json:"pin_type"`
}
if err := c.ShouldBindJSON(&req); err != nil {
@@ -251,27 +251,27 @@ func (tc *TaskController) UpdateTask(c *gin.Context) {
}
var req struct {
Name string `json:"name"`
Remark string `json:"remark"`
Command string `json:"command"`
PreCommand string `json:"pre_command"`
PostCommand string `json:"post_command"`
Tags string `json:"tags"`
Type string `json:"type"`
Config string `json:"config"`
Schedule string `json:"schedule"`
Timeout int `json:"timeout"`
WorkDir string `json:"work_dir"`
CleanConfig string `json:"clean_config"`
Envs string `json:"envs"`
Enabled bool `json:"enabled"`
Languages models.TaskLanguages `json:"languages"`
AgentID *string `json:"agent_id"`
TriggerType string `json:"trigger_type"`
RetryCount int `json:"retry_count"`
RetryInterval int `json:"retry_interval"`
RandomRange int `json:"random_range"`
PinType string `json:"pin_type"`
Name string `json:"name"`
Remark string `json:"remark"`
Command string `json:"command"`
PreCommand string `json:"pre_command"`
PostCommand string `json:"post_command"`
Tags string `json:"tags"`
Type string `json:"type"`
Config string `json:"config"`
Schedule string `json:"schedule"`
Timeout int `json:"timeout"`
WorkDir string `json:"work_dir"`
CleanConfig string `json:"clean_config"`
Envs string `json:"envs"`
Enabled bool `json:"enabled"`
Languages models.TaskLanguages `json:"languages"`
AgentID *string `json:"agent_id"`
TriggerType string `json:"trigger_type"`
RetryCount int `json:"retry_count"`
RetryInterval int `json:"retry_interval"`
RandomRange int `json:"random_range"`
PinType string `json:"pin_type"`
}
if err := c.ShouldBindJSON(&req); err != nil {
@@ -695,4 +695,3 @@ func (tc *TaskController) ToggleTask(c *gin.Context) {
utils.Success(c, vo.ToTaskVO(updatedTask))
}
+1 -2
View File
@@ -7,9 +7,9 @@ import (
"os"
"path/filepath"
"runtime"
"strings"
"sync"
"time"
"strings"
"unicode/utf8"
"github.com/engigu/baihu-panel/internal/constant"
@@ -23,7 +23,6 @@ import (
"golang.org/x/text/transform"
)
type TerminalController struct {
envService *services.EnvService
}
+3 -3
View File
@@ -64,9 +64,9 @@ func Init(cfg *Config) error {
log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer
gormlogger.Config{
SlowThreshold: time.Millisecond * 500, // 慢 SQL 阈值,默认是 200ms,这里改为 500ms
LogLevel: gormlogger.Warn, // 日志级别
IgnoreRecordNotFoundError: true, // 忽略 ErrRecordNotFound(找不到记录)错误
Colorful: true, // 禁用彩色打印
LogLevel: gormlogger.Warn, // 日志级别
IgnoreRecordNotFoundError: true, // 忽略 ErrRecordNotFound(找不到记录)错误
Colorful: true, // 禁用彩色打印
},
)
-1
View File
@@ -95,7 +95,6 @@ func getModelSignature(models []interface{}) string {
return hex.EncodeToString(hash[:])
}
// customMigrations 自定义迁移(处理 AutoMigrate 无法自动完成的变更)
func customMigrations() error {
// 检查 ql_tokens 表是否存在
+7 -8
View File
@@ -1,9 +1,9 @@
package executor
import (
"fmt"
"math/rand"
"strings"
"fmt"
"sync"
"time"
@@ -71,7 +71,6 @@ func (m *CronManager) Stop() {
m.logger.Infof("[CronManager] 调度管理服务已停止")
}
// AddTask 添加或更新计划任务
func (m *CronManager) AddTask(task CronTask) error {
m.mu.Lock()
@@ -109,15 +108,15 @@ func (m *CronManager) AddTask(task CronTask) error {
return m.OnTrigger(task)
}
return &ExecutionRequest{
TaskID: taskID,
Name: name,
TaskID: taskID,
Name: name,
Command: cmd,
PreCommand: task.GetPreCommand(),
PostCommand: task.GetPostCommand(),
Type: TaskTypeCron,
Timeout: timeout,
WorkDir: workDir,
Envs: func() []string {
Type: TaskTypeCron,
Timeout: timeout,
WorkDir: workDir,
Envs: func() []string {
if vars := task.GetEnvVars(); len(vars) > 0 {
return vars
}
+4 -4
View File
@@ -46,10 +46,10 @@ type Request struct {
PreCommand string
PostCommand string
WorkDir string
Envs []string
Timeout int // 任务超时时间(分钟)
Languages []map[string]string
UseMise bool
Envs []string
Timeout int // 任务超时时间(分钟)
Languages []map[string]string
UseMise bool
}
// Result 任务执行结果
+20 -20
View File
@@ -63,21 +63,21 @@ const (
// ExecutionRequest 执行请求(标准接口)
type ExecutionRequest struct {
TaskID string // 任务 ID
LogID string // 日志 ID
Name string // 任务名称
Type TaskType // 任务类型
Command string // 命令
MaskedCommand string // 脱敏后的命令(用于日志和展示)
PreCommand string // 前置命令
PostCommand string // 后置命令
WorkDir string // 工作目录
Envs []string // 环境变量
Secrets []string // 需要脱敏的密码
Timeout int // 超时时间(分钟)
Languages []map[string]string // 语言环境配置
UseMise bool // 是否使用 mise
Metadata ExecutionMetadata // 额外元数据
TaskID string // 任务 ID
LogID string // 日志 ID
Name string // 任务名称
Type TaskType // 任务类型
Command string // 命令
MaskedCommand string // 脱敏后的命令(用于日志和展示)
PreCommand string // 前置命令
PostCommand string // 后置命令
WorkDir string // 工作目录
Envs []string // 环境变量
Secrets []string // 需要脱敏的密码
Timeout int // 超时时间(分钟)
Languages []map[string]string // 语言环境配置
UseMise bool // 是否使用 mise
Metadata ExecutionMetadata // 额外元数据
}
// ExecutionMetadata 执行额外元数据
@@ -211,11 +211,11 @@ func NewScheduler(config SchedulerConfig, handler SchedulerEventHandler) *Schedu
Command: req.Command,
PreCommand: req.PreCommand,
PostCommand: req.PostCommand,
WorkDir: req.WorkDir,
Envs: req.Envs,
Timeout: req.Timeout,
Languages: req.Languages,
UseMise: req.UseMise,
WorkDir: req.WorkDir,
Envs: req.Envs,
Timeout: req.Timeout,
Languages: req.Languages,
UseMise: req.UseMise,
}, stdout, stderr, hooks)
},
taskQueue: make(chan *ExecutionRequest, config.QueueSize),
+14 -14
View File
@@ -58,9 +58,9 @@ type Agent struct {
Hostname string `json:"hostname" gorm:"size:100"` // Agent 主机名
OS string `json:"os" gorm:"size:20"` // 操作系统
Arch string `json:"arch" gorm:"size:20"` // 架构
ForceUpdate bool `json:"force_update" gorm:"default:false"` // 强制更新标志
Enabled *bool `json:"enabled" gorm:"default:true"` // 是否启用
SchedulerConfig AgentSchedulerConfig `json:"scheduler_config" gorm:"type:text"` // 调度配置,以 JSON 字符串形式存储在 Text 类型字段中
ForceUpdate bool `json:"force_update" gorm:"default:false"` // 强制更新标志
Enabled *bool `json:"enabled" gorm:"default:true"` // 是否启用
SchedulerConfig AgentSchedulerConfig `json:"scheduler_config" gorm:"type:text"` // 调度配置,以 JSON 字符串形式存储在 Text 类型字段中
CreatedAt LocalTime `json:"created_at"`
UpdatedAt LocalTime `json:"updated_at"`
}
@@ -71,15 +71,15 @@ func (Agent) TableName() string {
// AgentToken Agent 令牌
type AgentToken struct {
ID string `json:"id" gorm:"primaryKey;size:20"`
Token string `json:"token" gorm:"size:64;uniqueIndex;not null"` // 令牌
Remark string `json:"remark" gorm:"size:255"` // 备注
MaxUses int `json:"max_uses" gorm:"default:0"` // 最大使用次数,0 表示无限制
UsedCount int `json:"used_count" gorm:"default:0"` // 已使用次数
ExpiresAt *LocalTime `json:"expires_at"` // 过期时间,null 表示永不过期
Enabled *bool `json:"enabled" gorm:"default:true"` // 是否启用
CreatedAt LocalTime `json:"created_at"`
UpdatedAt LocalTime `json:"updated_at"`
ID string `json:"id" gorm:"primaryKey;size:20"`
Token string `json:"token" gorm:"size:64;uniqueIndex;not null"` // 令牌
Remark string `json:"remark" gorm:"size:255"` // 备注
MaxUses int `json:"max_uses" gorm:"default:0"` // 最大使用次数,0 表示无限制
UsedCount int `json:"used_count" gorm:"default:0"` // 已使用次数
ExpiresAt *LocalTime `json:"expires_at"` // 过期时间,null 表示永不过期
Enabled *bool `json:"enabled" gorm:"default:true"` // 是否启用
CreatedAt LocalTime `json:"created_at"`
UpdatedAt LocalTime `json:"updated_at"`
}
func (AgentToken) TableName() string {
@@ -88,8 +88,8 @@ func (AgentToken) TableName() string {
// AgentTask Agent 任务配置(用于下发给 Agent)
type AgentTask struct {
ID string `json:"id"`
Name string `json:"name"`
ID string `json:"id"`
Name string `json:"name"`
Command string `json:"command"`
PreCommand string `json:"pre_command"`
PostCommand string `json:"post_command"`
+16 -17
View File
@@ -6,17 +6,16 @@ import (
// EnvironmentVariable represents an environment variable
type EnvironmentVariable struct {
ID string `json:"id" gorm:"primaryKey;size:20"`
Name string `json:"name" gorm:"size:255;not null"`
Value BigText `json:"value"`
Remark string `json:"remark" gorm:"size:500"`
Type string `json:"type" gorm:"size:20;default:'normal'"`
Hidden *bool `json:"hidden" gorm:"default:true"`
Enabled *bool `json:"enabled" gorm:"default:true"`
UserID string `json:"user_id" gorm:"size:20;index"`
CreatedAt LocalTime `json:"created_at"`
UpdatedAt LocalTime `json:"updated_at"`
ID string `json:"id" gorm:"primaryKey;size:20"`
Name string `json:"name" gorm:"size:255;not null"`
Value BigText `json:"value"`
Remark string `json:"remark" gorm:"size:500"`
Type string `json:"type" gorm:"size:20;default:'normal'"`
Hidden *bool `json:"hidden" gorm:"default:true"`
Enabled *bool `json:"enabled" gorm:"default:true"`
UserID string `json:"user_id" gorm:"size:20;index"`
CreatedAt LocalTime `json:"created_at"`
UpdatedAt LocalTime `json:"updated_at"`
}
func (EnvironmentVariable) TableName() string {
@@ -25,12 +24,12 @@ func (EnvironmentVariable) TableName() string {
// Script represents a script file
type Script struct {
ID string `json:"id" gorm:"primaryKey;size:20"`
Name string `json:"name" gorm:"size:255;not null"`
Content BigText `json:"content"`
UserID string `json:"user_id" gorm:"size:20;index"`
CreatedAt LocalTime `json:"created_at"`
UpdatedAt LocalTime `json:"updated_at"`
ID string `json:"id" gorm:"primaryKey;size:20"`
Name string `json:"name" gorm:"size:255;not null"`
Content BigText `json:"content"`
UserID string `json:"user_id" gorm:"size:20;index"`
CreatedAt LocalTime `json:"created_at"`
UpdatedAt LocalTime `json:"updated_at"`
}
func (Script) TableName() string {
+8 -8
View File
@@ -5,14 +5,14 @@ import (
)
type Language struct {
ID string `json:"id" gorm:"primaryKey;size:20"`
Plugin string `json:"plugin" gorm:"size:100;not null;index"`
Version string `json:"version" gorm:"size:100;not null;index"`
InstallPath string `json:"install_path" gorm:"size:255"`
Source string `json:"source" gorm:"size:255"`
InstalledAt *LocalTime `json:"installed_at"`
CreatedAt LocalTime `json:"created_at"`
UpdatedAt LocalTime `json:"updated_at"`
ID string `json:"id" gorm:"primaryKey;size:20"`
Plugin string `json:"plugin" gorm:"size:100;not null;index"`
Version string `json:"version" gorm:"size:100;not null;index"`
InstallPath string `json:"install_path" gorm:"size:255"`
Source string `json:"source" gorm:"size:255"`
InstalledAt *LocalTime `json:"installed_at"`
CreatedAt LocalTime `json:"created_at"`
UpdatedAt LocalTime `json:"updated_at"`
}
func (Language) TableName() string {
+8 -8
View File
@@ -6,14 +6,14 @@ import (
// NotifyBinding 事件绑定表
type NotifyBinding struct {
ID string `json:"id" gorm:"primaryKey;size:20"`
Type string `json:"type" gorm:"size:20;not null;index"` // system 或 task
Event string `json:"event" gorm:"size:50;not null;index"` // 事件类型
WayID string `json:"way_id" gorm:"size:20;not null;index"` // 通知渠道ID
DataID string `json:"data_id" gorm:"size:20;index"` // 关联ID,系统事件为空,任务事件为任务ID
Extra BigText `json:"extra"` // 额外配置(如是否开启日志推送等,对应 BindingExtra 结构)
CreatedAt LocalTime `json:"created_at"`
UpdatedAt LocalTime `json:"updated_at"`
ID string `json:"id" gorm:"primaryKey;size:20"`
Type string `json:"type" gorm:"size:20;not null;index"` // system 或 task
Event string `json:"event" gorm:"size:50;not null;index"` // 事件类型
WayID string `json:"way_id" gorm:"size:20;not null;index"` // 通知渠道ID
DataID string `json:"data_id" gorm:"size:20;index"` // 关联ID,系统事件为空,任务事件为任务ID
Extra BigText `json:"extra"` // 额外配置(如是否开启日志推送等,对应 BindingExtra 结构)
CreatedAt LocalTime `json:"created_at"`
UpdatedAt LocalTime `json:"updated_at"`
}
// BindingExtra 存储在 Extra 字段中的 JSON 配置
+7 -7
View File
@@ -6,13 +6,13 @@ import (
// NotifyWay 消息推送渠道
type NotifyWay struct {
ID string `json:"id" gorm:"primaryKey;size:20"`
Name string `json:"name" gorm:"size:100;not null"`
Type string `json:"type" gorm:"size:50;not null;index"`
Config BigText `json:"config"`
Enabled *bool `json:"enabled" gorm:"default:true;index"`
CreatedAt LocalTime `json:"created_at"`
UpdatedAt LocalTime `json:"updated_at"`
ID string `json:"id" gorm:"primaryKey;size:20"`
Name string `json:"name" gorm:"size:100;not null"`
Type string `json:"type" gorm:"size:50;not null;index"`
Config BigText `json:"config"`
Enabled *bool `json:"enabled" gorm:"default:true;index"`
CreatedAt LocalTime `json:"created_at"`
UpdatedAt LocalTime `json:"updated_at"`
}
func (NotifyWay) TableName() string {
+3 -3
View File
@@ -6,9 +6,9 @@ import (
// Setting 系统设置
type Setting struct {
ID string `json:"id" gorm:"primaryKey;size:20"`
Section string `json:"section" gorm:"size:50;not null;index:idx_section_key"`
Key string `json:"key" gorm:"size:100;not null;index:idx_section_key"`
ID string `json:"id" gorm:"primaryKey;size:20"`
Section string `json:"section" gorm:"size:50;not null;index:idx_section_key"`
Key string `json:"key" gorm:"size:100;not null;index:idx_section_key"`
Value BigText `json:"value"`
}
+41 -41
View File
@@ -43,14 +43,14 @@ type CleanConfig struct {
// RepoConfig 仓库同步配置
type RepoConfig struct {
SourceType string `json:"source_type"` // url 或 git
SourceURL string `json:"source_url"` // 源地址
TargetPath string `json:"target_path"` // 目标路径
Branch string `json:"branch"` // Git 分支
SparsePath string `json:"sparse_path"` // 稀疏检出路径(仅拉取指定目录或文件)
SingleFile bool `json:"single_file"` // 单文件模式(直接下载文件而非 sparse-checkout
Proxy string `json:"proxy"` // 代理类型: none, ghproxy, mirror, custom
ProxyURL string `json:"proxy_url"` // 自定义代理地址
SourceType string `json:"source_type"` // url 或 git
SourceURL string `json:"source_url"` // 源地址
TargetPath string `json:"target_path"` // 目标路径
Branch string `json:"branch"` // Git 分支
SparsePath string `json:"sparse_path"` // 稀疏检出路径(仅拉取指定目录或文件)
SingleFile bool `json:"single_file"` // 单文件模式(直接下载文件而非 sparse-checkout
Proxy string `json:"proxy"` // 代理类型: none, ghproxy, mirror, custom
ProxyURL string `json:"proxy_url"` // 自定义代理地址
AuthToken string `json:"auth_token"` // 认证 Token
WhitelistPaths string `json:"whitelist_paths"` // 同步时保留的路径及脚本筛选白名单关键词,逗号或竖线分割
Blacklist string `json:"blacklist"` // 脚本筛选黑名单关键词,竖线分割
@@ -69,37 +69,37 @@ type TaskConfig struct {
// Task 代表一个计划任务
type Task struct {
ID string `json:"id" gorm:"primaryKey;size:20"`
Name string `json:"name" gorm:"size:255;not null"`
Remark string `json:"remark" gorm:"size:255;default:''"`
PinType string `json:"pin_type" gorm:"size:20;default:none;index"` // 置顶类型: constant.PinTypeNone, constant.PinTypeTop
Command BigText `json:"command"` // 普通任务的命令
PreCommand BigText `json:"pre_command"` // 执行前的命令
PostCommand BigText `json:"post_command"` // 执行后的命令
Tags string `json:"tags" gorm:"size:255;default:''"` // 标签,逗号分隔
Type string `json:"type" gorm:"size:20;default:'task'"` // 任务类型: constant.TaskTypeNormal, constant.TaskTypeRepo
TriggerType string `json:"trigger_type" gorm:"size:25;default:'cron'"` // 触发类型: constant.TriggerTypeCron, constant.TriggerTypeBaihuStartup
Config BigText `json:"config"` // 配置 JSON(仓库同步配置等)
Schedule string `json:"schedule" gorm:"size:100"` // cron 表达式
Timeout int `json:"timeout" gorm:"default:30"` // 超时时间(分钟),默认30分钟
WorkDir string `json:"work_dir" gorm:"size:255;default:''"` // 工作目录,为空则使用 scripts 目录
CleanConfig string `json:"clean_config" gorm:"size:255;default:''"` // 清理配置 JSON
Envs BigText `json:"envs"` // 环境变量ID列表,逗号分隔
Languages TaskLanguages `json:"languages" gorm:"type:text"` // 针对本地任务的语言配置列表
AgentID *string `json:"agent_id" gorm:"size:20;index"` // Agent ID,为空表示本地执行
RetryCount int `json:"retry_count" gorm:"default:0"` // 失败重试次数
RetryInterval int `json:"retry_interval" gorm:"default:0"` // 失败重试间隔(秒)
RandomRange int `json:"random_range" gorm:"default:0"` // 随机延迟范围(秒)
Enabled *bool `json:"enabled" gorm:"default:true"`
RunningGo BigText `json:"running_go"` // 正在运行的 go routine id 数组 (JSON)
RuntimeEnvs []string `json:"-" gorm:"-"` // 运行时环境变量(非持久化)
RuntimeSecrets []string `json:"-" gorm:"-"` // 运行时安全机密(非持久化)
LastRun *LocalTime `json:"last_run"`
NextRun *LocalTime `json:"next_run"`
SourceID string `json:"source_id" gorm:"size:255;index"` // 脚本资源唯一标识(路径 sanitized)
RepoTaskID string `json:"repo_task_id" gorm:"size:20;index"` // 所属的仓库任务 ID
CreatedAt LocalTime `json:"created_at"`
UpdatedAt LocalTime `json:"updated_at"`
ID string `json:"id" gorm:"primaryKey;size:20"`
Name string `json:"name" gorm:"size:255;not null"`
Remark string `json:"remark" gorm:"size:255;default:''"`
PinType string `json:"pin_type" gorm:"size:20;default:none;index"` // 置顶类型: constant.PinTypeNone, constant.PinTypeTop
Command BigText `json:"command"` // 普通任务的命令
PreCommand BigText `json:"pre_command"` // 执行前的命令
PostCommand BigText `json:"post_command"` // 执行后的命令
Tags string `json:"tags" gorm:"size:255;default:''"` // 标签,逗号分隔
Type string `json:"type" gorm:"size:20;default:'task'"` // 任务类型: constant.TaskTypeNormal, constant.TaskTypeRepo
TriggerType string `json:"trigger_type" gorm:"size:25;default:'cron'"` // 触发类型: constant.TriggerTypeCron, constant.TriggerTypeBaihuStartup
Config BigText `json:"config"` // 配置 JSON(仓库同步配置等)
Schedule string `json:"schedule" gorm:"size:100"` // cron 表达式
Timeout int `json:"timeout" gorm:"default:30"` // 超时时间(分钟),默认30分钟
WorkDir string `json:"work_dir" gorm:"size:255;default:''"` // 工作目录,为空则使用 scripts 目录
CleanConfig string `json:"clean_config" gorm:"size:255;default:''"` // 清理配置 JSON
Envs BigText `json:"envs"` // 环境变量ID列表,逗号分隔
Languages TaskLanguages `json:"languages" gorm:"type:text"` // 针对本地任务的语言配置列表
AgentID *string `json:"agent_id" gorm:"size:20;index"` // Agent ID,为空表示本地执行
RetryCount int `json:"retry_count" gorm:"default:0"` // 失败重试次数
RetryInterval int `json:"retry_interval" gorm:"default:0"` // 失败重试间隔(秒)
RandomRange int `json:"random_range" gorm:"default:0"` // 随机延迟范围(秒)
Enabled *bool `json:"enabled" gorm:"default:true"`
RunningGo BigText `json:"running_go"` // 正在运行的 go routine id 数组 (JSON)
RuntimeEnvs []string `json:"-" gorm:"-"` // 运行时环境变量(非持久化)
RuntimeSecrets []string `json:"-" gorm:"-"` // 运行时安全机密(非持久化)
LastRun *LocalTime `json:"last_run"`
NextRun *LocalTime `json:"next_run"`
SourceID string `json:"source_id" gorm:"size:255;index"` // 脚本资源唯一标识(路径 sanitized)
RepoTaskID string `json:"repo_task_id" gorm:"size:20;index"` // 所属的仓库任务 ID
CreatedAt LocalTime `json:"created_at"`
UpdatedAt LocalTime `json:"updated_at"`
}
func (t *Task) IsRunning() bool {
@@ -180,8 +180,8 @@ type TaskLog struct {
TaskID string `json:"task_id" gorm:"size:20;index"`
AgentID *string `json:"agent_id" gorm:"size:20;index"` // Agent ID,为空表示本地执行
Command BigText `json:"command"`
Output BigText `json:"-"` // gzip+base64 压缩后的日志
Error BigText `json:"error"` // 额外的系统错误信息
Output BigText `json:"-"` // gzip+base64 压缩后的日志
Error BigText `json:"error"` // 额外的系统错误信息
Status string `json:"status" gorm:"size:20;index"` // success, failed
Duration int64 `json:"duration"` // 执行耗时(毫秒)
ExitCode int `json:"exit_code"`
+8 -8
View File
@@ -6,14 +6,14 @@ import (
// User represents a system user
type User struct {
ID string `json:"id" gorm:"primaryKey;size:20"`
Username string `json:"username" gorm:"size:100;uniqueIndex;not null"`
Password string `json:"password" gorm:"size:255;not null"`
Email string `json:"email" gorm:"size:255"`
Role string `json:"role" gorm:"size:20;default:user"` // admin, user
TokenVersion int `json:"-" gorm:"default:1"` // 用于 JWT 失效校验
CreatedAt LocalTime `json:"created_at"`
UpdatedAt LocalTime `json:"updated_at"`
ID string `json:"id" gorm:"primaryKey;size:20"`
Username string `json:"username" gorm:"size:100;uniqueIndex;not null"`
Password string `json:"password" gorm:"size:255;not null"`
Email string `json:"email" gorm:"size:255"`
Role string `json:"role" gorm:"size:20;default:user"` // admin, user
TokenVersion int `json:"-" gorm:"default:1"` // 用于 JWT 失效校验
CreatedAt LocalTime `json:"created_at"`
UpdatedAt LocalTime `json:"updated_at"`
}
func (User) TableName() string {
+22 -22
View File
@@ -9,17 +9,17 @@ import (
// AgentVO 代理视图对象
type AgentVO struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
Status string `json:"status"`
LastSeen *models.LocalTime `json:"last_seen"`
IP string `json:"ip"`
Version string `json:"version"`
BuildTime string `json:"build_time"`
Hostname string `json:"hostname"`
OS string `json:"os"`
Arch string `json:"arch"`
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
Status string `json:"status"`
LastSeen *models.LocalTime `json:"last_seen"`
IP string `json:"ip"`
Version string `json:"version"`
BuildTime string `json:"build_time"`
Hostname string `json:"hostname"`
OS string `json:"os"`
Arch string `json:"arch"`
ForceUpdate bool `json:"force_update"`
Enabled bool `json:"enabled"`
SchedulerConfig *AgentSchedulerConfigVO `json:"scheduler_config"`
@@ -44,17 +44,17 @@ func ToAgentVO(agent *models.Agent) *AgentVO {
}
}
return &AgentVO{
ID: agent.ID,
Name: agent.Name,
Description: agent.Description,
Status: agent.Status,
LastSeen: agent.LastSeen,
IP: agent.IP,
Version: agent.Version,
BuildTime: agent.BuildTime,
Hostname: agent.Hostname,
OS: agent.OS,
Arch: agent.Arch,
ID: agent.ID,
Name: agent.Name,
Description: agent.Description,
Status: agent.Status,
LastSeen: agent.LastSeen,
IP: agent.IP,
Version: agent.Version,
BuildTime: agent.BuildTime,
Hostname: agent.Hostname,
OS: agent.OS,
Arch: agent.Arch,
ForceUpdate: agent.ForceUpdate,
Enabled: utils.DerefBool(agent.Enabled, true),
SchedulerConfig: schedulerConfigVO,
+47 -47
View File
@@ -8,34 +8,34 @@ import (
// TaskVO 任务视图对象
type TaskVO struct {
ID string `json:"id"`
Name string `json:"name"`
Remark string `json:"remark"`
Command string `json:"command"`
PreCommand string `json:"pre_command"`
PostCommand string `json:"post_command"`
Tags string `json:"tags"`
Type string `json:"type"`
TriggerType string `json:"trigger_type"`
Config string `json:"config"`
Schedule string `json:"schedule"`
Timeout int `json:"timeout"`
WorkDir string `json:"work_dir"`
CleanConfig string `json:"clean_config"`
Envs string `json:"envs"`
Languages models.TaskLanguages `json:"languages"`
AgentID *string `json:"agent_id"`
RepoTaskID string `json:"repo_task_id"`
Enabled bool `json:"enabled"`
RetryCount int `json:"retry_count"`
RetryInterval int `json:"retry_interval"`
RandomRange int `json:"random_range"`
PinType string `json:"pin_type"`
LastRun *models.LocalTime `json:"last_run"`
NextRun *models.LocalTime `json:"next_run"`
CreatedAt models.LocalTime `json:"created_at"`
UpdatedAt models.LocalTime `json:"updated_at"`
RunningStatus string `json:"running_status"`
ID string `json:"id"`
Name string `json:"name"`
Remark string `json:"remark"`
Command string `json:"command"`
PreCommand string `json:"pre_command"`
PostCommand string `json:"post_command"`
Tags string `json:"tags"`
Type string `json:"type"`
TriggerType string `json:"trigger_type"`
Config string `json:"config"`
Schedule string `json:"schedule"`
Timeout int `json:"timeout"`
WorkDir string `json:"work_dir"`
CleanConfig string `json:"clean_config"`
Envs string `json:"envs"`
Languages models.TaskLanguages `json:"languages"`
AgentID *string `json:"agent_id"`
RepoTaskID string `json:"repo_task_id"`
Enabled bool `json:"enabled"`
RetryCount int `json:"retry_count"`
RetryInterval int `json:"retry_interval"`
RandomRange int `json:"random_range"`
PinType string `json:"pin_type"`
LastRun *models.LocalTime `json:"last_run"`
NextRun *models.LocalTime `json:"next_run"`
CreatedAt models.LocalTime `json:"created_at"`
UpdatedAt models.LocalTime `json:"updated_at"`
RunningStatus string `json:"running_status"`
}
// ToTaskVO 将 Task 模型转换为 TaskVO
@@ -44,22 +44,22 @@ func ToTaskVO(task *models.Task) *TaskVO {
return nil
}
return &TaskVO{
ID: task.ID,
Name: task.Name,
Remark: task.Remark,
Command: string(task.Command),
PreCommand: string(task.PreCommand),
PostCommand: string(task.PostCommand),
Tags: task.Tags,
Type: task.Type,
TriggerType: task.TriggerType,
Config: string(task.Config),
Schedule: task.Schedule,
Timeout: task.Timeout,
WorkDir: task.WorkDir,
CleanConfig: task.CleanConfig,
Envs: string(task.Envs),
Languages: task.Languages,
ID: task.ID,
Name: task.Name,
Remark: task.Remark,
Command: string(task.Command),
PreCommand: string(task.PreCommand),
PostCommand: string(task.PostCommand),
Tags: task.Tags,
Type: task.Type,
TriggerType: task.TriggerType,
Config: string(task.Config),
Schedule: task.Schedule,
Timeout: task.Timeout,
WorkDir: task.WorkDir,
CleanConfig: task.CleanConfig,
Envs: string(task.Envs),
Languages: task.Languages,
AgentID: task.AgentID,
RepoTaskID: task.RepoTaskID,
Enabled: utils.DerefBool(task.Enabled, true),
@@ -68,9 +68,9 @@ func ToTaskVO(task *models.Task) *TaskVO {
RandomRange: task.RandomRange,
PinType: task.PinType,
LastRun: task.LastRun,
NextRun: task.NextRun,
CreatedAt: task.CreatedAt,
UpdatedAt: task.UpdatedAt,
NextRun: task.NextRun,
CreatedAt: task.CreatedAt,
UpdatedAt: task.UpdatedAt,
RunningStatus: func() string {
if task.IsRunning() {
return "running"
-1
View File
@@ -20,7 +20,6 @@ func setupEventHandlers(subscribers ...eventbus.Subscriber) {
}
}
func startAppLogCleanup(appLogSvc *services.AppLogService) {
// 初始化时执行一次清理
appLogSvc.CleanUp()
+1 -1
View File
@@ -1,8 +1,8 @@
package router
import (
"strings"
"os"
"strings"
"github.com/engigu/baihu-panel/internal/controllers"
"github.com/engigu/baihu-panel/internal/middleware"
-1
View File
@@ -132,7 +132,6 @@ func initPWARoutes(root *gin.RouterGroup) {
})
}
func handleManifest(ctx *gin.Context) {
staticFS := static.GetFS()
if staticFS == nil {
+9 -9
View File
@@ -15,15 +15,15 @@ func (c *BarkChannel) Send(config ChannelConfig, msg *Message) (*Result, error)
}
cli := message.Bark{
PushKey: pushKey,
Archive: config.GetString("archive"),
Group: config.GetString("group"),
Sound: config.GetString("sound"),
Icon: config.GetString("icon"),
Level: config.GetString("level"),
URL: config.GetString("url"),
Key: config.GetString("key"),
IV: config.GetString("iv"),
PushKey: pushKey,
Archive: config.GetString("archive"),
Group: config.GetString("group"),
Sound: config.GetString("sound"),
Icon: config.GetString("icon"),
Level: config.GetString("level"),
URL: config.GetString("url"),
Key: config.GetString("key"),
IV: config.GetString("iv"),
Server: config.GetString("server"),
Badge: config.GetString("badge"),
Copy: config.GetString("copy"),
+1 -1
View File
@@ -22,7 +22,7 @@ func NewBaseChannel(channelType string, supportedFormats []string) *BaseChannel
return &BaseChannel{channelType: channelType, supportedFormats: supportedFormats}
}
func (c *BaseChannel) GetType() string { return c.channelType }
func (c *BaseChannel) GetType() string { return c.channelType }
func (c *BaseChannel) GetSupportedFormats() []string { return c.supportedFormats }
// FormatContent 根据渠道支持的格式选择最佳内容
+1 -2
View File
@@ -12,8 +12,8 @@ import (
"github.com/engigu/baihu-panel/internal/constant"
"github.com/engigu/baihu-panel/internal/database"
"github.com/engigu/baihu-panel/internal/logger"
"github.com/engigu/baihu-panel/internal/executor"
"github.com/engigu/baihu-panel/internal/logger"
"github.com/engigu/baihu-panel/internal/models"
"github.com/engigu/baihu-panel/internal/services/tasks"
"github.com/engigu/baihu-panel/internal/utils"
@@ -378,7 +378,6 @@ func (s *AgentService) GetTasks(agentID string) []models.AgentTask {
return result
}
// ReportResult Agent 上报执行结果
func (s *AgentService) ReportResult(result *models.AgentTaskResult) error {
// 获取依赖的服务
+3 -3
View File
@@ -16,9 +16,9 @@ import (
// AgentWSManager WebSocket 连接管理器
type AgentWSManager struct {
connections map[string]*AgentConnection // Agent ID -> 连接对象
ipConnections map[string]int // IP -> 连接数
ipLastAttempt map[string]time.Time // IP -> 最后连接尝试时间
ipFailCount map[string]int // IP -> 连续失败次数
ipConnections map[string]int // IP -> 连接数
ipLastAttempt map[string]time.Time // IP -> 最后连接尝试时间
ipFailCount map[string]int // IP -> 连续失败次数
remoteWaiters map[string]chan *models.AgentTaskResult // 日志 ID -> 结果通道
mu sync.RWMutex
}
-1
View File
@@ -372,7 +372,6 @@ func (s *BackupService) restoreScriptsDir(r *zip.ReadCloser) {
}
}
func (s *BackupService) addDirToZip(zipWriter *zip.Writer, srcDir, prefix string) error {
return filepath.Walk(srcDir, func(path string, info os.FileInfo, err error) error {
if err != nil {
-2
View File
@@ -160,8 +160,6 @@ func LoadConfig(path string) (*AppConfig, error) {
return Config, nil
}
// applyEnvOverrides 从环境变量加载配置
func applyEnvOverrides() {
// Server
+3 -2
View File
@@ -109,7 +109,6 @@ func (s *MiseService) fetchLiveLanguages() ([]MiseLanguage, error) {
return s.listFallback()
}
func (s *MiseService) listFallback() ([]MiseLanguage, error) {
cmd := exec.Command("mise", "ls")
cmd.Env = os.Environ()
@@ -286,7 +285,7 @@ func (s *MiseService) syncToDB(languages []MiseLanguage) {
}
if queryErr == nil && rowsAffected == 0 {
// 如果不存在,则创建
// 如果不存在,则创建
newLang := models.Language{
ID: utils.GenerateID(),
Plugin: lang.Plugin,
@@ -329,6 +328,7 @@ func (s *MiseService) GetVerifyCommand(plugin, version string) (string, error) {
}
return m.GetVerifyCommand(version)
}
// UseGlobal 设置全局默认版本
func (s *MiseService) UseGlobal(plugin, version string) error {
cmd := exec.Command("mise", "use", "-g", fmt.Sprintf("%s@%s", plugin, version))
@@ -355,6 +355,7 @@ func (s *MiseService) UnsetGlobal(plugin, version string) error {
}
return nil
}
// Envs 获取全局环境变量
func (s *MiseService) Envs() (map[string]string, error) {
cmd := exec.Command("mise", "set")
+1 -1
View File
@@ -1,9 +1,9 @@
package repo
import (
"github.com/engigu/baihu-panel/internal/models"
"regexp"
"strings"
"github.com/engigu/baihu-panel/internal/models"
)
// QinglongStrategy 实现与青龙兼容的解析逻辑
+3 -3
View File
@@ -3,17 +3,17 @@ package repo
import (
"bufio"
"fmt"
"github.com/engigu/baihu-panel/internal/utils"
"github.com/robfig/cron/v3"
"os"
"path/filepath"
"regexp"
"strings"
"github.com/engigu/baihu-panel/internal/utils"
"github.com/robfig/cron/v3"
)
var (
// envRegex 匹配脚本中的环境名称设置,如 Env("名称")
envRegex = regexp.MustCompile(`(?i)(?:new[ \t]+)?Env\(['"]?([^'"]+)['"]?\)`)
envRegex = regexp.MustCompile(`(?i)(?:new[ \t]+)?Env\(['"]?([^'"]+)['"]?\)`)
// cronRegex 匹配脚本中的 cron 表达式设置
cronRegex = regexp.MustCompile(`(?i)(?:cron[ \t]*[:=][ \t]*['"]?([^'"\r\n]+))|(?:(?:^|[ \t\*\/])(([0-9\*\/\-,L?#]+[ \t]+){4,5}[0-9\*\/\-,L?#]+))`)
// cronFormatRegex 用于校验提取出的字符串是否符合 Cron 表达式格式 (5位或6位)
+1 -1
View File
@@ -4,11 +4,11 @@ import (
"encoding/json"
"time"
"github.com/engigu/baihu-panel/internal/constant"
"github.com/engigu/baihu-panel/internal/database"
"github.com/engigu/baihu-panel/internal/logger"
"github.com/engigu/baihu-panel/internal/models"
"github.com/engigu/baihu-panel/internal/systime"
"github.com/engigu/baihu-panel/internal/constant"
"github.com/engigu/baihu-panel/internal/utils"
)
-1
View File
@@ -92,7 +92,6 @@ func (ts *TaskService) CreateTask(p *TaskParam) *models.Task {
}
database.DB.Select("*").Create(task)
return task
}
+1 -1
View File
@@ -64,7 +64,7 @@ type TinyLog struct {
path string
writer *bufio.Writer
subscribers []chan []byte
remainder []byte // Leftover bytes from previous write (partial lines)
remainder []byte // Leftover bytes from previous write (partial lines)
masks []string // Secrets to mask
closed bool
}
+1
View File
@@ -8,6 +8,7 @@ import (
func GenerateID() string {
return xid.New().String()
}
// IsNumeric 检查字符串是否全为数字
func IsNumeric(s string) bool {
for _, c := range s {
-1
View File
@@ -14,7 +14,6 @@ var (
shellOnce sync.Once
)
// GetShell 返回当前操作系统的 shell 和参数
func GetShell() (shell string, args []string) {
shellOnce.Do(func() {