2
0
mirror of https://github.com/acepanel/panel.git synced 2026-02-04 05:31:44 +08:00

feat: 应用商店版本机制重构

This commit is contained in:
耗子
2024-10-09 22:22:10 +08:00
parent 6892cff96f
commit f77e4d17fc
6 changed files with 137 additions and 58 deletions

View File

@@ -7,26 +7,28 @@ import (
)
type App struct {
ID uint `gorm:"primaryKey" json:"id"`
Slug string `gorm:"not null;unique" json:"slug"`
Version string `gorm:"not null" json:"version"`
Show bool `gorm:"not null" json:"show"`
ShowOrder int `gorm:"not null" json:"show_order"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
ID uint `gorm:"primaryKey" json:"id"`
Slug string `gorm:"not null;unique" json:"slug"`
VersionSlug string `gorm:"not null" json:"version_slug"`
Version string `gorm:"not null" json:"version"`
Show bool `gorm:"not null" json:"show"`
ShowOrder int `gorm:"not null" json:"show_order"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
type AppRepo interface {
All() api.Apps
Get(slug string) (*api.App, error)
UpdateExist(slug string) bool
Installed() ([]*App, error)
GetInstalled(slug string) (*App, error)
GetInstalledAll(query string, cond ...string) ([]*App, error)
GetHomeShow() ([]map[string]string, error)
IsInstalled(query string, cond ...string) (bool, error)
Install(slug string) error
Uninstall(slug string) error
Update(slug string) error
Install(slug, versionSlug string) error
Uninstall(slug, versionSlug string) error
Update(slug, versionSlug string) error
UpdateShow(slug string, show bool) error
UpdateCache() error
}

View File

@@ -7,12 +7,14 @@ import (
"slices"
"github.com/expr-lang/expr"
"github.com/hashicorp/go-version"
"github.com/spf13/cast"
"github.com/TheTNB/panel/internal/app"
"github.com/TheTNB/panel/internal/biz"
"github.com/TheTNB/panel/pkg/api"
"github.com/TheTNB/panel/pkg/apploader"
"github.com/TheTNB/panel/pkg/str"
)
type appRepo struct {
@@ -42,14 +44,36 @@ func (r *appRepo) All() api.Apps {
}
func (r *appRepo) Get(slug string) (*api.App, error) {
for app := range slices.Values(r.All()) {
if app.Slug == slug {
return app, nil
for item := range slices.Values(r.All()) {
if item.Slug == slug {
return item, nil
}
}
return nil, errors.New("应用不存在")
}
func (r *appRepo) UpdateExist(slug string) bool {
item, err := r.Get(slug)
if err != nil {
return false
}
installed, err := r.GetInstalled(slug)
if err != nil {
return false
}
for v := range slices.Values(item.Versions) {
if v.Slug == installed.VersionSlug {
current := str.FirstElement(v.Subs)
if current != nil && current.Version != installed.Version {
return true
}
}
}
return false
}
func (r *appRepo) Installed() ([]*biz.App, error) {
var apps []*biz.App
if err := app.Orm.Find(&apps).Error; err != nil {
@@ -117,20 +141,31 @@ func (r *appRepo) IsInstalled(query string, cond ...string) (bool, error) {
return count > 0, nil
}
func (r *appRepo) Install(slug string) error {
func (r *appRepo) Install(slug, versionSlug string) error {
item, err := r.Get(slug)
if err != nil {
return err
}
panel, err := version.NewVersion(app.Version)
if err != nil {
return err
}
if installed, _ := r.IsInstalled(slug); installed {
return errors.New("应用已安装")
}
var shellUrl string
for version := range slices.Values(item.Versions) {
if version.PanelVersion == app.Version {
shellUrl = version.Install
for v := range slices.Values(item.Versions) {
vs, err := version.NewVersion(v.Panel)
if err != nil {
continue
}
if v.Slug == versionSlug {
if vs.GreaterThan(panel) {
return fmt.Errorf("应用 %s 需要面板版本 %s当前版本 %s", item.Name, v.Panel, app.Version)
}
shellUrl = v.Install
break
}
}
@@ -154,26 +189,34 @@ func (r *appRepo) Install(slug string) error {
return err
}
func (r *appRepo) Uninstall(slug string) error {
func (r *appRepo) Uninstall(slug, versionSlug string) error {
item, err := r.Get(slug)
if err != nil {
return err
}
panel, err := version.NewVersion(app.Version)
if err != nil {
return err
}
if installed, _ := r.IsInstalled(slug); !installed {
return errors.New("应用未安装")
}
var shellUrl string
for version := range slices.Values(item.Versions) {
if version.PanelVersion == app.Version {
shellUrl = version.Uninstall
for v := range slices.Values(item.Versions) {
vs, err := version.NewVersion(v.Panel)
if err != nil {
continue
}
if v.Slug == versionSlug {
if vs.GreaterThan(panel) {
return fmt.Errorf("应用 %s 需要面板版本 %s当前版本 %s", item.Name, v.Panel, app.Version)
}
shellUrl = v.Uninstall
break
}
}
if shellUrl == "" && len(item.Versions) > 0 {
shellUrl = item.Versions[0].Uninstall
}
if shellUrl == "" {
return fmt.Errorf("无法获取应用 %s 的卸载脚本", item.Name)
}
@@ -194,20 +237,31 @@ func (r *appRepo) Uninstall(slug string) error {
return err
}
func (r *appRepo) Update(slug string) error {
func (r *appRepo) Update(slug, versionSlug string) error {
item, err := r.Get(slug)
if err != nil {
return err
}
panel, err := version.NewVersion(app.Version)
if err != nil {
return err
}
if installed, _ := r.IsInstalled(slug); !installed {
return errors.New("应用未安装")
}
var shellUrl string
for version := range slices.Values(item.Versions) {
if version.PanelVersion == app.Version {
shellUrl = version.Update
for v := range slices.Values(item.Versions) {
vs, err := version.NewVersion(v.Panel)
if err != nil {
continue
}
if v.Slug == versionSlug {
if vs.GreaterThan(panel) {
return fmt.Errorf("应用 %s 需要面板版本 %s当前版本 %s", item.Name, v.Panel, app.Version)
}
shellUrl = v.Update
break
}
}

View File

@@ -1,5 +1,10 @@
package request
type App struct {
Slug string `json:"slug" form:"slug"`
VersionSlug string `json:"version_slug" form:"version_slug"`
}
type AppSlug struct {
Slug string `json:"slug" form:"slug"`
}

View File

@@ -8,7 +8,6 @@ import (
"github.com/TheTNB/panel/internal/biz"
"github.com/TheTNB/panel/internal/data"
"github.com/TheTNB/panel/internal/http/request"
"github.com/TheTNB/panel/pkg/str"
"github.com/TheTNB/panel/pkg/types"
)
@@ -37,23 +36,24 @@ func (s *AppService) List(w http.ResponseWriter, r *http.Request) {
var apps []types.StoreApp
for _, item := range all {
installed, installedVersion, currentVersion, show := false, "", "", false
if str.FirstElement(item.Versions) != nil {
currentVersion = str.FirstElement(item.Versions).Version
}
installed, installedVersion, installedVersionSlug, updateExist, show := false, "", "", false, false
if _, ok := installedAppMap[item.Slug]; ok {
installed = true
installedVersion = installedAppMap[item.Slug].Version
installedVersionSlug = installedAppMap[item.Slug].VersionSlug
updateExist = s.appRepo.UpdateExist(item.Slug)
show = installedAppMap[item.Slug].Show
}
apps = append(apps, types.StoreApp{
Name: item.Name,
Description: item.Description,
Slug: item.Slug,
Version: currentVersion,
Installed: installed,
InstalledVersion: installedVersion,
Show: show,
Name: item.Name,
Description: item.Description,
Slug: item.Slug,
Versions: item.Versions,
Installed: installed,
InstalledVersion: installedVersion,
InstalledVersionSlug: installedVersionSlug,
UpdateExist: updateExist,
Show: show,
})
}
@@ -66,13 +66,13 @@ func (s *AppService) List(w http.ResponseWriter, r *http.Request) {
}
func (s *AppService) Install(w http.ResponseWriter, r *http.Request) {
req, err := Bind[request.AppSlug](r)
req, err := Bind[request.App](r)
if err != nil {
Error(w, http.StatusUnprocessableEntity, err.Error())
return
}
if err = s.appRepo.Install(req.Slug); err != nil {
if err = s.appRepo.Install(req.Slug, req.VersionSlug); err != nil {
Error(w, http.StatusInternalServerError, err.Error())
return
}
@@ -81,13 +81,13 @@ func (s *AppService) Install(w http.ResponseWriter, r *http.Request) {
}
func (s *AppService) Uninstall(w http.ResponseWriter, r *http.Request) {
req, err := Bind[request.AppSlug](r)
req, err := Bind[request.App](r)
if err != nil {
Error(w, http.StatusUnprocessableEntity, err.Error())
return
}
if err = s.appRepo.Uninstall(req.Slug); err != nil {
if err = s.appRepo.Uninstall(req.Slug, req.VersionSlug); err != nil {
Error(w, http.StatusInternalServerError, err.Error())
return
}
@@ -96,13 +96,13 @@ func (s *AppService) Uninstall(w http.ResponseWriter, r *http.Request) {
}
func (s *AppService) Update(w http.ResponseWriter, r *http.Request) {
req, err := Bind[request.AppSlug](r)
req, err := Bind[request.App](r)
if err != nil {
Error(w, http.StatusUnprocessableEntity, err.Error())
return
}
if err = s.appRepo.Update(req.Slug); err != nil {
if err = s.appRepo.Update(req.Slug, req.VersionSlug); err != nil {
Error(w, http.StatusInternalServerError, err.Error())
return
}

View File

@@ -15,11 +15,16 @@ type App struct {
Categories []string `json:"categories"`
Depends string `json:"depends"`
Versions []struct {
Version string `json:"version"`
Install string `json:"install"`
Uninstall string `json:"uninstall"`
Update string `json:"update"`
PanelVersion string `json:"panel_version"`
Slug string `json:"slug"`
Name string `json:"name"`
Panel string `json:"panel"`
Install string `json:"install"`
Uninstall string `json:"uninstall"`
Update string `json:"update"`
Subs []struct {
Log string `json:"log"`
Version string `json:"version"`
} `json:"versions"`
} `json:"versions"`
Order int `json:"order"`
}

View File

@@ -10,11 +10,24 @@ type App struct {
// StoreApp 商店应用结构
type StoreApp struct {
Name string `json:"name"`
Description string `json:"description"`
Slug string `json:"slug"`
Version string `json:"version"`
Installed bool `json:"installed"`
InstalledVersion string `json:"installed_version"`
Show bool `json:"show"`
Name string `json:"name"`
Description string `json:"description"`
Slug string `json:"slug"`
Versions []struct {
Slug string `json:"slug"`
Name string `json:"name"`
Panel string `json:"panel"`
Install string `json:"install"`
Uninstall string `json:"uninstall"`
Update string `json:"update"`
Subs []struct {
Log string `json:"log"`
Version string `json:"version"`
} `json:"versions"`
} `json:"versions"`
Installed bool `json:"installed"`
InstalledVersion string `json:"installed_version"`
InstalledVersionSlug string `json:"installed_version_slug"`
UpdateExist bool `json:"update_exist"`
Show bool `json:"show"`
}