Browse Source

feat: node.js 运行环境增加模块列表 (#2482)

pull/2485/head
zhengkunwang 1 year ago committed by GitHub
parent
commit
a4bd9362ed
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 22
      backend/app/api/v1/runtime.go
  2. 6
      backend/app/dto/request/runtime.go
  3. 7
      backend/app/dto/response/runtime.go
  4. 3
      backend/app/service/app.go
  5. 35
      backend/app/service/runtime.go
  6. 1
      backend/i18n/lang/en.yaml
  7. 1
      backend/i18n/lang/zh-Hant.yaml
  8. 1
      backend/i18n/lang/zh.yaml
  9. 1
      backend/router/ro_runtime.go
  10. 54
      cmd/server/docs/docs.go
  11. 50
      cmd/server/docs/swagger.json
  12. 31
      cmd/server/docs/swagger.yaml
  13. 10
      frontend/src/api/interface/runtime.ts
  14. 4
      frontend/src/api/modules/runtime.ts
  15. 2
      frontend/src/lang/modules/en.ts
  16. 8
      frontend/src/lang/modules/tw.ts
  17. 2
      frontend/src/lang/modules/zh.ts
  18. 18
      frontend/src/views/website/runtime/node/index.vue
  19. 68
      frontend/src/views/website/runtime/node/module/index.vue

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

6
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"`
}

7
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"`
}

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

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

1
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."

1
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: "該備份帳號已在計劃任務中使用,無法刪除"

1
backend/i18n/lang/zh.yaml

@ -107,6 +107,7 @@ ErrRuntimeStart: "启动失败"
ErrPackageJsonNotFound: "package.json 文件不存在"
ErrScriptsNotFound: "没有在 package.json 中找到 scripts 配置项"
ErrContainerNameNotFound: "无法获取容器名称,请检查 .env 文件"
ErrNodeModulesNotFound: "node_modules 文件夹不存在!请编辑运行环境或者等待运行环境启动成功"
#setting
ErrBackupInUsed: "该备份账号已在计划任务中使用,无法删除"

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

54
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": {

50
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": {

31
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:

10
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;
}
}

4
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<any>(`/runtimes/operate`, req);
};
export const GetNodeModules = (req: Runtime.NodeModuleReq) => {
return http.post<Runtime.NodeModule[]>(`/runtimes/node/modules`, req);
};

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

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

2
frontend/src/lang/modules/zh.ts

@ -1682,6 +1682,8 @@ const message = {
taobao: '淘宝',
tencent: '腾讯',
imageSource: '镜像源',
moduleManager: '模块管理',
module: '模块',
},
process: {
pid: '进程ID',

18
frontend/src/views/website/runtime/node/index.vue

@ -70,7 +70,7 @@
/>
<fu-table-operations
:ellipsis="10"
width="250px"
width="300px"
:buttons="buttons"
:label="$t('commons.table.operate')"
fixed="right"
@ -83,6 +83,7 @@
<Delete ref="deleteRef" @close="search" />
<ComposeLogs ref="composeLogRef" />
<PortJumpDialog ref="dialogPortJumpRef" />
<Modules ref="moduleRef" />
</div>
</template>
@ -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<Runtime.RuntimeReq>({
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' });
};

68
frontend/src/views/website/runtime/node/module/index.vue

@ -0,0 +1,68 @@
<template>
<el-drawer :close-on-click-modal="false" v-model="open" size="50%">
<template #header>
<DrawerHeader :header="$t('runtime.moduleManager')" :back="handleClose" />
</template>
<el-row>
<el-col :span="24">
<ComplexTable :data="data" @search="search()" :height="650">
<el-table-column :label="$t('commons.table.name')" prop="name" min-width="100px"></el-table-column>
<el-table-column :label="$t('container.version')" prop="version" width="80px"></el-table-column>
<el-table-column
:label="$t('commons.table.protocol')"
prop="license"
width="120px"
></el-table-column>
<el-table-column
:label="$t('container.description')"
fix
min-width="120px"
prop="description"
></el-table-column>
</ComplexTable>
</el-col>
</el-row>
</el-drawer>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { GetNodeModules } from '@/api/modules/runtime';
interface NoodeRrops {
packageManager: string;
id: number;
}
const open = ref(false);
const id = ref(0);
const data = ref([]);
const acceptParams = async (props: NoodeRrops) => {
id.value = props.id;
data.value = [];
search();
open.value = true;
};
const search = async () => {
try {
const res = await GetNodeModules({ ID: id.value });
data.value = res.data;
console.log(res);
} catch (error) {}
};
const handleClose = () => {
open.value = false;
};
defineExpose({
acceptParams,
});
</script>
<style scoped lang="scss">
.table-border {
border: 1px solid var(--el-border-color);
}
</style>
Loading…
Cancel
Save