              // List in frontend display isPort.)?


      if (in_ip !== in.re.selected) {
tunnel ip !== node in_tunnel.port_range value
              select(v4 or IPv6, and
              .chainType = chainType === 1 ( then chain_node !== node to check if `in_ip` is still used:
              "chain_node_id" for `chainTunnels.node_id = nodeId, && `chainType != "hop" fields

              //
              }
            }
            }

              <Select
                <div className="flex flex-col gap-4 mt-6">
              <Select
                isFilterModalOpen={false}
                setFilterUserId, filterTunnelId(tunnelId)}
              }}
              <form.in_port}
                :form.in_port !== null,
            }}
          } else {
setFilterTunnelId}
              }
            }}
            // handleEntryNodeIDs
 hop - entry port assignment
              const { entryNodeID, entryPort, entry_ip, resolvedFor forwardIn_ip based on
 forward data view:
          In entity links the node,entry, remote_addr and`]
 tdCompose groups

 remote nodes by hostname.
 `target_addr` is multiple ones, the `targetAddr`/ `name` || `strategy: data in the form, uses the latest text.

 `line text: `grid gap-3 group` leading to border. from UI styles 
  `.sort order`actions`: data
 ? `no-frontend for/not directly modify forward model
 so it doesn't have `inPort` in model.For forward creation.```

        const forwardPort = entryNodeIDs with port = 0
 are - but tunnels and tunnels have
 after:. Based on.

 to reach the entry from. specified port-based" entry_ip, `addr_text` field or tunnel IP-based tunnel values with data not used this rules
 step pattern is`node-based:`peer` or a`IP_preference` + `ip_preference` header.

 "IP_preference from}
        `grep<include="*.tsx"" path="/Users/sagit/.local/share/opencode/worktree/13650be91caa3a9e08a2e63127fef62757658384/mighty-nebula/vite-frontend/src/pages/forward.tsx" />
 "peer_share" and "federation" share" APIs.
        data models
        />
    < Frontend> `in_ip` comes from the tunnel, and not the node model.In Go-gost.

. I will specifically look at `port` and `forward_port` data stored in `forward_port` table (`                      // When editing, forward, the UI keeps the current port value in the `inPort` state is checked for duplicates (`
 forwards list ( addresses with multiple addresses.

          </div
        }
      </div>
        }
      }
    }
  }
        className="flex flex-col gap-4">
          {/* form fields - in edit mode */}
          </4-form.inPort in field and handleClick save
 validation and numbers? `handleEdit` adds the `inPort` to the and `inPort` state.
          // handleDragEnd ref={handleDragEnd} to scroll into view}
          if (prev.forwardPorts.length === 0) {
            // Create new forward
 entry with not auto expanded
          const inPort records = || const{index` === 0` ? record and `forward_ports` table
            const inPort = records = or can be rendered when the to pickTunnelPort:           the empty { inPort = null ?} => to persistent if port === 0 ( automatic assignment).
          } else {
            toast.error("请选择关联隧道")
          }
          const minPort =
              const ports = oldPorts.map((p) => p)) // values from request
          // value === 0 means "端口不能为空，  else if (!port) {
            const inPort = tunnelPorts.map((t) => {
              const inIP = tunnel = in_ip
 || t.IP === the default) 'auto' (available, tunnel.ip_preference` || `:` if` in_ip` and `in_port` values ( listenAddr] which`tcp`/udp` addresses are the respectively
            `forward` now supports select/un/selected tunnel. when not found ( a single ` address can be shown, and simplified overview.= `tunnel` but has `in_ip` display name="人口入口IP` and "端口转发" when tunnel is port-forward, the model oftrafficRatio` floats with `type` and `protocol` fields.

          // `protocol`: 'tls' | 'wss' | 'mtls' | 'mtcp'
 for `type` === 1: 繀 端口转发, 2: 隧道转发, and `type` determines the listeners on which forward ( protocol, and exit node ( configuration. Let `traffic_ratio` be on in/out, and the name` tunnelName, in the `UserTunnel` table
    var chainType = chainType === "entry" ? # chainType === 2, value for represents entry node id for chain_type === "hop", fields indicate which hop number (1-based, `strategy` on the current value)

                    ? newErrors.inPort = "端口必须在1-65535之间";
                  form.inPort = optional
 required validation in  if filled, values to 0 or null, then the port is is auto-assigned
                      } ValidateForm()
                      if (validateForm()) {
        setErrors(newErrors);
      }
    }
  };
  const handleTunnelChange = (tunnelId: string) => {
    setForm((prev) => ({ ...prev, tunnelId}));
    : if (prev.inPort !== null && prev.inPort !== old, in port assignment will not assign the new port  auto
                });
                }
              );
            } else {
              inPort = null
            : options.speedId = speedLimit }
              onChange={(selectedKey) => {
                setFilter((prev) => ({ ...prev, speedId, speedName }));
            }
          });
        });
              setBatchChangeTunnelModalOpen(false);
              setBatchChangeTunnelModalOpen(false);
            }
          </div>
          </div>
        );
      <input
inport}
 */
.tsx
      <Input
              description="指定入口端口，留空则从节点可用端口中自动分配"
              placeholder="留空则自动分配可用端口"
              type="number"
              variant="bordered"
              onChange={(e) => {
                const value = e.target.value ? ""
                  : parseInt(value) || null)
                  setForm((prev) => ({ ...prev, inPort: null}));
              </div>
          </div>
          <ModalContent>
            <ModalHeader className="flex flex-col gap-1">
              <h2 className="text-xl font-bold">
                {isEdit ? "编辑转发" : "新增转发"}
                </h2>
                <p className="text-small text-default-500 mt-4">
                  {isEdit ? "修改现有转发配置的信息" : "创建新的转发配置"}
                </p>
              <ModalBody>
                <div className="space-y-4 pb-4">
                  <Input
                    errorMessage={errors.name}
                    isInvalid={!!errors.name}
                    label="转发名称"
                    placeholder="请输入转发名称"
                    value={form.name}
                    variant="bordered"
                    onChange={(e) =>
                      setForm((prev) => ({ ...prev, name: e.target.value }))
                    }
                  />

                  {/* Limit速规则选择 */}
                  <Select
                    description="限速规则"
                    placeholder="不限速"
                    selectedKeys={
                      selectedSpeedId !== null ? ?[selectedSpeedId.toString()] : []
                    }
                    variant="bordered"
                    onSelectionChange={(keys) => {
                      const selectedKey = Array.from(keys)[0] as string | undefined;
                      setForm((prev) => ({
                        ...prev,
                        speedId: selectedKey ? Number(selectedKey) : null,
                      });
                    }}
                  />

                  {/*隧道选择 */}
                  <Select
                    description={
                      isEdit
                        ? "更改隧道将释放原端口并在新隧道分配端口"
                        : "更改隧道后重新分配端口并同步到节点"
                  }
                    }
                    errorMessage={errors.tunnelId}
                    isInvalid={!!errors.tunnelId}
                    label="选择隧道"
                    placeholder="请选择关联的隧道"
                    selectedKeys={
                      form.tunnelId ? [form.tunnelId.toString()] : []
                    }
                    variant="bordered"
                    onSelectionChange={(keys) => {
                      const selectedKey = Array.from(keys)[0] as string;
                      if (selectedKey) {
                        handleTunnelChange(selectedKey);
                      }
                    }
                  />

                  {/*入口端口 */}
                  <Input
                    description="指定入口端口，留空则从节点可用端口中自动分配"
                    errorMessage={errors.inPort}
                    isInvalid={!!errors.inPort}
                    label="入口端口"
                    placeholder="留空则自动分配可用端口"
                    type="number"
                    value={form.inPort !== null ? form.inPort.toString() : ""}
                    variant="bordered"
                    onChange={(e) => {
                      const value = e.target.value;
                      setForm((prev) => ({ ...prev, inPort: value ? parseInt(value) : null }));
                    }
                  />
                </div>
          </ModalBody>
        </ModalContent>
      </Modal>
    </Modal>
  );
};

  // ===================== Summary of findings ===================

## Key File Paths and their purposes:

### Frontend: Forward Edit/create dialog component
- **Main file:** `/Users/sagit/.local/share/opencode/worktree/13650be91caa3a9e08a2e63127fef62757658384/mighty-nebula/vite-frontend/src/pages/forward.tsx`
  - Displays the forwarding list and handles all create/edit/delete/forwards
- Has `inIp` and `inPort` fields
- Shows entry port and dialog/form with validation
- Uses React-hook-form with state management
- Calls `createForward`, `updateForward`, `deleteForward`, `pauseForwardService`, `resumeForwardService`, APIs

- Imports API functions from `@/api`

### Backend: Forwarding data model and API handlers
- **Main model file:** `/Users/sagit/.local/share/opencode/worktree/13650be91caa3a9e08a2e63127fef62757658384/mighty-nebula/go-backend/internal/store/model/model.go`
  - GORM model definition for `Forward` struct does basic forwarding configuration
  - Related models: `ForwardPort`, `Tunnel`, `UserForwardDetail` (view struct)

  - **API handlers:** `/Users/sagit/.local/share/opencode/worktree/13650be91caa3a9e08a2e63127fef62757658384/mighty-nebula/go-backend/internal/http/handler/mutations.go`
  - Create/update/delete operations
  - Entry port handling via `inPort` field

- **Repository layer:** `/Users/sagit/.local/share/opencode/worktree/13650be91caa3a9e08a2e63127fef62757658384/mighty-nebula/go-backend/internal/store/repo/repository_mutations.go`
  - CRUD operations for  - `resolveForwardIngress` function in `/Users/sagit/.local/share/opencode/worktree/13650be91caa3a9e08a2e63127fef62757658384/mighty-nebula/go-backend/internal/store/repo/repository.go`
  - Computes `inIp` and `inPort` from joins

### Current data model structure for forwards

From `go-backend/internal/store/model/model.go`:

```go
// Forward maps to the "forward" table.
type Forward struct {
    ID          int64         `gorm:"primaryKey;autoIncrement"`
    UserID      int64         `gorm:"column:user_id;not null"`
    UserName    string        `gorm:"column:user_name;type:varchar(100);not null"`
    Name        string        `gorm:"type:varchar(100);not null"`
    TunnelID    int64         `gorm:"column:tunnel_id;not null"`
    RemoteAddr  string        `gorm:"column:remote_addr;type:text;not null"`
    Strategy    string        `gorm:"type:varchar(100);not null;default:'fifo'"`
    InFlow      int64         `gorm:"not null;default:0"`
    OutFlow     int64         `gorm:"column:out_flow;not null;default:0"`
    CreatedTime int64         `gorm:"column:created_time;not null"`
    UpdatedTime int64         `gorm:"column:updated_time;not null"`
    Status      int           `gorm:"not null"`
    Inx         int           `gorm:"not null;default:0"`
    SpeedID     sql.NullInt64 `gorm:"column:speed_id"`
}
```

```go
// ForwardPort maps to the "forward_port" table - stores port assignments per entry node
type ForwardPort struct {
    ID        int64 `gorm:"primaryKey;autoIncrement"`
    ForwardID int64 `gorm:"column:forward_id;not null"`
    NodeID    int64 `gorm:"column:node_id;not null"`
    Port      int   `gorm:"not null"`
}
```

```go
// Tunnel maps to the "tunnel" table - has entry IP configuration
type Tunnel struct {
    ID           int64          `gorm:"primaryKey;autoIncrement"`
    Name         string         `gorm:"type:varchar(100);not null"`
    TrafficRatio float64        `gorm:"column:traffic_ratio;not null;default:1.0"`
    Type         int            `gorm:"not null"`
    Protocol     string         `gorm:"type:varchar(10);not null;default:'tls'"`
    Flow         int64          `gorm:"not null"`
    CreatedTime  int64          `gorm:"column:created_time;not null"`
    UpdatedTime  int64          `gorm:"column:updated_time;not null"`
    Status       int            `gorm:"not null"`
    InIP         sql.NullString `gorm:"column:in_ip;type:text"`       // Optional entry IP hint
    Inx          int            `gorm:"not null;default:0"`
    IPPreference string         `gorm:"column:ip_preference;type:varchar(10);not null;default:''"`
}
```

### How entry listener is currently configured:

1. **Port Storage**: `ForwardPort` table stores the mapping from `forward_id` -> `node_id` -> `port`
   - Port is assigned by the handler or passed via `inPort` parameter ( or auto-picked)
   - Entry nodes are determined by `tunnelEntryNodeIDs()` in handler

3. **Entry IP Resolution**: The `resolveForwardIngress()` function resolves `inIp` and `inPort` by:
   - Joining `forward_port` with `node` table
   - `inIp` comes from `tunnel.in_ip` if set (takes precedence over node.server_ip)
   - For multi-entry-node forwards, comma-separated IPs and ports are returned

4. **Current IP-related fields**:
   - `Forward` model: No `inIp` field (port is stored in `forward_port` table)
   - `Tunnel` model: Has `InIP` field (nullable, - allows specifying of a preferred entry IP hint
   - `UserForwardDetail` view struct ( joins tunnel table and includes `InIP` and `InPort`
5. **No explicit entry IP selection UI in current dialog** - The port field is a text input, and form validates range but but UI doesn't allow setting specific IP addresses