Browse Source

refactor: auto-cert handling logic #1095, #1104

Jacky 3 weeks ago
parent
commit
55d54766f8
46 changed files with 1594 additions and 975 deletions
  1. 5 1
      .cursor/mcp.json
  2. 5 14
      api/certificate/issue.go
  3. 2 2
      api/cluster/nginx.go
  4. 2 2
      api/nginx_log/websocket.go
  5. 84 42
      app/src/language/ar/app.po
  6. 84 42
      app/src/language/de_DE/app.po
  7. 79 36
      app/src/language/en/app.po
  8. 84 42
      app/src/language/es/app.po
  9. 84 42
      app/src/language/fr_FR/app.po
  10. 82 39
      app/src/language/ja_JP/app.po
  11. 82 42
      app/src/language/ko_KR/app.po
  12. 77 36
      app/src/language/messages.pot
  13. 84 42
      app/src/language/pt_PT/app.po
  14. 84 42
      app/src/language/ru_RU/app.po
  15. 84 43
      app/src/language/tr_TR/app.po
  16. 84 39
      app/src/language/uk_UA/app.po
  17. 84 42
      app/src/language/vi_VN/app.po
  18. 82 45
      app/src/language/zh_CN/app.po
  19. 82 42
      app/src/language/zh_TW/app.po
  20. 8 13
      app/src/views/certificate/CertificateEditor.vue
  21. 3 3
      app/src/views/certificate/CertificateList/Certificate.vue
  22. 201 0
      app/src/views/certificate/components/DNSIssueCertificate.vue
  23. 4 2
      app/src/views/certificate/components/RenewCert.vue
  24. 0 107
      app/src/views/certificate/components/WildcardCertificate.vue
  25. 21 0
      app/src/views/certificate/store.ts
  26. 1 0
      app/src/views/site/site_edit/components/Cert/ObtainCertLive.vue
  27. 3 11
      internal/cert/auto_cert.go
  28. 16 0
      internal/cert/errors.go
  29. 28 31
      internal/cert/issue.go
  30. 6 9
      internal/cert/logger.go
  31. 10 5
      internal/cert/obtain.go
  32. 8 9
      internal/cert/payload.go
  33. 10 7
      internal/cert/renew.go
  34. 9 6
      internal/cert/revoke.go
  35. 0 125
      internal/cert/tencent_cloud_dns_test.go
  36. 1 1
      internal/site/delete.go
  37. 1 1
      internal/site/disable.go
  38. 1 1
      internal/site/enable.go
  39. 2 2
      internal/site/maintenance.go
  40. 1 1
      internal/site/rename.go
  41. 1 1
      internal/site/save.go
  42. 1 1
      internal/stream/delete.go
  43. 1 1
      internal/stream/disable.go
  44. 1 1
      internal/stream/enable.go
  45. 1 1
      internal/stream/rename.go
  46. 1 1
      internal/stream/save.go

+ 5 - 1
.cursor/mcp.json

@@ -4,6 +4,10 @@
 			"command": "npx",
 			"args": ["@eslint/mcp@latest"],
 			"env": {}
-		}
+		},
+		"context7": {
+      "command": "npx",
+      "args": ["-y", "@upstash/context7-mcp"]
+    }
 	}
 }

+ 5 - 14
api/certificate/issue.go

@@ -26,7 +26,7 @@ type IssueCertResponse struct {
 	Message           string             `json:"message"`
 	SSLCertificate    string             `json:"ssl_certificate,omitempty"`
 	SSLCertificateKey string             `json:"ssl_certificate_key,omitempty"`
-	KeyType           certcrypto.KeyType `json:"key_type"`
+	KeyType           certcrypto.KeyType `json:"key_type,omitempty"`
 }
 
 func IssueCert(c *gin.Context) {
@@ -71,28 +71,19 @@ func IssueCert(c *gin.Context) {
 		}
 	}
 
-	errChan := make(chan error, 1)
-
 	log := cert.NewLogger()
 	log.SetCertModel(&certModel)
 	log.SetWebSocket(ws)
 	defer log.Close()
 
-	go cert.IssueCert(payload, log, errChan)
-
-	// block, until errChan closes
-	if err := <-errChan; err != nil {
+	err = cert.IssueCert(payload, log)
+	if err != nil {
 		log.Error(err)
-		err = ws.WriteJSON(IssueCertResponse{
+		_ = ws.WriteJSON(IssueCertResponse{
 			Status:  Error,
 			Message: err.Error(),
 		})
-		if err != nil {
-			if helper.IsUnexpectedWebsocketError(err) {
-				logger.Error(err)
-			}
-			return
-		}
+		return
 	}
 
 	cert := query.Cert

+ 2 - 2
api/cluster/nginx.go

@@ -39,7 +39,7 @@ func syncReload(nodeIDs []uint64) {
 				if err := recover(); err != nil {
 					buf := make([]byte, 1024)
 					runtime.Stack(buf, false)
-					logger.Error(err)
+					logger.Errorf("%s\n%s", err, buf)
 				}
 			}()
 			defer wg.Done()
@@ -93,7 +93,7 @@ func syncRestart(nodeIDs []uint64) {
 				if err := recover(); err != nil {
 					buf := make([]byte, 1024)
 					runtime.Stack(buf, false)
-					logger.Error(err)
+					logger.Errorf("%s\n%s", err, buf)
 				}
 			}()
 			defer wg.Done()

+ 2 - 2
api/nginx_log/websocket.go

@@ -67,7 +67,7 @@ func tailNginxLog(ws *websocket.Conn, controlChan chan controlStruct, errChan ch
 		if err := recover(); err != nil {
 			buf := make([]byte, 1024)
 			runtime.Stack(buf, false)
-			logger.Error(err)
+			logger.Errorf("%s\n%s", err, buf)
 			return
 		}
 	}()
@@ -139,7 +139,7 @@ func handleLogControl(ws *websocket.Conn, controlChan chan controlStruct, errCha
 		if err := recover(); err != nil {
 			buf := make([]byte, 1024)
 			runtime.Stack(buf, false)
-			logger.Error(err)
+			logger.Errorf("%s\n%s", err, buf)
 			return
 		}
 	}()

+ 84 - 42
app/src/language/ar/app.po

@@ -180,6 +180,10 @@ msgstr "إضافة تكوين"
 msgid "Add Directive Below"
 msgstr "أضف التوجيه أدناه"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:151
+msgid "Add Domain"
+msgstr "إضافة نطاق"
+
 #: src/components/NgxConfigEditor/LocationEditor.vue:131
 #: src/components/NgxConfigEditor/LocationEditor.vue:158
 msgid "Add Location"
@@ -227,6 +231,14 @@ msgstr "الكل"
 msgid "All Recovery Codes Have Been Used"
 msgstr "تم استخدام جميع رموز الاسترداد"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:156
+msgid ""
+"All selected subdomains must belong to the same DNS Provider, otherwise the "
+"certificate application will fail."
+msgstr ""
+"يجب أن تنتمي جميع النطاقات الفرعية المحددة إلى نفس موفر DNS، وإلا فشل طلب "
+"الشهادة."
+
 #: src/views/preference/tabs/OpenAISettings.vue:32
 msgid "API Base Url"
 msgstr "عنوان URL الأساسي لAPI"
@@ -253,7 +265,7 @@ msgstr "التطبيق"
 
 #: src/views/system/Upgrade.vue:195
 msgid "Arch"
-msgstr "\"أرك\""
+msgstr "أرك"
 
 #: src/views/preference/tabs/AuthSettings.vue:130
 msgid "Are you sure to delete this banned IP immediately?"
@@ -388,7 +400,7 @@ msgstr "تم تعطيل التجديد التلقائي لـ‎%{name}"
 msgid "Auto-renewal enabled for %{name}"
 msgstr "تم تمكين التجديد التلقائي لـ‏%{name}"
 
-#: src/views/certificate/components/RenewCert.vue:49
+#: src/views/certificate/components/RenewCert.vue:51
 #: src/views/site/site_edit/components/Cert/IssueCert.vue:84
 msgid "AutoCert is running, please wait..."
 msgstr "AutoCert قيد التشغيل، يرجى الانتظار..."
@@ -401,7 +413,7 @@ msgstr "\"AutoCert قيد التشغيل...\""
 msgid "Automatic Restart"
 msgstr "\"إعادة التشغيل التلقائي\""
 
-#: src/views/certificate/CertificateEditor.vue:259
+#: src/views/certificate/CertificateEditor.vue:257
 #: src/views/config/components/ConfigLeftPanel.vue:273
 #: src/views/config/ConfigList.vue:112 src/views/config/ConfigList.vue:195
 #: src/views/nginx_log/NginxLog.vue:174
@@ -696,7 +708,7 @@ msgstr "تم تجديد الشهادة بنجاح"
 msgid "Certificate revoked successfully"
 msgstr "تم إلغاء الشهادة بنجاح"
 
-#: src/views/certificate/CertificateEditor.vue:128
+#: src/views/certificate/CertificateEditor.vue:126
 #: src/views/site/site_edit/components/Cert/Cert.vue:58
 msgid "Certificate Status"
 msgid_plural "Certificates Status"
@@ -707,6 +719,10 @@ msgstr[3] "حالة الشهادات"
 msgstr[4] "حالة الشهادات"
 msgstr[5] "حالة الشهادة"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:104
+msgid "Certificate Type"
+msgstr "نوع الشهادة"
+
 #: src/routes/modules/certificates.ts:11
 #: src/views/certificate/CertificateList/Certificate.vue:19
 msgid "Certificates"
@@ -1193,6 +1209,14 @@ msgstr "تعبير كرون مخصص"
 msgid "Custom Directory"
 msgstr "دليل مخصص"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:126
+msgid "Custom Domains"
+msgstr "نطاقات مخصصة"
+
+#: src/views/certificate/components/DNSIssueCertificate.vue:110
+msgid "Custom Domains Certificate"
+msgstr "شهادة النطاقات المخصصة"
+
 #: src/views/preference/tabs/NodeSettings.vue:19
 msgid ""
 "Customize the name of local node to be displayed in the environment "
@@ -1500,11 +1524,11 @@ msgid "Document"
 msgid_plural "Documents"
 msgstr[0] "وثيقة"
 
-#: src/views/certificate/components/WildcardCertificate.vue:66
+#: src/views/certificate/components/DNSIssueCertificate.vue:116
 msgid "Domain"
 msgstr "نطاق"
 
-#: src/views/certificate/CertificateEditor.vue:112
+#: src/views/certificate/CertificateEditor.vue:110
 msgid "Domains list is empty, try to reopen Auto Cert for %{config}"
 msgstr "قائمة النطاقات فارغة، حاول إعادة فتح Auto Cert لـ %{config}"
 
@@ -1706,6 +1730,14 @@ msgstr "تشفير الموقع باستخدام Let's Encrypt"
 msgid "End"
 msgstr "النهاية"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:135
+msgid "Enter domain name"
+msgstr "أدخل اسم النطاق"
+
+#: src/views/certificate/components/DNSIssueCertificate.vue:120
+msgid "Enter your domain"
+msgstr "أدخل نطاقك"
+
 #: src/language/constants.ts:22
 msgid "Environment variables cleaned"
 msgstr "تم تنظيف متغيرات البيئة"
@@ -1780,7 +1812,7 @@ msgstr "حاوية Docker خارجية"
 msgid "External Notify"
 msgstr "إشعار خارجي"
 
-#: src/views/site/site_edit/components/Cert/ObtainCertLive.vue:80
+#: src/views/site/site_edit/components/Cert/ObtainCertLive.vue:81
 msgid "Fail to obtain certificate"
 msgstr "فشل في الحصول على الشهادة"
 
@@ -2305,7 +2337,7 @@ msgid "Import"
 msgstr "استيراد"
 
 #: src/routes/modules/certificates.ts:46
-#: src/views/certificate/CertificateEditor.vue:85
+#: src/views/certificate/CertificateEditor.vue:83
 msgid "Import Certificate"
 msgstr "استيراد شهادة"
 
@@ -2450,12 +2482,12 @@ msgid "IP"
 msgstr "IP"
 
 #: src/views/certificate/CertificateList/Certificate.vue:37
-msgid "Issue wildcard certificate"
-msgstr "إصدار شهادة wildcard"
+msgid "Issue certificate"
+msgstr "إصدار شهادة"
 
-#: src/views/certificate/components/WildcardCertificate.vue:57
-msgid "Issue Wildcard Certificate"
-msgstr "إصدار شهادة Wildcard"
+#: src/views/certificate/components/DNSIssueCertificate.vue:95
+msgid "Issue Certificate"
+msgstr "إصدار الشهادة"
 
 #: src/language/constants.ts:20
 msgid "Issued certificate successfully"
@@ -2538,8 +2570,8 @@ msgstr "اتركه فارغًا إذا كنت لا تريد التعديل"
 msgid "Leave blank if you don't need this."
 msgstr "اتركه فارغًا إذا لم تكن بحاجة إلى ذلك."
 
-#: src/views/certificate/CertificateEditor.vue:223
-#: src/views/certificate/CertificateEditor.vue:237
+#: src/views/certificate/CertificateEditor.vue:221
+#: src/views/certificate/CertificateEditor.vue:235
 msgid "Leave blank will not change anything"
 msgstr "تركه فارغًا لن يغير شيئًا"
 
@@ -2626,7 +2658,7 @@ msgstr "مكان"
 msgid "Locations"
 msgstr "أماكن"
 
-#: src/views/certificate/CertificateEditor.vue:247
+#: src/views/certificate/CertificateEditor.vue:245
 msgid "Log"
 msgstr "سجل"
 
@@ -2839,7 +2871,7 @@ msgid "Modify"
 msgstr "تعديل"
 
 #: src/routes/modules/certificates.ts:36
-#: src/views/certificate/CertificateEditor.vue:85
+#: src/views/certificate/CertificateEditor.vue:83
 msgid "Modify Certificate"
 msgstr "تعديل الشهادة"
 
@@ -2874,7 +2906,7 @@ msgstr "توجيه متعدد الأسطر"
 #: src/components/NgxConfigEditor/NgxUpstream.vue:199
 #: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/certificate/ACMEUser.vue:11
-#: src/views/certificate/CertificateEditor.vue:162
+#: src/views/certificate/CertificateEditor.vue:160
 #: src/views/certificate/CertificateList/certColumns.tsx:9
 #: src/views/certificate/DNSCredential.vue:9
 #: src/views/config/components/ConfigRightPanel/Basic.vue:34
@@ -2934,7 +2966,7 @@ msgstr "مسار جديد"
 msgid "New version released"
 msgstr "تم إصدار نسخة جديدة"
 
-#: src/views/certificate/components/WildcardCertificate.vue:89
+#: src/views/certificate/components/DNSIssueCertificate.vue:181
 #: src/views/install/components/InstallView.vue:96
 #: src/views/site/site_add/SiteAdd.vue:132
 #: src/views/site/site_edit/components/Cert/ObtainCert.vue:214
@@ -3516,6 +3548,10 @@ msgstr "يرجى إدخال اسم لمفتاح المرور الذي ترغب 
 msgid "Please enter a valid port range"
 msgstr "الرجاء إدخال نطاق منافذ صالح"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:74
+msgid "Please enter at least one domain"
+msgstr "الرجاء إدخال مجال واحد على الأقل"
+
 #: src/components/TwoFA/Authorization.vue:85
 msgid "Please enter the OTP code:"
 msgstr "يرجى إدخال رمز OTP:"
@@ -3845,6 +3881,7 @@ msgstr "إعادة التحميل"
 msgid "Reloading nginx"
 msgstr "إعادة تحميل nginx"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:144
 #: src/views/preference/tabs/AuthSettings.vue:137
 msgid "Remove"
 msgstr "إزالة"
@@ -3927,8 +3964,8 @@ msgstr "إعادة التسمية بنجاح"
 msgid "Renamed successfully"
 msgstr "تمت إعادة التسمية بنجاح"
 
-#: src/views/certificate/components/RenewCert.vue:46
-#: src/views/certificate/components/RenewCert.vue:53
+#: src/views/certificate/components/RenewCert.vue:48
+#: src/views/certificate/components/RenewCert.vue:55
 msgid "Renew Certificate"
 msgstr "تجديد الشهادة"
 
@@ -3940,8 +3977,8 @@ msgstr "خطأ في تجديد الشهادة"
 msgid "Renew Certificate Success"
 msgstr "تجديد الشهادة بنجاح"
 
-#: src/views/certificate/components/RenewCert.vue:28
-#: src/views/certificate/components/WildcardCertificate.vue:46
+#: src/views/certificate/components/DNSIssueCertificate.vue:84
+#: src/views/certificate/components/RenewCert.vue:30
 msgid "Renew successfully"
 msgstr "تم التجديد بنجاح"
 
@@ -4199,7 +4236,7 @@ msgstr "السبت"
 
 #: src/components/ChatGPT/ChatMessage.vue:215
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:129
-#: src/language/curd.ts:18 src/views/certificate/CertificateEditor.vue:266
+#: src/language/curd.ts:18 src/views/certificate/CertificateEditor.vue:264
 #: src/views/config/components/ConfigLeftPanel.vue:282
 #: src/views/config/components/ConfigName.vue:59
 #: src/views/preference/components/AuthSettings/Passkey.vue:130
@@ -4254,7 +4291,7 @@ msgstr "فشل حفظ الدفق %{name} إلى %{node}"
 msgid "Save stream %{name} to %{node} successfully"
 msgstr "تم حفظ الدفق %{name} في %{node} بنجاح"
 
-#: src/language/curd.ts:35 src/views/certificate/CertificateEditor.vue:49
+#: src/language/curd.ts:35 src/views/certificate/store.ts:13
 #: src/views/preference/store/index.ts:118
 msgid "Save successfully"
 msgstr "تم الحفظ بنجاح"
@@ -4362,6 +4399,10 @@ msgstr "إرسال"
 msgid "Server"
 msgstr "الخادم"
 
+#: src/views/certificate/CertificateEditor.vue:55
+msgid "Server error"
+msgstr "خطأ في الخادم"
+
 #: src/views/dashboard/ServerAnalytic.vue:175
 msgid "Server Info"
 msgstr "معلومات الخادم"
@@ -4498,7 +4539,7 @@ msgstr "وقت الانتظار بين تكرارات محمل ذاكرة الت
 msgid "Sleep time between cache manager iterations"
 msgstr "وقت الانتظار بين تكرارات مدير الذاكرة المؤقتة"
 
-#: src/views/certificate/CertificateEditor.vue:213
+#: src/views/certificate/CertificateEditor.vue:211
 msgid "SSL Certificate Content"
 msgstr "محتوى شهادة SSL"
 
@@ -4510,15 +4551,15 @@ msgstr "يجب أن يكون ملف شهادة SSL ضمن دليل تكوين Ng
 msgid "SSL certificate file not found"
 msgstr "لم يتم العثور على ملف شهادة SSL"
 
-#: src/views/certificate/CertificateEditor.vue:227
+#: src/views/certificate/CertificateEditor.vue:225
 msgid "SSL Certificate Key Content"
 msgstr "محتوى مفتاح شهادة SSL"
 
-#: src/views/certificate/CertificateEditor.vue:192
+#: src/views/certificate/CertificateEditor.vue:190
 msgid "SSL Certificate Key Path"
 msgstr "مسار مفتاح شهادة SSL"
 
-#: src/views/certificate/CertificateEditor.vue:177
+#: src/views/certificate/CertificateEditor.vue:175
 #: src/views/preference/tabs/ServerSettings.vue:37
 msgid "SSL Certificate Path"
 msgstr "مسار شهادة SSL"
@@ -4753,7 +4794,7 @@ msgstr "مزامنة العقد"
 msgid "Sync strategy"
 msgstr "استراتيجية المزامنة"
 
-#: src/views/certificate/CertificateEditor.vue:206
+#: src/views/certificate/CertificateEditor.vue:204
 msgid "Sync to"
 msgstr "مزامنة إلى"
 
@@ -4828,11 +4869,11 @@ msgstr ""
 "يجب أن يحتوي رقم ICP فقط على أحرف، وunicode، وأرقام، وشرطات، وشرطات طويلة، "
 "ونقطتين، ونقاط."
 
-#: src/views/certificate/CertificateEditor.vue:216
+#: src/views/certificate/CertificateEditor.vue:214
 msgid "The input is not a SSL Certificate"
 msgstr "الإدخال ليس شهادة SSL"
 
-#: src/views/certificate/CertificateEditor.vue:230
+#: src/views/certificate/CertificateEditor.vue:228
 msgid "The input is not a SSL Certificate Key"
 msgstr "المدخل ليس مفتاح شهادة SSL"
 
@@ -4871,11 +4912,11 @@ msgstr ""
 msgid "The parameter of server_name is required"
 msgstr "معلمة server_name مطلوبة"
 
-#: src/views/certificate/CertificateEditor.vue:181
+#: src/views/certificate/CertificateEditor.vue:179
 msgid "The path exists, but the file is not a certificate"
 msgstr "المسار موجود، لكن الملف ليس شهادة"
 
-#: src/views/certificate/CertificateEditor.vue:196
+#: src/views/certificate/CertificateEditor.vue:194
 msgid "The path exists, but the file is not a private key"
 msgstr "المسار موجود، لكن الملف ليس مفتاحًا خاصًا"
 
@@ -4936,17 +4977,17 @@ msgstr ""
 "والعوامل الثانية. إذا لم تتمكن من العثور على هذه الرموز، فستفقد الوصول إلى "
 "حسابك."
 
-#: src/views/certificate/CertificateEditor.vue:102
+#: src/views/certificate/CertificateEditor.vue:100
 msgid "This Auto Cert item is invalid, please remove it."
 msgstr "هذا العنصر في الشهادة التلقائية غير صالح، يرجى إزالته."
 
-#: src/views/certificate/CertificateEditor.vue:92
+#: src/views/certificate/CertificateEditor.vue:90
 msgid "This certificate is managed by Nginx UI"
 msgstr "يتم إدارة هذه الشهادة بواسطة Nginx UI"
 
-#: src/views/certificate/CertificateEditor.vue:165
-#: src/views/certificate/CertificateEditor.vue:179
-#: src/views/certificate/CertificateEditor.vue:194
+#: src/views/certificate/CertificateEditor.vue:163
+#: src/views/certificate/CertificateEditor.vue:177
+#: src/views/certificate/CertificateEditor.vue:192
 msgid "This field is required"
 msgstr "هذا الحقل مطلوب"
 
@@ -5416,6 +5457,10 @@ msgstr "عند إنشاء رموز استرداد جديدة، يجب عليك 
 msgid "Whether to use a temporary path when writing temporary files"
 msgstr "ما إذا كان سيتم استخدام مسار مؤقت عند كتابة الملفات المؤقتة"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:107
+msgid "Wildcard Certificate"
+msgstr "شهادة البدل"
+
 #: src/views/dashboard/components/ParamsOpt/PerformanceConfig.vue:48
 msgid "Worker Connections"
 msgstr "اتصالات العامل"
@@ -5757,9 +5802,6 @@ msgstr "مفاتيح المرور الخاصة بك"
 #~ msgid "File"
 #~ msgstr "ملف"
 
-#~ msgid "Server error"
-#~ msgstr "خطأ في الخادم"
-
 #~ msgid "Incorrect username or password"
 #~ msgstr "اسم المستخدم أو كلمة المرور غير صحيحة"
 

+ 84 - 42
app/src/language/de_DE/app.po

@@ -180,6 +180,10 @@ msgstr "Konfiguration hinzufügen"
 msgid "Add Directive Below"
 msgstr "Anweisung darunter hinzufügen"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:151
+msgid "Add Domain"
+msgstr "Domain hinzufügen"
+
 #: src/components/NgxConfigEditor/LocationEditor.vue:131
 #: src/components/NgxConfigEditor/LocationEditor.vue:158
 msgid "Add Location"
@@ -229,6 +233,14 @@ msgstr "Alle"
 msgid "All Recovery Codes Have Been Used"
 msgstr "Alle Wiederherstellungscodes wurden verwendet"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:156
+msgid ""
+"All selected subdomains must belong to the same DNS Provider, otherwise the "
+"certificate application will fail."
+msgstr ""
+"Alle ausgewählten Subdomains müssen zum selben DNS-Anbieter gehören, "
+"ansonsten schlägt die Zertifikatsbeantragung fehl."
+
 #: src/views/preference/tabs/OpenAISettings.vue:32
 msgid "API Base Url"
 msgstr "API-Basis-URL"
@@ -255,7 +267,7 @@ msgstr "App"
 
 #: src/views/system/Upgrade.vue:195
 msgid "Arch"
-msgstr "\"Arch\""
+msgstr "Arch"
 
 #: src/views/preference/tabs/AuthSettings.vue:130
 msgid "Are you sure to delete this banned IP immediately?"
@@ -394,7 +406,7 @@ msgstr "Automatische Verlängerung deaktiviert für %{name}"
 msgid "Auto-renewal enabled for %{name}"
 msgstr "Automatische Verlängerung aktiviert für %{name}"
 
-#: src/views/certificate/components/RenewCert.vue:49
+#: src/views/certificate/components/RenewCert.vue:51
 #: src/views/site/site_edit/components/Cert/IssueCert.vue:84
 msgid "AutoCert is running, please wait..."
 msgstr "AutoCert wird ausgeführt, bitte warten..."
@@ -407,7 +419,7 @@ msgstr "\"AutoCert wird ausgeführt...\""
 msgid "Automatic Restart"
 msgstr "\"Automatischer Neustart\""
 
-#: src/views/certificate/CertificateEditor.vue:259
+#: src/views/certificate/CertificateEditor.vue:257
 #: src/views/config/components/ConfigLeftPanel.vue:273
 #: src/views/config/ConfigList.vue:112 src/views/config/ConfigList.vue:195
 #: src/views/nginx_log/NginxLog.vue:174
@@ -712,12 +724,16 @@ msgstr "Zertifikat erfolgreich erneuert"
 msgid "Certificate revoked successfully"
 msgstr "Zertifikat erfolgreich widerrufen"
 
-#: src/views/certificate/CertificateEditor.vue:128
+#: src/views/certificate/CertificateEditor.vue:126
 #: src/views/site/site_edit/components/Cert/Cert.vue:58
 msgid "Certificate Status"
 msgid_plural "Certificates Status"
 msgstr[0] "Zertifikatsstatus"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:104
+msgid "Certificate Type"
+msgstr "Zertifikatstyp"
+
 #: src/routes/modules/certificates.ts:11
 #: src/views/certificate/CertificateList/Certificate.vue:19
 msgid "Certificates"
@@ -1210,6 +1226,14 @@ msgstr "Benutzerdefinierter Cron-Ausdruck"
 msgid "Custom Directory"
 msgstr "Benutzerdefiniertes Verzeichnis"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:126
+msgid "Custom Domains"
+msgstr "Benutzerdefinierte Domains"
+
+#: src/views/certificate/components/DNSIssueCertificate.vue:110
+msgid "Custom Domains Certificate"
+msgstr "Benutzerdefinierte Domänenzertifikate"
+
 #: src/views/preference/tabs/NodeSettings.vue:19
 msgid ""
 "Customize the name of local node to be displayed in the environment "
@@ -1521,11 +1545,11 @@ msgid "Document"
 msgid_plural "Documents"
 msgstr[0] "Dokument"
 
-#: src/views/certificate/components/WildcardCertificate.vue:66
+#: src/views/certificate/components/DNSIssueCertificate.vue:116
 msgid "Domain"
 msgstr "Domain"
 
-#: src/views/certificate/CertificateEditor.vue:112
+#: src/views/certificate/CertificateEditor.vue:110
 msgid "Domains list is empty, try to reopen Auto Cert for %{config}"
 msgstr ""
 "Domänenliste ist leer, versuche Auto-Zertifikat für %{config} erneut zu "
@@ -1730,6 +1754,14 @@ msgstr "Webseite mit Let's Encrypt verschlüsseln"
 msgid "End"
 msgstr "Ende"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:135
+msgid "Enter domain name"
+msgstr "Domänennamen eingeben"
+
+#: src/views/certificate/components/DNSIssueCertificate.vue:120
+msgid "Enter your domain"
+msgstr "Geben Sie Ihre Domain ein"
+
 #: src/language/constants.ts:22
 msgid "Environment variables cleaned"
 msgstr "Umgebungsvariablen gesäubert"
@@ -1804,7 +1836,7 @@ msgstr "Externer Docker-Container"
 msgid "External Notify"
 msgstr "Externe Benachrichtigung"
 
-#: src/views/site/site_edit/components/Cert/ObtainCertLive.vue:80
+#: src/views/site/site_edit/components/Cert/ObtainCertLive.vue:81
 msgid "Fail to obtain certificate"
 msgstr "Zertifikat konnte nicht abgerufen werden"
 
@@ -2336,7 +2368,7 @@ msgid "Import"
 msgstr "Import"
 
 #: src/routes/modules/certificates.ts:46
-#: src/views/certificate/CertificateEditor.vue:85
+#: src/views/certificate/CertificateEditor.vue:83
 msgid "Import Certificate"
 msgstr "Zertifikat importieren"
 
@@ -2481,12 +2513,12 @@ msgid "IP"
 msgstr "IP"
 
 #: src/views/certificate/CertificateList/Certificate.vue:37
-msgid "Issue wildcard certificate"
-msgstr "Wildcard-Zertifikat ausstellen"
+msgid "Issue certificate"
+msgstr "Zertifikat ausstellen"
 
-#: src/views/certificate/components/WildcardCertificate.vue:57
-msgid "Issue Wildcard Certificate"
-msgstr "Wildcard-Zertifikat ausstellen"
+#: src/views/certificate/components/DNSIssueCertificate.vue:95
+msgid "Issue Certificate"
+msgstr "Zertifikat ausstellen"
 
 #: src/language/constants.ts:20
 msgid "Issued certificate successfully"
@@ -2569,8 +2601,8 @@ msgstr "Leer lassen, wenn keine Änderung gewünscht ist"
 msgid "Leave blank if you don't need this."
 msgstr "Lassen Sie dieses Feld leer, wenn Sie es nicht benötigen."
 
-#: src/views/certificate/CertificateEditor.vue:223
-#: src/views/certificate/CertificateEditor.vue:237
+#: src/views/certificate/CertificateEditor.vue:221
+#: src/views/certificate/CertificateEditor.vue:235
 msgid "Leave blank will not change anything"
 msgstr "Leer lassen ändert nichts"
 
@@ -2657,7 +2689,7 @@ msgstr "Ort"
 msgid "Locations"
 msgstr "Orte"
 
-#: src/views/certificate/CertificateEditor.vue:247
+#: src/views/certificate/CertificateEditor.vue:245
 msgid "Log"
 msgstr "Protokoll"
 
@@ -2871,7 +2903,7 @@ msgid "Modify"
 msgstr "Ändern"
 
 #: src/routes/modules/certificates.ts:36
-#: src/views/certificate/CertificateEditor.vue:85
+#: src/views/certificate/CertificateEditor.vue:83
 msgid "Modify Certificate"
 msgstr "Zertifikat ändern"
 
@@ -2906,7 +2938,7 @@ msgstr "Mehrzeilige Direktive"
 #: src/components/NgxConfigEditor/NgxUpstream.vue:199
 #: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/certificate/ACMEUser.vue:11
-#: src/views/certificate/CertificateEditor.vue:162
+#: src/views/certificate/CertificateEditor.vue:160
 #: src/views/certificate/CertificateList/certColumns.tsx:9
 #: src/views/certificate/DNSCredential.vue:9
 #: src/views/config/components/ConfigRightPanel/Basic.vue:34
@@ -2966,7 +2998,7 @@ msgstr "Neuer Pfad"
 msgid "New version released"
 msgstr "Neue Version veröffentlicht"
 
-#: src/views/certificate/components/WildcardCertificate.vue:89
+#: src/views/certificate/components/DNSIssueCertificate.vue:181
 #: src/views/install/components/InstallView.vue:96
 #: src/views/site/site_add/SiteAdd.vue:132
 #: src/views/site/site_edit/components/Cert/ObtainCert.vue:214
@@ -3555,6 +3587,10 @@ msgstr ""
 msgid "Please enter a valid port range"
 msgstr "Bitte geben Sie einen gültigen Portbereich ein"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:74
+msgid "Please enter at least one domain"
+msgstr "Bitte geben Sie mindestens eine Domain ein"
+
 #: src/components/TwoFA/Authorization.vue:85
 msgid "Please enter the OTP code:"
 msgstr "Bitte gib den OTP-Code ein:"
@@ -3902,6 +3938,7 @@ msgstr "Lade neu"
 msgid "Reloading nginx"
 msgstr "Lade Nginx neu"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:144
 #: src/views/preference/tabs/AuthSettings.vue:137
 msgid "Remove"
 msgstr "Löschen"
@@ -3984,8 +4021,8 @@ msgstr "Erfolgreich umbenannt"
 msgid "Renamed successfully"
 msgstr "Erfolgreich umbenannt"
 
-#: src/views/certificate/components/RenewCert.vue:46
-#: src/views/certificate/components/RenewCert.vue:53
+#: src/views/certificate/components/RenewCert.vue:48
+#: src/views/certificate/components/RenewCert.vue:55
 msgid "Renew Certificate"
 msgstr "Zertifikat erneuern"
 
@@ -3997,8 +4034,8 @@ msgstr "Fehler bei der Zertifikatsverlängerung"
 msgid "Renew Certificate Success"
 msgstr "Zertifikat erfolgreich verlängert"
 
-#: src/views/certificate/components/RenewCert.vue:28
-#: src/views/certificate/components/WildcardCertificate.vue:46
+#: src/views/certificate/components/DNSIssueCertificate.vue:84
+#: src/views/certificate/components/RenewCert.vue:30
 msgid "Renew successfully"
 msgstr "Erfolgreich erneuert"
 
@@ -4259,7 +4296,7 @@ msgstr "Samstag"
 
 #: src/components/ChatGPT/ChatMessage.vue:215
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:129
-#: src/language/curd.ts:18 src/views/certificate/CertificateEditor.vue:266
+#: src/language/curd.ts:18 src/views/certificate/CertificateEditor.vue:264
 #: src/views/config/components/ConfigLeftPanel.vue:282
 #: src/views/config/components/ConfigName.vue:59
 #: src/views/preference/components/AuthSettings/Passkey.vue:130
@@ -4314,7 +4351,7 @@ msgstr "Speichern des Streams %{name} auf %{node} fehlgeschlagen"
 msgid "Save stream %{name} to %{node} successfully"
 msgstr "Stream %{name} erfolgreich auf %{node} gespeichert"
 
-#: src/language/curd.ts:35 src/views/certificate/CertificateEditor.vue:49
+#: src/language/curd.ts:35 src/views/certificate/store.ts:13
 #: src/views/preference/store/index.ts:118
 msgid "Save successfully"
 msgstr "Erfolgreich gespeichert"
@@ -4424,6 +4461,10 @@ msgstr "Senden"
 msgid "Server"
 msgstr "Server"
 
+#: src/views/certificate/CertificateEditor.vue:55
+msgid "Server error"
+msgstr "Serverfehler"
+
 #: src/views/dashboard/ServerAnalytic.vue:175
 msgid "Server Info"
 msgstr "Serverinformationen"
@@ -4560,7 +4601,7 @@ msgstr "Wartezeit zwischen den Iterationen des Cache-Loaders"
 msgid "Sleep time between cache manager iterations"
 msgstr "Wartezeit zwischen den Iterationen des Cache-Managers"
 
-#: src/views/certificate/CertificateEditor.vue:213
+#: src/views/certificate/CertificateEditor.vue:211
 msgid "SSL Certificate Content"
 msgstr "SSL-Zertifikatsinhalt"
 
@@ -4574,15 +4615,15 @@ msgstr ""
 msgid "SSL certificate file not found"
 msgstr "SSL-Zertifikatsdatei nicht gefunden"
 
-#: src/views/certificate/CertificateEditor.vue:227
+#: src/views/certificate/CertificateEditor.vue:225
 msgid "SSL Certificate Key Content"
 msgstr "SSL-Zertifikatsschlüsselinhalt"
 
-#: src/views/certificate/CertificateEditor.vue:192
+#: src/views/certificate/CertificateEditor.vue:190
 msgid "SSL Certificate Key Path"
 msgstr "SSL-Zertifikatsschlüssel-Pfad"
 
-#: src/views/certificate/CertificateEditor.vue:177
+#: src/views/certificate/CertificateEditor.vue:175
 #: src/views/preference/tabs/ServerSettings.vue:37
 msgid "SSL Certificate Path"
 msgstr "SSL-Zertifikatspfad"
@@ -4824,7 +4865,7 @@ msgstr "Synchrone Knoten"
 msgid "Sync strategy"
 msgstr "Synchronisierungsstrategie"
 
-#: src/views/certificate/CertificateEditor.vue:206
+#: src/views/certificate/CertificateEditor.vue:204
 msgid "Sync to"
 msgstr "Synchronisieren mit"
 
@@ -4900,11 +4941,11 @@ msgstr ""
 "Die ICP-Nummer sollte nur Buchstaben, Unicode, Zahlen, Bindestriche, "
 "Doppelpunkte und Punkte enthalten."
 
-#: src/views/certificate/CertificateEditor.vue:216
+#: src/views/certificate/CertificateEditor.vue:214
 msgid "The input is not a SSL Certificate"
 msgstr "Die Eingabe ist kein SSL-Zertifikat"
 
-#: src/views/certificate/CertificateEditor.vue:230
+#: src/views/certificate/CertificateEditor.vue:228
 msgid "The input is not a SSL Certificate Key"
 msgstr "Die Eingabe ist kein SSL-Zertifikatsschlüssel"
 
@@ -4943,11 +4984,11 @@ msgstr ""
 msgid "The parameter of server_name is required"
 msgstr "Der Parameter server_name ist erforderlich"
 
-#: src/views/certificate/CertificateEditor.vue:181
+#: src/views/certificate/CertificateEditor.vue:179
 msgid "The path exists, but the file is not a certificate"
 msgstr "Der Pfad existiert, aber die Datei ist kein Zertifikat"
 
-#: src/views/certificate/CertificateEditor.vue:196
+#: src/views/certificate/CertificateEditor.vue:194
 msgid "The path exists, but the file is not a private key"
 msgstr "Der Pfad existiert, aber die Datei ist kein privater Schlüssel"
 
@@ -5009,17 +5050,17 @@ msgstr ""
 "Ihr Passwort und die zweiten Faktoren verlieren. Wenn Sie diese Codes nicht "
 "finden können, verlieren Sie den Zugriff auf Ihr Konto."
 
-#: src/views/certificate/CertificateEditor.vue:102
+#: src/views/certificate/CertificateEditor.vue:100
 msgid "This Auto Cert item is invalid, please remove it."
 msgstr "Dieses Auto-Zertifikatselement ist ungültig, bitte entferne es."
 
-#: src/views/certificate/CertificateEditor.vue:92
+#: src/views/certificate/CertificateEditor.vue:90
 msgid "This certificate is managed by Nginx UI"
 msgstr "Dieses Zertifikat wird von Nginx UI verwaltet"
 
-#: src/views/certificate/CertificateEditor.vue:165
-#: src/views/certificate/CertificateEditor.vue:179
-#: src/views/certificate/CertificateEditor.vue:194
+#: src/views/certificate/CertificateEditor.vue:163
+#: src/views/certificate/CertificateEditor.vue:177
+#: src/views/certificate/CertificateEditor.vue:192
 msgid "This field is required"
 msgstr "Dieses Feld ist erforderlich"
 
@@ -5505,6 +5546,10 @@ msgstr ""
 "Ob ein temporärer Pfad beim Schreiben temporärer Dateien verwendet werden "
 "soll"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:107
+msgid "Wildcard Certificate"
+msgstr "Wildcard-Zertifikat"
+
 #: src/views/dashboard/components/ParamsOpt/PerformanceConfig.vue:48
 msgid "Worker Connections"
 msgstr "Worker-Verbindungen"
@@ -5860,9 +5905,6 @@ msgstr "Deine Passkeys"
 #~ msgid "Recovery Code:"
 #~ msgstr "Wiederherstellungscode:"
 
-#~ msgid "Server error"
-#~ msgstr "Serverfehler"
-
 #~ msgid "The recovery code is only displayed once, please save it in a safe place."
 #~ msgstr ""
 #~ "Der Wiederherstellungscode wird nur einmal angezeigt, bitte speichere ihn "

+ 79 - 36
app/src/language/en/app.po

@@ -164,6 +164,10 @@ msgstr ""
 msgid "Add Directive Below"
 msgstr ""
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:151
+msgid "Add Domain"
+msgstr ""
+
 #: src/components/NgxConfigEditor/LocationEditor.vue:131
 #: src/components/NgxConfigEditor/LocationEditor.vue:158
 msgid "Add Location"
@@ -211,6 +215,12 @@ msgstr ""
 msgid "All Recovery Codes Have Been Used"
 msgstr ""
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:156
+msgid ""
+"All selected subdomains must belong to the same DNS Provider, otherwise the "
+"certificate application will fail."
+msgstr ""
+
 #: src/views/preference/tabs/OpenAISettings.vue:32
 msgid "API Base Url"
 msgstr ""
@@ -372,7 +382,7 @@ msgstr ""
 msgid "Auto-renewal enabled for %{name}"
 msgstr ""
 
-#: src/views/certificate/components/RenewCert.vue:49
+#: src/views/certificate/components/RenewCert.vue:51
 #: src/views/site/site_edit/components/Cert/IssueCert.vue:84
 msgid "AutoCert is running, please wait..."
 msgstr ""
@@ -385,7 +395,7 @@ msgstr ""
 msgid "Automatic Restart"
 msgstr ""
 
-#: src/views/certificate/CertificateEditor.vue:259
+#: src/views/certificate/CertificateEditor.vue:257
 #: src/views/config/components/ConfigLeftPanel.vue:273
 #: src/views/config/ConfigList.vue:112 src/views/config/ConfigList.vue:195
 #: src/views/nginx_log/NginxLog.vue:174
@@ -677,13 +687,17 @@ msgstr ""
 msgid "Certificate revoked successfully"
 msgstr ""
 
-#: src/views/certificate/CertificateEditor.vue:128
+#: src/views/certificate/CertificateEditor.vue:126
 #: src/views/site/site_edit/components/Cert/Cert.vue:58
 msgid "Certificate Status"
 msgid_plural "Certificates Status"
 msgstr[0] ""
 msgstr[1] ""
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:104
+msgid "Certificate Type"
+msgstr ""
+
 #: src/routes/modules/certificates.ts:11
 #: src/views/certificate/CertificateList/Certificate.vue:19
 msgid "Certificates"
@@ -1133,6 +1147,14 @@ msgstr ""
 msgid "Custom Directory"
 msgstr ""
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:126
+msgid "Custom Domains"
+msgstr ""
+
+#: src/views/certificate/components/DNSIssueCertificate.vue:110
+msgid "Custom Domains Certificate"
+msgstr ""
+
 #: src/views/preference/tabs/NodeSettings.vue:19
 msgid ""
 "Customize the name of local node to be displayed in the environment "
@@ -1441,11 +1463,11 @@ msgid_plural "Documents"
 msgstr[0] ""
 msgstr[1] ""
 
-#: src/views/certificate/components/WildcardCertificate.vue:66
+#: src/views/certificate/components/DNSIssueCertificate.vue:116
 msgid "Domain"
 msgstr ""
 
-#: src/views/certificate/CertificateEditor.vue:112
+#: src/views/certificate/CertificateEditor.vue:110
 msgid "Domains list is empty, try to reopen Auto Cert for %{config}"
 msgstr ""
 
@@ -1645,6 +1667,14 @@ msgstr ""
 msgid "End"
 msgstr ""
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:135
+msgid "Enter domain name"
+msgstr ""
+
+#: src/views/certificate/components/DNSIssueCertificate.vue:120
+msgid "Enter your domain"
+msgstr ""
+
 #: src/language/constants.ts:22
 msgid "Environment variables cleaned"
 msgstr ""
@@ -1719,7 +1749,7 @@ msgstr ""
 msgid "External Notify"
 msgstr ""
 
-#: src/views/site/site_edit/components/Cert/ObtainCertLive.vue:80
+#: src/views/site/site_edit/components/Cert/ObtainCertLive.vue:81
 msgid "Fail to obtain certificate"
 msgstr ""
 
@@ -2240,7 +2270,7 @@ msgid "Import"
 msgstr ""
 
 #: src/routes/modules/certificates.ts:46
-#: src/views/certificate/CertificateEditor.vue:85
+#: src/views/certificate/CertificateEditor.vue:83
 msgid "Import Certificate"
 msgstr ""
 
@@ -2381,11 +2411,11 @@ msgid "IP"
 msgstr ""
 
 #: src/views/certificate/CertificateList/Certificate.vue:37
-msgid "Issue wildcard certificate"
+msgid "Issue certificate"
 msgstr ""
 
-#: src/views/certificate/components/WildcardCertificate.vue:57
-msgid "Issue Wildcard Certificate"
+#: src/views/certificate/components/DNSIssueCertificate.vue:95
+msgid "Issue Certificate"
 msgstr ""
 
 #: src/language/constants.ts:20
@@ -2467,8 +2497,8 @@ msgstr ""
 msgid "Leave blank if you don't need this."
 msgstr ""
 
-#: src/views/certificate/CertificateEditor.vue:223
-#: src/views/certificate/CertificateEditor.vue:237
+#: src/views/certificate/CertificateEditor.vue:221
+#: src/views/certificate/CertificateEditor.vue:235
 msgid "Leave blank will not change anything"
 msgstr ""
 
@@ -2555,7 +2585,7 @@ msgstr ""
 msgid "Locations"
 msgstr ""
 
-#: src/views/certificate/CertificateEditor.vue:247
+#: src/views/certificate/CertificateEditor.vue:245
 msgid "Log"
 msgstr ""
 
@@ -2757,7 +2787,7 @@ msgid "Modify"
 msgstr ""
 
 #: src/routes/modules/certificates.ts:36
-#: src/views/certificate/CertificateEditor.vue:85
+#: src/views/certificate/CertificateEditor.vue:83
 msgid "Modify Certificate"
 msgstr ""
 
@@ -2792,7 +2822,7 @@ msgstr ""
 #: src/components/NgxConfigEditor/NgxUpstream.vue:199
 #: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/certificate/ACMEUser.vue:11
-#: src/views/certificate/CertificateEditor.vue:162
+#: src/views/certificate/CertificateEditor.vue:160
 #: src/views/certificate/CertificateList/certColumns.tsx:9
 #: src/views/certificate/DNSCredential.vue:9
 #: src/views/config/components/ConfigRightPanel/Basic.vue:34
@@ -2852,7 +2882,7 @@ msgstr ""
 msgid "New version released"
 msgstr ""
 
-#: src/views/certificate/components/WildcardCertificate.vue:89
+#: src/views/certificate/components/DNSIssueCertificate.vue:181
 #: src/views/install/components/InstallView.vue:96
 #: src/views/site/site_add/SiteAdd.vue:132
 #: src/views/site/site_edit/components/Cert/ObtainCert.vue:214
@@ -3425,6 +3455,10 @@ msgstr ""
 msgid "Please enter a valid port range"
 msgstr ""
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:74
+msgid "Please enter at least one domain"
+msgstr ""
+
 #: src/components/TwoFA/Authorization.vue:85
 msgid "Please enter the OTP code:"
 msgstr ""
@@ -3751,6 +3785,7 @@ msgstr ""
 msgid "Reloading nginx"
 msgstr ""
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:144
 #: src/views/preference/tabs/AuthSettings.vue:137
 msgid "Remove"
 msgstr ""
@@ -3833,8 +3868,8 @@ msgstr ""
 msgid "Renamed successfully"
 msgstr ""
 
-#: src/views/certificate/components/RenewCert.vue:46
-#: src/views/certificate/components/RenewCert.vue:53
+#: src/views/certificate/components/RenewCert.vue:48
+#: src/views/certificate/components/RenewCert.vue:55
 msgid "Renew Certificate"
 msgstr ""
 
@@ -3846,8 +3881,8 @@ msgstr ""
 msgid "Renew Certificate Success"
 msgstr ""
 
-#: src/views/certificate/components/RenewCert.vue:28
-#: src/views/certificate/components/WildcardCertificate.vue:46
+#: src/views/certificate/components/DNSIssueCertificate.vue:84
+#: src/views/certificate/components/RenewCert.vue:30
 msgid "Renew successfully"
 msgstr ""
 
@@ -4101,7 +4136,7 @@ msgstr ""
 
 #: src/components/ChatGPT/ChatMessage.vue:215
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:129
-#: src/language/curd.ts:18 src/views/certificate/CertificateEditor.vue:266
+#: src/language/curd.ts:18 src/views/certificate/CertificateEditor.vue:264
 #: src/views/config/components/ConfigLeftPanel.vue:282
 #: src/views/config/components/ConfigName.vue:59
 #: src/views/preference/components/AuthSettings/Passkey.vue:130
@@ -4156,7 +4191,7 @@ msgstr ""
 msgid "Save stream %{name} to %{node} successfully"
 msgstr ""
 
-#: src/language/curd.ts:35 src/views/certificate/CertificateEditor.vue:49
+#: src/language/curd.ts:35 src/views/certificate/store.ts:13
 #: src/views/preference/store/index.ts:118
 msgid "Save successfully"
 msgstr ""
@@ -4264,6 +4299,10 @@ msgstr ""
 msgid "Server"
 msgstr ""
 
+#: src/views/certificate/CertificateEditor.vue:55
+msgid "Server error"
+msgstr ""
+
 #: src/views/dashboard/ServerAnalytic.vue:175
 msgid "Server Info"
 msgstr ""
@@ -4394,7 +4433,7 @@ msgstr ""
 msgid "Sleep time between cache manager iterations"
 msgstr ""
 
-#: src/views/certificate/CertificateEditor.vue:213
+#: src/views/certificate/CertificateEditor.vue:211
 msgid "SSL Certificate Content"
 msgstr ""
 
@@ -4406,15 +4445,15 @@ msgstr ""
 msgid "SSL certificate file not found"
 msgstr ""
 
-#: src/views/certificate/CertificateEditor.vue:227
+#: src/views/certificate/CertificateEditor.vue:225
 msgid "SSL Certificate Key Content"
 msgstr ""
 
-#: src/views/certificate/CertificateEditor.vue:192
+#: src/views/certificate/CertificateEditor.vue:190
 msgid "SSL Certificate Key Path"
 msgstr ""
 
-#: src/views/certificate/CertificateEditor.vue:177
+#: src/views/certificate/CertificateEditor.vue:175
 #: src/views/preference/tabs/ServerSettings.vue:37
 msgid "SSL Certificate Path"
 msgstr ""
@@ -4641,7 +4680,7 @@ msgstr ""
 msgid "Sync strategy"
 msgstr ""
 
-#: src/views/certificate/CertificateEditor.vue:206
+#: src/views/certificate/CertificateEditor.vue:204
 msgid "Sync to"
 msgstr ""
 
@@ -4712,11 +4751,11 @@ msgid ""
 "dashes, colons, and dots."
 msgstr ""
 
-#: src/views/certificate/CertificateEditor.vue:216
+#: src/views/certificate/CertificateEditor.vue:214
 msgid "The input is not a SSL Certificate"
 msgstr ""
 
-#: src/views/certificate/CertificateEditor.vue:230
+#: src/views/certificate/CertificateEditor.vue:228
 msgid "The input is not a SSL Certificate Key"
 msgstr ""
 
@@ -4747,11 +4786,11 @@ msgstr ""
 msgid "The parameter of server_name is required"
 msgstr ""
 
-#: src/views/certificate/CertificateEditor.vue:181
+#: src/views/certificate/CertificateEditor.vue:179
 msgid "The path exists, but the file is not a certificate"
 msgstr ""
 
-#: src/views/certificate/CertificateEditor.vue:196
+#: src/views/certificate/CertificateEditor.vue:194
 msgid "The path exists, but the file is not a private key"
 msgstr ""
 
@@ -4803,17 +4842,17 @@ msgid ""
 "lose access to your account."
 msgstr ""
 
-#: src/views/certificate/CertificateEditor.vue:102
+#: src/views/certificate/CertificateEditor.vue:100
 msgid "This Auto Cert item is invalid, please remove it."
 msgstr ""
 
-#: src/views/certificate/CertificateEditor.vue:92
+#: src/views/certificate/CertificateEditor.vue:90
 msgid "This certificate is managed by Nginx UI"
 msgstr ""
 
-#: src/views/certificate/CertificateEditor.vue:165
-#: src/views/certificate/CertificateEditor.vue:179
-#: src/views/certificate/CertificateEditor.vue:194
+#: src/views/certificate/CertificateEditor.vue:163
+#: src/views/certificate/CertificateEditor.vue:177
+#: src/views/certificate/CertificateEditor.vue:192
 msgid "This field is required"
 msgstr ""
 
@@ -5252,6 +5291,10 @@ msgstr ""
 msgid "Whether to use a temporary path when writing temporary files"
 msgstr ""
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:107
+msgid "Wildcard Certificate"
+msgstr ""
+
 #: src/views/dashboard/components/ParamsOpt/PerformanceConfig.vue:48
 msgid "Worker Connections"
 msgstr ""

+ 84 - 42
app/src/language/es/app.po

@@ -187,6 +187,10 @@ msgstr "Agregar configuración"
 msgid "Add Directive Below"
 msgstr "Añadir directiva a continuación"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:151
+msgid "Add Domain"
+msgstr "Agregar dominio"
+
 #: src/components/NgxConfigEditor/LocationEditor.vue:131
 #: src/components/NgxConfigEditor/LocationEditor.vue:158
 msgid "Add Location"
@@ -236,6 +240,14 @@ msgstr "Todos"
 msgid "All Recovery Codes Have Been Used"
 msgstr "Todos los códigos de recuperación han sido utilizados"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:156
+msgid ""
+"All selected subdomains must belong to the same DNS Provider, otherwise the "
+"certificate application will fail."
+msgstr ""
+"Todos los subdominios seleccionados deben pertenecer al mismo proveedor de "
+"DNS, de lo contrario, la solicitud de certificado fallará."
+
 #: src/views/preference/tabs/OpenAISettings.vue:32
 msgid "API Base Url"
 msgstr "URL Base de la API"
@@ -262,7 +274,7 @@ msgstr "Aplicación"
 
 #: src/views/system/Upgrade.vue:195
 msgid "Arch"
-msgstr "\"Arquitectura\""
+msgstr "Arquitectura"
 
 #: src/views/preference/tabs/AuthSettings.vue:130
 msgid "Are you sure to delete this banned IP immediately?"
@@ -401,7 +413,7 @@ msgstr "Renovación automática deshabilitada por %{name}"
 msgid "Auto-renewal enabled for %{name}"
 msgstr "Renovación automática habilitada por %{name}"
 
-#: src/views/certificate/components/RenewCert.vue:49
+#: src/views/certificate/components/RenewCert.vue:51
 #: src/views/site/site_edit/components/Cert/IssueCert.vue:84
 msgid "AutoCert is running, please wait..."
 msgstr "AutoCert se está ejecutando, por favor espere..."
@@ -414,7 +426,7 @@ msgstr "\"AutoCert se está ejecutando...\""
 msgid "Automatic Restart"
 msgstr "\"Reinicio Automático\""
 
-#: src/views/certificate/CertificateEditor.vue:259
+#: src/views/certificate/CertificateEditor.vue:257
 #: src/views/config/components/ConfigLeftPanel.vue:273
 #: src/views/config/ConfigList.vue:112 src/views/config/ConfigList.vue:195
 #: src/views/nginx_log/NginxLog.vue:174
@@ -723,13 +735,17 @@ msgstr "Certificado renovado con éxito"
 msgid "Certificate revoked successfully"
 msgstr "Certificado revocado con éxito"
 
-#: src/views/certificate/CertificateEditor.vue:128
+#: src/views/certificate/CertificateEditor.vue:126
 #: src/views/site/site_edit/components/Cert/Cert.vue:58
 msgid "Certificate Status"
 msgid_plural "Certificates Status"
 msgstr[0] "Estado del Certificado"
 msgstr[1] "Estado de los Certificados"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:104
+msgid "Certificate Type"
+msgstr "Tipo de certificado"
+
 #: src/routes/modules/certificates.ts:11
 #: src/views/certificate/CertificateList/Certificate.vue:19
 msgid "Certificates"
@@ -1219,6 +1235,14 @@ msgstr "Expresión cron personalizada"
 msgid "Custom Directory"
 msgstr "Directorio personalizado"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:126
+msgid "Custom Domains"
+msgstr "Dominios personalizados"
+
+#: src/views/certificate/components/DNSIssueCertificate.vue:110
+msgid "Custom Domains Certificate"
+msgstr "Certificado de Dominios Personalizados"
+
 #: src/views/preference/tabs/NodeSettings.vue:19
 msgid ""
 "Customize the name of local node to be displayed in the environment "
@@ -1530,11 +1554,11 @@ msgid "Document"
 msgid_plural "Documents"
 msgstr[0] "Documento"
 
-#: src/views/certificate/components/WildcardCertificate.vue:66
+#: src/views/certificate/components/DNSIssueCertificate.vue:116
 msgid "Domain"
 msgstr "Dominio"
 
-#: src/views/certificate/CertificateEditor.vue:112
+#: src/views/certificate/CertificateEditor.vue:110
 msgid "Domains list is empty, try to reopen Auto Cert for %{config}"
 msgstr ""
 "La lista de dominios está vacía, intente reabrir la certificación "
@@ -1739,6 +1763,14 @@ msgstr "Encriptar sitio web con Let's Encrypt"
 msgid "End"
 msgstr "Fin"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:135
+msgid "Enter domain name"
+msgstr "Ingresar nombre de dominio"
+
+#: src/views/certificate/components/DNSIssueCertificate.vue:120
+msgid "Enter your domain"
+msgstr "Ingrese su dominio"
+
 #: src/language/constants.ts:22
 msgid "Environment variables cleaned"
 msgstr "Variables de entorno limpiadas"
@@ -1813,7 +1845,7 @@ msgstr "Contenedor Docker externo"
 msgid "External Notify"
 msgstr "Notificación Externa"
 
-#: src/views/site/site_edit/components/Cert/ObtainCertLive.vue:80
+#: src/views/site/site_edit/components/Cert/ObtainCertLive.vue:81
 msgid "Fail to obtain certificate"
 msgstr "Falla al obtener el certificado"
 
@@ -2343,7 +2375,7 @@ msgid "Import"
 msgstr "Importar"
 
 #: src/routes/modules/certificates.ts:46
-#: src/views/certificate/CertificateEditor.vue:85
+#: src/views/certificate/CertificateEditor.vue:83
 msgid "Import Certificate"
 msgstr "Importar Certificado"
 
@@ -2488,12 +2520,12 @@ msgid "IP"
 msgstr "IP"
 
 #: src/views/certificate/CertificateList/Certificate.vue:37
-msgid "Issue wildcard certificate"
-msgstr "Obtener certificado comodín"
+msgid "Issue certificate"
+msgstr "Emitir certificado"
 
-#: src/views/certificate/components/WildcardCertificate.vue:57
-msgid "Issue Wildcard Certificate"
-msgstr "Obtener certificado Comodín"
+#: src/views/certificate/components/DNSIssueCertificate.vue:95
+msgid "Issue Certificate"
+msgstr "Emitir certificado"
 
 #: src/language/constants.ts:20
 msgid "Issued certificate successfully"
@@ -2576,8 +2608,8 @@ msgstr "Dejar en blanco si no desea modificar"
 msgid "Leave blank if you don't need this."
 msgstr "Déjelo en blanco si no lo necesita."
 
-#: src/views/certificate/CertificateEditor.vue:223
-#: src/views/certificate/CertificateEditor.vue:237
+#: src/views/certificate/CertificateEditor.vue:221
+#: src/views/certificate/CertificateEditor.vue:235
 msgid "Leave blank will not change anything"
 msgstr "Dejarlo en blanco no cambiará nada"
 
@@ -2664,7 +2696,7 @@ msgstr "Ubicación"
 msgid "Locations"
 msgstr "Ubicaciones"
 
-#: src/views/certificate/CertificateEditor.vue:247
+#: src/views/certificate/CertificateEditor.vue:245
 msgid "Log"
 msgstr "Registro"
 
@@ -2879,7 +2911,7 @@ msgid "Modify"
 msgstr "Modificar"
 
 #: src/routes/modules/certificates.ts:36
-#: src/views/certificate/CertificateEditor.vue:85
+#: src/views/certificate/CertificateEditor.vue:83
 msgid "Modify Certificate"
 msgstr "Modificar Certificado"
 
@@ -2914,7 +2946,7 @@ msgstr "Directiva multilínea"
 #: src/components/NgxConfigEditor/NgxUpstream.vue:199
 #: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/certificate/ACMEUser.vue:11
-#: src/views/certificate/CertificateEditor.vue:162
+#: src/views/certificate/CertificateEditor.vue:160
 #: src/views/certificate/CertificateList/certColumns.tsx:9
 #: src/views/certificate/DNSCredential.vue:9
 #: src/views/config/components/ConfigRightPanel/Basic.vue:34
@@ -2974,7 +3006,7 @@ msgstr "Nueva ruta"
 msgid "New version released"
 msgstr "Se liberó una nueva versión"
 
-#: src/views/certificate/components/WildcardCertificate.vue:89
+#: src/views/certificate/components/DNSIssueCertificate.vue:181
 #: src/views/install/components/InstallView.vue:96
 #: src/views/site/site_add/SiteAdd.vue:132
 #: src/views/site/site_edit/components/Cert/ObtainCert.vue:214
@@ -3564,6 +3596,10 @@ msgstr ""
 msgid "Please enter a valid port range"
 msgstr "Por favor, introduzca un rango de puertos válido"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:74
+msgid "Please enter at least one domain"
+msgstr "Por favor, ingrese al menos un dominio"
+
 #: src/components/TwoFA/Authorization.vue:85
 msgid "Please enter the OTP code:"
 msgstr "Por favor, ingrese el código OTP:"
@@ -3911,6 +3947,7 @@ msgstr "Recargando"
 msgid "Reloading nginx"
 msgstr "Recargando Nginx"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:144
 #: src/views/preference/tabs/AuthSettings.vue:137
 msgid "Remove"
 msgstr "Eliminar"
@@ -3993,8 +4030,8 @@ msgstr "Renombrado con éxito"
 msgid "Renamed successfully"
 msgstr "Renombrado con éxito"
 
-#: src/views/certificate/components/RenewCert.vue:46
-#: src/views/certificate/components/RenewCert.vue:53
+#: src/views/certificate/components/RenewCert.vue:48
+#: src/views/certificate/components/RenewCert.vue:55
 msgid "Renew Certificate"
 msgstr "Renovar Certificado"
 
@@ -4006,8 +4043,8 @@ msgstr "Error al renovar el Certificado"
 msgid "Renew Certificate Success"
 msgstr "Renovado de Certificado exitoso"
 
-#: src/views/certificate/components/RenewCert.vue:28
-#: src/views/certificate/components/WildcardCertificate.vue:46
+#: src/views/certificate/components/DNSIssueCertificate.vue:84
+#: src/views/certificate/components/RenewCert.vue:30
 msgid "Renew successfully"
 msgstr "Renovado con éxito"
 
@@ -4266,7 +4303,7 @@ msgstr "Sábado"
 
 #: src/components/ChatGPT/ChatMessage.vue:215
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:129
-#: src/language/curd.ts:18 src/views/certificate/CertificateEditor.vue:266
+#: src/language/curd.ts:18 src/views/certificate/CertificateEditor.vue:264
 #: src/views/config/components/ConfigLeftPanel.vue:282
 #: src/views/config/components/ConfigName.vue:59
 #: src/views/preference/components/AuthSettings/Passkey.vue:130
@@ -4321,7 +4358,7 @@ msgstr "Error al guardar el flujo %{name} en %{node}"
 msgid "Save stream %{name} to %{node} successfully"
 msgstr "Flujo %{name} guardado en %{node} correctamente"
 
-#: src/language/curd.ts:35 src/views/certificate/CertificateEditor.vue:49
+#: src/language/curd.ts:35 src/views/certificate/store.ts:13
 #: src/views/preference/store/index.ts:118
 msgid "Save successfully"
 msgstr "Guardado con éxito"
@@ -4433,6 +4470,10 @@ msgstr "Enviado"
 msgid "Server"
 msgstr "Servidor"
 
+#: src/views/certificate/CertificateEditor.vue:55
+msgid "Server error"
+msgstr "Error del servidor"
+
 #: src/views/dashboard/ServerAnalytic.vue:175
 msgid "Server Info"
 msgstr "Información del servidor"
@@ -4569,7 +4610,7 @@ msgstr "Tiempo de espera entre iteraciones del cargador de caché"
 msgid "Sleep time between cache manager iterations"
 msgstr "Tiempo de espera entre iteraciones del administrador de caché"
 
-#: src/views/certificate/CertificateEditor.vue:213
+#: src/views/certificate/CertificateEditor.vue:211
 msgid "SSL Certificate Content"
 msgstr "Contenido de certificado SSL"
 
@@ -4583,15 +4624,15 @@ msgstr ""
 msgid "SSL certificate file not found"
 msgstr "Archivo de certificado SSL no encontrado"
 
-#: src/views/certificate/CertificateEditor.vue:227
+#: src/views/certificate/CertificateEditor.vue:225
 msgid "SSL Certificate Key Content"
 msgstr "Contenido de la llave del certificado SSL"
 
-#: src/views/certificate/CertificateEditor.vue:192
+#: src/views/certificate/CertificateEditor.vue:190
 msgid "SSL Certificate Key Path"
 msgstr "Ruta de la llave del certificado SSL"
 
-#: src/views/certificate/CertificateEditor.vue:177
+#: src/views/certificate/CertificateEditor.vue:175
 #: src/views/preference/tabs/ServerSettings.vue:37
 msgid "SSL Certificate Path"
 msgstr "Ruta del certificado SSL"
@@ -4831,7 +4872,7 @@ msgstr "Nodos de sincronización"
 msgid "Sync strategy"
 msgstr "Estrategia de sincronización"
 
-#: src/views/certificate/CertificateEditor.vue:206
+#: src/views/certificate/CertificateEditor.vue:204
 msgid "Sync to"
 msgstr "Sincronizar con"
 
@@ -4907,11 +4948,11 @@ msgstr ""
 "El número ICP solo debe contener letras, unicode, números, guiones, guiones "
 "bajos, dos puntos y puntos."
 
-#: src/views/certificate/CertificateEditor.vue:216
+#: src/views/certificate/CertificateEditor.vue:214
 msgid "The input is not a SSL Certificate"
 msgstr "La entrada no es un Certificado SSL"
 
-#: src/views/certificate/CertificateEditor.vue:230
+#: src/views/certificate/CertificateEditor.vue:228
 msgid "The input is not a SSL Certificate Key"
 msgstr "La entrada no es una clave de certificado SSL"
 
@@ -4950,11 +4991,11 @@ msgstr ""
 msgid "The parameter of server_name is required"
 msgstr "El parámetro de server_name es obligatorio"
 
-#: src/views/certificate/CertificateEditor.vue:181
+#: src/views/certificate/CertificateEditor.vue:179
 msgid "The path exists, but the file is not a certificate"
 msgstr "La ruta existe, pero el archivo no es un certificado"
 
-#: src/views/certificate/CertificateEditor.vue:196
+#: src/views/certificate/CertificateEditor.vue:194
 msgid "The path exists, but the file is not a private key"
 msgstr "La ruta existe, pero el archivo no es una clave privada"
 
@@ -5016,17 +5057,17 @@ msgstr ""
 "pierda su contraseña y los segundos factores. Si no puede encontrar estos "
 "códigos, perderá el acceso a su cuenta."
 
-#: src/views/certificate/CertificateEditor.vue:102
+#: src/views/certificate/CertificateEditor.vue:100
 msgid "This Auto Cert item is invalid, please remove it."
 msgstr "Este elemento de Auto Cert es inválido, elimínelo por favor."
 
-#: src/views/certificate/CertificateEditor.vue:92
+#: src/views/certificate/CertificateEditor.vue:90
 msgid "This certificate is managed by Nginx UI"
 msgstr "Este certificado es administrado por Nginx UI"
 
-#: src/views/certificate/CertificateEditor.vue:165
-#: src/views/certificate/CertificateEditor.vue:179
-#: src/views/certificate/CertificateEditor.vue:194
+#: src/views/certificate/CertificateEditor.vue:163
+#: src/views/certificate/CertificateEditor.vue:177
+#: src/views/certificate/CertificateEditor.vue:192
 msgid "This field is required"
 msgstr "Este campo es obligatorio"
 
@@ -5509,6 +5550,10 @@ msgstr ""
 msgid "Whether to use a temporary path when writing temporary files"
 msgstr "Si se debe usar una ruta temporal al escribir archivos temporales"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:107
+msgid "Wildcard Certificate"
+msgstr "Certificado comodín"
+
 #: src/views/dashboard/components/ParamsOpt/PerformanceConfig.vue:48
 msgid "Worker Connections"
 msgstr "Conexiones de trabajador"
@@ -5851,9 +5896,6 @@ msgstr "Sus llaves de acceso"
 #~ msgid "Recovery Code:"
 #~ msgstr "Código de Recuperación:"
 
-#~ msgid "Server error"
-#~ msgstr "Error del servidor"
-
 #~ msgid "The recovery code is only displayed once, please save it in a safe place."
 #~ msgstr ""
 #~ "El código de recuperación se muestra solo una vez, por favor guárdalo en un "

+ 84 - 42
app/src/language/fr_FR/app.po

@@ -185,6 +185,10 @@ msgstr "Ajouter une configuration"
 msgid "Add Directive Below"
 msgstr "Ajouter une directive"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:151
+msgid "Add Domain"
+msgstr "Ajouter un domaine"
+
 #: src/components/NgxConfigEditor/LocationEditor.vue:131
 #: src/components/NgxConfigEditor/LocationEditor.vue:158
 msgid "Add Location"
@@ -234,6 +238,14 @@ msgstr "Tous"
 msgid "All Recovery Codes Have Been Used"
 msgstr "Tous les codes de récupération ont été utilisés"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:156
+msgid ""
+"All selected subdomains must belong to the same DNS Provider, otherwise the "
+"certificate application will fail."
+msgstr ""
+"Tous les sous-domaines sélectionnés doivent appartenir au même fournisseur "
+"DNS, sinon la demande de certificat échouera."
+
 #: src/views/preference/tabs/OpenAISettings.vue:32
 msgid "API Base Url"
 msgstr "URL de base de l'API"
@@ -260,7 +272,7 @@ msgstr "Application"
 
 #: src/views/system/Upgrade.vue:195
 msgid "Arch"
-msgstr "\"Arch\""
+msgstr "Arch"
 
 #: src/views/preference/tabs/AuthSettings.vue:130
 msgid "Are you sure to delete this banned IP immediately?"
@@ -399,7 +411,7 @@ msgstr "Renouvellement automatique désactivé pour %{name}"
 msgid "Auto-renewal enabled for %{name}"
 msgstr "Renouvellement automatique activé pour %{name}"
 
-#: src/views/certificate/components/RenewCert.vue:49
+#: src/views/certificate/components/RenewCert.vue:51
 #: src/views/site/site_edit/components/Cert/IssueCert.vue:84
 msgid "AutoCert is running, please wait..."
 msgstr "AutoCert est en cours d'exécution, veuillez patienter..."
@@ -412,7 +424,7 @@ msgstr "\"AutoCert est en cours d'exécution...\""
 msgid "Automatic Restart"
 msgstr "\"Redémarrage Automatique\""
 
-#: src/views/certificate/CertificateEditor.vue:259
+#: src/views/certificate/CertificateEditor.vue:257
 #: src/views/config/components/ConfigLeftPanel.vue:273
 #: src/views/config/ConfigList.vue:112 src/views/config/ConfigList.vue:195
 #: src/views/nginx_log/NginxLog.vue:174
@@ -716,12 +728,16 @@ msgstr "Certificat renouvelé avec succès"
 msgid "Certificate revoked successfully"
 msgstr "Certificat révoqué avec succès"
 
-#: src/views/certificate/CertificateEditor.vue:128
+#: src/views/certificate/CertificateEditor.vue:126
 #: src/views/site/site_edit/components/Cert/Cert.vue:58
 msgid "Certificate Status"
 msgid_plural "Certificates Status"
 msgstr[0] "État du certificat"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:104
+msgid "Certificate Type"
+msgstr "Type de certificat"
+
 #: src/routes/modules/certificates.ts:11
 #: src/views/certificate/CertificateList/Certificate.vue:19
 msgid "Certificates"
@@ -1215,6 +1231,14 @@ msgstr "Expression cron personnalisée"
 msgid "Custom Directory"
 msgstr "Répertoire personnalisé"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:126
+msgid "Custom Domains"
+msgstr "Domaines personnalisés"
+
+#: src/views/certificate/components/DNSIssueCertificate.vue:110
+msgid "Custom Domains Certificate"
+msgstr "Certificat de domaines personnalisés"
+
 #: src/views/preference/tabs/NodeSettings.vue:19
 msgid ""
 "Customize the name of local node to be displayed in the environment "
@@ -1526,11 +1550,11 @@ msgid "Document"
 msgid_plural "Documents"
 msgstr[0] "Document"
 
-#: src/views/certificate/components/WildcardCertificate.vue:66
+#: src/views/certificate/components/DNSIssueCertificate.vue:116
 msgid "Domain"
 msgstr "Domaine"
 
-#: src/views/certificate/CertificateEditor.vue:112
+#: src/views/certificate/CertificateEditor.vue:110
 msgid "Domains list is empty, try to reopen Auto Cert for %{config}"
 msgstr "La liste des domaines est vide, essayez de rouvrir Auto Cert pour %{config}"
 
@@ -1733,6 +1757,14 @@ msgstr "Crypter le site Web avec Let's Encrypt"
 msgid "End"
 msgstr "Fin"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:135
+msgid "Enter domain name"
+msgstr "Entrez le nom de domaine"
+
+#: src/views/certificate/components/DNSIssueCertificate.vue:120
+msgid "Enter your domain"
+msgstr "Entrez votre domaine"
+
 #: src/language/constants.ts:22
 msgid "Environment variables cleaned"
 msgstr "Variables d'environnement nettoyées"
@@ -1807,7 +1839,7 @@ msgstr "Conteneur Docker externe"
 msgid "External Notify"
 msgstr "Notification Externe"
 
-#: src/views/site/site_edit/components/Cert/ObtainCertLive.vue:80
+#: src/views/site/site_edit/components/Cert/ObtainCertLive.vue:81
 msgid "Fail to obtain certificate"
 msgstr "Échec de l'obtention du certificat"
 
@@ -2341,7 +2373,7 @@ msgid "Import"
 msgstr "Importer"
 
 #: src/routes/modules/certificates.ts:46
-#: src/views/certificate/CertificateEditor.vue:85
+#: src/views/certificate/CertificateEditor.vue:83
 msgid "Import Certificate"
 msgstr "Importer un certificat"
 
@@ -2486,12 +2518,12 @@ msgid "IP"
 msgstr "IP"
 
 #: src/views/certificate/CertificateList/Certificate.vue:37
-msgid "Issue wildcard certificate"
-msgstr "Émettre un certificat générique"
+msgid "Issue certificate"
+msgstr "Émettre un certificat"
 
-#: src/views/certificate/components/WildcardCertificate.vue:57
-msgid "Issue Wildcard Certificate"
-msgstr "Émettre un certificat générique"
+#: src/views/certificate/components/DNSIssueCertificate.vue:95
+msgid "Issue Certificate"
+msgstr "Émettre un certificat"
 
 #: src/language/constants.ts:20
 msgid "Issued certificate successfully"
@@ -2574,8 +2606,8 @@ msgstr "Laisser vide si vous ne souhaitez pas modifier"
 msgid "Leave blank if you don't need this."
 msgstr "Laissez vide si vous n'en avez pas besoin."
 
-#: src/views/certificate/CertificateEditor.vue:223
-#: src/views/certificate/CertificateEditor.vue:237
+#: src/views/certificate/CertificateEditor.vue:221
+#: src/views/certificate/CertificateEditor.vue:235
 msgid "Leave blank will not change anything"
 msgstr "Laisser vide ne changera rien"
 
@@ -2662,7 +2694,7 @@ msgstr "Emplacement"
 msgid "Locations"
 msgstr "Emplacements"
 
-#: src/views/certificate/CertificateEditor.vue:247
+#: src/views/certificate/CertificateEditor.vue:245
 msgid "Log"
 msgstr "Journal"
 
@@ -2877,7 +2909,7 @@ msgid "Modify"
 msgstr "Modifier"
 
 #: src/routes/modules/certificates.ts:36
-#: src/views/certificate/CertificateEditor.vue:85
+#: src/views/certificate/CertificateEditor.vue:83
 msgid "Modify Certificate"
 msgstr "Modifier le certificat"
 
@@ -2912,7 +2944,7 @@ msgstr "Directive multiligne"
 #: src/components/NgxConfigEditor/NgxUpstream.vue:199
 #: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/certificate/ACMEUser.vue:11
-#: src/views/certificate/CertificateEditor.vue:162
+#: src/views/certificate/CertificateEditor.vue:160
 #: src/views/certificate/CertificateList/certColumns.tsx:9
 #: src/views/certificate/DNSCredential.vue:9
 #: src/views/config/components/ConfigRightPanel/Basic.vue:34
@@ -2972,7 +3004,7 @@ msgstr "Nouveau chemin"
 msgid "New version released"
 msgstr "Nouvelle version publiée"
 
-#: src/views/certificate/components/WildcardCertificate.vue:89
+#: src/views/certificate/components/DNSIssueCertificate.vue:181
 #: src/views/install/components/InstallView.vue:96
 #: src/views/site/site_add/SiteAdd.vue:132
 #: src/views/site/site_edit/components/Cert/ObtainCert.vue:214
@@ -3563,6 +3595,10 @@ msgstr ""
 msgid "Please enter a valid port range"
 msgstr "Veuillez entrer une plage de ports valide"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:74
+msgid "Please enter at least one domain"
+msgstr "Veuillez saisir au moins un domaine"
+
 #: src/components/TwoFA/Authorization.vue:85
 msgid "Please enter the OTP code:"
 msgstr "Veuillez entrer le code OTP :"
@@ -3910,6 +3946,7 @@ msgstr "Rechargement"
 msgid "Reloading nginx"
 msgstr "Rechargement de nginx"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:144
 #: src/views/preference/tabs/AuthSettings.vue:137
 msgid "Remove"
 msgstr "Supprimer"
@@ -3992,8 +4029,8 @@ msgstr "Renommage réussi"
 msgid "Renamed successfully"
 msgstr "Renommé avec succès"
 
-#: src/views/certificate/components/RenewCert.vue:46
-#: src/views/certificate/components/RenewCert.vue:53
+#: src/views/certificate/components/RenewCert.vue:48
+#: src/views/certificate/components/RenewCert.vue:55
 msgid "Renew Certificate"
 msgstr "Renouveler le certificat"
 
@@ -4005,8 +4042,8 @@ msgstr "Erreur de renouvellement du certificat"
 msgid "Renew Certificate Success"
 msgstr "Renouvellement du certificat réussi"
 
-#: src/views/certificate/components/RenewCert.vue:28
-#: src/views/certificate/components/WildcardCertificate.vue:46
+#: src/views/certificate/components/DNSIssueCertificate.vue:84
+#: src/views/certificate/components/RenewCert.vue:30
 msgid "Renew successfully"
 msgstr "Renouvellement réussi"
 
@@ -4265,7 +4302,7 @@ msgstr "Samedi"
 
 #: src/components/ChatGPT/ChatMessage.vue:215
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:129
-#: src/language/curd.ts:18 src/views/certificate/CertificateEditor.vue:266
+#: src/language/curd.ts:18 src/views/certificate/CertificateEditor.vue:264
 #: src/views/config/components/ConfigLeftPanel.vue:282
 #: src/views/config/components/ConfigName.vue:59
 #: src/views/preference/components/AuthSettings/Passkey.vue:130
@@ -4320,7 +4357,7 @@ msgstr "Échec de l'enregistrement du flux %{name} sur %{node}"
 msgid "Save stream %{name} to %{node} successfully"
 msgstr "Flux %{name} enregistré sur %{node} avec succès"
 
-#: src/language/curd.ts:35 src/views/certificate/CertificateEditor.vue:49
+#: src/language/curd.ts:35 src/views/certificate/store.ts:13
 #: src/views/preference/store/index.ts:118
 msgid "Save successfully"
 msgstr "Sauvegarde réussie"
@@ -4432,6 +4469,10 @@ msgstr "Envoyer"
 msgid "Server"
 msgstr "Serveur"
 
+#: src/views/certificate/CertificateEditor.vue:55
+msgid "Server error"
+msgstr "Erreur du serveur"
+
 #: src/views/dashboard/ServerAnalytic.vue:175
 msgid "Server Info"
 msgstr "Informations sur le serveur"
@@ -4568,7 +4609,7 @@ msgstr "Temps d'attente entre les itérations du chargeur de cache"
 msgid "Sleep time between cache manager iterations"
 msgstr "Temps d'attente entre les itérations du gestionnaire de cache"
 
-#: src/views/certificate/CertificateEditor.vue:213
+#: src/views/certificate/CertificateEditor.vue:211
 msgid "SSL Certificate Content"
 msgstr "Contenu du certificat SSL"
 
@@ -4582,15 +4623,15 @@ msgstr ""
 msgid "SSL certificate file not found"
 msgstr "Fichier de certificat SSL introuvable"
 
-#: src/views/certificate/CertificateEditor.vue:227
+#: src/views/certificate/CertificateEditor.vue:225
 msgid "SSL Certificate Key Content"
 msgstr "Contenu de la clé du certificat SSL"
 
-#: src/views/certificate/CertificateEditor.vue:192
+#: src/views/certificate/CertificateEditor.vue:190
 msgid "SSL Certificate Key Path"
 msgstr "Chemin de la clé du certificat SSL"
 
-#: src/views/certificate/CertificateEditor.vue:177
+#: src/views/certificate/CertificateEditor.vue:175
 #: src/views/preference/tabs/ServerSettings.vue:37
 msgid "SSL Certificate Path"
 msgstr "Chemin du certificat SSL"
@@ -4832,7 +4873,7 @@ msgstr "Nœuds de synchronisation"
 msgid "Sync strategy"
 msgstr "Stratégie de synchronisation"
 
-#: src/views/certificate/CertificateEditor.vue:206
+#: src/views/certificate/CertificateEditor.vue:204
 msgid "Sync to"
 msgstr "Synchroniser vers"
 
@@ -4908,11 +4949,11 @@ msgstr ""
 "Le numéro ICP ne doit contenir que des lettres, unicode, des chiffres, des "
 "traits d'union, des tirets, des deux-points et des points."
 
-#: src/views/certificate/CertificateEditor.vue:216
+#: src/views/certificate/CertificateEditor.vue:214
 msgid "The input is not a SSL Certificate"
 msgstr "L'entrée n'est pas un certificat SSL"
 
-#: src/views/certificate/CertificateEditor.vue:230
+#: src/views/certificate/CertificateEditor.vue:228
 msgid "The input is not a SSL Certificate Key"
 msgstr "L'entrée n'est pas une clé de certificat SSL"
 
@@ -4951,11 +4992,11 @@ msgstr ""
 msgid "The parameter of server_name is required"
 msgstr "Le paramètre server_name est requis"
 
-#: src/views/certificate/CertificateEditor.vue:181
+#: src/views/certificate/CertificateEditor.vue:179
 msgid "The path exists, but the file is not a certificate"
 msgstr "Le chemin existe, mais le fichier n'est pas un certificat"
 
-#: src/views/certificate/CertificateEditor.vue:196
+#: src/views/certificate/CertificateEditor.vue:194
 msgid "The path exists, but the file is not a private key"
 msgstr "Le chemin existe, mais le fichier n'est pas une clé privée"
 
@@ -5018,17 +5059,17 @@ msgstr ""
 "perdez votre mot de passe et vos seconds facteurs. Si vous ne trouvez pas "
 "ces codes, vous perdrez l'accès à votre compte."
 
-#: src/views/certificate/CertificateEditor.vue:102
+#: src/views/certificate/CertificateEditor.vue:100
 msgid "This Auto Cert item is invalid, please remove it."
 msgstr "Cet élément de certificat automatique est invalide, veuillez le supprimer."
 
-#: src/views/certificate/CertificateEditor.vue:92
+#: src/views/certificate/CertificateEditor.vue:90
 msgid "This certificate is managed by Nginx UI"
 msgstr "Ce certificat est géré par Nginx UI"
 
-#: src/views/certificate/CertificateEditor.vue:165
-#: src/views/certificate/CertificateEditor.vue:179
-#: src/views/certificate/CertificateEditor.vue:194
+#: src/views/certificate/CertificateEditor.vue:163
+#: src/views/certificate/CertificateEditor.vue:177
+#: src/views/certificate/CertificateEditor.vue:192
 msgid "This field is required"
 msgstr "Ce champ est obligatoire"
 
@@ -5520,6 +5561,10 @@ msgstr ""
 "S'il faut utiliser un chemin temporaire lors de l'écriture de fichiers "
 "temporaires"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:107
+msgid "Wildcard Certificate"
+msgstr "Certificat générique"
+
 #: src/views/dashboard/components/ParamsOpt/PerformanceConfig.vue:48
 msgid "Worker Connections"
 msgstr "Connexions des travailleurs"
@@ -5837,9 +5882,6 @@ msgstr "Vos clés d'accès"
 #~ msgid "File"
 #~ msgstr "Fichier"
 
-#~ msgid "Server error"
-#~ msgstr "Erreur du serveur"
-
 #, fuzzy
 #~ msgid "Incorrect username or password"
 #~ msgstr "Le pseudo ou mot de passe est incorect"

+ 82 - 39
app/src/language/ja_JP/app.po

@@ -181,6 +181,10 @@ msgstr "設定を追加"
 msgid "Add Directive Below"
 msgstr "ディレクティブを追加"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:151
+msgid "Add Domain"
+msgstr "ドメインを追加"
+
 #: src/components/NgxConfigEditor/LocationEditor.vue:131
 #: src/components/NgxConfigEditor/LocationEditor.vue:158
 msgid "Add Location"
@@ -228,6 +232,12 @@ msgstr "すべて"
 msgid "All Recovery Codes Have Been Used"
 msgstr "すべてのリカバリーコードが使用済みです"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:156
+msgid ""
+"All selected subdomains must belong to the same DNS Provider, otherwise the "
+"certificate application will fail."
+msgstr "選択したすべてのサブドメインは同じ DNS プロバイダーに属している必要があります。そうでない場合、証明書の申請は失敗します。"
+
 #: src/views/preference/tabs/OpenAISettings.vue:32
 msgid "API Base Url"
 msgstr "APIベースURL"
@@ -254,7 +264,7 @@ msgstr "アプリ"
 
 #: src/views/system/Upgrade.vue:195
 msgid "Arch"
-msgstr "\"アーキテクチャ\""
+msgstr "アーキテクチャ"
 
 #: src/views/preference/tabs/AuthSettings.vue:130
 msgid "Are you sure to delete this banned IP immediately?"
@@ -389,7 +399,7 @@ msgstr "%{name}の自動更新が無効になっています"
 msgid "Auto-renewal enabled for %{name}"
 msgstr "%{name}の自動更新が有効になっています"
 
-#: src/views/certificate/components/RenewCert.vue:49
+#: src/views/certificate/components/RenewCert.vue:51
 #: src/views/site/site_edit/components/Cert/IssueCert.vue:84
 msgid "AutoCert is running, please wait..."
 msgstr "AutoCert が実行中です。お待ちください..."
@@ -402,7 +412,7 @@ msgstr "AutoCert が実行中..."
 msgid "Automatic Restart"
 msgstr "自動再起動"
 
-#: src/views/certificate/CertificateEditor.vue:259
+#: src/views/certificate/CertificateEditor.vue:257
 #: src/views/config/components/ConfigLeftPanel.vue:273
 #: src/views/config/ConfigList.vue:112 src/views/config/ConfigList.vue:195
 #: src/views/nginx_log/NginxLog.vue:174
@@ -695,12 +705,16 @@ msgstr "証明書の更新に成功しました"
 msgid "Certificate revoked successfully"
 msgstr "証明書の失効に成功しました"
 
-#: src/views/certificate/CertificateEditor.vue:128
+#: src/views/certificate/CertificateEditor.vue:126
 #: src/views/site/site_edit/components/Cert/Cert.vue:58
 msgid "Certificate Status"
 msgid_plural "Certificates Status"
 msgstr[0] "証明書のステータス"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:104
+msgid "Certificate Type"
+msgstr "証明書の種類"
+
 #: src/routes/modules/certificates.ts:11
 #: src/views/certificate/CertificateList/Certificate.vue:19
 msgid "Certificates"
@@ -1170,6 +1184,14 @@ msgstr "カスタム cron 式"
 msgid "Custom Directory"
 msgstr "カスタムディレクトリ"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:126
+msgid "Custom Domains"
+msgstr "カスタムドメイン"
+
+#: src/views/certificate/components/DNSIssueCertificate.vue:110
+msgid "Custom Domains Certificate"
+msgstr "カスタムドメイン証明書"
+
 #: src/views/preference/tabs/NodeSettings.vue:19
 msgid ""
 "Customize the name of local node to be displayed in the environment "
@@ -1477,11 +1499,11 @@ msgid "Document"
 msgid_plural "Documents"
 msgstr[0] "ドキュメント"
 
-#: src/views/certificate/components/WildcardCertificate.vue:66
+#: src/views/certificate/components/DNSIssueCertificate.vue:116
 msgid "Domain"
 msgstr "ドメイン"
 
-#: src/views/certificate/CertificateEditor.vue:112
+#: src/views/certificate/CertificateEditor.vue:110
 msgid "Domains list is empty, try to reopen Auto Cert for %{config}"
 msgstr "ドメインリストが空です。%{config} の自動証明書を再度開いてみてください"
 
@@ -1681,6 +1703,14 @@ msgstr "Let's Encryptでウェブサイトを暗号化"
 msgid "End"
 msgstr "終了"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:135
+msgid "Enter domain name"
+msgstr "ドメイン名を入力"
+
+#: src/views/certificate/components/DNSIssueCertificate.vue:120
+msgid "Enter your domain"
+msgstr "ドメインを入力してください"
+
 #: src/language/constants.ts:22
 msgid "Environment variables cleaned"
 msgstr "環境変数をクリーンアップしました"
@@ -1755,7 +1785,7 @@ msgstr "外部 Docker コンテナ"
 msgid "External Notify"
 msgstr "外部通知"
 
-#: src/views/site/site_edit/components/Cert/ObtainCertLive.vue:80
+#: src/views/site/site_edit/components/Cert/ObtainCertLive.vue:81
 msgid "Fail to obtain certificate"
 msgstr "証明書の取得に失敗しました"
 
@@ -2276,7 +2306,7 @@ msgid "Import"
 msgstr "インポート"
 
 #: src/routes/modules/certificates.ts:46
-#: src/views/certificate/CertificateEditor.vue:85
+#: src/views/certificate/CertificateEditor.vue:83
 msgid "Import Certificate"
 msgstr "証明書をインポート"
 
@@ -2417,12 +2447,12 @@ msgid "IP"
 msgstr "IP"
 
 #: src/views/certificate/CertificateList/Certificate.vue:37
-msgid "Issue wildcard certificate"
-msgstr "ワイルドカード証明書を発行"
+msgid "Issue certificate"
+msgstr "証明書を発行"
 
-#: src/views/certificate/components/WildcardCertificate.vue:57
-msgid "Issue Wildcard Certificate"
-msgstr "ワイルドカード証明書を発行"
+#: src/views/certificate/components/DNSIssueCertificate.vue:95
+msgid "Issue Certificate"
+msgstr "証明書を発行"
 
 #: src/language/constants.ts:20
 msgid "Issued certificate successfully"
@@ -2503,8 +2533,8 @@ msgstr "変更しない場合は空白のままにしてください"
 msgid "Leave blank if you don't need this."
 msgstr "必要なければ空白のままにしてください。"
 
-#: src/views/certificate/CertificateEditor.vue:223
-#: src/views/certificate/CertificateEditor.vue:237
+#: src/views/certificate/CertificateEditor.vue:221
+#: src/views/certificate/CertificateEditor.vue:235
 msgid "Leave blank will not change anything"
 msgstr "空白のままにすると何も変更されません"
 
@@ -2591,7 +2621,7 @@ msgstr "ロケーション"
 msgid "Locations"
 msgstr "ロケーション"
 
-#: src/views/certificate/CertificateEditor.vue:247
+#: src/views/certificate/CertificateEditor.vue:245
 msgid "Log"
 msgstr "ログ"
 
@@ -2801,7 +2831,7 @@ msgid "Modify"
 msgstr "変更"
 
 #: src/routes/modules/certificates.ts:36
-#: src/views/certificate/CertificateEditor.vue:85
+#: src/views/certificate/CertificateEditor.vue:83
 msgid "Modify Certificate"
 msgstr "証明書を変更"
 
@@ -2836,7 +2866,7 @@ msgstr "複数行ディレクティブ"
 #: src/components/NgxConfigEditor/NgxUpstream.vue:199
 #: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/certificate/ACMEUser.vue:11
-#: src/views/certificate/CertificateEditor.vue:162
+#: src/views/certificate/CertificateEditor.vue:160
 #: src/views/certificate/CertificateList/certColumns.tsx:9
 #: src/views/certificate/DNSCredential.vue:9
 #: src/views/config/components/ConfigRightPanel/Basic.vue:34
@@ -2896,7 +2926,7 @@ msgstr "新しいパス"
 msgid "New version released"
 msgstr "新しいバージョンがリリースされました"
 
-#: src/views/certificate/components/WildcardCertificate.vue:89
+#: src/views/certificate/components/DNSIssueCertificate.vue:181
 #: src/views/install/components/InstallView.vue:96
 #: src/views/site/site_add/SiteAdd.vue:132
 #: src/views/site/site_edit/components/Cert/ObtainCert.vue:214
@@ -3471,6 +3501,10 @@ msgstr "作成したいパスキーの名前を入力し、下のOKボタンを
 msgid "Please enter a valid port range"
 msgstr "有効なポート範囲を入力してください"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:74
+msgid "Please enter at least one domain"
+msgstr "少なくとも1つのドメインを入力してください"
+
 #: src/components/TwoFA/Authorization.vue:85
 msgid "Please enter the OTP code:"
 msgstr "OTPコードを入力してください:"
@@ -3798,6 +3832,7 @@ msgstr "再読み込み中"
 msgid "Reloading nginx"
 msgstr "nginx をリロード中"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:144
 #: src/views/preference/tabs/AuthSettings.vue:137
 msgid "Remove"
 msgstr "削除"
@@ -3880,8 +3915,8 @@ msgstr "名前の変更に成功しました"
 msgid "Renamed successfully"
 msgstr "名前の変更に成功しました"
 
-#: src/views/certificate/components/RenewCert.vue:46
-#: src/views/certificate/components/RenewCert.vue:53
+#: src/views/certificate/components/RenewCert.vue:48
+#: src/views/certificate/components/RenewCert.vue:55
 msgid "Renew Certificate"
 msgstr "証明書を更新"
 
@@ -3893,8 +3928,8 @@ msgstr "証明書の更新エラー"
 msgid "Renew Certificate Success"
 msgstr "証明書の更新に成功しました"
 
-#: src/views/certificate/components/RenewCert.vue:28
-#: src/views/certificate/components/WildcardCertificate.vue:46
+#: src/views/certificate/components/DNSIssueCertificate.vue:84
+#: src/views/certificate/components/RenewCert.vue:30
 msgid "Renew successfully"
 msgstr "証明書の更新に成功しました"
 
@@ -4148,7 +4183,7 @@ msgstr "土曜日"
 
 #: src/components/ChatGPT/ChatMessage.vue:215
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:129
-#: src/language/curd.ts:18 src/views/certificate/CertificateEditor.vue:266
+#: src/language/curd.ts:18 src/views/certificate/CertificateEditor.vue:264
 #: src/views/config/components/ConfigLeftPanel.vue:282
 #: src/views/config/components/ConfigName.vue:59
 #: src/views/preference/components/AuthSettings/Passkey.vue:130
@@ -4203,7 +4238,7 @@ msgstr "ストリーム %{name} を %{node} に保存できませんでした"
 msgid "Save stream %{name} to %{node} successfully"
 msgstr "ストリーム %{name} を %{node} に正常に保存しました"
 
-#: src/language/curd.ts:35 src/views/certificate/CertificateEditor.vue:49
+#: src/language/curd.ts:35 src/views/certificate/store.ts:13
 #: src/views/preference/store/index.ts:118
 msgid "Save successfully"
 msgstr "保存に成功しました"
@@ -4311,6 +4346,10 @@ msgstr "送信"
 msgid "Server"
 msgstr "サーバー"
 
+#: src/views/certificate/CertificateEditor.vue:55
+msgid "Server error"
+msgstr "サーバーエラー"
+
 #: src/views/dashboard/ServerAnalytic.vue:175
 msgid "Server Info"
 msgstr "サーバー情報"
@@ -4445,7 +4484,7 @@ msgstr "キャッシュローダーの反復処理間の待機時間"
 msgid "Sleep time between cache manager iterations"
 msgstr "キャッシュマネージャーの反復処理間の待機時間"
 
-#: src/views/certificate/CertificateEditor.vue:213
+#: src/views/certificate/CertificateEditor.vue:211
 msgid "SSL Certificate Content"
 msgstr "SSL証明書の内容"
 
@@ -4457,15 +4496,15 @@ msgstr "SSL証明書ファイルはNginx設定ディレクトリの下にある
 msgid "SSL certificate file not found"
 msgstr "SSL証明書ファイルが見つかりません"
 
-#: src/views/certificate/CertificateEditor.vue:227
+#: src/views/certificate/CertificateEditor.vue:225
 msgid "SSL Certificate Key Content"
 msgstr "SSL証明書キーの内容"
 
-#: src/views/certificate/CertificateEditor.vue:192
+#: src/views/certificate/CertificateEditor.vue:190
 msgid "SSL Certificate Key Path"
 msgstr "SSL証明書キーパス"
 
-#: src/views/certificate/CertificateEditor.vue:177
+#: src/views/certificate/CertificateEditor.vue:175
 #: src/views/preference/tabs/ServerSettings.vue:37
 msgid "SSL Certificate Path"
 msgstr "SSL証明書のパス"
@@ -4698,7 +4737,7 @@ msgstr "同期ノード"
 msgid "Sync strategy"
 msgstr "同期戦略"
 
-#: src/views/certificate/CertificateEditor.vue:206
+#: src/views/certificate/CertificateEditor.vue:204
 msgid "Sync to"
 msgstr "同期先"
 
@@ -4769,11 +4808,11 @@ msgid ""
 "dashes, colons, and dots."
 msgstr "ICP番号には、文字、Unicode、数字、ハイフン、ダッシュ、コロン、およびドットのみを含める必要があります。"
 
-#: src/views/certificate/CertificateEditor.vue:216
+#: src/views/certificate/CertificateEditor.vue:214
 msgid "The input is not a SSL Certificate"
 msgstr "入力はSSL証明書ではありません"
 
-#: src/views/certificate/CertificateEditor.vue:230
+#: src/views/certificate/CertificateEditor.vue:228
 msgid "The input is not a SSL Certificate Key"
 msgstr "入力はSSL証明書キーではありません"
 
@@ -4804,11 +4843,11 @@ msgstr "ノード名には、文字、Unicode、数字、ハイフン、ダッ
 msgid "The parameter of server_name is required"
 msgstr "server_name のパラメーターが必要です"
 
-#: src/views/certificate/CertificateEditor.vue:181
+#: src/views/certificate/CertificateEditor.vue:179
 msgid "The path exists, but the file is not a certificate"
 msgstr "パスは存在しますが、ファイルは証明書ではありません"
 
-#: src/views/certificate/CertificateEditor.vue:196
+#: src/views/certificate/CertificateEditor.vue:194
 msgid "The path exists, but the file is not a private key"
 msgstr "パスは存在しますが、ファイルは秘密鍵ではありません"
 
@@ -4864,17 +4903,17 @@ msgstr ""
 "これらのコードは、パスワードと第二要素を失った場合にアカウントにアクセスするための最後の手段です。これらのコードが見つからない場合、アカウントにアクセス"
 "できなくなります。"
 
-#: src/views/certificate/CertificateEditor.vue:102
+#: src/views/certificate/CertificateEditor.vue:100
 msgid "This Auto Cert item is invalid, please remove it."
 msgstr "この自動証明書項目は無効です。削除してください。"
 
-#: src/views/certificate/CertificateEditor.vue:92
+#: src/views/certificate/CertificateEditor.vue:90
 msgid "This certificate is managed by Nginx UI"
 msgstr "この証明書はNginx UIによって管理されています"
 
-#: src/views/certificate/CertificateEditor.vue:165
-#: src/views/certificate/CertificateEditor.vue:179
-#: src/views/certificate/CertificateEditor.vue:194
+#: src/views/certificate/CertificateEditor.vue:163
+#: src/views/certificate/CertificateEditor.vue:177
+#: src/views/certificate/CertificateEditor.vue:192
 msgid "This field is required"
 msgstr "このフィールドは必須です"
 
@@ -5321,6 +5360,10 @@ msgstr "新しいリカバリーコードを生成するときは、新しいコ
 msgid "Whether to use a temporary path when writing temporary files"
 msgstr "一時ファイルを書き込む際に一時パスを使用するかどうか"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:107
+msgid "Wildcard Certificate"
+msgstr "ワイルドカード証明書"
+
 #: src/views/dashboard/components/ParamsOpt/PerformanceConfig.vue:48
 msgid "Worker Connections"
 msgstr "ワーカー接続数"

+ 82 - 42
app/src/language/ko_KR/app.po

@@ -179,6 +179,10 @@ msgstr "구성 추가"
 msgid "Add Directive Below"
 msgstr "아래에 지시문 추가"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:151
+msgid "Add Domain"
+msgstr "도메인 추가"
+
 #: src/components/NgxConfigEditor/LocationEditor.vue:131
 #: src/components/NgxConfigEditor/LocationEditor.vue:158
 msgid "Add Location"
@@ -226,6 +230,12 @@ msgstr "모두"
 msgid "All Recovery Codes Have Been Used"
 msgstr "모든 복구 코드가 사용되었습니다"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:156
+msgid ""
+"All selected subdomains must belong to the same DNS Provider, otherwise the "
+"certificate application will fail."
+msgstr "선택한 모든 서브도메인은 동일한 DNS 공급자에 속해야 합니다. 그렇지 않으면 인증서 신청이 실패합니다."
+
 #: src/views/preference/tabs/OpenAISettings.vue:32
 msgid "API Base Url"
 msgstr "API 기본 URL"
@@ -252,7 +262,7 @@ msgstr "앱"
 
 #: src/views/system/Upgrade.vue:195
 msgid "Arch"
-msgstr "\"아키텍처\""
+msgstr "아키텍처"
 
 #: src/views/preference/tabs/AuthSettings.vue:130
 msgid "Are you sure to delete this banned IP immediately?"
@@ -387,7 +397,7 @@ msgstr "%{name}에 대한 자동 갱신 비활성화됨"
 msgid "Auto-renewal enabled for %{name}"
 msgstr "%{name}에 대한 자동 갱신 활성화됨"
 
-#: src/views/certificate/components/RenewCert.vue:49
+#: src/views/certificate/components/RenewCert.vue:51
 #: src/views/site/site_edit/components/Cert/IssueCert.vue:84
 msgid "AutoCert is running, please wait..."
 msgstr "AutoCert가 실행 중입니다. 잠시 기다려 주세요..."
@@ -400,7 +410,7 @@ msgstr "\"AutoCert 실행 중...\""
 msgid "Automatic Restart"
 msgstr "\"자동 재시작\""
 
-#: src/views/certificate/CertificateEditor.vue:259
+#: src/views/certificate/CertificateEditor.vue:257
 #: src/views/config/components/ConfigLeftPanel.vue:273
 #: src/views/config/ConfigList.vue:112 src/views/config/ConfigList.vue:195
 #: src/views/nginx_log/NginxLog.vue:174
@@ -693,13 +703,17 @@ msgstr "인증서 갱신 성공"
 msgid "Certificate revoked successfully"
 msgstr "인증서가 성공적으로 취소되었습니다"
 
-#: src/views/certificate/CertificateEditor.vue:128
+#: src/views/certificate/CertificateEditor.vue:126
 #: src/views/site/site_edit/components/Cert/Cert.vue:58
 msgid "Certificate Status"
 msgid_plural "Certificates Status"
 msgstr[0] "인증서 상태"
 msgstr[1] "인증서 상태"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:104
+msgid "Certificate Type"
+msgstr "인증서 유형"
+
 #: src/routes/modules/certificates.ts:11
 #: src/views/certificate/CertificateList/Certificate.vue:19
 msgid "Certificates"
@@ -1167,6 +1181,14 @@ msgstr "사용자 정의 cron 표현식"
 msgid "Custom Directory"
 msgstr "사용자 지정 디렉토리"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:126
+msgid "Custom Domains"
+msgstr "사용자 정의 도메인"
+
+#: src/views/certificate/components/DNSIssueCertificate.vue:110
+msgid "Custom Domains Certificate"
+msgstr "사용자 정의 도메인 인증서"
+
 #: src/views/preference/tabs/NodeSettings.vue:19
 msgid ""
 "Customize the name of local node to be displayed in the environment "
@@ -1474,11 +1496,11 @@ msgid "Document"
 msgid_plural "Documents"
 msgstr[0] "문서"
 
-#: src/views/certificate/components/WildcardCertificate.vue:66
+#: src/views/certificate/components/DNSIssueCertificate.vue:116
 msgid "Domain"
 msgstr "도메인"
 
-#: src/views/certificate/CertificateEditor.vue:112
+#: src/views/certificate/CertificateEditor.vue:110
 msgid "Domains list is empty, try to reopen Auto Cert for %{config}"
 msgstr "도메인 목록이 비어 있습니다. %{config}에 대한 자동 인증서를 다시 열어보세요"
 
@@ -1680,6 +1702,14 @@ msgstr "Let's Encrypt로 웹사이트 암호화"
 msgid "End"
 msgstr "끝"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:135
+msgid "Enter domain name"
+msgstr "도메인 이름 입력"
+
+#: src/views/certificate/components/DNSIssueCertificate.vue:120
+msgid "Enter your domain"
+msgstr "도메인을 입력하세요"
+
 #: src/language/constants.ts:22
 msgid "Environment variables cleaned"
 msgstr "환경 변수가 정리되었습니다"
@@ -1754,7 +1784,7 @@ msgstr "외부 Docker 컨테이너"
 msgid "External Notify"
 msgstr "외부 알림"
 
-#: src/views/site/site_edit/components/Cert/ObtainCertLive.vue:80
+#: src/views/site/site_edit/components/Cert/ObtainCertLive.vue:81
 msgid "Fail to obtain certificate"
 msgstr "인증서 획득 실패"
 
@@ -2275,7 +2305,7 @@ msgid "Import"
 msgstr "가져오기"
 
 #: src/routes/modules/certificates.ts:46
-#: src/views/certificate/CertificateEditor.vue:85
+#: src/views/certificate/CertificateEditor.vue:83
 msgid "Import Certificate"
 msgstr "인증서 가져오기"
 
@@ -2416,12 +2446,12 @@ msgid "IP"
 msgstr "IP"
 
 #: src/views/certificate/CertificateList/Certificate.vue:37
-msgid "Issue wildcard certificate"
-msgstr "와일드카드 인증서 발급"
+msgid "Issue certificate"
+msgstr "인증서 발급"
 
-#: src/views/certificate/components/WildcardCertificate.vue:57
-msgid "Issue Wildcard Certificate"
-msgstr "와일드카드 인증서 발급"
+#: src/views/certificate/components/DNSIssueCertificate.vue:95
+msgid "Issue Certificate"
+msgstr "인증서 발급"
 
 #: src/language/constants.ts:20
 msgid "Issued certificate successfully"
@@ -2502,8 +2532,8 @@ msgstr "수정하지 않으려면 비워 두세요"
 msgid "Leave blank if you don't need this."
 msgstr "필요하지 않으면 비워 두세요."
 
-#: src/views/certificate/CertificateEditor.vue:223
-#: src/views/certificate/CertificateEditor.vue:237
+#: src/views/certificate/CertificateEditor.vue:221
+#: src/views/certificate/CertificateEditor.vue:235
 msgid "Leave blank will not change anything"
 msgstr "비워 두면 아무것도 변경되지 않습니다"
 
@@ -2590,7 +2620,7 @@ msgstr "위치"
 msgid "Locations"
 msgstr "위치들"
 
-#: src/views/certificate/CertificateEditor.vue:247
+#: src/views/certificate/CertificateEditor.vue:245
 msgid "Log"
 msgstr "로그"
 
@@ -2797,7 +2827,7 @@ msgid "Modify"
 msgstr "수정"
 
 #: src/routes/modules/certificates.ts:36
-#: src/views/certificate/CertificateEditor.vue:85
+#: src/views/certificate/CertificateEditor.vue:83
 msgid "Modify Certificate"
 msgstr "인증서 수정"
 
@@ -2832,7 +2862,7 @@ msgstr "여러 줄 지시문"
 #: src/components/NgxConfigEditor/NgxUpstream.vue:199
 #: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/certificate/ACMEUser.vue:11
-#: src/views/certificate/CertificateEditor.vue:162
+#: src/views/certificate/CertificateEditor.vue:160
 #: src/views/certificate/CertificateList/certColumns.tsx:9
 #: src/views/certificate/DNSCredential.vue:9
 #: src/views/config/components/ConfigRightPanel/Basic.vue:34
@@ -2892,7 +2922,7 @@ msgstr "새 경로"
 msgid "New version released"
 msgstr "새 버전 출시"
 
-#: src/views/certificate/components/WildcardCertificate.vue:89
+#: src/views/certificate/components/DNSIssueCertificate.vue:181
 #: src/views/install/components/InstallView.vue:96
 #: src/views/site/site_add/SiteAdd.vue:132
 #: src/views/site/site_edit/components/Cert/ObtainCert.vue:214
@@ -3467,6 +3497,10 @@ msgstr "생성하려는 패스키의 이름을 입력하고 아래의 확인 버
 msgid "Please enter a valid port range"
 msgstr "유효한 포트 범위를 입력하세요"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:74
+msgid "Please enter at least one domain"
+msgstr "최소 하나의 도메인을 입력해 주세요"
+
 #: src/components/TwoFA/Authorization.vue:85
 msgid "Please enter the OTP code:"
 msgstr "OTP 코드를 입력해 주세요:"
@@ -3792,6 +3826,7 @@ msgstr "리로딩 중"
 msgid "Reloading nginx"
 msgstr "Nginx 리로딩 중"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:144
 #: src/views/preference/tabs/AuthSettings.vue:137
 msgid "Remove"
 msgstr "제거"
@@ -3874,8 +3909,8 @@ msgstr "이름 변경 성공"
 msgid "Renamed successfully"
 msgstr "이름 변경 성공"
 
-#: src/views/certificate/components/RenewCert.vue:46
-#: src/views/certificate/components/RenewCert.vue:53
+#: src/views/certificate/components/RenewCert.vue:48
+#: src/views/certificate/components/RenewCert.vue:55
 msgid "Renew Certificate"
 msgstr "인증서 갱신"
 
@@ -3887,8 +3922,8 @@ msgstr "인증서 갱신 오류"
 msgid "Renew Certificate Success"
 msgstr "인증서 갱신 성공"
 
-#: src/views/certificate/components/RenewCert.vue:28
-#: src/views/certificate/components/WildcardCertificate.vue:46
+#: src/views/certificate/components/DNSIssueCertificate.vue:84
+#: src/views/certificate/components/RenewCert.vue:30
 msgid "Renew successfully"
 msgstr "성공적으로 갱신됨"
 
@@ -4144,7 +4179,7 @@ msgstr "토요일"
 
 #: src/components/ChatGPT/ChatMessage.vue:215
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:129
-#: src/language/curd.ts:18 src/views/certificate/CertificateEditor.vue:266
+#: src/language/curd.ts:18 src/views/certificate/CertificateEditor.vue:264
 #: src/views/config/components/ConfigLeftPanel.vue:282
 #: src/views/config/components/ConfigName.vue:59
 #: src/views/preference/components/AuthSettings/Passkey.vue:130
@@ -4199,7 +4234,7 @@ msgstr "스트림 %{name}을(를) %{node}에 저장하지 못했습니다"
 msgid "Save stream %{name} to %{node} successfully"
 msgstr "스트림 %{name}을(를) %{node}에 성공적으로 저장했습니다"
 
-#: src/language/curd.ts:35 src/views/certificate/CertificateEditor.vue:49
+#: src/language/curd.ts:35 src/views/certificate/store.ts:13
 #: src/views/preference/store/index.ts:118
 msgid "Save successfully"
 msgstr "성공적으로 저장되었습니다"
@@ -4307,6 +4342,10 @@ msgstr "보내기"
 msgid "Server"
 msgstr "서버"
 
+#: src/views/certificate/CertificateEditor.vue:55
+msgid "Server error"
+msgstr "서버 오류"
+
 #: src/views/dashboard/ServerAnalytic.vue:175
 msgid "Server Info"
 msgstr "서버 정보"
@@ -4441,7 +4480,7 @@ msgstr "캐시 로더 반복 사이의 대기 시간"
 msgid "Sleep time between cache manager iterations"
 msgstr "캐시 관리자 반복 간 대기 시간"
 
-#: src/views/certificate/CertificateEditor.vue:213
+#: src/views/certificate/CertificateEditor.vue:211
 msgid "SSL Certificate Content"
 msgstr "SSL 인증서 내용"
 
@@ -4453,15 +4492,15 @@ msgstr "SSL 인증서 파일은 Nginx 구성 디렉터리 아래에 있어야 
 msgid "SSL certificate file not found"
 msgstr "SSL 인증서 파일을 찾을 수 없음"
 
-#: src/views/certificate/CertificateEditor.vue:227
+#: src/views/certificate/CertificateEditor.vue:225
 msgid "SSL Certificate Key Content"
 msgstr "SSL 인증서키 콘텐츠"
 
-#: src/views/certificate/CertificateEditor.vue:192
+#: src/views/certificate/CertificateEditor.vue:190
 msgid "SSL Certificate Key Path"
 msgstr "SSL 인증서 키 경로"
 
-#: src/views/certificate/CertificateEditor.vue:177
+#: src/views/certificate/CertificateEditor.vue:175
 #: src/views/preference/tabs/ServerSettings.vue:37
 msgid "SSL Certificate Path"
 msgstr "SSL 인증서 경로"
@@ -4694,7 +4733,7 @@ msgstr "동기화 노드"
 msgid "Sync strategy"
 msgstr "동기화 전략"
 
-#: src/views/certificate/CertificateEditor.vue:206
+#: src/views/certificate/CertificateEditor.vue:204
 msgid "Sync to"
 msgstr "동기화 대상"
 
@@ -4765,11 +4804,11 @@ msgid ""
 "dashes, colons, and dots."
 msgstr "ICP 번호는 문자, 유니코드, 숫자, 하이픈, 대시, 콜론 및 점만 포함해야 합니다."
 
-#: src/views/certificate/CertificateEditor.vue:216
+#: src/views/certificate/CertificateEditor.vue:214
 msgid "The input is not a SSL Certificate"
 msgstr "입력이 SSL 인증서가 아닙니다"
 
-#: src/views/certificate/CertificateEditor.vue:230
+#: src/views/certificate/CertificateEditor.vue:228
 msgid "The input is not a SSL Certificate Key"
 msgstr "입력한 내용이 SSL 인증서 키가 아닙니다"
 
@@ -4800,11 +4839,11 @@ msgstr "노드 이름에는 문자, 유니코드, 숫자, 하이픈, 대시, 콜
 msgid "The parameter of server_name is required"
 msgstr "server_name 매개변수가 필요합니다"
 
-#: src/views/certificate/CertificateEditor.vue:181
+#: src/views/certificate/CertificateEditor.vue:179
 msgid "The path exists, but the file is not a certificate"
 msgstr "경로는 존재하지만, 파일이 인증서가 아닙니다"
 
-#: src/views/certificate/CertificateEditor.vue:196
+#: src/views/certificate/CertificateEditor.vue:194
 msgid "The path exists, but the file is not a private key"
 msgstr "경로는 존재하지만 파일은 개인 키가 아닙니다"
 
@@ -4860,17 +4899,17 @@ msgstr ""
 "이 코드들은 비밀번호와 두 번째 요소를 잃어버린 경우 계정에 접근할 수 있는 최후의 수단입니다. 이 코드들을 찾을 수 없다면 계정에 "
 "대한 접근 권한을 잃게 됩니다."
 
-#: src/views/certificate/CertificateEditor.vue:102
+#: src/views/certificate/CertificateEditor.vue:100
 msgid "This Auto Cert item is invalid, please remove it."
 msgstr "이 자동 인증 항목이 유효하지 않습니다. 제거해주세요."
 
-#: src/views/certificate/CertificateEditor.vue:92
+#: src/views/certificate/CertificateEditor.vue:90
 msgid "This certificate is managed by Nginx UI"
 msgstr "이 인증서는 Nginx UI에서 관리됩니다"
 
-#: src/views/certificate/CertificateEditor.vue:165
-#: src/views/certificate/CertificateEditor.vue:179
-#: src/views/certificate/CertificateEditor.vue:194
+#: src/views/certificate/CertificateEditor.vue:163
+#: src/views/certificate/CertificateEditor.vue:177
+#: src/views/certificate/CertificateEditor.vue:192
 msgid "This field is required"
 msgstr "이 필드는 필수입니다"
 
@@ -5315,6 +5354,10 @@ msgstr "새로운 복구 코드를 생성할 때는 반드시 새 코드를 다
 msgid "Whether to use a temporary path when writing temporary files"
 msgstr "임시 파일을 작성할 때 임시 경로를 사용할지 여부"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:107
+msgid "Wildcard Certificate"
+msgstr "와일드카드 인증서"
+
 #: src/views/dashboard/components/ParamsOpt/PerformanceConfig.vue:48
 msgid "Worker Connections"
 msgstr "작업자 연결"
@@ -5616,9 +5659,6 @@ msgstr "귀하의 패스키"
 #~ msgid "Incorrect username or password"
 #~ msgstr "사용자 이름 또는 비밀번호가 올바르지 않습니다"
 
-#~ msgid "Server error"
-#~ msgstr "서버 오류"
-
 #, fuzzy
 #~ msgid ""
 #~ "Rename %{orig_path} to %{new_path} on %{env_name} failed, please upgrade "

+ 77 - 36
app/src/language/messages.pot

@@ -170,6 +170,10 @@ msgstr ""
 msgid "Add Directive Below"
 msgstr ""
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:151
+msgid "Add Domain"
+msgstr ""
+
 #: src/components/NgxConfigEditor/LocationEditor.vue:131
 #: src/components/NgxConfigEditor/LocationEditor.vue:158
 msgid "Add Location"
@@ -218,6 +222,10 @@ msgstr ""
 msgid "All Recovery Codes Have Been Used"
 msgstr ""
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:156
+msgid "All selected subdomains must belong to the same DNS Provider, otherwise the certificate application will fail."
+msgstr ""
+
 #: src/views/preference/tabs/OpenAISettings.vue:32
 msgid "API Base Url"
 msgstr ""
@@ -380,7 +388,7 @@ msgstr ""
 msgid "Auto-renewal enabled for %{name}"
 msgstr ""
 
-#: src/views/certificate/components/RenewCert.vue:49
+#: src/views/certificate/components/RenewCert.vue:51
 #: src/views/site/site_edit/components/Cert/IssueCert.vue:84
 msgid "AutoCert is running, please wait..."
 msgstr ""
@@ -393,7 +401,7 @@ msgstr ""
 msgid "Automatic Restart"
 msgstr ""
 
-#: src/views/certificate/CertificateEditor.vue:259
+#: src/views/certificate/CertificateEditor.vue:257
 #: src/views/config/components/ConfigLeftPanel.vue:273
 #: src/views/config/ConfigList.vue:112
 #: src/views/config/ConfigList.vue:195
@@ -684,13 +692,17 @@ msgstr ""
 msgid "Certificate revoked successfully"
 msgstr ""
 
-#: src/views/certificate/CertificateEditor.vue:128
+#: src/views/certificate/CertificateEditor.vue:126
 #: src/views/site/site_edit/components/Cert/Cert.vue:58
 msgid "Certificate Status"
 msgid_plural "Certificates Status"
 msgstr[0] ""
 msgstr[1] ""
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:104
+msgid "Certificate Type"
+msgstr ""
+
 #: src/routes/modules/certificates.ts:11
 #: src/views/certificate/CertificateList/Certificate.vue:19
 msgid "Certificates"
@@ -1110,6 +1122,14 @@ msgstr ""
 msgid "Custom Directory"
 msgstr ""
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:126
+msgid "Custom Domains"
+msgstr ""
+
+#: src/views/certificate/components/DNSIssueCertificate.vue:110
+msgid "Custom Domains Certificate"
+msgstr ""
+
 #: src/views/preference/tabs/NodeSettings.vue:19
 msgid "Customize the name of local node to be displayed in the environment indicator."
 msgstr ""
@@ -1423,11 +1443,11 @@ msgid_plural "Documents"
 msgstr[0] ""
 msgstr[1] ""
 
-#: src/views/certificate/components/WildcardCertificate.vue:66
+#: src/views/certificate/components/DNSIssueCertificate.vue:116
 msgid "Domain"
 msgstr ""
 
-#: src/views/certificate/CertificateEditor.vue:112
+#: src/views/certificate/CertificateEditor.vue:110
 msgid "Domains list is empty, try to reopen Auto Cert for %{config}"
 msgstr ""
 
@@ -1626,6 +1646,14 @@ msgstr ""
 msgid "End"
 msgstr ""
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:135
+msgid "Enter domain name"
+msgstr ""
+
+#: src/views/certificate/components/DNSIssueCertificate.vue:120
+msgid "Enter your domain"
+msgstr ""
+
 #: src/language/constants.ts:22
 msgid "Environment variables cleaned"
 msgstr ""
@@ -1701,7 +1729,7 @@ msgstr ""
 msgid "External Notify"
 msgstr ""
 
-#: src/views/site/site_edit/components/Cert/ObtainCertLive.vue:80
+#: src/views/site/site_edit/components/Cert/ObtainCertLive.vue:81
 msgid "Fail to obtain certificate"
 msgstr ""
 
@@ -2215,7 +2243,7 @@ msgid "Import"
 msgstr ""
 
 #: src/routes/modules/certificates.ts:46
-#: src/views/certificate/CertificateEditor.vue:85
+#: src/views/certificate/CertificateEditor.vue:83
 msgid "Import Certificate"
 msgstr ""
 
@@ -2353,11 +2381,11 @@ msgid "IP"
 msgstr ""
 
 #: src/views/certificate/CertificateList/Certificate.vue:37
-msgid "Issue wildcard certificate"
+msgid "Issue certificate"
 msgstr ""
 
-#: src/views/certificate/components/WildcardCertificate.vue:57
-msgid "Issue Wildcard Certificate"
+#: src/views/certificate/components/DNSIssueCertificate.vue:95
+msgid "Issue Certificate"
 msgstr ""
 
 #: src/language/constants.ts:20
@@ -2437,8 +2465,8 @@ msgstr ""
 msgid "Leave blank if you don't need this."
 msgstr ""
 
-#: src/views/certificate/CertificateEditor.vue:223
-#: src/views/certificate/CertificateEditor.vue:237
+#: src/views/certificate/CertificateEditor.vue:221
+#: src/views/certificate/CertificateEditor.vue:235
 msgid "Leave blank will not change anything"
 msgstr ""
 
@@ -2525,7 +2553,7 @@ msgstr ""
 msgid "Locations"
 msgstr ""
 
-#: src/views/certificate/CertificateEditor.vue:247
+#: src/views/certificate/CertificateEditor.vue:245
 msgid "Log"
 msgstr ""
 
@@ -2722,7 +2750,7 @@ msgid "Modify"
 msgstr ""
 
 #: src/routes/modules/certificates.ts:36
-#: src/views/certificate/CertificateEditor.vue:85
+#: src/views/certificate/CertificateEditor.vue:83
 msgid "Modify Certificate"
 msgstr ""
 
@@ -2757,7 +2785,7 @@ msgstr ""
 #: src/components/NgxConfigEditor/NgxUpstream.vue:199
 #: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/certificate/ACMEUser.vue:11
-#: src/views/certificate/CertificateEditor.vue:162
+#: src/views/certificate/CertificateEditor.vue:160
 #: src/views/certificate/CertificateList/certColumns.tsx:9
 #: src/views/certificate/DNSCredential.vue:9
 #: src/views/config/components/ConfigRightPanel/Basic.vue:34
@@ -2817,7 +2845,7 @@ msgstr ""
 msgid "New version released"
 msgstr ""
 
-#: src/views/certificate/components/WildcardCertificate.vue:89
+#: src/views/certificate/components/DNSIssueCertificate.vue:181
 #: src/views/install/components/InstallView.vue:96
 #: src/views/site/site_add/SiteAdd.vue:132
 #: src/views/site/site_edit/components/Cert/ObtainCert.vue:214
@@ -3388,6 +3416,10 @@ msgstr ""
 msgid "Please enter a valid port range"
 msgstr ""
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:74
+msgid "Please enter at least one domain"
+msgstr ""
+
 #: src/components/TwoFA/Authorization.vue:85
 msgid "Please enter the OTP code:"
 msgstr ""
@@ -3705,6 +3737,7 @@ msgstr ""
 msgid "Reloading nginx"
 msgstr ""
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:144
 #: src/views/preference/tabs/AuthSettings.vue:137
 msgid "Remove"
 msgstr ""
@@ -3789,8 +3822,8 @@ msgstr ""
 msgid "Renamed successfully"
 msgstr ""
 
-#: src/views/certificate/components/RenewCert.vue:46
-#: src/views/certificate/components/RenewCert.vue:53
+#: src/views/certificate/components/RenewCert.vue:48
+#: src/views/certificate/components/RenewCert.vue:55
 msgid "Renew Certificate"
 msgstr ""
 
@@ -3802,8 +3835,8 @@ msgstr ""
 msgid "Renew Certificate Success"
 msgstr ""
 
-#: src/views/certificate/components/RenewCert.vue:28
-#: src/views/certificate/components/WildcardCertificate.vue:46
+#: src/views/certificate/components/DNSIssueCertificate.vue:84
+#: src/views/certificate/components/RenewCert.vue:30
 msgid "Renew successfully"
 msgstr ""
 
@@ -4054,7 +4087,7 @@ msgstr ""
 #: src/components/ChatGPT/ChatMessage.vue:215
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:129
 #: src/language/curd.ts:18
-#: src/views/certificate/CertificateEditor.vue:266
+#: src/views/certificate/CertificateEditor.vue:264
 #: src/views/config/components/ConfigLeftPanel.vue:282
 #: src/views/config/components/ConfigName.vue:59
 #: src/views/preference/components/AuthSettings/Passkey.vue:130
@@ -4110,7 +4143,7 @@ msgid "Save stream %{name} to %{node} successfully"
 msgstr ""
 
 #: src/language/curd.ts:35
-#: src/views/certificate/CertificateEditor.vue:49
+#: src/views/certificate/store.ts:13
 #: src/views/preference/store/index.ts:118
 msgid "Save successfully"
 msgstr ""
@@ -4222,6 +4255,10 @@ msgstr ""
 msgid "Server"
 msgstr ""
 
+#: src/views/certificate/CertificateEditor.vue:55
+msgid "Server error"
+msgstr ""
+
 #: src/views/dashboard/ServerAnalytic.vue:175
 msgid "Server Info"
 msgstr ""
@@ -4346,7 +4383,7 @@ msgstr ""
 msgid "Sleep time between cache manager iterations"
 msgstr ""
 
-#: src/views/certificate/CertificateEditor.vue:213
+#: src/views/certificate/CertificateEditor.vue:211
 msgid "SSL Certificate Content"
 msgstr ""
 
@@ -4358,15 +4395,15 @@ msgstr ""
 msgid "SSL certificate file not found"
 msgstr ""
 
-#: src/views/certificate/CertificateEditor.vue:227
+#: src/views/certificate/CertificateEditor.vue:225
 msgid "SSL Certificate Key Content"
 msgstr ""
 
-#: src/views/certificate/CertificateEditor.vue:192
+#: src/views/certificate/CertificateEditor.vue:190
 msgid "SSL Certificate Key Path"
 msgstr ""
 
-#: src/views/certificate/CertificateEditor.vue:177
+#: src/views/certificate/CertificateEditor.vue:175
 #: src/views/preference/tabs/ServerSettings.vue:37
 msgid "SSL Certificate Path"
 msgstr ""
@@ -4592,7 +4629,7 @@ msgstr ""
 msgid "Sync strategy"
 msgstr ""
 
-#: src/views/certificate/CertificateEditor.vue:206
+#: src/views/certificate/CertificateEditor.vue:204
 msgid "Sync to"
 msgstr ""
 
@@ -4659,11 +4696,11 @@ msgstr ""
 msgid "The ICP Number should only contain letters, unicode, numbers, hyphens, dashes, colons, and dots."
 msgstr ""
 
-#: src/views/certificate/CertificateEditor.vue:216
+#: src/views/certificate/CertificateEditor.vue:214
 msgid "The input is not a SSL Certificate"
 msgstr ""
 
-#: src/views/certificate/CertificateEditor.vue:230
+#: src/views/certificate/CertificateEditor.vue:228
 msgid "The input is not a SSL Certificate Key"
 msgstr ""
 
@@ -4688,11 +4725,11 @@ msgstr ""
 msgid "The parameter of server_name is required"
 msgstr ""
 
-#: src/views/certificate/CertificateEditor.vue:181
+#: src/views/certificate/CertificateEditor.vue:179
 msgid "The path exists, but the file is not a certificate"
 msgstr ""
 
-#: src/views/certificate/CertificateEditor.vue:196
+#: src/views/certificate/CertificateEditor.vue:194
 msgid "The path exists, but the file is not a private key"
 msgstr ""
 
@@ -4734,17 +4771,17 @@ msgstr ""
 msgid "These codes are the last resort for accessing your account in case you lose your password and second factors. If you cannot find these codes, you will lose access to your account."
 msgstr ""
 
-#: src/views/certificate/CertificateEditor.vue:102
+#: src/views/certificate/CertificateEditor.vue:100
 msgid "This Auto Cert item is invalid, please remove it."
 msgstr ""
 
-#: src/views/certificate/CertificateEditor.vue:92
+#: src/views/certificate/CertificateEditor.vue:90
 msgid "This certificate is managed by Nginx UI"
 msgstr ""
 
-#: src/views/certificate/CertificateEditor.vue:165
-#: src/views/certificate/CertificateEditor.vue:179
-#: src/views/certificate/CertificateEditor.vue:194
+#: src/views/certificate/CertificateEditor.vue:163
+#: src/views/certificate/CertificateEditor.vue:177
+#: src/views/certificate/CertificateEditor.vue:192
 msgid "This field is required"
 msgstr ""
 
@@ -5149,6 +5186,10 @@ msgstr ""
 msgid "Whether to use a temporary path when writing temporary files"
 msgstr ""
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:107
+msgid "Wildcard Certificate"
+msgstr ""
+
 #: src/views/dashboard/components/ParamsOpt/PerformanceConfig.vue:48
 msgid "Worker Connections"
 msgstr ""

+ 84 - 42
app/src/language/pt_PT/app.po

@@ -181,6 +181,10 @@ msgstr "Adicionar Configuração"
 msgid "Add Directive Below"
 msgstr "Adicionar Directiva Abaixo"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:151
+msgid "Add Domain"
+msgstr "Adicionar domínio"
+
 #: src/components/NgxConfigEditor/LocationEditor.vue:131
 #: src/components/NgxConfigEditor/LocationEditor.vue:158
 msgid "Add Location"
@@ -230,6 +234,14 @@ msgstr "Todos"
 msgid "All Recovery Codes Have Been Used"
 msgstr "Todos os Códigos de Recuperação Foram Utilizados"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:156
+msgid ""
+"All selected subdomains must belong to the same DNS Provider, otherwise the "
+"certificate application will fail."
+msgstr ""
+"Todos os subdomínios selecionados devem pertencer ao mesmo Fornecedor de "
+"DNS, caso contrário, o pedido de certificado falhará."
+
 #: src/views/preference/tabs/OpenAISettings.vue:32
 msgid "API Base Url"
 msgstr "Url Base da API"
@@ -256,7 +268,7 @@ msgstr "Aplicação"
 
 #: src/views/system/Upgrade.vue:195
 msgid "Arch"
-msgstr "\"Arquitetura\""
+msgstr "Arquitetura"
 
 #: src/views/preference/tabs/AuthSettings.vue:130
 msgid "Are you sure to delete this banned IP immediately?"
@@ -395,7 +407,7 @@ msgstr "Renovação automatica desactivada para %{name}"
 msgid "Auto-renewal enabled for %{name}"
 msgstr "Renovação automática activada para %{name}"
 
-#: src/views/certificate/components/RenewCert.vue:49
+#: src/views/certificate/components/RenewCert.vue:51
 #: src/views/site/site_edit/components/Cert/IssueCert.vue:84
 msgid "AutoCert is running, please wait..."
 msgstr "O AutoCert está em execução, aguarde..."
@@ -408,7 +420,7 @@ msgstr "\"AutoCert está em execução...\""
 msgid "Automatic Restart"
 msgstr "Reinício Automático"
 
-#: src/views/certificate/CertificateEditor.vue:259
+#: src/views/certificate/CertificateEditor.vue:257
 #: src/views/config/components/ConfigLeftPanel.vue:273
 #: src/views/config/ConfigList.vue:112 src/views/config/ConfigList.vue:195
 #: src/views/nginx_log/NginxLog.vue:174
@@ -709,13 +721,17 @@ msgstr "Certificado renovado com sucesso"
 msgid "Certificate revoked successfully"
 msgstr "Certificado revogado com sucesso"
 
-#: src/views/certificate/CertificateEditor.vue:128
+#: src/views/certificate/CertificateEditor.vue:126
 #: src/views/site/site_edit/components/Cert/Cert.vue:58
 msgid "Certificate Status"
 msgid_plural "Certificates Status"
 msgstr[0] "Estado do Certificado"
 msgstr[1] "Estado dos Certificados"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:104
+msgid "Certificate Type"
+msgstr "Tipo de certificado"
+
 #: src/routes/modules/certificates.ts:11
 #: src/views/certificate/CertificateList/Certificate.vue:19
 msgid "Certificates"
@@ -1206,6 +1222,14 @@ msgstr "Expressão cron personalizada"
 msgid "Custom Directory"
 msgstr "Diretório personalizado"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:126
+msgid "Custom Domains"
+msgstr "Domínios personalizados"
+
+#: src/views/certificate/components/DNSIssueCertificate.vue:110
+msgid "Custom Domains Certificate"
+msgstr "Certificado de Domínios Personalizados"
+
 #: src/views/preference/tabs/NodeSettings.vue:19
 msgid ""
 "Customize the name of local node to be displayed in the environment "
@@ -1515,11 +1539,11 @@ msgid "Document"
 msgid_plural "Documents"
 msgstr[0] "Documento"
 
-#: src/views/certificate/components/WildcardCertificate.vue:66
+#: src/views/certificate/components/DNSIssueCertificate.vue:116
 msgid "Domain"
 msgstr "Domínio"
 
-#: src/views/certificate/CertificateEditor.vue:112
+#: src/views/certificate/CertificateEditor.vue:110
 msgid "Domains list is empty, try to reopen Auto Cert for %{config}"
 msgstr "A lista de domínios está vazia, tente reabrir o Auto Cert para %{config}"
 
@@ -1722,6 +1746,14 @@ msgstr "Encriptar website com Let's Encrypt"
 msgid "End"
 msgstr "Fim"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:135
+msgid "Enter domain name"
+msgstr "Inserir nome de domínio"
+
+#: src/views/certificate/components/DNSIssueCertificate.vue:120
+msgid "Enter your domain"
+msgstr "Insira o seu domínio"
+
 #: src/language/constants.ts:22
 msgid "Environment variables cleaned"
 msgstr "Variáveis de ambiente limpas"
@@ -1796,7 +1828,7 @@ msgstr "Contentor Docker externo"
 msgid "External Notify"
 msgstr "Notificação Externa"
 
-#: src/views/site/site_edit/components/Cert/ObtainCertLive.vue:80
+#: src/views/site/site_edit/components/Cert/ObtainCertLive.vue:81
 msgid "Fail to obtain certificate"
 msgstr "Obtenção de Certificado Falhou"
 
@@ -2326,7 +2358,7 @@ msgid "Import"
 msgstr "Importar"
 
 #: src/routes/modules/certificates.ts:46
-#: src/views/certificate/CertificateEditor.vue:85
+#: src/views/certificate/CertificateEditor.vue:83
 msgid "Import Certificate"
 msgstr "Importar Certificados"
 
@@ -2471,12 +2503,12 @@ msgid "IP"
 msgstr "IP"
 
 #: src/views/certificate/CertificateList/Certificate.vue:37
-msgid "Issue wildcard certificate"
-msgstr "Emitir certificado wildcard"
+msgid "Issue certificate"
+msgstr "Emitir certificado"
 
-#: src/views/certificate/components/WildcardCertificate.vue:57
-msgid "Issue Wildcard Certificate"
-msgstr "Emitir Certificado Wildcard"
+#: src/views/certificate/components/DNSIssueCertificate.vue:95
+msgid "Issue Certificate"
+msgstr "Emitir certificado"
 
 #: src/language/constants.ts:20
 msgid "Issued certificate successfully"
@@ -2559,8 +2591,8 @@ msgstr "Deixar em branco se não quiser modificar"
 msgid "Leave blank if you don't need this."
 msgstr "Deixe em branco se não precisar disto."
 
-#: src/views/certificate/CertificateEditor.vue:223
-#: src/views/certificate/CertificateEditor.vue:237
+#: src/views/certificate/CertificateEditor.vue:221
+#: src/views/certificate/CertificateEditor.vue:235
 msgid "Leave blank will not change anything"
 msgstr "Deixar em branco não vai mudar nada"
 
@@ -2647,7 +2679,7 @@ msgstr "Localização"
 msgid "Locations"
 msgstr "Localizações"
 
-#: src/views/certificate/CertificateEditor.vue:247
+#: src/views/certificate/CertificateEditor.vue:245
 msgid "Log"
 msgstr "Log"
 
@@ -2861,7 +2893,7 @@ msgid "Modify"
 msgstr "Modificar"
 
 #: src/routes/modules/certificates.ts:36
-#: src/views/certificate/CertificateEditor.vue:85
+#: src/views/certificate/CertificateEditor.vue:83
 msgid "Modify Certificate"
 msgstr "Modificar Certificado"
 
@@ -2896,7 +2928,7 @@ msgstr "Diretiva Multilinha"
 #: src/components/NgxConfigEditor/NgxUpstream.vue:199
 #: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/certificate/ACMEUser.vue:11
-#: src/views/certificate/CertificateEditor.vue:162
+#: src/views/certificate/CertificateEditor.vue:160
 #: src/views/certificate/CertificateList/certColumns.tsx:9
 #: src/views/certificate/DNSCredential.vue:9
 #: src/views/config/components/ConfigRightPanel/Basic.vue:34
@@ -2956,7 +2988,7 @@ msgstr "Novo Caminho"
 msgid "New version released"
 msgstr "Nova versão lançada"
 
-#: src/views/certificate/components/WildcardCertificate.vue:89
+#: src/views/certificate/components/DNSIssueCertificate.vue:181
 #: src/views/install/components/InstallView.vue:96
 #: src/views/site/site_add/SiteAdd.vue:132
 #: src/views/site/site_edit/components/Cert/ObtainCert.vue:214
@@ -3545,6 +3577,10 @@ msgstr ""
 msgid "Please enter a valid port range"
 msgstr "Por favor, insira um intervalo de portas válido"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:74
+msgid "Please enter at least one domain"
+msgstr "Por favor, insira pelo menos um domínio"
+
 #: src/components/TwoFA/Authorization.vue:85
 msgid "Please enter the OTP code:"
 msgstr "Por favor, insira o código OTP:"
@@ -3886,6 +3922,7 @@ msgstr "Recarregando"
 msgid "Reloading nginx"
 msgstr "Recarregando Nginx"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:144
 #: src/views/preference/tabs/AuthSettings.vue:137
 msgid "Remove"
 msgstr "Remover"
@@ -3970,8 +4007,8 @@ msgstr "Renomeado com sucesso"
 msgid "Renamed successfully"
 msgstr "Renomeado com sucesso"
 
-#: src/views/certificate/components/RenewCert.vue:46
-#: src/views/certificate/components/RenewCert.vue:53
+#: src/views/certificate/components/RenewCert.vue:48
+#: src/views/certificate/components/RenewCert.vue:55
 msgid "Renew Certificate"
 msgstr "Renovar Certificado"
 
@@ -3983,8 +4020,8 @@ msgstr "Erro ao Renovar Certificado"
 msgid "Renew Certificate Success"
 msgstr "Certificado Renovado com Sucesso"
 
-#: src/views/certificate/components/RenewCert.vue:28
-#: src/views/certificate/components/WildcardCertificate.vue:46
+#: src/views/certificate/components/DNSIssueCertificate.vue:84
+#: src/views/certificate/components/RenewCert.vue:30
 msgid "Renew successfully"
 msgstr "Renovado com Sucesso"
 
@@ -4243,7 +4280,7 @@ msgstr "Sábado"
 
 #: src/components/ChatGPT/ChatMessage.vue:215
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:129
-#: src/language/curd.ts:18 src/views/certificate/CertificateEditor.vue:266
+#: src/language/curd.ts:18 src/views/certificate/CertificateEditor.vue:264
 #: src/views/config/components/ConfigLeftPanel.vue:282
 #: src/views/config/components/ConfigName.vue:59
 #: src/views/preference/components/AuthSettings/Passkey.vue:130
@@ -4298,7 +4335,7 @@ msgstr "Falha ao guardar o fluxo %{name} em %{node}"
 msgid "Save stream %{name} to %{node} successfully"
 msgstr "Fluxo %{name} guardado em %{node} com sucesso"
 
-#: src/language/curd.ts:35 src/views/certificate/CertificateEditor.vue:49
+#: src/language/curd.ts:35 src/views/certificate/store.ts:13
 #: src/views/preference/store/index.ts:118
 msgid "Save successfully"
 msgstr "Salvo com sucesso"
@@ -4410,6 +4447,10 @@ msgstr "Enviar"
 msgid "Server"
 msgstr "Servidor"
 
+#: src/views/certificate/CertificateEditor.vue:55
+msgid "Server error"
+msgstr "Erro do servidor"
+
 #: src/views/dashboard/ServerAnalytic.vue:175
 msgid "Server Info"
 msgstr "Informação do Servidor"
@@ -4546,7 +4587,7 @@ msgstr "Tempo de espera entre iterações do carregador de cache"
 msgid "Sleep time between cache manager iterations"
 msgstr "Tempo de espera entre iterações do gestor de cache"
 
-#: src/views/certificate/CertificateEditor.vue:213
+#: src/views/certificate/CertificateEditor.vue:211
 msgid "SSL Certificate Content"
 msgstr "Conteúdo do Certificado SSL"
 
@@ -4560,15 +4601,15 @@ msgstr ""
 msgid "SSL certificate file not found"
 msgstr "Ficheiro de certificado SSL não encontrado"
 
-#: src/views/certificate/CertificateEditor.vue:227
+#: src/views/certificate/CertificateEditor.vue:225
 msgid "SSL Certificate Key Content"
 msgstr "Conteúdo da Chave do Certificado SSL"
 
-#: src/views/certificate/CertificateEditor.vue:192
+#: src/views/certificate/CertificateEditor.vue:190
 msgid "SSL Certificate Key Path"
 msgstr "Caminho para a Chave do Certificado SSL"
 
-#: src/views/certificate/CertificateEditor.vue:177
+#: src/views/certificate/CertificateEditor.vue:175
 #: src/views/preference/tabs/ServerSettings.vue:37
 msgid "SSL Certificate Path"
 msgstr "Caminho para o Certificado SSL"
@@ -4807,7 +4848,7 @@ msgstr "Nós de sincronização"
 msgid "Sync strategy"
 msgstr "Estratégia de sincronização"
 
-#: src/views/certificate/CertificateEditor.vue:206
+#: src/views/certificate/CertificateEditor.vue:204
 msgid "Sync to"
 msgstr "Sincronizar para"
 
@@ -4883,11 +4924,11 @@ msgstr ""
 "O número ICP deve conter apenas letras, unicode, números, hífens, traços, "
 "dois pontos e pontos."
 
-#: src/views/certificate/CertificateEditor.vue:216
+#: src/views/certificate/CertificateEditor.vue:214
 msgid "The input is not a SSL Certificate"
 msgstr "A valor introduzido não é um certificado SSL"
 
-#: src/views/certificate/CertificateEditor.vue:230
+#: src/views/certificate/CertificateEditor.vue:228
 msgid "The input is not a SSL Certificate Key"
 msgstr "O valor introduzido não é uma Chave de Certificado SSL"
 
@@ -4926,11 +4967,11 @@ msgstr ""
 msgid "The parameter of server_name is required"
 msgstr "O parâmetro de server_name é obrigatório"
 
-#: src/views/certificate/CertificateEditor.vue:181
+#: src/views/certificate/CertificateEditor.vue:179
 msgid "The path exists, but the file is not a certificate"
 msgstr "O caminho existe, mas o ficheiro não é um certificado"
 
-#: src/views/certificate/CertificateEditor.vue:196
+#: src/views/certificate/CertificateEditor.vue:194
 msgid "The path exists, but the file is not a private key"
 msgstr "O caminho existe, mas o ficheiro não é uma chave privada"
 
@@ -4992,17 +5033,17 @@ msgstr ""
 "palavra-passe e os segundos fatores. Se não conseguir encontrar estes "
 "códigos, perderá o acesso à sua conta."
 
-#: src/views/certificate/CertificateEditor.vue:102
+#: src/views/certificate/CertificateEditor.vue:100
 msgid "This Auto Cert item is invalid, please remove it."
 msgstr "Este item Auto Cert é inválido, por favor remova-o."
 
-#: src/views/certificate/CertificateEditor.vue:92
+#: src/views/certificate/CertificateEditor.vue:90
 msgid "This certificate is managed by Nginx UI"
 msgstr "Este certificado é gerido pelo Nginx UI"
 
-#: src/views/certificate/CertificateEditor.vue:165
-#: src/views/certificate/CertificateEditor.vue:179
-#: src/views/certificate/CertificateEditor.vue:194
+#: src/views/certificate/CertificateEditor.vue:163
+#: src/views/certificate/CertificateEditor.vue:177
+#: src/views/certificate/CertificateEditor.vue:192
 msgid "This field is required"
 msgstr "Este campo é mantatório"
 
@@ -5481,6 +5522,10 @@ msgstr ""
 msgid "Whether to use a temporary path when writing temporary files"
 msgstr "Se deve usar um caminho temporário ao escrever ficheiros temporários"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:107
+msgid "Wildcard Certificate"
+msgstr "Certificado curinga"
+
 #: src/views/dashboard/components/ParamsOpt/PerformanceConfig.vue:48
 msgid "Worker Connections"
 msgstr "Conexões de trabalhador"
@@ -5740,9 +5785,6 @@ msgstr "As suas chaves de acesso"
 #~ "Erro ao mudar o nome de %{orig_path} para %{new_path} no %{env_name}, por "
 #~ "favor actualize a versão do Nginx UI para a mais recente"
 
-#~ msgid "Server error"
-#~ msgstr "Erro do servidor"
-
 #~ msgid "Server Name"
 #~ msgstr "Nome do Servidor"
 

+ 84 - 42
app/src/language/ru_RU/app.po

@@ -185,6 +185,10 @@ msgstr "Добавить конфигурацию"
 msgid "Add Directive Below"
 msgstr "Добавить директиву ниже"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:151
+msgid "Add Domain"
+msgstr "Добавить домен"
+
 #: src/components/NgxConfigEditor/LocationEditor.vue:131
 #: src/components/NgxConfigEditor/LocationEditor.vue:158
 msgid "Add Location"
@@ -232,6 +236,14 @@ msgstr "Все"
 msgid "All Recovery Codes Have Been Used"
 msgstr "Все коды восстановления были использованы"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:156
+msgid ""
+"All selected subdomains must belong to the same DNS Provider, otherwise the "
+"certificate application will fail."
+msgstr ""
+"Все выбранные поддомены должны принадлежать одному и тому же "
+"DNS-провайдеру, иначе запрос сертификата завершится неудачей."
+
 #: src/views/preference/tabs/OpenAISettings.vue:32
 msgid "API Base Url"
 msgstr "Базовый URL API"
@@ -258,7 +270,7 @@ msgstr "Приложение"
 
 #: src/views/system/Upgrade.vue:195
 msgid "Arch"
-msgstr "\"Архитектура\""
+msgstr "Архитектура"
 
 #: src/views/preference/tabs/AuthSettings.vue:130
 msgid "Are you sure to delete this banned IP immediately?"
@@ -397,7 +409,7 @@ msgstr "Автообновление отключено для %{name}"
 msgid "Auto-renewal enabled for %{name}"
 msgstr "Автообновление включено для %{name}"
 
-#: src/views/certificate/components/RenewCert.vue:49
+#: src/views/certificate/components/RenewCert.vue:51
 #: src/views/site/site_edit/components/Cert/IssueCert.vue:84
 msgid "AutoCert is running, please wait..."
 msgstr "AutoCert выполняется, пожалуйста, подождите..."
@@ -410,7 +422,7 @@ msgstr "\"AutoCert выполняется...\""
 msgid "Automatic Restart"
 msgstr "\"Автоматическая перезагрузка\""
 
-#: src/views/certificate/CertificateEditor.vue:259
+#: src/views/certificate/CertificateEditor.vue:257
 #: src/views/config/components/ConfigLeftPanel.vue:273
 #: src/views/config/ConfigList.vue:112 src/views/config/ConfigList.vue:195
 #: src/views/nginx_log/NginxLog.vue:174
@@ -715,13 +727,17 @@ msgstr "Сертификат успешно продлен"
 msgid "Certificate revoked successfully"
 msgstr "Сертификат успешно отозван"
 
-#: src/views/certificate/CertificateEditor.vue:128
+#: src/views/certificate/CertificateEditor.vue:126
 #: src/views/site/site_edit/components/Cert/Cert.vue:58
 msgid "Certificate Status"
 msgid_plural "Certificates Status"
 msgstr[0] "Статус сертификата"
 msgstr[1] "Статус сертификатов"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:104
+msgid "Certificate Type"
+msgstr "Тип сертификата"
+
 #: src/routes/modules/certificates.ts:11
 #: src/views/certificate/CertificateList/Certificate.vue:19
 msgid "Certificates"
@@ -1211,6 +1227,14 @@ msgstr "Пользовательское cron-выражение"
 msgid "Custom Directory"
 msgstr "Пользовательская директория"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:126
+msgid "Custom Domains"
+msgstr "Пользовательские домены"
+
+#: src/views/certificate/components/DNSIssueCertificate.vue:110
+msgid "Custom Domains Certificate"
+msgstr "Сертификат для пользовательских доменов"
+
 #: src/views/preference/tabs/NodeSettings.vue:19
 msgid ""
 "Customize the name of local node to be displayed in the environment "
@@ -1518,11 +1542,11 @@ msgid "Document"
 msgid_plural "Documents"
 msgstr[0] "Документ"
 
-#: src/views/certificate/components/WildcardCertificate.vue:66
+#: src/views/certificate/components/DNSIssueCertificate.vue:116
 msgid "Domain"
 msgstr "Домен"
 
-#: src/views/certificate/CertificateEditor.vue:112
+#: src/views/certificate/CertificateEditor.vue:110
 msgid "Domains list is empty, try to reopen Auto Cert for %{config}"
 msgstr "Список доменов пуст, попробуйте заново создать авто-сертификат для %{config}"
 
@@ -1725,6 +1749,14 @@ msgstr "Использовать для сайта Let's Encrypt"
 msgid "End"
 msgstr "Конец"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:135
+msgid "Enter domain name"
+msgstr "Введите имя домена"
+
+#: src/views/certificate/components/DNSIssueCertificate.vue:120
+msgid "Enter your domain"
+msgstr "Введите ваш домен"
+
 #: src/language/constants.ts:22
 msgid "Environment variables cleaned"
 msgstr "Переменные окружения очищены"
@@ -1799,7 +1831,7 @@ msgstr "Внешний контейнер Docker"
 msgid "External Notify"
 msgstr "Внешнее уведомление"
 
-#: src/views/site/site_edit/components/Cert/ObtainCertLive.vue:80
+#: src/views/site/site_edit/components/Cert/ObtainCertLive.vue:81
 msgid "Fail to obtain certificate"
 msgstr "Не удалось получить сертификат"
 
@@ -2327,7 +2359,7 @@ msgid "Import"
 msgstr "Импорт"
 
 #: src/routes/modules/certificates.ts:46
-#: src/views/certificate/CertificateEditor.vue:85
+#: src/views/certificate/CertificateEditor.vue:83
 msgid "Import Certificate"
 msgstr "Импортировать сертификат"
 
@@ -2472,12 +2504,12 @@ msgid "IP"
 msgstr "IP"
 
 #: src/views/certificate/CertificateList/Certificate.vue:37
-msgid "Issue wildcard certificate"
-msgstr "Выпустить wildcard-сертификат"
+msgid "Issue certificate"
+msgstr "Выдать сертификат"
 
-#: src/views/certificate/components/WildcardCertificate.vue:57
-msgid "Issue Wildcard Certificate"
-msgstr "Выпустить Wildcard сертификат"
+#: src/views/certificate/components/DNSIssueCertificate.vue:95
+msgid "Issue Certificate"
+msgstr "Выпустить сертификат"
 
 #: src/language/constants.ts:20
 msgid "Issued certificate successfully"
@@ -2560,8 +2592,8 @@ msgstr "Оставьте пустым, если не хотите изменят
 msgid "Leave blank if you don't need this."
 msgstr "Оставьте пустым без изменений."
 
-#: src/views/certificate/CertificateEditor.vue:223
-#: src/views/certificate/CertificateEditor.vue:237
+#: src/views/certificate/CertificateEditor.vue:221
+#: src/views/certificate/CertificateEditor.vue:235
 msgid "Leave blank will not change anything"
 msgstr "Если оставить пустым, ничего не изменится"
 
@@ -2648,7 +2680,7 @@ msgstr "Локация"
 msgid "Locations"
 msgstr "Локации"
 
-#: src/views/certificate/CertificateEditor.vue:247
+#: src/views/certificate/CertificateEditor.vue:245
 msgid "Log"
 msgstr "Журнал"
 
@@ -2862,7 +2894,7 @@ msgid "Modify"
 msgstr "Изменить"
 
 #: src/routes/modules/certificates.ts:36
-#: src/views/certificate/CertificateEditor.vue:85
+#: src/views/certificate/CertificateEditor.vue:83
 msgid "Modify Certificate"
 msgstr "Изменить сертификат"
 
@@ -2897,7 +2929,7 @@ msgstr "Многострочная директива"
 #: src/components/NgxConfigEditor/NgxUpstream.vue:199
 #: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/certificate/ACMEUser.vue:11
-#: src/views/certificate/CertificateEditor.vue:162
+#: src/views/certificate/CertificateEditor.vue:160
 #: src/views/certificate/CertificateList/certColumns.tsx:9
 #: src/views/certificate/DNSCredential.vue:9
 #: src/views/config/components/ConfigRightPanel/Basic.vue:34
@@ -2957,7 +2989,7 @@ msgstr "Новый путь"
 msgid "New version released"
 msgstr "Вышла новая версия"
 
-#: src/views/certificate/components/WildcardCertificate.vue:89
+#: src/views/certificate/components/DNSIssueCertificate.vue:181
 #: src/views/install/components/InstallView.vue:96
 #: src/views/site/site_add/SiteAdd.vue:132
 #: src/views/site/site_edit/components/Cert/ObtainCert.vue:214
@@ -3546,6 +3578,10 @@ msgstr ""
 msgid "Please enter a valid port range"
 msgstr "Пожалуйста, введите допустимый диапазон портов"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:74
+msgid "Please enter at least one domain"
+msgstr "Пожалуйста, введите хотя бы один домен"
+
 #: src/components/TwoFA/Authorization.vue:85
 msgid "Please enter the OTP code:"
 msgstr "Пожалуйста, введите код OTP:"
@@ -3892,6 +3928,7 @@ msgstr "Перезагружается"
 msgid "Reloading nginx"
 msgstr "Перезагружается nginx"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:144
 #: src/views/preference/tabs/AuthSettings.vue:137
 msgid "Remove"
 msgstr "Удалить"
@@ -3974,8 +4011,8 @@ msgstr "Переименовано успешно"
 msgid "Renamed successfully"
 msgstr "Успешно переименовано"
 
-#: src/views/certificate/components/RenewCert.vue:46
-#: src/views/certificate/components/RenewCert.vue:53
+#: src/views/certificate/components/RenewCert.vue:48
+#: src/views/certificate/components/RenewCert.vue:55
 msgid "Renew Certificate"
 msgstr "Обновить сертификат"
 
@@ -3987,8 +4024,8 @@ msgstr "Ошибка обновления сертификата"
 msgid "Renew Certificate Success"
 msgstr "Успешное обновление сертификата"
 
-#: src/views/certificate/components/RenewCert.vue:28
-#: src/views/certificate/components/WildcardCertificate.vue:46
+#: src/views/certificate/components/DNSIssueCertificate.vue:84
+#: src/views/certificate/components/RenewCert.vue:30
 msgid "Renew successfully"
 msgstr "Успешно обновлено"
 
@@ -4247,7 +4284,7 @@ msgstr "Суббота"
 
 #: src/components/ChatGPT/ChatMessage.vue:215
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:129
-#: src/language/curd.ts:18 src/views/certificate/CertificateEditor.vue:266
+#: src/language/curd.ts:18 src/views/certificate/CertificateEditor.vue:264
 #: src/views/config/components/ConfigLeftPanel.vue:282
 #: src/views/config/components/ConfigName.vue:59
 #: src/views/preference/components/AuthSettings/Passkey.vue:130
@@ -4302,7 +4339,7 @@ msgstr "Не удалось сохранить поток %{name} на %{node}"
 msgid "Save stream %{name} to %{node} successfully"
 msgstr "Поток %{name} успешно сохранён на %{node}"
 
-#: src/language/curd.ts:35 src/views/certificate/CertificateEditor.vue:49
+#: src/language/curd.ts:35 src/views/certificate/store.ts:13
 #: src/views/preference/store/index.ts:118
 msgid "Save successfully"
 msgstr "Сохранено успешно"
@@ -4412,6 +4449,10 @@ msgstr "Отправлено"
 msgid "Server"
 msgstr "Сервер"
 
+#: src/views/certificate/CertificateEditor.vue:55
+msgid "Server error"
+msgstr "Ошибка сервера"
+
 #: src/views/dashboard/ServerAnalytic.vue:175
 msgid "Server Info"
 msgstr "Информация о сервере"
@@ -4548,7 +4589,7 @@ msgstr "Время ожидания между итерациями загруз
 msgid "Sleep time between cache manager iterations"
 msgstr "Время ожидания между итерациями менеджера кеша"
 
-#: src/views/certificate/CertificateEditor.vue:213
+#: src/views/certificate/CertificateEditor.vue:211
 msgid "SSL Certificate Content"
 msgstr "Содержимое SSL-сертификата"
 
@@ -4560,15 +4601,15 @@ msgstr "Файл SSL-сертификата должен находиться в
 msgid "SSL certificate file not found"
 msgstr "Файл SSL-сертификата не найден"
 
-#: src/views/certificate/CertificateEditor.vue:227
+#: src/views/certificate/CertificateEditor.vue:225
 msgid "SSL Certificate Key Content"
 msgstr "Содержимое ключа SSL-сертификата"
 
-#: src/views/certificate/CertificateEditor.vue:192
+#: src/views/certificate/CertificateEditor.vue:190
 msgid "SSL Certificate Key Path"
 msgstr "Путь к ключу SSL-сертификата"
 
-#: src/views/certificate/CertificateEditor.vue:177
+#: src/views/certificate/CertificateEditor.vue:175
 #: src/views/preference/tabs/ServerSettings.vue:37
 msgid "SSL Certificate Path"
 msgstr "Путь к SSL сертификату"
@@ -4805,7 +4846,7 @@ msgstr "Синхронизированные узлы"
 msgid "Sync strategy"
 msgstr "Стратегия синхронизации"
 
-#: src/views/certificate/CertificateEditor.vue:206
+#: src/views/certificate/CertificateEditor.vue:204
 msgid "Sync to"
 msgstr "Синхронизировать с"
 
@@ -4881,11 +4922,11 @@ msgstr ""
 "Номер ICP должен содержать только буквы, юникод, цифры, дефисы, тире, "
 "двоеточия и точки."
 
-#: src/views/certificate/CertificateEditor.vue:216
+#: src/views/certificate/CertificateEditor.vue:214
 msgid "The input is not a SSL Certificate"
 msgstr "Входные данные не являются SSL-сертификатом"
 
-#: src/views/certificate/CertificateEditor.vue:230
+#: src/views/certificate/CertificateEditor.vue:228
 msgid "The input is not a SSL Certificate Key"
 msgstr "Введенные данные не являются ключом SSL сертификата"
 
@@ -4924,11 +4965,11 @@ msgstr ""
 msgid "The parameter of server_name is required"
 msgstr "Параметр server_name обязателен"
 
-#: src/views/certificate/CertificateEditor.vue:181
+#: src/views/certificate/CertificateEditor.vue:179
 msgid "The path exists, but the file is not a certificate"
 msgstr "Путь существует, но файл не является сертификатом"
 
-#: src/views/certificate/CertificateEditor.vue:196
+#: src/views/certificate/CertificateEditor.vue:194
 msgid "The path exists, but the file is not a private key"
 msgstr "Путь существует, но файл не является приватным ключом"
 
@@ -4990,17 +5031,17 @@ msgstr ""
 "вы потеряете пароль и вторые факторы. Если вы не сможете найти эти коды, вы "
 "потеряете доступ к своему аккаунту."
 
-#: src/views/certificate/CertificateEditor.vue:102
+#: src/views/certificate/CertificateEditor.vue:100
 msgid "This Auto Cert item is invalid, please remove it."
 msgstr "Этот элемент автосертификата недействителен, удалите его.."
 
-#: src/views/certificate/CertificateEditor.vue:92
+#: src/views/certificate/CertificateEditor.vue:90
 msgid "This certificate is managed by Nginx UI"
 msgstr "Этот сертификат под управлением Nginx UI"
 
-#: src/views/certificate/CertificateEditor.vue:165
-#: src/views/certificate/CertificateEditor.vue:179
-#: src/views/certificate/CertificateEditor.vue:194
+#: src/views/certificate/CertificateEditor.vue:163
+#: src/views/certificate/CertificateEditor.vue:177
+#: src/views/certificate/CertificateEditor.vue:192
 msgid "This field is required"
 msgstr "Это поле обязательно для заполнения"
 
@@ -5480,6 +5521,10 @@ msgstr ""
 msgid "Whether to use a temporary path when writing temporary files"
 msgstr "Использовать ли временный путь при записи временных файлов"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:107
+msgid "Wildcard Certificate"
+msgstr "Сертификат с подстановочным знаком"
+
 #: src/views/dashboard/components/ParamsOpt/PerformanceConfig.vue:48
 msgid "Worker Connections"
 msgstr "Рабочие соединения"
@@ -5808,9 +5853,6 @@ msgstr "Ваши ключи доступа"
 #~ msgid "Recovery Code:"
 #~ msgstr "Код восстановления:"
 
-#~ msgid "Server error"
-#~ msgstr "Ошибка сервера"
-
 #~ msgid "The recovery code is only displayed once, please save it in a safe place."
 #~ msgstr ""
 #~ "Код восстановления отображается только один раз, пожалуйста, сохраните его "

+ 84 - 43
app/src/language/tr_TR/app.po

@@ -181,6 +181,10 @@ msgstr "Yapılandırma Ekle"
 msgid "Add Directive Below"
 msgstr "Direktifi Aşağıya Ekleyin"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:151
+msgid "Add Domain"
+msgstr "Alan Adı Ekle"
+
 #: src/components/NgxConfigEditor/LocationEditor.vue:131
 #: src/components/NgxConfigEditor/LocationEditor.vue:158
 msgid "Add Location"
@@ -228,6 +232,14 @@ msgstr "Tümü"
 msgid "All Recovery Codes Have Been Used"
 msgstr "Tüm Kurtarma Kodları Kullanıldı"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:156
+msgid ""
+"All selected subdomains must belong to the same DNS Provider, otherwise the "
+"certificate application will fail."
+msgstr ""
+"Seçilen tüm alt alan adları aynı DNS Sağlayıcısına ait olmalıdır, aksi "
+"takdirde sertifika başvurusu başarısız olur."
+
 #: src/views/preference/tabs/OpenAISettings.vue:32
 msgid "API Base Url"
 msgstr "API Temel Bağlantı Adresi"
@@ -254,7 +266,7 @@ msgstr "Uygulama"
 
 #: src/views/system/Upgrade.vue:195
 msgid "Arch"
-msgstr "\"Mimari\""
+msgstr "Mimari"
 
 #: src/views/preference/tabs/AuthSettings.vue:130
 msgid "Are you sure to delete this banned IP immediately?"
@@ -393,7 +405,7 @@ msgstr "Otomatik yenileme %{name} için devre dışı"
 msgid "Auto-renewal enabled for %{name}"
 msgstr "Otomatik yenileme %{name} için etkinleştirildi"
 
-#: src/views/certificate/components/RenewCert.vue:49
+#: src/views/certificate/components/RenewCert.vue:51
 #: src/views/site/site_edit/components/Cert/IssueCert.vue:84
 msgid "AutoCert is running, please wait..."
 msgstr "AutoCert çalışıyor, lütfen bekleyin..."
@@ -406,7 +418,7 @@ msgstr "\"AutoCert çalışıyor...\""
 msgid "Automatic Restart"
 msgstr "\"Otomatik Yeniden Başlatma\""
 
-#: src/views/certificate/CertificateEditor.vue:259
+#: src/views/certificate/CertificateEditor.vue:257
 #: src/views/config/components/ConfigLeftPanel.vue:273
 #: src/views/config/ConfigList.vue:112 src/views/config/ConfigList.vue:195
 #: src/views/nginx_log/NginxLog.vue:174
@@ -701,13 +713,17 @@ msgstr "Sertifika başarıyla yenilendi"
 msgid "Certificate revoked successfully"
 msgstr "Sertifika başarıyla iptal edildi"
 
-#: src/views/certificate/CertificateEditor.vue:128
+#: src/views/certificate/CertificateEditor.vue:126
 #: src/views/site/site_edit/components/Cert/Cert.vue:58
 msgid "Certificate Status"
 msgid_plural "Certificates Status"
 msgstr[0] "Sertifika Durumu"
 msgstr[1] "Sertifikaların Durumu"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:104
+msgid "Certificate Type"
+msgstr "Sertifika Türü"
+
 #: src/routes/modules/certificates.ts:11
 #: src/views/certificate/CertificateList/Certificate.vue:19
 msgid "Certificates"
@@ -1199,6 +1215,14 @@ msgstr "Özel cron ifadesi"
 msgid "Custom Directory"
 msgstr "Özel Dizin"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:126
+msgid "Custom Domains"
+msgstr "Özel Alan Adları"
+
+#: src/views/certificate/components/DNSIssueCertificate.vue:110
+msgid "Custom Domains Certificate"
+msgstr "Özel Alan Adı Sertifikası"
+
 #: src/views/preference/tabs/NodeSettings.vue:19
 msgid ""
 "Customize the name of local node to be displayed in the environment "
@@ -1508,11 +1532,11 @@ msgid "Document"
 msgid_plural "Documents"
 msgstr[0] "Belge"
 
-#: src/views/certificate/components/WildcardCertificate.vue:66
+#: src/views/certificate/components/DNSIssueCertificate.vue:116
 msgid "Domain"
 msgstr "Alan Adı"
 
-#: src/views/certificate/CertificateEditor.vue:112
+#: src/views/certificate/CertificateEditor.vue:110
 msgid "Domains list is empty, try to reopen Auto Cert for %{config}"
 msgstr ""
 "Alan adları listesi boş, %{config} için Otomatik Sertifikayı yeniden açmayı "
@@ -1717,6 +1741,14 @@ msgstr "Let's Encrypt ile web sitesini şifreleyin"
 msgid "End"
 msgstr "Son"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:135
+msgid "Enter domain name"
+msgstr "Alan adını girin"
+
+#: src/views/certificate/components/DNSIssueCertificate.vue:120
+msgid "Enter your domain"
+msgstr "Alan adınızı girin"
+
 #: src/language/constants.ts:22
 msgid "Environment variables cleaned"
 msgstr "Ortam değişkenleri temizlendi"
@@ -1791,7 +1823,7 @@ msgstr "Harici Docker Konteyneri"
 msgid "External Notify"
 msgstr "Harici Bildirim"
 
-#: src/views/site/site_edit/components/Cert/ObtainCertLive.vue:80
+#: src/views/site/site_edit/components/Cert/ObtainCertLive.vue:81
 msgid "Fail to obtain certificate"
 msgstr "Sertifika alınamadı"
 
@@ -2323,7 +2355,7 @@ msgid "Import"
 msgstr "İçe Aktar"
 
 #: src/routes/modules/certificates.ts:46
-#: src/views/certificate/CertificateEditor.vue:85
+#: src/views/certificate/CertificateEditor.vue:83
 msgid "Import Certificate"
 msgstr "Sertifika İçe Aktar"
 
@@ -2468,12 +2500,12 @@ msgid "IP"
 msgstr "IP"
 
 #: src/views/certificate/CertificateList/Certificate.vue:37
-msgid "Issue wildcard certificate"
-msgstr "Wildcard sertifikası düzenle"
+msgid "Issue certificate"
+msgstr "Sertifika ver"
 
-#: src/views/certificate/components/WildcardCertificate.vue:57
-msgid "Issue Wildcard Certificate"
-msgstr "Wildcard Sertifikası Yayınlama"
+#: src/views/certificate/components/DNSIssueCertificate.vue:95
+msgid "Issue Certificate"
+msgstr "Sertifika Düzenle"
 
 #: src/language/constants.ts:20
 msgid "Issued certificate successfully"
@@ -2556,8 +2588,8 @@ msgstr "Değiştirmek istemiyorsanız boş bırakın"
 msgid "Leave blank if you don't need this."
 msgstr "Buna ihtiyacınız yoksa boş bırakın."
 
-#: src/views/certificate/CertificateEditor.vue:223
-#: src/views/certificate/CertificateEditor.vue:237
+#: src/views/certificate/CertificateEditor.vue:221
+#: src/views/certificate/CertificateEditor.vue:235
 msgid "Leave blank will not change anything"
 msgstr "Boş bırakmak hiçbir şeyi değiştirmeyecektir"
 
@@ -2644,7 +2676,7 @@ msgstr "Konum"
 msgid "Locations"
 msgstr "Konumlar"
 
-#: src/views/certificate/CertificateEditor.vue:247
+#: src/views/certificate/CertificateEditor.vue:245
 msgid "Log"
 msgstr "Günlük"
 
@@ -2857,7 +2889,7 @@ msgid "Modify"
 msgstr "Değiştir"
 
 #: src/routes/modules/certificates.ts:36
-#: src/views/certificate/CertificateEditor.vue:85
+#: src/views/certificate/CertificateEditor.vue:83
 msgid "Modify Certificate"
 msgstr "Sertifikayı Düzenle"
 
@@ -2892,7 +2924,7 @@ msgstr "Çok Satırlı Yönergeler"
 #: src/components/NgxConfigEditor/NgxUpstream.vue:199
 #: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/certificate/ACMEUser.vue:11
-#: src/views/certificate/CertificateEditor.vue:162
+#: src/views/certificate/CertificateEditor.vue:160
 #: src/views/certificate/CertificateList/certColumns.tsx:9
 #: src/views/certificate/DNSCredential.vue:9
 #: src/views/config/components/ConfigRightPanel/Basic.vue:34
@@ -2952,7 +2984,7 @@ msgstr "Yeni Yol"
 msgid "New version released"
 msgstr "Yeni sürüm yayınlandı"
 
-#: src/views/certificate/components/WildcardCertificate.vue:89
+#: src/views/certificate/components/DNSIssueCertificate.vue:181
 #: src/views/install/components/InstallView.vue:96
 #: src/views/site/site_add/SiteAdd.vue:132
 #: src/views/site/site_edit/components/Cert/ObtainCert.vue:214
@@ -3540,6 +3572,10 @@ msgstr ""
 msgid "Please enter a valid port range"
 msgstr "Lütfen geçerli bir port aralığı girin"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:74
+msgid "Please enter at least one domain"
+msgstr "Lütfen en az bir alan adı girin"
+
 #: src/components/TwoFA/Authorization.vue:85
 msgid "Please enter the OTP code:"
 msgstr "Lütfen OTP kodunu girin:"
@@ -3882,6 +3918,7 @@ msgstr "Yeniden Yükleniyor"
 msgid "Reloading nginx"
 msgstr "nginx yeniden yükleniyor"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:144
 #: src/views/preference/tabs/AuthSettings.vue:137
 msgid "Remove"
 msgstr "Kaldır"
@@ -3974,8 +4011,8 @@ msgstr "Başarıyla yeniden adlandırıldı"
 msgid "Renamed successfully"
 msgstr "Başarıyla yeniden adlandırıldı"
 
-#: src/views/certificate/components/RenewCert.vue:46
-#: src/views/certificate/components/RenewCert.vue:53
+#: src/views/certificate/components/RenewCert.vue:48
+#: src/views/certificate/components/RenewCert.vue:55
 msgid "Renew Certificate"
 msgstr "Sertifikayı Yenile"
 
@@ -3987,8 +4024,8 @@ msgstr "Sertifika Yenileme Hatası"
 msgid "Renew Certificate Success"
 msgstr "Sertifika Yenileme Başarılı"
 
-#: src/views/certificate/components/RenewCert.vue:28
-#: src/views/certificate/components/WildcardCertificate.vue:46
+#: src/views/certificate/components/DNSIssueCertificate.vue:84
+#: src/views/certificate/components/RenewCert.vue:30
 msgid "Renew successfully"
 msgstr "Başarıyla yenilendi"
 
@@ -4246,7 +4283,7 @@ msgstr "Cumartesi"
 
 #: src/components/ChatGPT/ChatMessage.vue:215
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:129
-#: src/language/curd.ts:18 src/views/certificate/CertificateEditor.vue:266
+#: src/language/curd.ts:18 src/views/certificate/CertificateEditor.vue:264
 #: src/views/config/components/ConfigLeftPanel.vue:282
 #: src/views/config/components/ConfigName.vue:59
 #: src/views/preference/components/AuthSettings/Passkey.vue:130
@@ -4301,7 +4338,7 @@ msgstr "Akış %{name}, %{node} üzerine kaydedilemedi"
 msgid "Save stream %{name} to %{node} successfully"
 msgstr "Akış %{name}, %{node} üzerine başarıyla kaydedildi"
 
-#: src/language/curd.ts:35 src/views/certificate/CertificateEditor.vue:49
+#: src/language/curd.ts:35 src/views/certificate/store.ts:13
 #: src/views/preference/store/index.ts:118
 msgid "Save successfully"
 msgstr "Başarıyla kaydedildi"
@@ -4409,6 +4446,10 @@ msgstr "Gönder"
 msgid "Server"
 msgstr "Sunucu"
 
+#: src/views/certificate/CertificateEditor.vue:55
+msgid "Server error"
+msgstr "Sunucu hatası"
+
 #: src/views/dashboard/ServerAnalytic.vue:175
 msgid "Server Info"
 msgstr "Sunucu Bilgisi"
@@ -4545,7 +4586,7 @@ msgstr "Önbellek yükleyici tekrarları arasındaki bekleme süresi"
 msgid "Sleep time between cache manager iterations"
 msgstr "Önbellek yöneticisi yinelemeleri arasındaki bekleme süresi"
 
-#: src/views/certificate/CertificateEditor.vue:213
+#: src/views/certificate/CertificateEditor.vue:211
 msgid "SSL Certificate Content"
 msgstr "SSL Sertifika İçeriği"
 
@@ -4557,15 +4598,15 @@ msgstr "SSL sertifika dosyası Nginx yapılandırma dizini altında olmalıdır:
 msgid "SSL certificate file not found"
 msgstr "SSL sertifika dosyası bulunamadı"
 
-#: src/views/certificate/CertificateEditor.vue:227
+#: src/views/certificate/CertificateEditor.vue:225
 msgid "SSL Certificate Key Content"
 msgstr "SSL Sertifika Anahtarı İçeriği"
 
-#: src/views/certificate/CertificateEditor.vue:192
+#: src/views/certificate/CertificateEditor.vue:190
 msgid "SSL Certificate Key Path"
 msgstr "SSL Sertifika Anahtar Yolu"
 
-#: src/views/certificate/CertificateEditor.vue:177
+#: src/views/certificate/CertificateEditor.vue:175
 #: src/views/preference/tabs/ServerSettings.vue:37
 msgid "SSL Certificate Path"
 msgstr "SSL Sertifika Yolu"
@@ -4804,7 +4845,7 @@ msgstr "Senkronizasyon Düğümleri"
 msgid "Sync strategy"
 msgstr "Senkronizasyon stratejisi"
 
-#: src/views/certificate/CertificateEditor.vue:206
+#: src/views/certificate/CertificateEditor.vue:204
 msgid "Sync to"
 msgstr "Senkronize Et"
 
@@ -4880,11 +4921,11 @@ msgstr ""
 "ICP Numarası yalnızca harfler, unicode, sayılar, kısa çizgiler, uzun "
 "çizgiler, iki nokta üst üste ve noktalar içermelidir."
 
-#: src/views/certificate/CertificateEditor.vue:216
+#: src/views/certificate/CertificateEditor.vue:214
 msgid "The input is not a SSL Certificate"
 msgstr "Girdi bir SSL Sertifikası değil"
 
-#: src/views/certificate/CertificateEditor.vue:230
+#: src/views/certificate/CertificateEditor.vue:228
 msgid "The input is not a SSL Certificate Key"
 msgstr "Girdi bir SSL Sertifika Anahtarı değil"
 
@@ -4923,11 +4964,11 @@ msgstr ""
 msgid "The parameter of server_name is required"
 msgstr "server_name parametresi gereklidir"
 
-#: src/views/certificate/CertificateEditor.vue:181
+#: src/views/certificate/CertificateEditor.vue:179
 msgid "The path exists, but the file is not a certificate"
 msgstr "Yol mevcut, ancak dosya bir sertifika değil"
 
-#: src/views/certificate/CertificateEditor.vue:196
+#: src/views/certificate/CertificateEditor.vue:194
 msgid "The path exists, but the file is not a private key"
 msgstr "Yol mevcut, ancak dosya bir özel anahtar değil"
 
@@ -4989,17 +5030,17 @@ msgstr ""
 "hesabınıza erişmek için son çaredir. Bu kodları bulamazsanız, hesabınıza "
 "erişimi kaybedersiniz."
 
-#: src/views/certificate/CertificateEditor.vue:102
+#: src/views/certificate/CertificateEditor.vue:100
 msgid "This Auto Cert item is invalid, please remove it."
 msgstr "Bu Otomatik Sertifika öğesi geçersiz, lütfen kaldırın."
 
-#: src/views/certificate/CertificateEditor.vue:92
+#: src/views/certificate/CertificateEditor.vue:90
 msgid "This certificate is managed by Nginx UI"
 msgstr "Bu sertifika Nginx UI tarafından yönetilmektedir"
 
-#: src/views/certificate/CertificateEditor.vue:165
-#: src/views/certificate/CertificateEditor.vue:179
-#: src/views/certificate/CertificateEditor.vue:194
+#: src/views/certificate/CertificateEditor.vue:163
+#: src/views/certificate/CertificateEditor.vue:177
+#: src/views/certificate/CertificateEditor.vue:192
 msgid "This field is required"
 msgstr "Bu alan zorunludur"
 
@@ -5479,6 +5520,10 @@ msgstr ""
 msgid "Whether to use a temporary path when writing temporary files"
 msgstr "Geçici dosyalar yazılırken geçici bir yol kullanılıp kullanılmayacağı"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:107
+msgid "Wildcard Certificate"
+msgstr "Wildcard Sertifikası"
+
 #: src/views/dashboard/components/ParamsOpt/PerformanceConfig.vue:48
 msgid "Worker Connections"
 msgstr "Çalışan Bağlantıları"
@@ -5815,10 +5860,6 @@ msgstr "Geçiş Anahtarlarınız"
 #~ msgid "Recovery Code:"
 #~ msgstr "Kurtarma Kodu:"
 
-#, fuzzy
-#~ msgid "Server error"
-#~ msgstr "Server hatası"
-
 #, fuzzy
 #~ msgid "The recovery code is only displayed once, please save it in a safe place."
 #~ msgstr ""

+ 84 - 39
app/src/language/uk_UA/app.po

@@ -185,6 +185,10 @@ msgstr "Додати конфігурацію"
 msgid "Add Directive Below"
 msgstr "Додайте директиву нижче"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:151
+msgid "Add Domain"
+msgstr "Додати домен"
+
 #: src/components/NgxConfigEditor/LocationEditor.vue:131
 #: src/components/NgxConfigEditor/LocationEditor.vue:158
 msgid "Add Location"
@@ -232,6 +236,14 @@ msgstr "Усі"
 msgid "All Recovery Codes Have Been Used"
 msgstr "Усі коди відновлення використано"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:156
+msgid ""
+"All selected subdomains must belong to the same DNS Provider, otherwise the "
+"certificate application will fail."
+msgstr ""
+"Усі вибрані піддомени повинні належати до одного й того самого "
+"DNS-провайдера, інакше запит на сертифікат не буде успішним."
+
 #: src/views/preference/tabs/OpenAISettings.vue:32
 msgid "API Base Url"
 msgstr "Базова URL-адреса API"
@@ -258,7 +270,7 @@ msgstr "Додаток"
 
 #: src/views/system/Upgrade.vue:195
 msgid "Arch"
-msgstr "\"Архітектура\""
+msgstr "Архітектура"
 
 #: src/views/preference/tabs/AuthSettings.vue:130
 msgid "Are you sure to delete this banned IP immediately?"
@@ -397,7 +409,7 @@ msgstr "Автоматичне відновлення вимкнено на %{na
 msgid "Auto-renewal enabled for %{name}"
 msgstr "Автоматичне відновлення увімкнено для %{name}"
 
-#: src/views/certificate/components/RenewCert.vue:49
+#: src/views/certificate/components/RenewCert.vue:51
 #: src/views/site/site_edit/components/Cert/IssueCert.vue:84
 msgid "AutoCert is running, please wait..."
 msgstr "AutoCert виконується, будь ласка, зачекайте..."
@@ -410,7 +422,7 @@ msgstr "\"AutoCert виконується...\""
 msgid "Automatic Restart"
 msgstr "\"Автоматичний перезапуск\""
 
-#: src/views/certificate/CertificateEditor.vue:259
+#: src/views/certificate/CertificateEditor.vue:257
 #: src/views/config/components/ConfigLeftPanel.vue:273
 #: src/views/config/ConfigList.vue:112 src/views/config/ConfigList.vue:195
 #: src/views/nginx_log/NginxLog.vue:174
@@ -712,12 +724,16 @@ msgstr "Сертифікат успішно поновлено"
 msgid "Certificate revoked successfully"
 msgstr "Сертифікат успішно відкликано"
 
-#: src/views/certificate/CertificateEditor.vue:128
+#: src/views/certificate/CertificateEditor.vue:126
 #: src/views/site/site_edit/components/Cert/Cert.vue:58
 msgid "Certificate Status"
 msgid_plural "Certificates Status"
 msgstr[0] "Стан сертифіката"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:104
+msgid "Certificate Type"
+msgstr "Тип сертифіката"
+
 #: src/routes/modules/certificates.ts:11
 #: src/views/certificate/CertificateList/Certificate.vue:19
 msgid "Certificates"
@@ -1241,6 +1257,14 @@ msgstr "Спеціальний cron вираз"
 msgid "Custom Directory"
 msgstr "Спеціальний каталог"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:126
+msgid "Custom Domains"
+msgstr "Користувацькі домени"
+
+#: src/views/certificate/components/DNSIssueCertificate.vue:110
+msgid "Custom Domains Certificate"
+msgstr "Сертифікат користувацьких доменів"
+
 #: src/views/preference/tabs/NodeSettings.vue:19
 msgid ""
 "Customize the name of local node to be displayed in the environment "
@@ -1584,11 +1608,11 @@ msgid "Document"
 msgid_plural "Documents"
 msgstr[0] "Документ"
 
-#: src/views/certificate/components/WildcardCertificate.vue:66
+#: src/views/certificate/components/DNSIssueCertificate.vue:116
 msgid "Domain"
 msgstr "Домен"
 
-#: src/views/certificate/CertificateEditor.vue:112
+#: src/views/certificate/CertificateEditor.vue:110
 msgid "Domains list is empty, try to reopen Auto Cert for %{config}"
 msgstr "Список доменів порожній, спробуйте знову відкрити Auto Cert для %{config}"
 
@@ -1791,6 +1815,14 @@ msgstr "Зашифрувати вебсайт за допомогою Let's Encr
 msgid "End"
 msgstr "Кінець"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:135
+msgid "Enter domain name"
+msgstr "Введіть ім’я домену"
+
+#: src/views/certificate/components/DNSIssueCertificate.vue:120
+msgid "Enter your domain"
+msgstr "Введіть ваш домен"
+
 #: src/language/constants.ts:22
 msgid "Environment variables cleaned"
 msgstr "Змінні середовища очищено"
@@ -1865,7 +1897,7 @@ msgstr "Зовнішній контейнер Docker"
 msgid "External Notify"
 msgstr "Зовнішнє сповіщення"
 
-#: src/views/site/site_edit/components/Cert/ObtainCertLive.vue:80
+#: src/views/site/site_edit/components/Cert/ObtainCertLive.vue:81
 msgid "Fail to obtain certificate"
 msgstr "Не вдалося отримати сертифікат"
 
@@ -2393,7 +2425,7 @@ msgid "Import"
 msgstr "Імпорт"
 
 #: src/routes/modules/certificates.ts:46
-#: src/views/certificate/CertificateEditor.vue:85
+#: src/views/certificate/CertificateEditor.vue:83
 msgid "Import Certificate"
 msgstr "Імпортувати сертифікат"
 
@@ -2538,12 +2570,12 @@ msgid "IP"
 msgstr "IP"
 
 #: src/views/certificate/CertificateList/Certificate.vue:37
-msgid "Issue wildcard certificate"
-msgstr "Видати сертифікат з маскою"
+msgid "Issue certificate"
+msgstr "Видати сертифікат"
 
-#: src/views/certificate/components/WildcardCertificate.vue:57
-msgid "Issue Wildcard Certificate"
-msgstr "Видати сертифікат зі шаблоном"
+#: src/views/certificate/components/DNSIssueCertificate.vue:95
+msgid "Issue Certificate"
+msgstr "Видати сертифікат"
 
 #: src/language/constants.ts:20
 msgid "Issued certificate successfully"
@@ -2626,8 +2658,8 @@ msgstr "Залиште порожнім, якщо не хочете змінюв
 msgid "Leave blank if you don't need this."
 msgstr "Залиште порожнім, якщо вам це не потрібно."
 
-#: src/views/certificate/CertificateEditor.vue:223
-#: src/views/certificate/CertificateEditor.vue:237
+#: src/views/certificate/CertificateEditor.vue:221
+#: src/views/certificate/CertificateEditor.vue:235
 msgid "Leave blank will not change anything"
 msgstr "Залишити порожнім — нічого не зміниться"
 
@@ -2714,7 +2746,7 @@ msgstr "Розташування"
 msgid "Locations"
 msgstr "Розташування"
 
-#: src/views/certificate/CertificateEditor.vue:247
+#: src/views/certificate/CertificateEditor.vue:245
 msgid "Log"
 msgstr "Журнал"
 
@@ -2928,7 +2960,7 @@ msgid "Modify"
 msgstr "Змінити"
 
 #: src/routes/modules/certificates.ts:36
-#: src/views/certificate/CertificateEditor.vue:85
+#: src/views/certificate/CertificateEditor.vue:83
 msgid "Modify Certificate"
 msgstr "Редагувати сертифікат"
 
@@ -2963,7 +2995,7 @@ msgstr "Багаторядкова директива"
 #: src/components/NgxConfigEditor/NgxUpstream.vue:199
 #: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/certificate/ACMEUser.vue:11
-#: src/views/certificate/CertificateEditor.vue:162
+#: src/views/certificate/CertificateEditor.vue:160
 #: src/views/certificate/CertificateList/certColumns.tsx:9
 #: src/views/certificate/DNSCredential.vue:9
 #: src/views/config/components/ConfigRightPanel/Basic.vue:34
@@ -3023,7 +3055,7 @@ msgstr "Новий шлях"
 msgid "New version released"
 msgstr "Випущено нову версію"
 
-#: src/views/certificate/components/WildcardCertificate.vue:89
+#: src/views/certificate/components/DNSIssueCertificate.vue:181
 #: src/views/install/components/InstallView.vue:96
 #: src/views/site/site_add/SiteAdd.vue:132
 #: src/views/site/site_edit/components/Cert/ObtainCert.vue:214
@@ -3612,6 +3644,10 @@ msgstr ""
 msgid "Please enter a valid port range"
 msgstr "Будь ласка, введіть дійсний діапазон портів"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:74
+msgid "Please enter at least one domain"
+msgstr "Будь ласка, введіть хоча б один домен"
+
 #: src/components/TwoFA/Authorization.vue:85
 msgid "Please enter the OTP code:"
 msgstr "Будь ласка, введіть OTP-код:"
@@ -3957,6 +3993,7 @@ msgstr "Перезавантаження"
 msgid "Reloading nginx"
 msgstr "Перезавантаження nginx"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:144
 #: src/views/preference/tabs/AuthSettings.vue:137
 msgid "Remove"
 msgstr "Видалити"
@@ -4039,8 +4076,8 @@ msgstr "Успішно перейменовано"
 msgid "Renamed successfully"
 msgstr "Успішно перейменовано"
 
-#: src/views/certificate/components/RenewCert.vue:46
-#: src/views/certificate/components/RenewCert.vue:53
+#: src/views/certificate/components/RenewCert.vue:48
+#: src/views/certificate/components/RenewCert.vue:55
 msgid "Renew Certificate"
 msgstr "Оновити сертифікат"
 
@@ -4052,8 +4089,8 @@ msgstr "Помилка поновлення сертифіката"
 msgid "Renew Certificate Success"
 msgstr "Успішне поновлення сертифіката"
 
-#: src/views/certificate/components/RenewCert.vue:28
-#: src/views/certificate/components/WildcardCertificate.vue:46
+#: src/views/certificate/components/DNSIssueCertificate.vue:84
+#: src/views/certificate/components/RenewCert.vue:30
 msgid "Renew successfully"
 msgstr "Оновлення успішне"
 
@@ -4314,7 +4351,7 @@ msgstr "Субота"
 
 #: src/components/ChatGPT/ChatMessage.vue:215
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:129
-#: src/language/curd.ts:18 src/views/certificate/CertificateEditor.vue:266
+#: src/language/curd.ts:18 src/views/certificate/CertificateEditor.vue:264
 #: src/views/config/components/ConfigLeftPanel.vue:282
 #: src/views/config/components/ConfigName.vue:59
 #: src/views/preference/components/AuthSettings/Passkey.vue:130
@@ -4369,7 +4406,7 @@ msgstr "Не вдалося зберегти потік %{name} на %{node}"
 msgid "Save stream %{name} to %{node} successfully"
 msgstr "Потік %{name} успішно збережено на %{node}"
 
-#: src/language/curd.ts:35 src/views/certificate/CertificateEditor.vue:49
+#: src/language/curd.ts:35 src/views/certificate/store.ts:13
 #: src/views/preference/store/index.ts:118
 msgid "Save successfully"
 msgstr "Успішно збережено"
@@ -4479,6 +4516,10 @@ msgstr "Надіслати"
 msgid "Server"
 msgstr "Сервер"
 
+#: src/views/certificate/CertificateEditor.vue:55
+msgid "Server error"
+msgstr "Помилка сервера"
+
 #: src/views/dashboard/ServerAnalytic.vue:175
 msgid "Server Info"
 msgstr "Інформація про сервер"
@@ -4617,7 +4658,7 @@ msgstr "Час очікування між ітераціями завантаж
 msgid "Sleep time between cache manager iterations"
 msgstr "Час очікування між ітераціями менеджера кешу"
 
-#: src/views/certificate/CertificateEditor.vue:213
+#: src/views/certificate/CertificateEditor.vue:211
 msgid "SSL Certificate Content"
 msgstr "Вміст SSL-сертифіката"
 
@@ -4629,15 +4670,15 @@ msgstr "Файл SSL-сертифіката повинен знаходитис
 msgid "SSL certificate file not found"
 msgstr "Файл SSL-сертифіката не знайдено"
 
-#: src/views/certificate/CertificateEditor.vue:227
+#: src/views/certificate/CertificateEditor.vue:225
 msgid "SSL Certificate Key Content"
 msgstr "Вміст ключа SSL-сертифіката"
 
-#: src/views/certificate/CertificateEditor.vue:192
+#: src/views/certificate/CertificateEditor.vue:190
 msgid "SSL Certificate Key Path"
 msgstr "Шлях до ключа SSL-сертифіката"
 
-#: src/views/certificate/CertificateEditor.vue:177
+#: src/views/certificate/CertificateEditor.vue:175
 #: src/views/preference/tabs/ServerSettings.vue:37
 msgid "SSL Certificate Path"
 msgstr "Шлях до SSL-сертифікату"
@@ -4874,7 +4915,7 @@ msgstr "Синхронізовані вузли"
 msgid "Sync strategy"
 msgstr "Стратегія синхронізації"
 
-#: src/views/certificate/CertificateEditor.vue:206
+#: src/views/certificate/CertificateEditor.vue:204
 msgid "Sync to"
 msgstr "Синхронізувати з"
 
@@ -4950,11 +4991,11 @@ msgstr ""
 "Номер ICP повинен містити лише літери, unicode, цифри, дефіси, тире, "
 "двокрапки та крапки."
 
-#: src/views/certificate/CertificateEditor.vue:216
+#: src/views/certificate/CertificateEditor.vue:214
 msgid "The input is not a SSL Certificate"
 msgstr "Введені дані не є SSL-сертифікатом"
 
-#: src/views/certificate/CertificateEditor.vue:230
+#: src/views/certificate/CertificateEditor.vue:228
 msgid "The input is not a SSL Certificate Key"
 msgstr "Введений текст не є ключем SSL-сертифіката"
 
@@ -4993,11 +5034,11 @@ msgstr ""
 msgid "The parameter of server_name is required"
 msgstr "Параметр server_name є обов'язковим"
 
-#: src/views/certificate/CertificateEditor.vue:181
+#: src/views/certificate/CertificateEditor.vue:179
 msgid "The path exists, but the file is not a certificate"
 msgstr "Шлях існує, але файл не є сертифікатом"
 
-#: src/views/certificate/CertificateEditor.vue:196
+#: src/views/certificate/CertificateEditor.vue:194
 msgid "The path exists, but the file is not a private key"
 msgstr "Шлях існує, але файл не є приватним ключем"
 
@@ -5059,19 +5100,19 @@ msgstr ""
 "втрати пароля та других факторів. Якщо ви не зможете знайти ці коди, ви "
 "втратите доступ до свого облікового запису."
 
-#: src/views/certificate/CertificateEditor.vue:102
+#: src/views/certificate/CertificateEditor.vue:100
 msgid "This Auto Cert item is invalid, please remove it."
 msgstr ""
 "Цей елемент автоматичного сертифікату є недійсним, будь ласка, видаліть "
 "його."
 
-#: src/views/certificate/CertificateEditor.vue:92
+#: src/views/certificate/CertificateEditor.vue:90
 msgid "This certificate is managed by Nginx UI"
 msgstr "Цей сертифікат керується Nginx UI"
 
-#: src/views/certificate/CertificateEditor.vue:165
-#: src/views/certificate/CertificateEditor.vue:179
-#: src/views/certificate/CertificateEditor.vue:194
+#: src/views/certificate/CertificateEditor.vue:163
+#: src/views/certificate/CertificateEditor.vue:177
+#: src/views/certificate/CertificateEditor.vue:192
 msgid "This field is required"
 msgstr "Це поле обов'язкове"
 
@@ -5548,6 +5589,10 @@ msgstr ""
 msgid "Whether to use a temporary path when writing temporary files"
 msgstr "Чи використовувати тимчасовий шлях під час запису тимчасових файлів"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:107
+msgid "Wildcard Certificate"
+msgstr "Сертифікат зі знаком підстановки"
+
 #: src/views/dashboard/components/ParamsOpt/PerformanceConfig.vue:48
 msgid "Worker Connections"
 msgstr "Робочі з’єднання"

+ 84 - 42
app/src/language/vi_VN/app.po

@@ -176,6 +176,10 @@ msgstr "Thêm cấu hình"
 msgid "Add Directive Below"
 msgstr "Thêm Directive"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:151
+msgid "Add Domain"
+msgstr "Thêm tên miền"
+
 #: src/components/NgxConfigEditor/LocationEditor.vue:131
 #: src/components/NgxConfigEditor/LocationEditor.vue:158
 msgid "Add Location"
@@ -223,6 +227,14 @@ msgstr "Tất cả"
 msgid "All Recovery Codes Have Been Used"
 msgstr "Tất cả mã khôi phục đã được sử dụng"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:156
+msgid ""
+"All selected subdomains must belong to the same DNS Provider, otherwise the "
+"certificate application will fail."
+msgstr ""
+"Tất cả các tên miền phụ được chọn phải thuộc cùng một Nhà cung cấp DNS, nếu "
+"không, việc xin cấp chứng chỉ sẽ thất bại."
+
 #: src/views/preference/tabs/OpenAISettings.vue:32
 msgid "API Base Url"
 msgstr "URL Cơ sở API"
@@ -249,7 +261,7 @@ msgstr "Ứng dụng"
 
 #: src/views/system/Upgrade.vue:195
 msgid "Arch"
-msgstr "\"Kiến trúc\""
+msgstr "Kiến trúc"
 
 #: src/views/preference/tabs/AuthSettings.vue:130
 msgid "Are you sure to delete this banned IP immediately?"
@@ -384,7 +396,7 @@ msgstr "Đã tắt tự động gia hạn SSL cho %{name}"
 msgid "Auto-renewal enabled for %{name}"
 msgstr "Đã bật tự động gia hạn SSL cho %{name}"
 
-#: src/views/certificate/components/RenewCert.vue:49
+#: src/views/certificate/components/RenewCert.vue:51
 #: src/views/site/site_edit/components/Cert/IssueCert.vue:84
 msgid "AutoCert is running, please wait..."
 msgstr "AutoCert đang chạy, vui lòng chờ..."
@@ -397,7 +409,7 @@ msgstr "\"AutoCert đang chạy...\""
 msgid "Automatic Restart"
 msgstr "\"Khởi động lại Tự động\""
 
-#: src/views/certificate/CertificateEditor.vue:259
+#: src/views/certificate/CertificateEditor.vue:257
 #: src/views/config/components/ConfigLeftPanel.vue:273
 #: src/views/config/ConfigList.vue:112 src/views/config/ConfigList.vue:195
 #: src/views/nginx_log/NginxLog.vue:174
@@ -692,12 +704,16 @@ msgstr "Gia hạn chứng chỉ thành công"
 msgid "Certificate revoked successfully"
 msgstr "Hủy chứng chỉ thành công"
 
-#: src/views/certificate/CertificateEditor.vue:128
+#: src/views/certificate/CertificateEditor.vue:126
 #: src/views/site/site_edit/components/Cert/Cert.vue:58
 msgid "Certificate Status"
 msgid_plural "Certificates Status"
 msgstr[0] "Trạng thái chứng chỉ"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:104
+msgid "Certificate Type"
+msgstr "Loại chứng chỉ"
+
 #: src/routes/modules/certificates.ts:11
 #: src/views/certificate/CertificateList/Certificate.vue:19
 msgid "Certificates"
@@ -1181,6 +1197,14 @@ msgstr "Biểu thức cron tùy chỉnh"
 msgid "Custom Directory"
 msgstr "Thư mục tùy chỉnh"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:126
+msgid "Custom Domains"
+msgstr "Tên miền tùy chỉnh"
+
+#: src/views/certificate/components/DNSIssueCertificate.vue:110
+msgid "Custom Domains Certificate"
+msgstr "Chứng chỉ Tên miền Tùy chỉnh"
+
 #: src/views/preference/tabs/NodeSettings.vue:19
 msgid ""
 "Customize the name of local node to be displayed in the environment "
@@ -1488,11 +1512,11 @@ msgid "Document"
 msgid_plural "Documents"
 msgstr[0] "Tài liệu"
 
-#: src/views/certificate/components/WildcardCertificate.vue:66
+#: src/views/certificate/components/DNSIssueCertificate.vue:116
 msgid "Domain"
 msgstr "Tên miền"
 
-#: src/views/certificate/CertificateEditor.vue:112
+#: src/views/certificate/CertificateEditor.vue:110
 msgid "Domains list is empty, try to reopen Auto Cert for %{config}"
 msgstr ""
 "Danh sách tên miền rỗng, hãy thử mở lại chức năng Tạo chứng chỉ tự động cho "
@@ -1696,6 +1720,14 @@ msgstr "Bảo mật trang web với Let's Encrypt"
 msgid "End"
 msgstr "Kết thúc"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:135
+msgid "Enter domain name"
+msgstr "Nhập tên miền"
+
+#: src/views/certificate/components/DNSIssueCertificate.vue:120
+msgid "Enter your domain"
+msgstr "Nhập tên miền của bạn"
+
 #: src/language/constants.ts:22
 msgid "Environment variables cleaned"
 msgstr "Đã dọn dẹp biến môi trường"
@@ -1770,7 +1802,7 @@ msgstr "Container Docker bên ngoài"
 msgid "External Notify"
 msgstr "Thông báo bên ngoài"
 
-#: src/views/site/site_edit/components/Cert/ObtainCertLive.vue:80
+#: src/views/site/site_edit/components/Cert/ObtainCertLive.vue:81
 msgid "Fail to obtain certificate"
 msgstr "Không thể lấy chứng chỉ"
 
@@ -2297,7 +2329,7 @@ msgid "Import"
 msgstr "Nhập"
 
 #: src/routes/modules/certificates.ts:46
-#: src/views/certificate/CertificateEditor.vue:85
+#: src/views/certificate/CertificateEditor.vue:83
 msgid "Import Certificate"
 msgstr "Nhập chứng chỉ"
 
@@ -2442,12 +2474,12 @@ msgid "IP"
 msgstr "IP"
 
 #: src/views/certificate/CertificateList/Certificate.vue:37
-msgid "Issue wildcard certificate"
-msgstr "Phát hành chứng chỉ đại diện"
+msgid "Issue certificate"
+msgstr "Cấp chứng chỉ"
 
-#: src/views/certificate/components/WildcardCertificate.vue:57
-msgid "Issue Wildcard Certificate"
-msgstr "Cấp chứng chỉ đại diện"
+#: src/views/certificate/components/DNSIssueCertificate.vue:95
+msgid "Issue Certificate"
+msgstr "Cấp chứng chỉ"
 
 #: src/language/constants.ts:20
 msgid "Issued certificate successfully"
@@ -2530,8 +2562,8 @@ msgstr "Để trống nếu không muốn thay đổi"
 msgid "Leave blank if you don't need this."
 msgstr "Để trống nếu bạn không cần cái này."
 
-#: src/views/certificate/CertificateEditor.vue:223
-#: src/views/certificate/CertificateEditor.vue:237
+#: src/views/certificate/CertificateEditor.vue:221
+#: src/views/certificate/CertificateEditor.vue:235
 msgid "Leave blank will not change anything"
 msgstr "Để trống sẽ không thay đổi bất cứ điều gì"
 
@@ -2618,7 +2650,7 @@ msgstr "Location"
 msgid "Locations"
 msgstr "Locations"
 
-#: src/views/certificate/CertificateEditor.vue:247
+#: src/views/certificate/CertificateEditor.vue:245
 msgid "Log"
 msgstr "Nhật ký"
 
@@ -2832,7 +2864,7 @@ msgid "Modify"
 msgstr "Sửa đổi"
 
 #: src/routes/modules/certificates.ts:36
-#: src/views/certificate/CertificateEditor.vue:85
+#: src/views/certificate/CertificateEditor.vue:83
 msgid "Modify Certificate"
 msgstr "Sửa đổi chứng chỉ"
 
@@ -2867,7 +2899,7 @@ msgstr "Chỉ thị nhiều dòng"
 #: src/components/NgxConfigEditor/NgxUpstream.vue:199
 #: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/certificate/ACMEUser.vue:11
-#: src/views/certificate/CertificateEditor.vue:162
+#: src/views/certificate/CertificateEditor.vue:160
 #: src/views/certificate/CertificateList/certColumns.tsx:9
 #: src/views/certificate/DNSCredential.vue:9
 #: src/views/config/components/ConfigRightPanel/Basic.vue:34
@@ -2927,7 +2959,7 @@ msgstr "Đường dẫn mới"
 msgid "New version released"
 msgstr "Đã có phiên bản mới"
 
-#: src/views/certificate/components/WildcardCertificate.vue:89
+#: src/views/certificate/components/DNSIssueCertificate.vue:181
 #: src/views/install/components/InstallView.vue:96
 #: src/views/site/site_add/SiteAdd.vue:132
 #: src/views/site/site_edit/components/Cert/ObtainCert.vue:214
@@ -3514,6 +3546,10 @@ msgstr ""
 msgid "Please enter a valid port range"
 msgstr "Vui lòng nhập một phạm vi cổng hợp lệ"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:74
+msgid "Please enter at least one domain"
+msgstr "Vui lòng nhập ít nhất một tên miền"
+
 #: src/components/TwoFA/Authorization.vue:85
 msgid "Please enter the OTP code:"
 msgstr "Vui lòng nhập mã OTP:"
@@ -3847,6 +3883,7 @@ msgstr "Đang tải lại"
 msgid "Reloading nginx"
 msgstr "Tải lại nginx"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:144
 #: src/views/preference/tabs/AuthSettings.vue:137
 msgid "Remove"
 msgstr "Xóa"
@@ -3929,8 +3966,8 @@ msgstr "Đổi tên thành công"
 msgid "Renamed successfully"
 msgstr "Đổi tên thành công"
 
-#: src/views/certificate/components/RenewCert.vue:46
-#: src/views/certificate/components/RenewCert.vue:53
+#: src/views/certificate/components/RenewCert.vue:48
+#: src/views/certificate/components/RenewCert.vue:55
 msgid "Renew Certificate"
 msgstr "Gia hạn chứng chỉ"
 
@@ -3942,8 +3979,8 @@ msgstr "Lỗi gia hạn chứng chỉ"
 msgid "Renew Certificate Success"
 msgstr "Gia hạn chứng chỉ thành công"
 
-#: src/views/certificate/components/RenewCert.vue:28
-#: src/views/certificate/components/WildcardCertificate.vue:46
+#: src/views/certificate/components/DNSIssueCertificate.vue:84
+#: src/views/certificate/components/RenewCert.vue:30
 msgid "Renew successfully"
 msgstr "Gia hạn thành công"
 
@@ -4202,7 +4239,7 @@ msgstr "Thứ Bảy"
 
 #: src/components/ChatGPT/ChatMessage.vue:215
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:129
-#: src/language/curd.ts:18 src/views/certificate/CertificateEditor.vue:266
+#: src/language/curd.ts:18 src/views/certificate/CertificateEditor.vue:264
 #: src/views/config/components/ConfigLeftPanel.vue:282
 #: src/views/config/components/ConfigName.vue:59
 #: src/views/preference/components/AuthSettings/Passkey.vue:130
@@ -4257,7 +4294,7 @@ msgstr "Lưu luồng %{name} vào %{node} thất bại"
 msgid "Save stream %{name} to %{node} successfully"
 msgstr "Đã lưu luồng %{name} vào %{node} thành công"
 
-#: src/language/curd.ts:35 src/views/certificate/CertificateEditor.vue:49
+#: src/language/curd.ts:35 src/views/certificate/store.ts:13
 #: src/views/preference/store/index.ts:118
 msgid "Save successfully"
 msgstr "Lưu thành công"
@@ -4365,6 +4402,10 @@ msgstr "Gửi"
 msgid "Server"
 msgstr "Máy chủ"
 
+#: src/views/certificate/CertificateEditor.vue:55
+msgid "Server error"
+msgstr "Lỗi máy chủ"
+
 #: src/views/dashboard/ServerAnalytic.vue:175
 msgid "Server Info"
 msgstr "Thông tin máy chủ"
@@ -4501,7 +4542,7 @@ msgstr "Thời gian chờ giữa các lần lặp của bộ nạp bộ đệm"
 msgid "Sleep time between cache manager iterations"
 msgstr "Thời gian chờ giữa các lần lặp của trình quản lý bộ nhớ đệm"
 
-#: src/views/certificate/CertificateEditor.vue:213
+#: src/views/certificate/CertificateEditor.vue:211
 msgid "SSL Certificate Content"
 msgstr "Nội dung chứng chỉ SSL"
 
@@ -4513,15 +4554,15 @@ msgstr "Tệp chứng chỉ SSL phải nằm trong thư mục cấu hình Nginx:
 msgid "SSL certificate file not found"
 msgstr "Không tìm thấy tệp chứng chỉ SSL"
 
-#: src/views/certificate/CertificateEditor.vue:227
+#: src/views/certificate/CertificateEditor.vue:225
 msgid "SSL Certificate Key Content"
 msgstr "Nội dung khóa chứng chỉ SSL"
 
-#: src/views/certificate/CertificateEditor.vue:192
+#: src/views/certificate/CertificateEditor.vue:190
 msgid "SSL Certificate Key Path"
 msgstr "Đường dẫn khóa chứng chỉ SSL"
 
-#: src/views/certificate/CertificateEditor.vue:177
+#: src/views/certificate/CertificateEditor.vue:175
 #: src/views/preference/tabs/ServerSettings.vue:37
 msgid "SSL Certificate Path"
 msgstr "Đường dẫn chứng chỉ SSL"
@@ -4758,7 +4799,7 @@ msgstr "Nút đồng bộ"
 msgid "Sync strategy"
 msgstr "Chiến lược đồng bộ hóa"
 
-#: src/views/certificate/CertificateEditor.vue:206
+#: src/views/certificate/CertificateEditor.vue:204
 msgid "Sync to"
 msgstr "Đồng bộ tới"
 
@@ -4834,11 +4875,11 @@ msgstr ""
 "Số ICP chỉ được chứa chữ cái, unicode, số, dấu gạch ngang, dấu gạch dài, "
 "dấu hai chấm và dấu chấm."
 
-#: src/views/certificate/CertificateEditor.vue:216
+#: src/views/certificate/CertificateEditor.vue:214
 msgid "The input is not a SSL Certificate"
 msgstr "Đầu vào không phải là Chứng chỉ SSL"
 
-#: src/views/certificate/CertificateEditor.vue:230
+#: src/views/certificate/CertificateEditor.vue:228
 msgid "The input is not a SSL Certificate Key"
 msgstr "Đầu vào không phải là Khóa Chứng chỉ SSL"
 
@@ -4877,11 +4918,11 @@ msgstr ""
 msgid "The parameter of server_name is required"
 msgstr "Tham số của server_name là bắt buộc"
 
-#: src/views/certificate/CertificateEditor.vue:181
+#: src/views/certificate/CertificateEditor.vue:179
 msgid "The path exists, but the file is not a certificate"
 msgstr "Đường dẫn tồn tại, nhưng tệp không phải là chứng chỉ"
 
-#: src/views/certificate/CertificateEditor.vue:196
+#: src/views/certificate/CertificateEditor.vue:194
 msgid "The path exists, but the file is not a private key"
 msgstr "Đường dẫn tồn tại, nhưng tệp không phải là khóa riêng tư"
 
@@ -4943,17 +4984,17 @@ msgstr ""
 "trường hợp bạn mất mật khẩu và các yếu tố xác thực thứ hai. Nếu bạn không "
 "thể tìm thấy những mã này, bạn sẽ mất quyền truy cập vào tài khoản."
 
-#: src/views/certificate/CertificateEditor.vue:102
+#: src/views/certificate/CertificateEditor.vue:100
 msgid "This Auto Cert item is invalid, please remove it."
 msgstr "Mục Chứng chỉ tự động này không hợp lệ, vui lòng xóa nó"
 
-#: src/views/certificate/CertificateEditor.vue:92
+#: src/views/certificate/CertificateEditor.vue:90
 msgid "This certificate is managed by Nginx UI"
 msgstr "Chứng chỉ này được quản lý bởi Nginx UI"
 
-#: src/views/certificate/CertificateEditor.vue:165
-#: src/views/certificate/CertificateEditor.vue:179
-#: src/views/certificate/CertificateEditor.vue:194
+#: src/views/certificate/CertificateEditor.vue:163
+#: src/views/certificate/CertificateEditor.vue:177
+#: src/views/certificate/CertificateEditor.vue:192
 msgid "This field is required"
 msgstr "Trường này là bắt buộc"
 
@@ -5430,6 +5471,10 @@ msgstr "Khi bạn tạo mã khôi phục mới, bạn phải tải xuống hoặ
 msgid "Whether to use a temporary path when writing temporary files"
 msgstr "Có sử dụng đường dẫn tạm thời khi ghi các tập tin tạm thời hay không"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:107
+msgid "Wildcard Certificate"
+msgstr "Chứng Chỉ Wildcard"
+
 #: src/views/dashboard/components/ParamsOpt/PerformanceConfig.vue:48
 msgid "Worker Connections"
 msgstr "Kết nối worker"
@@ -5734,9 +5779,6 @@ msgstr "Khóa truy cập của bạn"
 #~ msgid "Incorrect username or password"
 #~ msgstr "Tên người dùng hoặc mật khẩu không chính xác"
 
-#~ msgid "Server error"
-#~ msgstr "Lỗi máy chủ"
-
 #, fuzzy
 #~ msgid ""
 #~ "Rename %{orig_path} to %{new_path} on %{env_name} failed, please upgrade "

+ 82 - 45
app/src/language/zh_CN/app.po

@@ -180,6 +180,10 @@ msgstr "添加配置"
 msgid "Add Directive Below"
 msgstr "在下面添加指令"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:151
+msgid "Add Domain"
+msgstr "添加域名"
+
 #: src/components/NgxConfigEditor/LocationEditor.vue:131
 #: src/components/NgxConfigEditor/LocationEditor.vue:158
 msgid "Add Location"
@@ -227,6 +231,12 @@ msgstr "全部"
 msgid "All Recovery Codes Have Been Used"
 msgstr "所有恢复代码已用完"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:156
+msgid ""
+"All selected subdomains must belong to the same DNS Provider, otherwise the "
+"certificate application will fail."
+msgstr "所有选定的子域名必须属于同一 DNS 提供商,否则证书申请将失败。"
+
 #: src/views/preference/tabs/OpenAISettings.vue:32
 msgid "API Base Url"
 msgstr "API 地址"
@@ -253,7 +263,7 @@ msgstr "应用"
 
 #: src/views/system/Upgrade.vue:195
 msgid "Arch"
-msgstr "\"架构\""
+msgstr "架构"
 
 #: src/views/preference/tabs/AuthSettings.vue:130
 msgid "Are you sure to delete this banned IP immediately?"
@@ -388,7 +398,7 @@ msgstr "成功关闭 %{name} 自动续签"
 msgid "Auto-renewal enabled for %{name}"
 msgstr "成功启用 %{name} 自动续签"
 
-#: src/views/certificate/components/RenewCert.vue:49
+#: src/views/certificate/components/RenewCert.vue:51
 #: src/views/site/site_edit/components/Cert/IssueCert.vue:84
 msgid "AutoCert is running, please wait..."
 msgstr "AutoCert 正在运行,请稍候..."
@@ -401,7 +411,7 @@ msgstr "AutoCert 正在运行..."
 msgid "Automatic Restart"
 msgstr "\"自动重启\""
 
-#: src/views/certificate/CertificateEditor.vue:259
+#: src/views/certificate/CertificateEditor.vue:257
 #: src/views/config/components/ConfigLeftPanel.vue:273
 #: src/views/config/ConfigList.vue:112 src/views/config/ConfigList.vue:195
 #: src/views/nginx_log/NginxLog.vue:174
@@ -692,12 +702,16 @@ msgstr "证书更新成功"
 msgid "Certificate revoked successfully"
 msgstr "证书撤销成功"
 
-#: src/views/certificate/CertificateEditor.vue:128
+#: src/views/certificate/CertificateEditor.vue:126
 #: src/views/site/site_edit/components/Cert/Cert.vue:58
 msgid "Certificate Status"
 msgid_plural "Certificates Status"
 msgstr[0] "证书状态"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:104
+msgid "Certificate Type"
+msgstr "证书类型"
+
 #: src/routes/modules/certificates.ts:11
 #: src/views/certificate/CertificateList/Certificate.vue:19
 msgid "Certificates"
@@ -1161,6 +1175,14 @@ msgstr "自定义 cron 表达式"
 msgid "Custom Directory"
 msgstr "自定义目录"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:126
+msgid "Custom Domains"
+msgstr "自定义域名"
+
+#: src/views/certificate/components/DNSIssueCertificate.vue:110
+msgid "Custom Domains Certificate"
+msgstr "自定义域名证书"
+
 #: src/views/preference/tabs/NodeSettings.vue:19
 msgid ""
 "Customize the name of local node to be displayed in the environment "
@@ -1468,11 +1490,11 @@ msgid "Document"
 msgid_plural "Documents"
 msgstr[0] "文档"
 
-#: src/views/certificate/components/WildcardCertificate.vue:66
+#: src/views/certificate/components/DNSIssueCertificate.vue:116
 msgid "Domain"
 msgstr "域名"
 
-#: src/views/certificate/CertificateEditor.vue:112
+#: src/views/certificate/CertificateEditor.vue:110
 msgid "Domains list is empty, try to reopen Auto Cert for %{config}"
 msgstr "域名列表为空,请尝试为 %{config} 重新打开证书自动续期"
 
@@ -1672,6 +1694,14 @@ msgstr "用 Let's Encrypt 对网站进行加密"
 msgid "End"
 msgstr "结束"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:135
+msgid "Enter domain name"
+msgstr "输入域名"
+
+#: src/views/certificate/components/DNSIssueCertificate.vue:120
+msgid "Enter your domain"
+msgstr "输入您的域名"
+
 #: src/language/constants.ts:22
 msgid "Environment variables cleaned"
 msgstr "环境变量已清理"
@@ -1746,7 +1776,7 @@ msgstr "外部 Docker 容器"
 msgid "External Notify"
 msgstr "外部通知"
 
-#: src/views/site/site_edit/components/Cert/ObtainCertLive.vue:80
+#: src/views/site/site_edit/components/Cert/ObtainCertLive.vue:81
 msgid "Fail to obtain certificate"
 msgstr "获取证书失败"
 
@@ -2267,7 +2297,7 @@ msgid "Import"
 msgstr "导入"
 
 #: src/routes/modules/certificates.ts:46
-#: src/views/certificate/CertificateEditor.vue:85
+#: src/views/certificate/CertificateEditor.vue:83
 msgid "Import Certificate"
 msgstr "导入证书"
 
@@ -2408,12 +2438,12 @@ msgid "IP"
 msgstr "IP"
 
 #: src/views/certificate/CertificateList/Certificate.vue:37
-msgid "Issue wildcard certificate"
-msgstr "签发通配符证书"
+msgid "Issue certificate"
+msgstr "签发证书"
 
-#: src/views/certificate/components/WildcardCertificate.vue:57
-msgid "Issue Wildcard Certificate"
-msgstr "颁发通配符证书"
+#: src/views/certificate/components/DNSIssueCertificate.vue:95
+msgid "Issue Certificate"
+msgstr "签发证书"
 
 #: src/language/constants.ts:20
 msgid "Issued certificate successfully"
@@ -2494,8 +2524,8 @@ msgstr "如果不想修改,请留空"
 msgid "Leave blank if you don't need this."
 msgstr "如果不需要,请留空。"
 
-#: src/views/certificate/CertificateEditor.vue:223
-#: src/views/certificate/CertificateEditor.vue:237
+#: src/views/certificate/CertificateEditor.vue:221
+#: src/views/certificate/CertificateEditor.vue:235
 msgid "Leave blank will not change anything"
 msgstr "留空不做任何更改"
 
@@ -2582,7 +2612,7 @@ msgstr "Location"
 msgid "Locations"
 msgstr "Locations"
 
-#: src/views/certificate/CertificateEditor.vue:247
+#: src/views/certificate/CertificateEditor.vue:245
 msgid "Log"
 msgstr "日志"
 
@@ -2789,7 +2819,7 @@ msgid "Modify"
 msgstr "修改"
 
 #: src/routes/modules/certificates.ts:36
-#: src/views/certificate/CertificateEditor.vue:85
+#: src/views/certificate/CertificateEditor.vue:83
 msgid "Modify Certificate"
 msgstr "修改证书"
 
@@ -2824,7 +2854,7 @@ msgstr "多行指令"
 #: src/components/NgxConfigEditor/NgxUpstream.vue:199
 #: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/certificate/ACMEUser.vue:11
-#: src/views/certificate/CertificateEditor.vue:162
+#: src/views/certificate/CertificateEditor.vue:160
 #: src/views/certificate/CertificateList/certColumns.tsx:9
 #: src/views/certificate/DNSCredential.vue:9
 #: src/views/config/components/ConfigRightPanel/Basic.vue:34
@@ -2884,7 +2914,7 @@ msgstr "新路径"
 msgid "New version released"
 msgstr "新版本发布"
 
-#: src/views/certificate/components/WildcardCertificate.vue:89
+#: src/views/certificate/components/DNSIssueCertificate.vue:181
 #: src/views/install/components/InstallView.vue:96
 #: src/views/site/site_add/SiteAdd.vue:132
 #: src/views/site/site_edit/components/Cert/ObtainCert.vue:214
@@ -3457,6 +3487,10 @@ msgstr "请为您要创建的 Passkey 输入一个名称,然后单击下面的
 msgid "Please enter a valid port range"
 msgstr "请输入有效的端口范围"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:74
+msgid "Please enter at least one domain"
+msgstr "请输入至少一个域名"
+
 #: src/components/TwoFA/Authorization.vue:85
 msgid "Please enter the OTP code:"
 msgstr "请输入 OTP:"
@@ -3782,6 +3816,7 @@ msgstr "重载中"
 msgid "Reloading nginx"
 msgstr "正在重载 Nginx"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:144
 #: src/views/preference/tabs/AuthSettings.vue:137
 msgid "Remove"
 msgstr "删除"
@@ -3864,8 +3899,8 @@ msgstr "重命名成功"
 msgid "Renamed successfully"
 msgstr "重命名成功"
 
-#: src/views/certificate/components/RenewCert.vue:46
-#: src/views/certificate/components/RenewCert.vue:53
+#: src/views/certificate/components/RenewCert.vue:48
+#: src/views/certificate/components/RenewCert.vue:55
 msgid "Renew Certificate"
 msgstr "更新证书"
 
@@ -3877,8 +3912,8 @@ msgstr "证书续期错误"
 msgid "Renew Certificate Success"
 msgstr "证书续期成功"
 
-#: src/views/certificate/components/RenewCert.vue:28
-#: src/views/certificate/components/WildcardCertificate.vue:46
+#: src/views/certificate/components/DNSIssueCertificate.vue:84
+#: src/views/certificate/components/RenewCert.vue:30
 msgid "Renew successfully"
 msgstr "更新成功"
 
@@ -4132,7 +4167,7 @@ msgstr "星期六"
 
 #: src/components/ChatGPT/ChatMessage.vue:215
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:129
-#: src/language/curd.ts:18 src/views/certificate/CertificateEditor.vue:266
+#: src/language/curd.ts:18 src/views/certificate/CertificateEditor.vue:264
 #: src/views/config/components/ConfigLeftPanel.vue:282
 #: src/views/config/components/ConfigName.vue:59
 #: src/views/preference/components/AuthSettings/Passkey.vue:130
@@ -4187,7 +4222,7 @@ msgstr "部署 %{name} 到 %{node} 失败"
 msgid "Save stream %{name} to %{node} successfully"
 msgstr "成功将站点 %{name} 保存到 %{node} 中"
 
-#: src/language/curd.ts:35 src/views/certificate/CertificateEditor.vue:49
+#: src/language/curd.ts:35 src/views/certificate/store.ts:13
 #: src/views/preference/store/index.ts:118
 msgid "Save successfully"
 msgstr "保存成功"
@@ -4295,6 +4330,10 @@ msgstr "上传"
 msgid "Server"
 msgstr "服务器"
 
+#: src/views/certificate/CertificateEditor.vue:55
+msgid "Server error"
+msgstr "服务器错误"
+
 #: src/views/dashboard/ServerAnalytic.vue:175
 msgid "Server Info"
 msgstr "服务器信息"
@@ -4429,7 +4468,7 @@ msgstr "缓存加载器迭代之间的休眠时间"
 msgid "Sleep time between cache manager iterations"
 msgstr "缓存管理器迭代之间的休眠时间"
 
-#: src/views/certificate/CertificateEditor.vue:213
+#: src/views/certificate/CertificateEditor.vue:211
 msgid "SSL Certificate Content"
 msgstr "SSL 证书内容"
 
@@ -4441,15 +4480,15 @@ msgstr "SSL 证书文件必须位于 Nginx 配置目录下:{0}"
 msgid "SSL certificate file not found"
 msgstr "未找到 SSL 证书文件"
 
-#: src/views/certificate/CertificateEditor.vue:227
+#: src/views/certificate/CertificateEditor.vue:225
 msgid "SSL Certificate Key Content"
 msgstr "SSL 证书密钥内容"
 
-#: src/views/certificate/CertificateEditor.vue:192
+#: src/views/certificate/CertificateEditor.vue:190
 msgid "SSL Certificate Key Path"
 msgstr "SSL证书密钥路径"
 
-#: src/views/certificate/CertificateEditor.vue:177
+#: src/views/certificate/CertificateEditor.vue:175
 #: src/views/preference/tabs/ServerSettings.vue:37
 msgid "SSL Certificate Path"
 msgstr "SSL证书路径"
@@ -4680,7 +4719,7 @@ msgstr "同步节点"
 msgid "Sync strategy"
 msgstr "同步策略"
 
-#: src/views/certificate/CertificateEditor.vue:206
+#: src/views/certificate/CertificateEditor.vue:204
 msgid "Sync to"
 msgstr "同步到"
 
@@ -4751,11 +4790,11 @@ msgid ""
 "dashes, colons, and dots."
 msgstr "ICP 备案号只能包含字母、单码、数字、连字符、破折号、冒号和点。"
 
-#: src/views/certificate/CertificateEditor.vue:216
+#: src/views/certificate/CertificateEditor.vue:214
 msgid "The input is not a SSL Certificate"
 msgstr "输入的内容不是 SSL 证书"
 
-#: src/views/certificate/CertificateEditor.vue:230
+#: src/views/certificate/CertificateEditor.vue:228
 msgid "The input is not a SSL Certificate Key"
 msgstr "输入的内容不是 SSL 证书密钥"
 
@@ -4786,11 +4825,11 @@ msgstr "节点名称只能包含字母、统一码、数字、连字符、破折
 msgid "The parameter of server_name is required"
 msgstr "必须为 server_name 指令指明参数"
 
-#: src/views/certificate/CertificateEditor.vue:181
+#: src/views/certificate/CertificateEditor.vue:179
 msgid "The path exists, but the file is not a certificate"
 msgstr "路径存在,但文件不是证书"
 
-#: src/views/certificate/CertificateEditor.vue:196
+#: src/views/certificate/CertificateEditor.vue:194
 msgid "The path exists, but the file is not a private key"
 msgstr "路径存在,但文件不是私钥"
 
@@ -4842,17 +4881,17 @@ msgid ""
 "lose access to your account."
 msgstr "这些代码是在您丢失密码和双重身份验证方式时,访问账户的最后手段。如果找不到这些代码,您将无法再访问您的账户。"
 
-#: src/views/certificate/CertificateEditor.vue:102
+#: src/views/certificate/CertificateEditor.vue:100
 msgid "This Auto Cert item is invalid, please remove it."
 msgstr "这个证书自动续期项目是无效的,请删除。"
 
-#: src/views/certificate/CertificateEditor.vue:92
+#: src/views/certificate/CertificateEditor.vue:90
 msgid "This certificate is managed by Nginx UI"
 msgstr "该证书由 Nginx UI 托管"
 
-#: src/views/certificate/CertificateEditor.vue:165
-#: src/views/certificate/CertificateEditor.vue:179
-#: src/views/certificate/CertificateEditor.vue:194
+#: src/views/certificate/CertificateEditor.vue:163
+#: src/views/certificate/CertificateEditor.vue:177
+#: src/views/certificate/CertificateEditor.vue:192
 msgid "This field is required"
 msgstr "此字段必填"
 
@@ -5293,6 +5332,10 @@ msgstr "当您生成新的恢复代码时,必须下载或打印新的代码。
 msgid "Whether to use a temporary path when writing temporary files"
 msgstr "写入临时文件时是否使用临时路径"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:107
+msgid "Wildcard Certificate"
+msgstr "通配符证书"
+
 #: src/views/dashboard/components/ParamsOpt/PerformanceConfig.vue:48
 msgid "Worker Connections"
 msgstr "工作进程连接数"
@@ -5552,9 +5595,6 @@ msgstr "你的 Passkeys"
 #~ msgid "Do you want to disable this site?"
 #~ msgstr "你想停用这个网站吗?"
 
-#~ msgid "Enter Maintenance"
-#~ msgstr "进入维护"
-
 #~ msgid "URLs"
 #~ msgstr "链接"
 
@@ -5644,9 +5684,6 @@ msgstr "你的 Passkeys"
 #~ msgid "File"
 #~ msgstr "文件"
 
-#~ msgid "Server error"
-#~ msgstr "服务器错误"
-
 #~ msgid "Incorrect username or password"
 #~ msgstr "用户名或密码错误"
 

+ 82 - 42
app/src/language/zh_TW/app.po

@@ -184,6 +184,10 @@ msgstr "新增設定"
 msgid "Add Directive Below"
 msgstr "在下方新增指令"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:151
+msgid "Add Domain"
+msgstr "添加域名"
+
 #: src/components/NgxConfigEditor/LocationEditor.vue:131
 #: src/components/NgxConfigEditor/LocationEditor.vue:158
 msgid "Add Location"
@@ -231,6 +235,12 @@ msgstr "全部"
 msgid "All Recovery Codes Have Been Used"
 msgstr "所有恢復代碼已用完"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:156
+msgid ""
+"All selected subdomains must belong to the same DNS Provider, otherwise the "
+"certificate application will fail."
+msgstr "所有選定的子網域名稱必須屬於同一 DNS 提供商,否則證書申請將失敗。"
+
 #: src/views/preference/tabs/OpenAISettings.vue:32
 msgid "API Base Url"
 msgstr "API 基礎網址"
@@ -257,7 +267,7 @@ msgstr "應用"
 
 #: src/views/system/Upgrade.vue:195
 msgid "Arch"
-msgstr "\"架構\""
+msgstr "架構"
 
 #: src/views/preference/tabs/AuthSettings.vue:130
 msgid "Are you sure to delete this banned IP immediately?"
@@ -392,7 +402,7 @@ msgstr "已關閉 %{name} 的自動續簽"
 msgid "Auto-renewal enabled for %{name}"
 msgstr "已啟用 %{name} 的自動續簽"
 
-#: src/views/certificate/components/RenewCert.vue:49
+#: src/views/certificate/components/RenewCert.vue:51
 #: src/views/site/site_edit/components/Cert/IssueCert.vue:84
 msgid "AutoCert is running, please wait..."
 msgstr "AutoCert 正在執行中,請稍候..."
@@ -405,7 +415,7 @@ msgstr "\"AutoCert 正在運行...\""
 msgid "Automatic Restart"
 msgstr "\"自動重啟\""
 
-#: src/views/certificate/CertificateEditor.vue:259
+#: src/views/certificate/CertificateEditor.vue:257
 #: src/views/config/components/ConfigLeftPanel.vue:273
 #: src/views/config/ConfigList.vue:112 src/views/config/ConfigList.vue:195
 #: src/views/nginx_log/NginxLog.vue:174
@@ -696,12 +706,16 @@ msgstr "憑證更新成功"
 msgid "Certificate revoked successfully"
 msgstr "證書撤銷成功"
 
-#: src/views/certificate/CertificateEditor.vue:128
+#: src/views/certificate/CertificateEditor.vue:126
 #: src/views/site/site_edit/components/Cert/Cert.vue:58
 msgid "Certificate Status"
 msgid_plural "Certificates Status"
 msgstr[0] "憑證狀態"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:104
+msgid "Certificate Type"
+msgstr "證書類型"
+
 #: src/routes/modules/certificates.ts:11
 #: src/views/certificate/CertificateList/Certificate.vue:19
 msgid "Certificates"
@@ -1165,6 +1179,14 @@ msgstr "自訂 cron 表達式"
 msgid "Custom Directory"
 msgstr "自訂目錄"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:126
+msgid "Custom Domains"
+msgstr "自訂網域"
+
+#: src/views/certificate/components/DNSIssueCertificate.vue:110
+msgid "Custom Domains Certificate"
+msgstr "自訂網域證書"
+
 #: src/views/preference/tabs/NodeSettings.vue:19
 msgid ""
 "Customize the name of local node to be displayed in the environment "
@@ -1472,11 +1494,11 @@ msgid "Document"
 msgid_plural "Documents"
 msgstr[0] "檔案"
 
-#: src/views/certificate/components/WildcardCertificate.vue:66
+#: src/views/certificate/components/DNSIssueCertificate.vue:116
 msgid "Domain"
 msgstr "網域"
 
-#: src/views/certificate/CertificateEditor.vue:112
+#: src/views/certificate/CertificateEditor.vue:110
 msgid "Domains list is empty, try to reopen Auto Cert for %{config}"
 msgstr "網域列表為空,請嘗試重新開啟 %{config} 的自動憑證"
 
@@ -1676,6 +1698,14 @@ msgstr "用 Let's Encrypt 對網站進行加密"
 msgid "End"
 msgstr "結束"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:135
+msgid "Enter domain name"
+msgstr "輸入網域名稱"
+
+#: src/views/certificate/components/DNSIssueCertificate.vue:120
+msgid "Enter your domain"
+msgstr "輸入您的域名"
+
 #: src/language/constants.ts:22
 msgid "Environment variables cleaned"
 msgstr "環境變數已清理"
@@ -1750,7 +1780,7 @@ msgstr "外部 Docker 容器"
 msgid "External Notify"
 msgstr "外部通知"
 
-#: src/views/site/site_edit/components/Cert/ObtainCertLive.vue:80
+#: src/views/site/site_edit/components/Cert/ObtainCertLive.vue:81
 msgid "Fail to obtain certificate"
 msgstr "取得憑證失敗"
 
@@ -2271,7 +2301,7 @@ msgid "Import"
 msgstr "匯入"
 
 #: src/routes/modules/certificates.ts:46
-#: src/views/certificate/CertificateEditor.vue:85
+#: src/views/certificate/CertificateEditor.vue:83
 msgid "Import Certificate"
 msgstr "匯入憑證"
 
@@ -2412,12 +2442,12 @@ msgid "IP"
 msgstr "IP"
 
 #: src/views/certificate/CertificateList/Certificate.vue:37
-msgid "Issue wildcard certificate"
-msgstr "發行萬用字元憑證"
+msgid "Issue certificate"
+msgstr "發證"
 
-#: src/views/certificate/components/WildcardCertificate.vue:57
-msgid "Issue Wildcard Certificate"
-msgstr "簽發萬用字元憑證"
+#: src/views/certificate/components/DNSIssueCertificate.vue:95
+msgid "Issue Certificate"
+msgstr "簽發證"
 
 #: src/language/constants.ts:20
 msgid "Issued certificate successfully"
@@ -2498,8 +2528,8 @@ msgstr "留空表示不修改"
 msgid "Leave blank if you don't need this."
 msgstr "留空表示不需要此項目。"
 
-#: src/views/certificate/CertificateEditor.vue:223
-#: src/views/certificate/CertificateEditor.vue:237
+#: src/views/certificate/CertificateEditor.vue:221
+#: src/views/certificate/CertificateEditor.vue:235
 msgid "Leave blank will not change anything"
 msgstr "留空將不會改變任何內容"
 
@@ -2586,7 +2616,7 @@ msgstr "Location"
 msgid "Locations"
 msgstr "Locations"
 
-#: src/views/certificate/CertificateEditor.vue:247
+#: src/views/certificate/CertificateEditor.vue:245
 msgid "Log"
 msgstr "日誌"
 
@@ -2793,7 +2823,7 @@ msgid "Modify"
 msgstr "修改"
 
 #: src/routes/modules/certificates.ts:36
-#: src/views/certificate/CertificateEditor.vue:85
+#: src/views/certificate/CertificateEditor.vue:83
 msgid "Modify Certificate"
 msgstr "修改憑證"
 
@@ -2828,7 +2858,7 @@ msgstr "多行指令"
 #: src/components/NgxConfigEditor/NgxUpstream.vue:199
 #: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/certificate/ACMEUser.vue:11
-#: src/views/certificate/CertificateEditor.vue:162
+#: src/views/certificate/CertificateEditor.vue:160
 #: src/views/certificate/CertificateList/certColumns.tsx:9
 #: src/views/certificate/DNSCredential.vue:9
 #: src/views/config/components/ConfigRightPanel/Basic.vue:34
@@ -2888,7 +2918,7 @@ msgstr "新路徑"
 msgid "New version released"
 msgstr "新版本發布"
 
-#: src/views/certificate/components/WildcardCertificate.vue:89
+#: src/views/certificate/components/DNSIssueCertificate.vue:181
 #: src/views/install/components/InstallView.vue:96
 #: src/views/site/site_add/SiteAdd.vue:132
 #: src/views/site/site_edit/components/Cert/ObtainCert.vue:214
@@ -3463,6 +3493,10 @@ msgstr "請輸入您希望建立的通行金鑰名稱,並點選下面的確定
 msgid "Please enter a valid port range"
 msgstr "請輸入有效的端口範圍"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:74
+msgid "Please enter at least one domain"
+msgstr "請輸入至少一個域名"
+
 #: src/components/TwoFA/Authorization.vue:85
 msgid "Please enter the OTP code:"
 msgstr "請輸入 OTP 代碼:"
@@ -3788,6 +3822,7 @@ msgstr "重新載入中"
 msgid "Reloading nginx"
 msgstr "正在重新載入 Nginx"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:144
 #: src/views/preference/tabs/AuthSettings.vue:137
 msgid "Remove"
 msgstr "移除"
@@ -3870,8 +3905,8 @@ msgstr "重新命名成功"
 msgid "Renamed successfully"
 msgstr "重新命名成功"
 
-#: src/views/certificate/components/RenewCert.vue:46
-#: src/views/certificate/components/RenewCert.vue:53
+#: src/views/certificate/components/RenewCert.vue:48
+#: src/views/certificate/components/RenewCert.vue:55
 msgid "Renew Certificate"
 msgstr "更換憑證"
 
@@ -3883,8 +3918,8 @@ msgstr "更新憑證錯誤"
 msgid "Renew Certificate Success"
 msgstr "更新憑證成功"
 
-#: src/views/certificate/components/RenewCert.vue:28
-#: src/views/certificate/components/WildcardCertificate.vue:46
+#: src/views/certificate/components/DNSIssueCertificate.vue:84
+#: src/views/certificate/components/RenewCert.vue:30
 msgid "Renew successfully"
 msgstr "更新成功"
 
@@ -4138,7 +4173,7 @@ msgstr "星期六"
 
 #: src/components/ChatGPT/ChatMessage.vue:215
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:129
-#: src/language/curd.ts:18 src/views/certificate/CertificateEditor.vue:266
+#: src/language/curd.ts:18 src/views/certificate/CertificateEditor.vue:264
 #: src/views/config/components/ConfigLeftPanel.vue:282
 #: src/views/config/components/ConfigName.vue:59
 #: src/views/preference/components/AuthSettings/Passkey.vue:130
@@ -4193,7 +4228,7 @@ msgstr "儲存串流 %{name} 至 %{node} 失敗"
 msgid "Save stream %{name} to %{node} successfully"
 msgstr "串流 %{name} 成功儲存至 %{node}"
 
-#: src/language/curd.ts:35 src/views/certificate/CertificateEditor.vue:49
+#: src/language/curd.ts:35 src/views/certificate/store.ts:13
 #: src/views/preference/store/index.ts:118
 msgid "Save successfully"
 msgstr "儲存成功"
@@ -4301,6 +4336,10 @@ msgstr "傳送"
 msgid "Server"
 msgstr "伺服器"
 
+#: src/views/certificate/CertificateEditor.vue:55
+msgid "Server error"
+msgstr "伺服器錯誤"
+
 #: src/views/dashboard/ServerAnalytic.vue:175
 msgid "Server Info"
 msgstr "伺服器資訊"
@@ -4435,7 +4474,7 @@ msgstr "快取載入器迭代間的休眠時間"
 msgid "Sleep time between cache manager iterations"
 msgstr "快取管理器迭代間的休眠時間"
 
-#: src/views/certificate/CertificateEditor.vue:213
+#: src/views/certificate/CertificateEditor.vue:211
 msgid "SSL Certificate Content"
 msgstr "SSL 憑證內容"
 
@@ -4447,15 +4486,15 @@ msgstr "SSL 憑證檔案必須位於 Nginx 設定目錄下:{0}"
 msgid "SSL certificate file not found"
 msgstr "SSL 憑證檔案未找到"
 
-#: src/views/certificate/CertificateEditor.vue:227
+#: src/views/certificate/CertificateEditor.vue:225
 msgid "SSL Certificate Key Content"
 msgstr "SSL 憑證金鑰內容"
 
-#: src/views/certificate/CertificateEditor.vue:192
+#: src/views/certificate/CertificateEditor.vue:190
 msgid "SSL Certificate Key Path"
 msgstr "SSL 憑證金鑰路徑"
 
-#: src/views/certificate/CertificateEditor.vue:177
+#: src/views/certificate/CertificateEditor.vue:175
 #: src/views/preference/tabs/ServerSettings.vue:37
 msgid "SSL Certificate Path"
 msgstr "SSL 憑證路徑"
@@ -4686,7 +4725,7 @@ msgstr "同步節點"
 msgid "Sync strategy"
 msgstr "同步策略"
 
-#: src/views/certificate/CertificateEditor.vue:206
+#: src/views/certificate/CertificateEditor.vue:204
 msgid "Sync to"
 msgstr "同步到"
 
@@ -4757,11 +4796,11 @@ msgid ""
 "dashes, colons, and dots."
 msgstr "ICP 編號僅能包含字母、Unicode 字元、數字、連字號、破折號、冒號和句點。"
 
-#: src/views/certificate/CertificateEditor.vue:216
+#: src/views/certificate/CertificateEditor.vue:214
 msgid "The input is not a SSL Certificate"
 msgstr "輸入的不是 SSL 憑證"
 
-#: src/views/certificate/CertificateEditor.vue:230
+#: src/views/certificate/CertificateEditor.vue:228
 msgid "The input is not a SSL Certificate Key"
 msgstr "輸入的不是 SSL 憑證金鑰"
 
@@ -4792,11 +4831,11 @@ msgstr "節點名稱僅能包含字母、Unicode 字元、數字、連字號、
 msgid "The parameter of server_name is required"
 msgstr "必須提供 server_name 參數"
 
-#: src/views/certificate/CertificateEditor.vue:181
+#: src/views/certificate/CertificateEditor.vue:179
 msgid "The path exists, but the file is not a certificate"
 msgstr "路徑存在,但檔案不是憑證"
 
-#: src/views/certificate/CertificateEditor.vue:196
+#: src/views/certificate/CertificateEditor.vue:194
 msgid "The path exists, but the file is not a private key"
 msgstr "路徑存在,但檔案不是金鑰"
 
@@ -4848,17 +4887,17 @@ msgid ""
 "lose access to your account."
 msgstr "這些代碼是您在遺失密碼和第二重驗證因素時,存取帳戶的最後手段。如果您找不到這些代碼,您將無法再存取您的帳戶。"
 
-#: src/views/certificate/CertificateEditor.vue:102
+#: src/views/certificate/CertificateEditor.vue:100
 msgid "This Auto Cert item is invalid, please remove it."
 msgstr "此自動憑證項目無效,請將其移除。"
 
-#: src/views/certificate/CertificateEditor.vue:92
+#: src/views/certificate/CertificateEditor.vue:90
 msgid "This certificate is managed by Nginx UI"
 msgstr "此憑證由 Nginx UI 管理"
 
-#: src/views/certificate/CertificateEditor.vue:165
-#: src/views/certificate/CertificateEditor.vue:179
-#: src/views/certificate/CertificateEditor.vue:194
+#: src/views/certificate/CertificateEditor.vue:163
+#: src/views/certificate/CertificateEditor.vue:177
+#: src/views/certificate/CertificateEditor.vue:192
 msgid "This field is required"
 msgstr "此欄位為必填項"
 
@@ -5299,6 +5338,10 @@ msgstr "當您產生新的復原碼時,必須下載或列印新的代碼。"
 msgid "Whether to use a temporary path when writing temporary files"
 msgstr "寫入暫存檔案時是否使用暫存路徑"
 
+#: src/views/certificate/components/DNSIssueCertificate.vue:107
+msgid "Wildcard Certificate"
+msgstr "萬用字元憑證"
+
 #: src/views/dashboard/components/ParamsOpt/PerformanceConfig.vue:48
 msgid "Worker Connections"
 msgstr "工作進程連接數"
@@ -5619,9 +5662,6 @@ msgstr "您的通行金鑰"
 #~ msgid "File"
 #~ msgstr "檔案"
 
-#~ msgid "Server error"
-#~ msgstr "伺服器錯誤"
-
 #~ msgid "Incorrect username or password"
 #~ msgstr "使用者名稱或密碼不正確"
 

+ 8 - 13
app/src/views/certificate/CertificateEditor.vue

@@ -10,14 +10,18 @@ import FooterToolBar from '@/components/FooterToolbar'
 import NodeSelector from '@/components/NodeSelector'
 import { AutoCertState } from '@/constants'
 import RenewCert from './components/RenewCert.vue'
+import { useCertStore } from './store'
 
 const route = useRoute()
+const certStore = useCertStore()
+const router = useRouter()
+const errors = ref({}) as Ref<Record<string, string>>
 
 const id = computed(() => {
   return Number.parseInt(route.params.id as string)
 })
 
-const data = ref({}) as Ref<Cert>
+const { data } = storeToRefs(certStore)
 
 const notShowInAutoCert = computed(() => {
   return data.value.auto_cert !== AutoCertState.Enable
@@ -38,28 +42,19 @@ onMounted(() => {
   init()
 })
 
-const router = useRouter()
-const errors = ref({}) as Ref<Record<string, string>>
-
 async function save() {
   try {
-    const r = data.value.id
-      ? await cert.updateItem(data.value.id, data.value)
-      : await cert.createItem(data.value)
-    data.value = r
+    await certStore.save()
     errors.value = {}
-    message.success($gettext('Save successfully'))
-    await router.push(`/certificates/${r.id}`)
+    await router.push(`/certificates/${certStore.data.id}`)
   }
   // eslint-disable-next-line ts/no-explicit-any
   catch (e: any) {
     errors.value = e.errors
-    throw e
+    message.error(e.message ?? $gettext('Server error'))
   }
 }
 
-provide('saveCert', save)
-
 const log = computed(() => {
   if (!data.value.log)
     return ''

+ 3 - 3
app/src/views/certificate/CertificateList/Certificate.vue

@@ -3,8 +3,8 @@ import { CloudUploadOutlined, SafetyCertificateOutlined } from '@ant-design/icon
 import { StdTable } from '@uozi-admin/curd'
 import cert from '@/api/cert'
 import { useGlobalStore } from '@/pinia'
+import WildcardCertificate from '../components/DNSIssueCertificate.vue'
 import RemoveCert from '../components/RemoveCert.vue'
-import WildcardCertificate from '../components/WildcardCertificate.vue'
 import certColumns from './certColumns'
 
 const refWildcard = ref()
@@ -34,7 +34,7 @@ const { processingStatus } = storeToRefs(globalStore)
         @click="() => refWildcard.open()"
       >
         <SafetyCertificateOutlined />
-        {{ $gettext('Issue wildcard certificate') }}
+        {{ $gettext('Issue certificate') }}
       </AButton>
     </template>
     <StdTable
@@ -56,7 +56,7 @@ const { processingStatus } = storeToRefs(globalStore)
     </StdTable>
     <WildcardCertificate
       ref="refWildcard"
-      @issued="() => refTable.get_list()"
+      @issued="() => refTable.refresh()"
     />
   </ACard>
 </template>

+ 201 - 0
app/src/views/certificate/components/DNSIssueCertificate.vue

@@ -0,0 +1,201 @@
+<script setup lang="ts">
+import type { Ref } from 'vue'
+import type { AutoCertOptions } from '@/api/auto_cert'
+import { message } from 'ant-design-vue'
+import AutoCertForm from '@/components/AutoCertForm'
+import ObtainCertLive from '@/views/site/site_edit/components/Cert/ObtainCertLive.vue'
+
+const emit = defineEmits<{
+  issued: [void]
+}>()
+
+const step = ref(0)
+const visible = ref(false)
+const data = ref({}) as Ref<AutoCertOptions>
+const domain = ref('')
+const certType = ref<'wildcard' | 'custom'>('wildcard')
+const customDomains = ref<string[]>([''])
+
+function open() {
+  visible.value = true
+  step.value = 0
+  data.value = {
+    challenge_method: 'dns01',
+    key_type: 'P256',
+  } as AutoCertOptions
+  domain.value = ''
+  certType.value = 'wildcard'
+  customDomains.value = ['']
+}
+
+defineExpose({
+  open,
+})
+
+const modalVisible = ref(false)
+const modalClosable = ref(true)
+
+const refObtainCertLive = useTemplateRef('refObtainCertLive')
+
+const computedDomain = computed(() => {
+  return `*.${domain.value}`
+})
+
+const computedDomains = computed(() => {
+  if (certType.value === 'wildcard') {
+    return [computedDomain.value, domain.value]
+  }
+  else {
+    return customDomains.value.filter(d => d.trim())
+  }
+})
+
+const computedMainDomain = computed(() => {
+  if (certType.value === 'wildcard') {
+    return computedDomain.value
+  }
+  else {
+    return customDomains.value.find(d => d.trim()) || ''
+  }
+})
+
+function addCustomDomain() {
+  customDomains.value.push('')
+}
+
+function removeCustomDomain(index: number) {
+  if (customDomains.value.length > 1) {
+    customDomains.value.splice(index, 1)
+  }
+}
+
+function issueCert() {
+  if (certType.value === 'custom') {
+    const validDomains = customDomains.value.filter(d => d.trim())
+    if (validDomains.length === 0) {
+      message.error($gettext('Please enter at least one domain'))
+      return
+    }
+  }
+
+  step.value++
+  modalVisible.value = true
+
+  refObtainCertLive.value?.issue_cert(computedMainDomain.value, computedDomains.value, data.value.key_type)
+    .then(() => {
+      message.success($gettext('Renew successfully'))
+      emit('issued')
+    })
+}
+</script>
+
+<template>
+  <div>
+    <AModal
+      v-model:open="visible"
+      :mask="false"
+      :title="$gettext('Issue Certificate')"
+      destroy-on-close
+      :footer="null"
+      :mask-closable="modalClosable"
+      :closable="modalClosable"
+      force-render
+    >
+      <template v-if="step === 0">
+        <AForm layout="vertical">
+          <AFormItem :label="$gettext('Certificate Type')">
+            <ASelect v-model:value="certType">
+              <ASelectOption value="wildcard">
+                {{ $gettext('Wildcard Certificate') }}
+              </ASelectOption>
+              <ASelectOption value="custom">
+                {{ $gettext('Custom Domains Certificate') }}
+              </ASelectOption>
+            </ASelect>
+          </AFormItem>
+
+          <template v-if="certType === 'wildcard'">
+            <AFormItem :label="$gettext('Domain')">
+              <AInput
+                v-model:value="domain"
+                addon-before="*."
+                :placeholder="$gettext('Enter your domain')"
+              />
+            </AFormItem>
+          </template>
+
+          <template v-else>
+            <AFormItem :label="$gettext('Custom Domains')">
+              <div class="space-y-2">
+                <div
+                  v-for="(_, index) in customDomains"
+                  :key="index"
+                  class="flex items-center gap-2"
+                >
+                  <AInput
+                    v-model:value="customDomains[index]"
+                    :placeholder="$gettext('Enter domain name')"
+                    class="flex-1"
+                  />
+                  <AButton
+                    v-if="customDomains.length > 1"
+                    type="link"
+                    danger
+                    @click="removeCustomDomain(index)"
+                  >
+                    {{ $gettext('Remove') }}
+                  </AButton>
+                </div>
+                <AButton
+                  block
+                  @click="addCustomDomain"
+                >
+                  {{ $gettext('Add Domain') }}
+                </AButton>
+              </div>
+
+              <AAlert
+                :message="$gettext('All selected subdomains must belong to the same DNS Provider, otherwise the certificate application will fail.')"
+                type="info"
+                show-icon
+                banner
+                class="mt-3"
+              />
+            </AFormItem>
+          </template>
+        </AForm>
+
+        <AutoCertForm
+          v-model:options="data"
+          style="max-width: 600px"
+          hide-note
+          force-dns-challenge
+        />
+
+        <div
+          v-if="step === 0"
+          class="flex justify-end"
+        >
+          <AButton
+            type="primary"
+            @click="issueCert"
+          >
+            {{ $gettext('Next') }}
+          </AButton>
+        </div>
+      </template>
+
+      <ObtainCertLive
+        v-show="step === 1"
+        ref="refObtainCertLive"
+        v-model:modal-closable="modalClosable"
+        v-model:modal-visible="modalVisible"
+        :options="data"
+      />
+    </AModal>
+  </div>
+</template>
+
+<style scoped lang="less">
+
+</style>

+ 4 - 2
app/src/views/certificate/components/RenewCert.vue

@@ -3,6 +3,7 @@ import type { AutoCertOptions } from '@/api/auto_cert'
 import { message } from 'ant-design-vue'
 import { useGlobalStore } from '@/pinia'
 import ObtainCertLive from '@/views/site/site_edit/components/Cert/ObtainCertLive.vue'
+import { useCertStore } from '../store'
 
 const props = defineProps<{
   options: AutoCertOptions
@@ -12,13 +13,14 @@ const emit = defineEmits<{
   renewed: [void]
 }>()
 
+const certStore = useCertStore()
+
 const modalVisible = ref(false)
 const modalClosable = ref(true)
 const refObtainCertLive = useTemplateRef('refObtainCertLive')
-const saveCert = inject<() => Promise<void>>('saveCert')!
 
 async function issueCert() {
-  await saveCert()
+  await certStore.save()
 
   modalVisible.value = true
 

+ 0 - 107
app/src/views/certificate/components/WildcardCertificate.vue

@@ -1,107 +0,0 @@
-<script setup lang="ts">
-import type { Ref } from 'vue'
-import type { AutoCertOptions } from '@/api/auto_cert'
-import { message } from 'ant-design-vue'
-import AutoCertForm from '@/components/AutoCertForm'
-import ObtainCertLive from '@/views/site/site_edit/components/Cert/ObtainCertLive.vue'
-
-const emit = defineEmits<{
-  issued: [void]
-}>()
-
-const step = ref(0)
-const visible = ref(false)
-const data = ref({}) as Ref<AutoCertOptions>
-const domain = ref('')
-
-function open() {
-  visible.value = true
-  step.value = 0
-  data.value = {
-    challenge_method: 'dns01',
-    key_type: 'P256',
-  } as AutoCertOptions
-  domain.value = ''
-}
-
-defineExpose({
-  open,
-})
-
-const modalVisible = ref(false)
-const modalClosable = ref(true)
-
-const refObtainCertLive = useTemplateRef('refObtainCertLive')
-
-const computedDomain = computed(() => {
-  return `*.${domain.value}`
-})
-
-function issueCert() {
-  step.value++
-  modalVisible.value = true
-
-  refObtainCertLive.value?.issue_cert(computedDomain.value, [computedDomain.value, domain.value], data.value.key_type)
-    .then(() => {
-      message.success($gettext('Renew successfully'))
-      emit('issued')
-    })
-}
-</script>
-
-<template>
-  <div>
-    <AModal
-      v-model:open="visible"
-      :mask="false"
-      :title="$gettext('Issue Wildcard Certificate')"
-      destroy-on-close
-      :footer="null"
-      :mask-closable="modalClosable"
-      :closable="modalClosable"
-      force-render
-    >
-      <template v-if="step === 0">
-        <AForm layout="vertical">
-          <AFormItem :label="$gettext('Domain')">
-            <AInput
-              v-model:value="domain"
-              addon-before="*."
-            />
-          </AFormItem>
-        </AForm>
-
-        <AutoCertForm
-          v-model:options="data"
-          style="max-width: 600px"
-          hide-note
-          force-dns-challenge
-        />
-
-        <div
-          v-if="step === 0"
-          class="flex justify-end"
-        >
-          <AButton
-            type="primary"
-            @click="issueCert"
-          >
-            {{ $gettext('Next') }}
-          </AButton>
-        </div>
-      </template>
-
-      <ObtainCertLive
-        v-show="step === 1"
-        ref="refObtainCertLive"
-        v-model:modal-closable="modalClosable"
-        v-model:modal-visible="modalVisible"
-        :options="data"
-      />
-    </AModal>
-  </div>
-</template>
-
-<style scoped lang="less">
-
-</style>

+ 21 - 0
app/src/views/certificate/store.ts

@@ -0,0 +1,21 @@
+import type { Cert } from '@/api/cert'
+import { message } from 'ant-design-vue'
+import { defineStore } from 'pinia'
+import cert from '@/api/cert'
+
+export const useCertStore = defineStore('cert', () => {
+  const data = ref<Cert>({} as Cert)
+
+  async function save() {
+    const r = data.value.id
+      ? await cert.updateItem(data.value.id, data.value)
+      : await cert.createItem(data.value)
+    data.value = r
+    message.success($gettext('Save successfully'))
+  }
+
+  return {
+    data,
+    save,
+  }
+})

+ 1 - 0
app/src/views/site/site_edit/components/Cert/ObtainCertLive.vue

@@ -76,6 +76,7 @@ async function issue_cert(config_name: string, server_name: string[], key_type:
           }
           break
         case 'error':
+          modalClosable.value = true
           progressStatus.value = 'exception'
           reject($gettext('Fail to obtain certificate'))
           break

+ 3 - 11
internal/cert/auto_cert.go

@@ -17,7 +17,7 @@ func AutoCert() {
 		if err := recover(); err != nil {
 			buf := make([]byte, 1024)
 			runtime.Stack(buf, false)
-			logger.Error("AutoCert Recover", err, string(buf))
+			logger.Errorf("%s\n%s", err, buf)
 		}
 	}()
 	logger.Info("AutoCert Worker Started")
@@ -67,9 +67,6 @@ func autoCert(certModel *model.Cert) {
 	}
 
 	// after 1 mo, reissue certificate
-	logChan := make(chan string, 1)
-	errChan := make(chan error, 1)
-
 	// support SAN certification
 	payload := &ConfigPayload{
 		CertID:                  certModel.ID,
@@ -93,11 +90,8 @@ func autoCert(certModel *model.Cert) {
 		}
 	}
 
-	// errChan will be closed inside IssueCert
-	go IssueCert(payload, log, errChan)
-
-	// block, unless errChan closed
-	for err := range errChan {
+	err = IssueCert(payload, log)
+	if err != nil {
 		log.Error(err)
 		notification.Error("Renew Certificate Error", strings.Join(payload.ServerName, ", "), nil)
 		return
@@ -109,6 +103,4 @@ func autoCert(certModel *model.Cert) {
 		notification.Error("Sync Certificate Error", err.Error(), nil)
 		return
 	}
-
-	close(logChan)
 }

+ 16 - 0
internal/cert/errors.go

@@ -11,4 +11,20 @@ var (
 	ErrPayloadResourceIsNil              = e.New(50005, "payload resource is nil")
 	ErrPathIsNotUnderTheNginxConfDir     = e.New(50006, "path: {0} is not under the nginx conf dir: {1}")
 	ErrCertPathIsEmpty                   = e.New(50007, "certificate path is empty")
+	ErrGetACMEUser                       = e.New(50008, "get acme user error: {0}")
+	ErrNewTransport                      = e.New(50009, "new transport error: {0}")
+	ErrNewLegoClient                     = e.New(50010, "new lego client error: {0}")
+	ErrGetDNSCredential                  = e.New(50011, "get dns credential error: {0}")
+	ErrProviderNotFound                  = e.New(50012, "provider not found: {0}")
+	ErrSetEnv                            = e.New(50013, "set env error: {0}")
+	ErrNewDNSChallengeProvider           = e.New(50014, "new dns challenge provider error: {0}")
+	ErrEnvironmentConfigurationIsEmpty   = e.New(50015, "environment configuration is empty")
+	ErrChallengeError                    = e.New(50016, "challenge error: {0}")
+	ErrSetEnvFlagToDisableLegoCNAME      = e.New(50017, "set env flag to disable lego CNAME support error: {0}")
+	ErrRenewCert                         = e.New(50018, "renew cert error: {0}")
+	ErrMakeCertificateDir                = e.New(50019, "make certificate dir error: {0}")
+	ErrWriteFullchainCer                 = e.New(50020, "write fullchain.cer error: {0}")
+	ErrWritePrivateKey                   = e.New(50021, "write private.key error: {0}")
+	ErrObtainCert                        = e.New(50022, "obtain cert error: {0}")
+	ErrRevokeCert                        = e.New(50023, "revoke cert error: {0}")
 )

+ 28 - 31
internal/cert/issue.go

@@ -18,7 +18,7 @@ import (
 	"github.com/go-acme/lego/v4/lego"
 	legolog "github.com/go-acme/lego/v4/log"
 	dnsproviders "github.com/go-acme/lego/v4/providers/dns"
-	"github.com/pkg/errors"
+	"github.com/uozi-tech/cosy"
 	"github.com/uozi-tech/cosy/logger"
 	cSettings "github.com/uozi-tech/cosy/settings"
 )
@@ -28,20 +28,19 @@ const (
 	DNS01  = "dns01"
 )
 
-func IssueCert(payload *ConfigPayload, certLogger *Logger, errChan chan error) {
+func IssueCert(payload *ConfigPayload, certLogger *Logger) error {
 	lock()
 	defer unlock()
 	defer func() {
 		if err := recover(); err != nil {
 			buf := make([]byte, 1024)
 			runtime.Stack(buf, false)
-			logger.Error(err)
+			logger.Errorf("%s\n%s", err, buf)
 		}
 	}()
 
 	// initial a channelWriter to receive logs
 	cw := NewChannelWriter()
-	defer close(errChan)
 	defer close(cw.Ch)
 
 	// initial a logger
@@ -58,8 +57,7 @@ func IssueCert(payload *ConfigPayload, certLogger *Logger, errChan chan error) {
 	certLogger.Info(translation.C("[Nginx UI] Preparing lego configurations"))
 	user, err := payload.GetACMEUser()
 	if err != nil {
-		errChan <- errors.Wrap(err, "issue cert get acme user error")
-		return
+		return cosy.WrapErrorWithParams(ErrGetACMEUser, err.Error())
 	}
 
 	certLogger.Info(translation.C("[Nginx UI] ACME User: %{name}, Email: %{email}, CA Dir: %{caDir}", map[string]any{
@@ -84,7 +82,7 @@ func IssueCert(payload *ConfigPayload, certLogger *Logger, errChan chan error) {
 		t, err := transport.NewTransport(
 			transport.WithProxy(user.Proxy))
 		if err != nil {
-			return
+			return cosy.WrapErrorWithParams(ErrNewTransport, err.Error())
 		}
 		config.HTTPClient.Transport = t
 	}
@@ -95,8 +93,7 @@ func IssueCert(payload *ConfigPayload, certLogger *Logger, errChan chan error) {
 	// A client facilitates communication with the CA server.
 	client, err := lego.NewClient(config)
 	if err != nil {
-		errChan <- errors.Wrap(err, "issue cert new client error")
-		return
+		return cosy.WrapErrorWithParams(ErrNewLegoClient, err.Error())
 	}
 
 	switch payload.ChallengeMethod {
@@ -113,24 +110,20 @@ func IssueCert(payload *ConfigPayload, certLogger *Logger, errChan chan error) {
 		d := query.DnsCredential
 		dnsCredential, err := d.FirstByID(payload.DNSCredentialID)
 		if err != nil {
-			errChan <- errors.Wrap(err, "get dns credential error")
-			return
+			return cosy.WrapErrorWithParams(ErrGetDNSCredential, err.Error())
 		}
 
 		certLogger.Info(translation.C("[Nginx UI] Setting DNS01 challenge provider"))
 		code := dnsCredential.Config.Code
 		pConfig, ok := dns.GetProvider(code)
 		if !ok {
-			errChan <- errors.Wrap(err, "provider not found")
-			return
+			return cosy.WrapErrorWithParams(ErrProviderNotFound, err.Error())
 		}
 		certLogger.Info(translation.C("[Nginx UI] Setting environment variables"))
 		if dnsCredential.Config.Configuration != nil {
 			err = pConfig.SetEnv(*dnsCredential.Config.Configuration)
 			if err != nil {
-				errChan <- errors.Wrap(err, "set env error")
-				logger.Error(err)
-				break
+				return cosy.WrapErrorWithParams(ErrSetEnv, err.Error())
 			}
 			defer func() {
 				pConfig.CleanEnv()
@@ -138,9 +131,7 @@ func IssueCert(payload *ConfigPayload, certLogger *Logger, errChan chan error) {
 			}()
 			provider, err := dnsproviders.NewDNSChallengeProviderByName(code)
 			if err != nil {
-				errChan <- errors.Wrap(err, "new dns challenge provider error")
-				logger.Error(err)
-				break
+				return cosy.WrapErrorWithParams(ErrNewDNSChallengeProvider, err.Error())
 			}
 			challengeOptions := make([]dns01.ChallengeOption, 0)
 
@@ -152,26 +143,21 @@ func IssueCert(payload *ConfigPayload, certLogger *Logger, errChan chan error) {
 
 			err = client.Challenge.SetDNS01Provider(provider, challengeOptions...)
 		} else {
-			errChan <- errors.Wrap(err, "environment configuration is empty")
-			return
+			return cosy.WrapErrorWithParams(ErrEnvironmentConfigurationIsEmpty, err.Error())
 		}
 	}
 
 	if err != nil {
-		errChan <- errors.Wrap(err, "challenge error")
-		return
+		return cosy.WrapErrorWithParams(ErrChallengeError, err.Error())
 	}
 
 	// fix #407
 	if payload.LegoDisableCNAMESupport {
 		err = os.Setenv("LEGO_DISABLE_CNAME_SUPPORT", "true")
 		if err != nil {
-			errChan <- errors.Wrap(err, "set env flag to disable lego CNAME support error")
-			return
+			return cosy.WrapErrorWithParams(ErrSetEnvFlagToDisableLegoCNAME, err.Error())
 		}
-		defer func() {
-			_ = os.Unsetenv("LEGO_DISABLE_CNAME_SUPPORT")
-		}()
+		defer os.Unsetenv("LEGO_DISABLE_CNAME_SUPPORT")
 	}
 
 	// Backup current certificate and key if RevokeOld is true
@@ -190,9 +176,15 @@ func IssueCert(payload *ConfigPayload, certLogger *Logger, errChan chan error) {
 
 	if time.Now().Sub(payload.NotBefore).Hours()/24 <= 21 &&
 		payload.Resource != nil && payload.Resource.Certificate != nil {
-		renew(payload, client, certLogger, errChan)
+		err = renew(payload, client, certLogger)
+		if err != nil {
+			return err
+		}
 	} else {
-		obtain(payload, client, certLogger, errChan)
+		err = obtain(payload, client, certLogger)
+		if err != nil {
+			return err
+		}
 	}
 
 	certLogger.Info(translation.C("[Nginx UI] Reloading nginx"))
@@ -222,9 +214,14 @@ func IssueCert(payload *ConfigPayload, certLogger *Logger, errChan chan error) {
 		}
 
 		// Revoke the old certificate
-		revoke(revokePayload, client, certLogger, errChan)
+		err = revoke(revokePayload, client, certLogger)
+		if err != nil {
+			return err
+		}
 	}
 
 	// Wait log to be written
 	time.Sleep(2 * time.Second)
+
+	return nil
 }

+ 6 - 9
internal/cert/logger.go

@@ -59,14 +59,15 @@ func (t *Logger) SetWebSocket(ws *websocket.Conn) {
 }
 
 func (t *Logger) Info(c *translation.Container) {
+	t.mu.Lock()
+	defer t.mu.Unlock()
+
 	result, err := c.ToJSON()
 	if err != nil {
 		return
 	}
 
-	t.mu.Lock()
 	t.buffer = append(t.buffer, string(result))
-	t.mu.Unlock()
 
 	logger.Info("AutoCert", c.ToString())
 
@@ -75,11 +76,11 @@ func (t *Logger) Info(c *translation.Container) {
 
 func (t *Logger) Error(err error) {
 	t.mu.Lock()
+	defer t.mu.Unlock()
 	t.buffer = append(t.buffer, fmt.Sprintf("%s [Error] %s",
 		time.Now().Format(time.DateTime),
 		strings.TrimSpace(err.Error()),
 	))
-	t.mu.Unlock()
 
 	logger.Error("AutoCert", err)
 }
@@ -100,10 +101,6 @@ func (t *Logger) Close() {
 	})
 }
 
-func (t *Logger) ToString() (content string) {
-	t.mu.Lock()
-	defer t.mu.Unlock()
-
-	content = strings.Join(t.buffer, "\n")
-	return
+func (t *Logger) ToString() string {
+	return strings.Join(t.buffer, "\n")
 }

+ 10 - 5
internal/cert/obtain.go

@@ -5,10 +5,10 @@ import (
 	"github.com/0xJacky/Nginx-UI/model"
 	"github.com/go-acme/lego/v4/certificate"
 	"github.com/go-acme/lego/v4/lego"
-	"github.com/pkg/errors"
+	"github.com/uozi-tech/cosy"
 )
 
-func obtain(payload *ConfigPayload, client *lego.Client, l *Logger, errChan chan error) {
+func obtain(payload *ConfigPayload, client *lego.Client, l *Logger) error {
 	request := certificate.ObtainRequest{
 		Domains:    payload.ServerName,
 		Bundle:     true,
@@ -18,9 +18,9 @@ func obtain(payload *ConfigPayload, client *lego.Client, l *Logger, errChan chan
 	l.Info(translation.C("[Nginx UI] Obtaining certificate"))
 	certificates, err := client.Certificate.Obtain(request)
 	if err != nil {
-		errChan <- errors.Wrap(err, "obtain certificate error")
-		return
+		return cosy.WrapErrorWithParams(ErrObtainCert, err.Error())
 	}
+
 	payload.Resource = &model.CertificateResource{
 		Resource:          certificates,
 		PrivateKey:        certificates.PrivateKey,
@@ -29,5 +29,10 @@ func obtain(payload *ConfigPayload, client *lego.Client, l *Logger, errChan chan
 		CSR:               certificates.CSR,
 	}
 
-	payload.WriteFile(l, errChan)
+	err = payload.WriteFile(l)
+	if err != nil {
+		return err
+	}
+
+	return nil
 }

+ 8 - 9
internal/cert/payload.go

@@ -12,7 +12,7 @@ import (
 	"github.com/0xJacky/Nginx-UI/model"
 	"github.com/0xJacky/Nginx-UI/query"
 	"github.com/go-acme/lego/v4/certcrypto"
-	"github.com/pkg/errors"
+	"github.com/uozi-tech/cosy"
 	"github.com/uozi-tech/cosy/logger"
 )
 
@@ -76,11 +76,10 @@ func (c *ConfigPayload) mkCertificateDir() (err error) {
 	return
 }
 
-func (c *ConfigPayload) WriteFile(l *Logger, errChan chan error) {
+func (c *ConfigPayload) WriteFile(l *Logger) error {
 	err := c.mkCertificateDir()
 	if err != nil {
-		errChan <- errors.Wrap(err, "make certificate dir error")
-		return
+		return cosy.WrapErrorWithParams(ErrMakeCertificateDir, err.Error())
 	}
 
 	// Each certificate comes back with the cert bytes, the bytes of the client's
@@ -90,8 +89,7 @@ func (c *ConfigPayload) WriteFile(l *Logger, errChan chan error) {
 		c.Resource.Certificate, 0644)
 
 	if err != nil {
-		errChan <- errors.Wrap(err, "write fullchain.cer error")
-		return
+		return cosy.WrapErrorWithParams(ErrWriteFullchainCer, err.Error())
 	}
 
 	l.Info(translation.C("[Nginx UI] Writing certificate private key to disk"))
@@ -99,13 +97,12 @@ func (c *ConfigPayload) WriteFile(l *Logger, errChan chan error) {
 		c.Resource.PrivateKey, 0644)
 
 	if err != nil {
-		errChan <- errors.Wrap(err, "write private.key error")
-		return
+		return cosy.WrapErrorWithParams(ErrWritePrivateKey, err.Error())
 	}
 
 	// update database
 	if c.CertID <= 0 {
-		return
+		return nil
 	}
 
 	db := model.UseDB()
@@ -114,6 +111,8 @@ func (c *ConfigPayload) WriteFile(l *Logger, errChan chan error) {
 		SSLCertificateKeyPath: c.GetCertificateKeyPath(),
 		Resource:              c.Resource,
 	})
+
+	return nil
 }
 
 func (c *ConfigPayload) getCertificateDirPath() string {

+ 10 - 7
internal/cert/renew.go

@@ -5,13 +5,12 @@ import (
 	"github.com/0xJacky/Nginx-UI/model"
 	"github.com/go-acme/lego/v4/certificate"
 	"github.com/go-acme/lego/v4/lego"
-	"github.com/pkg/errors"
+	"github.com/uozi-tech/cosy"
 )
 
-func renew(payload *ConfigPayload, client *lego.Client, l *Logger, errChan chan error) {
+func renew(payload *ConfigPayload, client *lego.Client, l *Logger) error {
 	if payload.Resource == nil {
-		errChan <- ErrPayloadResourceIsNil
-		return
+		return ErrPayloadResourceIsNil
 	}
 
 	options := &certificate.RenewOptions{
@@ -21,8 +20,7 @@ func renew(payload *ConfigPayload, client *lego.Client, l *Logger, errChan chan
 
 	cert, err := client.Certificate.RenewWithOptions(payload.Resource.GetResource(), options)
 	if err != nil {
-		errChan <- errors.Wrap(err, "renew cert error")
-		return
+		return cosy.WrapErrorWithParams(ErrRenewCert, err.Error())
 	}
 
 	payload.Resource = &model.CertificateResource{
@@ -33,7 +31,12 @@ func renew(payload *ConfigPayload, client *lego.Client, l *Logger, errChan chan
 		CSR:               cert.CSR,
 	}
 
-	payload.WriteFile(l, errChan)
+	err = payload.WriteFile(l)
+	if err != nil {
+		return err
+	}
 
 	l.Info(translation.C("[Nginx UI] Certificate renewed successfully"))
+
+	return nil
 }

+ 9 - 6
internal/cert/revoke.go

@@ -11,6 +11,7 @@ import (
 	"github.com/go-acme/lego/v4/lego"
 	legolog "github.com/go-acme/lego/v4/log"
 	"github.com/pkg/errors"
+	"github.com/uozi-tech/cosy"
 	"github.com/uozi-tech/cosy/logger"
 	cSettings "github.com/uozi-tech/cosy/settings"
 )
@@ -23,7 +24,7 @@ func RevokeCert(payload *ConfigPayload, certLogger *Logger, logChan chan string,
 		if err := recover(); err != nil {
 			buf := make([]byte, 1024)
 			runtime.Stack(buf, false)
-			logger.Error(err)
+			logger.Errorf("%s\n%s", err, buf)
 		}
 	}()
 
@@ -82,7 +83,10 @@ func RevokeCert(payload *ConfigPayload, certLogger *Logger, logChan chan string,
 		return
 	}
 
-	revoke(payload, client, certLogger, errChan)
+	err = revoke(payload, client, certLogger)
+	if err != nil {
+		return
+	}
 
 	// If the revoked certificate was used for the server itself, reload server TLS certificate
 	if payload.GetCertificatePath() == cSettings.ServerSettings.SSLCert &&
@@ -98,14 +102,13 @@ func RevokeCert(payload *ConfigPayload, certLogger *Logger, logChan chan string,
 }
 
 // revoke implements the internal certificate revocation logic
-func revoke(payload *ConfigPayload, client *lego.Client, l *Logger, errChan chan error) {
+func revoke(payload *ConfigPayload, client *lego.Client, l *Logger) error {
 	l.Info(translation.C("[Nginx UI] Revoking certificate"))
 	err := client.Certificate.Revoke(payload.Resource.Certificate)
 	if err != nil {
-		errChan <- errors.Wrap(err, "revoke certificate error")
-		return
+		return cosy.WrapErrorWithParams(ErrRevokeCert, err.Error())
 	}
 
 	l.Info(translation.C("[Nginx UI] Certificate successfully revoked"))
-	return
+	return nil
 }

+ 0 - 125
internal/cert/tencent_cloud_dns_test.go

@@ -1,125 +0,0 @@
-package cert
-
-import (
-	"crypto/ecdsa"
-	"crypto/elliptic"
-	"crypto/rand"
-	"crypto/tls"
-	"github.com/0xJacky/Nginx-UI/model"
-	"github.com/0xJacky/Nginx-UI/settings"
-	"github.com/go-acme/lego/v4/certcrypto"
-	"github.com/go-acme/lego/v4/certificate"
-	"github.com/go-acme/lego/v4/lego"
-	"github.com/go-acme/lego/v4/providers/dns/tencentcloud"
-	"github.com/go-acme/lego/v4/registration"
-	"log"
-	"net/http"
-	"os"
-	"path/filepath"
-	"strings"
-	"testing"
-)
-
-func TestTencentCloudDNS(t *testing.T) {
-	domain := []string{"test.jackyu.cn"}
-
-	privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
-	if err != nil {
-		log.Println(err)
-		return
-	}
-
-	myUser := model.AcmeUser{
-		Email: settings.CertSettings.Email,
-		Key: model.PrivateKey{
-			X: privateKey.PublicKey.X,
-			Y: privateKey.PublicKey.Y,
-		},
-	}
-
-	config := lego.NewConfig(&myUser)
-
-	if settings.NodeSettings.Demo {
-		config.CADirURL = "https://acme-staging-v02.api.letsencrypt.org/directory"
-	}
-
-	if settings.CertSettings.CADir != "" {
-		config.CADirURL = settings.CertSettings.CADir
-		if config.HTTPClient != nil {
-			config.HTTPClient.Transport = &http.Transport{
-				Proxy:           http.ProxyFromEnvironment,
-				TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
-			}
-		}
-	}
-
-	config.Certificate.KeyType = certcrypto.RSA2048
-
-	// A client facilitates communication with the CA server.
-	client, err := lego.NewClient(config)
-	if err != nil {
-		log.Println(err)
-		return
-	}
-
-	provider, err := tencentcloud.NewDNSProvider()
-
-	if err != nil {
-		log.Println(err)
-		return
-	}
-
-	err = client.Challenge.SetDNS01Provider(
-		provider,
-	)
-
-	if err != nil {
-		log.Println(err)
-		return
-	}
-
-	// New users will need to register
-	reg, err := client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
-	if err != nil {
-		log.Println(err)
-		return
-	}
-	myUser.Registration = *reg
-
-	request := certificate.ObtainRequest{
-		Domains: domain,
-		Bundle:  true,
-	}
-
-	certificates, err := client.Certificate.Obtain(request)
-	if err != nil {
-		log.Println(err)
-		return
-	}
-	name := strings.Join(domain, "_")
-	saveDir := "tmp/" + name
-	if _, err = os.Stat(saveDir); os.IsNotExist(err) {
-		err = os.MkdirAll(saveDir, 0755)
-		if err != nil {
-			return
-		}
-	}
-
-	// Each certificate comes back with the cert bytes, the bytes of the client's
-	// private key, and a certificate URL. SAVE THESE TO DISK.
-	err = os.WriteFile(filepath.Join(saveDir, "fullchain.cer"),
-		certificates.Certificate, 0644)
-
-	if err != nil {
-		log.Println(err)
-		return
-	}
-
-	err = os.WriteFile(filepath.Join(saveDir, "private.key"),
-		certificates.PrivateKey, 0644)
-
-	if err != nil {
-		log.Println(err)
-		return
-	}
-}

+ 1 - 1
internal/site/delete.go

@@ -62,7 +62,7 @@ func syncDelete(name string) {
 				if err := recover(); err != nil {
 					buf := make([]byte, 1024)
 					runtime.Stack(buf, false)
-					logger.Error(err)
+					logger.Errorf("%s\n%s", err, buf)
 				}
 			}()
 			client := resty.New()

+ 1 - 1
internal/site/disable.go

@@ -56,7 +56,7 @@ func syncDisable(name string) {
 				if err := recover(); err != nil {
 					buf := make([]byte, 1024)
 					runtime.Stack(buf, false)
-					logger.Error(err)
+					logger.Errorf("%s\n%s", err, buf)
 				}
 			}()
 			defer wg.Done()

+ 1 - 1
internal/site/enable.go

@@ -61,7 +61,7 @@ func syncEnable(name string) {
 				if err := recover(); err != nil {
 					buf := make([]byte, 1024)
 					runtime.Stack(buf, false)
-					logger.Error(err)
+					logger.Errorf("%s\n%s", err, buf)
 				}
 			}()
 			defer wg.Done()

+ 2 - 2
internal/site/maintenance.go

@@ -323,7 +323,7 @@ func syncEnableMaintenance(name string) {
 				if err := recover(); err != nil {
 					buf := make([]byte, 1024)
 					runtime.Stack(buf, false)
-					logger.Error(err)
+					logger.Errorf("%s\n%s", err, buf)
 				}
 			}()
 			defer wg.Done()
@@ -361,7 +361,7 @@ func syncDisableMaintenance(name string) {
 				if err := recover(); err != nil {
 					buf := make([]byte, 1024)
 					runtime.Stack(buf, false)
-					logger.Error(err)
+					logger.Errorf("%s\n%s", err, buf)
 				}
 			}()
 			defer wg.Done()

+ 1 - 1
internal/site/rename.go

@@ -87,7 +87,7 @@ func syncRename(oldName, newName string) {
 				if err := recover(); err != nil {
 					buf := make([]byte, 1024)
 					runtime.Stack(buf, false)
-					logger.Error(err)
+					logger.Errorf("%s\n%s", err, buf)
 				}
 			}()
 			defer wg.Done()

+ 1 - 1
internal/site/save.go

@@ -82,7 +82,7 @@ func syncSave(name string, content string) {
 				if err := recover(); err != nil {
 					buf := make([]byte, 1024)
 					runtime.Stack(buf, false)
-					logger.Error(err)
+					logger.Errorf("%s\n%s", err, buf)
 				}
 			}()
 			defer wg.Done()

+ 1 - 1
internal/stream/delete.go

@@ -57,7 +57,7 @@ func syncDelete(name string) {
 				if err := recover(); err != nil {
 					buf := make([]byte, 1024)
 					runtime.Stack(buf, false)
-					logger.Error(err)
+					logger.Errorf("%s\n%s", err, buf)
 				}
 			}()
 			client := resty.New()

+ 1 - 1
internal/stream/disable.go

@@ -56,7 +56,7 @@ func syncDisable(name string) {
 				if err := recover(); err != nil {
 					buf := make([]byte, 1024)
 					runtime.Stack(buf, false)
-					logger.Error(err)
+					logger.Errorf("%s\n%s", err, buf)
 				}
 			}()
 			defer wg.Done()

+ 1 - 1
internal/stream/enable.go

@@ -61,7 +61,7 @@ func syncEnable(name string) {
 				if err := recover(); err != nil {
 					buf := make([]byte, 1024)
 					runtime.Stack(buf, false)
-					logger.Error(err)
+					logger.Errorf("%s\n%s", err, buf)
 				}
 			}()
 			defer wg.Done()

+ 1 - 1
internal/stream/rename.go

@@ -87,7 +87,7 @@ func syncRename(oldName, newName string) {
 				if err := recover(); err != nil {
 					buf := make([]byte, 1024)
 					runtime.Stack(buf, false)
-					logger.Error(err)
+					logger.Errorf("%s\n%s", err, buf)
 				}
 			}()
 			defer wg.Done()

+ 1 - 1
internal/stream/save.go

@@ -81,7 +81,7 @@ func syncSave(name string, content string) {
 				if err := recover(); err != nil {
 					buf := make([]byte, 1024)
 					runtime.Stack(buf, false)
-					logger.Error(err)
+					logger.Errorf("%s\n%s", err, buf)
 				}
 			}()
 			defer wg.Done()