From a4bd9362edd140f6e70ce1fc4afec1298e08f26c Mon Sep 17 00:00:00 2001 From: zhengkunwang <31820853+zhengkunwang223@users.noreply.github.com> Date: Mon, 9 Oct 2023 06:36:32 -0500 Subject: [PATCH] =?UTF-8?q?feat:=20node.js=20=E8=BF=90=E8=A1=8C=E7=8E=AF?= =?UTF-8?q?=E5=A2=83=E5=A2=9E=E5=8A=A0=E6=A8=A1=E5=9D=97=E5=88=97=E8=A1=A8?= =?UTF-8?q?=20(#2482)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app/api/v1/runtime.go | 22 ++++++ backend/app/dto/request/runtime.go | 6 ++ backend/app/dto/response/runtime.go | 7 ++ backend/app/service/app.go | 3 +- backend/app/service/runtime.go | 35 ++++++++++ backend/i18n/lang/en.yaml | 1 + backend/i18n/lang/zh-Hant.yaml | 1 + backend/i18n/lang/zh.yaml | 1 + backend/router/ro_runtime.go | 1 + cmd/server/docs/docs.go | 54 ++++++++++++++- cmd/server/docs/swagger.json | 50 ++++++++++++++ cmd/server/docs/swagger.yaml | 31 +++++++++ frontend/src/api/interface/runtime.ts | 10 +++ frontend/src/api/modules/runtime.ts | 4 ++ frontend/src/lang/modules/en.ts | 2 + frontend/src/lang/modules/tw.ts | 8 ++- frontend/src/lang/modules/zh.ts | 2 + .../src/views/website/runtime/node/index.vue | 18 ++++- .../website/runtime/node/module/index.vue | 68 +++++++++++++++++++ 19 files changed, 316 insertions(+), 8 deletions(-) create mode 100644 frontend/src/views/website/runtime/node/module/index.vue diff --git a/backend/app/api/v1/runtime.go b/backend/app/api/v1/runtime.go index 4da4cd485..d5af2a1b2 100644 --- a/backend/app/api/v1/runtime.go +++ b/backend/app/api/v1/runtime.go @@ -166,3 +166,25 @@ func (b *BaseApi) OperateRuntime(c *gin.Context) { } helper.SuccessWithOutData(c) } + +// @Tags Runtime +// @Summary Get Node modules +// @Description 获取 Node 项目的 modules +// @Accept json +// @Param request body request.NodeModuleReq true "request" +// @Success 200 +// @Security ApiKeyAuth +// @Router /runtimes/node/modules [post] +func (b *BaseApi) GetNodeModules(c *gin.Context) { + var req request.NodeModuleReq + if err := c.ShouldBindJSON(&req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) + return + } + res, err := runtimeService.GetNodeModules(req) + if err != nil { + helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) + return + } + helper.SuccessWithData(c, res) +} diff --git a/backend/app/dto/request/runtime.go b/backend/app/dto/request/runtime.go index 7838e58da..16aa5b3e7 100644 --- a/backend/app/dto/request/runtime.go +++ b/backend/app/dto/request/runtime.go @@ -53,3 +53,9 @@ type RuntimeOperate struct { Operate string `json:"operate"` ID uint `json:"ID"` } + +type NodeModuleReq struct { + Operate string `json:"operate"` + ID uint `json:"ID"` + Module string `json:"module"` +} diff --git a/backend/app/dto/response/runtime.go b/backend/app/dto/response/runtime.go index 09cb251b9..258168613 100644 --- a/backend/app/dto/response/runtime.go +++ b/backend/app/dto/response/runtime.go @@ -47,3 +47,10 @@ func NewRuntimeDTO(runtime model.Runtime) RuntimeDTO { Path: runtime.GetPath(), } } + +type NodeModule struct { + Name string `json:"name"` + Version string `json:"version"` + License string `json:"license"` + Description string `json:"description"` +} diff --git a/backend/app/service/app.go b/backend/app/service/app.go index e09a0e867..c9c28a7d9 100644 --- a/backend/app/service/app.go +++ b/backend/app/service/app.go @@ -699,8 +699,7 @@ func (a AppService) GetAppUpdate() (*response.AppUpdateRes, error) { res.CanUpdate = true return res, err } - list := &dto.AppList{} - list, err = getAppList() + list, err := getAppList() if err != nil { return res, err } diff --git a/backend/app/service/runtime.go b/backend/app/service/runtime.go index 55bd476c7..239d00fe1 100644 --- a/backend/app/service/runtime.go +++ b/backend/app/service/runtime.go @@ -16,6 +16,7 @@ import ( "github.com/1Panel-dev/1Panel/backend/utils/files" "github.com/pkg/errors" "github.com/subosito/gotenv" + "os" "path" "strconv" "strings" @@ -32,6 +33,7 @@ type IRuntimeService interface { Get(id uint) (res *response.RuntimeDTO, err error) GetNodePackageRunScript(req request.NodePackageReq) ([]response.PackageScripts, error) OperateRuntime(req request.RuntimeOperate) error + GetNodeModules(req request.NodeModuleReq) ([]response.NodeModule, error) } func NewRuntimeService() IRuntimeService { @@ -442,3 +444,36 @@ func (r *RuntimeService) OperateRuntime(req request.RuntimeOperate) error { } return runtimeRepo.Save(runtime) } + +func (r *RuntimeService) GetNodeModules(req request.NodeModuleReq) ([]response.NodeModule, error) { + runtime, err := runtimeRepo.GetFirst(commonRepo.WithByID(req.ID)) + if err != nil { + return nil, err + } + var res []response.NodeModule + nodeModulesPath := path.Join(runtime.CodeDir, "node_modules") + fileOp := files.NewFileOp() + if !fileOp.Stat(nodeModulesPath) { + return nil, buserr.New("ErrNodeModulesNotFound") + } + moduleDirs, err := os.ReadDir(nodeModulesPath) + if err != nil { + return nil, err + } + for _, moduleDir := range moduleDirs { + packagePath := path.Join(nodeModulesPath, moduleDir.Name(), "package.json") + if !fileOp.Stat(packagePath) { + continue + } + content, err := fileOp.GetContent(packagePath) + if err != nil { + continue + } + module := response.NodeModule{} + if err := json.Unmarshal(content, &module); err != nil { + continue + } + res = append(res, module) + } + return res, nil +} diff --git a/backend/i18n/lang/en.yaml b/backend/i18n/lang/en.yaml index 6f96b2841..707223070 100644 --- a/backend/i18n/lang/en.yaml +++ b/backend/i18n/lang/en.yaml @@ -107,6 +107,7 @@ ErrRuntimeStart: "Failed to start" ErrPackageJsonNotFound: "package.json file does not exist" ErrScriptsNotFound: "No scripts configuration item was found in package.json" ErrContainerNameNotFound: "Unable to get container name, please check .env file" +ErrNodeModulesNotFound: "The node_modules folder does not exist! Please edit the running environment or wait for the running environment to start successfully" #setting ErrBackupInUsed: "The backup account is already being used in a cronjob and cannot be deleted." diff --git a/backend/i18n/lang/zh-Hant.yaml b/backend/i18n/lang/zh-Hant.yaml index 2a36cd4b9..bae9f92e3 100644 --- a/backend/i18n/lang/zh-Hant.yaml +++ b/backend/i18n/lang/zh-Hant.yaml @@ -107,6 +107,7 @@ ErrRuntimeStart: "啟動失敗" ErrPackageJsonNotFound: "package.json 文件不存在" ErrScriptsNotFound: "沒有在 package.json 中找到 scripts 配置項" ErrContainerNameNotFound: "無法取得容器名稱,請檢查 .env 文件" +ErrNodeModulesNotFound: "node_modules 文件夾不存在!請編輯運行環境或者等待運行環境啟動成功" #setting ErrBackupInUsed: "該備份帳號已在計劃任務中使用,無法刪除" diff --git a/backend/i18n/lang/zh.yaml b/backend/i18n/lang/zh.yaml index 8184db9fb..5fc71d407 100644 --- a/backend/i18n/lang/zh.yaml +++ b/backend/i18n/lang/zh.yaml @@ -107,6 +107,7 @@ ErrRuntimeStart: "启动失败" ErrPackageJsonNotFound: "package.json 文件不存在" ErrScriptsNotFound: "没有在 package.json 中找到 scripts 配置项" ErrContainerNameNotFound: "无法获取容器名称,请检查 .env 文件" +ErrNodeModulesNotFound: "node_modules 文件夹不存在!请编辑运行环境或者等待运行环境启动成功" #setting ErrBackupInUsed: "该备份账号已在计划任务中使用,无法删除" diff --git a/backend/router/ro_runtime.go b/backend/router/ro_runtime.go index d3a331480..48a6da4a3 100644 --- a/backend/router/ro_runtime.go +++ b/backend/router/ro_runtime.go @@ -22,6 +22,7 @@ func (r *RuntimeRouter) InitRuntimeRouter(Router *gin.RouterGroup) { groupRouter.GET("/:id", baseApi.GetRuntime) groupRouter.POST("/node/package", baseApi.GetNodePackageRunScript) groupRouter.POST("/operate", baseApi.OperateRuntime) + groupRouter.POST("/node/modules", baseApi.GetNodeModules) } } diff --git a/cmd/server/docs/docs.go b/cmd/server/docs/docs.go index 995d2103e..233324dff 100644 --- a/cmd/server/docs/docs.go +++ b/cmd/server/docs/docs.go @@ -1,5 +1,5 @@ -// Code generated by swaggo/swag. DO NOT EDIT. - +// Package docs GENERATED BY SWAG; DO NOT EDIT +// This file was generated by swaggo/swag package docs import "github.com/swaggo/swag" @@ -7910,6 +7910,39 @@ const docTemplate = `{ } } }, + "/runtimes/node/modules": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "获取 Node 项目的 modules", + "consumes": [ + "application/json" + ], + "tags": [ + "Runtime" + ], + "summary": "Get Node modules", + "parameters": [ + { + "description": "request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.NodeModuleReq" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, "/runtimes/node/package": { "post": { "security": [ @@ -15568,6 +15601,9 @@ const docTemplate = `{ "name": { "type": "string" }, + "sort": { + "type": "integer" + }, "updatedAt": { "type": "string" } @@ -16653,6 +16689,20 @@ const docTemplate = `{ } } }, + "request.NodeModuleReq": { + "type": "object", + "properties": { + "ID": { + "type": "integer" + }, + "module": { + "type": "string" + }, + "operate": { + "type": "string" + } + } + }, "request.NodePackageReq": { "type": "object", "properties": { diff --git a/cmd/server/docs/swagger.json b/cmd/server/docs/swagger.json index 63ba15e56..c0203fd6f 100644 --- a/cmd/server/docs/swagger.json +++ b/cmd/server/docs/swagger.json @@ -7903,6 +7903,39 @@ } } }, + "/runtimes/node/modules": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "获取 Node 项目的 modules", + "consumes": [ + "application/json" + ], + "tags": [ + "Runtime" + ], + "summary": "Get Node modules", + "parameters": [ + { + "description": "request", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.NodeModuleReq" + } + } + ], + "responses": { + "200": { + "description": "OK" + } + } + } + }, "/runtimes/node/package": { "post": { "security": [ @@ -15561,6 +15594,9 @@ "name": { "type": "string" }, + "sort": { + "type": "integer" + }, "updatedAt": { "type": "string" } @@ -16646,6 +16682,20 @@ } } }, + "request.NodeModuleReq": { + "type": "object", + "properties": { + "ID": { + "type": "integer" + }, + "module": { + "type": "string" + }, + "operate": { + "type": "string" + } + } + }, "request.NodePackageReq": { "type": "object", "properties": { diff --git a/cmd/server/docs/swagger.yaml b/cmd/server/docs/swagger.yaml index 388261960..3875d9e48 100644 --- a/cmd/server/docs/swagger.yaml +++ b/cmd/server/docs/swagger.yaml @@ -2402,6 +2402,8 @@ definitions: type: string name: type: string + sort: + type: integer updatedAt: type: string type: object @@ -3129,6 +3131,15 @@ definitions: required: - scope type: object + request.NodeModuleReq: + properties: + ID: + type: integer + module: + type: string + operate: + type: string + type: object request.NodePackageReq: properties: codeDir: @@ -9161,6 +9172,26 @@ paths: formatEN: Delete website [name] formatZH: 删除网站 [name] paramKeys: [] + /runtimes/node/modules: + post: + consumes: + - application/json + description: 获取 Node 项目的 modules + parameters: + - description: request + in: body + name: request + required: true + schema: + $ref: '#/definitions/request.NodeModuleReq' + responses: + "200": + description: OK + security: + - ApiKeyAuth: [] + summary: Get Node modules + tags: + - Runtime /runtimes/node/package: post: consumes: diff --git a/frontend/src/api/interface/runtime.ts b/frontend/src/api/interface/runtime.ts index ca22905e6..957d620da 100644 --- a/frontend/src/api/interface/runtime.ts +++ b/frontend/src/api/interface/runtime.ts @@ -76,4 +76,14 @@ export namespace Runtime { ID: number; operate: string; } + + export interface NodeModule { + name: string; + version: string; + description: string; + } + + export interface NodeModuleReq { + ID: number; + } } diff --git a/frontend/src/api/modules/runtime.ts b/frontend/src/api/modules/runtime.ts index 02c26a191..98d4b101d 100644 --- a/frontend/src/api/modules/runtime.ts +++ b/frontend/src/api/modules/runtime.ts @@ -29,3 +29,7 @@ export const GetNodeScripts = (req: Runtime.NodeReq) => { export const OperateRuntime = (req: Runtime.RuntimeOperate) => { return http.post(`/runtimes/operate`, req); }; + +export const GetNodeModules = (req: Runtime.NodeModuleReq) => { + return http.post(`/runtimes/node/modules`, req); +}; diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts index 0ae884215..4e40e2551 100644 --- a/frontend/src/lang/modules/en.ts +++ b/frontend/src/lang/modules/en.ts @@ -1781,6 +1781,8 @@ const message = { taobao: 'Taobao', tencent: 'Tencent', imageSource: 'Image source', + moduleManager: 'Module Management', + module: 'Module', }, process: { pid: 'Process ID', diff --git a/frontend/src/lang/modules/tw.ts b/frontend/src/lang/modules/tw.ts index 951a352d2..a43a32ccd 100644 --- a/frontend/src/lang/modules/tw.ts +++ b/frontend/src/lang/modules/tw.ts @@ -1680,9 +1680,11 @@ const message = { close: '關閉', operatorHelper: '將對選取的執行環境進行 {0} 操作,是否繼續? ', statusHelper: '狀態說明:啟動中-容器已啟動,但應用正在啟動;異常-容器已啟動,但應用狀態異常', - taobao: 'Taobao', - tencent: 'Tencent', - imageSource: 'Image source', + taobao: '淘寶', + tencent: '騰訊', + imageSource: '鏡像源', + moduleManager: '模塊管理', + module: '模塊', }, process: { pid: '進程ID', diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index c49010d96..d702fe783 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -1682,6 +1682,8 @@ const message = { taobao: '淘宝', tencent: '腾讯', imageSource: '镜像源', + moduleManager: '模块管理', + module: '模块', }, process: { pid: '进程ID', diff --git a/frontend/src/views/website/runtime/node/index.vue b/frontend/src/views/website/runtime/node/index.vue index b9e1182c4..e95ffed69 100644 --- a/frontend/src/views/website/runtime/node/index.vue +++ b/frontend/src/views/website/runtime/node/index.vue @@ -70,7 +70,7 @@ /> + @@ -96,6 +97,7 @@ import Status from '@/components/status/index.vue'; import Delete from '@/views/website/runtime/delete/index.vue'; import i18n from '@/lang'; import RouterMenu from '../index.vue'; +import Modules from '@/views/website/runtime/node/module/index.vue'; import router from '@/routers/router'; import ComposeLogs from '@/components/compose-log/index.vue'; import { Promotion } from '@element-plus/icons-vue'; @@ -108,6 +110,7 @@ const operateRef = ref(); const deleteRef = ref(); const dialogPortJumpRef = ref(); const composeLogRef = ref(); +const moduleRef = ref(); const paginationConfig = reactive({ cacheSizeKey: 'runtime-page-size', @@ -122,6 +125,15 @@ const req = reactive({ type: 'node', }); const buttons = [ + { + label: i18n.global.t('runtime.module'), + click: function (row: Runtime.Runtime) { + openModules(row); + }, + disabled: function (row: Runtime.Runtime) { + return row.status === 'recreating' || row.status === 'stopped'; + }, + }, { label: i18n.global.t('container.stop'), click: function (row: Runtime.Runtime) { @@ -180,6 +192,10 @@ const search = async () => { } }; +const openModules = (row: Runtime.Runtime) => { + moduleRef.value.acceptParams({ id: row.id }); +}; + const openCreate = () => { operateRef.value.acceptParams({ type: 'node', mode: 'create' }); }; diff --git a/frontend/src/views/website/runtime/node/module/index.vue b/frontend/src/views/website/runtime/node/module/index.vue new file mode 100644 index 000000000..ef56f4bb6 --- /dev/null +++ b/frontend/src/views/website/runtime/node/module/index.vue @@ -0,0 +1,68 @@ + + + +