feat: 增加 inspect 详情显示

This commit is contained in:
ssongliu 2022-10-11 19:47:16 +08:00 committed by ssongliu
parent 39f9de0b00
commit 9962c6c4a8
9 changed files with 148 additions and 53 deletions

View File

@ -1,8 +1,6 @@
package v1
import (
"errors"
"github.com/1Panel-dev/1Panel/app/api/v1/helper"
"github.com/1Panel-dev/1Panel/app/dto"
"github.com/1Panel-dev/1Panel/constant"
@ -49,13 +47,18 @@ func (b *BaseApi) ContainerOperation(c *gin.Context) {
helper.SuccessWithData(c, nil)
}
func (b *BaseApi) ContainerDetail(c *gin.Context) {
id, ok := c.Params.Get("id")
if !ok {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, errors.New("error id in path"))
func (b *BaseApi) Inspect(c *gin.Context) {
var req dto.InspectReq
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
result, err := containerService.ContainerInspect(id)
if err := global.VALID.Struct(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
result, err := containerService.Inspect(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return

View File

@ -7,6 +7,11 @@ type PageContainer struct {
Status string `json:"status" validate:"required,oneof=all running"`
}
type InspectReq struct {
ID string `json:"id"`
Type string `json:"type"`
}
type ContainerInfo struct {
ContainerID string `json:"containerID"`
Name string `json:"name"`

View File

@ -27,7 +27,7 @@ type IContainerService interface {
PageVolume(req dto.PageInfo) (int64, interface{}, error)
ContainerOperation(req dto.ContainerOperation) error
ContainerLogs(param dto.ContainerLog) (string, error)
ContainerInspect(id string) (string, error)
Inspect(req dto.InspectReq) (string, error)
DeleteNetwork(req dto.BatchDelete) error
CreateNetwork(req dto.NetworkCreat) error
DeleteVolume(req dto.BatchDelete) error
@ -77,6 +77,30 @@ func (u *ContainerService) Page(req dto.PageContainer) (int64, interface{}, erro
return int64(total), backDatas, nil
}
func (u *ContainerService) Inspect(req dto.InspectReq) (string, error) {
client, err := docker.NewDockerClient()
if err != nil {
return "", err
}
var inspectInfo interface{}
switch req.Type {
case "container":
inspectInfo, err = client.ContainerInspect(context.Background(), req.ID)
case "network":
inspectInfo, err = client.NetworkInspect(context.TODO(), req.ID, types.NetworkInspectOptions{})
case "volume":
inspectInfo, err = client.VolumeInspect(context.TODO(), req.ID)
}
if err != nil {
return "", err
}
bytes, err := json.Marshal(inspectInfo)
if err != nil {
return "", err
}
return string(bytes), nil
}
func (u *ContainerService) ContainerOperation(req dto.ContainerOperation) error {
var err error
ctx := context.Background()
@ -105,22 +129,6 @@ func (u *ContainerService) ContainerOperation(req dto.ContainerOperation) error
return err
}
func (u *ContainerService) ContainerInspect(id string) (string, error) {
client, err := docker.NewDockerClient()
if err != nil {
return "", err
}
inspect, err := client.ContainerInspect(context.Background(), id)
if err != nil {
return "", err
}
bytes, err := json.Marshal(inspect)
if err != nil {
return "", err
}
return string(bytes), err
}
func (u *ContainerService) ContainerLogs(req dto.ContainerLog) (string, error) {
var (
options types.ContainerLogsOptions

View File

@ -21,7 +21,7 @@ func (s *ContainerRouter) InitContainerRouter(Router *gin.RouterGroup) {
baseApi := v1.ApiGroupApp.BaseApi
{
baRouter.POST("/search", baseApi.SearchContainer)
baRouter.GET("/detail/:id", baseApi.ContainerDetail)
baRouter.POST("/inspect", baseApi.Inspect)
withRecordRouter.POST("operate", baseApi.ContainerOperation)
withRecordRouter.POST("/log", baseApi.ContainerLogs)

View File

@ -16,6 +16,10 @@ export namespace Container {
containerID: string;
mode: string;
}
export interface ContainerInspect {
id: string;
type: string;
}
export interface ImageInfo {
id: string;

View File

@ -14,8 +14,8 @@ export const ContainerOperator = (params: Container.ContainerOperate) => {
return http.post(`/containers/operate`, params);
};
export const getContainerInspect = (containerID: string) => {
return http.get<string>(`/containers/detail/${containerID}`);
export const inspect = (params: Container.ContainerInspect) => {
return http.post<string>(`/containers/inspect`, params);
};
// image

View File

@ -37,7 +37,11 @@
min-width="100"
prop="name"
fix
/>
>
<template #default="{ row }">
<el-link @click="onInspect(row.containerID)" type="primary">{{ row.name }}</el-link>
</template>
</el-table-column>
<el-table-column
:label="$t('container.image')"
show-overflow-tooltip
@ -165,7 +169,7 @@ import { oneDark } from '@codemirror/theme-one-dark';
import { reactive, onMounted, ref } from 'vue';
import { dateFromat, dateFromatForName } from '@/utils/util';
import { Rules } from '@/global/form-rules';
import { ContainerOperator, getContainerInspect, getContainerLog, getContainerPage } from '@/api/modules/container';
import { ContainerOperator, inspect, getContainerLog, getContainerPage } from '@/api/modules/container';
import { Container } from '@/api/interface/container';
import { ElForm, ElMessage, ElMessageBox, FormInstance } from 'element-plus';
import i18n from '@/lang';
@ -244,11 +248,12 @@ const onCreate = async () => {
dialogCreateRef.value!.acceptParams();
};
const onDetail = async (row: Container.ContainerInfo) => {
const res = await getContainerInspect(row.containerID);
const onInspect = async (id: string) => {
const res = await inspect({ id: id, type: 'container' });
detailInfo.value = JSON.stringify(JSON.parse(res.data), null, 2);
detailVisiable.value = true;
};
const onLog = async (row: Container.ContainerInfo) => {
logSearch.container = row.name;
logSearch.containerID = row.containerID;
@ -371,12 +376,6 @@ const buttons = [
onLog(row);
},
},
{
label: i18n.global.t('commons.button.view'),
click: (row: Container.ContainerInfo) => {
onDetail(row);
},
},
];
onMounted(() => {

View File

@ -11,13 +11,11 @@
</el-button>
</template>
<el-table-column type="selection" fix />
<el-table-column
:label="$t('commons.table.name')"
show-overflow-tooltip
min-width="80"
prop="name"
fix
/>
<el-table-column :label="$t('commons.table.name')" show-overflow-tooltip min-width="80" prop="name" fix>
<template #default="{ row }">
<el-link @click="onInspect(row.id)" type="primary">{{ row.name }}</el-link>
</template>
</el-table-column>
<el-table-column :label="$t('container.driver')" show-overflow-tooltip min-width="40" prop="driver" />
<el-table-column :label="$t('container.attachable')" min-width="40" prop="attachable" fix>
<template #default="{ row }">
@ -46,6 +44,33 @@
</ComplexTable>
</el-card>
<el-dialog v-model="detailVisiable" :destroy-on-close="true" :close-on-click-modal="false" width="70%">
<template #header>
<div class="card-header">
<span>{{ $t('commons.button.view') }}</span>
</div>
</template>
<codemirror
:autofocus="true"
placeholder="None data"
:indent-with-tab="true"
:tabSize="4"
style="max-height: 500px"
:lineWrapping="true"
:matchBrackets="true"
theme="cobalt"
:styleActiveLine="true"
:extensions="extensions"
v-model="detailInfo"
:readOnly="true"
/>
<template #footer>
<span class="dialog-footer">
<el-button @click="detailVisiable = false">{{ $t('commons.button.cancel') }}</el-button>
</span>
</template>
</el-dialog>
<CreateDialog @search="search" ref="dialogCreateRef" />
</div>
</template>
@ -53,13 +78,20 @@
<script lang="ts" setup>
import ComplexTable from '@/components/complex-table/index.vue';
import CreateDialog from '@/views/container/network/create/index.vue';
import { Codemirror } from 'vue-codemirror';
import { javascript } from '@codemirror/lang-javascript';
import { oneDark } from '@codemirror/theme-one-dark';
import { reactive, onMounted, ref } from 'vue';
import { dateFromat } from '@/utils/util';
import { deleteNetwork, getNetworkPage } from '@/api/modules/container';
import { deleteNetwork, getNetworkPage, inspect } from '@/api/modules/container';
import { Container } from '@/api/interface/container';
import i18n from '@/lang';
import { useDeleteData } from '@/hooks/use-delete-data';
const detailVisiable = ref<boolean>(false);
const detailInfo = ref();
const extensions = [javascript(), oneDark];
const data = ref();
const selects = ref<any>([]);
const paginationConfig = reactive({
@ -103,6 +135,12 @@ const batchDelete = async (row: Container.NetworkInfo | null) => {
search();
};
const onInspect = async (id: string) => {
const res = await inspect({ id: id, type: 'network' });
detailInfo.value = JSON.stringify(JSON.parse(res.data), null, 2);
detailVisiable.value = true;
};
const buttons = [
{
label: i18n.global.t('commons.button.delete'),

View File

@ -11,13 +11,11 @@
</el-button>
</template>
<el-table-column type="selection" fix />
<el-table-column
:label="$t('commons.table.name')"
show-overflow-tooltip
min-width="80"
prop="name"
fix
/>
<el-table-column :label="$t('commons.table.name')" show-overflow-tooltip min-width="80" prop="name" fix>
<template #default="{ row }">
<el-link @click="onInspect(row.name)" type="primary">{{ row.name }}</el-link>
</template>
</el-table-column>
<el-table-column
:label="$t('container.mountpoint')"
show-overflow-tooltip
@ -35,6 +33,33 @@
</ComplexTable>
</el-card>
<el-dialog v-model="detailVisiable" :destroy-on-close="true" :close-on-click-modal="false" width="70%">
<template #header>
<div class="card-header">
<span>{{ $t('commons.button.view') }}</span>
</div>
</template>
<codemirror
:autofocus="true"
placeholder="None data"
:indent-with-tab="true"
:tabSize="4"
style="max-height: 500px"
:lineWrapping="true"
:matchBrackets="true"
theme="cobalt"
:styleActiveLine="true"
:extensions="extensions"
v-model="detailInfo"
:readOnly="true"
/>
<template #footer>
<span class="dialog-footer">
<el-button @click="detailVisiable = false">{{ $t('commons.button.cancel') }}</el-button>
</span>
</template>
</el-dialog>
<CreateDialog @search="search" ref="dialogCreateRef" />
</div>
</template>
@ -42,13 +67,20 @@
<script lang="ts" setup>
import ComplexTable from '@/components/complex-table/index.vue';
import CreateDialog from '@/views/container/volume/create/index.vue';
import { Codemirror } from 'vue-codemirror';
import { javascript } from '@codemirror/lang-javascript';
import { oneDark } from '@codemirror/theme-one-dark';
import { reactive, onMounted, ref } from 'vue';
import { dateFromat } from '@/utils/util';
import { deleteVolume, getVolumePage } from '@/api/modules/container';
import { deleteVolume, getVolumePage, inspect } from '@/api/modules/container';
import { Container } from '@/api/interface/container';
import i18n from '@/lang';
import { useDeleteData } from '@/hooks/use-delete-data';
const detailVisiable = ref<boolean>(false);
const detailInfo = ref();
const extensions = [javascript(), oneDark];
const data = ref();
const selects = ref<any>([]);
const paginationConfig = reactive({
@ -79,6 +111,12 @@ const search = async () => {
});
};
const onInspect = async (id: string) => {
const res = await inspect({ id: id, type: 'volume' });
detailInfo.value = JSON.stringify(JSON.parse(res.data), null, 2);
detailVisiable.value = true;
};
const batchDelete = async (row: Container.VolumeInfo | null) => {
let ids: Array<string> = [];
if (row === null) {