1
0
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:
zhengkunwang 2024-08-22 18:48:55 +08:00 committed by GitHub
parent cfe4fa7a92
commit 5865a958fc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 372 additions and 267 deletions

View File

@ -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

View File

@ -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()

View File

@ -57,3 +57,7 @@ type UpdateGroup struct {
Group uint `json:"group"`
NewGroup uint `json:"newGroup"`
}
type OperateWithTask struct {
TaskID string `json:"taskID"`
}

View File

@ -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
}

View File

@ -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()
}

View File

@ -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 (

View File

@ -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")

View File

@ -15,6 +15,7 @@ import (
var (
DB *gorm.DB
MonitorDB *gorm.DB
TaskDB *gorm.DB
CoreDB *gorm.DB
LOG *logrus.Logger
CONF configs.ServerConfig

View File

@ -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{})

View File

@ -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"

View File

@ -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: "同步"

View File

@ -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: "同步"

View File

@ -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
}

View File

@ -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 {

View File

@ -19,6 +19,7 @@ func Init() {
migrations.UpdateWebsite,
migrations.UpdateWebsiteDomain,
migrations.UpdateApp,
migrations.AddTaskDB,
})
if err := m.Migrate(); err != nil {
global.LOG.Error(err)

View File

@ -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{},
)
},
}

View File

@ -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)
}

View File

@ -251,4 +251,8 @@ export namespace App {
appInstallID: number;
updateVersion?: string;
}
export interface AppStoreSync {
taskID: string;
}
}

View File

@ -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 = () => {

View File

@ -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',

View File

@ -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();
};

View File

@ -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);

View File

@ -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(() => {