fix: 镜像仓库增加同步功能

pull/165/head
ssongliu 2023-02-27 11:46:23 +08:00 committed by ssongliu
parent 8b9cc1bee8
commit b3a69725bf
12 changed files with 280 additions and 165 deletions

View File

@ -57,11 +57,37 @@ func (b *BaseApi) ListRepo(c *gin.Context) {
helper.SuccessWithData(c, list)
}
// @Tags Container Image-repo
// @Summary Load repo status
// @Description 获取 docker 仓库状态
// @Accept json
// @Param request body dto.OperateByID true "request"
// @Produce json
// @Success 200
// @Security ApiKeyAuth
// @Router /containers/repo/status [get]
func (b *BaseApi) CheckRepoStatus(c *gin.Context) {
var req dto.OperateByID
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 := imageRepoService.Login(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags Container Image-repo
// @Summary Create image repo
// @Description 创建镜像仓库
// @Accept json
// @Param request body dto.ImageRepoCreate true "request"
// @Param request body dto.ImageRepoDelete true "request"
// @Produce json
// @Success 200
// @Security ApiKeyAuth

View File

@ -23,6 +23,7 @@ type ImageRepoService struct{}
type IImageRepoService interface {
Page(search dto.SearchWithPage) (int64, interface{}, error)
List() ([]dto.ImageRepoOption, error)
Login(req dto.OperateByID) error
Create(req dto.ImageRepoCreate) error
Update(req dto.ImageRepoUpdate) error
BatchDelete(req dto.ImageRepoDelete) error
@ -45,6 +46,19 @@ func (u *ImageRepoService) Page(req dto.SearchWithPage) (int64, interface{}, err
return total, dtoOps, err
}
func (u *ImageRepoService) Login(req dto.OperateByID) error {
repo, err := imageRepoRepo.Get(commonRepo.WithByID(req.ID))
if err != nil {
return err
}
if err := u.CheckConn(repo.DownloadUrl, repo.Username, repo.Password); err != nil {
_ = imageRepoRepo.Update(repo.ID, map[string]interface{}{"status": constant.StatusFailed, "message": err.Error})
return err
}
_ = imageRepoRepo.Update(repo.ID, map[string]interface{}{"status": constant.StatusSuccess})
return nil
}
func (u *ImageRepoService) List() ([]dto.ImageRepoOption, error) {
ops, err := imageRepoRepo.List(commonRepo.WithOrderBy("created_at desc"))
var dtoOps []dto.ImageRepoOption
@ -67,22 +81,30 @@ func (u *ImageRepoService) Create(req dto.ImageRepoCreate) error {
}
if req.Protocol == "http" {
_ = u.handleRegistries(req.DownloadUrl, "", "create")
stdout, err := cmd.Exec("systemctl restart docker")
if err != nil {
return errors.New(string(stdout))
}
ticker := time.NewTicker(3 * time.Second)
ctx, cancle := context.WithTimeout(context.Background(), time.Second*20)
for range ticker.C {
select {
case <-ctx.Done():
cancle()
return errors.New("the docker service cannot be restarted")
default:
stdout, err := cmd.Exec("systemctl is-active docker")
if string(stdout) == "active\n" && err == nil {
global.LOG.Info("docker restart with new conf successful!")
if err := func() error {
for range ticker.C {
select {
case <-ctx.Done():
cancle()
return errors.New("the docker service cannot be restarted")
default:
stdout, err := cmd.Exec("systemctl is-active docker")
if string(stdout) == "active\n" && err == nil {
global.LOG.Info("docker restart with new conf successful!")
return nil
}
}
}
return nil
}(); err != nil {
return err
}
cancle()
}
if err := copier.Copy(&imageRepo, &req); err != nil {
@ -90,7 +112,7 @@ func (u *ImageRepoService) Create(req dto.ImageRepoCreate) error {
}
imageRepo.Status = constant.StatusSuccess
if err := u.checkConn(req.DownloadUrl, req.Username, req.Password); err != nil {
if err := u.CheckConn(req.DownloadUrl, req.Username, req.Password); err != nil {
imageRepo.Status = constant.StatusFailed
imageRepo.Message = err.Error()
}
@ -141,14 +163,14 @@ func (u *ImageRepoService) Update(req dto.ImageRepoUpdate) error {
upMap["status"] = constant.StatusSuccess
upMap["message"] = ""
if err := u.checkConn(req.DownloadUrl, req.Username, req.Password); err != nil {
if err := u.CheckConn(req.DownloadUrl, req.Username, req.Password); err != nil {
upMap["status"] = constant.StatusFailed
upMap["message"] = err.Error()
}
return imageRepoRepo.Update(req.ID, upMap)
}
func (u *ImageRepoService) checkConn(host, user, password string) error {
func (u *ImageRepoService) CheckConn(host, user, password string) error {
stdout, err := cmd.Execf("docker login -u %s -p %s %s", user, password, host)
if err != nil {
return errors.New(string(stdout))

View File

@ -25,6 +25,7 @@ func (s *ContainerRouter) InitContainerRouter(Router *gin.RouterGroup) {
baRouter.POST("/operate", baseApi.ContainerOperation)
baRouter.GET("/repo", baseApi.ListRepo)
baRouter.POST("/repo/status", baseApi.CheckRepoStatus)
baRouter.POST("/repo/search", baseApi.SearchRepo)
baRouter.POST("/repo/update", baseApi.UpdateRepo)
baRouter.POST("/repo", baseApi.CreateRepo)

View File

@ -1947,7 +1947,7 @@ var doc = `{
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.ImageRepoCreate"
"$ref": "#/definitions/dto.ImageRepoDelete"
}
}
],
@ -2060,6 +2060,42 @@ var doc = `{
}
}
},
"/containers/repo/status": {
"get": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "获取 docker 仓库状态",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Container Image-repo"
],
"summary": "Load repo status",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.OperateByID"
}
}
],
"responses": {
"200": {
"description": ""
}
}
}
},
"/containers/repo/update": {
"post": {
"security": [
@ -5475,18 +5511,18 @@ var doc = `{
}
}
},
"/nginx": {
"/openResty": {
"get": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "获取 nginx 配置信息",
"description": "获取 OpenResty 配置信息",
"tags": [
"Nginx"
"OpenResty"
],
"summary": "Load nginx conf",
"summary": "Load OpenResty conf",
"responses": {
"200": {
"description": "OK",
@ -5497,21 +5533,21 @@ var doc = `{
}
}
},
"/nginx/file": {
"/openResty/file": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "上传更新 nginx 配置文件",
"description": "上传更新 OpenResty 配置文件",
"consumes": [
"application/json"
],
"tags": [
"Nginx"
"OpenResty"
],
"summary": "Update nginx conf by upload file",
"summary": "Update OpenResty conf by upload file",
"parameters": [
{
"description": "request",
@ -5537,21 +5573,21 @@ var doc = `{
}
}
},
"/nginx/scope": {
"/openResty/scope": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "获取部分 nginx 配置信息",
"description": "获取部分 OpenResty 配置信息",
"consumes": [
"application/json"
],
"tags": [
"Nginx"
"OpenResty"
],
"summary": "Load partial nginx conf",
"summary": "Load partial OpenResty conf",
"parameters": [
{
"description": "request",
@ -5573,18 +5609,18 @@ var doc = `{
}
}
},
"/nginx/status": {
"/openResty/status": {
"get": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "获取 nginx 状态信息",
"description": "获取 OpenResty 状态信息",
"tags": [
"Nginx"
"OpenResty"
],
"summary": "Load nginx status info",
"summary": "Load OpenResty status info",
"responses": {
"200": {
"description": "OK",
@ -5595,21 +5631,21 @@ var doc = `{
}
}
},
"/nginx/update": {
"/openResty/update": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "更新 nginx 配置信息",
"description": "更新 OpenResty 配置信息",
"consumes": [
"application/json"
],
"tags": [
"Nginx"
"OpenResty"
],
"summary": "Update nginx conf",
"summary": "Update OpenResty conf",
"parameters": [
{
"description": "request",
@ -9589,41 +9625,12 @@ var doc = `{
}
}
},
"dto.ImageRepoCreate": {
"type": "object",
"required": [
"name"
],
"properties": {
"auth": {
"type": "boolean"
},
"downloadUrl": {
"type": "string"
},
"name": {
"type": "string"
},
"password": {
"type": "string"
},
"protocol": {
"type": "string"
},
"username": {
"type": "string"
}
}
},
"dto.ImageRepoDelete": {
"type": "object",
"required": [
"ids"
],
"properties": {
"deleteInsecure": {
"type": "boolean"
},
"ids": {
"type": "array",
"items": {
@ -10785,7 +10792,10 @@ var doc = `{
"required": {
"type": "string"
},
"shortDesc": {
"shortDescEn": {
"type": "string"
},
"shortDescZh": {
"type": "string"
},
"status": {
@ -12108,7 +12118,10 @@ var doc = `{
"required": {
"type": "string"
},
"shortDesc": {
"shortDescEn": {
"type": "string"
},
"shortDescZh": {
"type": "string"
},
"status": {

View File

@ -1933,7 +1933,7 @@
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.ImageRepoCreate"
"$ref": "#/definitions/dto.ImageRepoDelete"
}
}
],
@ -2046,6 +2046,42 @@
}
}
},
"/containers/repo/status": {
"get": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "获取 docker 仓库状态",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Container Image-repo"
],
"summary": "Load repo status",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.OperateByID"
}
}
],
"responses": {
"200": {
"description": ""
}
}
}
},
"/containers/repo/update": {
"post": {
"security": [
@ -5461,18 +5497,18 @@
}
}
},
"/nginx": {
"/openResty": {
"get": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "获取 nginx 配置信息",
"description": "获取 OpenResty 配置信息",
"tags": [
"Nginx"
"OpenResty"
],
"summary": "Load nginx conf",
"summary": "Load OpenResty conf",
"responses": {
"200": {
"description": "OK",
@ -5483,21 +5519,21 @@
}
}
},
"/nginx/file": {
"/openResty/file": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "上传更新 nginx 配置文件",
"description": "上传更新 OpenResty 配置文件",
"consumes": [
"application/json"
],
"tags": [
"Nginx"
"OpenResty"
],
"summary": "Update nginx conf by upload file",
"summary": "Update OpenResty conf by upload file",
"parameters": [
{
"description": "request",
@ -5523,21 +5559,21 @@
}
}
},
"/nginx/scope": {
"/openResty/scope": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "获取部分 nginx 配置信息",
"description": "获取部分 OpenResty 配置信息",
"consumes": [
"application/json"
],
"tags": [
"Nginx"
"OpenResty"
],
"summary": "Load partial nginx conf",
"summary": "Load partial OpenResty conf",
"parameters": [
{
"description": "request",
@ -5559,18 +5595,18 @@
}
}
},
"/nginx/status": {
"/openResty/status": {
"get": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "获取 nginx 状态信息",
"description": "获取 OpenResty 状态信息",
"tags": [
"Nginx"
"OpenResty"
],
"summary": "Load nginx status info",
"summary": "Load OpenResty status info",
"responses": {
"200": {
"description": "OK",
@ -5581,21 +5617,21 @@
}
}
},
"/nginx/update": {
"/openResty/update": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "更新 nginx 配置信息",
"description": "更新 OpenResty 配置信息",
"consumes": [
"application/json"
],
"tags": [
"Nginx"
"OpenResty"
],
"summary": "Update nginx conf",
"summary": "Update OpenResty conf",
"parameters": [
{
"description": "request",
@ -9575,41 +9611,12 @@
}
}
},
"dto.ImageRepoCreate": {
"type": "object",
"required": [
"name"
],
"properties": {
"auth": {
"type": "boolean"
},
"downloadUrl": {
"type": "string"
},
"name": {
"type": "string"
},
"password": {
"type": "string"
},
"protocol": {
"type": "string"
},
"username": {
"type": "string"
}
}
},
"dto.ImageRepoDelete": {
"type": "object",
"required": [
"ids"
],
"properties": {
"deleteInsecure": {
"type": "boolean"
},
"ids": {
"type": "array",
"items": {
@ -10771,7 +10778,10 @@
"required": {
"type": "string"
},
"shortDesc": {
"shortDescEn": {
"type": "string"
},
"shortDescZh": {
"type": "string"
},
"status": {
@ -12094,7 +12104,10 @@
"required": {
"type": "string"
},
"shortDesc": {
"shortDescEn": {
"type": "string"
},
"shortDescZh": {
"type": "string"
},
"status": {

View File

@ -717,27 +717,8 @@ definitions:
- repoID
- tagName
type: object
dto.ImageRepoCreate:
properties:
auth:
type: boolean
downloadUrl:
type: string
name:
type: string
password:
type: string
protocol:
type: string
username:
type: string
required:
- name
type: object
dto.ImageRepoDelete:
properties:
deleteInsecure:
type: boolean
ids:
items:
type: integer
@ -1509,7 +1490,9 @@ definitions:
type: integer
required:
type: string
shortDesc:
shortDescEn:
type: string
shortDescZh:
type: string
status:
type: string
@ -2392,7 +2375,9 @@ definitions:
type: integer
required:
type: string
shortDesc:
shortDescEn:
type: string
shortDescZh:
type: string
status:
type: string
@ -3860,7 +3845,7 @@ paths:
name: request
required: true
schema:
$ref: '#/definitions/dto.ImageRepoCreate'
$ref: '#/definitions/dto.ImageRepoDelete'
produces:
- application/json
responses:
@ -3937,6 +3922,28 @@ paths:
summary: Page image repos
tags:
- Container Image-repo
/containers/repo/status:
get:
consumes:
- application/json
description: 获取 docker 仓库状态
parameters:
- description: request
in: body
name: request
required: true
schema:
$ref: '#/definitions/dto.OperateByID'
produces:
- application/json
responses:
"200":
description: ""
security:
- ApiKeyAuth: []
summary: Load repo status
tags:
- Container Image-repo
/containers/repo/update:
post:
consumes:
@ -6111,9 +6118,9 @@ paths:
summary: Page operation logs
tags:
- Logs
/nginx:
/openResty:
get:
description: 获取 nginx 配置信息
description: 获取 OpenResty 配置信息
responses:
"200":
description: OK
@ -6121,14 +6128,14 @@ paths:
$ref: '#/definitions/response.FileInfo'
security:
- ApiKeyAuth: []
summary: Load nginx conf
summary: Load OpenResty conf
tags:
- Nginx
/nginx/file:
- OpenResty
/openResty/file:
post:
consumes:
- application/json
description: 上传更新 nginx 配置文件
description: 上传更新 OpenResty 配置文件
parameters:
- description: request
in: body
@ -6141,20 +6148,20 @@ paths:
description: ""
security:
- ApiKeyAuth: []
summary: Update nginx conf by upload file
summary: Update OpenResty conf by upload file
tags:
- Nginx
- OpenResty
x-panel-log:
BeforeFuntions: []
bodyKeys: []
formatEN: Update nginx conf
formatZH: 更新 nginx 配置
paramKeys: []
/nginx/scope:
/openResty/scope:
post:
consumes:
- application/json
description: 获取部分 nginx 配置信息
description: 获取部分 OpenResty 配置信息
parameters:
- description: request
in: body
@ -6169,12 +6176,12 @@ paths:
type: anrry
security:
- ApiKeyAuth: []
summary: Load partial nginx conf
summary: Load partial OpenResty conf
tags:
- Nginx
/nginx/status:
- OpenResty
/openResty/status:
get:
description: 获取 nginx 状态信息
description: 获取 OpenResty 状态信息
responses:
"200":
description: OK
@ -6182,14 +6189,14 @@ paths:
$ref: '#/definitions/response.NginxStatus'
security:
- ApiKeyAuth: []
summary: Load nginx status info
summary: Load OpenResty status info
tags:
- Nginx
/nginx/update:
- OpenResty
/openResty/update:
post:
consumes:
- application/json
description: 更新 nginx 配置信息
description: 更新 OpenResty 配置信息
parameters:
- description: request
in: body
@ -6202,9 +6209,9 @@ paths:
description: ""
security:
- ApiKeyAuth: []
summary: Update nginx conf
summary: Update OpenResty conf
tags:
- Nginx
- OpenResty
x-panel-log:
BeforeFuntions:
- db: websites

View File

@ -76,6 +76,9 @@ export const createVolume = (params: Container.VolumeCreate) => {
};
// repo
export const checkRepoStatus = (id: number) => {
return http.post(`/containers/repo/status`, { id: id });
};
export const searchImageRepo = (params: SearchWithPage) => {
return http.post<ResPage<Container.RepoInfo>>(`/containers/repo/search`, params);
};

View File

@ -266,6 +266,7 @@ export default {
loadBackup: 'Import',
setting: 'Settings',
remoteAccess: 'Remote access',
remoteHelper: 'One in a row, for example:\n172.16.10.111\n172.16.10.112',
remoteConnHelper:
'Remote connection to mysql as user root may have security risks. Therefore, perform this operation with caution.',
changePassword: 'Password',

View File

@ -278,6 +278,7 @@ export default {
loadBackup: '',
setting: '',
remoteAccess: '访',
remoteHelper: '\n172.16.10.111\n172.16.10.112',
remoteConnHelper: 'root mysql ',
changePassword: '',
changePasswordHelper: '',

View File

@ -82,7 +82,7 @@ import OperatorDialog from '@/views/container/repo/operator/index.vue';
import { reactive, onMounted, ref } from 'vue';
import { dateFormat } from '@/utils/util';
import { Container } from '@/api/interface/container';
import { deleteImageRepo, loadDockerStatus, searchImageRepo } from '@/api/modules/container';
import { checkRepoStatus, deleteImageRepo, loadDockerStatus, searchImageRepo } from '@/api/modules/container';
import i18n from '@/lang';
import router from '@/routers';
import { ElMessageBox } from 'element-plus';
@ -156,7 +156,25 @@ const onDelete = async (row: Container.RepoInfo) => {
});
};
const onCheckConn = async (row: Container.RepoInfo) => {
loading.value = true;
await checkRepoStatus(row.id)
.then(() => {
loading.value = false;
search();
})
.catch(() => {
loading.value = false;
});
};
const buttons = [
{
label: i18n.global.t('commons.button.sync'),
click: (row: Container.RepoInfo) => {
onCheckConn(row);
},
},
{
label: i18n.global.t('commons.button.edit'),
disabled: (row: Container.RepoInfo) => {

View File

@ -38,7 +38,13 @@
prop="privilegeIPs"
:rules="Rules.requiredInput"
>
<el-input clearable v-model="changeForm.privilegeIPs" />
<el-input
:placeholder="$t('database.remoteHelper')"
clearable
:autosize="{ minRows: 2, maxRows: 5 }"
type="textarea"
v-model="changeForm.privilegeIPs"
/>
</el-form-item>
</div>
</el-col>
@ -145,7 +151,11 @@ const submitChangeInfo = async (formEl: FormInstance | undefined) => {
}
return;
}
param.value = changeForm.privilege;
if (changeForm.privilege !== 'ip') {
param.value = changeForm.privilege;
} else {
param.value = changeForm.privilegeIPs.replaceAll('/n', ',');
}
loading.value = true;
await updateMysqlAccess(param)
.then(() => {

View File

@ -217,10 +217,10 @@ const onSave = async (formEl: FormInstance | undefined, key: string, val: any) =
};
await updateSetting(param)
.then(async () => {
loading.value = false;
MsgSuccess(i18n.t('commons.msg.operationSuccess'));
if (param.key === 'UserName') {
await logOutApi();
loading.value = false;
MsgSuccess(i18n.t('commons.msg.operationSuccess'));
router.push({ name: 'login', params: { code: '' } });
globalStore.setLogStatus(false);
return;