1
0
mirror of https://github.com/1Panel-dev/1Panel.git synced 2025-01-19 16:29:17 +08:00

feat: 未认证设置状态下系统跳转页面功能实现 (#4444)

#### What this PR does / why we need it?

#### Summary of your change

#### Please indicate you've done the following:

- [ ] Made sure tests are passing and test coverage is added if needed.
- [ ] Made sure commit message follow the rule of [Conventional Commits specification](https://www.conventionalcommits.org/).
- [ ] Considered the docs impact and opened a new docs issue or PR with docs changes if needed.
This commit is contained in:
John Bro 2024-04-09 22:46:09 +08:00 committed by GitHub
parent 35785f1e40
commit 3e3f0d9452
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 276 additions and 7 deletions

View File

@ -121,6 +121,15 @@ func (b *BaseApi) CheckIsSafety(c *gin.Context) {
helper.SuccessWithData(c, status) helper.SuccessWithData(c, status)
} }
func (b *BaseApi) GetResponsePage(c *gin.Context) {
pageCode, err := authService.GetResponsePage()
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, pageCode)
}
// @Tags Auth // @Tags Auth
// @Summary Check System isDemo // @Summary Check System isDemo
// @Description 判断是否为demo环境 // @Description 判断是否为demo环境

View File

@ -55,6 +55,7 @@ type SettingInfo struct {
SnapshotIgnore string `json:"snapshotIgnore"` SnapshotIgnore string `json:"snapshotIgnore"`
XpackHideMenu string `json:"xpackHideMenu"` XpackHideMenu string `json:"xpackHideMenu"`
NoAuthSetting string `json:"noAuthSetting"`
} }
type SettingUpdate struct { type SettingUpdate struct {

View File

@ -20,6 +20,7 @@ type AuthService struct{}
type IAuthService interface { type IAuthService interface {
CheckIsSafety(code string) (string, error) CheckIsSafety(code string) (string, error)
GetResponsePage() (string, error)
VerifyCode(code string) (bool, error) VerifyCode(code string) (bool, error)
Login(c *gin.Context, info dto.Login, entrance string) (*dto.UserLoginInfo, error) Login(c *gin.Context, info dto.Login, entrance string) (*dto.UserLoginInfo, error)
LogOut(c *gin.Context) error LogOut(c *gin.Context) error
@ -185,3 +186,11 @@ func (u *AuthService) CheckIsSafety(code string) (string, error) {
} }
return "unpass", nil return "unpass", nil
} }
func (u *AuthService) GetResponsePage() (string, error) {
pageCode, err := settingRepo.Get(settingRepo.WithByKey("NoAuthSetting"))
if err != nil {
return "", err
}
return pageCode.Value, nil
}

View File

@ -78,6 +78,7 @@ func Init() {
migrations.AddXpackHideMenu, migrations.AddXpackHideMenu,
migrations.AddCronjobCommand, migrations.AddCronjobCommand,
migrations.NewMonitorDB, migrations.NewMonitorDB,
migrations.AddNoAuthSetting,
}) })
if err := m.Migrate(); err != nil { if err := m.Migrate(); err != nil {
global.LOG.Error(err) global.LOG.Error(err)

View File

@ -108,3 +108,13 @@ var NewMonitorDB = &gormigrate.Migration{
return nil return nil
}, },
} }
var AddNoAuthSetting = &gormigrate.Migration{
ID: "20240328-add-no-auth-setting",
Migrate: func(tx *gorm.DB) error {
if err := tx.Create(&model.Setting{Key: "NoAuthSetting", Value: "200"}).Error; err != nil {
return err
}
return nil
},
}

View File

@ -18,5 +18,6 @@ func (s *BaseRouter) InitRouter(Router *gin.RouterGroup) {
baseRouter.POST("/logout", baseApi.LogOut) baseRouter.POST("/logout", baseApi.LogOut)
baseRouter.GET("/demo", baseApi.CheckIsDemo) baseRouter.GET("/demo", baseApi.CheckIsDemo)
baseRouter.GET("/language", baseApi.GetLanguage) baseRouter.GET("/language", baseApi.GetLanguage)
baseRouter.GET("/respagecode", baseApi.GetResponsePage)
} }
} }

View File

@ -47,6 +47,7 @@ export namespace Setting {
dingVars: string; dingVars: string;
snapshotIgnore: string; snapshotIgnore: string;
xpackHideMenu: string; xpackHideMenu: string;
noAuthSetting: string;
} }
export interface SettingUpdate { export interface SettingUpdate {
key: string; key: string;

View File

@ -21,6 +21,10 @@ export const checkIsSafety = (code: string) => {
return http.get<string>(`/auth/issafety?code=${code}`); return http.get<string>(`/auth/issafety?code=${code}`);
}; };
export const getResponsePage = () => {
return http.get<string>(`/auth/respagecode`);
};
export const checkIsDemo = () => { export const checkIsDemo = () => {
return http.get<boolean>('/auth/demo'); return http.get<boolean>('/auth/demo');
}; };

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 21 KiB

View File

@ -0,0 +1,48 @@
<template>
<div class="not-container">
<img src="@/assets/images/error.svg" class="not-img" :alt="props.code" />
<div class="not-detail">
<h2>{{ props.code }}</h2>
<h4>{{ $t('setting.' + 'error' + props.code) }}</h4>
</div>
</div>
</template>
<script setup lang="ts">
const props = defineProps({
code: String,
});
</script>
<style scoped lang="scss">
.not-container {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
.not-img {
margin-top: 300px;
}
.not-detail {
margin-top: 300px;
display: flex;
flex-direction: column;
h2,
h4 {
padding: 0;
margin: 0;
}
h2 {
font-size: 60px;
color: #434e59;
}
h4 {
margin: 30px 0 20px;
font-size: 19px;
font-weight: normal;
color: #848587;
}
}
}
</style>

View File

@ -1388,6 +1388,15 @@ const message = {
sslDisable: 'Disable', sslDisable: 'Disable',
sslDisableHelper: sslDisableHelper:
'If the https service is disabled, you need to restart the panel for it to take effect. Do you want to continue?', 'If the https service is disabled, you need to restart the panel for it to take effect. Do you want to continue?',
noAuthSetting: 'Unauthorized Setting',
responseSetting: 'Response Setting',
help200: '200 - Help Page',
error400: 'Bad Request',
error401: 'Unauthorized',
error403: 'Forbidden',
error404: 'Not Found',
error408: 'Request Timeout',
error416: 'Range Not Satisfiable',
https: 'Setting up HTTPS protocol access for the panel can enhance the security of panel access.', https: 'Setting up HTTPS protocol access for the panel can enhance the security of panel access.',
certType: 'Certificate type', certType: 'Certificate type',

View File

@ -1335,6 +1335,15 @@ const message = {
sslChangeHelper: 'https 設置修改需要重啟服務是否繼續', sslChangeHelper: 'https 設置修改需要重啟服務是否繼續',
sslDisable: '禁用', sslDisable: '禁用',
sslDisableHelper: '禁用 https 服務需要重啟面板才能生效是否繼續', sslDisableHelper: '禁用 https 服務需要重啟面板才能生效是否繼續',
noAuthSetting: '未认证设置',
responseSetting: '响应设置',
help200: '200 - 幫助頁面',
error400: '錯誤請求',
error401: '未授權',
error403: '禁止訪問',
error404: '未找到',
error408: '請求超時',
error416: '無效請求',
https: '為面板設置 https 協議訪問提升面板訪問安全性', https: '為面板設置 https 協議訪問提升面板訪問安全性',
certType: '證書類型', certType: '證書類型',

View File

@ -1336,6 +1336,15 @@ const message = {
sslChangeHelper: 'https 设置修改需要重启服务是否继续', sslChangeHelper: 'https 设置修改需要重启服务是否继续',
sslDisable: '禁用', sslDisable: '禁用',
sslDisableHelper: '禁用 https 服务需要重启面板才能生效是否继续', sslDisableHelper: '禁用 https 服务需要重启面板才能生效是否继续',
noAuthSetting: '未认证设置',
responseSetting: '响应设置',
help200: '200 - 帮助页面',
error400: '错误请求',
error401: '未授权',
error403: '禁止访问',
error404: '未找到',
error408: '请求超时',
error416: '无效请求',
https: '为面板设置 https 协议访问提升面板访问安全性', https: '为面板设置 https 协议访问提升面板访问安全性',
certType: '证书类型', certType: '证书类型',

View File

@ -15,27 +15,29 @@
</div> </div>
</div> </div>
</div> </div>
<div v-if="!isSafety && !isErr && !isNotFound">
<div v-if="pageCode === '200' || !pageCode">
<UnSafe /> <UnSafe />
</div> </div>
<div v-if="pageCode !== '200' && pageCode">
<ErrCode :code="pageCode" />
</div>
<div v-if="isErr && mySafetyCode.code === 'err-ip' && !isNotFound"> <div v-if="isErr && mySafetyCode.code === 'err-ip' && !isNotFound">
<ErrIP /> <ErrIP />
</div> </div>
<div v-if="isErr && mySafetyCode.code === 'err-domain' && !isNotFound"> <div v-if="isErr && mySafetyCode.code === 'err-domain' && !isNotFound">
<ErrDomain /> <ErrDomain />
</div> </div>
<div v-if="isNotFound">
<ErrFound />
</div>
</div> </div>
</template> </template>
<script setup lang="ts" name="login"> <script setup lang="ts" name="login">
import { checkIsSafety } from '@/api/modules/auth'; import { checkIsSafety, getResponsePage } from '@/api/modules/auth';
import LoginForm from '../components/login-form.vue'; import LoginForm from '../components/login-form.vue';
import UnSafe from '@/components/error-message/unsafe.vue'; import UnSafe from '@/components/error-message/unsafe.vue';
import ErrIP from '@/components/error-message/err_ip.vue'; import ErrIP from '@/components/error-message/err_ip.vue';
import ErrFound from '@/components/error-message/404.vue'; import ErrCode from '@/components/error-message/error_code.vue';
import ErrDomain from '@/components/error-message/err_domain.vue'; import ErrDomain from '@/components/error-message/err_domain.vue';
import { ref, onMounted } from 'vue'; import { ref, onMounted } from 'vue';
import { GlobalStore } from '@/store'; import { GlobalStore } from '@/store';
@ -46,6 +48,8 @@ const screenWidth = ref(null);
const isErr = ref(); const isErr = ref();
const isNotFound = ref(); const isNotFound = ref();
const pageCode = ref();
const mySafetyCode = defineProps({ const mySafetyCode = defineProps({
code: { code: {
type: String, type: String,
@ -73,6 +77,8 @@ const getStatus = async () => {
} }
isNotFound.value = false; isNotFound.value = false;
if (res.data !== 'pass') { if (res.data !== 'pass') {
const resCode = await getResponsePage();
pageCode.value = resCode.data;
isSafety.value = false; isSafety.value = false;
return; return;
} }

View File

@ -32,7 +32,7 @@ const screenWidth = ref(null);
const getStatus = async () => { const getStatus = async () => {
const res = await checkIsSafety(globalStore.entrance); const res = await checkIsSafety(globalStore.entrance);
if (res.data === 'unpass') { if (res.data === 'unpass') {
router.replace({ name: 'entrance' }); router.replace({ name: 'entrance', params: { code: 0 } });
} }
}; };

View File

@ -47,6 +47,16 @@
<span class="input-help">{{ $t('setting.entranceHelper') }}</span> <span class="input-help">{{ $t('setting.entranceHelper') }}</span>
</el-form-item> </el-form-item>
<el-form-item :label="$t('setting.noAuthSetting')">
<el-input disabled v-model="form.noAuthSetting">
<template #append>
<el-button @click="onChangeResponse" icon="Setting">
{{ $t('commons.button.set') }}
</el-button>
</template>
</el-input>
</el-form-item>
<el-form-item :label="$t('setting.allowIPs')"> <el-form-item :label="$t('setting.allowIPs')">
<div style="width: 100%" v-if="form.allowIPs"> <div style="width: 100%" v-if="form.allowIPs">
<el-input <el-input
@ -163,6 +173,7 @@
<TimeoutSetting ref="timeoutRef" @search="search" /> <TimeoutSetting ref="timeoutRef" @search="search" />
<DomainSetting ref="domainRef" @search="search" /> <DomainSetting ref="domainRef" @search="search" />
<AllowIPsSetting ref="allowIPsRef" @search="search" /> <AllowIPsSetting ref="allowIPsRef" @search="search" />
<ResponseSetting ref="responseRef" @search="search()" />
</div> </div>
</template> </template>
@ -171,6 +182,7 @@ import { ref, reactive, onMounted } from 'vue';
import { ElForm, ElMessageBox } from 'element-plus'; import { ElForm, ElMessageBox } from 'element-plus';
import PortSetting from '@/views/setting/safe/port/index.vue'; import PortSetting from '@/views/setting/safe/port/index.vue';
import BindSetting from '@/views/setting/safe/bind/index.vue'; import BindSetting from '@/views/setting/safe/bind/index.vue';
import ResponseSetting from '@/views/setting/safe/response/index.vue';
import SSLSetting from '@/views/setting/safe/ssl/index.vue'; import SSLSetting from '@/views/setting/safe/ssl/index.vue';
import MfaSetting from '@/views/setting/safe/mfa/index.vue'; import MfaSetting from '@/views/setting/safe/mfa/index.vue';
import TimeoutSetting from '@/views/setting/safe/timeout/index.vue'; import TimeoutSetting from '@/views/setting/safe/timeout/index.vue';
@ -190,6 +202,7 @@ const portRef = ref();
const bindRef = ref(); const bindRef = ref();
const timeoutRef = ref(); const timeoutRef = ref();
const mfaRef = ref(); const mfaRef = ref();
const responseRef = ref();
const sslRef = ref(); const sslRef = ref();
const sslInfo = ref<Setting.SSLInfo>(); const sslInfo = ref<Setting.SSLInfo>();
@ -210,8 +223,40 @@ const form = reactive({
mfaInterval: 30, mfaInterval: 30,
allowIPs: '', allowIPs: '',
bindDomain: '', bindDomain: '',
noAuthSetting: i18n.global.t('setting.help200'),
}); });
const noAuthOptions = [
{
value: '200',
label: i18n.global.t('setting.help200'),
},
{
value: '400',
label: '400 - ' + i18n.global.t('setting.error400'),
},
{
value: '401',
label: '401 - ' + i18n.global.t('setting.error401'),
},
{
value: '403',
label: '403 - ' + i18n.global.t('setting.error403'),
},
{
value: '404',
label: '404 - ' + i18n.global.t('setting.error404'),
},
{
value: '408',
label: '408 - ' + i18n.global.t('setting.error408'),
},
{
value: '416',
label: '416 - ' + i18n.global.t('setting.error416'),
},
];
const unset = ref(i18n.global.t('setting.unSetting')); const unset = ref(i18n.global.t('setting.unSetting'));
const search = async () => { const search = async () => {
@ -232,6 +277,12 @@ const search = async () => {
form.mfaInterval = Number(res.data.mfaInterval); form.mfaInterval = Number(res.data.mfaInterval);
form.allowIPs = res.data.allowIPs.replaceAll(',', '\n'); form.allowIPs = res.data.allowIPs.replaceAll(',', '\n');
form.bindDomain = res.data.bindDomain; form.bindDomain = res.data.bindDomain;
for (const item of noAuthOptions) {
if (item.value === res.data.noAuthSetting) {
form.noAuthSetting = item.label;
}
}
}; };
const onSaveComplexity = async () => { const onSaveComplexity = async () => {
@ -277,6 +328,9 @@ const onChangePort = () => {
const onChangeBind = () => { const onChangeBind = () => {
bindRef.value.acceptParams({ ipv6: form.ipv6, bindAddress: form.bindAddress }); bindRef.value.acceptParams({ ipv6: form.ipv6, bindAddress: form.bindAddress });
}; };
const onChangeResponse = () => {
responseRef.value.acceptParams({ noAuthSetting: form.noAuthSetting, noAuthOptions: noAuthOptions });
};
const onChangeBindDomain = () => { const onChangeBindDomain = () => {
domainRef.value.acceptParams({ bindDomain: form.bindDomain }); domainRef.value.acceptParams({ bindDomain: form.bindDomain });
}; };

View File

@ -0,0 +1,97 @@
<template>
<div>
<el-drawer v-model="drawerVisible" :destroy-on-close="true" :close-on-click-modal="false" size="30%">
<template #header>
<DrawerHeader :header="$t('setting.noAuthSetting')" :back="handleClose" />
</template>
<el-form ref="formRef" label-position="top" :model="form" @submit.prevent v-loading="loading">
<el-row type="flex" justify="center">
<el-col :span="22">
<el-form-item
:label="$t('setting.responseSetting')"
prop="noAuthSetting"
:rules="Rules.requiredSelect"
>
<el-select v-model="form.noAuthSetting" filterable>
<el-option
v-for="item in options"
:key="item"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="drawerVisible = false">{{ $t('commons.button.cancel') }}</el-button>
<el-button :disabled="loading" type="primary" @click="onSave(formRef)">
{{ $t('commons.button.confirm') }}
</el-button>
</span>
</template>
</el-drawer>
</div>
</template>
<script setup lang="ts">
import { reactive, ref } from 'vue';
import DrawerHeader from '@/components/drawer-header/index.vue';
import { FormInstance } from 'element-plus';
import { Rules } from '@/global/form-rules';
import { updateSetting } from '@/api/modules/setting';
import { MsgSuccess } from '@/utils/message';
import i18n from '@/lang';
const drawerVisible = ref();
const loading = ref();
const formRef = ref<FormInstance>();
const emit = defineEmits<{ (e: 'search'): void }>();
const form = reactive({
noAuthSetting: '',
});
const options = ref([]);
interface DialogProps {
noAuthSetting: string;
noAuthOptions: [{ value: string; label: string }];
}
const acceptParams = (params: DialogProps): void => {
options.value = params.noAuthOptions;
form.noAuthSetting = params.noAuthSetting;
drawerVisible.value = true;
};
const onSave = async (formEl: FormInstance | undefined) => {
if (!formEl) return;
await formEl.validate(async (valid) => {
if (!valid) return;
loading.value = true;
await updateSetting({ key: 'NoAuthSetting', value: form.noAuthSetting })
.then(() => {
loading.value = false;
handleClose();
emit('search');
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
})
.catch(() => {
loading.value = false;
});
});
};
const handleClose = () => {
drawerVisible.value = false;
};
defineExpose({
acceptParams,
});
</script>
<style scoped lang="scss"></style>