feat: group listener ports (binds inbound ports to proxy groups)#57
feat: group listener ports (binds inbound ports to proxy groups)#57sodiseng wants to merge 2 commits into
Conversation
- Per-source provider settings: custom key, attach mode (grouped/inline/bare), emoji group name, node filter regex; persisted end-to-end (store, handoff, subscription save/load, server-side saved-sources normalization) - Grouped mode generates an airport group (select + use) that joins all manual-select groups as a regular member: excludable, sortable, and manually attachable to any group via new provider member refs - Fixed provider defaults (interval, health-check), provider path follows key - UI: inline status bar on URL source editors, provider settings in advanced dialog, read-only airport group card, provider members in group editor, airport groups in visual preview
Bind local inbound ports to existing proxy groups, generating mihomo listeners entries (proxy: <group name>, fixed mixed/0.0.0.0/udp). - core: GroupListenerBinding model, normalization (dedupe by target), generator merge with target/port/name validation against node listeners and mixed-port - UI: group listener management card in the proxy groups section (group select + port input, conflict hints, exposure warning), port badges on bound group cards - store actions + subscription save/load + auth handoff serialization
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0862e5d5a4
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| ...(opts.customProxyGroups ?? []) | ||
| .map((group) => (typeof group.name === "string" ? group.name.trim() : "")) | ||
| .filter(Boolean), |
There was a problem hiding this comment.
Filter disabled groups before reserving provider names
When a disabled custom proxy group has the same name as a grouped provider's requested group name, this helper still reserves that disabled name, but generateClashConfig builds the reserved custom set from only enabled groups. Panels that use buildProviderGroupInfo can therefore show/select a suffixed provider group such as ✈️ Air 2, while the generated YAML actually creates ✈️ Air; selecting the displayed name for features like group listeners then points at a group that does not exist in the output.
Useful? React with 👍 / 👎.
| if (list.length === 0) return [] as Array<Record<string, unknown>>; | ||
|
|
||
| const validTargets = new Set(availablePolicyTargets); | ||
| const usedPorts = new Set<number>(Array.isArray(listeners) ? listeners.map((l) => l.port) : []); |
There was a problem hiding this comment.
Include base listeners when rejecting duplicate ports
When the base YAML already declares a listener on the same port as a new group listener, usedPorts only contains generated node-listener ports and mixed-port. The base listeners are merged back in later, so the final YAML can contain two listener entries with the same port, causing the client to fail binding that inbound; seed this set from baseTopLevelPatch.listeners as well before accepting group listener ports.
Useful? React with 👍 / 👎.
| const groupedAttachments = groupedProviderKeys | ||
| .map((key) => attachmentByKey.get(key)) | ||
| .filter((attachment): attachment is ProxyProviderAttachment => Boolean(attachment)); |
There was a problem hiding this comment.
Preserve attachment order for grouped providers
Grouped provider names are assigned after groupedProviderKeys has been derived from the alphabetically sorted provider keys, while the UI/provider plan computes those same names in source attachment order. If two grouped providers request the same group name, or collide with a reserved name, the suffixes can be assigned to different provider keys in the UI versus the generated YAML, so advanced member selections or group listeners saved against one provider key end up pointing at a different airport group.
Useful? React with 👍 / 👎.
| if (typeof config.mixedPort === "number" && Number.isInteger(config.mixedPort)) { | ||
| usedPorts.add(config.mixedPort); |
There was a problem hiding this comment.
Reserve mixed-port from the merged base YAML
When mixed-port is set only in explicit base/DNS YAML, config.mixedPort still holds the default/user-config value, so this check reserves the wrong port. A group listener using the YAML-defined mixed-port is accepted and then merged into the final config alongside that same top-level port, which makes the client fail to bind one of the inbounds.
Useful? React with 👍 / 👎.
| const customKey = typeof item.providerKey === "string" ? item.providerKey.trim() : ""; | ||
| const key = customKey || defaultKey; | ||
| // key 重复时跳过后者(与旧行为一致);UI 层负责重复提示 | ||
| if (Object.prototype.hasOwnProperty.call(providers, key)) continue; |
There was a problem hiding this comment.
Prevent duplicate provider keys from dropping sources
When two URL sources are given the same custom providerKey (or one custom key collides with another source's default key), this branch silently skips the later provider. The new UI exposes free-form key editing but does not block or warn about duplicates, so a user can enable proxy-providers for multiple subscriptions and have one omitted from the generated YAML entirely.
Useful? React with 👍 / 👎.
|
感谢你实现策略组监听端口,这个功能方向我们希望保留,也愿意继续 review。 不过当前 #57 stacked on #56,而我们目前的产品决定是不引入 #56 的 proxy-providers 扩展。因此按现状无法直接合并。能否请你把 #57 调整为一个基于最新 功能范围
UI 调整目前单独的“分组监听管理”区域会继续增加页面密度,希望改为:
状态与生成逻辑
测试希望补充以下回归场景:
如果你愿意按这个范围调整,我们会优先继续 review #57。功能方向本身很有价值,感谢你的贡献。 |
What
Bind local inbound ports to existing proxy groups, generating mihomo
listenersentries withproxy: <group name>— traffic entering that port goes directly out through the bound policy group.给已存在的策略组绑定本地 inbound 监听端口,生成 mihomo
listeners条目(proxy: 组名):进入该端口的流量直接走绑定的策略组出站。Features / 功能
GroupListenerBinding {id, target, port}model; normalization (trim, drop invalid, dedupe by target — one port per group); generator merges entries after base-config and node listeners, validating target against all policy groups and deduping ports against node listeners /mixed-port/ each other. Fixedtype: mixed,listen: 0.0.0.0,udp: true, auto namesgroup-in-{i}(mirrors the existing node listener-ports convention).:{port}badge on their cards (module / custom / dialer / provider group cards).listenerPorts).mihomo reference
Listener
proxyfield ("使用出站代理或策略组直接出站") per https://wiki.metacubex.one/config/inbound/listeners/ (mixed listener fields:name/type/port/listen/udp).Tests
--max-warnings=0clean.