diff --git a/internal/web/service/node_contract.go b/internal/web/service/node_contract.go index 5526c0a..e1ca7d6 100644 --- a/internal/web/service/node_contract.go +++ b/internal/web/service/node_contract.go @@ -118,8 +118,8 @@ type NodeMutationRequest struct { Name string `json:"name" form:"name" validate:"required"` Remark string `json:"remark" form:"remark"` Scheme string `json:"scheme" form:"scheme" validate:"omitempty,oneof=http https"` - Address string `json:"address" form:"address" validate:"required"` - Port int `json:"port" form:"port" validate:"gte=1,lte=65535"` + Address string `json:"address" form:"address"` + Port int `json:"port" form:"port" validate:"gte=0,lte=65535"` BasePath string `json:"basePath" form:"basePath"` ApiToken *string `json:"apiToken,omitempty" form:"apiToken"` ClearApiToken bool `json:"clearApiToken,omitempty" form:"clearApiToken"` @@ -140,6 +140,16 @@ func (r *NodeMutationRequest) validateCredentials(create bool) error { if r == nil { return common.NewError("node request is required") } + // Agent nodes dial out to the master; address/port are not needed. + // Traditional nodes still require them. + if r.NodeType != "agent" { + if strings.TrimSpace(r.Address) == "" { + return common.NewError("address is required") + } + if r.Port < 1 || r.Port > 65535 { + return common.NewError("port must be between 1 and 65535") + } + } if r.ApiToken != nil && r.ClearApiToken { return common.NewError("apiToken and clearApiToken are mutually exclusive") } diff --git a/internal/web/translation/en-US.json b/internal/web/translation/en-US.json index 160efb5..e8e3ab5 100644 --- a/internal/web/translation/en-US.json +++ b/internal/web/translation/en-US.json @@ -1181,9 +1181,9 @@ "pinFetched": "Fetched the node's current certificate", "pinFetchFailed": "Could not fetch the certificate", "nodeType": "Node Type", - "nodeTypeTraditional": "Traditional Panel", - "nodeTypeAgent": "Agent", - "agentHint": "Agent nodes don't need a panel address. An agent token will be generated on creation for configuring the agent on the node machine.", + "nodeTypeTraditional": "Traditional Panel Node", + "nodeTypeAgent": "Agent Lightweight Node", + "agentHint": "Agent nodes connect out to the master; no address, port or API token needed. An install command is generated on creation.", "agentTokenTitle": "Agent Node Setup", "agentTokenCreated": "Node {{name}} created successfully", "agentTokenHint": "Copy the command below and run it on the node machine. This token is shown only once!", diff --git a/internal/web/translation/zh-CN.json b/internal/web/translation/zh-CN.json index 6e08e80..2ec503d 100644 --- a/internal/web/translation/zh-CN.json +++ b/internal/web/translation/zh-CN.json @@ -1063,9 +1063,9 @@ "pinFetched": "已获取节点当前证书", "pinFetchFailed": "无法获取证书", "nodeType": "节点类型", - "nodeTypeTraditional": "传统面板", - "nodeTypeAgent": "Agent", - "agentHint": "Agent 节点无需面板地址,创建后会自动生成 agent token,用于在节点机上配置 agent。", + "nodeTypeTraditional": "传统面板节点", + "nodeTypeAgent": "Agent 轻量节点", + "agentHint": "Agent 节点由节点机主动连接主控,无需填写地址、端口和 API 令牌。创建后会生成安装命令。", "agentTokenTitle": "Agent 节点安装", "agentTokenCreated": "节点 {{name}} 创建成功", "agentTokenHint": "复制下面的命令,在节点机上运行即可完成安装。此 token 只显示一次!",