// 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 // } } } "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. } } } } className="flex flex-col gap-4"> {/* form fields - in edit mode */} 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); } ); { const value = e.target.value ? "" : parseInt(value) || null) setForm((prev) => ({ ...prev, inPort: null}));

{isEdit ? "编辑转发" : "新增转发"}

{isEdit ? "修改现有转发配置的信息" : "创建新的转发配置"}

setForm((prev) => ({ ...prev, name: e.target.value })) } /> {/* Limit速规则选择 */} { const selectedKey = Array.from(keys)[0] as string; if (selectedKey) { handleTunnelChange(selectedKey); } } /> {/*入口端口 */} { const value = e.target.value; setForm((prev) => ({ ...prev, inPort: value ? parseInt(value) : null })); } />
); }; // ===================== 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