You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@apisix.apache.org by ju...@apache.org on 2021/04/19 02:44:57 UTC
[apisix-dashboard] branch master updated: fix: show correct health
checker (#1784)
This is an automated email from the ASF dual-hosted git repository.
juzhiyuan pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix-dashboard.git
The following commit(s) were added to refs/heads/master by this push:
new 5e46f60 fix: show correct health checker (#1784)
5e46f60 is described below
commit 5e46f6072113fc2d41807256a6786ed7c84d6674
Author: 琚致远 <ju...@apache.org>
AuthorDate: Mon Apr 19 10:44:52 2021 +0800
fix: show correct health checker (#1784)
Co-authored-by: litesun <7s...@gmail.com>
---
...an-skip-upstream-when-select-service-id.spec.js | 23 ++--
.../route/create-route-with-upstream.spec.js | 13 ++-
.../service/edit-service-with-upstream.spec.js | 7 +-
web/src/components/Upstream/UpstreamForm.tsx | 129 +++++++++++----------
.../components/Upstream/components/PassHost.tsx | 10 +-
.../Upstream/components/UpstreamSelector.tsx | 10 +-
.../Upstream/components/active-check/Host.tsx | 5 -
.../Upstream/components/active-check/HttpPath.tsx | 7 --
.../Upstream/components/active-check/Port.tsx | 4 +-
web/src/components/Upstream/locales/en-US.ts | 7 +-
web/src/components/Upstream/locales/zh-CN.ts | 7 +-
web/src/components/Upstream/service.ts | 91 ++++++++++++++-
web/src/components/Upstream/typings.d.ts | 3 +-
web/src/pages/Route/Create.tsx | 1 +
web/src/pages/Route/components/Step1/MetaView.tsx | 63 ++++++----
.../Route/components/Step2/RequestRewriteView.tsx | 8 +-
web/src/pages/Route/locales/en-US.ts | 2 +
web/src/pages/Route/locales/zh-CN.ts | 2 +
web/src/pages/Route/service.ts | 7 --
web/src/pages/Route/transform.ts | 17 ++-
web/src/pages/Route/typing.d.ts | 30 +----
web/src/pages/Service/Create.tsx | 14 ++-
web/src/pages/Service/components/Step1.tsx | 4 +-
web/src/pages/Service/service.ts | 7 --
web/src/pages/Upstream/Create.tsx | 17 +--
web/src/pages/Upstream/components/Step1.tsx | 13 +--
web/src/pages/Upstream/locales/zh-CN.ts | 2 +-
web/src/pages/Upstream/service.ts | 4 +-
web/src/pages/Upstream/transform.ts | 83 -------------
29 files changed, 297 insertions(+), 293 deletions(-)
diff --git a/web/cypress/integration/route/can-skip-upstream-when-select-service-id.spec.js b/web/cypress/integration/route/can-skip-upstream-when-select-service-id.spec.js
index d62cd87..aeb5118 100644
--- a/web/cypress/integration/route/can-skip-upstream-when-select-service-id.spec.js
+++ b/web/cypress/integration/route/can-skip-upstream-when-select-service-id.spec.js
@@ -61,7 +61,7 @@ context('Can select service_id skip upstream in route', () => {
cy.get(this.domSelector.name).type(this.data.routeName);
cy.contains('Next').click();
cy.get(this.domSelector.upstreamSelector).click();
- cy.contains('None').should('not.exist');
+ cy.get('.ant-select-item-option-disabled > .ant-select-item-option-content').contains('None');
cy.contains('Previous').click();
cy.wait(500);
@@ -91,9 +91,12 @@ context('Can select service_id skip upstream in route', () => {
cy.contains(this.data.routeName).siblings().contains('Configure').click();
cy.get(this.domSelector.serviceSelector).click();
cy.contains('None').click();
+ cy.get(this.domSelector.notification).should('contain', 'Please check the configuration of binding service');
+ cy.get(this.domSelector.notificationCloseIcon).click();
+
cy.contains('Next').click();
- cy.get(this.domSelector.upstream_id).click();
- cy.contains('None').should('not.exist');
+ cy.wait(500);
+ cy.get('[data-cy=upstream_selector]').click();
cy.contains(this.data.upstreamName).click();
cy.contains('Next').click();
cy.contains('Next').click();
@@ -101,13 +104,7 @@ context('Can select service_id skip upstream in route', () => {
cy.contains(this.data.submitSuccess);
});
- it('should delete upstream, service and route', function () {
- cy.visit('/');
- cy.contains('Service').click();
- cy.contains(this.data.serviceName).siblings().contains('Delete').click();
- cy.contains('button', 'Confirm').click();
- cy.get(this.domSelector.notification).should('contain', this.data.deleteServiceSuccess);
-
+ it('should delete route, service and upstream', function () {
cy.visit('/');
cy.contains('Route').click();
cy.contains(this.data.routeName).siblings().contains('More').click();
@@ -115,7 +112,13 @@ context('Can select service_id skip upstream in route', () => {
cy.get(this.domSelector.deleteAlert).should('be.visible').within(() => {
cy.contains('OK').click();
});
+
cy.get(this.domSelector.notification).should('contain', this.data.deleteRouteSuccess);
+ cy.visit('/');
+ cy.contains('Service').click();
+ cy.contains(this.data.serviceName).siblings().contains('Delete').click();
+ cy.contains('button', 'Confirm').click();
+ cy.get(this.domSelector.notification).should('contain', this.data.deleteServiceSuccess);
cy.visit('/');
cy.contains('Upstream').click();
diff --git a/web/cypress/integration/route/create-route-with-upstream.spec.js b/web/cypress/integration/route/create-route-with-upstream.spec.js
index 2a976b8..8a64a05 100644
--- a/web/cypress/integration/route/create-route-with-upstream.spec.js
+++ b/web/cypress/integration/route/create-route-with-upstream.spec.js
@@ -54,8 +54,7 @@ context('Create Route with Upstream', () => {
cy.get(this.domSelector.input).should('be.disabled');
// should enable Upstream input boxes after selecting Custom mode
cy.get(this.domSelector.upstreamSelector).click();
- cy.contains('Custom').click();
- cy.get(this.domSelector.input).should('not.be.disabled');
+ cy.contains('.ant-select-item-option-content', 'Custom').click();
cy.get(this.domSelector.nodes_0_host).clear().type(this.data.ip1);
cy.get(this.domSelector.nodes_0_port).type(this.data.port);
@@ -77,7 +76,9 @@ context('Create Route with Upstream', () => {
cy.contains(this.data.routeName).siblings().contains('Configure').click();
cy.get(this.domSelector.name).should('value', this.data.routeName);
- cy.contains('Next').click({ force: true });
+ cy.contains('Next').click({
+ force: true
+ });
// check if the changes have been saved
cy.get(this.domSelector.nodes_0_host).should('value', this.data.ip1);
@@ -87,7 +88,7 @@ context('Create Route with Upstream', () => {
cy.get(this.domSelector.input).should('be.disabled');
cy.contains(this.data.upstreamName).click();
- cy.contains('Custom').click();
+ cy.contains('.ant-select-item-option-content', 'Custom').click();
cy.get(this.domSelector.input).should('not.be.disabled');
cy.get(this.domSelector.nodes_0_host).clear().type(this.data.ip2);
@@ -107,7 +108,9 @@ context('Create Route with Upstream', () => {
cy.contains(this.data.routeName).siblings().contains('Configure').click();
// ensure it has already changed to edit page
cy.get(this.domSelector.name).should('value', this.data.routeName);
- cy.contains('Next').click({ force: true });
+ cy.contains('Next').click({
+ force: true
+ });
cy.get(this.domSelector.nodes_0_host).should('value', this.data.ip2);
});
diff --git a/web/cypress/integration/service/edit-service-with-upstream.spec.js b/web/cypress/integration/service/edit-service-with-upstream.spec.js
index 66e9c2f..150a9e4 100644
--- a/web/cypress/integration/service/edit-service-with-upstream.spec.js
+++ b/web/cypress/integration/service/edit-service-with-upstream.spec.js
@@ -63,11 +63,14 @@ context('Edit Service with Upstream', () => {
cy.contains('Search').click();
cy.contains(this.data.serviceName).siblings().contains('Configure').click();
- cy.get(this.domSelector.nodes_0_host).click({ force: true }).should('value', this.data.ip1);
+ cy.wait(500);
+ cy.get(this.domSelector.nodes_0_host).click({
+ force: true
+ }).should('value', this.data.ip1);
cy.get(this.domSelector.input).should('be.disabled');
cy.get(this.domSelector.upstreamSelector).click();
- cy.contains('Custom').click();
+ cy.contains('.ant-select-item-option-content', 'Custom').click();
cy.get(this.domSelector.nodes_0_host).should('not.be.disabled').clear().type(this.data.ip2);
cy.get(this.domSelector.nodes_0_port).type(this.data.port);
cy.get(this.domSelector.nodes_0_weight).type(this.data.weight);
diff --git a/web/src/components/Upstream/UpstreamForm.tsx b/web/src/components/Upstream/UpstreamForm.tsx
index 2cf0151..c8a606b 100644
--- a/web/src/components/Upstream/UpstreamForm.tsx
+++ b/web/src/components/Upstream/UpstreamForm.tsx
@@ -14,13 +14,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import { Divider, Form, Switch } from 'antd';
+import { Divider, Form, notification, Switch } from 'antd';
import React, { useState, forwardRef, useImperativeHandle, useEffect } from 'react';
import { useIntl } from 'umi';
import type { FormInstance } from 'antd/es/form';
import PanelSection from '@/components/PanelSection';
-import { transformRequest } from '@/pages/Upstream/transform';
import PassiveCheck from './components/passive-check';
import ActiveCheck from './components/active-check'
import Nodes from './components/Nodes'
@@ -31,7 +30,7 @@ import UpstreamSelector from './components/UpstreamSelector';
import Retries from './components/Retries';
import PassHost from './components/PassHost';
import TLSComponent from './components/TLS';
-import { transformUpstreamDataFromRequest } from './service';
+import { convertToRequestData } from './service';
type Upstream = {
name?: string;
@@ -46,14 +45,18 @@ type Props = {
// FIXME: use proper typing
ref?: any;
required?: boolean;
+ neverReadonly?: boolean
};
+/**
+ * UpstreamForm is used to reuse Upstream Form UI,
+ * before using this component, we need to execute the following command:
+ * form.setFieldsValue(convertToFormData(VALUE_FROM_API))
+*/
const UpstreamForm: React.FC<Props> = forwardRef(
- ({ form, disabled, list = [], showSelector, required = true }, ref) => {
+ ({ form, disabled = false, list = [], showSelector = false, required = true, neverReadonly = false }, ref) => {
const { formatMessage } = useIntl();
- const [readonly, setReadonly] = useState(
- Boolean(form.getFieldValue('upstream_id')) || disabled,
- );
+ const [readonly, setReadonly] = useState(false);
const [hiddenForm, setHiddenForm] = useState(false);
const timeoutFields = [
@@ -75,39 +78,58 @@ const UpstreamForm: React.FC<Props> = forwardRef(
];
useImperativeHandle(ref, () => ({
- getData: () => transformRequest(form.getFieldsValue()),
+ getData: () => convertToRequestData(form.getFieldsValue()),
}));
- useEffect(() => {
- const formData = transformRequest(form.getFieldsValue()) || {};
- const { upstream_id } = form.getFieldsValue();
+ const resetForm = (upstream_id: string) => {
+ if (upstream_id === undefined) {
+ return
+ }
+ if (!neverReadonly) {
+ setReadonly(!["Custom", "None"].includes(upstream_id) || disabled);
+ }
+
+ /**
+ * upstream_id === None <==> required === false
+ * No need to bind Upstream object.
+ * When creating Route and binds with a Service, no need to configure Upstream in Route.
+ */
if (upstream_id === 'None') {
setHiddenForm(true);
- if (required) {
- requestAnimationFrame(() => {
- form.resetFields();
- setHiddenForm(false);
- });
- }
- } else {
- if (upstream_id) {
- requestAnimationFrame(() => {
- const targetData = list.find((item) => item.id === upstream_id) as UpstreamComponent.ResponseData
- if (targetData) {
- form.setFieldsValue(transformUpstreamDataFromRequest(targetData));
- }
- });
- }
- if (!required && !Object.keys(formData).length) {
- requestAnimationFrame(() => {
- form.setFieldsValue({ upstream_id: 'None' });
- setHiddenForm(true);
- });
- }
+ form.resetFields()
+ form.setFieldsValue({ upstream_id: 'None' })
+ return
+ }
+
+ setHiddenForm(false)
+
+ // NOTE: Use Ant Design's form object to set data automatically
+ if (upstream_id === "Custom") {
+ return
+ }
+
+ // NOTE: Set data from Upstream List (Upstream Selector)
+ if (list.length === 0) {
+ return
+ }
+ form.resetFields()
+ const targetData = list.find((item) => item.id === upstream_id) as UpstreamComponent.ResponseData
+ if (targetData) {
+ form.setFieldsValue(targetData);
}
- setReadonly(Boolean(upstream_id) || disabled);
- }, [list]);
+ }
+
+ /**
+ * upstream_id
+ * - None: No need to bind Upstream to a resource (e.g Service).
+ * - Custom: Users could input values on UpstreamForm
+ * - Upstream ID from API
+ */
+ useEffect(() => {
+ const upstream_id = form.getFieldValue('upstream_id');
+ resetForm(upstream_id)
+ }, [form.getFieldValue('upstream_id'), list]);
const ActiveHealthCheck = () => (
<React.Fragment>
@@ -192,19 +214,26 @@ const UpstreamForm: React.FC<Props> = forwardRef(
}
</Form.Item>
<Divider orientation="left" plain />
- <Form.Item label={formatMessage({ id: 'page.upstream.step.healthyCheck.passive' })} name={['custom', 'checks', 'passive']} valuePropName="checked">
+ <Form.Item label={formatMessage({ id: 'page.upstream.step.healthyCheck.passive' })} name={['custom', 'checks', 'passive']} valuePropName="checked" tooltip={formatMessage({ id: 'component.upstream.other.health-check.passive-only' })}>
<Switch disabled={readonly} />
</Form.Item>
- <Form.Item shouldUpdate noStyle>
+ <Form.Item shouldUpdate={(prev, next) => prev.custom?.checks?.passive !== next.custom?.checks?.passive} noStyle>
{
() => {
const passive = form.getFieldValue(['custom', 'checks', 'passive'])
+ const active = form.getFieldValue(['custom', 'checks', 'active'])
if (passive) {
/*
* When enable passive check, we should enable active check, too.
* When we use form.setFieldsValue to enable active check, error throws.
* We choose to alert users first, and need users to enable active check manually.
*/
+ if (!active) {
+ notification.warn({
+ message: formatMessage({ id: 'component.upstream.other.health-check.invalid' }),
+ description: formatMessage({ id: 'component.upstream.other.health-check.passive-only' })
+ })
+ }
return <PassiveHealthCheck />
}
return null
@@ -225,32 +254,8 @@ const UpstreamForm: React.FC<Props> = forwardRef(
list={list}
disabled={disabled}
required={required}
- shouldUpdate={(prev, next) => {
- setReadonly(Boolean(next.upstream_id));
- if (prev.upstream_id !== next.upstream_id) {
- const id = next.upstream_id;
- if (id) {
- const targetData = list.find((item) => item.id === id) as UpstreamComponent.ResponseData
- if (targetData) {
- form.setFieldsValue(transformUpstreamDataFromRequest(targetData));
- }
- form.setFieldsValue({
- upstream_id: id,
- });
- }
- }
- return prev.upstream_id !== next.upstream_id;
- }}
- onChange={(upstream_id) => {
- setReadonly(Boolean(upstream_id));
- setHiddenForm(Boolean(upstream_id === 'None'));
- const targetData = list.find((item) => item.id === upstream_id) as UpstreamComponent.ResponseData
- if (targetData) {
- form.setFieldsValue(transformUpstreamDataFromRequest(targetData));
- }
- if (upstream_id === '') {
- form.resetFields();
- }
+ onChange={(nextUpstreamId) => {
+ resetForm(nextUpstreamId);
}}
/>
)}
diff --git a/web/src/components/Upstream/components/PassHost.tsx b/web/src/components/Upstream/components/PassHost.tsx
index a2f7e45..72b5821 100644
--- a/web/src/components/Upstream/components/PassHost.tsx
+++ b/web/src/components/Upstream/components/PassHost.tsx
@@ -15,7 +15,7 @@
* limitations under the License.
*/
import React from 'react'
-import { Form, Input, Select } from 'antd'
+import { Form, Input, notification, Select } from 'antd'
import { useIntl } from 'umi'
import type { FormInstance } from 'antd/lib/form'
@@ -79,6 +79,14 @@ const Component: React.FC<Props> = ({ form, readonly }) => {
</Form.Item>
);
}
+
+ if (form.getFieldValue('pass_host') === 'node' && (form.getFieldValue('nodes') || []).length !== 1) {
+ notification.warning({
+ message: formatMessage({id: 'component.upstream.other.pass_host-with-multiple-nodes.title'}),
+ description: formatMessage({id: 'component.upstream.other.pass_host-with-multiple-nodes'})
+ })
+ form.setFieldsValue({pass_host: 'pass'})
+ }
return null;
}}
</Form.Item>
diff --git a/web/src/components/Upstream/components/UpstreamSelector.tsx b/web/src/components/Upstream/components/UpstreamSelector.tsx
index 1f8b225..34f7fec 100644
--- a/web/src/components/Upstream/components/UpstreamSelector.tsx
+++ b/web/src/components/Upstream/components/UpstreamSelector.tsx
@@ -27,18 +27,16 @@ type Props = {
list?: Upstream[];
disabled?: boolean;
required?: boolean;
- shouldUpdate: (prev: any, next: any) => void;
onChange: (id: string) => void
}
-const Component: React.FC<Props> = ({ shouldUpdate, onChange, list = [], disabled, required }) => {
+const UpstreamSelector: React.FC<Props> = ({ onChange, list = [], disabled, required }) => {
const { formatMessage } = useIntl()
return (
<Form.Item
label={formatMessage({ id: 'page.upstream.step.select.upstream' })}
name="upstream_id"
- shouldUpdate={shouldUpdate as any}
>
<Select
showSearch
@@ -49,11 +47,11 @@ const Component: React.FC<Props> = ({ shouldUpdate, onChange, list = [], disable
item?.children.toLowerCase().includes(input.toLowerCase())
}
>
- {Boolean(!required) && <Select.Option value={'None'}>None</Select.Option>}
+ <Select.Option value="None" disabled={required}>{formatMessage({id: 'component.upstream.other.none'})}</Select.Option>
{[
{
name: formatMessage({ id: 'page.upstream.step.select.upstream.select.option' }),
- id: '',
+ id: 'Custom',
},
...list,
].map((item) => (
@@ -66,4 +64,4 @@ const Component: React.FC<Props> = ({ shouldUpdate, onChange, list = [], disable
)
}
-export default Component
+export default UpstreamSelector
diff --git a/web/src/components/Upstream/components/active-check/Host.tsx b/web/src/components/Upstream/components/active-check/Host.tsx
index f21a76b..148d60a 100644
--- a/web/src/components/Upstream/components/active-check/Host.tsx
+++ b/web/src/components/Upstream/components/active-check/Host.tsx
@@ -27,7 +27,6 @@ const Component: React.FC<Props> = ({ readonly }) => {
return (
<Form.Item
label={formatMessage({ id: 'component.upstream.fields.checks.active.host' })}
- required
tooltip={formatMessage({ id: 'component.upstream.fields.checks.active.host.tooltip' })}
style={{ marginBottom: 0 }}
>
@@ -35,10 +34,6 @@ const Component: React.FC<Props> = ({ readonly }) => {
name={['checks', 'active', 'host']}
rules={[
{
- required: true,
- message: formatMessage({ id: 'component.upstream.fields.checks.active.host.required' }),
- },
- {
pattern: new RegExp(
/^\\*?[0-9a-zA-Z-._]+$/,
'g',
diff --git a/web/src/components/Upstream/components/active-check/HttpPath.tsx b/web/src/components/Upstream/components/active-check/HttpPath.tsx
index db5c2b6..b99457a 100644
--- a/web/src/components/Upstream/components/active-check/HttpPath.tsx
+++ b/web/src/components/Upstream/components/active-check/HttpPath.tsx
@@ -28,7 +28,6 @@ const Component: React.FC<Props> = ({ readonly }) => {
return (
<Form.Item
label={formatMessage({ id: 'component.upstream.fields.checks.active.http_path' })}
- required
tooltip={formatMessage({
id: 'component.upstream.fields.checks.active.http_path.tooltip',
})}
@@ -36,12 +35,6 @@ const Component: React.FC<Props> = ({ readonly }) => {
<Form.Item
name={['checks', 'active', 'http_path']}
noStyle
- rules={[
- {
- required: true,
- message: formatMessage({ id: 'component.upstream.fields.checks.active.http_path.placeholder' }),
- },
- ]}
initialValue="/"
>
<Input
diff --git a/web/src/components/Upstream/components/active-check/Port.tsx b/web/src/components/Upstream/components/active-check/Port.tsx
index 8ca857b..030a6c7 100644
--- a/web/src/components/Upstream/components/active-check/Port.tsx
+++ b/web/src/components/Upstream/components/active-check/Port.tsx
@@ -25,8 +25,8 @@ type Props = {
const Component: React.FC<Props> = ({ readonly }) => {
const { formatMessage } = useIntl()
return (
- <Form.Item label={formatMessage({ id: 'component.upstream.fields.checks.active.port' })} required>
- <Form.Item name={['checks', 'active', 'port']} noStyle initialValue={80} rules={[{ required: true, message: formatMessage({ id: "component.upstream.fields.checks.active.port.required" }) }]}>
+ <Form.Item label={formatMessage({ id: 'component.upstream.fields.checks.active.port' })}>
+ <Form.Item name={['checks', 'active', 'port']} noStyle initialValue={80}>
<InputNumber
placeholder={formatMessage({
id: 'component.upstream.fields.checks.active.port',
diff --git a/web/src/components/Upstream/locales/en-US.ts b/web/src/components/Upstream/locales/en-US.ts
index 5387087..9f94442 100644
--- a/web/src/components/Upstream/locales/en-US.ts
+++ b/web/src/components/Upstream/locales/en-US.ts
@@ -52,7 +52,6 @@ export default {
'component.upstream.fields.checks.active.host.scope': 'Only letters, numbers and . are supported',
'component.upstream.fields.checks.active.port': 'Port',
- 'component.upstream.fields.checks.active.port.required': 'Please enter the port',
'component.upstream.fields.checks.active.http_path': 'HTTP Path',
'component.upstream.fields.checks.active.http_path.tooltip': 'The path that should be used when issuing the HTTP GET request to the target. The default value is /.',
@@ -90,4 +89,10 @@ export default {
'component.upstream.fields.checks.passive.unhealthy.timeouts': 'Timeouts',
'component.upstream.fields.checks.passive.unhealthy.timeouts.tooltip': 'Number of timeouts in proxied traffic to consider a target unhealthy, as observed by passive health checks.',
+
+ 'component.upstream.other.none': 'None',
+ 'component.upstream.other.pass_host-with-multiple-nodes.title': 'Please check the target node configuration',
+ 'component.upstream.other.pass_host-with-multiple-nodes': 'When using a host name or IP in the target node list, make sure there is only one target node',
+ 'component.upstream.other.health-check.passive-only': 'When passive health check is enabled, active health check needs to be enabled at the same time.',
+ 'component.upstream.other.health-check.invalid': 'Please check the health check configuration',
}
diff --git a/web/src/components/Upstream/locales/zh-CN.ts b/web/src/components/Upstream/locales/zh-CN.ts
index 5f93dcd..809a60a 100644
--- a/web/src/components/Upstream/locales/zh-CN.ts
+++ b/web/src/components/Upstream/locales/zh-CN.ts
@@ -52,7 +52,6 @@ export default {
'component.upstream.fields.checks.active.host.scope': '仅支持字母、数字和 . ',
'component.upstream.fields.checks.active.port': '端口',
- 'component.upstream.fields.checks.active.port.required': '请输入端口',
'component.upstream.fields.checks.active.http_path': '请求路径',
'component.upstream.fields.checks.active.http_path.tooltip': '向目标节点发出 HTTP GET 请求时应使用的路径。',
@@ -95,4 +94,10 @@ export default {
'component.upstream.fields.checks.passive.unhealthy.timeouts': '超时时间',
'component.upstream.fields.checks.passive.unhealthy.timeouts.tooltip': '根据被动健康检查的观察,在代理中认为目标不健康的超时次数。',
+
+ 'component.upstream.other.none': '不选择(仅在绑定服务时可用)',
+ 'component.upstream.other.pass_host-with-multiple-nodes.title': '请检查目标节点配置',
+ 'component.upstream.other.pass_host-with-multiple-nodes': '当使用目标节点列表中的主机名或者 IP 时,请确认只有一个目标节点',
+ 'component.upstream.other.health-check.passive-only': '启用被动健康检查时,需要同时启用主动健康检查。',
+ 'component.upstream.other.health-check.invalid': '请检查健康检查配置',
}
diff --git a/web/src/components/Upstream/service.ts b/web/src/components/Upstream/service.ts
index 2642100..098ed22 100644
--- a/web/src/components/Upstream/service.ts
+++ b/web/src/components/Upstream/service.ts
@@ -14,15 +14,23 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import cloneDeep from 'lodash/cloneDeep'
+import { notification } from 'antd';
+import { isNil, omitBy, omit, pick, cloneDeep } from 'lodash';
+import { formatMessage, request } from 'umi';
/**
* Because we have some `custom` field in Upstream Form, like custom.tls/custom.checks.active etc,
* we need to transform data that doesn't have `custom` field to data contains `custom` field
*/
-export const transformUpstreamDataFromRequest = (originData: UpstreamComponent.ResponseData) => {
+export const convertToFormData = (originData: UpstreamComponent.ResponseData) => {
+ if (originData === undefined) {
+ // NOTE: When binding Service without Upstream configuration (None), originData === undefined
+ return undefined
+ }
+
const data = cloneDeep(originData)
data.custom = {}
+ data.upstream_id = "Custom"
if (data.checks) {
data.custom.checks = {}
@@ -40,5 +48,84 @@ export const transformUpstreamDataFromRequest = (originData: UpstreamComponent.R
data.custom.tls = "enable"
}
+ if (data.id) {
+ data.upstream_id = data.id;
+ }
+
return data
}
+
+/**
+ * Transform Upstream Form data from custom data to API needed data
+*/
+export const convertToRequestData = (
+ formData: UpstreamModule.RequestBody,
+): UpstreamModule.RequestBody | undefined | { upstream_id: string } => {
+ let data = omitBy(formData, isNil) as UpstreamModule.RequestBody;
+ data = omit(data, 'custom');
+
+ const {
+ type,
+ hash_on,
+ key,
+ k8s_deployment_info,
+ nodes,
+ pass_host,
+ upstream_host,
+ upstream_id = "Custom",
+ checks
+ } = data;
+
+ if (!["Custom", "None"].includes(upstream_id)) {
+ return { upstream_id };
+ }
+
+ data = omit(data, "upstream_id") as any
+
+ if (nodes && k8s_deployment_info) {
+ return undefined;
+ }
+
+ if (!nodes && !k8s_deployment_info) {
+ return undefined;
+ }
+
+ if (type === 'chash') {
+ if (!hash_on) {
+ return undefined;
+ }
+
+ if (hash_on !== 'consumer' && !key) {
+ return undefined;
+ }
+ }
+
+ if (pass_host === 'rewrite' && !upstream_host) {
+ return undefined;
+ }
+
+ if (checks?.passive && !checks.active) {
+ notification.error({
+ message: formatMessage({id: 'component.upstream.other.health-check.invalid'}),
+ description: formatMessage({id: 'component.upstream.other.health-check.passive-only'})
+ })
+ return undefined
+ }
+
+ if (nodes) {
+ // NOTE: https://github.com/ant-design/ant-design/issues/27396
+ data.nodes = data.nodes?.map((item) => {
+ return pick(item, ['host', 'port', 'weight']);
+ });
+ return data;
+ }
+
+ return undefined;
+};
+
+export const fetchUpstreamList = () => {
+ return request<Res<ResListData<UpstreamComponent.ResponseData>>>('/upstreams').then(({ data }) => ({
+ data: data.rows.map(row => convertToFormData(row)),
+ total: data.total_size,
+ }));
+};
diff --git a/web/src/components/Upstream/typings.d.ts b/web/src/components/Upstream/typings.d.ts
index bbe22d3..e7b2382 100644
--- a/web/src/components/Upstream/typings.d.ts
+++ b/web/src/components/Upstream/typings.d.ts
@@ -42,7 +42,7 @@ declare namespace UpstreamComponent {
}
type ResponseData = {
- nodes: Node[];
+ nodes?: Node[];
retries?: number;
timeout?: Timeout;
tls?: TLS;
@@ -61,6 +61,7 @@ declare namespace UpstreamComponent {
desc?: string;
service_name?: string;
id?: string;
+ upstream_id?: string;
// NOTE: custom field
custom?: Record<string, any>;
}
diff --git a/web/src/pages/Route/Create.tsx b/web/src/pages/Route/Create.tsx
index 8c478d1..8da9546 100644
--- a/web/src/pages/Route/Create.tsx
+++ b/web/src/pages/Route/Create.tsx
@@ -102,6 +102,7 @@ const Page: React.FC<Props> = (props) => {
return (
<Step1
form={form1}
+ upstreamForm={form2}
advancedMatchingRules={advancedMatchingRules}
onChange={({ action, data }) => {
if (action === 'redirectOptionChange') {
diff --git a/web/src/pages/Route/components/Step1/MetaView.tsx b/web/src/pages/Route/components/Step1/MetaView.tsx
index b377f05..4cbd64a 100644
--- a/web/src/pages/Route/components/Step1/MetaView.tsx
+++ b/web/src/pages/Route/components/Step1/MetaView.tsx
@@ -16,7 +16,7 @@
*/
import React, { useEffect, useState } from 'react';
import Form from 'antd/es/form';
-import { Input, Switch, Select, Button, Tag, AutoComplete, Row, Col } from 'antd';
+import { Input, Switch, Select, Button, Tag, AutoComplete, Row, Col, notification } from 'antd';
import { useIntl } from 'umi';
import PanelSection from '@/components/PanelSection';
@@ -24,7 +24,7 @@ import { FORM_ITEM_WITHOUT_LABEL } from '@/pages/Route/constants';
import LabelsDrawer from '@/components/LabelsfDrawer';
import { fetchLabelList, fetchServiceList } from '../../service';
-const MetaView: React.FC<RouteModule.Step1PassProps> = ({ disabled, form, isEdit, onChange = () => { } }) => {
+const MetaView: React.FC<RouteModule.Step1PassProps> = ({ disabled, form, isEdit, upstreamForm, onChange = () => { } }) => {
const { formatMessage } = useIntl();
const [visible, setVisible] = useState(false);
const [labelList, setLabelList] = useState<LabelList>({});
@@ -285,27 +285,44 @@ const MetaView: React.FC<RouteModule.Step1PassProps> = ({ disabled, form, isEdit
)
const ServiceSelector: React.FC = () => (
- <Form.Item label={formatMessage({ id: 'page.route.service' })} tooltip={formatMessage({id: 'page.route.fields.service_id.tooltip'})}>
- <Row>
- <Col span={5}>
- <Form.Item noStyle name="service_id">
- <Select disabled={disabled}>
- {/* TODO: value === '' means no service_id select, need to find a better way */}
- <Select.Option value="" key={Math.random().toString(36).substring(7)}>
- {formatMessage({ id: "page.route.service.none" })}
- </Select.Option>
- {serviceList.map((item) => {
- return (
- <Select.Option value={item.id} key={item.id}>
- {item.name}
- </Select.Option>
- );
- })}
- </Select>
- </Form.Item>
- </Col>
- </Row>
- </Form.Item>
+ <React.Fragment>
+ <Form.Item label={formatMessage({ id: 'page.route.service' })} tooltip={formatMessage({ id: 'page.route.fields.service_id.tooltip' })}>
+ <Row>
+ <Col span={5}>
+ <Form.Item noStyle name="service_id">
+ <Select disabled={disabled}>
+ {/* TODO: value === '' means no service_id select, need to find a better way */}
+ <Select.Option value="" key={Math.random().toString(36).substring(7)}>
+ {formatMessage({ id: "page.route.service.none" })}
+ </Select.Option>
+ {serviceList.map((item) => {
+ return (
+ <Select.Option value={item.id} key={item.id}>
+ {item.name}
+ </Select.Option>
+ );
+ })}
+ </Select>
+ </Form.Item>
+ </Col>
+ </Row>
+ </Form.Item>
+ <Form.Item noStyle shouldUpdate={(prev, next) => {
+ if (next.service_id === '') {
+ const upstream_id = upstreamForm?.getFieldValue('upstream_id')
+ if (upstream_id === 'None') {
+ notification.warning({
+ message: formatMessage({ id: 'page.route.fields.service_id.invalid' }),
+ description: formatMessage({ id: 'page.route.fields.service_id.without-upstream' })
+ })
+ form.setFieldsValue({ service_id: prev.service_id })
+ }
+ }
+ return prev.service_id !== next.service_id
+ }}>
+ {() => null}
+ </Form.Item>
+ </React.Fragment>
)
return (
diff --git a/web/src/pages/Route/components/Step2/RequestRewriteView.tsx b/web/src/pages/Route/components/Step2/RequestRewriteView.tsx
index 2e6a458..f3aa1ad 100644
--- a/web/src/pages/Route/components/Step2/RequestRewriteView.tsx
+++ b/web/src/pages/Route/components/Step2/RequestRewriteView.tsx
@@ -15,9 +15,9 @@
* limitations under the License.
*/
import React, { useEffect, useState } from 'react';
-import UpstreamForm from '@/components/Upstream';
-import { fetchUpstreamList } from '../../service';
+import UpstreamForm from '@/components/Upstream';
+import { fetchUpstreamList } from '@/components/Upstream/service';
const RequestRewriteView: React.FC<RouteModule.Step2PassProps> = ({
form,
@@ -25,9 +25,9 @@ const RequestRewriteView: React.FC<RouteModule.Step2PassProps> = ({
disabled,
hasServiceId = false,
}) => {
- const [list, setList] = useState<UpstreamModule.RequestBody[]>([]);
+ const [list, setList] = useState<UpstreamComponent.ResponseData[]>([]);
useEffect(() => {
- fetchUpstreamList().then(({ data }) => setList(data));
+ fetchUpstreamList().then(({ data }) => setList(data as UpstreamComponent.ResponseData[]));
}, []);
return (
<UpstreamForm
diff --git a/web/src/pages/Route/locales/en-US.ts b/web/src/pages/Route/locales/en-US.ts
index 06de908..2609800 100644
--- a/web/src/pages/Route/locales/en-US.ts
+++ b/web/src/pages/Route/locales/en-US.ts
@@ -171,6 +171,8 @@ export default {
'page.route.advanced-match.operator.sample.IN': 'Please enter an array, e.g ["1", "2"]',
'page.route.advanced-match.operator.sample.~~': 'Please enter a regular expression, e.g [a-z]+',
+ 'page.route.fields.service_id.invalid': 'Please check the configuration of binding service',
+ 'page.route.fields.service_id.without-upstream': 'If you do not bind the service, you must set the Upstream (Step 2)',
'page.route.advanced-match.tooltip': 'It supports route matching through request headers, request parameters and cookies, and can be applied to scenarios such as grayscale publishing and blue-green testing.',
'page.route.fields.custom.redirectOption.tooltip': 'This is related to redirect plugin',
diff --git a/web/src/pages/Route/locales/zh-CN.ts b/web/src/pages/Route/locales/zh-CN.ts
index 8228f60..3c134bc 100644
--- a/web/src/pages/Route/locales/zh-CN.ts
+++ b/web/src/pages/Route/locales/zh-CN.ts
@@ -170,6 +170,8 @@ export default {
'page.route.advanced-match.operator.sample.IN': '请输入数组,示例:["1", "2"]',
'page.route.advanced-match.operator.sample.~~': '请输入正则表达式,示例:[a-z]+',
+ 'page.route.fields.service_id.invalid': '请检查路由绑定的服务',
+ 'page.route.fields.service_id.without-upstream': '如果不绑定服务,则必须设置上游服务(步骤 2)',
'page.route.advanced-match.tooltip': '支持通过请求头,请求参数、Cookie 进行路由匹配,可应用于灰度发布,蓝绿测试等场景。',
'page.route.fields.custom.redirectOption.tooltip': '在此配置 redirect 插件',
diff --git a/web/src/pages/Route/service.ts b/web/src/pages/Route/service.ts
index f2b291f..99144cd 100644
--- a/web/src/pages/Route/service.ts
+++ b/web/src/pages/Route/service.ts
@@ -72,13 +72,6 @@ export const checkUniqueName = (name = '', exclude = '') =>
),
});
-export const fetchUpstreamList = () => {
- return request<Res<ResListData<UpstreamModule.RequestBody>>>('/upstreams').then(({ data }) => ({
- data: data.rows,
- total: data.total_size,
- }));
-};
-
export const fetchUpstreamItem = (sid: string) => {
return request(`/upstreams/${sid}`).then(({ nodes, timeout, id }) => {
return {
diff --git a/web/src/pages/Route/transform.ts b/web/src/pages/Route/transform.ts
index b7f62b0..4b03caa 100644
--- a/web/src/pages/Route/transform.ts
+++ b/web/src/pages/Route/transform.ts
@@ -22,6 +22,7 @@ import {
URI_REWRITE_TYPE,
HOST_REWRITE_TYPE
} from '@/pages/Route/constants';
+import { convertToFormData } from '@/components/Upstream/service';
export const transformProxyRewrite2Plugin = (data: RouteModule.ProxyRewrite): RouteModule.ProxyRewrite => {
let omitFieldsList: string[] = ['kvHeaders'];
@@ -178,11 +179,16 @@ export const transformStepData = ({
unset(data.plugins, ['proxy-rewrite']);
}
- if (Object.keys(redirect).length === 0 || redirect.http_to_https) {
+ if ((Object.keys(redirect).length === 0 || redirect.http_to_https) && form2Data) {
+ /**
+ * Due to convertToRequestData under the Upstream component,
+ * if upstream_id === Custom or None, it will be omitted.
+ * So upstream_id here mush be a valid Upstream ID from API.
+ */
if (form2Data.upstream_id) {
- data.upstream_id = form2Data.upstream_id;
+ data.upstream_id = form2Data.upstream_id
} else {
- data.upstream = form2Data;
+ data.upstream = form2Data
}
if (redirect.http_to_https) {
@@ -207,7 +213,6 @@ export const transformStepData = ({
'hostRewriteType',
'proxyRewrite',
service_id.length === 0 ? 'service_id' : '',
- form2Data.upstream_id === 'None' ? 'upstream_id' : '',
!Object.keys(data.plugins || {}).length ? 'plugins' : '',
!Object.keys(data.script || {}).length ? 'script' : '',
form1Data.hosts.filter(Boolean).length === 0 ? 'hosts' : '',
@@ -328,10 +333,10 @@ export const transformRouteData = (data: RouteModule.Body) => {
const advancedMatchingRules: RouteModule.MatchingRule[] = transformVarsToRules(vars);
if (upstream && Object.keys(upstream).length) {
- upstream.upstream_id = '';
+ upstream.upstream_id = 'Custom';
}
- const form2Data: RouteModule.Form2Data = upstream || { upstream_id };
+ const form2Data: UpstreamComponent.ResponseData = convertToFormData(upstream) || { upstream_id: upstream_id || 'None' };
const { plugins, script, plugin_config_id = '' } = data;
diff --git a/web/src/pages/Route/typing.d.ts b/web/src/pages/Route/typing.d.ts
index ea27938..0355c2c 100644
--- a/web/src/pages/Route/typing.d.ts
+++ b/web/src/pages/Route/typing.d.ts
@@ -73,19 +73,8 @@ declare namespace RouteModule {
host?: string;
hosts: string[];
remote_addrs: string[];
+ upstream: UpstreamComponent.ResponseData;
vars: [string, Operator, string | any[]][];
- upstream: {
- upstream_id?: string;
- type: 'roundrobin' | 'chash' | 'ewma';
- hash_on?: string;
- key?: string;
- nodes: Record<string, number>;
- timeout: {
- connect: number;
- send: number;
- read: number;
- };
- };
upstream_path?: {
type?: string;
from?: string;
@@ -110,6 +99,7 @@ declare namespace RouteModule {
type Step1PassProps = {
form: FormInstance;
+ upstreamForm?: FormInstance;
advancedMatchingRules: MatchingRule[];
disabled?: boolean;
isEdit?: boolean;
@@ -161,23 +151,9 @@ declare namespace RouteModule {
hasServiceId: boolean;
};
- type Form2Data = {
- type: 'roundrobin' | 'chash' | 'ewma';
- hash_on?: string;
- key?: string;
- upstreamPath?: string;
- upstream_id?: string;
- timeout: {
- connect: number;
- send: number;
- read: number;
- };
- nodes: Record<string, number>;
- };
-
type RequestData = {
form1Data: Form1Data;
- form2Data: Form2Data;
+ form2Data: UpstreamComponent.ResponseData;
step3Data: Step3Data;
advancedMatchingRules: MatchingRule[];
};
diff --git a/web/src/pages/Service/Create.tsx b/web/src/pages/Service/Create.tsx
index 4d1dbc2..ab977a9 100644
--- a/web/src/pages/Service/Create.tsx
+++ b/web/src/pages/Service/Create.tsx
@@ -22,6 +22,7 @@ import { omit } from 'lodash';
import ActionBar from '@/components/ActionBar';
import PluginPage from '@/components/Plugin';
+import { convertToFormData } from '@/components/Upstream/service';
import Preview from './components/Preview';
import Step1 from './components/Step1';
import { create, update, fetchItem } from './service';
@@ -48,11 +49,11 @@ const Page: React.FC = (props) => {
const { serviceId } = (props as any).match.params;
if (serviceId) {
fetchItem(serviceId).then(({ data }) => {
- if (data.upstream_id && data.upstream_id !== '') {
- upstreamForm.setFieldsValue({ upstream_id: data.upstream_id });
+ if (data.upstream_id) {
+ upstreamForm.setFieldsValue({ upstream_id: data.upstream_id })
}
if (data.upstream) {
- upstreamForm.setFieldsValue(data.upstream);
+ upstreamForm.setFieldsValue(convertToFormData(data.upstream))
}
form.setFieldsValue(omit(data, ['upstream_id', 'upstream', 'plugins']));
setPlugins(data.plugins || {});
@@ -67,6 +68,9 @@ const Page: React.FC = (props) => {
};
const upstreamFormData = upstreamRef.current?.getData();
+ if (!upstreamFormData) {
+ return
+ }
if (!upstreamFormData.upstream_id) {
data.upstream = upstreamFormData;
} else {
@@ -78,8 +82,8 @@ const Page: React.FC = (props) => {
.then(() => {
notification.success({
message: `${serviceId
- ? formatMessage({ id: 'component.global.edit' })
- : formatMessage({ id: 'component.global.create' })
+ ? formatMessage({ id: 'component.global.edit' })
+ : formatMessage({ id: 'component.global.create' })
} ${formatMessage({ id: 'menu.service' })} ${formatMessage({
id: 'component.status.success',
})}`,
diff --git a/web/src/pages/Service/components/Step1.tsx b/web/src/pages/Service/components/Step1.tsx
index 12b38a0..3817456 100644
--- a/web/src/pages/Service/components/Step1.tsx
+++ b/web/src/pages/Service/components/Step1.tsx
@@ -19,7 +19,7 @@ import { Form, Input } from 'antd';
import { useIntl } from 'umi';
import UpstreamForm from '@/components/Upstream';
-import { fetchUpstreamList } from '../service';
+import { fetchUpstreamList } from '@/components/Upstream/service';
const FORM_LAYOUT = {
labelCol: {
@@ -37,7 +37,7 @@ const Step1: React.FC<ServiceModule.Step1PassProps> = ({
disabled,
}) => {
const { formatMessage } = useIntl();
- const [list, setList] = useState<UpstreamModule.RequestBody[]>([]);
+ const [list, setList] = useState<UpstreamComponent.ResponseData[]>([]);
useEffect(() => {
fetchUpstreamList().then(({ data }) => setList(data));
}, []);
diff --git a/web/src/pages/Service/service.ts b/web/src/pages/Service/service.ts
index bda706e..659c3ef 100644
--- a/web/src/pages/Service/service.ts
+++ b/web/src/pages/Service/service.ts
@@ -28,13 +28,6 @@ export const fetchList = ({ current = 1, pageSize = 10, ...res }) =>
total: data.total_size,
}));
-export const fetchUpstreamList = () => {
- return request<Res<ResListData<UpstreamModule.RequestBody>>>('/upstreams').then(({ data }) => ({
- data: data.rows,
- total: data.total_size,
- }));
-};
-
export const create = (data: ServiceModule.Entity) =>
request('/services', {
method: 'POST',
diff --git a/web/src/pages/Upstream/Create.tsx b/web/src/pages/Upstream/Create.tsx
index 9ab672e..b276d74 100644
--- a/web/src/pages/Upstream/Create.tsx
+++ b/web/src/pages/Upstream/Create.tsx
@@ -16,12 +16,10 @@
*/
import React, { useState, useEffect, useRef } from 'react';
import { PageContainer } from '@ant-design/pro-layout';
-import { Card, Steps, notification, Form, Button } from 'antd';
+import { Card, Steps, notification, Form } from 'antd';
import { history, useIntl } from 'umi';
-import { QuestionCircleOutlined } from '@ant-design/icons';
import ActionBar from '@/components/ActionBar';
-import { transformUpstreamDataFromRequest } from '@/components/Upstream/service';
import Step1 from './components/Step1';
import { fetchOne, create, update } from './service';
@@ -36,8 +34,8 @@ const Page: React.FC = (props) => {
const { id } = (props as any).match.params;
if (id) {
- fetchOne(id).then(({ data }) => {
- form1.setFieldsValue(transformUpstreamDataFromRequest(data));
+ fetchOne(id).then(data => {
+ form1.setFieldsValue(data);
});
}
}, []);
@@ -81,13 +79,6 @@ const Page: React.FC = (props) => {
title={(props as any).match.params.id
? formatMessage({ id: 'page.upstream.configure' })
: formatMessage({ id: 'page.upstream.create' })}
-
- extra={
- // TODO: support Document modal
- <Button type="default" disabled>
- <QuestionCircleOutlined />
- {formatMessage({ id: 'component.document' })}
- </Button>}
>
<Card bordered={false}>
<Steps current={step - 1} style={{ marginBottom: 30 }}>
@@ -95,7 +86,7 @@ const Page: React.FC = (props) => {
<Steps.Step title={formatMessage({ id: 'page.upstream.create.preview' })} />
</Steps>
- {step === 1 && <Step1 form={form1} upstreamRef={upstreamRef} />}
+ {step === 1 && <Step1 form={form1} upstreamRef={upstreamRef} neverReadonly />}
{step === 2 && <Step1 form={form1} upstreamRef={upstreamRef} disabled />}
</Card>
</PageContainer>
diff --git a/web/src/pages/Upstream/components/Step1.tsx b/web/src/pages/Upstream/components/Step1.tsx
index 40f3c31..226e71c 100644
--- a/web/src/pages/Upstream/components/Step1.tsx
+++ b/web/src/pages/Upstream/components/Step1.tsx
@@ -14,27 +14,22 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import React, { useEffect, useState } from 'react';
+import React from 'react';
import { Form, Input } from 'antd';
import type { FormInstance } from 'antd/lib/form';
import { useIntl } from 'umi';
import UpstreamForm from '@/components/Upstream';
-import { fetchList } from '../service';
type Props = {
form: FormInstance;
disabled?: boolean;
upstreamRef?: React.MutableRefObject<any>;
+ neverReadonly?: boolean;
};
-const Step1: React.FC<Props> = ({ form, disabled, upstreamRef }) => {
+const Step1: React.FC<Props> = ({ form, disabled, upstreamRef, neverReadonly }) => {
const { formatMessage } = useIntl();
- const [list, setList] = useState<UpstreamModule.RequestBody[]>([]);
-
- useEffect(() => {
- fetchList({}).then(({ data }) => setList(data));
- }, []);
return (
<>
@@ -56,7 +51,7 @@ const Step1: React.FC<Props> = ({ form, disabled, upstreamRef }) => {
/>
</Form.Item>
</Form>
- <UpstreamForm ref={upstreamRef} form={form} disabled={disabled} list={list} />
+ <UpstreamForm ref={upstreamRef} form={form} disabled={disabled} neverReadonly={neverReadonly} />
</>
);
};
diff --git a/web/src/pages/Upstream/locales/zh-CN.ts b/web/src/pages/Upstream/locales/zh-CN.ts
index 73611a2..eb0c19a 100644
--- a/web/src/pages/Upstream/locales/zh-CN.ts
+++ b/web/src/pages/Upstream/locales/zh-CN.ts
@@ -34,7 +34,7 @@ export default {
'page.upstream.step.type': '负载均衡算法',
'page.upstream.step.pass-host': 'Host 请求头',
'page.upstream.step.pass-host.pass': '保持与客户端请求一致的主机名',
- 'page.upstream.step.pass-host.node': '使用上游节点列表中的主机名或 IP',
+ 'page.upstream.step.pass-host.node': '使用目标节点列表中的主机名或 IP',
'page.upstream.step.pass-host.rewrite': '自定义 Host 请求头(即将废弃)',
'page.upstream.step.pass-host.upstream_host': '自定义主机名',
'page.upstream.step.connect.timeout': '连接超时',
diff --git a/web/src/pages/Upstream/service.ts b/web/src/pages/Upstream/service.ts
index 3712f34..9dee862 100644
--- a/web/src/pages/Upstream/service.ts
+++ b/web/src/pages/Upstream/service.ts
@@ -16,6 +16,8 @@
*/
import { request } from 'umi';
+import { convertToFormData } from '@/components/Upstream/service';
+
export const fetchList = ({ current = 1, pageSize = 10, ...res }) => {
return request<Res<ResListData<UpstreamModule.RequestBody>>>('/upstreams', {
params: {
@@ -29,7 +31,7 @@ export const fetchList = ({ current = 1, pageSize = 10, ...res }) => {
}));
};
-export const fetchOne = (id: string) => request<Res<any>>(`/upstreams/${id}`);
+export const fetchOne = (id: string) => request<Res<any>>(`/upstreams/${id}`).then(({data}) => convertToFormData(data));
export const create = (data: UpstreamModule.RequestBody) =>
request('/upstreams', {
diff --git a/web/src/pages/Upstream/transform.ts b/web/src/pages/Upstream/transform.ts
deleted file mode 100644
index 0b520f2..0000000
--- a/web/src/pages/Upstream/transform.ts
+++ /dev/null
@@ -1,83 +0,0 @@
-import { pickBy, identity, omit, pick } from 'lodash';
-
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-export const transformRequest = (
- formData: UpstreamModule.RequestBody,
-): UpstreamModule.RequestBody | undefined | { upstream_id: string } => {
- let data = pickBy(formData, identity) as UpstreamModule.RequestBody;
-
- data = omit(data, 'custom')
-
- const {
- type,
- hash_on,
- key,
- k8s_deployment_info,
- nodes,
- pass_host,
- upstream_host,
- upstream_id,
- } = data;
-
- data.checks = pickBy(data.checks || {}, identity);
- if (data.checks.active) {
- data.checks.active = pickBy(
- data.checks.active,
- identity,
- ) as UpstreamModule.HealthCheck['active'];
- }
-
- if (upstream_id) {
- return { upstream_id };
- }
-
- if (Object.keys(data.checks).length === 0) {
- data = omit(data, 'checks');
- }
- if (nodes && k8s_deployment_info) {
- return undefined;
- }
-
- if (!nodes && !k8s_deployment_info) {
- return undefined;
- }
-
- if (type === 'chash') {
- if (!hash_on) {
- return undefined;
- }
-
- if (hash_on !== 'consumer' && !key) {
- return undefined;
- }
- }
-
- if (pass_host === 'rewrite' && !upstream_host) {
- return undefined;
- }
-
- if (nodes) {
- // NOTE: https://github.com/ant-design/ant-design/issues/27396
- data.nodes = data.nodes?.map((item) => {
- return pick(item, ['host', 'port', 'weight']);
- });
- return data;
- }
-
- return undefined;
-};