Initial commit: TaskPool React panel
- React frontend with route-level code splitting - Backend rebranded from Baihu to TaskPool - DB brand migration script and local compatibility
This commit is contained in:
@@ -0,0 +1,161 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/engigu/taskpool/internal/constant"
|
||||
)
|
||||
|
||||
// AgentSchedulerConfig Agent 调度器配置
|
||||
type AgentSchedulerConfig struct {
|
||||
WorkerCount int `json:"worker_count"`
|
||||
QueueSize int `json:"queue_size"`
|
||||
RateInterval time.Duration `json:"rate_interval"`
|
||||
Verbose bool `json:"verbose"`
|
||||
StrictQueue bool `json:"strict_queue"`
|
||||
}
|
||||
|
||||
// Value 序列化为数据库字符串
|
||||
func (c AgentSchedulerConfig) Value() (driver.Value, error) {
|
||||
bytes, err := json.Marshal(c)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return string(bytes), nil
|
||||
}
|
||||
|
||||
// Scan 反序列化数据库字符串为结构体
|
||||
func (c *AgentSchedulerConfig) Scan(value interface{}) error {
|
||||
if value == nil {
|
||||
return nil
|
||||
}
|
||||
bytes, ok := value.([]byte)
|
||||
if !ok {
|
||||
str, ok := value.(string)
|
||||
if !ok {
|
||||
return errors.New("invalid type for AgentSchedulerConfig")
|
||||
}
|
||||
bytes = []byte(str)
|
||||
}
|
||||
return json.Unmarshal(bytes, c)
|
||||
}
|
||||
|
||||
// Agent 远程执行代理
|
||||
type Agent struct {
|
||||
ID string `json:"id" gorm:"primaryKey;size:20"`
|
||||
Name string `json:"name" gorm:"size:100;not null"` // Agent 名称
|
||||
Token string `json:"token" gorm:"size:64;index"` // 认证 Token(可重复使用)
|
||||
MachineID string `json:"machine_id" gorm:"size:64;uniqueIndex"` // 机器识别码(唯一)
|
||||
Description string `json:"description" gorm:"size:255"` // 描述
|
||||
Status string `json:"status" gorm:"size:20;default:'pending';index"` // 状态: constant.AgentStatusOnline, constant.AgentStatusOffline
|
||||
LastSeen *LocalTime `json:"last_seen"` // 最后心跳时间
|
||||
IP string `json:"ip" gorm:"size:45"` // Agent IP 地址
|
||||
Version string `json:"version" gorm:"size:50"` // Agent 版本
|
||||
BuildTime string `json:"build_time" gorm:"size:30"` // Agent 构建时间
|
||||
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 类型字段中
|
||||
CreatedAt LocalTime `json:"created_at"`
|
||||
UpdatedAt LocalTime `json:"updated_at"`
|
||||
}
|
||||
|
||||
func (Agent) TableName() string {
|
||||
return constant.TablePrefix + "agents"
|
||||
}
|
||||
|
||||
// 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"`
|
||||
}
|
||||
|
||||
func (AgentToken) TableName() string {
|
||||
return constant.TablePrefix + "tokens"
|
||||
}
|
||||
|
||||
// AgentTask Agent 任务配置(用于下发给 Agent)
|
||||
type AgentTask struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Command string `json:"command"`
|
||||
PreCommand string `json:"pre_command"`
|
||||
PostCommand string `json:"post_command"`
|
||||
Schedule string `json:"schedule"`
|
||||
Timeout int `json:"timeout"`
|
||||
WorkDir string `json:"work_dir"`
|
||||
Envs string `json:"envs"`
|
||||
Languages []map[string]string `json:"languages"`
|
||||
RandomRange int `json:"random_range"`
|
||||
Secrets []string `json:"secrets"`
|
||||
Enabled bool `json:"enabled"`
|
||||
}
|
||||
|
||||
func (t AgentTask) GetID() string {
|
||||
return t.ID
|
||||
}
|
||||
|
||||
func (t AgentTask) GetName() string {
|
||||
return t.Name
|
||||
}
|
||||
|
||||
func (t AgentTask) GetCommand() string {
|
||||
return t.Command
|
||||
}
|
||||
|
||||
func (t AgentTask) GetPreCommand() string {
|
||||
return t.PreCommand
|
||||
}
|
||||
|
||||
func (t AgentTask) GetPostCommand() string {
|
||||
return t.PostCommand
|
||||
}
|
||||
|
||||
func (t AgentTask) GetSchedule() string {
|
||||
return t.Schedule
|
||||
}
|
||||
|
||||
func (t AgentTask) GetRandomRange() int {
|
||||
return t.RandomRange
|
||||
}
|
||||
|
||||
func (t AgentTask) GetSecrets() []string {
|
||||
return t.Secrets
|
||||
}
|
||||
|
||||
// AgentTaskResult Agent 上报的任务执行结果
|
||||
type AgentTaskResult struct {
|
||||
TaskID string `json:"task_id"`
|
||||
LogID string `json:"log_id"`
|
||||
AgentID string `json:"agent_id"`
|
||||
Command string `json:"command"`
|
||||
Output string `json:"output"`
|
||||
Error string `json:"error"` // 额外的系统错误信息
|
||||
Status string `json:"status"` // success, failed
|
||||
Duration int64 `json:"duration"` // 耗时(毫秒)
|
||||
ExitCode int `json:"exit_code"`
|
||||
StartTime int64 `json:"start_time"` // Unix 时间戳
|
||||
EndTime int64 `json:"end_time"` // Unix 时间戳
|
||||
}
|
||||
|
||||
// AgentRegisterRequest Agent 注册请求
|
||||
type AgentRegisterRequest struct {
|
||||
Name string `json:"name"`
|
||||
Hostname string `json:"hostname"`
|
||||
Version string `json:"version"`
|
||||
BuildTime string `json:"build_time"`
|
||||
Token string `json:"token"` // 注册令牌
|
||||
MachineID string `json:"machine_id"` // 机器识别码
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"github.com/engigu/taskpool/internal/constant"
|
||||
)
|
||||
|
||||
// AppLog 统一应用日志与通知记录
|
||||
type AppLog struct {
|
||||
ID string `json:"id" gorm:"primaryKey;size:20"`
|
||||
Category string `json:"category" gorm:"size:50;index;not null"` // 大类:constant.LogCategorySystemNotice(系统通知), constant.LogCategoryPushLog(推送记录) 等
|
||||
Title string `json:"title" gorm:"size:255"` // 消息标题
|
||||
Content BigText `json:"content"` // 详细内容/Payload
|
||||
Level string `json:"level" gorm:"size:20;index"` // 级别:constant.LogLevelInfo, constant.LogLevelWarning, constant.LogLevelError
|
||||
Status string `json:"status" gorm:"size:20;index"` // 状态:系统通知为 constant.LogStatusRead/constant.LogStatusUnread,推送为 constant.LogStatusSuccess/constant.LogStatusFailed
|
||||
RefID string `json:"ref_id" gorm:"size:50;index"` // 关联对象ID(选填,比如绑定的通知渠道ID、任务ID等)
|
||||
ErrorMsg BigText `json:"error_msg"` // 执行错误信息详情
|
||||
CreatedAt LocalTime `json:"created_at" gorm:"index"`
|
||||
ReadAt *LocalTime `json:"read_at"` // 已读时间(仅对通知生效)
|
||||
ChannelName string `json:"channel_name" gorm:"-"` // 推送记录的关联渠道名称(动态查询)
|
||||
}
|
||||
|
||||
func (AppLog) TableName() string {
|
||||
return constant.TablePrefix + "app_logs"
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/schema"
|
||||
)
|
||||
|
||||
// BigText 自定义大数据文本类型,自动处理跨数据库类型差异
|
||||
// MySQL: LONGTEXT (4GB)
|
||||
// PostgreSQL: TEXT
|
||||
// SQLite: TEXT
|
||||
type BigText string
|
||||
|
||||
func (BigText) GormDBDataType(db *gorm.DB, field *schema.Field) string {
|
||||
switch db.Dialector.Name() {
|
||||
case "mysql":
|
||||
return "LONGTEXT"
|
||||
case "postgres":
|
||||
return "TEXT"
|
||||
case "sqlite":
|
||||
return "TEXT"
|
||||
}
|
||||
return "TEXT"
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"github.com/engigu/taskpool/internal/constant"
|
||||
)
|
||||
|
||||
// DataRelation 通用数据关联表
|
||||
type DataRelation struct {
|
||||
ID string `json:"id" gorm:"primaryKey;size:20"`
|
||||
DataID string `json:"data_id" gorm:"size:20;index;not null"`
|
||||
RelateID string `json:"relate_id" gorm:"size:20;index;not null"`
|
||||
Type string `json:"type" gorm:"size:50;index;not null"`
|
||||
CreatedAt LocalTime `json:"created_at"`
|
||||
UpdatedAt LocalTime `json:"updated_at"`
|
||||
}
|
||||
|
||||
func (DataRelation) TableName() string {
|
||||
return constant.TablePrefix + "data_relations"
|
||||
}
|
||||
|
||||
// DataStorage 通用数据存储表
|
||||
type DataStorage struct {
|
||||
ID string `json:"id" gorm:"primaryKey;size:20"`
|
||||
Type string `json:"type" gorm:"size:50;index;not null"`
|
||||
Name string `json:"name" gorm:"size:255;index;not null"`
|
||||
Data BigText `json:"data"`
|
||||
CreatedAt LocalTime `json:"created_at"`
|
||||
UpdatedAt LocalTime `json:"updated_at"`
|
||||
}
|
||||
|
||||
func (DataStorage) TableName() string {
|
||||
return constant.TablePrefix + "data_storages"
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/engigu/taskpool/internal/constant"
|
||||
)
|
||||
|
||||
// Dependency 依赖包模型
|
||||
type Dependency struct {
|
||||
ID string `json:"id" gorm:"primaryKey;size:20"`
|
||||
Name string `json:"name" gorm:"size:100;not null"`
|
||||
Version string `json:"version" gorm:"size:50"`
|
||||
Language string `json:"language" gorm:"size:100;index"` // 关联语言 (node, python...)
|
||||
LangVersion string `json:"lang_version" gorm:"size:100;index"` // 关联语言版本
|
||||
Remark string `json:"remark" gorm:"size:255"`
|
||||
Log BigText `json:"log"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
func (Dependency) TableName() string {
|
||||
return constant.TablePrefix + "deps"
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"github.com/engigu/taskpool/internal/constant"
|
||||
)
|
||||
|
||||
// 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"`
|
||||
Tags string `json:"-" gorm:"-"`
|
||||
CreatedAt LocalTime `json:"created_at"`
|
||||
UpdatedAt LocalTime `json:"updated_at"`
|
||||
}
|
||||
|
||||
func (EnvironmentVariable) TableName() string {
|
||||
return constant.TablePrefix + "envs"
|
||||
}
|
||||
|
||||
// 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"`
|
||||
}
|
||||
|
||||
func (Script) TableName() string {
|
||||
return constant.TablePrefix + "scripts"
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package models
|
||||
|
||||
import "time"
|
||||
|
||||
// ExportData 全量或部分业务数据的导出/导入结构
|
||||
type ExportData struct {
|
||||
Version string `json:"version"`
|
||||
ExportAt LocalTime `json:"export_at"`
|
||||
Tasks []Task `json:"tasks"`
|
||||
Envs []EnvironmentVariable `json:"envs"`
|
||||
Tags []DataStorage `json:"tags"`
|
||||
Bindings []NotifyBinding `json:"bindings"`
|
||||
}
|
||||
|
||||
// NewExportData 创建一个导出数据对象
|
||||
func NewExportData() *ExportData {
|
||||
return &ExportData{
|
||||
Version: "1.0",
|
||||
ExportAt: LocalTime(time.Now()),
|
||||
Tasks: make([]Task, 0),
|
||||
Envs: make([]EnvironmentVariable, 0),
|
||||
Tags: make([]DataStorage, 0),
|
||||
Bindings: make([]NotifyBinding, 0),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"github.com/engigu/taskpool/internal/constant"
|
||||
)
|
||||
|
||||
// NodeMetrics 表示互联节点的性能指标,使用 JSON 存储
|
||||
type NodeMetrics struct {
|
||||
CPUPercent float64 `json:"cpu_percent"`
|
||||
MemPercent float64 `json:"mem_percent"`
|
||||
DiskPercent float64 `json:"disk_percent"`
|
||||
TxBytes uint64 `json:"tx_bytes,omitempty"`
|
||||
RxBytes uint64 `json:"rx_bytes,omitempty"`
|
||||
}
|
||||
|
||||
// InterconnectNode represents a connected remote panel
|
||||
type InterconnectNode struct {
|
||||
ID string `json:"id" gorm:"primaryKey;size:20"`
|
||||
Name string `json:"name" gorm:"size:255;not null"`
|
||||
URL string `json:"url" gorm:"size:255;not null"`
|
||||
Token string `json:"token" gorm:"size:255"`
|
||||
Remark string `json:"remark" gorm:"size:500"`
|
||||
CreatedAt LocalTime `json:"created_at"`
|
||||
UpdatedAt LocalTime `json:"updated_at"`
|
||||
Status string `json:"status" gorm:"size:50"` // online / offline
|
||||
Metrics NodeMetrics `json:"metrics" gorm:"serializer:json"`
|
||||
LastHeartbeatAt *LocalTime `json:"last_heartbeat_at"`
|
||||
}
|
||||
|
||||
func (InterconnectNode) TableName() string {
|
||||
return constant.TablePrefix + "interconnect_nodes"
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"github.com/engigu/taskpool/internal/constant"
|
||||
)
|
||||
|
||||
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"`
|
||||
}
|
||||
|
||||
func (Language) TableName() string {
|
||||
return constant.TablePrefix + "languages"
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"github.com/engigu/taskpool/internal/constant"
|
||||
)
|
||||
|
||||
// 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"`
|
||||
}
|
||||
|
||||
// BindingExtra 存储在 Extra 字段中的 JSON 配置
|
||||
type BindingExtra struct {
|
||||
EnableLog bool `json:"enable_log"`
|
||||
LogLimit int `json:"log_limit"` // 日志字数限制,默认 1000
|
||||
}
|
||||
|
||||
func (NotifyBinding) TableName() string {
|
||||
return constant.TablePrefix + "notify_bindings"
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"github.com/engigu/taskpool/internal/constant"
|
||||
)
|
||||
|
||||
// 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"`
|
||||
}
|
||||
|
||||
func (NotifyWay) TableName() string {
|
||||
return constant.TablePrefix + "notify_ways"
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"github.com/engigu/taskpool/internal/constant"
|
||||
)
|
||||
|
||||
// SendStats 任务执行统计
|
||||
type SendStats struct {
|
||||
ID string `json:"id" gorm:"primaryKey;size:20"`
|
||||
TaskID string `json:"task_id" gorm:"size:20;uniqueIndex:idx_task_day_status"`
|
||||
Day string `json:"day" gorm:"size:10;uniqueIndex:idx_task_day_status"` // 格式: 2006-01-02
|
||||
Status string `json:"status" gorm:"size:20;uniqueIndex:idx_task_day_status"`
|
||||
Num int `json:"num" gorm:"default:0"`
|
||||
}
|
||||
|
||||
func (SendStats) TableName() string {
|
||||
return constant.TablePrefix + "send_stats"
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"github.com/engigu/taskpool/internal/constant"
|
||||
)
|
||||
|
||||
// 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"`
|
||||
Value BigText `json:"value"`
|
||||
}
|
||||
|
||||
func (Setting) TableName() string {
|
||||
return constant.TablePrefix + "settings"
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/engigu/taskpool/internal/constant"
|
||||
)
|
||||
|
||||
// TaskLanguages 自定义语言配置列表类型,处理 JSON 序列化
|
||||
type TaskLanguages []map[string]string
|
||||
|
||||
func (t TaskLanguages) Value() (driver.Value, error) {
|
||||
if t == nil {
|
||||
return "[]", nil
|
||||
}
|
||||
b, err := json.Marshal(t)
|
||||
return string(b), err
|
||||
}
|
||||
|
||||
func (t *TaskLanguages) Scan(v interface{}) error {
|
||||
if v == nil {
|
||||
*t = nil
|
||||
return nil
|
||||
}
|
||||
var data []byte
|
||||
switch s := v.(type) {
|
||||
case string:
|
||||
data = []byte(s)
|
||||
case []byte:
|
||||
data = s
|
||||
default:
|
||||
return fmt.Errorf("invalid type for TaskLanguages: %T", v)
|
||||
}
|
||||
return json.Unmarshal(data, t)
|
||||
}
|
||||
|
||||
// CleanConfig 清理配置结构
|
||||
type CleanConfig struct {
|
||||
Type string `json:"type"` // "day" 或 "count"
|
||||
Keep int `json:"keep"` // 保留天数或条数
|
||||
}
|
||||
|
||||
// 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"` // 自定义代理地址
|
||||
AuthToken string `json:"auth_token"` // 认证 Token
|
||||
WhitelistPaths string `json:"whitelist_paths"` // 同步时保留的路径及脚本筛选白名单关键词,逗号或竖线分割
|
||||
Blacklist string `json:"blacklist"` // 脚本筛选黑名单关键词,竖线分割
|
||||
Dependence string `json:"dependence"` // 脚本依赖文件关键词,竖线分割
|
||||
Extensions string `json:"extensions"` // 脚本文件后缀关键词,竖线分割
|
||||
AutoAddCron bool `json:"auto_add_cron"` // 自动解析脚本注释添加定时任务
|
||||
CommentToTask string `json:"commenttotask"` // 兼容 QL 格式任务脚本注释解析
|
||||
RepoSource string `json:"repo_source"` // 仓库来源,如果是选择了这个 ql 导入的仓库,= ql
|
||||
RepoDirName string `json:"repo_dir_name"` // 自定义仓库目录名
|
||||
}
|
||||
|
||||
// TaskConfig 任务配置 RepoConfig+TaskConfig=task.config
|
||||
type TaskConfig struct {
|
||||
Concurrency int `json:"$task_concurrency"` // 0: disable concurrency, 1: enable concurrency
|
||||
AllEnvs bool `json:"$task_all_envs"` // 开启则注入全部环境变量
|
||||
}
|
||||
|
||||
// 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:"-"` // 标签,逗号分隔
|
||||
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.TriggerTypeTaskPoolStartup
|
||||
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" gorm:"-"` // 环境变量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 {
|
||||
if string(t.RunningGo) == "" || string(t.RunningGo) == "[]" {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (Task) TableName() string {
|
||||
return constant.TablePrefix + "tasks"
|
||||
}
|
||||
|
||||
func (t *Task) GetID() string {
|
||||
return t.ID
|
||||
}
|
||||
|
||||
func (t *Task) GetName() string {
|
||||
return t.Name
|
||||
}
|
||||
|
||||
func (t *Task) GetCommand() string {
|
||||
return string(t.Command)
|
||||
}
|
||||
|
||||
func (t *Task) GetPreCommand() string {
|
||||
return string(t.PreCommand)
|
||||
}
|
||||
|
||||
func (t *Task) GetPostCommand() string {
|
||||
return string(t.PostCommand)
|
||||
}
|
||||
|
||||
func (t *Task) GetTimeout() int {
|
||||
return t.Timeout
|
||||
}
|
||||
|
||||
func (t *Task) GetWorkDir() string {
|
||||
return t.WorkDir
|
||||
}
|
||||
|
||||
func (t *Task) GetEnvs() string {
|
||||
return string(t.Envs)
|
||||
}
|
||||
|
||||
func (t *Task) GetLanguages() []map[string]string {
|
||||
return []map[string]string(t.Languages)
|
||||
}
|
||||
|
||||
func (t *Task) GetEnvVars() []string {
|
||||
return t.RuntimeEnvs
|
||||
}
|
||||
|
||||
func (t *Task) GetSecrets() []string {
|
||||
return t.RuntimeSecrets
|
||||
}
|
||||
|
||||
func (t *Task) GetUseMise() bool {
|
||||
return t.AgentID == nil || *t.AgentID == ""
|
||||
}
|
||||
|
||||
func (t *Task) UseMise() bool {
|
||||
return t.GetUseMise()
|
||||
}
|
||||
|
||||
// CronTask 计划任务接口
|
||||
func (t *Task) GetSchedule() string {
|
||||
return t.Schedule
|
||||
}
|
||||
|
||||
func (t *Task) GetRandomRange() int {
|
||||
return t.RandomRange
|
||||
}
|
||||
|
||||
// TaskLog 代表任务执行的日志记录
|
||||
type TaskLog struct {
|
||||
ID string `json:"id" gorm:"primaryKey;size:20"`
|
||||
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"` // 额外的系统错误信息
|
||||
Status string `json:"status" gorm:"size:20;index"` // success, failed
|
||||
Duration int64 `json:"duration"` // 执行耗时(毫秒)
|
||||
ExitCode int `json:"exit_code"`
|
||||
StartTime *LocalTime `json:"start_time"`
|
||||
EndTime *LocalTime `json:"end_time"`
|
||||
CreatedAt LocalTime `json:"created_at"`
|
||||
}
|
||||
|
||||
func (TaskLog) TableName() string {
|
||||
return constant.TablePrefix + "task_logs"
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/engigu/taskpool/internal/systime"
|
||||
)
|
||||
|
||||
const TimeFormat = "2006-01-02 15:04:05"
|
||||
|
||||
// LocalTime 自定义时间类型,JSON 序列化为 "年-月-日 时:分:秒" 格式
|
||||
type LocalTime time.Time
|
||||
|
||||
func (t LocalTime) MarshalJSON() ([]byte, error) {
|
||||
tt := time.Time(t)
|
||||
if tt.IsZero() {
|
||||
return []byte("null"), nil
|
||||
}
|
||||
// 统一输出为东八区时间
|
||||
tt = systime.InCST(tt)
|
||||
return []byte(fmt.Sprintf(`"%s"`, tt.Format(TimeFormat))), nil
|
||||
}
|
||||
|
||||
func (t *LocalTime) UnmarshalJSON(data []byte) error {
|
||||
if string(data) == "null" || string(data) == `""` {
|
||||
return nil
|
||||
}
|
||||
// 去掉引号
|
||||
s := string(data)
|
||||
if len(s) >= 2 && s[0] == '"' && s[len(s)-1] == '"' {
|
||||
s = s[1 : len(s)-1]
|
||||
}
|
||||
tt, err := time.ParseInLocation(TimeFormat, s, time.Local)
|
||||
if err != nil {
|
||||
// 尝试解析 ISO 格式
|
||||
tt, err = time.Parse(time.RFC3339, s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
*t = LocalTime(tt)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t LocalTime) Value() (driver.Value, error) {
|
||||
return time.Time(t), nil
|
||||
}
|
||||
|
||||
func (t *LocalTime) Scan(v interface{}) error {
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
switch val := v.(type) {
|
||||
case time.Time:
|
||||
*t = LocalTime(val)
|
||||
case string:
|
||||
tt, err := time.ParseInLocation(TimeFormat, val, time.Local)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*t = LocalTime(tt)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t LocalTime) Time() time.Time {
|
||||
return time.Time(t)
|
||||
}
|
||||
|
||||
func Now() LocalTime {
|
||||
return LocalTime(systime.Now())
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"github.com/engigu/taskpool/internal/constant"
|
||||
)
|
||||
|
||||
// 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"`
|
||||
}
|
||||
|
||||
func (User) TableName() string {
|
||||
return constant.TablePrefix + "users"
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
package vo
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/engigu/taskpool/internal/models"
|
||||
"github.com/engigu/taskpool/internal/utils"
|
||||
)
|
||||
|
||||
// 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"`
|
||||
ForceUpdate bool `json:"force_update"`
|
||||
Enabled bool `json:"enabled"`
|
||||
SchedulerConfig *AgentSchedulerConfigVO `json:"scheduler_config"`
|
||||
CreatedAt models.LocalTime `json:"created_at"`
|
||||
UpdatedAt models.LocalTime `json:"updated_at"`
|
||||
// 隐藏 Token 和 MachineID
|
||||
}
|
||||
|
||||
// ToAgentVO 将 Agent 模型转换为 AgentVO
|
||||
func ToAgentVO(agent *models.Agent) *AgentVO {
|
||||
if agent == nil {
|
||||
return nil
|
||||
}
|
||||
var schedulerConfigVO *AgentSchedulerConfigVO
|
||||
if agent.SchedulerConfig.WorkerCount > 0 {
|
||||
schedulerConfigVO = &AgentSchedulerConfigVO{
|
||||
WorkerCount: agent.SchedulerConfig.WorkerCount,
|
||||
QueueSize: agent.SchedulerConfig.QueueSize,
|
||||
RateInterval: int(agent.SchedulerConfig.RateInterval / time.Millisecond),
|
||||
Verbose: agent.SchedulerConfig.Verbose,
|
||||
StrictQueue: agent.SchedulerConfig.StrictQueue,
|
||||
}
|
||||
}
|
||||
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,
|
||||
ForceUpdate: agent.ForceUpdate,
|
||||
Enabled: utils.DerefBool(agent.Enabled, true),
|
||||
SchedulerConfig: schedulerConfigVO,
|
||||
CreatedAt: agent.CreatedAt,
|
||||
UpdatedAt: agent.UpdatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
// ToAgentVOList 将 Agent 模型列表转换为 AgentVO 列表
|
||||
func ToAgentVOList(agents []*models.Agent) []*AgentVO {
|
||||
if agents == nil {
|
||||
return nil
|
||||
}
|
||||
vos := make([]*AgentVO, len(agents))
|
||||
for i, a := range agents {
|
||||
vos[i] = ToAgentVO(a)
|
||||
}
|
||||
return vos
|
||||
}
|
||||
|
||||
// ToAgentVOListFromModels 将 Agent 模型列表转换为 AgentVO 列表
|
||||
func ToAgentVOListFromModels(agents []models.Agent) []*AgentVO {
|
||||
vos := make([]*AgentVO, len(agents))
|
||||
for i := range agents {
|
||||
vos[i] = ToAgentVO(&agents[i])
|
||||
}
|
||||
return vos
|
||||
}
|
||||
|
||||
// AgentTokenVO 代理令牌视图对象
|
||||
type AgentTokenVO struct {
|
||||
ID string `json:"id"`
|
||||
Token string `json:"token"`
|
||||
Remark string `json:"remark"`
|
||||
MaxUses int `json:"max_uses"`
|
||||
UsedCount int `json:"used_count"`
|
||||
ExpiresAt *models.LocalTime `json:"expires_at"`
|
||||
Enabled bool `json:"enabled"`
|
||||
CreatedAt models.LocalTime `json:"created_at"`
|
||||
}
|
||||
|
||||
// ToAgentTokenVO 将 AgentToken 模型转换为 AgentTokenVO
|
||||
func ToAgentTokenVO(token *models.AgentToken) *AgentTokenVO {
|
||||
if token == nil {
|
||||
return nil
|
||||
}
|
||||
return &AgentTokenVO{
|
||||
ID: token.ID,
|
||||
Token: token.Token,
|
||||
Remark: token.Remark,
|
||||
MaxUses: token.MaxUses,
|
||||
UsedCount: token.UsedCount,
|
||||
ExpiresAt: token.ExpiresAt,
|
||||
Enabled: utils.DerefBool(token.Enabled, true),
|
||||
CreatedAt: token.CreatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
// ToAgentTokenVOList 将 AgentToken 模型列表转换为 AgentTokenVO 列表
|
||||
func ToAgentTokenVOList(tokens []*models.AgentToken) []*AgentTokenVO {
|
||||
if tokens == nil {
|
||||
return nil
|
||||
}
|
||||
vos := make([]*AgentTokenVO, len(tokens))
|
||||
for i, t := range tokens {
|
||||
vos[i] = ToAgentTokenVO(t)
|
||||
}
|
||||
return vos
|
||||
}
|
||||
|
||||
// ToAgentTokenVOListFromModels 将 AgentToken 模型列表转换为 AgentTokenVO 列表
|
||||
func ToAgentTokenVOListFromModels(tokens []models.AgentToken) []*AgentTokenVO {
|
||||
vos := make([]*AgentTokenVO, len(tokens))
|
||||
for i := range tokens {
|
||||
vos[i] = ToAgentTokenVO(&tokens[i])
|
||||
}
|
||||
return vos
|
||||
}
|
||||
|
||||
// AgentSchedulerConfigVO 调度配置视图对象
|
||||
type AgentSchedulerConfigVO struct {
|
||||
WorkerCount int `json:"worker_count"`
|
||||
QueueSize int `json:"queue_size"`
|
||||
RateInterval int `json:"rate_interval"` // 毫秒
|
||||
Verbose bool `json:"verbose"`
|
||||
StrictQueue bool `json:"strict_queue"`
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package vo
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/engigu/taskpool/internal/models"
|
||||
)
|
||||
|
||||
// DependencyVO 依赖包视图对象
|
||||
type DependencyVO struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Version string `json:"version"`
|
||||
Language string `json:"language"`
|
||||
LangVersion string `json:"lang_version"`
|
||||
Remark string `json:"remark"`
|
||||
Log string `json:"log,omitempty"` // 仅在需要时返回
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
// ToDependencyVO 将 Dependency 模型转换为 DependencyVO
|
||||
func ToDependencyVO(dep *models.Dependency) *DependencyVO {
|
||||
if dep == nil {
|
||||
return nil
|
||||
}
|
||||
return &DependencyVO{
|
||||
ID: dep.ID,
|
||||
Name: dep.Name,
|
||||
Version: dep.Version,
|
||||
Language: dep.Language,
|
||||
LangVersion: dep.LangVersion,
|
||||
Remark: dep.Remark,
|
||||
Log: string(dep.Log),
|
||||
CreatedAt: dep.CreatedAt,
|
||||
UpdatedAt: dep.UpdatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
// ToDependencyVOListFromModels 将 Dependency 模型列表转换为 DependencyVO 列表
|
||||
func ToDependencyVOListFromModels(deps []models.Dependency) []*DependencyVO {
|
||||
vos := make([]*DependencyVO, len(deps))
|
||||
for i := range deps {
|
||||
vos[i] = ToDependencyVO(&deps[i])
|
||||
}
|
||||
return vos
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package vo
|
||||
|
||||
import (
|
||||
"github.com/engigu/taskpool/internal/models"
|
||||
)
|
||||
|
||||
// ScriptVO 脚本视图对象
|
||||
type ScriptVO struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Content string `json:"content,omitempty"` // 仅在拉取详情时返回
|
||||
CreatedAt models.LocalTime `json:"created_at"`
|
||||
UpdatedAt models.LocalTime `json:"updated_at"`
|
||||
}
|
||||
|
||||
// ToScriptVO 将 Script 模型转换为 ScriptVO
|
||||
func ToScriptVO(script *models.Script) *ScriptVO {
|
||||
if script == nil {
|
||||
return nil
|
||||
}
|
||||
return &ScriptVO{
|
||||
ID: script.ID,
|
||||
Name: script.Name,
|
||||
Content: string(script.Content),
|
||||
CreatedAt: script.CreatedAt,
|
||||
UpdatedAt: script.UpdatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
// ToScriptVOListFromModels 将 Script 模型列表转换为 ScriptVO 列表
|
||||
func ToScriptVOListFromModels(scripts []models.Script) []*ScriptVO {
|
||||
vos := make([]*ScriptVO, len(scripts))
|
||||
for i := range scripts {
|
||||
vos[i] = ToScriptVO(&scripts[i])
|
||||
}
|
||||
return vos
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
package vo
|
||||
|
||||
import (
|
||||
"github.com/engigu/taskpool/internal/constant"
|
||||
"github.com/engigu/taskpool/internal/models"
|
||||
"github.com/engigu/taskpool/internal/utils"
|
||||
)
|
||||
|
||||
// UserVO 用户视图对象
|
||||
type UserVO struct {
|
||||
ID string `json:"id"`
|
||||
Username string `json:"username"`
|
||||
Email string `json:"email"`
|
||||
Role string `json:"role"`
|
||||
CreatedAt models.LocalTime `json:"created_at"`
|
||||
UpdatedAt models.LocalTime `json:"updated_at"`
|
||||
}
|
||||
|
||||
// ToUserVO 将 User 模型转换为 UserVO
|
||||
func ToUserVO(user *models.User) *UserVO {
|
||||
if user == nil {
|
||||
return nil
|
||||
}
|
||||
return &UserVO{
|
||||
ID: user.ID,
|
||||
Username: user.Username,
|
||||
Email: user.Email,
|
||||
Role: user.Role,
|
||||
CreatedAt: user.CreatedAt,
|
||||
UpdatedAt: user.UpdatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
// EnvVO 环境变量视图对象
|
||||
type EnvVO struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Value string `json:"value"`
|
||||
Remark string `json:"remark"`
|
||||
Type string `json:"type"`
|
||||
Tags string `json:"tags"`
|
||||
Hidden bool `json:"hidden"`
|
||||
Enabled bool `json:"enabled"`
|
||||
CreatedAt models.LocalTime `json:"created_at"`
|
||||
UpdatedAt models.LocalTime `json:"updated_at"`
|
||||
}
|
||||
|
||||
// ToEnvVO 将 Env 模型转换为 EnvVO
|
||||
func ToEnvVO(env *models.EnvironmentVariable) *EnvVO {
|
||||
if env == nil {
|
||||
return nil
|
||||
}
|
||||
val := string(env.Value)
|
||||
if env.Type == constant.EnvTypeSecret {
|
||||
val = "********"
|
||||
}
|
||||
return &EnvVO{
|
||||
ID: env.ID,
|
||||
Name: env.Name,
|
||||
Value: val,
|
||||
Remark: env.Remark,
|
||||
Type: env.Type,
|
||||
Tags: env.Tags,
|
||||
Hidden: utils.DerefBool(env.Hidden, true),
|
||||
Enabled: utils.DerefBool(env.Enabled, true),
|
||||
CreatedAt: env.CreatedAt,
|
||||
UpdatedAt: env.UpdatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
// ToEnvVOList 将 Env 模型列表转换为 EnvVO 列表
|
||||
func ToEnvVOList(envs []*models.EnvironmentVariable) []*EnvVO {
|
||||
if envs == nil {
|
||||
return nil
|
||||
}
|
||||
vos := make([]*EnvVO, len(envs))
|
||||
for i, e := range envs {
|
||||
vos[i] = ToEnvVO(e)
|
||||
}
|
||||
return vos
|
||||
}
|
||||
|
||||
// ToEnvVOListFromModels 将 Env 模型列表转换为 EnvVO 列表
|
||||
func ToEnvVOListFromModels(envs []models.EnvironmentVariable) []*EnvVO {
|
||||
vos := make([]*EnvVO, len(envs))
|
||||
for i := range envs {
|
||||
vos[i] = ToEnvVO(&envs[i])
|
||||
}
|
||||
return vos
|
||||
}
|
||||
|
||||
// LoginLogVO 登录日志视图对象
|
||||
type LoginLogVO struct {
|
||||
ID string `json:"id"`
|
||||
Username string `json:"username"`
|
||||
IP string `json:"ip"`
|
||||
UserAgent string `json:"user_agent"`
|
||||
Status string `json:"status"`
|
||||
Message string `json:"message"`
|
||||
CreatedAt models.LocalTime `json:"created_at"`
|
||||
}
|
||||
|
||||
// TokenConfig Token 配置结构体
|
||||
type TokenConfig struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
Token string `json:"token"`
|
||||
ExpireAt string `json:"expire_at"`
|
||||
}
|
||||
@@ -0,0 +1,263 @@
|
||||
package vo
|
||||
|
||||
import (
|
||||
"github.com/engigu/taskpool/internal/executor"
|
||||
"github.com/engigu/taskpool/internal/models"
|
||||
"github.com/engigu/taskpool/internal/utils"
|
||||
)
|
||||
|
||||
// TaskCreateReq 任务创建请求
|
||||
type TaskCreateReq struct {
|
||||
Name string `json:"name" binding:"required" example:"测试任务"`
|
||||
Remark string `json:"remark" example:"备注信息"`
|
||||
Command string `json:"command" example:"echo 'Hello World'"`
|
||||
PreCommand string `json:"pre_command" example:"echo 'pre'"`
|
||||
PostCommand string `json:"post_command" example:"echo 'post'"`
|
||||
Tags string `json:"tags" example:"test,dev"`
|
||||
Type string `json:"type" example:"repo"` // 可以是 common, repo 等
|
||||
Config string `json:"config" swaggertype:"string" example:"{\"source_url\":\"https://github.com/abc/repo\",\"branch\":\"main\"}"`
|
||||
Schedule string `json:"schedule" example:"0 0 * * *"`
|
||||
Timeout int `json:"timeout" example:"3600"`
|
||||
WorkDir string `json:"work_dir" example:"/tmp"`
|
||||
CleanConfig string `json:"clean_config" example:"true"`
|
||||
Envs string `json:"envs" example:"{\"ENV_VAR\":\"value\"}"`
|
||||
Languages models.TaskLanguages `json:"languages"`
|
||||
AgentID *string `json:"agent_id" example:"agent-1"`
|
||||
TriggerType string `json:"trigger_type" example:"cron"`
|
||||
RetryCount int `json:"retry_count" example:"3"`
|
||||
RetryInterval int `json:"retry_interval" example:"60"`
|
||||
RandomRange int `json:"random_range" example:"10"`
|
||||
PinType string `json:"pin_type" example:"time"`
|
||||
}
|
||||
|
||||
// TaskUpdateReq 任务更新请求
|
||||
type TaskUpdateReq struct {
|
||||
Name string `json:"name" example:"测试任务"`
|
||||
Remark string `json:"remark" example:"备注信息"`
|
||||
Command string `json:"command" example:"echo 'Hello World'"`
|
||||
PreCommand string `json:"pre_command" example:"echo 'pre'"`
|
||||
PostCommand string `json:"post_command" example:"echo 'post'"`
|
||||
Tags string `json:"tags" example:"test,dev"`
|
||||
Type string `json:"type" example:"repo"`
|
||||
Config string `json:"config" swaggertype:"string" example:"{\"source_url\":\"https://github.com/abc/repo\",\"branch\":\"main\"}"`
|
||||
Schedule string `json:"schedule" example:"0 0 * * *"`
|
||||
Timeout int `json:"timeout" example:"3600"`
|
||||
WorkDir string `json:"work_dir" example:"/tmp"`
|
||||
CleanConfig string `json:"clean_config" example:"true"`
|
||||
Envs string `json:"envs" example:"{\"ENV_VAR\":\"value\"}"`
|
||||
Enabled bool `json:"enabled" example:"true"`
|
||||
Languages models.TaskLanguages `json:"languages"`
|
||||
AgentID *string `json:"agent_id" example:"agent-1"`
|
||||
TriggerType string `json:"trigger_type" example:"cron"`
|
||||
RetryCount int `json:"retry_count" example:"3"`
|
||||
RetryInterval int `json:"retry_interval" example:"60"`
|
||||
RandomRange int `json:"random_range" example:"10"`
|
||||
PinType string `json:"pin_type" example:"time"`
|
||||
}
|
||||
|
||||
// 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"`
|
||||
}
|
||||
|
||||
// ToTaskVO 将 Task 模型转换为 TaskVO
|
||||
func ToTaskVO(task *models.Task) *TaskVO {
|
||||
if task == nil {
|
||||
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,
|
||||
AgentID: task.AgentID,
|
||||
RepoTaskID: task.RepoTaskID,
|
||||
Enabled: utils.DerefBool(task.Enabled, true),
|
||||
RetryCount: task.RetryCount,
|
||||
RetryInterval: task.RetryInterval,
|
||||
RandomRange: task.RandomRange,
|
||||
PinType: task.PinType,
|
||||
LastRun: task.LastRun,
|
||||
NextRun: task.NextRun,
|
||||
CreatedAt: task.CreatedAt,
|
||||
UpdatedAt: task.UpdatedAt,
|
||||
RunningStatus: func() string {
|
||||
if task.IsRunning() {
|
||||
return "running"
|
||||
}
|
||||
return "idle"
|
||||
}(),
|
||||
}
|
||||
}
|
||||
|
||||
// ToTaskVOList 将 Task 模型列表转换为 TaskVO 列表
|
||||
func ToTaskVOList(tasks []*models.Task) []*TaskVO {
|
||||
if tasks == nil {
|
||||
return nil
|
||||
}
|
||||
vos := make([]*TaskVO, len(tasks))
|
||||
for i, t := range tasks {
|
||||
vos[i] = ToTaskVO(t)
|
||||
}
|
||||
return vos
|
||||
}
|
||||
|
||||
// ToTaskVOListFromModels 将 Task 模型列表转换为 TaskVO 列表
|
||||
func ToTaskVOListFromModels(tasks []models.Task) []*TaskVO {
|
||||
vos := make([]*TaskVO, len(tasks))
|
||||
for i := range tasks {
|
||||
vos[i] = ToTaskVO(&tasks[i])
|
||||
}
|
||||
return vos
|
||||
}
|
||||
|
||||
// TaskLogVO 任务历史视图对象
|
||||
type TaskLogVO struct {
|
||||
ID string `json:"id"`
|
||||
TaskID string `json:"task_id"`
|
||||
TaskName string `json:"task_name"`
|
||||
TaskType string `json:"task_type"`
|
||||
AgentID *string `json:"agent_id"`
|
||||
Command string `json:"command"`
|
||||
Error string `json:"error"`
|
||||
Status string `json:"status"`
|
||||
Duration int64 `json:"duration"`
|
||||
ExitCode int `json:"exit_code"`
|
||||
StartTime *models.LocalTime `json:"start_time"`
|
||||
EndTime *models.LocalTime `json:"end_time"`
|
||||
CreatedAt models.LocalTime `json:"created_at"`
|
||||
Output string `json:"output,omitempty"`
|
||||
}
|
||||
|
||||
// ToTaskLogVO 将 TaskLog 模型转换为 TaskLogVO
|
||||
// Note: This function assumes the Task field within models.TaskLog is preloaded
|
||||
// or that taskName and taskType are provided from an external source.
|
||||
func ToTaskLogVO(log *models.TaskLog) *TaskLogVO {
|
||||
if log == nil {
|
||||
return nil
|
||||
}
|
||||
return &TaskLogVO{
|
||||
ID: log.ID,
|
||||
TaskID: log.TaskID,
|
||||
AgentID: log.AgentID,
|
||||
Command: string(log.Command),
|
||||
Error: string(log.Error),
|
||||
Status: log.Status,
|
||||
Duration: log.Duration,
|
||||
ExitCode: log.ExitCode,
|
||||
StartTime: log.StartTime,
|
||||
EndTime: log.EndTime,
|
||||
CreatedAt: log.CreatedAt,
|
||||
Output: string(log.Output),
|
||||
}
|
||||
}
|
||||
|
||||
// ToTaskLogVOList 将 TaskLog 模型列表转换为 TaskLogVO 列表
|
||||
func ToTaskLogVOList(logs []*models.TaskLog) []*TaskLogVO {
|
||||
if logs == nil {
|
||||
return nil
|
||||
}
|
||||
vos := make([]*TaskLogVO, len(logs))
|
||||
for i, l := range logs {
|
||||
vos[i] = ToTaskLogVO(l)
|
||||
}
|
||||
return vos
|
||||
}
|
||||
|
||||
// ToTaskLogVOListFromModels 将 TaskLog 模型列表转换为 TaskLogVO 列表
|
||||
func ToTaskLogVOListFromModels(logs []models.TaskLog) []*TaskLogVO {
|
||||
vos := make([]*TaskLogVO, len(logs))
|
||||
for i := range logs {
|
||||
vos[i] = ToTaskLogVO(&logs[i])
|
||||
}
|
||||
return vos
|
||||
}
|
||||
|
||||
// ExecutionResultVO 任务执行结果视图对象
|
||||
type ExecutionResultVO struct {
|
||||
TaskID string `json:"task_id"`
|
||||
LogID string `json:"log_id,omitempty"`
|
||||
Success bool `json:"success"`
|
||||
Status string `json:"status"`
|
||||
Output string `json:"output,omitempty"`
|
||||
Error string `json:"error,omitempty"`
|
||||
Duration int64 `json:"duration,omitempty"`
|
||||
ExitCode int `json:"exit_code,omitempty"`
|
||||
StartTime string `json:"start_time,omitempty"`
|
||||
EndTime string `json:"end_time,omitempty"`
|
||||
}
|
||||
|
||||
// ToExecutionResultVO 将 ExecutionResult 转换为 ExecutionResultVO
|
||||
func ToExecutionResultVO(res *executor.ExecutionResult) *ExecutionResultVO {
|
||||
if res == nil {
|
||||
return nil
|
||||
}
|
||||
vo := &ExecutionResultVO{
|
||||
TaskID: res.TaskID,
|
||||
LogID: res.LogID,
|
||||
Success: res.Success,
|
||||
Status: res.Status,
|
||||
Output: res.Output,
|
||||
Error: res.Error,
|
||||
Duration: res.Duration,
|
||||
ExitCode: res.ExitCode,
|
||||
}
|
||||
if !res.StartTime.IsZero() {
|
||||
vo.StartTime = res.StartTime.Format("2006-01-02 15:04:05")
|
||||
}
|
||||
if !res.EndTime.IsZero() {
|
||||
vo.EndTime = res.EndTime.Format("2006-01-02 15:04:05")
|
||||
}
|
||||
return vo
|
||||
}
|
||||
|
||||
// ToExecutionResultVOList 将 ExecutionResult 列表转换为 ExecutionResultVO 列表
|
||||
func ToExecutionResultVOList(results []executor.ExecutionResult) []*ExecutionResultVO {
|
||||
if results == nil {
|
||||
return nil
|
||||
}
|
||||
vos := make([]*ExecutionResultVO, len(results))
|
||||
for i := range results {
|
||||
vos[i] = ToExecutionResultVO(&results[i])
|
||||
}
|
||||
return vos
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package vo
|
||||
|
||||
// WSMessage 通用 WebSocket 消息结构
|
||||
type WSMessage struct {
|
||||
Type string `json:"type"` // 事件类型: task_status, notice, system_stats
|
||||
Timestamp int64 `json:"timestamp"` // 毫秒时间戳
|
||||
Payload interface{} `json:"payload"` // 负载数据
|
||||
}
|
||||
Reference in New Issue
Block a user