fix: 系统时间同步样式修改 (#1123)

This commit is contained in:
ssongliu 2023-05-24 15:43:21 +08:00 committed by GitHub
parent b979c2574c
commit be6b7157f4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 268 additions and 143 deletions

View File

@ -205,23 +205,22 @@ func (b *BaseApi) LoadTimeZone(c *gin.Context) {
// @Summary Sync system time
// @Description 系统时间同步
// @Accept json
// @Param request body dto.SyncTimeZone true "request"
// @Param request body dto.SyncTime 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]"}
// @x-panel-log {"bodyKeys":["ntpSite"],"paramKeys":[],"BeforeFuntions":[],"formatZH":"系统时间同步[ntpSite]","formatEN":"sync system time [ntpSite]"}
func (b *BaseApi) SyncTime(c *gin.Context) {
var req dto.SyncTimeZone
var req dto.SyncTime
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
ntime, err := settingService.SyncTime(req)
if err != nil {
if err := settingService.SyncTime(req); 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, nil)
}
// @Tags System Setting

View File

@ -111,9 +111,8 @@ type UpgradeInfo struct {
ReleaseNote string `json:"releaseNote"`
}
type SyncTimeZone struct {
NtpSite string `json:"ntpSite"`
TimeZone string `json:"timeZone"`
type SyncTime struct {
NtpSite string `json:"ntpSite"`
}
type Upgrade struct {

View File

@ -35,7 +35,7 @@ type ISettingService interface {
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)
SyncTime(req dto.SyncTime) error
}
func NewISettingService() ISettingService {
@ -72,46 +72,49 @@ func (u *SettingService) LoadTimeZone() ([]string, error) {
}
func (u *SettingService) Update(key, value string) error {
if key == "ExpirationDays" {
if err := settingRepo.Update(key, value); err != nil {
return err
}
switch key {
case "ExpirationDays":
timeout, _ := strconv.Atoi(value)
if err := settingRepo.Update("ExpirationTime", time.Now().AddDate(0, 0, timeout).Format("2006-01-02 15:04:05")); err != nil {
return err
}
}
if key == "BindDomain" {
case "BindDomain":
global.CONF.System.BindDomain = value
}
if key == "AllowIPs" {
case "AllowIPs":
global.CONF.System.AllowIPs = value
}
if err := settingRepo.Update(key, value); err != nil {
return err
}
if key == "UserName" {
case "TimeZone":
if err := ntp.UpdateSystemTimeZone(value); err != nil {
return err
}
go func() {
_, err := cmd.Exec("systemctl restart 1panel.service")
if err != nil {
global.LOG.Errorf("restart system for new time zone failed, err: %v", err)
}
}()
case "UserName", "Password":
_ = global.SESSION.Clean()
}
return nil
}
func (u *SettingService) SyncTime(req dto.SyncTimeZone) (time.Time, error) {
func (u *SettingService) SyncTime(req dto.SyncTime) error {
if err := settingRepo.Update("NtpSite", req.NtpSite); err != nil {
return err
}
ntime, err := ntp.GetRemoteTime(req.NtpSite)
if err != nil {
return ntime, err
return err
}
ts := ntime.Format("2006-01-02 15:04:05")
if err := ntp.UpdateSystemTime(ts, req.TimeZone); err != nil {
return ntime, err
if err := ntp.UpdateSystemTime(ts); err != nil {
return 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
return nil
}
func (u *SettingService) UpdatePort(port uint) error {

View File

@ -348,7 +348,7 @@ var AddBindAndAllowIPs = &gormigrate.Migration{
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 {
if err := tx.Create(&model.Setting{Key: "NtpSite", Value: "pool.ntp.org"}).Error; err != nil {
return err
}
return nil

View File

@ -31,7 +31,7 @@ type packet struct {
}
func GetRemoteTime(site string) (time.Time, error) {
conn, err := net.Dial("udp", site)
conn, err := net.Dial("udp", site+":123")
if err != nil {
return time.Time{}, fmt.Errorf("failed to connect: %v", err)
}
@ -59,15 +59,10 @@ func GetRemoteTime(site string) (time.Time, error) {
return showtime, nil
}
func UpdateSystemTime(dateTime, timezone string) error {
func UpdateSystemTime(dateTime string) error {
system := runtime.GOOS
if system == "linux" {
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)
stdout2, err := cmd.Execf(`%s date -s "%s"`, cmd.SudoHandleCmd(), dateTime)
if err != nil {
return fmt.Errorf("update system time failed,stdout: %s, err: %v", stdout2, err)
}
@ -75,3 +70,15 @@ func UpdateSystemTime(dateTime, timezone string) error {
}
return fmt.Errorf("the current system architecture %v does not support synchronization", system)
}
func UpdateSystemTimeZone(timezone string) error {
system := runtime.GOOS
if system == "linux" {
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)
}
return nil
}
return fmt.Errorf("the current system architecture %v does not support synchronization", system)
}

View File

@ -7878,7 +7878,7 @@ var doc = `{
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.SyncTimeZone"
"$ref": "#/definitions/dto.SyncTime"
}
}
],
@ -7893,11 +7893,10 @@ var doc = `{
"x-panel-log": {
"BeforeFuntions": [],
"bodyKeys": [
"ntpSite",
"timeZone"
"ntpSite"
],
"formatEN": "sync system time [ntpSite]-[timeZone]",
"formatZH": "系统时间同步[ntpSite]-[timeZone]",
"formatEN": "sync system time [ntpSite]",
"formatZH": "系统时间同步[ntpSite]",
"paramKeys": []
}
}
@ -12621,14 +12620,11 @@ var doc = `{
}
}
},
"dto.SyncTimeZone": {
"dto.SyncTime": {
"type": "object",
"properties": {
"ntpSite": {
"type": "string"
},
"timeZone": {
"type": "string"
}
}
},

View File

@ -7864,7 +7864,7 @@
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.SyncTimeZone"
"$ref": "#/definitions/dto.SyncTime"
}
}
],
@ -7879,11 +7879,10 @@
"x-panel-log": {
"BeforeFuntions": [],
"bodyKeys": [
"ntpSite",
"timeZone"
"ntpSite"
],
"formatEN": "sync system time [ntpSite]-[timeZone]",
"formatZH": "系统时间同步[ntpSite]-[timeZone]",
"formatEN": "sync system time [ntpSite]",
"formatZH": "系统时间同步[ntpSite]",
"paramKeys": []
}
}
@ -12607,14 +12606,11 @@
}
}
},
"dto.SyncTimeZone": {
"dto.SyncTime": {
"type": "object",
"properties": {
"ntpSite": {
"type": "string"
},
"timeZone": {
"type": "string"
}
}
},

View File

@ -1694,12 +1694,10 @@ definitions:
required:
- id
type: object
dto.SyncTimeZone:
dto.SyncTime:
properties:
ntpSite:
type: string
timeZone:
type: string
type: object
dto.UpdateDescription:
properties:
@ -8276,7 +8274,7 @@ paths:
name: request
required: true
schema:
$ref: '#/definitions/dto.SyncTimeZone'
$ref: '#/definitions/dto.SyncTime'
responses:
"200":
description: OK
@ -8291,9 +8289,8 @@ paths:
BeforeFuntions: []
bodyKeys:
- ntpSite
- timeZone
formatEN: sync system time [ntpSite]-[timeZone]
formatZH: 系统时间同步[ntpSite]-[timeZone]
formatEN: sync system time [ntpSite]
formatZH: 系统时间同步[ntpSite]
paramKeys: []
/settings/update:
post:

View File

@ -38,8 +38,8 @@ export const handleExpired = (param: Setting.PasswordUpdate) => {
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 syncTime = (ntpSite: string) => {
return http.post<string>(`/settings/time/sync`, { ntpSite: ntpSite });
};
export const cleanMonitors = () => {

View File

@ -907,11 +907,17 @@ const message = {
'If you do not operate the panel for more than {0} seconds, the panel automatically logs out',
syncTime: 'Server time',
timeZone: 'Time Zone',
timeZoneHelper: 'Timezone modification depends on the system timedatectl service.',
timeZoneChangeHelper: 'Changing the time zone requires restarting the service. Do you want to continue?',
timeZoneHelper:
'Timezone modification depends on the system timedatectl service. take effect after restart the 1Panel service.',
timeZoneCN: 'Bei Jing',
timeZoneAM: 'Los Angeles',
timeZoneNY: 'New York',
ntpALi: 'Alibaba',
ntpGoogle: 'Google',
syncSite: 'Ntp Server',
syncSiteHelper:
'This operation will use {0} as the source for system time synchronization. Do you want to continue?',
changePassword: 'Password change',
oldPassword: 'Original password',
newPassword: 'New password',

View File

@ -905,12 +905,16 @@ const message = {
sessionTimeoutError: '最小超时时间为 300 ',
sessionTimeoutHelper: '如果用户超过 {0} 秒未操作面板面板将自动退出登录',
syncTime: '服务器时间',
timeZone: '时区',
timeZoneHelper: '时区修改依赖于系统 timedatectl 服务',
timeZone: '系统时区',
timeZoneChangeHelper: '系统时区修改需要重启服务是否继续',
timeZoneHelper: '时区修改依赖于系统 timedatectl 服务重启 1Panel 服务后生效',
timeZoneCN: '北京',
timeZoneAM: '洛杉矶',
timeZoneNY: '纽约',
syncSite: '同步地址',
ntpALi: '阿里',
ntpGoogle: '谷歌',
syncSite: 'NTP 服务器',
syncSiteHelper: '该操作将使用 {0} 作为源进行系统时间同步是否继续',
changePassword: '密码修改',
oldPassword: '原密码',
newPassword: '新密码',

View File

@ -73,6 +73,15 @@
</span>
</el-form-item>
<el-form-item :label="$t('setting.timeZone')" prop="timeZone">
<el-input disabled v-model.number="form.timeZone">
<template #append>
<el-button @click="onChangeTimeZone" icon="Setting">
{{ $t('commons.button.set') }}
</el-button>
</template>
</el-input>
</el-form-item>
<el-form-item :label="$t('setting.syncTime')">
<el-input disabled v-model="form.localTime">
<template #append>
@ -92,6 +101,7 @@
<UserName ref="userNameRef" />
<PanelName ref="panelNameRef" @search="search()" />
<Timeout ref="timeoutRef" @search="search()" />
<TimeZone ref="timezoneRef" @search="search()" />
<Ntp ref="ntpRef" @search="search()" />
</div>
</template>
@ -108,6 +118,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 TimeZone from '@/views/setting/panel/timezone/index.vue';
import Ntp from '@/views/setting/panel/ntp/index.vue';
const loading = ref(false);
@ -137,6 +148,7 @@ const passwordRef = ref();
const panelNameRef = ref();
const timeoutRef = ref();
const ntpRef = ref();
const timezoneRef = ref();
const search = async () => {
const res = await getSettingInfo();
@ -164,8 +176,11 @@ const onChangeTitle = () => {
const onChangeTimeout = () => {
timeoutRef.value.acceptParams({ sessionTimeout: form.sessionTimeout });
};
const onChangeTimeZone = () => {
timezoneRef.value.acceptParams({ timeZone: form.timeZone });
};
const onChangeNtp = () => {
ntpRef.value.acceptParams({ localTime: form.localTime, timeZone: form.timeZone, ntpSite: form.ntpSite });
ntpRef.value.acceptParams({ localTime: form.localTime, ntpSite: form.ntpSite });
};
const onSave = async (key: string, val: any) => {

View File

@ -4,56 +4,25 @@
<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-form-item :label="$t('setting.syncSite')" prop="ntpSite" :rules="Rules.requiredInput">
<el-input v-model="form.ntpSite" />
<el-button type="primary" link class="tagClass" @click="form.ntpSite = 'pool.ntp.org'">
{{ $t('website.default') }}
</el-button>
<el-button
:disabled="canChangeZone()"
type="primary"
link
class="tagClass"
@click="form.timeZone = 'America/Los_Angeles'"
>
{{ $t('setting.timeZoneAM') }}
<el-button type="primary" link class="tagClass" @click="form.ntpSite = 'ntp.aliyun.com'">
{{ $t('setting.ntpALi') }}
</el-button>
<el-button
:disabled="canChangeZone()"
type="primary"
link
class="tagClass"
@click="form.timeZone = 'America/New_York'"
>
{{ $t('setting.timeZoneNY') }}
<el-button type="primary" link class="tagClass" @click="form.ntpSite = 'time.google.com'">
{{ $t('setting.ntpGoogle') }}
</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>
@ -72,24 +41,20 @@
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 { syncTime } from '@/api/modules/setting';
import { ElMessageBox, 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: '',
});
@ -97,38 +62,37 @@ const form = reactive({
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;
});
ElMessageBox.confirm(
i18n.global.t('setting.syncSiteHelper', [form.ntpSite]),
i18n.global.t('setting.syncSite'),
{
confirmButtonText: i18n.global.t('commons.button.confirm'),
cancelButtonText: i18n.global.t('commons.button.cancel'),
type: 'info',
},
).then(async () => {
loading.value = true;
await syncTime(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;
});
});
});
};

View File

@ -0,0 +1,139 @@
<template>
<div>
<el-drawer v-model="drawerVisiable" :destroy-on-close="true" :close-on-click-modal="false" size="30%">
<template #header>
<DrawerHeader :header="$t('setting.timeZone')" :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-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="onSave(formRef)">
{{ $t('commons.button.confirm') }}
</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, updateSetting } from '@/api/modules/setting';
import { ElMessageBox, FormInstance } from 'element-plus';
import { Rules } from '@/global/form-rules';
import router from '@/routers';
import { GlobalStore } from '@/store';
const globalStore = GlobalStore();
interface DialogProps {
timeZone: string;
}
const drawerVisiable = ref();
const loading = ref();
const form = reactive({
timeZone: '',
});
const formRef = ref<FormInstance>();
const zones = ref<Array<string>>([]);
const acceptParams = (params: DialogProps): void => {
loadTimeZones();
form.timeZone = params.timeZone;
drawerVisiable.value = true;
};
const loadTimeZones = async () => {
const res = await loadTimeZone();
zones.value = res.data;
};
const canChangeZone = () => {
return zones.value.length === 0;
};
const onSave = async (formEl: FormInstance | undefined) => {
if (!formEl) return;
formEl.validate(async (valid) => {
if (!valid) return;
ElMessageBox.confirm(i18n.global.t('setting.timeZoneChangeHelper'), i18n.global.t('setting.timeZone'), {
confirmButtonText: i18n.global.t('commons.button.confirm'),
cancelButtonText: i18n.global.t('commons.button.cancel'),
type: 'info',
}).then(async () => {
await updateSetting({ key: 'TimeZone', value: form.timeZone })
.then(async () => {
loading.value = false;
router.push({ name: 'entrance', params: { code: globalStore.entrance } });
globalStore.setLogStatus(false);
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
drawerVisiable.value = false;
return;
})
.catch(() => {
loading.value = false;
});
});
});
};
const handleClose = () => {
drawerVisiable.value = false;
};
defineExpose({
acceptParams,
});
</script>
<style scoped lang="scss">
.tagClass {
margin-top: 5px;
}
</style>