diff --git a/backend/app/api/v1/entry.go b/backend/app/api/v1/entry.go index b7b768a06..3fd42289c 100644 --- a/backend/app/api/v1/entry.go +++ b/backend/app/api/v1/entry.go @@ -14,6 +14,7 @@ var ( backupService = service.ServiceGroupApp.BackupService groupService = service.ServiceGroupApp.GroupService containerService = service.ServiceGroupApp.ContainerService + imageRepoService = service.ServiceGroupApp.ImageRepoService commandService = service.ServiceGroupApp.CommandService operationService = service.ServiceGroupApp.OperationService fileService = service.ServiceGroupApp.FileService diff --git a/backend/app/api/v1/image_repo.go b/backend/app/api/v1/image_repo.go new file mode 100644 index 000000000..e17f09376 --- /dev/null +++ b/backend/app/api/v1/image_repo.go @@ -0,0 +1,92 @@ +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) GetRepoList(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 := imageRepoService.Page(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) CreateRepo(c *gin.Context) { + var req dto.ImageRepoCreate + 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.Create(req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) + return + } + helper.SuccessWithData(c, nil) +} + +func (b *BaseApi) DeleteRepo(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 := imageRepoService.BatchDelete(req.Ids); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) + return + } + helper.SuccessWithData(c, nil) +} + +func (b *BaseApi) UpdateRepo(c *gin.Context) { + var req dto.ImageRepoUpdate + 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["download_url"] = req.DownloadUrl + upMap["repo_name"] = req.RepoName + upMap["username"] = req.Username + upMap["password"] = req.Password + upMap["auth"] = req.Auth + if err := imageRepoService.Update(id, upMap); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) + return + } + helper.SuccessWithData(c, nil) +} diff --git a/backend/app/dto/image_repo.go b/backend/app/dto/image_repo.go new file mode 100644 index 000000000..57cd235ba --- /dev/null +++ b/backend/app/dto/image_repo.go @@ -0,0 +1,31 @@ +package dto + +import "time" + +type ImageRepoCreate struct { + Name string `json:"name" validate:"required"` + DownloadUrl string `json:"downloadUrl"` + RepoName string `json:"repoName"` + Username string `json:"username"` + Password string `json:"password"` + Auth bool `json:"auth"` +} + +type ImageRepoUpdate struct { + ID uint `json:"id"` + DownloadUrl string `json:"downloadUrl"` + RepoName string `json:"repoName"` + Username string `json:"username"` + Password string `json:"password"` + Auth bool `json:"auth"` +} + +type ImageRepoInfo struct { + ID uint `json:"id"` + CreatedAt time.Time `json:"createdAt"` + Name string `json:"name"` + DownloadUrl string `json:"downloadUrl"` + RepoName string `json:"repoName"` + Username string `json:"username"` + Auth bool `json:"auth"` +} diff --git a/backend/app/model/image_repo.go b/backend/app/model/image_repo.go new file mode 100644 index 000000000..9d6bd6ca7 --- /dev/null +++ b/backend/app/model/image_repo.go @@ -0,0 +1,12 @@ +package model + +type ImageRepo struct { + BaseModel + + 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"` + Username string `gorm:"type:varchar(256)" json:"username"` + Password string `gorm:"type:varchar(256)" json:"password"` + Auth bool `gorm:"type:varchar(256)" json:"auth"` +} diff --git a/backend/app/repo/entry.go b/backend/app/repo/entry.go index e8e1193cd..35bfff802 100644 --- a/backend/app/repo/entry.go +++ b/backend/app/repo/entry.go @@ -4,6 +4,7 @@ type RepoGroup struct { HostRepo BackupRepo GroupRepo + ImageRepoRepo CommandRepo OperationRepo CommonRepo diff --git a/backend/app/repo/image_repo.go b/backend/app/repo/image_repo.go new file mode 100644 index 000000000..442763e7b --- /dev/null +++ b/backend/app/repo/image_repo.go @@ -0,0 +1,58 @@ +package repo + +import ( + "github.com/1Panel-dev/1Panel/app/model" + "github.com/1Panel-dev/1Panel/global" +) + +type ImageRepoRepo struct{} + +type IImageRepoRepo interface { + Get(opts ...DBOption) (model.ImageRepo, error) + Page(limit, offset int, opts ...DBOption) (int64, []model.ImageRepo, error) + Create(imageRepo *model.ImageRepo) error + Update(id uint, vars map[string]interface{}) error + Delete(opts ...DBOption) error +} + +func NewIImageRepoRepo() IImageRepoRepo { + return &ImageRepoRepo{} +} + +func (u *ImageRepoRepo) Get(opts ...DBOption) (model.ImageRepo, error) { + var imageRepo model.ImageRepo + db := global.DB + for _, opt := range opts { + db = opt(db) + } + err := db.First(&imageRepo).Error + return imageRepo, err +} + +func (u *ImageRepoRepo) Page(page, size int, opts ...DBOption) (int64, []model.ImageRepo, error) { + var ops []model.ImageRepo + db := global.DB.Model(&model.ImageRepo{}) + for _, opt := range opts { + db = opt(db) + } + count := int64(0) + db = db.Count(&count) + err := db.Limit(size).Offset(size * (page - 1)).Find(&ops).Error + return count, ops, err +} + +func (u *ImageRepoRepo) Create(imageRepo *model.ImageRepo) error { + return global.DB.Create(imageRepo).Error +} + +func (u *ImageRepoRepo) Update(id uint, vars map[string]interface{}) error { + return global.DB.Model(&model.ImageRepo{}).Where("id = ?", id).Updates(vars).Error +} + +func (u *ImageRepoRepo) Delete(opts ...DBOption) error { + db := global.DB + for _, opt := range opts { + db = opt(db) + } + return db.Delete(&model.ImageRepo{}).Error +} diff --git a/backend/app/service/entry.go b/backend/app/service/entry.go index 3d939869b..539de4e01 100644 --- a/backend/app/service/entry.go +++ b/backend/app/service/entry.go @@ -7,6 +7,7 @@ type ServiceGroup struct { HostService BackupService GroupService + ImageRepoService ContainerService CommandService OperationService @@ -25,6 +26,7 @@ var ( commandRepo = repo.RepoGroupApp.CommandRepo operationRepo = repo.RepoGroupApp.OperationRepo commonRepo = repo.RepoGroupApp.CommonRepo + imageRepoRepo = repo.RepoGroupApp.ImageRepoRepo cronjobRepo = repo.RepoGroupApp.CronjobRepo settingRepo = repo.RepoGroupApp.SettingRepo appRepo = repo.RepoGroupApp.AppRepo diff --git a/backend/app/service/image_repo.go b/backend/app/service/image_repo.go new file mode 100644 index 000000000..7c92b4c12 --- /dev/null +++ b/backend/app/service/image_repo.go @@ -0,0 +1,56 @@ +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 ImageRepoService struct{} + +type IImageRepoService interface { + Page(search dto.PageInfo) (int64, interface{}, error) + Create(imageRepoDto dto.ImageRepoCreate) error + Update(id uint, upMap map[string]interface{}) error + BatchDelete(ids []uint) error +} + +func NewIImageRepoService() IImageRepoService { + return &ImageRepoService{} +} + +func (u *ImageRepoService) Page(search dto.PageInfo) (int64, interface{}, error) { + total, ops, err := imageRepoRepo.Page(search.Page, search.PageSize, commonRepo.WithOrderBy("created_at desc")) + var dtoOps []dto.ImageRepoInfo + for _, op := range ops { + var item dto.ImageRepoInfo + if err := copier.Copy(&item, &op); err != nil { + return 0, nil, errors.WithMessage(constant.ErrStructTransform, err.Error()) + } + dtoOps = append(dtoOps, item) + } + return total, dtoOps, err +} + +func (u *ImageRepoService) Create(imageRepoDto dto.ImageRepoCreate) error { + imageRepo, _ := imageRepoRepo.Get(commonRepo.WithByName(imageRepoDto.RepoName)) + if imageRepo.ID != 0 { + return constant.ErrRecordExist + } + if err := copier.Copy(&imageRepo, &imageRepoDto); err != nil { + return errors.WithMessage(constant.ErrStructTransform, err.Error()) + } + if err := imageRepoRepo.Create(&imageRepo); err != nil { + return err + } + return nil +} + +func (u *ImageRepoService) BatchDelete(ids []uint) error { + return imageRepoRepo.Delete(commonRepo.WithIdsIn(ids)) +} + +func (u *ImageRepoService) Update(id uint, upMap map[string]interface{}) error { + return imageRepoRepo.Update(id, upMap) +} diff --git a/backend/init/migration/migrate.go b/backend/init/migration/migrate.go index 1c002d7d6..3d63b4b3c 100644 --- a/backend/init/migration/migrate.go +++ b/backend/init/migration/migrate.go @@ -16,6 +16,7 @@ func Init() { migrations.AddTableBackupAccount, migrations.AddTableCronjob, migrations.AddTableApp, + migrations.AddTableImageRepo, }) if err := m.Migrate(); err != nil { global.LOG.Error(err) diff --git a/backend/init/migration/migrations/init.go b/backend/init/migration/migrations/init.go index 8df51b646..f1ec2f146 100644 --- a/backend/init/migration/migrations/init.go +++ b/backend/init/migration/migrations/init.go @@ -153,3 +153,20 @@ var AddTableApp = &gormigrate.Migration{ return tx.AutoMigrate(&model.App{}, &model.AppDetail{}, &model.Tag{}, &model.AppTag{}, &model.AppInstall{}, &model.AppInstallResource{}, &model.Database{}, &model.AppInstallBackup{}) }, } + +var AddTableImageRepo = &gormigrate.Migration{ + ID: "20201009-add-table-imagerepo", + Migrate: func(tx *gorm.DB) error { + if err := tx.AutoMigrate(&model.ImageRepo{}); err != nil { + return err + } + item := &model.ImageRepo{ + Name: "Docker Hub", + DownloadUrl: "docker.io", + } + if err := tx.Create(item).Error; err != nil { + return err + } + return nil + }, +} diff --git a/backend/router/container.go b/backend/router/ro_container.go similarity index 79% rename from backend/router/container.go rename to backend/router/ro_container.go index 0149a79f5..ffdcf956e 100644 --- a/backend/router/container.go +++ b/backend/router/ro_container.go @@ -24,5 +24,10 @@ func (s *ContainerRouter) InitContainerRouter(Router *gin.RouterGroup) { baRouter.GET("/detail/:id", baseApi.ContainerDetail) withRecordRouter.POST("operate", baseApi.ContainerOperation) withRecordRouter.POST("/log", baseApi.ContainerLogs) + + baRouter.POST("/repo/search", baseApi.GetRepoList) + baRouter.PUT("/repo/:id", baseApi.UpdateRepo) + withRecordRouter.POST("/repo", baseApi.CreateRepo) + withRecordRouter.POST("/repo/del", baseApi.DeleteRepo) } } diff --git a/backend/router/operation_log.go b/backend/router/ro_operation_log.go similarity index 100% rename from backend/router/operation_log.go rename to backend/router/ro_operation_log.go diff --git a/frontend/src/api/interface/container.ts b/frontend/src/api/interface/container.ts index c85c9e3be..418283ab2 100644 --- a/frontend/src/api/interface/container.ts +++ b/frontend/src/api/interface/container.ts @@ -21,4 +21,30 @@ export namespace Container { containerID: string; mode: string; } + + export interface RepoCreate { + name: string; + downloadUrl: string; + repoName: string; + username: string; + password: string; + auth: boolean; + } + export interface RepoUpdate { + id: number; + downloadUrl: string; + username: string; + password: string; + auth: boolean; + } + export interface RepoInfo { + id: number; + createdAt: Date; + name: string; + downloadUrl: string; + repoName: string; + username: string; + password: string; + auth: boolean; + } } diff --git a/frontend/src/api/modules/command.ts b/frontend/src/api/modules/command.ts index 721bae516..46769939c 100644 --- a/frontend/src/api/modules/command.ts +++ b/frontend/src/api/modules/command.ts @@ -15,7 +15,6 @@ export const addCommand = (params: Command.CommandOperate) => { }; export const editCommand = (params: Command.CommandOperate) => { - console.log(params.id); return http.put(`/commands/${params.id}`, params); }; diff --git a/frontend/src/api/modules/container.ts b/frontend/src/api/modules/container.ts index c8259e608..a6ad17a55 100644 --- a/frontend/src/api/modules/container.ts +++ b/frontend/src/api/modules/container.ts @@ -1,5 +1,5 @@ import http from '@/api'; -import { ResPage } from '../interface'; +import { ResPage, ReqPage } from '../interface'; import { Container } from '../interface/container'; export const getContainerPage = (params: Container.ContainerSearch) => { @@ -17,3 +17,17 @@ export const ContainerOperator = (params: Container.ContainerOperate) => { export const getContainerInspect = (containerID: string) => { return http.get(`/containers/detail/${containerID}`); }; + +// repo +export const getRepoPage = (params: ReqPage) => { + return http.post>(`/containers/repo/search`, params); +}; +export const repoCreate = (params: Container.RepoCreate) => { + return http.post(`/containers/repo`, params); +}; +export const repoUpdate = (params: Container.RepoUpdate) => { + return http.put(`/containers/repo/${params.id}`, params); +}; +export const deleteRepo = (params: { ids: number[] }) => { + return http.post(`/containers/repo/del`, params); +}; diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts index e755128a3..c8056cbdd 100644 --- a/frontend/src/lang/modules/en.ts +++ b/frontend/src/lang/modules/en.ts @@ -1,5 +1,7 @@ export default { commons: { + true: 'true', + false: 'false', button: { create: 'Create', add: 'Add', @@ -169,6 +171,13 @@ export default { last4Hour: 'Last 4 Hours', lastHour: 'Last Hour', last10Min: 'Last 10 Minutes', + + repo: 'Repo', + name: 'Name', + downloadUrl: 'Download URL', + imageRepo: 'ImageRepo', + repoHelper: 'Does it include a mirror repository/organization/project?', + auth: 'Auth', }, cronjob: { cronTask: 'Task', diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index 639a4c572..83e5c46a0 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -1,5 +1,7 @@ export default { commons: { + true: '是', + false: '否', button: { create: '新建', add: '添加', @@ -166,6 +168,13 @@ export default { last4Hour: '最近 4 小时', lastHour: '最近 1 小时', last10Min: '最近 10 分钟', + + repo: '仓库', + name: '名称', + downloadUrl: '下载地址', + imageRepo: '镜像库', + repoHelper: '是否包含镜像仓库/组织/项目?', + auth: '认证', }, cronjob: { cronTask: '计划任务', diff --git a/frontend/src/views/container/container/create/index.vue b/frontend/src/views/container/container/create/index.vue new file mode 100644 index 000000000..5f0df1405 --- /dev/null +++ b/frontend/src/views/container/container/create/index.vue @@ -0,0 +1,204 @@ + + + diff --git a/frontend/src/views/container/container/index.vue b/frontend/src/views/container/container/index.vue index 66c243909..13743cfe3 100644 --- a/frontend/src/views/container/container/index.vue +++ b/frontend/src/views/container/container/index.vue @@ -1,51 +1,65 @@ - + + diff --git a/frontend/src/views/container/repo/operator/index.vue b/frontend/src/views/container/repo/operator/index.vue new file mode 100644 index 000000000..6b4ee5d80 --- /dev/null +++ b/frontend/src/views/container/repo/operator/index.vue @@ -0,0 +1,107 @@ + + +