feat: 证书增加编辑功能 (#4901)

This commit is contained in:
zhengkunwang 2024-05-07 18:01:00 +08:00 committed by GitHub
parent 736914f443
commit 43ebf6eef9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 202 additions and 70 deletions

View file

@ -32,8 +32,9 @@ type WebsiteSSLRenew struct {
}
type WebsiteSSLApply struct {
ID uint `json:"ID" validate:"required"`
SkipDNSCheck bool `json:"SkipDNSCheck"`
ID uint `json:"ID" validate:"required"`
SkipDNSCheck bool `json:"skipDNSCheck"`
Nameservers []string `json:"nameservers"`
}
type WebsiteAcmeAccountCreate struct {
@ -66,9 +67,18 @@ type WebsiteBatchDelReq struct {
}
type WebsiteSSLUpdate struct {
ID uint `json:"id" validate:"required"`
AutoRenew bool `json:"autoRenew"`
Description string `json:"description"`
ID uint `json:"id" validate:"required"`
AutoRenew bool `json:"autoRenew"`
Description string `json:"description"`
PrimaryDomain string `json:"primaryDomain" validate:"required"`
OtherDomains string `json:"otherDomains"`
Provider string `json:"provider" validate:"required"`
AcmeAccountID uint `json:"acmeAccountId" validate:"required"`
DnsAccountID uint `json:"dnsAccountId"`
KeyType string `json:"keyType"`
Apply bool `json:"apply"`
PushDir bool `json:"pushDir"`
Dir string `json:"dir"`
}
type WebsiteSSLUpload struct {

View file

@ -9,24 +9,24 @@ import (
type WebsiteSSL struct {
BaseModel
PrimaryDomain string `gorm:"type:varchar(256);not null" json:"primaryDomain"`
PrivateKey string `gorm:"type:longtext;not null" json:"privateKey"`
Pem string `gorm:"type:longtext;not null" json:"pem"`
Domains string `gorm:"type:varchar(256);not null" json:"domains"`
CertURL string `gorm:"type:varchar(256);not null" json:"certURL"`
Type string `gorm:"type:varchar(64);not null" json:"type"`
Provider string `gorm:"type:varchar(64);not null" json:"provider"`
Organization string `gorm:"type:varchar(64);not null" json:"organization"`
DnsAccountID uint `gorm:"type:integer;not null" json:"dnsAccountId"`
AcmeAccountID uint `gorm:"type:integer;not null" json:"acmeAccountId"`
CaID uint `gorm:"type:integer;not null;default:0" json:"caId"`
AutoRenew bool `gorm:"type:varchar(64);not null" json:"autoRenew"`
PrimaryDomain string `json:"primaryDomain"`
PrivateKey string `json:"privateKey"`
Pem string `json:"pem"`
Domains string `json:"domains"`
CertURL string `json:"certURL"`
Type string `json:"type"`
Provider string `json:"provider"`
Organization string `json:"organization"`
DnsAccountID uint `json:"dnsAccountId"`
AcmeAccountID uint `gorm:"column:acme_account_id" json:"acmeAccountId" `
CaID uint `json:"caId"`
AutoRenew bool `json:"autoRenew"`
ExpireDate time.Time `json:"expireDate"`
StartDate time.Time `json:"startDate"`
Status string `gorm:"not null;default:ready" json:"status"`
Status string `json:"status"`
Message string `json:"message"`
KeyType string `gorm:"not null;default:2048" json:"keyType"`
PushDir bool `gorm:"not null;default:0" json:"pushDir"`
KeyType string `json:"keyType"`
PushDir bool `json:"pushDir"`
Dir string `json:"dir"`
Description string `json:"description"`

View file

@ -21,6 +21,7 @@ type ISSLRepo interface {
Create(ctx context.Context, ssl *model.WebsiteSSL) error
Save(ssl *model.WebsiteSSL) error
DeleteBy(opts ...DBOption) error
SaveByMap(ssl *model.WebsiteSSL, params map[string]interface{}) error
}
type WebsiteSSLRepo struct {
@ -82,7 +83,15 @@ func (w WebsiteSSLRepo) Create(ctx context.Context, ssl *model.WebsiteSSL) error
}
func (w WebsiteSSLRepo) Save(ssl *model.WebsiteSSL) error {
return getDb().Save(&ssl).Error
return getDb().Model(&model.WebsiteSSL{BaseModel: model.BaseModel{
ID: ssl.ID,
}}).Save(&ssl).Error
}
func (w WebsiteSSLRepo) SaveByMap(ssl *model.WebsiteSSL, params map[string]interface{}) error {
return getDb().Model(&model.WebsiteSSL{BaseModel: model.BaseModel{
ID: ssl.ID,
}}).Updates(params).Error
}
func (w WebsiteSSLRepo) DeleteBy(opts ...DBOption) error {

View file

@ -346,6 +346,7 @@ func (w WebsiteCAService) ObtainSSL(req request.WebsiteCAObtain) (*model.Website
websiteSSL.StartDate = cert.NotBefore
websiteSSL.Type = cert.Issuer.CommonName
websiteSSL.Organization = rootCsr.Subject.Organization[0]
websiteSSL.Status = constant.SSLReady
if req.Renew {
if err := websiteSSLRepo.Save(websiteSSL); err != nil {

View file

@ -191,7 +191,7 @@ func (w WebsiteSSLService) ObtainSSL(apply request.WebsiteSSLApply) error {
if err != nil {
return err
}
if err = client.UseDns(ssl.DnsType(dnsAccount.Type), dnsAccount.Authorization, apply.SkipDNSCheck); err != nil {
if err = client.UseDns(ssl.DnsType(dnsAccount.Type), dnsAccount.Authorization, apply.SkipDNSCheck, apply.Nameservers); err != nil {
return err
}
case constant.Http:
@ -368,9 +368,49 @@ func (w WebsiteSSLService) Update(update request.WebsiteSSLUpdate) error {
if err != nil {
return err
}
websiteSSL.AutoRenew = update.AutoRenew
websiteSSL.Description = update.Description
return websiteSSLRepo.Save(websiteSSL)
updateParams := make(map[string]interface{})
updateParams["primary_domain"] = update.PrimaryDomain
updateParams["description"] = update.Description
updateParams["provider"] = update.Provider
updateParams["key_type"] = update.KeyType
updateParams["push_dir"] = update.PushDir
acmeAccount, err := websiteAcmeRepo.GetFirst(commonRepo.WithByID(update.AcmeAccountID))
if err != nil {
return err
}
updateParams["acme_account_id"] = acmeAccount.ID
if update.PushDir {
if !files.NewFileOp().Stat(update.Dir) {
return buserr.New(constant.ErrLinkPathNotFound)
}
updateParams["dir"] = update.Dir
}
var domains []string
if update.OtherDomains != "" {
otherDomainArray := strings.Split(update.OtherDomains, "\n")
for _, domain := range otherDomainArray {
if !common.IsValidDomain(domain) {
return buserr.WithName("ErrDomainFormat", domain)
}
domains = append(domains, domain)
}
}
updateParams["domains"] = strings.Join(domains, ",")
if update.Provider == constant.DNSAccount || update.Provider == constant.Http {
updateParams["auto_renew"] = update.AutoRenew
} else {
updateParams["auto_renew"] = false
}
if update.Provider == constant.DNSAccount {
dnsAccount, err := websiteDnsRepo.GetFirst(commonRepo.WithByID(update.DnsAccountID))
if err != nil {
return err
}
updateParams["dns_account_id"] = dnsAccount.ID
}
return websiteSSLRepo.SaveByMap(websiteSSL, updateParams)
}
func (w WebsiteSSLService) Upload(req request.WebsiteSSLUpload) error {

View file

@ -84,7 +84,7 @@ type DNSParam struct {
SecretID string `json:"secretID"`
}
func (c *AcmeClient) UseDns(dnsType DnsType, params string, skipDNSCheck bool) error {
func (c *AcmeClient) UseDns(dnsType DnsType, params string, skipDNSCheck bool, nameservers []string) error {
var (
param DNSParam
p challenge.Provider
@ -99,15 +99,15 @@ func (c *AcmeClient) UseDns(dnsType DnsType, params string, skipDNSCheck bool) e
case DnsPod:
dnsPodConfig := dnspod.NewDefaultConfig()
dnsPodConfig.LoginToken = param.ID + "," + param.Token
dnsPodConfig.PropagationTimeout = 60 * time.Minute
dnsPodConfig.PollingInterval = 5 * time.Second
dnsPodConfig.PropagationTimeout = 15 * time.Minute
dnsPodConfig.PollingInterval = 10 * time.Second
dnsPodConfig.TTL = 3600
p, err = dnspod.NewDNSProviderConfig(dnsPodConfig)
case AliYun:
alidnsConfig := alidns.NewDefaultConfig()
alidnsConfig.SecretKey = param.SecretKey
alidnsConfig.APIKey = param.AccessKey
alidnsConfig.PropagationTimeout = 60 * time.Minute
alidnsConfig.PropagationTimeout = 15 * time.Minute
alidnsConfig.PollingInterval = 5 * time.Second
alidnsConfig.TTL = 3600
p, err = alidns.NewDNSProviderConfig(alidnsConfig)
@ -115,58 +115,61 @@ func (c *AcmeClient) UseDns(dnsType DnsType, params string, skipDNSCheck bool) e
cloudflareConfig := cloudflare.NewDefaultConfig()
cloudflareConfig.AuthEmail = param.Email
cloudflareConfig.AuthToken = param.APIkey
cloudflareConfig.PropagationTimeout = 60 * time.Minute
cloudflareConfig.PollingInterval = 5 * time.Second
cloudflareConfig.PropagationTimeout = 15 * time.Minute
cloudflareConfig.PollingInterval = 10 * time.Second
cloudflareConfig.TTL = 3600
p, err = cloudflare.NewDNSProviderConfig(cloudflareConfig)
case NameCheap:
namecheapConfig := namecheap.NewDefaultConfig()
namecheapConfig.APIKey = param.APIkey
namecheapConfig.APIUser = param.APIUser
namecheapConfig.PropagationTimeout = 60 * time.Minute
namecheapConfig.PropagationTimeout = 15 * time.Minute
namecheapConfig.PollingInterval = 5 * time.Second
namecheapConfig.TTL = 3600
p, err = namecheap.NewDNSProviderConfig(namecheapConfig)
case NameSilo:
nameSiloConfig := namesilo.NewDefaultConfig()
nameSiloConfig.APIKey = param.APIkey
nameSiloConfig.PropagationTimeout = 60 * time.Minute
nameSiloConfig.PollingInterval = 5 * time.Second
nameSiloConfig.PropagationTimeout = 15 * time.Minute
nameSiloConfig.PollingInterval = 10 * time.Second
nameSiloConfig.TTL = 3600
p, err = namesilo.NewDNSProviderConfig(nameSiloConfig)
case Godaddy:
godaddyConfig := godaddy.NewDefaultConfig()
godaddyConfig.APIKey = param.APIkey
godaddyConfig.APISecret = param.APISecret
godaddyConfig.PropagationTimeout = 60 * time.Minute
godaddyConfig.PollingInterval = 5 * time.Second
godaddyConfig.PropagationTimeout = 15 * time.Minute
godaddyConfig.PollingInterval = 10 * time.Second
godaddyConfig.TTL = 3600
p, err = godaddy.NewDNSProviderConfig(godaddyConfig)
case NameCom:
nameComConfig := namedotcom.NewDefaultConfig()
nameComConfig.APIToken = param.Token
nameComConfig.Username = param.APIUser
nameComConfig.PropagationTimeout = 30 * time.Minute
nameComConfig.PollingInterval = 30 * time.Second
nameComConfig.PropagationTimeout = 15 * time.Minute
nameComConfig.PollingInterval = 10 * time.Second
nameComConfig.TTL = 3600
p, err = namedotcom.NewDNSProviderConfig(nameComConfig)
case TencentCloud:
tencentCloudConfig := tencentcloud.NewDefaultConfig()
tencentCloudConfig.SecretID = param.SecretID
tencentCloudConfig.SecretKey = param.SecretKey
tencentCloudConfig.PropagationTimeout = 30 * time.Minute
tencentCloudConfig.PollingInterval = 30 * time.Second
tencentCloudConfig.PropagationTimeout = 15 * time.Minute
tencentCloudConfig.PollingInterval = 10 * time.Second
tencentCloudConfig.TTL = 3600
p, err = tencentcloud.NewDNSProviderConfig(tencentCloudConfig)
}
if err != nil {
return err
}
if skipDNSCheck {
return c.Client.Challenge.SetDNS01Provider(p, dns01.AddDNSTimeout(10*time.Minute), dns01.DisableCompletePropagationRequirement())
}
return c.Client.Challenge.SetDNS01Provider(p, dns01.AddDNSTimeout(10*time.Minute))
return c.Client.Challenge.SetDNS01Provider(p,
dns01.CondOption(len(nameservers) > 0,
dns01.AddRecursiveNameservers(nameservers)),
dns01.CondOption(skipDNSCheck,
dns01.DisableCompletePropagationRequirement()),
dns01.AddDNSTimeout(10*time.Minute),
)
}
func (c *AcmeClient) UseManualDns() error {

View file

@ -165,10 +165,14 @@ export namespace Website {
provider: string;
websites?: Website.Website[];
autoRenew: boolean;
acmeAccountId?: number;
acmeAccountId: number;
status: string;
domains: string;
description: string;
dnsAccountId?: number;
pushDir: boolean;
dir: string;
keyType: string;
}
export interface SSLDTO extends SSL {
@ -198,6 +202,14 @@ export namespace Website {
id: number;
autoRenew: boolean;
description: string;
primaryDomain: string;
otherDomains: string;
acmeAccountId: number;
provider: string;
dnsAccountId?: number;
keyType: string;
pushDir: boolean;
dir: string;
}
export interface AcmeAccount extends CommonModel {

View file

@ -136,7 +136,7 @@
<script lang="ts" setup>
import DrawerHeader from '@/components/drawer-header/index.vue';
import { Website } from '@/api/interface/website';
import { CreateSSL, ListWebsites, SearchAcmeAccount, SearchDnsAccount } from '@/api/modules/website';
import { CreateSSL, ListWebsites, SearchAcmeAccount, SearchDnsAccount, UpdateSSL } from '@/api/modules/website';
import { Rules } from '@/global/form-rules';
import i18n from '@/lang';
import { FormInstance } from 'element-plus';
@ -182,6 +182,7 @@ const rules = ref({
const websiteID = ref();
const initData = () => ({
id: 0,
primaryDomain: '',
otherDomains: '',
provider: 'dnsAccount',
@ -196,8 +197,8 @@ const initData = () => ({
});
const ssl = ref(initData());
const operate = ref('create');
const dnsResolve = ref<Website.DNSResolve[]>([]);
const em = defineEmits(['close', 'submit']);
const handleClose = () => {
resetForm();
@ -211,8 +212,26 @@ const resetForm = () => {
websiteID.value = undefined;
};
const acceptParams = () => {
resetForm();
const acceptParams = (op: string, websiteSSL: Website.SSLDTO) => {
operate.value = op;
if (op == 'create') {
resetForm();
}
if (op == 'edit') {
console.log(websiteSSL);
ssl.value.acmeAccountId = websiteSSL.acmeAccountId;
if (websiteSSL.dnsAccountId > 0) {
ssl.value.dnsAccountId = websiteSSL.dnsAccountId;
}
ssl.value.primaryDomain = websiteSSL.primaryDomain;
ssl.value.pushDir = websiteSSL.pushDir;
ssl.value.dir = websiteSSL.dir;
ssl.value.otherDomains = websiteSSL.domains;
ssl.value.autoRenew = websiteSSL.autoRenew;
ssl.value.description = websiteSSL.description;
ssl.value.id = websiteSSL.id;
ssl.value.provider = websiteSSL.provider;
}
ssl.value.websiteId = Number(id.value);
getAcmeAccounts();
getDnsAccounts();
@ -227,7 +246,7 @@ const getPath = (dir: string) => {
const getAcmeAccounts = async () => {
const res = await SearchAcmeAccount(acmeReq);
acmeAccounts.value = res.data.items || [];
if (acmeAccounts.value.length > 0) {
if (acmeAccounts.value.length > 0 && ssl.value.acmeAccountId == undefined) {
ssl.value.acmeAccountId = res.data.items[0].id;
}
};
@ -235,7 +254,7 @@ const getAcmeAccounts = async () => {
const getDnsAccounts = async () => {
const res = await SearchDnsAccount(dnsReq);
dnsAccounts.value = res.data.items || [];
if (dnsAccounts.value.length > 0) {
if (dnsAccounts.value.length > 0 && ssl.value.dnsAccountId == undefined) {
ssl.value.dnsAccountId = res.data.items[0].id;
}
};
@ -273,17 +292,42 @@ const submit = async (formEl: FormInstance | undefined) => {
return;
}
loading.value = true;
CreateSSL(ssl.value)
.then((res: any) => {
if (ssl.value.provider != 'dnsManual') {
em('submit', res.data.id);
}
handleClose();
MsgSuccess(i18n.global.t('commons.msg.createSuccess'));
})
.finally(() => {
loading.value = false;
});
if (operate.value == 'create') {
CreateSSL(ssl.value)
.then((res: any) => {
if (ssl.value.provider != 'dnsManual') {
em('submit', res.data.id);
}
handleClose();
MsgSuccess(i18n.global.t('commons.msg.createSuccess'));
})
.finally(() => {
loading.value = false;
});
}
if (operate.value == 'edit') {
const sslUpdate = {
id: ssl.value.id,
primaryDomain: ssl.value.primaryDomain,
otherDomains: ssl.value.otherDomains,
acmeAccountId: ssl.value.acmeAccountId,
dnsAccountId: ssl.value.dnsAccountId,
autoRenew: ssl.value.autoRenew,
keyType: ssl.value.keyType,
pushDir: ssl.value.pushDir,
dir: ssl.value.dir,
description: ssl.value.description,
provider: ssl.value.provider,
};
UpdateSSL(sslUpdate)
.then(() => {
handleClose();
MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));
})
.finally(() => {
loading.value = false;
});
}
});
};

View file

@ -138,11 +138,11 @@
/>
</ComplexTable>
</template>
<DnsAccount ref="dnsAccountRef"></DnsAccount>
<AcmeAccount ref="acmeAccountRef"></AcmeAccount>
<Create ref="sslCreateRef" @close="search()" @submit="openLog"></Create>
<Detail ref="detailRef"></Detail>
<SSLUpload ref="sslUploadRef" @close="search()"></SSLUpload>
<DnsAccount ref="dnsAccountRef" />
<AcmeAccount ref="acmeAccountRef" />
<Create ref="sslCreateRef" @close="search()" @submit="openLog" />
<Detail ref="detailRef" />
<SSLUpload ref="sslUploadRef" @close="search()" />
<Apply ref="applyRef" @search="search" @submit="openLog" />
<OpDialog ref="opRef" @search="search" @cancel="search" />
<Log ref="logRef" @close="search()" />
@ -189,8 +189,8 @@ const sslUploadRef = ref();
const applyRef = ref();
const logRef = ref();
const caRef = ref();
let selects = ref<any>([]);
const obtainRef = ref();
let selects = ref<any>([]);
const routerButton = [
{
@ -234,6 +234,15 @@ const buttons = [
return row.provider == 'manual';
},
},
{
label: i18n.global.t('commons.button.edit'),
click: function (row: Website.SSLDTO) {
onEdit(row);
},
disabled: function (row: Website.SSLDTO) {
return row.provider == 'selfSigned';
},
},
{
label: i18n.global.t('file.download'),
click: function (row: Website.SSLDTO) {
@ -292,7 +301,7 @@ const updateDesc = (row: Website.SSLDTO, bulr: Function) => {
const updateConfig = (row: Website.SSLDTO) => {
loading.value = true;
UpdateSSL({ id: row.id, autoRenew: row.autoRenew, description: row.description })
UpdateSSL(row)
.then(() => {
MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));
})
@ -308,8 +317,12 @@ const openDnsAccount = () => {
dnsAccountRef.value.acceptParams();
};
const openSSL = () => {
sslCreateRef.value.acceptParams();
sslCreateRef.value.acceptParams('create');
};
const onEdit = (row: Website.SSL) => {
sslCreateRef.value.acceptParams('edit', row);
};
const openUpload = () => {
sslUploadRef.value.acceptParams();
};