From 7a6e5376c4a9f7624ab11c1bc858bde8e852cba6 Mon Sep 17 00:00:00 2001 From: zhengkunwang <31820853+zhengkunwang223@users.noreply.github.com> Date: Tue, 4 Mar 2025 22:37:20 +0800 Subject: [PATCH] feat(system): Fix issue with certificate failure in settings panel (#8063) --- agent/app/api/v2/website_ca.go | 5 +- core/app/model/agent.go | 47 +++++++++++++++ core/app/repo/agent.go | 37 ++++++++++++ core/app/service/entry.go | 2 + core/app/service/setting.go | 59 ++++++++++++++++++- core/global/global.go | 1 + core/init/db/db.go | 1 + frontend/src/api/index.ts | 14 ++++- frontend/src/api/modules/website.ts | 4 ++ frontend/src/views/setting/safe/ssl/index.vue | 14 ++--- 10 files changed, 173 insertions(+), 11 deletions(-) create mode 100644 core/app/model/agent.go create mode 100644 core/app/repo/agent.go diff --git a/agent/app/api/v2/website_ca.go b/agent/app/api/v2/website_ca.go index f0efb6f0c..d6d00a0a5 100644 --- a/agent/app/api/v2/website_ca.go +++ b/agent/app/api/v2/website_ca.go @@ -113,11 +113,12 @@ func (b *BaseApi) ObtainWebsiteCA(c *gin.Context) { if err := helper.CheckBindAndValidate(&req, c); err != nil { return } - if _, err := websiteCAService.ObtainSSL(req); err != nil { + res, err := websiteCAService.ObtainSSL(req) + if err != nil { helper.InternalServer(c, err) return } - helper.SuccessWithOutData(c) + helper.SuccessWithData(c, res) } // @Tags Website CA diff --git a/core/app/model/agent.go b/core/app/model/agent.go new file mode 100644 index 000000000..b86c4ac1d --- /dev/null +++ b/core/app/model/agent.go @@ -0,0 +1,47 @@ +package model + +import ( + "time" +) + +type WebsiteSSL struct { + BaseModel + 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 `json:"status"` + Message string `json:"message"` + KeyType string `json:"keyType"` + PushDir bool `json:"pushDir"` + Dir string `json:"dir"` + Description string `json:"description"` + SkipDNS bool `json:"skipDNS"` + Nameserver1 string `json:"nameserver1"` + Nameserver2 string `json:"nameserver2"` + DisableCNAME bool `json:"disableCNAME"` + ExecShell bool `json:"execShell"` + Shell string `json:"shell"` +} + +func (w WebsiteSSL) TableName() string { + return "website_ssls" +} + +type WebsiteCA struct { + BaseModel + CSR string `gorm:"not null;" json:"csr"` + Name string `gorm:"not null;" json:"name"` + PrivateKey string `gorm:"not null" json:"privateKey"` + KeyType string `gorm:"not null;default:2048" json:"keyType"` +} diff --git a/core/app/repo/agent.go b/core/app/repo/agent.go new file mode 100644 index 000000000..6a2b8849d --- /dev/null +++ b/core/app/repo/agent.go @@ -0,0 +1,37 @@ +package repo + +import ( + "github.com/1Panel-dev/1Panel/core/app/model" + "github.com/1Panel-dev/1Panel/core/global" +) + +type AgentRepo struct{} + +type IAgentRepo interface { + GetWebsiteSSL(opts ...global.DBOption) (model.WebsiteSSL, error) + GetCA(opts ...global.DBOption) (model.WebsiteCA, error) +} + +func NewIAgentRepo() IAgentRepo { + return &AgentRepo{} +} + +func (a *AgentRepo) GetWebsiteSSL(opts ...global.DBOption) (model.WebsiteSSL, error) { + var ssl model.WebsiteSSL + db := global.AgentDB + for _, opt := range opts { + db = opt(db) + } + err := db.First(&ssl).Error + return ssl, err +} + +func (a *AgentRepo) GetCA(opts ...global.DBOption) (model.WebsiteCA, error) { + var ca model.WebsiteCA + db := global.AgentDB + for _, opt := range opts { + db = opt(db) + } + err := db.First(&ca).Error + return ca, err +} diff --git a/core/app/service/entry.go b/core/app/service/entry.go index b3479e4c6..d2dcc227b 100644 --- a/core/app/service/entry.go +++ b/core/app/service/entry.go @@ -13,4 +13,6 @@ var ( upgradeLogRepo = repo.NewIUpgradeLogRepo() taskRepo = repo.NewITaskRepo() + + agentRepo = repo.NewIAgentRepo() ) diff --git a/core/app/service/setting.go b/core/app/service/setting.go index 6e4080e43..f0e9ab0bc 100644 --- a/core/app/service/setting.go +++ b/core/app/service/setting.go @@ -1,6 +1,7 @@ package service import ( + "bytes" "crypto/rand" "crypto/rsa" "crypto/tls" @@ -8,7 +9,10 @@ import ( "encoding/json" "encoding/pem" "fmt" + "github.com/1Panel-dev/1Panel/core/app/model" + "github.com/1Panel-dev/1Panel/core/utils/req_helper" "net" + "net/http" "os" "path" "strconv" @@ -265,6 +269,48 @@ func (u *SettingService) UpdateSSL(c *gin.Context, req dto.SSLUpdate) error { return err } secret = string(certFile) + case "select": + ssl, err := agentRepo.GetWebsiteSSL(repo.WithByID(req.SSLID)) + if err != nil { + return err + } + secret = ssl.Pem + key = ssl.PrivateKey + if err := settingRepo.Update("SSLID", strconv.Itoa(int(req.SSLID))); err != nil { + return err + } + case "self": + ca, err := agentRepo.GetCA(repo.WithByName("1Panel")) + if err != nil { + return err + } + params := make(map[string]interface{}) + params["domains"] = req.Domain + params["time"] = 10 + params["unit"] = "year" + params["keyType"] = "P256" + params["id"] = ca.ID + jsonData, err := json.Marshal(params) + if err != nil { + return err + } + res, err := req_helper.NewLocalClient("/api/v2/websites/ca/obtain", http.MethodPost, bytes.NewReader(jsonData)) + if err != nil { + return err + } + jsonBytes, err := json.Marshal(res) + if err != nil { + return err + } + var ssl model.WebsiteSSL + if err := json.Unmarshal(jsonBytes, &ssl); err != nil { + return err + } + secret = ssl.Pem + key = ssl.PrivateKey + if err := settingRepo.Update("SSLID", strconv.Itoa(int(ssl.ID))); err != nil { + return err + } } if err := os.WriteFile(path.Join(secretDir, "server.crt.tmp"), []byte(secret), 0600); err != nil { @@ -325,7 +371,18 @@ func (u *SettingService) LoadFromCert() (*dto.SSLInfo, error) { keyFile, _ := os.ReadFile(path.Join(global.CONF.Base.InstallDir, "1panel/secret/server.key")) data.Key = string(keyFile) case "select": - // TODO select ssl from website + sslID, err := settingRepo.Get(repo.WithByKey("SSLID")) + if err != nil { + return nil, err + } + id, _ := strconv.Atoi(sslID.Value) + ssl, err := agentRepo.GetWebsiteSSL(repo.WithByID(uint(id))) + if err != nil { + return nil, err + } + data.Domain = ssl.PrimaryDomain + data.SSLID = uint(id) + data.Timeout = ssl.ExpireDate.Format(constant.DateTimeLayout) } return &data, nil } diff --git a/core/global/global.go b/core/global/global.go index b01c4ec22..0c972ffcd 100644 --- a/core/global/global.go +++ b/core/global/global.go @@ -13,6 +13,7 @@ import ( var ( DB *gorm.DB TaskDB *gorm.DB + AgentDB *gorm.DB LOG *logrus.Logger CONF ServerConfig Api ApiInterface diff --git a/core/init/db/db.go b/core/init/db/db.go index 5614781f4..1096c0466 100644 --- a/core/init/db/db.go +++ b/core/init/db/db.go @@ -10,4 +10,5 @@ import ( func Init() { global.DB = common.LoadDBConnByPath(path.Join(global.CONF.Base.InstallDir, "1panel/db/core.db"), "core") global.TaskDB = common.LoadDBConnByPath(path.Join(global.CONF.Base.InstallDir, "1panel/db/task.db"), "task") + global.AgentDB = common.LoadDBConnByPath(path.Join(global.CONF.Base.InstallDir, "1panel/db/agent.db"), "agent") } diff --git a/frontend/src/api/index.ts b/frontend/src/api/index.ts index b0d86ca8b..4d128913b 100644 --- a/frontend/src/api/index.ts +++ b/frontend/src/api/index.ts @@ -27,7 +27,9 @@ class RequestHttp { 'Accept-Language': language, ...config.headers, }; - config.headers.CurrentNode = globalStore.currentNode; + if (config.headers.CurrentNode == undefined) { + config.headers.CurrentNode = globalStore.currentNode; + } if (config.url === '/core/auth/login' || config.url === '/core/auth/mfalogin') { let entrance = Base64.encode(globalStore.entrance); config.headers.EntranceCode = entrance; @@ -125,6 +127,16 @@ class RequestHttp { withCredentials: true, }); } + postLocalNode(url: string, params?: object, timeout?: number): Promise> { + return this.service.post(url, params, { + baseURL: import.meta.env.VITE_API_URL as string, + timeout: timeout ? timeout : (ResultEnum.TIMEOUT as number), + withCredentials: true, + headers: { + CurrentNode: 'local', + }, + }); + } put(url: string, params?: object, _object = {}): Promise> { return this.service.put(url, params, _object); } diff --git a/frontend/src/api/modules/website.ts b/frontend/src/api/modules/website.ts index 0515d83ad..b9e90127c 100644 --- a/frontend/src/api/modules/website.ts +++ b/frontend/src/api/modules/website.ts @@ -110,6 +110,10 @@ export const listSSL = (req: Website.SSLReq) => { return http.post(`/websites/ssl/search`, req); }; +export const listLocalNodeSSL = (req: Website.SSLReq) => { + return http.postLocalNode(`/websites/ssl/search`, req); +}; + export const createSSL = (req: Website.SSLCreate) => { return http.post(`/websites/ssl`, req, TimeoutEnum.T_10M); }; diff --git a/frontend/src/views/setting/safe/ssl/index.vue b/frontend/src/views/setting/safe/ssl/index.vue index bfc785489..8d5ce2a1f 100644 --- a/frontend/src/views/setting/safe/ssl/index.vue +++ b/frontend/src/views/setting/safe/ssl/index.vue @@ -27,10 +27,10 @@ {{ $t('setting.domainOrIP') }} {{ form.domain }} - {{ $t('setting.timeOut') }} {{ form.timeout }} + {{ $t('setting.timeOut') }} {{ form.timeout }} import { Website } from '@/api/interface/website'; import { dateFormatSimple, getProvider } from '@/utils/util'; -import { listSSL } from '@/api/modules/website'; +import { listLocalNodeSSL } from '@/api/modules/website'; import { reactive, ref } from 'vue'; import i18n from '@/lang'; import { MsgSuccess } from '@/utils/message'; @@ -131,7 +131,7 @@ const loading = ref(); const drawerVisible = ref(); const form = reactive({ - ssl: 'enable', + ssl: 'Enable', domain: '', sslType: 'self', itemSSLType: 'paste', @@ -172,7 +172,7 @@ const acceptParams = async (params: DialogProps): Promise => { if (params.sslInfo?.sslID) { form.sslID = params.sslInfo.sslID; - const ssls = await listSSL({}); + const ssls = await listLocalNodeSSL({}); sslList.value = ssls.data || []; changeSSl(params.sslInfo?.sslID); } else { @@ -183,7 +183,7 @@ const acceptParams = async (params: DialogProps): Promise => { const emit = defineEmits<{ (e: 'search'): void }>(); const loadSSLs = async () => { - const res = await listSSL({}); + const res = await listLocalNodeSSL({}); sslList.value = res.data || []; }; @@ -228,7 +228,7 @@ const onSaveSSL = async (formEl: FormInstance | undefined) => { itemType = form.itemSSLType === 'paste' ? 'import-paste' : 'import-local'; } let param = { - ssl: 'enable', + ssl: 'Enable', sslType: itemType, domain: '', sslID: form.sslID,