mirror of https://github.com/kubevela/velaux.git
Compare commits
9 Commits
Author | SHA1 | Date |
---|---|---|
|
be7a7ba470 | |
|
9a30505e4e | |
|
56c4dc9ed5 | |
|
7d64df983d | |
|
430a9c36ad | |
|
d007fc3f35 | |
|
b9f1663867 | |
|
a18675b341 | |
|
e431a48460 |
|
@ -1,6 +1,7 @@
|
|||
import { post, get, rdelete, put } from './request';
|
||||
import { addons, addonRegistries, enabledAddon } from './productionLink';
|
||||
import { getDomain } from '../utils/common';
|
||||
import type { EnableAddonRequest } from '../interface/addon';
|
||||
|
||||
const baseURLOject = getDomain();
|
||||
const base = baseURLOject.MOCK || baseURLOject.APIBASE;
|
||||
|
@ -23,7 +24,16 @@ export function getAddonsList(params: any) {
|
|||
|
||||
export function getAddonsDetails(params: { name: string; version?: string }) {
|
||||
const gurl = `${base + addons}/${params.name}`;
|
||||
return get(gurl, params).then((res) => res);
|
||||
return get(
|
||||
gurl,
|
||||
params.version
|
||||
? {
|
||||
params: {
|
||||
version: params.version,
|
||||
},
|
||||
}
|
||||
: {},
|
||||
).then((res) => res);
|
||||
}
|
||||
|
||||
export function disableAddon(params: { name: string }) {
|
||||
|
@ -31,12 +41,7 @@ export function disableAddon(params: { name: string }) {
|
|||
return post(gurl, params).then((res) => res);
|
||||
}
|
||||
|
||||
export function enableAddon(params: {
|
||||
name: string;
|
||||
version: string;
|
||||
clusters?: string[];
|
||||
properties: any;
|
||||
}) {
|
||||
export function enableAddon(params: EnableAddonRequest) {
|
||||
const gurl = `${base + addons}/${params.name}/enable`;
|
||||
const req: any = { args: params.properties, version: params.version };
|
||||
if (params.clusters) {
|
||||
|
@ -45,12 +50,7 @@ export function enableAddon(params: {
|
|||
return post(gurl, req).then((res) => res);
|
||||
}
|
||||
|
||||
export function upgradeAddon(params: {
|
||||
name: string;
|
||||
version: string;
|
||||
clusters?: string[];
|
||||
properties: any;
|
||||
}) {
|
||||
export function upgradeAddon(params: EnableAddonRequest) {
|
||||
const gurl = `${base + addons}/${params.name}/update`;
|
||||
const req: any = { args: params.properties, version: params.version };
|
||||
if (params.clusters) {
|
||||
|
|
|
@ -470,15 +470,6 @@ a {
|
|||
display: inline-block;
|
||||
}
|
||||
|
||||
.next-drawer-close {
|
||||
right: 24px;
|
||||
|
||||
.next-drawer-close-icon.next-icon::before {
|
||||
width: 16px !important;
|
||||
height: 16px !important;
|
||||
font-size: 16px !important;
|
||||
}
|
||||
}
|
||||
.next-btn.danger-btn {
|
||||
color: @dangerColor !important;
|
||||
border: @dangerColor 1px solid !important;
|
||||
|
|
|
@ -8,3 +8,46 @@
|
|||
height: 60px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.next-drawer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
.next-drawer-header {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
z-index: 1000;
|
||||
display: flex;
|
||||
flex: 0;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 55px;
|
||||
padding: 16px 24px;
|
||||
font-size: 16px;
|
||||
line-height: 22px;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
.next-drawer-body {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
min-height: 0;
|
||||
margin-top: 71px;
|
||||
padding: 24px;
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.next-drawer-close {
|
||||
right: 26px !important;
|
||||
|
||||
.next-drawer-close-icon.next-icon::before {
|
||||
color: #000;
|
||||
font-weight: 800 !important;
|
||||
font-size: 24px !important;
|
||||
line-height: 24px !important;
|
||||
&:hover {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -288,9 +288,6 @@ class UISchema extends Component<Props, State> {
|
|||
if (!uiSchema || enableCodeEdit) {
|
||||
return this.renderCodeEdit();
|
||||
}
|
||||
if (mode == 'edit' && value === undefined) {
|
||||
return <div />;
|
||||
}
|
||||
let onlyShowRequired = false;
|
||||
let couldShowParamCount = 0;
|
||||
uiSchema.map((param) => {
|
||||
|
@ -359,7 +356,7 @@ class UISchema extends Component<Props, State> {
|
|||
if (initValue === undefined) {
|
||||
initValue = param.validate?.defaultValue;
|
||||
}
|
||||
const disableEdit = (param.validate?.immutable && this.props.mode == 'edit') || false;
|
||||
const disableEdit = (param.validate?.immutable && mode == 'edit') || false;
|
||||
const getGroup = (children: React.ReactNode) => {
|
||||
return (
|
||||
<Group
|
||||
|
|
|
@ -120,8 +120,8 @@ class Group extends React.Component<Props, State> {
|
|||
type: 'confirm',
|
||||
content: (
|
||||
<Translation>
|
||||
If Switch is turned off, The configuration will be reset. Are you
|
||||
sure you want to do this?
|
||||
The configuration will be reset if the switch is turned off. Are you
|
||||
sure want to do this?
|
||||
</Translation>
|
||||
),
|
||||
onOk: () => {
|
||||
|
|
|
@ -16,6 +16,10 @@ export interface Addon {
|
|||
registryName?: string;
|
||||
availableVersions?: string[];
|
||||
url?: string;
|
||||
system?: {
|
||||
vela?: string;
|
||||
kubernetes?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface AddonStatus {
|
||||
|
@ -69,3 +73,10 @@ export interface AddonBaseStatus {
|
|||
name: string;
|
||||
phase: 'disabled' | 'enabled' | 'enabling' | 'suspend' | 'disabling';
|
||||
}
|
||||
|
||||
export interface EnableAddonRequest {
|
||||
name: string;
|
||||
version: string;
|
||||
properties: Record<string, any>;
|
||||
clusters?: string[];
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
.next-drawer-body {
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
background: rgb(59, 59, 59);
|
||||
.prepare {
|
||||
display: flex;
|
||||
|
|
|
@ -2,9 +2,10 @@ import React, { Component, Fragment } from 'react';
|
|||
import { Grid, Form, Input, Field, Button, Message, Icon, Dialog } from '@b-design/ui';
|
||||
import { updateUser } from '../../../../api/users';
|
||||
import type { LoginUserInfo } from '../../../../interface/user';
|
||||
import { checkUserPassword, checkUserEmail } from '../../../../utils/common';
|
||||
import { checkUserPassword } from '../../../../utils/common';
|
||||
import Translation from '../../../../components/Translation';
|
||||
import i18n from '../../../../i18n';
|
||||
import locale from '../../../../utils/locale';
|
||||
|
||||
type Props = {
|
||||
userInfo?: LoginUserInfo;
|
||||
|
@ -96,6 +97,7 @@ class EditPlatFormUserDialog extends Component<Props, State> {
|
|||
title={this.showTitle()}
|
||||
style={{ width: '600px' }}
|
||||
onOk={this.onUpdateUser}
|
||||
locale={locale().Dialog}
|
||||
footerActions={['ok']}
|
||||
>
|
||||
<Form {...formItemLayout} field={this.field}>
|
||||
|
@ -143,7 +145,7 @@ class EditPlatFormUserDialog extends Component<Props, State> {
|
|||
rules: [
|
||||
{
|
||||
required: true,
|
||||
pattern: checkUserEmail,
|
||||
format: 'email',
|
||||
message: <Translation>Please input a valid email</Translation>,
|
||||
},
|
||||
],
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
:root {
|
||||
--primary-color: #1b58f4;
|
||||
--warning-color: var(--message-warning-color-icon-inline, #fac800);
|
||||
}
|
||||
@border-radius-8: 8px;
|
||||
@F7F7F7: #f7f7f7;
|
||||
@F9F8FF: #f9f8ff;
|
||||
|
|
|
@ -342,7 +342,7 @@
|
|||
"Instructions for assessing whether the container is alive.": "容器健康状态探针",
|
||||
"Specify the annotations in the workload": "指定工作负载的注释",
|
||||
"Specify the labels in the workload": "指定工作负载的标签",
|
||||
"If Switch is turned off, The configuration will be reset. Are you sure you want to do this?": "如果关闭,配置将重置。确定操作吗?",
|
||||
"The configuration will be reset if the switch is turned off. Are you sure want to do this?": "如果关闭,配置将重置。确定操作吗?",
|
||||
"Mount PVC type volume": "挂载 PVC 数据卷",
|
||||
"Mount HostPath type volume": "挂载 HostPath 数据卷",
|
||||
"Mount EmptyDir type volume": "挂载 EmptyDir 数据卷",
|
||||
|
|
|
@ -146,7 +146,12 @@ class CardContent extends React.Component<Props, State> {
|
|||
<Row className="content-main-btn">
|
||||
{tags?.map((tag: string) => {
|
||||
return (
|
||||
<Tag style={{ marginRight: '8px' }} color={getTagColor(tag)} key={tag}>
|
||||
<Tag
|
||||
title={tag}
|
||||
style={{ marginRight: '8px' }}
|
||||
color={getTagColor(tag)}
|
||||
key={tag}
|
||||
>
|
||||
{tag}
|
||||
</Tag>
|
||||
);
|
||||
|
|
|
@ -13,13 +13,18 @@
|
|||
img {
|
||||
width: 100%;
|
||||
}
|
||||
ol, ul {
|
||||
ol,
|
||||
ul {
|
||||
list-style: decimal;
|
||||
}
|
||||
p {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
.addon-readme li>p {
|
||||
.addon-readme li > p {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.warning-text {
|
||||
color: var(--warning-color);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { Fragment } from 'react';
|
||||
import React from 'react';
|
||||
import {
|
||||
Form,
|
||||
Button,
|
||||
|
@ -10,6 +10,10 @@ import {
|
|||
Message,
|
||||
Select,
|
||||
Checkbox,
|
||||
Grid,
|
||||
Dropdown,
|
||||
Menu,
|
||||
Icon,
|
||||
} from '@b-design/ui';
|
||||
import type { Rule } from '@alifd/field';
|
||||
import { If } from 'tsx-control-statements/components';
|
||||
|
@ -24,18 +28,23 @@ import ReactMarkdown from 'react-markdown';
|
|||
import remarkGfm from 'remark-gfm';
|
||||
import Empty from '../../../../components/Empty';
|
||||
import DrawerWithFooter from '../../../../components/Drawer';
|
||||
import Group from '../../../../extends/Group';
|
||||
import Translation from '../../../../components/Translation';
|
||||
import UISchema from '../../../../components/UISchema';
|
||||
import type { Addon, AddonStatus } from '../../../../interface/addon';
|
||||
import type { Addon, AddonStatus, EnableAddonRequest } from '../../../../interface/addon';
|
||||
import locale from '../../../../utils/locale';
|
||||
import StatusShow from '../../../../components/StatusShow';
|
||||
import type { ApplicationStatus } from '../../../../interface/application';
|
||||
import type { ApplicationStatus, UIParam } from '../../../../interface/application';
|
||||
import i18n from '../../../../i18n';
|
||||
import type { NameAlias } from '../../../../interface/env';
|
||||
import Permission from '../../../../components/Permission';
|
||||
import 'github-markdown-css/github-markdown-light.css';
|
||||
import './index.less';
|
||||
import { Link } from 'dva/router';
|
||||
import { listApplicationServiceEndpoints } from '../../../../api/observation';
|
||||
import type { Endpoint } from '../../../../interface/observation';
|
||||
import { getLink } from '../../../../utils/utils';
|
||||
|
||||
const { Col, Row } = Grid;
|
||||
|
||||
type Props = {
|
||||
addonName: string;
|
||||
|
@ -58,17 +67,19 @@ type State = {
|
|||
clusters?: string[];
|
||||
allClusters?: NameAlias[];
|
||||
enabledClusters?: string[];
|
||||
endpoints?: Endpoint[];
|
||||
propertiesMode: 'code' | 'native';
|
||||
schema?: UIParam[];
|
||||
};
|
||||
|
||||
class AddonDetailDialog extends React.Component<Props, State> {
|
||||
timer?: number;
|
||||
readonly refreshTime = 1000;
|
||||
form: Field;
|
||||
statusLoop: boolean;
|
||||
uiSchemaRef: React.RefObject<UISchema>;
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
propertiesMode: 'native',
|
||||
addonDetailInfo: {
|
||||
name: '',
|
||||
},
|
||||
|
@ -86,12 +97,7 @@ class AddonDetailDialog extends React.Component<Props, State> {
|
|||
componentDidMount() {
|
||||
this.loadAddonDetail();
|
||||
this.loadAddonStatus();
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this.timer) {
|
||||
window.clearInterval(this.timer);
|
||||
}
|
||||
this.loadAddonEndpoints();
|
||||
}
|
||||
|
||||
loadAddonDetail = async () => {
|
||||
|
@ -100,7 +106,7 @@ class AddonDetailDialog extends React.Component<Props, State> {
|
|||
getAddonsDetails({ name: this.props.addonName, version: version })
|
||||
.then((res: Addon) => {
|
||||
if (res) {
|
||||
this.setState({ addonDetailInfo: res, loading: false });
|
||||
this.setState({ addonDetailInfo: res, schema: res.uiSchema, loading: false });
|
||||
if (!this.state.version && res.version) {
|
||||
this.setState({ version: res.version });
|
||||
}
|
||||
|
@ -116,7 +122,7 @@ class AddonDetailDialog extends React.Component<Props, State> {
|
|||
getAddonsStatus({ name: this.props.addonName })
|
||||
.then((res: AddonStatus) => {
|
||||
if (!res) return;
|
||||
if (res.phase == 'enabling' && !this.statusLoop) {
|
||||
if ((res.phase == 'enabling' || res.phase === 'disabling') && !this.statusLoop) {
|
||||
this.statusLoop = true;
|
||||
setTimeout(() => {
|
||||
this.statusLoop = false;
|
||||
|
@ -158,26 +164,40 @@ class AddonDetailDialog extends React.Component<Props, State> {
|
|||
});
|
||||
};
|
||||
|
||||
handleSubmit = () => {
|
||||
const { status } = this.state;
|
||||
if (status === 'enabled') {
|
||||
Dialog.confirm({
|
||||
content:
|
||||
'Please make sure that the Addon is no longer in use and the related application has been recycled.',
|
||||
onOk: this.disableAddon,
|
||||
locale: locale().Dialog,
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (status === 'disabled') {
|
||||
this.form.validate((errors: any, values: any) => {
|
||||
if (errors) {
|
||||
return;
|
||||
}
|
||||
this.enableAddon(values.properties);
|
||||
});
|
||||
}
|
||||
loadAddonEndpoints = () => {
|
||||
// TODO: the app name and namespace should get from the api server.
|
||||
const appName = 'addon-' + this.props.addonName;
|
||||
listApplicationServiceEndpoints({
|
||||
appName: appName,
|
||||
appNs: 'vela-system',
|
||||
}).then((re) => {
|
||||
if (re && re.endpoints) {
|
||||
this.setState({ endpoints: re.endpoints });
|
||||
} else {
|
||||
this.setState({ endpoints: [] });
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
onDisable = () => {
|
||||
Dialog.confirm({
|
||||
content:
|
||||
'Please make sure that the Addon is no longer in use and the related application has been recycled.',
|
||||
onOk: this.disableAddon,
|
||||
locale: locale().Dialog,
|
||||
});
|
||||
return;
|
||||
};
|
||||
|
||||
onEnable = () => {
|
||||
this.form.validate((errors: any, values: any) => {
|
||||
if (errors) {
|
||||
return;
|
||||
}
|
||||
this.enableAddon(values.properties);
|
||||
});
|
||||
};
|
||||
|
||||
onUpgrade = () => {
|
||||
this.form.validate((errors: any, values: any) => {
|
||||
if (errors) {
|
||||
|
@ -195,17 +215,21 @@ class AddonDetailDialog extends React.Component<Props, State> {
|
|||
return;
|
||||
}
|
||||
this.setState({ upgradeLoading: true });
|
||||
upgradeAddon({
|
||||
const params: EnableAddonRequest = {
|
||||
name: this.props.addonName,
|
||||
version: this.state.version,
|
||||
clusters: this.state.clusters,
|
||||
properties: values.properties,
|
||||
}).then(() => {
|
||||
};
|
||||
if (this.state.addonDetailInfo?.deployTo?.runtimeCluster) {
|
||||
params.clusters = this.state.clusters;
|
||||
}
|
||||
upgradeAddon(params).then(() => {
|
||||
this.loadAddonStatus();
|
||||
this.setState({ upgradeLoading: false });
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
enableAddon = async (properties: any) => {
|
||||
if (!this.state.version) {
|
||||
Message.warning(i18n.t('Please firstly select want to enable version'));
|
||||
|
@ -220,12 +244,15 @@ class AddonDetailDialog extends React.Component<Props, State> {
|
|||
}
|
||||
this.setState({ statusLoading: true }, () => {
|
||||
if (this.state.version) {
|
||||
enableAddon({
|
||||
const params: EnableAddonRequest = {
|
||||
name: this.props.addonName,
|
||||
version: this.state.version,
|
||||
clusters: this.state.clusters,
|
||||
properties: properties,
|
||||
}).then(() => {
|
||||
};
|
||||
if (this.state.addonDetailInfo?.deployTo?.runtimeCluster) {
|
||||
params.clusters = this.state.clusters;
|
||||
}
|
||||
enableAddon(params).then(() => {
|
||||
this.loadAddonStatus();
|
||||
});
|
||||
}
|
||||
|
@ -253,7 +280,9 @@ class AddonDetailDialog extends React.Component<Props, State> {
|
|||
};
|
||||
changeVersion = (version: string) => {
|
||||
this.setState({ version: version }, () => {
|
||||
this.loadAddonDetail();
|
||||
this.setState({ schema: undefined }, () => {
|
||||
this.loadAddonDetail();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -278,6 +307,9 @@ class AddonDetailDialog extends React.Component<Props, State> {
|
|||
clusters,
|
||||
allClusters,
|
||||
enabledClusters,
|
||||
endpoints,
|
||||
propertiesMode,
|
||||
schema,
|
||||
} = this.state;
|
||||
const { showAddon, addonName } = this.props;
|
||||
const validator = (rule: Rule, value: any, callback: (error?: string) => void) => {
|
||||
|
@ -293,32 +325,30 @@ class AddonDetailDialog extends React.Component<Props, State> {
|
|||
item.uiType = 'Password';
|
||||
}
|
||||
});
|
||||
const buttons = [
|
||||
<Fragment>
|
||||
<Button type="secondary" onClick={this.onClose} style={{ marginRight: '16px' }}>
|
||||
<Translation>Cancel</Translation>
|
||||
</Button>
|
||||
const buttons = [];
|
||||
if (status === 'enabled' || status === 'enabling' || status === 'disabling') {
|
||||
buttons.push(
|
||||
<Permission
|
||||
request={{
|
||||
resource: `addon:${addonName}`,
|
||||
action: status === 'enabled' ? 'disable' : 'enable',
|
||||
action: 'disable',
|
||||
}}
|
||||
project={''}
|
||||
>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={this.handleSubmit}
|
||||
warning={status === 'enabled'}
|
||||
type="secondary"
|
||||
onClick={this.onDisable}
|
||||
title={status}
|
||||
style={{ backgroundColor: status === 'enabled' ? 'red' : '' }}
|
||||
loading={statusLoading || loading || status == 'enabling' || status == 'disabling'}
|
||||
disabled={status == 'enabling' || status == 'disabling' || version == ''}
|
||||
className="danger-btn"
|
||||
loading={status === 'disabling'}
|
||||
disabled={status === 'disabling'}
|
||||
>
|
||||
<Translation>{status === 'enabled' ? 'Disable' : 'Enable'}</Translation>
|
||||
<Translation>Disable</Translation>
|
||||
</Button>
|
||||
</Permission>
|
||||
</Fragment>,
|
||||
];
|
||||
</Permission>,
|
||||
);
|
||||
}
|
||||
|
||||
if (status == 'enabled' || status == 'suspend') {
|
||||
buttons.push(
|
||||
<Permission request={{ resource: `addon:${addonName}`, action: 'update' }} project={''}>
|
||||
|
@ -334,6 +364,21 @@ class AddonDetailDialog extends React.Component<Props, State> {
|
|||
);
|
||||
}
|
||||
|
||||
if (status === 'disabled' || status === 'enabling') {
|
||||
buttons.push(
|
||||
<Permission request={{ resource: `addon:${addonName}`, action: 'enable' }} project={''}>
|
||||
<Button
|
||||
loading={status === 'enabling'}
|
||||
type="primary"
|
||||
onClick={this.onEnable}
|
||||
style={{ marginLeft: '16px' }}
|
||||
>
|
||||
<Translation>Enable</Translation>
|
||||
</Button>
|
||||
</Permission>,
|
||||
);
|
||||
}
|
||||
|
||||
const getAppStatusShowType = (statusInfo: string | undefined) => {
|
||||
if (!statusInfo) {
|
||||
return 'notice';
|
||||
|
@ -364,6 +409,8 @@ class AddonDetailDialog extends React.Component<Props, State> {
|
|||
};
|
||||
});
|
||||
|
||||
const outerEndpoint = endpoints?.filter((end) => !end.endpoint.inner);
|
||||
|
||||
return (
|
||||
<div className="basic">
|
||||
<DrawerWithFooter
|
||||
|
@ -383,16 +430,52 @@ class AddonDetailDialog extends React.Component<Props, State> {
|
|||
</If>
|
||||
|
||||
<If condition={addonsStatus && addonsStatus.status}>
|
||||
<Message
|
||||
type={getAppStatusShowType(addonsStatus?.status)}
|
||||
size="medium"
|
||||
style={{ padding: '8px', marginBottom: '10px' }}
|
||||
>
|
||||
{`${i18n.t('Addon status is ')}${addonsStatus?.status || 'Initing'}`}
|
||||
<a style={{ marginLeft: '16px' }} onClick={() => this.updateStatusShow(true)}>
|
||||
<Translation>Check the details</Translation>
|
||||
</a>
|
||||
</Message>
|
||||
<Row>
|
||||
<Col span={16}>
|
||||
<Message
|
||||
type={getAppStatusShowType(addonsStatus?.status)}
|
||||
size="medium"
|
||||
style={{ padding: '8px', marginBottom: '10px' }}
|
||||
>
|
||||
{`${i18n.t('Addon status is ')}${addonsStatus?.status || 'Init'}`}
|
||||
<Link
|
||||
style={{ marginLeft: '16px' }}
|
||||
to={`/applications/addon-${addonDetailInfo?.name}`}
|
||||
>
|
||||
<Translation>Check the details</Translation>
|
||||
</Link>
|
||||
</Message>
|
||||
</Col>
|
||||
<If condition={outerEndpoint && outerEndpoint?.length > 0}>
|
||||
<Col span={8} className={'flexright'}>
|
||||
<Dropdown
|
||||
trigger={
|
||||
<Button style={{ marginLeft: '16px' }} type="secondary">
|
||||
<Translation>Endpoints</Translation>
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
<Menu>
|
||||
{outerEndpoint?.map((item) => {
|
||||
const linkURL = getLink(item);
|
||||
return (
|
||||
<Menu.Item key={linkURL}>
|
||||
<a
|
||||
style={{ color: '#1b58f4' }}
|
||||
target="_blank"
|
||||
href={linkURL}
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{linkURL}
|
||||
</a>
|
||||
</Menu.Item>
|
||||
);
|
||||
})}
|
||||
</Menu>
|
||||
</Dropdown>
|
||||
</Col>
|
||||
</If>
|
||||
</Row>
|
||||
</If>
|
||||
|
||||
{/* select the addon version */}
|
||||
|
@ -405,6 +488,18 @@ class AddonDetailDialog extends React.Component<Props, State> {
|
|||
value={version}
|
||||
/>
|
||||
</Form.Item>
|
||||
{addonDetailInfo?.system && (
|
||||
<span className="warning-text">
|
||||
This version requirements: (
|
||||
{addonDetailInfo?.system.vela
|
||||
? `KubeVela: ${addonDetailInfo?.system.vela}`
|
||||
: ''}
|
||||
{addonDetailInfo?.system.kubernetes
|
||||
? ` Kubernetes: ${addonDetailInfo?.system.kubernetes}`
|
||||
: ''}
|
||||
)
|
||||
</span>
|
||||
)}
|
||||
</If>
|
||||
|
||||
<If condition={addonDetailInfo?.deployTo?.runtimeCluster}>
|
||||
|
@ -419,34 +514,69 @@ class AddonDetailDialog extends React.Component<Props, State> {
|
|||
</If>
|
||||
</Card>
|
||||
|
||||
<If condition={addonDetailInfo?.uiSchema}>
|
||||
<Group
|
||||
title={<Translation>Properties</Translation>}
|
||||
description={<Translation>Set the addon configuration parameters</Translation>}
|
||||
required={true}
|
||||
closed={status === 'enabled'}
|
||||
alwaysShow={true}
|
||||
disableAddon={true}
|
||||
hasToggleIcon={true}
|
||||
<If condition={schema}>
|
||||
<Card
|
||||
contentHeight={'auto'}
|
||||
className="withActions"
|
||||
title="Properties"
|
||||
subTitle={
|
||||
schema
|
||||
? [
|
||||
<Button
|
||||
style={{ marginTop: '-12px' }}
|
||||
onClick={() => {
|
||||
if (propertiesMode === 'native') {
|
||||
this.setState({ propertiesMode: 'code' });
|
||||
} else {
|
||||
this.setState({ propertiesMode: 'native' });
|
||||
}
|
||||
}}
|
||||
>
|
||||
<If condition={propertiesMode === 'native'}>
|
||||
<Icon
|
||||
style={{ color: '#1b58f4' }}
|
||||
type={'display-code'}
|
||||
title={'Switch to the coding mode'}
|
||||
/>
|
||||
</If>
|
||||
<If condition={propertiesMode === 'code'}>
|
||||
<Icon
|
||||
style={{ color: '#1b58f4' }}
|
||||
type={'laptop'}
|
||||
title={'Switch to the native mode'}
|
||||
/>
|
||||
</If>
|
||||
</Button>,
|
||||
]
|
||||
: []
|
||||
}
|
||||
>
|
||||
<Form field={this.form}>
|
||||
{this.state.mode && (
|
||||
<UISchema
|
||||
{...this.form.init('properties', {
|
||||
rules: [
|
||||
{
|
||||
validator: validator,
|
||||
message: 'Please check addon properties',
|
||||
},
|
||||
],
|
||||
})}
|
||||
ref={this.uiSchemaRef}
|
||||
uiSchema={addonDetailInfo?.uiSchema}
|
||||
mode={this.state.mode}
|
||||
/>
|
||||
)}
|
||||
</Form>
|
||||
</Group>
|
||||
<Row>
|
||||
<If condition={schema}>
|
||||
{this.state.mode && (
|
||||
<UISchema
|
||||
{...this.form.init(`properties`, {
|
||||
rules: [
|
||||
{
|
||||
validator: validator,
|
||||
message: i18n.t('Please check the addon properties'),
|
||||
},
|
||||
],
|
||||
})}
|
||||
enableCodeEdit={propertiesMode === 'code'}
|
||||
uiSchema={schema}
|
||||
definition={{
|
||||
name: addonDetailInfo?.name || '',
|
||||
type: 'addon',
|
||||
description: addonDetailInfo?.description || '',
|
||||
}}
|
||||
ref={this.uiSchemaRef}
|
||||
mode={this.state.mode}
|
||||
/>
|
||||
)}
|
||||
</If>
|
||||
</Row>
|
||||
</Card>
|
||||
</If>
|
||||
<If condition={addonDetailInfo?.dependencies}>
|
||||
<Card
|
||||
|
|
|
@ -7,7 +7,6 @@ import {
|
|||
Button,
|
||||
Input,
|
||||
Select,
|
||||
Divider,
|
||||
Icon,
|
||||
Card,
|
||||
Loading,
|
||||
|
@ -246,14 +245,12 @@ class ComponentDialog extends React.Component<Props, State> {
|
|||
return (
|
||||
<div>
|
||||
<span>{alias ? `${alias}(${name})` : name}</span>
|
||||
<Divider />
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div>
|
||||
<span>{i18n.t('New Component')} </span>
|
||||
<Divider />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -289,7 +289,12 @@ class Header extends Component<Props, State> {
|
|||
if (item && !item.endpoint.inner) {
|
||||
return (
|
||||
<Menu.Item key={linkURL}>
|
||||
<a style={{ color: '#1b58f4' }} target="_blank" href={linkURL}>
|
||||
<a
|
||||
style={{ color: '#1b58f4' }}
|
||||
target="_blank"
|
||||
href={linkURL}
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
{linkURL}
|
||||
</a>
|
||||
</Menu.Item>
|
||||
|
|
|
@ -71,8 +71,8 @@ class PodDetail extends React.Component<Props, State> {
|
|||
}
|
||||
};
|
||||
|
||||
onOpenCloudShell = () => {
|
||||
const { env } = this.props;
|
||||
onOpenCloudShell = (containerName: string) => {
|
||||
const { env, pod } = this.props;
|
||||
if (!checkEnabledAddon('cloudshell', this.props.enabledAddons)) {
|
||||
Dialog.alert({
|
||||
title: i18n.t('CloudShell feature is not enabled'),
|
||||
|
@ -97,13 +97,15 @@ class PodDetail extends React.Component<Props, State> {
|
|||
});
|
||||
return;
|
||||
}
|
||||
const shellScript = `vela exec ${env?.appDeployName} -e ${env?.appDeployNamespace} -- bash`;
|
||||
const shellScript = `vela exec ${env?.appDeployName} -n ${env?.appDeployNamespace} --component ${pod.component} --cluster ${pod.cluster} --pod ${pod.metadata.name} --container ${containerName} -- bash`;
|
||||
Dialog.show({
|
||||
footer: false,
|
||||
style: { width: 400 },
|
||||
style: { width: 600 },
|
||||
content: (
|
||||
<div>
|
||||
<h5>1. Copy the command:</h5>
|
||||
<h5>
|
||||
1. <Translation>Copy the command</Translation>:
|
||||
</h5>
|
||||
<code className="code">
|
||||
{shellScript}{' '}
|
||||
<CopyToClipboard
|
||||
|
@ -115,7 +117,9 @@ class PodDetail extends React.Component<Props, State> {
|
|||
<AiOutlineCopy size={14} />
|
||||
</CopyToClipboard>
|
||||
</code>
|
||||
<h5>2. Open Cloud Shell:</h5>
|
||||
<h5>
|
||||
2. <Translation>Open Cloud Shell</Translation>:
|
||||
</h5>
|
||||
<div>
|
||||
<Button
|
||||
size="small"
|
||||
|
@ -128,7 +132,7 @@ class PodDetail extends React.Component<Props, State> {
|
|||
}
|
||||
}}
|
||||
>
|
||||
Open Cloud Shell
|
||||
<Translation>Open Cloud Shell</Translation>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -264,7 +268,7 @@ class PodDetail extends React.Component<Props, State> {
|
|||
</a>
|
||||
<a
|
||||
title="Console Shell"
|
||||
onClick={() => this.onOpenCloudShell()}
|
||||
onClick={() => this.onOpenCloudShell(record.name)}
|
||||
className="actionIcon"
|
||||
>
|
||||
<AiOutlineCode size={20} />
|
||||
|
|
|
@ -43,7 +43,7 @@ export const ShowRevision = (props: RevisionProps) => {
|
|||
React.useEffect(() => {
|
||||
loadRevisionDetail(props.appName, props.revision, setDetail);
|
||||
}, [props.appName, props.revision]);
|
||||
const containerId = props.revision.version;
|
||||
const containerId = props.revision.version + 'detail';
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
|
|
|
@ -37,7 +37,7 @@ export default class CallBackPage extends React.Component<Props> {
|
|||
if (res && res.accessToken) {
|
||||
localStorage.setItem('token', res.accessToken);
|
||||
localStorage.setItem('refreshToken', res.refreshToken);
|
||||
this.props.history.push('/');
|
||||
this.props.history.push('/applications');
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
.dialog-cluoudService-wraper {
|
||||
.dialog-cloudService-wrapper {
|
||||
width: 1000px;
|
||||
.cloud-server-wraper {
|
||||
.cloud-server-wrapper {
|
||||
.next-select-trigger {
|
||||
width: 100%;
|
||||
}
|
||||
|
|
|
@ -271,7 +271,7 @@ class CloudServiceDialog extends React.Component<Props, State> {
|
|||
<React.Fragment>
|
||||
<Dialog
|
||||
locale={locale().Dialog}
|
||||
className="dialog-cluoudService-wraper"
|
||||
className="dialog-cloudService-wrapper"
|
||||
title={<Translation>Connect Kubernetes Cluster From Cloud</Translation>}
|
||||
autoFocus={true}
|
||||
visible={visible}
|
||||
|
@ -294,7 +294,7 @@ class CloudServiceDialog extends React.Component<Props, State> {
|
|||
available permission set
|
||||
</Translation>
|
||||
</Message>
|
||||
<Form {...formItemLayout} field={this.field} className="cloud-server-wraper">
|
||||
<Form {...formItemLayout} field={this.field} className="cloud-server-wrapper">
|
||||
<FormItem label={<Translation>Provider</Translation>} required={true}>
|
||||
<Select
|
||||
locale={locale().Select}
|
||||
|
|
|
@ -25,7 +25,7 @@ type Props = {
|
|||
};
|
||||
|
||||
type State = {
|
||||
dexConfig: DexConfig;
|
||||
dexConfig?: DexConfig;
|
||||
loginType: string;
|
||||
loginErrorMessage: string;
|
||||
loginLoading: boolean;
|
||||
|
@ -36,19 +36,12 @@ export default class LoginPage extends Component<Props, State> {
|
|||
super(props);
|
||||
this.field = new Field(this);
|
||||
this.state = {
|
||||
dexConfig: {
|
||||
clientID: '',
|
||||
clientSecret: '',
|
||||
issuer: '',
|
||||
redirectURL: '',
|
||||
},
|
||||
loginType: '',
|
||||
loginErrorMessage: '',
|
||||
loginLoading: false,
|
||||
};
|
||||
}
|
||||
componentDidMount() {
|
||||
this.ontDexConfig();
|
||||
this.onGetLoginType();
|
||||
}
|
||||
onGetLoginType = () => {
|
||||
|
@ -62,7 +55,7 @@ export default class LoginPage extends Component<Props, State> {
|
|||
() => {
|
||||
const { loginType } = this.state;
|
||||
if (loginType === 'dex') {
|
||||
this.onGetDexCode();
|
||||
this.ontDexConfig();
|
||||
}
|
||||
},
|
||||
);
|
||||
|
@ -74,9 +67,14 @@ export default class LoginPage extends Component<Props, State> {
|
|||
getDexConfig()
|
||||
.then((res) => {
|
||||
if (res) {
|
||||
this.setState({
|
||||
dexConfig: res,
|
||||
});
|
||||
this.setState(
|
||||
{
|
||||
dexConfig: res,
|
||||
},
|
||||
() => {
|
||||
this.onGetDexCode();
|
||||
},
|
||||
);
|
||||
}
|
||||
})
|
||||
.catch();
|
||||
|
@ -116,10 +114,12 @@ export default class LoginPage extends Component<Props, State> {
|
|||
});
|
||||
};
|
||||
onGetDexCode = () => {
|
||||
const { clientID, issuer, redirectURL } = this.state.dexConfig;
|
||||
const newRedirectURl = encodeURIComponent(redirectURL);
|
||||
const dexClientURL = `${issuer}/auth?client_id=${clientID}&redirect_uri=${newRedirectURl}&response_type=code&scope=openid+profile+email+offline_access&state=velaux`;
|
||||
window.location.href = dexClientURL;
|
||||
if (this.state.dexConfig) {
|
||||
const { clientID, issuer, redirectURL } = this.state.dexConfig;
|
||||
const newRedirectURl = encodeURIComponent(redirectURL);
|
||||
const dexClientURL = `${issuer}/auth?client_id=${clientID}&redirect_uri=${newRedirectURl}&response_type=code&scope=openid+profile+email+offline_access&state=velaux`;
|
||||
window.location.href = dexClientURL;
|
||||
}
|
||||
};
|
||||
render() {
|
||||
const FormItem = Form.Item;
|
||||
|
|
|
@ -1,9 +1,4 @@
|
|||
import React from 'react';
|
||||
import Group from '../../extends/Group';
|
||||
export default function NotFound() {
|
||||
return (
|
||||
<div>
|
||||
<Group title="envplan" description="envplan" />
|
||||
</div>
|
||||
);
|
||||
return <div>The route is not exist(404)</div>;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react';
|
||||
import { Grid, Form, Input, Field, Button, Message, Select } from '@b-design/ui';
|
||||
import DrawerWithFooter from '../../../../components/Drawer';
|
||||
import { checkUserPassword, checkUserEmail } from '../../../../utils/common';
|
||||
import { checkUserPassword } from '../../../../utils/common';
|
||||
import Translation from '../../../../components/Translation';
|
||||
import { createUser, updateUser } from '../../../../api/users';
|
||||
import { checkName } from '../../../../utils/common';
|
||||
|
@ -231,7 +231,7 @@ class CreateUser extends React.Component<Props, State> {
|
|||
rules: [
|
||||
{
|
||||
required: true,
|
||||
pattern: checkUserEmail,
|
||||
format: 'email',
|
||||
message: <Translation>Please input a valid email</Translation>,
|
||||
},
|
||||
],
|
||||
|
|
|
@ -203,7 +203,6 @@ export const replaceUrl = function (text: string) {
|
|||
};
|
||||
|
||||
export const checkUserPassword = /^(?=.*[0-9])(?=.*[a-zA-Z])([a-zA-Z0-9]{8,16})$/;
|
||||
export const checkUserEmail = /^[a-z0-9._%+\-]+@[a-z0-9.\-]+\.[a-z]{2,4}$/;
|
||||
|
||||
export function isMatchBusinessCode(businessCode: number) {
|
||||
const tokenExpiredList = [12002, 12010];
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { Dialog } from '@b-design/ui';
|
||||
import i18n from '../i18n';
|
||||
import locale from './locale';
|
||||
|
||||
class ResetLogin {
|
||||
private static singleton: ResetLogin;
|
||||
|
@ -14,6 +15,7 @@ class ResetLogin {
|
|||
content: i18n.t('Authentication failed, please log in again'),
|
||||
closeable: true,
|
||||
closeMode: [],
|
||||
locale: locale().Dialog,
|
||||
footerActions: ['ok'],
|
||||
onOk: () => {
|
||||
localStorage.removeItem('token');
|
||||
|
|
|
@ -110,6 +110,13 @@ export function getLink(endpointObj: Endpoint) {
|
|||
if (appProtocol && appProtocol !== '') {
|
||||
protocol = appProtocol;
|
||||
}
|
||||
// Support to open this address in a new window directly.
|
||||
if (protocol == 'tcp') {
|
||||
protocol = 'http';
|
||||
}
|
||||
if (host == '') {
|
||||
return path;
|
||||
}
|
||||
if (path === '/') {
|
||||
path = '';
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue