1
0
mirror of https://github.com/1Panel-dev/1Panel.git synced 2025-01-19 08:19:15 +08:00

feat: http 仓库添加时增加授信操作

This commit is contained in:
ssongliu 2022-10-10 16:47:05 +08:00 committed by ssongliu
parent 2cb1b57069
commit f98f9a5872
14 changed files with 140 additions and 61 deletions

View File

@ -94,7 +94,7 @@ func (b *BaseApi) UpdateRepo(c *gin.Context) {
upMap := make(map[string]interface{})
upMap["download_url"] = req.DownloadUrl
upMap["repo_name"] = req.RepoName
upMap["protocol"] = req.Protocol
upMap["username"] = req.Username
upMap["password"] = req.Password
upMap["auth"] = req.Auth

View File

@ -19,7 +19,7 @@ type ImageRemove struct {
}
type ImagePull struct {
RepoID uint `josn:"repoID" validate:"required"`
RepoID uint `josn:"repoID"`
ImageName string `josn:"imageName" validate:"required"`
}

View File

@ -5,7 +5,7 @@ import "time"
type ImageRepoCreate struct {
Name string `json:"name" validate:"required"`
DownloadUrl string `json:"downloadUrl"`
RepoName string `json:"repoName"`
Protocol string `json:"protocol"`
Username string `json:"username"`
Password string `json:"password"`
Auth bool `json:"auth"`
@ -14,7 +14,7 @@ type ImageRepoCreate struct {
type ImageRepoUpdate struct {
ID uint `json:"id"`
DownloadUrl string `json:"downloadUrl"`
RepoName string `json:"repoName"`
Protocol string `json:"protocol"`
Username string `json:"username"`
Password string `json:"password"`
Auth bool `json:"auth"`
@ -25,7 +25,7 @@ type ImageRepoInfo struct {
CreatedAt time.Time `json:"createdAt"`
Name string `json:"name"`
DownloadUrl string `json:"downloadUrl"`
RepoName string `json:"repoName"`
Protocol string `json:"protocol"`
Username string `json:"username"`
Auth bool `json:"auth"`
}

View File

@ -5,7 +5,7 @@ type ImageRepo struct {
Name string `gorm:"type:varchar(64);not null" json:"name"`
DownloadUrl string `gorm:"type:varchar(256)" json:"downloadUrl"`
RepoName string `gorm:"type:varchar(256)" json:"repoName"`
Protocol string `gorm:"type:varchar(64)" json:"protocol"`
Username string `gorm:"type:varchar(256)" json:"username"`
Password string `gorm:"type:varchar(256)" json:"password"`
Auth bool `gorm:"type:varchar(256)" json:"auth"`

View File

@ -78,7 +78,20 @@ func (u *ImageService) ImagePull(req dto.ImagePull) error {
if err != nil {
return err
}
ctx := context.Background()
if req.RepoID == 0 {
go func() {
out, err := client.ImagePull(context.TODO(), req.ImageName, types.ImagePullOptions{})
if err != nil {
global.LOG.Errorf("image %s pull failed, err: %v", req.ImageName, err)
return
}
defer out.Close()
buf := new(bytes.Buffer)
_, _ = buf.ReadFrom(out)
global.LOG.Debugf("image %s pull stdout: %v", req.ImageName, buf.String())
}()
return nil
}
repo, err := imageRepoRepo.Get(commonRepo.WithByID(req.RepoID))
if err != nil {
return err
@ -97,11 +110,8 @@ func (u *ImageService) ImagePull(req dto.ImagePull) error {
options.RegistryAuth = authStr
}
image := repo.DownloadUrl + "/" + req.ImageName
if len(repo.RepoName) != 0 {
image = fmt.Sprintf("%s/%s/%s", repo.DownloadUrl, repo.RepoName, req.ImageName)
}
go func() {
out, err := client.ImagePull(ctx, image, options)
out, err := client.ImagePull(context.TODO(), image, options)
if err != nil {
global.LOG.Errorf("image %s pull failed, err: %v", image, err)
return

View File

@ -1,6 +1,9 @@
package service
import (
"encoding/json"
"io/ioutil"
"github.com/1Panel-dev/1Panel/app/dto"
"github.com/1Panel-dev/1Panel/constant"
"github.com/jinzhu/copier"
@ -48,10 +51,34 @@ func (u *ImageRepoService) List() ([]dto.ImageRepoOption, error) {
}
func (u *ImageRepoService) Create(imageRepoDto dto.ImageRepoCreate) error {
imageRepo, _ := imageRepoRepo.Get(commonRepo.WithByName(imageRepoDto.RepoName))
imageRepo, _ := imageRepoRepo.Get(commonRepo.WithByName(imageRepoDto.Name))
if imageRepo.ID != 0 {
return constant.ErrRecordExist
}
if imageRepo.Protocol == "http" {
file, err := ioutil.ReadFile(constant.DaemonJsonDir)
if err != nil {
return err
}
deamonMap := make(map[string]interface{})
if err := json.Unmarshal(file, &deamonMap); err != nil {
return err
}
if _, ok := deamonMap["insecure-registries"]; ok {
if k, v := deamonMap["insecure-registries"].([]interface{}); v {
k = append(k, imageRepoDto.DownloadUrl)
deamonMap["insecure-registries"] = k
}
}
newJson, err := json.Marshal(deamonMap)
if err != nil {
return err
}
if err := ioutil.WriteFile(constant.DaemonJsonDir, newJson, 0777); err != nil {
return err
}
}
if err := copier.Copy(&imageRepo, &imageRepoDto); err != nil {
return errors.WithMessage(constant.ErrStructTransform, err.Error())
}
@ -61,6 +88,10 @@ func (u *ImageRepoService) Create(imageRepoDto dto.ImageRepoCreate) error {
return nil
}
type DeamonJson struct {
InsecureRegistries []string `json:"insecure-registries"`
}
func (u *ImageRepoService) BatchDelete(ids []uint) error {
for _, id := range ids {
if id == 1 {

View File

@ -2,11 +2,14 @@ package service
import (
"context"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"os"
"testing"
"github.com/1Panel-dev/1Panel/constant"
"github.com/1Panel-dev/1Panel/utils/docker"
)
@ -28,3 +31,34 @@ func TestImage(t *testing.T) {
fmt.Println(err)
}
}
func TestDeam(t *testing.T) {
file, err := ioutil.ReadFile(constant.DaemonJsonDir)
if err != nil {
fmt.Println(err)
}
deamonMap := make(map[string]interface{})
err = json.Unmarshal(file, &deamonMap)
fmt.Println(err)
for k, v := range deamonMap {
fmt.Println(k, v)
}
if _, ok := deamonMap["insecure-registries"]; ok {
if k, v := deamonMap["insecure-registries"].(string); v {
fmt.Println("string ", k)
}
if k, v := deamonMap["insecure-registries"].([]interface{}); v {
fmt.Println("[]string ", k)
k = append(k, "172.16.10.111:8085")
deamonMap["insecure-registries"] = k
}
}
newss, err := json.Marshal(deamonMap)
if err != nil {
fmt.Println(err)
}
fmt.Println(string(newss))
if err := ioutil.WriteFile(constant.DaemonJsonDir, newss, 0777); err != nil {
fmt.Println(err)
}
}

View File

@ -9,4 +9,6 @@ const (
ContainerOpUnpause = "unPause"
ContainerOpRename = "reName"
ContainerOpRemove = "remove"
DaemonJsonDir = "/System/Volumes/Data/Users/slooop/.docker/daemon.json"
)

View File

@ -53,7 +53,7 @@ export namespace Container {
export interface RepoCreate {
name: string;
downloadUrl: string;
repoName: string;
protocol: string;
username: string;
password: string;
auth: boolean;
@ -61,6 +61,7 @@ export namespace Container {
export interface RepoUpdate {
id: number;
downloadUrl: string;
protocol: string;
username: string;
password: string;
auth: boolean;
@ -70,7 +71,7 @@ export namespace Container {
createdAt: Date;
name: string;
downloadUrl: string;
repoName: string;
protocol: string;
username: string;
password: string;
auth: boolean;

View File

@ -172,7 +172,6 @@ export default {
last10Min: 'Last 10 Minutes',
image: 'Image',
pullFromRepo: 'Pull from repo',
imagePull: 'Image pull',
imagePush: 'Image push',
repoName: 'Repo Name',
@ -189,11 +188,13 @@ export default {
exportImage: 'ExportImage',
version: 'Version',
size: 'Size',
from: 'From',
repo: 'Repo',
name: 'Name',
protocol: 'protocol',
downloadUrl: 'Download URL',
imageRepo: 'ImageRepo',
imageRepo: 'Image repo',
repoHelper: 'Does it include a mirror repository/organization/project?',
auth: 'Auth',
},

View File

@ -169,7 +169,6 @@ export default {
last10Min: '最近 10 分钟',
image: '镜像',
pullFromRepo: '从仓库中拉取',
imagePull: '镜像拉取',
imagePush: '镜像推送',
repoName: '仓库名',
@ -186,11 +185,13 @@ export default {
exportImage: '导出镜像',
version: '版本',
size: '大小',
from: '来源',
repo: '仓库',
name: '名称',
protocol: '协议',
downloadUrl: '下载地址',
imageRepo: '镜像',
imageRepo: '镜像',
repoHelper: '是否包含镜像仓库/组织/项目?',
auth: '认证',
},

View File

@ -3,10 +3,10 @@
<el-card style="margin-top: 20px">
<ComplexTable :pagination-config="paginationConfig" v-model:selects="selects" :data="data" @search="search">
<template #toolbar>
<el-button type="primary" @click="pullVisiable = true">
{{ $t('container.pullFromRepo') }}
<el-button @click="onOpenPull">
{{ $t('container.imagePull') }}
</el-button>
<el-button @click="loadVisiable = true">
<el-button @click="onOpenload">
{{ $t('container.importImage') }}
</el-button>
<el-button @click="onBatchDelete(null)">
@ -30,28 +30,30 @@
</ComplexTable>
</el-card>
<el-dialog v-model="pullVisiable" :destroy-on-close="true" :close-on-click-modal="false" width="50%">
<el-dialog v-model="pullVisiable" :destroy-on-close="true" :close-on-click-modal="false" width="30%">
<template #header>
<div class="card-header">
<span>{{ $t('container.imagePull') }}</span>
</div>
</template>
<el-form ref="pullFormRef" :model="pullForm" label-width="80px">
<el-form-item :label="$t('container.repoName')" :rules="Rules.requiredSelect" prop="repoID">
<el-form-item :label="$t('container.from')">
<el-checkbox v-model="pullForm.fromRepo">{{ $t('container.imageRepo') }}</el-checkbox>
</el-form-item>
<el-form-item
v-if="pullForm.fromRepo"
:label="$t('container.repoName')"
:rules="Rules.requiredSelect"
prop="repoID"
>
<el-select style="width: 100%" filterable v-model="pullForm.repoID">
<el-option
v-for="item in repos"
:key="item.id"
:value="item.id"
:label="item.name + ' [ ' + item.downloadUrl + ' ] '"
/>
<el-option v-for="item in repos" :key="item.id" :value="item.id" :label="item.name" />
</el-select>
</el-form-item>
<el-form-item :label="$t('container.imageName')" :rules="Rules.requiredInput" prop="imageName">
<el-input v-model="pullForm.imageName"></el-input>
</el-form-item>
<el-form-item v-if="pullForm.imageName !== ''">
<el-tag>docker pull {{ loadDetailInfo(pullForm.repoID) }}/{{ pullForm.imageName }}</el-tag>
<el-input v-model="pullForm.imageName">
<template v-if="pullForm.fromRepo" #prepend>{{ loadDetailInfo(pullForm.repoID) }}/</template>
</el-input>
</el-form-item>
</el-form>
<template #footer>
@ -73,24 +75,13 @@
<el-form ref="pushFormRef" :model="pushForm" label-width="80px">
<el-form-item :label="$t('container.repoName')" :rules="Rules.requiredSelect" prop="repoID">
<el-select style="width: 100%" filterable v-model="pushForm.repoID">
<el-option
v-for="item in repos"
:key="item.id"
:value="item.id"
:label="item.name + ' [ ' + item.downloadUrl + ' ] '"
/>
<el-option v-for="item in repos" :key="item.id" :value="item.id" :label="item.name" />
</el-select>
</el-form-item>
<el-form-item :label="$t('container.label')" :rules="Rules.requiredInput" prop="tagName">
<el-input v-model="pushForm.tagName"></el-input>
</el-form-item>
<el-form-item v-if="pushForm.tagName !== ''">
<el-tag>
docker tag {{ pushForm.imageName }} {{ loadDetailInfo(pushForm.repoID) }}/{{ pushForm.tagName }}
</el-tag>
</el-form-item>
<el-form-item v-if="pushForm.tagName !== ''">
<el-tag>docker push {{ loadDetailInfo(pushForm.repoID) }}/{{ pushForm.tagName }}</el-tag>
<el-input v-model="pushForm.tagName">
<template #prepend>{{ loadDetailInfo(pushForm.repoID) }}/</template>
</el-input>
</el-form-item>
</el-form>
<template #footer>
@ -198,6 +189,7 @@ type FormInstance = InstanceType<typeof ElForm>;
const pullVisiable = ref(false);
const pullFormRef = ref<FormInstance>();
const pullForm = reactive({
fromRepo: true,
repoID: 1,
imageName: '',
});
@ -248,12 +240,20 @@ const loadLoadDir = async (path: string) => {
loadForm.path = path;
};
const onOpenPull = () => {
pullVisiable.value = true;
pullForm.imageName = '';
pullForm.repoID = 1;
};
const submitPull = async (formEl: FormInstance | undefined) => {
if (!formEl) return;
formEl.validate(async (valid) => {
if (!valid) return;
try {
loading.value = true;
if (!pullForm.fromRepo) {
pullForm.repoID = 0;
}
pullVisiable.value = false;
await imagePull(pullForm);
loading.value = false;
@ -284,6 +284,10 @@ const submitPush = async (formEl: FormInstance | undefined) => {
});
};
const onOpenload = () => {
loadVisiable.value = true;
loadForm.path = '';
};
const submitLoad = async (formEl: FormInstance | undefined) => {
if (!formEl) return;
formEl.validate(async (valid) => {

View File

@ -19,13 +19,7 @@
min-width="100"
fix
/>
<el-table-column
:label="$t('container.imageRepo')"
show-overflow-tooltip
prop="repoName"
min-width="70"
fix
/>
<el-table-column :label="$t('container.protocol')" prop="protocol" min-width="60" fix />
<el-table-column :label="$t('commons.table.createdAt')" min-width="80" fix>
<template #default="{ row }">
{{ dateFromat(0, 0, row.createdAt) }}
@ -78,6 +72,7 @@ const onOpenDialog = async (
title: string,
rowData: Partial<Container.RepoInfo> = {
auth: true,
protocol: 'http',
},
) => {
let params = {

View File

@ -24,9 +24,11 @@
<el-form-item :label="$t('container.downloadUrl')" prop="downloadUrl">
<el-input v-model="dialogData.rowData!.downloadUrl" :placeholder="'172.16.10.10:8081'"></el-input>
</el-form-item>
<el-form-item :label="$t('container.imageRepo')" prop="repoName">
<el-checkbox v-model="hasRepo">{{ $t('container.repoHelper') }}</el-checkbox>
<el-input v-if="hasRepo" v-model="dialogData.rowData!.repoName"></el-input>
<el-form-item :label="$t('container.protocol')" prop="protocol">
<el-radio-group v-model="dialogData.rowData!.protocol">
<el-radio label="http">http</el-radio>
<el-radio label="https">https</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<template #footer>
@ -61,16 +63,14 @@ const dialogData = ref<DialogProps>({
const acceptParams = (params: DialogProps): void => {
dialogData.value = params;
title.value = i18n.global.t('commons.button.' + dialogData.value.title);
hasRepo.value = params.rowData?.repoName ? params.rowData?.repoName.length !== 0 : false;
repoVisiable.value = true;
};
const emit = defineEmits<{ (e: 'search'): void }>();
const hasRepo = ref(false);
const rules = reactive({
name: [Rules.requiredInput, Rules.name],
downloadUrl: [Rules.requiredInput],
repoName: [Rules.requiredInput],
protocol: [Rules.requiredSelect],
username: [Rules.requiredInput],
password: [Rules.requiredInput],
auth: [Rules.requiredSelect],