mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-03-01 03:24:14 +08:00
feat: 完成 compose 模版功能
This commit is contained in:
parent
e50c4c39c1
commit
56c3c2f4cb
100
backend/app/api/v1/compose_template.go
Normal file
100
backend/app/api/v1/compose_template.go
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
package v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/1Panel-dev/1Panel/app/api/v1/helper"
|
||||||
|
"github.com/1Panel-dev/1Panel/app/dto"
|
||||||
|
"github.com/1Panel-dev/1Panel/constant"
|
||||||
|
"github.com/1Panel-dev/1Panel/global"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (b *BaseApi) CreateComposeTemplate(c *gin.Context) {
|
||||||
|
var req dto.ComposeTemplateCreate
|
||||||
|
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 := composeTemplateService.Create(req); err != nil {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithData(c, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseApi) SearchComposeTemplate(c *gin.Context) {
|
||||||
|
var req dto.PageInfo
|
||||||
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
total, list, err := composeTemplateService.SearchWithPage(req)
|
||||||
|
if err != nil {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
helper.SuccessWithData(c, dto.PageResult{
|
||||||
|
Items: list,
|
||||||
|
Total: total,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseApi) ListComposeTemplate(c *gin.Context) {
|
||||||
|
list, err := composeTemplateService.List()
|
||||||
|
if err != nil {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
helper.SuccessWithData(c, list)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseApi) DeleteComposeTemplate(c *gin.Context) {
|
||||||
|
var req dto.BatchDeleteReq
|
||||||
|
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 := composeTemplateService.Delete(req.Ids); err != nil {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithData(c, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BaseApi) UpdateComposeTemplate(c *gin.Context) {
|
||||||
|
var req dto.ComposeTemplateUpdate
|
||||||
|
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
|
||||||
|
}
|
||||||
|
id, err := helper.GetParamID(c)
|
||||||
|
if err != nil {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
upMap := make(map[string]interface{})
|
||||||
|
upMap["from"] = req.From
|
||||||
|
upMap["content"] = req.Content
|
||||||
|
upMap["description"] = req.Description
|
||||||
|
if err := composeTemplateService.Update(id, upMap); err != nil {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
helper.SuccessWithData(c, nil)
|
||||||
|
}
|
@ -9,17 +9,18 @@ type ApiGroup struct {
|
|||||||
var ApiGroupApp = new(ApiGroup)
|
var ApiGroupApp = new(ApiGroup)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
authService = service.ServiceGroupApp.AuthService
|
authService = service.ServiceGroupApp.AuthService
|
||||||
hostService = service.ServiceGroupApp.HostService
|
hostService = service.ServiceGroupApp.HostService
|
||||||
backupService = service.ServiceGroupApp.BackupService
|
backupService = service.ServiceGroupApp.BackupService
|
||||||
groupService = service.ServiceGroupApp.GroupService
|
groupService = service.ServiceGroupApp.GroupService
|
||||||
containerService = service.ServiceGroupApp.ContainerService
|
containerService = service.ServiceGroupApp.ContainerService
|
||||||
imageRepoService = service.ServiceGroupApp.ImageRepoService
|
composeTemplateService = service.ServiceGroupApp.ComposeTemplateService
|
||||||
imageService = service.ServiceGroupApp.ImageService
|
imageRepoService = service.ServiceGroupApp.ImageRepoService
|
||||||
commandService = service.ServiceGroupApp.CommandService
|
imageService = service.ServiceGroupApp.ImageService
|
||||||
operationService = service.ServiceGroupApp.OperationService
|
commandService = service.ServiceGroupApp.CommandService
|
||||||
fileService = service.ServiceGroupApp.FileService
|
operationService = service.ServiceGroupApp.OperationService
|
||||||
cronjobService = service.ServiceGroupApp.CronjobService
|
fileService = service.ServiceGroupApp.FileService
|
||||||
settingService = service.ServiceGroupApp.SettingService
|
cronjobService = service.ServiceGroupApp.CronjobService
|
||||||
appService = service.ServiceGroupApp.AppService
|
settingService = service.ServiceGroupApp.SettingService
|
||||||
|
appService = service.ServiceGroupApp.AppService
|
||||||
)
|
)
|
||||||
|
@ -246,7 +246,7 @@ func (b *BaseApi) LoadFromFile(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
buf := make([]byte, 1024*500)
|
buf := make([]byte, 1024*2)
|
||||||
if _, err := file.Read(buf); err != nil {
|
if _, err := file.Read(buf); err != nil {
|
||||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||||
return
|
return
|
||||||
|
25
backend/app/dto/compose_template.go
Normal file
25
backend/app/dto/compose_template.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package dto
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type ComposeTemplateCreate struct {
|
||||||
|
Name string `json:"name" validate:"required"`
|
||||||
|
From string `json:"from" validate:"required,oneof=edit path"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Content string `json:"content"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ComposeTemplateUpdate struct {
|
||||||
|
From string `json:"from" validate:"required,oneof=edit path"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Content string `json:"content"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ComposeTemplateInfo struct {
|
||||||
|
ID uint `json:"id"`
|
||||||
|
CreatedAt time.Time `json:"createdAt"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
From string `json:"from"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Content string `json:"content"`
|
||||||
|
}
|
10
backend/app/model/compose_template.go
Normal file
10
backend/app/model/compose_template.go
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
type ComposeTemplate struct {
|
||||||
|
BaseModel
|
||||||
|
|
||||||
|
Name string `gorm:"type:varchar(64);not null;unique" json:"name"`
|
||||||
|
From string `gorm:"type:varchar(64);not null" json:"from"`
|
||||||
|
Description string `gorm:"type:varchar(256);" json:"description"`
|
||||||
|
Content string `gorm:"type:longtext" json:"content"`
|
||||||
|
}
|
69
backend/app/repo/compose_template.go
Normal file
69
backend/app/repo/compose_template.go
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
package repo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/1Panel-dev/1Panel/app/model"
|
||||||
|
"github.com/1Panel-dev/1Panel/global"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ComposeTemplateRepo struct{}
|
||||||
|
|
||||||
|
type IComposeTemplateRepo interface {
|
||||||
|
Get(opts ...DBOption) (model.ComposeTemplate, error)
|
||||||
|
List(opts ...DBOption) ([]model.ComposeTemplate, error)
|
||||||
|
Page(limit, offset int, opts ...DBOption) (int64, []model.ComposeTemplate, error)
|
||||||
|
Create(compose *model.ComposeTemplate) error
|
||||||
|
Update(id uint, vars map[string]interface{}) error
|
||||||
|
Delete(opts ...DBOption) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewIComposeTemplateRepo() IComposeTemplateRepo {
|
||||||
|
return &ComposeTemplateRepo{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *ComposeTemplateRepo) Get(opts ...DBOption) (model.ComposeTemplate, error) {
|
||||||
|
var compose model.ComposeTemplate
|
||||||
|
db := global.DB
|
||||||
|
for _, opt := range opts {
|
||||||
|
db = opt(db)
|
||||||
|
}
|
||||||
|
err := db.First(&compose).Error
|
||||||
|
return compose, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *ComposeTemplateRepo) Page(page, size int, opts ...DBOption) (int64, []model.ComposeTemplate, error) {
|
||||||
|
var users []model.ComposeTemplate
|
||||||
|
db := global.DB.Model(&model.ComposeTemplate{})
|
||||||
|
for _, opt := range opts {
|
||||||
|
db = opt(db)
|
||||||
|
}
|
||||||
|
count := int64(0)
|
||||||
|
db = db.Count(&count)
|
||||||
|
err := db.Limit(size).Offset(size * (page - 1)).Find(&users).Error
|
||||||
|
return count, users, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *ComposeTemplateRepo) List(opts ...DBOption) ([]model.ComposeTemplate, error) {
|
||||||
|
var composes []model.ComposeTemplate
|
||||||
|
db := global.DB.Model(&model.ComposeTemplate{})
|
||||||
|
for _, opt := range opts {
|
||||||
|
db = opt(db)
|
||||||
|
}
|
||||||
|
err := db.Find(&composes).Error
|
||||||
|
return composes, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *ComposeTemplateRepo) Create(compose *model.ComposeTemplate) error {
|
||||||
|
return global.DB.Create(compose).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *ComposeTemplateRepo) Update(id uint, vars map[string]interface{}) error {
|
||||||
|
return global.DB.Model(&model.ComposeTemplate{}).Where("id = ?", id).Updates(vars).Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *ComposeTemplateRepo) Delete(opts ...DBOption) error {
|
||||||
|
db := global.DB
|
||||||
|
for _, opt := range opts {
|
||||||
|
db = opt(db)
|
||||||
|
}
|
||||||
|
return db.Delete(&model.ComposeTemplate{}).Error
|
||||||
|
}
|
@ -5,6 +5,7 @@ type RepoGroup struct {
|
|||||||
BackupRepo
|
BackupRepo
|
||||||
GroupRepo
|
GroupRepo
|
||||||
ImageRepoRepo
|
ImageRepoRepo
|
||||||
|
ComposeTemplateRepo
|
||||||
CommandRepo
|
CommandRepo
|
||||||
OperationRepo
|
OperationRepo
|
||||||
CommonRepo
|
CommonRepo
|
||||||
|
80
backend/app/service/compose_template.go
Normal file
80
backend/app/service/compose_template.go
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/1Panel-dev/1Panel/app/dto"
|
||||||
|
"github.com/1Panel-dev/1Panel/constant"
|
||||||
|
"github.com/jinzhu/copier"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ComposeTemplateService struct{}
|
||||||
|
|
||||||
|
type IComposeTemplateService interface {
|
||||||
|
List() ([]dto.ComposeTemplateInfo, error)
|
||||||
|
SearchWithPage(search dto.PageInfo) (int64, interface{}, error)
|
||||||
|
Create(composeDto dto.ComposeTemplateCreate) error
|
||||||
|
Update(id uint, upMap map[string]interface{}) error
|
||||||
|
Delete(ids []uint) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewIComposeTemplateService() IComposeTemplateService {
|
||||||
|
return &ComposeTemplateService{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *ComposeTemplateService) List() ([]dto.ComposeTemplateInfo, error) {
|
||||||
|
composes, err := composeRepo.List()
|
||||||
|
if err != nil {
|
||||||
|
return nil, constant.ErrRecordNotFound
|
||||||
|
}
|
||||||
|
var dtoLists []dto.ComposeTemplateInfo
|
||||||
|
for _, compose := range composes {
|
||||||
|
var item dto.ComposeTemplateInfo
|
||||||
|
if err := copier.Copy(&item, &compose); err != nil {
|
||||||
|
return nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||||
|
}
|
||||||
|
dtoLists = append(dtoLists, item)
|
||||||
|
}
|
||||||
|
return dtoLists, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *ComposeTemplateService) SearchWithPage(search dto.PageInfo) (int64, interface{}, error) {
|
||||||
|
total, composes, err := composeRepo.Page(search.Page, search.PageSize)
|
||||||
|
var dtoComposeTemplates []dto.ComposeTemplateInfo
|
||||||
|
for _, compose := range composes {
|
||||||
|
var item dto.ComposeTemplateInfo
|
||||||
|
if err := copier.Copy(&item, &compose); err != nil {
|
||||||
|
return 0, nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||||
|
}
|
||||||
|
dtoComposeTemplates = append(dtoComposeTemplates, item)
|
||||||
|
}
|
||||||
|
return total, dtoComposeTemplates, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *ComposeTemplateService) Create(composeDto dto.ComposeTemplateCreate) error {
|
||||||
|
compose, _ := composeRepo.Get(commonRepo.WithByName(composeDto.Name))
|
||||||
|
if compose.ID != 0 {
|
||||||
|
return constant.ErrRecordExist
|
||||||
|
}
|
||||||
|
if err := copier.Copy(&compose, &composeDto); err != nil {
|
||||||
|
return errors.WithMessage(constant.ErrStructTransform, err.Error())
|
||||||
|
}
|
||||||
|
if err := composeRepo.Create(&compose); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *ComposeTemplateService) Delete(ids []uint) error {
|
||||||
|
if len(ids) == 1 {
|
||||||
|
compose, _ := composeRepo.Get(commonRepo.WithByID(ids[0]))
|
||||||
|
if compose.ID == 0 {
|
||||||
|
return constant.ErrRecordNotFound
|
||||||
|
}
|
||||||
|
return composeRepo.Delete(commonRepo.WithByID(ids[0]))
|
||||||
|
}
|
||||||
|
return composeRepo.Delete(commonRepo.WithIdsIn(ids))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *ComposeTemplateService) Update(id uint, upMap map[string]interface{}) error {
|
||||||
|
return composeRepo.Update(id, upMap)
|
||||||
|
}
|
@ -8,6 +8,7 @@ type ServiceGroup struct {
|
|||||||
BackupService
|
BackupService
|
||||||
GroupService
|
GroupService
|
||||||
ImageService
|
ImageService
|
||||||
|
ComposeTemplateService
|
||||||
ImageRepoService
|
ImageRepoService
|
||||||
ContainerService
|
ContainerService
|
||||||
CommandService
|
CommandService
|
||||||
@ -28,6 +29,7 @@ var (
|
|||||||
operationRepo = repo.RepoGroupApp.OperationRepo
|
operationRepo = repo.RepoGroupApp.OperationRepo
|
||||||
commonRepo = repo.RepoGroupApp.CommonRepo
|
commonRepo = repo.RepoGroupApp.CommonRepo
|
||||||
imageRepoRepo = repo.RepoGroupApp.ImageRepoRepo
|
imageRepoRepo = repo.RepoGroupApp.ImageRepoRepo
|
||||||
|
composeRepo = repo.RepoGroupApp.ComposeTemplateRepo
|
||||||
cronjobRepo = repo.RepoGroupApp.CronjobRepo
|
cronjobRepo = repo.RepoGroupApp.CronjobRepo
|
||||||
settingRepo = repo.RepoGroupApp.SettingRepo
|
settingRepo = repo.RepoGroupApp.SettingRepo
|
||||||
appRepo = repo.RepoGroupApp.AppRepo
|
appRepo = repo.RepoGroupApp.AppRepo
|
||||||
@ -36,6 +38,7 @@ var (
|
|||||||
tagRepo = repo.RepoGroupApp.TagRepo
|
tagRepo = repo.RepoGroupApp.TagRepo
|
||||||
appInstallRepo = repo.RepoGroupApp.AppInstallRepo
|
appInstallRepo = repo.RepoGroupApp.AppInstallRepo
|
||||||
appInstallResourceRepo = repo.RepoGroupApp.AppInstallResourceRpo
|
appInstallResourceRepo = repo.RepoGroupApp.AppInstallResourceRpo
|
||||||
|
appContainerRepo = repo.RepoGroupApp.AppContainerRepo
|
||||||
dataBaseRepo = repo.RepoGroupApp.DatabaseRepo
|
dataBaseRepo = repo.RepoGroupApp.DatabaseRepo
|
||||||
appInstallBackupRepo = repo.RepoGroupApp.AppInstallBackupRepo
|
appInstallBackupRepo = repo.RepoGroupApp.AppInstallBackupRepo
|
||||||
)
|
)
|
||||||
|
@ -157,7 +157,7 @@ var AddTableApp = &gormigrate.Migration{
|
|||||||
var AddTableImageRepo = &gormigrate.Migration{
|
var AddTableImageRepo = &gormigrate.Migration{
|
||||||
ID: "20201009-add-table-imagerepo",
|
ID: "20201009-add-table-imagerepo",
|
||||||
Migrate: func(tx *gorm.DB) error {
|
Migrate: func(tx *gorm.DB) error {
|
||||||
if err := tx.AutoMigrate(&model.ImageRepo{}); err != nil {
|
if err := tx.AutoMigrate(&model.ImageRepo{}, &model.ComposeTemplate{}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
item := &model.ImageRepo{
|
item := &model.ImageRepo{
|
||||||
|
@ -33,6 +33,12 @@ func (s *ContainerRouter) InitContainerRouter(Router *gin.RouterGroup) {
|
|||||||
withRecordRouter.POST("/repo", baseApi.CreateRepo)
|
withRecordRouter.POST("/repo", baseApi.CreateRepo)
|
||||||
withRecordRouter.POST("/repo/del", baseApi.DeleteRepo)
|
withRecordRouter.POST("/repo/del", baseApi.DeleteRepo)
|
||||||
|
|
||||||
|
baRouter.POST("/compose/search", baseApi.SearchComposeTemplate)
|
||||||
|
baRouter.PUT("/compose/:id", baseApi.UpdateComposeTemplate)
|
||||||
|
baRouter.GET("/compose", baseApi.ListComposeTemplate)
|
||||||
|
withRecordRouter.POST("/compose", baseApi.CreateComposeTemplate)
|
||||||
|
withRecordRouter.POST("/compose/del", baseApi.DeleteComposeTemplate)
|
||||||
|
|
||||||
baRouter.POST("/image/search", baseApi.SearchImage)
|
baRouter.POST("/image/search", baseApi.SearchImage)
|
||||||
baRouter.GET("/image", baseApi.ListImage)
|
baRouter.GET("/image", baseApi.ListImage)
|
||||||
baRouter.POST("/image/pull", baseApi.ImagePull)
|
baRouter.POST("/image/pull", baseApi.ImagePull)
|
||||||
|
@ -161,6 +161,27 @@ export namespace Container {
|
|||||||
downloadUrl: string;
|
downloadUrl: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface TemplateCreate {
|
||||||
|
name: string;
|
||||||
|
from: string;
|
||||||
|
description: string;
|
||||||
|
content: string;
|
||||||
|
}
|
||||||
|
export interface TemplateUpdate {
|
||||||
|
id: number;
|
||||||
|
from: string;
|
||||||
|
description: string;
|
||||||
|
content: string;
|
||||||
|
}
|
||||||
|
export interface TemplateInfo {
|
||||||
|
id: number;
|
||||||
|
createdAt: Date;
|
||||||
|
name: string;
|
||||||
|
from: string;
|
||||||
|
description: string;
|
||||||
|
content: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface BatchDelete {
|
export interface BatchDelete {
|
||||||
ids: Array<string>;
|
ids: Array<string>;
|
||||||
}
|
}
|
||||||
|
@ -2,33 +2,30 @@ import http from '@/api';
|
|||||||
import { ResPage, ReqPage } from '../interface';
|
import { ResPage, ReqPage } from '../interface';
|
||||||
import { Container } from '../interface/container';
|
import { Container } from '../interface/container';
|
||||||
|
|
||||||
export const getContainerPage = (params: ReqPage) => {
|
export const searchContainer = (params: ReqPage) => {
|
||||||
return http.post<ResPage<Container.ContainerInfo>>(`/containers/search`, params);
|
return http.post<ResPage<Container.ContainerInfo>>(`/containers/search`, params);
|
||||||
};
|
};
|
||||||
export const createContainer = (params: Container.ContainerCreate) => {
|
export const createContainer = (params: Container.ContainerCreate) => {
|
||||||
return http.post(`/containers`, params);
|
return http.post(`/containers`, params);
|
||||||
};
|
};
|
||||||
|
export const logContainer = (params: Container.ContainerLogSearch) => {
|
||||||
export const getContainerLog = (params: Container.ContainerLogSearch) => {
|
|
||||||
return http.post<string>(`/containers/log`, params);
|
return http.post<string>(`/containers/log`, params);
|
||||||
};
|
};
|
||||||
export const ContainerStats = (id: string) => {
|
export const ContainerStats = (id: string) => {
|
||||||
return http.get<Container.ContainerStats>(`/containers/stats/${id}`);
|
return http.get<Container.ContainerStats>(`/containers/stats/${id}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ContainerOperator = (params: Container.ContainerOperate) => {
|
export const ContainerOperator = (params: Container.ContainerOperate) => {
|
||||||
return http.post(`/containers/operate`, params);
|
return http.post(`/containers/operate`, params);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const inspect = (params: Container.ContainerInspect) => {
|
export const inspect = (params: Container.ContainerInspect) => {
|
||||||
return http.post<string>(`/containers/inspect`, params);
|
return http.post<string>(`/containers/inspect`, params);
|
||||||
};
|
};
|
||||||
|
|
||||||
// image
|
// image
|
||||||
export const getImagePage = (params: ReqPage) => {
|
export const searchImage = (params: ReqPage) => {
|
||||||
return http.post<ResPage<Container.ImageInfo>>(`/containers/image/search`, params);
|
return http.post<ResPage<Container.ImageInfo>>(`/containers/image/search`, params);
|
||||||
};
|
};
|
||||||
export const imageOptions = () => {
|
export const listImage = () => {
|
||||||
return http.get<Array<Container.Options>>(`/containers/image`);
|
return http.get<Array<Container.Options>>(`/containers/image`);
|
||||||
};
|
};
|
||||||
export const imageBuild = (params: Container.ImageBuild) => {
|
export const imageBuild = (params: Container.ImageBuild) => {
|
||||||
@ -54,7 +51,7 @@ export const imageRemove = (params: Container.BatchDelete) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// network
|
// network
|
||||||
export const getNetworkPage = (params: ReqPage) => {
|
export const searchNetwork = (params: ReqPage) => {
|
||||||
return http.post<ResPage<Container.NetworkInfo>>(`/containers/network/search`, params);
|
return http.post<ResPage<Container.NetworkInfo>>(`/containers/network/search`, params);
|
||||||
};
|
};
|
||||||
export const deleteNetwork = (params: Container.BatchDelete) => {
|
export const deleteNetwork = (params: Container.BatchDelete) => {
|
||||||
@ -65,10 +62,10 @@ export const createNetwork = (params: Container.NetworkCreate) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// volume
|
// volume
|
||||||
export const getVolumePage = (params: ReqPage) => {
|
export const searchVolume = (params: ReqPage) => {
|
||||||
return http.post<ResPage<Container.VolumeInfo>>(`/containers/volume/search`, params);
|
return http.post<ResPage<Container.VolumeInfo>>(`/containers/volume/search`, params);
|
||||||
};
|
};
|
||||||
export const volumeOptions = () => {
|
export const listVolume = () => {
|
||||||
return http.get<Array<Container.Options>>(`/containers/volume`);
|
return http.get<Array<Container.Options>>(`/containers/volume`);
|
||||||
};
|
};
|
||||||
export const deleteVolume = (params: Container.BatchDelete) => {
|
export const deleteVolume = (params: Container.BatchDelete) => {
|
||||||
@ -79,18 +76,35 @@ export const createVolume = (params: Container.VolumeCreate) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// repo
|
// repo
|
||||||
export const getRepoPage = (params: ReqPage) => {
|
export const searchImageRepo = (params: ReqPage) => {
|
||||||
return http.post<ResPage<Container.RepoInfo>>(`/containers/repo/search`, params);
|
return http.post<ResPage<Container.RepoInfo>>(`/containers/repo/search`, params);
|
||||||
};
|
};
|
||||||
export const getRepoOption = () => {
|
export const listImageRepo = () => {
|
||||||
return http.get<Container.RepoOptions>(`/containers/repo`);
|
return http.get<Container.RepoOptions>(`/containers/repo`);
|
||||||
};
|
};
|
||||||
export const repoCreate = (params: Container.RepoCreate) => {
|
export const createImageRepo = (params: Container.RepoCreate) => {
|
||||||
return http.post(`/containers/repo`, params);
|
return http.post(`/containers/repo`, params);
|
||||||
};
|
};
|
||||||
export const repoUpdate = (params: Container.RepoUpdate) => {
|
export const updateImageRepo = (params: Container.RepoUpdate) => {
|
||||||
return http.put(`/containers/repo/${params.id}`, params);
|
return http.put(`/containers/repo/${params.id}`, params);
|
||||||
};
|
};
|
||||||
export const deleteRepo = (params: { ids: number[] }) => {
|
export const deleteImageRepo = (params: { ids: number[] }) => {
|
||||||
return http.post(`/containers/repo/del`, params);
|
return http.post(`/containers/repo/del`, params);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// composeTemplate
|
||||||
|
export const searchComposeTemplate = (params: ReqPage) => {
|
||||||
|
return http.post<ResPage<Container.TemplateInfo>>(`/containers/compose/search`, params);
|
||||||
|
};
|
||||||
|
export const listComposeTemplate = (params: ReqPage) => {
|
||||||
|
return http.post<ResPage<Container.TemplateInfo>>(`/containers/compose/search`, params);
|
||||||
|
};
|
||||||
|
export const deleteComposeTemplate = (params: { ids: number[] }) => {
|
||||||
|
return http.post(`/containers/compose/del`, params);
|
||||||
|
};
|
||||||
|
export const createComposeTemplate = (params: Container.TemplateCreate) => {
|
||||||
|
return http.post(`/containers/compose`, params);
|
||||||
|
};
|
||||||
|
export const updateComposeTemplate = (params: Container.TemplateUpdate) => {
|
||||||
|
return http.put(`/containers/compose/${params.id}`, params);
|
||||||
|
};
|
||||||
|
@ -93,6 +93,7 @@ export default {
|
|||||||
number: 'Please enter the correct number',
|
number: 'Please enter the correct number',
|
||||||
ip: 'Please enter the correct IP address',
|
ip: 'Please enter the correct IP address',
|
||||||
port: 'Please enter the correct port',
|
port: 'Please enter the correct port',
|
||||||
|
selectHelper: 'Please select the correct {0} file',
|
||||||
},
|
},
|
||||||
res: {
|
res: {
|
||||||
paramError: 'The request failed, please try again later!',
|
paramError: 'The request failed, please try again later!',
|
||||||
@ -161,7 +162,6 @@ export default {
|
|||||||
reName: 'ReName',
|
reName: 'ReName',
|
||||||
remove: 'Remove',
|
remove: 'Remove',
|
||||||
container: 'Container',
|
container: 'Container',
|
||||||
schedule: 'Schedule',
|
|
||||||
upTime: 'UpTime',
|
upTime: 'UpTime',
|
||||||
all: 'All',
|
all: 'All',
|
||||||
lastDay: 'Last Day',
|
lastDay: 'Last Day',
|
||||||
@ -242,6 +242,11 @@ export default {
|
|||||||
imageRepo: 'Image repo',
|
imageRepo: 'Image repo',
|
||||||
repoHelper: 'Does it include a mirror repository/organization/project?',
|
repoHelper: 'Does it include a mirror repository/organization/project?',
|
||||||
auth: 'Auth',
|
auth: 'Auth',
|
||||||
|
|
||||||
|
compose: 'Compose',
|
||||||
|
composeTemplate: 'Compose template',
|
||||||
|
description: 'Description',
|
||||||
|
content: 'Content',
|
||||||
},
|
},
|
||||||
cronjob: {
|
cronjob: {
|
||||||
cronTask: 'Task',
|
cronTask: 'Task',
|
||||||
|
@ -2,6 +2,7 @@ export default {
|
|||||||
commons: {
|
commons: {
|
||||||
true: '是',
|
true: '是',
|
||||||
false: '否',
|
false: '否',
|
||||||
|
example: '例如:',
|
||||||
button: {
|
button: {
|
||||||
create: '新建',
|
create: '新建',
|
||||||
add: '添加',
|
add: '添加',
|
||||||
@ -90,6 +91,7 @@ export default {
|
|||||||
number: '请输入正确的数字',
|
number: '请输入正确的数字',
|
||||||
ip: '请输入正确的 IP 地址',
|
ip: '请输入正确的 IP 地址',
|
||||||
port: '请输入正确的端口',
|
port: '请输入正确的端口',
|
||||||
|
selectHelper: '请选择正确的 {0} 文件',
|
||||||
},
|
},
|
||||||
res: {
|
res: {
|
||||||
paramError: '请求失败,请稍后重试!',
|
paramError: '请求失败,请稍后重试!',
|
||||||
@ -158,7 +160,6 @@ export default {
|
|||||||
reName: '重命名',
|
reName: '重命名',
|
||||||
remove: '移除',
|
remove: '移除',
|
||||||
container: '容器',
|
container: '容器',
|
||||||
schedule: '编排',
|
|
||||||
upTime: '运行时长',
|
upTime: '运行时长',
|
||||||
all: '全部',
|
all: '全部',
|
||||||
lastDay: '最近一天',
|
lastDay: '最近一天',
|
||||||
@ -242,6 +243,11 @@ export default {
|
|||||||
imageRepo: '镜像仓库',
|
imageRepo: '镜像仓库',
|
||||||
repoHelper: '是否包含镜像仓库/组织/项目?',
|
repoHelper: '是否包含镜像仓库/组织/项目?',
|
||||||
auth: '认证',
|
auth: '认证',
|
||||||
|
|
||||||
|
compose: '编排',
|
||||||
|
composeTemplate: '编排模版',
|
||||||
|
description: '描述',
|
||||||
|
content: '内容',
|
||||||
},
|
},
|
||||||
cronjob: {
|
cronjob: {
|
||||||
cronTask: '计划任务',
|
cronTask: '计划任务',
|
||||||
|
169
frontend/src/views/container/compose/template/index.vue
Normal file
169
frontend/src/views/container/compose/template/index.vue
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<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="onOpenDialog('create')">
|
||||||
|
{{ $t('commons.button.create') }}
|
||||||
|
</el-button>
|
||||||
|
<el-button type="danger" plain :disabled="selects.length === 0" @click="onBatchDelete(null)">
|
||||||
|
{{ $t('commons.button.delete') }}
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
<el-table-column type="selection" fix></el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
:label="$t('commons.table.name')"
|
||||||
|
show-overflow-tooltip
|
||||||
|
min-width="100"
|
||||||
|
prop="name"
|
||||||
|
fix
|
||||||
|
>
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-link @click="onOpenDetail(row)" type="primary">{{ row.name }}</el-link>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column :label="$t('container.description')" prop="description" min-width="200" fix />
|
||||||
|
<el-table-column :label="$t('commons.table.createdAt')" min-width="80" fix>
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ dateFromat(0, 0, row.createdAt) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<fu-table-operations :buttons="buttons" :label="$t('commons.table.operate')" />
|
||||||
|
</ComplexTable>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<el-dialog v-model="detailVisiable" :destroy-on-close="true" :close-on-click-modal="false" width="70%">
|
||||||
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span>{{ $t('commons.button.view') }}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<codemirror
|
||||||
|
:autofocus="true"
|
||||||
|
placeholder="None data"
|
||||||
|
:indent-with-tab="true"
|
||||||
|
:tabSize="4"
|
||||||
|
style="max-height: 500px"
|
||||||
|
:lineWrapping="true"
|
||||||
|
:matchBrackets="true"
|
||||||
|
theme="cobalt"
|
||||||
|
:styleActiveLine="true"
|
||||||
|
:extensions="extensions"
|
||||||
|
v-model="detailInfo"
|
||||||
|
:readOnly="true"
|
||||||
|
/>
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button @click="detailVisiable = false">{{ $t('commons.button.cancel') }}</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
|
<OperatorDialog @search="search" ref="dialogRef" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import ComplexTable from '@/components/complex-table/index.vue';
|
||||||
|
import { Codemirror } from 'vue-codemirror';
|
||||||
|
import { javascript } from '@codemirror/lang-javascript';
|
||||||
|
import { oneDark } from '@codemirror/theme-one-dark';
|
||||||
|
import { reactive, onMounted, ref } from 'vue';
|
||||||
|
import { dateFromat } from '@/utils/util';
|
||||||
|
import { Container } from '@/api/interface/container';
|
||||||
|
import OperatorDialog from '@/views/container/compose/template/operator/index.vue';
|
||||||
|
import { deleteComposeTemplate, searchComposeTemplate } from '@/api/modules/container';
|
||||||
|
import { useDeleteData } from '@/hooks/use-delete-data';
|
||||||
|
import i18n from '@/lang';
|
||||||
|
import { LoadFile } from '@/api/modules/files';
|
||||||
|
|
||||||
|
const data = ref();
|
||||||
|
const selects = ref<any>([]);
|
||||||
|
const detailVisiable = ref(false);
|
||||||
|
const detailInfo = ref();
|
||||||
|
const extensions = [javascript(), oneDark];
|
||||||
|
|
||||||
|
const paginationConfig = reactive({
|
||||||
|
page: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
total: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
const search = async () => {
|
||||||
|
let params = {
|
||||||
|
page: paginationConfig.page,
|
||||||
|
pageSize: paginationConfig.pageSize,
|
||||||
|
};
|
||||||
|
await searchComposeTemplate(params).then((res) => {
|
||||||
|
if (res.data) {
|
||||||
|
data.value = res.data.items;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onOpenDetail = async (row: Container.TemplateInfo) => {
|
||||||
|
if (row.from === 'edit') {
|
||||||
|
detailInfo.value = row.content;
|
||||||
|
detailVisiable.value = true;
|
||||||
|
} else {
|
||||||
|
const res = await LoadFile({ path: row.content });
|
||||||
|
detailInfo.value = res.data;
|
||||||
|
detailVisiable.value = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const dialogRef = ref();
|
||||||
|
const onOpenDialog = async (
|
||||||
|
title: string,
|
||||||
|
rowData: Partial<Container.TemplateInfo> = {
|
||||||
|
name: '',
|
||||||
|
from: 'edit',
|
||||||
|
description: '',
|
||||||
|
content: '',
|
||||||
|
},
|
||||||
|
) => {
|
||||||
|
let params = {
|
||||||
|
title,
|
||||||
|
rowData: { ...rowData },
|
||||||
|
};
|
||||||
|
dialogRef.value!.acceptParams(params);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onBatchDelete = async (row: Container.RepoInfo | null) => {
|
||||||
|
let ids: Array<number> = [];
|
||||||
|
if (row) {
|
||||||
|
ids.push(row.id);
|
||||||
|
} else {
|
||||||
|
selects.value.forEach((item: Container.RepoInfo) => {
|
||||||
|
ids.push(item.id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
await useDeleteData(deleteComposeTemplate, { ids: ids }, 'commons.msg.delete', true);
|
||||||
|
search();
|
||||||
|
};
|
||||||
|
|
||||||
|
const buttons = [
|
||||||
|
{
|
||||||
|
label: i18n.global.t('commons.button.edit'),
|
||||||
|
disabled: (row: Container.RepoInfo) => {
|
||||||
|
return row.downloadUrl === 'docker.io';
|
||||||
|
},
|
||||||
|
click: (row: Container.RepoInfo) => {
|
||||||
|
onOpenDialog('edit', row);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: i18n.global.t('commons.button.delete'),
|
||||||
|
disabled: (row: Container.RepoInfo) => {
|
||||||
|
return row.downloadUrl === 'docker.io';
|
||||||
|
},
|
||||||
|
click: (row: Container.RepoInfo) => {
|
||||||
|
onBatchDelete(row);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
search();
|
||||||
|
});
|
||||||
|
</script>
|
133
frontend/src/views/container/compose/template/operator/index.vue
Normal file
133
frontend/src/views/container/compose/template/operator/index.vue
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog v-model="templateVisiable" :destroy-on-close="true" :close-on-click-modal="false" width="50%">
|
||||||
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span>{{ title }}{{ $t('container.composeTemplate') }}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-form ref="formRef" :model="dialogData.rowData" :rules="rules" label-width="80px">
|
||||||
|
<el-form-item :label="$t('container.name')" prop="name">
|
||||||
|
<el-input :disabled="dialogData.title === 'edit'" v-model="dialogData.rowData!.name"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="$t('container.description')">
|
||||||
|
<el-input v-model="dialogData.rowData!.description"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="$t('container.from')">
|
||||||
|
<el-radio-group v-model="dialogData.rowData!.from">
|
||||||
|
<el-radio label="edit">{{ $t('container.edit') }}</el-radio>
|
||||||
|
<el-radio label="path">{{ $t('container.pathSelect') }}</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item v-if="dialogData.rowData!.from === 'path'" :rules="Rules.requiredSelect" prop="content">
|
||||||
|
<el-input
|
||||||
|
clearable
|
||||||
|
:placeholder="$t('commons.example') + '/tmp/docker-compose.yml'"
|
||||||
|
v-model="dialogData.rowData!.content"
|
||||||
|
>
|
||||||
|
<template #append>
|
||||||
|
<FileList @choose="loadDir" :dir="false"></FileList>
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item v-if="dialogData.rowData!.from === 'edit'">
|
||||||
|
<codemirror
|
||||||
|
:autofocus="true"
|
||||||
|
placeholder="None data"
|
||||||
|
:indent-with-tab="true"
|
||||||
|
:tabSize="4"
|
||||||
|
style="max-height: 500px; width: 100%"
|
||||||
|
:lineWrapping="true"
|
||||||
|
:matchBrackets="true"
|
||||||
|
theme="cobalt"
|
||||||
|
:styleActiveLine="true"
|
||||||
|
:extensions="extensions"
|
||||||
|
v-model="dialogData.rowData!.content"
|
||||||
|
:readOnly="true"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button @click="templateVisiable = false">{{ $t('commons.button.cancel') }}</el-button>
|
||||||
|
<el-button type="primary" @click="onSubmit(formRef)">
|
||||||
|
{{ $t('commons.button.confirm') }}
|
||||||
|
</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { reactive, ref } from 'vue';
|
||||||
|
import FileList from '@/components/file-list/index.vue';
|
||||||
|
import { Codemirror } from 'vue-codemirror';
|
||||||
|
import { javascript } from '@codemirror/lang-javascript';
|
||||||
|
import { oneDark } from '@codemirror/theme-one-dark';
|
||||||
|
import { Rules } from '@/global/form-rules';
|
||||||
|
import i18n from '@/lang';
|
||||||
|
import { ElForm, ElMessage } from 'element-plus';
|
||||||
|
import { Container } from '@/api/interface/container';
|
||||||
|
import { createComposeTemplate, updateComposeTemplate } from '@/api/modules/container';
|
||||||
|
|
||||||
|
interface DialogProps {
|
||||||
|
title: string;
|
||||||
|
rowData?: Container.TemplateInfo;
|
||||||
|
getTableList?: () => Promise<any>;
|
||||||
|
}
|
||||||
|
const extensions = [javascript(), oneDark];
|
||||||
|
const title = ref<string>('');
|
||||||
|
const templateVisiable = ref(false);
|
||||||
|
const dialogData = ref<DialogProps>({
|
||||||
|
title: '',
|
||||||
|
});
|
||||||
|
const acceptParams = (params: DialogProps): void => {
|
||||||
|
dialogData.value = params;
|
||||||
|
title.value = i18n.global.t('commons.button.' + dialogData.value.title);
|
||||||
|
templateVisiable.value = true;
|
||||||
|
};
|
||||||
|
const emit = defineEmits<{ (e: 'search'): void }>();
|
||||||
|
|
||||||
|
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 rules = reactive({
|
||||||
|
name: [Rules.requiredInput, Rules.name],
|
||||||
|
content: [Rules.requiredInput, { validator: varifyPath, trigger: 'change', required: true }],
|
||||||
|
});
|
||||||
|
|
||||||
|
type FormInstance = InstanceType<typeof ElForm>;
|
||||||
|
const formRef = ref<FormInstance>();
|
||||||
|
|
||||||
|
function restForm() {
|
||||||
|
if (formRef.value) {
|
||||||
|
formRef.value.resetFields();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const onSubmit = async (formEl: FormInstance | undefined) => {
|
||||||
|
if (!formEl) return;
|
||||||
|
formEl.validate(async (valid) => {
|
||||||
|
if (!valid) return;
|
||||||
|
if (dialogData.value.title === 'create') {
|
||||||
|
await createComposeTemplate(dialogData.value.rowData!);
|
||||||
|
}
|
||||||
|
if (dialogData.value.title === 'edit') {
|
||||||
|
await updateComposeTemplate(dialogData.value.rowData!);
|
||||||
|
}
|
||||||
|
ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
|
||||||
|
restForm();
|
||||||
|
emit('search');
|
||||||
|
templateVisiable.value = false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const loadDir = async (path: string) => {
|
||||||
|
dialogData.value.rowData!.content = path;
|
||||||
|
};
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
acceptParams,
|
||||||
|
});
|
||||||
|
</script>
|
@ -189,7 +189,7 @@ import { reactive, ref } from 'vue';
|
|||||||
import { Rules } from '@/global/form-rules';
|
import { Rules } from '@/global/form-rules';
|
||||||
import i18n from '@/lang';
|
import i18n from '@/lang';
|
||||||
import { ElForm, ElMessage } from 'element-plus';
|
import { ElForm, ElMessage } from 'element-plus';
|
||||||
import { imageOptions, volumeOptions, createContainer } from '@/api/modules/container';
|
import { listImage, listVolume, createContainer } from '@/api/modules/container';
|
||||||
import { Container } from '@/api/interface/container';
|
import { Container } from '@/api/interface/container';
|
||||||
|
|
||||||
const createVisiable = ref(false);
|
const createVisiable = ref(false);
|
||||||
@ -259,11 +259,11 @@ const handleVolumesDelete = (index: number) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const loadImageOptions = async () => {
|
const loadImageOptions = async () => {
|
||||||
const res = await imageOptions();
|
const res = await listImage();
|
||||||
images.value = res.data;
|
images.value = res.data;
|
||||||
};
|
};
|
||||||
const loadVolumeOptions = async () => {
|
const loadVolumeOptions = async () => {
|
||||||
const res = await volumeOptions();
|
const res = await listVolume();
|
||||||
volumes.value = res.data;
|
volumes.value = res.data;
|
||||||
};
|
};
|
||||||
const onSubmit = async (formEl: FormInstance | undefined) => {
|
const onSubmit = async (formEl: FormInstance | undefined) => {
|
||||||
|
@ -171,7 +171,7 @@ import { oneDark } from '@codemirror/theme-one-dark';
|
|||||||
import { reactive, onMounted, ref } from 'vue';
|
import { reactive, onMounted, ref } from 'vue';
|
||||||
import { dateFromat, dateFromatForName } from '@/utils/util';
|
import { dateFromat, dateFromatForName } from '@/utils/util';
|
||||||
import { Rules } from '@/global/form-rules';
|
import { Rules } from '@/global/form-rules';
|
||||||
import { ContainerOperator, inspect, getContainerLog, getContainerPage } from '@/api/modules/container';
|
import { ContainerOperator, inspect, logContainer, searchContainer } from '@/api/modules/container';
|
||||||
import { Container } from '@/api/interface/container';
|
import { Container } from '@/api/interface/container';
|
||||||
import { ElForm, ElMessage, ElMessageBox, FormInstance } from 'element-plus';
|
import { ElForm, ElMessage, ElMessageBox, FormInstance } from 'element-plus';
|
||||||
import i18n from '@/lang';
|
import i18n from '@/lang';
|
||||||
@ -234,7 +234,7 @@ const timeOptions = ref([
|
|||||||
const search = async () => {
|
const search = async () => {
|
||||||
containerSearch.page = paginationConfig.page;
|
containerSearch.page = paginationConfig.page;
|
||||||
containerSearch.pageSize = paginationConfig.pageSize;
|
containerSearch.pageSize = paginationConfig.pageSize;
|
||||||
await getContainerPage(containerSearch).then((res) => {
|
await searchContainer(containerSearch).then((res) => {
|
||||||
if (res.data) {
|
if (res.data) {
|
||||||
data.value = res.data.items;
|
data.value = res.data.items;
|
||||||
}
|
}
|
||||||
@ -272,7 +272,7 @@ const onCloseLog = async () => {
|
|||||||
clearInterval(Number(timer));
|
clearInterval(Number(timer));
|
||||||
};
|
};
|
||||||
const searchLogs = async () => {
|
const searchLogs = async () => {
|
||||||
const res = await getContainerLog(logSearch);
|
const res = await logContainer(logSearch);
|
||||||
logInfo.value = res.data;
|
logInfo.value = res.data;
|
||||||
};
|
};
|
||||||
const onDownload = async () => {
|
const onDownload = async () => {
|
||||||
|
@ -93,6 +93,16 @@ const form = reactive({
|
|||||||
tagStr: '',
|
tagStr: '',
|
||||||
tags: [] as Array<string>,
|
tags: [] as Array<string>,
|
||||||
});
|
});
|
||||||
|
const varifyPath = (rule: any, value: any, callback: any) => {
|
||||||
|
if (value.indexOf('docker-compose.yml') === -1) {
|
||||||
|
callback(new Error(i18n.global.t('commons.rule.selectHelper', ['Dockerfile'])));
|
||||||
|
}
|
||||||
|
callback();
|
||||||
|
};
|
||||||
|
const rules = reactive({
|
||||||
|
name: [Rules.requiredInput, Rules.name],
|
||||||
|
content: [Rules.requiredInput, { validator: varifyPath, trigger: 'change', required: true }],
|
||||||
|
});
|
||||||
const acceptParams = async () => {
|
const acceptParams = async () => {
|
||||||
buildVisiable.value = true;
|
buildVisiable.value = true;
|
||||||
form.from = 'path';
|
form.from = 'path';
|
||||||
|
@ -78,7 +78,7 @@ import Push from '@/views/container/image/push/index.vue';
|
|||||||
import Save from '@/views/container/image/save/index.vue';
|
import Save from '@/views/container/image/save/index.vue';
|
||||||
import Load from '@/views/container/image/load/index.vue';
|
import Load from '@/views/container/image/load/index.vue';
|
||||||
import Build from '@/views/container/image/build/index.vue';
|
import Build from '@/views/container/image/build/index.vue';
|
||||||
import { getImagePage, getRepoOption, imageRemove } from '@/api/modules/container';
|
import { searchImage, listImageRepo, imageRemove } from '@/api/modules/container';
|
||||||
import i18n from '@/lang';
|
import i18n from '@/lang';
|
||||||
import { ElForm } from 'element-plus';
|
import { ElForm } from 'element-plus';
|
||||||
import { useDeleteData } from '@/hooks/use-delete-data';
|
import { useDeleteData } from '@/hooks/use-delete-data';
|
||||||
@ -112,7 +112,7 @@ const search = async () => {
|
|||||||
page: paginationConfig.page,
|
page: paginationConfig.page,
|
||||||
pageSize: paginationConfig.pageSize,
|
pageSize: paginationConfig.pageSize,
|
||||||
};
|
};
|
||||||
await getImagePage(repoSearch).then((res) => {
|
await searchImage(repoSearch).then((res) => {
|
||||||
if (res.data) {
|
if (res.data) {
|
||||||
data.value = res.data.items;
|
data.value = res.data.items;
|
||||||
}
|
}
|
||||||
@ -120,7 +120,7 @@ const search = async () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
const loadRepos = async () => {
|
const loadRepos = async () => {
|
||||||
const res = await getRepoOption();
|
const res = await listImageRepo();
|
||||||
repos.value = res.data;
|
repos.value = res.data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -17,8 +17,11 @@
|
|||||||
<el-radio-button class="topButton" size="large" label="repo">
|
<el-radio-button class="topButton" size="large" label="repo">
|
||||||
{{ $t('container.repo') }}
|
{{ $t('container.repo') }}
|
||||||
</el-radio-button>
|
</el-radio-button>
|
||||||
<el-radio-button class="topButton" size="large" label="schedule">
|
<el-radio-button class="topButton" size="large" label="compose">
|
||||||
{{ $t('container.schedule') }}
|
{{ $t('container.compose') }}
|
||||||
|
</el-radio-button>
|
||||||
|
<el-radio-button class="topButton" size="large" label="composeTemplate">
|
||||||
|
{{ $t('container.composeTemplate') }}
|
||||||
</el-radio-button>
|
</el-radio-button>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-card>
|
</el-card>
|
||||||
@ -27,7 +30,7 @@
|
|||||||
<Image v-if="activeNames === 'image'" />
|
<Image v-if="activeNames === 'image'" />
|
||||||
<Network v-if="activeNames === 'network'" />
|
<Network v-if="activeNames === 'network'" />
|
||||||
<Volume v-if="activeNames === 'volume'" />
|
<Volume v-if="activeNames === 'volume'" />
|
||||||
<About v-if="activeNames === 'schedule'" />
|
<ComposeTemplate v-if="activeNames === 'composeTemplate'" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -38,7 +41,7 @@ import Repo from '@/views/container/repo/index.vue';
|
|||||||
import Image from '@/views/container/image/index.vue';
|
import Image from '@/views/container/image/index.vue';
|
||||||
import Network from '@/views/container/network/index.vue';
|
import Network from '@/views/container/network/index.vue';
|
||||||
import Volume from '@/views/container/volume/index.vue';
|
import Volume from '@/views/container/volume/index.vue';
|
||||||
import About from '@/views/setting/tabs/about.vue';
|
import ComposeTemplate from '@/views/container/compose/template/index.vue';
|
||||||
|
|
||||||
const activeNames = ref('container');
|
const activeNames = ref('container');
|
||||||
</script>
|
</script>
|
||||||
|
@ -83,7 +83,7 @@ import { javascript } from '@codemirror/lang-javascript';
|
|||||||
import { oneDark } from '@codemirror/theme-one-dark';
|
import { oneDark } from '@codemirror/theme-one-dark';
|
||||||
import { reactive, onMounted, ref } from 'vue';
|
import { reactive, onMounted, ref } from 'vue';
|
||||||
import { dateFromat } from '@/utils/util';
|
import { dateFromat } from '@/utils/util';
|
||||||
import { deleteNetwork, getNetworkPage, inspect } from '@/api/modules/container';
|
import { deleteNetwork, searchNetwork, inspect } from '@/api/modules/container';
|
||||||
import { Container } from '@/api/interface/container';
|
import { Container } from '@/api/interface/container';
|
||||||
import i18n from '@/lang';
|
import i18n from '@/lang';
|
||||||
import { useDeleteData } from '@/hooks/use-delete-data';
|
import { useDeleteData } from '@/hooks/use-delete-data';
|
||||||
@ -114,7 +114,7 @@ const search = async () => {
|
|||||||
page: paginationConfig.page,
|
page: paginationConfig.page,
|
||||||
pageSize: paginationConfig.pageSize,
|
pageSize: paginationConfig.pageSize,
|
||||||
};
|
};
|
||||||
await getNetworkPage(params).then((res) => {
|
await searchNetwork(params).then((res) => {
|
||||||
if (res.data) {
|
if (res.data) {
|
||||||
data.value = res.data.items;
|
data.value = res.data.items;
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,7 @@ import OperatorDialog from '@/views/container/repo/operator/index.vue';
|
|||||||
import { reactive, onMounted, ref } from 'vue';
|
import { reactive, onMounted, ref } from 'vue';
|
||||||
import { dateFromat } from '@/utils/util';
|
import { dateFromat } from '@/utils/util';
|
||||||
import { Container } from '@/api/interface/container';
|
import { Container } from '@/api/interface/container';
|
||||||
import { deleteRepo, getRepoPage } from '@/api/modules/container';
|
import { deleteImageRepo, searchImageRepo } from '@/api/modules/container';
|
||||||
import { useDeleteData } from '@/hooks/use-delete-data';
|
import { useDeleteData } from '@/hooks/use-delete-data';
|
||||||
import i18n from '@/lang';
|
import i18n from '@/lang';
|
||||||
|
|
||||||
@ -49,25 +49,20 @@ const paginationConfig = reactive({
|
|||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
total: 0,
|
total: 0,
|
||||||
});
|
});
|
||||||
const repoSearch = reactive({
|
|
||||||
page: 1,
|
|
||||||
pageSize: 5,
|
|
||||||
});
|
|
||||||
|
|
||||||
const search = async () => {
|
const search = async () => {
|
||||||
repoSearch.page = paginationConfig.page;
|
let params = {
|
||||||
repoSearch.pageSize = paginationConfig.pageSize;
|
page: paginationConfig.page,
|
||||||
await getRepoPage(repoSearch).then((res) => {
|
pageSize: paginationConfig.pageSize,
|
||||||
|
};
|
||||||
|
await searchImageRepo(params).then((res) => {
|
||||||
if (res.data) {
|
if (res.data) {
|
||||||
data.value = res.data.items;
|
data.value = res.data.items;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
interface DialogExpose {
|
const dialogRef = ref();
|
||||||
acceptParams: (params: any) => void;
|
|
||||||
}
|
|
||||||
const dialogRef = ref<DialogExpose>();
|
|
||||||
const onOpenDialog = async (
|
const onOpenDialog = async (
|
||||||
title: string,
|
title: string,
|
||||||
rowData: Partial<Container.RepoInfo> = {
|
rowData: Partial<Container.RepoInfo> = {
|
||||||
@ -91,7 +86,7 @@ const onBatchDelete = async (row: Container.RepoInfo | null) => {
|
|||||||
ids.push(item.id);
|
ids.push(item.id);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
await useDeleteData(deleteRepo, { ids: ids }, 'commons.msg.delete', true);
|
await useDeleteData(deleteImageRepo, { ids: ids }, 'commons.msg.delete', true);
|
||||||
search();
|
search();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ import { Rules } from '@/global/form-rules';
|
|||||||
import i18n from '@/lang';
|
import i18n from '@/lang';
|
||||||
import { ElForm, ElMessage } from 'element-plus';
|
import { ElForm, ElMessage } from 'element-plus';
|
||||||
import { Container } from '@/api/interface/container';
|
import { Container } from '@/api/interface/container';
|
||||||
import { repoCreate, repoUpdate } from '@/api/modules/container';
|
import { createImageRepo, updateImageRepo } from '@/api/modules/container';
|
||||||
|
|
||||||
interface DialogProps {
|
interface DialogProps {
|
||||||
title: string;
|
title: string;
|
||||||
@ -89,10 +89,10 @@ const onSubmit = async (formEl: FormInstance | undefined) => {
|
|||||||
formEl.validate(async (valid) => {
|
formEl.validate(async (valid) => {
|
||||||
if (!valid) return;
|
if (!valid) return;
|
||||||
if (dialogData.value.title === 'create') {
|
if (dialogData.value.title === 'create') {
|
||||||
await repoCreate(dialogData.value.rowData!);
|
await createImageRepo(dialogData.value.rowData!);
|
||||||
}
|
}
|
||||||
if (dialogData.value.title === 'edit') {
|
if (dialogData.value.title === 'edit') {
|
||||||
await repoUpdate(dialogData.value.rowData!);
|
await updateImageRepo(dialogData.value.rowData!);
|
||||||
}
|
}
|
||||||
ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
|
ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
|
||||||
restForm();
|
restForm();
|
||||||
|
@ -72,7 +72,7 @@ import { javascript } from '@codemirror/lang-javascript';
|
|||||||
import { oneDark } from '@codemirror/theme-one-dark';
|
import { oneDark } from '@codemirror/theme-one-dark';
|
||||||
import { reactive, onMounted, ref } from 'vue';
|
import { reactive, onMounted, ref } from 'vue';
|
||||||
import { dateFromat } from '@/utils/util';
|
import { dateFromat } from '@/utils/util';
|
||||||
import { deleteVolume, getVolumePage, inspect } from '@/api/modules/container';
|
import { deleteVolume, searchVolume, inspect } from '@/api/modules/container';
|
||||||
import { Container } from '@/api/interface/container';
|
import { Container } from '@/api/interface/container';
|
||||||
import i18n from '@/lang';
|
import i18n from '@/lang';
|
||||||
import { useDeleteData } from '@/hooks/use-delete-data';
|
import { useDeleteData } from '@/hooks/use-delete-data';
|
||||||
@ -103,7 +103,7 @@ const search = async () => {
|
|||||||
page: paginationConfig.page,
|
page: paginationConfig.page,
|
||||||
pageSize: paginationConfig.pageSize,
|
pageSize: paginationConfig.pageSize,
|
||||||
};
|
};
|
||||||
await getVolumePage(params).then((res) => {
|
await searchVolume(params).then((res) => {
|
||||||
if (res.data) {
|
if (res.data) {
|
||||||
data.value = res.data.items;
|
data.value = res.data.items;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user