mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-10-09 15:06:37 +08:00
feat: 增加同步本地应用接口 (#6217)
This commit is contained in:
parent
5865a958fc
commit
034645f50c
11 changed files with 266 additions and 222 deletions
|
@ -72,7 +72,11 @@ func (b *BaseApi) SyncApp(c *gin.Context) {
|
||||||
// @Router /apps/sync/local [post]
|
// @Router /apps/sync/local [post]
|
||||||
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"应用商店同步","formatEN":"App store synchronization"}
|
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"应用商店同步","formatEN":"App store synchronization"}
|
||||||
func (b *BaseApi) SyncLocalApp(c *gin.Context) {
|
func (b *BaseApi) SyncLocalApp(c *gin.Context) {
|
||||||
go appService.SyncAppListFromLocal()
|
var req dto.OperateWithTask
|
||||||
|
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
go appService.SyncAppListFromLocal(req.TaskID)
|
||||||
helper.SuccessWithOutData(c)
|
helper.SuccessWithOutData(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -43,7 +43,7 @@ type IAppService interface {
|
||||||
SyncAppListFromRemote(taskID string) error
|
SyncAppListFromRemote(taskID string) error
|
||||||
GetAppUpdate() (*response.AppUpdateRes, error)
|
GetAppUpdate() (*response.AppUpdateRes, error)
|
||||||
GetAppDetailByID(id uint) (*response.AppDetailDTO, error)
|
GetAppDetailByID(id uint) (*response.AppDetailDTO, error)
|
||||||
SyncAppListFromLocal()
|
SyncAppListFromLocal(taskID string)
|
||||||
GetIgnoredApp() ([]response.IgnoredApp, error)
|
GetIgnoredApp() ([]response.IgnoredApp, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -489,25 +489,25 @@ func (a AppService) Install(req request.AppInstallCreate) (appInstall *model.App
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a AppService) SyncAppListFromLocal() {
|
func (a AppService) SyncAppListFromLocal(TaskID string) {
|
||||||
fileOp := files.NewFileOp()
|
|
||||||
localAppDir := constant.LocalAppResourceDir
|
|
||||||
if !fileOp.Stat(localAppDir) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
dirEntries []os.DirEntry
|
dirEntries []os.DirEntry
|
||||||
localApps []model.App
|
localApps []model.App
|
||||||
)
|
)
|
||||||
|
|
||||||
defer func() {
|
syncTask, err := task.NewTaskWithOps(i18n.GetMsgByKey("LocalApp"), task.TaskSync, task.TaskScopeAppStore, TaskID, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
global.LOG.Errorf("Sync local app failed %v", err)
|
global.LOG.Errorf("Create sync task failed %v", err)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}()
|
|
||||||
|
|
||||||
global.LOG.Infof("Starting local application synchronization ...")
|
syncTask.AddSubTask(task.GetTaskName(i18n.GetMsgByKey("LocalApp"), task.TaskSync, task.TaskScopeAppStore), func(t *task.Task) (err error) {
|
||||||
|
fileOp := files.NewFileOp()
|
||||||
|
localAppDir := constant.LocalAppResourceDir
|
||||||
|
if !fileOp.Stat(localAppDir) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
dirEntries, err = os.ReadDir(localAppDir)
|
dirEntries, err = os.ReadDir(localAppDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
|
@ -517,12 +517,12 @@ func (a AppService) SyncAppListFromLocal() {
|
||||||
appDir := filepath.Join(localAppDir, dirEntry.Name())
|
appDir := filepath.Join(localAppDir, dirEntry.Name())
|
||||||
appDirEntries, err := os.ReadDir(appDir)
|
appDirEntries, err := os.ReadDir(appDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
global.LOG.Errorf(i18n.GetMsgWithMap("ErrAppDirNull", map[string]interface{}{"name": dirEntry.Name(), "err": err.Error()}))
|
t.Log(i18n.GetWithNameAndErr("ErrAppDirNull", dirEntry.Name(), err))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
app, err := handleLocalApp(appDir)
|
app, err := handleLocalApp(appDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
global.LOG.Errorf(i18n.GetMsgWithMap("LocalAppErr", map[string]interface{}{"name": dirEntry.Name(), "err": err.Error()}))
|
t.Log(i18n.GetWithNameAndErr("LocalAppErr", dirEntry.Name(), err))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
var appDetails []model.AppDetail
|
var appDetails []model.AppDetail
|
||||||
|
@ -534,7 +534,7 @@ func (a AppService) SyncAppListFromLocal() {
|
||||||
}
|
}
|
||||||
versionDir := filepath.Join(appDir, appDirEntry.Name())
|
versionDir := filepath.Join(appDir, appDirEntry.Name())
|
||||||
if err = handleLocalAppDetail(versionDir, &appDetail); err != nil {
|
if err = handleLocalAppDetail(versionDir, &appDetail); err != nil {
|
||||||
global.LOG.Errorf(i18n.GetMsgWithMap("LocalAppVersionErr", map[string]interface{}{"name": app.Name, "version": appDetail.Version, "err": err.Error()}))
|
t.Log(i18n.GetMsgWithMap("LocalAppVersionErr", map[string]interface{}{"name": app.Name, "version": appDetail.Version, "err": err.Error()}))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
appDetails = append(appDetails, appDetail)
|
appDetails = append(appDetails, appDetail)
|
||||||
|
@ -544,7 +544,7 @@ func (a AppService) SyncAppListFromLocal() {
|
||||||
app.Details = appDetails
|
app.Details = appDetails
|
||||||
localApps = append(localApps, *app)
|
localApps = append(localApps, *app)
|
||||||
} else {
|
} else {
|
||||||
global.LOG.Errorf(i18n.GetMsgWithMap("LocalAppVersionNull", map[string]interface{}{"name": app.Name}))
|
t.Log(i18n.GetWithName("LocalAppVersionNull", app.Name))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -714,6 +714,11 @@ func (a AppService) SyncAppListFromLocal() {
|
||||||
}
|
}
|
||||||
tx.Commit()
|
tx.Commit()
|
||||||
global.LOG.Infof("Synchronization of local applications completed")
|
global.LOG.Infof("Synchronization of local applications completed")
|
||||||
|
return nil
|
||||||
|
}, nil)
|
||||||
|
go func() {
|
||||||
|
_ = syncTask.Execute()
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a AppService) GetAppUpdate() (*response.AppUpdateRes, error) {
|
func (a AppService) GetAppUpdate() (*response.AppUpdateRes, error) {
|
||||||
|
@ -941,24 +946,31 @@ func (a AppService) SyncAppListFromRemote(taskID string) (err error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tx, ctx := getTxAndContext()
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}()
|
||||||
if len(addAppArray) > 0 {
|
if len(addAppArray) > 0 {
|
||||||
if err = appRepo.BatchCreate(context.Background(), addAppArray); err != nil {
|
if err = appRepo.BatchCreate(ctx, addAppArray); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(deleteAppArray) > 0 {
|
if len(deleteAppArray) > 0 {
|
||||||
if err = appRepo.BatchDelete(context.Background(), deleteAppArray); err != nil {
|
if err = appRepo.BatchDelete(ctx, deleteAppArray); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err = appDetailRepo.DeleteByAppIds(context.Background(), deleteIds); err != nil {
|
if err = appDetailRepo.DeleteByAppIds(ctx, deleteIds); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err = tagRepo.DeleteAll(context.Background()); err != nil {
|
if err = tagRepo.DeleteAll(ctx); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if len(tags) > 0 {
|
if len(tags) > 0 {
|
||||||
if err = tagRepo.BatchCreate(context.Background(), tags); err != nil {
|
if err = tagRepo.BatchCreate(ctx, tags); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for _, tag := range tags {
|
for _, tag := range tags {
|
||||||
|
@ -966,7 +978,7 @@ func (a AppService) SyncAppListFromRemote(taskID string) (err error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, update := range updateAppArray {
|
for _, update := range updateAppArray {
|
||||||
if err = appRepo.Save(context.Background(), &update); err != nil {
|
if err = appRepo.Save(ctx, &update); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1011,32 +1023,33 @@ func (a AppService) SyncAppListFromRemote(taskID string) (err error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(addDetails) > 0 {
|
if len(addDetails) > 0 {
|
||||||
if err = appDetailRepo.BatchCreate(context.Background(), addDetails); err != nil {
|
if err = appDetailRepo.BatchCreate(ctx, addDetails); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(deleteDetails) > 0 {
|
if len(deleteDetails) > 0 {
|
||||||
if err = appDetailRepo.BatchDelete(context.Background(), deleteDetails); err != nil {
|
if err = appDetailRepo.BatchDelete(ctx, deleteDetails); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, u := range updateDetails {
|
for _, u := range updateDetails {
|
||||||
if err = appDetailRepo.Update(context.Background(), u); err != nil {
|
if err = appDetailRepo.Update(ctx, u); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(oldAppIds) > 0 {
|
if len(oldAppIds) > 0 {
|
||||||
if err = appTagRepo.DeleteByAppIds(context.Background(), oldAppIds); err != nil {
|
if err = appTagRepo.DeleteByAppIds(ctx, oldAppIds); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(appTags) > 0 {
|
if len(appTags) > 0 {
|
||||||
if err = appTagRepo.BatchCreate(context.Background(), appTags); err != nil {
|
if err = appTagRepo.BatchCreate(ctx, appTags); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
tx.Commit()
|
||||||
|
|
||||||
_ = settingService.Update("AppStoreSyncStatus", constant.SyncSuccess)
|
_ = settingService.Update("AppStoreSyncStatus", constant.SyncSuccess)
|
||||||
_ = settingService.Update("AppStoreLastModified", strconv.Itoa(list.LastModified))
|
_ = settingService.Update("AppStoreLastModified", strconv.Itoa(list.LastModified))
|
||||||
|
|
|
@ -235,4 +235,5 @@ AppLink: 'Associated Application'
|
||||||
EnableSSL: "Enable HTTPS"
|
EnableSSL: "Enable HTTPS"
|
||||||
AppStore: "App Store"
|
AppStore: "App Store"
|
||||||
TaskSync: "Sync"
|
TaskSync: "Sync"
|
||||||
|
LocalApp: "Local App"
|
||||||
|
|
||||||
|
|
|
@ -237,4 +237,5 @@ AppLink: '關聯應用'
|
||||||
EnableSSL: "開啟 HTTPS"
|
EnableSSL: "開啟 HTTPS"
|
||||||
AppStore: "應用商店"
|
AppStore: "應用商店"
|
||||||
TaskSync: "同步"
|
TaskSync: "同步"
|
||||||
|
LocalApp: "本地應用"
|
||||||
|
|
||||||
|
|
|
@ -238,3 +238,4 @@ AppLink: "关联应用"
|
||||||
EnableSSL: "开启 HTTPS"
|
EnableSSL: "开启 HTTPS"
|
||||||
AppStore: "应用商店"
|
AppStore: "应用商店"
|
||||||
TaskSync: "同步"
|
TaskSync: "同步"
|
||||||
|
LocalApp: "本地应用"
|
|
@ -13,7 +13,8 @@ func (a *AppRouter) InitRouter(Router *gin.RouterGroup) {
|
||||||
|
|
||||||
baseApi := v2.ApiGroupApp.BaseApi
|
baseApi := v2.ApiGroupApp.BaseApi
|
||||||
{
|
{
|
||||||
appRouter.POST("/sync", baseApi.SyncApp)
|
appRouter.POST("/sync/remote", baseApi.SyncApp)
|
||||||
|
appRouter.POST("/sync/local", baseApi.SyncLocalApp)
|
||||||
appRouter.GET("/checkupdate", baseApi.GetAppListUpdate)
|
appRouter.GET("/checkupdate", baseApi.GetAppListUpdate)
|
||||||
appRouter.POST("/search", baseApi.SearchApp)
|
appRouter.POST("/search", baseApi.SearchApp)
|
||||||
appRouter.GET("/:key", baseApi.GetApp)
|
appRouter.GET("/:key", baseApi.GetApp)
|
||||||
|
|
|
@ -4,7 +4,11 @@ import { App } from '../interface/app';
|
||||||
import { TimeoutEnum } from '@/enums/http-enum';
|
import { TimeoutEnum } from '@/enums/http-enum';
|
||||||
|
|
||||||
export const SyncApp = (req: App.AppStoreSync) => {
|
export const SyncApp = (req: App.AppStoreSync) => {
|
||||||
return http.post('apps/sync', req);
|
return http.post('apps/sync/remote', req);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const SyncLocalApp = (req: App.AppStoreSync) => {
|
||||||
|
return http.post('apps/sync/local', req);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const GetAppListUpdate = () => {
|
export const GetAppListUpdate = () => {
|
||||||
|
|
|
@ -1878,6 +1878,7 @@ const message = {
|
||||||
supportedArchitectures: 'Architectures',
|
supportedArchitectures: 'Architectures',
|
||||||
link: 'Link',
|
link: 'Link',
|
||||||
showCurrentArch: 'Architecture',
|
showCurrentArch: 'Architecture',
|
||||||
|
syncLocalApp: 'Sync Local App',
|
||||||
},
|
},
|
||||||
website: {
|
website: {
|
||||||
website: 'Website',
|
website: 'Website',
|
||||||
|
|
|
@ -1744,6 +1744,7 @@ const message = {
|
||||||
supportedArchitectures: '支持架構',
|
supportedArchitectures: '支持架構',
|
||||||
link: '鏈接',
|
link: '鏈接',
|
||||||
showCurrentArch: '僅顯示當前架構',
|
showCurrentArch: '僅顯示當前架構',
|
||||||
|
syncLocalApp: '同步本地應用',
|
||||||
},
|
},
|
||||||
website: {
|
website: {
|
||||||
website: '網站',
|
website: '網站',
|
||||||
|
|
|
@ -1686,7 +1686,7 @@ const message = {
|
||||||
noService: '无{0}',
|
noService: '无{0}',
|
||||||
toInstall: '去安装',
|
toInstall: '去安装',
|
||||||
param: '参数配置',
|
param: '参数配置',
|
||||||
syncAppList: '更新应用列表',
|
syncAppList: '更新远程应用',
|
||||||
alreadyRun: '已安装',
|
alreadyRun: '已安装',
|
||||||
less1Minute: '小于1分钟',
|
less1Minute: '小于1分钟',
|
||||||
appOfficeWebsite: '官方网站',
|
appOfficeWebsite: '官方网站',
|
||||||
|
@ -1745,6 +1745,7 @@ const message = {
|
||||||
supportedArchitectures: '支持架构',
|
supportedArchitectures: '支持架构',
|
||||||
link: '链接',
|
link: '链接',
|
||||||
showCurrentArch: '仅显示当前服务器架构应用',
|
showCurrentArch: '仅显示当前服务器架构应用',
|
||||||
|
syncLocalApp: '同步本地应用',
|
||||||
},
|
},
|
||||||
website: {
|
website: {
|
||||||
website: '网站',
|
website: '网站',
|
||||||
|
|
|
@ -53,11 +53,12 @@
|
||||||
</el-row>
|
</el-row>
|
||||||
</template>
|
</template>
|
||||||
<template #leftToolBar>
|
<template #leftToolBar>
|
||||||
<el-badge is-dot :hidden="!canUpdate">
|
|
||||||
<el-button @click="sync" type="primary" plain :disabled="syncing">
|
<el-button @click="sync" type="primary" plain :disabled="syncing">
|
||||||
{{ $t('app.syncAppList') }}
|
{{ $t('app.syncAppList') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</el-badge>
|
<el-button @click="syncLocal" type="primary" plain :disabled="syncing" class="ml-2">
|
||||||
|
{{ $t('app.syncLocalApp') }}
|
||||||
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
<template #rightToolBar>
|
<template #rightToolBar>
|
||||||
<el-checkbox class="!mr-2.5" v-model="req.showCurrentArch" @change="search(req)">
|
<el-checkbox class="!mr-2.5" v-model="req.showCurrentArch" @change="search(req)">
|
||||||
|
@ -174,8 +175,7 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { App } from '@/api/interface/app';
|
import { App } from '@/api/interface/app';
|
||||||
import { onMounted, reactive, ref, computed } from 'vue';
|
import { onMounted, reactive, ref, computed } from 'vue';
|
||||||
import { GetAppTags, SearchApp, SyncApp } from '@/api/modules/app';
|
import { GetAppTags, SearchApp, SyncApp, SyncLocalApp } from '@/api/modules/app';
|
||||||
import i18n from '@/lang';
|
|
||||||
import Install from '../detail/install/index.vue';
|
import Install from '../detail/install/index.vue';
|
||||||
import router from '@/routers';
|
import router from '@/routers';
|
||||||
import { MsgSuccess } from '@/utils/message';
|
import { MsgSuccess } from '@/utils/message';
|
||||||
|
@ -281,7 +281,6 @@ const sync = () => {
|
||||||
if (res.message != '') {
|
if (res.message != '') {
|
||||||
MsgSuccess(res.message);
|
MsgSuccess(res.message);
|
||||||
} else {
|
} else {
|
||||||
MsgSuccess(i18n.global.t('app.syncStart'));
|
|
||||||
openTaskLog(taskID);
|
openTaskLog(taskID);
|
||||||
}
|
}
|
||||||
canUpdate.value = false;
|
canUpdate.value = false;
|
||||||
|
@ -292,6 +291,23 @@ const sync = () => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const syncLocal = () => {
|
||||||
|
const taskID = newUUID();
|
||||||
|
const syncReq = {
|
||||||
|
taskID: taskID,
|
||||||
|
};
|
||||||
|
syncing.value = true;
|
||||||
|
SyncLocalApp(syncReq)
|
||||||
|
.then(() => {
|
||||||
|
openTaskLog(taskID);
|
||||||
|
canUpdate.value = false;
|
||||||
|
search(req);
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
syncing.value = false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const changeTag = (key: string) => {
|
const changeTag = (key: string) => {
|
||||||
req.tags = [];
|
req.tags = [];
|
||||||
activeTag.value = key;
|
activeTag.value = key;
|
||||||
|
|
Loading…
Add table
Reference in a new issue