From 415b5084fd8aa153ad11f9bc0a5d175c547b4c33 Mon Sep 17 00:00:00 2001 From: ssongliu Date: Thu, 25 Sep 2025 18:38:36 +0800 Subject: [PATCH] feat: Support customizing whether scripts are automatically synchronized --- core/app/api/v2/setting.go | 20 ++++++ core/app/dto/setting.go | 4 ++ core/init/cron/cron.go | 5 +- core/init/migration/migrate.go | 1 + core/init/migration/migrations/init.go | 10 +++ core/init/run/run.go | 9 ++- core/router/ro_setting.go | 1 + frontend/src/api/modules/setting.ts | 3 + frontend/src/lang/modules/en.ts | 6 ++ frontend/src/lang/modules/es-es.ts | 7 ++ frontend/src/lang/modules/ja.ts | 5 ++ frontend/src/lang/modules/ko.ts | 5 ++ frontend/src/lang/modules/ms.ts | 6 ++ frontend/src/lang/modules/pt-br.ts | 7 ++ frontend/src/lang/modules/ru.ts | 7 ++ frontend/src/lang/modules/tr.ts | 7 ++ frontend/src/lang/modules/zh-Hant.ts | 5 ++ frontend/src/lang/modules/zh.ts | 5 ++ frontend/src/views/cronjob/library/index.vue | 68 ++++++++++++++++++-- 19 files changed, 174 insertions(+), 7 deletions(-) diff --git a/core/app/api/v2/setting.go b/core/app/api/v2/setting.go index 448e418c2a63..ca16ea1af8bc 100644 --- a/core/app/api/v2/setting.go +++ b/core/app/api/v2/setting.go @@ -11,6 +11,7 @@ import ( "github.com/1Panel-dev/1Panel/core/app/api/v2/helper" "github.com/1Panel-dev/1Panel/core/app/dto" + "github.com/1Panel-dev/1Panel/core/app/repo" "github.com/1Panel-dev/1Panel/core/constant" "github.com/1Panel-dev/1Panel/core/global" "github.com/1Panel-dev/1Panel/core/utils/mfa" @@ -32,6 +33,25 @@ func (b *BaseApi) GetSettingInfo(c *gin.Context) { helper.SuccessWithData(c, setting) } +// @Tags System Setting +// @Summary Load system setting by key +// @Success 200 {string} info +// @Security ApiKeyAuth +// @Security Timestamp +// @Router /core/settings/by [post] +func (b *BaseApi) GetSettingByKey(c *gin.Context) { + var req dto.SettingKey + if err := helper.CheckBindAndValidate(&req, c); err != nil { + return + } + setting, err := repo.NewISettingRepo().GetValueByKey(req.Key) + if err != nil { + helper.InternalServer(c, err) + return + } + helper.SuccessWithData(c, setting) +} + // @Tags System Setting // @Summary Load system terminal setting info // @Success 200 {object} dto.TerminalInfo diff --git a/core/app/dto/setting.go b/core/app/dto/setting.go index bd86fa68be64..4f458b96d88a 100644 --- a/core/app/dto/setting.go +++ b/core/app/dto/setting.go @@ -52,6 +52,10 @@ type SettingInfo struct { ApiKeyValidityTime string `json:"apiKeyValidityTime"` } +type SettingKey struct { + Key string `json:"key" validate:"required,oneof=ScriptSync"` +} + type SettingUpdate struct { Key string `json:"key" validate:"required"` Value string `json:"value"` diff --git a/core/init/cron/cron.go b/core/init/cron/cron.go index 297d7e2db7fb..3fac4df905ac 100644 --- a/core/init/cron/cron.go +++ b/core/init/cron/cron.go @@ -5,6 +5,8 @@ import ( mathRand "math/rand" "time" + "github.com/1Panel-dev/1Panel/core/app/repo" + "github.com/1Panel-dev/1Panel/core/constant" "github.com/1Panel-dev/1Panel/core/global" "github.com/1Panel-dev/1Panel/core/init/cron/job" "github.com/1Panel-dev/1Panel/core/utils/common" @@ -19,7 +21,8 @@ func Init() { global.LOG.Errorf("[core] can not add backup token refresh corn job: %s", err.Error()) } - if !global.CONF.Base.IsOffLine { + scriptSync, _ := repo.NewISettingRepo().GetValueByKey("ScriptSync") + if !global.CONF.Base.IsOffLine && scriptSync == constant.StatusEnable { scriptJob := job.NewScriptJob() if _, err := global.Cron.AddJob(fmt.Sprintf("%v %v * * *", mathRand.Intn(60), mathRand.Intn(3)), scriptJob); err != nil { global.LOG.Errorf("[core] can not add script sync corn job: %s", err.Error()) diff --git a/core/init/migration/migrate.go b/core/init/migration/migrate.go index 11720bbe22d5..7224a778ab31 100644 --- a/core/init/migration/migrate.go +++ b/core/init/migration/migrate.go @@ -24,6 +24,7 @@ func Init() { migrations.AddDiskMenu, migrations.AddSimpleNodeGroup, migrations.AddUpgradeBackupCopies, + migrations.AddScriptSync, }) if err := m.Migrate(); err != nil { global.LOG.Error(err) diff --git a/core/init/migration/migrations/init.go b/core/init/migration/migrations/init.go index 82b7499c1d7a..60cf269eb9e5 100644 --- a/core/init/migration/migrations/init.go +++ b/core/init/migration/migrations/init.go @@ -545,3 +545,13 @@ var AddUpgradeBackupCopies = &gormigrate.Migration{ return nil }, } + +var AddScriptSync = &gormigrate.Migration{ + ID: "20250916-add-script-sync", + Migrate: func(tx *gorm.DB) error { + if err := tx.Create(&model.Setting{Key: "ScriptSync", Value: constant.StatusEnable}).Error; err != nil { + return err + } + return nil + }, +} diff --git a/core/init/run/run.go b/core/init/run/run.go index 770ee54ab346..56cb18cdab58 100644 --- a/core/init/run/run.go +++ b/core/init/run/run.go @@ -2,12 +2,17 @@ package run import ( "github.com/1Panel-dev/1Panel/core/app/dto" + "github.com/1Panel-dev/1Panel/core/app/repo" "github.com/1Panel-dev/1Panel/core/app/service" + "github.com/1Panel-dev/1Panel/core/constant" "github.com/1Panel-dev/1Panel/core/global" ) func Init() { - if err := service.NewIScriptService().Sync(dto.OperateByTaskID{}); err != nil { - global.LOG.Errorf("sync scripts from remote failed, err: %v", err) + scriptSync, _ := repo.NewISettingRepo().GetValueByKey("ScriptSync") + if !global.CONF.Base.IsOffLine && scriptSync == constant.StatusEnable { + if err := service.NewIScriptService().Sync(dto.OperateByTaskID{}); err != nil { + global.LOG.Errorf("sync scripts from remote failed, err: %v", err) + } } } diff --git a/core/router/ro_setting.go b/core/router/ro_setting.go index 7ba82d27eeaa..87b1dddda5d8 100644 --- a/core/router/ro_setting.go +++ b/core/router/ro_setting.go @@ -20,6 +20,7 @@ func (s *SettingRouter) InitRouter(Router *gin.RouterGroup) { { router.POST("/search", baseApi.GetSettingInfo) router.POST("/expired/handle", baseApi.HandlePasswordExpired) + settingRouter.POST("/by", baseApi.GetSettingByKey) settingRouter.POST("/terminal/search", baseApi.GetTerminalSettingInfo) settingRouter.GET("/search/available", baseApi.GetSystemAvailable) settingRouter.POST("/update", baseApi.UpdateSetting) diff --git a/frontend/src/api/modules/setting.ts b/frontend/src/api/modules/setting.ts index 103f3834e7b9..bf5c13a7afe0 100644 --- a/frontend/src/api/modules/setting.ts +++ b/frontend/src/api/modules/setting.ts @@ -76,6 +76,9 @@ export const getAgentSettingByKey = (key: string) => { export const getSettingInfo = () => { return http.post(`/core/settings/search`); }; +export const getSettingBy = (key: string) => { + return http.post(`/core/settings/by`, { key: key }); +}; export const getTerminalInfo = () => { return http.post(`/core/settings/terminal/search`); }; diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts index feb246789fde..a6b454d5c990 100644 --- a/frontend/src/lang/modules/en.ts +++ b/frontend/src/lang/modules/en.ts @@ -1131,6 +1131,12 @@ const message = { alertTitle: 'Planned Task - {0} 「{1}」 Task Failure Alert', library: { script: 'Script', + syncNow: 'Sync Now', + turnOnSync: 'Enable Auto Sync', + turnOnSyncHelper: + 'Enabling auto sync will perform automatic synchronization during early morning hours daily', + turnOffSync: 'Disable Auto Sync', + turnOffSyncHelper: 'Disabling auto sync may cause script synchronization delays, confirm?', isInteractive: 'Interactive', interactive: 'Interactive script', interactiveHelper: 'Requires user input during execution and cannot be used in scheduled tasks.', diff --git a/frontend/src/lang/modules/es-es.ts b/frontend/src/lang/modules/es-es.ts index 2394b653d778..27a3636c82ea 100644 --- a/frontend/src/lang/modules/es-es.ts +++ b/frontend/src/lang/modules/es-es.ts @@ -1131,6 +1131,13 @@ const message = { alertTitle: 'Tarea programada - {0} 「{1}」 Alerta de fallo', library: { script: 'Script', + syncNow: 'Sincronizar Ahora', + turnOnSync: 'Activar Sincronización Automática', + turnOnSyncHelper: + 'Activar la sincronización automática realizará sincronizaciones automáticas durante las primeras horas de la madrugada diariamente', + turnOffSync: 'Desactivar Sincronización Automática', + turnOffSyncHelper: + 'Desactivar la sincronización automática puede causar retrasos en la sincronización de scripts, ¿confirmar?', isInteractive: 'Interactivo', interactive: 'Script interactivo', interactiveHelper: diff --git a/frontend/src/lang/modules/ja.ts b/frontend/src/lang/modules/ja.ts index 280850320d3a..559c3643d268 100644 --- a/frontend/src/lang/modules/ja.ts +++ b/frontend/src/lang/modules/ja.ts @@ -1094,6 +1094,11 @@ const message = { alertTitle: '計画タスク - {0}「{1}」タスク障害アラート', library: { script: 'スクリプト', + syncNow: '今すぐ同期', + turnOnSync: '自動同期を有効化', + turnOnSyncHelper: '自動同期を有効にすると、毎日未明の時間帯に自動同期が実行されます', + turnOffSync: '自動同期を無効化', + turnOffSyncHelper: '自動同期を無効にするとスクリプトの同期が遅れる可能性がありますが、よろしいですか?', isInteractive: '対話型', interactive: '対話型スクリプト', interactiveHelper: '実行中にユーザー入力が必要で、スケジュールタスクでは使用できません。', diff --git a/frontend/src/lang/modules/ko.ts b/frontend/src/lang/modules/ko.ts index 133dfea4ecc4..6b02c2352e81 100644 --- a/frontend/src/lang/modules/ko.ts +++ b/frontend/src/lang/modules/ko.ts @@ -1087,6 +1087,11 @@ const message = { alertTitle: '예정된 작업 - {0} 「{1}」 작업 실패 경고', library: { script: '스크립트', + syncNow: '지금 동기화', + turnOnSync: '자동 동기화 켜기', + turnOnSyncHelper: '자동 동기화를 켜면 매일 새벽 시간에 자동 동기화가 수행됩니다', + turnOffSync: '자동 동기화 끄기', + turnOffSyncHelper: '자동 동기화를 끄면 스크립트 동기화가 지연될 수 있습니다. 확인하시겠습니까?', isInteractive: '대화형', interactive: '대화형 스크립트', interactiveHelper: '실행 중 사용자 입력이 필요하며 예약 작업에서는 사용할 수 없습니다.', diff --git a/frontend/src/lang/modules/ms.ts b/frontend/src/lang/modules/ms.ts index 4b6bf45d3138..88545fb30d60 100644 --- a/frontend/src/lang/modules/ms.ts +++ b/frontend/src/lang/modules/ms.ts @@ -1122,6 +1122,12 @@ const message = { alertTitle: 'Tugas Terancang - {0} 「{1}」 Amaran Kegagalan Tugas', library: { script: 'Skrip', + syncNow: 'Segerakan Sekarang', + turnOnSync: 'Hidupkan Segerakan Auto', + turnOnSyncHelper: + 'Menghidupkan segerakan auto akan melakukan penyegerakan automatik pada waktu awal pagi setiap hari', + turnOffSync: 'Matikan Segerakan Auto', + turnOffSyncHelper: 'Mematikan segerakan auto mungkin menyebabkan kelewatan penyegerakan skrip, sahkan?', isInteractive: 'Interaktif', interactive: 'Skrip interaktif', interactiveHelper: diff --git a/frontend/src/lang/modules/pt-br.ts b/frontend/src/lang/modules/pt-br.ts index f4673f48e081..604cb5742422 100644 --- a/frontend/src/lang/modules/pt-br.ts +++ b/frontend/src/lang/modules/pt-br.ts @@ -1114,6 +1114,13 @@ const message = { alertTitle: 'Tarefa Planejada - {0} 「{1}」 Alerta de Falha na Tarefa', library: { script: 'Script', + syncNow: 'Sincronizar Agora', + turnOnSync: 'Ativar Sincronização Automática', + turnOnSyncHelper: + 'Ativar a sincronização automática realizará sincronizações automáticas durante as primeiras horas da manhã diariamente', + turnOffSync: 'Desativar Sincronização Automática', + turnOffSyncHelper: + 'Desativar a sincronização automática pode causar atrasos na sincronização de scripts, confirmar?', isInteractive: 'Interativo', interactive: 'Script interativo', interactiveHelper: diff --git a/frontend/src/lang/modules/ru.ts b/frontend/src/lang/modules/ru.ts index 875ec46a6ab4..e9f4f665734c 100644 --- a/frontend/src/lang/modules/ru.ts +++ b/frontend/src/lang/modules/ru.ts @@ -1117,6 +1117,13 @@ const message = { alertTitle: 'Плановая задача - {0} «{1}» Оповещение о сбое задачи', library: { script: 'Скрипт', + syncNow: 'Синхронизировать Сейчас', + turnOnSync: 'Включить Автосинхронизацию', + turnOnSyncHelper: + 'Включение автосинхронизации будет выполнять автоматическую синхронизацию в ранние утренние часы ежедневно', + turnOffSync: 'Отключить Автосинхронизацию', + turnOffSyncHelper: + 'Отключение автосинхронизации может вызвать задержки синхронизации скриптов, подтвердить?', isInteractive: 'Интерактивный', interactive: 'Интерактивный скрипт', interactiveHelper: diff --git a/frontend/src/lang/modules/tr.ts b/frontend/src/lang/modules/tr.ts index 2a7ebf3d23e3..22c825d2c455 100644 --- a/frontend/src/lang/modules/tr.ts +++ b/frontend/src/lang/modules/tr.ts @@ -1142,6 +1142,13 @@ const message = { alertTitle: 'Planlanmış Görev - {0} 「{1}」 Görev Başarısızlık Uyarısı', library: { script: 'Script', + syncNow: 'Hemen Senkronize Et', + turnOnSync: 'Otomatik Senkronizasyonu Aç', + turnOnSyncHelper: + 'Otomatik senkronizasyonu açmak, her gün sabahın erken saatlerinde otomatik senkronizasyon gerçekleştirecektir', + turnOffSync: 'Otomatik Senkronizasyonu Kapat', + turnOffSyncHelper: + 'Otomatik senkronizasyonu kapatmak, betik senkronizasyon gecikmelerine neden olabilir, onaylıyor musunuz?', isInteractive: 'Etkileşimli', interactive: 'Etkileşimli script', interactiveHelper: 'Yürütme sırasında kullanıcı girişi gerektirir ve zamanlanmış görevlerde kullanılamaz.', diff --git a/frontend/src/lang/modules/zh-Hant.ts b/frontend/src/lang/modules/zh-Hant.ts index 8aaefb3da273..982574aec0cf 100644 --- a/frontend/src/lang/modules/zh-Hant.ts +++ b/frontend/src/lang/modules/zh-Hant.ts @@ -1076,6 +1076,11 @@ const message = { alertTitle: '計畫任務-{0}「{1}」任務失敗告警', library: { script: '腳本', + syncNow: '立即同步', + turnOnSync: '開啟自動同步', + turnOnSyncHelper: '開啟自動同步將在每天凌晨時段進行自動同步', + turnOffSync: '關閉自動同步', + turnOffSyncHelper: '關閉自動同步可能導致腳本同步不及時,是否確認?', isInteractive: '互動式', interactive: '互動式腳本', interactiveHelper: '在腳本執行過程中需要使用者輸入參數或做出選擇,且無法用於計劃任務中。', diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index 475e9c4e81d7..32e00d49a8af 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -1076,6 +1076,11 @@ const message = { alertTitle: '计划任务-{0}「 {1} 」任务失败告警', library: { script: '脚本', + syncNow: '立即同步', + turnOnSync: '开启自动同步', + turnOnSyncHelper: '开启自动同步将在每天凌晨时段进行自动同步', + turnOffSync: '关闭自动同步', + turnOffSyncHelper: '关闭自动同步可能导致脚本同步不及时,是否确认?', isInteractive: '交互式', interactive: '交互式脚本', interactiveHelper: '在脚本执行过程中需要用户输入参数或做出选择,且无法用于计划任务中。', diff --git a/frontend/src/views/cronjob/library/index.vue b/frontend/src/views/cronjob/library/index.vue index eea9d3f57309..beaccada0343 100644 --- a/frontend/src/views/cronjob/library/index.vue +++ b/frontend/src/views/cronjob/library/index.vue @@ -5,9 +5,26 @@ {{ $t('commons.button.create') }} - - {{ $t('commons.button.sync') }} - + + + {{ $t('commons.button.sync') }} + + + + + {{ $t('commons.table.group') }} @@ -116,6 +133,7 @@ import { GlobalStore } from '@/store'; import { getGroupList } from '@/api/modules/group'; import CodemirrorDrawer from '@/components/codemirror-pro/drawer.vue'; import { MsgSuccess } from '@/utils/message'; +import { getSettingBy, updateSetting } from '@/api/modules/setting'; const globalStore = GlobalStore(); const mobile = computed(() => { @@ -129,6 +147,7 @@ const opRef = ref(); const runRef = ref(); const taskLogRef = ref(); +const scriptSync = ref(); const data = ref(); const paginationConfig = reactive({ @@ -194,7 +213,7 @@ const onDelete = async (row: Cronjob.ScriptInfo | null) => { }; const onSync = async () => { - ElMessageBox.confirm(i18n.global.t('cronjob.library.syncHelper', i18n.global.t('commons.button.sync')), { + ElMessageBox.confirm(i18n.global.t('cronjob.library.syncHelper'), i18n.global.t('commons.button.syncNow'), { confirmButtonText: i18n.global.t('commons.button.confirm'), cancelButtonText: i18n.global.t('commons.button.cancel'), type: 'info', @@ -217,6 +236,46 @@ const openTaskLog = (taskID: string) => { taskLogRef.value.openWithTaskID(taskID, true, 'local'); }; +const loadSyncStatus = async () => { + const res = await getSettingBy('ScriptSync'); + scriptSync.value = res.data; +}; +const handleSyncOp = async (command: string) => { + let val = 'Enable'; + switch (command) { + case 'sync': + onSync(); + return; + case 'turnOnSync': + val = 'Enable'; + break; + case 'turnOffSync': + val = 'Disable'; + break; + default: + return; + } + ElMessageBox.confirm( + i18n.global.t('cronjob.library.' + command + 'Helper'), + i18n.global.t('cronjob.library.' + command), + { + confirmButtonText: i18n.global.t('commons.button.confirm'), + cancelButtonText: i18n.global.t('commons.button.cancel'), + type: 'info', + }, + ).then(async () => { + loading.value = true; + await updateSetting({ key: 'ScriptSync', value: val }) + .then(() => { + loadSyncStatus(); + MsgSuccess(i18n.global.t('commons.msg.operationSuccess')); + }) + .finally(() => { + loading.value = false; + }); + }); +}; + const search = async () => { let params = { info: searchInfo.value, @@ -297,6 +356,7 @@ const buttons = [ onMounted(() => { search(); + loadSyncStatus(); loadGroupOptions(); });