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 (
|
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"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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"`
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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 ""
|
||||||
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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 = () => {
|
||||||
|
@ -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.',
|
||||||
|
@ -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: '开启后会验证手机应用验证码',
|
||||||
|
@ -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();
|
||||||
|
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">
|
<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"
|
||||||
/>
|
/>
|
||||||
|
@ -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')">
|
||||||
|
Loading…
x
Reference in New Issue
Block a user