BuildXrayConfig 根据节点 inbound 配置和用户凭据生成 Xray 配置 JSON。
(nodeInbounds []inbounds.Inbound, userAccesses []users.UserInbound, userMap map[string]users.User, opts BuildOptions)
| 237 | |
| 238 | // BuildXrayConfig 根据节点 inbound 配置和用户凭据生成 Xray 配置 JSON。 |
| 239 | func BuildXrayConfig(nodeInbounds []inbounds.Inbound, userAccesses []users.UserInbound, userMap map[string]users.User, opts BuildOptions) (string, error) { |
| 240 | if len(nodeInbounds) == 0 { |
| 241 | return "", fmt.Errorf("at least one inbound is required") |
| 242 | } |
| 243 | |
| 244 | // 过滤已启用的用户访问记录 |
| 245 | activeAccesses := make([]users.UserInbound, 0, len(userAccesses)) |
| 246 | for _, acc := range userAccesses { |
| 247 | u, ok := userMap[acc.UserID] |
| 248 | if ok && u.EffectiveEnabled() { |
| 249 | activeAccesses = append(activeAccesses, acc) |
| 250 | } |
| 251 | } |
| 252 | // 端口转发 inbound 不需要用户列表;只有当节点存在非 portforward 的 inbound 时才要求活跃用户 |
| 253 | hasProxyInbound := false |
| 254 | for _, ib := range nodeInbounds { |
| 255 | if ib.Protocol != "portforward" { |
| 256 | hasProxyInbound = true |
| 257 | break |
| 258 | } |
| 259 | } |
| 260 | if hasProxyInbound && len(activeAccesses) == 0 { |
| 261 | return "", fmt.Errorf("at least one active user is required") |
| 262 | } |
| 263 | |
| 264 | // in-process 模式:api.listen 留空,commander 不对外暴露 gRPC 端口。 |
| 265 | // 流量统计通过 in-process API 直接访问,不走网络。 |
| 266 | const apiListenAddr = "" |
| 267 | |
| 268 | xrayInbounds := make([]xrayInbound, 0, len(nodeInbounds)) |
| 269 | var routingRules []xrayRoutingRule |
| 270 | |
| 271 | // 收集用于 V2Ray Stats 的用户 email 列表 |
| 272 | seenUsers := make(map[string]struct{}) |
| 273 | |
| 274 | for _, ib := range nodeInbounds { |
| 275 | tag := ib.Tag |
| 276 | if tag == "" { |
| 277 | tag = fmt.Sprintf("%s-%d", ib.Protocol, ib.Port) |
| 278 | } |
| 279 | |
| 280 | // 过滤出被分配到此 inbound 的用户凭据,按 UserID 去重(同一用户可能同时有直接分配和组分配) |
| 281 | ibAccesses := make([]users.UserInbound, 0, len(activeAccesses)) |
| 282 | seenUserID := make(map[string]struct{}) |
| 283 | for _, acc := range activeAccesses { |
| 284 | if acc.InboundID == "" || acc.InboundID == ib.ID { |
| 285 | if _, dup := seenUserID[acc.UserID]; !dup { |
| 286 | seenUserID[acc.UserID] = struct{}{} |
| 287 | ibAccesses = append(ibAccesses, acc) |
| 288 | } |
| 289 | } |
| 290 | } |
| 291 | |
| 292 | // portforward:由 NodeGate layer4 处理,Xray 不需要生成任何配置。 |
| 293 | if ib.Protocol == "portforward" { |
| 294 | continue |
| 295 | } |
| 296 |
no test coverage detected