fix: preserve bind IP when editing forward without explicit inIp change

- Add replaceForwardPortsPreservingInIP to maintain existing InIP values
- Track inIpTouched state in frontend to distinguish user changes
- Only send inIp in update request when user explicitly changed it
- Add unit tests for forward port replacement with preserved InIP
This commit is contained in:
sagitchu
2026-03-03 20:22:58 +08:00
parent 75edeb9afa
commit 1450b25475
5 changed files with 129 additions and 29 deletions
+17 -6
View File
@@ -586,6 +586,7 @@ export default function ForwardPage() {
strategy: "fifo",
speedId: null,
});
const [inIpTouched, setInIpTouched] = useState(false);
// 表单验证错误
const [errors, setErrors] = useState<{ [key: string]: string }>({});
@@ -1276,6 +1277,7 @@ export default function ForwardPage() {
// 新增转发
const handleAdd = () => {
setIsEdit(false);
setInIpTouched(false);
setForm({
name: "",
tunnelId: null,
@@ -1293,6 +1295,7 @@ export default function ForwardPage() {
// 编辑转发
const handleEdit = (forward: Forward) => {
setIsEdit(true);
setInIpTouched(false);
setForm({
id: forward.id,
userId: forward.userId,
@@ -1357,11 +1360,17 @@ export default function ForwardPage() {
const nextTunnelId = parseInt(tunnelId);
const options = tunnelInIpOptionMap.get(nextTunnelId) || [];
setForm((prev) => ({
...prev,
tunnelId: nextTunnelId,
inIp: options.includes(prev.inIp) ? prev.inIp : "",
}));
setInIpTouched(false);
setForm((prev) => {
const tunnelChanged = prev.tunnelId !== nextTunnelId;
return {
...prev,
tunnelId: nextTunnelId,
inIp: tunnelChanged ? "" : options.includes(prev.inIp) ? prev.inIp : "",
};
});
};
// 提交表单
@@ -1388,7 +1397,7 @@ export default function ForwardPage() {
name: form.name,
tunnelId: form.tunnelId,
inPort: form.inPort,
inIp: form.inIp || undefined,
...(inIpTouched ? { inIp: form.inIp || "" } : {}),
remoteAddr: processedRemoteAddr,
strategy: addressCount > 1 ? form.strategy : "fifo",
speedId: normalizeSpeedId(form.speedId),
@@ -4288,6 +4297,8 @@ export default function ForwardPage() {
onSelectionChange={(keys) => {
const selectedKey = Array.from(keys)[0] as string;
setInIpTouched(true);
setForm((prev) => ({
...prev,
inIp: selectedKey === "__default__" ? "" : selectedKey,
@@ -115,7 +115,10 @@ const validateNyItem = (line: string, value: unknown): ParsedNyImportLine => {
return { line, error: "listen_port格式错误,应为1-65535之间的数字" };
}
if (normalizedListenPort !== null && !isValidListenPort(normalizedListenPort)) {
if (
normalizedListenPort !== null &&
!isValidListenPort(normalizedListenPort)
) {
return { line, error: "listen_port必须为1-65535之间的数字" };
}