mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-01-31 22:18:07 +08:00
feat: 拉取推送镜像增加实时日志
This commit is contained in:
parent
b1c2c4be83
commit
72b2633bdf
@ -71,12 +71,13 @@ func (b *BaseApi) ImagePull(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := imageService.ImagePull(req); err != nil {
|
logPath, err := imageService.ImagePull(req)
|
||||||
|
if err != nil {
|
||||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
helper.SuccessWithData(c, nil)
|
helper.SuccessWithData(c, logPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *BaseApi) ImagePush(c *gin.Context) {
|
func (b *BaseApi) ImagePush(c *gin.Context) {
|
||||||
@ -90,12 +91,13 @@ func (b *BaseApi) ImagePush(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := imageService.ImagePush(req); err != nil {
|
logPath, err := imageService.ImagePush(req)
|
||||||
|
if err != nil {
|
||||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
helper.SuccessWithData(c, nil)
|
helper.SuccessWithData(c, logPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *BaseApi) ImageRemove(c *gin.Context) {
|
func (b *BaseApi) ImageRemove(c *gin.Context) {
|
||||||
|
@ -2,13 +2,13 @@ package service
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||||
@ -19,15 +19,18 @@ import (
|
|||||||
"github.com/docker/docker/pkg/archive"
|
"github.com/docker/docker/pkg/archive"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const dockerLogDir = constant.TmpDir + "/docker_logs"
|
||||||
|
|
||||||
type ImageService struct{}
|
type ImageService struct{}
|
||||||
|
|
||||||
type IImageService interface {
|
type IImageService interface {
|
||||||
Page(req dto.PageInfo) (int64, interface{}, error)
|
Page(req dto.PageInfo) (int64, interface{}, error)
|
||||||
List() ([]dto.Options, error)
|
List() ([]dto.Options, error)
|
||||||
ImagePull(req dto.ImagePull) error
|
ImageBuild(req dto.ImageBuild) (string, error)
|
||||||
|
ImagePull(req dto.ImagePull) (string, error)
|
||||||
ImageLoad(req dto.ImageLoad) error
|
ImageLoad(req dto.ImageLoad) error
|
||||||
ImageSave(req dto.ImageSave) error
|
ImageSave(req dto.ImageSave) error
|
||||||
ImagePush(req dto.ImagePush) error
|
ImagePush(req dto.ImagePush) (string, error)
|
||||||
ImageRemove(req dto.BatchDelete) error
|
ImageRemove(req dto.BatchDelete) error
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,11 +141,13 @@ func (u *ImageService) ImageBuild(req dto.ImageBuild) (string, error) {
|
|||||||
}
|
}
|
||||||
go func() {
|
go func() {
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
|
defer tar.Close()
|
||||||
res, err := client.ImageBuild(context.TODO(), tar, opts)
|
res, err := client.ImageBuild(context.TODO(), tar, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
global.LOG.Errorf("build image %s failed, err: %v", req.Name, err)
|
global.LOG.Errorf("build image %s failed, err: %v", req.Name, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
global.LOG.Debugf("build image %s successful!", req.Name)
|
global.LOG.Debugf("build image %s successful!", req.Name)
|
||||||
_, _ = io.Copy(file, res.Body)
|
_, _ = io.Copy(file, res.Body)
|
||||||
}()
|
}()
|
||||||
@ -150,28 +155,38 @@ func (u *ImageService) ImageBuild(req dto.ImageBuild) (string, error) {
|
|||||||
return logName, nil
|
return logName, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *ImageService) ImagePull(req dto.ImagePull) error {
|
func (u *ImageService) ImagePull(req dto.ImagePull) (string, error) {
|
||||||
client, err := docker.NewDockerClient()
|
client, err := docker.NewDockerClient()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return "", err
|
||||||
|
}
|
||||||
|
if _, err := os.Stat(dockerLogDir); err != nil && os.IsNotExist(err) {
|
||||||
|
if err = os.MkdirAll(dockerLogDir, os.ModePerm); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
path := fmt.Sprintf("%s/image_pull_%s_%s.log", dockerLogDir, strings.ReplaceAll(req.ImageName, ":", "_"), time.Now().Format("20060102150405"))
|
||||||
|
file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
}
|
}
|
||||||
if req.RepoID == 0 {
|
if req.RepoID == 0 {
|
||||||
go func() {
|
go func() {
|
||||||
|
defer file.Close()
|
||||||
out, err := client.ImagePull(context.TODO(), req.ImageName, types.ImagePullOptions{})
|
out, err := client.ImagePull(context.TODO(), req.ImageName, types.ImagePullOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
global.LOG.Errorf("image %s pull failed, err: %v", req.ImageName, err)
|
global.LOG.Errorf("image %s pull failed, err: %v", req.ImageName, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer out.Close()
|
defer out.Close()
|
||||||
buf := new(bytes.Buffer)
|
global.LOG.Debugf("pull image %s successful!", req.ImageName)
|
||||||
_, _ = buf.ReadFrom(out)
|
_, _ = io.Copy(file, out)
|
||||||
global.LOG.Debugf("image %s pull stdout: %v", req.ImageName, buf.String())
|
|
||||||
}()
|
}()
|
||||||
return nil
|
return path, nil
|
||||||
}
|
}
|
||||||
repo, err := imageRepoRepo.Get(commonRepo.WithByID(req.RepoID))
|
repo, err := imageRepoRepo.Get(commonRepo.WithByID(req.RepoID))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return "", err
|
||||||
}
|
}
|
||||||
options := types.ImagePullOptions{}
|
options := types.ImagePullOptions{}
|
||||||
if repo.Auth {
|
if repo.Auth {
|
||||||
@ -181,24 +196,24 @@ func (u *ImageService) ImagePull(req dto.ImagePull) error {
|
|||||||
}
|
}
|
||||||
encodedJSON, err := json.Marshal(authConfig)
|
encodedJSON, err := json.Marshal(authConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return "", err
|
||||||
}
|
}
|
||||||
authStr := base64.URLEncoding.EncodeToString(encodedJSON)
|
authStr := base64.URLEncoding.EncodeToString(encodedJSON)
|
||||||
options.RegistryAuth = authStr
|
options.RegistryAuth = authStr
|
||||||
}
|
}
|
||||||
image := repo.DownloadUrl + "/" + req.ImageName
|
image := repo.DownloadUrl + "/" + req.ImageName
|
||||||
go func() {
|
go func() {
|
||||||
|
defer file.Close()
|
||||||
out, err := client.ImagePull(context.TODO(), image, options)
|
out, err := client.ImagePull(context.TODO(), image, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
global.LOG.Errorf("image %s pull failed, err: %v", image, err)
|
global.LOG.Errorf("image %s pull failed, err: %v", image, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer out.Close()
|
defer out.Close()
|
||||||
buf := new(bytes.Buffer)
|
global.LOG.Debugf("pull image %s successful!", req.ImageName)
|
||||||
_, _ = buf.ReadFrom(out)
|
_, _ = io.Copy(file, out)
|
||||||
global.LOG.Debugf("image %s pull stdout: %v", image, buf.String())
|
|
||||||
}()
|
}()
|
||||||
return nil
|
return path, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *ImageService) ImageLoad(req dto.ImageLoad) error {
|
func (u *ImageService) ImageLoad(req dto.ImageLoad) error {
|
||||||
@ -251,14 +266,14 @@ func (u *ImageService) ImageTag(req dto.ImageTag) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *ImageService) ImagePush(req dto.ImagePush) error {
|
func (u *ImageService) ImagePush(req dto.ImagePush) (string, error) {
|
||||||
client, err := docker.NewDockerClient()
|
client, err := docker.NewDockerClient()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return "", err
|
||||||
}
|
}
|
||||||
repo, err := imageRepoRepo.Get(commonRepo.WithByID(req.RepoID))
|
repo, err := imageRepoRepo.Get(commonRepo.WithByID(req.RepoID))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return "", err
|
||||||
}
|
}
|
||||||
options := types.ImagePushOptions{}
|
options := types.ImagePushOptions{}
|
||||||
if repo.Auth {
|
if repo.Auth {
|
||||||
@ -268,7 +283,7 @@ func (u *ImageService) ImagePush(req dto.ImagePush) error {
|
|||||||
}
|
}
|
||||||
encodedJSON, err := json.Marshal(authConfig)
|
encodedJSON, err := json.Marshal(authConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return "", err
|
||||||
}
|
}
|
||||||
authStr := base64.URLEncoding.EncodeToString(encodedJSON)
|
authStr := base64.URLEncoding.EncodeToString(encodedJSON)
|
||||||
options.RegistryAuth = authStr
|
options.RegistryAuth = authStr
|
||||||
@ -276,22 +291,33 @@ func (u *ImageService) ImagePush(req dto.ImagePush) error {
|
|||||||
newName := fmt.Sprintf("%s/%s", repo.DownloadUrl, req.Name)
|
newName := fmt.Sprintf("%s/%s", repo.DownloadUrl, req.Name)
|
||||||
if newName != req.TagName {
|
if newName != req.TagName {
|
||||||
if err := client.ImageTag(context.TODO(), req.TagName, newName); err != nil {
|
if err := client.ImageTag(context.TODO(), req.TagName, newName); err != nil {
|
||||||
return err
|
return "", err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _, err := os.Stat(dockerLogDir); err != nil && os.IsNotExist(err) {
|
||||||
|
if err = os.MkdirAll(dockerLogDir, os.ModePerm); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
path := fmt.Sprintf("%s/image_push_%s_%s.log", dockerLogDir, strings.ReplaceAll(req.Name, ":", "_"), time.Now().Format("20060102150405"))
|
||||||
|
file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
go func() {
|
go func() {
|
||||||
|
defer file.Close()
|
||||||
out, err := client.ImagePush(context.TODO(), newName, options)
|
out, err := client.ImagePush(context.TODO(), newName, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
global.LOG.Errorf("image %s push failed, err: %v", req.TagName, err)
|
global.LOG.Errorf("image %s push failed, err: %v", req.TagName, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer out.Close()
|
defer out.Close()
|
||||||
buf := new(bytes.Buffer)
|
global.LOG.Debugf("push image %s successful!", req.Name)
|
||||||
_, _ = buf.ReadFrom(out)
|
_, _ = io.Copy(file, out)
|
||||||
global.LOG.Debugf("image %s push stdout: %v", req.TagName, buf.String())
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return nil
|
return path, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *ImageService) ImageRemove(req dto.BatchDelete) error {
|
func (u *ImageService) ImageRemove(req dto.BatchDelete) error {
|
||||||
|
@ -32,10 +32,10 @@ export const imageBuild = (params: Container.ImageBuild) => {
|
|||||||
return http.post<string>(`/containers/image/build`, params);
|
return http.post<string>(`/containers/image/build`, params);
|
||||||
};
|
};
|
||||||
export const imagePull = (params: Container.ImagePull) => {
|
export const imagePull = (params: Container.ImagePull) => {
|
||||||
return http.post(`/containers/image/pull`, params);
|
return http.post<string>(`/containers/image/pull`, params);
|
||||||
};
|
};
|
||||||
export const imagePush = (params: Container.ImagePush) => {
|
export const imagePush = (params: Container.ImagePush) => {
|
||||||
return http.post(`/containers/image/push`, params);
|
return http.post<string>(`/containers/image/push`, params);
|
||||||
};
|
};
|
||||||
export const imageLoad = (params: Container.ImageLoad) => {
|
export const imageLoad = (params: Container.ImageLoad) => {
|
||||||
return http.post(`/containers/image/load`, params);
|
return http.post(`/containers/image/load`, params);
|
||||||
|
@ -1,5 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-dialog v-model="pullVisiable" :destroy-on-close="true" :close-on-click-modal="false" width="30%">
|
<el-dialog
|
||||||
|
v-model="pullVisiable"
|
||||||
|
@close="onCloseLog"
|
||||||
|
:destroy-on-close="true"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
width="50%"
|
||||||
|
>
|
||||||
<template #header>
|
<template #header>
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<span>{{ $t('container.imagePull') }}</span>
|
<span>{{ $t('container.imagePull') }}</span>
|
||||||
@ -25,10 +31,28 @@
|
|||||||
</el-input>
|
</el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
|
|
||||||
|
<codemirror
|
||||||
|
v-if="logVisiable"
|
||||||
|
:autofocus="true"
|
||||||
|
placeholder="Wait for pull output..."
|
||||||
|
:indent-with-tab="true"
|
||||||
|
:tabSize="4"
|
||||||
|
style="max-height: 300px"
|
||||||
|
:lineWrapping="true"
|
||||||
|
:matchBrackets="true"
|
||||||
|
theme="cobalt"
|
||||||
|
:styleActiveLine="true"
|
||||||
|
:extensions="extensions"
|
||||||
|
v-model="logInfo"
|
||||||
|
:readOnly="true"
|
||||||
|
/>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<span class="dialog-footer">
|
<span class="dialog-footer">
|
||||||
<el-button @click="pullVisiable = false">{{ $t('commons.button.cancel') }}</el-button>
|
<el-button :disabled="buttonDisabled" @click="pullVisiable = false">
|
||||||
<el-button type="primary" @click="onSubmit(formRef)">
|
{{ $t('commons.button.cancel') }}
|
||||||
|
</el-button>
|
||||||
|
<el-button :disabled="buttonDisabled" type="primary" @click="onSubmit(formRef)">
|
||||||
{{ $t('container.pull') }}
|
{{ $t('container.pull') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</span>
|
</span>
|
||||||
@ -43,6 +67,10 @@ import i18n from '@/lang';
|
|||||||
import { ElForm, ElMessage } from 'element-plus';
|
import { ElForm, ElMessage } from 'element-plus';
|
||||||
import { imagePull } from '@/api/modules/container';
|
import { imagePull } from '@/api/modules/container';
|
||||||
import { Container } from '@/api/interface/container';
|
import { Container } from '@/api/interface/container';
|
||||||
|
import { Codemirror } from 'vue-codemirror';
|
||||||
|
import { javascript } from '@codemirror/lang-javascript';
|
||||||
|
import { oneDark } from '@codemirror/theme-one-dark';
|
||||||
|
import { LoadFile } from '@/api/modules/files';
|
||||||
|
|
||||||
const pullVisiable = ref(false);
|
const pullVisiable = ref(false);
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
@ -51,6 +79,13 @@ const form = reactive({
|
|||||||
imageName: '',
|
imageName: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const buttonDisabled = ref(false);
|
||||||
|
|
||||||
|
const logVisiable = ref(false);
|
||||||
|
const logInfo = ref();
|
||||||
|
const extensions = [javascript(), oneDark];
|
||||||
|
let timer: NodeJS.Timer | null = null;
|
||||||
|
|
||||||
interface DialogProps {
|
interface DialogProps {
|
||||||
repos: Array<Container.RepoOptions>;
|
repos: Array<Container.RepoOptions>;
|
||||||
}
|
}
|
||||||
@ -64,6 +99,8 @@ const acceptParams = async (params: DialogProps): Promise<void> => {
|
|||||||
form.repoID = 1;
|
form.repoID = 1;
|
||||||
form.imageName = '';
|
form.imageName = '';
|
||||||
dialogData.value.repos = params.repos;
|
dialogData.value.repos = params.repos;
|
||||||
|
buttonDisabled.value = false;
|
||||||
|
logInfo.value = '';
|
||||||
};
|
};
|
||||||
|
|
||||||
const emit = defineEmits<{ (e: 'search'): void }>();
|
const emit = defineEmits<{ (e: 'search'): void }>();
|
||||||
@ -75,20 +112,31 @@ const onSubmit = async (formEl: FormInstance | undefined) => {
|
|||||||
if (!formEl) return;
|
if (!formEl) return;
|
||||||
formEl.validate(async (valid) => {
|
formEl.validate(async (valid) => {
|
||||||
if (!valid) return;
|
if (!valid) return;
|
||||||
try {
|
if (!form.fromRepo) {
|
||||||
if (!form.fromRepo) {
|
form.repoID = 0;
|
||||||
form.repoID = 0;
|
|
||||||
}
|
|
||||||
pullVisiable.value = false;
|
|
||||||
await imagePull(form);
|
|
||||||
emit('search');
|
|
||||||
ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
|
|
||||||
} catch {
|
|
||||||
emit('search');
|
|
||||||
}
|
}
|
||||||
|
const res = await imagePull(form);
|
||||||
|
logVisiable.value = true;
|
||||||
|
buttonDisabled.value = true;
|
||||||
|
loadLogs(res.data);
|
||||||
|
ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const loadLogs = async (path: string) => {
|
||||||
|
timer = setInterval(async () => {
|
||||||
|
if (logVisiable.value) {
|
||||||
|
const res = await LoadFile({ path: path });
|
||||||
|
logInfo.value = res.data;
|
||||||
|
}
|
||||||
|
}, 1000 * 3);
|
||||||
|
};
|
||||||
|
const onCloseLog = async () => {
|
||||||
|
emit('search');
|
||||||
|
clearInterval(Number(timer));
|
||||||
|
timer = null;
|
||||||
|
};
|
||||||
|
|
||||||
function loadDetailInfo(id: number) {
|
function loadDetailInfo(id: number) {
|
||||||
for (const item of dialogData.value.repos) {
|
for (const item of dialogData.value.repos) {
|
||||||
if (item.id === id) {
|
if (item.id === id) {
|
||||||
|
@ -1,5 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-dialog v-model="pushVisiable" :destroy-on-close="true" :close-on-click-modal="false" width="30%">
|
<el-dialog
|
||||||
|
v-model="pushVisiable"
|
||||||
|
@close="onCloseLog"
|
||||||
|
:destroy-on-close="true"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
width="50%"
|
||||||
|
>
|
||||||
<template #header>
|
<template #header>
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<span>{{ $t('container.imagePush') }}</span>
|
<span>{{ $t('container.imagePush') }}</span>
|
||||||
@ -22,10 +28,28 @@
|
|||||||
</el-input>
|
</el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
|
|
||||||
|
<codemirror
|
||||||
|
v-if="logVisiable"
|
||||||
|
:autofocus="true"
|
||||||
|
placeholder="Wait for pull output..."
|
||||||
|
:indent-with-tab="true"
|
||||||
|
:tabSize="4"
|
||||||
|
style="max-height: 300px"
|
||||||
|
:lineWrapping="true"
|
||||||
|
:matchBrackets="true"
|
||||||
|
theme="cobalt"
|
||||||
|
:styleActiveLine="true"
|
||||||
|
:extensions="extensions"
|
||||||
|
v-model="logInfo"
|
||||||
|
:readOnly="true"
|
||||||
|
/>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<span class="dialog-footer">
|
<span class="dialog-footer">
|
||||||
<el-button @click="pushVisiable = false">{{ $t('commons.button.cancel') }}</el-button>
|
<el-button :disabled="buttonDisabled" @click="pushVisiable = false">
|
||||||
<el-button type="primary" @click="onSubmit(formRef)">
|
{{ $t('commons.button.cancel') }}
|
||||||
|
</el-button>
|
||||||
|
<el-button :disabled="buttonDisabled" type="primary" @click="onSubmit(formRef)">
|
||||||
{{ $t('container.push') }}
|
{{ $t('container.push') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</span>
|
</span>
|
||||||
@ -40,6 +64,10 @@ import i18n from '@/lang';
|
|||||||
import { ElForm, ElMessage } from 'element-plus';
|
import { ElForm, ElMessage } from 'element-plus';
|
||||||
import { imagePush } from '@/api/modules/container';
|
import { imagePush } from '@/api/modules/container';
|
||||||
import { Container } from '@/api/interface/container';
|
import { Container } from '@/api/interface/container';
|
||||||
|
import { Codemirror } from 'vue-codemirror';
|
||||||
|
import { javascript } from '@codemirror/lang-javascript';
|
||||||
|
import { oneDark } from '@codemirror/theme-one-dark';
|
||||||
|
import { LoadFile } from '@/api/modules/files';
|
||||||
|
|
||||||
const pushVisiable = ref(false);
|
const pushVisiable = ref(false);
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
@ -49,6 +77,13 @@ const form = reactive({
|
|||||||
name: '',
|
name: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const buttonDisabled = ref(false);
|
||||||
|
|
||||||
|
const logVisiable = ref(false);
|
||||||
|
const logInfo = ref();
|
||||||
|
const extensions = [javascript(), oneDark];
|
||||||
|
let timer: NodeJS.Timer | null = null;
|
||||||
|
|
||||||
interface DialogProps {
|
interface DialogProps {
|
||||||
repos: Array<Container.RepoOptions>;
|
repos: Array<Container.RepoOptions>;
|
||||||
tags: Array<string>;
|
tags: Array<string>;
|
||||||
@ -76,17 +111,28 @@ const onSubmit = async (formEl: FormInstance | undefined) => {
|
|||||||
if (!formEl) return;
|
if (!formEl) return;
|
||||||
formEl.validate(async (valid) => {
|
formEl.validate(async (valid) => {
|
||||||
if (!valid) return;
|
if (!valid) return;
|
||||||
try {
|
const res = await imagePush(form);
|
||||||
pushVisiable.value = false;
|
logVisiable.value = true;
|
||||||
await imagePush(form);
|
buttonDisabled.value = true;
|
||||||
emit('search');
|
loadLogs(res.data);
|
||||||
ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
|
ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
|
||||||
} catch {
|
|
||||||
emit('search');
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const loadLogs = async (path: string) => {
|
||||||
|
timer = setInterval(async () => {
|
||||||
|
if (logVisiable.value) {
|
||||||
|
const res = await LoadFile({ path: path });
|
||||||
|
logInfo.value = res.data;
|
||||||
|
}
|
||||||
|
}, 1000 * 3);
|
||||||
|
};
|
||||||
|
const onCloseLog = async () => {
|
||||||
|
emit('search');
|
||||||
|
clearInterval(Number(timer));
|
||||||
|
timer = null;
|
||||||
|
};
|
||||||
|
|
||||||
function loadDetailInfo(id: number) {
|
function loadDetailInfo(id: number) {
|
||||||
for (const item of dialogData.value.repos) {
|
for (const item of dialogData.value.repos) {
|
||||||
if (item.id === id) {
|
if (item.id === id) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user