- 新增 internal/selfupdate:发行坐标(基址常量+env 覆盖)、Gitea latest tag 查询、sha256 旁车校验下载、原子换名(旧二进制留 .bak、 失败可回滚),含单测 - 主控:UpdatePanels 对 agent 节点改为记录更新意向(最新 tag 的 V 系→A 系目标;已达标报错不记意向);意向经现有 GET config 轮询 捎带下发;心跳收 updateStatus 回报(failed/current 清意向留状态、 过期 requested_at 忽略),遥测版本达标自动确认 success - agent:识别意向→比自身 A 版本(已达标报 current 不下载)→按架构 下载资产+sha256 校验→换名留 .bak→刷一次心跳→stopXray→ syscall.Exec 自重启;exec 失败回滚 .bak 并报 failed;失败 10 分钟 内不重下同一意向;旧版全程保持运行 - NodeView 增 agentUpdateState/Error/Target 供节点页可观测 - 单测:selfupdate 换名/校验、agent 换名重启/坏校验保旧版/已达标 跳过/exec 失败回滚、主控意向记录/已达标跳过、config 捎带意向、 心跳 failed 清意向、心跳新版本确认 success
64 lines
3.3 KiB
Go
64 lines
3.3 KiB
Go
package model
|
|
|
|
// AgentNode binds a lightweight agent (token-registered, no web panel) to a
|
|
// 3x-ui node row and carries the per-agent egress configuration.
|
|
//
|
|
// Agents are the only writers of their own LastSeen; the panel owns everything
|
|
// else. Authentication uses only the SHA-256 hex of the token; since the
|
|
// next-core batch (item 19) the raw token is additionally kept recoverably
|
|
// in TokenPlain — an operator-accepted tradeoff so the node edit page can
|
|
// re-display the full install command.
|
|
type AgentNode struct {
|
|
Id int `json:"id" gorm:"primaryKey;autoIncrement"`
|
|
TokenHash string `json:"-" gorm:"size:64;uniqueIndex"`
|
|
TokenPrefix string `json:"tokenPrefix" gorm:"size:16;index"`
|
|
// TokenPlain is a recoverable copy of the raw token: stored at
|
|
// issuance, and backfilled from the first authenticated agent request
|
|
// for rows created before this field existed (item 19). Existing
|
|
// values are never overwritten by requests; only issuance (create /
|
|
// panel-side re-issue) writes a fresh one. Never serialized.
|
|
TokenPlain string `json:"-" gorm:"type:text"`
|
|
Name string `json:"name"`
|
|
// NodeID is the 3x-ui node whose inbounds this agent serves. Nil means the
|
|
// token was issued but not yet bound to a node.
|
|
NodeID *int `json:"nodeId" gorm:"index"`
|
|
// Outbounds is the full xray "outbounds" JSON array pushed to this agent,
|
|
// replacing the template defaults. Empty means "use template defaults".
|
|
Outbounds string `json:"outbounds" gorm:"type:text"`
|
|
// RoutingRules is the full xray "routing.rules" JSON array pushed to this
|
|
// agent, replacing the template defaults. Empty means "use template defaults".
|
|
RoutingRules string `json:"routingRules" gorm:"type:text"`
|
|
LastSeen int64 `json:"lastSeen"`
|
|
CreatedAt int64 `json:"createdAt" gorm:"autoCreateTime:milli"`
|
|
UpdatedAt int64 `json:"updatedAt" gorm:"autoUpdateTime:milli"`
|
|
|
|
// Heartbeat telemetry (P13): the agent piggybacks host/runtime facts on
|
|
// its 60s heartbeat and the panel overlays them onto the node list row.
|
|
// All fields are best-effort: an agent that never reported telemetry has
|
|
// TelemetryAt == 0 and the UI keeps showing "-" for its row.
|
|
AgentVersion string `json:"agentVersion"`
|
|
XrayVersion string `json:"xrayVersion"`
|
|
UptimeSecs uint64 `json:"uptimeSecs"`
|
|
LatencyMs int `json:"latencyMs"`
|
|
CpuPct float64 `json:"cpuPct"`
|
|
MemPct float64 `json:"memPct"`
|
|
IPv4 string `json:"ipv4"`
|
|
IPv6 string `json:"ipv6"`
|
|
// TelemetryAt is the unix-milli time of the last heartbeat that carried
|
|
// a telemetry object.
|
|
TelemetryAt int64 `json:"telemetryAt"`
|
|
|
|
// One-click update (item 20): the master records an update intent here;
|
|
// the agent picks it up from its config poll, downloads + verifies +
|
|
// swaps its binary, restarts itself and reports the outcome back on
|
|
// the heartbeat. UpdateRequestedAt == 0 means no intent is pending.
|
|
UpdateRequestedAt int64 `json:"updateRequestedAt"`
|
|
UpdateTarget string `json:"updateTarget"` // A-form display target, e.g. "A1.0.1"
|
|
UpdateTag string `json:"updateTag"` // release tag to download, e.g. "V1.0.1"
|
|
// UpdateState: "" | pending | downloading | swapped | failed |
|
|
// current | success (success = new A version observed by the master).
|
|
UpdateState string `json:"updateState"`
|
|
UpdateError string `json:"updateError"`
|
|
UpdateStateAt int64 `json:"updateStateAt"`
|
|
}
|