1
0
mirror of https://github.com/1Panel-dev/1Panel.git synced 2025-03-14 01:34:47 +08:00

feat: 时间同步增加时区、同步地址自定义设置 (#1102)

This commit is contained in:
ssongliu 2023-05-22 17:45:39 +08:00 committed by GitHub
parent ce19107c95
commit 53600900f2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 281 additions and 74 deletions

View File

@ -2,14 +2,12 @@ package v1
import ( import (
"errors" "errors"
"time"
"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/constant" "github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/global" "github.com/1Panel-dev/1Panel/backend/global"
"github.com/1Panel-dev/1Panel/backend/utils/mfa" "github.com/1Panel-dev/1Panel/backend/utils/mfa"
"github.com/1Panel-dev/1Panel/backend/utils/ntp"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
@ -189,25 +187,40 @@ func (b *BaseApi) HandlePasswordExpired(c *gin.Context) {
} }
// @Tags System Setting // @Tags System Setting
// @Summary Sync system time // @Summary Load time zone options
// @Description 系统时间同步 // @Description 加载系统可用时区
// @Success 200 {string} ntime // @Success 200 {string}
// @Security ApiKeyAuth // @Security ApiKeyAuth
// @Router /settings/time/sync [post] // @Router /settings/time/option [get]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFuntions":[],"formatZH":"系统时间同步","formatEN":"sync system time"} func (b *BaseApi) LoadTimeZone(c *gin.Context) {
func (b *BaseApi) SyncTime(c *gin.Context) { zones, err := settingService.LoadTimeZone()
ntime, err := ntp.GetRemoteTime()
if err != nil { if err != nil {
helper.SuccessWithData(c, time.Now().Format("2006-01-02 15:04:05 MST -0700"))
return
}
ts := ntime.Format("2006-01-02 15:04:05")
if err := ntp.UpdateSystemDate(ts); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return return
} }
helper.SuccessWithData(c, zones)
}
// @Tags System Setting
// @Summary Sync system time
// @Description 系统时间同步
// @Accept json
// @Param request body dto.SyncTimeZone true "request"
// @Success 200 {string} ntime
// @Security ApiKeyAuth
// @Router /settings/time/sync [post]
// @x-panel-log {"bodyKeys":["ntpSite", "timeZone"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"系统时间同步[ntpSite]-[timeZone]","formatEN":"sync system time [ntpSite]-[timeZone]"}
func (b *BaseApi) SyncTime(c *gin.Context) {
var req dto.SyncTimeZone
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
ntime, err := settingService.SyncTime(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, ntime.Format("2006-01-02 15:04:05 MST -0700")) helper.SuccessWithData(c, ntime.Format("2006-01-02 15:04:05 MST -0700"))
} }

View File

@ -9,6 +9,8 @@ type SettingInfo struct {
SessionTimeout string `json:"sessionTimeout"` SessionTimeout string `json:"sessionTimeout"`
LocalTime string `json:"localTime"` LocalTime string `json:"localTime"`
TimeZone string `json:"timeZone"`
NtpSite string `json:"ntpSite"`
Port string `json:"port"` Port string `json:"port"`
PanelName string `json:"panelName"` PanelName string `json:"panelName"`
@ -109,6 +111,11 @@ type UpgradeInfo struct {
ReleaseNote string `json:"releaseNote"` ReleaseNote string `json:"releaseNote"`
} }
type SyncTimeZone struct {
NtpSite string `json:"ntpSite"`
TimeZone string `json:"timeZone"`
}
type Upgrade struct { type Upgrade struct {
Version string `json:"version"` Version string `json:"version"`
} }

View File

@ -19,6 +19,7 @@ import (
"github.com/1Panel-dev/1Panel/backend/utils/common" "github.com/1Panel-dev/1Panel/backend/utils/common"
"github.com/1Panel-dev/1Panel/backend/utils/encrypt" "github.com/1Panel-dev/1Panel/backend/utils/encrypt"
"github.com/1Panel-dev/1Panel/backend/utils/files" "github.com/1Panel-dev/1Panel/backend/utils/files"
"github.com/1Panel-dev/1Panel/backend/utils/ntp"
"github.com/1Panel-dev/1Panel/backend/utils/ssl" "github.com/1Panel-dev/1Panel/backend/utils/ssl"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
@ -27,12 +28,14 @@ type SettingService struct{}
type ISettingService interface { type ISettingService interface {
GetSettingInfo() (*dto.SettingInfo, error) GetSettingInfo() (*dto.SettingInfo, error)
LoadTimeZone() ([]string, error)
Update(key, value string) error Update(key, value string) error
UpdatePassword(c *gin.Context, old, new string) error UpdatePassword(c *gin.Context, old, new string) error
UpdatePort(port uint) error UpdatePort(port uint) error
UpdateSSL(c *gin.Context, req dto.SSLUpdate) error UpdateSSL(c *gin.Context, req dto.SSLUpdate) error
LoadFromCert() (*dto.SSLInfo, error) LoadFromCert() (*dto.SSLInfo, error)
HandlePasswordExpired(c *gin.Context, old, new string) error HandlePasswordExpired(c *gin.Context, old, new string) error
SyncTime(req dto.SyncTimeZone) (time.Time, error)
} }
func NewISettingService() ISettingService { func NewISettingService() ISettingService {
@ -60,6 +63,14 @@ func (u *SettingService) GetSettingInfo() (*dto.SettingInfo, error) {
return &info, err return &info, err
} }
func (u *SettingService) LoadTimeZone() ([]string, error) {
std, err := cmd.Exec("timedatectl list-timezones")
if err != nil {
return []string{}, nil
}
return strings.Split(std, "\n"), err
}
func (u *SettingService) Update(key, value string) error { func (u *SettingService) Update(key, value string) error {
if key == "ExpirationDays" { if key == "ExpirationDays" {
timeout, _ := strconv.Atoi(value) timeout, _ := strconv.Atoi(value)
@ -82,6 +93,27 @@ func (u *SettingService) Update(key, value string) error {
return nil return nil
} }
func (u *SettingService) SyncTime(req dto.SyncTimeZone) (time.Time, error) {
ntime, err := ntp.GetRemoteTime(req.NtpSite)
if err != nil {
return ntime, err
}
ts := ntime.Format("2006-01-02 15:04:05")
if err := ntp.UpdateSystemTime(ts, req.TimeZone); err != nil {
return ntime, err
}
if err := settingRepo.Update("TimeZone", req.TimeZone); err != nil {
return ntime, err
}
if err := settingRepo.Update("NtpSite", req.NtpSite); err != nil {
return ntime, err
}
return ntime, nil
}
func (u *SettingService) UpdatePort(port uint) error { func (u *SettingService) UpdatePort(port uint) error {
if common.ScanPort(int(port)) { if common.ScanPort(int(port)) {
return buserr.WithDetail(constant.ErrPortInUsed, port, nil) return buserr.WithDetail(constant.ErrPortInUsed, port, nil)

View File

@ -345,6 +345,12 @@ var AddBindAndAllowIPs = &gormigrate.Migration{
if err := tx.Create(&model.Setting{Key: "AllowIPs", Value: ""}).Error; err != nil { if err := tx.Create(&model.Setting{Key: "AllowIPs", Value: ""}).Error; err != nil {
return err return err
} }
if err := tx.Create(&model.Setting{Key: "TimeZone", Value: common.LoadTimeZone()}).Error; err != nil {
return err
}
if err := tx.Create(&model.Setting{Key: "NtpSite", Value: "pool.ntp.org:123"}).Error; err != nil {
return err
}
return nil return nil
}, },
} }

View File

@ -26,6 +26,7 @@ func (s *SettingRouter) InitSettingRouter(Router *gin.RouterGroup) {
settingRouter.POST("/ssl/update", baseApi.UpdateSSL) settingRouter.POST("/ssl/update", baseApi.UpdateSSL)
settingRouter.GET("/ssl/info", baseApi.LoadFromCert) settingRouter.GET("/ssl/info", baseApi.LoadFromCert)
settingRouter.POST("/password/update", baseApi.UpdatePassword) settingRouter.POST("/password/update", baseApi.UpdatePassword)
settingRouter.GET("/time/option", baseApi.LoadTimeZone)
settingRouter.POST("/time/sync", baseApi.SyncTime) settingRouter.POST("/time/sync", baseApi.SyncTime)
settingRouter.POST("/monitor/clean", baseApi.CleanMonitor) settingRouter.POST("/monitor/clean", baseApi.CleanMonitor)
settingRouter.GET("/mfa", baseApi.GetMFA) settingRouter.GET("/mfa", baseApi.GetMFA)

View File

@ -121,3 +121,11 @@ func HasNoPasswordSudo() bool {
err2 := cmd2.Run() err2 := cmd2.Run()
return err2 == nil return err2 == nil
} }
func SudoHandleCmd() string {
cmd := exec.Command("sudo", "-n", "ls")
if err := cmd.Run(); err == nil {
return "sudo "
}
return ""
}

View File

@ -30,8 +30,8 @@ type packet struct {
TxTimeFrac uint32 TxTimeFrac uint32
} }
func GetRemoteTime() (time.Time, error) { func GetRemoteTime(site string) (time.Time, error) {
conn, err := net.Dial("udp", "pool.ntp.org:123") conn, err := net.Dial("udp", site)
if err != nil { if err != nil {
return time.Time{}, fmt.Errorf("failed to connect: %v", err) return time.Time{}, fmt.Errorf("failed to connect: %v", err)
} }
@ -59,11 +59,17 @@ func GetRemoteTime() (time.Time, error) {
return showtime, nil return showtime, nil
} }
func UpdateSystemDate(dateTime string) error { func UpdateSystemTime(dateTime, timezone string) error {
system := runtime.GOOS system := runtime.GOOS
if system == "linux" { if system == "linux" {
if _, err := cmd.Execf(`date -s "` + dateTime + `"`); err != nil { stdout, err := cmd.Execf(`%s timedatectl set-timezone "%s"`, cmd.SudoHandleCmd(), timezone)
return fmt.Errorf("update system date failed, err: %v", err) if err != nil {
return fmt.Errorf("update system time zone failed, stdout: %s, err: %v", stdout, err)
}
stdout2, err := cmd.Execf(`%s timedatectl set-time "%s"`, cmd.SudoHandleCmd(), dateTime)
if err != nil {
return fmt.Errorf("update system time failed,stdout: %s, err: %v", stdout2, err)
} }
return nil return nil
} }

View File

@ -9,6 +9,8 @@ export namespace Setting {
sessionTimeout: number; sessionTimeout: number;
localTime: string; localTime: string;
timeZone: string;
ntpSite: string;
panelName: string; panelName: string;
theme: string; theme: string;

View File

@ -35,8 +35,11 @@ export const handleExpired = (param: Setting.PasswordUpdate) => {
return http.post(`/settings/expired/handle`, param); return http.post(`/settings/expired/handle`, param);
}; };
export const syncTime = () => { export const loadTimeZone = () => {
return http.post<string>(`/settings/time/sync`, {}); return http.get<Array<string>>(`/settings/time/option`);
};
export const syncTime = (timeZone: string, ntpSite: string) => {
return http.post<string>(`/settings/time/sync`, { timeZone: timeZone, ntpSite: ntpSite });
}; };
export const cleanMonitors = () => { export const cleanMonitors = () => {

View File

@ -885,7 +885,12 @@ const message = {
sessionTimeoutHelper: sessionTimeoutHelper:
'If you do not operate the panel for more than {0} seconds, the panel automatically logs out', 'If you do not operate the panel for more than {0} seconds, the panel automatically logs out',
syncTime: 'Server time', syncTime: 'Server time',
second: ' S', timeZone: 'Time Zone',
timeZoneHelper: 'Timezone modification depends on the system timedatectl service.',
timeZoneCN: 'Bei Jing',
timeZoneAM: 'Los Angeles',
timeZoneNY: 'New York',
syncSite: 'Ntp Server',
changePassword: 'Password change', changePassword: 'Password change',
oldPassword: 'Original password', oldPassword: 'Original password',
newPassword: 'New password', newPassword: 'New password',
@ -949,6 +954,7 @@ const message = {
allowIPsWarnning: allowIPsWarnning:
'设After setting the authorized IP address, only the IP address in the setting can access the 1Panel service. Do you want to continue?', '设After setting the authorized IP address, only the IP address in the setting can access the 1Panel service. Do you want to continue?',
allowIPsHelper1: 'If the authorized IP address is empty, the authorized IP address is canceled', allowIPsHelper1: 'If the authorized IP address is empty, the authorized IP address is canceled',
allowIPEgs: 'e.g. 172.16.10.111',
mfa: 'MFA', mfa: 'MFA',
mfaAlert: mfaAlert:
'MFA password is generated based on the current time. Please ensure that the server time is synchronized.', 'MFA password is generated based on the current time. Please ensure that the server time is synchronized.',

View File

@ -235,7 +235,7 @@ const message = {
terminal: '终端', terminal: '终端',
settings: '面板设置', settings: '面板设置',
toolbox: '工具箱', toolbox: '工具箱',
logs: '面板日志', logs: '日志审计',
runtime: '运行环境', runtime: '运行环境',
}, },
home: { home: {
@ -741,7 +741,7 @@ const message = {
commands: '快捷命令', commands: '快捷命令',
files: '文件管理', files: '文件管理',
backups: '备份账号', backups: '备份账号',
logs: '面板日志', logs: '日志审计',
settings: '面板设置', settings: '面板设置',
cronjobs: '计划任务', cronjobs: '计划任务',
databases: '数据库', databases: '数据库',
@ -885,7 +885,12 @@ const message = {
sessionTimeoutError: '最小超时时间为 300 ', sessionTimeoutError: '最小超时时间为 300 ',
sessionTimeoutHelper: '如果用户超过 {0} 秒未操作面板面板将自动退出登录', sessionTimeoutHelper: '如果用户超过 {0} 秒未操作面板面板将自动退出登录',
syncTime: '服务器时间', syncTime: '服务器时间',
second: ' ', timeZone: '时区',
timeZoneHelper: '时区修改依赖于系统 timedatectl 服务',
timeZoneCN: '北京',
timeZoneAM: '洛杉矶',
timeZoneNY: '纽约',
syncSite: '同步地址',
changePassword: '密码修改', changePassword: '密码修改',
oldPassword: '原密码', oldPassword: '原密码',
newPassword: '新密码', newPassword: '新密码',
@ -976,6 +981,7 @@ const message = {
allowIPsHelper: '设置授权 IP 仅有设置中的 IP 可以访问 1Panel 服务', allowIPsHelper: '设置授权 IP 仅有设置中的 IP 可以访问 1Panel 服务',
allowIPsWarnning: '设置授权 IP 仅有设置中的 IP 可以访问 1Panel 服务是否继续', allowIPsWarnning: '设置授权 IP 仅有设置中的 IP 可以访问 1Panel 服务是否继续',
allowIPsHelper1: '授权 IP 为空时则取消授权 IP', allowIPsHelper1: '授权 IP 为空时则取消授权 IP',
allowIPEgs: '172.16.10.111',
mfa: '两步验证', mfa: '两步验证',
mfaAlert: '两步验证密码是基于当前时间生成请确保服务器时间已同步', mfaAlert: '两步验证密码是基于当前时间生成请确保服务器时间已同步',
mfaHelper: '开启后会验证手机应用验证码', mfaHelper: '开启后会验证手机应用验证码',

View File

@ -76,12 +76,9 @@
<el-form-item :label="$t('setting.syncTime')"> <el-form-item :label="$t('setting.syncTime')">
<el-input disabled v-model="form.localTime"> <el-input disabled v-model="form.localTime">
<template #append> <template #append>
<el-button v-show="!show" @click="onSyncTime" icon="Refresh"> <el-button v-show="!show" @click="onChangeNtp" icon="Setting">
{{ $t('commons.button.sync') }} {{ $t('commons.button.set') }}
</el-button> </el-button>
<div style="width: 45px" v-show="show">
<span>{{ count }} {{ $t('setting.second') }}</span>
</div>
</template> </template>
</el-input> </el-input>
</el-form-item> </el-form-item>
@ -95,6 +92,7 @@
<UserName ref="userNameRef" /> <UserName ref="userNameRef" />
<PanelName ref="panelNameRef" @search="search()" /> <PanelName ref="panelNameRef" @search="search()" />
<Timeout ref="timeoutRef" @search="search()" /> <Timeout ref="timeoutRef" @search="search()" />
<Ntp ref="ntpRef" @search="search()" />
</div> </div>
</template> </template>
@ -102,7 +100,7 @@
import { ref, reactive, onMounted, computed } from 'vue'; import { ref, reactive, onMounted, computed } from 'vue';
import { ElForm } from 'element-plus'; import { ElForm } from 'element-plus';
import LayoutContent from '@/layout/layout-content.vue'; import LayoutContent from '@/layout/layout-content.vue';
import { syncTime, getSettingInfo, updateSetting, getSystemAvailable } from '@/api/modules/setting'; import { getSettingInfo, updateSetting, getSystemAvailable } from '@/api/modules/setting';
import { GlobalStore } from '@/store'; import { GlobalStore } from '@/store';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useTheme } from '@/hooks/use-theme'; import { useTheme } from '@/hooks/use-theme';
@ -111,6 +109,7 @@ import Password from '@/views/setting/panel/password/index.vue';
import UserName from '@/views/setting/panel/username/index.vue'; import UserName from '@/views/setting/panel/username/index.vue';
import Timeout from '@/views/setting/panel/timeout/index.vue'; import Timeout from '@/views/setting/panel/timeout/index.vue';
import PanelName from '@/views/setting/panel/name/index.vue'; import PanelName from '@/views/setting/panel/name/index.vue';
import Ntp from '@/views/setting/panel/ntp/index.vue';
const loading = ref(false); const loading = ref(false);
const i18n = useI18n(); const i18n = useI18n();
@ -124,21 +123,21 @@ const form = reactive({
email: '', email: '',
sessionTimeout: 0, sessionTimeout: 0,
localTime: '', localTime: '',
timeZone: '',
ntpSite: '',
panelName: '', panelName: '',
theme: '', theme: '',
language: '', language: '',
complexityVerification: '', complexityVerification: '',
}); });
const timer = ref();
const TIME_COUNT = ref(10);
const count = ref();
const show = ref(); const show = ref();
const userNameRef = ref(); const userNameRef = ref();
const passwordRef = ref(); const passwordRef = ref();
const panelNameRef = ref(); const panelNameRef = ref();
const timeoutRef = ref(); const timeoutRef = ref();
const ntpRef = ref();
const search = async () => { const search = async () => {
const res = await getSettingInfo(); const res = await getSettingInfo();
@ -146,6 +145,8 @@ const search = async () => {
form.password = '******'; form.password = '******';
form.sessionTimeout = Number(res.data.sessionTimeout); form.sessionTimeout = Number(res.data.sessionTimeout);
form.localTime = res.data.localTime; form.localTime = res.data.localTime;
form.timeZone = res.data.timeZone;
form.ntpSite = res.data.ntpSite;
form.panelName = res.data.panelName; form.panelName = res.data.panelName;
form.theme = res.data.theme; form.theme = res.data.theme;
form.language = res.data.language; form.language = res.data.language;
@ -164,6 +165,9 @@ const onChangeTitle = () => {
const onChangeTimeout = () => { const onChangeTimeout = () => {
timeoutRef.value.acceptParams({ sessionTimeout: form.sessionTimeout }); timeoutRef.value.acceptParams({ sessionTimeout: form.sessionTimeout });
}; };
const onChangeNtp = () => {
ntpRef.value.acceptParams({ localTime: form.localTime, timeZone: form.timeZone, ntpSite: form.ntpSite });
};
const onSave = async (key: string, val: any) => { const onSave = async (key: string, val: any) => {
loading.value = true; loading.value = true;
@ -193,34 +197,6 @@ const onSave = async (key: string, val: any) => {
}); });
}; };
function countdown() {
count.value = TIME_COUNT.value;
show.value = true;
timer.value = setInterval(() => {
if (count.value > 0 && count.value <= TIME_COUNT.value) {
count.value--;
} else {
show.value = false;
clearInterval(timer.value);
timer.value = null;
}
}, 1000);
}
const onSyncTime = async () => {
loading.value = true;
await syncTime()
.then((res) => {
loading.value = false;
form.localTime = res.data;
countdown();
MsgSuccess(i18n.t('commons.msg.operationSuccess'));
})
.catch(() => {
loading.value = false;
});
};
onMounted(() => { onMounted(() => {
search(); search();
getSystemAvailable(); getSystemAvailable();

View File

@ -0,0 +1,148 @@
<template>
<div>
<el-drawer v-model="drawerVisiable" :destroy-on-close="true" :close-on-click-modal="false" size="30%">
<template #header>
<DrawerHeader :header="$t('setting.syncTime')" :back="handleClose" />
</template>
<el-alert v-if="canChangeZone()" style="margin-bottom: 20px" :closable="false" type="warning">
<template #default>
<span>
<span>{{ $t('setting.timeZoneHelper') }}</span>
</span>
</template>
</el-alert>
<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.timeZone')" prop="timeZone" :rules="Rules.requiredInput">
<el-select filterable :disabled="canChangeZone()" v-model="form.timeZone">
<el-option v-for="item in zones" :key="item" :label="item" :value="item" />
</el-select>
<el-button
:disabled="canChangeZone()"
type="primary"
link
class="tagClass"
@click="form.timeZone = 'Asia/Shanghai'"
>
{{ $t('setting.timeZoneCN') }}
</el-button>
<el-button
:disabled="canChangeZone()"
type="primary"
link
class="tagClass"
@click="form.timeZone = 'America/Los_Angeles'"
>
{{ $t('setting.timeZoneAM') }}
</el-button>
<el-button
:disabled="canChangeZone()"
type="primary"
link
class="tagClass"
@click="form.timeZone = 'America/New_York'"
>
{{ $t('setting.timeZoneNY') }}
</el-button>
</el-form-item>
<el-form-item :label="$t('setting.syncTime')" prop="localTime">
<el-input v-model="form.localTime" disabled />
</el-form-item>
<el-form-item :label="$t('setting.syncSite')" prop="ntpSite" :rules="Rules.requiredInput">
<el-input v-model="form.ntpSite" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="drawerVisiable = false">{{ $t('commons.button.cancel') }}</el-button>
<el-button :disabled="loading" type="primary" @click="onSyncTime(formRef)">
{{ $t('commons.button.sync') }}
</el-button>
</span>
</template>
</el-drawer>
</div>
</template>
<script lang="ts" setup>
import { reactive, ref } from 'vue';
import i18n from '@/lang';
import { MsgSuccess } from '@/utils/message';
import { loadTimeZone, syncTime } from '@/api/modules/setting';
import { FormInstance } from 'element-plus';
import { Rules } from '@/global/form-rules';
const emit = defineEmits<{ (e: 'search'): void }>();
interface DialogProps {
timeZone: string;
localTime: string;
ntpSite: string;
}
const drawerVisiable = ref();
const loading = ref();
const zones = ref<Array<string>>([]);
const oldTimeZone = ref();
const form = reactive({
timeZone: '',
localTime: '',
ntpSite: '',
});
const formRef = ref<FormInstance>();
const acceptParams = (params: DialogProps): void => {
loadTimeZones();
oldTimeZone.value = params.timeZone;
form.timeZone = params.timeZone;
form.localTime = params.localTime;
form.ntpSite = params.ntpSite;
drawerVisiable.value = true;
};
const canChangeZone = () => {
return zones.value.length === 0;
};
const loadTimeZones = async () => {
const res = await loadTimeZone();
zones.value = res.data;
};
const onSyncTime = async (formEl: FormInstance | undefined) => {
if (!formEl) return;
formEl.validate(async (valid) => {
if (!valid) return;
loading.value = true;
await syncTime(form.timeZone, form.ntpSite)
.then((res) => {
loading.value = false;
form.localTime = res.data;
emit('search');
handleClose();
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
})
.catch(() => {
loading.value = false;
});
});
};
const handleClose = () => {
drawerVisiable.value = false;
};
defineExpose({
acceptParams,
});
</script>
<style scoped lang="scss">
.tagClass {
margin-top: 5px;
}
</style>

View File

@ -11,14 +11,14 @@
<table style="width: 100%" class="tab-table"> <table style="width: 100%" class="tab-table">
<tr v-if="allowIPs.length !== 0"> <tr v-if="allowIPs.length !== 0">
<th scope="col" width="90%" align="left"> <th scope="col" width="90%" align="left">
<label>IP</label> <label>{{ $t('setting.allowIPs') }}</label>
</th> </th>
<th align="left"></th> <th align="left"></th>
</tr> </tr>
<tr v-for="(row, index) in allowIPs" :key="index"> <tr v-for="(row, index) in allowIPs" :key="index">
<td width="90%"> <td width="90%">
<el-input <el-input
:placeholder="$t('container.serverExample')" :placeholder="$t('setting.allowIPEgs')"
style="width: 100%" style="width: 100%"
v-model="row.value" v-model="row.value"
/> />

View File

@ -10,21 +10,14 @@
<template #header> <template #header>
<DrawerHeader :header="$t('setting.mfa')" :back="handleClose" /> <DrawerHeader :header="$t('setting.mfa')" :back="handleClose" />
</template> </template>
<el-alert :closable="false" type="warning"> <el-alert style="margin-bottom: 20px" :closable="false" type="warning">
<template #default> <template #default>
<span> <span>
<span>{{ $t('setting.mfaAlert') }}</span> <span>{{ $t('setting.mfaAlert') }}</span>
</span> </span>
</template> </template>
</el-alert> </el-alert>
<el-form <el-form :model="form" ref="formRef" @submit.prevent v-loading="loading" label-position="top">
:model="form"
style="margin-top: 20px"
ref="formRef"
@submit.prevent
v-loading="loading"
label-position="top"
>
<el-row type="flex" justify="center"> <el-row type="flex" justify="center">
<el-col :span="22"> <el-col :span="22">
<el-form-item :label="$t('setting.mfaHelper1')"> <el-form-item :label="$t('setting.mfaHelper1')">