fix: Cancel synchronization between recommended nodes on dashboard (#8270)

This commit is contained in:
ssongliu 2025-03-28 16:57:40 +08:00 committed by GitHub
parent 076f3f0e0a
commit 5ee392b11e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 102 additions and 293 deletions

View file

@ -61,16 +61,25 @@ func (b *BaseApi) LoadAppLauncherOption(c *gin.Context) {
helper.SuccessWithData(c, data)
}
func (b *BaseApi) SyncAppLauncher(c *gin.Context) {
var req dto.AppLauncherSync
// @Tags Dashboard
// @Summary Update app Launcher
// @Accept json
// @Param request body dto.SettingUpdate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /dashboard/app/launcher/show [post]
// @x-panel-log {"bodyKeys":["key", "value"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"首页应用 [key] => 显示:[value]","formatEN":"app launcher [key] => show: [value]"}
func (b *BaseApi) UpdateAppLauncher(c *gin.Context) {
var req dto.SettingUpdate
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := dashboardService.Sync(req); err != nil {
helper.BadRequest(c, err)
if err := dashboardService.ChangeShow(req); err != nil {
helper.InternalServer(c, err)
return
}
helper.SuccessWithOutData(c)
}

View file

@ -9,12 +9,10 @@ type LauncherRepo struct{}
type ILauncherRepo interface {
Get(opts ...DBOption) (model.AppLauncher, error)
List(opts ...DBOption) ([]model.AppLauncher, error)
ListName(opts ...DBOption) ([]string, error)
Create(launcher *model.AppLauncher) error
Save(launcher *model.AppLauncher) error
Delete(opts ...DBOption) error
SyncAll(data []model.AppLauncher) error
}
func NewILauncherRepo() ILauncherRepo {
@ -30,14 +28,18 @@ func (u *LauncherRepo) Get(opts ...DBOption) (model.AppLauncher, error) {
err := db.First(&launcher).Error
return launcher, err
}
func (u *LauncherRepo) List(opts ...DBOption) ([]model.AppLauncher, error) {
func (u *LauncherRepo) ListName(opts ...DBOption) ([]string, error) {
var ops []model.AppLauncher
db := global.DB.Model(&model.AppLauncher{})
for _, opt := range opts {
db = opt(db)
}
err := db.Find(&ops).Error
return ops, err
_ = db.Find(&ops).Error
var names []string
for i := 0; i < len(ops); i++ {
names = append(names, ops[i].Key)
}
return names, nil
}
func (u *LauncherRepo) Create(launcher *model.AppLauncher) error {
@ -55,21 +57,3 @@ func (u *LauncherRepo) Delete(opts ...DBOption) error {
}
return db.Delete(&model.AppLauncher{}).Error
}
func (u *LauncherRepo) SyncAll(data []model.AppLauncher) error {
tx := global.DB.Begin()
if err := tx.Where("1 = 1").Delete(&model.AppLauncher{}).Error; err != nil {
tx.Rollback()
return err
}
if len(data) == 0 {
tx.Commit()
return nil
}
if err := tx.Model(model.AppLauncher{}).Save(&data).Error; err != nil {
tx.Rollback()
return err
}
tx.Commit()
return nil
}

View file

@ -42,6 +42,12 @@ func WithByName(name string) DBOption {
}
}
func WithByKey(key string) DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("key = ?", key)
}
}
func WithByLowerName(name string) DBOption {
return func(g *gorm.DB) *gorm.DB {
return g.Where("LOWER(name) = LOWER(?)", name)

View file

@ -13,6 +13,7 @@ import (
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/app/model"
"github.com/1Panel-dev/1Panel/agent/app/repo"
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/global"
"github.com/1Panel-dev/1Panel/agent/utils/ai_tools/gpu"
@ -31,14 +32,13 @@ import (
type DashboardService struct{}
type IDashboardService interface {
Sync(req dto.AppLauncherSync) error
LoadOsInfo() (*dto.OsInfo, error)
LoadBaseInfo(ioOption string, netOption string) (*dto.DashboardBase, error)
LoadCurrentInfoForNode() *dto.NodeCurrent
LoadCurrentInfo(ioOption string, netOption string) *dto.DashboardCurrent
LoadAppLauncher() ([]dto.AppLauncher, error)
ChangeShow(req dto.SettingUpdate) error
ListLauncherOption(filter string) ([]dto.LauncherOption, error)
Restart(operation string) error
}
@ -47,14 +47,6 @@ func NewIDashboardService() IDashboardService {
return &DashboardService{}
}
func (u *DashboardService) Sync(req dto.AppLauncherSync) error {
var launchers []model.AppLauncher
for _, item := range req.Keys {
launchers = append(launchers, model.AppLauncher{Key: item})
}
return launcherRepo.SyncAll(launchers)
}
func (u *DashboardService) Restart(operation string) error {
if operation != "1panel" && operation != "system" && operation != "1panel-agent" {
return fmt.Errorf("handle restart operation %s failed, err: nonsupport such operation", operation)
@ -285,7 +277,7 @@ func (u *DashboardService) LoadAppLauncher() ([]dto.AppLauncher, error) {
return data, err
}
showList := loadShowList()
showList, _ := launcherRepo.ListName()
defaultList := []string{"openresty", "mysql", "halo", "redis", "maxkb", "wordpress"}
allList := common.RemoveRepeatStr(append(defaultList, showList...))
for _, showItem := range allList {
@ -343,8 +335,23 @@ func (u *DashboardService) LoadAppLauncher() ([]dto.AppLauncher, error) {
return data, nil
}
func (u *DashboardService) ChangeShow(req dto.SettingUpdate) error {
launcher, _ := launcherRepo.Get(repo.WithByKey(req.Key))
if req.Value == constant.StatusEnable && launcher.ID == 0 {
if err := launcherRepo.Create(&model.AppLauncher{Key: req.Key}); err != nil {
return err
}
}
if req.Value == constant.StatusDisable && launcher.ID != 0 {
if err := launcherRepo.Delete(repo.WithByKey(req.Key)); err != nil {
return err
}
}
return nil
}
func (u *DashboardService) ListLauncherOption(filter string) ([]dto.LauncherOption, error) {
showList := loadShowList()
showList, _ := launcherRepo.ListName()
var data []dto.LauncherOption
optionMap := make(map[string]bool)
appInstalls, err := appInstallRepo.ListBy(context.Background())
@ -500,25 +507,6 @@ func loadGPUInfo() []dto.GPUInfo {
return data
}
func loadShowList() []string {
var data []string
if global.IsMaster {
var list []AppLauncher
if err := global.CoreDB.Model(AppLauncher{}).Where("1 == 1").Find(&list).Error; err != nil {
return []string{}
}
for _, item := range list {
data = append(data, item.Key)
}
return data
}
list, _ := launcherRepo.List()
for _, item := range list {
data = append(data, item.Key)
}
return data
}
type AppLauncher struct {
Key string `json:"key"`
}

View file

@ -60,7 +60,7 @@ func (u *DeviceService) Scan() dto.CleanData {
upgradePath := path.Join(global.Dir.BaseDir, upgradePath)
upgradeSize, _ := fileOp.GetDirSize(upgradePath)
treeData = append(treeData, dto.CleanTree{
upgradeTree := dto.CleanTree{
ID: uuid.NewString(),
Label: "upgrade",
Size: uint64(upgradeSize),
@ -68,7 +68,15 @@ func (u *DeviceService) Scan() dto.CleanData {
IsRecommend: true,
Type: "upgrade",
Children: loadTreeWithDir(true, "upgrade", upgradePath, fileOp),
})
}
if len(upgradeTree.Children) != 0 {
sort.Slice(upgradeTree.Children, func(i, j int) bool {
return common.CompareVersion(upgradeTree.Children[i].Label, upgradeTree.Children[j].Label)
})
upgradeTree.Children[0].IsCheck = false
upgradeTree.Children[0].IsRecommend = false
}
treeData = append(treeData, upgradeTree)
snapTree := loadSnapshotTree(fileOp)
snapSize := uint64(0)
@ -563,11 +571,6 @@ func loadTreeWithDir(isCheck bool, treeType, pathItem string, fileOp fileUtils.F
if err != nil {
return lists
}
sort.Slice(files, func(i, j int) bool {
infoI, _ := files[i].Info()
infoJ, _ := files[i].Info()
return infoI.ModTime().Before(infoJ.ModTime())
})
for _, file := range files {
if treeType == "old_upgrade" {
continue
@ -589,10 +592,6 @@ func loadTreeWithDir(isCheck bool, treeType, pathItem string, fileOp fileUtils.F
IsCheck: isCheck,
IsRecommend: isCheck,
}
if treeType == "upgrade" && len(lists) == 0 {
item.IsCheck = false
item.IsRecommend = false
}
lists = append(lists, item)
}
}
@ -742,3 +741,31 @@ func scanFile(pathItem string, size *int64, count *int) {
}
}
}
func loadRestorePath(upgradeDir string) (string, error) {
if _, err := os.Stat(upgradeDir); err != nil && os.IsNotExist(err) {
return "no such file", nil
}
files, err := os.ReadDir(upgradeDir)
if err != nil {
return "", err
}
type itemState struct {
Name string
CreateAt time.Time
}
var folders []itemState
for _, file := range files {
if file.IsDir() {
info, _ := file.Info()
folders = append(folders, itemState{Name: file.Name(), CreateAt: info.ModTime()})
}
}
if len(folders) == 0 {
return "no such file", nil
}
sort.Slice(folders, func(i, j int) bool {
return folders[i].CreateAt.After(folders[j].CreateAt)
})
return folders[0].Name, nil
}

View file

@ -13,7 +13,7 @@ func (s *DashboardRouter) InitRouter(Router *gin.RouterGroup) {
{
cmdRouter.GET("/base/os", baseApi.LoadDashboardOsInfo)
cmdRouter.GET("/app/launcher", baseApi.LoadAppLauncher)
cmdRouter.POST("/app/launcher/sync", baseApi.SyncAppLauncher)
cmdRouter.POST("/app/launcher/show", baseApi.UpdateAppLauncher)
cmdRouter.POST("/app/launcher/option", baseApi.LoadAppLauncherOption)
cmdRouter.GET("/base/:ioOption/:netOption", baseApi.LoadDashboardBaseInfo)
cmdRouter.GET("/current/node", baseApi.LoadCurrentInfoForNode)

View file

@ -1,38 +0,0 @@
package v2
import (
"github.com/1Panel-dev/1Panel/core/app/api/v2/helper"
"github.com/1Panel-dev/1Panel/core/app/dto"
"github.com/gin-gonic/gin"
)
func (b *BaseApi) SearchAppLauncher(c *gin.Context) {
data, err := appLauncherService.Search()
if err != nil {
helper.InternalServer(c, err)
return
}
helper.SuccessWithData(c, data)
}
// @Tags App Launcher
// @Summary Update app Launcher
// @Accept json
// @Param request body dto.SettingUpdate true "request"
// @Success 200
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /core/app/launcher/show [post]
// @x-panel-log {"bodyKeys":["key", "value"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"首页应用 [key] => 显示:[value]","formatEN":"app launcher [key] => show: [value]"}
func (b *BaseApi) UpdateAppLauncher(c *gin.Context) {
var req dto.SettingUpdate
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := appLauncherService.ChangeShow(req); err != nil {
helper.InternalServer(c, err)
return
}
helper.SuccessWithOutData(c)
}

View file

@ -9,14 +9,13 @@ type ApiGroup struct {
var ApiGroupApp = new(ApiGroup)
var (
hostService = service.NewIHostService()
authService = service.NewIAuthService()
backupService = service.NewIBackupService()
settingService = service.NewISettingService()
logService = service.NewILogService()
upgradeService = service.NewIUpgradeService()
groupService = service.NewIGroupService()
commandService = service.NewICommandService()
appLauncherService = service.NewIAppLauncher()
scriptService = service.NewIScriptService()
hostService = service.NewIHostService()
authService = service.NewIAuthService()
backupService = service.NewIBackupService()
settingService = service.NewISettingService()
logService = service.NewILogService()
upgradeService = service.NewIUpgradeService()
groupService = service.NewIGroupService()
commandService = service.NewICommandService()
scriptService = service.NewIScriptService()
)

View file

@ -1,6 +0,0 @@
package model
type AppLauncher struct {
BaseModel
Key string `json:"key"`
}

View file

@ -1,50 +0,0 @@
package repo
import (
"github.com/1Panel-dev/1Panel/core/app/model"
"github.com/1Panel-dev/1Panel/core/global"
)
type LauncherRepo struct{}
type ILauncherRepo interface {
Get(opts ...global.DBOption) (model.AppLauncher, error)
List(opts ...global.DBOption) ([]model.AppLauncher, error)
Create(launcher *model.AppLauncher) error
Delete(opts ...global.DBOption) error
}
func NewILauncherRepo() ILauncherRepo {
return &LauncherRepo{}
}
func (u *LauncherRepo) Get(opts ...global.DBOption) (model.AppLauncher, error) {
var launcher model.AppLauncher
db := global.DB
for _, opt := range opts {
db = opt(db)
}
err := db.First(&launcher).Error
return launcher, err
}
func (u *LauncherRepo) List(opts ...global.DBOption) ([]model.AppLauncher, error) {
var ops []model.AppLauncher
db := global.DB.Model(&model.AppLauncher{})
for _, opt := range opts {
db = opt(db)
}
err := db.Find(&ops).Error
return ops, err
}
func (u *LauncherRepo) Create(launcher *model.AppLauncher) error {
return global.DB.Create(launcher).Error
}
func (u *LauncherRepo) Delete(opts ...global.DBOption) error {
db := global.DB
for _, opt := range opts {
db = opt(db)
}
return db.Delete(&model.AppLauncher{}).Error
}

View file

@ -1,72 +0,0 @@
package service
import (
"bytes"
"encoding/json"
"net/http"
"github.com/1Panel-dev/1Panel/core/app/dto"
"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/proxy_local"
"github.com/1Panel-dev/1Panel/core/utils/xpack"
)
type LauncherService struct{}
type IAppLauncher interface {
Search() ([]string, error)
ChangeShow(req dto.SettingUpdate) error
}
func NewIAppLauncher() IAppLauncher {
return &LauncherService{}
}
func (u *LauncherService) Search() ([]string, error) {
launchers, err := launcherRepo.List(repo.WithOrderBy("created_at"))
if err != nil {
return nil, err
}
var data []string
for _, launcher := range launchers {
data = append(data, launcher.Key)
}
return data, nil
}
func (u *LauncherService) ChangeShow(req dto.SettingUpdate) error {
launcher, _ := launcherRepo.Get(repo.WithByKey(req.Key))
if req.Value == constant.StatusEnable && launcher.ID == 0 {
if err := launcherRepo.Create(&model.AppLauncher{Key: req.Key}); err != nil {
return err
}
}
if req.Value == constant.StatusDisable && launcher.ID != 0 {
if err := launcherRepo.Delete(repo.WithByKey(req.Key)); err != nil {
return err
}
}
go syncLauncherToAgent()
return nil
}
func syncLauncherToAgent() {
launchers, _ := launcherRepo.List()
var list []string
launcherMap := make(map[string]struct{})
for _, item := range launchers {
if _, ok := launcherMap[item.Key]; ok {
continue
}
launcherMap[item.Key] = struct{}{}
list = append(list, item.Key)
}
launcherData := struct {
Keys []string
}{Keys: list}
itemData, _ := json.Marshal(launcherData)
_, _ = proxy_local.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

@ -9,7 +9,6 @@ var (
backupRepo = repo.NewIBackupRepo()
logRepo = repo.NewILogRepo()
groupRepo = repo.NewIGroupRepo()
launcherRepo = repo.NewILauncherRepo()
upgradeLogRepo = repo.NewIUpgradeLogRepo()
taskRepo = repo.NewITaskRepo()

View file

@ -6,11 +6,11 @@ import (
"path"
"sort"
"strings"
"time"
"github.com/1Panel-dev/1Panel/core/global"
"github.com/1Panel-dev/1Panel/core/i18n"
cmdUtils "github.com/1Panel-dev/1Panel/core/utils/cmd"
"github.com/1Panel-dev/1Panel/core/utils/common"
"github.com/1Panel-dev/1Panel/core/utils/files"
"github.com/spf13/cobra"
@ -93,22 +93,17 @@ func loadRestorePath(upgradeDir string) (string, error) {
if err != nil {
return "", err
}
type itemState struct {
Name string
CreateAt time.Time
}
var folders []itemState
var folders []string
for _, file := range files {
if file.IsDir() {
info, _ := file.Info()
folders = append(folders, itemState{Name: file.Name(), CreateAt: info.ModTime()})
folders = append(folders, file.Name())
}
}
if len(folders) == 0 {
return "no such file", nil
}
sort.Slice(folders, func(i, j int) bool {
return folders[i].CreateAt.After(folders[j].CreateAt)
return common.ComparePanelVersion(folders[i], folders[j])
})
return folders[0].Name, nil
return folders[0], nil
}

View file

@ -14,7 +14,6 @@ func Init() {
migrations.InitOneDrive,
migrations.InitHost,
migrations.InitTerminalSetting,
migrations.InitAppLauncher,
migrations.InitBackup,
migrations.InitGoogle,
migrations.AddTaskDB,

View file

@ -240,13 +240,6 @@ var InitTerminalSetting = &gormigrate.Migration{
},
}
var InitAppLauncher = &gormigrate.Migration{
ID: "20241029-init-app-launcher",
Migrate: func(tx *gorm.DB) error {
return tx.AutoMigrate(&model.AppLauncher{})
},
}
var InitBackup = &gormigrate.Migration{
ID: "20241107-init-backup",
Migrate: func(tx *gorm.DB) error {
@ -330,7 +323,7 @@ var AddSystemIP = &gormigrate.Migration{
}
var InitScriptLibrary = &gormigrate.Migration{
ID: "20250318-init-script-library",
ID: "20250328-init-script-library",
Migrate: func(tx *gorm.DB) error {
if err := tx.AutoMigrate(&model.ScriptLibrary{}); err != nil {
return err

View file

@ -1,20 +0,0 @@
package router
import (
v2 "github.com/1Panel-dev/1Panel/core/app/api/v2"
"github.com/1Panel-dev/1Panel/core/middleware"
"github.com/gin-gonic/gin"
)
type AppLauncherRouter struct{}
func (s *AppLauncherRouter) InitRouter(Router *gin.RouterGroup) {
launcherRouter := Router.Group("launcher").
Use(middleware.SessionAuth()).
Use(middleware.PasswordExpired())
baseApi := v2.ApiGroupApp.BaseApi
{
launcherRouter.GET("/search", baseApi.SearchAppLauncher)
launcherRouter.POST("/change/show", baseApi.UpdateAppLauncher)
}
}

View file

@ -9,7 +9,6 @@ func commonGroups() []CommonRouter {
&CommandRouter{},
&HostRouter{},
&GroupRouter{},
&AppLauncherRouter{},
&ScriptRouter{},
}
}

View file

@ -12,10 +12,7 @@ export const loadAppLauncherOption = (filter: string) => {
return http.post<Array<Dashboard.AppLauncherOption>>(`/dashboard/app/launcher/option`, { filter: filter });
};
export const changeLauncherStatus = (key: string, val: string) => {
return http.post(`/core/launcher/change/show`, { key: key, value: val });
};
export const resetLauncherStatus = () => {
return http.post(`/core/launcher/reset`);
return http.post(`/dashboard/app/launcher/show`, { key: key, value: val });
};
export const loadBaseInfo = (ioOption: string, netOption: string) => {