- Agent 通过 token 向主控注册,心跳/拉取 xray 配置/上报流量 - 主控按节点下发完整 xray 配置(含出站+路由),agent 只应用不自建 - Agent 心跳同步更新节点在线状态;后台探针跳过 agent 托管节点 - 「添加节点」支持 Agent 类型,自动生成 token 并弹窗展示 - VLESS/SS2022 入站;同机测试支持 api_port 隔离 - 新增 install-master.sh / install-agent.sh 一键安装脚本 - systemd + OpenWrt procd 服务文件,docs/AGENT.md
99 lines
2.8 KiB
Go
99 lines
2.8 KiB
Go
package service
|
|
|
|
import (
|
|
"encoding/json"
|
|
"testing"
|
|
|
|
jsonutil "github.com/mhsanaei/3x-ui/v3/internal/util/json_util"
|
|
"github.com/mhsanaei/3x-ui/v3/internal/xray"
|
|
)
|
|
|
|
func testAgentCfg() *xray.Config {
|
|
return &xray.Config{
|
|
OutboundConfigs: jsonutil.RawMessage(`[{"tag":"direct","protocol":"freedom"},{"tag":"blocked","protocol":"blackhole"}]`),
|
|
RouterConfig: jsonutil.RawMessage(`{"rules":[
|
|
{"type":"field","inboundTag":["api"],"outboundTag":"api"},
|
|
{"type":"field","ip":["geoip:private"],"outboundTag":"blocked"}
|
|
]}`),
|
|
}
|
|
}
|
|
|
|
func agentRulesOf(t *testing.T, cfg *xray.Config) []any {
|
|
t.Helper()
|
|
var routing map[string]any
|
|
if err := json.Unmarshal(cfg.RouterConfig, &routing); err != nil {
|
|
t.Fatalf("unmarshal routing: %v", err)
|
|
}
|
|
rules, _ := routing["rules"].([]any)
|
|
return rules
|
|
}
|
|
|
|
func hasAPIRule(rules []any) bool {
|
|
for _, r := range rules {
|
|
m, ok := r.(map[string]any)
|
|
if !ok {
|
|
continue
|
|
}
|
|
for _, tag := range inboundTagsOf(m["inboundTag"]) {
|
|
if tag == "api" {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func TestApplyAgentEgressOverride(t *testing.T) {
|
|
cfg := testAgentCfg()
|
|
outbounds := `[{"tag":"PPFL3-L1","protocol":"socks","settings":{"servers":[{"address":"10.240.64.2","port":23183}]}}]`
|
|
rules := `[{"type":"field","inboundTag":["in-18692-tcp"],"outboundTag":"PPFL3-L1"}]`
|
|
if err := applyAgentEgress(cfg, outbounds, rules); err != nil {
|
|
t.Fatalf("applyAgentEgress: %v", err)
|
|
}
|
|
var obs []any
|
|
if err := json.Unmarshal(cfg.OutboundConfigs, &obs); err != nil {
|
|
t.Fatalf("unmarshal outbounds: %v", err)
|
|
}
|
|
if len(obs) != 1 || obs[0].(map[string]any)["tag"] != "PPFL3-L1" {
|
|
t.Fatalf("outbounds not replaced: %s", cfg.OutboundConfigs)
|
|
}
|
|
rs := agentRulesOf(t, cfg)
|
|
if len(rs) != 2 {
|
|
t.Fatalf("expected 2 rules (custom + api), got %d", len(rs))
|
|
}
|
|
if !hasAPIRule(rs) {
|
|
t.Fatal("api routing rule missing after override")
|
|
}
|
|
// The api rule must come first so it wins over user rules.
|
|
first, _ := rs[0].(map[string]any)
|
|
if first["outboundTag"] != "api" {
|
|
t.Fatal("api rule should be prepended")
|
|
}
|
|
}
|
|
|
|
func TestApplyAgentEgressKeepsTemplate(t *testing.T) {
|
|
cfg := testAgentCfg()
|
|
if err := applyAgentEgress(cfg, "", ""); err != nil {
|
|
t.Fatalf("applyAgentEgress: %v", err)
|
|
}
|
|
var obs []any
|
|
if err := json.Unmarshal(cfg.OutboundConfigs, &obs); err != nil {
|
|
t.Fatalf("unmarshal outbounds: %v", err)
|
|
}
|
|
if len(obs) != 2 {
|
|
t.Fatalf("template outbounds should be kept, got %d", len(obs))
|
|
}
|
|
if !hasAPIRule(agentRulesOf(t, cfg)) {
|
|
t.Fatal("api routing rule missing")
|
|
}
|
|
}
|
|
|
|
func TestApplyAgentEgressRejectsBadJSON(t *testing.T) {
|
|
if err := applyAgentEgress(testAgentCfg(), "not-json", "[]"); err == nil {
|
|
t.Fatal("expected error for bad outbounds JSON")
|
|
}
|
|
if err := applyAgentEgress(testAgentCfg(), "[]", `{"not":"array"}`); err == nil {
|
|
t.Fatal("expected error for non-array routing rules")
|
|
}
|
|
}
|