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:
parent
ce19107c95
commit
53600900f2
@ -2,14 +2,12 @@ package v1
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
|
||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||
"github.com/1Panel-dev/1Panel/backend/global"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/mfa"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/ntp"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
@ -189,25 +187,40 @@ func (b *BaseApi) HandlePasswordExpired(c *gin.Context) {
|
||||
}
|
||||
|
||||
// @Tags System Setting
|
||||
// @Summary Sync system time
|
||||
// @Description 系统时间同步
|
||||
// @Success 200 {string} ntime
|
||||
// @Summary Load time zone options
|
||||
// @Description 加载系统可用时区
|
||||
// @Success 200 {string}
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /settings/time/sync [post]
|
||||
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFuntions":[],"formatZH":"系统时间同步","formatEN":"sync system time"}
|
||||
func (b *BaseApi) SyncTime(c *gin.Context) {
|
||||
ntime, err := ntp.GetRemoteTime()
|
||||
// @Router /settings/time/option [get]
|
||||
func (b *BaseApi) LoadTimeZone(c *gin.Context) {
|
||||
zones, err := settingService.LoadTimeZone()
|
||||
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)
|
||||
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"))
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,8 @@ type SettingInfo struct {
|
||||
|
||||
SessionTimeout string `json:"sessionTimeout"`
|
||||
LocalTime string `json:"localTime"`
|
||||
TimeZone string `json:"timeZone"`
|
||||
NtpSite string `json:"ntpSite"`
|
||||
|
||||
Port string `json:"port"`
|
||||
PanelName string `json:"panelName"`
|
||||
@ -109,6 +111,11 @@ type UpgradeInfo struct {
|
||||
ReleaseNote string `json:"releaseNote"`
|
||||
}
|
||||
|
||||
type SyncTimeZone struct {
|
||||
NtpSite string `json:"ntpSite"`
|
||||
TimeZone string `json:"timeZone"`
|
||||
}
|
||||
|
||||
type Upgrade struct {
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
@ -19,6 +19,7 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/common"
|
||||
"github.com/1Panel-dev/1Panel/backend/utils/encrypt"
|
||||
"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/gin-gonic/gin"
|
||||
)
|
||||
@ -27,12 +28,14 @@ type SettingService struct{}
|
||||
|
||||
type ISettingService interface {
|
||||
GetSettingInfo() (*dto.SettingInfo, error)
|
||||
LoadTimeZone() ([]string, error)
|
||||
Update(key, value string) error
|
||||
UpdatePassword(c *gin.Context, old, new string) error
|
||||
UpdatePort(port uint) error
|
||||
UpdateSSL(c *gin.Context, req dto.SSLUpdate) error
|
||||
LoadFromCert() (*dto.SSLInfo, error)
|
||||
HandlePasswordExpired(c *gin.Context, old, new string) error
|
||||
SyncTime(req dto.SyncTimeZone) (time.Time, error)
|
||||
}
|
||||
|
||||
func NewISettingService() ISettingService {
|
||||
@ -60,6 +63,14 @@ func (u *SettingService) GetSettingInfo() (*dto.SettingInfo, error) {
|
||||
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 {
|
||||
if key == "ExpirationDays" {
|
||||
timeout, _ := strconv.Atoi(value)
|
||||
@ -82,6 +93,27 @@ func (u *SettingService) Update(key, value string) error {
|
||||
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 {
|
||||
if common.ScanPort(int(port)) {
|
||||
return buserr.WithDetail(constant.ErrPortInUsed, port, nil)
|
||||
|
@ -345,6 +345,12 @@ var AddBindAndAllowIPs = &gormigrate.Migration{
|
||||
if err := tx.Create(&model.Setting{Key: "AllowIPs", Value: ""}).Error; err != nil {
|
||||
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
|
||||
},
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ func (s *SettingRouter) InitSettingRouter(Router *gin.RouterGroup) {
|
||||
settingRouter.POST("/ssl/update", baseApi.UpdateSSL)
|
||||
settingRouter.GET("/ssl/info", baseApi.LoadFromCert)
|
||||
settingRouter.POST("/password/update", baseApi.UpdatePassword)
|
||||
settingRouter.GET("/time/option", baseApi.LoadTimeZone)
|
||||
settingRouter.POST("/time/sync", baseApi.SyncTime)
|
||||
settingRouter.POST("/monitor/clean", baseApi.CleanMonitor)
|
||||
settingRouter.GET("/mfa", baseApi.GetMFA)
|
||||
|
@ -121,3 +121,11 @@ func HasNoPasswordSudo() bool {
|
||||
err2 := cmd2.Run()
|
||||
return err2 == nil
|
||||
}
|
||||
|
||||
func SudoHandleCmd() string {
|
||||
cmd := exec.Command("sudo", "-n", "ls")
|
||||
if err := cmd.Run(); err == nil {
|
||||
return "sudo "
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
@ -30,8 +30,8 @@ type packet struct {
|
||||
TxTimeFrac uint32
|
||||
}
|
||||
|
||||
func GetRemoteTime() (time.Time, error) {
|
||||
conn, err := net.Dial("udp", "pool.ntp.org:123")
|
||||
func GetRemoteTime(site string) (time.Time, error) {
|
||||
conn, err := net.Dial("udp", site)
|
||||
if err != nil {
|
||||
return time.Time{}, fmt.Errorf("failed to connect: %v", err)
|
||||
}
|
||||
@ -59,11 +59,17 @@ func GetRemoteTime() (time.Time, error) {
|
||||
return showtime, nil
|
||||
}
|
||||
|
||||
func UpdateSystemDate(dateTime string) error {
|
||||
func UpdateSystemTime(dateTime, timezone string) error {
|
||||
system := runtime.GOOS
|
||||
if system == "linux" {
|
||||
if _, err := cmd.Execf(`date -s "` + dateTime + `"`); err != nil {
|
||||
return fmt.Errorf("update system date failed, err: %v", err)
|
||||
stdout, err := cmd.Execf(`%s timedatectl set-timezone "%s"`, cmd.SudoHandleCmd(), timezone)
|
||||
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
|
||||
}
|
||||
|
@ -9,6 +9,8 @@ export namespace Setting {
|
||||
|
||||
sessionTimeout: number;
|
||||
localTime: string;
|
||||
timeZone: string;
|
||||
ntpSite: string;
|
||||
|
||||
panelName: string;
|
||||
theme: string;
|
||||
|
@ -35,8 +35,11 @@ export const handleExpired = (param: Setting.PasswordUpdate) => {
|
||||
return http.post(`/settings/expired/handle`, param);
|
||||
};
|
||||
|
||||
export const syncTime = () => {
|
||||
return http.post<string>(`/settings/time/sync`, {});
|
||||
export const loadTimeZone = () => {
|
||||
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 = () => {
|
||||
|
@ -885,7 +885,12 @@ const message = {
|
||||
sessionTimeoutHelper:
|
||||
'If you do not operate the panel for more than {0} seconds, the panel automatically logs out',
|
||||
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',
|
||||
oldPassword: 'Original password',
|
||||
newPassword: 'New password',
|
||||
@ -949,6 +954,7 @@ const message = {
|
||||
allowIPsWarnning:
|
||||
'设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',
|
||||
allowIPEgs: 'e.g. 172.16.10.111',
|
||||
mfa: 'MFA',
|
||||
mfaAlert:
|
||||
'MFA password is generated based on the current time. Please ensure that the server time is synchronized.',
|
||||
|
@ -235,7 +235,7 @@ const message = {
|
||||
terminal: '终端',
|
||||
settings: '面板设置',
|
||||
toolbox: '工具箱',
|
||||
logs: '面板日志',
|
||||
logs: '日志审计',
|
||||
runtime: '运行环境',
|
||||
},
|
||||
home: {
|
||||
@ -741,7 +741,7 @@ const message = {
|
||||
commands: '快捷命令',
|
||||
files: '文件管理',
|
||||
backups: '备份账号',
|
||||
logs: '面板日志',
|
||||
logs: '日志审计',
|
||||
settings: '面板设置',
|
||||
cronjobs: '计划任务',
|
||||
databases: '数据库',
|
||||
@ -885,7 +885,12 @@ const message = {
|
||||
sessionTimeoutError: '最小超时时间为 300 秒',
|
||||
sessionTimeoutHelper: '如果用户超过 {0} 秒未操作面板,面板将自动退出登录',
|
||||
syncTime: '服务器时间',
|
||||
second: ' 秒',
|
||||
timeZone: '时区',
|
||||
timeZoneHelper: '时区修改依赖于系统 timedatectl 服务。',
|
||||
timeZoneCN: '北京',
|
||||
timeZoneAM: '洛杉矶',
|
||||
timeZoneNY: '纽约',
|
||||
syncSite: '同步地址',
|
||||
changePassword: '密码修改',
|
||||
oldPassword: '原密码',
|
||||
newPassword: '新密码',
|
||||
@ -976,6 +981,7 @@ const message = {
|
||||
allowIPsHelper: '设置授权 IP 后,仅有设置中的 IP 可以访问 1Panel 服务',
|
||||
allowIPsWarnning: '设置授权 IP 后,仅有设置中的 IP 可以访问 1Panel 服务,是否继续?',
|
||||
allowIPsHelper1: '授权 IP 为空时,则取消授权 IP',
|
||||
allowIPEgs: '例:172.16.10.111',
|
||||
mfa: '两步验证',
|
||||
mfaAlert: '两步验证密码是基于当前时间生成,请确保服务器时间已同步',
|
||||
mfaHelper: '开启后会验证手机应用验证码',
|
||||
|
@ -76,12 +76,9 @@
|
||||
<el-form-item :label="$t('setting.syncTime')">
|
||||
<el-input disabled v-model="form.localTime">
|
||||
<template #append>
|
||||
<el-button v-show="!show" @click="onSyncTime" icon="Refresh">
|
||||
{{ $t('commons.button.sync') }}
|
||||
<el-button v-show="!show" @click="onChangeNtp" icon="Setting">
|
||||
{{ $t('commons.button.set') }}
|
||||
</el-button>
|
||||
<div style="width: 45px" v-show="show">
|
||||
<span>{{ count }} {{ $t('setting.second') }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
@ -95,6 +92,7 @@
|
||||
<UserName ref="userNameRef" />
|
||||
<PanelName ref="panelNameRef" @search="search()" />
|
||||
<Timeout ref="timeoutRef" @search="search()" />
|
||||
<Ntp ref="ntpRef" @search="search()" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -102,7 +100,7 @@
|
||||
import { ref, reactive, onMounted, computed } from 'vue';
|
||||
import { ElForm } from 'element-plus';
|
||||
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 { useI18n } from 'vue-i18n';
|
||||
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 Timeout from '@/views/setting/panel/timeout/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 i18n = useI18n();
|
||||
@ -124,21 +123,21 @@ const form = reactive({
|
||||
email: '',
|
||||
sessionTimeout: 0,
|
||||
localTime: '',
|
||||
timeZone: '',
|
||||
ntpSite: '',
|
||||
panelName: '',
|
||||
theme: '',
|
||||
language: '',
|
||||
complexityVerification: '',
|
||||
});
|
||||
|
||||
const timer = ref();
|
||||
const TIME_COUNT = ref(10);
|
||||
const count = ref();
|
||||
const show = ref();
|
||||
|
||||
const userNameRef = ref();
|
||||
const passwordRef = ref();
|
||||
const panelNameRef = ref();
|
||||
const timeoutRef = ref();
|
||||
const ntpRef = ref();
|
||||
|
||||
const search = async () => {
|
||||
const res = await getSettingInfo();
|
||||
@ -146,6 +145,8 @@ const search = async () => {
|
||||
form.password = '******';
|
||||
form.sessionTimeout = Number(res.data.sessionTimeout);
|
||||
form.localTime = res.data.localTime;
|
||||
form.timeZone = res.data.timeZone;
|
||||
form.ntpSite = res.data.ntpSite;
|
||||
form.panelName = res.data.panelName;
|
||||
form.theme = res.data.theme;
|
||||
form.language = res.data.language;
|
||||
@ -164,6 +165,9 @@ const onChangeTitle = () => {
|
||||
const onChangeTimeout = () => {
|
||||
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) => {
|
||||
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(() => {
|
||||
search();
|
||||
getSystemAvailable();
|
||||
|
148
frontend/src/views/setting/panel/ntp/index.vue
Normal file
148
frontend/src/views/setting/panel/ntp/index.vue
Normal 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>
|
@ -11,14 +11,14 @@
|
||||
<table style="width: 100%" class="tab-table">
|
||||
<tr v-if="allowIPs.length !== 0">
|
||||
<th scope="col" width="90%" align="left">
|
||||
<label>IP</label>
|
||||
<label>{{ $t('setting.allowIPs') }}</label>
|
||||
</th>
|
||||
<th align="left"></th>
|
||||
</tr>
|
||||
<tr v-for="(row, index) in allowIPs" :key="index">
|
||||
<td width="90%">
|
||||
<el-input
|
||||
:placeholder="$t('container.serverExample')"
|
||||
:placeholder="$t('setting.allowIPEgs')"
|
||||
style="width: 100%"
|
||||
v-model="row.value"
|
||||
/>
|
||||
|
@ -10,21 +10,14 @@
|
||||
<template #header>
|
||||
<DrawerHeader :header="$t('setting.mfa')" :back="handleClose" />
|
||||
</template>
|
||||
<el-alert :closable="false" type="warning">
|
||||
<el-alert style="margin-bottom: 20px" :closable="false" type="warning">
|
||||
<template #default>
|
||||
<span>
|
||||
<span>{{ $t('setting.mfaAlert') }}</span>
|
||||
</span>
|
||||
</template>
|
||||
</el-alert>
|
||||
<el-form
|
||||
:model="form"
|
||||
style="margin-top: 20px"
|
||||
ref="formRef"
|
||||
@submit.prevent
|
||||
v-loading="loading"
|
||||
label-position="top"
|
||||
>
|
||||
<el-form :model="form" ref="formRef" @submit.prevent v-loading="loading" label-position="top">
|
||||
<el-row type="flex" justify="center">
|
||||
<el-col :span="22">
|
||||
<el-form-item :label="$t('setting.mfaHelper1')">
|
||||
|
Loading…
x
Reference in New Issue
Block a user