mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-01-18 22:22:59 +08:00
parent
2096049708
commit
0c5a5a6454
@ -90,11 +90,12 @@ func (b *BaseApi) CreateCompose(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if err := containerService.CreateCompose(req); err != nil {
|
||||
log, err := containerService.CreateCompose(req)
|
||||
if err != nil {
|
||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, nil)
|
||||
helper.SuccessWithData(c, log)
|
||||
}
|
||||
|
||||
// @Tags Container Compose
|
||||
|
@ -33,7 +33,7 @@ type IContainerService interface {
|
||||
PageVolume(req dto.SearchWithPage) (int64, interface{}, error)
|
||||
ListVolume() ([]dto.Options, error)
|
||||
PageCompose(req dto.SearchWithPage) (int64, interface{}, error)
|
||||
CreateCompose(req dto.ComposeCreate) error
|
||||
CreateCompose(req dto.ComposeCreate) (string, error)
|
||||
ComposeOperation(req dto.ComposeOperation) error
|
||||
ContainerCreate(req dto.ContainerCreate) error
|
||||
ContainerOperation(req dto.ContainerOperation) error
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"sort"
|
||||
"strings"
|
||||
@ -124,11 +125,11 @@ func (u *ContainerService) PageCompose(req dto.SearchWithPage) (int64, interface
|
||||
return int64(total), BackDatas, nil
|
||||
}
|
||||
|
||||
func (u *ContainerService) CreateCompose(req dto.ComposeCreate) error {
|
||||
func (u *ContainerService) CreateCompose(req dto.ComposeCreate) (string, error) {
|
||||
if req.From == "template" {
|
||||
template, err := composeRepo.Get(commonRepo.WithByID(req.Template))
|
||||
if err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
req.From = "edit"
|
||||
req.File = template.Content
|
||||
@ -137,14 +138,14 @@ func (u *ContainerService) CreateCompose(req dto.ComposeCreate) error {
|
||||
dir := fmt.Sprintf("%s/docker/compose/%s", constant.DataDir, req.Name)
|
||||
if _, err := os.Stat(dir); err != nil && os.IsNotExist(err) {
|
||||
if err = os.MkdirAll(dir, os.ModePerm); err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
path := fmt.Sprintf("%s/docker-compose.yml", dir)
|
||||
file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
|
||||
if err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
defer file.Close()
|
||||
write := bufio.NewWriter(file)
|
||||
@ -157,13 +158,28 @@ func (u *ContainerService) CreateCompose(req dto.ComposeCreate) error {
|
||||
if req.From == "path" {
|
||||
req.Name = path.Base(strings.ReplaceAll(req.Path, "/docker-compose.yml", ""))
|
||||
}
|
||||
if stdout, err := compose.Up(req.Path); err != nil {
|
||||
_, _ = compose.Down(req.Path)
|
||||
return errors.New(stdout)
|
||||
logName := strings.ReplaceAll(req.Path, "docker-compose.yml", "compose.log")
|
||||
file, err := os.OpenFile(logName, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
_ = composeRepo.CreateRecord(&model.Compose{Name: req.Name})
|
||||
go func() {
|
||||
defer file.Close()
|
||||
cmd := exec.Command("docker-compose", "-f", req.Path, "up", "-d")
|
||||
stdout, err := cmd.CombinedOutput()
|
||||
_, _ = file.Write(stdout)
|
||||
if err != nil {
|
||||
global.LOG.Errorf("docker-compose up %s failed, err: %v", req.Name, err)
|
||||
_, _ = compose.Down(req.Path)
|
||||
_, _ = file.WriteString("docker-compose up failed!")
|
||||
return
|
||||
}
|
||||
global.LOG.Infof("docker-compose up %s successful!", req.Name)
|
||||
_ = composeRepo.CreateRecord(&model.Compose{Name: req.Name})
|
||||
_, _ = file.WriteString("docker-compose up successful!")
|
||||
}()
|
||||
|
||||
return nil
|
||||
return logName, nil
|
||||
}
|
||||
|
||||
func (u *ContainerService) ComposeOperation(req dto.ComposeOperation) error {
|
||||
|
@ -7,7 +7,7 @@ import (
|
||||
)
|
||||
|
||||
func Up(filePath string) (string, error) {
|
||||
stdout, err := cmd.Execf("docker-compose -f %s up -d --quiet-pull", filePath)
|
||||
stdout, err := cmd.Execf("docker-compose -f %s up -d", filePath)
|
||||
return stdout, err
|
||||
}
|
||||
|
||||
|
@ -117,7 +117,7 @@ export const searchCompose = (params: SearchWithPage) => {
|
||||
return http.post<ResPage<Container.ComposeInfo>>(`/containers/compose/search`, params);
|
||||
};
|
||||
export const upCompose = (params: Container.ComposeCreate) => {
|
||||
return http.post(`/containers/compose`, params, 600000);
|
||||
return http.post<string>(`/containers/compose`, params, 600000);
|
||||
};
|
||||
export const composeOperator = (params: Container.ComposeOpration) => {
|
||||
return http.post(`/containers/compose/operate`, params);
|
||||
|
@ -1,12 +1,18 @@
|
||||
<template>
|
||||
<el-drawer v-model="drawerVisiable" :destroy-on-close="true" :close-on-click-modal="false" size="50%">
|
||||
<el-drawer
|
||||
v-model="drawerVisiable"
|
||||
@close="handleClose"
|
||||
:destroy-on-close="true"
|
||||
:close-on-click-modal="false"
|
||||
size="50%"
|
||||
>
|
||||
<template #header>
|
||||
<DrawerHeader :header="$t('container.compose')" :back="handleClose" />
|
||||
</template>
|
||||
<div v-loading="loading">
|
||||
<el-form ref="formRef" label-position="top" :model="form" :rules="rules" label-width="80px">
|
||||
<el-row type="flex" justify="center">
|
||||
<el-col :span="22">
|
||||
<div>
|
||||
<el-row type="flex" justify="center">
|
||||
<el-col :span="22">
|
||||
<el-form ref="formRef" label-position="top" :model="form" :rules="rules" label-width="80px">
|
||||
<el-form-item :label="$t('container.from')">
|
||||
<el-radio-group v-model="form.from">
|
||||
<el-radio label="edit">{{ $t('commons.button.edit') }}</el-radio>
|
||||
@ -46,7 +52,7 @@
|
||||
placeholder="#Define or paste the content of your docker-compose file here"
|
||||
:indent-with-tab="true"
|
||||
:tabSize="4"
|
||||
style="width: 100%; height: calc(100vh - 340px)"
|
||||
style="width: 100%; height: 200px"
|
||||
:lineWrapping="true"
|
||||
:matchBrackets="true"
|
||||
theme="cobalt"
|
||||
@ -55,16 +61,32 @@
|
||||
v-model="form.file"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</el-form>
|
||||
<codemirror
|
||||
v-if="logVisiable"
|
||||
:autofocus="true"
|
||||
placeholder="Waiting for build output..."
|
||||
:indent-with-tab="true"
|
||||
:tabSize="4"
|
||||
style="max-height: calc(100vh - 537px)"
|
||||
:lineWrapping="true"
|
||||
:matchBrackets="true"
|
||||
theme="cobalt"
|
||||
:styleActiveLine="true"
|
||||
:extensions="extensions"
|
||||
@ready="handleReady"
|
||||
v-model="logInfo"
|
||||
:readOnly="true"
|
||||
/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button :disabled="loading" @click="drawerVisiable = false">
|
||||
<el-button @click="drawerVisiable = false">
|
||||
{{ $t('commons.button.cancel') }}
|
||||
</el-button>
|
||||
<el-button type="primary" :disabled="loading" @click="onSubmit(formRef)">
|
||||
<el-button type="primary" :disabled="buttonDisabled" @click="onSubmit(formRef)">
|
||||
{{ $t('commons.button.confirm') }}
|
||||
</el-button>
|
||||
</span>
|
||||
@ -73,7 +95,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref } from 'vue';
|
||||
import { nextTick, reactive, ref, shallowRef } from 'vue';
|
||||
import FileList from '@/components/file-list/index.vue';
|
||||
import { Codemirror } from 'vue-codemirror';
|
||||
import { javascript } from '@codemirror/lang-javascript';
|
||||
@ -83,23 +105,34 @@ import i18n from '@/lang';
|
||||
import { ElForm } from 'element-plus';
|
||||
import DrawerHeader from '@/components/drawer-header/index.vue';
|
||||
import { listComposeTemplate, upCompose } from '@/api/modules/container';
|
||||
import { MsgSuccess } from '@/utils/message';
|
||||
import { loadBaseDir } from '@/api/modules/setting';
|
||||
import { LoadFile } from '@/api/modules/files';
|
||||
import { formatImageStdout } from '@/utils/docker';
|
||||
|
||||
const extensions = [javascript(), oneDark];
|
||||
const view = shallowRef();
|
||||
const handleReady = (payload) => {
|
||||
view.value = payload.view;
|
||||
};
|
||||
const logVisiable = ref();
|
||||
const logInfo = ref();
|
||||
|
||||
const drawerVisiable = ref(false);
|
||||
const templateOptions = ref();
|
||||
const buttonDisabled = ref(false);
|
||||
|
||||
const loading = ref(false);
|
||||
const baseDir = ref();
|
||||
const composeFile = ref();
|
||||
|
||||
let timer: NodeJS.Timer | null = null;
|
||||
|
||||
const varifyPath = (rule: any, value: any, callback: any) => {
|
||||
if (value.indexOf('docker-compose.yml') === -1) {
|
||||
callback(new Error(i18n.global.t('commons.rule.selectHelper', ['docker-compose.yml'])));
|
||||
}
|
||||
callback();
|
||||
};
|
||||
|
||||
const form = reactive({
|
||||
name: '',
|
||||
from: 'edit',
|
||||
@ -126,12 +159,17 @@ const acceptParams = (): void => {
|
||||
form.from = 'edit';
|
||||
form.path = '';
|
||||
form.file = '';
|
||||
logVisiable.value = false;
|
||||
logInfo.value = '';
|
||||
loadTemplates();
|
||||
loadPath();
|
||||
};
|
||||
const emit = defineEmits<{ (e: 'search'): void }>();
|
||||
|
||||
const handleClose = () => {
|
||||
emit('search');
|
||||
clearInterval(Number(timer));
|
||||
timer = null;
|
||||
drawerVisiable.value = false;
|
||||
};
|
||||
|
||||
@ -152,20 +190,38 @@ const onSubmit = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return;
|
||||
formEl.validate(async (valid) => {
|
||||
if (!valid) return;
|
||||
loading.value = true;
|
||||
upCompose(form)
|
||||
.then(() => {
|
||||
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||
loading.value = false;
|
||||
emit('search');
|
||||
drawerVisiable.value = false;
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
const res = await upCompose(form);
|
||||
logInfo.value = '';
|
||||
buttonDisabled.value = true;
|
||||
logVisiable.value = true;
|
||||
loadLogs(res.data);
|
||||
});
|
||||
};
|
||||
|
||||
const loadLogs = async (path: string) => {
|
||||
timer = setInterval(async () => {
|
||||
if (logVisiable.value) {
|
||||
const res = await LoadFile({ path: path });
|
||||
logInfo.value = formatImageStdout(res.data);
|
||||
nextTick(() => {
|
||||
const state = view.value.state;
|
||||
view.value.dispatch({
|
||||
selection: { anchor: state.doc.length, head: state.doc.length },
|
||||
scrollIntoView: true,
|
||||
});
|
||||
});
|
||||
if (
|
||||
logInfo.value.endsWith('docker-compose up failed!') ||
|
||||
logInfo.value.endsWith('docker-compose up successful!')
|
||||
) {
|
||||
clearInterval(Number(timer));
|
||||
timer = null;
|
||||
buttonDisabled.value = false;
|
||||
}
|
||||
}
|
||||
}, 1000 * 3);
|
||||
};
|
||||
|
||||
const loadDir = async (path: string) => {
|
||||
form.path = path;
|
||||
};
|
||||
|
@ -2,7 +2,7 @@
|
||||
<el-drawer
|
||||
v-model="drawerVisiable"
|
||||
:destroy-on-close="true"
|
||||
@close="onCloseLog"
|
||||
@close="handleClose"
|
||||
:close-on-click-modal="false"
|
||||
size="50%"
|
||||
>
|
||||
@ -141,6 +141,9 @@ const emit = defineEmits<{ (e: 'search'): void }>();
|
||||
|
||||
const handleClose = () => {
|
||||
drawerVisiable.value = false;
|
||||
emit('search');
|
||||
clearInterval(Number(timer));
|
||||
timer = null;
|
||||
};
|
||||
|
||||
type FormInstance = InstanceType<typeof ElForm>;
|
||||
@ -181,11 +184,6 @@ const loadLogs = async (path: string) => {
|
||||
}
|
||||
}, 1000 * 3);
|
||||
};
|
||||
const onCloseLog = async () => {
|
||||
emit('search');
|
||||
clearInterval(Number(timer));
|
||||
timer = null;
|
||||
};
|
||||
|
||||
const loadBuildDir = async (path: string) => {
|
||||
form.dockerfile = path;
|
||||
|
Loading…
Reference in New Issue
Block a user