[ISSUE#11957] Console support init password for nacos username (#12148)

* feat:add register pagg

* fix:i18n

* console-ui build

* fix

* fix
This commit is contained in:
xpy01xpy 2024-05-30 14:53:41 +08:00 committed by GitHub
parent bc039bc125
commit 0a797223c0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 658 additions and 296 deletions

View File

@ -27,6 +27,23 @@ function goLogin() {
window.location = `${base_url}#/login`; window.location = `${base_url}#/login`;
} }
function goRegister() {
const url = window.location.href;
localStorage.removeItem('token');
const base_url = url.split('#')[0];
window.location = `${base_url}#/register`;
}
function generateRandomPassword(length) {
const charset = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
let password = '';
for (let i = 0; i < length; i++) {
const randomIndex = Math.floor(Math.random() * charset.length);
password += charset[randomIndex];
}
return password;
}
const global = window; const global = window;
/** /**
@ -564,5 +581,7 @@ export {
setParam, setParam,
setParams, setParams,
goLogin, goLogin,
goRegister,
generateRandomPassword,
request, request,
}; };

View File

@ -32,6 +32,7 @@ import Layout from './layouts/MainLayout';
import { LANGUAGE_KEY, REDUX_DEVTOOLS, THEME } from './constants'; import { LANGUAGE_KEY, REDUX_DEVTOOLS, THEME } from './constants';
import Login from './pages/Login'; import Login from './pages/Login';
import Register from './pages/Register';
import Namespace from './pages/NameSpace'; import Namespace from './pages/NameSpace';
import Newconfig from './pages/ConfigurationManagement/NewConfig'; import Newconfig from './pages/ConfigurationManagement/NewConfig';
import Configsync from './pages/ConfigurationManagement/ConfigSync'; import Configsync from './pages/ConfigurationManagement/ConfigSync';
@ -136,6 +137,7 @@ class App extends React.Component {
{loginPageEnabled && loginPageEnabled === 'false' ? null : ( {loginPageEnabled && loginPageEnabled === 'false' ? null : (
<Route path="/login" component={Login} /> <Route path="/login" component={Login} />
)} )}
<Route path="/register" component={Register} />
{/* <Route path="/login" component={Login} /> */} {/* <Route path="/login" component={Login} /> */}
<Layout> <Layout>
{consoleUiEnable && {consoleUiEnable &&

View File

@ -27,10 +27,12 @@ const I18N_CONF = {
}, },
Login: { Login: {
login: '登录', login: '登录',
initPassword: '初始化密码',
internalSysTip1: '内部系统不可暴露到公网', internalSysTip1: '内部系统不可暴露到公网',
submit: '提交', submit: '提交',
pleaseInputUsername: '请输入用户名', pleaseInputUsername: '请输入用户名',
pleaseInputPassword: '请输入密码', pleaseInputPassword: '请输入密码',
pleaseInputPasswordTips: '请输入密码若密码为空将使用随机密码',
invalidUsernameOrPassword: '用户名或密码错误', invalidUsernameOrPassword: '用户名或密码错误',
passwordRequired: '密码不能为空', passwordRequired: '密码不能为空',
usernameRequired: '用户名不能为空', usernameRequired: '用户名不能为空',

View File

@ -0,0 +1,160 @@
import React from 'react';
import { Card, Form, Input, Message, ConfigProvider, Field, Dialog } from '@alifd/next';
import { withRouter } from 'react-router-dom';
import './index.scss';
import Header from '../../layouts/Header';
import PropTypes from 'prop-types';
import { admin, guide, state } from '../../reducers/base';
import { connect } from 'react-redux';
import { generateRandomPassword } from '../../globalLib';
const FormItem = Form.Item;
@withRouter
@ConfigProvider.config
@connect(state => ({ ...state.locale }))
class Register extends React.Component {
static displayName = 'Register';
static propTypes = {
locale: PropTypes.object,
history: PropTypes.object,
};
constructor(props) {
super(props);
this.state = {
consoleUiEnable: true,
guideMsg: '',
};
this.field = new Field(this);
}
componentDidMount() {
if (localStorage.getItem('token')) {
const [baseUrl] = location.href.split('#');
location.href = `${baseUrl}#/`;
}
this.handleSearch();
}
handleSearch = () => {
state().then(res => {
if (res?.console_ui_enabled === 'false') {
this.setState({ consoleUiEnable: true });
guide().then(res => {
this.setState({ guideMsg: res?.data });
});
} else {
this.setState({ consoleUiEnable: false });
}
});
};
handleSubmit = () => {
const { locale = {} } = this.props;
this.field.validate((errors, values) => {
if (errors) {
return;
}
const data = {
password: generateRandomPassword(10),
...values
};
admin(data)
.then(res => {
if (res.username && res.password) {
localStorage.setItem('token', JSON.stringify(res));
Dialog.alert({
title: locale.Login.initPassword + locale.ListeningToQuery.success,
content: locale.Password.newPassword + '' + res.password,
onOk: () => {
this.props.history.push('/');
}
});
}
})
.catch(() => {
Message.error({
content: locale.Login.invalidUsernameOrPassword,
});
});
});
};
onKeyDown = event => {
// 'keypress' event misbehaves on mobile so we track 'Enter' key via 'keydown' event
if (event.key === 'Enter') {
event.preventDefault();
event.stopPropagation();
this.handleSubmit();
}
};
render() {
const { locale = {} } = this.props;
const { consoleUiEnable, guideMsg } = this.state;
return (
<div className="home-page">
<Header />
<section
className="top-section"
style={{
background: 'url(img/black_dot.png) repeat',
backgroundSize: '14px 14px',
}}
>
<div className="vertical-middle product-area">
<img className="product-logo" src="img/nacos.png" />
<p className="product-desc">{locale.Login.productDesc}</p>
</div>
<div className="animation animation1" />
<div className="animation animation2" />
<div className="animation animation3" />
<div className="animation animation4" />
<div className="animation animation5" />
<Card className="login-panel" contentHeight="auto">
<div className="login-header">{locale.Login.initPassword}</div>
<div className="internal-sys-tip">
<div>{locale.Login.internalSysTip1}</div>
<div>{locale.Login.internalSysTip2}</div>
</div>
{!consoleUiEnable && (
<Form className="login-form" field={this.field}>
<FormItem>
<Input
value="nacos"
readOnly
placeholder={locale.Login.pleaseInputUsername}
/>
</FormItem>
<FormItem>
<Input
htmlType="password"
placeholder={locale.Login.pleaseInputPasswordTips}
{...this.field.init('password', {})}
onKeyDown={this.onKeyDown}
/>
</FormItem>
<FormItem label=" ">
<Form.Submit onClick={this.handleSubmit}>{locale.Login.submit}</Form.Submit>
</FormItem>
</Form>
)}
{consoleUiEnable && (
<Message type="notice" style={{ marginTop: 30 }}>
<div dangerouslySetInnerHTML={{ __html: guideMsg }} />
</Message>
)}
</Card>
</section>
</div>
);
}
}
export default Register;

View File

@ -0,0 +1,4 @@
import Register from './Register';
export default Register;

View File

@ -0,0 +1,142 @@
/*!
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed 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.
*/
$animationDuration: 2s;
// 品牌色
$brandColor: #2e3034;
$mobileWidth: 640px;
// 页面主体最大宽度
$contentWidth: 1280px;
@keyframes slashStar {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
.home-page {
.top-section {
position: relative;
height: 100vh;
.login-panel {
position: absolute;
right: 40px;
width: 480px;
height: 540px;
top: 90px;
border: 0px;
input,
input::-webkit-input-placeholder {
font-size: 16px;
}
.login-header {
width: 100%;
line-height: 45px;
font-size: 32px;
margin-top: 58px;
text-align: center;
}
.internal-sys-tip {
width: 100%;
line-height: 25px;
font-size: 20px;
margin-top: 25px;
text-align: center;
font-weight: 800;
color: #ff0000cc;
}
.login-form {
width: 360px;
margin: 40px auto auto auto;
input {
height: 60px;
}
button {
width: 100%;
height: 60px;
font-size: 16px;
background: #4190ff 100%;
color: white;
border: 0px;
}
}
}
.animation {
position: absolute;
width: 6px;
height: 6px;
border-radius: 50%;
background-color: #1be1f6;
&1 {
left: 15%;
top: 70%;
animation: slashStar $animationDuration ease-in-out 0.3s infinite;
}
&2 {
left: 34%;
top: 35%;
animation: slashStar $animationDuration ease-in-out 1.2s infinite;
}
&3 {
left: 53%;
top: 20%;
animation: slashStar $animationDuration ease-in-out 0.5s infinite;
}
&4 {
left: 72%;
top: 64%;
animation: slashStar $animationDuration ease-in-out 0.8s infinite;
}
&5 {
left: 87%;
top: 30%;
animation: slashStar $animationDuration ease-in-out 1.5s infinite;
}
}
.vertical-middle {
position: absolute;
left: 0;
top: 50%;
margin-top: -47px;
transform: translateY(-50%);
}
.product-area {
width: 600px;
margin-left: 40px;
}
.product-logo {
display: block;
width: 257px;
height: 50px;
margin: 0;
}
.product-desc {
opacity: 0.8;
font-family: Avenir-Medium;
font-size: 24px;
color: #fff;
max-width: 780px;
margin: 12px auto 30px;
text-align: left;
line-height: 30px;
}
}
}

View File

@ -23,10 +23,18 @@ import { Redirect } from 'react-router-dom';
class Welcome extends React.Component { class Welcome extends React.Component {
static propTypes = { static propTypes = {
functionMode: PropTypes.string, functionMode: PropTypes.string,
authAdminRequest: PropTypes.string,
}; };
render() { render() {
const { functionMode } = this.props; const { functionMode, authAdminRequest } = this.props;
if (authAdminRequest && authAdminRequest === 'true') {
return (
<>
<Redirect to="/register" />
</>
);
}
const path = functionMode === 'naming' ? 'serviceManagement' : 'configurationManagement'; const path = functionMode === 'naming' ? 'serviceManagement' : 'configurationManagement';
return <>{functionMode !== '' && <Redirect to={`/${path}`} />}</>; return <>{functionMode !== '' && <Redirect to={`/${path}`} />}</>;
} }

View File

@ -25,6 +25,7 @@ const initialState = {
authEnabled: '', authEnabled: '',
notice: '', notice: '',
consoleUiEnable: '', consoleUiEnable: '',
authAdminRequest: '',
guideMsg: '', guideMsg: '',
}; };
@ -33,6 +34,7 @@ const initialState = {
* @param {*} param0 * @param {*} param0
*/ */
const login = user => request.post('v1/auth/users/login', user); const login = user => request.post('v1/auth/users/login', user);
const admin = user => request.post('v1/auth/users/admin', user);
/** /**
* 单独在login处调用 获取提示信息 * 单独在login处调用 获取提示信息
@ -57,6 +59,7 @@ const getState = () => dispatch =>
functionMode: res.function_mode, functionMode: res.function_mode,
loginPageEnabled: res.login_page_enabled, loginPageEnabled: res.login_page_enabled,
authEnabled: res.auth_enabled, authEnabled: res.auth_enabled,
authAdminRequest: res.auth_admin_request,
consoleUiEnable: res.console_ui_enabled, consoleUiEnable: res.console_ui_enabled,
startupMode: res.startup_mode, startupMode: res.startup_mode,
}, },
@ -72,6 +75,7 @@ const getState = () => dispatch =>
loginPageEnabled: null, loginPageEnabled: null,
authEnabled: null, authEnabled: null,
consoleUiEnable: null, consoleUiEnable: null,
authAdminRequest: null,
}, },
}); });
}); });
@ -129,4 +133,4 @@ export default (state = initialState, action) => {
} }
}; };
export { getState, login, getNotice, getGuide, guide, state }; export { getState, login, getNotice, getGuide, guide, state, admin };

File diff suppressed because one or more lines are too long

View File

@ -35,7 +35,7 @@
<link rel="stylesheet" type="text/css" href="console-ui/public/css/icon.css"> <link rel="stylesheet" type="text/css" href="console-ui/public/css/icon.css">
<link rel="stylesheet" type="text/css" href="console-ui/public/css/font-awesome.css"> <link rel="stylesheet" type="text/css" href="console-ui/public/css/font-awesome.css">
<!-- 第三方css结束 --> <!-- 第三方css结束 -->
<link href="./css/main.css?c02a3284f12026e72980" rel="stylesheet"></head> <link href="./css/main.css?42101e8cf76887c3acb3" rel="stylesheet"></head>
<body> <body>
<div id="root" style="overflow:hidden"></div> <div id="root" style="overflow:hidden"></div>
@ -56,6 +56,6 @@
<script src="console-ui/public/js/merge.js"></script> <script src="console-ui/public/js/merge.js"></script>
<script src="console-ui/public/js/loader.js"></script> <script src="console-ui/public/js/loader.js"></script>
<!-- 第三方js结束 --> <!-- 第三方js结束 -->
<script type="text/javascript" src="./js/main.js?c02a3284f12026e72980"></script></body> <script type="text/javascript" src="./js/main.js?42101e8cf76887c3acb3"></script></body>
</html> </html>

File diff suppressed because one or more lines are too long