Browse Source

feat: 增加系统安全入口功能

pull/774/head
ssongliu 2 years ago committed by ssongliu
parent
commit
d5f400670c
  1. 39
      backend/app/api/v1/auth.go
  2. 1
      backend/app/dto/setting.go
  3. 39
      backend/app/service/auth.go
  4. 10
      backend/app/service/setting.go
  5. 1
      backend/init/migration/migrate.go
  6. 13
      backend/init/migration/migrations/init.go
  7. 3
      backend/init/router/router.go
  8. 18
      backend/middleware/safety.go
  9. 3
      backend/router/ro_base.go
  10. 2
      cmd/server/cmd/user-info.go
  11. 4
      frontend/src/api/helper/check-status.ts
  12. 11
      frontend/src/api/index.ts
  13. 1
      frontend/src/api/interface/setting.ts
  14. 6
      frontend/src/api/modules/auth.ts
  15. 2
      frontend/src/components/app-layout/menu/index.vue
  16. 11
      frontend/src/lang/modules/en.ts
  17. 11
      frontend/src/lang/modules/zh.ts
  18. 3
      frontend/src/routers/index.ts
  19. 2
      frontend/src/routers/router.ts
  20. 1
      frontend/src/store/index.ts
  21. 1
      frontend/src/store/interface/index.ts
  22. 57
      frontend/src/views/login/index.vue
  23. 4
      frontend/src/views/setting/panel/index.vue
  24. 2
      frontend/src/views/setting/panel/password/index.vue
  25. 73
      frontend/src/views/setting/safe/index.vue

39
backend/app/api/v1/auth.go

@ -1,8 +1,6 @@
package v1 package v1
import ( import (
"errors"
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper" "github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
"github.com/1Panel-dev/1Panel/backend/app/dto" "github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/app/model" "github.com/1Panel-dev/1Panel/backend/app/model"
@ -102,44 +100,17 @@ func (b *BaseApi) Captcha(c *gin.Context) {
// @Summary Load safety status // @Summary Load safety status
// @Description 获取系统安全登录状态 // @Description 获取系统安全登录状态
// @Success 200 // @Success 200
// @Failure 402 // @Router /auth/issafety [get]
// @Router /auth/status [get] func (b *BaseApi) CheckIsSafety(c *gin.Context) {
func (b *BaseApi) GetSafetyStatus(c *gin.Context) { code := c.DefaultQuery("code", "")
if err := authService.SafetyStatus(c); err != nil { helper.SuccessWithData(c, authService.CheckIsSafety(code))
helper.ErrorWithDetail(c, constant.CodeErrUnSafety, constant.ErrTypeNotSafety, err)
return
}
helper.SuccessWithData(c, nil)
}
func (b *BaseApi) SafeEntrance(c *gin.Context) {
code, exist := c.Params.Get("code")
if !exist {
helper.ErrorWithDetail(c, constant.CodeErrUnSafety, constant.ErrTypeNotSafety, errors.New("missing code"))
return
}
ok, err := authService.VerifyCode(code)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrUnSafety, constant.ErrTypeNotSafety, errors.New("missing code"))
return
}
if !ok {
helper.ErrorWithDetail(c, constant.CodeErrUnSafety, constant.ErrTypeNotSafety, errors.New("missing code"))
return
}
if err := authService.SafeEntrance(c, code); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrUnSafety, constant.ErrTypeNotSafety, errors.New("missing code"))
return
}
helper.SuccessWithData(c, nil)
} }
// @Tags Auth // @Tags Auth
// @Summary Check is First login // @Summary Check is First login
// @Description 判断是否为首次登录 // @Description 判断是否为首次登录
// @Success 200 // @Success 200
// @Router /auth/status [get] // @Router /auth/isfirst [get]
func (b *BaseApi) CheckIsFirstLogin(c *gin.Context) { func (b *BaseApi) CheckIsFirstLogin(c *gin.Context) {
helper.SuccessWithData(c, authService.CheckIsFirst()) helper.SuccessWithData(c, authService.CheckIsFirst())
} }

1
backend/app/dto/setting.go

@ -16,6 +16,7 @@ type SettingInfo struct {
Language string `json:"language"` Language string `json:"language"`
ServerPort string `json:"serverPort"` ServerPort string `json:"serverPort"`
SecurityEntranceStatus string `json:"securityEntranceStatus"`
SecurityEntrance string `json:"securityEntrance"` SecurityEntrance string `json:"securityEntrance"`
ExpirationDays string `json:"expirationDays"` ExpirationDays string `json:"expirationDays"`
ExpirationTime string `json:"expirationTime"` ExpirationTime string `json:"expirationTime"`

39
backend/app/service/auth.go

@ -19,11 +19,10 @@ import (
type AuthService struct{} type AuthService struct{}
type IAuthService interface { type IAuthService interface {
SafetyStatus(c *gin.Context) error CheckIsSafety(code string) bool
CheckIsFirst() bool CheckIsFirst() bool
InitUser(c *gin.Context, req dto.InitUser) error InitUser(c *gin.Context, req dto.InitUser) error
VerifyCode(code string) (bool, error) VerifyCode(code string) (bool, error)
SafeEntrance(c *gin.Context, code string) error
Login(c *gin.Context, info dto.Login) (*dto.UserLoginInfo, error) Login(c *gin.Context, info dto.Login) (*dto.UserLoginInfo, error)
LogOut(c *gin.Context) error LogOut(c *gin.Context) error
MFALogin(c *gin.Context, info dto.MFALogin) (*dto.UserLoginInfo, error) MFALogin(c *gin.Context, info dto.MFALogin) (*dto.UserLoginInfo, error)
@ -33,22 +32,6 @@ func NewIAuthService() IAuthService {
return &AuthService{} return &AuthService{}
} }
func (u *AuthService) SafeEntrance(c *gin.Context, code string) error {
codeWithMD5 := encrypt.Md5(code)
cookieValue, _ := encrypt.StringEncrypt(codeWithMD5)
c.SetCookie(codeWithMD5, cookieValue, 604800, "", "", false, false)
expiredSetting, err := settingRepo.Get(settingRepo.WithByKey("ExpirationDays"))
if err != nil {
return err
}
timeout, _ := strconv.Atoi(expiredSetting.Value)
if err := settingRepo.Update("ExpirationTime", time.Now().AddDate(0, 0, timeout).Format("2006-01-02 15:04:05")); err != nil {
return err
}
return nil
}
func (u *AuthService) Login(c *gin.Context, info dto.Login) (*dto.UserLoginInfo, error) { func (u *AuthService) Login(c *gin.Context, info dto.Login) (*dto.UserLoginInfo, error) {
nameSetting, err := settingRepo.Get(settingRepo.WithByKey("UserName")) nameSetting, err := settingRepo.Get(settingRepo.WithByKey("UserName"))
if err != nil { if err != nil {
@ -164,23 +147,19 @@ func (u *AuthService) VerifyCode(code string) (bool, error) {
return setting.Value == code, nil return setting.Value == code, nil
} }
func (u *AuthService) SafetyStatus(c *gin.Context) error { func (u *AuthService) CheckIsSafety(code string) bool {
setting, err := settingRepo.Get(settingRepo.WithByKey("SecurityEntrance")) status, err := settingRepo.Get(settingRepo.WithByKey("SecurityEntranceStatus"))
if err != nil { if err != nil {
return err return false
} }
codeWithEcrypt, err := c.Cookie(encrypt.Md5(setting.Value)) if status.Value == "disable" {
if err != nil { return true
return err
} }
code, err := encrypt.StringDecrypt(codeWithEcrypt) setting, err := settingRepo.Get(settingRepo.WithByKey("SecurityEntrance"))
if err != nil { if err != nil {
return err return false
}
if code != encrypt.Md5(setting.Value) {
return errors.New("code not match")
} }
return nil return setting.Value == code
} }
func (u *AuthService) CheckIsFirst() bool { func (u *AuthService) CheckIsFirst() bool {

10
backend/app/service/setting.go

@ -57,6 +57,16 @@ func (u *SettingService) Update(key, value string) error {
return err return err
} }
} }
if key == "SecurityEntrance" {
if err := settingRepo.Update("SecurityEntranceStatus", "enable"); err != nil {
return err
}
}
if key == "SecurityEntranceStatus" {
if err := settingRepo.Update("SecurityEntrance", ""); err != nil {
return err
}
}
if err := settingRepo.Update(key, value); err != nil { if err := settingRepo.Update(key, value); err != nil {
return err return err
} }

1
backend/init/migration/migrate.go

@ -25,6 +25,7 @@ func Init() {
migrations.UpdateTableApp, migrations.UpdateTableApp,
migrations.UpdateTableHost, migrations.UpdateTableHost,
migrations.UpdateTableWebsite, migrations.UpdateTableWebsite,
migrations.AddEntranceStatus,
}) })
if err := m.Migrate(); err != nil { if err := m.Migrate(); err != nil {
global.LOG.Error(err) global.LOG.Error(err)

13
backend/init/migration/migrations/init.go

@ -287,3 +287,16 @@ var UpdateTableWebsite = &gormigrate.Migration{
return nil return nil
}, },
} }
var AddEntranceStatus = &gormigrate.Migration{
ID: "20230414-add-entrance-status",
Migrate: func(tx *gorm.DB) error {
if err := tx.Create(&model.Setting{Key: "SecurityEntranceStatus", Value: "disable"}).Error; err != nil {
return err
}
if err := tx.Model(&model.Setting{}).Where("key = ?", "SecurityEntrance").Updates(map[string]interface{}{"value": ""}).Error; err != nil {
return err
}
return tx.AutoMigrate(&model.Website{})
},
}

3
backend/init/router/router.go

@ -6,7 +6,6 @@ import (
"github.com/gin-contrib/gzip" "github.com/gin-contrib/gzip"
v1 "github.com/1Panel-dev/1Panel/backend/app/api/v1"
"github.com/1Panel-dev/1Panel/backend/global" "github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/i18n" "github.com/1Panel-dev/1Panel/backend/i18n"
"github.com/1Panel-dev/1Panel/backend/middleware" "github.com/1Panel-dev/1Panel/backend/middleware"
@ -53,8 +52,6 @@ func Routers() *gin.Engine {
setWebStatic(Router) setWebStatic(Router)
Router.Use(i18n.GinI18nLocalize()) Router.Use(i18n.GinI18nLocalize())
Router.GET("/api/v1/info", v1.ApiGroupApp.BaseApi.GetSafetyStatus)
Router.GET("/api/v1/:code", v1.ApiGroupApp.BaseApi.SafeEntrance)
Router.SetFuncMap(template.FuncMap{ Router.SetFuncMap(template.FuncMap{
"Localize": ginI18n.GetMessage, "Localize": ginI18n.GetMessage,

18
backend/middleware/safety.go

@ -1,18 +0,0 @@
package middleware
import (
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
"github.com/1Panel-dev/1Panel/backend/app/service"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/gin-gonic/gin"
)
func SafetyAuth() gin.HandlerFunc {
return func(c *gin.Context) {
if err := service.NewIAuthService().SafetyStatus(c); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrUnSafety, constant.ErrTypeNotSafety, nil)
return
}
c.Next()
}
}

3
backend/router/ro_base.go

@ -14,7 +14,8 @@ func (s *BaseRouter) InitBaseRouter(Router *gin.RouterGroup) {
baseRouter.GET("/captcha", baseApi.Captcha) baseRouter.GET("/captcha", baseApi.Captcha)
baseRouter.POST("/mfalogin", baseApi.MFALogin) baseRouter.POST("/mfalogin", baseApi.MFALogin)
baseRouter.POST("/login", baseApi.Login) baseRouter.POST("/login", baseApi.Login)
baseRouter.GET("/status", baseApi.CheckIsFirstLogin) baseRouter.GET("/isfirst", baseApi.CheckIsFirstLogin)
baseRouter.GET("/issafety", baseApi.CheckIsSafety)
baseRouter.POST("/init", baseApi.InitUserInfo) baseRouter.POST("/init", baseApi.InitUserInfo)
baseRouter.POST("/logout", baseApi.LogOut) baseRouter.POST("/logout", baseApi.LogOut)
baseRouter.GET("/demo", baseApi.CheckIsDemo) baseRouter.GET("/demo", baseApi.CheckIsDemo)

2
cmd/server/cmd/user-info.go

@ -39,6 +39,7 @@ var userinfoCmd = &cobra.Command{
user := getSettingByKey(db, "UserName") user := getSettingByKey(db, "UserName")
password := getSettingByKey(db, "Password") password := getSettingByKey(db, "Password")
port := getSettingByKey(db, "ServerPort") port := getSettingByKey(db, "ServerPort")
entrance := getSettingByKey(db, "SecurityEntrance")
enptrySetting := getSettingByKey(db, "EncryptKey") enptrySetting := getSettingByKey(db, "EncryptKey")
p := "" p := ""
@ -49,6 +50,7 @@ var userinfoCmd = &cobra.Command{
p = password p = password
} }
fmt.Printf("entrance: %s\n", entrance)
fmt.Printf("username: %s\n", user) fmt.Printf("username: %s\n", user)
fmt.Printf("password: %s\n", p) fmt.Printf("password: %s\n", p)
fmt.Printf("port: %s\n", port) fmt.Printf("port: %s\n", port)

4
frontend/src/api/helper/check-status.ts

@ -1,6 +1,8 @@
import i18n from '@/lang'; import i18n from '@/lang';
import router from '@/routers'; import router from '@/routers';
import { MsgError } from '@/utils/message'; import { MsgError } from '@/utils/message';
import { GlobalStore } from '@/store';
const globalStore = GlobalStore();
export const checkStatus = (status: number, msg: string): void => { export const checkStatus = (status: number, msg: string): void => {
switch (status) { switch (status) {
@ -11,7 +13,7 @@ export const checkStatus = (status: number, msg: string): void => {
MsgError(msg ? msg : i18n.global.t('commons.res.notFound')); MsgError(msg ? msg : i18n.global.t('commons.res.notFound'));
break; break;
case 403: case 403:
router.replace({ path: '/login' }); router.replace({ path: '/login', params: { code: globalStore.entrance } });
MsgError(msg ? msg : i18n.global.t('commons.res.forbidden')); MsgError(msg ? msg : i18n.global.t('commons.res.forbidden'));
break; break;
case 500: case 500:

11
frontend/src/api/index.ts

@ -43,17 +43,12 @@ class RequestHttp {
globalStore.setCsrfToken(response.headers['x-csrf-token']); globalStore.setCsrfToken(response.headers['x-csrf-token']);
} }
if (data.code == ResultEnum.OVERDUE || data.code == ResultEnum.FORBIDDEN) { if (data.code == ResultEnum.OVERDUE || data.code == ResultEnum.FORBIDDEN) {
router.replace({ router.push({
path: '/login', name: 'login',
params: { code: globalStore.entrance },
}); });
return Promise.reject(data); return Promise.reject(data);
} }
if (data.code == ResultEnum.UNSAFETY) {
router.replace({
path: '/login',
});
return data;
}
if (data.code == ResultEnum.EXPIRED) { if (data.code == ResultEnum.EXPIRED) {
router.push({ name: 'Expired' }); router.push({ name: 'Expired' });
return data; return data;

1
frontend/src/api/interface/setting.ts

@ -15,6 +15,7 @@ export namespace Setting {
language: string; language: string;
serverPort: number; serverPort: number;
securityEntranceStatus: string;
securityEntrance: string; securityEntrance: string;
expirationDays: number; expirationDays: number;
expirationTime: string; expirationTime: string;

6
frontend/src/api/modules/auth.ts

@ -26,7 +26,11 @@ export const loginStatus = () => {
}; };
export const checkIsFirst = () => { export const checkIsFirst = () => {
return http.get<boolean>('/auth/status'); return http.get<boolean>('/auth/isfirst');
};
export const checkIsSafety = (code: string) => {
return http.get<boolean>(`/auth/issafety?code=${code}`);
}; };
export const initUser = (params: Login.InitUser) => { export const initUser = (params: Login.InitUser) => {

2
frontend/src/components/app-layout/menu/index.vue

@ -80,7 +80,7 @@ const logout = () => {
}) })
.then(() => { .then(() => {
systemLogOut(); systemLogOut();
router.push({ name: 'login' }); router.push({ name: 'login', params: { code: globalStore.entrance } });
globalStore.setLogStatus(false); globalStore.setLogStatus(false);
MsgSuccess(i18n.global.t('commons.msg.operationSuccess')); MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
}) })

11
frontend/src/lang/modules/en.ts

@ -114,15 +114,8 @@ const message = {
errorMfaInfo: 'Incorrect authentication information, please try again!', errorMfaInfo: 'Incorrect authentication information, please try again!',
captchaHelper: 'Captcha', captchaHelper: 'Captcha',
errorCaptcha: 'Captcha code error!', errorCaptcha: 'Captcha code error!',
safeEntrance: 'Please use the correct entry to log in to the panel', safeEntrance:
reason: 'Cause of error:', 'The command "1pctl user-info" can be used in SSH terminal to view the panel entrance as the secure login has been enabled in the current environment.',
reasonHelper:
'At present, the newly installed machine has enabled the security entrance login. The newly installed machine will have a random 8-character security entrance name, which can also be modified in the panel Settings. If you do not record or do not remember, you can use the following methods to solve the problem',
solution: 'The solution:',
solutionHelper:
'Run the following command on the SSH terminal to solve the problem: 1. View the /etc/init.d/bt default command on the panel',
warnning:
'Note: [Closing the security entrance] will make your panel login address directly exposed to the Internet, very dangerous, please exercise caution',
codeInput: 'Please enter the 6-digit verification code of the MFA validator', codeInput: 'Please enter the 6-digit verification code of the MFA validator',
mfaTitle: 'MFA Certification', mfaTitle: 'MFA Certification',
mfaCode: 'MFA verification code', mfaCode: 'MFA verification code',

11
frontend/src/lang/modules/zh.ts

@ -118,13 +118,7 @@ const message = {
errorMfaInfo: '错误的验证信息请重试', errorMfaInfo: '错误的验证信息请重试',
captchaHelper: '验证码', captchaHelper: '验证码',
errorCaptcha: '验证码错误', errorCaptcha: '验证码错误',
safeEntrance: '请使用正确的入口登录面板', safeEntrance: '当前环境已经开启了安全入口登录 SSH 终端输入以下命令来查看面板入口: 1pctl user-info',
reason: '错误原因',
reasonHelper:
'当前新安装的已经开启了安全入口登录新装机器都会随机一个8位字符的安全入口名称亦可以在面板设置处修改如您没记录或不记得了可以使用以下方式解决',
solution: '解决方法',
solutionHelper: ' SSH 终端输入以下一种命令来解决 1.查看面板入口/etc/init.d/bt default',
warnning: '注意关闭安全入口将使您的面板登录地址被直接暴露在互联网上非常危险请谨慎操作',
codeInput: '请输入 MFA 验证器的 6 位验证码', codeInput: '请输入 MFA 验证器的 6 位验证码',
mfaTitle: 'MFA 认证', mfaTitle: 'MFA 认证',
mfaCode: 'MFA 验证码', mfaCode: 'MFA 验证码',
@ -806,6 +800,9 @@ const message = {
portHelper: '建议端口范围8888 - 65535注意有安全组的服务器请提前在安全组放行新端口', portHelper: '建议端口范围8888 - 65535注意有安全组的服务器请提前在安全组放行新端口',
portChange: '端口修改', portChange: '端口修改',
portChangeHelper: '服务端口修改需要重启服务是否继续', portChangeHelper: '服务端口修改需要重启服务是否继续',
entrance: '安全入口',
entranceHelper: '开启安全入口后只能通过指定安全入口登录面板',
entranceError: '请输入 8 位安全登录入口仅支持输入数字或字母',
theme: '主题颜色', theme: '主题颜色',
componentSize: '组件大小', componentSize: '组件大小',
dark: '暗色', dark: '暗色',

3
frontend/src/routers/index.ts

@ -17,7 +17,8 @@ router.beforeEach((to, from, next) => {
const globalStore = GlobalStore(); const globalStore = GlobalStore();
if (!globalStore.isLogin) { if (!globalStore.isLogin) {
next({ next({
path: '/login', name: 'login',
params: { code: globalStore.entrance },
}); });
NProgress.done(); NProgress.done();
return; return;

2
frontend/src/routers/router.ts

@ -58,7 +58,7 @@ menuList.unshift(homeRouter);
export const routes: RouteRecordRaw[] = [ export const routes: RouteRecordRaw[] = [
homeRouter, homeRouter,
{ {
path: '/login', path: '/login/:code?',
name: 'login', name: 'login',
props: true, props: true,
component: () => import('@/views/login/index.vue'), component: () => import('@/views/login/index.vue'),

1
frontend/src/store/index.ts

@ -12,6 +12,7 @@ export const GlobalStore = defineStore({
loadingText: '', loadingText: '',
isLogin: false, isLogin: false,
csrfToken: '', csrfToken: '',
entrance: '',
language: '', language: '',
themeConfig: { themeConfig: {
panelName: '', panelName: '',

1
frontend/src/store/interface/index.ts

@ -11,6 +11,7 @@ export interface GlobalState {
isLoading: boolean; isLoading: boolean;
loadingText: string; loadingText: string;
isLogin: boolean; isLogin: boolean;
entrance: string;
csrfToken: string; csrfToken: string;
language: string; // zh | en language: string; // zh | en
// assemblySize: string; // small | default | large // assemblySize: string; // small | default | large

57
frontend/src/views/login/index.vue

@ -1,6 +1,6 @@
<template> <template>
<div> <div>
<div class="login-backgroud" v-if="statusCode == 1"> <div class="login-backgroud" v-if="isSafety">
<div class="login-wrapper"> <div class="login-wrapper">
<div :class="screenWidth > 1110 ? 'left inline-block' : ''"> <div :class="screenWidth > 1110 ? 'left inline-block' : ''">
<div class="login-title"> <div class="login-title">
@ -15,36 +15,36 @@
</div> </div>
</div> </div>
</div> </div>
<div style="margin-left: 50px" v-if="statusCode == -1"> <div style="margin-left: 50px" v-if="!isSafety">
<h1>{{ $t('commons.login.safeEntrance') }}</h1> <div class="not-found">
<div style="line-height: 30px"> <h1>404 NOT FOUND</h1>
<span style="font-weight: 500">{{ $t('commons.login.reason') }}</span> <p>{{ $t('commons.login.safeEntrance') }}</p>
<span>
{{ $t('commons.login.reasonHelper') }}
</span>
</div>
<div style="line-height: 30px">
<span style="font-weight: 500">{{ $t('commons.login.solution') }}</span>
<span>{{ $t('commons.login.solutionHelper') }}</span>
</div>
<div style="line-height: 30px">
<span style="color: red">
{{ $t('commons.login.warnning') }}
</span>
</div> </div>
</div> </div>
</div> </div>
</template> </template>
<script setup lang="ts" name="login"> <script setup lang="ts" name="login">
import { checkIsSafety } from '@/api/modules/auth';
import LoginForm from './components/login-form.vue'; import LoginForm from './components/login-form.vue';
import { ref, onMounted } from 'vue'; import { ref, onMounted } from 'vue';
import { GlobalStore } from '@/store';
const globalStore = GlobalStore();
const statusCode = ref<number>(0); const isSafety = ref(false);
const screenWidth = ref(null); const screenWidth = ref(null);
interface Props {
code: string;
}
const mySafetyCode = withDefaults(defineProps<Props>(), {
code: '',
});
const getStatus = async () => { const getStatus = async () => {
statusCode.value = 1; const res = await checkIsSafety(mySafetyCode.code);
isSafety.value = res.data;
globalStore.entrance = mySafetyCode.code;
}; };
onMounted(() => { onMounted(() => {
@ -138,4 +138,23 @@ onMounted(() => {
} }
} }
} }
.not-found {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100vh;
.h1 {
font-size: 5rem;
margin: 0 0 1rem;
}
.p {
font-size: 1.2rem;
max-width: 500px;
text-align: center;
margin: 0 0 2rem;
}
}
</style> </style>

4
frontend/src/views/setting/panel/index.vue

@ -194,7 +194,7 @@ const onSaveUserName = async (formEl: FormInstance | undefined, key: string, val
await logOutApi(); await logOutApi();
loading.value = false; loading.value = false;
MsgSuccess(i18n.t('commons.msg.operationSuccess')); MsgSuccess(i18n.t('commons.msg.operationSuccess'));
router.push({ name: 'login', params: { code: '' } }); router.push({ name: 'login', params: { code: globalStore.entrance } });
globalStore.setLogStatus(false); globalStore.setLogStatus(false);
return; return;
}) })
@ -250,7 +250,7 @@ const onSave = async (formEl: FormInstance | undefined, key: string, val: any) =
await logOutApi(); await logOutApi();
loading.value = false; loading.value = false;
MsgSuccess(i18n.t('commons.msg.operationSuccess')); MsgSuccess(i18n.t('commons.msg.operationSuccess'));
router.push({ name: 'login', params: { code: '' } }); router.push({ name: 'login', params: { code: globalStore.entrance } });
globalStore.setLogStatus(false); globalStore.setLogStatus(false);
return; return;
} }

2
frontend/src/views/setting/panel/password/index.vue

@ -119,7 +119,7 @@ const submitChangePassword = async (formEl: FormInstance | undefined) => {
passwordVisiable.value = false; passwordVisiable.value = false;
MsgSuccess(i18n.global.t('commons.msg.operationSuccess')); MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
await logOutApi(); await logOutApi();
router.push({ name: 'login', params: { code: '' } }); router.push({ name: 'login', params: { code: globalStore.entrance } });
globalStore.setLogStatus(false); globalStore.setLogStatus(false);
}) })
.catch(() => { .catch(() => {

73
frontend/src/views/setting/safe/index.vue

@ -20,6 +20,33 @@
</el-input> </el-input>
</el-form-item> </el-form-item>
<el-form-item :label="$t('setting.entrance')" required>
<el-switch
@change="handleEntrance"
v-model="form.securityEntranceStatus"
active-value="enable"
inactive-value="disable"
/>
<span class="input-help">
{{ $t('setting.entranceHelper') }}
</span>
<el-input
@blur="codeError = false"
v-if="isEntranceShow"
clearable
v-model.number="form.securityEntrance"
>
<template #append>
<el-button style="width: 85px" @click="onSaveEntrance" icon="Collection">
{{ $t('commons.button.save') }}
</el-button>
</template>
</el-input>
<span class="input-error" v-if="codeError">
{{ $t('setting.entranceError') }}
</span>
</el-form-item>
<el-form-item <el-form-item
:label="$t('setting.expirationTime')" :label="$t('setting.expirationTime')"
prop="expirationTime" prop="expirationTime"
@ -142,10 +169,13 @@ import i18n from '@/lang';
import { Rules, checkNumberRange } from '@/global/form-rules'; import { Rules, checkNumberRange } from '@/global/form-rules';
import { dateFormatSimple } from '@/utils/util'; import { dateFormatSimple } from '@/utils/util';
import { MsgError, MsgSuccess } from '@/utils/message'; import { MsgError, MsgSuccess } from '@/utils/message';
import { GlobalStore } from '@/store';
const globalStore = GlobalStore();
const loading = ref(false); const loading = ref(false);
const form = reactive({ const form = reactive({
serverPort: 9999, serverPort: 9999,
securityEntranceStatus: 'disable',
securityEntrance: '', securityEntrance: '',
expirationDays: 0, expirationDays: 0,
expirationTime: '', expirationTime: '',
@ -163,6 +193,8 @@ const timeoutForm = reactive({
const search = async () => { const search = async () => {
const res = await getSettingInfo(); const res = await getSettingInfo();
form.serverPort = Number(res.data.serverPort); form.serverPort = Number(res.data.serverPort);
form.securityEntranceStatus = res.data.securityEntranceStatus;
isEntranceShow.value = res.data.securityEntranceStatus === 'enable';
form.securityEntrance = res.data.securityEntrance; form.securityEntrance = res.data.securityEntrance;
form.expirationDays = Number(res.data.expirationDays); form.expirationDays = Number(res.data.expirationDays);
form.expirationTime = res.data.expirationTime; form.expirationTime = res.data.expirationTime;
@ -171,6 +203,9 @@ const search = async () => {
form.mfaSecret = res.data.mfaSecret; form.mfaSecret = res.data.mfaSecret;
}; };
const isEntranceShow = ref(false);
const codeError = ref(false);
const isMFAShow = ref<boolean>(false); const isMFAShow = ref<boolean>(false);
const otp = reactive<Setting.MFAInfo>({ const otp = reactive<Setting.MFAInfo>({
secret: '', secret: '',
@ -259,6 +294,44 @@ const handleMFA = async () => {
} }
}; };
const handleEntrance = async () => {
if (form.securityEntranceStatus === 'enable') {
isEntranceShow.value = true;
} else {
isEntranceShow.value = false;
loading.value = true;
await updateSetting({ key: 'SecurityEntranceStatus', value: 'disable' })
.then(() => {
globalStore.entrance = '';
loading.value = false;
search();
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
})
.catch(() => {
loading.value = false;
});
}
};
const onSaveEntrance = async () => {
const reg = /^[A-Za-z0-9]{8}$/;
if ((!reg.test(form.securityEntrance) && form.securityEntrance !== '') || form.securityEntrance === '') {
codeError.value = true;
return;
}
loading.value = true;
await updateSetting({ key: 'SecurityEntrance', value: form.securityEntrance })
.then(() => {
globalStore.entrance = form.securityEntrance;
loading.value = false;
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
search();
})
.catch(() => {
loading.value = false;
});
};
const handleClose = () => { const handleClose = () => {
timeoutVisiable.value = false; timeoutVisiable.value = false;
}; };

Loading…
Cancel
Save