feat: password confirmation

This commit is contained in:
LoadChange 2020-01-14 20:18:50 +08:00
parent 29119d0956
commit 0b41b3d394
26 changed files with 170 additions and 98 deletions

View File

@ -26,7 +26,7 @@ module.exports = Object.assign({}, base, {
context: ['/'],
changeOrigin: true,
secure: false,
target: 'http://localhost:8848',
target: 'http://11.239.112.161:8848',
pathRewrite: {'^/v1' : '/nacos/v1'}
}],
disableHostCheck: true,

View File

@ -47,6 +47,7 @@
"eslint-plugin-import": "^2.14.0",
"eslint-plugin-prettier": "^3.0.0",
"eslint-plugin-react": "^7.17.0",
"eslint-plugin-react-hooks": "^2.3.0",
"file-loader": "^5.0.2",
"html-webpack-plugin": "^3.2.0",
"husky": "^3.1.0",

View File

@ -125,9 +125,7 @@ class RegionGroup extends React.Component {
? false
: window.location.search.indexOf('hideTopbar=') === -1,
},
() => {
this.setRegionWidth();
}
() => this.setRegionWidth()
);
}

View File

@ -512,6 +512,7 @@ const request = (function(_global) {
if (url.includes('password')) {
return;
}
localStorage.removeItem('token');
const base_url = url.split('#')[0];
window.location = `${base_url}#/login`;
}

View File

@ -22,10 +22,7 @@ import { changeLanguage } from '@/reducers/locale';
import './index.scss';
@withRouter
@connect(
state => ({ ...state.locale }),
{ changeLanguage }
)
@connect(state => ({ ...state.locale }), { changeLanguage })
@ConfigProvider.config
class Header extends React.Component {
static displayName = 'Header';
@ -56,10 +53,15 @@ class Header extends React.Component {
getUsername = () => {
const token = window.localStorage.getItem('token');
if (token) {
const base64Url = token.split('.')[1];
const [, base64Url = ''] = token.split('.');
const base64 = base64Url.replace('-', '+').replace('_', '/');
const parsedToken = JSON.parse(window.atob(base64));
return parsedToken.sub;
try {
const parsedToken = JSON.parse(window.atob(base64));
return parsedToken.sub;
} catch (e) {
delete localStorage.token;
location.reload();
}
}
return '';
};

View File

@ -30,10 +30,12 @@ class MainLayout extends React.Component {
static propTypes = {
locale: PropTypes.object,
location: PropTypes.object,
history: PropTypes.object,
version: PropTypes.any,
getState: PropTypes.func,
functionMode: PropTypes.string,
children: PropTypes.object,
};
componentDidMount() {
@ -97,20 +99,23 @@ class MainLayout extends React.Component {
className="nav-menu"
openMode="single"
>
{MenuData.map((subMenu, idx) =>
subMenu.children ? (
<SubMenu key={String(idx)} label={locale[subMenu.key]}>
{subMenu.children.map((item, i) => (
<Item
key={[idx, i].join('-')}
onClick={() => this.navTo(item.url)}
className={this.isCurrentPath(item.url)}
>
{locale[item.key]}
</Item>
))}
</SubMenu>
) : (
{MenuData.map((subMenu, idx) => {
if (subMenu.children) {
return (
<SubMenu key={String(idx)} label={locale[subMenu.key]}>
{subMenu.children.map((item, i) => (
<Item
key={[idx, i].join('-')}
onClick={() => this.navTo(item.url)}
className={this.isCurrentPath(item.url)}
>
{locale[item.key]}
</Item>
))}
</SubMenu>
);
}
return (
<Item
key={idx}
className={['first-menu', this.isCurrentPath(subMenu.url)]
@ -120,8 +125,8 @@ class MainLayout extends React.Component {
>
{locale[subMenu.key]}
</Item>
)
)}
);
})}
</Menu>
</>
)}

View File

@ -1,3 +1,5 @@
import { isJsonString } from '../utils/nacosutil';
const configurationMenu = {
key: 'configurationManagementVirtual',
children: [
@ -33,8 +35,8 @@ const authorityControlMenu = {
};
export default function(model) {
const { token } = localStorage;
const { globalAdmin } = JSON.parse(token || '{}');
const { token = '{}' } = localStorage;
const { globalAdmin } = isJsonString(token) ? JSON.parse(token) || {} : {};
return [
model === 'naming' ? undefined : configurationMenu,

View File

@ -523,17 +523,25 @@ const I18N_CONF = {
createUser: 'Create user',
username: 'Username',
password: 'Password',
rePassword: 'Repeat',
usernamePlaceholder: 'Please Enter Username',
passwordPlaceholder: 'Please Enter Password',
rePasswordPlaceholder: 'Please Enter Repeat Password',
usernameError: 'User name cannot be empty!',
passwordError: 'Password cannot be empty!',
rePasswordError: 'Repeat Password cannot be empty!',
rePasswordError2: 'Passwords are inconsistent!',
},
PasswordReset: {
resetPassword: 'Password Reset',
username: 'Username',
password: 'Password',
rePassword: 'Repeat',
passwordPlaceholder: 'Please Enter Password',
rePasswordPlaceholder: 'Please Enter Repeat Password',
passwordError: 'Password cannot be empty!',
rePasswordError: 'Repeat Password cannot be empty!',
rePasswordError2: 'Passwords are inconsistent!',
},
RolesManagement: {
roleManagement: 'Role management',

View File

@ -520,17 +520,25 @@ const I18N_CONF = {
createUser: '创建用户',
username: '用户名',
password: '密码',
rePassword: '确认密码',
usernamePlaceholder: '请输入用户名',
passwordPlaceholder: '请输入密码',
rePasswordPlaceholder: '请输入确认密码',
usernameError: '用户名不能为空',
passwordError: '密码不能为空!',
rePasswordError: '确认密码不能为空!',
rePasswordError2: '两次输入密码不一致!',
},
PasswordReset: {
resetPassword: '密码重置',
username: '用户名',
password: '密码',
rePassword: '确认密码',
passwordError: '密码不能为空',
passwordPlaceholder: '请输入密码',
rePasswordPlaceholder: '请输入确认密码',
rePasswordError: '确认密码不能为空!',
rePasswordError2: '两次输入密码不一致!',
},
RolesManagement: {
roleManagement: '角色管理',

View File

@ -77,9 +77,12 @@ class RolesManagement extends React.Component {
<Table.Column title={locale.username} dataIndex="username" />
<Table.Column
title={locale.operation}
dataIndex="username"
cell={(value, index, record) => (
<>
dataIndex="role"
cell={(value, index, record) => {
if (value === 'GLOBAL_ADMIN') {
return null;
}
return (
<Button
type="primary"
warning
@ -96,8 +99,8 @@ class RolesManagement extends React.Component {
>
{locale.deleteRole}
</Button>
</>
)}
);
}}
/>
</Table>
{roles.totalCount > pageSize && (

View File

@ -32,6 +32,8 @@ class NewUser extends React.Component {
static propTypes = {
locale: PropTypes.object,
visible: PropTypes.bool,
onOk: PropTypes.func,
onCancel: PropTypes.func,
};
check() {
@ -39,6 +41,7 @@ class NewUser extends React.Component {
const errors = {
username: locale.usernameError,
password: locale.passwordError,
rePassword: locale.rePasswordError,
};
const vals = Object.keys(errors).map(key => {
const val = this.field.getValue(key);
@ -47,10 +50,15 @@ class NewUser extends React.Component {
}
return val;
});
if (vals.filter(v => v).length === 2) {
return vals;
if (vals.filter(v => v).length !== 3) {
return null;
}
return null;
const [password, rePassword] = ['password', 'rePassword'].map(k => this.field.getValue(k));
if (password !== rePassword) {
this.field.setError('rePassword', locale.rePasswordError2);
return null;
}
return vals;
}
render() {
@ -79,6 +87,13 @@ class NewUser extends React.Component {
<FormItem label={locale.password} required help={getError('password')}>
<Input name="password" htmlType="password" placeholder={locale.passwordPlaceholder} />
</FormItem>
<FormItem label={locale.rePassword} required help={getError('rePassword')}>
<Input
name="rePassword"
htmlType="password"
placeholder={locale.rePasswordPlaceholder}
/>
</FormItem>
</Form>
</Dialog>
</>

View File

@ -36,7 +36,10 @@ class PasswordReset extends React.Component {
check() {
const { locale } = this.props;
const errors = { password: locale.passwordError };
const errors = {
password: locale.passwordError,
rePassword: locale.rePasswordError,
};
const vals = Object.keys(errors).map(key => {
const val = this.field.getValue(key);
if (!val) {
@ -44,10 +47,15 @@ class PasswordReset extends React.Component {
}
return val;
});
if (vals.filter(v => v).length === 1) {
return [this.props.username, ...vals];
if (vals.filter(v => v).length !== 2) {
return null;
}
return null;
const [password, rePassword] = ['password', 'rePassword'].map(k => this.field.getValue(k));
if (password !== rePassword) {
this.field.setError('rePassword', locale.rePasswordError2);
return null;
}
return [this.props.username, ...vals];
}
render() {
@ -76,6 +84,13 @@ class PasswordReset extends React.Component {
<FormItem label={locale.password} required help={getError('password')}>
<Input name="password" htmlType="password" placeholder={locale.passwordPlaceholder} />
</FormItem>
<FormItem label={locale.rePassword} required help={getError('rePassword')}>
<Input
name="rePassword"
htmlType="password"
placeholder={locale.rePasswordPlaceholder}
/>
</FormItem>
</Form>
</Dialog>
</>

View File

@ -14,6 +14,7 @@
import React from 'react';
import { Button, ConfigProvider, Dialog, Field, Form, Input, Loading, Tab } from '@alifd/next';
import { getParams, request } from '../../../globalLib';
import { generateUrl } from '../../../utils/nacosutil';
import './index.scss';
import PropTypes from 'prop-types';
@ -140,9 +141,12 @@ class ConfigDetail extends React.Component {
goList() {
this.props.history.push(
`/configurationManagement?serverId=${this.serverId}&group=${this.searchGroup}&dataId=${
this.searchDataId
}&namespace=${this.tenant}`
generateUrl('/configurationManagement', {
serverId: this.serverId,
group: this.searchGroup,
dataId: this.searchDataId,
namespace: this.tenant,
})
);
}

View File

@ -14,6 +14,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import { getParams } from '../../../globalLib';
import { generateUrl } from '../../../utils/nacosutil';
import request from '../../../utils/request';
import validateContent from 'utils/validateContent';
import SuccessDialog from '../../../components/SuccessDialog';
@ -96,7 +97,7 @@ class ConfigEditor extends React.Component {
dataId: getParams('dataId').trim(),
group,
},
() =>
() => {
this.getConfig(true).then(res => {
if (!res) {
this.getConfig();
@ -107,7 +108,8 @@ class ConfigEditor extends React.Component {
tabActiveKey: 'beta',
betaPublishSuccess: true,
});
})
});
}
);
} else {
if (group) {
@ -315,11 +317,11 @@ class ConfigEditor extends React.Component {
goBack() {
const serverId = getParams('serverId') || '';
const tenant = getParams('namespace');
const searchGroup = getParams('searchGroup') || '';
const searchDataId = getParams('searchDataId') || '';
const namespace = getParams('namespace');
const group = getParams('searchGroup') || '';
const dataId = getParams('searchDataId') || '';
this.props.history.push(
`/configurationManagement?serverId=${serverId}&group=${searchGroup}&dataId=${searchDataId}&namespace=${tenant}`
generateUrl('/configurationManagement', { serverId, group, dataId, namespace })
);
}

View File

@ -14,6 +14,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import { getParams, request } from '../../../globalLib';
import { generateUrl } from '../../../utils/nacosutil';
import { Button, ConfigProvider, Dialog, Field, Form, Input } from '@alifd/next';
import './index.scss';
@ -96,11 +97,10 @@ class ConfigRollback extends React.Component {
}
goList() {
const tenant = getParams('namespace');
const namespace = getParams('namespace');
const { serverId, dataId, group } = this;
this.props.history.push(
`/historyRollback?serverId=${this.serverId}&group=${this.group}&dataId=${
this.dataId
}&namespace=${tenant}`
generateUrl('/historyRollback', { serverId, dataId, group, namespace })
);
}

View File

@ -16,6 +16,7 @@ import PropTypes from 'prop-types';
import { Button, Checkbox, ConfigProvider, Dialog, Field, Form, Input, Loading } from '@alifd/next';
import SuccessDialog from '../../../components/SuccessDialog';
import { getParams, request } from '../../../globalLib';
import { generateUrl } from '../../../utils/nacosutil';
import './index.scss';
@ -91,13 +92,9 @@ class ConfigSync extends React.Component {
const { locale = {} } = this.props;
this.tenant = getParams('namespace') || '';
this.serverId = getParams('serverId') || 'center';
let url = `/diamond-ops/configList/detail/serverId/${this.serverId}/dataId/${
this.dataId
}/group/${this.group}/tenant/${this.tenant}?id=`;
let url = `/diamond-ops/configList/detail/serverId/${this.serverId}/dataId/${this.dataId}/group/${this.group}/tenant/${this.tenant}?id=`;
if (this.tenant === 'global' || !this.tenant) {
url = `/diamond-ops/configList/detail/serverId/${this.serverId}/dataId/${this.dataId}/group/${
this.group
}?id=`;
url = `/diamond-ops/configList/detail/serverId/${this.serverId}/dataId/${this.dataId}/group/${this.group}?id=`;
}
request({
url,
@ -168,9 +165,7 @@ class ConfigSync extends React.Component {
request({
type: 'put',
contentType: 'application/json',
url: `/diamond-ops/configList/serverId/${this.serverId}/dataId/${payload.dataId}/group/${
payload.group
}?id=`,
url: `/diamond-ops/configList/serverId/${this.serverId}/dataId/${payload.dataId}/group/${payload.group}?id=`,
data: JSON.stringify(payload),
success(res) {
const _payload = {};
@ -193,7 +188,7 @@ class ConfigSync extends React.Component {
const dataId = this.field.getValue('dataId');
const gruop = this.field.getValue('group');
this.props.history.push(
`/diamond-ops/static/pages/config-sync/index.html?serverId=center&dataId=${dataId}&group=${gruop}`
generateUrl('/diamond-ops/static/pages/config-sync/index.html', { dataId, gruop })
);
}
@ -209,9 +204,8 @@ class ConfigSync extends React.Component {
}
goResult() {
this.props.history.push(
`/consistencyEfficacy?serverId=${this.serverId}&dataId=${this.dataId}&group=${this.group}`
);
const { serverId, dataId, group } = this;
this.props.history.push(generateUrl('/consistencyEfficacy', { serverId, dataId, group }));
}
openLoading() {

View File

@ -133,7 +133,8 @@ class ConfigurationManagement extends React.Component {
<div>
<div style={{ fontSize: '15px', lineHeight: '22px' }}>
{locale.ad}
<a href={'https://survey.aliyun.com/survey/k0BjJ2ARC'} target={'_blank'}>
{/* eslint-disable */}
<a href="https://survey.aliyun.com/survey/k0BjJ2ARC" target="_blank">
{locale.questionnaire2}
</a>
</div>
@ -438,21 +439,12 @@ class ConfigurationManagement extends React.Component {
isPageEnter: e.keyCode && e.keyCode === 13,
currentPage: value,
},
() => {
this.getData(value, false);
}
() => this.getData(value, false)
);
}
handlePageSizeChange(pageSize) {
this.setState(
{
pageSize,
},
() => {
this.changePage(1);
}
);
this.setState({ pageSize }, () => this.changePage(1));
}
onInputUpdate() {}
@ -690,7 +682,7 @@ class ConfigurationManagement extends React.Component {
}
exportData() {
let url = `v1/cs/configs?export=true&group=${this.group}&tenant=${getParams(
const url = `v1/cs/configs?export=true&group=${this.group}&tenant=${getParams(
'namespace'
)}&appName=${this.appName}&ids=&dataId=${this.dataId}`;
window.location.href = url;

View File

@ -46,10 +46,11 @@ class DashboardCard extends React.Component {
</strong>
<strong>
<span>
{/* eslint-disable */}
<a
style={{ marginLeft: 10, color: '#33cde5' }}
href={item.url}
target={'_blank'}
target="_blank"
>
{locale.viewDetails1}
</a>

View File

@ -81,9 +81,7 @@ class HistoryDetail extends React.Component {
goList() {
this.props.history.push(
`/historyRollback?serverId=${this.serverId}&group=${this.group}&dataId=${
this.dataId
}&namespace=${this.tenant}`
`/historyRollback?serverId=${this.serverId}&group=${this.group}&dataId=${this.dataId}&namespace=${this.tenant}`
);
}

View File

@ -133,9 +133,7 @@ class HistoryRollback extends React.Component {
beforeSend() {
self.openLoading();
},
url: `v1/cs/history?search=accurate&dataId=${this.dataId}&group=${
this.group
}&&pageNo=${pageNo}&pageSize=${this.state.pageSize}`,
url: `v1/cs/history?search=accurate&dataId=${this.dataId}&group=${this.group}&&pageNo=${pageNo}&pageSize=${this.state.pageSize}`,
success(data) {
if (data != null) {
self.setState({

View File

@ -192,9 +192,7 @@ class NewConfig extends React.Component {
this.tenant = getParams('namespace') || '';
this.serverId = getParams('serverId') || '';
this.props.history.push(
`/configurationManagement?serverId=${this.serverId}&group=${this.searchGroup}&dataId=${
this.searchDataId
}&namespace=${this.tenant}`
`/configurationManagement?serverId=${this.serverId}&group=${this.searchGroup}&dataId=${this.searchDataId}&namespace=${this.tenant}`
);
}

View File

@ -24,6 +24,13 @@ class Login extends React.Component {
this.field = new Field(this);
}
componentDidMount() {
if (localStorage.getItem('token')) {
const [baseUrl] = location.href.split('#');
location.href = `${baseUrl}#/`;
}
}
handleSubmit = () => {
const { locale = {} } = this.props;
this.field.validate((errors, values) => {
@ -35,11 +42,11 @@ class Login extends React.Component {
localStorage.setItem('token', JSON.stringify(res));
this.props.history.push('/');
})
.catch(() =>
.catch(() => {
Message.error({
content: locale.invalidUsernameOrPassword,
})
);
});
});
});
};

View File

@ -27,6 +27,7 @@ class Password extends React.Component {
static propTypes = {
locale: PropTypes.object,
history: PropTypes.object,
};
constructor(props) {

View File

@ -37,10 +37,7 @@ const FormItem = Form.Item;
const { Row, Col } = Grid;
const { Column } = Table;
@connect(
state => ({ subscriberData: state.subscribers }),
{ getSubscribers, removeSubscribers }
)
@connect(state => ({ subscriberData: state.subscribers }), { getSubscribers, removeSubscribers })
@ConfigProvider.config
class SubscriberList extends React.Component {
static displayName = 'SubscriberList';

View File

@ -46,3 +46,18 @@ export const getParameter = (search, name) => {
const [, value = ''] = hit.split('=');
return value;
};
export const isJsonString = str => {
try {
if (typeof JSON.parse(str) === 'object') {
return true;
}
} catch (e) {}
return false;
};
export const generateUrl = (url, params) => {
return [url, '?', Object.keys(params).map(key => [key, params[key].join('=')].join('&'))].join(
''
);
};

View File

@ -1,6 +1,7 @@
import axios from 'axios';
import qs from 'qs';
import { Message } from '@alifd/next';
import { browserHistory } from 'react-router';
// import { SUCCESS_RESULT_CODE } from '../constants';
const API_GENERAL_ERROR_MESSAGE = 'Request error, please try again later!';
@ -16,6 +17,7 @@ const request = () => {
if (!config.url.includes('auth/users/login')) {
const { accessToken = '' } = JSON.parse(localStorage.token || '{}');
config.params.accessToken = accessToken;
config.headers = Object.assign({}, config.headers, { accessToken });
}
if (['post', 'put'].includes(config.method)) {
config.data = qs.stringify(config.data);
@ -41,10 +43,15 @@ const request = () => {
error => {
if (error.response) {
const { data, status } = error.response;
Message.error(data && typeof data === 'string' ? data : `HTTP ERROR: ${status}`);
if (status !== 403) {
Message.error(data && typeof data === 'string' ? data : `HTTP ERROR: ${status}`);
}
} else {
Message.error(API_GENERAL_ERROR_MESSAGE);
}
localStorage.removeItem('token');
const [baseUrl] = location.href.split('#');
location.href = `${baseUrl}#/login`;
return Promise.reject(error);
}
);