diff --git a/backend/app/repo/website.go b/backend/app/repo/website.go index d3784cf8e..c525337a8 100644 --- a/backend/app/repo/website.go +++ b/backend/app/repo/website.go @@ -95,7 +95,7 @@ func (w *WebsiteRepo) Page(page, size int, opts ...DBOption) (int64, []model.Web func (w *WebsiteRepo) List(opts ...DBOption) ([]model.Website, error) { var websites []model.Website - err := getDb(opts...).Model(&model.Website{}).Preload("WebsiteSSL").Find(&websites).Error + err := getDb(opts...).Model(&model.Website{}).Preload("Domains").Preload("WebsiteSSL").Find(&websites).Error return websites, err } diff --git a/backend/app/service/website_ssl.go b/backend/app/service/website_ssl.go index 9b66ccd39..4cddcec2d 100644 --- a/backend/app/service/website_ssl.go +++ b/backend/app/service/website_ssl.go @@ -260,6 +260,25 @@ func (w WebsiteSSLService) ObtainSSL(apply request.WebsiteSSLApply) error { if err != nil { return } + + websites, _ := websiteRepo.GetBy(websiteRepo.WithWebsiteSSLID(websiteSSL.ID)) + if len(websites) > 0 { + for _, website := range websites { + legoLogger.Logger.Println(i18n.GetMsgWithMap("ApplyWebSiteSSLLog", map[string]interface{}{"name": website.PrimaryDomain})) + if err := createPemFile(website, *websiteSSL); err != nil { + legoLogger.Logger.Println(i18n.GetMsgWithMap("ErrUpdateWebsiteSSL", map[string]interface{}{"name": website.PrimaryDomain, "err": err.Error()})) + } + } + nginxInstall, err := getAppInstallByKey(constant.AppOpenresty) + if err != nil { + return + } + if err := opNginx(nginxInstall.ContainerName, constant.NginxReload); err != nil { + legoLogger.Logger.Println(i18n.GetMsgByKey(constant.ErrSSLApply)) + return + } + legoLogger.Logger.Println(i18n.GetMsgByKey("ApplyWebSiteSSLSuccess")) + } }() return nil diff --git a/backend/i18n/lang/en.yaml b/backend/i18n/lang/en.yaml index c706d6951..55540329d 100644 --- a/backend/i18n/lang/en.yaml +++ b/backend/i18n/lang/en.yaml @@ -105,6 +105,10 @@ DNSAccountName: 'DNS account [{{ .name }}] manufacturer [{{.type}}]' PushDirLog: 'Certificate pushed to directory [{{ .path }}] {{ .status }}' ErrDeleteCAWithSSL: "There is an issued certificate under the current organization and cannot be deleted" ErrDeleteWithPanelSSL: "Panel SSL configuration uses this certificate and cannot be deleted" +ErrDefaultCA: "The default organization cannot be deleted" +ApplyWebSiteSSLLog: "Start updating {{ .name }} website certificate" +ErrUpdateWebsiteSSL: "{{ .name }} website failed to update certificate: {{ .err }}" +ApplyWebSiteSSLSuccess: "Update website certificate successfully" #mysql ErrUserIsExist: "The current user already exists. Please enter a new user" diff --git a/backend/i18n/lang/zh-Hant.yaml b/backend/i18n/lang/zh-Hant.yaml index 7fede492f..ea8eb6f2d 100644 --- a/backend/i18n/lang/zh-Hant.yaml +++ b/backend/i18n/lang/zh-Hant.yaml @@ -105,6 +105,10 @@ DNSAccountName: 'DNS 帳號 [{{ .name }}] 廠商 [{{.type}}]' PushDirLog: '憑證推送到目錄 [{{ .path }}] {{ .status }}' ErrDeleteCAWithSSL: "目前機構下存在已簽發證書,無法刪除" ErrDeleteWithPanelSSL: "面板 SSL 配置使用此證書,無法刪除" +ErrDefaultCA: "默認機構不能刪除" +ApplyWebSiteSSLLog: "開始更新 {{ .name }} 網站憑證" +ErrUpdateWebsiteSSL: "{{ .name }} 網站更新憑證失敗: {{ .err }}" +ApplyWebSiteSSLSuccess: "更新網站憑證成功" #mysql diff --git a/backend/i18n/lang/zh.yaml b/backend/i18n/lang/zh.yaml index be94628cd..a93cbf2c8 100644 --- a/backend/i18n/lang/zh.yaml +++ b/backend/i18n/lang/zh.yaml @@ -105,6 +105,10 @@ DNSAccountName: 'DNS 账号 [{{ .name }}] 厂商 [{{.type}}]' PushDirLog: '证书推送到目录 [{{ .path }}] {{ .status }}' ErrDeleteCAWithSSL: "当前机构下存在已签发证书,无法删除" ErrDeleteWithPanelSSL: "面板 SSL 配置使用此证书,无法删除" +ErrDefaultCA: "默认机构不能删除" +ApplyWebSiteSSLLog: "开始更新 {{ .name }} 网站证书" +ErrUpdateWebsiteSSL: "{{ .name }} 网站更新证书失败: {{ .err }}" +ApplyWebSiteSSLSuccess: "更新网站证书成功" #mysql ErrUserIsExist: "当前用户已存在,请重新输入" diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts index 5cb514b75..5083e0bbf 100644 --- a/frontend/src/lang/modules/en.ts +++ b/frontend/src/lang/modules/en.ts @@ -1852,6 +1852,7 @@ const message = { pushDirHelper: 'Two files will be generated in this directory, the certificate file: fullchain.pem and the key file: privkey.pem', organizationDetail: 'Organization Details', + fromWebsite: 'From Websites', }, firewall: { create: 'Create rule', diff --git a/frontend/src/lang/modules/tw.ts b/frontend/src/lang/modules/tw.ts index e4208e3b5..1cc5a6065 100644 --- a/frontend/src/lang/modules/tw.ts +++ b/frontend/src/lang/modules/tw.ts @@ -1738,6 +1738,7 @@ const message = { dir: '目錄', pushDirHelper: '會在此目錄下產生兩個文件,憑證檔案:fullchain.pem 金鑰檔案:privkey.pem', organizationDetail: '機構詳情', + fromWebsite: '從網站獲取', }, firewall: { create: '創建規則', diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index 8991d45cb..eb5a42930 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -1738,6 +1738,7 @@ const message = { dir: '目录', pushDirHelper: '会在此目录下生成两个文件,证书文件:fullchain.pem 密钥文件:privkey.pem', organizationDetail: '机构详情', + fromWebsite: '从网站中获取', }, firewall: { create: '创建规则', diff --git a/frontend/src/views/website/ssl/ca/obtain/index.vue b/frontend/src/views/website/ssl/ca/obtain/index.vue index 2f256d347..c17659c94 100644 --- a/frontend/src/views/website/ssl/ca/obtain/index.vue +++ b/frontend/src/views/website/ssl/ca/obtain/index.vue @@ -14,7 +14,7 @@ type="textarea" :autosize="{ minRows: 4, maxRows: 10 }" v-model="obtain.domains" - :placeholder="$t('website.domainHelper')" + :placeholder="$t('ssl.domainHelper')" > diff --git a/frontend/src/views/website/ssl/create/index.vue b/frontend/src/views/website/ssl/create/index.vue index 42692f1cb..54c1c135a 100644 --- a/frontend/src/views/website/ssl/create/index.vue +++ b/frontend/src/views/website/ssl/create/index.vue @@ -6,9 +6,25 @@ - - - + + + + + + + + + + + + + + import DrawerHeader from '@/components/drawer-header/index.vue'; import { Website } from '@/api/interface/website'; -import { CreateSSL, SearchAcmeAccount, SearchDnsAccount } from '@/api/modules/website'; +import { CreateSSL, ListWebsites, SearchAcmeAccount, SearchDnsAccount } from '@/api/modules/website'; import { Rules } from '@/global/form-rules'; import i18n from '@/lang'; import { FormInstance } from 'element-plus'; @@ -136,6 +152,7 @@ const acmeReq = reactive({ const dnsAccounts = ref(); const acmeAccounts = ref(); const sslForm = ref(); +const websites = ref(); const rules = ref({ primaryDomain: [Rules.requiredInput, Rules.domain], acmeAccountId: [Rules.requiredSelectBusiness], @@ -145,6 +162,7 @@ const rules = ref({ keyType: [Rules.requiredInput], dir: [Rules.requiredInput], }); +const websiteID = ref(); const initData = () => ({ primaryDomain: '', @@ -173,6 +191,7 @@ const resetForm = () => { sslForm.value?.resetFields(); dnsResolve.value = []; ssl.value = initData(); + websiteID.value = undefined; }; const acceptParams = () => { @@ -180,6 +199,7 @@ const acceptParams = () => { ssl.value.websiteId = Number(id.value); getAcmeAccounts(); getDnsAccounts(); + listwebsites(); open.value = true; }; @@ -207,6 +227,28 @@ const changeProvider = () => { dnsResolve.value = []; }; +const listwebsites = async () => { + const res = await ListWebsites(); + websites.value = res.data; +}; + +const changeWebsite = () => { + if (websiteID.value > 0) { + const selectedWebsite = websites.value.find((website) => website.id == websiteID.value); + + if (selectedWebsite && selectedWebsite.domains && selectedWebsite.domains.length > 0) { + const primaryDomain = selectedWebsite.domains[0].domain; + const otherDomains = selectedWebsite.domains + .slice(1) + .map((domain) => domain.domain) + .join('\n'); + + ssl.value.primaryDomain = primaryDomain; + ssl.value.otherDomains = otherDomains; + } + } +}; + const submit = async (formEl: FormInstance | undefined) => { if (!formEl) return; await formEl.validate((valid) => {