mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-12-19 14:01:28 +08:00
parent
e46d8c5812
commit
2a21f194d3
6 changed files with 111 additions and 145 deletions
|
|
@ -203,9 +203,8 @@ func (u *ContainerService) CreateCompose(req dto.ComposeCreate) error {
|
|||
}
|
||||
go func() {
|
||||
taskItem.AddSubTask(i18n.GetMsgByKey("ComposeCreate"), func(t *task.Task) error {
|
||||
cmd := getComposeCmd(req.Path, "up")
|
||||
out, err := cmd.CombinedOutput()
|
||||
taskItem.Log(i18n.GetWithName("ComposeCreateRes", string(out)))
|
||||
err := compose.UpWithTask(req.Path, t)
|
||||
t.LogWithStatus(i18n.GetMsgByKey("ComposeCreate"), err)
|
||||
if err != nil {
|
||||
_, _ = compose.Down(req.Path)
|
||||
return err
|
||||
|
|
|
|||
|
|
@ -1,11 +1,21 @@
|
|||
package compose
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/task"
|
||||
"github.com/1Panel-dev/1Panel/agent/buserr"
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
"github.com/1Panel-dev/1Panel/agent/i18n"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/cmd"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/common"
|
||||
"time"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/docker"
|
||||
"github.com/goccy/go-yaml"
|
||||
)
|
||||
|
||||
func checkCmd() error {
|
||||
|
|
@ -27,6 +37,58 @@ func Up(filePath string) (string, error) {
|
|||
return stdout, err
|
||||
}
|
||||
|
||||
func UpWithTask(filePath string, task *task.Task) error {
|
||||
content, err := os.ReadFile(filePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
env, _ := os.ReadFile(path.Join(path.Dir(filePath), ".env"))
|
||||
var compose docker.ComposeProject
|
||||
if err := yaml.Unmarshal(content, &compose); err != nil {
|
||||
return fmt.Errorf("parse docker-compose file failed: %v", err)
|
||||
}
|
||||
images, err := docker.GetImagesFromDockerCompose(env, content)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dockerCLi, err := docker.NewClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
errMsg := ""
|
||||
for _, image := range images {
|
||||
task.Log(i18n.GetWithName("PullImageStart", image))
|
||||
if err = dockerCLi.PullImageWithProcess(task, image); err != nil {
|
||||
errOur := err.Error()
|
||||
if errOur != "" {
|
||||
if strings.Contains(errOur, "no such host") {
|
||||
errMsg = i18n.GetMsgByKey("ErrNoSuchHost") + ":"
|
||||
}
|
||||
if strings.Contains(errOur, "Error response from daemon") {
|
||||
errMsg = i18n.GetMsgByKey("PullImageTimeout") + ":"
|
||||
}
|
||||
}
|
||||
message := errMsg + errOur
|
||||
installErr := errors.New(message)
|
||||
task.LogFailedWithErr(i18n.GetMsgByKey("PullImage"), installErr)
|
||||
if exist, _ := dockerCLi.ImageExists(image); !exist {
|
||||
return installErr
|
||||
} else {
|
||||
task.Log(i18n.GetMsgByKey("UseExistImage"))
|
||||
}
|
||||
} else {
|
||||
task.Log(i18n.GetMsgByKey("PullImageSuccess"))
|
||||
}
|
||||
}
|
||||
|
||||
dockerCommand := global.CONF.DockerConfig.Command
|
||||
if dockerCommand == "docker-compose" {
|
||||
return cmd.NewCommandMgr(cmd.WithTask(*task)).Run("docker-compose", "-f", filePath, "up", "-d")
|
||||
} else {
|
||||
return cmd.NewCommandMgr(cmd.WithTask(*task)).Run("docker", "compose", "-f", filePath, "up", "-d")
|
||||
}
|
||||
}
|
||||
|
||||
func Down(filePath string) (string, error) {
|
||||
if err := checkCmd(); err != nil {
|
||||
return "", err
|
||||
|
|
|
|||
|
|
@ -69,7 +69,12 @@
|
|||
</span>
|
||||
</template>
|
||||
</DrawerPro>
|
||||
<TaskLog ref="taskLogRef" width="70%" />
|
||||
<TaskLog ref="taskLogRef" width="70%">
|
||||
<template #task-footer>
|
||||
<el-button @click="handleClose">{{ $t('commons.table.backToList') }}</el-button>
|
||||
<el-button type="primary" @click="closeTask">{{ $t('commons.table.keepEdit') }}</el-button>
|
||||
</template>
|
||||
</TaskLog>
|
||||
<FileList ref="fileRef" @choose="loadDir" />
|
||||
</template>
|
||||
|
||||
|
|
@ -167,8 +172,12 @@ const changeFrom = () => {
|
|||
|
||||
const handleClose = () => {
|
||||
emit('search');
|
||||
taskLogRef.value?.handleClose();
|
||||
drawerVisible.value = false;
|
||||
};
|
||||
const closeTask = () => {
|
||||
taskLogRef.value?.handleClose();
|
||||
};
|
||||
|
||||
const loadPath = async () => {
|
||||
const pathRes = await loadBaseDir();
|
||||
|
|
|
|||
|
|
@ -1,119 +0,0 @@
|
|||
<template>
|
||||
<DrawerPro
|
||||
v-model="composeVisible"
|
||||
:header="$t('commons.button.edit')"
|
||||
@close="handleClose"
|
||||
:resource="name"
|
||||
size="large"
|
||||
:autoClose="false"
|
||||
:fullScreen="true"
|
||||
>
|
||||
<div v-loading="loading">
|
||||
<el-form ref="formRef" @submit.prevent label-position="top">
|
||||
<el-form-item>
|
||||
<CodemirrorPro
|
||||
v-model="content"
|
||||
mode="yaml"
|
||||
:heightDiff="175"
|
||||
placeholder="#Define or paste the content of your docker-compose file here"
|
||||
></CodemirrorPro>
|
||||
</el-form-item>
|
||||
<div v-if="createdBy === '1Panel'">
|
||||
<el-form-item :label="$t('container.env')" prop="environmentStr">
|
||||
<el-input
|
||||
type="textarea"
|
||||
:placeholder="$t('container.tagHelper')"
|
||||
:rows="3"
|
||||
v-model="environmentStr"
|
||||
/>
|
||||
</el-form-item>
|
||||
<span class="input-help whitespace-break-spaces">
|
||||
{{ $t('container.editComposeHelper') }}
|
||||
</span>
|
||||
<CodemirrorPro
|
||||
v-model="envFileContent"
|
||||
:height="45"
|
||||
:minHeight="45"
|
||||
disabled
|
||||
mode="yaml"
|
||||
></CodemirrorPro>
|
||||
</div>
|
||||
</el-form>
|
||||
</div>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button :disabled="loading" @click="composeVisible = false">
|
||||
{{ $t('commons.button.cancel') }}
|
||||
</el-button>
|
||||
<el-button :disabled="loading" type="primary" @click="onSubmitEdit()">
|
||||
{{ $t('commons.button.confirm') }}
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</DrawerPro>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
import { composeUpdate } from '@/api/modules/container';
|
||||
import i18n from '@/lang';
|
||||
import { MsgSuccess } from '@/utils/message';
|
||||
|
||||
const loading = ref(false);
|
||||
const composeVisible = ref(false);
|
||||
const path = ref();
|
||||
const content = ref();
|
||||
const name = ref();
|
||||
const environmentStr = ref();
|
||||
const environmentEnv = ref();
|
||||
const createdBy = ref();
|
||||
const envFileContent = ref(`env_file:\n - 1panel.env`);
|
||||
|
||||
const emit = defineEmits<{ (e: 'search'): void }>();
|
||||
|
||||
const onSubmitEdit = async () => {
|
||||
const param = {
|
||||
name: name.value,
|
||||
path: path.value,
|
||||
content: content.value,
|
||||
createdBy: createdBy.value,
|
||||
env: environmentStr.value?.split('\n') || [],
|
||||
};
|
||||
loading.value = true;
|
||||
await composeUpdate(param)
|
||||
.then(() => {
|
||||
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||
composeVisible.value = false;
|
||||
if (environmentStr.value) {
|
||||
emit('search');
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
};
|
||||
|
||||
interface DialogProps {
|
||||
name: string;
|
||||
path: string;
|
||||
content: string;
|
||||
env: Array<string>;
|
||||
envStr: string;
|
||||
createdBy: string;
|
||||
}
|
||||
const acceptParams = (props: DialogProps): void => {
|
||||
composeVisible.value = true;
|
||||
path.value = props.path;
|
||||
name.value = props.name;
|
||||
content.value = props.content;
|
||||
createdBy.value = props.createdBy;
|
||||
environmentEnv.value = props.env || [];
|
||||
environmentStr.value = environmentEnv.value.join('\n');
|
||||
};
|
||||
const handleClose = () => {
|
||||
composeVisible.value = false;
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
acceptParams,
|
||||
});
|
||||
</script>
|
||||
|
|
@ -13,14 +13,19 @@
|
|||
{{ $t('container.createCompose') }}
|
||||
</el-button>
|
||||
</template>
|
||||
<template #rightToolBar>
|
||||
<TableRefresh @search="search()" />
|
||||
<TableSetting title="container-refresh" @search="search()" />
|
||||
</template>
|
||||
<template #main>
|
||||
<el-row v-if="data.length > 0" :gutter="20" class="row-box">
|
||||
<el-col :span="6">
|
||||
<el-col :span="7">
|
||||
<el-card>
|
||||
<el-input
|
||||
v-model="searchName"
|
||||
:placeholder="$t('commons.button.search')"
|
||||
clearable
|
||||
class="w-4/5"
|
||||
@clear="search"
|
||||
@keyup.enter="search"
|
||||
>
|
||||
|
|
@ -29,17 +34,32 @@
|
|||
</template>
|
||||
</el-input>
|
||||
|
||||
<ComplexTable :show-header="false" @row-click="loadDetail" :data="data">
|
||||
<el-table class="mt-2" :show-header="false" @row-click="loadDetail" :data="data">
|
||||
<el-table-column prop="name">
|
||||
<template #default="{ row }">
|
||||
<div class="cursor-pointer">
|
||||
<div class="font-medium truncate">
|
||||
<div class="font-medium text-base">
|
||||
{{ row.name }}
|
||||
</div>
|
||||
<div class="mb-1">
|
||||
<el-text class="w-12" link size="small" type="info">
|
||||
{{ loadFrom(row) }}
|
||||
</el-text>
|
||||
<el-divider direction="vertical" />
|
||||
<el-text v-if="row.containerCount === 0" type="danger" size="small">
|
||||
<el-text link size="small" type="info" class="ml-2">
|
||||
{{ row.createdAt }}
|
||||
</el-text>
|
||||
<el-divider direction="vertical" />
|
||||
<el-text
|
||||
link
|
||||
v-if="row.containerCount === 0"
|
||||
type="danger"
|
||||
size="small"
|
||||
>
|
||||
{{ $t('container.exited') }}
|
||||
</el-text>
|
||||
<el-text
|
||||
link
|
||||
v-else
|
||||
:type="
|
||||
row.containerCount === row.runningCount ? 'success' : 'warning'
|
||||
|
|
@ -50,21 +70,16 @@
|
|||
$t('container.running', [row.runningCount, row.containerCount])
|
||||
}}
|
||||
</el-text>
|
||||
<el-divider direction="vertical" />
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
icon="Folder"
|
||||
:disabled="!currentCompose?.workdir"
|
||||
@click="openComposeFolder"
|
||||
/>
|
||||
</div>
|
||||
<div class="mt-1 mb-2">
|
||||
<el-tag size="small" type="info">{{ loadFrom(row) }}</el-tag>
|
||||
<el-tag size="small" type="info" class="ml-2">
|
||||
{{ row.createdAt }}
|
||||
</el-tag>
|
||||
</div>
|
||||
<el-button
|
||||
plain
|
||||
round
|
||||
size="small"
|
||||
:disabled="!currentCompose?.workdir"
|
||||
@click="openComposeFolder"
|
||||
>
|
||||
{{ $t('home.dir') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
plain
|
||||
round
|
||||
|
|
@ -101,10 +116,10 @@
|
|||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</ComplexTable>
|
||||
</el-table>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="18">
|
||||
<el-col :span="17">
|
||||
<el-card v-if="currentCompose" v-loading="detailLoading">
|
||||
<el-table
|
||||
v-if="composeContainers.length > 0"
|
||||
|
|
|
|||
|
|
@ -365,7 +365,7 @@
|
|||
<TerminalDialog ref="dialogTerminalRef" />
|
||||
|
||||
<PortJumpDialog ref="dialogPortJumpRef" />
|
||||
<TaskLog ref="taskLogRef" width="70%" />
|
||||
<TaskLog ref="taskLogRef" width="70%" @close="search" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue