mirror of
https://gitee.com/goploy/goploy.git
synced 2024-11-29 18:57:59 +08:00
update password when first login & add password period
This commit is contained in:
parent
1e1f5f883b
commit
d86ddd0e9d
@ -20,6 +20,7 @@ import (
|
||||
"github.com/zhenorzz/goploy/internal/model"
|
||||
"github.com/zhenorzz/goploy/internal/server"
|
||||
"github.com/zhenorzz/goploy/internal/server/response"
|
||||
"github.com/zhenorzz/goploy/internal/validator"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
@ -58,6 +59,7 @@ func (User) Login(gp *server.Goploy) server.Response {
|
||||
type ReqData struct {
|
||||
Account string `json:"account" validate:"required,min=1,max=25"`
|
||||
Password string `json:"password" validate:"required,password"`
|
||||
NewPassword string `json:"newPassword"`
|
||||
CaptchaKey string `json:"captchaKey" validate:"omitempty"`
|
||||
}
|
||||
var reqData ReqData
|
||||
@ -138,23 +140,23 @@ func (User) Login(gp *server.Goploy) server.Response {
|
||||
|
||||
} else {
|
||||
if userData.ID == 0 {
|
||||
return response.JSON{Code: response.Error, Message: "We couldn't verify your identity. Please confirm if your username and password are correct."}
|
||||
return response.JSON{Code: response.Error, Message: "We couldn't verify your identity. Please confirm if your username and password are correct"}
|
||||
}
|
||||
if err := userData.Validate(reqData.Password); err != nil {
|
||||
errorTimes := userCache.IncErrorTimes(reqData.Account, cache.UserCacheExpireTime, cache.UserCacheShowCaptchaTime)
|
||||
// error times over 5 times, then lock the account 15 minutes
|
||||
if errorTimes >= cache.UserCacheMaxErrorTimes {
|
||||
if userCache.IncrErrorTimes(reqData.Account, cache.UserCacheExpireTime) >= cache.UserCacheMaxErrorTimes {
|
||||
userCache.LockAccount(reqData.Account, cache.UserCacheLockTime)
|
||||
}
|
||||
return response.JSON{Code: response.Deny, Message: err.Error()}
|
||||
}
|
||||
}
|
||||
|
||||
userCache.DeleteShowCaptcha(reqData.Account)
|
||||
userCache.DeleteErrorTimes(reqData.Account)
|
||||
|
||||
if userData.State == model.Disable {
|
||||
return response.JSON{Code: response.AccountDisabled, Message: "Account is disabled"}
|
||||
}
|
||||
|
||||
namespaceList, err := model.Namespace{UserID: userData.ID}.GetAllByUserID()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
@ -162,6 +164,29 @@ func (User) Login(gp *server.Goploy) server.Response {
|
||||
return response.JSON{Code: response.Error, Message: "No space assigned, please contact the administrator"}
|
||||
}
|
||||
|
||||
if reqData.NewPassword != "" {
|
||||
if err := validator.Validate.Var(reqData.NewPassword, "password"); err != nil {
|
||||
return response.JSON{Code: response.PasswordExpired, Message: err.Error()}
|
||||
}
|
||||
if reqData.Password == reqData.NewPassword {
|
||||
return response.JSON{Code: response.PasswordExpired, Message: "The password cannot be the same as the previous one"}
|
||||
}
|
||||
if err := (model.User{ID: userData.ID, Password: reqData.NewPassword, PasswordUpdateTime: sql.NullString{
|
||||
String: time.Now().Format("20060102150405"),
|
||||
Valid: true,
|
||||
}}).UpdatePassword(); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
} else if !userData.PasswordUpdateTime.Valid {
|
||||
return response.JSON{Code: response.PasswordExpired, Message: "You need to change your password upon first login"}
|
||||
} else if config.Toml.APP.PasswordPeriod > 0 {
|
||||
passwordUpdateTime, _ := time.Parse(time.DateTime, userData.PasswordUpdateTime.String)
|
||||
passwordUpdateTime = passwordUpdateTime.AddDate(0, 0, config.Toml.APP.PasswordPeriod)
|
||||
if passwordUpdateTime.Before(time.Now()) {
|
||||
return response.JSON{Code: response.PasswordExpired, Message: "Password expired, please change"}
|
||||
}
|
||||
}
|
||||
|
||||
token, err := userData.CreateToken()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
@ -482,7 +507,14 @@ func (User) ChangePassword(gp *server.Goploy) server.Response {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
if err := (model.User{ID: gp.UserInfo.ID, Password: reqData.NewPassword}).UpdatePassword(); err != nil {
|
||||
if reqData.OldPassword == reqData.NewPassword {
|
||||
return response.JSON{Code: response.Error, Message: "The password cannot be the same as the previous one."}
|
||||
}
|
||||
|
||||
if err := (model.User{ID: gp.UserInfo.ID, Password: reqData.NewPassword, PasswordUpdateTime: sql.NullString{
|
||||
String: time.Now().Format("20060102150405"),
|
||||
Valid: true,
|
||||
}}).UpdatePassword(); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
return response.JSON{}
|
||||
|
@ -37,6 +37,7 @@ type APPConfig struct {
|
||||
DeployLimit int32 `toml:"deployLimit"`
|
||||
ShutdownTimeout time.Duration `toml:"shutdownTimeout"`
|
||||
RepositoryPath string `toml:"repositoryPath"`
|
||||
PasswordPeriod int `toml:"passwordPeriod"`
|
||||
}
|
||||
|
||||
type CORSConfig struct {
|
||||
|
2
database/1.16.2.sql
Normal file
2
database/1.16.2.sql
Normal file
@ -0,0 +1,2 @@
|
||||
alter table user
|
||||
add password_update_time datetime null after password;
|
@ -247,6 +247,7 @@ CREATE TABLE IF NOT EXISTS `user` (
|
||||
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`account` varchar(30) NOT NULL DEFAULT '',
|
||||
`password` varchar(60) NOT NULL DEFAULT '',
|
||||
`password_update_time` datetime DEFAULT NULL,
|
||||
`name` varchar(30) NOT NULL DEFAULT '',
|
||||
`contact` varchar(255) NOT NULL DEFAULT '',
|
||||
`state` tinyint(1) NOT NULL DEFAULT '1' COMMENT '0.disable 1.enable',
|
||||
|
@ -3,6 +3,8 @@ env = 'production'
|
||||
deployLimit = 8
|
||||
shutdownTimeout = 10
|
||||
repositoryPath = ''
|
||||
# How many days before the password needs to be changed?
|
||||
passwordPeriod = 0
|
||||
|
||||
[cors]
|
||||
enabled = false
|
||||
|
25
internal/cache/memory/user.go
vendored
25
internal/cache/memory/user.go
vendored
@ -8,7 +8,6 @@ import (
|
||||
const (
|
||||
UserCacheKey = "login_error_times_"
|
||||
UserCacheLockKey = "login_lock_"
|
||||
UserCacheShowCaptchaKey = "login_show_captcha_"
|
||||
)
|
||||
|
||||
type UserCache struct {
|
||||
@ -18,6 +17,7 @@ type UserCache struct {
|
||||
|
||||
type user struct {
|
||||
times int
|
||||
data any
|
||||
expireIn time.Time
|
||||
}
|
||||
|
||||
@ -25,7 +25,7 @@ var userCache = &UserCache{
|
||||
data: make(map[string]user),
|
||||
}
|
||||
|
||||
func (uc *UserCache) IncErrorTimes(account string, expireTime time.Duration, showCaptchaTime time.Duration) int {
|
||||
func (uc *UserCache) IncrErrorTimes(account string, expireTime time.Duration) int {
|
||||
uc.Lock()
|
||||
defer uc.Unlock()
|
||||
|
||||
@ -47,16 +47,6 @@ func (uc *UserCache) IncErrorTimes(account string, expireTime time.Duration, sho
|
||||
delete(uc.data, cacheKey)
|
||||
})
|
||||
|
||||
// show captcha
|
||||
showCaptchaKey := getShowCaptchaKey(account)
|
||||
uc.data[showCaptchaKey] = user{
|
||||
times: 1,
|
||||
expireIn: time.Now().Add(showCaptchaTime),
|
||||
}
|
||||
time.AfterFunc(showCaptchaTime, func() {
|
||||
delete(uc.data, showCaptchaKey)
|
||||
})
|
||||
|
||||
return times
|
||||
}
|
||||
|
||||
@ -97,17 +87,16 @@ func (uc *UserCache) IsShowCaptcha(account string) bool {
|
||||
uc.RLock()
|
||||
defer uc.RUnlock()
|
||||
|
||||
showCaptchaKey := getShowCaptchaKey(account)
|
||||
v, ok := uc.data[showCaptchaKey]
|
||||
v, ok := uc.data[getCacheKey(account)]
|
||||
|
||||
return ok && !v.expireIn.IsZero() && v.expireIn.After(time.Now()) && v.times > 0
|
||||
}
|
||||
|
||||
func (uc *UserCache) DeleteShowCaptcha(account string) {
|
||||
func (uc *UserCache) DeleteErrorTimes(account string) {
|
||||
uc.Lock()
|
||||
defer uc.Unlock()
|
||||
|
||||
delete(uc.data, getShowCaptchaKey(account))
|
||||
delete(uc.data, getCacheKey(account))
|
||||
}
|
||||
|
||||
func getCacheKey(account string) string {
|
||||
@ -118,10 +107,6 @@ func getLockKey(account string) string {
|
||||
return UserCacheLockKey + account
|
||||
}
|
||||
|
||||
func getShowCaptchaKey(account string) string {
|
||||
return UserCacheShowCaptchaKey + account
|
||||
}
|
||||
|
||||
func GetUserCache() *UserCache {
|
||||
return userCache
|
||||
}
|
||||
|
5
internal/cache/user.go
vendored
5
internal/cache/user.go
vendored
@ -3,16 +3,15 @@ package cache
|
||||
import "time"
|
||||
|
||||
type User interface {
|
||||
IncErrorTimes(account string, expireTime time.Duration, showCaptchaTime time.Duration) int
|
||||
IncrErrorTimes(account string, expireTime time.Duration) int
|
||||
LockAccount(account string, lockTime time.Duration)
|
||||
IsLock(account string) bool
|
||||
IsShowCaptcha(account string) bool
|
||||
DeleteShowCaptcha(account string)
|
||||
DeleteErrorTimes(account string)
|
||||
}
|
||||
|
||||
const (
|
||||
UserCacheMaxErrorTimes = 5
|
||||
UserCacheExpireTime = 5 * time.Minute
|
||||
UserCacheLockTime = 15 * time.Minute
|
||||
UserCacheShowCaptchaTime = 15 * time.Minute
|
||||
)
|
||||
|
@ -6,6 +6,7 @@ package model
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"github.com/zhenorzz/goploy/config"
|
||||
"time"
|
||||
@ -27,6 +28,7 @@ type User struct {
|
||||
ID int64 `json:"id"`
|
||||
Account string `json:"account"`
|
||||
Password string `json:"password"`
|
||||
PasswordUpdateTime sql.NullString `json:"passwordUpdateTime"`
|
||||
Name string `json:"name"`
|
||||
Contact string `json:"contact"`
|
||||
SuperManager int64 `json:"superManager"`
|
||||
@ -57,12 +59,12 @@ func (u User) GetData() (User, error) {
|
||||
func (u User) GetDataByAccount() (User, error) {
|
||||
var user User
|
||||
err := sq.
|
||||
Select("id, account, password, name, contact, super_manager, state, insert_time, update_time").
|
||||
Select("id, account, password, password_update_time, name, contact, super_manager, state, insert_time, update_time").
|
||||
From(userTable).
|
||||
Where(sq.Eq{"account": u.Account}).
|
||||
RunWith(DB).
|
||||
QueryRow().
|
||||
Scan(&user.ID, &user.Account, &user.Password, &user.Name, &user.Contact, &user.SuperManager, &user.State, &user.InsertTime, &user.UpdateTime)
|
||||
Scan(&user.ID, &user.Account, &user.Password, &user.PasswordUpdateTime, &user.Name, &user.Contact, &user.SuperManager, &user.State, &user.InsertTime, &user.UpdateTime)
|
||||
if err != nil {
|
||||
return user, err
|
||||
}
|
||||
@ -222,6 +224,7 @@ func (u User) UpdatePassword() error {
|
||||
Update(userTable).
|
||||
SetMap(sq.Eq{
|
||||
"password": string(hashedPassword),
|
||||
"password_update_time": u.PasswordUpdateTime,
|
||||
}).
|
||||
Where(sq.Eq{"id": u.ID}).
|
||||
RunWith(DB).
|
||||
|
@ -31,6 +31,7 @@ const (
|
||||
IllegalRequest = 10001
|
||||
NamespaceInvalid = 10002
|
||||
IllegalParam = 10003
|
||||
PasswordExpired = 10004
|
||||
LoginExpired = 10086
|
||||
)
|
||||
|
||||
|
@ -6,6 +6,7 @@ package server
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/golang-jwt/jwt"
|
||||
log "github.com/sirupsen/logrus"
|
||||
@ -157,7 +158,7 @@ func (rt *Router) doRequest(w http.ResponseWriter, r *http.Request) (*Goploy, Re
|
||||
UserID: gp.UserInfo.ID,
|
||||
}.GetDataByUserNamespace()
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return gp, response.JSON{Code: response.NamespaceInvalid, Message: "No available namespace"}
|
||||
} else {
|
||||
return gp, response.JSON{Code: response.Deny, Message: err.Error()}
|
||||
|
@ -19,6 +19,7 @@ export class Login extends Request {
|
||||
public param: {
|
||||
account: string
|
||||
password: string
|
||||
newPassword: string
|
||||
captchaKey: string
|
||||
}
|
||||
public declare datagram: {
|
||||
|
@ -1,6 +1,7 @@
|
||||
{
|
||||
"default": "Default",
|
||||
"name": "Name",
|
||||
"signIn": "Sign in",
|
||||
"tag": "Tag",
|
||||
"script": "Script",
|
||||
"owner": "Owner",
|
||||
|
@ -1,6 +1,7 @@
|
||||
{
|
||||
"default": "默认",
|
||||
"name": "名称",
|
||||
"signIn": "登录",
|
||||
"tag": "标签",
|
||||
"script": "脚本",
|
||||
"owner": "拥有者",
|
||||
|
@ -31,11 +31,12 @@ const mutations: MutationTree<UserState> = {
|
||||
const actions: ActionTree<UserState, RootState> = {
|
||||
// user login
|
||||
login(_, userInfo) {
|
||||
const { account, password, captchaKey } = userInfo
|
||||
const { account, password, newPassword, captchaKey } = userInfo
|
||||
return new Promise((resolve, reject) => {
|
||||
new Login({
|
||||
account: account.trim(),
|
||||
password: password,
|
||||
newPassword: newPassword,
|
||||
captchaKey: captchaKey,
|
||||
})
|
||||
.request()
|
||||
|
@ -5,7 +5,7 @@
|
||||
</el-row>
|
||||
<el-form
|
||||
ref="form"
|
||||
:model="loginForm"
|
||||
:model="loginFormData"
|
||||
:rules="loginRules"
|
||||
class="login-form"
|
||||
auto-complete="on"
|
||||
@ -13,7 +13,7 @@
|
||||
>
|
||||
<div class="title-container">
|
||||
<h3 class="title">
|
||||
Sign in to Goploy <sub>{{ version }}</sub>
|
||||
Goploy <sub>{{ version }}</sub>
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
@ -22,8 +22,8 @@
|
||||
<svg-icon icon-class="user" />
|
||||
</span>
|
||||
<input
|
||||
v-model="loginForm.account"
|
||||
placeholder="account"
|
||||
v-model="loginFormData.account"
|
||||
:placeholder="$t('account')"
|
||||
name="account"
|
||||
type="text"
|
||||
tabindex="1"
|
||||
@ -36,18 +36,69 @@
|
||||
<svg-icon icon-class="password" />
|
||||
</span>
|
||||
<input
|
||||
:key="passwordType"
|
||||
v-model="loginForm.password"
|
||||
:type="passwordType"
|
||||
placeholder="password"
|
||||
v-model="loginFormData.password"
|
||||
:type="loginFormProps.type.password"
|
||||
:placeholder="$t('password')"
|
||||
name="password"
|
||||
tabindex="2"
|
||||
auto-complete="on"
|
||||
@keyup.enter="handleLogin"
|
||||
/>
|
||||
<span class="show-pwd" @click="showPwd">
|
||||
<span class="show-pwd" @click="showPwd(inputElem.password)">
|
||||
<svg-icon
|
||||
:icon-class="passwordType === 'password' ? 'eye' : 'eye-open'"
|
||||
:icon-class="
|
||||
loginFormProps.type[inputElem.password] === 'password'
|
||||
? 'eye'
|
||||
: 'eye-open'
|
||||
"
|
||||
/>
|
||||
</span>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item
|
||||
v-if="loginFormProps.showEditPassword"
|
||||
prop="newPassword"
|
||||
class="login-form-input"
|
||||
>
|
||||
<span class="svg-container">
|
||||
<svg-icon icon-class="password" />
|
||||
</span>
|
||||
<input
|
||||
v-model="loginFormData.newPassword"
|
||||
:type="loginFormProps.type.newPassword"
|
||||
:placeholder="$t('userPage.newPassword')"
|
||||
/>
|
||||
<span class="show-pwd" @click="showPwd(inputElem.newPassword)">
|
||||
<svg-icon
|
||||
:icon-class="
|
||||
loginFormProps.type[inputElem.newPassword] === 'password'
|
||||
? 'eye'
|
||||
: 'eye-open'
|
||||
"
|
||||
/>
|
||||
</span>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item
|
||||
v-if="loginFormProps.showEditPassword"
|
||||
prop="confirmPassword"
|
||||
class="login-form-input"
|
||||
>
|
||||
<span class="svg-container">
|
||||
<svg-icon icon-class="password" />
|
||||
</span>
|
||||
<input
|
||||
v-model="loginFormData.confirmPassword"
|
||||
:type="loginFormProps.type.confirmPassword"
|
||||
:placeholder="$t('userPage.rePassword')"
|
||||
/>
|
||||
<span class="show-pwd" @click="showPwd(inputElem.confirmPassword)">
|
||||
<svg-icon
|
||||
:icon-class="
|
||||
loginFormProps.type[inputElem.confirmPassword] === 'password'
|
||||
? 'eye'
|
||||
: 'eye-open'
|
||||
"
|
||||
/>
|
||||
</span>
|
||||
</el-form-item>
|
||||
@ -72,7 +123,7 @@
|
||||
style="width: 100%; margin-bottom: 30px"
|
||||
@click.prevent="handleLogin"
|
||||
>
|
||||
Sign in
|
||||
{{ $t('signIn') }}
|
||||
</el-button>
|
||||
<el-divider v-if="Object.keys(mediaLoginUrl).length > 0" class="divider">
|
||||
<span class="media-logo">
|
||||
@ -102,16 +153,34 @@ import { GetCaptcha, CheckCaptcha, GetConfig } from '@/api/user'
|
||||
import GoCaptchaBtn from './components/GoCaptchaBtn.vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
const { locale } = useI18n({ useScope: 'global' })
|
||||
enum inputElem {
|
||||
password = 'password',
|
||||
newPassword = 'newPassword',
|
||||
confirmPassword = 'confirmPassword',
|
||||
}
|
||||
const version = import.meta.env.VITE_APP_VERSION
|
||||
const store = useStore()
|
||||
const router = useRouter()
|
||||
const form = ref<InstanceType<typeof ElForm>>()
|
||||
const loginForm = ref({
|
||||
const loginFormData = ref({
|
||||
account: import.meta.env.PROD === true ? '' : 'admin',
|
||||
password: import.meta.env.PROD === true ? '' : 'admin!@#',
|
||||
newPassword: '',
|
||||
confirmPassword: '',
|
||||
phrase: '',
|
||||
captchaKey: '',
|
||||
})
|
||||
|
||||
const loginFormProps = ref({
|
||||
loading: false,
|
||||
showEditPassword: false,
|
||||
type: {
|
||||
password: 'password',
|
||||
newPassword: 'password',
|
||||
confirmPassword: 'password',
|
||||
},
|
||||
})
|
||||
|
||||
const loginRules: InstanceType<typeof ElForm>['rules'] = {
|
||||
account: [
|
||||
{
|
||||
@ -141,10 +210,39 @@ const loginRules: InstanceType<typeof ElForm>['rules'] = {
|
||||
},
|
||||
},
|
||||
],
|
||||
newPassword: [
|
||||
{
|
||||
required: true,
|
||||
trigger: ['blur'],
|
||||
validator: (_, value) => {
|
||||
if (!validPassword(value)) {
|
||||
return new Error(
|
||||
'8 to 16 characters and a minimum of 2 character sets from these classes: [letters], [numbers], [special characters]'
|
||||
)
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
confirmPassword: [
|
||||
{
|
||||
required: true,
|
||||
trigger: ['blur'],
|
||||
validator: (_, value) => {
|
||||
if (value === '') {
|
||||
return new Error('Please enter the password again')
|
||||
} else if (value !== loginFormData.value.newPassword) {
|
||||
return new Error('The two passwords do not match!')
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
const redirectUri = window.location.origin + '/#/login'
|
||||
const passwordType = ref('password')
|
||||
const loading = ref(false)
|
||||
const redirect = ref()
|
||||
const query = ref()
|
||||
@ -204,9 +302,10 @@ function getConfig() {
|
||||
captchaEnabled.value = response.data.captcha.enabled
|
||||
ldapEnabled.value = response.data.ldap.enabled
|
||||
for (const media in response.data['mediaURL']) {
|
||||
if (response.data['mediaURL'][media] != '') {
|
||||
mediaLoginUrl.value[media] = `${
|
||||
response.data['mediaURL'][media]
|
||||
const key = media as keyof typeof response.data['mediaURL']
|
||||
if (response.data['mediaURL'][key] != '') {
|
||||
mediaLoginUrl.value[key] = `${
|
||||
response.data['mediaURL'][key]
|
||||
}&redirect_uri=${encodeURIComponent(redirectUri)}`
|
||||
}
|
||||
}
|
||||
@ -222,7 +321,7 @@ function handleRequestCaptCode() {
|
||||
captchaBase64.value = response.data.base64
|
||||
captchaThumbBase64.value = response.data.thumbBase64
|
||||
captchaKey.value = response.data.key
|
||||
loginForm.value.captchaKey = response.data.key
|
||||
loginFormData.value.captchaKey = response.data.key
|
||||
})
|
||||
}
|
||||
|
||||
@ -256,16 +355,12 @@ function handleConfirm(dots: { x: number; y: number; index: number }[]) {
|
||||
})
|
||||
}
|
||||
|
||||
const password = ref<HTMLInputElement>()
|
||||
function showPwd() {
|
||||
if (passwordType.value === 'password') {
|
||||
passwordType.value = ''
|
||||
function showPwd(index: inputElem) {
|
||||
if (loginFormProps.value.type[index] === 'password') {
|
||||
loginFormProps.value.type[index] = ''
|
||||
} else {
|
||||
passwordType.value = 'password'
|
||||
loginFormProps.value.type[index] = 'password'
|
||||
}
|
||||
nextTick(() => {
|
||||
password.value?.focus()
|
||||
})
|
||||
}
|
||||
|
||||
function handleExtLogin(account: string, time: number, token: string) {
|
||||
@ -288,7 +383,7 @@ function handleLogin() {
|
||||
if (valid) {
|
||||
loading.value = true
|
||||
store
|
||||
.dispatch('user/login', loginForm.value)
|
||||
.dispatch('user/login', loginFormData.value)
|
||||
.then(() => {
|
||||
router.push({
|
||||
path: redirect.value || '/',
|
||||
@ -296,11 +391,13 @@ function handleLogin() {
|
||||
})
|
||||
loading.value = false
|
||||
})
|
||||
.catch(() => {
|
||||
if (captchaEnabled.value) {
|
||||
.catch((error) => {
|
||||
if (error.data.code == 10004) {
|
||||
loginFormProps.value.showEditPassword = true
|
||||
} else if (captchaEnabled.value) {
|
||||
captchaShow.value = true
|
||||
captchaStatus.value = 'default'
|
||||
loginForm.value.captchaKey = ''
|
||||
loginFormData.value.captchaKey = ''
|
||||
}
|
||||
loading.value = false
|
||||
})
|
||||
@ -364,6 +461,7 @@ $cursor: #2f2f2f;
|
||||
}
|
||||
.el-form-item__error {
|
||||
padding-top: 4px;
|
||||
z-index: 1;
|
||||
}
|
||||
.el-form-item {
|
||||
background: #fff;
|
||||
|
@ -144,8 +144,6 @@ const formRules: InstanceType<typeof ElForm>['rules'] = {
|
||||
new: [
|
||||
{
|
||||
required: true,
|
||||
message:
|
||||
'8 to 16 characters and a minimum of 2 character sets from these classes: [letters], [numbers], [special characters]',
|
||||
trigger: ['blur'],
|
||||
validator: (_, value) => {
|
||||
if (!validPassword(value)) {
|
||||
|
Loading…
Reference in New Issue
Block a user