mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-01-31 22:18:07 +08:00
feat: 同步远程应用增加日志 (#6209)
This commit is contained in:
parent
cfe4fa7a92
commit
5865a958fc
@ -2,6 +2,7 @@ package v2
|
||||
|
||||
import (
|
||||
"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
|
||||
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
@ -31,20 +32,22 @@ func (b *BaseApi) SearchApp(c *gin.Context) {
|
||||
}
|
||||
|
||||
// @Tags App
|
||||
// @Summary Sync app list
|
||||
// @Description 同步应用列表
|
||||
// @Summary Sync remote app list
|
||||
// @Description 同步远程应用列表
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /apps/sync [post]
|
||||
// @Router /apps/sync/remote [post]
|
||||
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"应用商店同步","formatEN":"App store synchronization"}
|
||||
func (b *BaseApi) SyncApp(c *gin.Context) {
|
||||
go appService.SyncAppListFromLocal()
|
||||
var req dto.OperateWithTask
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
res, err := appService.GetAppUpdate()
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
|
||||
if !res.CanUpdate {
|
||||
if res.IsSyncing {
|
||||
helper.SuccessWithMsg(c, i18n.GetMsgByKey("AppStoreIsSyncing"))
|
||||
@ -54,11 +57,23 @@ func (b *BaseApi) SyncApp(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
go func() {
|
||||
if err := appService.SyncAppListFromRemote(); err != nil {
|
||||
if err = appService.SyncAppListFromRemote(req.TaskID); err != nil {
|
||||
global.LOG.Errorf("Synchronization with the App Store failed [%s]", err.Error())
|
||||
}
|
||||
}()
|
||||
helper.SuccessWithData(c, "")
|
||||
helper.SuccessWithData(c, nil)
|
||||
}
|
||||
|
||||
// @Tags App
|
||||
// @Summary Sync local app list
|
||||
// @Description 同步本地应用列表
|
||||
// @Success 200
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /apps/sync/local [post]
|
||||
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"应用商店同步","formatEN":"App store synchronization"}
|
||||
func (b *BaseApi) SyncLocalApp(c *gin.Context) {
|
||||
go appService.SyncAppListFromLocal()
|
||||
helper.SuccessWithOutData(c)
|
||||
}
|
||||
|
||||
// @Tags App
|
||||
|
@ -35,10 +35,10 @@ func ErrorWithDetail(ctx *gin.Context, code int, msgKey string, err error) {
|
||||
case errors.As(err, &buserr.BusinessError{}):
|
||||
res.Message = err.Error()
|
||||
default:
|
||||
res.Message = i18n.GetMsgWithMap(msgKey, map[string]interface{}{"detail": err})
|
||||
res.Message = i18n.GetMsgWithDetail(msgKey, err.Error())
|
||||
}
|
||||
} else {
|
||||
res.Message = i18n.GetMsgWithMap(msgKey, map[string]interface{}{"detail": err})
|
||||
res.Message = i18n.GetMsgWithDetail(msgKey, err.Error())
|
||||
}
|
||||
ctx.JSON(http.StatusOK, res)
|
||||
ctx.Abort()
|
||||
|
@ -57,3 +57,7 @@ type UpdateGroup struct {
|
||||
Group uint `json:"group"`
|
||||
NewGroup uint `json:"newGroup"`
|
||||
}
|
||||
|
||||
type OperateWithTask struct {
|
||||
TaskID string `json:"taskID"`
|
||||
}
|
||||
|
@ -2,6 +2,8 @@ package repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||
"gorm.io/gorm"
|
||||
@ -25,6 +27,25 @@ func NewITaskRepo() ITaskRepo {
|
||||
return &TaskRepo{}
|
||||
}
|
||||
|
||||
func getTaskDb(opts ...DBOption) *gorm.DB {
|
||||
db := global.TaskDB
|
||||
for _, opt := range opts {
|
||||
db = opt(db)
|
||||
}
|
||||
return db
|
||||
}
|
||||
|
||||
func getTaskTx(ctx context.Context, opts ...DBOption) *gorm.DB {
|
||||
tx, ok := ctx.Value(constant.DB).(*gorm.DB)
|
||||
if ok {
|
||||
for _, opt := range opts {
|
||||
tx = opt(tx)
|
||||
}
|
||||
return tx
|
||||
}
|
||||
return getTaskDb(opts...)
|
||||
}
|
||||
|
||||
func (t TaskRepo) WithByID(id string) DBOption {
|
||||
return func(g *gorm.DB) *gorm.DB {
|
||||
return g.Where("id = ?", id)
|
||||
@ -44,12 +65,12 @@ func (t TaskRepo) WithResourceID(id uint) DBOption {
|
||||
}
|
||||
|
||||
func (t TaskRepo) Create(ctx context.Context, task *model.Task) error {
|
||||
return getTx(ctx).Create(&task).Error
|
||||
return getTaskTx(ctx).Create(&task).Error
|
||||
}
|
||||
|
||||
func (t TaskRepo) GetFirst(opts ...DBOption) (model.Task, error) {
|
||||
var task model.Task
|
||||
db := getDb(opts...).Model(&model.Task{})
|
||||
db := getTaskDb(opts...).Model(&model.Task{})
|
||||
if err := db.First(&task).Error; err != nil {
|
||||
return task, err
|
||||
}
|
||||
@ -58,7 +79,7 @@ func (t TaskRepo) GetFirst(opts ...DBOption) (model.Task, error) {
|
||||
|
||||
func (t TaskRepo) Page(page, size int, opts ...DBOption) (int64, []model.Task, error) {
|
||||
var tasks []model.Task
|
||||
db := getDb(opts...).Model(&model.Task{})
|
||||
db := getTaskDb(opts...).Model(&model.Task{})
|
||||
count := int64(0)
|
||||
db = db.Count(&count)
|
||||
err := db.Limit(size).Offset(size * (page - 1)).Find(&tasks).Error
|
||||
@ -66,5 +87,5 @@ func (t TaskRepo) Page(page, size int, opts ...DBOption) (int64, []model.Task, e
|
||||
}
|
||||
|
||||
func (t TaskRepo) Update(ctx context.Context, task *model.Task) error {
|
||||
return getTx(ctx).Save(&task).Error
|
||||
return getTaskTx(ctx).Save(&task).Error
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ type IAppService interface {
|
||||
GetApp(key string) (*response.AppDTO, error)
|
||||
GetAppDetail(appId uint, version, appType string) (response.AppDetailDTO, error)
|
||||
Install(req request.AppInstallCreate) (*model.AppInstall, error)
|
||||
SyncAppListFromRemote() error
|
||||
SyncAppListFromRemote(taskID string) error
|
||||
GetAppUpdate() (*response.AppUpdateRes, error)
|
||||
GetAppDetailByID(id uint) (*response.AppDetailDTO, error)
|
||||
SyncAppListFromLocal()
|
||||
@ -805,242 +805,244 @@ var InitTypes = map[string]struct{}{
|
||||
"node": {},
|
||||
}
|
||||
|
||||
func (a AppService) SyncAppListFromRemote() (err error) {
|
||||
global.LOG.Infof("Starting synchronization with App Store...")
|
||||
updateRes, err := a.GetAppUpdate()
|
||||
func (a AppService) SyncAppListFromRemote(taskID string) (err error) {
|
||||
syncTask, err := task.NewTaskWithOps(i18n.GetMsgByKey("App"), task.TaskSync, task.TaskScopeAppStore, taskID, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !updateRes.CanUpdate {
|
||||
if updateRes.IsSyncing {
|
||||
global.LOG.Infof("AppStore is Syncing!")
|
||||
return
|
||||
}
|
||||
global.LOG.Infof("The App Store is at the latest version")
|
||||
return
|
||||
}
|
||||
|
||||
list := &dto.AppList{}
|
||||
if updateRes.AppList == nil {
|
||||
list, err = getAppList()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
list = updateRes.AppList
|
||||
}
|
||||
settingService := NewISettingService()
|
||||
_ = settingService.Update("AppStoreSyncStatus", constant.Syncing)
|
||||
|
||||
var (
|
||||
tags []*model.Tag
|
||||
appTags []*model.AppTag
|
||||
oldAppIds []uint
|
||||
)
|
||||
for _, t := range list.Extra.Tags {
|
||||
tags = append(tags, &model.Tag{
|
||||
Key: t.Key,
|
||||
Name: t.Name,
|
||||
Sort: t.Sort,
|
||||
})
|
||||
}
|
||||
oldApps, err := appRepo.GetBy(appRepo.WithResource(constant.AppResourceRemote))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for _, old := range oldApps {
|
||||
oldAppIds = append(oldAppIds, old.ID)
|
||||
}
|
||||
|
||||
transport := xpack.LoadRequestTransport()
|
||||
baseRemoteUrl := fmt.Sprintf("%s/%s/1panel", global.CONF.System.AppRepo, global.CONF.System.Mode)
|
||||
appsMap := getApps(oldApps, list.Apps)
|
||||
|
||||
global.LOG.Infof("Starting synchronization of application details...")
|
||||
for _, l := range list.Apps {
|
||||
app := appsMap[l.AppProperty.Key]
|
||||
_, iconRes, err := httpUtil.HandleGetWithTransport(l.Icon, http.MethodGet, transport, constant.TimeOut20s)
|
||||
syncTask.AddSubTask(task.GetTaskName(i18n.GetMsgByKey("App"), task.TaskSync, task.TaskScopeAppStore), func(t *task.Task) (err error) {
|
||||
updateRes, err := a.GetAppUpdate()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
iconStr := ""
|
||||
if !strings.Contains(string(iconRes), "<xml>") {
|
||||
iconStr = base64.StdEncoding.EncodeToString(iconRes)
|
||||
if !updateRes.CanUpdate {
|
||||
if updateRes.IsSyncing {
|
||||
t.Log(i18n.GetMsgByKey("AppStoreIsSyncing"))
|
||||
return nil
|
||||
}
|
||||
t.Log(i18n.GetMsgByKey("AppStoreIsLastVersion"))
|
||||
return nil
|
||||
}
|
||||
|
||||
app.Icon = iconStr
|
||||
app.TagsKey = l.AppProperty.Tags
|
||||
if l.AppProperty.Recommend > 0 {
|
||||
app.Recommend = l.AppProperty.Recommend
|
||||
list := &dto.AppList{}
|
||||
if updateRes.AppList == nil {
|
||||
list, err = getAppList()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
app.Recommend = 9999
|
||||
list = updateRes.AppList
|
||||
}
|
||||
app.ReadMe = l.ReadMe
|
||||
app.LastModified = l.LastModified
|
||||
versions := l.Versions
|
||||
detailsMap := getAppDetails(app.Details, versions)
|
||||
for _, v := range versions {
|
||||
version := v.Name
|
||||
detail := detailsMap[version]
|
||||
versionUrl := fmt.Sprintf("%s/%s/%s", baseRemoteUrl, app.Key, version)
|
||||
settingService := NewISettingService()
|
||||
_ = settingService.Update("AppStoreSyncStatus", constant.Syncing)
|
||||
|
||||
if _, ok := InitTypes[app.Type]; ok {
|
||||
dockerComposeUrl := fmt.Sprintf("%s/%s", versionUrl, "docker-compose.yml")
|
||||
_, composeRes, err := httpUtil.HandleGetWithTransport(dockerComposeUrl, http.MethodGet, transport, constant.TimeOut20s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
detail.DockerCompose = string(composeRes)
|
||||
} else {
|
||||
detail.DockerCompose = ""
|
||||
var (
|
||||
tags []*model.Tag
|
||||
appTags []*model.AppTag
|
||||
oldAppIds []uint
|
||||
)
|
||||
for _, t := range list.Extra.Tags {
|
||||
tags = append(tags, &model.Tag{
|
||||
Key: t.Key,
|
||||
Name: t.Name,
|
||||
Sort: t.Sort,
|
||||
})
|
||||
}
|
||||
oldApps, err := appRepo.GetBy(appRepo.WithResource(constant.AppResourceRemote))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, old := range oldApps {
|
||||
oldAppIds = append(oldAppIds, old.ID)
|
||||
}
|
||||
|
||||
transport := xpack.LoadRequestTransport()
|
||||
baseRemoteUrl := fmt.Sprintf("%s/%s/1panel", global.CONF.System.AppRepo, global.CONF.System.Mode)
|
||||
appsMap := getApps(oldApps, list.Apps)
|
||||
|
||||
t.LogStart(i18n.GetMsgByKey("SyncAppDetail"))
|
||||
for _, l := range list.Apps {
|
||||
app := appsMap[l.AppProperty.Key]
|
||||
_, iconRes, err := httpUtil.HandleGetWithTransport(l.Icon, http.MethodGet, transport, constant.TimeOut20s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
iconStr := ""
|
||||
if !strings.Contains(string(iconRes), "<xml>") {
|
||||
iconStr = base64.StdEncoding.EncodeToString(iconRes)
|
||||
}
|
||||
|
||||
paramByte, _ := json.Marshal(v.AppForm)
|
||||
detail.Params = string(paramByte)
|
||||
detail.DownloadUrl = fmt.Sprintf("%s/%s", versionUrl, app.Key+"-"+version+".tar.gz")
|
||||
detail.DownloadCallBackUrl = v.DownloadCallBackUrl
|
||||
detail.Update = true
|
||||
detail.LastModified = v.LastModified
|
||||
detailsMap[version] = detail
|
||||
}
|
||||
var newDetails []model.AppDetail
|
||||
for _, detail := range detailsMap {
|
||||
newDetails = append(newDetails, detail)
|
||||
}
|
||||
app.Details = newDetails
|
||||
appsMap[l.AppProperty.Key] = app
|
||||
}
|
||||
|
||||
global.LOG.Infof("Synchronization of application details Success")
|
||||
|
||||
var (
|
||||
addAppArray []model.App
|
||||
updateAppArray []model.App
|
||||
deleteAppArray []model.App
|
||||
deleteIds []uint
|
||||
tagMap = make(map[string]uint, len(tags))
|
||||
)
|
||||
|
||||
for _, v := range appsMap {
|
||||
if v.ID == 0 {
|
||||
addAppArray = append(addAppArray, v)
|
||||
} else {
|
||||
if v.Status == constant.AppTakeDown {
|
||||
installs, _ := appInstallRepo.ListBy(appInstallRepo.WithAppId(v.ID))
|
||||
if len(installs) > 0 {
|
||||
updateAppArray = append(updateAppArray, v)
|
||||
continue
|
||||
}
|
||||
deleteAppArray = append(deleteAppArray, v)
|
||||
deleteIds = append(deleteIds, v.ID)
|
||||
app.Icon = iconStr
|
||||
app.TagsKey = l.AppProperty.Tags
|
||||
if l.AppProperty.Recommend > 0 {
|
||||
app.Recommend = l.AppProperty.Recommend
|
||||
} else {
|
||||
updateAppArray = append(updateAppArray, v)
|
||||
app.Recommend = 9999
|
||||
}
|
||||
}
|
||||
}
|
||||
tx, ctx := getTxAndContext()
|
||||
defer tx.Rollback()
|
||||
if len(addAppArray) > 0 {
|
||||
if err = appRepo.BatchCreate(ctx, addAppArray); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
if len(deleteAppArray) > 0 {
|
||||
if err = appRepo.BatchDelete(ctx, deleteAppArray); err != nil {
|
||||
return
|
||||
}
|
||||
if err = appDetailRepo.DeleteByAppIds(ctx, deleteIds); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
if err = tagRepo.DeleteAll(ctx); err != nil {
|
||||
return
|
||||
}
|
||||
if len(tags) > 0 {
|
||||
if err = tagRepo.BatchCreate(ctx, tags); err != nil {
|
||||
return
|
||||
}
|
||||
for _, t := range tags {
|
||||
tagMap[t.Key] = t.ID
|
||||
}
|
||||
}
|
||||
for _, update := range updateAppArray {
|
||||
if err = appRepo.Save(ctx, &update); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
apps := append(addAppArray, updateAppArray...)
|
||||
app.ReadMe = l.ReadMe
|
||||
app.LastModified = l.LastModified
|
||||
versions := l.Versions
|
||||
detailsMap := getAppDetails(app.Details, versions)
|
||||
for _, v := range versions {
|
||||
version := v.Name
|
||||
detail := detailsMap[version]
|
||||
versionUrl := fmt.Sprintf("%s/%s/%s", baseRemoteUrl, app.Key, version)
|
||||
|
||||
var (
|
||||
addDetails []model.AppDetail
|
||||
updateDetails []model.AppDetail
|
||||
deleteDetails []model.AppDetail
|
||||
)
|
||||
for _, app := range apps {
|
||||
for _, t := range app.TagsKey {
|
||||
tagId, ok := tagMap[t]
|
||||
if ok {
|
||||
appTags = append(appTags, &model.AppTag{
|
||||
AppId: app.ID,
|
||||
TagId: tagId,
|
||||
})
|
||||
}
|
||||
}
|
||||
for _, d := range app.Details {
|
||||
d.AppId = app.ID
|
||||
if d.ID == 0 {
|
||||
addDetails = append(addDetails, d)
|
||||
} else {
|
||||
if d.Status == constant.AppTakeDown {
|
||||
runtime, _ := runtimeRepo.GetFirst(runtimeRepo.WithDetailId(d.ID))
|
||||
if runtime != nil {
|
||||
updateDetails = append(updateDetails, d)
|
||||
continue
|
||||
if _, ok := InitTypes[app.Type]; ok {
|
||||
dockerComposeUrl := fmt.Sprintf("%s/%s", versionUrl, "docker-compose.yml")
|
||||
_, composeRes, err := httpUtil.HandleGetWithTransport(dockerComposeUrl, http.MethodGet, transport, constant.TimeOut20s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
installs, _ := appInstallRepo.ListBy(appInstallRepo.WithDetailIdsIn([]uint{d.ID}))
|
||||
if len(installs) > 0 {
|
||||
updateDetails = append(updateDetails, d)
|
||||
continue
|
||||
}
|
||||
deleteDetails = append(deleteDetails, d)
|
||||
detail.DockerCompose = string(composeRes)
|
||||
} else {
|
||||
updateDetails = append(updateDetails, d)
|
||||
detail.DockerCompose = ""
|
||||
}
|
||||
|
||||
paramByte, _ := json.Marshal(v.AppForm)
|
||||
detail.Params = string(paramByte)
|
||||
detail.DownloadUrl = fmt.Sprintf("%s/%s", versionUrl, app.Key+"-"+version+".tar.gz")
|
||||
detail.DownloadCallBackUrl = v.DownloadCallBackUrl
|
||||
detail.Update = true
|
||||
detail.LastModified = v.LastModified
|
||||
detailsMap[version] = detail
|
||||
}
|
||||
var newDetails []model.AppDetail
|
||||
for _, detail := range detailsMap {
|
||||
newDetails = append(newDetails, detail)
|
||||
}
|
||||
app.Details = newDetails
|
||||
appsMap[l.AppProperty.Key] = app
|
||||
}
|
||||
t.LogSuccess(i18n.GetMsgByKey("SyncAppDetail"))
|
||||
|
||||
var (
|
||||
addAppArray []model.App
|
||||
updateAppArray []model.App
|
||||
deleteAppArray []model.App
|
||||
deleteIds []uint
|
||||
tagMap = make(map[string]uint, len(tags))
|
||||
)
|
||||
|
||||
for _, v := range appsMap {
|
||||
if v.ID == 0 {
|
||||
addAppArray = append(addAppArray, v)
|
||||
} else {
|
||||
if v.Status == constant.AppTakeDown {
|
||||
installs, _ := appInstallRepo.ListBy(appInstallRepo.WithAppId(v.ID))
|
||||
if len(installs) > 0 {
|
||||
updateAppArray = append(updateAppArray, v)
|
||||
continue
|
||||
}
|
||||
deleteAppArray = append(deleteAppArray, v)
|
||||
deleteIds = append(deleteIds, v.ID)
|
||||
} else {
|
||||
updateAppArray = append(updateAppArray, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(addDetails) > 0 {
|
||||
if err = appDetailRepo.BatchCreate(ctx, addDetails); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
if len(deleteDetails) > 0 {
|
||||
if err = appDetailRepo.BatchDelete(ctx, deleteDetails); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
for _, u := range updateDetails {
|
||||
if err = appDetailRepo.Update(ctx, u); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if len(oldAppIds) > 0 {
|
||||
if err = appTagRepo.DeleteByAppIds(ctx, oldAppIds); err != nil {
|
||||
if len(addAppArray) > 0 {
|
||||
if err = appRepo.BatchCreate(context.Background(), addAppArray); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
if len(deleteAppArray) > 0 {
|
||||
if err = appRepo.BatchDelete(context.Background(), deleteAppArray); err != nil {
|
||||
return
|
||||
}
|
||||
if err = appDetailRepo.DeleteByAppIds(context.Background(), deleteIds); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
if err = tagRepo.DeleteAll(context.Background()); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if len(appTags) > 0 {
|
||||
if err = appTagRepo.BatchCreate(ctx, appTags); err != nil {
|
||||
return
|
||||
if len(tags) > 0 {
|
||||
if err = tagRepo.BatchCreate(context.Background(), tags); err != nil {
|
||||
return
|
||||
}
|
||||
for _, tag := range tags {
|
||||
tagMap[tag.Key] = tag.ID
|
||||
}
|
||||
}
|
||||
}
|
||||
tx.Commit()
|
||||
for _, update := range updateAppArray {
|
||||
if err = appRepo.Save(context.Background(), &update); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
apps := append(addAppArray, updateAppArray...)
|
||||
|
||||
_ = settingService.Update("AppStoreSyncStatus", constant.SyncSuccess)
|
||||
_ = settingService.Update("AppStoreLastModified", strconv.Itoa(list.LastModified))
|
||||
var (
|
||||
addDetails []model.AppDetail
|
||||
updateDetails []model.AppDetail
|
||||
deleteDetails []model.AppDetail
|
||||
)
|
||||
for _, app := range apps {
|
||||
for _, tag := range app.TagsKey {
|
||||
tagId, ok := tagMap[tag]
|
||||
if ok {
|
||||
appTags = append(appTags, &model.AppTag{
|
||||
AppId: app.ID,
|
||||
TagId: tagId,
|
||||
})
|
||||
}
|
||||
}
|
||||
for _, d := range app.Details {
|
||||
d.AppId = app.ID
|
||||
if d.ID == 0 {
|
||||
addDetails = append(addDetails, d)
|
||||
} else {
|
||||
if d.Status == constant.AppTakeDown {
|
||||
runtime, _ := runtimeRepo.GetFirst(runtimeRepo.WithDetailId(d.ID))
|
||||
if runtime != nil {
|
||||
updateDetails = append(updateDetails, d)
|
||||
continue
|
||||
}
|
||||
installs, _ := appInstallRepo.ListBy(appInstallRepo.WithDetailIdsIn([]uint{d.ID}))
|
||||
if len(installs) > 0 {
|
||||
updateDetails = append(updateDetails, d)
|
||||
continue
|
||||
}
|
||||
deleteDetails = append(deleteDetails, d)
|
||||
} else {
|
||||
updateDetails = append(updateDetails, d)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(addDetails) > 0 {
|
||||
if err = appDetailRepo.BatchCreate(context.Background(), addDetails); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
if len(deleteDetails) > 0 {
|
||||
if err = appDetailRepo.BatchDelete(context.Background(), deleteDetails); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
for _, u := range updateDetails {
|
||||
if err = appDetailRepo.Update(context.Background(), u); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
global.LOG.Infof("Synchronization with the App Store was successful!")
|
||||
return
|
||||
if len(oldAppIds) > 0 {
|
||||
if err = appTagRepo.DeleteByAppIds(context.Background(), oldAppIds); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if len(appTags) > 0 {
|
||||
if err = appTagRepo.BatchCreate(context.Background(), appTags); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
_ = settingService.Update("AppStoreSyncStatus", constant.SyncSuccess)
|
||||
_ = settingService.Update("AppStoreLastModified", strconv.Itoa(list.LastModified))
|
||||
t.Log(i18n.GetMsgByKey("AppStoreSyncSuccess"))
|
||||
return nil
|
||||
}, nil)
|
||||
|
||||
return syncTask.Execute()
|
||||
}
|
||||
|
@ -50,6 +50,7 @@ const (
|
||||
TaskUpdate = "TaskUpdate"
|
||||
TaskRestart = "TaskRestart"
|
||||
TaskBackup = "TaskBackup"
|
||||
TaskSync = "TaskSync"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -57,6 +58,7 @@ const (
|
||||
TaskScopeApp = "App"
|
||||
TaskScopeRuntime = "Runtime"
|
||||
TaskScopeDatabase = "Database"
|
||||
TaskScopeAppStore = "AppStore"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -13,7 +13,7 @@ func NewAppStoreJob() *app {
|
||||
|
||||
func (a *app) Run() {
|
||||
global.LOG.Info("AppStore scheduled task in progress ...")
|
||||
if err := service.NewIAppService().SyncAppListFromRemote(); err != nil {
|
||||
if err := service.NewIAppService().SyncAppListFromRemote(""); err != nil {
|
||||
global.LOG.Errorf("AppStore sync failed %s", err.Error())
|
||||
}
|
||||
global.LOG.Info("AppStore scheduled task has completed")
|
||||
|
@ -15,6 +15,7 @@ import (
|
||||
var (
|
||||
DB *gorm.DB
|
||||
MonitorDB *gorm.DB
|
||||
TaskDB *gorm.DB
|
||||
CoreDB *gorm.DB
|
||||
LOG *logrus.Logger
|
||||
CONF configs.ServerConfig
|
||||
|
@ -32,25 +32,20 @@ func GetMsgWithMap(key string, maps map[string]interface{}) string {
|
||||
}
|
||||
}
|
||||
|
||||
func GetMsgWithName(key string, name string, err error) string {
|
||||
func GetMsgWithDetail(key string, detail string) string {
|
||||
var (
|
||||
content string
|
||||
dataMap = make(map[string]interface{})
|
||||
)
|
||||
dataMap["name"] = name
|
||||
if err != nil {
|
||||
dataMap["err"] = err.Error()
|
||||
}
|
||||
dataMap["detail"] = detail
|
||||
content, _ = global.I18n.Localize(&i18n.LocalizeConfig{
|
||||
MessageID: key,
|
||||
TemplateData: dataMap,
|
||||
})
|
||||
content = strings.ReplaceAll(content, "<no value>", "")
|
||||
if content == "" {
|
||||
return key
|
||||
} else {
|
||||
if content != "" {
|
||||
return content
|
||||
}
|
||||
return key
|
||||
}
|
||||
|
||||
func GetErrMsg(key string, maps map[string]interface{}) string {
|
||||
@ -87,14 +82,6 @@ func GetWithName(key string, name string) string {
|
||||
return content
|
||||
}
|
||||
|
||||
func GetWithMap(key string, dataMap map[string]string) string {
|
||||
content, _ := global.I18n.Localize(&i18n.LocalizeConfig{
|
||||
MessageID: key,
|
||||
TemplateData: dataMap,
|
||||
})
|
||||
return content
|
||||
}
|
||||
|
||||
func GetWithNameAndErr(key string, name string, err error) string {
|
||||
var (
|
||||
dataMap = make(map[string]interface{})
|
||||
|
@ -62,6 +62,10 @@ PullImageStart: "Start pulling image {{ .name }}"
|
||||
PullImageSuccess: "Image pulled successfully"
|
||||
UpgradeAppStart: "Start upgrading application {{ .name }}"
|
||||
UpgradeAppSuccess: "App {{ .name }} upgraded successfully"
|
||||
AppStoreIsLastVersion: "The app store is up to date"
|
||||
AppStoreSyncSuccess: "App store synchronized successfully"
|
||||
SyncAppDetail: "Synchronize app configuration"
|
||||
|
||||
|
||||
#file
|
||||
ErrFileCanNotRead: "File can not read"
|
||||
@ -229,4 +233,6 @@ Stop: 'Stop',
|
||||
Image: 'Image',
|
||||
AppLink: 'Associated Application'
|
||||
EnableSSL: "Enable HTTPS"
|
||||
AppStore: "App Store"
|
||||
TaskSync: "Sync"
|
||||
|
||||
|
@ -63,6 +63,10 @@ PullImageStart: "開始拉取鏡像 {{ .name }}"
|
||||
PullImageSuccess: "鏡像拉取成功"
|
||||
UpgradeAppStart: "開始升級應用程式 {{ .name }}"
|
||||
UpgradeAppSuccess: "應用程式 {{ .name }} 升級成功"
|
||||
AppStoreIsLastVersion: "應用商店已經是最新版本"
|
||||
AppStoreSyncSuccess: "應用商店同步成功"
|
||||
SyncAppDetail: "同步應用配置"
|
||||
|
||||
|
||||
#file
|
||||
ErrFileCanNotRead: "此文件不支持預覽"
|
||||
@ -231,4 +235,6 @@ Stop: '停止',
|
||||
Image: '鏡像',
|
||||
AppLink: '關聯應用'
|
||||
EnableSSL: "開啟 HTTPS"
|
||||
AppStore: "應用商店"
|
||||
TaskSync: "同步"
|
||||
|
||||
|
@ -62,6 +62,9 @@ PullImageStart: "开始拉取镜像 {{ .name }}"
|
||||
PullImageSuccess: "镜像拉取成功"
|
||||
UpgradeAppStart: "开始升级应用 {{ .name }}"
|
||||
UpgradeAppSuccess: "应用 {{ .name }} 升级成功"
|
||||
AppStoreIsLastVersion: "应用商店已经是最新版本"
|
||||
AppStoreSyncSuccess: "应用商店同步成功"
|
||||
SyncAppDetail: "同步应用配置"
|
||||
|
||||
#file
|
||||
ErrFileCanNotRead: "此文件不支持预览"
|
||||
@ -124,7 +127,7 @@ ExecShellSuccess: "脚本执行成功"
|
||||
ErrUserIsExist: "当前用户已存在,请重新输入"
|
||||
ErrDatabaseIsExist: "当前数据库已存在,请重新输入"
|
||||
ErrExecTimeOut: "SQL 执行超时,请检查数据库"
|
||||
ErrRemoteExist: "远程数据库已存在该名称,请修改后重试"vv
|
||||
ErrRemoteExist: "远程数据库已存在该名称,请修改后重试"
|
||||
ErrLocalExist: "本地数据库已存在该名称,请修改后重试"
|
||||
|
||||
#redis
|
||||
@ -233,3 +236,5 @@ Stop: "停止"
|
||||
Image: "镜像"
|
||||
AppLink: "关联应用"
|
||||
EnableSSL: "开启 HTTPS"
|
||||
AppStore: "应用商店"
|
||||
TaskSync: "同步"
|
||||
|
@ -15,7 +15,7 @@ func Init() {
|
||||
|
||||
func syncApp() {
|
||||
_ = service.NewISettingService().Update("AppStoreSyncStatus", constant.SyncSuccess)
|
||||
if err := service.NewIAppService().SyncAppListFromRemote(); err != nil {
|
||||
if err := service.NewIAppService().SyncAppListFromRemote(""); err != nil {
|
||||
global.LOG.Errorf("App Store synchronization failed")
|
||||
return
|
||||
}
|
||||
|
@ -13,6 +13,21 @@ import (
|
||||
"gorm.io/gorm/logger"
|
||||
)
|
||||
|
||||
func GetDBWithPath(dbPath string) (*gorm.DB, error) {
|
||||
db, _ := gorm.Open(sqlite.Open(dbPath), &gorm.Config{
|
||||
DisableForeignKeyConstraintWhenMigrating: true,
|
||||
Logger: newLogger(),
|
||||
})
|
||||
sqlDB, dbError := db.DB()
|
||||
if dbError != nil {
|
||||
return nil, dbError
|
||||
}
|
||||
sqlDB.SetConnMaxIdleTime(10)
|
||||
sqlDB.SetMaxOpenConns(100)
|
||||
sqlDB.SetConnMaxLifetime(time.Hour)
|
||||
return db, nil
|
||||
}
|
||||
|
||||
func Init() {
|
||||
if _, err := os.Stat(global.CONF.System.DbPath); err != nil {
|
||||
if err := os.MkdirAll(global.CONF.System.DbPath, os.ModePerm); err != nil {
|
||||
@ -29,27 +44,37 @@ func Init() {
|
||||
}
|
||||
|
||||
initMonitorDB()
|
||||
initTaskDB()
|
||||
|
||||
db, err := gorm.Open(sqlite.Open(fullPath), &gorm.Config{
|
||||
DisableForeignKeyConstraintWhenMigrating: true,
|
||||
Logger: newLogger(),
|
||||
})
|
||||
db, err := GetDBWithPath(fullPath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
sqlDB, dbError := db.DB()
|
||||
if dbError != nil {
|
||||
panic(dbError)
|
||||
}
|
||||
sqlDB.SetConnMaxIdleTime(10)
|
||||
sqlDB.SetMaxOpenConns(100)
|
||||
sqlDB.SetConnMaxLifetime(time.Hour)
|
||||
|
||||
global.DB = db
|
||||
global.LOG.Info("init db successfully")
|
||||
|
||||
}
|
||||
|
||||
func initTaskDB() {
|
||||
fullPath := path.Join(global.CONF.System.DbPath, "task.db")
|
||||
if _, err := os.Stat(fullPath); err != nil {
|
||||
f, err := os.Create(fullPath)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("init task db file failed, err: %v", err))
|
||||
}
|
||||
_ = f.Close()
|
||||
}
|
||||
|
||||
db, err := GetDBWithPath(fullPath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
global.TaskDB = db
|
||||
global.LOG.Info("init task db successfully")
|
||||
}
|
||||
|
||||
func initMonitorDB() {
|
||||
fullPath := path.Join(global.CONF.System.DbPath, "monitor.db")
|
||||
if _, err := os.Stat(fullPath); err != nil {
|
||||
|
@ -19,6 +19,7 @@ func Init() {
|
||||
migrations.UpdateWebsite,
|
||||
migrations.UpdateWebsiteDomain,
|
||||
migrations.UpdateApp,
|
||||
migrations.AddTaskDB,
|
||||
})
|
||||
if err := m.Migrate(); err != nil {
|
||||
global.LOG.Error(err)
|
||||
|
@ -242,3 +242,12 @@ var UpdateApp = &gormigrate.Migration{
|
||||
&model.App{})
|
||||
},
|
||||
}
|
||||
|
||||
var AddTaskDB = &gormigrate.Migration{
|
||||
ID: "20240822-add-task-table",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
return global.TaskDB.AutoMigrate(
|
||||
&model.Task{},
|
||||
)
|
||||
},
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package server
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"github.com/1Panel-dev/1Panel/agent/init/business"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
@ -12,7 +13,6 @@ import (
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
"github.com/1Panel-dev/1Panel/agent/i18n"
|
||||
"github.com/1Panel-dev/1Panel/agent/init/app"
|
||||
"github.com/1Panel-dev/1Panel/agent/init/business"
|
||||
"github.com/1Panel-dev/1Panel/agent/init/db"
|
||||
"github.com/1Panel-dev/1Panel/agent/init/hook"
|
||||
"github.com/1Panel-dev/1Panel/agent/init/log"
|
||||
@ -36,7 +36,6 @@ func Start() {
|
||||
gin.SetMode("debug")
|
||||
cron.Run()
|
||||
InitOthers()
|
||||
business.Init()
|
||||
hook.Init()
|
||||
|
||||
rootRouter := router.Routers()
|
||||
@ -75,6 +74,7 @@ func Start() {
|
||||
ClientAuth: tls.RequireAnyClientCert,
|
||||
}
|
||||
global.LOG.Info("listen at https://0.0.0.0:9999")
|
||||
business.Init()
|
||||
if err := server.ListenAndServeTLS("", ""); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
@ -251,4 +251,8 @@ export namespace App {
|
||||
appInstallID: number;
|
||||
updateVersion?: string;
|
||||
}
|
||||
|
||||
export interface AppStoreSync {
|
||||
taskID: string;
|
||||
}
|
||||
}
|
||||
|
@ -3,8 +3,8 @@ import { ResPage } from '../interface';
|
||||
import { App } from '../interface/app';
|
||||
import { TimeoutEnum } from '@/enums/http-enum';
|
||||
|
||||
export const SyncApp = () => {
|
||||
return http.post<any>('apps/sync', {});
|
||||
export const SyncApp = (req: App.AppStoreSync) => {
|
||||
return http.post('apps/sync', req);
|
||||
};
|
||||
|
||||
export const GetAppListUpdate = () => {
|
||||
|
@ -1857,7 +1857,7 @@ const message = {
|
||||
syncAllAppHelper: 'All applications are about to be synchronized. Do you want to continue? ',
|
||||
hostModeHelper:
|
||||
'The current application network mode is host mode. If you need to open the port, please open it manually on the firewall page.',
|
||||
showLocal: 'Show Local App',
|
||||
showLocal: 'Local',
|
||||
reload: 'Reload',
|
||||
upgradeWarn:
|
||||
'Upgrading the application will replace the docker-compose.yml file. If there are any changes, you can click to view the file comparison',
|
||||
@ -1877,7 +1877,7 @@ const message = {
|
||||
requireMemory: 'Memory',
|
||||
supportedArchitectures: 'Architectures',
|
||||
link: 'Link',
|
||||
showCurrentArch: 'Show current architecture App',
|
||||
showCurrentArch: 'Architecture',
|
||||
},
|
||||
website: {
|
||||
website: 'Website',
|
||||
|
@ -3,6 +3,7 @@ import i18n from '@/lang';
|
||||
import useClipboard from 'vue-clipboard3';
|
||||
const { toClipboard } = useClipboard();
|
||||
import { MsgError, MsgSuccess } from '@/utils/message';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
export function deepCopy<T>(obj: any): T {
|
||||
let newObj: any;
|
||||
@ -616,3 +617,7 @@ export const getFileType = (extension: string) => {
|
||||
});
|
||||
return type;
|
||||
};
|
||||
|
||||
export const newUUID = () => {
|
||||
return uuidv4();
|
||||
};
|
||||
|
@ -168,6 +168,7 @@
|
||||
</LayoutContent>
|
||||
<Install ref="installRef" />
|
||||
<Detail ref="detailRef" />
|
||||
<TaskLog ref="taskLogRef" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
@ -179,8 +180,9 @@ import Install from '../detail/install/index.vue';
|
||||
import router from '@/routers';
|
||||
import { MsgSuccess } from '@/utils/message';
|
||||
import { GlobalStore } from '@/store';
|
||||
import { getLanguage } from '@/utils/util';
|
||||
import { getLanguage, newUUID } from '@/utils/util';
|
||||
import Detail from '../detail/index.vue';
|
||||
import TaskLog from '@/components/task-log/index.vue';
|
||||
|
||||
const globalStore = GlobalStore();
|
||||
|
||||
@ -218,6 +220,7 @@ const installKey = ref('');
|
||||
const moreTag = ref('');
|
||||
const mainHeight = ref(0);
|
||||
const detailRef = ref();
|
||||
const taskLogRef = ref();
|
||||
|
||||
const search = async (req: App.AppReq) => {
|
||||
loading.value = true;
|
||||
@ -263,14 +266,23 @@ const openDetail = (key: string) => {
|
||||
detailRef.value.acceptParams(key, 'install');
|
||||
};
|
||||
|
||||
const openTaskLog = (taskID: string) => {
|
||||
taskLogRef.value.openWithTaskID(taskID);
|
||||
};
|
||||
|
||||
const sync = () => {
|
||||
syncing.value = true;
|
||||
SyncApp()
|
||||
const taskID = newUUID();
|
||||
const syncReq = {
|
||||
taskID: taskID,
|
||||
};
|
||||
SyncApp(syncReq)
|
||||
.then((res) => {
|
||||
if (res.message != '') {
|
||||
MsgSuccess(res.message);
|
||||
} else {
|
||||
MsgSuccess(i18n.global.t('app.syncStart'));
|
||||
openTaskLog(taskID);
|
||||
}
|
||||
canUpdate.value = false;
|
||||
search(req);
|
||||
|
@ -123,7 +123,7 @@ import { Container } from '@/api/interface/container';
|
||||
import { loadResourceLimit } from '@/api/modules/container';
|
||||
import CodemirrorPro from '@/components/codemirror-pro/index.vue';
|
||||
import TaskLog from '@/components/task-log/index.vue';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { newUUID } from '@/utils/util';
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
@ -269,7 +269,7 @@ const openTaskLog = (taskID: string) => {
|
||||
|
||||
const install = () => {
|
||||
loading.value = true;
|
||||
const taskID = uuidv4();
|
||||
const taskID = newUUID();
|
||||
req.taskID = taskID;
|
||||
InstallApp(req)
|
||||
.then(() => {
|
||||
|
Loading…
x
Reference in New Issue
Block a user