mirror of
https://github.com/acepanel/panel.git
synced 2026-02-04 05:31:44 +08:00
feat: 应用商店版本机制重构
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"`
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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"`
|
||||
}
|
||||
|
||||
@@ -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"`
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user