Merge branch 'codeCheck' of https://git.mesalab.cn/nezha/nezha-fronted into codeCheck

This commit is contained in:
zhangyu
2021-03-18 11:00:24 +08:00
7 changed files with 656 additions and 15 deletions

View File

@@ -444,6 +444,16 @@ li{
.el-textarea__inner {
padding: 5px 60px 5px 15px;
}
.el-radio__input .el-radio__inner:hover {
border-color: $btn-normal-background-color-active-new;
}
.el-radio__input.is-checked .el-radio__inner {
border-color: $btn-normal-background-color-active-new;
background: $btn-normal-background-color-active-new;
}
.el-radio__input.is-checked+.el-radio__label{
color: $btn-normal-background-color-active-new;;
}
.nz-input-append {
position: absolute;
right: 8px;

View File

@@ -122,7 +122,8 @@
<el-menu-item index="101-1">
<div :style="language=='cn'?'color:#f90':''" @click="changeLocal('cn')" id="header-to-chinese">中文</div>
</el-menu-item>
<el-menu-item class="nz-menu-line" @click.stop index="6-2">
<template v-if="username">
<el-menu-item class="nz-menu-line" @click.stop index="6-2" >
<div style="height: 1px; width: 100%; background-color: #cccccc;"></div>
</el-menu-item>
<el-menu-item index="101-3">
@@ -131,6 +132,7 @@
<el-menu-item index="101-4">
<div @click="logout" id="header-to-logout">{{$t('overall.signOut')}}</div>
</el-menu-item>
</template>
</el-submenu>
</el-menu>
@@ -511,11 +513,13 @@
},
mounted() {
this.$i18n.locale = this.language;
if (sessionStorage.getItem('nz-token')) {
this.initEvent();
this.getAssetData();
this.getUserData();
this.getProjectList();
this.getLinkData();
}
// 刷新后有高亮
/*let activePath = this.$route.path.slice(1);
this.activeIndex = activePath;*/

View File

@@ -93,6 +93,56 @@ const cn = {
},
SyncSave:'保存同时更新 Assets'
},
setup:{
step0:"欢迎",
step1:"数据库",
step2:"Redis",
step3:"系统",
host:"Host",
port:"端口",
language:"语言",
welcome:{
header:"欢迎使用哪吒安装向导!",
guid:"向导将为您做什么?",
guid_1:"创建基本的配置",
guid_2:"尝试在数据库和Redis设置中查找问题",
toContinue:"继续",
creatFile:"出于安全原因,您需要通过创建文件来验证安装",
createFileTip:"这可以通过执行以下命令来完成",
next:"完成后单击“下一步”按钮."
},
database:{
configTitle:'配置数据库连接',
configTip:"请手动创建数据库,并设置连接到此数据库的配置参数,完成后按“下一步”按钮",
},
redis:{
configTitle:'配置Redis连接',
configTip:"请设置连接到此redis的配置参数完成后按“下一步”按钮",
},
system:{
configTitle:'系统配置',
configTip:"请输入管理员的用户名和密码并设置Nezha WEB模块的安装信息如IP:port"
},
name:"数据库名称",
username:"用户名",
password:"密码",
alertPath:'Alert API',
alertPrefix:'Alert prefix',
haMode:'HA mode',
haVip:'Virtual IP',
next:"下一步",
back:"返回",
finish:"完成",
invalidDb:"数据库配置可能存在一些错误",
invalidRedis:"Redis的配置可能有一些错误",
invalidPassword:"Redis的密码可能错误",
requirePassword:"Redis可能需要密码",
wait:"配置已保存,请耐心等待生效",
reloadTimeout:'重新启动服务器花了太多时间,安装可能有一些问题',
hadConfig:"已经有人开始配置系统",
invalidCode:"身份验证无效,请按照{page}中的描述继续",
welcomePage:"欢迎页面"
},
webshell: {
shellTitle: "本地 Shell",
upload: "上传",

View File

@@ -99,6 +99,56 @@ const en = {
SyncSave:'Save&Sync'
},
pageSize: '/page',
setup:{
step0:"Welcome",
step1:"Database",
step2:"Redis",
step3:"System",
host:"Host",
port:"Port",
language:"Language",
welcome:{
header:"Welcome to Nezha setup wizard!",
guid:"What will the wizard do for you?",
guid_1:"Create a basic configuration",
guid_2:"Tries to find problems within your Database and Redis setup",
toContinue:"To continue",
creatFile:"For security reasons you need to authenticate for the installation by creating the file",
createFileTip:"This can be done by executing the following command",
next:"Click the 'Next' button when you've finished."
},
database:{
configTitle:'Configure DB connection',
configTip:"Please create database manually,and set the configuration parameters for connection to this database,Press 'Next' button when done",
},
redis:{
configTitle:'Configure Redis connection',
configTip:"Please set the configuration parameters for connection to this redis,Press 'Next' button when done",
},
system:{
configTitle:'System configuration',
configTip:"Please enter username and password for administrator ,and set the Nezha WEB module install information,like IP:port"
},
name:"Database name",
username:"Username",
password:"Password",
alertPath:'Alert API',
alertPrefix:'Alert prefix',
haMode:'HA mode',
haVip:'Virtual IP',
next:"Next",
back:"Back",
finish:"Finish",
invalidDb:"There may be some errors in the configuration of the database",
invalidRedis:"There may be some errors in the configuration of the Redis",
invalidPassword:"Redis's password may be wrong",
requirePassword:"The password may be required by the Redis",
wait:"The configuration has been saved, please wait patiently for it to take effect",
reloadTimeout:'It took too much time to restart the server, there may be some problems when you install',
hadConfig:"Someone has started to configure the system",
invalidCode:"The authentication is invalid ,please follow the description in {page} 'To continue'",
welcomePage:"Welcome page"
},
webshell:{
shellTitle:'Local Shell',
upload:'Upload',

View File

@@ -0,0 +1,523 @@
<template>
<div class="setup">
<div class="logo-header">
<img height="100px" src="../../../assets/img/logo-big.png">
</div>
<div style="display: flex">
<div class="step-box">
<div class="step-inner">
<el-steps direction="vertical" :active="step" finish-status="success">
<el-step :title="$t('setup.step0')" ></el-step>
<el-step :title="$t('setup.step1')" ></el-step>
<el-step :title="$t('setup.step2')"></el-step>
<el-step :title="$t('setup.step3')" ></el-step>
</el-steps>
</div>
</div>
<div class="setup-box">
<div style="position: relative; height: 50px">
<el-menu mode="horizontal" class="language-select">
<el-submenu index="1">
<template slot="title">{{$t('setup.language')}}</template>
<el-menu-item index="1">
<div :style="language=='en'?'color:#f90':''" @click="changeLocal('en')" id="header-to-english">English</div>
</el-menu-item>
<el-menu-item index="2">
<div :style="language=='cn'?'color:#f90':''" @click="changeLocal('cn')" id="header-to-chinese">中文</div>
</el-menu-item>
</el-submenu>
</el-menu>
</div>
<div class="setup-inner">
<template v-if="activeStep == 0">
<div class="welcome">
<div class="wel-header">
<!--Welcome to Nezha setup wizard!-->
{{$t('setup.welcome.header')}}
</div>
<div class="content-divider"/>
<div class="wel-wizard">
<div class="wizard-header"><!--What will the wizard do for you?-->{{$t('setup.welcome.guid')}}</div>
<ul>
<li><!--Create a basic, single site configuration-->{{$t('setup.welcome.guid_1')}}</li>
<li><!--Tries to find problems within your Database and Redis setup-->{{$t('setup.welcome.guid_2')}}</li>
</ul>
</div>
<div class="content-divider"/>
<div class="wel-continue">
<div class="wizard-header"><!--To continue-->{{$t('setup.welcome.toContinue')}}:</div>
<div>
<!--For security reasons you need to authenticate for the installation by creating the file-->{{$t('setup.welcome.creatFile')}} '/opt/nezha/nz-web/tmp/nezha.auth'.<br/><!--This can be done by executing the following command-->{{$t('setup.welcome.createFileTip')}}:
</div>
<pre>echo -n {{validateCode}} /opt/nezha/nz-web/tmp/nezha.auth</pre>
<div><!--Click the 'Next' button when you've finished.-->{{$t('setup.welcome.next')}}</div>
</div>
</div>
</template>
<template v-if="activeStep == 1">
<div class="setup-config">
<el-form ref="db-form" :model="database" label-width="80px" :rules="dbRules" label-position="top" size="small" style="width: 600px" :validate-on-rule-change="false">
<el-form-item :label="$t('setup.host')" prop="host" key="dbhost">
<el-input v-model="database.host"></el-input>
</el-form-item>
<el-form-item :label="$t('setup.port')" prop="port" key="dbport">
<el-input v-model="database.port"></el-input>
</el-form-item>
<el-form-item :label="$t('setup.name')" prop="name" key="dbname">
<el-input v-model="database.name"></el-input>
</el-form-item>
<el-form-item :label="$t('setup.username')" prop="username" key="dbusername">
<el-input v-model="database.username"></el-input>
</el-form-item>
<el-form-item :label="$t('setup.password')" prop="password" key="dbpassword">
<el-input v-model="database.password" type="password" show-password></el-input>
</el-form-item>
</el-form>
<div class="setup-help">
<div class="help-header">
{{$t('setup.database.configTitle')}}
</div>
<div class="help-body">
{{$t('setup.database.configTip')}}
</div>
</div>
</div>
</template>
<template v-if="activeStep == 2">
<div class="setup-config">
<el-form ref="redis-form" :model="redis" label-width="80px" :rules="redisRules" label-position="top" size="small" style="width: 600px" :validate-on-rule-change="false">
<el-form-item :label="$t('setup.host')" prop="host" key="rdhost">
<el-input v-model="redis.host"></el-input>
</el-form-item>
<el-form-item :label="$t('setup.port')" prop="port" key="rdport">
<el-input v-model="redis.port"></el-input>
</el-form-item>
<el-form-item :label="$t('setup.password')" prop="password" key="rdpassword">
<el-input v-model="redis.password" type="password" show-password></el-input>
</el-form-item>
</el-form>
<div class="setup-help">
<div class="help-header">
{{$t('setup.redis.configTitle')}}
</div>
<div class="help-body">
{{$t('setup.redis.configTip')}}
</div>
</div>
</div>
</template>
<template v-if="activeStep == 3">
<div class="setup-config">
<el-form ref="sys-form" :model="system" label-width="80px" :rules="sysRules" label-position="top" size="small" style="width: 600px" :validate-on-rule-change="false">
<el-form-item :label="$t('setup.username')" prop="host" key="syshost">
<el-input v-model="system.username"></el-input>
</el-form-item>
<el-form-item :label="$t('setup.password')" prop="password" key="syspassword">
<el-input v-model="system.password" type="password" show-password></el-input>
</el-form-item>
<el-form-item :label="$t('setup.alertPath')" prop="alertPath" key="sysalertPath">
<el-input v-model="system.alertPath" ></el-input>
</el-form-item>
<!-- <el-form-item :label="$t('setup.alertPrefix')" prop="alertPrefix" key="sysalertPrefix">
<el-input v-model="system.alertPrefix" ></el-input>
</el-form-item>
<el-form-item :label="$t('setup.haMode')" prop="haMode" key="syshaMode">
<el-radio v-model="system.haMode" :label="1" >HA deploy</el-radio>
<el-radio v-model="system.haMode" :label="2" >Simple deploy</el-radio>
</el-form-item>
<el-form-item :label="$t('setup.haVip')" prop="haVip" v-if="system.haMode == 2" key="sysHavip">
<el-input v-model="system.haVip" ></el-input>
</el-form-item>-->
</el-form>
<div class="setup-help">
<div class="help-header">
{{$t('setup.system.configTitle')}}
</div>
<div class="help-body">
{{$t('setup.system.configTip')}}
</div>
</div>
</div>
</template>
<div class="setup-bottom-btn">
<button @click="preStep" class="nz-btn nz-btn-size-normal-new nz-btn-style-normal-new" v-if="activeStep != 0">
<span>{{$t('setup.back')}}</span>
</button>
<button @click="nextStep" class="nz-btn nz-btn-size-normal-new nz-btn-style-normal-new" :disabled="prevent_next" v-if="activeStep != 3" :class="{'nz-btn-disabled':prevent_next}">
<span>{{$t('setup.next')}}</span>
</button>
<button @click="finishStep" class="nz-btn nz-btn-size-normal-new nz-btn-style-normal-new" :disabled="prevent_finish" v-if="activeStep == 3" :class="{'nz-btn-disabled':prevent_finish}">
<span>{{$t('setup.finish')}}</span>
</button>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import {getUUID} from "../../common/js/common";
import {host, port} from "../../common/js/validate";
export default {
name: "setup",
data(){
return{
validateCode:"",
language:"en",
step:3,
activeStep:3,
database:{
host:"",
port:3306,
name:'nz',
username:"",
password:"",
},
dbRules:{
host:[
{required:true,message:this.$t('validate.required'),trigger: 'blur'},
{validator:host, trigger: 'blur'}
],
port:[{validator:port,trigger:'blur'}],
name:[{required:true,message:this.$t('validate.required'),trigger: 'blur'}],
username:[{required:true,message:this.$t('validate.required'),trigger: 'blur'}],
password:[{required:true,message:this.$t('validate.required'),trigger: 'blur'}],
},
redis:{
host:"",
port:6379,
password:"",
},
redisRules:{
host:[
{required:true,message:this.$t('validate.required'),trigger: 'blur'},
{validator:host, trigger: 'blur'}
],
port:[{validator:port,trigger:'blur'}],
},
system:{
username:"",
password:"",
alertPath:"",
alertPrefix:"",
haMode:1,
haVip:"",
},
sysRules:{
username:[{required:true,message:this.$t('validate.required'),trigger: 'blur'}],
password:[{required:true,message:this.$t('validate.required'),trigger: 'blur'}],
alertPath:[{required:true,message:this.$t('validate.required'),trigger: 'blur'}],
haVip:[{required:true,message:this.$t('validate.required'),trigger: 'blur'}],
},
reloadTime:5000,
reloadTimeout:null,
reloadTimeCount:0,
prevent_next:false,
prevent_finish:false,
}
},
created() {
this.getValidateCode();
},
methods:{
changeStep:function(step){
switch (step) {
case 0:
this.activeStep = 0;
break;
case 1:
if(this.prevent_next){return}
this.prevent_next=true
if(this.activeStep > 1){
this.activeStep = 1;
this.prevent_next=false;
}else{
this.getValidateCode();
this.$get("setup/checkCode?code="+this.validateCode).then(response=>{
if(response.code == 200 ){
this.activeStep = 1;
this.step = 1;
}else{
this.activeStep = 0;
this.step = 0;
if(response.code == 574012){
this.$alert(this.$t('setup.hadConfig'),{type:"warning"})
}else{
this.$alert(this.$t('setup.invalidCode',{page:''}),{type:"warning"})
}
}
this.prevent_next=false;
})
}
break;
case 2:
if(this.prevent_next){return}
this.prevent_next=true
if(this.activeStep > 2){
this.activeStep = 2
this.prevent_next=false;
}else {
this.$refs['db-form'].validate((valid) => {
if (valid) {
this.getValidateCode();
this.$post("setup/checkDb", {database:this.database,code:this.validateCode}).then(response => {
if (response.code == 200) {
this.activeStep = 2;
this.step = 2;
} else {
this.activeStep = 1;
this.step = 1;
if(response.code == 574002){
this.$alert(this.$t('setup.invalidCode',{page:this.$t('setup.welcomePage')}),{type:"warning"})
}else{
this.$alert(this.$t('setup.invalidDb'),{type:"warning"})
}
}
this.prevent_next=false;
})
}else{
this.prevent_next=false;
}
})
}
break;
case 3:
if(this.prevent_next){return}
this.prevent_next=true
if(this.activeStep > 3){
this.activeStep =3
this.prevent_next=false;
}else{
this.$refs['redis-form'].validate((valid) => {
if (valid) {
this.getValidateCode();
this.$post("setup/checkRedis",{redis:this.redis,code:this.validateCode}).then(response=>{
if(response.code == 200 ){
this.activeStep = 3;
this.step = 3;
}else{
this.activeStep = 2;
this.step = 2;
if(response.code == 574004 ){ //密码无效
this.$alert(this.$t('setup.invalidPassword'),{type:"warning"})
}else if(response.code == 574005){
this.$alert(this.$t('setup.requirePassword'),{type:"warning"})
}else if(response.code == 574002){
this.$alert(this.$t('setup.invalidCode',{page:this.$t('setup.welcomePage')}),{type:"warning"})
}else{
this.$alert(this.$t('setup.invalidRedis'),{type:"warning"})
}
}
this.prevent_next=false;
})
}else{
this.prevent_next=false;
}
})
}
break;
case 4:
if(this.prevent_finish){return}
this.prevent_next=true
this.$refs['sys-form'].validate((valid) => {
if (valid) {
this.getValidateCode();
let params={
database:this.database,
redis:this.redis,
system:this.system,
code:this.validateCode
}
this.$post("setup/config",params).then(response=>{
if(response.code == 200 ){
this.activeStep = 3;
this.step = 4;
this.$alert(this.$t('setup.wait'),{type:"success"})
this.reloadTimeout = setTimeout(this.jumpToLogin,this.reloadTime)
}else{
this.activeStep = 3;
this.step = 3;
if(response.code == 574002){
this.$alert(this.$t('setup.invalidCode',{page:this.$t('setup.welcomePage')}),{type:"warning"})
}
}
})
}else{
this.prevent_next=false;
}
})
break;
}
},
jumpToLogin:function(){
this.reloadTimeCount+=this.reloadTime;
if(this.reloadTimeCount > 5*60*1000){
clearTimeout(this.reloadTimeout)
this.$alert(this.$t('setup.reloadTimeout'),{type:"warning"})
return;
}
this.$get("healthy").then(response=>{
if(response.code == 200){
this.$router.push({
path:"/"
})
clearTimeout(this.reloadTimeout)
}else{
this.reloadTimeout = setTimeout(this.jumpToLogin,this.reloadTime)
}
})
},
nextStep:function(){
this.changeStep(this.activeStep+1);
},
preStep:function(){
this.changeStep(this.activeStep-1);
},
finishStep:function(){
this.changeStep(4);
},
getValidateCode:function(){
const saveValidateCodeFunc = function (validateCode){
let saveItem = {
code:validateCode,
time:Date.now(),
expire:30*60*1000
}
localStorage.setItem("setup-validate-code",JSON.stringify(saveItem))
}
let validateCodeJSON = localStorage.getItem("setup-validate-code");
if(validateCodeJSON != 'undefined' && validateCodeJSON != null){
let validateCode = JSON.parse(validateCodeJSON);
if(Date.now() - validateCode.time > validateCode.expire){
this.validateCode = getUUID();
saveValidateCodeFunc(this.validateCode)
}else{
this.validateCode = validateCode.code;
}
}else{
this.validateCode = getUUID();
saveValidateCodeFunc(this.validateCode)
}
},
changeLocal:function(local){
this.language = local;
this.$i18n.locale = local;
}
},
}
</script>
<style scoped>
.setup{
/*display: flex;*/
width: 1250px;
height: 100%;
margin: 0 auto;
}
.logo-header{
padding-left: 20px;
width: 100%;
height: 100px;
}
.language-select{
position: absolute;
right: 20px;
height: 50px;
z-index: 2;
}
.setup .step-box{
width: 200px;
height: 100%;
}
.setup .setup-box{
width: calc(100% - 200px);
height: 100%;
}
.step-box .step-inner{
height: 600px;
padding:20px;
}
.setup-box .setup-inner{
position: relative;
width: calc(100% - 42px);
height: 600px;
padding: 20px;
border: 1px solid #EEEEEE;
}
.setup-inner .setup-config{
position: relative;
}
.setup-inner .setup-help{
width: calc(100% - 640px);
height: 500px;
position: absolute;
top: 20px;
right: 0px;
border-left: 1px solid #ddd;
padding-left: 10px;
}
.setup .setup-help .help-header{
font-weight: 800;
font-size: 30px;
}
.setup .setup-help .help-body{
line-height: 35px;
font-size: 16px;
}
.welcome{
line-height: 35px;
font-size: 16px;
}
.welcome .wel-header{
font-weight: 800;
font-size: 30px;
}
.wizard-header{
font-weight: 600;
font-size: 20px;
}
.welcome ul li{
list-style: inside !important;
}
.welcome pre{
border: 1px solid #ddd;
border-left: 4px solid #e6522c;
border-radius: 0;
font-family: "Courier New", Monaco, Menlo, Consolas, monospace;
background-color: #f5f5f5;
color: #333;
padding: 15px;
}
.welcome .content-divider{
height: 1px;
width: 100%;
border-bottom: 2px solid #C0C4CC;
margin: 5px 0px;
}
.setup-bottom-btn{
width: 100%;
height: 49px;
position: absolute;
bottom: 0;
right: 20px;
padding-top: 20px;
text-align: right;
}
</style>
<style>
.setup .el-menu--horizontal>.el-submenu.is-active .el-submenu__title {
border-bottom: unset !important;
color: #303133;
background-color:transparent;
}
.language-select .el-submenu__title{
height: 50px !important;
}
.language-select .el-submenu{
height: 50px !important;
}
</style>

View File

@@ -8,7 +8,7 @@ import VueResource from 'vue-resource'
Vue.use(VueResource);
const loginWhiteList = ['/login']; // 免登陆白名单
const loginWhiteList = ['/login','/setup']; // 免登陆白名单
const permissionWhiteList = ['/menu', ...loginWhiteList]; // 权限白名单
router.beforeEach((to, from, next) => {

View File

@@ -13,6 +13,10 @@ export default new Router({
path: '/login',
component: resolve => require(['../components/common/login.vue'], resolve),
},
{
path: '/setup',
component: resolve => require(['../components/page/config/setup.vue'], resolve),
},
{
path: '/',
component: resolve => require(['../components/common/home.vue'], resolve),