mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-10-09 15:06:37 +08:00
feat: 手动创建证书增加更新功能 (#3087)
Refs https://github.com/1Panel-dev/1Panel/issues/2329
This commit is contained in:
parent
73b2804d68
commit
59c216c184
8 changed files with 62 additions and 33 deletions
|
@ -65,6 +65,10 @@ 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 {
|
||||||
|
|
|
@ -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"`
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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"
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -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'),
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue