diff --git a/.gitignore b/.gitignore index 0c5231617..e1f13e39a 100644 --- a/.gitignore +++ b/.gitignore @@ -4,8 +4,8 @@ *.dll *.so *.dylib -build/1panel_agent -build/1panel_core +build/1panel-agent +build/1panel-core # Mac .DS_Store diff --git a/Makefile b/Makefile index cfb37dd3b..2e53bb073 100644 --- a/Makefile +++ b/Makefile @@ -10,11 +10,11 @@ WEB_PATH=$(BASE_PAH)/frontend ASSERT_PATH= $(BASE_PAH)/core/cmd/server/web/assets CORE_MAIN= $(BASE_PAH)/cmd/server/main.go -CORE_NAME=1panel_core +CORE_NAME=1panel-core AGENT_PATH=$(BASE_PAH)/agent AGENT_MAIN= $(AGENT_PATH)/cmd/server/main.go -AGENT_NAME=1panel_agent +AGENT_NAME=1panel-agent clean_assets: @@ -61,4 +61,4 @@ build_xpack_all: build_frontend build_core_xpack_on_linux build_agent_xpack_on_l build_on_local: clean_assets build_frontend build_core_on_darwin build_agent_on_darwin upx_bin -build_xpack_on_local: clean_assets build_frontend build_agent_xpack_on_darwin build_agent_xpack_on_darwin upx_bin +build_xpack_on_local: clean_assets build_frontend build_core_xpack_on_darwin build_agent_xpack_on_darwin upx_bin diff --git a/agent/app/service/snapshot_create.go b/agent/app/service/snapshot_create.go index 793674678..690e22b5c 100644 --- a/agent/app/service/snapshot_create.go +++ b/agent/app/service/snapshot_create.go @@ -148,7 +148,7 @@ func (u *SnapshotService) HandleSnapshot(req dto.SnapshotCreate) error { taskItem.AddSubTask( "SnapCloseDBConn", func(t *task.Task) error { - taskItem.Log("######################## 6 / 8 ########################") + taskItem.Log("---------------------- 6 / 8 ----------------------") closeDatabase(itemHelper.snapAgentDB) closeDatabase(itemHelper.snapCoreDB) return nil @@ -194,7 +194,7 @@ type snapHelper struct { } func loadDbConn(snap *snapHelper, targetDir string, req dto.SnapshotCreate) error { - snap.Task.Log("######################## 1 / 8 ########################") + snap.Task.Log("---------------------- 1 / 8 ----------------------") snap.Task.LogStart(i18n.GetMsgByKey("SnapDBInfo")) pathDB := path.Join(global.CONF.System.BaseDir, "1panel/db") @@ -246,17 +246,17 @@ func loadDbConn(snap *snapHelper, targetDir string, req dto.SnapshotCreate) erro } func snapBaseData(snap snapHelper, targetDir string) error { - snap.Task.Log("######################## 2 / 8 ########################") + snap.Task.Log("---------------------- 2 / 8 ----------------------") snap.Task.LogStart(i18n.GetMsgByKey("SnapBaseInfo")) - err := common.CopyFile("/usr/local/bin/1panel", targetDir) - snap.Task.LogWithStatus(i18n.GetWithName("SnapCopy", "/usr/local/bin/1panel"), err) + err := common.CopyFile("/usr/local/bin/1panel-core", targetDir) + snap.Task.LogWithStatus(i18n.GetWithName("SnapCopy", "/usr/local/bin/1panel-core"), err) if err != nil { return err } - err = common.CopyFile("/usr/local/bin/1panel_agent", targetDir) - snap.Task.LogWithStatus(i18n.GetWithName("SnapCopy", "/usr/local/bin/1panel_agent"), err) + err = common.CopyFile("/usr/local/bin/1panel-agent", targetDir) + snap.Task.LogWithStatus(i18n.GetWithName("SnapCopy", "/usr/local/bin/1panel-agent"), err) if err != nil { return err } @@ -273,8 +273,8 @@ func snapBaseData(snap snapHelper, targetDir string) error { return err } - err = common.CopyFile("/etc/systemd/system/1panel_agent.service", targetDir) - snap.Task.LogWithStatus(i18n.GetWithName("SnapCopy", "/etc/systemd/system/1panel_agent.service"), err) + err = common.CopyFile("/etc/systemd/system/1panel-agent.service", targetDir) + snap.Task.LogWithStatus(i18n.GetWithName("SnapCopy", "/etc/systemd/system/1panel-agent.service"), err) if err != nil { return err } @@ -301,7 +301,7 @@ func snapBaseData(snap snapHelper, targetDir string) error { } func snapAppImage(snap snapHelper, req dto.SnapshotCreate, targetDir string) error { - snap.Task.Log("######################## 3 / 8 ########################") + snap.Task.Log("---------------------- 3 / 8 ----------------------") snap.Task.LogStart(i18n.GetMsgByKey("SnapInstallApp")) var imageList []string @@ -333,7 +333,7 @@ func snapAppImage(snap snapHelper, req dto.SnapshotCreate, targetDir string) err } func snapBackupData(snap snapHelper, req dto.SnapshotCreate, targetDir string) error { - snap.Task.Log("######################## 4 / 8 ########################") + snap.Task.Log("---------------------- 4 / 8 ----------------------") snap.Task.LogStart(i18n.GetMsgByKey("SnapLocalBackup")) excludes := loadBackupExcludes(snap, req.BackupData) @@ -389,7 +389,7 @@ func loadAppBackupExcludes(req []dto.DataTree) []string { } func snapPanelData(snap snapHelper, req dto.SnapshotCreate, targetDir string) error { - snap.Task.Log("######################## 5 / 8 ########################") + snap.Task.Log("---------------------- 5 / 8 ----------------------") snap.Task.LogStart(i18n.GetMsgByKey("SnapPanelData")) excludes := loadPanelExcludes(req.PanelData) @@ -447,7 +447,7 @@ func loadPanelExcludes(req []dto.DataTree) []string { } func snapCompress(snap snapHelper, rootDir string, secret string) error { - snap.Task.Log("######################## 7 / 8 ########################") + snap.Task.Log("---------------------- 7 / 8 ----------------------") snap.Task.LogStart(i18n.GetMsgByKey("SnapCompress")) tmpDir := path.Join(global.CONF.System.TmpDir, "system") @@ -471,7 +471,7 @@ func snapCompress(snap snapHelper, rootDir string, secret string) error { } func snapUpload(snap snapHelper, accounts string, file string) error { - snap.Task.Log("######################## 8 / 8 ########################") + snap.Task.Log("---------------------- 8 / 8 ----------------------") snap.Task.LogStart(i18n.GetMsgByKey("SnapUpload")) source := path.Join(global.CONF.System.TmpDir, "system", path.Base(file)) diff --git a/agent/app/service/snapshot_recover.go b/agent/app/service/snapshot_recover.go index b25019200..5b737bada 100644 --- a/agent/app/service/snapshot_recover.go +++ b/agent/app/service/snapshot_recover.go @@ -83,7 +83,7 @@ func (u *SnapshotService) SnapshotRecover(req dto.SnapshotRecover) error { taskItem.AddSubTaskWithAlias( "RecoverDecompress", func(t *task.Task) error { - itemHelper.Task.Log("######################## 2 / 10 ########################") + itemHelper.Task.Log("---------------------- 2 / 10 ----------------------") itemHelper.Task.LogStart(i18n.GetWithName("RecoverDecompress", snap.Name)) err := itemHelper.FileOp.TarGzExtractPro(fmt.Sprintf("%s/%s.tar.gz", rootDir, snap.Name), rootDir, req.Secret) itemHelper.Task.LogWithStatus(i18n.GetMsgByKey("Decompress"), err) @@ -139,7 +139,7 @@ func (u *SnapshotService) SnapshotRecover(req dto.SnapshotRecover) error { taskItem.AddSubTaskWithAlias( "RecoverBackups", func(t *task.Task) error { - itemHelper.Task.Log("######################## 8 / 10 ########################") + itemHelper.Task.Log("---------------------- 8 / 10 ----------------------") itemHelper.Task.LogStart(i18n.GetWithName("RecoverBackups", snap.Name)) err := itemHelper.FileOp.TarGzExtractPro(path.Join(rootDir, snap.Name, "/1panel_backup.tar.gz"), snapJson.BackupDataDir, "") itemHelper.Task.LogWithStatus(i18n.GetMsgByKey("Decompress"), err) @@ -153,7 +153,7 @@ func (u *SnapshotService) SnapshotRecover(req dto.SnapshotRecover) error { taskItem.AddSubTaskWithAlias( "RecoverPanelData", func(t *task.Task) error { - itemHelper.Task.Log("######################## 9 / 10 ########################") + itemHelper.Task.Log("---------------------- 9 / 10 ----------------------") itemHelper.Task.LogStart(i18n.GetWithName("RecoverPanelData", snap.Name)) err := itemHelper.FileOp.TarGzExtractPro(path.Join(rootDir, snap.Name, "/1panel_data.tar.gz"), path.Join(snapJson.BaseDir, "1panel"), "") itemHelper.Task.LogWithStatus(i18n.GetMsgByKey("Decompress"), err) @@ -183,7 +183,7 @@ func (u *SnapshotService) SnapshotRecover(req dto.SnapshotRecover) error { } func handleDownloadSnapshot(itemHelper *snapRecoverHelper, snap model.Snapshot, targetDir string) error { - itemHelper.Task.Log("######################## 1 / 10 ########################") + itemHelper.Task.Log("---------------------- 1 / 10 ----------------------") itemHelper.Task.LogStart(i18n.GetMsgByKey("RecoverDownload")) account, client, err := NewBackupClientWithID(snap.DownloadAccountID) @@ -200,7 +200,7 @@ func handleDownloadSnapshot(itemHelper *snapRecoverHelper, snap model.Snapshot, } func backupBeforeRecover(name string, itemHelper *snapRecoverHelper) error { - itemHelper.Task.Log("######################## 3 / 10 ########################") + itemHelper.Task.Log("---------------------- 3 / 10 ----------------------") itemHelper.Task.LogStart(i18n.GetMsgByKey("BackupBeforeRecover")) rootDir := fmt.Sprintf("%s/1panel_original/original_%s", global.CONF.System.BaseDir, name) @@ -224,13 +224,13 @@ func backupBeforeRecover(name string, itemHelper *snapRecoverHelper) error { if err != nil { return err } - err = itemHelper.FileOp.CopyFile("/usr/local/bin/1panel", baseDir) - itemHelper.Task.LogWithStatus(i18n.GetWithName("SnapCopy", "/usr/local/bin/1panel"), err) + err = itemHelper.FileOp.CopyFile("/usr/local/bin/1panel-core", baseDir) + itemHelper.Task.LogWithStatus(i18n.GetWithName("SnapCopy", "/usr/local/bin/1panel-core"), err) if err != nil { return err } - err = itemHelper.FileOp.CopyFile("/usr/local/bin/1panel_agent", baseDir) - itemHelper.Task.LogWithStatus(i18n.GetWithName("SnapCopy", "/usr/local/bin/1panel_agent"), err) + err = itemHelper.FileOp.CopyFile("/usr/local/bin/1panel-agent", baseDir) + itemHelper.Task.LogWithStatus(i18n.GetWithName("SnapCopy", "/usr/local/bin/1panel-agent"), err) if err != nil { return err } @@ -239,8 +239,8 @@ func backupBeforeRecover(name string, itemHelper *snapRecoverHelper) error { if err != nil { return err } - err = itemHelper.FileOp.CopyFile("/etc/systemd/system/1panel_agent.service", baseDir) - itemHelper.Task.LogWithStatus(i18n.GetWithName("SnapCopy", "/etc/systemd/system/1panel_agent.service"), err) + err = itemHelper.FileOp.CopyFile("/etc/systemd/system/1panel-agent.service", baseDir) + itemHelper.Task.LogWithStatus(i18n.GetWithName("SnapCopy", "/etc/systemd/system/1panel-agent.service"), err) if err != nil { return err } @@ -253,7 +253,7 @@ func backupBeforeRecover(name string, itemHelper *snapRecoverHelper) error { } func readFromJson(rootDir string, itemHelper *snapRecoverHelper) (SnapshotJson, error) { - itemHelper.Task.Log("######################## 4 / 10 ########################") + itemHelper.Task.Log("---------------------- 4 / 10 ----------------------") itemHelper.Task.LogStart(i18n.GetMsgByKey("Readjson")) snapJsonPath := path.Join(rootDir, "base/snapshot.json") @@ -277,7 +277,7 @@ func readFromJson(rootDir string, itemHelper *snapRecoverHelper) (SnapshotJson, } func recoverAppData(src string, itemHelper *snapRecoverHelper) error { - itemHelper.Task.Log("######################## 5 / 10 ########################") + itemHelper.Task.Log("---------------------- 5 / 10 ----------------------") itemHelper.Task.LogStart(i18n.GetMsgByKey("RecoverApp")) if _, err := os.Stat(path.Join(src, "images.tar.gz")); err != nil { @@ -325,7 +325,7 @@ func recoverAppData(src string, itemHelper *snapRecoverHelper) error { } func recoverBaseData(src string, itemHelper *snapRecoverHelper) error { - itemHelper.Task.Log("######################## 6 / 10 ########################") + itemHelper.Task.Log("---------------------- 6 / 10 ----------------------") itemHelper.Task.LogStart(i18n.GetMsgByKey("SnapBaseInfo")) err := itemHelper.FileOp.CopyFile(path.Join(src, "1pctl"), "/usr/local/bin") @@ -335,12 +335,12 @@ func recoverBaseData(src string, itemHelper *snapRecoverHelper) error { } err = itemHelper.FileOp.CopyFile(path.Join(src, "1panel"), "/usr/local/bin") - itemHelper.Task.LogWithStatus(i18n.GetWithName("SnapCopy", "/usr/local/bin/1panel"), err) + itemHelper.Task.LogWithStatus(i18n.GetWithName("SnapCopy", "/usr/local/bin/1panel-core"), err) if err != nil { return err } - err = itemHelper.FileOp.CopyFile(path.Join(src, "1panel_agent"), "/usr/local/bin") - itemHelper.Task.LogWithStatus(i18n.GetWithName("SnapCopy", "/usr/local/bin/1panel_agent"), err) + err = itemHelper.FileOp.CopyFile(path.Join(src, "1panel-agent"), "/usr/local/bin") + itemHelper.Task.LogWithStatus(i18n.GetWithName("SnapCopy", "/usr/local/bin/1panel-agent"), err) if err != nil { return err } @@ -349,8 +349,8 @@ func recoverBaseData(src string, itemHelper *snapRecoverHelper) error { if err != nil { return err } - err = itemHelper.FileOp.CopyFile(path.Join(src, "1panel_agent.service"), "/etc/systemd/system") - itemHelper.Task.LogWithStatus(i18n.GetWithName("SnapCopy", "/etc/systemd/system/1panel_agent.service"), err) + err = itemHelper.FileOp.CopyFile(path.Join(src, "1panel-agent.service"), "/etc/systemd/system") + itemHelper.Task.LogWithStatus(i18n.GetWithName("SnapCopy", "/etc/systemd/system/1panel-agent.service"), err) if err != nil { return err } @@ -375,7 +375,7 @@ func recoverBaseData(src string, itemHelper *snapRecoverHelper) error { } func recoverDBData(src string, itemHelper *snapRecoverHelper) error { - itemHelper.Task.Log("######################## 7 / 10 ########################") + itemHelper.Task.Log("---------------------- 7 / 10 ----------------------") itemHelper.Task.LogStart(i18n.GetMsgByKey("RecoverDBData")) err := itemHelper.FileOp.CopyDirWithExclude(src, path.Join(global.CONF.System.BaseDir, "1panel"), nil) @@ -384,7 +384,7 @@ func recoverDBData(src string, itemHelper *snapRecoverHelper) error { } func restartCompose(composePath string, itemHelper *snapRecoverHelper) error { - itemHelper.Task.Log("######################## 10 / 10 ########################") + itemHelper.Task.Log("---------------------- 10 / 10 ----------------------") itemHelper.Task.LogStart(i18n.GetMsgByKey("RecoverCompose")) composes, err := composeRepo.ListRecord() diff --git a/agent/app/service/snapshot_rollback.go b/agent/app/service/snapshot_rollback.go index 5af322e68..1fab2615b 100644 --- a/agent/app/service/snapshot_rollback.go +++ b/agent/app/service/snapshot_rollback.go @@ -42,16 +42,16 @@ func (u *SnapshotService) SnapshotRollback(req dto.SnapshotRecover) error { nil, ) taskItem.AddSubTask( - i18n.GetWithName("SnapCopy", "/usr/local/bin/1panel"), + i18n.GetWithName("SnapCopy", "/usr/local/bin/1panel-core"), func(t *task.Task) error { return FileOp.CopyFile(path.Join(baseDir, "1panel"), "/usr/local/bin") }, nil, ) taskItem.AddSubTask( - i18n.GetWithName("SnapCopy", "/usr/local/bin/1panel_agent"), + i18n.GetWithName("SnapCopy", "/usr/local/bin/1panel-agent"), func(t *task.Task) error { - return FileOp.CopyFile(path.Join(baseDir, "1panel_agent"), "/usr/local/bin") + return FileOp.CopyFile(path.Join(baseDir, "1panel-agent"), "/usr/local/bin") }, nil, ) @@ -63,7 +63,7 @@ func (u *SnapshotService) SnapshotRollback(req dto.SnapshotRecover) error { nil, ) taskItem.AddSubTask( - i18n.GetWithName("SnapCopy", "/etc/systemd/system/1panel_agent.service"), + i18n.GetWithName("SnapCopy", "/etc/systemd/system/1panel-agent.service"), func(t *task.Task) error { return FileOp.CopyFile(path.Join(baseDir, "1panel.service"), "/etc/systemd/system") }, diff --git a/agent/init/hook/hook.go b/agent/init/hook/hook.go index 9ec292652..bf202d536 100644 --- a/agent/init/hook/hook.go +++ b/agent/init/hook/hook.go @@ -26,6 +26,17 @@ func Init() { if err != nil { global.LOG.Fatalf("load current node before start failed, err: %v", err) } + baseDir, err := settingRepo.Get(settingRepo.WithByKey("BaseDir")) + if err != nil { + global.LOG.Fatalf("load base dir before start failed, err: %v", err) + } + global.CONF.System.BaseDir = baseDir.Value + version, err := settingRepo.Get(settingRepo.WithByKey("SystemVersion")) + if err != nil { + global.LOG.Fatalf("load system version before start failed, err: %v", err) + } + global.CONF.System.Version = version.Value + global.IsMaster = node.Value == "127.0.0.1" || len(node.Value) == 0 if global.IsMaster { db.InitCoreDB() diff --git a/agent/init/migration/migrate.go b/agent/init/migration/migrate.go index 328fc7812..95f44a6c1 100644 --- a/agent/init/migration/migrate.go +++ b/agent/init/migration/migrate.go @@ -15,7 +15,6 @@ func Init() { migrations.InitImageRepo, migrations.InitDefaultCA, migrations.InitPHPExtensions, - migrations.AddTask, migrations.UpdateWebsite, migrations.UpdateWebsiteDomain, migrations.UpdateApp, @@ -23,6 +22,7 @@ func Init() { migrations.UpdateAppInstall, migrations.UpdateSnapshot, migrations.UpdateCronjob, + migrations.InitBaseDir, }) if err := m.Migrate(); err != nil { global.LOG.Error(err) diff --git a/agent/init/migration/migrations/init.go b/agent/init/migration/migrations/init.go index 3ddc6cd86..6be726073 100644 --- a/agent/init/migration/migrations/init.go +++ b/agent/init/migration/migrations/init.go @@ -53,7 +53,6 @@ var AddTable = &gormigrate.Migration{ &model.WebsiteDnsAccount{}, &model.WebsiteDomain{}, &model.WebsiteSSL{}, - &model.Task{}, ) }, } @@ -78,6 +77,12 @@ var InitSetting = &gormigrate.Migration{ return err } global.IsMaster = isMaster + if err := tx.Create(&model.Setting{Key: "BaseDir", Value: global.CONF.System.BaseDir}).Error; err != nil { + return err + } + if err := tx.Create(&model.Setting{Key: "CurrentNode", Value: currentNode}).Error; err != nil { + return err + } if err := tx.Create(&model.Setting{Key: "CurrentNode", Value: currentNode}).Error; err != nil { return err } @@ -211,14 +216,6 @@ var InitPHPExtensions = &gormigrate.Migration{ }, } -var AddTask = &gormigrate.Migration{ - ID: "20240802-add-task", - Migrate: func(tx *gorm.DB) error { - return tx.AutoMigrate( - &model.Task{}) - }, -} - var UpdateWebsite = &gormigrate.Migration{ ID: "20240812-update-website", Migrate: func(tx *gorm.DB) error { @@ -273,3 +270,13 @@ var UpdateCronjob = &gormigrate.Migration{ return tx.AutoMigrate(&model.Cronjob{}, &model.JobRecords{}) }, } + +var InitBaseDir = &gormigrate.Migration{ + ID: "20241122-init-setting", + Migrate: func(tx *gorm.DB) error { + if err := tx.Create(&model.Setting{Key: "BaseDir", Value: global.CONF.System.BaseDir}).Error; err != nil { + return err + } + return nil + }, +} diff --git a/agent/init/viper/viper.go b/agent/init/viper/viper.go index 734515308..61401f63e 100644 --- a/agent/init/viper/viper.go +++ b/agent/init/viper/viper.go @@ -38,9 +38,6 @@ func Init() { panic(fmt.Errorf("Fatal error config file: %s \n", err)) } } else { - baseDir = loadParams("BASE_DIR") - version = loadParams("ORIGINAL_VERSION") - reader := bytes.NewReader(conf.AppYaml) if err := v.ReadConfig(reader); err != nil { panic(fmt.Errorf("Fatal error config file: %s \n", err)) diff --git a/cmd/server/cmd/restore.go b/cmd/server/cmd/restore.go index b800d3c9b..f4dbf5b8f 100644 --- a/cmd/server/cmd/restore.go +++ b/cmd/server/cmd/restore.go @@ -6,11 +6,9 @@ import ( "path" "sort" "strings" - "time" cmdUtils "github.com/1Panel-dev/1Panel/core/utils/cmd" "github.com/1Panel-dev/1Panel/core/utils/files" - "github.com/pkg/errors" "github.com/spf13/cobra" ) @@ -45,31 +43,20 @@ var restoreCmd = &cobra.Command{ tmpPath = path.Join(upgradeDir, tmpPath, "original") fmt.Printf("(0/4) 开始从 %s 目录回滚 1Panel 服务及数据... \n", tmpPath) - if err := files.CopyFile(path.Join(tmpPath, "1panel"), "/usr/local/bin", false); err != nil { + if err := files.CopyItem(false, true, path.Join(tmpPath, "1panel*"), "/usr/local/bin"); err != nil { return err } fmt.Println("(1/4) 1panel 二进制回滚成功") - if err := files.CopyFile(path.Join(tmpPath, "1pctl"), "/usr/local/bin", false); err != nil { + if err := files.CopyItem(false, true, path.Join(tmpPath, "1pctl"), "/usr/local/bin"); err != nil { return err } fmt.Println("(2/4) 1panel 脚本回滚成功") - if err := files.CopyFile(path.Join(tmpPath, "1panel.service"), "/etc/systemd/system", false); err != nil { + if err := files.CopyItem(false, true, path.Join(tmpPath, "1panel*.service"), "/etc/systemd/system"); err != nil { return err } fmt.Println("(3/4) 1panel 服务回滚成功") - checkPointOfWal() if _, err := os.Stat(path.Join(tmpPath, "core.db")); err == nil { - if err := files.CopyFile(path.Join(tmpPath, "core.db"), path.Join(baseDir, "core/db"), false); err != nil { - return err - } - } - if _, err := os.Stat(path.Join(tmpPath, "agent.db")); err == nil { - if err := files.CopyFile(path.Join(tmpPath, "agent.db"), path.Join(baseDir, "1panel/db"), false); err != nil { - return err - } - } - if _, err := os.Stat(path.Join(tmpPath, "db.tar.gz")); err == nil { - if err := handleUnTar(path.Join(tmpPath, "db.tar.gz"), path.Join(baseDir, "1panel")); err != nil { + if err := files.CopyItem(true, true, path.Join(tmpPath, "db"), path.Join(baseDir, "1panel")); err != nil { return err } } @@ -80,14 +67,6 @@ var restoreCmd = &cobra.Command{ }, } -func checkPointOfWal() { - db, err := loadDBConn() - if err != nil { - return - } - _ = db.Exec("PRAGMA wal_checkpoint(TRUNCATE);").Error -} - func loadRestorePath(upgradeDir string) (string, error) { if _, err := os.Stat(upgradeDir); err != nil && os.IsNotExist(err) { return "暂无可回滚文件", nil @@ -110,18 +89,3 @@ func loadRestorePath(upgradeDir string) (string, error) { }) return folders[0], nil } - -func handleUnTar(sourceFile, targetDir string) error { - if _, err := os.Stat(targetDir); err != nil && os.IsNotExist(err) { - if err = os.MkdirAll(targetDir, os.ModePerm); err != nil { - return err - } - } - - commands := fmt.Sprintf("tar zxvfC %s %s", sourceFile, targetDir) - stdout, err := cmdUtils.ExecWithTimeOut(commands, 20*time.Second) - if err != nil { - return errors.New(stdout) - } - return nil -} diff --git a/core/app/dto/task.go b/core/app/dto/task.go new file mode 100644 index 000000000..f88baf660 --- /dev/null +++ b/core/app/dto/task.go @@ -0,0 +1,13 @@ +package dto + +import "github.com/1Panel-dev/1Panel/core/app/model" + +type SearchTaskLogReq struct { + Status string `json:"status"` + Type string `json:"type"` + PageInfo +} + +type TaskDTO struct { + model.Task +} diff --git a/core/app/model/task.go b/core/app/model/task.go new file mode 100644 index 000000000..46d003237 --- /dev/null +++ b/core/app/model/task.go @@ -0,0 +1,18 @@ +package model + +import "time" + +type Task struct { + ID string `gorm:"primarykey;" json:"id"` + Name string `json:"name"` + Type string `json:"type"` + Operate string `json:"operate"` + LogFile string `json:"logFile"` + Status string `json:"status"` + ErrorMsg string `json:"errorMsg"` + OperationLogID uint `json:"operationLogID"` + ResourceID uint `json:"resourceID"` + CurrentStep string `json:"currentStep"` + EndAt time.Time `json:"endAt"` + CreatedAt time.Time `json:"createdAt"` +} diff --git a/core/app/model/upgrade_log.go b/core/app/model/upgrade_log.go new file mode 100644 index 000000000..11230b9a6 --- /dev/null +++ b/core/app/model/upgrade_log.go @@ -0,0 +1,9 @@ +package model + +type UpgradeLog struct { + BaseModel + NodeID uint `json:"nodeID"` + OldVersion string `json:"oldVersion"` + NewVersion string `json:"newVersion"` + BackupFile string `json:"backupFile"` +} diff --git a/core/app/repo/common.go b/core/app/repo/common.go index 048c2b3ed..d1b34c78f 100644 --- a/core/app/repo/common.go +++ b/core/app/repo/common.go @@ -17,6 +17,7 @@ type ICommonRepo interface { WithByType(ty string) DBOption WithByKey(key string) DBOption WithOrderBy(orderStr string) DBOption + WithByStatus(status string) DBOption WithOrderRuleBy(orderBy, order string) DBOption } @@ -66,6 +67,11 @@ func (c *CommonRepo) WithByKey(key string) DBOption { return g.Where("key = ?", key) } } +func (c *CommonRepo) WithByStatus(status string) DBOption { + return func(g *gorm.DB) *gorm.DB { + return g.Where("status = ?", status) + } +} func (c *CommonRepo) WithOrderBy(orderStr string) DBOption { return func(g *gorm.DB) *gorm.DB { return g.Order(orderStr) diff --git a/core/app/repo/task.go b/core/app/repo/task.go new file mode 100644 index 000000000..0d7edcef5 --- /dev/null +++ b/core/app/repo/task.go @@ -0,0 +1,90 @@ +package repo + +import ( + "context" + + "github.com/1Panel-dev/1Panel/core/app/model" + "github.com/1Panel-dev/1Panel/core/global" + "gorm.io/gorm" +) + +type TaskRepo struct { +} + +type ITaskRepo interface { + Save(ctx context.Context, task *model.Task) error + GetFirst(opts ...DBOption) (model.Task, error) + Page(page, size int, opts ...DBOption) (int64, []model.Task, error) + Update(ctx context.Context, task *model.Task) error + + WithByID(id string) DBOption + WithResourceID(id uint) DBOption + WithOperate(taskOperate string) DBOption +} + +func NewITaskRepo() ITaskRepo { + return &TaskRepo{} +} + +func getTaskDb(opts ...DBOption) *gorm.DB { + db := global.TaskDB + for _, opt := range opts { + db = opt(db) + } + return db +} + +func getTaskTx(ctx context.Context, opts ...DBOption) *gorm.DB { + tx, ok := ctx.Value("db").(*gorm.DB) + if ok { + for _, opt := range opts { + tx = opt(tx) + } + return tx + } + return getTaskDb(opts...) +} + +func (t TaskRepo) WithByID(id string) DBOption { + return func(g *gorm.DB) *gorm.DB { + return g.Where("id = ?", id) + } +} + +func (t TaskRepo) WithOperate(taskOperate string) DBOption { + return func(g *gorm.DB) *gorm.DB { + return g.Where("operate = ?", taskOperate) + } +} + +func (t TaskRepo) WithResourceID(id uint) DBOption { + return func(g *gorm.DB) *gorm.DB { + return g.Where("resource_id = ?", id) + } +} + +func (t TaskRepo) Save(ctx context.Context, task *model.Task) error { + return getTaskTx(ctx).Save(&task).Error +} + +func (t TaskRepo) GetFirst(opts ...DBOption) (model.Task, error) { + var task model.Task + db := getTaskDb(opts...).Model(&model.Task{}) + if err := db.First(&task).Error; err != nil { + return task, err + } + return task, nil +} + +func (t TaskRepo) Page(page, size int, opts ...DBOption) (int64, []model.Task, error) { + var tasks []model.Task + db := getTaskDb(opts...).Model(&model.Task{}) + count := int64(0) + db = db.Count(&count) + err := db.Limit(size).Offset(size * (page - 1)).Find(&tasks).Error + return count, tasks, err +} + +func (t TaskRepo) Update(ctx context.Context, task *model.Task) error { + return getTaskTx(ctx).Save(&task).Error +} diff --git a/core/app/repo/upgrade_log.go b/core/app/repo/upgrade_log.go new file mode 100644 index 000000000..1a9d27b5d --- /dev/null +++ b/core/app/repo/upgrade_log.go @@ -0,0 +1,93 @@ +package repo + +import ( + "github.com/1Panel-dev/1Panel/core/app/model" + "github.com/1Panel-dev/1Panel/core/global" + "gorm.io/gorm" +) + +type UpgradeLogRepo struct{} + +type IUpgradeLogRepo interface { + Get(opts ...DBOption) (model.UpgradeLog, error) + List(opts ...DBOption) ([]model.UpgradeLog, error) + Create(log *model.UpgradeLog) error + Page(limit, offset int, opts ...DBOption) (int64, []model.UpgradeLog, error) + Delete(opts ...DBOption) error + + WithByNodeID(nodeID uint) DBOption + WithByIDs(ids []uint) DBOption + WithByID(id uint) DBOption +} + +func NewIUpgradeLogRepo() IUpgradeLogRepo { + return &UpgradeLogRepo{} +} + +func (u *UpgradeLogRepo) Get(opts ...DBOption) (model.UpgradeLog, error) { + var log model.UpgradeLog + db := global.DB + for _, opt := range opts { + db = opt(db) + } + err := db.First(&log).Error + return log, err +} + +func (u *UpgradeLogRepo) List(opts ...DBOption) ([]model.UpgradeLog, error) { + var logs []model.UpgradeLog + db := global.DB + for _, opt := range opts { + db = opt(db) + } + err := db.Find(&logs).Error + return logs, err +} + +func (u *UpgradeLogRepo) Clean() error { + return global.DB.Exec("delete from upgrade_logs;").Error +} + +func (u *UpgradeLogRepo) Create(log *model.UpgradeLog) error { + return global.DB.Create(log).Error +} + +func (u *UpgradeLogRepo) Save(log *model.UpgradeLog) error { + return global.DB.Save(log).Error +} + +func (u *UpgradeLogRepo) Delete(opts ...DBOption) error { + db := global.DB + for _, opt := range opts { + db = opt(db) + } + return db.Delete(&model.UpgradeLog{}).Error +} + +func (u *UpgradeLogRepo) Page(page, size int, opts ...DBOption) (int64, []model.UpgradeLog, error) { + var ops []model.UpgradeLog + db := global.DB.Model(&model.UpgradeLog{}) + for _, opt := range opts { + db = opt(db) + } + count := int64(0) + db = db.Count(&count) + err := db.Limit(size).Offset(size * (page - 1)).Find(&ops).Error + return count, ops, err +} + +func (c *UpgradeLogRepo) WithByNodeID(nodeID uint) DBOption { + return func(g *gorm.DB) *gorm.DB { + return g.Where("node_id = ?", nodeID) + } +} +func (c *UpgradeLogRepo) WithByID(id uint) DBOption { + return func(g *gorm.DB) *gorm.DB { + return g.Where("id = ?", id) + } +} +func (c *UpgradeLogRepo) WithByIDs(ids []uint) DBOption { + return func(g *gorm.DB) *gorm.DB { + return g.Where("id in (?)", ids) + } +} diff --git a/core/app/service/entry.go b/core/app/service/entry.go index 5a27332c7..6ab8eea8c 100644 --- a/core/app/service/entry.go +++ b/core/app/service/entry.go @@ -11,4 +11,6 @@ var ( logRepo = repo.NewILogRepo() groupRepo = repo.NewIGroupRepo() launcherRepo = repo.NewILauncherRepo() + + taskRepo = repo.NewITaskRepo() ) diff --git a/core/app/service/task.go b/core/app/service/task.go new file mode 100644 index 000000000..1b291cf80 --- /dev/null +++ b/core/app/service/task.go @@ -0,0 +1,42 @@ +package service + +import ( + "github.com/1Panel-dev/1Panel/core/app/dto" + "github.com/1Panel-dev/1Panel/core/app/repo" +) + +type TaskLogService struct{} + +type ITaskLogService interface { + Page(req dto.SearchTaskLogReq) (int64, []dto.TaskDTO, error) +} + +func NewITaskService() ITaskLogService { + return &TaskLogService{} +} + +func (u *TaskLogService) Page(req dto.SearchTaskLogReq) (int64, []dto.TaskDTO, error) { + opts := []repo.DBOption{ + commonRepo.WithOrderBy("created_at desc"), + } + if req.Status != "" { + opts = append(opts, commonRepo.WithByStatus(req.Status)) + } + if req.Type != "" { + opts = append(opts, commonRepo.WithByType(req.Type)) + } + + total, tasks, err := taskRepo.Page( + req.Page, + req.PageSize, + opts..., + ) + var items []dto.TaskDTO + for _, t := range tasks { + item := dto.TaskDTO{ + Task: t, + } + items = append(items, item) + } + return total, items, err +} diff --git a/core/app/service/upgrade.go b/core/app/service/upgrade.go index fe3e0010c..4f2344988 100644 --- a/core/app/service/upgrade.go +++ b/core/app/service/upgrade.go @@ -83,8 +83,10 @@ func (u *UpgradeService) LoadNotes(req dto.Upgrade) (string, error) { func (u *UpgradeService) Upgrade(req dto.Upgrade) error { global.LOG.Info("start to upgrade now...") timeStr := time.Now().Format(constant.DateTimeSlimLayout) - rootDir := path.Join(global.CONF.System.BaseDir, fmt.Sprintf("1panel/tmp/upgrade/upgrade_%s/downloads", timeStr)) - originalDir := path.Join(global.CONF.System.BaseDir, fmt.Sprintf("1panel/tmp/upgrade/upgrade_%s/original", timeStr)) + baseDir := path.Join(global.CONF.System.BaseDir, fmt.Sprintf("1panel/tmp/upgrade/%s", req.Version)) + rootDir := path.Join(baseDir, fmt.Sprintf("upgrade_%s/downloads", timeStr)) + _ = os.RemoveAll(baseDir) + originalDir := path.Join(baseDir, fmt.Sprintf("upgrade_%s/original", timeStr)) if err := os.MkdirAll(rootDir, os.ModePerm); err != nil { return err } @@ -127,13 +129,13 @@ func (u *UpgradeService) Upgrade(req dto.Upgrade) error { } global.LOG.Info("backup original data successful, now start to upgrade!") - if err := files.CopyFile(path.Join(tmpDir, "1panel"), "/usr/local/bin", false); err != nil { + if err := files.CopyItem(false, true, path.Join(tmpDir, "1panel*"), "/usr/local/bin"); err != nil { global.LOG.Errorf("upgrade 1panel failed, err: %v", err) u.handleRollback(originalDir, 1) return } - if err := files.CopyFile(path.Join(tmpDir, "1pctl"), "/usr/local/bin", false); err != nil { + if err := files.CopyItem(false, true, path.Join(tmpDir, "1pctl"), "/usr/local/bin"); err != nil { global.LOG.Errorf("upgrade 1pctl failed, err: %v", err) u.handleRollback(originalDir, 2) return @@ -144,7 +146,7 @@ func (u *UpgradeService) Upgrade(req dto.Upgrade) error { return } - if err := files.CopyFile(path.Join(tmpDir, "1panel.service"), "/etc/systemd/system", false); err != nil { + if err := files.CopyItem(false, true, path.Join(tmpDir, "1panel*.service"), "/etc/systemd/system"); err != nil { global.LOG.Errorf("upgrade 1panel.service failed, err: %v", err) u.handleRollback(originalDir, 3) return @@ -154,24 +156,22 @@ func (u *UpgradeService) Upgrade(req dto.Upgrade) error { go writeLogs(req.Version) _ = settingRepo.Update("SystemVersion", req.Version) _ = settingRepo.Update("SystemStatus", "Free") - checkPointOfWal() _, _ = cmd.ExecWithTimeOut("systemctl daemon-reload && systemctl restart 1panel.service", 1*time.Minute) }() return nil } func (u *UpgradeService) handleBackup(originalDir string) error { - if err := files.CopyFile("/usr/local/bin/1panel", originalDir, false); err != nil { + if err := files.CopyItem(false, true, "/usr/local/bin/1panel*", originalDir); err != nil { return err } - if err := files.CopyFile("/usr/local/bin/1pctl", originalDir, false); err != nil { + if err := files.CopyItem(false, true, "/usr/local/bin/1pctl", originalDir); err != nil { return err } - if err := files.CopyFile("/etc/systemd/system/1panel.service", originalDir, false); err != nil { + if err := files.CopyItem(false, true, "/etc/systemd/system/1panel*.service", originalDir); err != nil { return err } - checkPointOfWal() - if err := files.HandleTar(path.Join(global.CONF.System.BaseDir, "1panel/db"), originalDir, "db.tar.gz", "db/1Panel.db-*", ""); err != nil { + if err := files.CopyItem(true, true, path.Join(global.CONF.System.BaseDir, "1panel/db"), originalDir); err != nil { return err } return nil @@ -180,31 +180,25 @@ func (u *UpgradeService) handleBackup(originalDir string) error { func (u *UpgradeService) handleRollback(originalDir string, errStep int) { _ = settingRepo.Update("SystemStatus", "Free") - checkPointOfWal() dbPath := path.Join(global.CONF.System.BaseDir, "1panel/db") - if _, err := os.Stat(path.Join(originalDir, "1Panel.db")); err == nil { - if err := files.CopyFile(path.Join(originalDir, "1Panel.db"), dbPath, false); err != nil { + if _, err := os.Stat(path.Join(originalDir, "db")); err == nil { + if err := files.CopyItem(true, true, path.Join(originalDir, "db"), dbPath); err != nil { global.LOG.Errorf("rollback 1panel db failed, err: %v", err) } } - if _, err := os.Stat(path.Join(originalDir, "db.tar.gz")); err == nil { - if err := files.HandleUnTar(path.Join(originalDir, "db.tar.gz"), dbPath, ""); err != nil { - global.LOG.Errorf("rollback 1panel db failed, err: %v", err) - } - } - if err := files.CopyFile(path.Join(originalDir, "1panel"), "/usr/local/bin", false); err != nil { + if err := files.CopyItem(false, true, path.Join(originalDir, "1panel*"), "/usr/local/bin"); err != nil { global.LOG.Errorf("rollback 1pctl failed, err: %v", err) } if errStep == 1 { return } - if err := files.CopyFile(path.Join(originalDir, "1pctl"), "/usr/local/bin", false); err != nil { + if err := files.CopyItem(false, true, path.Join(originalDir, "1pctl"), "/usr/local/bin"); err != nil { global.LOG.Errorf("rollback 1panel failed, err: %v", err) } if errStep == 2 { return } - if err := files.CopyFile(path.Join(originalDir, "1panel.service"), "/etc/systemd/system", false); err != nil { + if err := files.CopyItem(false, true, path.Join(originalDir, "1panel*.service"), "/etc/systemd/system"); err != nil { global.LOG.Errorf("rollback 1panel failed, err: %v", err) } } @@ -345,9 +339,3 @@ func loadArch() (string, error) { } return "", fmt.Errorf("unsupported such arch: %s", std) } - -func checkPointOfWal() { - if err := global.DB.Exec("PRAGMA wal_checkpoint(TRUNCATE);").Error; err != nil { - global.LOG.Errorf("handle check point failed, err: %v", err) - } -} diff --git a/core/app/task/task.go b/core/app/task/task.go new file mode 100644 index 000000000..dd6fbb136 --- /dev/null +++ b/core/app/task/task.go @@ -0,0 +1,253 @@ +package task + +import ( + "context" + "fmt" + "log" + "os" + "path" + "strconv" + "time" + + "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/global" + "github.com/1Panel-dev/1Panel/core/i18n" + "github.com/google/uuid" +) + +type ActionFunc func(*Task) error +type RollbackFunc func(*Task) + +type Task struct { + Name string + TaskID string + Logger *log.Logger + SubTasks []*SubTask + Rollbacks []RollbackFunc + logFile *os.File + taskRepo repo.ITaskRepo + Task *model.Task + ParentID string +} + +type SubTask struct { + RootTask *Task + Name string + StepAlias string + Retry int + Timeout time.Duration + Action ActionFunc + Rollback RollbackFunc + Error error + IgnoreErr bool +} + +const ( + TaskUpgrade = "TaskUpgrade" + TaskAddNode = "TaskAddNode" +) + +const ( + TaskScopeSystem = "System" +) + +const ( + TaskSuccess = "Success" + TaskFailed = "Failed" +) + +func GetTaskName(resourceName, operate, scope string) string { + return fmt.Sprintf("%s%s [%s]", i18n.GetMsgByKey(operate), i18n.GetMsgByKey(scope), resourceName) +} + +func NewTaskWithOps(resourceName, operate, scope, taskID string, resourceID uint) (*Task, error) { + return NewTask(GetTaskName(resourceName, operate, scope), operate, scope, taskID, resourceID) +} + +func NewTask(name, operate, taskScope, taskID string, resourceID uint) (*Task, error) { + if taskID == "" { + taskID = uuid.New().String() + } + logItem := path.Join(global.CONF.System.BaseDir, "1panel/log") + logDir := path.Join(logItem, taskScope) + if _, err := os.Stat(logDir); os.IsNotExist(err) { + if err = os.MkdirAll(logDir, 0755); err != nil { + return nil, fmt.Errorf("failed to create log directory: %w", err) + } + } + logPath := path.Join(logItem, taskScope, taskID+".log") + file, err := os.OpenFile(logPath, os.O_TRUNC|os.O_CREATE|os.O_WRONLY, 0666) + if err != nil { + return nil, fmt.Errorf("failed to open log file: %w", err) + } + logger := log.New(file, "", log.LstdFlags) + taskModel := &model.Task{ + ID: taskID, + Name: name, + Type: taskScope, + LogFile: logPath, + Status: constant.StatusRunning, + ResourceID: resourceID, + Operate: operate, + } + taskRepo := repo.NewITaskRepo() + task := &Task{Name: name, logFile: file, Logger: logger, taskRepo: taskRepo, Task: taskModel} + return task, nil +} + +func (t *Task) AddSubTask(name string, action ActionFunc, rollback RollbackFunc) { + subTask := &SubTask{RootTask: t, Name: name, Retry: 0, Timeout: 10 * time.Minute, Action: action, Rollback: rollback} + t.SubTasks = append(t.SubTasks, subTask) +} + +func (t *Task) AddSubTaskWithAlias(key string, action ActionFunc, rollback RollbackFunc) { + subTask := &SubTask{RootTask: t, Name: i18n.GetMsgByKey(key), StepAlias: key, Retry: 0, Timeout: 10 * time.Minute, Action: action, Rollback: rollback} + t.SubTasks = append(t.SubTasks, subTask) +} + +func (t *Task) AddSubTaskWithOps(name string, action ActionFunc, rollback RollbackFunc, retry int, timeout time.Duration) { + subTask := &SubTask{RootTask: t, Name: name, Retry: retry, Timeout: timeout, Action: action, Rollback: rollback} + t.SubTasks = append(t.SubTasks, subTask) +} + +func (t *Task) AddSubTaskWithIgnoreErr(name string, action ActionFunc) { + subTask := &SubTask{RootTask: t, Name: name, Retry: 0, Timeout: 10 * time.Minute, Action: action, Rollback: nil, IgnoreErr: true} + t.SubTasks = append(t.SubTasks, subTask) +} + +func (s *SubTask) Execute() error { + subTaskName := s.Name + if s.Name == "" { + subTaskName = i18n.GetMsgByKey("SubTask") + } + var err error + for i := 0; i < s.Retry+1; i++ { + if i > 0 { + s.RootTask.Log(i18n.GetWithName("TaskRetry", strconv.Itoa(i))) + } + ctx, cancel := context.WithTimeout(context.Background(), s.Timeout) + defer cancel() + + done := make(chan error) + go func() { + done <- s.Action(s.RootTask) + }() + + select { + case <-ctx.Done(): + s.RootTask.Log(i18n.GetWithName("TaskTimeout", subTaskName)) + case err = <-done: + if err != nil { + s.RootTask.Log(i18n.GetWithNameAndErr("SubTaskFailed", subTaskName, err)) + } else { + s.RootTask.Log(i18n.GetWithName("SubTaskSuccess", subTaskName)) + return nil + } + } + + if i == s.Retry { + if s.Rollback != nil { + s.Rollback(s.RootTask) + } + } + time.Sleep(1 * time.Second) + } + return err +} + +func (t *Task) updateTask(task *model.Task) { + _ = t.taskRepo.Update(context.Background(), task) +} + +func (t *Task) Execute() error { + if err := t.taskRepo.Save(context.Background(), t.Task); err != nil { + return err + } + var err error + t.Log(i18n.GetWithName("TaskStart", t.Name)) + for _, subTask := range t.SubTasks { + t.Task.CurrentStep = subTask.StepAlias + t.updateTask(t.Task) + if err = subTask.Execute(); err == nil { + if subTask.Rollback != nil { + t.Rollbacks = append(t.Rollbacks, subTask.Rollback) + } + } else { + if subTask.IgnoreErr { + err = nil + continue + } + t.Task.ErrorMsg = err.Error() + t.Task.Status = constant.StatusFailed + for _, rollback := range t.Rollbacks { + rollback(t) + } + t.updateTask(t.Task) + break + } + } + if t.Task.Status == constant.StatusRunning { + t.Task.Status = constant.StatusSuccess + t.Log(i18n.GetWithName("TaskSuccess", t.Name)) + } else { + t.Log(i18n.GetWithName("TaskFailed", t.Name)) + } + t.Log("[TASK-END]") + t.Task.EndAt = time.Now() + t.updateTask(t.Task) + _ = t.logFile.Close() + return err +} + +func (t *Task) DeleteLogFile() { + _ = os.Remove(t.Task.LogFile) +} + +func (t *Task) LogWithStatus(msg string, err error) { + if err != nil { + t.Logger.Printf(i18n.GetWithNameAndErr("FailedStatus", msg, err)) + } else { + t.Logger.Printf(i18n.GetWithName("SuccessStatus", msg)) + } +} + +func (t *Task) Log(msg string) { + t.Logger.Printf(msg) +} + +func (t *Task) Logf(format string, v ...any) { + t.Logger.Printf(format, v...) +} + +func (t *Task) LogFailed(msg string) { + t.Logger.Printf(msg + i18n.GetMsgByKey("Failed")) +} + +func (t *Task) LogFailedWithErr(msg string, err error) { + t.Logger.Printf(fmt.Sprintf("%s %s : %s", msg, i18n.GetMsgByKey("Failed"), err.Error())) +} + +func (t *Task) LogSuccess(msg string) { + t.Logger.Printf(msg + i18n.GetMsgByKey("Success")) +} +func (t *Task) LogSuccessF(format string, v ...any) { + t.Logger.Printf(fmt.Sprintf(format, v...) + i18n.GetMsgByKey("Success")) +} + +func (t *Task) LogStart(msg string) { + t.Logger.Printf(fmt.Sprintf("%s%s", i18n.GetMsgByKey("Start"), msg)) +} + +func (t *Task) LogWithOps(operate, msg string) { + t.Logger.Printf("%s%s", i18n.GetMsgByKey(operate), msg) +} + +func (t *Task) LogSuccessWithOps(operate, msg string) { + t.Logger.Printf("%s%s%s", i18n.GetMsgByKey(operate), msg, i18n.GetMsgByKey("Success")) +} + +func (t *Task) LogFailedWithOps(operate, msg string, err error) { + t.Logger.Printf("%s%s%s : %s ", i18n.GetMsgByKey(operate), msg, i18n.GetMsgByKey("Failed"), err.Error()) +} diff --git a/core/constant/status.go b/core/constant/status.go index 166c0483c..3df82969e 100644 --- a/core/constant/status.go +++ b/core/constant/status.go @@ -12,4 +12,6 @@ const ( StatusStarting = "starting" StatusHealthy = "healthy" StatusUnhealthy = "unhealthy" + StatusUpgrading = "upgrading" + StatusRunning = "running" ) diff --git a/core/global/global.go b/core/global/global.go index 36765352d..73560d5cd 100644 --- a/core/global/global.go +++ b/core/global/global.go @@ -15,6 +15,7 @@ import ( var ( DB *gorm.DB + TaskDB *gorm.DB LOG *logrus.Logger CONF configs.ServerConfig VALID *validator.Validate diff --git a/core/i18n/i18n.go b/core/i18n/i18n.go index db6867285..dfd7d20bb 100644 --- a/core/i18n/i18n.go +++ b/core/i18n/i18n.go @@ -32,25 +32,20 @@ func GetMsgWithMap(key string, maps map[string]interface{}) string { } } -func GetMsgWithName(key string, name string, err error) string { +func GetMsgWithDetail(key string, detail string) string { var ( content string dataMap = make(map[string]interface{}) ) - dataMap["name"] = name - if err != nil { - dataMap["err"] = err.Error() - } + dataMap["detail"] = detail content, _ = global.I18n.Localize(&i18n.LocalizeConfig{ MessageID: key, TemplateData: dataMap, }) - content = strings.ReplaceAll(content, "", "") - if content == "" { - return key - } else { + if content != "" { return content } + return key } func GetErrMsg(key string, maps map[string]interface{}) string { @@ -75,6 +70,41 @@ func GetMsgByKey(key string) string { return content } +func Get(key string) string { + content, _ := global.I18n.Localize(&i18n.LocalizeConfig{ + MessageID: key, + }) + if content != "" { + return content + } + return key +} + +func GetWithName(key string, name string) string { + var ( + dataMap = make(map[string]interface{}) + ) + dataMap["name"] = name + content, _ := global.I18n.Localize(&i18n.LocalizeConfig{ + MessageID: key, + TemplateData: dataMap, + }) + return content +} + +func GetWithNameAndErr(key string, name string, err error) string { + var ( + dataMap = make(map[string]interface{}) + ) + dataMap["name"] = name + dataMap["err"] = err.Error() + content, _ := global.I18n.Localize(&i18n.LocalizeConfig{ + MessageID: key, + TemplateData: dataMap, + }) + return content +} + //go:embed lang/* var fs embed.FS var bundle *i18n.Bundle diff --git a/core/i18n/lang/en.yaml b/core/i18n/lang/en.yaml index 7b7ed8d25..3aa83105a 100644 --- a/core/i18n/lang/en.yaml +++ b/core/i18n/lang/en.yaml @@ -29,3 +29,42 @@ ErrNoSuchHost: "Network connection failed" ErrBackupInUsed: "The backup account is currently in use in a scheduled task and cannot be deleted." ErrBackupCheck: "Backup account test connection failed {{.err}}" ErrBackupLocalDelete: "Deleting local server backup accounts is not currently supported." + +#task +TaskStart: "{{.name}} Task Start [START]" +TaskEnd: "{{.name}} Task End [COMPLETED]" +TaskFailed: "{{.name}} Task Failed" +TaskTimeout: "{{.name}} Timeout" +TaskSuccess: "{{.name}} Task Successful" +TaskRetry: "Starting the {{.name}} retry" +SubTaskSuccess: "{{ .name }} Successful" +SubTaskFailed: "{{ .name }} Failed: {{ .err }}" +TaskInstall: "Install" +TaskUninstall: "Uninstall" +TaskCreate: "Create" +TaskDelete: "Delete" +TaskUpgrade: "Upgrade" +TaskUpdate: "Update" +TaskRestart: "Restart" +TaskRollback: "Rollback" +SuccessStatus: "{{ .name }} Successful" +FailedStatus: "{{ .name }} Failed {{.err}}" +PullImage: "Pull Image" +Start: "Start" +Run: "Launch" +Stop: "Stop" +SubTask: "Subtask" + +#upgrade node +NodeUpgrade: "Upgrade Node {name}" +NewSSHClient: "Initialize SSH Connection" +BackupBeforeUpgrade: "Backup Data Before Upgrade" +UploadUpgradeFile: "Distribute Upgrade Required Files" +RestartAfterUpgrade: "Start Service After Upgrade" + +#add node +TaskAddNode: "Add Node" +GenerateSSLInfo: "Generate Node SSL Information" +MakeAgentPackage: "Generate Node Installation Package" +SendAgent: "Distribute Node Installation Package" +StartService: "Start Service" \ No newline at end of file diff --git a/core/i18n/lang/zh-Hant.yaml b/core/i18n/lang/zh-Hant.yaml index e1c529cd5..85e257fb1 100644 --- a/core/i18n/lang/zh-Hant.yaml +++ b/core/i18n/lang/zh-Hant.yaml @@ -28,4 +28,43 @@ ErrNoSuchHost: "網路連接失敗" #backup ErrBackupInUsed: "該備份帳號已在計劃任務中使用,無法刪除" ErrBackupCheck: "備份帳號測試連接失敗 {{.err}}" -ErrBackupLocalDelete: "暫不支持刪除本地伺服器備份帳號" \ No newline at end of file +ErrBackupLocalDelete: "暫不支持刪除本地伺服器備份帳號" + +#task +TaskStart: "{{.name}} 任務開始 [START]" +TaskEnd: "{{.name}} 任務結束 [COMPLETED]" +TaskFailed: "{{.name}} 任務失敗" +TaskTimeout: "{{.name}} 超時" +TaskSuccess: "{{.name}} 任務成功" +TaskRetry: "開始第 {{.name}} 次重試" +SubTaskSuccess: "{{ .name }} 成功" +SubTaskFailed: "{{ .name }} 失敗: {{ .err }}" +TaskInstall: "安裝" +TaskUninstall: "卸載" +TaskCreate: "創建" +TaskDelete: "刪除" +TaskUpgrade: "升級" +TaskUpdate: "更新" +TaskRestart: "重啟" +TaskRollback: "回滾" +SuccessStatus: "{{ .name }} 成功" +FailedStatus: "{{ .name }} 失敗 {{.err}}" +PullImage: "拉取鏡像" +Start: "開始" +Run: "啟動" +Stop: "停止" +SubTask: "子任務" + +#node upgrade +NodeUpgrade: "升級節點 {name}" +NewSSHClient: "初始化 SSH 連接" +BackupBeforeUpgrade: "升級前備份數據" +UploadUpgradeFile: "下發升級所需文件" +RestartAfterUpgrade: "升級後啟動服務" + +#node create +TaskAddNode: "添加節點" +GenerateSSLInfo: "生成節點 SSL 信息" +MakeAgentPackage: "生成節點安裝包" +SendAgent: "下發節點安裝包" +StartService: "啟動服務" \ No newline at end of file diff --git a/core/i18n/lang/zh.yaml b/core/i18n/lang/zh.yaml index 273ea35f0..fc66b1b28 100644 --- a/core/i18n/lang/zh.yaml +++ b/core/i18n/lang/zh.yaml @@ -30,4 +30,44 @@ ErrNoSuchHost: "网络连接失败" #backup ErrBackupInUsed: "该备份账号已在计划任务中使用,无法删除" ErrBackupCheck: "备份账号测试连接失败 {{ .err}}" -ErrBackupLocalDelete: "暂不支持删除本地服务器备份账号" \ No newline at end of file +ErrBackupLocalDelete: "暂不支持删除本地服务器备份账号" + +#task +#task +TaskStart: "{{.name}} 任务开始 [START]" +TaskEnd: "{{.name}} 任务结束 [COMPLETED]" +TaskFailed: "{{.name}} 任务失败" +TaskTimeout: "{{.name}} 超时" +TaskSuccess: "{{.name}} 任务成功" +TaskRetry: "开始第 {{.name}} 次重试" +SubTaskSuccess: "{{ .name }} 成功" +SubTaskFailed: "{{ .name }} 失败: {{ .err }}" +TaskInstall: "安装" +TaskUninstall: "卸载" +TaskCreate: "创建" +TaskDelete: "删除" +TaskUpgrade: "升级" +TaskUpdate: "更新" +TaskRestart: "重启" +TaskRollback: "回滚" +SuccessStatus: "{{ .name }} 成功" +FailedStatus: "{{ .name }} 失败 {{.err}}" +PullImage: "拉取镜像" +Start: "开始" +Run: "启动" +Stop: "停止" +SubTask: "子任务" + +#upgrade node +NodeUpgrade: "升级节点 {name}" +NewSSHClient: "初始化 SSH 连接" +BackupBeforeUpgrade: "升级前备份数据" +UploadUpgradeFile: "下发升级所需文件" +RestartAfterUpgrade: "升级后启动服务" + +#add node +TaskAddNode: "添加节点" +GenerateSSLInfo: "生成节点 SSL 信息" +MakeAgentPackage: "生成节点安装包" +SendAgent: "下发节点安装包" +StartService: "启动服务" \ No newline at end of file diff --git a/core/init/db/db.go b/core/init/db/db.go index f69d2ad83..2432fa18a 100644 --- a/core/init/db/db.go +++ b/core/init/db/db.go @@ -14,6 +14,11 @@ import ( ) func Init() { + initDB() + initTaskDB() +} + +func initDB() { dbPath := path.Join(global.CONF.System.BaseDir, "1panel/db") if _, err := os.Stat(dbPath); err != nil { if err := os.MkdirAll(dbPath, os.ModePerm); err != nil { @@ -29,7 +34,50 @@ func Init() { _ = f.Close() } - newLogger := logger.New( + db, err := NewDBWithPath(fullPath) + if err != nil { + panic(err) + } + + global.DB = db + global.LOG.Info("init db successfully") +} + +func initTaskDB() { + fullPath := path.Join(global.CONF.System.BaseDir, "1panel/db/task.db") + if _, err := os.Stat(fullPath); err != nil { + f, err := os.Create(fullPath) + if err != nil { + panic(fmt.Errorf("init task db file failed, err: %v", err)) + } + _ = f.Close() + } + + db, err := NewDBWithPath(fullPath) + if err != nil { + panic(err) + } + + global.TaskDB = db + global.LOG.Info("init task db successfully") +} + +func NewDBWithPath(dbPath string) (*gorm.DB, error) { + db, _ := gorm.Open(sqlite.Open(dbPath), &gorm.Config{ + DisableForeignKeyConstraintWhenMigrating: true, + Logger: getLogger(), + }) + sqlDB, dbError := db.DB() + if dbError != nil { + return nil, dbError + } + sqlDB.SetConnMaxIdleTime(10) + sqlDB.SetMaxOpenConns(100) + sqlDB.SetConnMaxLifetime(time.Hour) + return db, nil +} +func getLogger() logger.Interface { + return logger.New( log.New(os.Stdout, "\r\n", log.LstdFlags), logger.Config{ SlowThreshold: time.Second, @@ -38,22 +86,4 @@ func Init() { Colorful: false, }, ) - - db, err := gorm.Open(sqlite.Open(fullPath), &gorm.Config{ - DisableForeignKeyConstraintWhenMigrating: true, - Logger: newLogger, - }) - if err != nil { - panic(err) - } - sqlDB, dbError := db.DB() - if dbError != nil { - panic(dbError) - } - sqlDB.SetConnMaxIdleTime(10) - sqlDB.SetMaxOpenConns(100) - sqlDB.SetConnMaxLifetime(time.Hour) - - global.DB = db - global.LOG.Info("init db successfully") } diff --git a/core/init/hook/hook.go b/core/init/hook/hook.go index 6f3db20b2..a360c7830 100644 --- a/core/init/hook/hook.go +++ b/core/init/hook/hook.go @@ -40,6 +40,11 @@ func Init() { global.LOG.Errorf("load service ssl from setting failed, err: %v", err) } global.CONF.System.SSL = sslSetting.Value + versionSetting, err := settingRepo.Get(commonRepo.WithByKey("SystemVersion")) + if err != nil { + global.LOG.Errorf("load version from setting failed, err: %v", err) + } + global.CONF.System.Version = versionSetting.Value if _, err := settingRepo.Get(commonRepo.WithByKey("SystemStatus")); err != nil { _ = settingRepo.Create("SystemStatus", "Free") diff --git a/core/init/migration/migrate.go b/core/init/migration/migrate.go index 060fbd27b..162961815 100644 --- a/core/init/migration/migrate.go +++ b/core/init/migration/migrate.go @@ -18,6 +18,7 @@ func Init() { migrations.InitAppLauncher, migrations.InitBackup, migrations.InitGoogle, + migrations.AddTaskDB, }) 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 8a8288f02..59ad67c62 100644 --- a/core/init/migration/migrations/init.go +++ b/core/init/migration/migrations/init.go @@ -15,7 +15,7 @@ import ( ) var AddTable = &gormigrate.Migration{ - ID: "20240819-add-table", + ID: "20241224-add-table", Migrate: func(tx *gorm.DB) error { return tx.AutoMigrate( &model.OperationLog{}, @@ -25,6 +25,7 @@ var AddTable = &gormigrate.Migration{ &model.Group{}, &model.Host{}, &model.Command{}, + &model.UpgradeLog{}, ) }, } @@ -260,3 +261,12 @@ var InitGoogle = &gormigrate.Migration{ return nil }, } + +var AddTaskDB = &gormigrate.Migration{ + ID: "20241125-add-task-table", + Migrate: func(tx *gorm.DB) error { + return global.TaskDB.AutoMigrate( + &model.Task{}, + ) + }, +} diff --git a/core/utils/files/files.go b/core/utils/files/files.go index 6a5c22f42..4ed117c97 100644 --- a/core/utils/files/files.go +++ b/core/utils/files/files.go @@ -47,6 +47,30 @@ func CopyFile(src, dst string, withName bool) error { return nil } +func CopyItem(isDir, withName bool, src, dst string) error { + if path.Base(src) != path.Base(dst) && !withName { + dst = path.Join(dst, path.Base(src)) + } + srcInfo, err := os.Stat(path.Dir(src)) + if err != nil { + return err + } + if _, err := os.Stat(path.Dir(dst)); err != nil { + if os.IsNotExist(err) { + _ = os.MkdirAll(path.Dir(dst), srcInfo.Mode()) + } + } + cmdStr := fmt.Sprintf(`cp -rf %s %s`, src, dst+"/") + if !isDir { + cmdStr = fmt.Sprintf(`cp -f %s %s`, src, dst+"/") + } + stdout, err := cmd.Exec(cmdStr) + if err != nil { + return fmt.Errorf("handle %s failed, stdout: %s, err: %v", cmdStr, stdout, err) + } + return nil +} + func HandleTar(sourceDir, targetDir, name, exclusionRules string, secret string) error { if _, err := os.Stat(targetDir); err != nil && os.IsNotExist(err) { if err = os.MkdirAll(targetDir, os.ModePerm); err != nil { diff --git a/core/utils/ssh/ssh.go b/core/utils/ssh/ssh.go index 6f6f12c27..1528f4379 100644 --- a/core/utils/ssh/ssh.go +++ b/core/utils/ssh/ssh.go @@ -71,6 +71,17 @@ func (c *SSHClient) Run(shell string) (string, error) { return string(buf), err } +func (c *SSHClient) Runf(shell string, args ...interface{}) (string, error) { + session, err := c.Client.NewSession() + if err != nil { + return "", err + } + defer session.Close() + buf, err := session.CombinedOutput(fmt.Sprintf(shell, args...)) + + return string(buf), err +} + func (c *SSHClient) Close() { _ = c.Client.Close() } diff --git a/frontend/src/api/modules/setting.ts b/frontend/src/api/modules/setting.ts index d59e17ba6..e8c7f1732 100644 --- a/frontend/src/api/modules/setting.ts +++ b/frontend/src/api/modules/setting.ts @@ -130,11 +130,11 @@ export const loadSnapshotSize = (param: SearchWithPage) => { // upgrade export const loadUpgradeInfo = () => { - return http.get(`/settings/upgrade`); + return http.get(`/core/settings/upgrade`); }; export const loadReleaseNotes = (version: string) => { - return http.post(`/settings/upgrade/notes`, { version: version }); + return http.post(`/core/settings/upgrade/notes`, { version: version }); }; export const upgrade = (version: string) => { - return http.post(`/settings/upgrade`, { version: version }); + return http.post(`/core/settings/upgrade`, { version: version }); };