feat: 手动创建证书增加更新功能 (#3087)

Refs https://github.com/1Panel-dev/1Panel/issues/2329
This commit is contained in:
zhengkunwang 2023-11-28 15:42:10 +08:00 committed by GitHub
parent 73b2804d68
commit 59c216c184
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 62 additions and 33 deletions

View file

@ -63,8 +63,12 @@ type WebsiteBatchDelReq struct {
} }
type WebsiteSSLUpdate struct { type WebsiteSSLUpdate struct {
ID uint `json:"id" validate:"required"` ID uint `json:"id" validate:"required"`
AutoRenew bool `json:"autoRenew"` AutoRenew bool `json:"autoRenew"`
Description string `json:"description"`
PrivateKey string `json:"privateKey"`
Certificate string `json:"certificate"`
Type string `json:"type" validate:"required,oneof=autoRenew description certificate privateKey"`
} }
type WebsiteSSLUpload struct { type WebsiteSSLUpload struct {
@ -73,6 +77,7 @@ type WebsiteSSLUpload struct {
PrivateKeyPath string `json:"privateKeyPath"` PrivateKeyPath string `json:"privateKeyPath"`
CertificatePath string `json:"certificatePath"` CertificatePath string `json:"certificatePath"`
Type string `json:"type" validate:"required,oneof=paste local"` Type string `json:"type" validate:"required,oneof=paste local"`
SSLID uint `json:"sslID"`
} }
type WebsiteCASearch struct { type WebsiteCASearch struct {

View file

@ -28,6 +28,7 @@ type WebsiteSSL struct {
KeyType string `gorm:"not null;default:2048" json:"keyType"` KeyType string `gorm:"not null;default:2048" json:"keyType"`
PushDir bool `gorm:"not null;default:0" json:"pushDir"` PushDir bool `gorm:"not null;default:0" json:"pushDir"`
Dir string `json:"dir"` Dir string `json:"dir"`
Description string `json:"description"`
AcmeAccount WebsiteAcmeAccount `json:"acmeAccount" gorm:"-:migration"` AcmeAccount WebsiteAcmeAccount `json:"acmeAccount" gorm:"-:migration"`
DnsAccount WebsiteDnsAccount `json:"dnsAccount" gorm:"-:migration"` DnsAccount WebsiteDnsAccount `json:"dnsAccount" gorm:"-:migration"`

View file

@ -345,10 +345,16 @@ func (w WebsiteSSLService) Update(update request.WebsiteSSLUpdate) error {
} }
func (w WebsiteSSLService) Upload(req request.WebsiteSSLUpload) error { func (w WebsiteSSLService) Upload(req request.WebsiteSSLUpload) error {
newSSL := &model.WebsiteSSL{ websiteSSL := &model.WebsiteSSL{
Provider: constant.Manual, Provider: constant.Manual,
} }
var err error
if req.SSLID > 0 {
websiteSSL, err = websiteSSLRepo.GetFirst(commonRepo.WithByID(req.SSLID))
if err != nil {
return err
}
}
if req.Type == "local" { if req.Type == "local" {
fileOp := files.NewFileOp() fileOp := files.NewFileOp()
if !fileOp.Stat(req.PrivateKeyPath) { if !fileOp.Stat(req.PrivateKeyPath) {
@ -360,24 +366,24 @@ func (w WebsiteSSLService) Upload(req request.WebsiteSSLUpload) error {
if content, err := fileOp.GetContent(req.PrivateKeyPath); err != nil { if content, err := fileOp.GetContent(req.PrivateKeyPath); err != nil {
return err return err
} else { } else {
newSSL.PrivateKey = string(content) websiteSSL.PrivateKey = string(content)
} }
if content, err := fileOp.GetContent(req.CertificatePath); err != nil { if content, err := fileOp.GetContent(req.CertificatePath); err != nil {
return err return err
} else { } else {
newSSL.Pem = string(content) websiteSSL.Pem = string(content)
} }
} else { } else {
newSSL.PrivateKey = req.PrivateKey websiteSSL.PrivateKey = req.PrivateKey
newSSL.Pem = req.Certificate websiteSSL.Pem = req.Certificate
} }
privateKeyCertBlock, _ := pem.Decode([]byte(newSSL.PrivateKey)) privateKeyCertBlock, _ := pem.Decode([]byte(websiteSSL.PrivateKey))
if privateKeyCertBlock == nil { if privateKeyCertBlock == nil {
return buserr.New("ErrSSLKeyFormat") return buserr.New("ErrSSLKeyFormat")
} }
certBlock, _ := pem.Decode([]byte(newSSL.Pem)) certBlock, _ := pem.Decode([]byte(websiteSSL.Pem))
if certBlock == nil { if certBlock == nil {
return buserr.New("ErrSSLCertificateFormat") return buserr.New("ErrSSLCertificateFormat")
} }
@ -385,23 +391,23 @@ func (w WebsiteSSLService) Upload(req request.WebsiteSSLUpload) error {
if err != nil { if err != nil {
return err return err
} }
newSSL.ExpireDate = cert.NotAfter websiteSSL.ExpireDate = cert.NotAfter
newSSL.StartDate = cert.NotBefore websiteSSL.StartDate = cert.NotBefore
newSSL.Type = cert.Issuer.CommonName websiteSSL.Type = cert.Issuer.CommonName
if len(cert.Issuer.Organization) > 0 { if len(cert.Issuer.Organization) > 0 {
newSSL.Organization = cert.Issuer.Organization[0] websiteSSL.Organization = cert.Issuer.Organization[0]
} else { } else {
newSSL.Organization = cert.Issuer.CommonName websiteSSL.Organization = cert.Issuer.CommonName
} }
var domains []string var domains []string
if len(cert.DNSNames) > 0 { if len(cert.DNSNames) > 0 {
newSSL.PrimaryDomain = cert.DNSNames[0] websiteSSL.PrimaryDomain = cert.DNSNames[0]
domains = cert.DNSNames[1:] domains = cert.DNSNames[1:]
} }
if len(cert.IPAddresses) > 0 { if len(cert.IPAddresses) > 0 {
if newSSL.PrimaryDomain == "" { if websiteSSL.PrimaryDomain == "" {
newSSL.PrimaryDomain = cert.IPAddresses[0].String() websiteSSL.PrimaryDomain = cert.IPAddresses[0].String()
for _, ip := range cert.IPAddresses[1:] { for _, ip := range cert.IPAddresses[1:] {
domains = append(domains, ip.String()) domains = append(domains, ip.String())
} }
@ -411,9 +417,12 @@ func (w WebsiteSSLService) Upload(req request.WebsiteSSLUpload) error {
} }
} }
} }
newSSL.Domains = strings.Join(domains, ",") websiteSSL.Domains = strings.Join(domains, ",")
return websiteSSLRepo.Create(context.Background(), newSSL) if websiteSSL.ID > 0 {
return websiteSSLRepo.Save(websiteSSL)
}
return websiteSSLRepo.Create(context.Background(), websiteSSL)
} }
func (w WebsiteSSLService) SyncForRestart() error { func (w WebsiteSSLService) SyncForRestart() error {

View file

@ -27,7 +27,7 @@ var AddWebsiteCA = &gormigrate.Migration{
} }
var UpdateWebsiteSSL = &gormigrate.Migration{ var UpdateWebsiteSSL = &gormigrate.Migration{
ID: "20231127-update-website-ssl", ID: "20231128-update-website-ssl",
Migrate: func(tx *gorm.DB) error { Migrate: func(tx *gorm.DB) error {
if err := tx.AutoMigrate(&model.WebsiteSSL{}); err != nil { if err := tx.AutoMigrate(&model.WebsiteSSL{}); err != nil {
return err return err

View file

@ -451,6 +451,7 @@ export namespace Website {
privateKeyPath: string; privateKeyPath: string;
certificatePath: string; certificatePath: string;
type: string; type: string;
sslID: number;
} }
export interface SSLObtain { export interface SSLObtain {

View file

@ -53,7 +53,7 @@
</el-descriptions> </el-descriptions>
</div> </div>
<div v-else-if="curr === 'ssl'"> <div v-else-if="curr === 'ssl'">
<el-input v-model="ssl.pem" :autosize="{ minRows: 10, maxRows: 15 }" type="textarea" id="textArea" /> <el-input v-model="ssl.pem" :autosize="{ minRows: 15, maxRows: 30 }" type="textarea" id="textArea" />
<div> <div>
<br /> <br />
<el-button type="primary" @click="copyText(ssl.pem)">{{ $t('file.copy') }}</el-button> <el-button type="primary" @click="copyText(ssl.pem)">{{ $t('file.copy') }}</el-button>
@ -62,7 +62,7 @@
<div v-else> <div v-else>
<el-input <el-input
v-model="ssl.privateKey" v-model="ssl.privateKey"
:autosize="{ minRows: 10, maxRows: 15 }" :autosize="{ minRows: 15, maxRows: 30 }"
type="textarea" type="textarea"
id="textArea" id="textArea"
/> />

View file

@ -83,11 +83,13 @@
placement="bottom" placement="bottom"
:width="400" :width="400"
trigger="hover" trigger="hover"
:content="row.message"
> >
<template #reference> <template #reference>
<Status :key="row.status" :status="row.status"></Status> <Status :key="row.status" :status="row.status"></Status>
</template> </template>
<div class="max-h-96 overflow-auto">
<span>{{ row.message }}</span>
</div>
</el-popover> </el-popover>
<div v-else> <div v-else>
<Status :key="row.status" :status="row.status"></Status> <Status :key="row.status" :status="row.status"></Status>
@ -214,6 +216,18 @@ const buttons = [
applySSL(row); applySSL(row);
} }
}, },
show: function (row: Website.SSLDTO) {
return row.provider != 'manual';
},
},
{
label: i18n.global.t('commons.operate.update'),
click: function (row: Website.SSLDTO) {
sslUploadRef.value.acceptParams(row.id);
},
show: function (row: Website.SSLDTO) {
return row.provider == 'manual';
},
}, },
{ {
label: i18n.global.t('commons.button.delete'), label: i18n.global.t('commons.button.delete'),

View file

@ -70,13 +70,15 @@ const rules = ref({
certificatePath: [Rules.requiredInput], certificatePath: [Rules.requiredInput],
type: [Rules.requiredSelect], type: [Rules.requiredSelect],
}); });
const ssl = ref({ const initData = () => ({
privateKey: '', privateKey: '',
certificate: '', certificate: '',
privateKeyPath: '', privateKeyPath: '',
certificatePath: '', certificatePath: '',
type: 'paste', type: 'paste',
sslID: 0,
}); });
const ssl = ref(initData());
const em = defineEmits(['close']); const em = defineEmits(['close']);
const handleClose = () => { const handleClose = () => {
@ -86,17 +88,14 @@ const handleClose = () => {
}; };
const resetForm = () => { const resetForm = () => {
sslForm.value?.resetFields(); sslForm.value?.resetFields();
ssl.value = { ssl.value = initData();
privateKey: '',
certificate: '',
privateKeyPath: '',
certificatePath: '',
type: 'paste',
};
}; };
const acceptParams = () => { const acceptParams = (id: number) => {
resetForm(); resetForm();
if (id && id > 0) {
ssl.value.sslID = id;
}
open.value = true; open.value = true;
}; };