feat: 增加acme账号管理

This commit is contained in:
zhengkunwang223 2022-11-11 17:41:39 +08:00 committed by zhengkunwang223
parent 6d2e372a07
commit ac864ff4e7
36 changed files with 705 additions and 76 deletions

View file

@ -32,7 +32,10 @@ var (
operationService = service.ServiceGroupApp.OperationService
commandService = service.ServiceGroupApp.CommandService
websiteGroupService = service.ServiceGroupApp.WebsiteGroupService
websiteService = service.ServiceGroupApp.WebsiteService
webSiteDnsAccountService = service.ServiceGroupApp.WebSiteDnsAccountService
websiteDnsAccountService = service.ServiceGroupApp.WebSiteDnsAccountService
websiteSSLService = service.ServiceGroupApp.WebSiteSSLService
websiteAcmeAccountService = service.ServiceGroupApp.WebSiteAcmeAccountService
)

View file

@ -0,0 +1,54 @@
package v1
import (
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/gin-gonic/gin"
)
func (b *BaseApi) PageWebsiteAcmeAccount(c *gin.Context) {
var req dto.PageInfo
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
total, accounts, err := websiteAcmeAccountService.Page(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, dto.PageResult{
Total: total,
Items: accounts,
})
}
func (b *BaseApi) CreateWebsiteAcmeAccount(c *gin.Context) {
var req dto.WebsiteAcmeAccountCreate
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
res, err := websiteAcmeAccountService.Create(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, res)
}
func (b *BaseApi) DeleteWebsiteAcmeAccount(c *gin.Context) {
id, err := helper.GetParamID(c)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if err := websiteAcmeAccountService.Delete(id); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}

View file

@ -13,7 +13,7 @@ func (b *BaseApi) PageWebsiteDnsAccount(c *gin.Context) {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
total, accounts, err := webSiteDnsAccountService.Page(req)
total, accounts, err := websiteDnsAccountService.Page(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
@ -30,7 +30,7 @@ func (b *BaseApi) CreateWebsiteDnsAccount(c *gin.Context) {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if _, err := webSiteDnsAccountService.Create(req); err != nil {
if _, err := websiteDnsAccountService.Create(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
@ -43,7 +43,7 @@ func (b *BaseApi) UpdateWebsiteDnsAccount(c *gin.Context) {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if _, err := webSiteDnsAccountService.Update(req); err != nil {
if _, err := websiteDnsAccountService.Update(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
@ -58,7 +58,7 @@ func (b *BaseApi) DeleteWebsiteDnsAccount(c *gin.Context) {
return
}
if err := webSiteDnsAccountService.Delete(id); err != nil {
if err := websiteDnsAccountService.Delete(id); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}

View file

@ -0,0 +1,66 @@
package v1
import (
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/gin-gonic/gin"
)
func (b *BaseApi) PageWebsiteSSL(c *gin.Context) {
var req dto.PageInfo
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
total, accounts, err := websiteSSLService.Page(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, dto.PageResult{
Total: total,
Items: accounts,
})
}
//func (b *BaseApi) CreateWebsiteSSL(c *gin.Context) {
// var req dto.WebsiteSSLCreate
// if err := c.ShouldBindJSON(&req); err != nil {
// helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
// return
// }
// if _, err := WebsiteSSLService.Create(req); err != nil {
// helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
// return
// }
// helper.SuccessWithData(c, nil)
//}
//
//func (b *BaseApi) UpdateWebsiteSSL(c *gin.Context) {
// var req dto.WebsiteSSLUpdate
// if err := c.ShouldBindJSON(&req); err != nil {
// helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
// return
// }
// if _, err := WebsiteSSLService.Update(req); err != nil {
// helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
// return
// }
// helper.SuccessWithData(c, nil)
//}
//
//func (b *BaseApi) DeleteWebsiteSSL(c *gin.Context) {
//
// id, err := helper.GetParamID(c)
// if err != nil {
// helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
// return
// }
//
// if err := WebsiteSSLService.Delete(id); err != nil {
// helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
// return
// }
// helper.SuccessWithData(c, nil)
//}

View file

@ -0,0 +1,11 @@
package dto
import "github.com/1Panel-dev/1Panel/backend/app/model"
type WebsiteAcmeAccountDTO struct {
model.WebsiteAcmeAccount
}
type WebsiteAcmeAccountCreate struct {
Email string `json:"email" validate:"required"`
}

View file

@ -0,0 +1,13 @@
package dto
import "github.com/1Panel-dev/1Panel/backend/app/model"
type WebsiteSSLDTO struct {
model.WebSiteSSL
}
type WebsiteSSLCreate struct {
Name string `json:"name" validate:"required"`
Type string `json:"type" validate:"required"`
Authorization map[string]string `json:"authorization" validate:"required"`
}

View file

@ -4,13 +4,19 @@ import "time"
type WebSite struct {
BaseModel
PrimaryDomain string `gorm:"type:varchar(128);not null" json:"primaryDomain"`
Type string `gorm:"type:varchar(64);not null" json:"type"`
Alias string `gorm:"type:varchar(128);not null" json:"alias"`
Remark string `gorm:"type:longtext;" json:"remark"`
Status string `gorm:"type:varchar(64);not null" json:"status"`
ExpireDate time.Time `json:"expireDate"`
AppInstallID uint `gorm:"type:integer" json:"appInstallID"`
WebSiteGroupID uint `gorm:"type:integer" json:"webSiteGroupID"`
WebSiteSSLID uint `gorm:"type:integer" json:"webSiteSSLID"`
PrimaryDomain string `gorm:"type:varchar(128);not null" json:"primaryDomain"`
Type string `gorm:"type:varchar(64);not null" json:"type"`
Alias string `gorm:"type:varchar(128);not null" json:"alias"`
Remark string `gorm:"type:longtext;" json:"remark"`
Status string `gorm:"type:varchar(64);not null" json:"status"`
ExpireDate time.Time `json:"expireDate"`
AppInstallID uint `gorm:"type:integer" json:"appInstallId"`
WebSiteGroupID uint `gorm:"type:integer" json:"webSiteGroupId"`
WebSiteSSLID uint `gorm:"type:integer" json:"webSiteSSLId"`
WebsiteDnsAccountID uint `gorm:"type:integer" json:"websiteDnsAccountId"`
WebsiteAcmeAccountID uint `gorm:"type:integer" json:"websiteAcmeAccountId"`
}
func (w WebSite) TableName() string {
return "websites"
}

View file

@ -0,0 +1,12 @@
package model
type WebsiteAcmeAccount struct {
BaseModel
Email string `gorm:"type:varchar(256);not null" json:"email"`
URL string `gorm:"type:varchar(256);not null" json:"url"`
PrivateKey string `gorm:"type:longtext;not null" json:"_"`
}
func (w WebsiteAcmeAccount) TableName() string {
return "website_acme_accounts"
}

View file

@ -6,3 +6,7 @@ type WebsiteDnsAccount struct {
Type string `gorm:"type:varchar(64);not null" json:"type"`
Authorization string `gorm:"type:varchar(256);not null" json:"_"`
}
func (w WebsiteDnsAccount) TableName() string {
return "website_dns_accounts"
}

View file

@ -6,3 +6,7 @@ type WebSiteDomain struct {
Domain string `gorm:"type:varchar(256);not null" json:"domain"`
Port int `gorm:"type:integer" json:"port"`
}
func (w WebSiteDomain) TableName() string {
return "website_domains"
}

View file

@ -5,3 +5,7 @@ type WebSiteGroup struct {
Name string `gorm:"type:varchar(64);not null" json:"name"`
Default bool `json:"default"`
}
func (w WebSiteGroup) TableName() string {
return "website_groups"
}

View file

@ -1,5 +1,19 @@
package model
import "time"
type WebSiteSSL struct {
BaseModel
PrivateKey string `gorm:"type:longtext;not null" json:"privateKey"`
Pem string `gorm:"type:longtext;not null" json:"pem"`
Domain string `gorm:"type:varchar(256);not null" json:"domain"`
CertURL string `gorm:"type:varchar(256);not null" json:"certURL"`
Type string `gorm:"type:varchar(64);not null" json:"type"`
IssuerName string `gorm:"type:varchar(64);not null" json:"issuerName"`
ExpireDate time.Time `json:"expireDate"`
StartDate time.Time `json:"startDate"`
}
func (w WebSiteSSL) TableName() string {
return "website_ssls"
}

View file

@ -24,6 +24,8 @@ type RepoGroup struct {
WebSiteDomainRepo
WebSiteGroupRepo
WebsiteDnsAccountRepo
WebsiteSSLRepo
WebsiteAcmeAccountRepo
}
var RepoGroupApp = new(RepoGroup)

View file

@ -0,0 +1,27 @@
package repo
import "github.com/1Panel-dev/1Panel/backend/app/model"
type WebsiteAcmeAccountRepo struct {
}
func (w WebsiteAcmeAccountRepo) Page(page, size int, opts ...DBOption) (int64, []model.WebsiteAcmeAccount, error) {
var accounts []model.WebsiteAcmeAccount
db := getDb(opts...).Model(&model.WebsiteAcmeAccount{})
count := int64(0)
db = db.Count(&count)
err := db.Debug().Limit(size).Offset(size * (page - 1)).Find(&accounts).Error
return count, accounts, err
}
func (w WebsiteAcmeAccountRepo) Create(account model.WebsiteAcmeAccount) error {
return getDb().Create(&account).Error
}
func (w WebsiteAcmeAccountRepo) Save(account model.WebsiteAcmeAccount) error {
return getDb().Save(&account).Error
}
func (w WebsiteAcmeAccountRepo) DeleteBy(opts ...DBOption) error {
return getDb(opts...).Debug().Delete(&model.WebsiteAcmeAccount{}).Error
}

View file

@ -0,0 +1,27 @@
package repo
import "github.com/1Panel-dev/1Panel/backend/app/model"
type WebsiteSSLRepo struct {
}
func (w WebsiteSSLRepo) Page(page, size int, opts ...DBOption) (int64, []model.WebSiteSSL, error) {
var sslList []model.WebSiteSSL
db := getDb(opts...).Model(&model.WebSiteSSL{})
count := int64(0)
db = db.Count(&count)
err := db.Debug().Limit(size).Offset(size * (page - 1)).Find(&sslList).Error
return count, sslList, err
}
func (w WebsiteSSLRepo) Create(ssl model.WebSiteSSL) error {
return getDb().Create(&ssl).Error
}
func (w WebsiteSSLRepo) Save(ssl model.WebSiteSSL) error {
return getDb().Save(&ssl).Error
}
func (w WebsiteSSLRepo) DeleteBy(opts ...DBOption) error {
return getDb(opts...).Delete(&model.WebSiteSSL{}).Error
}

View file

@ -29,6 +29,8 @@ type ServiceGroup struct {
WebsiteGroupService
WebsiteService
WebSiteDnsAccountService
WebSiteSSLService
WebSiteAcmeAccountService
}
var ServiceGroupApp = new(ServiceGroup)
@ -64,4 +66,6 @@ var (
websiteGroupRepo = repo.RepoGroupApp.WebSiteGroupRepo
websiteDomainRepo = repo.RepoGroupApp.WebSiteDomainRepo
websiteDnsRepo = repo.RepoGroupApp.WebsiteDnsAccountRepo
websiteSSLRepo = repo.RepoGroupApp.WebsiteSSLRepo
websiteAcmeRepo = repo.RepoGroupApp.WebsiteAcmeAccountRepo
)

View file

@ -0,0 +1,41 @@
package service
import (
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/1Panel-dev/1Panel/backend/utils/ssl"
)
type WebSiteAcmeAccountService struct {
}
func (w WebSiteAcmeAccountService) Page(search dto.PageInfo) (int64, []dto.WebsiteAcmeAccountDTO, error) {
total, accounts, err := websiteAcmeRepo.Page(search.Page, search.PageSize, commonRepo.WithOrderBy("created_at desc"))
var accountDTOs []dto.WebsiteAcmeAccountDTO
for _, account := range accounts {
accountDTOs = append(accountDTOs, dto.WebsiteAcmeAccountDTO{
WebsiteAcmeAccount: account,
})
}
return total, accountDTOs, err
}
func (w WebSiteAcmeAccountService) Create(create dto.WebsiteAcmeAccountCreate) (dto.WebsiteAcmeAccountDTO, error) {
client, err := ssl.NewAcmeClient(create.Email, "")
if err != nil {
return dto.WebsiteAcmeAccountDTO{}, err
}
acmeAccount := model.WebsiteAcmeAccount{
Email: create.Email,
URL: client.User.Registration.URI,
PrivateKey: string(ssl.GetPrivateKey(client.User.GetPrivateKey())),
}
if err := websiteAcmeRepo.Create(acmeAccount); err != nil {
return dto.WebsiteAcmeAccountDTO{}, err
}
return dto.WebsiteAcmeAccountDTO{WebsiteAcmeAccount: acmeAccount}, nil
}
func (w WebSiteAcmeAccountService) Delete(id uint) error {
return websiteAcmeRepo.DeleteBy(commonRepo.WithByID(id))
}

View file

@ -0,0 +1,62 @@
package service
import (
"github.com/1Panel-dev/1Panel/backend/app/dto"
)
type WebSiteSSLService struct {
}
func (w WebSiteSSLService) Page(search dto.PageInfo) (int64, []dto.WebsiteSSLDTO, error) {
total, sslList, err := websiteSSLRepo.Page(search.Page, search.PageSize, commonRepo.WithOrderBy("created_at desc"))
var sslDTOs []dto.WebsiteSSLDTO
for _, ssl := range sslList {
sslDTOs = append(sslDTOs, dto.WebsiteSSLDTO{
WebSiteSSL: ssl,
})
}
return total, sslDTOs, err
}
//func (w WebSiteSSLService) Create(create dto.WebsiteSSLCreate) (dto.WebsiteSSLCreate, error) {
//
// authorization, err := json.Marshal(create.Authorization)
// if err != nil {
// return dto.WebsiteSSLCreate{}, err
// }
//
// if err := websiteSSLRepo.Create(model.WebsiteDnsAccount{
// Name: create.Name,
// Type: create.Type,
// Authorization: string(authorization),
// }); err != nil {
// return dto.WebsiteSSLCreate{}, err
// }
//
// return create, nil
//}
//
//func (w WebSiteSSLService) Update(update dto.WebsiteDnsAccountUpdate) (dto.WebsiteDnsAccountUpdate, error) {
//
// authorization, err := json.Marshal(update.Authorization)
// if err != nil {
// return dto.WebsiteDnsAccountUpdate{}, err
// }
//
// if err := websiteSSLRepo.Save(model.WebsiteDnsAccount{
// BaseModel: model.BaseModel{
// ID: update.ID,
// },
// Name: update.Name,
// Type: update.Type,
// Authorization: string(authorization),
// }); err != nil {
// return dto.WebsiteDnsAccountUpdate{}, err
// }
//
// return update, nil
//}
func (w WebSiteSSLService) Delete(id uint) error {
return websiteSSLRepo.DeleteBy(commonRepo.WithByID(id))
}

View file

@ -18,7 +18,6 @@ func Init() {
migrations.AddTableApp,
migrations.AddTableImageRepo,
migrations.AddTableWebsite,
migrations.AddTableDnsAccount,
migrations.AddTableDatabaseMysql,
})
if err := m.Migrate(); err != nil {

View file

@ -181,7 +181,7 @@ var AddTableDatabaseMysql = &gormigrate.Migration{
var AddTableWebsite = &gormigrate.Migration{
ID: "20201009-add-table-website",
Migrate: func(tx *gorm.DB) error {
if err := tx.AutoMigrate(&model.WebSite{}, &model.WebSiteDomain{}, &model.WebSiteGroup{}); err != nil {
if err := tx.AutoMigrate(&model.WebSite{}, &model.WebSiteDomain{}, &model.WebSiteGroup{}, &model.WebsiteDnsAccount{}, &model.WebSiteSSL{}, &model.WebsiteAcmeAccount{}); err != nil {
return err
}
item := &model.WebSiteGroup{
@ -194,13 +194,3 @@ var AddTableWebsite = &gormigrate.Migration{
return nil
},
}
var AddTableDnsAccount = &gormigrate.Migration{
ID: "20201009-add-table-dns",
Migrate: func(tx *gorm.DB) error {
if err := tx.AutoMigrate(&model.WebsiteDnsAccount{}); err != nil {
return err
}
return nil
},
}

View file

@ -83,6 +83,7 @@ func Routers() *gin.Engine {
systemRouter.InitWebsiteGroupRouter(PrivateGroup)
systemRouter.InitWebsiteDnsAccountRouter(PrivateGroup)
systemRouter.InitDatabaseRouter(PrivateGroup)
systemRouter.InitWebsiteAcmeAccountRouter(PrivateGroup)
}
return Router

View file

@ -17,6 +17,7 @@ type RouterGroup struct {
WebsiteRouter
WebsiteGroupRouter
WebsiteDnsAccountRouter
WebsiteAcmeAccountRouter
DatabaseRouter
}

View file

@ -0,0 +1,22 @@
package router
import (
v1 "github.com/1Panel-dev/1Panel/backend/app/api/v1"
"github.com/1Panel-dev/1Panel/backend/middleware"
"github.com/gin-gonic/gin"
)
type WebsiteAcmeAccountRouter struct {
}
func (a *WebsiteAcmeAccountRouter) InitWebsiteAcmeAccountRouter(Router *gin.RouterGroup) {
groupRouter := Router.Group("websites/acme")
groupRouter.Use(middleware.JwtAuth()).Use(middleware.SessionAuth())
baseApi := v1.ApiGroupApp.BaseApi
{
groupRouter.POST("/search", baseApi.PageWebsiteAcmeAccount)
groupRouter.POST("", baseApi.CreateWebsiteAcmeAccount)
groupRouter.DELETE("/:id", baseApi.DeleteWebsiteAcmeAccount)
}
}

View file

@ -0,0 +1,21 @@
package router
import (
v1 "github.com/1Panel-dev/1Panel/backend/app/api/v1"
"github.com/1Panel-dev/1Panel/backend/middleware"
"github.com/gin-gonic/gin"
)
type WebsiteSSLRouter struct {
}
func (a *WebsiteSSLRouter) InitWebsiteSSLRouter(Router *gin.RouterGroup) {
groupRouter := Router.Group("websites/ssl")
groupRouter.Use(middleware.JwtAuth()).Use(middleware.SessionAuth())
baseApi := v1.ApiGroupApp.BaseApi
{
groupRouter.POST("", baseApi.PageWebsiteSSL)
}
}

View file

@ -1,27 +1,27 @@
-----BEGIN privateKey-----
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
-----END privateKey-----

View file

@ -1,6 +1,7 @@
package ssl
import (
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
@ -10,8 +11,9 @@ import (
"github.com/go-acme/lego/v4/registration"
)
func GetPrivateKey(priKey *rsa.PrivateKey) []byte {
derStream := x509.MarshalPKCS1PrivateKey(priKey)
func GetPrivateKey(priKey crypto.PrivateKey) []byte {
rsaKey := priKey.(*rsa.PrivateKey)
derStream := x509.MarshalPKCS1PrivateKey(rsaKey)
block := &pem.Block{
Type: "privateKey",
Bytes: derStream,
@ -25,9 +27,10 @@ func NewRegisterClient(email string) (*AcmeClient, error) {
if err != nil {
panic(err)
}
myUser := &AcmeUser{
Email: email,
key: priKey,
Key: priKey,
}
config := newConfig(myUser)
client, err := lego.NewClient(config)
@ -58,7 +61,7 @@ func NewPrivateKeyClient(email string, privateKey string) (*AcmeClient, error) {
}
myUser := &AcmeUser{
Email: email,
key: priKey,
Key: priKey,
}
config := newConfig(myUser)
client, err := lego.NewClient(config)

View file

@ -16,7 +16,7 @@ import (
type AcmeUser struct {
Email string
Registration *registration.Resource
key crypto.PrivateKey
Key crypto.PrivateKey
}
func (u *AcmeUser) GetEmail() string {
@ -27,7 +27,7 @@ func (u *AcmeUser) GetRegistration() *registration.Resource {
return u.Registration
}
func (u *AcmeUser) GetPrivateKey() crypto.PrivateKey {
return u.key
return u.Key
}
type AcmeClient struct {

View file

@ -8,15 +8,15 @@ export namespace WebSite {
remark: string;
domains: string[];
appType: string;
appInstallID?: number;
webSiteGroupID: number;
appInstallId?: number;
webSiteGroupId: number;
otherDomains: string;
appinstall?: NewAppInstall;
}
export interface NewAppInstall {
name: string;
appDetailID: number;
appDetailId: number;
params: any;
}
@ -36,8 +36,8 @@ export namespace WebSite {
alias: string;
remark: string;
appType: string;
appInstallID: number;
webSiteGroupID: number;
appInstallId: number;
webSiteGroupId: number;
otherDomains: string;
}
@ -45,7 +45,7 @@ export namespace WebSite {
id: number;
primaryDomain: string;
remark: string;
webSiteGroupID: number;
webSiteGroupId: number;
}
export interface Group extends CommonModel {
@ -103,4 +103,24 @@ export namespace WebSite {
type: string;
authorization: Object;
}
export interface SSL extends CommonModel {
privateKey: string;
pem: string;
domain: string;
certURL: string;
type: string;
issuerName: string;
expireDate: string;
startDate: string;
}
export interface AcmeAccount extends CommonModel {
email: string;
url: string;
}
export interface AcmeAccountCreate {
email: string;
}
}

View file

@ -73,3 +73,19 @@ export const UpdateDnsAccount = (req: WebSite.DnsAccountUpdate) => {
export const DeleteDnsAccount = (id: number) => {
return http.delete<any>(`/websites/dns/${id}`);
};
export const SearchSSL = (req: ReqPage) => {
return http.post<ResPage<WebSite.SSL>>(`/websites/ssl`, req);
};
export const SearchAcmeAccount = (req: ReqPage) => {
return http.post<ResPage<WebSite.AcmeAccount>>(`/websites/acme/search`, req);
};
export const CreateAcmeAccount = (req: WebSite.AcmeAccountCreate) => {
return http.post<WebSite.AcmeAccount>(`/websites/acme`, req);
};
export const DeleteAcmeAccount = (id: number) => {
return http.delete<any>(`/websites/acme/${id}`);
};

View file

@ -704,5 +704,8 @@ export default {
manual: '手动解析',
key: '密钥',
check: '查看',
accountManage: '账户管理',
email: '邮箱',
addAccount: '新增账户',
},
};

View file

@ -13,7 +13,7 @@
<el-input v-model="form.primaryDomain"></el-input>
</el-form-item>
<el-form-item :label="$t('website.group')" prop="webSiteGroupID">
<el-select v-model="form.webSiteGroupID">
<el-select v-model="form.webSiteGroupId">
<el-option
v-for="(group, index) in groups"
:key="index"
@ -59,11 +59,11 @@ let form = reactive({
id: websiteId.value,
primaryDomain: '',
remark: '',
webSiteGroupID: 0,
webSiteGroupId: 0,
});
let rules = ref({
primaryDomain: [Rules.requiredInput],
webSiteGroupID: [Rules.requiredSelect],
webSiteGroupId: [Rules.requiredSelect],
});
let groups = ref<WebSite.Group[]>([]);
@ -91,7 +91,7 @@ const search = () => {
// form.id = res.data.id;
form.primaryDomain = res.data.primaryDomain;
form.remark = res.data.remark;
form.webSiteGroupID = res.data.webSiteGroupID;
form.webSiteGroupId = res.data.webSiteGroupId;
});
});
};

View file

@ -59,10 +59,6 @@ const accountData = ref<AccountProps>({
});
const types = [
{
label: i18n.global.t('website.manual'),
value: 'Manual',
},
{
label: 'DnsPod',
value: 'DnsPod',
@ -93,7 +89,7 @@ let rules = ref({
let account = ref({
id: 0,
name: '',
type: '',
type: 'DnsPod',
authorization: {},
});
const em = defineEmits(['close']);
@ -108,14 +104,13 @@ const resetForm = () => {
account.value = {
id: 0,
name: '',
type: '',
type: 'DnsPod',
authorization: {},
};
accountForm.value?.resetFields();
};
const acceptParams = async (props: AccountProps) => {
resetForm();
accountData.value.mode = props.mode;
if (props.mode === 'edit') {
account.value = props.form;

View file

@ -6,8 +6,9 @@
<el-tab-pane :label="$t('website.dnsAccount')">
<Account :id="id" v-if="index == '1'"></Account>
</el-tab-pane>
<el-tab-pane :label="$t('website.applySSL')"></el-tab-pane>
<el-tab-pane :label="$t('website.SSLList')"></el-tab-pane>
<el-tab-pane :label="$t('website.applySSL')">
<SSL v-if="index == '2'"></SSL>
</el-tab-pane>
</el-tabs>
</template>
@ -15,6 +16,7 @@
import { computed, ref } from 'vue';
import Current from './current/index.vue';
import Account from './account/index.vue';
import SSL from './ssl/index.vue';
const props = defineProps({
id: {

View file

@ -0,0 +1,78 @@
<template>
<el-dialog v-model="open" :title="$t('commons.button.create')" width="30%" :before-close="handleClose">
<el-form
ref="accountForm"
label-position="right"
:model="account"
label-width="100px"
:rules="rules"
v-loading="loading"
>
<el-form-item :label="$t('website.email')" prop="email">
<el-input v-model="account.email"></el-input>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="handleClose" :disabled="loading">{{ $t('commons.button.cancel') }}</el-button>
<el-button type="primary" @click="submit(accountForm)" :loading="loading">
{{ $t('commons.button.confirm') }}
</el-button>
</span>
</template>
</el-dialog>
</template>
<script lang="ts" setup>
import { ElMessage, FormInstance } from 'element-plus';
import { ref } from 'vue';
import { Rules } from '@/global/form-rules';
import { CreateAcmeAccount } from '@/api/modules/website';
import i18n from '@/lang';
let open = ref();
let loading = ref(false);
let accountForm = ref<FormInstance>();
let rules = ref({
email: [Rules.requiredInput],
});
let account = ref({
email: '',
});
const em = defineEmits(['close']);
const handleClose = () => {
resetForm();
open.value = false;
em('close', false);
};
const resetForm = () => {};
const acceptParams = () => {
open.value = true;
};
const submit = async (formEl: FormInstance | undefined) => {
if (!formEl) return;
await formEl.validate((valid) => {
if (!valid) {
return;
}
loading.value = true;
CreateAcmeAccount(account.value)
.then(() => {
ElMessage.success(i18n.global.t('commons.msg.createSuccess'));
handleClose();
})
.finally(() => {
loading.value = false;
});
});
};
defineExpose({
acceptParams,
});
</script>

View file

@ -0,0 +1,78 @@
<template>
<el-dialog v-model="open" :title="$t('website.accountManage')">
<ComplexTable :data="data" :pagination-config="paginationConfig" @search="search()" v-loading="loading">
<template #toolbar>
<el-button type="primary" plain @click="openCreate">{{ $t('commons.button.create') }}</el-button>
</template>
<el-table-column :label="$t('website.email')" fix show-overflow-tooltip prop="email"></el-table-column>
<el-table-column label="URL" show-overflow-tooltip prop="url"></el-table-column>
<fu-table-operations
:ellipsis="1"
:buttons="buttons"
:label="$t('commons.table.operate')"
fixed="right"
fix
/>
</ComplexTable>
<Create ref="createRef" @close="search()"></Create>
</el-dialog>
</template>
<script lang="ts" setup>
import { WebSite } from '@/api/interface/website';
import { DeleteAcmeAccount, SearchAcmeAccount } from '@/api/modules/website';
import ComplexTable from '@/components/complex-table/index.vue';
import { useDeleteData } from '@/hooks/use-delete-data';
import i18n from '@/lang';
import { reactive, ref } from 'vue';
import Create from './create/index.vue';
let open = ref(false);
let loading = ref(false);
let data = ref();
let createRef = ref();
const paginationConfig = reactive({
currentPage: 1,
pageSize: 20,
total: 0,
});
const buttons = [
{
label: i18n.global.t('app.delete'),
click: function (row: WebSite.AcmeAccount) {
deleteAccount(row.id);
},
},
];
const acceptParams = () => {
search();
open.value = true;
};
const search = async () => {
const req = {
page: paginationConfig.currentPage,
pageSize: paginationConfig.pageSize,
};
await SearchAcmeAccount(req).then((res) => {
data.value = res.data.items;
paginationConfig.total = res.data.total;
});
};
const openCreate = () => {
createRef.value.acceptParams();
};
const deleteAccount = async (id: number) => {
loading.value = true;
await useDeleteData(DeleteAcmeAccount, id, 'commons.msg.delete', false);
loading.value = false;
search();
};
defineExpose({
acceptParams,
});
</script>

View file

@ -0,0 +1,46 @@
<template>
<div>
<ComplexTable :data="data" :pagination-config="paginationConfig" @search="search()">
<template #toolbar>
<el-button type="primary" plain>{{ $t('commons.button.create') }}</el-button>
<el-button type="primary" plain @click="openAccount()">{{ $t('website.accountManage') }}</el-button>
</template>
<el-table-column :label="$t('commons.table.name')" fix show-overflow-tooltip prop="name"></el-table-column>
</ComplexTable>
<Account ref="accountRef"></Account>
</div>
</template>
<script lang="ts" setup>
import ComplexTable from '@/components/complex-table/index.vue';
import { onMounted, reactive, ref } from 'vue';
import { SearchSSL } from '@/api/modules/website';
import Account from './account/index.vue';
const paginationConfig = reactive({
currentPage: 1,
pageSize: 20,
total: 0,
});
const accountRef = ref();
let data = ref();
const search = () => {
const req = {
page: paginationConfig.currentPage,
pageSize: paginationConfig.pageSize,
};
SearchSSL(req).then((res) => {
data.value = res.data.items;
paginationConfig.total = res.data.total;
});
};
const openAccount = () => {
accountRef.value.acceptParams();
};
onMounted(() => {
search();
});
</script>