mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-03-01 03:24:14 +08:00
feat: 编排增加编辑功能
This commit is contained in:
parent
df31f71879
commit
0da3caba65
@ -357,3 +357,21 @@ func (b *BaseApi) CreateVolume(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
helper.SuccessWithData(c, nil)
|
helper.SuccessWithData(c, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *BaseApi) ComposeUpdate(c *gin.Context) {
|
||||||
|
var req dto.ComposeUpdate
|
||||||
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := global.VALID.Struct(req); err != nil {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := containerService.ComposeUpdate(req); err != nil {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithData(c, nil)
|
||||||
|
}
|
||||||
|
@ -133,6 +133,11 @@ type ComposeCreate struct {
|
|||||||
Template uint `json:"template"`
|
Template uint `json:"template"`
|
||||||
}
|
}
|
||||||
type ComposeOperation struct {
|
type ComposeOperation struct {
|
||||||
|
Name string `json:"name" validate:"required"`
|
||||||
Path string `json:"path" validate:"required"`
|
Path string `json:"path" validate:"required"`
|
||||||
Operation string `json:"operation" validate:"required,oneof=up stop pause unpause restart down"`
|
Operation string `json:"operation" validate:"required,oneof=up stop down"`
|
||||||
|
}
|
||||||
|
type ComposeUpdate struct {
|
||||||
|
Path string `json:"path" validate:"required"`
|
||||||
|
Content string `json:"content" validate:"required"`
|
||||||
}
|
}
|
||||||
|
@ -2,15 +2,17 @@ package service
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/app/model"
|
||||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||||
"github.com/1Panel-dev/1Panel/backend/global"
|
"github.com/1Panel-dev/1Panel/backend/global"
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/utils/compose"
|
||||||
"github.com/1Panel-dev/1Panel/backend/utils/docker"
|
"github.com/1Panel-dev/1Panel/backend/utils/docker"
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/docker/docker/api/types/filters"
|
"github.com/docker/docker/api/types/filters"
|
||||||
@ -40,6 +42,8 @@ func (u *ContainerService) PageCompose(req dto.PageInfo) (int64, interface{}, er
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, nil, err
|
return 0, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
composeCreatedByLocal, _ := composeRepo.ListRecord()
|
||||||
composeMap := make(map[string]dto.ComposeInfo)
|
composeMap := make(map[string]dto.ComposeInfo)
|
||||||
for _, container := range list {
|
for _, container := range list {
|
||||||
if name, ok := container.Labels[composeProjectLabel]; ok {
|
if name, ok := container.Labels[composeProjectLabel]; ok {
|
||||||
@ -72,10 +76,22 @@ func (u *ContainerService) PageCompose(req dto.PageInfo) (int64, interface{}, er
|
|||||||
} else {
|
} else {
|
||||||
composeItem.Path = workdir
|
composeItem.Path = workdir
|
||||||
}
|
}
|
||||||
|
for i := 0; i < len(composeCreatedByLocal); i++ {
|
||||||
|
if composeCreatedByLocal[i].Name == name {
|
||||||
|
composeItem.CreatedBy = "local"
|
||||||
|
composeCreatedByLocal = append(composeCreatedByLocal[:i], composeCreatedByLocal[i+1:]...)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
composeMap[name] = composeItem
|
composeMap[name] = composeItem
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for _, item := range composeCreatedByLocal {
|
||||||
|
if err := composeRepo.DeleteRecord(commonRepo.WithByName(item.Name)); err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
for key, value := range composeMap {
|
for key, value := range composeMap {
|
||||||
value.Name = key
|
value.Name = key
|
||||||
records = append(records, value)
|
records = append(records, value)
|
||||||
@ -124,23 +140,49 @@ func (u *ContainerService) CreateCompose(req dto.ComposeCreate) error {
|
|||||||
write.Flush()
|
write.Flush()
|
||||||
req.Path = path
|
req.Path = path
|
||||||
}
|
}
|
||||||
cmd := exec.Command("docker-compose", "-f", req.Path, "up", "-d")
|
if stdout, err := compose.Up(req.Path); err != nil {
|
||||||
stdout, err := cmd.CombinedOutput()
|
return errors.New(string(stdout))
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
global.LOG.Debugf("docker-compose up %s successful, logs: %v", req.Name, string(stdout))
|
global.LOG.Debugf("docker-compose up %s successful", req.Name)
|
||||||
|
|
||||||
|
_ = composeRepo.CreateRecord(&model.Compose{Name: req.Name})
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *ContainerService) ComposeOperation(req dto.ComposeOperation) error {
|
func (u *ContainerService) ComposeOperation(req dto.ComposeOperation) error {
|
||||||
cmd := exec.Command("docker-compose", "-f", req.Path, req.Operation)
|
if _, err := os.Stat(req.Path); err != nil {
|
||||||
stdout, err := cmd.CombinedOutput()
|
return fmt.Errorf("load file with path %s failed, %v", req.Path, err)
|
||||||
|
}
|
||||||
|
if stdout, err := compose.Operate(req.Path, req.Operation); err != nil {
|
||||||
|
return errors.New(string(stdout))
|
||||||
|
}
|
||||||
|
global.LOG.Debugf("docker-compose %s %s successful", req.Operation, req.Name)
|
||||||
|
if req.Operation == "down" {
|
||||||
|
_ = composeRepo.DeleteRecord(commonRepo.WithByName(req.Name))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *ContainerService) ComposeUpdate(req dto.ComposeUpdate) error {
|
||||||
|
if _, err := os.Stat(req.Path); err != nil {
|
||||||
|
return fmt.Errorf("load file with path %s failed, %v", req.Path, err)
|
||||||
|
}
|
||||||
|
file, err := os.OpenFile(req.Path, os.O_WRONLY|os.O_TRUNC, 0640)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
global.LOG.Debugf("docker-compose %s %s successful: logs: %v", req.Operation, req.Path, string(stdout))
|
defer file.Close()
|
||||||
|
write := bufio.NewWriter(file)
|
||||||
|
_, _ = write.WriteString(req.Content)
|
||||||
|
write.Flush()
|
||||||
|
|
||||||
return err
|
if stdout, err := compose.Down(req.Path); err != nil {
|
||||||
|
return errors.New(string(stdout))
|
||||||
|
}
|
||||||
|
if stdout, err := compose.Up(req.Path); err != nil {
|
||||||
|
return errors.New(string(stdout))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -94,7 +94,7 @@ func (u *DashboardService) LoadBaseInfo(ioOption string, netOption string) (*dto
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
baseInfo.DatabaseNumber = len(cornjobs)
|
baseInfo.CronjobNumber = len(cornjobs)
|
||||||
|
|
||||||
cpuInfo, err := cpu.Info()
|
cpuInfo, err := cpu.Info()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -36,8 +36,9 @@ func (s *ContainerRouter) InitContainerRouter(Router *gin.RouterGroup) {
|
|||||||
withRecordRouter.POST("/repo/del", baseApi.DeleteRepo)
|
withRecordRouter.POST("/repo/del", baseApi.DeleteRepo)
|
||||||
|
|
||||||
baRouter.POST("/compose/search", baseApi.SearchCompose)
|
baRouter.POST("/compose/search", baseApi.SearchCompose)
|
||||||
baRouter.POST("/compose/up", baseApi.CreateCompose)
|
baRouter.POST("/compose", baseApi.CreateCompose)
|
||||||
baRouter.POST("/compose/operate", baseApi.OperatorCompose)
|
baRouter.POST("/compose/operate", baseApi.OperatorCompose)
|
||||||
|
baRouter.POST("/compose/update", baseApi.ComposeUpdate)
|
||||||
|
|
||||||
baRouter.POST("/template/search", baseApi.SearchComposeTemplate)
|
baRouter.POST("/template/search", baseApi.SearchComposeTemplate)
|
||||||
baRouter.PUT("/template/:id", baseApi.UpdateComposeTemplate)
|
baRouter.PUT("/template/:id", baseApi.UpdateComposeTemplate)
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
package compose
|
package compose
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"os/exec"
|
||||||
|
|
||||||
"github.com/compose-spec/compose-go/loader"
|
"github.com/compose-spec/compose-go/loader"
|
||||||
"github.com/compose-spec/compose-go/types"
|
"github.com/compose-spec/compose-go/types"
|
||||||
"os/exec"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func Up(filePath string) (string, error) {
|
func Up(filePath string) (string, error) {
|
||||||
@ -30,6 +31,12 @@ func Restart(filePath string) (string, error) {
|
|||||||
return string(stdout), err
|
return string(stdout), err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Operate(filePath, operation string) (string, error) {
|
||||||
|
cmd := exec.Command("docker-compose", "-f", filePath, operation)
|
||||||
|
stdout, err := cmd.CombinedOutput()
|
||||||
|
return string(stdout), err
|
||||||
|
}
|
||||||
|
|
||||||
func Rmf(filePath string) (string, error) {
|
func Rmf(filePath string) (string, error) {
|
||||||
cmd := exec.Command("docker-compose", "-f", filePath, "rm", "-f")
|
cmd := exec.Command("docker-compose", "-f", filePath, "rm", "-f")
|
||||||
stdout, err := cmd.CombinedOutput()
|
stdout, err := cmd.CombinedOutput()
|
||||||
|
@ -196,6 +196,10 @@ export namespace Container {
|
|||||||
operation: string;
|
operation: string;
|
||||||
path: string;
|
path: string;
|
||||||
}
|
}
|
||||||
|
export interface ComposeUpdate {
|
||||||
|
path: string;
|
||||||
|
content: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface TemplateCreate {
|
export interface TemplateCreate {
|
||||||
name: string;
|
name: string;
|
||||||
|
@ -114,11 +114,14 @@ export const searchCompose = (params: ReqPage) => {
|
|||||||
return http.post<ResPage<Container.ComposeInfo>>(`/containers/compose/search`, params);
|
return http.post<ResPage<Container.ComposeInfo>>(`/containers/compose/search`, params);
|
||||||
};
|
};
|
||||||
export const upCompose = (params: Container.ComposeCreate) => {
|
export const upCompose = (params: Container.ComposeCreate) => {
|
||||||
return http.post(`/containers/compose/up`, params);
|
return http.post(`/containers/compose`, params);
|
||||||
};
|
};
|
||||||
export const ComposeOperator = (params: Container.ComposeOpration) => {
|
export const composeOperator = (params: Container.ComposeOpration) => {
|
||||||
return http.post(`/containers/compose/operate`, params);
|
return http.post(`/containers/compose/operate`, params);
|
||||||
};
|
};
|
||||||
|
export const composeUpdate = (params: Container.ComposeUpdate) => {
|
||||||
|
return http.post(`/containers/compose/update`, params);
|
||||||
|
};
|
||||||
|
|
||||||
// docker
|
// docker
|
||||||
export const loadDaemonJson = () => {
|
export const loadDaemonJson = () => {
|
||||||
|
83
frontend/src/views/container/compose/edit/index.vue
Normal file
83
frontend/src/views/container/compose/edit/index.vue
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog v-model="composeVisiable" :destroy-on-close="true" :close-on-click-modal="false" width="70%">
|
||||||
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span>{{ $t('commons.button.edit') }}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div v-loading="loading">
|
||||||
|
<codemirror
|
||||||
|
ref="mymirror"
|
||||||
|
: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="content"
|
||||||
|
:readOnly="true"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button :disabled="loading" @click="composeVisiable = false">
|
||||||
|
{{ $t('commons.button.cancel') }}
|
||||||
|
</el-button>
|
||||||
|
<el-button :disabled="loading" type="primary" @click="onSubmitEdit()">
|
||||||
|
{{ $t('commons.button.confirm') }}
|
||||||
|
</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { Codemirror } from 'vue-codemirror';
|
||||||
|
import { javascript } from '@codemirror/lang-javascript';
|
||||||
|
import { oneDark } from '@codemirror/theme-one-dark';
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { ElMessage } from 'element-plus';
|
||||||
|
import { composeUpdate } from '@/api/modules/container';
|
||||||
|
import i18n from '@/lang';
|
||||||
|
|
||||||
|
const loading = ref(false);
|
||||||
|
const composeVisiable = ref(false);
|
||||||
|
const extensions = [javascript(), oneDark];
|
||||||
|
const path = ref();
|
||||||
|
const content = ref();
|
||||||
|
|
||||||
|
const onSubmitEdit = async () => {
|
||||||
|
const param = {
|
||||||
|
path: path.value,
|
||||||
|
content: content.value,
|
||||||
|
};
|
||||||
|
loading.value = true;
|
||||||
|
await composeUpdate(param)
|
||||||
|
.then(() => {
|
||||||
|
loading.value = false;
|
||||||
|
ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
|
||||||
|
composeVisiable.value = false;
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
interface DialogProps {
|
||||||
|
path: string;
|
||||||
|
content: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const acceptParams = (props: DialogProps): void => {
|
||||||
|
composeVisiable.value = true;
|
||||||
|
path.value = props.path;
|
||||||
|
content.value = props.content;
|
||||||
|
};
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
acceptParams,
|
||||||
|
});
|
||||||
|
</script>
|
@ -153,7 +153,7 @@ const onSubmitSave = async () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const loadMysqlConf = async () => {
|
const loadMysqlConf = async () => {
|
||||||
const res = await LoadFile({ path: '/opt/1Panel/docker/config/daemon.json' });
|
const res = await LoadFile({ path: '/opt/1Panel/docker/conf/daemon.json' });
|
||||||
dockerConf.value = res.data;
|
dockerConf.value = res.data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user