probe-only node mode: master flag + agent gate + panel switch (V2.1.0 A)
agent_nodes gains probe_only; getConfig always serves the flag and withholds the sing-box payload (null config, empty hash) for flagged nodes while probe tasks, fast interval, stat iface and update intents ride as usual. The agent gates both core branches when the local agent.json preset or the master flag is on, retires a running core once (best-effort tail flush, switchover bookkeeping untouched so turning the flag off self-heals through the existing hash-current-but-not-running path), and makes the traffic cycle an explicit no-op. Panel: node create/update accept probeOnly, the node view overlays it from the binding, the node form gains a switch (carried into the generated install command), the list shows a probe-only tag in the core-version cell, and install-agent.sh --probe-only writes the preset, skips the core download/self-check and inherits the flag on no-arg reruns.
This commit is contained in:
@@ -671,22 +671,37 @@ func (a *AgentController) getConfig(c *gin.Context) {
|
||||
c.JSON(409, gin.H{"message": "agent token is not bound to a node"})
|
||||
return
|
||||
}
|
||||
sbCfg, sbErr := a.singboxService.GetSingboxConfigForAgent(*agent.NodeID, agent.SingboxOutbounds, agent.SingboxRoutingRules)
|
||||
if sbErr != nil {
|
||||
logger.Warningf("agent config: singbox config build failed for node %d: %v", *agent.NodeID, sbErr)
|
||||
c.JSON(500, gin.H{"message": "failed to build sing-box config"})
|
||||
return
|
||||
// Probe-only nodes (V2.1.0) get no core payload at all: the build
|
||||
// is skipped entirely, so a broken data model on such a node can
|
||||
// never 500 its poll, and the probe keys + update intent below
|
||||
// ride along exactly as for normal nodes. singbox_config stays
|
||||
// null and the hash empty, which agents read as "no payload".
|
||||
var sbRaw json.RawMessage
|
||||
sbHash := ""
|
||||
if !agent.ProbeOnly {
|
||||
sbCfg, sbErr := a.singboxService.GetSingboxConfigForAgent(*agent.NodeID, agent.SingboxOutbounds, agent.SingboxRoutingRules)
|
||||
if sbErr != nil {
|
||||
logger.Warningf("agent config: singbox config build failed for node %d: %v", *agent.NodeID, sbErr)
|
||||
c.JSON(500, gin.H{"message": "failed to build sing-box config"})
|
||||
return
|
||||
}
|
||||
raw, mErr := json.Marshal(sbCfg)
|
||||
if mErr != nil {
|
||||
logger.Warningf("agent config: singbox config encode failed for node %d: %v", *agent.NodeID, mErr)
|
||||
c.JSON(500, gin.H{"message": "failed to encode sing-box config"})
|
||||
return
|
||||
}
|
||||
sbRaw = raw
|
||||
sbSum := sha256.Sum256(sbRaw)
|
||||
sbHash = hex.EncodeToString(sbSum[:])
|
||||
}
|
||||
sbRaw, mErr := json.Marshal(sbCfg)
|
||||
if mErr != nil {
|
||||
logger.Warningf("agent config: singbox config encode failed for node %d: %v", *agent.NodeID, mErr)
|
||||
c.JSON(500, gin.H{"message": "failed to encode sing-box config"})
|
||||
return
|
||||
}
|
||||
sbSum := sha256.Sum256(sbRaw)
|
||||
payload := gin.H{
|
||||
"singbox_config": json.RawMessage(sbRaw),
|
||||
"singbox_config_hash": hex.EncodeToString(sbSum[:]),
|
||||
"singbox_config": sbRaw,
|
||||
"singbox_config_hash": sbHash,
|
||||
// Probe-only (V2.1.0): the per-node flag rides every poll so
|
||||
// toggling it in the panel takes effect without touching the
|
||||
// node. Always present from this master version.
|
||||
"probe_only": agent.ProbeOnly,
|
||||
// Item 7 B4: the agent's probe task set rides the existing
|
||||
// config poll (no new channel). Always present from this
|
||||
// master version — an empty list means "run no probes", while
|
||||
|
||||
Reference in New Issue
Block a user