mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-03-14 01:34:47 +08:00
feat: node.js 运行环境增加模块列表 (#2482)
This commit is contained in:
parent
0496b282b1
commit
a4bd9362ed
@ -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)
|
||||
}
|
||||
|
@ -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"`
|
||||
}
|
||||
|
@ -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"`
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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."
|
||||
|
@ -107,6 +107,7 @@ ErrRuntimeStart: "啟動失敗"
|
||||
ErrPackageJsonNotFound: "package.json 文件不存在"
|
||||
ErrScriptsNotFound: "沒有在 package.json 中找到 scripts 配置項"
|
||||
ErrContainerNameNotFound: "無法取得容器名稱,請檢查 .env 文件"
|
||||
ErrNodeModulesNotFound: "node_modules 文件夾不存在!請編輯運行環境或者等待運行環境啟動成功"
|
||||
|
||||
#setting
|
||||
ErrBackupInUsed: "該備份帳號已在計劃任務中使用,無法刪除"
|
||||
|
@ -107,6 +107,7 @@ ErrRuntimeStart: "启动失败"
|
||||
ErrPackageJsonNotFound: "package.json 文件不存在"
|
||||
ErrScriptsNotFound: "没有在 package.json 中找到 scripts 配置项"
|
||||
ErrContainerNameNotFound: "无法获取容器名称,请检查 .env 文件"
|
||||
ErrNodeModulesNotFound: "node_modules 文件夹不存在!请编辑运行环境或者等待运行环境启动成功"
|
||||
|
||||
#setting
|
||||
ErrBackupInUsed: "该备份账号已在计划任务中使用,无法删除"
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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": {
|
||||
|
@ -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": {
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
};
|
||||
|
@ -1781,6 +1781,8 @@ const message = {
|
||||
taobao: 'Taobao',
|
||||
tencent: 'Tencent',
|
||||
imageSource: 'Image source',
|
||||
moduleManager: 'Module Management',
|
||||
module: 'Module',
|
||||
},
|
||||
process: {
|
||||
pid: 'Process ID',
|
||||
|
@ -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',
|
||||
|
@ -1682,6 +1682,8 @@ const message = {
|
||||
taobao: '淘宝',
|
||||
tencent: '腾讯',
|
||||
imageSource: '镜像源',
|
||||
moduleManager: '模块管理',
|
||||
module: '模块',
|
||||
},
|
||||
process: {
|
||||
pid: '进程ID',
|
||||
|
@ -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
Normal file
68
frontend/src/views/website/runtime/node/module/index.vue
Normal file
@ -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…
x
Reference in New Issue
Block a user