mirror of https://github.com/kubevela/velaux.git
Compare commits
4 Commits
main
...
v1.5.0-bet
Author | SHA1 | Date |
---|---|---|
|
d007fc3f35 | |
|
b9f1663867 | |
|
a18675b341 | |
|
e431a48460 |
|
@ -9,6 +9,7 @@
|
|||
.next-drawer-body {
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
background: rgb(59, 59, 59);
|
||||
.prepare {
|
||||
display: flex;
|
||||
|
|
|
@ -2,7 +2,7 @@ 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';
|
||||
|
||||
|
@ -143,7 +143,7 @@ class EditPlatFormUserDialog extends Component<Props, State> {
|
|||
rules: [
|
||||
{
|
||||
required: true,
|
||||
pattern: checkUserEmail,
|
||||
format: 'email',
|
||||
message: <Translation>Please input a valid email</Translation>,
|
||||
},
|
||||
],
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { Fragment } from 'react';
|
||||
import React from 'react';
|
||||
import {
|
||||
Form,
|
||||
Button,
|
||||
|
@ -10,6 +10,9 @@ import {
|
|||
Message,
|
||||
Select,
|
||||
Checkbox,
|
||||
Grid,
|
||||
Dropdown,
|
||||
Menu,
|
||||
} from '@b-design/ui';
|
||||
import type { Rule } from '@alifd/field';
|
||||
import { If } from 'tsx-control-statements/components';
|
||||
|
@ -36,6 +39,12 @@ 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,11 +67,10 @@ type State = {
|
|||
clusters?: string[];
|
||||
allClusters?: NameAlias[];
|
||||
enabledClusters?: string[];
|
||||
endpoints?: Endpoint[];
|
||||
};
|
||||
|
||||
class AddonDetailDialog extends React.Component<Props, State> {
|
||||
timer?: number;
|
||||
readonly refreshTime = 1000;
|
||||
form: Field;
|
||||
statusLoop: boolean;
|
||||
uiSchemaRef: React.RefObject<UISchema>;
|
||||
|
@ -86,12 +94,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 () => {
|
||||
|
@ -116,7 +119,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 +161,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) {
|
||||
|
@ -278,6 +295,7 @@ class AddonDetailDialog extends React.Component<Props, State> {
|
|||
clusters,
|
||||
allClusters,
|
||||
enabledClusters,
|
||||
endpoints,
|
||||
} = this.state;
|
||||
const { showAddon, addonName } = this.props;
|
||||
const validator = (rule: Rule, value: any, callback: (error?: string) => void) => {
|
||||
|
@ -293,32 +311,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 +350,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 +395,8 @@ class AddonDetailDialog extends React.Component<Props, State> {
|
|||
};
|
||||
});
|
||||
|
||||
const outerEndpoint = endpoints?.filter((end) => !end.endpoint.inner);
|
||||
|
||||
return (
|
||||
<div className="basic">
|
||||
<DrawerWithFooter
|
||||
|
@ -383,16 +416,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 */}
|
||||
|
|
|
@ -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} />
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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];
|
||||
|
|
Loading…
Reference in New Issue