diff --git a/agent/app/api/v2/dashboard.go b/agent/app/api/v2/dashboard.go index 32cc07801..ec72ee77a 100644 --- a/agent/app/api/v2/dashboard.go +++ b/agent/app/api/v2/dashboard.go @@ -4,6 +4,8 @@ import ( "errors" "github.com/1Panel-dev/1Panel/agent/app/api/v2/helper" + "github.com/1Panel-dev/1Panel/agent/app/dto" + "github.com/1Panel-dev/1Panel/agent/constant" "github.com/gin-gonic/gin" ) @@ -23,6 +25,43 @@ func (b *BaseApi) LoadDashboardOsInfo(c *gin.Context) { helper.SuccessWithData(c, data) } +// @Tags Dashboard +// @Summary Load app launcher +// @Description 获取应用展示列表 +// @Accept json +// @Success 200 {Array} dto.dto.AppLauncher +// @Security ApiKeyAuth +// @Router /dashboard/app/launcher [get] +func (b *BaseApi) LoadAppLauncher(c *gin.Context) { + data, err := dashboardService.LoadAppLauncher() + if err != nil { + helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) + return + } + helper.SuccessWithData(c, data) +} + +// @Tags Dashboard +// @Summary Load app launcher options +// @Description 获取应用展示选项 +// @Accept json +// @Param request body dto.SearchByFilter true "request" +// @Success 200 {Array} dto.LauncherOption +// @Security ApiKeyAuth +// @Router /dashboard/app/launcher/option [post] +func (b *BaseApi) LoadAppLauncherOption(c *gin.Context) { + var req dto.SearchByFilter + if err := helper.CheckBind(&req, c); err != nil { + return + } + data, err := dashboardService.ListLauncherOption(req.Filter) + if err != nil { + helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) + return + } + helper.SuccessWithData(c, data) +} + // @Tags Dashboard // @Summary Load dashboard base info // @Description 获取首页基础数据 diff --git a/agent/app/dto/common_req.go b/agent/app/dto/common_req.go index 47cd4e6ea..11e9a2ff8 100644 --- a/agent/app/dto/common_req.go +++ b/agent/app/dto/common_req.go @@ -27,6 +27,10 @@ type Operate struct { Operation string `json:"operation" validate:"required"` } +type SearchByFilter struct { + Filter string `json:"filter"` +} + type BatchDeleteReq struct { Ids []uint `json:"ids" validate:"required"` } diff --git a/agent/app/dto/dashboard.go b/agent/app/dto/dashboard.go index d00b2965f..6fd1c36c1 100644 --- a/agent/app/dto/dashboard.go +++ b/agent/app/dto/dashboard.go @@ -126,3 +126,33 @@ type GPUInfo struct { MemTotal string `json:"memTotal"` FanSpeed string `json:"fanSpeed"` } + +type AppLauncher struct { + Key string `json:"key"` + Type string `json:"type"` + Name string `json:"name"` + Icon string `json:"icon"` + Limit int `json:"limit"` + ShortDescZh string `json:"shortDescZh"` + ShortDescEn string `json:"shortDescEn"` + Recommend int `json:"recomend"` + + IsInstall bool `json:"isInstall"` + IsRecommend bool `json:"isRecommend"` + Detail []InstallDetail `json:"detail"` +} +type InstallDetail struct { + InstallID uint `json:"installID"` + DetailID uint `json:"detailID"` + Name string `json:"name"` + Version string `json:"version"` + Path string `json:"path"` + Status string `json:"status"` + WebUI string `json:"webUI"` + HttpPort int `json:"httpPort"` + HttpsPort int `json:"httpsPort"` +} +type LauncherOption struct { + Key string `json:"key"` + IsShow bool `json:"isShow"` +} diff --git a/agent/app/service/dashboard.go b/agent/app/service/dashboard.go index 35199d3d8..ae302cd50 100644 --- a/agent/app/service/dashboard.go +++ b/agent/app/service/dashboard.go @@ -3,6 +3,7 @@ package service import ( "encoding/json" "fmt" + "net/http" "sort" "strings" "sync" @@ -12,6 +13,7 @@ import ( "github.com/1Panel-dev/1Panel/agent/constant" "github.com/1Panel-dev/1Panel/agent/global" "github.com/1Panel-dev/1Panel/agent/utils/cmd" + "github.com/1Panel-dev/1Panel/agent/utils/common" "github.com/1Panel-dev/1Panel/agent/utils/copier" "github.com/1Panel-dev/1Panel/agent/utils/xpack" "github.com/shirou/gopsutil/v3/cpu" @@ -30,6 +32,8 @@ type IDashboardService interface { LoadCurrentInfoForNode() *dto.NodeCurrent LoadCurrentInfo(ioOption string, netOption string) *dto.DashboardCurrent + LoadAppLauncher() ([]dto.AppLauncher, error) + ListLauncherOption(filter string) ([]dto.LauncherOption, error) Restart(operation string) error } @@ -240,6 +244,109 @@ func (u *DashboardService) LoadCurrentInfo(ioOption string, netOption string) *d return ¤tInfo } +func (u *DashboardService) LoadAppLauncher() ([]dto.AppLauncher, error) { + var ( + data []dto.AppLauncher + recommendList []dto.AppLauncher + ) + appInstalls, err := appInstallRepo.ListBy() + if err != nil { + return data, err + } + apps, err := appRepo.GetBy() + if err != nil { + return data, err + } + + showList := loadShowList() + defaultList := []string{"openresty", "mysql", "halo", "redis", "maxkb", "wordpress"} + allList := common.RemoveRepeatStr(append(defaultList, showList...)) + for _, showItem := range allList { + var itemData dto.AppLauncher + for _, app := range apps { + if showItem == app.Key { + itemData.Key = app.Key + itemData.Type = app.Type + itemData.Name = app.Name + itemData.Icon = app.Icon + itemData.Limit = app.Limit + itemData.ShortDescEn = app.ShortDescEn + itemData.ShortDescZh = app.ShortDescZh + itemData.Recommend = app.Recommend + break + } + } + if len(itemData.Icon) == 0 { + continue + } + for _, install := range appInstalls { + if install.App.Key == showItem { + itemData.IsInstall = true + itemData.Detail = append(itemData.Detail, dto.InstallDetail{ + InstallID: install.ID, + DetailID: install.AppDetailId, + Name: install.Name, + Version: install.Version, + Status: install.Status, + Path: install.GetPath(), + WebUI: install.WebUI, + HttpPort: install.HttpPort, + HttpsPort: install.HttpsPort, + }) + } + } + if ArryContains(defaultList, showItem) && len(itemData.Detail) == 0 { + itemData.IsRecommend = true + recommendList = append(recommendList, itemData) + continue + } + if !ArryContains(showList, showItem) && len(itemData.Detail) != 0 { + continue + } + data = append(data, itemData) + } + + sort.Slice(recommendList, func(i, j int) bool { + return recommendList[i].Recommend < recommendList[j].Recommend + }) + sort.Slice(data, func(i, j int) bool { + return data[i].Name < data[j].Name + }) + data = append(data, recommendList...) + return data, nil +} + +func (u *DashboardService) ListLauncherOption(filter string) ([]dto.LauncherOption, error) { + showList := loadShowList() + var data []dto.LauncherOption + optionMap := make(map[string]bool) + appInstalls, err := appInstallRepo.ListBy() + if err != nil { + return data, err + } + + for _, install := range appInstalls { + isShow := false + for _, item := range showList { + if install.App.Key == item { + isShow = true + break + } + } + optionMap[install.App.Key] = isShow + } + for key, val := range optionMap { + if len(filter) != 0 && !strings.Contains(key, filter) { + continue + } + data = append(data, dto.LauncherOption{Key: key, IsShow: val}) + } + sort.Slice(data, func(i, j int) bool { + return data[i].Key < data[j].Key + }) + return data, nil +} + type diskInfo struct { Type string Mount string @@ -355,3 +462,42 @@ 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 + } + res, err := xpack.RequestToMaster("/api/v2/core/launcher/search", http.MethodGet, nil) + if err != nil { + return data + } + item, err := json.Marshal(res) + if err != nil { + return data + } + if err := json.Unmarshal(item, &data); err != nil { + return data + } + return data +} + +type AppLauncher struct { + Key string `json:"key"` +} + +func ArryContains(arr []string, element string) bool { + for _, v := range arr { + if v == element { + return true + } + } + return false +} diff --git a/agent/router/ro_dashboard.go b/agent/router/ro_dashboard.go index f78e58c46..125290e4d 100644 --- a/agent/router/ro_dashboard.go +++ b/agent/router/ro_dashboard.go @@ -12,6 +12,8 @@ func (s *DashboardRouter) InitRouter(Router *gin.RouterGroup) { baseApi := v2.ApiGroupApp.BaseApi { cmdRouter.GET("/base/os", baseApi.LoadDashboardOsInfo) + cmdRouter.GET("/app/launcher", baseApi.LoadAppLauncher) + cmdRouter.POST("/app/launcher/option", baseApi.LoadAppLauncherOption) cmdRouter.GET("/base/:ioOption/:netOption", baseApi.LoadDashboardBaseInfo) cmdRouter.GET("/current/node", baseApi.LoadCurrentInfoForNode) cmdRouter.GET("/current/:ioOption/:netOption", baseApi.LoadDashboardCurrentInfo) diff --git a/agent/utils/common/common.go b/agent/utils/common/common.go index fb318b017..61abfbcad 100644 --- a/agent/utils/common/common.go +++ b/agent/utils/common/common.go @@ -229,6 +229,17 @@ func RemoveRepeatElement(a interface{}) (ret []interface{}) { return ret } +func RemoveRepeatStr(list []string) (ret []string) { + mapItem := make(map[string]struct{}) + for _, item := range list { + mapItem[item] = struct{}{} + } + for key := range mapItem { + ret = append(ret, key) + } + return ret +} + func LoadSizeUnit(value float64) string { val := int64(value) if val%1024 != 0 { diff --git a/core/app/api/v2/app_launcher.go b/core/app/api/v2/app_launcher.go new file mode 100644 index 000000000..b923687c7 --- /dev/null +++ b/core/app/api/v2/app_launcher.go @@ -0,0 +1,39 @@ +package v2 + +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/constant" + "github.com/gin-gonic/gin" +) + +func (b *BaseApi) SearchAppLauncher(c *gin.Context) { + data, err := appLauncherService.Search() + if err != nil { + helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) + return + } + helper.SuccessWithData(c, data) +} + +// @Tags App Launcher +// @Summary Update app Launcher +// @Description 更新首页显示应用 +// @Accept json +// @Param request body dto.ChangeShow true "request" +// @Success 200 +// @Security ApiKeyAuth +// @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.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) + return + } + helper.SuccessWithData(c, nil) +} diff --git a/core/app/api/v2/entry.go b/core/app/api/v2/entry.go index 196897634..b5de17ea5 100644 --- a/core/app/api/v2/entry.go +++ b/core/app/api/v2/entry.go @@ -9,12 +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() + 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() ) diff --git a/core/app/model/app_launcher.go b/core/app/model/app_launcher.go new file mode 100644 index 000000000..83457836d --- /dev/null +++ b/core/app/model/app_launcher.go @@ -0,0 +1,6 @@ +package model + +type AppLauncher struct { + BaseModel + Key string `json:"key"` +} diff --git a/core/app/repo/app_launcher.go b/core/app/repo/app_launcher.go new file mode 100644 index 000000000..adf8ced98 --- /dev/null +++ b/core/app/repo/app_launcher.go @@ -0,0 +1,50 @@ +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 ...DBOption) (model.AppLauncher, error) + List(opts ...DBOption) ([]model.AppLauncher, error) + Create(launcher *model.AppLauncher) error + Delete(opts ...DBOption) error +} + +func NewILauncherRepo() ILauncherRepo { + return &LauncherRepo{} +} + +func (u *LauncherRepo) Get(opts ...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 ...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 ...DBOption) error { + db := global.DB + for _, opt := range opts { + db = opt(db) + } + return db.Delete(&model.AppLauncher{}).Error +} diff --git a/core/app/repo/common.go b/core/app/repo/common.go index f9a779921..048c2b3ed 100644 --- a/core/app/repo/common.go +++ b/core/app/repo/common.go @@ -15,6 +15,7 @@ type ICommonRepo interface { WithByName(name string) DBOption WithLikeName(name string) DBOption WithByType(ty string) DBOption + WithByKey(key string) DBOption WithOrderBy(orderStr string) DBOption WithOrderRuleBy(orderBy, order string) DBOption @@ -60,6 +61,11 @@ func (c *CommonRepo) WithByType(ty string) DBOption { return g.Where("`type` = ?", ty) } } +func (c *CommonRepo) WithByKey(key string) DBOption { + return func(g *gorm.DB) *gorm.DB { + return g.Where("key = ?", key) + } +} func (c *CommonRepo) WithOrderBy(orderStr string) DBOption { return func(g *gorm.DB) *gorm.DB { return g.Order(orderStr) diff --git a/core/app/repo/setting.go b/core/app/repo/setting.go index 0759ccff4..90847c539 100644 --- a/core/app/repo/setting.go +++ b/core/app/repo/setting.go @@ -3,7 +3,6 @@ package repo import ( "github.com/1Panel-dev/1Panel/core/app/model" "github.com/1Panel-dev/1Panel/core/global" - "gorm.io/gorm" ) type SettingRepo struct{} @@ -13,7 +12,6 @@ type ISettingRepo interface { Get(opts ...DBOption) (model.Setting, error) Create(key, value string) error Update(key, value string) error - WithByKey(key string) DBOption } func NewISettingRepo() ISettingRepo { @@ -48,12 +46,6 @@ func (u *SettingRepo) Get(opts ...DBOption) (model.Setting, error) { return settings, err } -func (c *SettingRepo) WithByKey(key string) DBOption { - return func(g *gorm.DB) *gorm.DB { - return g.Where("key = ?", key) - } -} - func (u *SettingRepo) Update(key, value string) error { return global.DB.Model(&model.Setting{}).Where("key = ?", key).Updates(map[string]interface{}{"value": value}).Error } diff --git a/core/app/service/app_launcher.go b/core/app/service/app_launcher.go new file mode 100644 index 000000000..b6da7a8ae --- /dev/null +++ b/core/app/service/app_launcher.go @@ -0,0 +1,45 @@ +package service + +import ( + "github.com/1Panel-dev/1Panel/core/app/dto" + "github.com/1Panel-dev/1Panel/core/constant" +) + +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(commonRepo.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(commonRepo.WithByKey(req.Key)) + if req.Value == constant.StatusEnable { + if launcher.ID != 0 { + return nil + } + launcher.Key = req.Key + return launcherRepo.Create(&launcher) + } + if launcher.ID == 0 { + return nil + } + + return launcherRepo.Delete(commonRepo.WithByKey(req.Key)) +} diff --git a/core/app/service/auth.go b/core/app/service/auth.go index 5ea76e400..39aef2c79 100644 --- a/core/app/service/auth.go +++ b/core/app/service/auth.go @@ -32,11 +32,11 @@ func NewIAuthService() IAuthService { } func (u *AuthService) Login(c *gin.Context, info dto.Login, entrance string) (*dto.UserLoginInfo, error) { - nameSetting, err := settingRepo.Get(settingRepo.WithByKey("UserName")) + nameSetting, err := settingRepo.Get(commonRepo.WithByKey("UserName")) if err != nil { return nil, errors.WithMessage(constant.ErrRecordNotFound, err.Error()) } - passwordSetting, err := settingRepo.Get(settingRepo.WithByKey("Password")) + passwordSetting, err := settingRepo.Get(commonRepo.WithByKey("Password")) if err != nil { return nil, errors.WithMessage(constant.ErrRecordNotFound, err.Error()) } @@ -47,14 +47,14 @@ func (u *AuthService) Login(c *gin.Context, info dto.Login, entrance string) (*d if !hmac.Equal([]byte(info.Password), []byte(pass)) || nameSetting.Value != info.Name { return nil, constant.ErrAuth } - entranceSetting, err := settingRepo.Get(settingRepo.WithByKey("SecurityEntrance")) + entranceSetting, err := settingRepo.Get(commonRepo.WithByKey("SecurityEntrance")) if err != nil { return nil, err } if len(entranceSetting.Value) != 0 && entranceSetting.Value != entrance { return nil, buserr.New(constant.ErrEntrance) } - mfa, err := settingRepo.Get(settingRepo.WithByKey("MFAStatus")) + mfa, err := settingRepo.Get(commonRepo.WithByKey("MFAStatus")) if err != nil { return nil, err } @@ -68,11 +68,11 @@ func (u *AuthService) Login(c *gin.Context, info dto.Login, entrance string) (*d } func (u *AuthService) MFALogin(c *gin.Context, info dto.MFALogin, entrance string) (*dto.UserLoginInfo, error) { - nameSetting, err := settingRepo.Get(settingRepo.WithByKey("UserName")) + nameSetting, err := settingRepo.Get(commonRepo.WithByKey("UserName")) if err != nil { return nil, errors.WithMessage(constant.ErrRecordNotFound, err.Error()) } - passwordSetting, err := settingRepo.Get(settingRepo.WithByKey("Password")) + passwordSetting, err := settingRepo.Get(commonRepo.WithByKey("Password")) if err != nil { return nil, errors.WithMessage(constant.ErrRecordNotFound, err.Error()) } @@ -83,18 +83,18 @@ func (u *AuthService) MFALogin(c *gin.Context, info dto.MFALogin, entrance strin if !hmac.Equal([]byte(info.Password), []byte(pass)) || nameSetting.Value != info.Name { return nil, constant.ErrAuth } - entranceSetting, err := settingRepo.Get(settingRepo.WithByKey("SecurityEntrance")) + entranceSetting, err := settingRepo.Get(commonRepo.WithByKey("SecurityEntrance")) if err != nil { return nil, err } if len(entranceSetting.Value) != 0 && entranceSetting.Value != entrance { return nil, buserr.New(constant.ErrEntrance) } - mfaSecret, err := settingRepo.Get(settingRepo.WithByKey("MFASecret")) + mfaSecret, err := settingRepo.Get(commonRepo.WithByKey("MFASecret")) if err != nil { return nil, err } - mfaInterval, err := settingRepo.Get(settingRepo.WithByKey("MFAInterval")) + mfaInterval, err := settingRepo.Get(commonRepo.WithByKey("MFAInterval")) if err != nil { return nil, err } @@ -107,11 +107,11 @@ func (u *AuthService) MFALogin(c *gin.Context, info dto.MFALogin, entrance strin } func (u *AuthService) generateSession(c *gin.Context, name, authMethod string) (*dto.UserLoginInfo, error) { - setting, err := settingRepo.Get(settingRepo.WithByKey("SessionTimeout")) + setting, err := settingRepo.Get(commonRepo.WithByKey("SessionTimeout")) if err != nil { return nil, err } - httpsSetting, err := settingRepo.Get(settingRepo.WithByKey("SSL")) + httpsSetting, err := settingRepo.Get(commonRepo.WithByKey("SSL")) if err != nil { return nil, err } @@ -151,7 +151,7 @@ func (u *AuthService) generateSession(c *gin.Context, name, authMethod string) ( } func (u *AuthService) LogOut(c *gin.Context) error { - httpsSetting, err := settingRepo.Get(settingRepo.WithByKey("SSL")) + httpsSetting, err := settingRepo.Get(commonRepo.WithByKey("SSL")) if err != nil { return err } @@ -167,7 +167,7 @@ func (u *AuthService) LogOut(c *gin.Context) error { } func (u *AuthService) VerifyCode(code string) (bool, error) { - setting, err := settingRepo.Get(settingRepo.WithByKey("SecurityEntrance")) + setting, err := settingRepo.Get(commonRepo.WithByKey("SecurityEntrance")) if err != nil { return false, err } @@ -175,7 +175,7 @@ func (u *AuthService) VerifyCode(code string) (bool, error) { } func (u *AuthService) CheckIsSafety(code string) (string, error) { - status, err := settingRepo.Get(settingRepo.WithByKey("SecurityEntrance")) + status, err := settingRepo.Get(commonRepo.WithByKey("SecurityEntrance")) if err != nil { return "", err } @@ -189,7 +189,7 @@ func (u *AuthService) CheckIsSafety(code string) (string, error) { } func (u *AuthService) GetResponsePage() (string, error) { - pageCode, err := settingRepo.Get(settingRepo.WithByKey("NoAuthSetting")) + pageCode, err := settingRepo.Get(commonRepo.WithByKey("NoAuthSetting")) if err != nil { return "", err } diff --git a/core/app/service/backup.go b/core/app/service/backup.go index 34fea463e..e249fe6d3 100644 --- a/core/app/service/backup.go +++ b/core/app/service/backup.go @@ -173,7 +173,7 @@ func (u *BackupService) SearchWithPage(req dto.SearchPageWithType) (int64, inter func (u *BackupService) LoadOneDriveInfo() (dto.OneDriveInfo, error) { var data dto.OneDriveInfo data.RedirectUri = constant.OneDriveRedirectURI - clientID, err := settingRepo.Get(settingRepo.WithByKey("OneDriveID")) + clientID, err := settingRepo.Get(commonRepo.WithByKey("OneDriveID")) if err != nil { return data, err } @@ -182,7 +182,7 @@ func (u *BackupService) LoadOneDriveInfo() (dto.OneDriveInfo, error) { return data, err } data.ClientID = string(idItem) - clientSecret, err := settingRepo.Get(settingRepo.WithByKey("OneDriveSc")) + clientSecret, err := settingRepo.Get(commonRepo.WithByKey("OneDriveSc")) if err != nil { return data, err } diff --git a/core/app/service/entry.go b/core/app/service/entry.go index 229a7dfa5..5a27332c7 100644 --- a/core/app/service/entry.go +++ b/core/app/service/entry.go @@ -3,11 +3,12 @@ package service import "github.com/1Panel-dev/1Panel/core/app/repo" var ( - hostRepo = repo.NewIHostRepo() - commandRepo = repo.NewICommandRepo() - commonRepo = repo.NewICommonRepo() - settingRepo = repo.NewISettingRepo() - backupRepo = repo.NewIBackupRepo() - logRepo = repo.NewILogRepo() - groupRepo = repo.NewIGroupRepo() + hostRepo = repo.NewIHostRepo() + commandRepo = repo.NewICommandRepo() + commonRepo = repo.NewICommonRepo() + settingRepo = repo.NewISettingRepo() + backupRepo = repo.NewIBackupRepo() + logRepo = repo.NewILogRepo() + groupRepo = repo.NewIGroupRepo() + launcherRepo = repo.NewILauncherRepo() ) diff --git a/core/app/service/setting.go b/core/app/service/setting.go index b5ddc2c55..33ff5da55 100644 --- a/core/app/service/setting.go +++ b/core/app/service/setting.go @@ -74,7 +74,7 @@ func (u *SettingService) GetSettingInfo() (*dto.SettingInfo, error) { func (u *SettingService) Update(key, value string) error { switch key { case "AppStoreLastModified": - exist, _ := settingRepo.Get(settingRepo.WithByKey("AppStoreLastModified")) + exist, _ := settingRepo.Get(commonRepo.WithByKey("AppStoreLastModified")) if exist.ID == 0 { _ = settingRepo.Create("AppStoreLastModified", value) return nil @@ -270,14 +270,14 @@ func (u *SettingService) UpdateSSL(c *gin.Context, req dto.SSLUpdate) error { } func (u *SettingService) LoadFromCert() (*dto.SSLInfo, error) { - ssl, err := settingRepo.Get(settingRepo.WithByKey("SSL")) + ssl, err := settingRepo.Get(commonRepo.WithByKey("SSL")) if err != nil { return nil, err } if ssl.Value == "disable" { return &dto.SSLInfo{}, nil } - sslType, err := settingRepo.Get(settingRepo.WithByKey("SSLType")) + sslType, err := settingRepo.Get(commonRepo.WithByKey("SSLType")) if err != nil { return nil, err } @@ -311,7 +311,7 @@ func (u *SettingService) LoadFromCert() (*dto.SSLInfo, error) { } func (u *SettingService) HandlePasswordExpired(c *gin.Context, old, new string) error { - setting, err := settingRepo.Get(settingRepo.WithByKey("Password")) + setting, err := settingRepo.Get(commonRepo.WithByKey("Password")) if err != nil { return err } @@ -328,7 +328,7 @@ func (u *SettingService) HandlePasswordExpired(c *gin.Context, old, new string) return err } - expiredSetting, err := settingRepo.Get(settingRepo.WithByKey("ExpirationDays")) + expiredSetting, err := settingRepo.Get(commonRepo.WithByKey("ExpirationDays")) if err != nil { return err } diff --git a/core/app/service/upgrade.go b/core/app/service/upgrade.go index 2a28dca63..fe3e0010c 100644 --- a/core/app/service/upgrade.go +++ b/core/app/service/upgrade.go @@ -33,11 +33,11 @@ func NewIUpgradeService() IUpgradeService { func (u *UpgradeService) SearchUpgrade() (*dto.UpgradeInfo, error) { var upgrade dto.UpgradeInfo - currentVersion, err := settingRepo.Get(settingRepo.WithByKey("SystemVersion")) + currentVersion, err := settingRepo.Get(commonRepo.WithByKey("SystemVersion")) if err != nil { return nil, err } - DeveloperMode, err := settingRepo.Get(settingRepo.WithByKey("DeveloperMode")) + DeveloperMode, err := settingRepo.Get(commonRepo.WithByKey("DeveloperMode")) if err != nil { return nil, err } diff --git a/core/init/hook/hook.go b/core/init/hook/hook.go index 8a4144b71..6f3db20b2 100644 --- a/core/init/hook/hook.go +++ b/core/init/hook/hook.go @@ -14,33 +14,34 @@ import ( func Init() { settingRepo := repo.NewISettingRepo() - masterSetting, err := settingRepo.Get(settingRepo.WithByKey("MasterAddr")) + commonRepo := repo.NewICommonRepo() + masterSetting, err := settingRepo.Get(commonRepo.WithByKey("MasterAddr")) if err != nil { global.LOG.Errorf("load master addr from setting failed, err: %v", err) } global.CONF.System.MasterAddr = masterSetting.Value - portSetting, err := settingRepo.Get(settingRepo.WithByKey("ServerPort")) + portSetting, err := settingRepo.Get(commonRepo.WithByKey("ServerPort")) if err != nil { global.LOG.Errorf("load service port from setting failed, err: %v", err) } global.CONF.System.Port = portSetting.Value - ipv6Setting, err := settingRepo.Get(settingRepo.WithByKey("Ipv6")) + ipv6Setting, err := settingRepo.Get(commonRepo.WithByKey("Ipv6")) if err != nil { global.LOG.Errorf("load ipv6 status from setting failed, err: %v", err) } global.CONF.System.Ipv6 = ipv6Setting.Value - bindAddressSetting, err := settingRepo.Get(settingRepo.WithByKey("BindAddress")) + bindAddressSetting, err := settingRepo.Get(commonRepo.WithByKey("BindAddress")) if err != nil { global.LOG.Errorf("load bind address from setting failed, err: %v", err) } global.CONF.System.BindAddress = bindAddressSetting.Value - sslSetting, err := settingRepo.Get(settingRepo.WithByKey("SSL")) + sslSetting, err := settingRepo.Get(commonRepo.WithByKey("SSL")) if err != nil { global.LOG.Errorf("load service ssl from setting failed, err: %v", err) } global.CONF.System.SSL = sslSetting.Value - if _, err := settingRepo.Get(settingRepo.WithByKey("SystemStatus")); err != nil { + if _, err := settingRepo.Get(commonRepo.WithByKey("SystemStatus")); err != nil { _ = settingRepo.Create("SystemStatus", "Free") } if err := settingRepo.Update("SystemStatus", "Free"); err != nil { diff --git a/core/init/migration/migrate.go b/core/init/migration/migrate.go index a7f453453..958b7d57d 100644 --- a/core/init/migration/migrate.go +++ b/core/init/migration/migrate.go @@ -15,6 +15,7 @@ func Init() { migrations.InitMasterAddr, migrations.InitHost, migrations.InitTerminalSetting, + migrations.InitAppLauncher, }) 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 c8d8eebb2..68e73534b 100644 --- a/core/init/migration/migrations/init.go +++ b/core/init/migration/migrations/init.go @@ -233,3 +233,10 @@ var InitTerminalSetting = &gormigrate.Migration{ return nil }, } + +var InitAppLauncher = &gormigrate.Migration{ + ID: "20241029-init-app-launcher", + Migrate: func(tx *gorm.DB) error { + return tx.AutoMigrate(&model.AppLauncher{}) + }, +} diff --git a/core/middleware/bind_domain.go b/core/middleware/bind_domain.go index 128780e07..9edbef1f8 100644 --- a/core/middleware/bind_domain.go +++ b/core/middleware/bind_domain.go @@ -13,7 +13,8 @@ import ( func BindDomain() gin.HandlerFunc { return func(c *gin.Context) { settingRepo := repo.NewISettingRepo() - status, err := settingRepo.Get(settingRepo.WithByKey("BindDomain")) + commonRepo := repo.NewICommonRepo() + status, err := settingRepo.Get(commonRepo.WithByKey("BindDomain")) if err != nil { helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) return diff --git a/core/middleware/helper.go b/core/middleware/helper.go index 124b69588..3faee0f3c 100644 --- a/core/middleware/helper.go +++ b/core/middleware/helper.go @@ -8,7 +8,8 @@ import ( func LoadErrCode(errInfo string) int { settingRepo := repo.NewISettingRepo() - codeVal, err := settingRepo.Get(settingRepo.WithByKey("NoAuthSetting")) + commonRepo := repo.NewICommonRepo() + codeVal, err := settingRepo.Get(commonRepo.WithByKey("NoAuthSetting")) if err != nil { return 500 } diff --git a/core/middleware/ip_limit.go b/core/middleware/ip_limit.go index 1b3e491c6..22dbec0f3 100644 --- a/core/middleware/ip_limit.go +++ b/core/middleware/ip_limit.go @@ -15,7 +15,8 @@ import ( func WhiteAllow() gin.HandlerFunc { return func(c *gin.Context) { settingRepo := repo.NewISettingRepo() - status, err := settingRepo.Get(settingRepo.WithByKey("AllowIPs")) + commonRepo := repo.NewICommonRepo() + status, err := settingRepo.Get(commonRepo.WithByKey("AllowIPs")) if err != nil { helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) return diff --git a/core/middleware/loading.go b/core/middleware/loading.go index fca8afe73..dbdfdd1df 100644 --- a/core/middleware/loading.go +++ b/core/middleware/loading.go @@ -10,7 +10,8 @@ import ( func GlobalLoading() gin.HandlerFunc { return func(c *gin.Context) { settingRepo := repo.NewISettingRepo() - status, err := settingRepo.Get(settingRepo.WithByKey("SystemStatus")) + commonRepo := repo.NewICommonRepo() + status, err := settingRepo.Get(commonRepo.WithByKey("SystemStatus")) if err != nil { helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) return diff --git a/core/middleware/password_expired.go b/core/middleware/password_expired.go index 8efd2f809..35cd8d193 100644 --- a/core/middleware/password_expired.go +++ b/core/middleware/password_expired.go @@ -21,7 +21,8 @@ func PasswordExpired() gin.HandlerFunc { return } settingRepo := repo.NewISettingRepo() - setting, err := settingRepo.Get(settingRepo.WithByKey("ExpirationDays")) + commonRepo := repo.NewICommonRepo() + setting, err := settingRepo.Get(commonRepo.WithByKey("ExpirationDays")) if err != nil { helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypePasswordExpired, err) return @@ -32,7 +33,7 @@ func PasswordExpired() gin.HandlerFunc { return } - extime, err := settingRepo.Get(settingRepo.WithByKey("ExpirationTime")) + extime, err := settingRepo.Get(commonRepo.WithByKey("ExpirationTime")) if err != nil { helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypePasswordExpired, err) return diff --git a/core/middleware/session.go b/core/middleware/session.go index 303c9094e..e48d33851 100644 --- a/core/middleware/session.go +++ b/core/middleware/session.go @@ -32,7 +32,8 @@ func SessionAuth() gin.HandlerFunc { return } settingRepo := repo.NewISettingRepo() - setting, err := settingRepo.Get(settingRepo.WithByKey("SessionTimeout")) + commonRepo := repo.NewICommonRepo() + setting, err := settingRepo.Get(commonRepo.WithByKey("SessionTimeout")) if err != nil { global.LOG.Errorf("create operation record failed, err: %v", err) } diff --git a/core/router/app_launcher.go b/core/router/app_launcher.go new file mode 100644 index 000000000..02c815c1e --- /dev/null +++ b/core/router/app_launcher.go @@ -0,0 +1,17 @@ +package router + +import ( + v2 "github.com/1Panel-dev/1Panel/core/app/api/v2" + "github.com/gin-gonic/gin" +) + +type AppLauncherRouter struct{} + +func (s *AppLauncherRouter) InitRouter(Router *gin.RouterGroup) { + launcherRouter := Router.Group("launcher") + baseApi := v2.ApiGroupApp.BaseApi + { + launcherRouter.GET("/search", baseApi.SearchAppLauncher) + launcherRouter.POST("/change/show", baseApi.UpdateAppLauncher) + } +} diff --git a/core/router/common.go b/core/router/common.go index 748d81896..cfb82012f 100644 --- a/core/router/common.go +++ b/core/router/common.go @@ -9,5 +9,6 @@ func commonGroups() []CommonRouter { &CommandRouter{}, &HostRouter{}, &GroupRouter{}, + &AppLauncherRouter{}, } } diff --git a/core/utils/jwt/jwt.go b/core/utils/jwt/jwt.go index b7b52cea0..b70c541dc 100644 --- a/core/utils/jwt/jwt.go +++ b/core/utils/jwt/jwt.go @@ -27,7 +27,8 @@ type BaseClaims struct { func NewJWT() *JWT { settingRepo := repo.NewISettingRepo() - jwtSign, _ := settingRepo.Get(settingRepo.WithByKey("JWTSigningKey")) + commonRepo := repo.NewICommonRepo() + jwtSign, _ := settingRepo.Get(commonRepo.WithByKey("JWTSigningKey")) return &JWT{ []byte(jwtSign.Value), } diff --git a/frontend/src/api/interface/dashboard.ts b/frontend/src/api/interface/dashboard.ts index e593f9e4e..60d4ae609 100644 --- a/frontend/src/api/interface/dashboard.ts +++ b/frontend/src/api/interface/dashboard.ts @@ -8,6 +8,34 @@ export namespace Dashboard { diskSize: number; } + export interface AppLauncher { + key: string; + icon: string; + limit: number; + shortDescEn: string; + shortDescZh: string; + currentRow: InstallDetail; + + isInstall: boolean; + isRecommend: boolean; + detail: Array; + } + export interface AppLauncherOption { + key: string; + isShow: boolean; + } + export interface InstallDetail { + installID: number; + detailID: string; + name: string; + version: string; + path: string; + status: string; + appType: string; + webUI: string; + httpPort: string; + httpsPort: string; + } export interface BaseInfo { websiteNumber: number; databaseNumber: number; diff --git a/frontend/src/api/modules/dashboard.ts b/frontend/src/api/modules/dashboard.ts index 59697cd44..cc1fccc0e 100644 --- a/frontend/src/api/modules/dashboard.ts +++ b/frontend/src/api/modules/dashboard.ts @@ -5,6 +5,19 @@ export const loadOsInfo = () => { return http.get(`/dashboard/base/os`); }; +export const loadAppLauncher = () => { + return http.get>(`/dashboard/app/launcher`); +}; +export const loadAppLauncherOption = (filter: string) => { + return http.post>(`/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`); +}; + export const loadBaseInfo = (ioOption: string, netOption: string) => { return http.get(`/dashboard/base/${ioOption}/${netOption}`); }; diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts index c2e7ac593..23b8716d5 100644 --- a/frontend/src/lang/modules/en.ts +++ b/frontend/src/lang/modules/en.ts @@ -336,6 +336,8 @@ const message = { name: 'Tamper Proof', }, home: { + recommend: 'recommend', + dir: 'dir', restart_1panel: 'Restart Panel', restart_system: 'Restart Server', operationSuccess: 'Operation succeeded, rebooting, please refresh the browser manually later!', diff --git a/frontend/src/lang/modules/tw.ts b/frontend/src/lang/modules/tw.ts index 73ccf193f..8114b446a 100644 --- a/frontend/src/lang/modules/tw.ts +++ b/frontend/src/lang/modules/tw.ts @@ -330,6 +330,8 @@ const message = { tamper: '防篡改', }, home: { + recommend: '推薦', + dir: '目錄', restart_1panel: '重啟面板', restart_system: '重啟服務器', operationSuccess: '操作成功,正在重啟,請稍後手動刷新瀏覽器!', diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index ee6194a97..7a1231471 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -330,6 +330,8 @@ const message = { tamper: '防篡改', }, home: { + recommend: '推荐', + dir: '目录', restart_1panel: '重启面板', restart_system: '重启服务器', operationSuccess: '操作成功,正在重启,请稍后手动刷新浏览器!', diff --git a/frontend/src/views/home/app/index.vue b/frontend/src/views/home/app/index.vue index 1d570d808..da3764e18 100644 --- a/frontend/src/views/home/app/index.vue +++ b/frontend/src/views/home/app/index.vue @@ -1,67 +1,212 @@