Files
3x-ui/internal/web/service/config.json
T
MHSanaei 6b16d8c37a feat: apply inbound/outbound/routing changes live via Xray gRPC API
Add a hot-apply layer that computes a diff between the old and new
generated config and applies only the changed parts through the Xray
gRPC HandlerService and RoutingService, avoiding a full process restart
whenever possible. A restart is still performed when sections that have
no reload API (log, dns, policy, observatory, ...) actually change.

Key additions:
- internal/xray/hot_diff.go: ComputeHotDiff with canonical-JSON
  comparison (sorted keys, null=absent, full number precision) so UI
  reformatting never triggers a spurious restart
- internal/xray/api.go: AddOutbound/DelOutbound, ApplyRoutingConfig,
  GetBalancerInfo, SetBalancerTarget, TestRoute gRPC wrappers
- internal/web/service/xray.go: tryHotApply, ensureAPIServices,
  GetBalancersStatus, OverrideBalancer, TestRoute service methods
- internal/web/controller/xray_setting.go: balancerStatus,
  balancerOverride, routeTest API endpoints
- frontend: BalancersTab live-status/override columns, RouteTester
  component, Restart button removed (Save now hot-applies)
- balancer-helpers.ts: syncObservatories never creates observatory
  sections for random/roundRobin balancers (no reload API → restart)
- i18n: balancerLive/Override/routeTester keys added to all 13 locales
2026-06-10 23:01:33 +02:00

87 lines
1.5 KiB
JSON

{
"api": {
"services": [
"HandlerService",
"LoggerService",
"StatsService",
"RoutingService"
],
"tag": "api"
},
"inbounds": [{
"listen": "127.0.0.1",
"port": 62789,
"protocol": "tunnel",
"settings": {
"rewriteAddress": "127.0.0.1"
},
"tag": "api"
}],
"log": {
"access": "none",
"dnsLog": false,
"error": "",
"loglevel": "warning",
"maskAddress": ""
},
"metrics": {
"listen": "127.0.0.1:11111",
"tag": "metrics_out"
},
"outbounds": [{
"protocol": "freedom",
"settings": {
"domainStrategy": "AsIs",
"finalRules": [
{ "action": "allow" }
]
},
"tag": "direct"
},
{
"protocol": "blackhole",
"settings": {},
"tag": "blocked"
}
],
"policy": {
"levels": {
"0": {
"statsUserDownlink": true,
"statsUserUplink": true
}
},
"system": {
"statsInboundDownlink": true,
"statsInboundUplink": true,
"statsOutboundDownlink": false,
"statsOutboundUplink": false
}
},
"routing": {
"domainStrategy": "AsIs",
"rules": [{
"inboundTag": [
"api"
],
"outboundTag": "api",
"type": "field"
},
{
"ip": [
"geoip:private"
],
"outboundTag": "blocked",
"type": "field"
},
{
"outboundTag": "blocked",
"protocol": [
"bittorrent"
],
"type": "field"
}
]
},
"stats": {}
}