fix: Modify the recommended application synchronization mode (#8050)

This commit is contained in:
ssongliu 2025-03-03 17:05:29 +08:00 committed by GitHub
parent adbf44a176
commit 4cfc3439cb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
24 changed files with 88 additions and 106 deletions

View file

@ -62,7 +62,7 @@ func (b *BaseApi) LoadAppLauncherOption(c *gin.Context) {
}
func (b *BaseApi) SyncAppLauncher(c *gin.Context) {
var req dto.SyncFromMaster
var req []dto.AppLauncherSync
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}

View file

@ -100,6 +100,10 @@ type DashboardCurrent struct {
ShotTime time.Time `json:"shotTime"`
}
type AppLauncherSync struct {
Key string `json:"key"`
}
type DiskInfo struct {
Path string `json:"path"`
Type string `json:"type"`

View file

@ -2,6 +2,7 @@ package repo
import (
"context"
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/global"
@ -19,6 +20,7 @@ type ITaskRepo interface {
Update(ctx context.Context, task *model.Task) error
UpdateRunningTaskToFailed() error
CountExecutingTask() (int64, error)
Delete(opts ...DBOption) error
WithByID(id string) DBOption
WithResourceID(id uint) DBOption
@ -108,3 +110,11 @@ func (t TaskRepo) CountExecutingTask() (int64, error) {
err := getTaskDb(t.WithByStatus(constant.StatusExecuting)).Model(&model.Task{}).Count(&count).Error
return count, err
}
func (u TaskRepo) Delete(opts ...DBOption) error {
db := global.TaskDB
for _, opt := range opts {
db = opt(db)
}
return db.Delete(&model.Task{}).Error
}

View file

@ -251,16 +251,13 @@ func (u *BackupRecordService) LoadRecordSize(req dto.SearchForSize) ([]dto.Recor
var datas []dto.RecordFileSize
var wg sync.WaitGroup
for i := 0; i < len(list); i++ {
item := dto.RecordFileSize{ID: list[i].ID}
datas = append(datas, dto.RecordFileSize{ID: list[i].ID})
if val, ok := clientMap[fmt.Sprintf("%v", list[i].DownloadID)]; ok {
wg.Add(1)
go func(index int) {
item.Size, _ = val.client.Size(path.Join(val.backupPath, list[i].FilePath))
datas = append(datas, item)
datas[index].Size, _ = val.client.Size(path.Join(val.backupPath, list[i].FilePath))
wg.Done()
}(i)
} else {
datas = append(datas, item)
}
}
wg.Wait()

View file

@ -2,7 +2,6 @@ package service
import (
"encoding/json"
"fmt"
network "net"
"os"
"sort"
@ -10,9 +9,6 @@ import (
"sync"
"time"
"github.com/1Panel-dev/1Panel/agent/app/repo"
"github.com/1Panel-dev/1Panel/agent/buserr"
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/app/model"
"github.com/1Panel-dev/1Panel/agent/constant"
@ -33,7 +29,7 @@ import (
type DashboardService struct{}
type IDashboardService interface {
Sync(req dto.SyncFromMaster) error
Sync(req []dto.AppLauncherSync) error
LoadOsInfo() (*dto.OsInfo, error)
LoadBaseInfo(ioOption string, netOption string) (*dto.DashboardBase, error)
@ -48,33 +44,12 @@ func NewIDashboardService() IDashboardService {
return &DashboardService{}
}
func (u *DashboardService) Sync(req dto.SyncFromMaster) error {
var launcherItem model.AppLauncher
if err := json.Unmarshal([]byte(req.Data), &launcherItem); err != nil {
return err
}
launcher, _ := launcherRepo.Get(settingRepo.WithByKey(req.Name))
switch req.Operation {
case "create":
if launcher.ID != 0 {
launcherItem.ID = launcher.ID
return launcherRepo.Save(&launcherItem)
}
return launcherRepo.Create(&launcherItem)
case "delete":
if launcher.ID == 0 {
return buserr.New("ErrRecordNotFound")
}
return launcherRepo.Delete(repo.WithByID(launcher.ID))
case "update":
if launcher.ID == 0 {
return buserr.New("ErrRecordNotFound")
}
launcherItem.ID = launcher.ID
return launcherRepo.Save(&launcherItem)
default:
return fmt.Errorf("not support such operation %s", req.Operation)
func (u *DashboardService) Sync(req []dto.AppLauncherSync) error {
var launchers []model.AppLauncher
for _, item := range req {
launchers = append(launchers, model.AppLauncher{Key: item.Key})
}
return launcherRepo.SyncAll(launchers)
}
func (u *DashboardService) LoadOsInfo() (*dto.OsInfo, error) {

View file

@ -16,6 +16,7 @@ import (
"github.com/docker/docker/api/types/filters"
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/app/repo"
"github.com/1Panel-dev/1Panel/agent/app/task"
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
@ -37,7 +38,6 @@ const (
uploadPath = "1panel/uploads"
downloadPath = "1panel/download"
logPath = "1panel/log"
taskPath = "1panel/task"
)
func (u *DeviceService) Scan() dto.CleanData {
@ -254,18 +254,10 @@ func (u *DeviceService) Clean(req []dto.Clean) {
dropFileOrDir(path.Join(global.Dir.BaseDir, logPath, item.Name))
}
case "task_log":
pathItem := path.Join(global.Dir.BaseDir, taskPath, item.Name)
dropFileOrDir(path.Join(global.Dir.BaseDir, taskPath, item.Name))
pathItem := path.Join(global.Dir.BaseDir, logPath, item.Name)
dropFileOrDir(pathItem)
if len(item.Name) == 0 {
files, _ := os.ReadDir(pathItem)
if len(files) == 0 {
continue
}
for _, file := range files {
_ = cronjobRepo.DeleteRecord(cronjobRepo.WithByRecordFile(path.Join(pathItem, file.Name())))
}
} else {
_ = cronjobRepo.DeleteRecord(cronjobRepo.WithByRecordFile(pathItem))
_ = taskRepo.Delete(repo.WithByType(item.Name))
}
case "images":
dropImages()
@ -506,8 +498,8 @@ func loadLogTree(fileOp fileUtils.FileOp) []dto.CleanTree {
}
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "system_log", Size: uint64(size), Children: list1, Type: "system_log", IsRecommend: true})
path2 := path.Join(global.Dir.BaseDir, taskPath)
list2 := loadTreeWithAllFile(false, path2, "task_log", path2, fileOp)
path2 := path.Join(global.Dir.BaseDir, logPath)
list2 := loadTreeWithDir(false, "task_log", path2, fileOp)
size2, _ := fileOp.GetDirSize(path2)
treeData = append(treeData, dto.CleanTree{ID: uuid.NewString(), Label: "task_log", Size: uint64(size2), Children: list2, Type: "task_log"})
return treeData
@ -570,6 +562,9 @@ func loadTreeWithDir(isCheck bool, treeType, pathItem string, fileOp fileUtils.F
if (treeType == "old_upgrade" || treeType == "upgrade") && !strings.HasPrefix(file.Name(), "upgrade_2023") {
continue
}
if treeType == "task_log" && file.Name() == "ssl" {
continue
}
if file.IsDir() {
size, err := fileOp.GetDirSize(path.Join(pathItem, file.Name()))
if err != nil {

View file

@ -9,6 +9,7 @@ import (
"github.com/1Panel-dev/1Panel/core/app/model"
"github.com/1Panel-dev/1Panel/core/app/repo"
"github.com/1Panel-dev/1Panel/core/constant"
"github.com/1Panel-dev/1Panel/core/utils/req_helper"
"github.com/1Panel-dev/1Panel/core/utils/xpack"
)
@ -37,32 +38,23 @@ func (u *LauncherService) Search() ([]string, error) {
func (u *LauncherService) ChangeShow(req dto.SettingUpdate) error {
launcher, _ := launcherRepo.Get(repo.WithByKey(req.Key))
if req.Value == constant.StatusEnable {
if launcher.ID != 0 {
go syncLauncherToAgent(launcher, "create")
return nil
}
launcher.Key = req.Key
if err := launcherRepo.Create(&launcher); err != nil {
if req.Value == constant.StatusEnable && launcher.ID == 0 {
if err := launcherRepo.Create(&model.AppLauncher{Key: req.Key}); err != nil {
return err
}
go syncLauncherToAgent(launcher, "create")
return nil
}
if launcher.ID == 0 {
go syncLauncherToAgent(launcher, "delete")
return nil
if req.Value == constant.StatusDisable && launcher.ID != 0 {
if err := launcherRepo.Delete(repo.WithByKey(req.Key)); err != nil {
return err
}
}
if err := launcherRepo.Delete(repo.WithByKey(req.Key)); err != nil {
return err
}
go syncLauncherToAgent(launcher, "delete")
go syncLauncherToAgent()
return nil
}
func syncLauncherToAgent(launcher model.AppLauncher, operation string) {
itemData, _ := json.Marshal(launcher)
itemJson := dto.SyncToAgent{Name: launcher.Key, Operation: operation, Data: string(itemData)}
bodyItem, _ := json.Marshal(itemJson)
_ = xpack.RequestToAllAgent("/api/v2/backups/sync", http.MethodPost, bytes.NewReader((bodyItem)))
func syncLauncherToAgent() {
launchers, _ := launcherRepo.List()
itemData, _ := json.Marshal(launchers)
_, _ = req_helper.NewLocalClient("/api/v2/dashboard/app/launcher/sync", http.MethodPost, bytes.NewReader((itemData)))
_ = xpack.RequestToAllAgent("/api/v2/dashboard/app/launcher/sync", http.MethodPost, bytes.NewReader((itemData)))
}

View file

@ -63,6 +63,7 @@ const getType = (status: string) => {
case 'disable':
case 'unhealthy':
case 'failed':
case 'lost':
return 'danger';
case 'paused':
case 'exited':

View file

@ -3185,9 +3185,8 @@ const message = {
'Detected that there is already 1panel service on this node. Adding this node will use the original service port and installation directory of 1panel. Do you want to continue?',
coreExist:
'Detected that there is already 1panel-core service on this node. Unable to add this node, please check and try again!',
agentExist: 'Detected that there is already 1panel-agent service on this node',
forceAdd: 'Force Add',
forceAddHelper: 'Force add will forcibly replace the 1panel-agent service on this node',
agentExist:
'Detected that the 1panel-agent service already exists on this node. Continuing to add will retain the node data and only replace the 1panel-agent service. Do you want to continue?',
reinstallHelper: 'Reinstall node {0}, do you want to continue?',
unhealthyCheck: 'Abnormal Check',
fixOperation: 'Fix Operation',

View file

@ -2997,9 +2997,8 @@ const message = {
'このードに既に1panelサービスが存在しますこのノードを追加すると1panelの元のサービスポートとインストールディレクトリが使用されます続行しますか',
coreExist:
'このードに既に1panel-coreサービスが存在しますこのノードを追加できません確認して再試行してください',
agentExist: 'このードに既に1panel-agentサービスが存在します',
forceAdd: '強制追加',
forceAddHelper: '強制追加はこのードの1panel-agentサービスを強制的に置き換えます',
agentExist:
'このードに1panel-agentサービスが既に存在することが検出されました追加を続行するとノードデータは保持され1panel-agentサービスのみが置き換えられます続行しますか',
reinstallHelper: 'ノード{0}を再インストールします続行しますか',
unhealthyCheck: '異常チェック',
fixOperation: '修正操作',

View file

@ -2946,9 +2946,8 @@ const message = {
' 노드에 이미 1panel 서비스가 존재합니다. 노드를 추가하면 1panel의 원래 서비스 포트와 설치 디렉토리를 사용합니다. 계속하시겠습니까?',
coreExist:
' 노드에 이미 1panel-core 서비스가 존재합니다. 노드를 추가할 없습니다. 확인 다시 시도하십시오!',
agentExist: ' 노드에 이미 1panel-agent 서비스가 존재합니다',
forceAdd: '강제 추가',
forceAddHelper: '강제 추가는 노드의 1panel-agent 서비스를 강제로 교체합니다',
agentExist:
' 노드에 1panel-agent 서비스가 이미 존재하는 것으로 감지되었습니다. 추가를 계속하면 노드 데이터는 유지되고 1panel-agent 서비스만 교체됩니다. 계속하시겠습니까?',
reinstallHelper: '노드 {0} 재설치합니다. 계속하시겠습니까?',
unhealthyCheck: '비정상 체크',
fixOperation: '수정 작업',

View file

@ -3065,9 +3065,8 @@ const message = {
'Dikesan bahawa terdapat perkhidmatan 1panel yang sudah ada pada nod ini. Menambah nod ini akan menggunakan port dan direktori pemasangan perkhidmatan asal 1panel. Adakah anda ingin meneruskan?',
coreExist:
'Dikesan bahawa terdapat perkhidmatan 1panel-core yang sudah ada pada nod ini. Tidak dapat menambah nod ini, sila semak dan cuba lagi!',
agentExist: 'Dikesan bahawa terdapat perkhidmatan 1panel-agent yang sudah ada pada nod ini',
forceAdd: 'Tambah Secara Paksa',
forceAddHelper: 'Tambah secara paksa akan menggantikan perkhidmatan 1panel-agent pada nod ini',
agentExist:
'Terbukti bahawa perkhidmatan 1panel-agent sudah wujud pada nod ini. Melanjutkan penambahan akan mengekalkan data nod dan hanya menggantikan perkhidmatan 1panel-agent. Adakah anda ingin meneruskan?',
reinstallHelper: 'Pasang semula nod {0}, adakah anda ingin meneruskan?',
unhealthyCheck: 'Pemeriksaan Tidak Normal',
fixOperation: 'Operasi Pembetulan',

View file

@ -3069,9 +3069,8 @@ const message = {
'Detectado que existe um serviço 1panel neste . Adicionar este usará a porta e o diretório de instalação do serviço original do 1panel. Deseja continuar?',
coreExist:
'Detectado que existe um serviço 1panel-core neste . Não é possível adicionar este , por favor verifique e tente novamente!',
agentExist: 'Detectado que existe um serviço 1panel-agent neste ',
forceAdd: 'Adicionar Forçadamente',
forceAddHelper: 'Adicionar forçadamente substituirá o serviço 1panel-agent neste ',
agentExist:
'Detectado que o serviço 1panel-agent existe neste . Continuar a adicionar irá manter os dados do e apenas substituir o serviço 1panel-agent. Você deseja continuar?',
reinstallHelper: 'Reinstalar o {0}, deseja continuar?',
unhealthyCheck: 'Verificação Anormal',
fixOperation: 'Operação de Correção',

View file

@ -3058,9 +3058,8 @@ const message = {
'Обнаружено, что на этом узле уже существует служба 1panel. Добавление этого узла будет использовать оригинальный порт и каталог установки службы 1panel. Вы хотите продолжить?',
coreExist:
'Обнаружено, что на этом узле уже существует служба 1panel-core. Невозможно добавить этот узел, пожалуйста, проверьте и попробуйте снова!',
agentExist: 'Обнаружено, что на этом узле уже существует служба 1panel-agent',
forceAdd: 'Принудительное добавление',
forceAddHelper: 'Принудительное добавление заменит службу 1panel-agent на этом узле',
agentExist:
'Обнаружено, что служба 1panel-agent уже существует на этом узле. Продолжение добавления сохранит данные узла и только заменит службу 1panel-agent. Вы хотите продолжить?',
reinstallHelper: 'Переустановить узел {0}, вы хотите продолжить?',
unhealthyCheck: 'Проверка на неисправности',
fixOperation: 'Решение проблемы',

View file

@ -2948,9 +2948,8 @@ const message = {
panelExist:
'已檢測到該節點上已存在 1panel 服務新增該節點將沿用 1panel 原服務的埠號及安裝目錄是否繼續',
coreExist: '已檢測到該節點上已存在 1panel-core 服務無法新增該節點請檢查後再試',
agentExist: '已檢測到該節點上已存在 1panel-agent 服務',
forceAdd: '強制新增',
forceAddHelper: '強制新增將強制替換該節點上的 1panel-agent 服務',
agentExist:
'檢測到該節點上已存在 1panel-agent 服務繼續添加將保留該節點數據僅替換 1panel-agent 服務是否繼續',
reinstallHelper: '重新安裝節點 {0}是否繼續',
unhealthyCheck: '異常檢查',
fixOperation: '修復方案',

View file

@ -2935,9 +2935,8 @@ const message = {
panelExist:
'检测到该节点上已存在 1panel 服务添加该节点将沿用 1panel 原服务的端口以及安装目录是否继续',
coreExist: '检测到该节点上已存在 1panel-core 服务无法添加该节点请检查后重试',
agentExist: '检测到该节点上已存在 1panel-agent 服务',
forceAdd: '强制添加',
forceAddHelper: '强制添加将强制替换该节点上的 1panel-agent 服务',
agentExist:
'检测到该节点上已存在 1panel-agent 服务继续添加将保留该节点数据仅替换 1panel-agent 服务是否继续',
reinstallHelper: '重新安装节点 {0}, 是否继续',
unhealthyCheck: '异常检查',
fixOperation: '修复方案',

View file

@ -1,7 +1,7 @@
<template>
<div>
<LayoutContent
back-name="Container"
back-name="ContainerItem"
:title="isCreate ? $t('container.create') : $t('commons.button.edit') + ' - ' + form.name"
>
<template #prompt>

View file

@ -594,7 +594,7 @@ const acceptParams = (params: DialogProps): void => {
list = dialogData.value.rowData.sourceAccountIDs.split(',');
for (const item of list) {
if (item) {
dialogData.value.rowData.sourceAccountItems.push(item);
dialogData.value.rowData.sourceAccountItems.push(Number(item));
}
}
}
@ -678,6 +678,10 @@ const verifyScript = (rule: any, value: any, callback: any) => {
const verifySpec = (rule: any, value: any, callback: any) => {
if (dialogData.value.rowData!.specCustom) {
if (dialogData.value.rowData!.specs.length === 0) {
callback(new Error(i18n.global.t('commons.rule.requiredInput')));
return;
}
for (let i = 0; i < dialogData.value.rowData!.specs.length; i++) {
if (dialogData.value.rowData!.specs[i]) {
continue;
@ -803,7 +807,6 @@ const rules = reactive({
{ validator: verifySpec, trigger: 'blur', required: true },
{ validator: verifySpec, trigger: 'change', required: true },
],
specCustom: [Rules.requiredSelect],
script: [{ validator: verifyScript, trigger: 'blur', required: true }],
containerName: [Rules.requiredSelect],

View file

@ -589,6 +589,7 @@ const changeType = async () => {
redirect_uri: res.data.redirect_uri,
};
}
break;
case 'GoogleDrive':
const res2 = await getClientInfo('GoogleDrive');
clientInfo.value = res2.data;
@ -599,9 +600,11 @@ const changeType = async () => {
redirect_uri: res2.data.redirect_uri,
};
}
break;
case 'SFTP':
dialogData.value.rowData.varsJson['port'] = 22;
dialogData.value.rowData.varsJson['authMode'] = 'password';
break;
}
};
const changeClientFrom = () => {

View file

@ -259,9 +259,6 @@ const buttons = [
},
{
label: i18n.global.t('commons.button.sync'),
disabled: (row: any) => {
return row.status.indexOf('Lost') !== -1 || row.status === 'Disable';
},
click: (row: any) => {
onSync(row);
},

View file

@ -122,7 +122,7 @@ const form = reactive({
});
const acceptParams = () => {
search();
search(true);
iniTerm();
};

View file

@ -635,7 +635,20 @@ function load18n(label: string) {
case 'docker_log':
return i18n.global.t('clean.dockerLog');
case 'task_log':
return i18n.global.t('clean.taskLog');
return i18n.global.t('logs.task');
case 'Database':
case 'Cronjob':
case 'Database':
case 'Container':
case 'App':
case 'System':
return i18n.global.t('menu.' + label.toLowerCase());
case 'Snapshot':
return i18n.global.t('setting.snapshot');
case 'AppStore':
return i18n.global.t('menu.apps');
case 'AITools':
return i18n.global.t('menu.aiTools');
case 'shell':
return i18n.global.t('clean.shell');
case 'containerShell':

View file

@ -223,7 +223,7 @@ const goDashboard = async (port: any, protocol: string) => {
const operateRuntime = async (operate: string, ID: number) => {
try {
const action = await ElMessageBox.confirm(
i18n.global.t('runtime.operatorHelper', [i18n.global.t('commons.operate' + operate)]),
i18n.global.t('runtime.operatorHelper', [i18n.global.t('commons.operate.' + operate)]),
i18n.global.t('commons.operate.' + operate),
{
confirmButtonText: i18n.global.t('commons.button.confirm'),

View file

@ -20,7 +20,7 @@
>
<template #label>
<a href="javascript:void(0);" class="check-label-a" @click="toPage(item[0])">
{{ $t('app.' + item[0]) }}
{{ $t(item[0] === 'website' ? 'menu.website' : 'app.' + item[0]) }}
</a>
</template>
<span class="resources">