Просмотр исходного кода

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
MHSanaei 18 часов назад
Родитель
Сommit
052dd85ad3

+ 6 - 0
frontend/public/openapi.json

@@ -1824,6 +1824,10 @@
       },
       },
       "InboundOption": {
       "InboundOption": {
         "properties": {
         "properties": {
+          "enable": {
+            "example": true,
+            "type": "boolean"
+          },
           "id": {
           "id": {
             "example": 1,
             "example": 1,
             "type": "integer"
             "type": "integer"
@@ -1880,6 +1884,7 @@
           }
           }
         },
         },
         "required": [
         "required": [
+          "enable",
           "id",
           "id",
           "port",
           "port",
           "protocol",
           "protocol",
@@ -2795,6 +2800,7 @@
                   "success": true,
                   "success": true,
                   "obj": [
                   "obj": [
                     {
                     {
+                      "enable": true,
                       "id": 1,
                       "id": 1,
                       "listen": "",
                       "listen": "",
                       "nodeAddress": "",
                       "nodeAddress": "",

+ 1 - 0
frontend/src/generated/examples.ts

@@ -399,6 +399,7 @@ export const EXAMPLES: Record<string, unknown> = {
     "xver": 0
     "xver": 0
   },
   },
   "InboundOption": {
   "InboundOption": {
+    "enable": true,
     "id": 1,
     "id": 1,
     "listen": "",
     "listen": "",
     "nodeAddress": "",
     "nodeAddress": "",

+ 5 - 0
frontend/src/generated/schemas.ts

@@ -1798,6 +1798,10 @@ export const SCHEMAS: Record<string, unknown> = {
   },
   },
   "InboundOption": {
   "InboundOption": {
     "properties": {
     "properties": {
+      "enable": {
+        "example": true,
+        "type": "boolean"
+      },
       "id": {
       "id": {
         "example": 1,
         "example": 1,
         "type": "integer"
         "type": "integer"
@@ -1854,6 +1858,7 @@ export const SCHEMAS: Record<string, unknown> = {
       }
       }
     },
     },
     "required": [
     "required": [
+      "enable",
       "id",
       "id",
       "port",
       "port",
       "protocol",
       "protocol",

+ 1 - 0
frontend/src/generated/types.ts

@@ -393,6 +393,7 @@ export interface InboundFallback {
 }
 }
 
 
 export interface InboundOption {
 export interface InboundOption {
+  enable: boolean;
   id: number;
   id: number;
   listen?: string;
   listen?: string;
   nodeAddress?: string;
   nodeAddress?: string;

+ 1 - 0
frontend/src/generated/zod.ts

@@ -420,6 +420,7 @@ export const InboundFallbackSchema = z.object({
 export type InboundFallback = z.infer<typeof InboundFallbackSchema>;
 export type InboundFallback = z.infer<typeof InboundFallbackSchema>;
 
 
 export const InboundOptionSchema = z.object({
 export const InboundOptionSchema = z.object({
+  enable: z.boolean(),
   id: z.number().int(),
   id: z.number().int(),
   listen: z.string().optional(),
   listen: z.string().optional(),
   nodeAddress: z.string().optional(),
   nodeAddress: z.string().optional(),

+ 2 - 1
frontend/src/pages/clients/ClientFormModal.tsx

@@ -376,12 +376,13 @@ export default function ClientFormModal({
   const inboundOptions = useMemo(
   const inboundOptions = useMemo(
     () => (inbounds || [])
     () => (inbounds || [])
       .filter((ib) => MULTI_CLIENT_PROTOCOLS.has(ib.protocol || ''))
       .filter((ib) => MULTI_CLIENT_PROTOCOLS.has(ib.protocol || ''))
+      .filter((ib) => ib.enable || (form.inboundIds || []).includes(ib.id))
       .map((ib) => ({
       .map((ib) => ({
         label: formatInboundLabel(ib.tag, ib.remark),
         label: formatInboundLabel(ib.tag, ib.remark),
         value: ib.id,
         value: ib.id,
         title: formatInboundLabel(ib.tag, ib.remark),
         title: formatInboundLabel(ib.tag, ib.remark),
       })),
       })),
-    [inbounds],
+    [inbounds, form.inboundIds],
   );
   );
 
 
   const linkRows = useMemo(() => form.externalLinks.filter((r) => r.kind === 'link'), [form.externalLinks]);
   const linkRows = useMemo(() => form.externalLinks.filter((r) => r.kind === 'link'), [form.externalLinks]);

+ 4 - 1
internal/web/service/inbound.go

@@ -297,6 +297,7 @@ type InboundOption struct {
 	Tag            string `json:"tag" example:"in-443-tcp"`
 	Tag            string `json:"tag" example:"in-443-tcp"`
 	Protocol       string `json:"protocol" example:"vless"`
 	Protocol       string `json:"protocol" example:"vless"`
 	Port           int    `json:"port" example:"443"`
 	Port           int    `json:"port" example:"443"`
+	Enable         bool   `json:"enable" example:"true"`
 	TlsFlowCapable bool   `json:"tlsFlowCapable" example:"true"`
 	TlsFlowCapable bool   `json:"tlsFlowCapable" example:"true"`
 	SsMethod       string `json:"ssMethod"`
 	SsMethod       string `json:"ssMethod"`
 	WgPublicKey    string `json:"wgPublicKey,omitempty"`
 	WgPublicKey    string `json:"wgPublicKey,omitempty"`
@@ -325,6 +326,7 @@ func (s *InboundService) GetInboundOptions(userId int) ([]InboundOption, error)
 		Tag               string `gorm:"column:tag"`
 		Tag               string `gorm:"column:tag"`
 		Protocol          string `gorm:"column:protocol"`
 		Protocol          string `gorm:"column:protocol"`
 		Port              int    `gorm:"column:port"`
 		Port              int    `gorm:"column:port"`
+		Enable            bool   `gorm:"column:enable"`
 		StreamSettings    string `gorm:"column:stream_settings"`
 		StreamSettings    string `gorm:"column:stream_settings"`
 		Settings          string `gorm:"column:settings"`
 		Settings          string `gorm:"column:settings"`
 		Listen            string `gorm:"column:listen"`
 		Listen            string `gorm:"column:listen"`
@@ -334,7 +336,7 @@ func (s *InboundService) GetInboundOptions(userId int) ([]InboundOption, error)
 		NodeAddress       string `gorm:"column:node_address"`
 		NodeAddress       string `gorm:"column:node_address"`
 	}
 	}
 	err := db.Table("inbounds").
 	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").
 		Joins("LEFT JOIN nodes ON nodes.id = inbounds.node_id").
 		Where("inbounds.user_id = ?", userId).
 		Where("inbounds.user_id = ?", userId).
 		Order("inbounds.id ASC").
 		Order("inbounds.id ASC").
@@ -355,6 +357,7 @@ func (s *InboundService) GetInboundOptions(userId int) ([]InboundOption, error)
 			Tag:               r.Tag,
 			Tag:               r.Tag,
 			Protocol:          r.Protocol,
 			Protocol:          r.Protocol,
 			Port:              r.Port,
 			Port:              r.Port,
+			Enable:            r.Enable,
 			TlsFlowCapable:    inboundCanEnableTlsFlow(r.Protocol, r.StreamSettings, r.Settings),
 			TlsFlowCapable:    inboundCanEnableTlsFlow(r.Protocol, r.StreamSettings, r.Settings),
 			SsMethod:          inboundShadowsocksMethod(r.Protocol, r.Settings),
 			SsMethod:          inboundShadowsocksMethod(r.Protocol, r.Settings),
 			WgPublicKey:       wgPublicKey,
 			WgPublicKey:       wgPublicKey,