feat(clients): hide disabled inbounds in the client form selector

The attach-inbounds select in the client add/edit modal listed every inbound, so panels with many disabled inbounds had to scroll past dead entries. InboundOption now carries the inbound's enable flag and the form drops disabled inbounds from the options, keeping ones the client is already attached to so edit mode still renders existing assignments.

Closes #5645
This commit is contained in:
MHSanaei
2026-07-03 09:26:06 +02:00
parent b2ceb854f5
commit 052dd85ad3
7 changed files with 20 additions and 2 deletions
+6
View File
@@ -1824,6 +1824,10 @@
},
"InboundOption": {
"properties": {
"enable": {
"example": true,
"type": "boolean"
},
"id": {
"example": 1,
"type": "integer"
@@ -1880,6 +1884,7 @@
}
},
"required": [
"enable",
"id",
"port",
"protocol",
@@ -2795,6 +2800,7 @@
"success": true,
"obj": [
{
"enable": true,
"id": 1,
"listen": "",
"nodeAddress": "",
+1
View File
@@ -399,6 +399,7 @@ export const EXAMPLES: Record<string, unknown> = {
"xver": 0
},
"InboundOption": {
"enable": true,
"id": 1,
"listen": "",
"nodeAddress": "",
+5
View File
@@ -1798,6 +1798,10 @@ export const SCHEMAS: Record<string, unknown> = {
},
"InboundOption": {
"properties": {
"enable": {
"example": true,
"type": "boolean"
},
"id": {
"example": 1,
"type": "integer"
@@ -1854,6 +1858,7 @@ export const SCHEMAS: Record<string, unknown> = {
}
},
"required": [
"enable",
"id",
"port",
"protocol",
+1
View File
@@ -393,6 +393,7 @@ export interface InboundFallback {
}
export interface InboundOption {
enable: boolean;
id: number;
listen?: string;
nodeAddress?: string;
+1
View File
@@ -420,6 +420,7 @@ export const InboundFallbackSchema = z.object({
export type InboundFallback = z.infer<typeof InboundFallbackSchema>;
export const InboundOptionSchema = z.object({
enable: z.boolean(),
id: z.number().int(),
listen: z.string().optional(),
nodeAddress: z.string().optional(),
@@ -376,12 +376,13 @@ export default function ClientFormModal({
const inboundOptions = useMemo(
() => (inbounds || [])
.filter((ib) => MULTI_CLIENT_PROTOCOLS.has(ib.protocol || ''))
.filter((ib) => ib.enable || (form.inboundIds || []).includes(ib.id))
.map((ib) => ({
label: formatInboundLabel(ib.tag, ib.remark),
value: ib.id,
title: formatInboundLabel(ib.tag, ib.remark),
})),
[inbounds],
[inbounds, form.inboundIds],
);
const linkRows = useMemo(() => form.externalLinks.filter((r) => r.kind === 'link'), [form.externalLinks]);
+4 -1
View File
@@ -297,6 +297,7 @@ type InboundOption struct {
Tag string `json:"tag" example:"in-443-tcp"`
Protocol string `json:"protocol" example:"vless"`
Port int `json:"port" example:"443"`
Enable bool `json:"enable" example:"true"`
TlsFlowCapable bool `json:"tlsFlowCapable" example:"true"`
SsMethod string `json:"ssMethod"`
WgPublicKey string `json:"wgPublicKey,omitempty"`
@@ -325,6 +326,7 @@ func (s *InboundService) GetInboundOptions(userId int) ([]InboundOption, error)
Tag string `gorm:"column:tag"`
Protocol string `gorm:"column:protocol"`
Port int `gorm:"column:port"`
Enable bool `gorm:"column:enable"`
StreamSettings string `gorm:"column:stream_settings"`
Settings string `gorm:"column:settings"`
Listen string `gorm:"column:listen"`
@@ -334,7 +336,7 @@ func (s *InboundService) GetInboundOptions(userId int) ([]InboundOption, error)
NodeAddress string `gorm:"column:node_address"`
}
err := db.Table("inbounds").
Select("inbounds.id, inbounds.remark, inbounds.tag, inbounds.protocol, inbounds.port, inbounds.stream_settings, inbounds.settings, inbounds.listen, inbounds.share_addr, inbounds.share_addr_strategy, inbounds.node_id, COALESCE(nodes.address, '') AS node_address").
Select("inbounds.id, inbounds.remark, inbounds.tag, inbounds.protocol, inbounds.port, inbounds.enable, inbounds.stream_settings, inbounds.settings, inbounds.listen, inbounds.share_addr, inbounds.share_addr_strategy, inbounds.node_id, COALESCE(nodes.address, '') AS node_address").
Joins("LEFT JOIN nodes ON nodes.id = inbounds.node_id").
Where("inbounds.user_id = ?", userId).
Order("inbounds.id ASC").
@@ -355,6 +357,7 @@ func (s *InboundService) GetInboundOptions(userId int) ([]InboundOption, error)
Tag: r.Tag,
Protocol: r.Protocol,
Port: r.Port,
Enable: r.Enable,
TlsFlowCapable: inboundCanEnableTlsFlow(r.Protocol, r.StreamSettings, r.Settings),
SsMethod: inboundShadowsocksMethod(r.Protocol, r.Settings),
WgPublicKey: wgPublicKey,