mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-01-31 14:08:06 +08:00
feat: 增加应用从OSS更新功能
This commit is contained in:
parent
226a81ecf3
commit
5f5a48041b
4
apps/apps.json
Normal file
4
apps/apps.json
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"version": "0.0.1",
|
||||||
|
"package": ""
|
||||||
|
}
|
@ -3,8 +3,7 @@ system:
|
|||||||
db_type: sqlite
|
db_type: sqlite
|
||||||
level: debug
|
level: debug
|
||||||
data_dir: /opt/1Panel/data
|
data_dir: /opt/1Panel/data
|
||||||
resource_dir: /opt/1Panel/data/resource
|
app_oss: "https://1panel.oss-cn-hangzhou.aliyuncs.com/apps.json"
|
||||||
app_dir: /opt/1Panel/data/apps
|
|
||||||
|
|
||||||
mysql:
|
mysql:
|
||||||
path: localhost
|
path: localhost
|
||||||
|
@ -24,49 +24,6 @@ type AppDetailDTO struct {
|
|||||||
Params interface{} `json:"params"`
|
Params interface{} `json:"params"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type AppList struct {
|
|
||||||
Version string `json:"version"`
|
|
||||||
Tags []Tag `json:"tags"`
|
|
||||||
Items []AppDefine `json:"items"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type AppDefine struct {
|
|
||||||
Key string `json:"key"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
Tags []string `json:"tags"`
|
|
||||||
Versions []string `json:"versions"`
|
|
||||||
Icon string `json:"icon"`
|
|
||||||
Author string `json:"author"`
|
|
||||||
Source string `json:"source"`
|
|
||||||
ShortDesc string `json:"short_desc"`
|
|
||||||
Type string `json:"type"`
|
|
||||||
Required []string `json:"Required"`
|
|
||||||
CrossVersionUpdate bool `json:"crossVersionUpdate"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (define AppDefine) GetRequired() string {
|
|
||||||
by, _ := json.Marshal(define.Required)
|
|
||||||
return string(by)
|
|
||||||
}
|
|
||||||
|
|
||||||
type Tag struct {
|
|
||||||
Key string `json:"key"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type AppForm struct {
|
|
||||||
FormFields []AppFormFields `json:"form_fields"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type AppFormFields struct {
|
|
||||||
Type string `json:"type"`
|
|
||||||
LabelZh string `json:"label_zh"`
|
|
||||||
LabelEn string `json:"label_en"`
|
|
||||||
Required string `json:"required"`
|
|
||||||
Default string `json:"default"`
|
|
||||||
EnvKey string `json:"env_variable"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type AppRequest struct {
|
type AppRequest struct {
|
||||||
PageInfo
|
PageInfo
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
@ -143,7 +100,55 @@ type ContainerExec struct {
|
|||||||
Auth AuthParam `json:"auth"`
|
Auth AuthParam `json:"auth"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AppOssConfig struct {
|
||||||
|
Version string `json:"version"`
|
||||||
|
Package string `json:"package"`
|
||||||
|
}
|
||||||
|
|
||||||
type AppVersion struct {
|
type AppVersion struct {
|
||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
DetailId uint `json:"detailId"`
|
DetailId uint `json:"detailId"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AppList struct {
|
||||||
|
Version string `json:"version"`
|
||||||
|
Tags []Tag `json:"tags"`
|
||||||
|
Items []AppDefine `json:"items"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AppDefine struct {
|
||||||
|
Key string `json:"key"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Tags []string `json:"tags"`
|
||||||
|
Versions []string `json:"versions"`
|
||||||
|
Icon string `json:"icon"`
|
||||||
|
Author string `json:"author"`
|
||||||
|
Source string `json:"source"`
|
||||||
|
ShortDesc string `json:"short_desc"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Required []string `json:"Required"`
|
||||||
|
CrossVersionUpdate bool `json:"crossVersionUpdate"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (define AppDefine) GetRequired() string {
|
||||||
|
by, _ := json.Marshal(define.Required)
|
||||||
|
return string(by)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Tag struct {
|
||||||
|
Key string `json:"key"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AppForm struct {
|
||||||
|
FormFields []AppFormFields `json:"form_fields"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AppFormFields struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
LabelZh string `json:"label_zh"`
|
||||||
|
LabelEn string `json:"label_en"`
|
||||||
|
Required string `json:"required"`
|
||||||
|
Default string `json:"default"`
|
||||||
|
EnvKey string `json:"env_variable"`
|
||||||
|
}
|
||||||
|
@ -84,5 +84,5 @@ func (a AppInstallRepo) BatchUpdateBy(maps map[string]interface{}, opts ...DBOpt
|
|||||||
if len(opts) == 0 {
|
if len(opts) == 0 {
|
||||||
db = db.Where("1=1")
|
db = db.Where("1=1")
|
||||||
}
|
}
|
||||||
return db.Updates(&maps).Error
|
return db.Debug().Updates(&maps).Error
|
||||||
}
|
}
|
||||||
|
@ -444,7 +444,10 @@ func (a AppService) SyncInstalled(installId uint) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a AppService) SyncAppList() error {
|
func (a AppService) SyncAppList() error {
|
||||||
//TODO 从 oss 拉取最新列表
|
if err := getAppFromOss(); err != nil {
|
||||||
|
global.LOG.Errorf("get app from oss error: %s", err.Error())
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
appDir := constant.AppResourceDir
|
appDir := constant.AppResourceDir
|
||||||
iconDir := path.Join(appDir, "icons")
|
iconDir := path.Join(appDir, "icons")
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"github.com/1Panel-dev/1Panel/app/dto"
|
"github.com/1Panel-dev/1Panel/app/dto"
|
||||||
"github.com/1Panel-dev/1Panel/app/model"
|
"github.com/1Panel-dev/1Panel/app/model"
|
||||||
"github.com/1Panel-dev/1Panel/constant"
|
"github.com/1Panel-dev/1Panel/constant"
|
||||||
|
"github.com/1Panel-dev/1Panel/global"
|
||||||
"github.com/1Panel-dev/1Panel/utils/cmd"
|
"github.com/1Panel-dev/1Panel/utils/cmd"
|
||||||
"github.com/1Panel-dev/1Panel/utils/common"
|
"github.com/1Panel-dev/1Panel/utils/common"
|
||||||
"github.com/1Panel-dev/1Panel/utils/compose"
|
"github.com/1Panel-dev/1Panel/utils/compose"
|
||||||
@ -14,7 +15,9 @@ import (
|
|||||||
"github.com/joho/godotenv"
|
"github.com/joho/godotenv"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
|
"io/ioutil"
|
||||||
"math"
|
"math"
|
||||||
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"reflect"
|
"reflect"
|
||||||
@ -229,13 +232,9 @@ func restoreInstall(install model.AppInstall, backup model.AppInstallBackup) err
|
|||||||
if !fileOp.Stat(backupFile) {
|
if !fileOp.Stat(backupFile) {
|
||||||
return errors.New(fmt.Sprintf("%s file is not exist", backup.Name))
|
return errors.New(fmt.Sprintf("%s file is not exist", backup.Name))
|
||||||
}
|
}
|
||||||
backDir := installDir + "_back"
|
|
||||||
if fileOp.Stat(backDir) {
|
backupDir, err := fileOp.Backup(installDir)
|
||||||
if err := fileOp.DeleteDir(backDir); err != nil {
|
if err != nil {
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err := fileOp.Rename(installDir, backDir); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := fileOp.Decompress(backupFile, installKeyDir, files.TarGz); err != nil {
|
if err := fileOp.Decompress(backupFile, installKeyDir, files.TarGz); err != nil {
|
||||||
@ -280,7 +279,7 @@ func restoreInstall(install model.AppInstall, backup model.AppInstallBackup) err
|
|||||||
}
|
}
|
||||||
|
|
||||||
install.Param = backup.Param
|
install.Param = backup.Param
|
||||||
_ = fileOp.DeleteDir(backDir)
|
_ = fileOp.DeleteDir(backupDir)
|
||||||
if out, err := compose.Up(install.GetComposePath()); err != nil {
|
if out, err := compose.Up(install.GetComposePath()); err != nil {
|
||||||
return handleErr(install, err, out)
|
return handleErr(install, err, out)
|
||||||
}
|
}
|
||||||
@ -456,3 +455,50 @@ func handleErr(install model.AppInstall, err error, out string) error {
|
|||||||
_ = appInstallRepo.Save(&install)
|
_ = appInstallRepo.Save(&install)
|
||||||
return reErr
|
return reErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getAppFromOss() error {
|
||||||
|
res, err := http.Get(global.CONF.System.AppOss)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
appByte, err := ioutil.ReadAll(res.Body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var ossConfig dto.AppOssConfig
|
||||||
|
if err := json.Unmarshal(appByte, &ossConfig); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
appDir := constant.AppResourceDir
|
||||||
|
oldListFile := path.Join(appDir, "list.json")
|
||||||
|
content, err := os.ReadFile(oldListFile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
list := &dto.AppList{}
|
||||||
|
if err := json.Unmarshal(content, list); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if list.Version == ossConfig.Version {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
fileOp := files.NewFileOp()
|
||||||
|
if _, err := fileOp.Backup(appDir); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
packageName := path.Base(ossConfig.Package)
|
||||||
|
packagePath := path.Join(constant.ResourceDir, packageName)
|
||||||
|
if err := fileOp.DownloadFile(ossConfig.Package, packagePath); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := fileOp.Decompress(packagePath, constant.ResourceDir, files.Zip); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
_ = fileOp.DeleteFile(packagePath)
|
||||||
|
}()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -126,7 +126,7 @@ func (f FileService) ChangeName(re dto.FileRename) error {
|
|||||||
func (f FileService) Wget(w dto.FileWget) (string, error) {
|
func (f FileService) Wget(w dto.FileWget) (string, error) {
|
||||||
fo := files.NewFileOp()
|
fo := files.NewFileOp()
|
||||||
key := "file-wget-" + uuid.NewV4().String()
|
key := "file-wget-" + uuid.NewV4().String()
|
||||||
return key, fo.DownloadFile(w.Url, filepath.Join(w.Path, w.Name), key)
|
return key, fo.DownloadFileWithProcess(w.Url, filepath.Join(w.Path, w.Name), key)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f FileService) MvFile(m dto.FileMove) error {
|
func (f FileService) MvFile(m dto.FileMove) error {
|
||||||
|
@ -137,7 +137,7 @@ func (w *WriteCounter) SaveProcess() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f FileOp) DownloadFile(url, dst, key string) error {
|
func (f FileOp) DownloadFileWithProcess(url, dst, key string) error {
|
||||||
resp, err := http.Get(url)
|
resp, err := http.Get(url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
global.LOG.Errorf("get download file [%s] error, err %s", dst, err.Error())
|
global.LOG.Errorf("get download file [%s] error, err %s", dst, err.Error())
|
||||||
@ -167,6 +167,26 @@ func (f FileOp) DownloadFile(url, dst, key string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f FileOp) DownloadFile(url, dst string) error {
|
||||||
|
resp, err := http.Get(url)
|
||||||
|
if err != nil {
|
||||||
|
global.LOG.Errorf("get download file [%s] error, err %s", dst, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
out, err := os.Create(dst)
|
||||||
|
if err != nil {
|
||||||
|
global.LOG.Errorf("create download file [%s] error, err %s", dst, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = io.Copy(out, resp.Body); err != nil {
|
||||||
|
global.LOG.Errorf("save download file [%s] error, err %s", dst, err.Error())
|
||||||
|
}
|
||||||
|
out.Close()
|
||||||
|
resp.Body.Close()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (f FileOp) Cut(oldPaths []string, dst string) error {
|
func (f FileOp) Cut(oldPaths []string, dst string) error {
|
||||||
for _, p := range oldPaths {
|
for _, p := range oldPaths {
|
||||||
base := filepath.Base(p)
|
base := filepath.Base(p)
|
||||||
@ -396,3 +416,19 @@ func (f FileOp) Decompress(srcFile string, dst string, cType CompressType) error
|
|||||||
}
|
}
|
||||||
return format.Extract(context.Background(), input, nil, handler)
|
return format.Extract(context.Background(), input, nil, handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f FileOp) Backup(srcFile string) (string, error) {
|
||||||
|
backupPath := srcFile + "_bak"
|
||||||
|
info, _ := f.Fs.Stat(backupPath)
|
||||||
|
if info != nil {
|
||||||
|
if info.IsDir() {
|
||||||
|
f.DeleteDir(backupPath)
|
||||||
|
} else {
|
||||||
|
f.DeleteFile(backupPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := f.Rename(srcFile, backupPath); err != nil {
|
||||||
|
return backupPath, err
|
||||||
|
}
|
||||||
|
return backupPath, nil
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user