diff --git a/agent/app/api/v2/app.go b/agent/app/api/v2/app.go index bba3e87c3..b6431ee59 100644 --- a/agent/app/api/v2/app.go +++ b/agent/app/api/v2/app.go @@ -5,7 +5,6 @@ import ( "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" "github.com/1Panel-dev/1Panel/agent/i18n" "github.com/gin-gonic/gin" ) @@ -56,11 +55,10 @@ func (b *BaseApi) SyncApp(c *gin.Context) { } return } - go func() { - if err = appService.SyncAppListFromRemote(req.TaskID); err != nil { - global.LOG.Errorf("Synchronization with the App Store failed [%s]", err.Error()) - } - }() + if err = appService.SyncAppListFromRemote(req.TaskID); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) + return + } helper.SuccessWithData(c, nil) } diff --git a/agent/app/dto/app.go b/agent/app/dto/app.go index 0fe36618f..16bce0905 100644 --- a/agent/app/dto/app.go +++ b/agent/app/dto/app.go @@ -96,6 +96,7 @@ type AppProperty struct { Document string `json:"document"` Architectures []string `json:"architectures"` MemoryRequired int `json:"memoryRequired"` + GpuSupport bool `json:"gpuSupport"` } type AppConfigVersion struct { diff --git a/agent/app/dto/request/app.go b/agent/app/dto/request/app.go index 5593cb8cc..47b5fc0fb 100644 --- a/agent/app/dto/request/app.go +++ b/agent/app/dto/request/app.go @@ -36,6 +36,7 @@ type AppContainerConfig struct { DockerCompose string `json:"dockerCompose"` HostMode bool `json:"hostMode"` PullImage bool `json:"pullImage"` + GpuConfig bool `json:"gpuConfig"` } type AppInstalledSearch struct { diff --git a/agent/app/dto/response/app.go b/agent/app/dto/response/app.go index c0f0165c7..664e0061f 100644 --- a/agent/app/dto/response/app.go +++ b/agent/app/dto/response/app.go @@ -44,6 +44,7 @@ type AppDto struct { Tags []model.Tag `json:"tags"` Github string `json:"github"` Website string `json:"website"` + GpuSupport bool `json:"gpuSupport"` } type TagDTO struct { @@ -73,6 +74,7 @@ type AppDetailDTO struct { HostMode bool `json:"hostMode"` Architectures string `json:"architectures"` MemoryRequired int `json:"memoryRequired"` + GpuSupport bool `json:"gpuSupport"` } type IgnoredApp struct { diff --git a/agent/app/model/app.go b/agent/app/model/app.go index 695007f56..071bb5b9d 100644 --- a/agent/app/model/app.go +++ b/agent/app/model/app.go @@ -28,6 +28,7 @@ type App struct { LastModified int `json:"lastModified"` Architectures string `json:"architectures"` MemoryRequired int `json:"memoryRequired"` + GpuSupport bool `json:"gpuSupport"` Details []AppDetail `json:"-" gorm:"-:migration"` TagsKey []string `json:"tags" yaml:"tags" gorm:"-"` diff --git a/agent/app/service/app.go b/agent/app/service/app.go index 9249507fe..d2f3ff9c0 100644 --- a/agent/app/service/app.go +++ b/agent/app/service/app.go @@ -111,6 +111,7 @@ func (a AppService) PageApp(req request.AppSearch) (interface{}, error) { Limit: ap.Limit, Website: ap.Website, Github: ap.Github, + GpuSupport: ap.GpuSupport, } appDTOs = append(appDTOs, appDTO) appTags, err := appTagRepo.GetByAppId(ap.ID) @@ -263,7 +264,8 @@ func (a AppService) GetAppDetail(appID uint, version, appType string) (response. appDetailDTO.Enable = false } appDetailDTO.Architectures = app.Architectures - appDetailDTO.MemoryLimit = app.MemoryLimit + appDetailDTO.MemoryRequired = app.MemoryRequired + appDetailDTO.GpuSupport = app.GpuSupport return appDetailDTO, nil } func (a AppService) GetAppDetailByID(id uint) (*response.AppDetailDTO, error) { @@ -1057,5 +1059,11 @@ func (a AppService) SyncAppListFromRemote(taskID string) (err error) { return nil }, nil) - return syncTask.Execute() + go func() { + if err = syncTask.Execute(); err != nil { + _ = NewISettingService().Update("AppStoreSyncStatus", constant.Error) + } + }() + + return nil } diff --git a/agent/app/service/app_utils.go b/agent/app/service/app_utils.go index 21bfdf2ba..918b798f9 100644 --- a/agent/app/service/app_utils.go +++ b/agent/app/service/app_utils.go @@ -1120,6 +1120,7 @@ func getApps(oldApps []model.App, items []dto.AppDefine) map[string]model.App { app.ReadMe = item.ReadMe app.MemoryRequired = config.MemoryRequired app.Architectures = strings.Join(config.Architectures, ",") + app.GpuSupport = config.GpuSupport apps[key] = app } return apps @@ -1509,6 +1510,17 @@ func addDockerComposeCommonParam(composeMap map[string]interface{}, serviceName "cpus": "${CPUS}", "memory": "${MEMORY_LIMIT}", } + if req.GpuConfig { + resource["reservations"] = map[string]interface{}{ + "devices": []map[string]interface{}{ + { + "driver": "nvidia", + "count": "all", + "capabilities": []string{"gpu"}, + }, + }, + } + } deploy["resources"] = resource serviceValue["deploy"] = deploy diff --git a/agent/init/migration/migrations/init.go b/agent/init/migration/migrations/init.go index dc5e9fdd7..e48c41285 100644 --- a/agent/init/migration/migrations/init.go +++ b/agent/init/migration/migrations/init.go @@ -245,7 +245,7 @@ var AddTaskDB = &gormigrate.Migration{ } var UpdateApp = &gormigrate.Migration{ - ID: "20240823-update-app", + ID: "20240826-update-app", Migrate: func(tx *gorm.DB) error { return tx.AutoMigrate( &model.App{}) diff --git a/frontend/src/api/interface/app.ts b/frontend/src/api/interface/app.ts index 9f00e30e9..699961bbb 100644 --- a/frontend/src/api/interface/app.ts +++ b/frontend/src/api/interface/app.ts @@ -51,6 +51,7 @@ export namespace App { hostMode?: boolean; memoryRequired: number; architectures: string; + gpuSupport: boolean; } export interface AppReq extends ReqPage { diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts index ba6e3be51..c5ae5412f 100644 --- a/frontend/src/lang/modules/en.ts +++ b/frontend/src/lang/modules/en.ts @@ -1880,6 +1880,9 @@ const message = { showCurrentArch: 'Architecture', syncLocalApp: 'Sync Local App', memoryRequiredHelper: 'Current application memory requirement {0}', + gpuConfig: 'Enable GPU Support', + gpuConfigHelper: + 'Please ensure the machine has an NVIDIA GPU and that NVIDIA drivers and the NVIDIA Docker Container Toolkit are installed', }, website: { website: 'Website', diff --git a/frontend/src/lang/modules/tw.ts b/frontend/src/lang/modules/tw.ts index ca541209d..86f9498a0 100644 --- a/frontend/src/lang/modules/tw.ts +++ b/frontend/src/lang/modules/tw.ts @@ -1746,6 +1746,8 @@ const message = { showCurrentArch: '僅顯示當前架構', syncLocalApp: '同步本地應用', memoryRequiredHelper: '目前應用記憶體需求 {0}', + gpuConfig: '開啟 GPU 支援', + gpuConfigHelper: '請確保機器有 NVIDIA GPU 並且安裝 NVIDIA 驅動 和 NVIDIA docker Container Toolkit', }, website: { website: '網站', diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index a87c851cd..e3975ef4f 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -1747,6 +1747,8 @@ const message = { showCurrentArch: '仅显示当前服务器架构应用', syncLocalApp: '同步本地应用', memoryRequiredHelper: '当前应用内存需求 {0}', + gpuConfig: '开启 GPU 支持', + gpuConfigHelper: '请确保机器有 NVIDIA GPU 并且安装 NVIDIA 驱动 和 NVIDIA docker Container Toolkit', }, website: { website: '网站', diff --git a/frontend/src/views/app-store/detail/install/index.vue b/frontend/src/views/app-store/detail/install/index.vue index 79ed369d4..731664575 100644 --- a/frontend/src/views/app-store/detail/install/index.vue +++ b/frontend/src/views/app-store/detail/install/index.vue @@ -90,6 +90,10 @@ {{ $t('container.limitHelper', [limits.memory]) }}{{ req.memoryUnit }}B + + + {{ $t('app.gpuConfigHelper') }} + {{ $t('container.forcePullHelper') }} @@ -170,6 +174,7 @@ const initData = () => ({ appID: '', pullImage: true, taskID: '', + gpuConfig: false, }); const req = reactive(initData()); const limits = ref({ @@ -189,6 +194,7 @@ const paramKey = ref(1); const isHostMode = ref(false); const taskLogRef = ref(); const memoryRequired = ref(0); +const gpuSupport = ref(false); const changeUnit = () => { if (req.memoryUnit == 'M') { @@ -237,6 +243,7 @@ const getAppDetail = async (version: string) => { installData.value.params = res.data.params; paramKey.value++; memoryRequired.value = res.data.memoryRequired; + gpuSupport.value = res.data.gpuSupport; } catch (error) { } finally { loading.value = false;