浏览代码

Merge pull request #420 from 0xJacky/enhance/auto-cert

Enhance auto cert
Jacky 10 月之前
父节点
当前提交
47d4013f3d
共有 43 个文件被更改,包括 1884 次插入1080 次删除
  1. 60 0
      api/certificate/certificate.go
  2. 4 11
      api/certificate/issue.go
  3. 1 0
      api/certificate/router.go
  4. 1 2
      api/notification/notification.go
  5. 0 2
      api/sites/domain.go
  6. 19 19
      app/package.json
  7. 255 253
      app/pnpm-lock.yaml
  8. 1 0
      app/src/api/cert.ts
  9. 2 2
      app/src/api/curd.ts
  10. 4 2
      app/src/components/Notification/Notification.vue
  11. 31 0
      app/src/components/Notification/detailRender.ts
  12. 15 1
      app/src/components/StdDesign/StdDataDisplay/StdTable.vue
  13. 1 1
      app/src/language/LINGUAS
  14. 4 0
      app/src/language/constants.ts
  15. 134 83
      app/src/language/en/app.po
  16. 135 83
      app/src/language/es/app.po
  17. 135 83
      app/src/language/fr_FR/app.po
  18. 135 83
      app/src/language/ko_KR/app.po
  19. 125 83
      app/src/language/messages.pot
  20. 135 83
      app/src/language/ru_RU/app.po
  21. 135 83
      app/src/language/vi_VN/app.po
  22. 二进制
      app/src/language/zh_CN/app.mo
  23. 129 83
      app/src/language/zh_CN/app.po
  24. 135 83
      app/src/language/zh_TW/app.po
  25. 1 1
      app/src/version.json
  26. 7 2
      app/src/views/certificate/Certificate.vue
  27. 7 0
      app/src/views/certificate/CertificateEditor.vue
  28. 2 0
      app/src/views/notification/Notification.vue
  29. 1 1
      app/version.json
  30. 3 0
      gen.sh
  31. 1 2
      internal/analytic/node_record.go
  32. 8 3
      internal/cert/auto_cert.go
  33. 1 1
      internal/cert/cert.go
  34. 64 20
      internal/cert/payload.go
  35. 135 0
      internal/cert/sync.go
  36. 18 0
      internal/cert/write_file.go
  37. 1 0
      internal/helper/directory.go
  38. 1 1
      internal/kernal/boot.go
  39. 2 4
      model/cert.go
  40. 19 0
      model/environment.go
  41. 9 1
      query/certs.gen.go
  42. 3 3
      query/config_backups.gen.go
  43. 5 1
      query/environments.gen.go

+ 60 - 0
api/certificate/certificate.go

@@ -4,6 +4,8 @@ import (
 	"github.com/0xJacky/Nginx-UI/api"
 	"github.com/0xJacky/Nginx-UI/internal/cert"
 	"github.com/0xJacky/Nginx-UI/internal/cosy"
+	"github.com/0xJacky/Nginx-UI/internal/nginx"
+	"github.com/0xJacky/Nginx-UI/internal/notification"
 	"github.com/0xJacky/Nginx-UI/model"
 	"github.com/0xJacky/Nginx-UI/query"
 	"github.com/gin-gonic/gin"
@@ -86,6 +88,7 @@ type certJson struct {
 	ChallengeMethod       string             `json:"challenge_method"`
 	DnsCredentialID       int                `json:"dns_credential_id"`
 	ACMEUserID            int                `json:"acme_user_id"`
+	SyncNodeIds           []int              `json:"sync_node_ids"`
 }
 
 func AddCert(c *gin.Context) {
@@ -103,6 +106,7 @@ func AddCert(c *gin.Context) {
 		ChallengeMethod:       json.ChallengeMethod,
 		DnsCredentialID:       json.DnsCredentialID,
 		ACMEUserID:            json.ACMEUserID,
+		SyncNodeIds:           json.SyncNodeIds,
 	}
 
 	err := certModel.Insert()
@@ -126,6 +130,12 @@ func AddCert(c *gin.Context) {
 		return
 	}
 
+	err = cert.SyncToRemoteServer(certModel)
+	if err != nil {
+		notification.Error("Sync Certificate Error", err.Error())
+		return
+	}
+
 	c.JSON(http.StatusOK, Transformer(certModel))
 }
 
@@ -154,6 +164,7 @@ func ModifyCert(c *gin.Context) {
 		KeyType:               json.KeyType,
 		DnsCredentialID:       json.DnsCredentialID,
 		ACMEUserID:            json.ACMEUserID,
+		SyncNodeIds:           json.SyncNodeIds,
 	})
 
 	if err != nil {
@@ -175,9 +186,58 @@ func ModifyCert(c *gin.Context) {
 		return
 	}
 
+	err = cert.SyncToRemoteServer(certModel)
+	if err != nil {
+		notification.Error("Sync Certificate Error", err.Error())
+		return
+	}
+
 	GetCert(c)
 }
 
 func RemoveCert(c *gin.Context) {
 	cosy.Core[model.Cert](c).Destroy()
 }
+
+func SyncCertificate(c *gin.Context) {
+	var json cert.SyncCertificatePayload
+
+	if !api.BindAndValid(c, &json) {
+		return
+	}
+
+	certModel := &model.Cert{
+		Name:                  json.Name,
+		SSLCertificatePath:    json.SSLCertificatePath,
+		SSLCertificateKeyPath: json.SSLCertificateKeyPath,
+		KeyType:               json.KeyType,
+		AutoCert:              model.AutoCertSync,
+	}
+
+	db := model.UseDB()
+
+	err := db.Where(certModel).FirstOrCreate(certModel).Error
+	if err != nil {
+		api.ErrHandler(c, err)
+		return
+	}
+
+	content := &cert.Content{
+		SSLCertificatePath:    json.SSLCertificatePath,
+		SSLCertificateKeyPath: json.SSLCertificateKeyPath,
+		SSLCertificate:        json.SSLCertificate,
+		SSLCertificateKey:     json.SSLCertificateKey,
+	}
+
+	err = content.WriteFile()
+	if err != nil {
+		api.ErrHandler(c, err)
+		return
+	}
+
+	nginx.Reload()
+
+	c.JSON(http.StatusOK, gin.H{
+		"message": "ok",
+	})
+}

+ 4 - 11
api/certificate/issue.go

@@ -3,13 +3,11 @@ package certificate
 import (
 	"github.com/0xJacky/Nginx-UI/internal/cert"
 	"github.com/0xJacky/Nginx-UI/internal/logger"
-	"github.com/0xJacky/Nginx-UI/internal/nginx"
 	"github.com/0xJacky/Nginx-UI/model"
 	"github.com/gin-gonic/gin"
 	"github.com/go-acme/lego/v4/certcrypto"
 	"github.com/gorilla/websocket"
 	"net/http"
-	"strings"
 )
 
 const (
@@ -71,7 +69,6 @@ func IssueCert(c *gin.Context) {
 	payload := &cert.ConfigPayload{}
 
 	err = ws.ReadJSON(payload)
-
 	if err != nil {
 		logger.Error(err)
 		return
@@ -122,14 +119,10 @@ func IssueCert(c *gin.Context) {
 		return
 	}
 
-	certDirName := strings.Join(payload.ServerName, "_") + "_" + string(payload.GetKeyType())
-	sslCertificatePath := nginx.GetConfPath("ssl", certDirName, "fullchain.cer")
-	sslCertificateKeyPath := nginx.GetConfPath("ssl", certDirName, "private.key")
-
 	err = certModel.Updates(&model.Cert{
 		Domains:               payload.ServerName,
-		SSLCertificatePath:    sslCertificatePath,
-		SSLCertificateKeyPath: sslCertificateKeyPath,
+		SSLCertificatePath:    payload.GetCertificatePath(),
+		SSLCertificateKeyPath: payload.GetCertificateKeyPath(),
 		AutoCert:              model.AutoCertEnabled,
 		KeyType:               payload.KeyType,
 		ChallengeMethod:       payload.ChallengeMethod,
@@ -152,8 +145,8 @@ func IssueCert(c *gin.Context) {
 	err = ws.WriteJSON(IssueCertResponse{
 		Status:            Success,
 		Message:           "Issued certificate successfully",
-		SSLCertificate:    sslCertificatePath,
-		SSLCertificateKey: sslCertificateKeyPath,
+		SSLCertificate:    payload.GetCertificatePath(),
+		SSLCertificateKey: payload.GetCertificateKeyPath(),
 		KeyType:           payload.GetKeyType(),
 	})
 

+ 1 - 0
api/certificate/router.go

@@ -16,6 +16,7 @@ func InitCertificateRouter(r *gin.RouterGroup) {
 	r.POST("cert", AddCert)
 	r.POST("cert/:id", ModifyCert)
 	r.DELETE("cert/:id", RemoveCert)
+	r.PUT("cert_sync", SyncCertificate)
 	r.GET("certificate/dns_providers", GetDNSProvidersList)
 	r.GET("certificate/dns_provider/:code", GetDNSProvider)
 }

+ 1 - 2
api/notification/notification.go

@@ -30,8 +30,7 @@ func GetList(c *gin.Context) {
 }
 
 func Destroy(c *gin.Context) {
-	cosy.Core[model.Notification](c).
-		PermanentlyDelete()
+	cosy.Core[model.Notification](c).Destroy()
 }
 
 func DestroyAll(c *gin.Context) {

+ 0 - 2
api/sites/domain.go

@@ -22,14 +22,12 @@ func GetDomains(c *gin.Context) {
 	sort := c.DefaultQuery("sort", "desc")
 
 	configFiles, err := os.ReadDir(nginx.GetConfPath("sites-available"))
-
 	if err != nil {
 		api.ErrHandler(c, err)
 		return
 	}
 
 	enabledConfig, err := os.ReadDir(nginx.GetConfPath("sites-enabled"))
-
 	if err != nil {
 		api.ErrHandler(c, err)
 		return

+ 19 - 19
app/package.json

@@ -1,6 +1,6 @@
 {
   "name": "nginx-ui-app-next",
-  "version": "2.0.0-beta.24",
+  "version": "2.0.0-beta.25",
   "type": "module",
   "scripts": {
     "dev": "vite",
@@ -13,15 +13,15 @@
   "dependencies": {
     "@ant-design/icons-vue": "^7.0.1",
     "@formkit/auto-animate": "^0.8.2",
-    "@vue/reactivity": "^3.4.27",
-    "@vue/shared": "^3.4.27",
-    "@vueuse/core": "^10.9.0",
+    "@vue/reactivity": "^3.4.29",
+    "@vue/shared": "^3.4.29",
+    "@vueuse/core": "^10.11.0",
     "@xterm/addon-attach": "^0.11.0",
     "@xterm/addon-fit": "^0.10.0",
     "@xterm/xterm": "^5.5.0",
-    "ant-design-vue": "^4.2.1",
-    "apexcharts": "^3.49.0",
-    "axios": "^1.6.8",
+    "ant-design-vue": "^4.2.3",
+    "apexcharts": "^3.49.1",
+    "axios": "^1.7.2",
     "dayjs": "^1.11.11",
     "highlight.js": "^11.9.0",
     "lodash": "^4.17.21",
@@ -32,9 +32,9 @@
     "reconnecting-websocket": "^4.4.0",
     "sortablejs": "^1.15.2",
     "vite-plugin-build-id": "^0.2.9",
-    "vue": "^3.4.27",
+    "vue": "^3.4.29",
     "vue-github-button": "github:0xJacky/vue-github-button",
-    "vue-router": "^4.3.2",
+    "vue-router": "^4.3.3",
     "vue3-ace-editor": "2.2.4",
     "vue3-apexcharts": "1.4.4",
     "vue3-gettext": "3.0.0-beta.4",
@@ -42,16 +42,16 @@
   },
   "devDependencies": {
     "@antfu/eslint-config-vue": "^0.43.1",
-    "@types/lodash": "^4.17.1",
+    "@types/lodash": "^4.17.5",
     "@types/nprogress": "^0.2.3",
     "@types/sortablejs": "^1.15.8",
     "@typescript-eslint/eslint-plugin": "^6.21.0",
     "@typescript-eslint/parser": "^6.21.0",
-    "@vitejs/plugin-vue": "^5.0.4",
+    "@vitejs/plugin-vue": "^5.0.5",
     "@vitejs/plugin-vue-jsx": "^3.1.0",
-    "@vue/compiler-sfc": "^3.4.27",
+    "@vue/compiler-sfc": "^3.4.29",
     "@vue/tsconfig": "^0.5.1",
-    "ace-builds": "^1.33.1",
+    "ace-builds": "^1.35.0",
     "autoprefixer": "^10.4.19",
     "eslint": "^8.57.0",
     "eslint-import-resolver-alias": "^1.1.2",
@@ -59,17 +59,17 @@
     "eslint-plugin-import": "^2.29.1",
     "eslint-plugin-regex": "^1.10.0",
     "eslint-plugin-sonarjs": "^0.23.0",
-    "eslint-plugin-vue": "^9.25.0",
+    "eslint-plugin-vue": "^9.26.0",
     "less": "^4.2.0",
     "postcss": "^8.4.38",
-    "tailwindcss": "^3.4.3",
+    "tailwindcss": "^3.4.4",
     "typescript": "5.3.3",
-    "unplugin-auto-import": "^0.17.5",
+    "unplugin-auto-import": "^0.17.6",
     "unplugin-vue-components": "^0.26.0",
-    "unplugin-vue-define-options": "^1.4.4",
-    "vite": "^5.2.11",
+    "unplugin-vue-define-options": "^1.4.5",
+    "vite": "^5.3.1",
     "vite-svg-loader": "^5.1.0",
     "vue-tsc": "^1.8.27"
   },
-  "packageManager": "pnpm@9.0.6+sha256.0624e30eff866cdeb363b15061bdb7fd9425b17bc1bb42c22f5f4efdea21f6b3"
+  "packageManager": "pnpm@9.3.0+sha512.ee7b93e0c2bd11409c6424f92b866f31d3ea1bef5fbe47d3c7500cdc3c9668833d2e55681ad66df5b640c61fa9dc25d546efa54d76d7f8bf54b13614ac293631"
 }

文件差异内容过多而无法显示
+ 255 - 253
app/pnpm-lock.yaml


+ 1 - 0
app/src/api/cert.ts

@@ -21,6 +21,7 @@ export interface Cert extends ModelBase {
   key_type: string
   log: string
   certificate_info: CertificateInfo
+  sync_node_ids: number[]
 }
 
 export interface CertificateInfo {

+ 2 - 2
app/src/api/curd.ts

@@ -50,8 +50,8 @@ class Curd<T> {
   }
 
   // eslint-disable-next-line @typescript-eslint/no-explicit-any
-  _destroy(id: any = null) {
-    return http.delete(`${this.baseUrl}/${id}`)
+  _destroy(id: any = null, params: any = {}) {
+    return http.delete(`${this.baseUrl}/${id}`, { params })
   }
 
   // eslint-disable-next-line @typescript-eslint/no-explicit-any

+ 4 - 2
app/src/components/Notification/Notification.vue

@@ -6,6 +6,8 @@ import notification from '@/api/notification'
 import type { Notification } from '@/api/notification'
 import { NotificationTypeT } from '@/constants'
 import { useUserStore } from '@/pinia'
+import { detailRender } from '@/components/Notification/detailRender'
+import type { customRender } from '@/components/StdDesign/StdDataDisplay/StdTableTransformer'
 
 const loading = ref(false)
 
@@ -109,8 +111,8 @@ function viewAll() {
                 </span>
               </template>
               <AListItemMeta
-                :title="item.title"
-                :description="item.details"
+                :title="$gettext(item.title)"
+                :description="detailRender({ text: item.details, record: item } as customRender)"
               >
                 <template #avatar>
                   <div>

+ 31 - 0
app/src/components/Notification/detailRender.ts

@@ -0,0 +1,31 @@
+import type { customRender } from '@/components/StdDesign/StdDataDisplay/StdTableTransformer'
+
+export const detailRender = (args: customRender) => {
+  switch (args.record.title) {
+    case 'Sync Certificate Success':
+      return syncCertificateSuccess(args.text)
+    case 'Sync Certificate Error':
+      return syncCertificateError(args.text)
+    default:
+      return args.text
+  }
+}
+
+function syncCertificateSuccess(text: string) {
+  const data = JSON.parse(text)
+
+  return $gettext('Sync Certificate %{cert_name} to %{env_name} successfully',
+    { cert_name: data.cert_name, env_name: data.env_name })
+}
+
+function syncCertificateError(text: string) {
+  const data = JSON.parse(text)
+
+  if (data.status_code === 404) {
+    return $gettext('Sync Certificate %{cert_name} to %{env_name} failed, please upgrade the remote Nginx UI to the latest version',
+      { cert_name: data.cert_name, env_name: data.env_name }, true)
+  }
+
+  return $gettext('Sync Certificate %{cert_name} to %{env_name} failed, response: %{resp}',
+    { cert_name: data.cert_name, env_name: data.env_name, resp: data.resp_body }, true)
+}

+ 15 - 1
app/src/components/StdDesign/StdDataDisplay/StdTable.vue

@@ -180,7 +180,7 @@ defineExpose({
 })
 
 function destroy(id: number | string) {
-  props.api!.destroy(id).then(() => {
+  props.api!.destroy(id, { permanent: params.trash }).then(() => {
     get_list()
     message.success($gettext('Deleted successfully'))
   }).catch(e => {
@@ -544,6 +544,20 @@ const paginationSize = computed(() => {
                 {{ $gettext('Recover') }}
               </AButton>
             </APopconfirm>
+            <APopconfirm
+              v-if="params.trash"
+              :cancel-text="$gettext('No')"
+              :ok-text="$gettext('OK')"
+              :title="$gettext('Are you sure you want to delete this item permanently?')"
+              @confirm="destroy(record[rowKey])"
+            >
+              <AButton
+                type="link"
+                size="small"
+              >
+                {{ $gettext('Delete Permanently') }}
+              </AButton>
+            </APopconfirm>
           </template>
         </template>
       </template>

+ 1 - 1
app/src/language/LINGUAS

@@ -1 +1 @@
-es fr_FR ko_KR ru_RU vi_VN zh_CN zh_TW
+en zh_CN zh_TW fr_FR es ru_RU vi_VN ko_KR

+ 4 - 0
app/src/language/constants.ts

@@ -18,6 +18,7 @@ export const msg = [
   $gettext('Reloading nginx'),
   $gettext('Finished'),
   $gettext('Issued certificate successfully'),
+  $gettext('Environment variables cleaned'),
 
   $gettext('Initialing core upgrader'),
   $gettext('Initial core upgrader error'),
@@ -32,4 +33,7 @@ export const msg = [
 
   $gettext('Renew Certificate Success'),
   $gettext('Renew Certificate Error'),
+
+  $gettext('Sync Certificate Success'),
+  $gettext('Sync Certificate Error'),
 ]

+ 134 - 83
app/src/language/en/app.po

@@ -24,10 +24,10 @@ msgid "ACME User"
 msgstr "Username"
 
 #: src/views/certificate/ACMEUser.vue:59
-#: src/views/certificate/Certificate.vue:108
-#: src/views/certificate/DNSCredential.vue:29 src/views/config/config.ts:34
+#: src/views/certificate/Certificate.vue:113
+#: src/views/certificate/DNSCredential.vue:33 src/views/config/config.ts:34
 #: src/views/domain/DomainList.vue:47 src/views/environment/Environment.vue:129
-#: src/views/notification/Notification.vue:35
+#: src/views/notification/Notification.vue:37
 #: src/views/stream/StreamList.vue:47 src/views/user/User.vue:43
 msgid "Action"
 msgstr "Action"
@@ -95,7 +95,7 @@ msgid "Arch"
 msgstr ""
 
 #: src/components/Notification/Notification.vue:84
-#: src/views/notification/Notification.vue:70
+#: src/views/notification/Notification.vue:72
 #, fuzzy
 msgid "Are you sure you want to clear all notifications?"
 msgstr "Are you sure you want to remove this directive?"
@@ -105,7 +105,12 @@ msgstr "Are you sure you want to remove this directive?"
 msgid "Are you sure you want to clear the record of chat?"
 msgstr "Are you sure you want to remove this directive?"
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:509
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:551
+#, fuzzy
+msgid "Are you sure you want to delete this item permanently?"
+msgstr "Are you sure you want to remove this directive?"
+
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:523
 #, fuzzy
 msgid "Are you sure you want to delete this item?"
 msgstr "Are you sure you want to remove this directive?"
@@ -115,7 +120,7 @@ msgstr "Are you sure you want to remove this directive?"
 msgid "Are you sure you want to delete?"
 msgstr "Are you sure you want to remove this directive?"
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:523
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:537
 #, fuzzy
 msgid "Are you sure you want to recover this item?"
 msgstr "Are you sure you want to remove this directive?"
@@ -163,7 +168,7 @@ msgstr "Auto-renewal disabled for %{name}"
 msgid "Auto-renewal enabled for %{name}"
 msgstr "Auto-renewal enabled for %{name}"
 
-#: src/views/certificate/CertificateEditor.vue:235
+#: src/views/certificate/CertificateEditor.vue:242
 #: src/views/config/Config.vue:73 src/views/config/ConfigEdit.vue:87
 #: src/views/domain/DomainEdit.vue:256 src/views/nginx_log/NginxLog.vue:168
 #: src/views/stream/StreamEdit.vue:247
@@ -192,7 +197,7 @@ msgid "Basic Mode"
 msgstr "Basic Mode"
 
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:54
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:445
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:459
 #, fuzzy
 msgid "Batch Modify"
 msgstr "Modify Config"
@@ -237,12 +242,12 @@ msgstr "Certificate is valid"
 msgid "Certificate Renewal Interval"
 msgstr "Certificate is valid"
 
-#: src/views/certificate/CertificateEditor.vue:127
+#: src/views/certificate/CertificateEditor.vue:128
 #: src/views/domain/cert/Cert.vue:33
 msgid "Certificate Status"
 msgstr "Certificate Status"
 
-#: src/routes/index.ts:119 src/views/certificate/Certificate.vue:124
+#: src/routes/index.ts:119 src/views/certificate/Certificate.vue:129
 #, fuzzy
 msgid "Certificates"
 msgstr "Certificate Status"
@@ -276,12 +281,12 @@ msgstr ""
 
 #: src/components/ChatGPT/ChatGPT.vue:276
 #: src/components/Notification/Notification.vue:89
-#: src/views/notification/Notification.vue:75
+#: src/views/notification/Notification.vue:77
 msgid "Clear"
 msgstr ""
 
 #: src/components/Notification/Notification.vue:40
-#: src/views/notification/Notification.vue:44
+#: src/views/notification/Notification.vue:46
 #, fuzzy
 msgid "Cleared successfully"
 msgstr "Disabled successfully"
@@ -350,7 +355,7 @@ msgstr "Created at"
 msgid "Create Another"
 msgstr "Create Another"
 
-#: src/views/notification/Notification.vue:29 src/views/user/User.vue:31
+#: src/views/notification/Notification.vue:31 src/views/user/User.vue:31
 msgid "Created at"
 msgstr "Created at"
 
@@ -393,7 +398,7 @@ msgstr "Database (Optional, default: database)"
 msgid "Days"
 msgstr ""
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:516
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:530
 #: src/views/domain/DomainList.vue:155
 #: src/views/domain/ngx_conf/NgxServer.vue:114
 #: src/views/domain/ngx_conf/NgxUpstream.vue:126
@@ -401,6 +406,10 @@ msgstr ""
 msgid "Delete"
 msgstr ""
 
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:558
+msgid "Delete Permanently"
+msgstr ""
+
 #: src/views/domain/DomainList.vue:78
 msgid "Delete site: %{site_name}"
 msgstr ""
@@ -409,7 +418,7 @@ msgstr ""
 msgid "Delete stream: %{stream_name}"
 msgstr ""
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:171
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:185
 #, fuzzy
 msgid "Deleted successfully"
 msgstr "Disabled successfully"
@@ -442,7 +451,7 @@ msgstr "Saved successfully"
 msgid "Description"
 msgstr ""
 
-#: src/views/notification/Notification.vue:25
+#: src/views/notification/Notification.vue:26
 msgid "Details"
 msgstr ""
 
@@ -489,7 +498,7 @@ msgstr "Disabled successfully"
 msgid "Disk IO"
 msgstr "Disk IO"
 
-#: src/routes/index.ts:162 src/views/certificate/DNSCredential.vue:36
+#: src/routes/index.ts:162 src/views/certificate/DNSCredential.vue:40
 msgid "DNS Credentials"
 msgstr ""
 
@@ -557,15 +566,15 @@ msgstr ""
 msgid "Domain Config Created Successfully"
 msgstr "Domain Config Created Successfully"
 
-#: src/views/certificate/CertificateEditor.vue:111
+#: src/views/certificate/CertificateEditor.vue:112
 msgid "Domains list is empty, try to reopen Auto Cert for %{config}"
 msgstr ""
 
-#: src/language/constants.ts:25
+#: src/language/constants.ts:26
 msgid "Download latest release error"
 msgstr ""
 
-#: src/language/constants.ts:24
+#: src/language/constants.ts:25
 msgid "Downloading latest release"
 msgstr ""
 
@@ -693,6 +702,10 @@ msgstr "Encrypt website with Let's Encrypt"
 msgid "Environment"
 msgstr ""
 
+#: src/language/constants.ts:21
+msgid "Environment variables cleaned"
+msgstr ""
+
 #: src/views/dashboard/Environments.vue:82
 #, fuzzy
 msgid "Environments"
@@ -710,7 +723,7 @@ msgstr ""
 msgid "Executable Path"
 msgstr ""
 
-#: src/views/certificate/Certificate.vue:96
+#: src/views/certificate/Certificate.vue:101
 msgid "Expired"
 msgstr ""
 
@@ -754,7 +767,7 @@ msgstr ""
 msgid "File"
 msgstr ""
 
-#: src/language/constants.ts:30
+#: src/language/constants.ts:31
 msgid "File exists"
 msgstr ""
 
@@ -794,7 +807,7 @@ msgstr "Save error %{msg}"
 msgid "Format successfully"
 msgstr "Saved successfully"
 
-#: src/views/certificate/Certificate.vue:43
+#: src/views/certificate/Certificate.vue:44
 #, fuzzy
 msgid "General Certificate"
 msgstr "Certificate is valid"
@@ -844,11 +857,11 @@ msgstr ""
 msgid "If left blank, the default CA Dir will be used."
 msgstr ""
 
-#: src/views/certificate/Certificate.vue:131
+#: src/views/certificate/Certificate.vue:136
 msgid "Import"
 msgstr ""
 
-#: src/routes/index.ts:153 src/views/certificate/CertificateEditor.vue:84
+#: src/routes/index.ts:153 src/views/certificate/CertificateEditor.vue:85
 #, fuzzy
 msgid "Import Certificate"
 msgstr "Certificate Status"
@@ -857,11 +870,11 @@ msgstr "Certificate Status"
 msgid "Info"
 msgstr ""
 
-#: src/language/constants.ts:23
+#: src/language/constants.ts:24
 msgid "Initial core upgrader error"
 msgstr ""
 
-#: src/language/constants.ts:22
+#: src/language/constants.ts:23
 msgid "Initialing core upgrader"
 msgstr ""
 
@@ -887,7 +900,7 @@ msgstr ""
 msgid "Invalid"
 msgstr "Invalid E-mail!"
 
-#: src/views/certificate/Certificate.vue:139
+#: src/views/certificate/Certificate.vue:144
 #, fuzzy
 msgid "Issue wildcard certificate"
 msgstr "Certificate is valid"
@@ -906,7 +919,7 @@ msgstr "Enabled successfully"
 msgid "Jwt Secret"
 msgstr ""
 
-#: src/views/certificate/Certificate.vue:61
+#: src/views/certificate/Certificate.vue:66
 #: src/views/certificate/WildcardCertificate.vue:79
 #: src/views/domain/cert/components/AutoCertStepOne.vue:80
 msgid "Key Type"
@@ -924,8 +937,8 @@ msgstr "Leave blank for no change"
 msgid "Leave blank for the default: https://api.openai.com/"
 msgstr ""
 
-#: src/views/certificate/CertificateEditor.vue:200
-#: src/views/certificate/CertificateEditor.vue:213
+#: src/views/certificate/CertificateEditor.vue:207
+#: src/views/certificate/CertificateEditor.vue:220
 #, fuzzy
 msgid "Leave blank will not change anything"
 msgstr "Leave blank for no change"
@@ -972,7 +985,7 @@ msgstr "Location"
 msgid "Locations"
 msgstr "Locations"
 
-#: src/views/certificate/CertificateEditor.vue:223
+#: src/views/certificate/CertificateEditor.vue:230
 #, fuzzy
 msgid "Log"
 msgstr "Login"
@@ -1029,7 +1042,7 @@ msgstr "Manage Sites"
 msgid "Manage Users"
 msgstr "Manage Users"
 
-#: src/views/certificate/Certificate.vue:42
+#: src/views/certificate/Certificate.vue:43
 #, fuzzy
 msgid "Managed Certificate"
 msgstr "Certificate is valid"
@@ -1054,12 +1067,12 @@ msgstr "Advance Mode"
 
 #: src/components/ChatGPT/ChatGPT.vue:249
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:194
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:491
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:505
 #, fuzzy
 msgid "Modify"
 msgstr "Modify Config"
 
-#: src/routes/index.ts:144 src/views/certificate/CertificateEditor.vue:84
+#: src/routes/index.ts:144 src/views/certificate/CertificateEditor.vue:85
 #, fuzzy
 msgid "Modify Certificate"
 msgstr "Certificate Status"
@@ -1080,8 +1093,8 @@ msgstr "Single Directive"
 
 #: src/views/certificate/ACMEUser.vue:13
 #: src/views/certificate/Certificate.vue:20
-#: src/views/certificate/CertificateEditor.vue:146
-#: src/views/certificate/DNSCredential.vue:10 src/views/config/config.ts:7
+#: src/views/certificate/CertificateEditor.vue:147
+#: src/views/certificate/DNSCredential.vue:11 src/views/config/config.ts:7
 #: src/views/domain/cert/ChangeCert.vue:17
 #: src/views/domain/components/RightSettings.vue:83
 #: src/views/domain/components/SiteDuplicate.vue:133
@@ -1157,12 +1170,13 @@ msgstr "Saved successfully"
 
 #: src/components/ChatGPT/ChatGPT.vue:270
 #: src/components/Notification/Notification.vue:82
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:507
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:521
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:535
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:549
 #: src/views/domain/DomainList.vue:144
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:90
 #: src/views/domain/ngx_conf/LocationEditor.vue:71
-#: src/views/notification/Notification.vue:68
+#: src/views/notification/Notification.vue:70
 #: src/views/preference/BasicSettings.vue:101
 #: src/views/stream/StreamList.vue:165
 msgid "No"
@@ -1172,7 +1186,7 @@ msgstr "No"
 msgid "Node Secret"
 msgstr ""
 
-#: src/views/certificate/Certificate.vue:102
+#: src/views/certificate/Certificate.vue:107
 msgid "Not After"
 msgstr ""
 
@@ -1184,12 +1198,12 @@ msgstr "Not Found"
 msgid "Not Valid Before: %{date}"
 msgstr "Not Valid Before: %{date}"
 
-#: src/views/certificate/DNSCredential.vue:45
+#: src/views/certificate/DNSCredential.vue:49
 #: src/views/domain/cert/components/AutoCertStepOne.vue:45
 msgid "Note"
 msgstr ""
 
-#: src/views/notification/Notification.vue:60
+#: src/views/notification/Notification.vue:62
 #, fuzzy
 msgid "Notification"
 msgstr "Certificate is valid"
@@ -1222,8 +1236,9 @@ msgstr ""
 #: src/components/ChatGPT/ChatGPT.vue:271
 #: src/components/Notification/Notification.vue:83
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:56
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:508
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:522
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:536
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:550
 #: src/views/domain/cert/components/ObtainCert.vue:136
 #: src/views/domain/components/Deploy.vue:20
 #: src/views/domain/components/RightSettings.vue:50
@@ -1231,14 +1246,14 @@ msgstr ""
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:49
 #: src/views/domain/ngx_conf/NgxServer.vue:83
 #: src/views/domain/ngx_conf/NgxUpstream.vue:32
-#: src/views/notification/Notification.vue:69
+#: src/views/notification/Notification.vue:71
 #: src/views/stream/components/Deploy.vue:20
 #: src/views/stream/components/RightSettings.vue:50
 #: src/views/stream/StreamList.vue:166
 msgid "OK"
 msgstr ""
 
-#: src/views/certificate/DNSCredential.vue:55
+#: src/views/certificate/DNSCredential.vue:59
 msgid "Once the verification is complete, the records will be removed."
 msgstr ""
 
@@ -1290,15 +1305,15 @@ msgstr "Password (*)"
 msgid "Path"
 msgstr "Path"
 
-#: src/language/constants.ts:27
+#: src/language/constants.ts:28
 msgid "Perform core upgrade error"
 msgstr ""
 
-#: src/language/constants.ts:26
+#: src/language/constants.ts:27
 msgid "Performing core upgrade"
 msgstr ""
 
-#: src/views/certificate/DNSCredential.vue:49
+#: src/views/certificate/DNSCredential.vue:53
 msgid ""
 "Please fill in the API authentication credentials provided by your DNS "
 "provider."
@@ -1329,7 +1344,7 @@ msgstr "Please input your password!"
 msgid "Please input your username!"
 msgstr "Please input your username!"
 
-#: src/views/certificate/DNSCredential.vue:58
+#: src/views/certificate/DNSCredential.vue:62
 msgid ""
 "Please note that the unit of time configurations below are all in seconds."
 msgstr ""
@@ -1364,7 +1379,7 @@ msgstr ""
 msgid "Project Team"
 msgstr "Project Team"
 
-#: src/views/certificate/DNSCredential.vue:18
+#: src/views/certificate/DNSCredential.vue:19
 msgid "Provider"
 msgstr ""
 
@@ -1378,11 +1393,11 @@ msgstr "Reads"
 msgid "Receive"
 msgstr "Receive"
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:530
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:544
 msgid "Recover"
 msgstr ""
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:179
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:193
 #, fuzzy
 msgid "Recovered Successfully"
 msgstr "Saved successfully"
@@ -1455,12 +1470,12 @@ msgstr "Username"
 msgid "Renew Certificate"
 msgstr "Certificate is valid"
 
-#: src/language/constants.ts:34
+#: src/language/constants.ts:35
 #, fuzzy
 msgid "Renew Certificate Error"
 msgstr "Certificate is valid"
 
-#: src/language/constants.ts:33
+#: src/language/constants.ts:34
 #, fuzzy
 msgid "Renew Certificate Success"
 msgstr "Certificate is valid"
@@ -1471,11 +1486,11 @@ msgstr "Certificate is valid"
 msgid "Renew successfully"
 msgstr "Enabled successfully"
 
-#: src/language/constants.ts:31
+#: src/language/constants.ts:32
 msgid "Requested with wrong parameters"
 msgstr ""
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:439
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:453
 msgid "Reset"
 msgstr ""
 
@@ -1497,7 +1512,7 @@ msgid "Running"
 msgstr ""
 
 #: src/components/ChatGPT/ChatGPT.vue:252
-#: src/views/certificate/CertificateEditor.vue:242
+#: src/views/certificate/CertificateEditor.vue:249
 #: src/views/config/ConfigEdit.vue:96 src/views/domain/DomainEdit.vue:263
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:120
 #: src/views/preference/Preference.vue:130 src/views/stream/StreamEdit.vue:254
@@ -1515,7 +1530,7 @@ msgstr "Save error %{msg}"
 
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:39
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:104
-#: src/views/certificate/CertificateEditor.vue:45
+#: src/views/certificate/CertificateEditor.vue:46
 #: src/views/preference/Preference.vue:66
 #, fuzzy
 msgid "Save successfully"
@@ -1546,8 +1561,8 @@ msgstr "Send"
 #: src/components/StdDesign/StdDataDisplay/methods/exportCsv.ts:46
 #: src/components/StdDesign/StdDataDisplay/methods/sortable.ts:126
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:42
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:182
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:221
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:196
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:235
 #: src/views/config/ConfigEdit.vue:40 src/views/domain/DomainList.vue:81
 #: src/views/environment/Environment.vue:139 src/views/other/Install.vue:70
 #: src/views/preference/Preference.vue:70 src/views/stream/StreamList.vue:113
@@ -1604,24 +1619,24 @@ msgstr "Sites List"
 msgid "Sites List"
 msgstr "Sites List"
 
-#: src/views/certificate/CertificateEditor.vue:191
+#: src/views/certificate/CertificateEditor.vue:198
 #, fuzzy
 msgid "SSL Certificate Content"
 msgstr "Certificate Status"
 
-#: src/views/certificate/CertificateEditor.vue:204
+#: src/views/certificate/CertificateEditor.vue:211
 #, fuzzy
 msgid "SSL Certificate Key Content"
 msgstr "Certificate Status"
 
-#: src/views/certificate/Certificate.vue:75
-#: src/views/certificate/CertificateEditor.vue:176
+#: src/views/certificate/Certificate.vue:80
+#: src/views/certificate/CertificateEditor.vue:177
 #, fuzzy
 msgid "SSL Certificate Key Path"
 msgstr "Certificate Status"
 
-#: src/views/certificate/Certificate.vue:67
-#: src/views/certificate/CertificateEditor.vue:161
+#: src/views/certificate/Certificate.vue:72
+#: src/views/certificate/CertificateEditor.vue:162
 #, fuzzy
 msgid "SSL Certificate Path"
 msgstr "Certificate Status"
@@ -1637,7 +1652,7 @@ msgid "Stable"
 msgstr "Enabled"
 
 #: src/views/certificate/ACMEUser.vue:42
-#: src/views/certificate/Certificate.vue:83 src/views/domain/DomainList.vue:22
+#: src/views/certificate/Certificate.vue:88 src/views/domain/DomainList.vue:22
 #: src/views/environment/Environment.vue:76 src/views/stream/StreamList.vue:22
 msgid "Status"
 msgstr "Status"
@@ -1673,6 +1688,42 @@ msgstr ""
 msgid "Switch to light theme"
 msgstr ""
 
+#: src/views/certificate/Certificate.vue:42
+#, fuzzy
+msgid "Sync Certificate"
+msgstr "Certificate is valid"
+
+#: src/components/Notification/detailRender.ts:25
+#, fuzzy
+msgid ""
+"Sync Certificate %{cert_name} to %{env_name} failed, please upgrade the "
+"remote Nginx UI to the latest version"
+msgstr "Saved successfully"
+
+#: src/components/Notification/detailRender.ts:29
+#, fuzzy
+msgid "Sync Certificate %{cert_name} to %{env_name} failed, response: %{resp}"
+msgstr "Saved successfully"
+
+#: src/components/Notification/detailRender.ts:17
+#, fuzzy
+msgid "Sync Certificate %{cert_name} to %{env_name} successfully"
+msgstr "Saved successfully"
+
+#: src/language/constants.ts:38
+#, fuzzy
+msgid "Sync Certificate Error"
+msgstr "Certificate is valid"
+
+#: src/language/constants.ts:37
+#, fuzzy
+msgid "Sync Certificate Success"
+msgstr "Certificate is valid"
+
+#: src/views/certificate/CertificateEditor.vue:191
+msgid "Sync to"
+msgstr ""
+
 #: src/routes/index.ts:248
 msgid "System"
 msgstr ""
@@ -1703,11 +1754,11 @@ msgstr ""
 msgid "The filename cannot contain the following characters: %{c}"
 msgstr "The filename cannot contain the following characters: %{c}"
 
-#: src/views/certificate/CertificateEditor.vue:194
+#: src/views/certificate/CertificateEditor.vue:201
 msgid "The input is not a SSL Certificate"
 msgstr ""
 
-#: src/views/certificate/CertificateEditor.vue:207
+#: src/views/certificate/CertificateEditor.vue:214
 #, fuzzy
 msgid "The input is not a SSL Certificate Key"
 msgstr "Certificate Status"
@@ -1718,12 +1769,12 @@ msgid ""
 "dashes, and dots."
 msgstr ""
 
-#: src/views/certificate/CertificateEditor.vue:165
+#: src/views/certificate/CertificateEditor.vue:166
 #, fuzzy
 msgid "The path exists, but the file is not a certificate"
 msgstr "Certificate Status"
 
-#: src/views/certificate/CertificateEditor.vue:180
+#: src/views/certificate/CertificateEditor.vue:181
 msgid "The path exists, but the file is not a private key"
 msgstr ""
 
@@ -1756,17 +1807,17 @@ msgstr ""
 msgid "The username or password is incorrect"
 msgstr ""
 
-#: src/views/certificate/CertificateEditor.vue:101
+#: src/views/certificate/CertificateEditor.vue:102
 msgid "This Auto Cert item is invalid, please remove it."
 msgstr ""
 
-#: src/views/certificate/CertificateEditor.vue:91
+#: src/views/certificate/CertificateEditor.vue:92
 msgid "This certificate is managed by Nginx UI"
 msgstr ""
 
-#: src/views/certificate/CertificateEditor.vue:149
-#: src/views/certificate/CertificateEditor.vue:163
-#: src/views/certificate/CertificateEditor.vue:178
+#: src/views/certificate/CertificateEditor.vue:150
+#: src/views/certificate/CertificateEditor.vue:164
+#: src/views/certificate/CertificateEditor.vue:179
 msgid "This field is required"
 msgstr ""
 
@@ -1778,7 +1829,7 @@ msgstr ""
 msgid "Tips"
 msgstr ""
 
-#: src/views/notification/Notification.vue:18
+#: src/views/notification/Notification.vue:19
 msgid "Title"
 msgstr ""
 
@@ -1799,12 +1850,12 @@ msgid "Trash"
 msgstr ""
 
 #: src/views/certificate/Certificate.vue:37 src/views/config/config.ts:12
-#: src/views/notification/Notification.vue:12
+#: src/views/notification/Notification.vue:13
 msgid "Type"
 msgstr ""
 
 #: src/views/certificate/ACMEUser.vue:53
-#: src/views/certificate/DNSCredential.vue:23 src/views/config/config.ts:27
+#: src/views/certificate/DNSCredential.vue:27 src/views/config/config.ts:27
 #: src/views/config/ConfigEdit.vue:121
 #: src/views/domain/components/RightSettings.vue:86
 #: src/views/domain/DomainList.vue:41 src/views/environment/Environment.vue:122
@@ -1823,7 +1874,7 @@ msgstr "Saved successfully"
 msgid "Upgrade"
 msgstr ""
 
-#: src/language/constants.ts:28
+#: src/language/constants.ts:29
 #, fuzzy
 msgid "Upgraded successfully"
 msgstr "Saved successfully"
@@ -1858,11 +1909,11 @@ msgid "Username (*)"
 msgstr "Username (*)"
 
 #: src/views/certificate/ACMEUser.vue:87
-#: src/views/certificate/Certificate.vue:92
+#: src/views/certificate/Certificate.vue:97
 msgid "Valid"
 msgstr ""
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:477
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:491
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:103
 msgid "View"
 msgstr ""
@@ -1887,7 +1938,7 @@ msgstr "Basic Mode"
 msgid "Warning"
 msgstr "Warning"
 
-#: src/views/certificate/DNSCredential.vue:52
+#: src/views/certificate/DNSCredential.vue:56
 msgid ""
 "We will add one or more TXT records to the DNS records of your domain for "
 "ownership verification."

+ 135 - 83
app/src/language/es/app.po

@@ -29,10 +29,10 @@ msgid "ACME User"
 msgstr "Usuario"
 
 #: src/views/certificate/ACMEUser.vue:59
-#: src/views/certificate/Certificate.vue:108
-#: src/views/certificate/DNSCredential.vue:29 src/views/config/config.ts:34
+#: src/views/certificate/Certificate.vue:113
+#: src/views/certificate/DNSCredential.vue:33 src/views/config/config.ts:34
 #: src/views/domain/DomainList.vue:47 src/views/environment/Environment.vue:129
-#: src/views/notification/Notification.vue:35
+#: src/views/notification/Notification.vue:37
 #: src/views/stream/StreamList.vue:47 src/views/user/User.vue:43
 msgid "Action"
 msgstr "Acción"
@@ -98,7 +98,7 @@ msgid "Arch"
 msgstr "Arquitectura"
 
 #: src/components/Notification/Notification.vue:84
-#: src/views/notification/Notification.vue:70
+#: src/views/notification/Notification.vue:72
 msgid "Are you sure you want to clear all notifications?"
 msgstr "¿Está seguro de que desea borrar todas las notificaciones?"
 
@@ -106,7 +106,12 @@ msgstr "¿Está seguro de que desea borrar todas las notificaciones?"
 msgid "Are you sure you want to clear the record of chat?"
 msgstr "¿Está seguro de que desea borrar el registro del chat?"
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:509
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:551
+#, fuzzy
+msgid "Are you sure you want to delete this item permanently?"
+msgstr "¿Está seguro de que quiere borrar?"
+
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:523
 #, fuzzy
 msgid "Are you sure you want to delete this item?"
 msgstr "¿Está seguro de que quiere borrar?"
@@ -115,7 +120,7 @@ msgstr "¿Está seguro de que quiere borrar?"
 msgid "Are you sure you want to delete?"
 msgstr "¿Está seguro de que quiere borrar?"
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:523
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:537
 #, fuzzy
 msgid "Are you sure you want to recover this item?"
 msgstr "¿Está seguro de que quiere borrar esta directiva?"
@@ -162,7 +167,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/CertificateEditor.vue:235
+#: src/views/certificate/CertificateEditor.vue:242
 #: src/views/config/Config.vue:73 src/views/config/ConfigEdit.vue:87
 #: src/views/domain/DomainEdit.vue:256 src/views/nginx_log/NginxLog.vue:168
 #: src/views/stream/StreamEdit.vue:247
@@ -189,7 +194,7 @@ msgid "Basic Mode"
 msgstr "Modo Básico"
 
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:54
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:445
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:459
 msgid "Batch Modify"
 msgstr "Modificar por lotes"
 
@@ -233,12 +238,12 @@ msgstr "El certificado es válido"
 msgid "Certificate Renewal Interval"
 msgstr "El certificado es válido"
 
-#: src/views/certificate/CertificateEditor.vue:127
+#: src/views/certificate/CertificateEditor.vue:128
 #: src/views/domain/cert/Cert.vue:33
 msgid "Certificate Status"
 msgstr "Estado del Certificado"
 
-#: src/routes/index.ts:119 src/views/certificate/Certificate.vue:124
+#: src/routes/index.ts:119 src/views/certificate/Certificate.vue:129
 msgid "Certificates"
 msgstr "Certificados"
 
@@ -269,12 +274,12 @@ msgstr "Borrar las variables de entorno"
 
 #: src/components/ChatGPT/ChatGPT.vue:276
 #: src/components/Notification/Notification.vue:89
-#: src/views/notification/Notification.vue:75
+#: src/views/notification/Notification.vue:77
 msgid "Clear"
 msgstr "Borrar"
 
 #: src/components/Notification/Notification.vue:40
-#: src/views/notification/Notification.vue:44
+#: src/views/notification/Notification.vue:46
 msgid "Cleared successfully"
 msgstr "Limpiado exitoso"
 
@@ -340,7 +345,7 @@ msgstr "Crear"
 msgid "Create Another"
 msgstr "Crear otro"
 
-#: src/views/notification/Notification.vue:29 src/views/user/User.vue:31
+#: src/views/notification/Notification.vue:31 src/views/user/User.vue:31
 msgid "Created at"
 msgstr "Creado el"
 
@@ -383,7 +388,7 @@ msgstr "Base de datos (Opcional, default: database)"
 msgid "Days"
 msgstr ""
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:516
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:530
 #: src/views/domain/DomainList.vue:155
 #: src/views/domain/ngx_conf/NgxServer.vue:114
 #: src/views/domain/ngx_conf/NgxUpstream.vue:126
@@ -391,6 +396,10 @@ msgstr ""
 msgid "Delete"
 msgstr "Eliminar"
 
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:558
+msgid "Delete Permanently"
+msgstr ""
+
 #: src/views/domain/DomainList.vue:78
 msgid "Delete site: %{site_name}"
 msgstr "Eliminar sitio: %{site_name}"
@@ -399,7 +408,7 @@ msgstr "Eliminar sitio: %{site_name}"
 msgid "Delete stream: %{stream_name}"
 msgstr "Eliminar stream: %{site_name}"
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:171
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:185
 msgid "Deleted successfully"
 msgstr "Borrado exitoso"
 
@@ -430,7 +439,7 @@ msgstr "Desplegado con éxito"
 msgid "Description"
 msgstr "Descripción"
 
-#: src/views/notification/Notification.vue:25
+#: src/views/notification/Notification.vue:26
 msgid "Details"
 msgstr "Detalles"
 
@@ -475,7 +484,7 @@ msgstr "Desactivado con éxito"
 msgid "Disk IO"
 msgstr "I/O del disco"
 
-#: src/routes/index.ts:162 src/views/certificate/DNSCredential.vue:36
+#: src/routes/index.ts:162 src/views/certificate/DNSCredential.vue:40
 msgid "DNS Credentials"
 msgstr "Credenciales de DNS"
 
@@ -535,17 +544,17 @@ msgstr "Dominio"
 msgid "Domain Config Created Successfully"
 msgstr "Configuración de dominio creada con éxito"
 
-#: src/views/certificate/CertificateEditor.vue:111
+#: src/views/certificate/CertificateEditor.vue:112
 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 automática "
 "para %{config}"
 
-#: src/language/constants.ts:25
+#: src/language/constants.ts:26
 msgid "Download latest release error"
 msgstr "Error al descargar la última versión"
 
-#: src/language/constants.ts:24
+#: src/language/constants.ts:25
 msgid "Downloading latest release"
 msgstr "Descargando la última versión"
 
@@ -666,6 +675,11 @@ msgstr "Encriptar sitio web con Let's Encrypt"
 msgid "Environment"
 msgstr "Entorno"
 
+#: src/language/constants.ts:21
+#, fuzzy
+msgid "Environment variables cleaned"
+msgstr "Configuración de variables de entorno"
+
 #: src/views/dashboard/Environments.vue:82
 msgid "Environments"
 msgstr "Entornos"
@@ -682,7 +696,7 @@ msgstr "Registros de acceso"
 msgid "Executable Path"
 msgstr "Ruta ejecutable"
 
-#: src/views/certificate/Certificate.vue:96
+#: src/views/certificate/Certificate.vue:101
 msgid "Expired"
 msgstr "Vencido"
 
@@ -726,7 +740,7 @@ msgstr ""
 msgid "File"
 msgstr "Archivo"
 
-#: src/language/constants.ts:30
+#: src/language/constants.ts:31
 msgid "File exists"
 msgstr "El archivo existe"
 
@@ -763,7 +777,7 @@ msgstr "Error de formato %{msg}"
 msgid "Format successfully"
 msgstr "Formateado correctamente"
 
-#: src/views/certificate/Certificate.vue:43
+#: src/views/certificate/Certificate.vue:44
 msgid "General Certificate"
 msgstr "Certificado General"
 
@@ -811,11 +825,11 @@ msgstr "HTTP01"
 msgid "If left blank, the default CA Dir will be used."
 msgstr ""
 
-#: src/views/certificate/Certificate.vue:131
+#: src/views/certificate/Certificate.vue:136
 msgid "Import"
 msgstr "Importar"
 
-#: src/routes/index.ts:153 src/views/certificate/CertificateEditor.vue:84
+#: src/routes/index.ts:153 src/views/certificate/CertificateEditor.vue:85
 msgid "Import Certificate"
 msgstr "Importar Certificado"
 
@@ -823,11 +837,11 @@ msgstr "Importar Certificado"
 msgid "Info"
 msgstr "Información"
 
-#: src/language/constants.ts:23
+#: src/language/constants.ts:24
 msgid "Initial core upgrader error"
 msgstr "Error de actualización de kernel inicial"
 
-#: src/language/constants.ts:22
+#: src/language/constants.ts:23
 msgid "Initialing core upgrader"
 msgstr "Inicializando la actualización del kernel"
 
@@ -852,7 +866,7 @@ msgstr ""
 msgid "Invalid"
 msgstr "Válido"
 
-#: src/views/certificate/Certificate.vue:139
+#: src/views/certificate/Certificate.vue:144
 msgid "Issue wildcard certificate"
 msgstr "Obtener certificado comodín"
 
@@ -868,7 +882,7 @@ msgstr "Certificado emitido con éxito"
 msgid "Jwt Secret"
 msgstr "Secreto Jwt"
 
-#: src/views/certificate/Certificate.vue:61
+#: src/views/certificate/Certificate.vue:66
 #: src/views/certificate/WildcardCertificate.vue:79
 #: src/views/domain/cert/components/AutoCertStepOne.vue:80
 #, fuzzy
@@ -887,8 +901,8 @@ msgstr "Para no modificar dejar en blanco"
 msgid "Leave blank for the default: https://api.openai.com/"
 msgstr "Dejar en blanco para el valor predeterminado: https://api.openai.com/"
 
-#: src/views/certificate/CertificateEditor.vue:200
-#: src/views/certificate/CertificateEditor.vue:213
+#: src/views/certificate/CertificateEditor.vue:207
+#: src/views/certificate/CertificateEditor.vue:220
 msgid "Leave blank will not change anything"
 msgstr "Dejarlo en blanco no cambiará nada"
 
@@ -931,7 +945,7 @@ msgstr "Ubicación"
 msgid "Locations"
 msgstr "Ubicaciones"
 
-#: src/views/certificate/CertificateEditor.vue:223
+#: src/views/certificate/CertificateEditor.vue:230
 msgid "Log"
 msgstr "Registro"
 
@@ -985,7 +999,7 @@ msgstr "Administrar Transmisiones"
 msgid "Manage Users"
 msgstr "Administrar usuarios"
 
-#: src/views/certificate/Certificate.vue:42
+#: src/views/certificate/Certificate.vue:43
 msgid "Managed Certificate"
 msgstr "Certificado Administrado"
 
@@ -1009,11 +1023,11 @@ msgstr "Modo de ejecución"
 
 #: src/components/ChatGPT/ChatGPT.vue:249
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:194
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:491
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:505
 msgid "Modify"
 msgstr "Modificar"
 
-#: src/routes/index.ts:144 src/views/certificate/CertificateEditor.vue:84
+#: src/routes/index.ts:144 src/views/certificate/CertificateEditor.vue:85
 msgid "Modify Certificate"
 msgstr "Modificar Certificado"
 
@@ -1032,8 +1046,8 @@ msgstr "Directiva multilínea"
 
 #: src/views/certificate/ACMEUser.vue:13
 #: src/views/certificate/Certificate.vue:20
-#: src/views/certificate/CertificateEditor.vue:146
-#: src/views/certificate/DNSCredential.vue:10 src/views/config/config.ts:7
+#: src/views/certificate/CertificateEditor.vue:147
+#: src/views/certificate/DNSCredential.vue:11 src/views/config/config.ts:7
 #: src/views/domain/cert/ChangeCert.vue:17
 #: src/views/domain/components/RightSettings.vue:83
 #: src/views/domain/components/SiteDuplicate.vue:133
@@ -1106,12 +1120,13 @@ msgstr "Nginx reiniciado con éxito"
 
 #: src/components/ChatGPT/ChatGPT.vue:270
 #: src/components/Notification/Notification.vue:82
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:507
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:521
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:535
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:549
 #: src/views/domain/DomainList.vue:144
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:90
 #: src/views/domain/ngx_conf/LocationEditor.vue:71
-#: src/views/notification/Notification.vue:68
+#: src/views/notification/Notification.vue:70
 #: src/views/preference/BasicSettings.vue:101
 #: src/views/stream/StreamList.vue:165
 msgid "No"
@@ -1121,7 +1136,7 @@ msgstr "No"
 msgid "Node Secret"
 msgstr "Secreto del nodo"
 
-#: src/views/certificate/Certificate.vue:102
+#: src/views/certificate/Certificate.vue:107
 msgid "Not After"
 msgstr "No después de"
 
@@ -1133,12 +1148,12 @@ msgstr "No encontrado"
 msgid "Not Valid Before: %{date}"
 msgstr "No válido antes: %{date}"
 
-#: src/views/certificate/DNSCredential.vue:45
+#: src/views/certificate/DNSCredential.vue:49
 #: src/views/domain/cert/components/AutoCertStepOne.vue:45
 msgid "Note"
 msgstr "Nota"
 
-#: src/views/notification/Notification.vue:60
+#: src/views/notification/Notification.vue:62
 msgid "Notification"
 msgstr "Notificación"
 
@@ -1168,8 +1183,9 @@ msgstr ""
 #: src/components/ChatGPT/ChatGPT.vue:271
 #: src/components/Notification/Notification.vue:83
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:56
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:508
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:522
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:536
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:550
 #: src/views/domain/cert/components/ObtainCert.vue:136
 #: src/views/domain/components/Deploy.vue:20
 #: src/views/domain/components/RightSettings.vue:50
@@ -1177,14 +1193,14 @@ msgstr ""
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:49
 #: src/views/domain/ngx_conf/NgxServer.vue:83
 #: src/views/domain/ngx_conf/NgxUpstream.vue:32
-#: src/views/notification/Notification.vue:69
+#: src/views/notification/Notification.vue:71
 #: src/views/stream/components/Deploy.vue:20
 #: src/views/stream/components/RightSettings.vue:50
 #: src/views/stream/StreamList.vue:166
 msgid "OK"
 msgstr "OK"
 
-#: src/views/certificate/DNSCredential.vue:55
+#: src/views/certificate/DNSCredential.vue:59
 msgid "Once the verification is complete, the records will be removed."
 msgstr "Una vez que se complete la verificación, los registros se eliminarán."
 
@@ -1235,15 +1251,15 @@ msgstr "Contraseña (*)"
 msgid "Path"
 msgstr "Ruta"
 
-#: src/language/constants.ts:27
+#: src/language/constants.ts:28
 msgid "Perform core upgrade error"
 msgstr "Error al ejecutar la actualización del kernel"
 
-#: src/language/constants.ts:26
+#: src/language/constants.ts:27
 msgid "Performing core upgrade"
 msgstr "Realizando la actualizaciónd el kernel"
 
-#: src/views/certificate/DNSCredential.vue:49
+#: src/views/certificate/DNSCredential.vue:53
 msgid ""
 "Please fill in the API authentication credentials provided by your DNS "
 "provider."
@@ -1281,7 +1297,7 @@ msgstr "¡Por favor ingrese su contraseña!"
 msgid "Please input your username!"
 msgstr "¡Por favor ingrese su nombre de usuario!"
 
-#: src/views/certificate/DNSCredential.vue:58
+#: src/views/certificate/DNSCredential.vue:62
 msgid ""
 "Please note that the unit of time configurations below are all in seconds."
 msgstr ""
@@ -1317,7 +1333,7 @@ msgstr "Prohibir la eliminación del usuario predeterminado"
 msgid "Project Team"
 msgstr "Grupo del proyecto"
 
-#: src/views/certificate/DNSCredential.vue:18
+#: src/views/certificate/DNSCredential.vue:19
 msgid "Provider"
 msgstr "Proveedor"
 
@@ -1331,11 +1347,11 @@ msgstr "Lecturas"
 msgid "Receive"
 msgstr "Recibido"
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:530
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:544
 msgid "Recover"
 msgstr ""
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:179
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:193
 #, fuzzy
 msgid "Recovered Successfully"
 msgstr "Eliminado con éxito"
@@ -1406,11 +1422,11 @@ msgstr "Renombrar"
 msgid "Renew Certificate"
 msgstr "Renovar Certificado"
 
-#: src/language/constants.ts:34
+#: src/language/constants.ts:35
 msgid "Renew Certificate Error"
 msgstr "Error al renovar el Certificado"
 
-#: src/language/constants.ts:33
+#: src/language/constants.ts:34
 msgid "Renew Certificate Success"
 msgstr "Renovado de Certificado exitoso"
 
@@ -1419,11 +1435,11 @@ msgstr "Renovado de Certificado exitoso"
 msgid "Renew successfully"
 msgstr "Renovado con éxito"
 
-#: src/language/constants.ts:31
+#: src/language/constants.ts:32
 msgid "Requested with wrong parameters"
 msgstr "Pedido con parámetros incorrectos"
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:439
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:453
 msgid "Reset"
 msgstr "Limpiar"
 
@@ -1444,7 +1460,7 @@ msgid "Running"
 msgstr "Corriendo"
 
 #: src/components/ChatGPT/ChatGPT.vue:252
-#: src/views/certificate/CertificateEditor.vue:242
+#: src/views/certificate/CertificateEditor.vue:249
 #: src/views/config/ConfigEdit.vue:96 src/views/domain/DomainEdit.vue:263
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:120
 #: src/views/preference/Preference.vue:130 src/views/stream/StreamEdit.vue:254
@@ -1462,7 +1478,7 @@ msgstr "Error al guardar %{msg}"
 
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:39
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:104
-#: src/views/certificate/CertificateEditor.vue:45
+#: src/views/certificate/CertificateEditor.vue:46
 #: src/views/preference/Preference.vue:66
 msgid "Save successfully"
 msgstr "Guardado con éxito"
@@ -1492,8 +1508,8 @@ msgstr "Enviado"
 #: src/components/StdDesign/StdDataDisplay/methods/exportCsv.ts:46
 #: src/components/StdDesign/StdDataDisplay/methods/sortable.ts:126
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:42
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:182
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:221
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:196
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:235
 #: src/views/config/ConfigEdit.vue:40 src/views/domain/DomainList.vue:81
 #: src/views/environment/Environment.vue:139 src/views/other/Install.vue:70
 #: src/views/preference/Preference.vue:70 src/views/stream/StreamList.vue:113
@@ -1549,21 +1565,21 @@ msgstr "Registros del sitio"
 msgid "Sites List"
 msgstr "Lista de sitios"
 
-#: src/views/certificate/CertificateEditor.vue:191
+#: src/views/certificate/CertificateEditor.vue:198
 msgid "SSL Certificate Content"
 msgstr "Contenido de certificado SSL"
 
-#: src/views/certificate/CertificateEditor.vue:204
+#: src/views/certificate/CertificateEditor.vue:211
 msgid "SSL Certificate Key Content"
 msgstr "Contenido de la llave del certificado SSL"
 
-#: src/views/certificate/Certificate.vue:75
-#: src/views/certificate/CertificateEditor.vue:176
+#: src/views/certificate/Certificate.vue:80
+#: src/views/certificate/CertificateEditor.vue:177
 msgid "SSL Certificate Key Path"
 msgstr "Ruta de la llave del certificado SSL"
 
-#: src/views/certificate/Certificate.vue:67
-#: src/views/certificate/CertificateEditor.vue:161
+#: src/views/certificate/Certificate.vue:72
+#: src/views/certificate/CertificateEditor.vue:162
 msgid "SSL Certificate Path"
 msgstr "Ruta del certificado SSL"
 
@@ -1576,7 +1592,7 @@ msgid "Stable"
 msgstr "Estable"
 
 #: src/views/certificate/ACMEUser.vue:42
-#: src/views/certificate/Certificate.vue:83 src/views/domain/DomainList.vue:22
+#: src/views/certificate/Certificate.vue:88 src/views/domain/DomainList.vue:22
 #: src/views/environment/Environment.vue:76 src/views/stream/StreamList.vue:22
 msgid "Status"
 msgstr "Estado"
@@ -1611,6 +1627,42 @@ msgstr "Cambiar al tema oscuro"
 msgid "Switch to light theme"
 msgstr "Cambiar al tema claro"
 
+#: src/views/certificate/Certificate.vue:42
+#, fuzzy
+msgid "Sync Certificate"
+msgstr "Renovar Certificado"
+
+#: src/components/Notification/detailRender.ts:25
+#, fuzzy
+msgid ""
+"Sync Certificate %{cert_name} to %{env_name} failed, please upgrade the "
+"remote Nginx UI to the latest version"
+msgstr "Duplicado con éxito de %{conf_name} a %{node_name}"
+
+#: src/components/Notification/detailRender.ts:29
+#, fuzzy
+msgid "Sync Certificate %{cert_name} to %{env_name} failed, response: %{resp}"
+msgstr "Duplicado con éxito de %{conf_name} a %{node_name}"
+
+#: src/components/Notification/detailRender.ts:17
+#, fuzzy
+msgid "Sync Certificate %{cert_name} to %{env_name} successfully"
+msgstr "Duplicado con éxito de %{conf_name} a %{node_name}"
+
+#: src/language/constants.ts:38
+#, fuzzy
+msgid "Sync Certificate Error"
+msgstr "Error al renovar el Certificado"
+
+#: src/language/constants.ts:37
+#, fuzzy
+msgid "Sync Certificate Success"
+msgstr "Renovado de Certificado exitoso"
+
+#: src/views/certificate/CertificateEditor.vue:191
+msgid "Sync to"
+msgstr ""
+
 #: src/routes/index.ts:248
 msgid "System"
 msgstr "Sistema"
@@ -1641,11 +1693,11 @@ msgid "The filename cannot contain the following characters: %{c}"
 msgstr ""
 "El nombre del archivo no puede contener los siguientes caracteres: %{c}"
 
-#: src/views/certificate/CertificateEditor.vue:194
+#: src/views/certificate/CertificateEditor.vue:201
 msgid "The input is not a SSL Certificate"
 msgstr "La entrada no es un Certificado SSL"
 
-#: src/views/certificate/CertificateEditor.vue:207
+#: src/views/certificate/CertificateEditor.vue:214
 msgid "The input is not a SSL Certificate Key"
 msgstr "La entrada no es una clave de certificado SSL"
 
@@ -1655,12 +1707,12 @@ msgid ""
 "dashes, and dots."
 msgstr ""
 
-#: src/views/certificate/CertificateEditor.vue:165
+#: src/views/certificate/CertificateEditor.vue:166
 #, fuzzy
 msgid "The path exists, but the file is not a certificate"
 msgstr "La ruta existe, pero el archivo no es una clave privada"
 
-#: src/views/certificate/CertificateEditor.vue:180
+#: src/views/certificate/CertificateEditor.vue:181
 msgid "The path exists, but the file is not a private key"
 msgstr "La ruta existe, pero el archivo no es una clave privada"
 
@@ -1694,17 +1746,17 @@ msgstr "La URL no es válida"
 msgid "The username or password is incorrect"
 msgstr "El nombre de usuario o contraseña son incorrectos"
 
-#: src/views/certificate/CertificateEditor.vue:101
+#: src/views/certificate/CertificateEditor.vue:102
 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:91
+#: src/views/certificate/CertificateEditor.vue:92
 msgid "This certificate is managed by Nginx UI"
 msgstr "Este certificado es administrado por Nginx UI"
 
-#: src/views/certificate/CertificateEditor.vue:149
-#: src/views/certificate/CertificateEditor.vue:163
-#: src/views/certificate/CertificateEditor.vue:178
+#: src/views/certificate/CertificateEditor.vue:150
+#: src/views/certificate/CertificateEditor.vue:164
+#: src/views/certificate/CertificateEditor.vue:179
 msgid "This field is required"
 msgstr "Este campo es obligatorio"
 
@@ -1716,7 +1768,7 @@ msgstr "Este campo no debe estar vacío"
 msgid "Tips"
 msgstr ""
 
-#: src/views/notification/Notification.vue:18
+#: src/views/notification/Notification.vue:19
 msgid "Title"
 msgstr "Título"
 
@@ -1741,12 +1793,12 @@ msgid "Trash"
 msgstr ""
 
 #: src/views/certificate/Certificate.vue:37 src/views/config/config.ts:12
-#: src/views/notification/Notification.vue:12
+#: src/views/notification/Notification.vue:13
 msgid "Type"
 msgstr "Tipo"
 
 #: src/views/certificate/ACMEUser.vue:53
-#: src/views/certificate/DNSCredential.vue:23 src/views/config/config.ts:27
+#: src/views/certificate/DNSCredential.vue:27 src/views/config/config.ts:27
 #: src/views/config/ConfigEdit.vue:121
 #: src/views/domain/components/RightSettings.vue:86
 #: src/views/domain/DomainList.vue:41 src/views/environment/Environment.vue:122
@@ -1764,7 +1816,7 @@ msgstr "Actualización exitosa"
 msgid "Upgrade"
 msgstr "Actualizar"
 
-#: src/language/constants.ts:28
+#: src/language/constants.ts:29
 msgid "Upgraded successfully"
 msgstr "Actualización exitosa"
 
@@ -1797,11 +1849,11 @@ msgid "Username (*)"
 msgstr "Nombre de usuario (*)"
 
 #: src/views/certificate/ACMEUser.vue:87
-#: src/views/certificate/Certificate.vue:92
+#: src/views/certificate/Certificate.vue:97
 msgid "Valid"
 msgstr "Válido"
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:477
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:491
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:103
 msgid "View"
 msgstr "Ver"
@@ -1826,7 +1878,7 @@ msgstr "Modo Básico"
 msgid "Warning"
 msgstr "Advertencia"
 
-#: src/views/certificate/DNSCredential.vue:52
+#: src/views/certificate/DNSCredential.vue:56
 msgid ""
 "We will add one or more TXT records to the DNS records of your domain for "
 "ownership verification."

+ 135 - 83
app/src/language/fr_FR/app.po

@@ -26,10 +26,10 @@ msgid "ACME User"
 msgstr "Nom d'utilisateur"
 
 #: src/views/certificate/ACMEUser.vue:59
-#: src/views/certificate/Certificate.vue:108
-#: src/views/certificate/DNSCredential.vue:29 src/views/config/config.ts:34
+#: src/views/certificate/Certificate.vue:113
+#: src/views/certificate/DNSCredential.vue:33 src/views/config/config.ts:34
 #: src/views/domain/DomainList.vue:47 src/views/environment/Environment.vue:129
-#: src/views/notification/Notification.vue:35
+#: src/views/notification/Notification.vue:37
 #: src/views/stream/StreamList.vue:47 src/views/user/User.vue:43
 msgid "Action"
 msgstr "Action"
@@ -99,7 +99,7 @@ msgid "Arch"
 msgstr "Arch"
 
 #: src/components/Notification/Notification.vue:84
-#: src/views/notification/Notification.vue:70
+#: src/views/notification/Notification.vue:72
 #, fuzzy
 msgid "Are you sure you want to clear all notifications?"
 msgstr "Voulez-vous vraiment effacer l'historique du chat ?"
@@ -108,7 +108,12 @@ msgstr "Voulez-vous vraiment effacer l'historique du chat ?"
 msgid "Are you sure you want to clear the record of chat?"
 msgstr "Voulez-vous vraiment effacer l'historique du chat ?"
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:509
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:551
+#, fuzzy
+msgid "Are you sure you want to delete this item permanently?"
+msgstr "Etes-vous sûr que vous voulez supprimer ?"
+
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:523
 #, fuzzy
 msgid "Are you sure you want to delete this item?"
 msgstr "Etes-vous sûr que vous voulez supprimer ?"
@@ -117,7 +122,7 @@ msgstr "Etes-vous sûr que vous voulez supprimer ?"
 msgid "Are you sure you want to delete?"
 msgstr "Etes-vous sûr que vous voulez supprimer ?"
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:523
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:537
 #, fuzzy
 msgid "Are you sure you want to recover this item?"
 msgstr "Voulez-vous vraiment supprimer cette directive ?"
@@ -165,7 +170,7 @@ msgstr "Renouvellement automatique désactivé pour %{name}"
 msgid "Auto-renewal enabled for %{name}"
 msgstr "Renouvellement automatique activé pour %{name}"
 
-#: src/views/certificate/CertificateEditor.vue:235
+#: src/views/certificate/CertificateEditor.vue:242
 #: src/views/config/Config.vue:73 src/views/config/ConfigEdit.vue:87
 #: src/views/domain/DomainEdit.vue:256 src/views/nginx_log/NginxLog.vue:168
 #: src/views/stream/StreamEdit.vue:247
@@ -192,7 +197,7 @@ msgid "Basic Mode"
 msgstr "Mode simple"
 
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:54
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:445
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:459
 #, fuzzy
 msgid "Batch Modify"
 msgstr "Batch Modify"
@@ -237,12 +242,12 @@ msgstr "Le certificat est valide"
 msgid "Certificate Renewal Interval"
 msgstr "Le certificat est valide"
 
-#: src/views/certificate/CertificateEditor.vue:127
+#: src/views/certificate/CertificateEditor.vue:128
 #: src/views/domain/cert/Cert.vue:33
 msgid "Certificate Status"
 msgstr "État du certificat"
 
-#: src/routes/index.ts:119 src/views/certificate/Certificate.vue:124
+#: src/routes/index.ts:119 src/views/certificate/Certificate.vue:129
 #, fuzzy
 msgid "Certificates"
 msgstr "État du certificat"
@@ -275,12 +280,12 @@ msgstr "Nettoyage des variables d'environnement"
 
 #: src/components/ChatGPT/ChatGPT.vue:276
 #: src/components/Notification/Notification.vue:89
-#: src/views/notification/Notification.vue:75
+#: src/views/notification/Notification.vue:77
 msgid "Clear"
 msgstr "Effacer"
 
 #: src/components/Notification/Notification.vue:40
-#: src/views/notification/Notification.vue:44
+#: src/views/notification/Notification.vue:46
 #, fuzzy
 msgid "Cleared successfully"
 msgstr "Désactivé avec succès"
@@ -348,7 +353,7 @@ msgstr "Créé le"
 msgid "Create Another"
 msgstr "Créer un autre"
 
-#: src/views/notification/Notification.vue:29 src/views/user/User.vue:31
+#: src/views/notification/Notification.vue:31 src/views/user/User.vue:31
 msgid "Created at"
 msgstr "Créé le"
 
@@ -391,7 +396,7 @@ msgstr "Base de données (Facultatif, par défaut : database)"
 msgid "Days"
 msgstr ""
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:516
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:530
 #: src/views/domain/DomainList.vue:155
 #: src/views/domain/ngx_conf/NgxServer.vue:114
 #: src/views/domain/ngx_conf/NgxUpstream.vue:126
@@ -399,6 +404,10 @@ msgstr ""
 msgid "Delete"
 msgstr "Supprimer"
 
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:558
+msgid "Delete Permanently"
+msgstr ""
+
 #: src/views/domain/DomainList.vue:78
 msgid "Delete site: %{site_name}"
 msgstr "Supprimer le site : %{site_name}"
@@ -408,7 +417,7 @@ msgstr "Supprimer le site : %{site_name}"
 msgid "Delete stream: %{stream_name}"
 msgstr "Supprimer le site : %{site_name}"
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:171
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:185
 #, fuzzy
 msgid "Deleted successfully"
 msgstr "Désactivé avec succès"
@@ -441,7 +450,7 @@ msgstr "Sauvegarde réussie"
 msgid "Description"
 msgstr "Description"
 
-#: src/views/notification/Notification.vue:25
+#: src/views/notification/Notification.vue:26
 msgid "Details"
 msgstr ""
 
@@ -488,7 +497,7 @@ msgstr "Désactivé avec succès"
 msgid "Disk IO"
 msgstr "E/S disque"
 
-#: src/routes/index.ts:162 src/views/certificate/DNSCredential.vue:36
+#: src/routes/index.ts:162 src/views/certificate/DNSCredential.vue:40
 msgid "DNS Credentials"
 msgstr "Identifiants DNS"
 
@@ -552,18 +561,18 @@ msgstr ""
 msgid "Domain Config Created Successfully"
 msgstr "La configuration du domaine a été créée avec succès"
 
-#: src/views/certificate/CertificateEditor.vue:111
+#: src/views/certificate/CertificateEditor.vue:112
 #, fuzzy
 msgid "Domains list is empty, try to reopen Auto Cert for %{config}"
 msgstr ""
 "La liste des domaines est vide, essayez de rouvrir la certification "
 "automatique pour %{config}"
 
-#: src/language/constants.ts:25
+#: src/language/constants.ts:26
 msgid "Download latest release error"
 msgstr "Erreur de téléchargement de la dernière version"
 
-#: src/language/constants.ts:24
+#: src/language/constants.ts:25
 msgid "Downloading latest release"
 msgstr "Téléchargement de la dernière version"
 
@@ -691,6 +700,11 @@ msgstr "Crypter le site Web avec Let's Encrypt"
 msgid "Environment"
 msgstr ""
 
+#: src/language/constants.ts:21
+#, fuzzy
+msgid "Environment variables cleaned"
+msgstr "Définition des variables d'environnement"
+
 #: src/views/dashboard/Environments.vue:82
 #, fuzzy
 msgid "Environments"
@@ -708,7 +722,7 @@ msgstr "Journaux d'erreurs"
 msgid "Executable Path"
 msgstr "Chemin exécutable"
 
-#: src/views/certificate/Certificate.vue:96
+#: src/views/certificate/Certificate.vue:101
 msgid "Expired"
 msgstr ""
 
@@ -754,7 +768,7 @@ msgstr ""
 msgid "File"
 msgstr "Fichier"
 
-#: src/language/constants.ts:30
+#: src/language/constants.ts:31
 msgid "File exists"
 msgstr "Le fichier existe"
 
@@ -793,7 +807,7 @@ msgstr "Erreur de format %{msg}"
 msgid "Format successfully"
 msgstr "Formaté avec succès"
 
-#: src/views/certificate/Certificate.vue:43
+#: src/views/certificate/Certificate.vue:44
 #, fuzzy
 msgid "General Certificate"
 msgstr "Changer de certificat"
@@ -842,12 +856,12 @@ msgstr "HTTP01"
 msgid "If left blank, the default CA Dir will be used."
 msgstr ""
 
-#: src/views/certificate/Certificate.vue:131
+#: src/views/certificate/Certificate.vue:136
 #, fuzzy
 msgid "Import"
 msgstr "Exporter"
 
-#: src/routes/index.ts:153 src/views/certificate/CertificateEditor.vue:84
+#: src/routes/index.ts:153 src/views/certificate/CertificateEditor.vue:85
 #, fuzzy
 msgid "Import Certificate"
 msgstr "État du certificat"
@@ -856,11 +870,11 @@ msgstr "État du certificat"
 msgid "Info"
 msgstr ""
 
-#: src/language/constants.ts:23
+#: src/language/constants.ts:24
 msgid "Initial core upgrader error"
 msgstr "Erreur du programme de mise à niveau initial du core"
 
-#: src/language/constants.ts:22
+#: src/language/constants.ts:23
 msgid "Initialing core upgrader"
 msgstr "Initialisation du programme de mise à niveau du core"
 
@@ -884,7 +898,7 @@ msgstr ""
 msgid "Invalid"
 msgstr ""
 
-#: src/views/certificate/Certificate.vue:139
+#: src/views/certificate/Certificate.vue:144
 #, fuzzy
 msgid "Issue wildcard certificate"
 msgstr "Obtenir un certificat"
@@ -902,7 +916,7 @@ msgstr "Certificat délivré avec succès"
 msgid "Jwt Secret"
 msgstr "Secret Jwt"
 
-#: src/views/certificate/Certificate.vue:61
+#: src/views/certificate/Certificate.vue:66
 #: src/views/certificate/WildcardCertificate.vue:79
 #: src/views/domain/cert/components/AutoCertStepOne.vue:80
 #, fuzzy
@@ -921,8 +935,8 @@ msgstr "Laisser vide pour aucun changement"
 msgid "Leave blank for the default: https://api.openai.com/"
 msgstr "Laissez vide pour la valeur par défaut : https://api.openai.com/"
 
-#: src/views/certificate/CertificateEditor.vue:200
-#: src/views/certificate/CertificateEditor.vue:213
+#: src/views/certificate/CertificateEditor.vue:207
+#: src/views/certificate/CertificateEditor.vue:220
 #, fuzzy
 msgid "Leave blank will not change anything"
 msgstr "Laisser vide pour aucun changement"
@@ -971,7 +985,7 @@ msgstr "Localisation"
 msgid "Locations"
 msgstr "Localisations"
 
-#: src/views/certificate/CertificateEditor.vue:223
+#: src/views/certificate/CertificateEditor.vue:230
 #, fuzzy
 msgid "Log"
 msgstr "Connexion"
@@ -1028,7 +1042,7 @@ msgstr "Gérer les sites"
 msgid "Manage Users"
 msgstr "Gérer les utilisateurs"
 
-#: src/views/certificate/Certificate.vue:42
+#: src/views/certificate/Certificate.vue:43
 #, fuzzy
 msgid "Managed Certificate"
 msgstr "Changer de certificat"
@@ -1053,11 +1067,11 @@ msgstr "Mode d'exécution"
 
 #: src/components/ChatGPT/ChatGPT.vue:249
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:194
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:491
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:505
 msgid "Modify"
 msgstr "Modifier"
 
-#: src/routes/index.ts:144 src/views/certificate/CertificateEditor.vue:84
+#: src/routes/index.ts:144 src/views/certificate/CertificateEditor.vue:85
 #, fuzzy
 msgid "Modify Certificate"
 msgstr "État du certificat"
@@ -1077,8 +1091,8 @@ msgstr "Directive multiligne"
 
 #: src/views/certificate/ACMEUser.vue:13
 #: src/views/certificate/Certificate.vue:20
-#: src/views/certificate/CertificateEditor.vue:146
-#: src/views/certificate/DNSCredential.vue:10 src/views/config/config.ts:7
+#: src/views/certificate/CertificateEditor.vue:147
+#: src/views/certificate/DNSCredential.vue:11 src/views/config/config.ts:7
 #: src/views/domain/cert/ChangeCert.vue:17
 #: src/views/domain/components/RightSettings.vue:83
 #: src/views/domain/components/SiteDuplicate.vue:133
@@ -1152,12 +1166,13 @@ msgstr "Nginx a redémarré avec succès"
 
 #: src/components/ChatGPT/ChatGPT.vue:270
 #: src/components/Notification/Notification.vue:82
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:507
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:521
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:535
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:549
 #: src/views/domain/DomainList.vue:144
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:90
 #: src/views/domain/ngx_conf/LocationEditor.vue:71
-#: src/views/notification/Notification.vue:68
+#: src/views/notification/Notification.vue:70
 #: src/views/preference/BasicSettings.vue:101
 #: src/views/stream/StreamList.vue:165
 msgid "No"
@@ -1168,7 +1183,7 @@ msgstr "Non"
 msgid "Node Secret"
 msgstr "Secret Jwt"
 
-#: src/views/certificate/Certificate.vue:102
+#: src/views/certificate/Certificate.vue:107
 msgid "Not After"
 msgstr ""
 
@@ -1180,12 +1195,12 @@ msgstr "Introuvable"
 msgid "Not Valid Before: %{date}"
 msgstr "Non valide avant : %{date}"
 
-#: src/views/certificate/DNSCredential.vue:45
+#: src/views/certificate/DNSCredential.vue:49
 #: src/views/domain/cert/components/AutoCertStepOne.vue:45
 msgid "Note"
 msgstr "Note"
 
-#: src/views/notification/Notification.vue:60
+#: src/views/notification/Notification.vue:62
 #, fuzzy
 msgid "Notification"
 msgstr "Certification"
@@ -1217,8 +1232,9 @@ msgstr ""
 #: src/components/ChatGPT/ChatGPT.vue:271
 #: src/components/Notification/Notification.vue:83
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:56
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:508
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:522
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:536
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:550
 #: src/views/domain/cert/components/ObtainCert.vue:136
 #: src/views/domain/components/Deploy.vue:20
 #: src/views/domain/components/RightSettings.vue:50
@@ -1226,14 +1242,14 @@ msgstr ""
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:49
 #: src/views/domain/ngx_conf/NgxServer.vue:83
 #: src/views/domain/ngx_conf/NgxUpstream.vue:32
-#: src/views/notification/Notification.vue:69
+#: src/views/notification/Notification.vue:71
 #: src/views/stream/components/Deploy.vue:20
 #: src/views/stream/components/RightSettings.vue:50
 #: src/views/stream/StreamList.vue:166
 msgid "OK"
 msgstr "OK"
 
-#: src/views/certificate/DNSCredential.vue:55
+#: src/views/certificate/DNSCredential.vue:59
 msgid "Once the verification is complete, the records will be removed."
 msgstr ""
 
@@ -1284,15 +1300,15 @@ msgstr "Mot de passe (*)"
 msgid "Path"
 msgstr "Chemin"
 
-#: src/language/constants.ts:27
+#: src/language/constants.ts:28
 msgid "Perform core upgrade error"
 msgstr "Erreur lors de la mise a niveau du core"
 
-#: src/language/constants.ts:26
+#: src/language/constants.ts:27
 msgid "Performing core upgrade"
 msgstr "Exécution de la mise à niveau du core"
 
-#: src/views/certificate/DNSCredential.vue:49
+#: src/views/certificate/DNSCredential.vue:53
 msgid ""
 "Please fill in the API authentication credentials provided by your DNS "
 "provider."
@@ -1330,7 +1346,7 @@ msgstr "Veuillez saisir votre mot de passe !"
 msgid "Please input your username!"
 msgstr "Veuillez saisir votre nom d'utilisateur !"
 
-#: src/views/certificate/DNSCredential.vue:58
+#: src/views/certificate/DNSCredential.vue:62
 msgid ""
 "Please note that the unit of time configurations below are all in seconds."
 msgstr ""
@@ -1365,7 +1381,7 @@ msgstr "Interdire la suppression de l'utilisateur par défaut"
 msgid "Project Team"
 msgstr "Groupe de projet"
 
-#: src/views/certificate/DNSCredential.vue:18
+#: src/views/certificate/DNSCredential.vue:19
 msgid "Provider"
 msgstr "Fournisseur"
 
@@ -1381,11 +1397,11 @@ msgstr "Lectures"
 msgid "Receive"
 msgstr "Recevoir"
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:530
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:544
 msgid "Recover"
 msgstr ""
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:179
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:193
 #, fuzzy
 msgid "Recovered Successfully"
 msgstr "Enregistré avec succès"
@@ -1459,12 +1475,12 @@ msgstr "Nom d'utilisateur"
 msgid "Renew Certificate"
 msgstr "Changer de certificat"
 
-#: src/language/constants.ts:34
+#: src/language/constants.ts:35
 #, fuzzy
 msgid "Renew Certificate Error"
 msgstr "Changer de certificat"
 
-#: src/language/constants.ts:33
+#: src/language/constants.ts:34
 #, fuzzy
 msgid "Renew Certificate Success"
 msgstr "Changer de certificat"
@@ -1475,11 +1491,11 @@ msgstr "Changer de certificat"
 msgid "Renew successfully"
 msgstr "Activé avec succès"
 
-#: src/language/constants.ts:31
+#: src/language/constants.ts:32
 msgid "Requested with wrong parameters"
 msgstr ""
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:439
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:453
 msgid "Reset"
 msgstr "Réinitialiser"
 
@@ -1500,7 +1516,7 @@ msgid "Running"
 msgstr "En cours d'éxécution"
 
 #: src/components/ChatGPT/ChatGPT.vue:252
-#: src/views/certificate/CertificateEditor.vue:242
+#: src/views/certificate/CertificateEditor.vue:249
 #: src/views/config/ConfigEdit.vue:96 src/views/domain/DomainEdit.vue:263
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:120
 #: src/views/preference/Preference.vue:130 src/views/stream/StreamEdit.vue:254
@@ -1518,7 +1534,7 @@ msgstr "Enregistrer l'erreur %{msg}"
 
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:39
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:104
-#: src/views/certificate/CertificateEditor.vue:45
+#: src/views/certificate/CertificateEditor.vue:46
 #: src/views/preference/Preference.vue:66
 msgid "Save successfully"
 msgstr "Sauvegarde réussie"
@@ -1548,8 +1564,8 @@ msgstr "Envoyer"
 #: src/components/StdDesign/StdDataDisplay/methods/exportCsv.ts:46
 #: src/components/StdDesign/StdDataDisplay/methods/sortable.ts:126
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:42
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:182
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:221
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:196
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:235
 #: src/views/config/ConfigEdit.vue:40 src/views/domain/DomainList.vue:81
 #: src/views/environment/Environment.vue:139 src/views/other/Install.vue:70
 #: src/views/preference/Preference.vue:70 src/views/stream/StreamList.vue:113
@@ -1607,23 +1623,23 @@ msgstr "Journaux du site"
 msgid "Sites List"
 msgstr "Liste des sites"
 
-#: src/views/certificate/CertificateEditor.vue:191
+#: src/views/certificate/CertificateEditor.vue:198
 #, fuzzy
 msgid "SSL Certificate Content"
 msgstr "Contenu de la certification SSL"
 
-#: src/views/certificate/CertificateEditor.vue:204
+#: src/views/certificate/CertificateEditor.vue:211
 #, fuzzy
 msgid "SSL Certificate Key Content"
 msgstr "Contenu de la clé de certification SSL"
 
-#: src/views/certificate/Certificate.vue:75
-#: src/views/certificate/CertificateEditor.vue:176
+#: src/views/certificate/Certificate.vue:80
+#: src/views/certificate/CertificateEditor.vue:177
 msgid "SSL Certificate Key Path"
 msgstr "Chemin de la clé du certificat SSL"
 
-#: src/views/certificate/Certificate.vue:67
-#: src/views/certificate/CertificateEditor.vue:161
+#: src/views/certificate/Certificate.vue:72
+#: src/views/certificate/CertificateEditor.vue:162
 msgid "SSL Certificate Path"
 msgstr "Chemin du certificat SSL"
 
@@ -1638,7 +1654,7 @@ msgid "Stable"
 msgstr "Tableau"
 
 #: src/views/certificate/ACMEUser.vue:42
-#: src/views/certificate/Certificate.vue:83 src/views/domain/DomainList.vue:22
+#: src/views/certificate/Certificate.vue:88 src/views/domain/DomainList.vue:22
 #: src/views/environment/Environment.vue:76 src/views/stream/StreamList.vue:22
 msgid "Status"
 msgstr "Statut"
@@ -1675,6 +1691,42 @@ msgstr ""
 msgid "Switch to light theme"
 msgstr ""
 
+#: src/views/certificate/Certificate.vue:42
+#, fuzzy
+msgid "Sync Certificate"
+msgstr "Changer de certificat"
+
+#: src/components/Notification/detailRender.ts:25
+#, fuzzy
+msgid ""
+"Sync Certificate %{cert_name} to %{env_name} failed, please upgrade the "
+"remote Nginx UI to the latest version"
+msgstr "Dupliqué avec succès"
+
+#: src/components/Notification/detailRender.ts:29
+#, fuzzy
+msgid "Sync Certificate %{cert_name} to %{env_name} failed, response: %{resp}"
+msgstr "Dupliqué avec succès"
+
+#: src/components/Notification/detailRender.ts:17
+#, fuzzy
+msgid "Sync Certificate %{cert_name} to %{env_name} successfully"
+msgstr "Dupliqué avec succès"
+
+#: src/language/constants.ts:38
+#, fuzzy
+msgid "Sync Certificate Error"
+msgstr "Changer de certificat"
+
+#: src/language/constants.ts:37
+#, fuzzy
+msgid "Sync Certificate Success"
+msgstr "Changer de certificat"
+
+#: src/views/certificate/CertificateEditor.vue:191
+msgid "Sync to"
+msgstr ""
+
 #: src/routes/index.ts:248
 msgid "System"
 msgstr "Système"
@@ -1705,11 +1757,11 @@ msgstr ""
 msgid "The filename cannot contain the following characters: %{c}"
 msgstr "Le nom de fichier ne peut pas contenir les caractères suivants : %{c}"
 
-#: src/views/certificate/CertificateEditor.vue:194
+#: src/views/certificate/CertificateEditor.vue:201
 msgid "The input is not a SSL Certificate"
 msgstr ""
 
-#: src/views/certificate/CertificateEditor.vue:207
+#: src/views/certificate/CertificateEditor.vue:214
 #, fuzzy
 msgid "The input is not a SSL Certificate Key"
 msgstr "Chemin de la clé du certificat SSL"
@@ -1720,12 +1772,12 @@ msgid ""
 "dashes, and dots."
 msgstr ""
 
-#: src/views/certificate/CertificateEditor.vue:165
+#: src/views/certificate/CertificateEditor.vue:166
 #, fuzzy
 msgid "The path exists, but the file is not a certificate"
 msgstr "Chemin de la clé du certificat SSL"
 
-#: src/views/certificate/CertificateEditor.vue:180
+#: src/views/certificate/CertificateEditor.vue:181
 msgid "The path exists, but the file is not a private key"
 msgstr ""
 
@@ -1759,20 +1811,20 @@ msgstr ""
 msgid "The username or password is incorrect"
 msgstr "Le pseudo ou mot de passe est incorect"
 
-#: src/views/certificate/CertificateEditor.vue:101
+#: src/views/certificate/CertificateEditor.vue:102
 #, fuzzy
 msgid "This Auto Cert item is invalid, please remove it."
 msgstr ""
 "Cet élément de certification automatique n'est pas valide, veuillez le "
 "supprimer."
 
-#: src/views/certificate/CertificateEditor.vue:91
+#: src/views/certificate/CertificateEditor.vue:92
 msgid "This certificate is managed by Nginx UI"
 msgstr ""
 
-#: src/views/certificate/CertificateEditor.vue:149
-#: src/views/certificate/CertificateEditor.vue:163
-#: src/views/certificate/CertificateEditor.vue:178
+#: src/views/certificate/CertificateEditor.vue:150
+#: src/views/certificate/CertificateEditor.vue:164
+#: src/views/certificate/CertificateEditor.vue:179
 msgid "This field is required"
 msgstr ""
 
@@ -1784,7 +1836,7 @@ msgstr ""
 msgid "Tips"
 msgstr ""
 
-#: src/views/notification/Notification.vue:18
+#: src/views/notification/Notification.vue:19
 msgid "Title"
 msgstr ""
 
@@ -1809,12 +1861,12 @@ msgid "Trash"
 msgstr ""
 
 #: src/views/certificate/Certificate.vue:37 src/views/config/config.ts:12
-#: src/views/notification/Notification.vue:12
+#: src/views/notification/Notification.vue:13
 msgid "Type"
 msgstr "Type"
 
 #: src/views/certificate/ACMEUser.vue:53
-#: src/views/certificate/DNSCredential.vue:23 src/views/config/config.ts:27
+#: src/views/certificate/DNSCredential.vue:27 src/views/config/config.ts:27
 #: src/views/config/ConfigEdit.vue:121
 #: src/views/domain/components/RightSettings.vue:86
 #: src/views/domain/DomainList.vue:41 src/views/environment/Environment.vue:122
@@ -1832,7 +1884,7 @@ msgstr "Mis à jour avec succés"
 msgid "Upgrade"
 msgstr "Mettre à niveau"
 
-#: src/language/constants.ts:28
+#: src/language/constants.ts:29
 msgid "Upgraded successfully"
 msgstr "Mise à niveau réussie"
 
@@ -1866,11 +1918,11 @@ msgid "Username (*)"
 msgstr "Nom d'utilisateur (*)"
 
 #: src/views/certificate/ACMEUser.vue:87
-#: src/views/certificate/Certificate.vue:92
+#: src/views/certificate/Certificate.vue:97
 msgid "Valid"
 msgstr ""
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:477
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:491
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:103
 msgid "View"
 msgstr "Voir"
@@ -1895,7 +1947,7 @@ msgstr "Mode simple"
 msgid "Warning"
 msgstr "Avertissement"
 
-#: src/views/certificate/DNSCredential.vue:52
+#: src/views/certificate/DNSCredential.vue:56
 msgid ""
 "We will add one or more TXT records to the DNS records of your domain for "
 "ownership verification."

+ 135 - 83
app/src/language/ko_KR/app.po

@@ -28,10 +28,10 @@ msgid "ACME User"
 msgstr "사용자 이름"
 
 #: src/views/certificate/ACMEUser.vue:59
-#: src/views/certificate/Certificate.vue:108
-#: src/views/certificate/DNSCredential.vue:29 src/views/config/config.ts:34
+#: src/views/certificate/Certificate.vue:113
+#: src/views/certificate/DNSCredential.vue:33 src/views/config/config.ts:34
 #: src/views/domain/DomainList.vue:47 src/views/environment/Environment.vue:129
-#: src/views/notification/Notification.vue:35
+#: src/views/notification/Notification.vue:37
 #: src/views/stream/StreamList.vue:47 src/views/user/User.vue:43
 msgid "Action"
 msgstr "작업"
@@ -97,7 +97,7 @@ msgid "Arch"
 msgstr "아키텍처"
 
 #: src/components/Notification/Notification.vue:84
-#: src/views/notification/Notification.vue:70
+#: src/views/notification/Notification.vue:72
 msgid "Are you sure you want to clear all notifications?"
 msgstr "모든 알림을 지우시겠습니까?"
 
@@ -105,7 +105,12 @@ msgstr "모든 알림을 지우시겠습니까?"
 msgid "Are you sure you want to clear the record of chat?"
 msgstr "기록을 지우시겠습니까?"
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:509
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:551
+#, fuzzy
+msgid "Are you sure you want to delete this item permanently?"
+msgstr "정말 삭제하시겠습니까?"
+
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:523
 #, fuzzy
 msgid "Are you sure you want to delete this item?"
 msgstr "정말 삭제하시겠습니까?"
@@ -114,7 +119,7 @@ msgstr "정말 삭제하시겠습니까?"
 msgid "Are you sure you want to delete?"
 msgstr "정말 삭제하시겠습니까?"
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:523
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:537
 #, fuzzy
 msgid "Are you sure you want to recover this item?"
 msgstr "이 지시문을 정말로 제거하시겠습니까?"
@@ -161,7 +166,7 @@ msgstr "%{name}에 대한 자동 갱신 비활성화됨"
 msgid "Auto-renewal enabled for %{name}"
 msgstr "%{name}에 대한 자동 갱신 활성화됨"
 
-#: src/views/certificate/CertificateEditor.vue:235
+#: src/views/certificate/CertificateEditor.vue:242
 #: src/views/config/Config.vue:73 src/views/config/ConfigEdit.vue:87
 #: src/views/domain/DomainEdit.vue:256 src/views/nginx_log/NginxLog.vue:168
 #: src/views/stream/StreamEdit.vue:247
@@ -188,7 +193,7 @@ msgid "Basic Mode"
 msgstr "기본 모드"
 
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:54
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:445
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:459
 msgid "Batch Modify"
 msgstr "일괄 수정"
 
@@ -232,12 +237,12 @@ msgstr "인증서가 유효합니다"
 msgid "Certificate Renewal Interval"
 msgstr "인증서가 유효합니다"
 
-#: src/views/certificate/CertificateEditor.vue:127
+#: src/views/certificate/CertificateEditor.vue:128
 #: src/views/domain/cert/Cert.vue:33
 msgid "Certificate Status"
 msgstr "인증서 상태"
 
-#: src/routes/index.ts:119 src/views/certificate/Certificate.vue:124
+#: src/routes/index.ts:119 src/views/certificate/Certificate.vue:129
 msgid "Certificates"
 msgstr "인증서"
 
@@ -268,12 +273,12 @@ msgstr "환경 변수 정리"
 
 #: src/components/ChatGPT/ChatGPT.vue:276
 #: src/components/Notification/Notification.vue:89
-#: src/views/notification/Notification.vue:75
+#: src/views/notification/Notification.vue:77
 msgid "Clear"
 msgstr "클리어"
 
 #: src/components/Notification/Notification.vue:40
-#: src/views/notification/Notification.vue:44
+#: src/views/notification/Notification.vue:46
 msgid "Cleared successfully"
 msgstr "성공적으로 제거됨"
 
@@ -338,7 +343,7 @@ msgstr "생성"
 msgid "Create Another"
 msgstr "다른 것 생성하기"
 
-#: src/views/notification/Notification.vue:29 src/views/user/User.vue:31
+#: src/views/notification/Notification.vue:31 src/views/user/User.vue:31
 msgid "Created at"
 msgstr "생성 시간"
 
@@ -381,7 +386,7 @@ msgstr "데이터베이스 (선택사항, 기본값: database)"
 msgid "Days"
 msgstr ""
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:516
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:530
 #: src/views/domain/DomainList.vue:155
 #: src/views/domain/ngx_conf/NgxServer.vue:114
 #: src/views/domain/ngx_conf/NgxUpstream.vue:126
@@ -389,6 +394,10 @@ msgstr ""
 msgid "Delete"
 msgstr "삭제"
 
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:558
+msgid "Delete Permanently"
+msgstr ""
+
 #: src/views/domain/DomainList.vue:78
 msgid "Delete site: %{site_name}"
 msgstr "사이트 삭제: %{site_name}"
@@ -397,7 +406,7 @@ msgstr "사이트 삭제: %{site_name}"
 msgid "Delete stream: %{stream_name}"
 msgstr "스트림 삭제: %{stream_name}"
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:171
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:185
 msgid "Deleted successfully"
 msgstr "성공적으로 삭제됨"
 
@@ -428,7 +437,7 @@ msgstr "성공적으로 배포됨"
 msgid "Description"
 msgstr "설명"
 
-#: src/views/notification/Notification.vue:25
+#: src/views/notification/Notification.vue:26
 msgid "Details"
 msgstr "세부 사항"
 
@@ -473,7 +482,7 @@ msgstr "성공적으로 비활성화됨"
 msgid "Disk IO"
 msgstr "디스크 IO"
 
-#: src/routes/index.ts:162 src/views/certificate/DNSCredential.vue:36
+#: src/routes/index.ts:162 src/views/certificate/DNSCredential.vue:40
 msgid "DNS Credentials"
 msgstr "DNS 인증 정보"
 
@@ -533,16 +542,16 @@ msgstr "도메인"
 msgid "Domain Config Created Successfully"
 msgstr "도메인 구성이 성공적으로 생성되었습니다"
 
-#: src/views/certificate/CertificateEditor.vue:111
+#: src/views/certificate/CertificateEditor.vue:112
 msgid "Domains list is empty, try to reopen Auto Cert for %{config}"
 msgstr ""
 "도메인 목록이 비어 있습니다. %{config}에 대한 자동 인증서를 다시 열어보세요"
 
-#: src/language/constants.ts:25
+#: src/language/constants.ts:26
 msgid "Download latest release error"
 msgstr "최신 릴리스 다운로드 오류"
 
-#: src/language/constants.ts:24
+#: src/language/constants.ts:25
 msgid "Downloading latest release"
 msgstr "최신 릴리스 다운로드 중"
 
@@ -664,6 +673,11 @@ msgstr "Let's Encrypt로 웹사이트 암호화"
 msgid "Environment"
 msgstr "환경"
 
+#: src/language/constants.ts:21
+#, fuzzy
+msgid "Environment variables cleaned"
+msgstr "환경 변수 설정"
+
 #: src/views/dashboard/Environments.vue:82
 msgid "Environments"
 msgstr "환경"
@@ -680,7 +694,7 @@ msgstr "오류 로그"
 msgid "Executable Path"
 msgstr "실행 가능 경로"
 
-#: src/views/certificate/Certificate.vue:96
+#: src/views/certificate/Certificate.vue:101
 msgid "Expired"
 msgstr "만료됨"
 
@@ -724,7 +738,7 @@ msgstr "저장 실패, 구성에서 구문 오류가 감지되었습니다."
 msgid "File"
 msgstr "파일"
 
-#: src/language/constants.ts:30
+#: src/language/constants.ts:31
 msgid "File exists"
 msgstr "파일이 존재함"
 
@@ -764,7 +778,7 @@ msgstr "형식 오류 %{msg}"
 msgid "Format successfully"
 msgstr "성공적으로 형식 지정됨"
 
-#: src/views/certificate/Certificate.vue:43
+#: src/views/certificate/Certificate.vue:44
 #, fuzzy
 msgid "General Certificate"
 msgstr "일반 인증서"
@@ -814,11 +828,11 @@ msgstr "HTTP01"
 msgid "If left blank, the default CA Dir will be used."
 msgstr ""
 
-#: src/views/certificate/Certificate.vue:131
+#: src/views/certificate/Certificate.vue:136
 msgid "Import"
 msgstr "가져오기"
 
-#: src/routes/index.ts:153 src/views/certificate/CertificateEditor.vue:84
+#: src/routes/index.ts:153 src/views/certificate/CertificateEditor.vue:85
 #, fuzzy
 msgid "Import Certificate"
 msgstr "인증서 상태"
@@ -827,11 +841,11 @@ msgstr "인증서 상태"
 msgid "Info"
 msgstr "정보"
 
-#: src/language/constants.ts:23
+#: src/language/constants.ts:24
 msgid "Initial core upgrader error"
 msgstr "초기 코어 업그레이더 오류"
 
-#: src/language/constants.ts:22
+#: src/language/constants.ts:23
 msgid "Initialing core upgrader"
 msgstr "코어 업그레이더 초기화"
 
@@ -857,7 +871,7 @@ msgstr "간격"
 msgid "Invalid"
 msgstr "유효함"
 
-#: src/views/certificate/Certificate.vue:139
+#: src/views/certificate/Certificate.vue:144
 #, fuzzy
 msgid "Issue wildcard certificate"
 msgstr "인증서 유효"
@@ -876,7 +890,7 @@ msgstr "성공적으로 활성화됨"
 msgid "Jwt Secret"
 msgstr "Jwt 토큰"
 
-#: src/views/certificate/Certificate.vue:61
+#: src/views/certificate/Certificate.vue:66
 #: src/views/certificate/WildcardCertificate.vue:79
 #: src/views/domain/cert/components/AutoCertStepOne.vue:80
 msgid "Key Type"
@@ -894,8 +908,8 @@ msgstr "변경사항이 없으면 비워두세요"
 msgid "Leave blank for the default: https://api.openai.com/"
 msgstr "기본값을 사용하려면 비워 두세요: https://api.openai.com/"
 
-#: src/views/certificate/CertificateEditor.vue:200
-#: src/views/certificate/CertificateEditor.vue:213
+#: src/views/certificate/CertificateEditor.vue:207
+#: src/views/certificate/CertificateEditor.vue:220
 #, fuzzy
 msgid "Leave blank will not change anything"
 msgstr "변경사항이 없으면 비워두세요"
@@ -942,7 +956,7 @@ msgstr "위치"
 msgid "Locations"
 msgstr "위치들"
 
-#: src/views/certificate/CertificateEditor.vue:223
+#: src/views/certificate/CertificateEditor.vue:230
 #, fuzzy
 msgid "Log"
 msgstr "로그인"
@@ -1004,7 +1018,7 @@ msgstr "스트림 관리"
 msgid "Manage Users"
 msgstr "사용자 관리"
 
-#: src/views/certificate/Certificate.vue:42
+#: src/views/certificate/Certificate.vue:43
 #, fuzzy
 msgid "Managed Certificate"
 msgstr "인증서 유효"
@@ -1029,12 +1043,12 @@ msgstr "실행 모드"
 
 #: src/components/ChatGPT/ChatGPT.vue:249
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:194
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:491
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:505
 #, fuzzy
 msgid "Modify"
 msgstr "설정 수정"
 
-#: src/routes/index.ts:144 src/views/certificate/CertificateEditor.vue:84
+#: src/routes/index.ts:144 src/views/certificate/CertificateEditor.vue:85
 #, fuzzy
 msgid "Modify Certificate"
 msgstr "인증서 상태"
@@ -1055,8 +1069,8 @@ msgstr "단일 지시문"
 
 #: src/views/certificate/ACMEUser.vue:13
 #: src/views/certificate/Certificate.vue:20
-#: src/views/certificate/CertificateEditor.vue:146
-#: src/views/certificate/DNSCredential.vue:10 src/views/config/config.ts:7
+#: src/views/certificate/CertificateEditor.vue:147
+#: src/views/certificate/DNSCredential.vue:11 src/views/config/config.ts:7
 #: src/views/domain/cert/ChangeCert.vue:17
 #: src/views/domain/components/RightSettings.vue:83
 #: src/views/domain/components/SiteDuplicate.vue:133
@@ -1132,12 +1146,13 @@ msgstr "Nginx가 성공적으로 재시작됨"
 
 #: src/components/ChatGPT/ChatGPT.vue:270
 #: src/components/Notification/Notification.vue:82
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:507
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:521
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:535
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:549
 #: src/views/domain/DomainList.vue:144
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:90
 #: src/views/domain/ngx_conf/LocationEditor.vue:71
-#: src/views/notification/Notification.vue:68
+#: src/views/notification/Notification.vue:70
 #: src/views/preference/BasicSettings.vue:101
 #: src/views/stream/StreamList.vue:165
 msgid "No"
@@ -1147,7 +1162,7 @@ msgstr "아니요"
 msgid "Node Secret"
 msgstr "노드 시크릿"
 
-#: src/views/certificate/Certificate.vue:102
+#: src/views/certificate/Certificate.vue:107
 msgid "Not After"
 msgstr "만료일"
 
@@ -1159,12 +1174,12 @@ msgstr "찾을 수 없음"
 msgid "Not Valid Before: %{date}"
 msgstr "유효 시작일: %{date}"
 
-#: src/views/certificate/DNSCredential.vue:45
+#: src/views/certificate/DNSCredential.vue:49
 #: src/views/domain/cert/components/AutoCertStepOne.vue:45
 msgid "Note"
 msgstr "참고"
 
-#: src/views/notification/Notification.vue:60
+#: src/views/notification/Notification.vue:62
 #, fuzzy
 msgid "Notification"
 msgstr "알림"
@@ -1197,8 +1212,9 @@ msgstr ""
 #: src/components/ChatGPT/ChatGPT.vue:271
 #: src/components/Notification/Notification.vue:83
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:56
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:508
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:522
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:536
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:550
 #: src/views/domain/cert/components/ObtainCert.vue:136
 #: src/views/domain/components/Deploy.vue:20
 #: src/views/domain/components/RightSettings.vue:50
@@ -1206,14 +1222,14 @@ msgstr ""
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:49
 #: src/views/domain/ngx_conf/NgxServer.vue:83
 #: src/views/domain/ngx_conf/NgxUpstream.vue:32
-#: src/views/notification/Notification.vue:69
+#: src/views/notification/Notification.vue:71
 #: src/views/stream/components/Deploy.vue:20
 #: src/views/stream/components/RightSettings.vue:50
 #: src/views/stream/StreamList.vue:166
 msgid "OK"
 msgstr "확인"
 
-#: src/views/certificate/DNSCredential.vue:55
+#: src/views/certificate/DNSCredential.vue:59
 msgid "Once the verification is complete, the records will be removed."
 msgstr "검증이 완료되면, 레코드는 제거됩니다."
 
@@ -1265,15 +1281,15 @@ msgstr "비밀번호 (*)"
 msgid "Path"
 msgstr "경로"
 
-#: src/language/constants.ts:27
+#: src/language/constants.ts:28
 msgid "Perform core upgrade error"
 msgstr "핵심 업그레이드 오류 수행"
 
-#: src/language/constants.ts:26
+#: src/language/constants.ts:27
 msgid "Performing core upgrade"
 msgstr "핵심 업그레이드 수행 중"
 
-#: src/views/certificate/DNSCredential.vue:49
+#: src/views/certificate/DNSCredential.vue:53
 msgid ""
 "Please fill in the API authentication credentials provided by your DNS "
 "provider."
@@ -1306,7 +1322,7 @@ msgstr "비밀번호를 입력해주세요!"
 msgid "Please input your username!"
 msgstr "사용자 이름을 입력해주세요!"
 
-#: src/views/certificate/DNSCredential.vue:58
+#: src/views/certificate/DNSCredential.vue:62
 msgid ""
 "Please note that the unit of time configurations below are all in seconds."
 msgstr "아래의 시간 설정 단위는 모두 초 단위임을 유의해주세요."
@@ -1341,7 +1357,7 @@ msgstr "기본 사용자 삭제 금지"
 msgid "Project Team"
 msgstr "프로젝트 팀"
 
-#: src/views/certificate/DNSCredential.vue:18
+#: src/views/certificate/DNSCredential.vue:19
 msgid "Provider"
 msgstr "제공자"
 
@@ -1355,11 +1371,11 @@ msgstr "읽기"
 msgid "Receive"
 msgstr "수신"
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:530
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:544
 msgid "Recover"
 msgstr ""
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:179
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:193
 #, fuzzy
 msgid "Recovered Successfully"
 msgstr "성공적으로 제거됨"
@@ -1434,12 +1450,12 @@ msgstr "이름 변경"
 msgid "Renew Certificate"
 msgstr "인증서 갱신"
 
-#: src/language/constants.ts:34
+#: src/language/constants.ts:35
 #, fuzzy
 msgid "Renew Certificate Error"
 msgstr "인증서 갱신 오류"
 
-#: src/language/constants.ts:33
+#: src/language/constants.ts:34
 #, fuzzy
 msgid "Renew Certificate Success"
 msgstr "인증서 갱신 성공"
@@ -1450,11 +1466,11 @@ msgstr "인증서 갱신 성공"
 msgid "Renew successfully"
 msgstr "성공적으로 갱신됨"
 
-#: src/language/constants.ts:31
+#: src/language/constants.ts:32
 msgid "Requested with wrong parameters"
 msgstr "잘못된 매개변수로 요청됨"
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:439
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:453
 msgid "Reset"
 msgstr "재설정"
 
@@ -1476,7 +1492,7 @@ msgid "Running"
 msgstr "실행 중"
 
 #: src/components/ChatGPT/ChatGPT.vue:252
-#: src/views/certificate/CertificateEditor.vue:242
+#: src/views/certificate/CertificateEditor.vue:249
 #: src/views/config/ConfigEdit.vue:96 src/views/domain/DomainEdit.vue:263
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:120
 #: src/views/preference/Preference.vue:130 src/views/stream/StreamEdit.vue:254
@@ -1494,7 +1510,7 @@ msgstr "저장 오류 %{msg}"
 
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:39
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:104
-#: src/views/certificate/CertificateEditor.vue:45
+#: src/views/certificate/CertificateEditor.vue:46
 #: src/views/preference/Preference.vue:66
 #, fuzzy
 msgid "Save successfully"
@@ -1525,8 +1541,8 @@ msgstr "보내기"
 #: src/components/StdDesign/StdDataDisplay/methods/exportCsv.ts:46
 #: src/components/StdDesign/StdDataDisplay/methods/sortable.ts:126
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:42
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:182
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:221
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:196
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:235
 #: src/views/config/ConfigEdit.vue:40 src/views/domain/DomainList.vue:81
 #: src/views/environment/Environment.vue:139 src/views/other/Install.vue:70
 #: src/views/preference/Preference.vue:70 src/views/stream/StreamList.vue:113
@@ -1583,23 +1599,23 @@ msgstr "사이트 로그"
 msgid "Sites List"
 msgstr "사이트 목록"
 
-#: src/views/certificate/CertificateEditor.vue:191
+#: src/views/certificate/CertificateEditor.vue:198
 #, fuzzy
 msgid "SSL Certificate Content"
 msgstr "인증서 상태"
 
-#: src/views/certificate/CertificateEditor.vue:204
+#: src/views/certificate/CertificateEditor.vue:211
 msgid "SSL Certificate Key Content"
 msgstr "SSL 인증서키 콘텐츠"
 
-#: src/views/certificate/Certificate.vue:75
-#: src/views/certificate/CertificateEditor.vue:176
+#: src/views/certificate/Certificate.vue:80
+#: src/views/certificate/CertificateEditor.vue:177
 #, fuzzy
 msgid "SSL Certificate Key Path"
 msgstr "SSL 인증서 키 경로"
 
-#: src/views/certificate/Certificate.vue:67
-#: src/views/certificate/CertificateEditor.vue:161
+#: src/views/certificate/Certificate.vue:72
+#: src/views/certificate/CertificateEditor.vue:162
 #, fuzzy
 msgid "SSL Certificate Path"
 msgstr "SSL 인증서 경로"
@@ -1615,7 +1631,7 @@ msgid "Stable"
 msgstr "활성화됨"
 
 #: src/views/certificate/ACMEUser.vue:42
-#: src/views/certificate/Certificate.vue:83 src/views/domain/DomainList.vue:22
+#: src/views/certificate/Certificate.vue:88 src/views/domain/DomainList.vue:22
 #: src/views/environment/Environment.vue:76 src/views/stream/StreamList.vue:22
 msgid "Status"
 msgstr "상태"
@@ -1651,6 +1667,42 @@ msgstr "다크 테마로 변경"
 msgid "Switch to light theme"
 msgstr "라이트 테마로 변경"
 
+#: src/views/certificate/Certificate.vue:42
+#, fuzzy
+msgid "Sync Certificate"
+msgstr "인증서 갱신"
+
+#: src/components/Notification/detailRender.ts:25
+#, fuzzy
+msgid ""
+"Sync Certificate %{cert_name} to %{env_name} failed, please upgrade the "
+"remote Nginx UI to the latest version"
+msgstr "%{conf_name}을(를) %{node_name}(으)로 성공적으로 복제함"
+
+#: src/components/Notification/detailRender.ts:29
+#, fuzzy
+msgid "Sync Certificate %{cert_name} to %{env_name} failed, response: %{resp}"
+msgstr "%{conf_name}을(를) %{node_name}(으)로 성공적으로 복제함"
+
+#: src/components/Notification/detailRender.ts:17
+#, fuzzy
+msgid "Sync Certificate %{cert_name} to %{env_name} successfully"
+msgstr "%{conf_name}을(를) %{node_name}(으)로 성공적으로 복제함"
+
+#: src/language/constants.ts:38
+#, fuzzy
+msgid "Sync Certificate Error"
+msgstr "인증서 갱신 오류"
+
+#: src/language/constants.ts:37
+#, fuzzy
+msgid "Sync Certificate Success"
+msgstr "인증서 갱신 성공"
+
+#: src/views/certificate/CertificateEditor.vue:191
+msgid "Sync to"
+msgstr ""
+
 #: src/routes/index.ts:248
 msgid "System"
 msgstr "시스템"
@@ -1681,11 +1733,11 @@ msgstr ""
 msgid "The filename cannot contain the following characters: %{c}"
 msgstr "파일 이름은 다음 문자를 포함할 수 없습니다: %{c}"
 
-#: src/views/certificate/CertificateEditor.vue:194
+#: src/views/certificate/CertificateEditor.vue:201
 msgid "The input is not a SSL Certificate"
 msgstr "입력이 SSL 인증서가 아닙니다"
 
-#: src/views/certificate/CertificateEditor.vue:207
+#: src/views/certificate/CertificateEditor.vue:214
 #, fuzzy
 msgid "The input is not a SSL Certificate Key"
 msgstr "Certificate Status"
@@ -1696,12 +1748,12 @@ msgid ""
 "dashes, and dots."
 msgstr ""
 
-#: src/views/certificate/CertificateEditor.vue:165
+#: src/views/certificate/CertificateEditor.vue:166
 #, fuzzy
 msgid "The path exists, but the file is not a certificate"
 msgstr "Certificate Status"
 
-#: src/views/certificate/CertificateEditor.vue:180
+#: src/views/certificate/CertificateEditor.vue:181
 msgid "The path exists, but the file is not a private key"
 msgstr "경로는 존재하지만 파일은 개인 키가 아닙니다"
 
@@ -1736,17 +1788,17 @@ msgstr "유효한 URL이 아닙니다"
 msgid "The username or password is incorrect"
 msgstr "사용자 이름 또는 비밀번호가 올바르지 않습니다"
 
-#: src/views/certificate/CertificateEditor.vue:101
+#: src/views/certificate/CertificateEditor.vue:102
 msgid "This Auto Cert item is invalid, please remove it."
 msgstr "이 자동 인증 항목이 유효하지 않습니다. 제거해주세요."
 
-#: src/views/certificate/CertificateEditor.vue:91
+#: src/views/certificate/CertificateEditor.vue:92
 msgid "This certificate is managed by Nginx UI"
 msgstr "이 인증서는 Nginx UI에서 관리됩니다"
 
-#: src/views/certificate/CertificateEditor.vue:149
-#: src/views/certificate/CertificateEditor.vue:163
-#: src/views/certificate/CertificateEditor.vue:178
+#: src/views/certificate/CertificateEditor.vue:150
+#: src/views/certificate/CertificateEditor.vue:164
+#: src/views/certificate/CertificateEditor.vue:179
 msgid "This field is required"
 msgstr "이 필드는 필수입니다"
 
@@ -1758,7 +1810,7 @@ msgstr "이 필드는 비워둘 수 없습니다"
 msgid "Tips"
 msgstr "팁"
 
-#: src/views/notification/Notification.vue:18
+#: src/views/notification/Notification.vue:19
 msgid "Title"
 msgstr "제목"
 
@@ -1782,12 +1834,12 @@ msgid "Trash"
 msgstr ""
 
 #: src/views/certificate/Certificate.vue:37 src/views/config/config.ts:12
-#: src/views/notification/Notification.vue:12
+#: src/views/notification/Notification.vue:13
 msgid "Type"
 msgstr "유형"
 
 #: src/views/certificate/ACMEUser.vue:53
-#: src/views/certificate/DNSCredential.vue:23 src/views/config/config.ts:27
+#: src/views/certificate/DNSCredential.vue:27 src/views/config/config.ts:27
 #: src/views/config/ConfigEdit.vue:121
 #: src/views/domain/components/RightSettings.vue:86
 #: src/views/domain/DomainList.vue:41 src/views/environment/Environment.vue:122
@@ -1806,7 +1858,7 @@ msgstr "성공적으로 저장되었습니다"
 msgid "Upgrade"
 msgstr "업그레이드"
 
-#: src/language/constants.ts:28
+#: src/language/constants.ts:29
 #, fuzzy
 msgid "Upgraded successfully"
 msgstr "성공적으로 저장되었습니다"
@@ -1841,11 +1893,11 @@ msgid "Username (*)"
 msgstr "사용자 이름 (*)"
 
 #: src/views/certificate/ACMEUser.vue:87
-#: src/views/certificate/Certificate.vue:92
+#: src/views/certificate/Certificate.vue:97
 msgid "Valid"
 msgstr "유효함"
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:477
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:491
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:103
 msgid "View"
 msgstr "보기"
@@ -1871,7 +1923,7 @@ msgstr "기본 모드"
 msgid "Warning"
 msgstr "경고"
 
-#: src/views/certificate/DNSCredential.vue:52
+#: src/views/certificate/DNSCredential.vue:56
 msgid ""
 "We will add one or more TXT records to the DNS records of your domain for "
 "ownership verification."

+ 125 - 83
app/src/language/messages.pot

@@ -18,12 +18,12 @@ msgid "ACME User"
 msgstr ""
 
 #: src/views/certificate/ACMEUser.vue:59
-#: src/views/certificate/Certificate.vue:108
-#: src/views/certificate/DNSCredential.vue:29
+#: src/views/certificate/Certificate.vue:113
+#: src/views/certificate/DNSCredential.vue:33
 #: src/views/config/config.ts:34
 #: src/views/domain/DomainList.vue:47
 #: src/views/environment/Environment.vue:129
-#: src/views/notification/Notification.vue:35
+#: src/views/notification/Notification.vue:37
 #: src/views/stream/StreamList.vue:47
 #: src/views/user/User.vue:43
 msgid "Action"
@@ -91,7 +91,7 @@ msgid "Arch"
 msgstr ""
 
 #: src/components/Notification/Notification.vue:84
-#: src/views/notification/Notification.vue:70
+#: src/views/notification/Notification.vue:72
 msgid "Are you sure you want to clear all notifications?"
 msgstr ""
 
@@ -99,7 +99,11 @@ msgstr ""
 msgid "Are you sure you want to clear the record of chat?"
 msgstr ""
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:509
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:551
+msgid "Are you sure you want to delete this item permanently?"
+msgstr ""
+
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:523
 msgid "Are you sure you want to delete this item?"
 msgstr ""
 
@@ -108,7 +112,7 @@ msgstr ""
 msgid "Are you sure you want to delete?"
 msgstr ""
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:523
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:537
 msgid "Are you sure you want to recover this item?"
 msgstr ""
 
@@ -153,7 +157,7 @@ msgstr ""
 msgid "Auto-renewal enabled for %{name}"
 msgstr ""
 
-#: src/views/certificate/CertificateEditor.vue:235
+#: src/views/certificate/CertificateEditor.vue:242
 #: src/views/config/Config.vue:73
 #: src/views/config/ConfigEdit.vue:87
 #: src/views/domain/DomainEdit.vue:256
@@ -183,7 +187,7 @@ msgid "Basic Mode"
 msgstr ""
 
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:54
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:445
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:459
 msgid "Batch Modify"
 msgstr ""
 
@@ -226,13 +230,13 @@ msgstr ""
 msgid "Certificate Renewal Interval"
 msgstr ""
 
-#: src/views/certificate/CertificateEditor.vue:127
+#: src/views/certificate/CertificateEditor.vue:128
 #: src/views/domain/cert/Cert.vue:33
 msgid "Certificate Status"
 msgstr ""
 
 #: src/routes/index.ts:119
-#: src/views/certificate/Certificate.vue:124
+#: src/views/certificate/Certificate.vue:129
 msgid "Certificates"
 msgstr ""
 
@@ -263,12 +267,12 @@ msgstr ""
 
 #: src/components/ChatGPT/ChatGPT.vue:276
 #: src/components/Notification/Notification.vue:89
-#: src/views/notification/Notification.vue:75
+#: src/views/notification/Notification.vue:77
 msgid "Clear"
 msgstr ""
 
 #: src/components/Notification/Notification.vue:40
-#: src/views/notification/Notification.vue:44
+#: src/views/notification/Notification.vue:46
 msgid "Cleared successfully"
 msgstr ""
 
@@ -333,7 +337,7 @@ msgstr ""
 msgid "Create Another"
 msgstr ""
 
-#: src/views/notification/Notification.vue:29
+#: src/views/notification/Notification.vue:31
 #: src/views/user/User.vue:31
 msgid "Created at"
 msgstr ""
@@ -375,7 +379,7 @@ msgstr ""
 msgid "Days"
 msgstr ""
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:516
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:530
 #: src/views/domain/DomainList.vue:155
 #: src/views/domain/ngx_conf/NgxServer.vue:114
 #: src/views/domain/ngx_conf/NgxUpstream.vue:126
@@ -383,6 +387,10 @@ msgstr ""
 msgid "Delete"
 msgstr ""
 
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:558
+msgid "Delete Permanently"
+msgstr ""
+
 #: src/views/domain/DomainList.vue:78
 msgid "Delete site: %{site_name}"
 msgstr ""
@@ -391,7 +399,7 @@ msgstr ""
 msgid "Delete stream: %{stream_name}"
 msgstr ""
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:171
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:185
 msgid "Deleted successfully"
 msgstr ""
 
@@ -422,7 +430,7 @@ msgstr ""
 msgid "Description"
 msgstr ""
 
-#: src/views/notification/Notification.vue:25
+#: src/views/notification/Notification.vue:26
 msgid "Details"
 msgstr ""
 
@@ -472,7 +480,7 @@ msgid "Disk IO"
 msgstr ""
 
 #: src/routes/index.ts:162
-#: src/views/certificate/DNSCredential.vue:36
+#: src/views/certificate/DNSCredential.vue:40
 msgid "DNS Credentials"
 msgstr ""
 
@@ -532,15 +540,15 @@ msgstr ""
 msgid "Domain Config Created Successfully"
 msgstr ""
 
-#: src/views/certificate/CertificateEditor.vue:111
+#: src/views/certificate/CertificateEditor.vue:112
 msgid "Domains list is empty, try to reopen Auto Cert for %{config}"
 msgstr ""
 
-#: src/language/constants.ts:25
+#: src/language/constants.ts:26
 msgid "Download latest release error"
 msgstr ""
 
-#: src/language/constants.ts:24
+#: src/language/constants.ts:25
 msgid "Downloading latest release"
 msgstr ""
 
@@ -666,6 +674,10 @@ msgstr ""
 msgid "Environment"
 msgstr ""
 
+#: src/language/constants.ts:21
+msgid "Environment variables cleaned"
+msgstr ""
+
 #: src/views/dashboard/Environments.vue:82
 msgid "Environments"
 msgstr ""
@@ -684,7 +696,7 @@ msgstr ""
 msgid "Executable Path"
 msgstr ""
 
-#: src/views/certificate/Certificate.vue:96
+#: src/views/certificate/Certificate.vue:101
 msgid "Expired"
 msgstr ""
 
@@ -727,7 +739,7 @@ msgstr ""
 msgid "File"
 msgstr ""
 
-#: src/language/constants.ts:30
+#: src/language/constants.ts:31
 msgid "File exists"
 msgstr ""
 
@@ -765,7 +777,7 @@ msgstr ""
 msgid "Format successfully"
 msgstr ""
 
-#: src/views/certificate/Certificate.vue:43
+#: src/views/certificate/Certificate.vue:44
 msgid "General Certificate"
 msgstr ""
 
@@ -813,12 +825,12 @@ msgstr ""
 msgid "If left blank, the default CA Dir will be used."
 msgstr ""
 
-#: src/views/certificate/Certificate.vue:131
+#: src/views/certificate/Certificate.vue:136
 msgid "Import"
 msgstr ""
 
 #: src/routes/index.ts:153
-#: src/views/certificate/CertificateEditor.vue:84
+#: src/views/certificate/CertificateEditor.vue:85
 msgid "Import Certificate"
 msgstr ""
 
@@ -826,11 +838,11 @@ msgstr ""
 msgid "Info"
 msgstr ""
 
-#: src/language/constants.ts:23
+#: src/language/constants.ts:24
 msgid "Initial core upgrader error"
 msgstr ""
 
-#: src/language/constants.ts:22
+#: src/language/constants.ts:23
 msgid "Initialing core upgrader"
 msgstr ""
 
@@ -855,7 +867,7 @@ msgstr ""
 msgid "Invalid"
 msgstr ""
 
-#: src/views/certificate/Certificate.vue:139
+#: src/views/certificate/Certificate.vue:144
 msgid "Issue wildcard certificate"
 msgstr ""
 
@@ -871,7 +883,7 @@ msgstr ""
 msgid "Jwt Secret"
 msgstr ""
 
-#: src/views/certificate/Certificate.vue:61
+#: src/views/certificate/Certificate.vue:66
 #: src/views/certificate/WildcardCertificate.vue:79
 #: src/views/domain/cert/components/AutoCertStepOne.vue:80
 msgid "Key Type"
@@ -889,8 +901,8 @@ msgstr ""
 msgid "Leave blank for the default: https://api.openai.com/"
 msgstr ""
 
-#: src/views/certificate/CertificateEditor.vue:200
-#: src/views/certificate/CertificateEditor.vue:213
+#: src/views/certificate/CertificateEditor.vue:207
+#: src/views/certificate/CertificateEditor.vue:220
 msgid "Leave blank will not change anything"
 msgstr ""
 
@@ -932,7 +944,7 @@ msgstr ""
 msgid "Locations"
 msgstr ""
 
-#: src/views/certificate/CertificateEditor.vue:223
+#: src/views/certificate/CertificateEditor.vue:230
 msgid "Log"
 msgstr ""
 
@@ -981,7 +993,7 @@ msgstr ""
 msgid "Manage Users"
 msgstr ""
 
-#: src/views/certificate/Certificate.vue:42
+#: src/views/certificate/Certificate.vue:43
 msgid "Managed Certificate"
 msgstr ""
 
@@ -1004,12 +1016,12 @@ msgstr ""
 
 #: src/components/ChatGPT/ChatGPT.vue:249
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:194
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:491
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:505
 msgid "Modify"
 msgstr ""
 
 #: src/routes/index.ts:144
-#: src/views/certificate/CertificateEditor.vue:84
+#: src/views/certificate/CertificateEditor.vue:85
 msgid "Modify Certificate"
 msgstr ""
 
@@ -1027,8 +1039,8 @@ msgstr ""
 
 #: src/views/certificate/ACMEUser.vue:13
 #: src/views/certificate/Certificate.vue:20
-#: src/views/certificate/CertificateEditor.vue:146
-#: src/views/certificate/DNSCredential.vue:10
+#: src/views/certificate/CertificateEditor.vue:147
+#: src/views/certificate/DNSCredential.vue:11
 #: src/views/config/config.ts:7
 #: src/views/domain/cert/ChangeCert.vue:17
 #: src/views/domain/components/RightSettings.vue:83
@@ -1105,12 +1117,13 @@ msgstr ""
 
 #: src/components/ChatGPT/ChatGPT.vue:270
 #: src/components/Notification/Notification.vue:82
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:507
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:521
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:535
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:549
 #: src/views/domain/DomainList.vue:144
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:90
 #: src/views/domain/ngx_conf/LocationEditor.vue:71
-#: src/views/notification/Notification.vue:68
+#: src/views/notification/Notification.vue:70
 #: src/views/preference/BasicSettings.vue:101
 #: src/views/stream/StreamList.vue:165
 msgid "No"
@@ -1120,7 +1133,7 @@ msgstr ""
 msgid "Node Secret"
 msgstr ""
 
-#: src/views/certificate/Certificate.vue:102
+#: src/views/certificate/Certificate.vue:107
 msgid "Not After"
 msgstr ""
 
@@ -1132,12 +1145,12 @@ msgstr ""
 msgid "Not Valid Before: %{date}"
 msgstr ""
 
-#: src/views/certificate/DNSCredential.vue:45
+#: src/views/certificate/DNSCredential.vue:49
 #: src/views/domain/cert/components/AutoCertStepOne.vue:45
 msgid "Note"
 msgstr ""
 
-#: src/views/notification/Notification.vue:60
+#: src/views/notification/Notification.vue:62
 msgid "Notification"
 msgstr ""
 
@@ -1168,8 +1181,9 @@ msgstr ""
 #: src/components/ChatGPT/ChatGPT.vue:271
 #: src/components/Notification/Notification.vue:83
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:56
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:508
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:522
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:536
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:550
 #: src/views/domain/cert/components/ObtainCert.vue:136
 #: src/views/domain/components/Deploy.vue:20
 #: src/views/domain/components/RightSettings.vue:50
@@ -1177,14 +1191,14 @@ msgstr ""
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:49
 #: src/views/domain/ngx_conf/NgxServer.vue:83
 #: src/views/domain/ngx_conf/NgxUpstream.vue:32
-#: src/views/notification/Notification.vue:69
+#: src/views/notification/Notification.vue:71
 #: src/views/stream/components/Deploy.vue:20
 #: src/views/stream/components/RightSettings.vue:50
 #: src/views/stream/StreamList.vue:166
 msgid "OK"
 msgstr ""
 
-#: src/views/certificate/DNSCredential.vue:55
+#: src/views/certificate/DNSCredential.vue:59
 msgid "Once the verification is complete, the records will be removed."
 msgstr ""
 
@@ -1236,15 +1250,15 @@ msgstr ""
 msgid "Path"
 msgstr ""
 
-#: src/language/constants.ts:27
+#: src/language/constants.ts:28
 msgid "Perform core upgrade error"
 msgstr ""
 
-#: src/language/constants.ts:26
+#: src/language/constants.ts:27
 msgid "Performing core upgrade"
 msgstr ""
 
-#: src/views/certificate/DNSCredential.vue:49
+#: src/views/certificate/DNSCredential.vue:53
 msgid "Please fill in the API authentication credentials provided by your DNS provider."
 msgstr ""
 
@@ -1271,7 +1285,7 @@ msgstr ""
 msgid "Please input your username!"
 msgstr ""
 
-#: src/views/certificate/DNSCredential.vue:58
+#: src/views/certificate/DNSCredential.vue:62
 msgid "Please note that the unit of time configurations below are all in seconds."
 msgstr ""
 
@@ -1306,7 +1320,7 @@ msgstr ""
 msgid "Project Team"
 msgstr ""
 
-#: src/views/certificate/DNSCredential.vue:18
+#: src/views/certificate/DNSCredential.vue:19
 msgid "Provider"
 msgstr ""
 
@@ -1320,11 +1334,11 @@ msgstr ""
 msgid "Receive"
 msgstr ""
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:530
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:544
 msgid "Recover"
 msgstr ""
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:179
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:193
 msgid "Recovered Successfully"
 msgstr ""
 
@@ -1390,11 +1404,11 @@ msgstr ""
 msgid "Renew Certificate"
 msgstr ""
 
-#: src/language/constants.ts:34
+#: src/language/constants.ts:35
 msgid "Renew Certificate Error"
 msgstr ""
 
-#: src/language/constants.ts:33
+#: src/language/constants.ts:34
 msgid "Renew Certificate Success"
 msgstr ""
 
@@ -1403,11 +1417,11 @@ msgstr ""
 msgid "Renew successfully"
 msgstr ""
 
-#: src/language/constants.ts:31
+#: src/language/constants.ts:32
 msgid "Requested with wrong parameters"
 msgstr ""
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:439
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:453
 msgid "Reset"
 msgstr ""
 
@@ -1428,7 +1442,7 @@ msgid "Running"
 msgstr ""
 
 #: src/components/ChatGPT/ChatGPT.vue:252
-#: src/views/certificate/CertificateEditor.vue:242
+#: src/views/certificate/CertificateEditor.vue:249
 #: src/views/config/ConfigEdit.vue:96
 #: src/views/domain/DomainEdit.vue:263
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:120
@@ -1449,7 +1463,7 @@ msgstr ""
 
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:39
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:104
-#: src/views/certificate/CertificateEditor.vue:45
+#: src/views/certificate/CertificateEditor.vue:46
 #: src/views/preference/Preference.vue:66
 msgid "Save successfully"
 msgstr ""
@@ -1480,8 +1494,8 @@ msgstr ""
 #: src/components/StdDesign/StdDataDisplay/methods/exportCsv.ts:46
 #: src/components/StdDesign/StdDataDisplay/methods/sortable.ts:126
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:42
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:182
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:221
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:196
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:235
 #: src/views/config/ConfigEdit.vue:40
 #: src/views/domain/DomainList.vue:81
 #: src/views/environment/Environment.vue:139
@@ -1538,21 +1552,21 @@ msgstr ""
 msgid "Sites List"
 msgstr ""
 
-#: src/views/certificate/CertificateEditor.vue:191
+#: src/views/certificate/CertificateEditor.vue:198
 msgid "SSL Certificate Content"
 msgstr ""
 
-#: src/views/certificate/CertificateEditor.vue:204
+#: src/views/certificate/CertificateEditor.vue:211
 msgid "SSL Certificate Key Content"
 msgstr ""
 
-#: src/views/certificate/Certificate.vue:75
-#: src/views/certificate/CertificateEditor.vue:176
+#: src/views/certificate/Certificate.vue:80
+#: src/views/certificate/CertificateEditor.vue:177
 msgid "SSL Certificate Key Path"
 msgstr ""
 
-#: src/views/certificate/Certificate.vue:67
-#: src/views/certificate/CertificateEditor.vue:161
+#: src/views/certificate/Certificate.vue:72
+#: src/views/certificate/CertificateEditor.vue:162
 msgid "SSL Certificate Path"
 msgstr ""
 
@@ -1566,7 +1580,7 @@ msgid "Stable"
 msgstr ""
 
 #: src/views/certificate/ACMEUser.vue:42
-#: src/views/certificate/Certificate.vue:83
+#: src/views/certificate/Certificate.vue:88
 #: src/views/domain/DomainList.vue:22
 #: src/views/environment/Environment.vue:76
 #: src/views/stream/StreamList.vue:22
@@ -1603,6 +1617,34 @@ msgstr ""
 msgid "Switch to light theme"
 msgstr ""
 
+#: src/views/certificate/Certificate.vue:42
+msgid "Sync Certificate"
+msgstr ""
+
+#: src/components/Notification/detailRender.ts:25
+msgid "Sync Certificate %{cert_name} to %{env_name} failed, please upgrade the remote Nginx UI to the latest version"
+msgstr ""
+
+#: src/components/Notification/detailRender.ts:29
+msgid "Sync Certificate %{cert_name} to %{env_name} failed, response: %{resp}"
+msgstr ""
+
+#: src/components/Notification/detailRender.ts:17
+msgid "Sync Certificate %{cert_name} to %{env_name} successfully"
+msgstr ""
+
+#: src/language/constants.ts:38
+msgid "Sync Certificate Error"
+msgstr ""
+
+#: src/language/constants.ts:37
+msgid "Sync Certificate Success"
+msgstr ""
+
+#: src/views/certificate/CertificateEditor.vue:191
+msgid "Sync to"
+msgstr ""
+
 #: src/routes/index.ts:248
 msgid "System"
 msgstr ""
@@ -1629,11 +1671,11 @@ msgstr ""
 msgid "The filename cannot contain the following characters: %{c}"
 msgstr ""
 
-#: src/views/certificate/CertificateEditor.vue:194
+#: src/views/certificate/CertificateEditor.vue:201
 msgid "The input is not a SSL Certificate"
 msgstr ""
 
-#: src/views/certificate/CertificateEditor.vue:207
+#: src/views/certificate/CertificateEditor.vue:214
 msgid "The input is not a SSL Certificate Key"
 msgstr ""
 
@@ -1641,11 +1683,11 @@ msgstr ""
 msgid "The model name should only contain letters, unicode, numbers, hyphens, dashes, and dots."
 msgstr ""
 
-#: src/views/certificate/CertificateEditor.vue:165
+#: src/views/certificate/CertificateEditor.vue:166
 msgid "The path exists, but the file is not a certificate"
 msgstr ""
 
-#: src/views/certificate/CertificateEditor.vue:180
+#: src/views/certificate/CertificateEditor.vue:181
 msgid "The path exists, but the file is not a private key"
 msgstr ""
 
@@ -1671,17 +1713,17 @@ msgstr ""
 msgid "The username or password is incorrect"
 msgstr ""
 
-#: src/views/certificate/CertificateEditor.vue:101
+#: src/views/certificate/CertificateEditor.vue:102
 msgid "This Auto Cert item is invalid, please remove it."
 msgstr ""
 
-#: src/views/certificate/CertificateEditor.vue:91
+#: src/views/certificate/CertificateEditor.vue:92
 msgid "This certificate is managed by Nginx UI"
 msgstr ""
 
-#: src/views/certificate/CertificateEditor.vue:149
-#: src/views/certificate/CertificateEditor.vue:163
-#: src/views/certificate/CertificateEditor.vue:178
+#: src/views/certificate/CertificateEditor.vue:150
+#: src/views/certificate/CertificateEditor.vue:164
+#: src/views/certificate/CertificateEditor.vue:179
 msgid "This field is required"
 msgstr ""
 
@@ -1693,7 +1735,7 @@ msgstr ""
 msgid "Tips"
 msgstr ""
 
-#: src/views/notification/Notification.vue:18
+#: src/views/notification/Notification.vue:19
 msgid "Title"
 msgstr ""
 
@@ -1711,12 +1753,12 @@ msgstr ""
 
 #: src/views/certificate/Certificate.vue:37
 #: src/views/config/config.ts:12
-#: src/views/notification/Notification.vue:12
+#: src/views/notification/Notification.vue:13
 msgid "Type"
 msgstr ""
 
 #: src/views/certificate/ACMEUser.vue:53
-#: src/views/certificate/DNSCredential.vue:23
+#: src/views/certificate/DNSCredential.vue:27
 #: src/views/config/config.ts:27
 #: src/views/config/ConfigEdit.vue:121
 #: src/views/domain/components/RightSettings.vue:86
@@ -1738,7 +1780,7 @@ msgstr ""
 msgid "Upgrade"
 msgstr ""
 
-#: src/language/constants.ts:28
+#: src/language/constants.ts:29
 msgid "Upgraded successfully"
 msgstr ""
 
@@ -1772,11 +1814,11 @@ msgid "Username (*)"
 msgstr ""
 
 #: src/views/certificate/ACMEUser.vue:87
-#: src/views/certificate/Certificate.vue:92
+#: src/views/certificate/Certificate.vue:97
 msgid "Valid"
 msgstr ""
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:477
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:491
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:103
 msgid "View"
 msgstr ""
@@ -1800,7 +1842,7 @@ msgstr ""
 msgid "Warning"
 msgstr ""
 
-#: src/views/certificate/DNSCredential.vue:52
+#: src/views/certificate/DNSCredential.vue:56
 msgid "We will add one or more TXT records to the DNS records of your domain for ownership verification."
 msgstr ""
 

+ 135 - 83
app/src/language/ru_RU/app.po

@@ -24,10 +24,10 @@ msgid "ACME User"
 msgstr "Пользователь"
 
 #: src/views/certificate/ACMEUser.vue:59
-#: src/views/certificate/Certificate.vue:108
-#: src/views/certificate/DNSCredential.vue:29 src/views/config/config.ts:34
+#: src/views/certificate/Certificate.vue:113
+#: src/views/certificate/DNSCredential.vue:33 src/views/config/config.ts:34
 #: src/views/domain/DomainList.vue:47 src/views/environment/Environment.vue:129
-#: src/views/notification/Notification.vue:35
+#: src/views/notification/Notification.vue:37
 #: src/views/stream/StreamList.vue:47 src/views/user/User.vue:43
 msgid "Action"
 msgstr "Действие"
@@ -95,7 +95,7 @@ msgid "Arch"
 msgstr ""
 
 #: src/components/Notification/Notification.vue:84
-#: src/views/notification/Notification.vue:70
+#: src/views/notification/Notification.vue:72
 #, fuzzy
 msgid "Are you sure you want to clear all notifications?"
 msgstr "Вы уверены, что хотите удалить все уведомления?"
@@ -105,7 +105,12 @@ msgstr "Вы уверены, что хотите удалить все увед
 msgid "Are you sure you want to clear the record of chat?"
 msgstr "Вы уверены, что хотите очистить сообщения чата?"
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:509
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:551
+#, fuzzy
+msgid "Are you sure you want to delete this item permanently?"
+msgstr "Вы уверены, что хотите удалить?"
+
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:523
 #, fuzzy
 msgid "Are you sure you want to delete this item?"
 msgstr "Вы уверены, что хотите удалить?"
@@ -115,7 +120,7 @@ msgstr "Вы уверены, что хотите удалить?"
 msgid "Are you sure you want to delete?"
 msgstr "Вы уверены, что хотите удалить?"
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:523
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:537
 #, fuzzy
 msgid "Are you sure you want to recover this item?"
 msgstr "Вы уверены, что хотите удалить эту директиву?"
@@ -163,7 +168,7 @@ msgstr "Автообновление отключено для %{name}"
 msgid "Auto-renewal enabled for %{name}"
 msgstr "Автообновление включено для %{name}"
 
-#: src/views/certificate/CertificateEditor.vue:235
+#: src/views/certificate/CertificateEditor.vue:242
 #: src/views/config/Config.vue:73 src/views/config/ConfigEdit.vue:87
 #: src/views/domain/DomainEdit.vue:256 src/views/nginx_log/NginxLog.vue:168
 #: src/views/stream/StreamEdit.vue:247
@@ -192,7 +197,7 @@ msgid "Basic Mode"
 msgstr "Простой режим"
 
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:54
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:445
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:459
 #, fuzzy
 msgid "Batch Modify"
 msgstr "Изменение конфигурации"
@@ -237,12 +242,12 @@ msgstr "Сертификат действителен"
 msgid "Certificate Renewal Interval"
 msgstr "Сертификат действителен"
 
-#: src/views/certificate/CertificateEditor.vue:127
+#: src/views/certificate/CertificateEditor.vue:128
 #: src/views/domain/cert/Cert.vue:33
 msgid "Certificate Status"
 msgstr "Статус сертификата"
 
-#: src/routes/index.ts:119 src/views/certificate/Certificate.vue:124
+#: src/routes/index.ts:119 src/views/certificate/Certificate.vue:129
 #, fuzzy
 msgid "Certificates"
 msgstr "Статус сертификата"
@@ -276,12 +281,12 @@ msgstr "Очистка переменных среды"
 
 #: src/components/ChatGPT/ChatGPT.vue:276
 #: src/components/Notification/Notification.vue:89
-#: src/views/notification/Notification.vue:75
+#: src/views/notification/Notification.vue:77
 msgid "Clear"
 msgstr "Очистить"
 
 #: src/components/Notification/Notification.vue:40
-#: src/views/notification/Notification.vue:44
+#: src/views/notification/Notification.vue:46
 #, fuzzy
 msgid "Cleared successfully"
 msgstr "Отключено успешно"
@@ -350,7 +355,7 @@ msgstr "Создан в"
 msgid "Create Another"
 msgstr "Создать еще"
 
-#: src/views/notification/Notification.vue:29 src/views/user/User.vue:31
+#: src/views/notification/Notification.vue:31 src/views/user/User.vue:31
 msgid "Created at"
 msgstr "Создан в"
 
@@ -393,7 +398,7 @@ msgstr "База данных (Опционально, по умолчанию:
 msgid "Days"
 msgstr ""
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:516
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:530
 #: src/views/domain/DomainList.vue:155
 #: src/views/domain/ngx_conf/NgxServer.vue:114
 #: src/views/domain/ngx_conf/NgxUpstream.vue:126
@@ -401,6 +406,10 @@ msgstr ""
 msgid "Delete"
 msgstr "Удалить"
 
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:558
+msgid "Delete Permanently"
+msgstr ""
+
 #: src/views/domain/DomainList.vue:78
 msgid "Delete site: %{site_name}"
 msgstr ""
@@ -409,7 +418,7 @@ msgstr ""
 msgid "Delete stream: %{stream_name}"
 msgstr ""
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:171
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:185
 #, fuzzy
 msgid "Deleted successfully"
 msgstr "Отключено успешно"
@@ -442,7 +451,7 @@ msgstr "Saved successfully"
 msgid "Description"
 msgstr "Описание"
 
-#: src/views/notification/Notification.vue:25
+#: src/views/notification/Notification.vue:26
 msgid "Details"
 msgstr ""
 
@@ -489,7 +498,7 @@ msgstr "Отключено успешно"
 msgid "Disk IO"
 msgstr "Нагрузка на Диск IO"
 
-#: src/routes/index.ts:162 src/views/certificate/DNSCredential.vue:36
+#: src/routes/index.ts:162 src/views/certificate/DNSCredential.vue:40
 msgid "DNS Credentials"
 msgstr ""
 
@@ -557,17 +566,17 @@ msgstr ""
 msgid "Domain Config Created Successfully"
 msgstr "Конфигурация домена успешно создана"
 
-#: src/views/certificate/CertificateEditor.vue:111
+#: src/views/certificate/CertificateEditor.vue:112
 #, fuzzy
 msgid "Domains list is empty, try to reopen Auto Cert for %{config}"
 msgstr ""
 "Список доменов пуст, попробуйте заново открыть авто-сертификат для %{config}"
 
-#: src/language/constants.ts:25
+#: src/language/constants.ts:26
 msgid "Download latest release error"
 msgstr "Ошибка загрузки последней версии"
 
-#: src/language/constants.ts:24
+#: src/language/constants.ts:25
 msgid "Downloading latest release"
 msgstr "Загрузка последней версии"
 
@@ -695,6 +704,11 @@ msgstr "Использовать для сайта Let's Encrypt"
 msgid "Environment"
 msgstr "Окружение"
 
+#: src/language/constants.ts:21
+#, fuzzy
+msgid "Environment variables cleaned"
+msgstr "Настройка переменных сред"
+
 #: src/views/dashboard/Environments.vue:82
 #, fuzzy
 msgid "Environments"
@@ -712,7 +726,7 @@ msgstr "Ошибка логирования"
 msgid "Executable Path"
 msgstr "Исполняемый путь"
 
-#: src/views/certificate/Certificate.vue:96
+#: src/views/certificate/Certificate.vue:101
 msgid "Expired"
 msgstr ""
 
@@ -756,7 +770,7 @@ msgstr "Не удалось сохранить, обнаружены синта
 msgid "File"
 msgstr "Файл"
 
-#: src/language/constants.ts:30
+#: src/language/constants.ts:31
 msgid "File exists"
 msgstr "Файл существует"
 
@@ -796,7 +810,7 @@ msgstr "Ошибка форматирования %{msg}"
 msgid "Format successfully"
 msgstr "Форматирование успешно"
 
-#: src/views/certificate/Certificate.vue:43
+#: src/views/certificate/Certificate.vue:44
 #, fuzzy
 msgid "General Certificate"
 msgstr "Сертификат действителен"
@@ -846,12 +860,12 @@ msgstr ""
 msgid "If left blank, the default CA Dir will be used."
 msgstr ""
 
-#: src/views/certificate/Certificate.vue:131
+#: src/views/certificate/Certificate.vue:136
 #, fuzzy
 msgid "Import"
 msgstr "Экспорт"
 
-#: src/routes/index.ts:153 src/views/certificate/CertificateEditor.vue:84
+#: src/routes/index.ts:153 src/views/certificate/CertificateEditor.vue:85
 #, fuzzy
 msgid "Import Certificate"
 msgstr "Статус сертификата"
@@ -860,11 +874,11 @@ msgstr "Статус сертификата"
 msgid "Info"
 msgstr ""
 
-#: src/language/constants.ts:23
+#: src/language/constants.ts:24
 msgid "Initial core upgrader error"
 msgstr "Ошибка первоначального обновления ядра"
 
-#: src/language/constants.ts:22
+#: src/language/constants.ts:23
 msgid "Initialing core upgrader"
 msgstr "Инициализация программы обновления ядра"
 
@@ -890,7 +904,7 @@ msgstr ""
 msgid "Invalid"
 msgstr "Действительный"
 
-#: src/views/certificate/Certificate.vue:139
+#: src/views/certificate/Certificate.vue:144
 #, fuzzy
 msgid "Issue wildcard certificate"
 msgstr "Получить сертификат"
@@ -909,7 +923,7 @@ msgstr "Сертификат успешно выдан"
 msgid "Jwt Secret"
 msgstr ""
 
-#: src/views/certificate/Certificate.vue:61
+#: src/views/certificate/Certificate.vue:66
 #: src/views/certificate/WildcardCertificate.vue:79
 #: src/views/domain/cert/components/AutoCertStepOne.vue:80
 #, fuzzy
@@ -928,8 +942,8 @@ msgstr "Оставьте пустым без изменений"
 msgid "Leave blank for the default: https://api.openai.com/"
 msgstr "Оставьте пустым для значения по умолчанию: https://api.openai.com/"
 
-#: src/views/certificate/CertificateEditor.vue:200
-#: src/views/certificate/CertificateEditor.vue:213
+#: src/views/certificate/CertificateEditor.vue:207
+#: src/views/certificate/CertificateEditor.vue:220
 #, fuzzy
 msgid "Leave blank will not change anything"
 msgstr "Оставьте пустым без изменений"
@@ -976,7 +990,7 @@ msgstr "Location"
 msgid "Locations"
 msgstr "Locations"
 
-#: src/views/certificate/CertificateEditor.vue:223
+#: src/views/certificate/CertificateEditor.vue:230
 #, fuzzy
 msgid "Log"
 msgstr "Логин"
@@ -1033,7 +1047,7 @@ msgstr "Управление потоками"
 msgid "Manage Users"
 msgstr "Пользователи"
 
-#: src/views/certificate/Certificate.vue:42
+#: src/views/certificate/Certificate.vue:43
 #, fuzzy
 msgid "Managed Certificate"
 msgstr "Управление сертификатами"
@@ -1058,12 +1072,12 @@ msgstr "Расширенный режим"
 
 #: src/components/ChatGPT/ChatGPT.vue:249
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:194
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:491
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:505
 #, fuzzy
 msgid "Modify"
 msgstr "Изменить"
 
-#: src/routes/index.ts:144 src/views/certificate/CertificateEditor.vue:84
+#: src/routes/index.ts:144 src/views/certificate/CertificateEditor.vue:85
 #, fuzzy
 msgid "Modify Certificate"
 msgstr "Статус сертификата"
@@ -1084,8 +1098,8 @@ msgstr "Одиночная директива"
 
 #: src/views/certificate/ACMEUser.vue:13
 #: src/views/certificate/Certificate.vue:20
-#: src/views/certificate/CertificateEditor.vue:146
-#: src/views/certificate/DNSCredential.vue:10 src/views/config/config.ts:7
+#: src/views/certificate/CertificateEditor.vue:147
+#: src/views/certificate/DNSCredential.vue:11 src/views/config/config.ts:7
 #: src/views/domain/cert/ChangeCert.vue:17
 #: src/views/domain/components/RightSettings.vue:83
 #: src/views/domain/components/SiteDuplicate.vue:133
@@ -1162,12 +1176,13 @@ msgstr "Nginx успешно перезапущен"
 
 #: src/components/ChatGPT/ChatGPT.vue:270
 #: src/components/Notification/Notification.vue:82
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:507
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:521
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:535
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:549
 #: src/views/domain/DomainList.vue:144
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:90
 #: src/views/domain/ngx_conf/LocationEditor.vue:71
-#: src/views/notification/Notification.vue:68
+#: src/views/notification/Notification.vue:70
 #: src/views/preference/BasicSettings.vue:101
 #: src/views/stream/StreamList.vue:165
 msgid "No"
@@ -1177,7 +1192,7 @@ msgstr "Нет"
 msgid "Node Secret"
 msgstr ""
 
-#: src/views/certificate/Certificate.vue:102
+#: src/views/certificate/Certificate.vue:107
 msgid "Not After"
 msgstr ""
 
@@ -1189,12 +1204,12 @@ msgstr "Не найден"
 msgid "Not Valid Before: %{date}"
 msgstr "Недействительно до: %{date}"
 
-#: src/views/certificate/DNSCredential.vue:45
+#: src/views/certificate/DNSCredential.vue:49
 #: src/views/domain/cert/components/AutoCertStepOne.vue:45
 msgid "Note"
 msgstr "Заметка"
 
-#: src/views/notification/Notification.vue:60
+#: src/views/notification/Notification.vue:62
 #, fuzzy
 msgid "Notification"
 msgstr "Сертификат"
@@ -1227,8 +1242,9 @@ msgstr ""
 #: src/components/ChatGPT/ChatGPT.vue:271
 #: src/components/Notification/Notification.vue:83
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:56
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:508
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:522
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:536
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:550
 #: src/views/domain/cert/components/ObtainCert.vue:136
 #: src/views/domain/components/Deploy.vue:20
 #: src/views/domain/components/RightSettings.vue:50
@@ -1236,14 +1252,14 @@ msgstr ""
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:49
 #: src/views/domain/ngx_conf/NgxServer.vue:83
 #: src/views/domain/ngx_conf/NgxUpstream.vue:32
-#: src/views/notification/Notification.vue:69
+#: src/views/notification/Notification.vue:71
 #: src/views/stream/components/Deploy.vue:20
 #: src/views/stream/components/RightSettings.vue:50
 #: src/views/stream/StreamList.vue:166
 msgid "OK"
 msgstr ""
 
-#: src/views/certificate/DNSCredential.vue:55
+#: src/views/certificate/DNSCredential.vue:59
 msgid "Once the verification is complete, the records will be removed."
 msgstr ""
 
@@ -1295,15 +1311,15 @@ msgstr "Пароль (*)"
 msgid "Path"
 msgstr "Путь"
 
-#: src/language/constants.ts:27
+#: src/language/constants.ts:28
 msgid "Perform core upgrade error"
 msgstr ""
 
-#: src/language/constants.ts:26
+#: src/language/constants.ts:27
 msgid "Performing core upgrade"
 msgstr ""
 
-#: src/views/certificate/DNSCredential.vue:49
+#: src/views/certificate/DNSCredential.vue:53
 msgid ""
 "Please fill in the API authentication credentials provided by your DNS "
 "provider."
@@ -1336,7 +1352,7 @@ msgstr "Введите ваш пароль!"
 msgid "Please input your username!"
 msgstr "Введите ваше имя пользователя!"
 
-#: src/views/certificate/DNSCredential.vue:58
+#: src/views/certificate/DNSCredential.vue:62
 msgid ""
 "Please note that the unit of time configurations below are all in seconds."
 msgstr ""
@@ -1371,7 +1387,7 @@ msgstr "Запретить удаление пользователя по умо
 msgid "Project Team"
 msgstr "Команда проекта"
 
-#: src/views/certificate/DNSCredential.vue:18
+#: src/views/certificate/DNSCredential.vue:19
 msgid "Provider"
 msgstr "Провайдер"
 
@@ -1385,11 +1401,11 @@ msgstr "Чтение"
 msgid "Receive"
 msgstr "Принято"
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:530
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:544
 msgid "Recover"
 msgstr ""
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:179
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:193
 #, fuzzy
 msgid "Recovered Successfully"
 msgstr "Успешно сохранено"
@@ -1464,12 +1480,12 @@ msgstr "Имя пользователя"
 msgid "Renew Certificate"
 msgstr "Сертификат действителен"
 
-#: src/language/constants.ts:34
+#: src/language/constants.ts:35
 #, fuzzy
 msgid "Renew Certificate Error"
 msgstr "Сертификат действителен"
 
-#: src/language/constants.ts:33
+#: src/language/constants.ts:34
 #, fuzzy
 msgid "Renew Certificate Success"
 msgstr "Сертификат действителен"
@@ -1480,11 +1496,11 @@ msgstr "Сертификат действителен"
 msgid "Renew successfully"
 msgstr "Активировано успешно"
 
-#: src/language/constants.ts:31
+#: src/language/constants.ts:32
 msgid "Requested with wrong parameters"
 msgstr "Запрос с неправильными параметрами"
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:439
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:453
 msgid "Reset"
 msgstr "Сброс"
 
@@ -1506,7 +1522,7 @@ msgid "Running"
 msgstr "Выполняется"
 
 #: src/components/ChatGPT/ChatGPT.vue:252
-#: src/views/certificate/CertificateEditor.vue:242
+#: src/views/certificate/CertificateEditor.vue:249
 #: src/views/config/ConfigEdit.vue:96 src/views/domain/DomainEdit.vue:263
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:120
 #: src/views/preference/Preference.vue:130 src/views/stream/StreamEdit.vue:254
@@ -1524,7 +1540,7 @@ msgstr "Ошибка сохранения %{msg}"
 
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:39
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:104
-#: src/views/certificate/CertificateEditor.vue:45
+#: src/views/certificate/CertificateEditor.vue:46
 #: src/views/preference/Preference.vue:66
 #, fuzzy
 msgid "Save successfully"
@@ -1555,8 +1571,8 @@ msgstr "Отправлено"
 #: src/components/StdDesign/StdDataDisplay/methods/exportCsv.ts:46
 #: src/components/StdDesign/StdDataDisplay/methods/sortable.ts:126
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:42
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:182
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:221
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:196
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:235
 #: src/views/config/ConfigEdit.vue:40 src/views/domain/DomainList.vue:81
 #: src/views/environment/Environment.vue:139 src/views/other/Install.vue:70
 #: src/views/preference/Preference.vue:70 src/views/stream/StreamList.vue:113
@@ -1613,24 +1629,24 @@ msgstr "Логи сайтов"
 msgid "Sites List"
 msgstr "Список сайтов"
 
-#: src/views/certificate/CertificateEditor.vue:191
+#: src/views/certificate/CertificateEditor.vue:198
 #, fuzzy
 msgid "SSL Certificate Content"
 msgstr "Содержание сертификата SSL"
 
-#: src/views/certificate/CertificateEditor.vue:204
+#: src/views/certificate/CertificateEditor.vue:211
 #, fuzzy
 msgid "SSL Certificate Key Content"
 msgstr "Содержание ключа сертификата SSL"
 
-#: src/views/certificate/Certificate.vue:75
-#: src/views/certificate/CertificateEditor.vue:176
+#: src/views/certificate/Certificate.vue:80
+#: src/views/certificate/CertificateEditor.vue:177
 #, fuzzy
 msgid "SSL Certificate Key Path"
 msgstr "Путь к ключу сертификата SSL"
 
-#: src/views/certificate/Certificate.vue:67
-#: src/views/certificate/CertificateEditor.vue:161
+#: src/views/certificate/Certificate.vue:72
+#: src/views/certificate/CertificateEditor.vue:162
 #, fuzzy
 msgid "SSL Certificate Path"
 msgstr "Путь к сертификату SSL"
@@ -1646,7 +1662,7 @@ msgid "Stable"
 msgstr "Таблица"
 
 #: src/views/certificate/ACMEUser.vue:42
-#: src/views/certificate/Certificate.vue:83 src/views/domain/DomainList.vue:22
+#: src/views/certificate/Certificate.vue:88 src/views/domain/DomainList.vue:22
 #: src/views/environment/Environment.vue:76 src/views/stream/StreamList.vue:22
 msgid "Status"
 msgstr "Статус"
@@ -1682,6 +1698,42 @@ msgstr ""
 msgid "Switch to light theme"
 msgstr ""
 
+#: src/views/certificate/Certificate.vue:42
+#, fuzzy
+msgid "Sync Certificate"
+msgstr "Сертификат действителен"
+
+#: src/components/Notification/detailRender.ts:25
+#, fuzzy
+msgid ""
+"Sync Certificate %{cert_name} to %{env_name} failed, please upgrade the "
+"remote Nginx UI to the latest version"
+msgstr "Продублированно %{conf_name} в %{node_name}"
+
+#: src/components/Notification/detailRender.ts:29
+#, fuzzy
+msgid "Sync Certificate %{cert_name} to %{env_name} failed, response: %{resp}"
+msgstr "Продублированно %{conf_name} в %{node_name}"
+
+#: src/components/Notification/detailRender.ts:17
+#, fuzzy
+msgid "Sync Certificate %{cert_name} to %{env_name} successfully"
+msgstr "Продублированно %{conf_name} в %{node_name}"
+
+#: src/language/constants.ts:38
+#, fuzzy
+msgid "Sync Certificate Error"
+msgstr "Сертификат действителен"
+
+#: src/language/constants.ts:37
+#, fuzzy
+msgid "Sync Certificate Success"
+msgstr "Сертификат действителен"
+
+#: src/views/certificate/CertificateEditor.vue:191
+msgid "Sync to"
+msgstr ""
+
 #: src/routes/index.ts:248
 msgid "System"
 msgstr "Система"
@@ -1712,11 +1764,11 @@ msgstr ""
 msgid "The filename cannot contain the following characters: %{c}"
 msgstr "The filename cannot contain the following characters: %{c}"
 
-#: src/views/certificate/CertificateEditor.vue:194
+#: src/views/certificate/CertificateEditor.vue:201
 msgid "The input is not a SSL Certificate"
 msgstr ""
 
-#: src/views/certificate/CertificateEditor.vue:207
+#: src/views/certificate/CertificateEditor.vue:214
 #, fuzzy
 msgid "The input is not a SSL Certificate Key"
 msgstr "Путь к ключу сертификата SSL"
@@ -1727,12 +1779,12 @@ msgid ""
 "dashes, and dots."
 msgstr ""
 
-#: src/views/certificate/CertificateEditor.vue:165
+#: src/views/certificate/CertificateEditor.vue:166
 #, fuzzy
 msgid "The path exists, but the file is not a certificate"
 msgstr "Путь к ключу сертификата SSL"
 
-#: src/views/certificate/CertificateEditor.vue:180
+#: src/views/certificate/CertificateEditor.vue:181
 msgid "The path exists, but the file is not a private key"
 msgstr "Путь существует, но файл не является приватным ключом"
 
@@ -1767,18 +1819,18 @@ msgstr "URL-адрес неверный"
 msgid "The username or password is incorrect"
 msgstr "Имя пользователя или пароль неверны"
 
-#: src/views/certificate/CertificateEditor.vue:101
+#: src/views/certificate/CertificateEditor.vue:102
 #, fuzzy
 msgid "This Auto Cert item is invalid, please remove it."
 msgstr "Этот элемент автосертификата недействителен, удалите его.."
 
-#: src/views/certificate/CertificateEditor.vue:91
+#: src/views/certificate/CertificateEditor.vue:92
 msgid "This certificate is managed by Nginx UI"
 msgstr "Этот сертификат находится под управлением  Nginx UI"
 
-#: src/views/certificate/CertificateEditor.vue:149
-#: src/views/certificate/CertificateEditor.vue:163
-#: src/views/certificate/CertificateEditor.vue:178
+#: src/views/certificate/CertificateEditor.vue:150
+#: src/views/certificate/CertificateEditor.vue:164
+#: src/views/certificate/CertificateEditor.vue:179
 msgid "This field is required"
 msgstr ""
 
@@ -1790,7 +1842,7 @@ msgstr "Это поле обязательно к заполнению"
 msgid "Tips"
 msgstr ""
 
-#: src/views/notification/Notification.vue:18
+#: src/views/notification/Notification.vue:19
 msgid "Title"
 msgstr "Заголовок"
 
@@ -1811,12 +1863,12 @@ msgid "Trash"
 msgstr ""
 
 #: src/views/certificate/Certificate.vue:37 src/views/config/config.ts:12
-#: src/views/notification/Notification.vue:12
+#: src/views/notification/Notification.vue:13
 msgid "Type"
 msgstr "Тип"
 
 #: src/views/certificate/ACMEUser.vue:53
-#: src/views/certificate/DNSCredential.vue:23 src/views/config/config.ts:27
+#: src/views/certificate/DNSCredential.vue:27 src/views/config/config.ts:27
 #: src/views/config/ConfigEdit.vue:121
 #: src/views/domain/components/RightSettings.vue:86
 #: src/views/domain/DomainList.vue:41 src/views/environment/Environment.vue:122
@@ -1835,7 +1887,7 @@ msgstr "Обновлено успешно"
 msgid "Upgrade"
 msgstr "Обновление"
 
-#: src/language/constants.ts:28
+#: src/language/constants.ts:29
 #, fuzzy
 msgid "Upgraded successfully"
 msgstr "Обновление успешно выполнено"
@@ -1870,11 +1922,11 @@ msgid "Username (*)"
 msgstr "Имя пользователя (*)"
 
 #: src/views/certificate/ACMEUser.vue:87
-#: src/views/certificate/Certificate.vue:92
+#: src/views/certificate/Certificate.vue:97
 msgid "Valid"
 msgstr "Действительный"
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:477
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:491
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:103
 msgid "View"
 msgstr "Просмотр"
@@ -1899,7 +1951,7 @@ msgstr "Простой режим"
 msgid "Warning"
 msgstr "Внимание"
 
-#: src/views/certificate/DNSCredential.vue:52
+#: src/views/certificate/DNSCredential.vue:56
 msgid ""
 "We will add one or more TXT records to the DNS records of your domain for "
 "ownership verification."

+ 135 - 83
app/src/language/vi_VN/app.po

@@ -24,10 +24,10 @@ msgid "ACME User"
 msgstr "Người dùng"
 
 #: src/views/certificate/ACMEUser.vue:59
-#: src/views/certificate/Certificate.vue:108
-#: src/views/certificate/DNSCredential.vue:29 src/views/config/config.ts:34
+#: src/views/certificate/Certificate.vue:113
+#: src/views/certificate/DNSCredential.vue:33 src/views/config/config.ts:34
 #: src/views/domain/DomainList.vue:47 src/views/environment/Environment.vue:129
-#: src/views/notification/Notification.vue:35
+#: src/views/notification/Notification.vue:37
 #: src/views/stream/StreamList.vue:47 src/views/user/User.vue:43
 msgid "Action"
 msgstr "Hành động"
@@ -95,7 +95,7 @@ msgid "Arch"
 msgstr ""
 
 #: src/components/Notification/Notification.vue:84
-#: src/views/notification/Notification.vue:70
+#: src/views/notification/Notification.vue:72
 #, fuzzy
 msgid "Are you sure you want to clear all notifications?"
 msgstr "Bạn có chắc chắn muốn xóa tất cả thông báo không ?"
@@ -105,7 +105,12 @@ msgstr "Bạn có chắc chắn muốn xóa tất cả thông báo không ?"
 msgid "Are you sure you want to clear the record of chat?"
 msgstr "Bạn có chắc chắn muốn xóa lịch sử trò chuyện không ?"
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:509
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:551
+#, fuzzy
+msgid "Are you sure you want to delete this item permanently?"
+msgstr "Bạn chắc chắn muốn xóa nó "
+
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:523
 #, fuzzy
 msgid "Are you sure you want to delete this item?"
 msgstr "Bạn chắc chắn muốn xóa nó "
@@ -115,7 +120,7 @@ msgstr "Bạn chắc chắn muốn xóa nó "
 msgid "Are you sure you want to delete?"
 msgstr "Bạn chắc chắn muốn xóa nó "
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:523
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:537
 #, fuzzy
 msgid "Are you sure you want to recover this item?"
 msgstr "Bạn chắc chắn muốn xoá directive này ?"
@@ -163,7 +168,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/CertificateEditor.vue:235
+#: src/views/certificate/CertificateEditor.vue:242
 #: src/views/config/Config.vue:73 src/views/config/ConfigEdit.vue:87
 #: src/views/domain/DomainEdit.vue:256 src/views/nginx_log/NginxLog.vue:168
 #: src/views/stream/StreamEdit.vue:247
@@ -192,7 +197,7 @@ msgid "Basic Mode"
 msgstr "Cơ bản"
 
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:54
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:445
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:459
 #, fuzzy
 msgid "Batch Modify"
 msgstr "Sửa đổi cấu hình"
@@ -237,12 +242,12 @@ msgstr "Chứng chỉ SSL hợp lệ"
 msgid "Certificate Renewal Interval"
 msgstr "Chứng chỉ SSL hợp lệ"
 
-#: src/views/certificate/CertificateEditor.vue:127
+#: src/views/certificate/CertificateEditor.vue:128
 #: src/views/domain/cert/Cert.vue:33
 msgid "Certificate Status"
 msgstr "Trạng thái chứng chỉ"
 
-#: src/routes/index.ts:119 src/views/certificate/Certificate.vue:124
+#: src/routes/index.ts:119 src/views/certificate/Certificate.vue:129
 #, fuzzy
 msgid "Certificates"
 msgstr "Chứng chỉ"
@@ -276,12 +281,12 @@ msgstr "Xoá các biến môi trường"
 
 #: src/components/ChatGPT/ChatGPT.vue:276
 #: src/components/Notification/Notification.vue:89
-#: src/views/notification/Notification.vue:75
+#: src/views/notification/Notification.vue:77
 msgid "Clear"
 msgstr "Xoá"
 
 #: src/components/Notification/Notification.vue:40
-#: src/views/notification/Notification.vue:44
+#: src/views/notification/Notification.vue:46
 #, fuzzy
 msgid "Cleared successfully"
 msgstr "Đã xóa thành công"
@@ -350,7 +355,7 @@ msgstr "Ngày tạo"
 msgid "Create Another"
 msgstr "Tạo thêm"
 
-#: src/views/notification/Notification.vue:29 src/views/user/User.vue:31
+#: src/views/notification/Notification.vue:31 src/views/user/User.vue:31
 msgid "Created at"
 msgstr "Ngày tạo"
 
@@ -393,7 +398,7 @@ msgstr "Tên cơ sở dữ liệu (Tuỳ chọn, Mặc định là: database)"
 msgid "Days"
 msgstr ""
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:516
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:530
 #: src/views/domain/DomainList.vue:155
 #: src/views/domain/ngx_conf/NgxServer.vue:114
 #: src/views/domain/ngx_conf/NgxUpstream.vue:126
@@ -401,6 +406,10 @@ msgstr ""
 msgid "Delete"
 msgstr "Xoá"
 
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:558
+msgid "Delete Permanently"
+msgstr ""
+
 #: src/views/domain/DomainList.vue:78
 msgid "Delete site: %{site_name}"
 msgstr "Xoá trang web: %{site_name}"
@@ -410,7 +419,7 @@ msgstr "Xoá trang web: %{site_name}"
 msgid "Delete stream: %{stream_name}"
 msgstr "Xoá trang web: %{site_name}"
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:171
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:185
 #, fuzzy
 msgid "Deleted successfully"
 msgstr "Đã xoá thành công"
@@ -443,7 +452,7 @@ msgstr "Triển khai thành công"
 msgid "Description"
 msgstr "Mô tả"
 
-#: src/views/notification/Notification.vue:25
+#: src/views/notification/Notification.vue:26
 msgid "Details"
 msgstr "Chi tiết"
 
@@ -490,7 +499,7 @@ msgstr "Đã tắt thành công"
 msgid "Disk IO"
 msgstr "Disk IO"
 
-#: src/routes/index.ts:162 src/views/certificate/DNSCredential.vue:36
+#: src/routes/index.ts:162 src/views/certificate/DNSCredential.vue:40
 msgid "DNS Credentials"
 msgstr "Xác thực DNS"
 
@@ -558,17 +567,17 @@ msgstr ""
 msgid "Domain Config Created Successfully"
 msgstr "Tên miền đã được tạo"
 
-#: src/views/certificate/CertificateEditor.vue:111
+#: src/views/certificate/CertificateEditor.vue:112
 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 "
 "%{config}"
 
-#: src/language/constants.ts:25
+#: src/language/constants.ts:26
 msgid "Download latest release error"
 msgstr "Đã có lỗi xảy ra khi tải về phiên bản mới nhất"
 
-#: src/language/constants.ts:24
+#: src/language/constants.ts:25
 msgid "Downloading latest release"
 msgstr "Đang tải phiên bản mới nhất"
 
@@ -696,6 +705,11 @@ msgstr "Bảo mật trang web với Let's Encrypt"
 msgid "Environment"
 msgstr "Environment"
 
+#: src/language/constants.ts:21
+#, fuzzy
+msgid "Environment variables cleaned"
+msgstr "Đặt biến môi trường"
+
 #: src/views/dashboard/Environments.vue:82
 #, fuzzy
 msgid "Environments"
@@ -713,7 +727,7 @@ msgstr "Log lỗi"
 msgid "Executable Path"
 msgstr "Đường dẫn thực thi"
 
-#: src/views/certificate/Certificate.vue:96
+#: src/views/certificate/Certificate.vue:101
 msgid "Expired"
 msgstr "Đã hết hạn"
 
@@ -757,7 +771,7 @@ msgstr "Không lưu được, đã phát hiện thấy (các) lỗi cú pháp tr
 msgid "File"
 msgstr "Tệp tin"
 
-#: src/language/constants.ts:30
+#: src/language/constants.ts:31
 msgid "File exists"
 msgstr "Tệp tin đã tồn tại"
 
@@ -798,7 +812,7 @@ msgstr "Lưu lỗi %{msg}"
 msgid "Format successfully"
 msgstr "Định dạng thành công"
 
-#: src/views/certificate/Certificate.vue:43
+#: src/views/certificate/Certificate.vue:44
 #, fuzzy
 msgid "General Certificate"
 msgstr "Chứng chỉ chung"
@@ -848,12 +862,12 @@ msgstr ""
 msgid "If left blank, the default CA Dir will be used."
 msgstr ""
 
-#: src/views/certificate/Certificate.vue:131
+#: src/views/certificate/Certificate.vue:136
 #, fuzzy
 msgid "Import"
 msgstr "Xuất"
 
-#: src/routes/index.ts:153 src/views/certificate/CertificateEditor.vue:84
+#: src/routes/index.ts:153 src/views/certificate/CertificateEditor.vue:85
 #, fuzzy
 msgid "Import Certificate"
 msgstr "Chứng chỉ"
@@ -862,11 +876,11 @@ msgstr "Chứng chỉ"
 msgid "Info"
 msgstr "Thông tin"
 
-#: src/language/constants.ts:23
+#: src/language/constants.ts:24
 msgid "Initial core upgrader error"
 msgstr "Không thể khởi tạo trình nâng cấp"
 
-#: src/language/constants.ts:22
+#: src/language/constants.ts:23
 msgid "Initialing core upgrader"
 msgstr "Đang khởi tạo trình nâng cấp"
 
@@ -892,7 +906,7 @@ msgstr ""
 msgid "Invalid"
 msgstr "Hợp lệ"
 
-#: src/views/certificate/Certificate.vue:139
+#: src/views/certificate/Certificate.vue:144
 #, fuzzy
 msgid "Issue wildcard certificate"
 msgstr "Gia hạn SSL"
@@ -911,7 +925,7 @@ msgstr "Cấp chứng chỉ thành công"
 msgid "Jwt Secret"
 msgstr ""
 
-#: src/views/certificate/Certificate.vue:61
+#: src/views/certificate/Certificate.vue:66
 #: src/views/certificate/WildcardCertificate.vue:79
 #: src/views/domain/cert/components/AutoCertStepOne.vue:80
 #, fuzzy
@@ -930,8 +944,8 @@ msgstr "Bỏ trống nếu không thay đổi"
 msgid "Leave blank for the default: https://api.openai.com/"
 msgstr "Bỏ trống để sử dụng địa chỉ mặc định: https://api.openai.com/"
 
-#: src/views/certificate/CertificateEditor.vue:200
-#: src/views/certificate/CertificateEditor.vue:213
+#: src/views/certificate/CertificateEditor.vue:207
+#: src/views/certificate/CertificateEditor.vue:220
 #, fuzzy
 msgid "Leave blank will not change anything"
 msgstr "Bỏ trống nếu không thay đổi"
@@ -978,7 +992,7 @@ msgstr "Location"
 msgid "Locations"
 msgstr "Locations"
 
-#: src/views/certificate/CertificateEditor.vue:223
+#: src/views/certificate/CertificateEditor.vue:230
 #, fuzzy
 msgid "Log"
 msgstr "Log"
@@ -1035,7 +1049,7 @@ msgstr "Quản lý Website"
 msgid "Manage Users"
 msgstr "Người dùng"
 
-#: src/views/certificate/Certificate.vue:42
+#: src/views/certificate/Certificate.vue:43
 msgid "Managed Certificate"
 msgstr ""
 
@@ -1059,12 +1073,12 @@ msgstr "Run Mode"
 
 #: src/components/ChatGPT/ChatGPT.vue:249
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:194
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:491
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:505
 #, fuzzy
 msgid "Modify"
 msgstr "Sửa"
 
-#: src/routes/index.ts:144 src/views/certificate/CertificateEditor.vue:84
+#: src/routes/index.ts:144 src/views/certificate/CertificateEditor.vue:85
 #, fuzzy
 msgid "Modify Certificate"
 msgstr "Sửa chứng chỉ"
@@ -1085,8 +1099,8 @@ msgstr "Single Directive"
 
 #: src/views/certificate/ACMEUser.vue:13
 #: src/views/certificate/Certificate.vue:20
-#: src/views/certificate/CertificateEditor.vue:146
-#: src/views/certificate/DNSCredential.vue:10 src/views/config/config.ts:7
+#: src/views/certificate/CertificateEditor.vue:147
+#: src/views/certificate/DNSCredential.vue:11 src/views/config/config.ts:7
 #: src/views/domain/cert/ChangeCert.vue:17
 #: src/views/domain/components/RightSettings.vue:83
 #: src/views/domain/components/SiteDuplicate.vue:133
@@ -1162,12 +1176,13 @@ msgstr "Restart Nginx thành công"
 
 #: src/components/ChatGPT/ChatGPT.vue:270
 #: src/components/Notification/Notification.vue:82
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:507
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:521
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:535
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:549
 #: src/views/domain/DomainList.vue:144
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:90
 #: src/views/domain/ngx_conf/LocationEditor.vue:71
-#: src/views/notification/Notification.vue:68
+#: src/views/notification/Notification.vue:70
 #: src/views/preference/BasicSettings.vue:101
 #: src/views/stream/StreamList.vue:165
 msgid "No"
@@ -1177,7 +1192,7 @@ msgstr "Không"
 msgid "Node Secret"
 msgstr ""
 
-#: src/views/certificate/Certificate.vue:102
+#: src/views/certificate/Certificate.vue:107
 msgid "Not After"
 msgstr "Không phải sau khi"
 
@@ -1189,12 +1204,12 @@ msgstr "Không tìm thấy"
 msgid "Not Valid Before: %{date}"
 msgstr "Không hợp lệ trước: %{date}"
 
-#: src/views/certificate/DNSCredential.vue:45
+#: src/views/certificate/DNSCredential.vue:49
 #: src/views/domain/cert/components/AutoCertStepOne.vue:45
 msgid "Note"
 msgstr "Ghi chú"
 
-#: src/views/notification/Notification.vue:60
+#: src/views/notification/Notification.vue:62
 #, fuzzy
 msgid "Notification"
 msgstr "Thông báo"
@@ -1227,8 +1242,9 @@ msgstr ""
 #: src/components/ChatGPT/ChatGPT.vue:271
 #: src/components/Notification/Notification.vue:83
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:56
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:508
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:522
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:536
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:550
 #: src/views/domain/cert/components/ObtainCert.vue:136
 #: src/views/domain/components/Deploy.vue:20
 #: src/views/domain/components/RightSettings.vue:50
@@ -1236,14 +1252,14 @@ msgstr ""
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:49
 #: src/views/domain/ngx_conf/NgxServer.vue:83
 #: src/views/domain/ngx_conf/NgxUpstream.vue:32
-#: src/views/notification/Notification.vue:69
+#: src/views/notification/Notification.vue:71
 #: src/views/stream/components/Deploy.vue:20
 #: src/views/stream/components/RightSettings.vue:50
 #: src/views/stream/StreamList.vue:166
 msgid "OK"
 msgstr ""
 
-#: src/views/certificate/DNSCredential.vue:55
+#: src/views/certificate/DNSCredential.vue:59
 msgid "Once the verification is complete, the records will be removed."
 msgstr "Sau khi quá trình xác minh hoàn tất, bản ghi sẽ bị xóa."
 
@@ -1295,15 +1311,15 @@ msgstr "Mật khẩu (*)"
 msgid "Path"
 msgstr "Đường dẫn"
 
-#: src/language/constants.ts:27
+#: src/language/constants.ts:28
 msgid "Perform core upgrade error"
 msgstr "Nâng cấp core không thành công"
 
-#: src/language/constants.ts:26
+#: src/language/constants.ts:27
 msgid "Performing core upgrade"
 msgstr "Nâng cấp core"
 
-#: src/views/certificate/DNSCredential.vue:49
+#: src/views/certificate/DNSCredential.vue:53
 msgid ""
 "Please fill in the API authentication credentials provided by your DNS "
 "provider."
@@ -1338,7 +1354,7 @@ msgstr "Vui lòng nhập mật khẩu!"
 msgid "Please input your username!"
 msgstr "Vui lòng nhập username!"
 
-#: src/views/certificate/DNSCredential.vue:58
+#: src/views/certificate/DNSCredential.vue:62
 msgid ""
 "Please note that the unit of time configurations below are all in seconds."
 msgstr "Lưu ý đơn vị cấu hình thời gian bên dưới được tính bằng giây."
@@ -1373,7 +1389,7 @@ msgstr "Cấm xóa người dùng mặc định"
 msgid "Project Team"
 msgstr "Đội ngũ phát triển"
 
-#: src/views/certificate/DNSCredential.vue:18
+#: src/views/certificate/DNSCredential.vue:19
 msgid "Provider"
 msgstr "Nhà cung cấp"
 
@@ -1387,11 +1403,11 @@ msgstr "Đọc"
 msgid "Receive"
 msgstr "Nhận"
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:530
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:544
 msgid "Recover"
 msgstr ""
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:179
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:193
 #, fuzzy
 msgid "Recovered Successfully"
 msgstr "Xoá thành công"
@@ -1466,12 +1482,12 @@ msgstr "Username"
 msgid "Renew Certificate"
 msgstr "Gia hạn chứng chỉ SSL"
 
-#: src/language/constants.ts:34
+#: src/language/constants.ts:35
 #, fuzzy
 msgid "Renew Certificate Error"
 msgstr "Gia hạn chứng chỉ SSL thất bại"
 
-#: src/language/constants.ts:33
+#: src/language/constants.ts:34
 #, fuzzy
 msgid "Renew Certificate Success"
 msgstr "Gia hạn chứng chỉ SSL thành công"
@@ -1482,11 +1498,11 @@ msgstr "Gia hạn chứng chỉ SSL thành công"
 msgid "Renew successfully"
 msgstr "Gia hạn chứng chỉ SSL"
 
-#: src/language/constants.ts:31
+#: src/language/constants.ts:32
 msgid "Requested with wrong parameters"
 msgstr "Yêu cầu có chứa tham số sai"
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:439
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:453
 msgid "Reset"
 msgstr "Đặt lại"
 
@@ -1508,7 +1524,7 @@ msgid "Running"
 msgstr "Running"
 
 #: src/components/ChatGPT/ChatGPT.vue:252
-#: src/views/certificate/CertificateEditor.vue:242
+#: src/views/certificate/CertificateEditor.vue:249
 #: src/views/config/ConfigEdit.vue:96 src/views/domain/DomainEdit.vue:263
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:120
 #: src/views/preference/Preference.vue:130 src/views/stream/StreamEdit.vue:254
@@ -1526,7 +1542,7 @@ msgstr "Đã xảy ra lỗi khi lưu %{msg}"
 
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:39
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:104
-#: src/views/certificate/CertificateEditor.vue:45
+#: src/views/certificate/CertificateEditor.vue:46
 #: src/views/preference/Preference.vue:66
 #, fuzzy
 msgid "Save successfully"
@@ -1557,8 +1573,8 @@ msgstr "Gửi"
 #: src/components/StdDesign/StdDataDisplay/methods/exportCsv.ts:46
 #: src/components/StdDesign/StdDataDisplay/methods/sortable.ts:126
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:42
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:182
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:221
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:196
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:235
 #: src/views/config/ConfigEdit.vue:40 src/views/domain/DomainList.vue:81
 #: src/views/environment/Environment.vue:139 src/views/other/Install.vue:70
 #: src/views/preference/Preference.vue:70 src/views/stream/StreamList.vue:113
@@ -1616,21 +1632,21 @@ msgstr "Logs"
 msgid "Sites List"
 msgstr "Danh sách Website"
 
-#: src/views/certificate/CertificateEditor.vue:191
+#: src/views/certificate/CertificateEditor.vue:198
 msgid "SSL Certificate Content"
 msgstr ""
 
-#: src/views/certificate/CertificateEditor.vue:204
+#: src/views/certificate/CertificateEditor.vue:211
 msgid "SSL Certificate Key Content"
 msgstr ""
 
-#: src/views/certificate/Certificate.vue:75
-#: src/views/certificate/CertificateEditor.vue:176
+#: src/views/certificate/Certificate.vue:80
+#: src/views/certificate/CertificateEditor.vue:177
 msgid "SSL Certificate Key Path"
 msgstr ""
 
-#: src/views/certificate/Certificate.vue:67
-#: src/views/certificate/CertificateEditor.vue:161
+#: src/views/certificate/Certificate.vue:72
+#: src/views/certificate/CertificateEditor.vue:162
 msgid "SSL Certificate Path"
 msgstr ""
 
@@ -1644,7 +1660,7 @@ msgid "Stable"
 msgstr "Ổn định"
 
 #: src/views/certificate/ACMEUser.vue:42
-#: src/views/certificate/Certificate.vue:83 src/views/domain/DomainList.vue:22
+#: src/views/certificate/Certificate.vue:88 src/views/domain/DomainList.vue:22
 #: src/views/environment/Environment.vue:76 src/views/stream/StreamList.vue:22
 msgid "Status"
 msgstr "Trạng thái"
@@ -1680,6 +1696,42 @@ msgstr "Sử dụng Dark theme"
 msgid "Switch to light theme"
 msgstr "Sử dụng Light theme"
 
+#: src/views/certificate/Certificate.vue:42
+#, fuzzy
+msgid "Sync Certificate"
+msgstr "Gia hạn chứng chỉ SSL"
+
+#: src/components/Notification/detailRender.ts:25
+#, fuzzy
+msgid ""
+"Sync Certificate %{cert_name} to %{env_name} failed, please upgrade the "
+"remote Nginx UI to the latest version"
+msgstr "Nhân bản %{conf_name} thành %{node_name} thành công"
+
+#: src/components/Notification/detailRender.ts:29
+#, fuzzy
+msgid "Sync Certificate %{cert_name} to %{env_name} failed, response: %{resp}"
+msgstr "Nhân bản %{conf_name} thành %{node_name} thành công"
+
+#: src/components/Notification/detailRender.ts:17
+#, fuzzy
+msgid "Sync Certificate %{cert_name} to %{env_name} successfully"
+msgstr "Nhân bản %{conf_name} thành %{node_name} thành công"
+
+#: src/language/constants.ts:38
+#, fuzzy
+msgid "Sync Certificate Error"
+msgstr "Gia hạn chứng chỉ SSL thất bại"
+
+#: src/language/constants.ts:37
+#, fuzzy
+msgid "Sync Certificate Success"
+msgstr "Gia hạn chứng chỉ SSL thành công"
+
+#: src/views/certificate/CertificateEditor.vue:191
+msgid "Sync to"
+msgstr ""
+
 #: src/routes/index.ts:248
 msgid "System"
 msgstr "Thông tin"
@@ -1710,11 +1762,11 @@ msgstr ""
 msgid "The filename cannot contain the following characters: %{c}"
 msgstr "Tên tệp không thể chứa các ký tự sau: %{c}"
 
-#: src/views/certificate/CertificateEditor.vue:194
+#: src/views/certificate/CertificateEditor.vue:201
 msgid "The input is not a SSL Certificate"
 msgstr ""
 
-#: src/views/certificate/CertificateEditor.vue:207
+#: src/views/certificate/CertificateEditor.vue:214
 msgid "The input is not a SSL Certificate Key"
 msgstr ""
 
@@ -1724,11 +1776,11 @@ msgid ""
 "dashes, and dots."
 msgstr ""
 
-#: src/views/certificate/CertificateEditor.vue:165
+#: src/views/certificate/CertificateEditor.vue:166
 msgid "The path exists, but the file is not a certificate"
 msgstr ""
 
-#: src/views/certificate/CertificateEditor.vue:180
+#: src/views/certificate/CertificateEditor.vue:181
 msgid "The path exists, but the file is not a private key"
 msgstr ""
 
@@ -1760,17 +1812,17 @@ msgstr ""
 msgid "The username or password is incorrect"
 msgstr "Tên người dùng hoặc mật khẩu không chính xác"
 
-#: src/views/certificate/CertificateEditor.vue:101
+#: src/views/certificate/CertificateEditor.vue:102
 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:91
+#: src/views/certificate/CertificateEditor.vue:92
 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:149
-#: src/views/certificate/CertificateEditor.vue:163
-#: src/views/certificate/CertificateEditor.vue:178
+#: src/views/certificate/CertificateEditor.vue:150
+#: src/views/certificate/CertificateEditor.vue:164
+#: src/views/certificate/CertificateEditor.vue:179
 msgid "This field is required"
 msgstr ""
 
@@ -1782,7 +1834,7 @@ msgstr "Trường này không được để trống"
 msgid "Tips"
 msgstr ""
 
-#: src/views/notification/Notification.vue:18
+#: src/views/notification/Notification.vue:19
 msgid "Title"
 msgstr "Tiêu đề"
 
@@ -1807,12 +1859,12 @@ msgid "Trash"
 msgstr ""
 
 #: src/views/certificate/Certificate.vue:37 src/views/config/config.ts:12
-#: src/views/notification/Notification.vue:12
+#: src/views/notification/Notification.vue:13
 msgid "Type"
 msgstr "Loại"
 
 #: src/views/certificate/ACMEUser.vue:53
-#: src/views/certificate/DNSCredential.vue:23 src/views/config/config.ts:27
+#: src/views/certificate/DNSCredential.vue:27 src/views/config/config.ts:27
 #: src/views/config/ConfigEdit.vue:121
 #: src/views/domain/components/RightSettings.vue:86
 #: src/views/domain/DomainList.vue:41 src/views/environment/Environment.vue:122
@@ -1831,7 +1883,7 @@ msgstr "Cập nhật thành công"
 msgid "Upgrade"
 msgstr "Cập nhật"
 
-#: src/language/constants.ts:28
+#: src/language/constants.ts:29
 #, fuzzy
 msgid "Upgraded successfully"
 msgstr "Cập nhật thành công"
@@ -1866,11 +1918,11 @@ msgid "Username (*)"
 msgstr "Username (*)"
 
 #: src/views/certificate/ACMEUser.vue:87
-#: src/views/certificate/Certificate.vue:92
+#: src/views/certificate/Certificate.vue:97
 msgid "Valid"
 msgstr "Hợp lệ"
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:477
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:491
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:103
 msgid "View"
 msgstr "Xem"
@@ -1896,7 +1948,7 @@ msgstr "Cơ bản"
 msgid "Warning"
 msgstr "Lưu ý"
 
-#: src/views/certificate/DNSCredential.vue:52
+#: src/views/certificate/DNSCredential.vue:56
 msgid ""
 "We will add one or more TXT records to the DNS records of your domain for "
 "ownership verification."

二进制
app/src/language/zh_CN/app.mo


+ 129 - 83
app/src/language/zh_CN/app.po

@@ -27,10 +27,10 @@ msgid "ACME User"
 msgstr "ACME 用户"
 
 #: src/views/certificate/ACMEUser.vue:59
-#: src/views/certificate/Certificate.vue:108
-#: src/views/certificate/DNSCredential.vue:29 src/views/config/config.ts:34
+#: src/views/certificate/Certificate.vue:113
+#: src/views/certificate/DNSCredential.vue:33 src/views/config/config.ts:34
 #: src/views/domain/DomainList.vue:47 src/views/environment/Environment.vue:129
-#: src/views/notification/Notification.vue:35
+#: src/views/notification/Notification.vue:37
 #: src/views/stream/StreamList.vue:47 src/views/user/User.vue:43
 msgid "Action"
 msgstr "操作"
@@ -95,7 +95,7 @@ msgid "Arch"
 msgstr "架构"
 
 #: src/components/Notification/Notification.vue:84
-#: src/views/notification/Notification.vue:70
+#: src/views/notification/Notification.vue:72
 msgid "Are you sure you want to clear all notifications?"
 msgstr "您确定要清除所有通知吗?"
 
@@ -103,7 +103,11 @@ msgstr "您确定要清除所有通知吗?"
 msgid "Are you sure you want to clear the record of chat?"
 msgstr "你确定你要清除聊天记录吗?"
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:509
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:551
+msgid "Are you sure you want to delete this item permanently?"
+msgstr "您确定要永久删除此项目吗?"
+
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:523
 msgid "Are you sure you want to delete this item?"
 msgstr "你确定要删除这个项目吗?"
 
@@ -111,7 +115,7 @@ msgstr "你确定要删除这个项目吗?"
 msgid "Are you sure you want to delete?"
 msgstr "您确定要删除吗?"
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:523
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:537
 msgid "Are you sure you want to recover this item?"
 msgstr "您确定要恢复这个项目吗?"
 
@@ -156,7 +160,7 @@ msgstr "成功关闭 %{name} 自动续签"
 msgid "Auto-renewal enabled for %{name}"
 msgstr "成功启用 %{name} 自动续签"
 
-#: src/views/certificate/CertificateEditor.vue:235
+#: src/views/certificate/CertificateEditor.vue:242
 #: src/views/config/Config.vue:73 src/views/config/ConfigEdit.vue:87
 #: src/views/domain/DomainEdit.vue:256 src/views/nginx_log/NginxLog.vue:168
 #: src/views/stream/StreamEdit.vue:247
@@ -183,7 +187,7 @@ msgid "Basic Mode"
 msgstr "基本模式"
 
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:54
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:445
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:459
 msgid "Batch Modify"
 msgstr "批量修改"
 
@@ -226,12 +230,12 @@ msgstr "此证书有效"
 msgid "Certificate Renewal Interval"
 msgstr "证书续期间隔"
 
-#: src/views/certificate/CertificateEditor.vue:127
+#: src/views/certificate/CertificateEditor.vue:128
 #: src/views/domain/cert/Cert.vue:33
 msgid "Certificate Status"
 msgstr "证书状态"
 
-#: src/routes/index.ts:119 src/views/certificate/Certificate.vue:124
+#: src/routes/index.ts:119 src/views/certificate/Certificate.vue:129
 msgid "Certificates"
 msgstr "证书"
 
@@ -262,12 +266,12 @@ msgstr "正在清理环境变量"
 
 #: src/components/ChatGPT/ChatGPT.vue:276
 #: src/components/Notification/Notification.vue:89
-#: src/views/notification/Notification.vue:75
+#: src/views/notification/Notification.vue:77
 msgid "Clear"
 msgstr "清空"
 
 #: src/components/Notification/Notification.vue:40
-#: src/views/notification/Notification.vue:44
+#: src/views/notification/Notification.vue:46
 msgid "Cleared successfully"
 msgstr "清除成功"
 
@@ -332,7 +336,7 @@ msgstr "创建"
 msgid "Create Another"
 msgstr "再创建一个"
 
-#: src/views/notification/Notification.vue:29 src/views/user/User.vue:31
+#: src/views/notification/Notification.vue:31 src/views/user/User.vue:31
 msgid "Created at"
 msgstr "创建时间"
 
@@ -375,7 +379,7 @@ msgstr "数据库 (可选,默认: database)"
 msgid "Days"
 msgstr "天"
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:516
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:530
 #: src/views/domain/DomainList.vue:155
 #: src/views/domain/ngx_conf/NgxServer.vue:114
 #: src/views/domain/ngx_conf/NgxUpstream.vue:126
@@ -383,6 +387,10 @@ msgstr "天"
 msgid "Delete"
 msgstr "删除"
 
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:558
+msgid "Delete Permanently"
+msgstr "彻底删除"
+
 #: src/views/domain/DomainList.vue:78
 msgid "Delete site: %{site_name}"
 msgstr "删除站点: %{site_name}"
@@ -391,7 +399,7 @@ msgstr "删除站点: %{site_name}"
 msgid "Delete stream: %{stream_name}"
 msgstr "删除 Stream: %{stream_name}"
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:171
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:185
 msgid "Deleted successfully"
 msgstr "删除成功"
 
@@ -422,7 +430,7 @@ msgstr "部署成功"
 msgid "Description"
 msgstr "描述"
 
-#: src/views/notification/Notification.vue:25
+#: src/views/notification/Notification.vue:26
 msgid "Details"
 msgstr "详情"
 
@@ -467,7 +475,7 @@ msgstr "禁用成功"
 msgid "Disk IO"
 msgstr "磁盘 IO"
 
-#: src/routes/index.ts:162 src/views/certificate/DNSCredential.vue:36
+#: src/routes/index.ts:162 src/views/certificate/DNSCredential.vue:40
 msgid "DNS Credentials"
 msgstr "DNS 凭证"
 
@@ -526,15 +534,15 @@ msgstr "域名"
 msgid "Domain Config Created Successfully"
 msgstr "域名配置文件创建成功"
 
-#: src/views/certificate/CertificateEditor.vue:111
+#: src/views/certificate/CertificateEditor.vue:112
 msgid "Domains list is empty, try to reopen Auto Cert for %{config}"
 msgstr "域名列表为空,请尝试为 %{config} 重新打开证书自动续期。"
 
-#: src/language/constants.ts:25
+#: src/language/constants.ts:26
 msgid "Download latest release error"
 msgstr "下载最新版本错误"
 
-#: src/language/constants.ts:24
+#: src/language/constants.ts:25
 msgid "Downloading latest release"
 msgstr "下载最新版本"
 
@@ -654,6 +662,10 @@ msgstr "用 Let's Encrypt 对网站进行加密"
 msgid "Environment"
 msgstr "环境"
 
+#: src/language/constants.ts:21
+msgid "Environment variables cleaned"
+msgstr "环境变量已清理"
+
 #: src/views/dashboard/Environments.vue:82
 msgid "Environments"
 msgstr "环境"
@@ -670,7 +682,7 @@ msgstr "错误日志"
 msgid "Executable Path"
 msgstr "可执行文件路径"
 
-#: src/views/certificate/Certificate.vue:96
+#: src/views/certificate/Certificate.vue:101
 msgid "Expired"
 msgstr "已过期"
 
@@ -712,7 +724,7 @@ msgstr "保存失败,在配置中检测到语法错误。"
 msgid "File"
 msgstr "文件"
 
-#: src/language/constants.ts:30
+#: src/language/constants.ts:31
 msgid "File exists"
 msgstr "文件已存在"
 
@@ -749,7 +761,7 @@ msgstr "保存错误 %{msg}"
 msgid "Format successfully"
 msgstr "格式化成功"
 
-#: src/views/certificate/Certificate.vue:43
+#: src/views/certificate/Certificate.vue:44
 msgid "General Certificate"
 msgstr "普通证书"
 
@@ -797,11 +809,11 @@ msgstr "HTTP01"
 msgid "If left blank, the default CA Dir will be used."
 msgstr "如果留空,则使用默认 CA Dir。"
 
-#: src/views/certificate/Certificate.vue:131
+#: src/views/certificate/Certificate.vue:136
 msgid "Import"
 msgstr "导入"
 
-#: src/routes/index.ts:153 src/views/certificate/CertificateEditor.vue:84
+#: src/routes/index.ts:153 src/views/certificate/CertificateEditor.vue:85
 msgid "Import Certificate"
 msgstr "导入证书"
 
@@ -809,11 +821,11 @@ msgstr "导入证书"
 msgid "Info"
 msgstr "信息"
 
-#: src/language/constants.ts:23
+#: src/language/constants.ts:24
 msgid "Initial core upgrader error"
 msgstr "初始化核心升级程序错误"
 
-#: src/language/constants.ts:22
+#: src/language/constants.ts:23
 msgid "Initialing core upgrader"
 msgstr "初始化核心升级器"
 
@@ -837,7 +849,7 @@ msgstr "间隔"
 msgid "Invalid"
 msgstr "无效的"
 
-#: src/views/certificate/Certificate.vue:139
+#: src/views/certificate/Certificate.vue:144
 msgid "Issue wildcard certificate"
 msgstr "签发通配符证书"
 
@@ -853,7 +865,7 @@ msgstr "证书申请成功"
 msgid "Jwt Secret"
 msgstr "Jwt 密钥"
 
-#: src/views/certificate/Certificate.vue:61
+#: src/views/certificate/Certificate.vue:66
 #: src/views/certificate/WildcardCertificate.vue:79
 #: src/views/domain/cert/components/AutoCertStepOne.vue:80
 msgid "Key Type"
@@ -871,8 +883,8 @@ msgstr "留空表示不修改"
 msgid "Leave blank for the default: https://api.openai.com/"
 msgstr "留空为默认:https://api.openai.com/"
 
-#: src/views/certificate/CertificateEditor.vue:200
-#: src/views/certificate/CertificateEditor.vue:213
+#: src/views/certificate/CertificateEditor.vue:207
+#: src/views/certificate/CertificateEditor.vue:220
 msgid "Leave blank will not change anything"
 msgstr "留空不做任何更改"
 
@@ -914,7 +926,7 @@ msgstr "Location"
 msgid "Locations"
 msgstr "Locations"
 
-#: src/views/certificate/CertificateEditor.vue:223
+#: src/views/certificate/CertificateEditor.vue:230
 msgid "Log"
 msgstr "日志"
 
@@ -972,7 +984,7 @@ msgstr "管理 Stream"
 msgid "Manage Users"
 msgstr "用户管理"
 
-#: src/views/certificate/Certificate.vue:42
+#: src/views/certificate/Certificate.vue:43
 msgid "Managed Certificate"
 msgstr "托管证书"
 
@@ -995,11 +1007,11 @@ msgstr "模型"
 
 #: src/components/ChatGPT/ChatGPT.vue:249
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:194
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:491
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:505
 msgid "Modify"
 msgstr "修改"
 
-#: src/routes/index.ts:144 src/views/certificate/CertificateEditor.vue:84
+#: src/routes/index.ts:144 src/views/certificate/CertificateEditor.vue:85
 msgid "Modify Certificate"
 msgstr "修改证书"
 
@@ -1017,8 +1029,8 @@ msgstr "多行指令"
 
 #: src/views/certificate/ACMEUser.vue:13
 #: src/views/certificate/Certificate.vue:20
-#: src/views/certificate/CertificateEditor.vue:146
-#: src/views/certificate/DNSCredential.vue:10 src/views/config/config.ts:7
+#: src/views/certificate/CertificateEditor.vue:147
+#: src/views/certificate/DNSCredential.vue:11 src/views/config/config.ts:7
 #: src/views/domain/cert/ChangeCert.vue:17
 #: src/views/domain/components/RightSettings.vue:83
 #: src/views/domain/components/SiteDuplicate.vue:133
@@ -1091,12 +1103,13 @@ msgstr "Nginx 重启成功"
 
 #: src/components/ChatGPT/ChatGPT.vue:270
 #: src/components/Notification/Notification.vue:82
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:507
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:521
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:535
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:549
 #: src/views/domain/DomainList.vue:144
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:90
 #: src/views/domain/ngx_conf/LocationEditor.vue:71
-#: src/views/notification/Notification.vue:68
+#: src/views/notification/Notification.vue:70
 #: src/views/preference/BasicSettings.vue:101
 #: src/views/stream/StreamList.vue:165
 msgid "No"
@@ -1106,7 +1119,7 @@ msgstr "取消"
 msgid "Node Secret"
 msgstr "节点密钥"
 
-#: src/views/certificate/Certificate.vue:102
+#: src/views/certificate/Certificate.vue:107
 msgid "Not After"
 msgstr "有效期"
 
@@ -1118,12 +1131,12 @@ msgstr "找不到页面"
 msgid "Not Valid Before: %{date}"
 msgstr "此前无效: %{date}"
 
-#: src/views/certificate/DNSCredential.vue:45
+#: src/views/certificate/DNSCredential.vue:49
 #: src/views/domain/cert/components/AutoCertStepOne.vue:45
 msgid "Note"
 msgstr "注意"
 
-#: src/views/notification/Notification.vue:60
+#: src/views/notification/Notification.vue:62
 msgid "Notification"
 msgstr "通知"
 
@@ -1153,8 +1166,9 @@ msgstr "确定"
 #: src/components/ChatGPT/ChatGPT.vue:271
 #: src/components/Notification/Notification.vue:83
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:56
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:508
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:522
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:536
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:550
 #: src/views/domain/cert/components/ObtainCert.vue:136
 #: src/views/domain/components/Deploy.vue:20
 #: src/views/domain/components/RightSettings.vue:50
@@ -1162,14 +1176,14 @@ msgstr "确定"
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:49
 #: src/views/domain/ngx_conf/NgxServer.vue:83
 #: src/views/domain/ngx_conf/NgxUpstream.vue:32
-#: src/views/notification/Notification.vue:69
+#: src/views/notification/Notification.vue:71
 #: src/views/stream/components/Deploy.vue:20
 #: src/views/stream/components/RightSettings.vue:50
 #: src/views/stream/StreamList.vue:166
 msgid "OK"
 msgstr "确定"
 
-#: src/views/certificate/DNSCredential.vue:55
+#: src/views/certificate/DNSCredential.vue:59
 msgid "Once the verification is complete, the records will be removed."
 msgstr "一旦验证完成,这些记录将被删除。"
 
@@ -1220,15 +1234,15 @@ msgstr "密码 (*)"
 msgid "Path"
 msgstr "路径"
 
-#: src/language/constants.ts:27
+#: src/language/constants.ts:28
 msgid "Perform core upgrade error"
 msgstr "执行核心升级错误"
 
-#: src/language/constants.ts:26
+#: src/language/constants.ts:27
 msgid "Performing core upgrade"
 msgstr "正在进行核心升级"
 
-#: src/views/certificate/DNSCredential.vue:49
+#: src/views/certificate/DNSCredential.vue:53
 msgid ""
 "Please fill in the API authentication credentials provided by your DNS "
 "provider."
@@ -1261,7 +1275,7 @@ msgstr "请输入您的密码!"
 msgid "Please input your username!"
 msgstr "请输入您的用户名!"
 
-#: src/views/certificate/DNSCredential.vue:58
+#: src/views/certificate/DNSCredential.vue:62
 msgid ""
 "Please note that the unit of time configurations below are all in seconds."
 msgstr "请注意,下面的时间单位配置均以秒为单位。"
@@ -1295,7 +1309,7 @@ msgstr "禁止删除默认用户"
 msgid "Project Team"
 msgstr "项目团队"
 
-#: src/views/certificate/DNSCredential.vue:18
+#: src/views/certificate/DNSCredential.vue:19
 msgid "Provider"
 msgstr "提供商"
 
@@ -1309,11 +1323,11 @@ msgstr "读"
 msgid "Receive"
 msgstr "下载"
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:530
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:544
 msgid "Recover"
 msgstr "恢复"
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:179
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:193
 msgid "Recovered Successfully"
 msgstr "恢复成功"
 
@@ -1379,11 +1393,11 @@ msgstr "重命名"
 msgid "Renew Certificate"
 msgstr "更新证书"
 
-#: src/language/constants.ts:34
+#: src/language/constants.ts:35
 msgid "Renew Certificate Error"
 msgstr "证书续期错误"
 
-#: src/language/constants.ts:33
+#: src/language/constants.ts:34
 msgid "Renew Certificate Success"
 msgstr "证书续期成功"
 
@@ -1392,11 +1406,11 @@ msgstr "证书续期成功"
 msgid "Renew successfully"
 msgstr "更新成功"
 
-#: src/language/constants.ts:31
+#: src/language/constants.ts:32
 msgid "Requested with wrong parameters"
 msgstr "请求参数错误"
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:439
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:453
 msgid "Reset"
 msgstr "重置"
 
@@ -1417,7 +1431,7 @@ msgid "Running"
 msgstr "运行中"
 
 #: src/components/ChatGPT/ChatGPT.vue:252
-#: src/views/certificate/CertificateEditor.vue:242
+#: src/views/certificate/CertificateEditor.vue:249
 #: src/views/config/ConfigEdit.vue:96 src/views/domain/DomainEdit.vue:263
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:120
 #: src/views/preference/Preference.vue:130 src/views/stream/StreamEdit.vue:254
@@ -1435,7 +1449,7 @@ msgstr "保存错误 %{msg}"
 
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:39
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:104
-#: src/views/certificate/CertificateEditor.vue:45
+#: src/views/certificate/CertificateEditor.vue:46
 #: src/views/preference/Preference.vue:66
 msgid "Save successfully"
 msgstr "保存成功"
@@ -1465,8 +1479,8 @@ msgstr "上传"
 #: src/components/StdDesign/StdDataDisplay/methods/exportCsv.ts:46
 #: src/components/StdDesign/StdDataDisplay/methods/sortable.ts:126
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:42
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:182
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:221
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:196
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:235
 #: src/views/config/ConfigEdit.vue:40 src/views/domain/DomainList.vue:81
 #: src/views/environment/Environment.vue:139 src/views/other/Install.vue:70
 #: src/views/preference/Preference.vue:70 src/views/stream/StreamList.vue:113
@@ -1521,21 +1535,21 @@ msgstr "站点列表"
 msgid "Sites List"
 msgstr "站点列表"
 
-#: src/views/certificate/CertificateEditor.vue:191
+#: src/views/certificate/CertificateEditor.vue:198
 msgid "SSL Certificate Content"
 msgstr "SSL 证书内容"
 
-#: src/views/certificate/CertificateEditor.vue:204
+#: src/views/certificate/CertificateEditor.vue:211
 msgid "SSL Certificate Key Content"
 msgstr "SSL 证书密钥内容"
 
-#: src/views/certificate/Certificate.vue:75
-#: src/views/certificate/CertificateEditor.vue:176
+#: src/views/certificate/Certificate.vue:80
+#: src/views/certificate/CertificateEditor.vue:177
 msgid "SSL Certificate Key Path"
 msgstr "SSL证书密钥路径"
 
-#: src/views/certificate/Certificate.vue:67
-#: src/views/certificate/CertificateEditor.vue:161
+#: src/views/certificate/Certificate.vue:72
+#: src/views/certificate/CertificateEditor.vue:162
 msgid "SSL Certificate Path"
 msgstr "SSL证书路径"
 
@@ -1548,7 +1562,7 @@ msgid "Stable"
 msgstr "稳定"
 
 #: src/views/certificate/ACMEUser.vue:42
-#: src/views/certificate/Certificate.vue:83 src/views/domain/DomainList.vue:22
+#: src/views/certificate/Certificate.vue:88 src/views/domain/DomainList.vue:22
 #: src/views/environment/Environment.vue:76 src/views/stream/StreamList.vue:22
 msgid "Status"
 msgstr "状态"
@@ -1583,6 +1597,38 @@ msgstr "切换到深色主题"
 msgid "Switch to light theme"
 msgstr "切换到浅色"
 
+#: src/views/certificate/Certificate.vue:42
+msgid "Sync Certificate"
+msgstr "同步证书"
+
+#: src/components/Notification/detailRender.ts:25
+msgid ""
+"Sync Certificate %{cert_name} to %{env_name} failed, please upgrade the "
+"remote Nginx UI to the latest version"
+msgstr ""
+"同步证书 %{cert_name} 到 %{env_name} 失败,请先将远程的 Nginx UI 升级到最新版"
+"本"
+
+#: src/components/Notification/detailRender.ts:29
+msgid "Sync Certificate %{cert_name} to %{env_name} failed, response: %{resp}"
+msgstr "同步证书 %{cert_name} 到 %{env_name} 失败,响应:%{resp}"
+
+#: src/components/Notification/detailRender.ts:17
+msgid "Sync Certificate %{cert_name} to %{env_name} successfully"
+msgstr "证书 %{cert_name} 已成功同步到 %{env_name}"
+
+#: src/language/constants.ts:38
+msgid "Sync Certificate Error"
+msgstr "同步证书错误"
+
+#: src/language/constants.ts:37
+msgid "Sync Certificate Success"
+msgstr "同步证书成功"
+
+#: src/views/certificate/CertificateEditor.vue:191
+msgid "Sync to"
+msgstr "同步到"
+
 #: src/routes/index.ts:248
 msgid "System"
 msgstr "系统"
@@ -1612,11 +1658,11 @@ msgstr ""
 msgid "The filename cannot contain the following characters: %{c}"
 msgstr "文件名不能包含以下字符: %{c}"
 
-#: src/views/certificate/CertificateEditor.vue:194
+#: src/views/certificate/CertificateEditor.vue:201
 msgid "The input is not a SSL Certificate"
 msgstr "输入的内容不是 SSL 证书"
 
-#: src/views/certificate/CertificateEditor.vue:207
+#: src/views/certificate/CertificateEditor.vue:214
 msgid "The input is not a SSL Certificate Key"
 msgstr "输入的内容不是 SSL 证书密钥"
 
@@ -1626,11 +1672,11 @@ msgid ""
 "dashes, and dots."
 msgstr "模型只能包含字母、Unicode、数字、连词符、破折号和点。"
 
-#: src/views/certificate/CertificateEditor.vue:165
+#: src/views/certificate/CertificateEditor.vue:166
 msgid "The path exists, but the file is not a certificate"
 msgstr "路径存在,但文件不是证书"
 
-#: src/views/certificate/CertificateEditor.vue:180
+#: src/views/certificate/CertificateEditor.vue:181
 msgid "The path exists, but the file is not a private key"
 msgstr "路径存在,但文件不是私钥"
 
@@ -1660,17 +1706,17 @@ msgstr "URL 无效."
 msgid "The username or password is incorrect"
 msgstr "用户名或密码错误"
 
-#: src/views/certificate/CertificateEditor.vue:101
+#: src/views/certificate/CertificateEditor.vue:102
 msgid "This Auto Cert item is invalid, please remove it."
 msgstr "这个证书自动续期项目是无效的,请删除。"
 
-#: src/views/certificate/CertificateEditor.vue:91
+#: src/views/certificate/CertificateEditor.vue:92
 msgid "This certificate is managed by Nginx UI"
 msgstr "该证书由 Nginx UI 托管"
 
-#: src/views/certificate/CertificateEditor.vue:149
-#: src/views/certificate/CertificateEditor.vue:163
-#: src/views/certificate/CertificateEditor.vue:178
+#: src/views/certificate/CertificateEditor.vue:150
+#: src/views/certificate/CertificateEditor.vue:164
+#: src/views/certificate/CertificateEditor.vue:179
 msgid "This field is required"
 msgstr "此字段必填"
 
@@ -1682,7 +1728,7 @@ msgstr "该字段不能为空"
 msgid "Tips"
 msgstr "提示"
 
-#: src/views/notification/Notification.vue:18
+#: src/views/notification/Notification.vue:19
 msgid "Title"
 msgstr "标题"
 
@@ -1705,12 +1751,12 @@ msgid "Trash"
 msgstr "回收站"
 
 #: src/views/certificate/Certificate.vue:37 src/views/config/config.ts:12
-#: src/views/notification/Notification.vue:12
+#: src/views/notification/Notification.vue:13
 msgid "Type"
 msgstr "类型"
 
 #: src/views/certificate/ACMEUser.vue:53
-#: src/views/certificate/DNSCredential.vue:23 src/views/config/config.ts:27
+#: src/views/certificate/DNSCredential.vue:27 src/views/config/config.ts:27
 #: src/views/config/ConfigEdit.vue:121
 #: src/views/domain/components/RightSettings.vue:86
 #: src/views/domain/DomainList.vue:41 src/views/environment/Environment.vue:122
@@ -1728,7 +1774,7 @@ msgstr "更新成功"
 msgid "Upgrade"
 msgstr "升级"
 
-#: src/language/constants.ts:28
+#: src/language/constants.ts:29
 msgid "Upgraded successfully"
 msgstr "升级成功"
 
@@ -1761,11 +1807,11 @@ msgid "Username (*)"
 msgstr "用户名 (*)"
 
 #: src/views/certificate/ACMEUser.vue:87
-#: src/views/certificate/Certificate.vue:92
+#: src/views/certificate/Certificate.vue:97
 msgid "Valid"
 msgstr "有效的"
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:477
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:491
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:103
 msgid "View"
 msgstr "查看"
@@ -1788,7 +1834,7 @@ msgstr "预览模式"
 msgid "Warning"
 msgstr "警告"
 
-#: src/views/certificate/DNSCredential.vue:52
+#: src/views/certificate/DNSCredential.vue:56
 msgid ""
 "We will add one or more TXT records to the DNS records of your domain for "
 "ownership verification."

+ 135 - 83
app/src/language/zh_TW/app.po

@@ -29,10 +29,10 @@ msgid "ACME User"
 msgstr "使用者名稱"
 
 #: src/views/certificate/ACMEUser.vue:59
-#: src/views/certificate/Certificate.vue:108
-#: src/views/certificate/DNSCredential.vue:29 src/views/config/config.ts:34
+#: src/views/certificate/Certificate.vue:113
+#: src/views/certificate/DNSCredential.vue:33 src/views/config/config.ts:34
 #: src/views/domain/DomainList.vue:47 src/views/environment/Environment.vue:129
-#: src/views/notification/Notification.vue:35
+#: src/views/notification/Notification.vue:37
 #: src/views/stream/StreamList.vue:47 src/views/user/User.vue:43
 msgid "Action"
 msgstr "操作"
@@ -100,7 +100,7 @@ msgid "Arch"
 msgstr "架構"
 
 #: src/components/Notification/Notification.vue:84
-#: src/views/notification/Notification.vue:70
+#: src/views/notification/Notification.vue:72
 #, fuzzy
 msgid "Are you sure you want to clear all notifications?"
 msgstr "您確定要清除聊天記錄嗎?"
@@ -109,7 +109,12 @@ msgstr "您確定要清除聊天記錄嗎?"
 msgid "Are you sure you want to clear the record of chat?"
 msgstr "您確定要清除聊天記錄嗎?"
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:509
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:551
+#, fuzzy
+msgid "Are you sure you want to delete this item permanently?"
+msgstr "您確定要刪除嗎?"
+
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:523
 #, fuzzy
 msgid "Are you sure you want to delete this item?"
 msgstr "您確定要刪除嗎?"
@@ -118,7 +123,7 @@ msgstr "您確定要刪除嗎?"
 msgid "Are you sure you want to delete?"
 msgstr "您確定要刪除嗎?"
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:523
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:537
 #, fuzzy
 msgid "Are you sure you want to recover this item?"
 msgstr "您確定要刪除這條指令嗎?"
@@ -165,7 +170,7 @@ msgstr "已關閉 %{name} 的自動續簽"
 msgid "Auto-renewal enabled for %{name}"
 msgstr "已啟用 %{name} 的自動續簽"
 
-#: src/views/certificate/CertificateEditor.vue:235
+#: src/views/certificate/CertificateEditor.vue:242
 #: src/views/config/Config.vue:73 src/views/config/ConfigEdit.vue:87
 #: src/views/domain/DomainEdit.vue:256 src/views/nginx_log/NginxLog.vue:168
 #: src/views/stream/StreamEdit.vue:247
@@ -192,7 +197,7 @@ msgid "Basic Mode"
 msgstr "基本模式"
 
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:54
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:445
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:459
 msgid "Batch Modify"
 msgstr "批次修改"
 
@@ -236,12 +241,12 @@ msgstr "此憑證有效"
 msgid "Certificate Renewal Interval"
 msgstr "憑證自動續簽"
 
-#: src/views/certificate/CertificateEditor.vue:127
+#: src/views/certificate/CertificateEditor.vue:128
 #: src/views/domain/cert/Cert.vue:33
 msgid "Certificate Status"
 msgstr "憑證狀態"
 
-#: src/routes/index.ts:119 src/views/certificate/Certificate.vue:124
+#: src/routes/index.ts:119 src/views/certificate/Certificate.vue:129
 #, fuzzy
 msgid "Certificates"
 msgstr "憑證狀態"
@@ -274,12 +279,12 @@ msgstr "清理環境變數"
 
 #: src/components/ChatGPT/ChatGPT.vue:276
 #: src/components/Notification/Notification.vue:89
-#: src/views/notification/Notification.vue:75
+#: src/views/notification/Notification.vue:77
 msgid "Clear"
 msgstr "清除"
 
 #: src/components/Notification/Notification.vue:40
-#: src/views/notification/Notification.vue:44
+#: src/views/notification/Notification.vue:46
 #, fuzzy
 msgid "Cleared successfully"
 msgstr "成功停用"
@@ -347,7 +352,7 @@ msgstr "建立時間"
 msgid "Create Another"
 msgstr "再建立一個"
 
-#: src/views/notification/Notification.vue:29 src/views/user/User.vue:31
+#: src/views/notification/Notification.vue:31 src/views/user/User.vue:31
 msgid "Created at"
 msgstr "建立時間"
 
@@ -390,7 +395,7 @@ msgstr "資料庫 (可選,預設: database)"
 msgid "Days"
 msgstr ""
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:516
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:530
 #: src/views/domain/DomainList.vue:155
 #: src/views/domain/ngx_conf/NgxServer.vue:114
 #: src/views/domain/ngx_conf/NgxUpstream.vue:126
@@ -398,6 +403,10 @@ msgstr ""
 msgid "Delete"
 msgstr "刪除"
 
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:558
+msgid "Delete Permanently"
+msgstr ""
+
 #: src/views/domain/DomainList.vue:78
 msgid "Delete site: %{site_name}"
 msgstr "刪除網站:%{site_name}"
@@ -407,7 +416,7 @@ msgstr "刪除網站:%{site_name}"
 msgid "Delete stream: %{stream_name}"
 msgstr "刪除網站:%{site_name}"
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:171
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:185
 #, fuzzy
 msgid "Deleted successfully"
 msgstr "成功停用"
@@ -439,7 +448,7 @@ msgstr "部署成功"
 msgid "Description"
 msgstr "描述"
 
-#: src/views/notification/Notification.vue:25
+#: src/views/notification/Notification.vue:26
 msgid "Details"
 msgstr ""
 
@@ -485,7 +494,7 @@ msgstr "成功停用"
 msgid "Disk IO"
 msgstr "磁碟 IO"
 
-#: src/routes/index.ts:162 src/views/certificate/DNSCredential.vue:36
+#: src/routes/index.ts:162 src/views/certificate/DNSCredential.vue:40
 msgid "DNS Credentials"
 msgstr "DNS 認證"
 
@@ -547,16 +556,16 @@ msgstr "網域"
 msgid "Domain Config Created Successfully"
 msgstr "網域設定檔成功建立"
 
-#: src/views/certificate/CertificateEditor.vue:111
+#: src/views/certificate/CertificateEditor.vue:112
 #, fuzzy
 msgid "Domains list is empty, try to reopen Auto Cert for %{config}"
 msgstr "網域列表為空,請嘗試重新開啟 %{config} 的自動憑證"
 
-#: src/language/constants.ts:25
+#: src/language/constants.ts:26
 msgid "Download latest release error"
 msgstr "下載最新版本錯誤"
 
-#: src/language/constants.ts:24
+#: src/language/constants.ts:25
 msgid "Downloading latest release"
 msgstr "正在下載最新版本"
 
@@ -678,6 +687,11 @@ msgstr "用 Let's Encrypt 對網站進行加密"
 msgid "Environment"
 msgstr "環境"
 
+#: src/language/constants.ts:21
+#, fuzzy
+msgid "Environment variables cleaned"
+msgstr "設定環境變數中"
+
 #: src/views/dashboard/Environments.vue:82
 msgid "Environments"
 msgstr "環境"
@@ -694,7 +708,7 @@ msgstr "錯誤日誌"
 msgid "Executable Path"
 msgstr "可執行檔路徑"
 
-#: src/views/certificate/Certificate.vue:96
+#: src/views/certificate/Certificate.vue:101
 msgid "Expired"
 msgstr ""
 
@@ -738,7 +752,7 @@ msgstr "儲存失敗,在設定中檢測到語法錯誤。"
 msgid "File"
 msgstr "檔案"
 
-#: src/language/constants.ts:30
+#: src/language/constants.ts:31
 msgid "File exists"
 msgstr "檔案已存在"
 
@@ -777,7 +791,7 @@ msgstr "格式錯誤 %{msg}"
 msgid "Format successfully"
 msgstr "成功格式化"
 
-#: src/views/certificate/Certificate.vue:43
+#: src/views/certificate/Certificate.vue:44
 #, fuzzy
 msgid "General Certificate"
 msgstr "更換憑證"
@@ -826,12 +840,12 @@ msgstr "HTTP01"
 msgid "If left blank, the default CA Dir will be used."
 msgstr ""
 
-#: src/views/certificate/Certificate.vue:131
+#: src/views/certificate/Certificate.vue:136
 #, fuzzy
 msgid "Import"
 msgstr "匯出"
 
-#: src/routes/index.ts:153 src/views/certificate/CertificateEditor.vue:84
+#: src/routes/index.ts:153 src/views/certificate/CertificateEditor.vue:85
 #, fuzzy
 msgid "Import Certificate"
 msgstr "憑證狀態"
@@ -840,11 +854,11 @@ msgstr "憑證狀態"
 msgid "Info"
 msgstr ""
 
-#: src/language/constants.ts:23
+#: src/language/constants.ts:24
 msgid "Initial core upgrader error"
 msgstr "初始化核心升級程式錯誤"
 
-#: src/language/constants.ts:22
+#: src/language/constants.ts:23
 msgid "Initialing core upgrader"
 msgstr "正在初始化核心升級程式"
 
@@ -869,7 +883,7 @@ msgstr ""
 msgid "Invalid"
 msgstr "無效的郵箱!"
 
-#: src/views/certificate/Certificate.vue:139
+#: src/views/certificate/Certificate.vue:144
 #, fuzzy
 msgid "Issue wildcard certificate"
 msgstr "取得憑證"
@@ -887,7 +901,7 @@ msgstr "成功頒發憑證"
 msgid "Jwt Secret"
 msgstr "Jwt Secret"
 
-#: src/views/certificate/Certificate.vue:61
+#: src/views/certificate/Certificate.vue:66
 #: src/views/certificate/WildcardCertificate.vue:79
 #: src/views/domain/cert/components/AutoCertStepOne.vue:80
 #, fuzzy
@@ -906,8 +920,8 @@ msgstr "留空表示不修改"
 msgid "Leave blank for the default: https://api.openai.com/"
 msgstr "預設留空:https://api.openai.com/"
 
-#: src/views/certificate/CertificateEditor.vue:200
-#: src/views/certificate/CertificateEditor.vue:213
+#: src/views/certificate/CertificateEditor.vue:207
+#: src/views/certificate/CertificateEditor.vue:220
 #, fuzzy
 msgid "Leave blank will not change anything"
 msgstr "留空表示不修改"
@@ -953,7 +967,7 @@ msgstr "Location"
 msgid "Locations"
 msgstr "Locations"
 
-#: src/views/certificate/CertificateEditor.vue:223
+#: src/views/certificate/CertificateEditor.vue:230
 #, fuzzy
 msgid "Log"
 msgstr "登入"
@@ -1008,7 +1022,7 @@ msgstr "管理網站"
 msgid "Manage Users"
 msgstr "管理使用者"
 
-#: src/views/certificate/Certificate.vue:42
+#: src/views/certificate/Certificate.vue:43
 #, fuzzy
 msgid "Managed Certificate"
 msgstr "更換憑證"
@@ -1033,11 +1047,11 @@ msgstr "執行模式"
 
 #: src/components/ChatGPT/ChatGPT.vue:249
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:194
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:491
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:505
 msgid "Modify"
 msgstr "修改"
 
-#: src/routes/index.ts:144 src/views/certificate/CertificateEditor.vue:84
+#: src/routes/index.ts:144 src/views/certificate/CertificateEditor.vue:85
 #, fuzzy
 msgid "Modify Certificate"
 msgstr "憑證狀態"
@@ -1057,8 +1071,8 @@ msgstr "多行指令"
 
 #: src/views/certificate/ACMEUser.vue:13
 #: src/views/certificate/Certificate.vue:20
-#: src/views/certificate/CertificateEditor.vue:146
-#: src/views/certificate/DNSCredential.vue:10 src/views/config/config.ts:7
+#: src/views/certificate/CertificateEditor.vue:147
+#: src/views/certificate/DNSCredential.vue:11 src/views/config/config.ts:7
 #: src/views/domain/cert/ChangeCert.vue:17
 #: src/views/domain/components/RightSettings.vue:83
 #: src/views/domain/components/SiteDuplicate.vue:133
@@ -1131,12 +1145,13 @@ msgstr "Nginx 重啟成功"
 
 #: src/components/ChatGPT/ChatGPT.vue:270
 #: src/components/Notification/Notification.vue:82
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:507
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:521
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:535
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:549
 #: src/views/domain/DomainList.vue:144
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:90
 #: src/views/domain/ngx_conf/LocationEditor.vue:71
-#: src/views/notification/Notification.vue:68
+#: src/views/notification/Notification.vue:70
 #: src/views/preference/BasicSettings.vue:101
 #: src/views/stream/StreamList.vue:165
 msgid "No"
@@ -1146,7 +1161,7 @@ msgstr "取消"
 msgid "Node Secret"
 msgstr "Node Secret"
 
-#: src/views/certificate/Certificate.vue:102
+#: src/views/certificate/Certificate.vue:107
 msgid "Not After"
 msgstr ""
 
@@ -1158,12 +1173,12 @@ msgstr "找不到頁面"
 msgid "Not Valid Before: %{date}"
 msgstr "此前無效: %{date}"
 
-#: src/views/certificate/DNSCredential.vue:45
+#: src/views/certificate/DNSCredential.vue:49
 #: src/views/domain/cert/components/AutoCertStepOne.vue:45
 msgid "Note"
 msgstr "備註"
 
-#: src/views/notification/Notification.vue:60
+#: src/views/notification/Notification.vue:62
 #, fuzzy
 msgid "Notification"
 msgstr "憑證"
@@ -1195,8 +1210,9 @@ msgstr ""
 #: src/components/ChatGPT/ChatGPT.vue:271
 #: src/components/Notification/Notification.vue:83
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:56
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:508
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:522
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:536
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:550
 #: src/views/domain/cert/components/ObtainCert.vue:136
 #: src/views/domain/components/Deploy.vue:20
 #: src/views/domain/components/RightSettings.vue:50
@@ -1204,14 +1220,14 @@ msgstr ""
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:49
 #: src/views/domain/ngx_conf/NgxServer.vue:83
 #: src/views/domain/ngx_conf/NgxUpstream.vue:32
-#: src/views/notification/Notification.vue:69
+#: src/views/notification/Notification.vue:71
 #: src/views/stream/components/Deploy.vue:20
 #: src/views/stream/components/RightSettings.vue:50
 #: src/views/stream/StreamList.vue:166
 msgid "OK"
 msgstr "確定"
 
-#: src/views/certificate/DNSCredential.vue:55
+#: src/views/certificate/DNSCredential.vue:59
 msgid "Once the verification is complete, the records will be removed."
 msgstr ""
 
@@ -1262,15 +1278,15 @@ msgstr "密碼 (*)"
 msgid "Path"
 msgstr "路徑"
 
-#: src/language/constants.ts:27
+#: src/language/constants.ts:28
 msgid "Perform core upgrade error"
 msgstr "執行核心升級錯誤"
 
-#: src/language/constants.ts:26
+#: src/language/constants.ts:27
 msgid "Performing core upgrade"
 msgstr "正在執行核心升級"
 
-#: src/views/certificate/DNSCredential.vue:49
+#: src/views/certificate/DNSCredential.vue:53
 msgid ""
 "Please fill in the API authentication credentials provided by your DNS "
 "provider."
@@ -1304,7 +1320,7 @@ msgstr "請輸入您的密碼!"
 msgid "Please input your username!"
 msgstr "請輸入您的使用者名稱!"
 
-#: src/views/certificate/DNSCredential.vue:58
+#: src/views/certificate/DNSCredential.vue:62
 msgid ""
 "Please note that the unit of time configurations below are all in seconds."
 msgstr ""
@@ -1338,7 +1354,7 @@ msgstr "禁止刪除預設使用者"
 msgid "Project Team"
 msgstr "專案團隊"
 
-#: src/views/certificate/DNSCredential.vue:18
+#: src/views/certificate/DNSCredential.vue:19
 msgid "Provider"
 msgstr "供應商"
 
@@ -1352,11 +1368,11 @@ msgstr "讀取"
 msgid "Receive"
 msgstr "接收"
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:530
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:544
 msgid "Recover"
 msgstr ""
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:179
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:193
 #, fuzzy
 msgid "Recovered Successfully"
 msgstr "儲存成功"
@@ -1431,12 +1447,12 @@ msgstr "使用者名稱"
 msgid "Renew Certificate"
 msgstr "更換憑證"
 
-#: src/language/constants.ts:34
+#: src/language/constants.ts:35
 #, fuzzy
 msgid "Renew Certificate Error"
 msgstr "更換憑證"
 
-#: src/language/constants.ts:33
+#: src/language/constants.ts:34
 #, fuzzy
 msgid "Renew Certificate Success"
 msgstr "更換憑證"
@@ -1447,11 +1463,11 @@ msgstr "更換憑證"
 msgid "Renew successfully"
 msgstr "啟用成功"
 
-#: src/language/constants.ts:31
+#: src/language/constants.ts:32
 msgid "Requested with wrong parameters"
 msgstr "請求參數錯誤"
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:439
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:453
 msgid "Reset"
 msgstr "重設"
 
@@ -1472,7 +1488,7 @@ msgid "Running"
 msgstr "執行中"
 
 #: src/components/ChatGPT/ChatGPT.vue:252
-#: src/views/certificate/CertificateEditor.vue:242
+#: src/views/certificate/CertificateEditor.vue:249
 #: src/views/config/ConfigEdit.vue:96 src/views/domain/DomainEdit.vue:263
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:120
 #: src/views/preference/Preference.vue:130 src/views/stream/StreamEdit.vue:254
@@ -1490,7 +1506,7 @@ msgstr "儲存錯誤 %{msg}"
 
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:39
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:104
-#: src/views/certificate/CertificateEditor.vue:45
+#: src/views/certificate/CertificateEditor.vue:46
 #: src/views/preference/Preference.vue:66
 msgid "Save successfully"
 msgstr "儲存成功"
@@ -1520,8 +1536,8 @@ msgstr "傳送"
 #: src/components/StdDesign/StdDataDisplay/methods/exportCsv.ts:46
 #: src/components/StdDesign/StdDataDisplay/methods/sortable.ts:126
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:42
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:182
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:221
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:196
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:235
 #: src/views/config/ConfigEdit.vue:40 src/views/domain/DomainList.vue:81
 #: src/views/environment/Environment.vue:139 src/views/other/Install.vue:70
 #: src/views/preference/Preference.vue:70 src/views/stream/StreamList.vue:113
@@ -1579,23 +1595,23 @@ msgstr "網站日誌"
 msgid "Sites List"
 msgstr "網站列表"
 
-#: src/views/certificate/CertificateEditor.vue:191
+#: src/views/certificate/CertificateEditor.vue:198
 #, fuzzy
 msgid "SSL Certificate Content"
 msgstr "SSL 認證內容"
 
-#: src/views/certificate/CertificateEditor.vue:204
+#: src/views/certificate/CertificateEditor.vue:211
 #, fuzzy
 msgid "SSL Certificate Key Content"
 msgstr "SSL 憑證金鑰內容"
 
-#: src/views/certificate/Certificate.vue:75
-#: src/views/certificate/CertificateEditor.vue:176
+#: src/views/certificate/Certificate.vue:80
+#: src/views/certificate/CertificateEditor.vue:177
 msgid "SSL Certificate Key Path"
 msgstr "SSL 憑證金鑰路徑"
 
-#: src/views/certificate/Certificate.vue:67
-#: src/views/certificate/CertificateEditor.vue:161
+#: src/views/certificate/Certificate.vue:72
+#: src/views/certificate/CertificateEditor.vue:162
 msgid "SSL Certificate Path"
 msgstr "SSL 憑證路徑"
 
@@ -1609,7 +1625,7 @@ msgid "Stable"
 msgstr "穩定"
 
 #: src/views/certificate/ACMEUser.vue:42
-#: src/views/certificate/Certificate.vue:83 src/views/domain/DomainList.vue:22
+#: src/views/certificate/Certificate.vue:88 src/views/domain/DomainList.vue:22
 #: src/views/environment/Environment.vue:76 src/views/stream/StreamList.vue:22
 msgid "Status"
 msgstr "狀態"
@@ -1645,6 +1661,42 @@ msgstr ""
 msgid "Switch to light theme"
 msgstr ""
 
+#: src/views/certificate/Certificate.vue:42
+#, fuzzy
+msgid "Sync Certificate"
+msgstr "更換憑證"
+
+#: src/components/Notification/detailRender.ts:25
+#, fuzzy
+msgid ""
+"Sync Certificate %{cert_name} to %{env_name} failed, please upgrade the "
+"remote Nginx UI to the latest version"
+msgstr "成功複製 %{conf_name} 到 %{node_name}"
+
+#: src/components/Notification/detailRender.ts:29
+#, fuzzy
+msgid "Sync Certificate %{cert_name} to %{env_name} failed, response: %{resp}"
+msgstr "成功複製 %{conf_name} 到 %{node_name}"
+
+#: src/components/Notification/detailRender.ts:17
+#, fuzzy
+msgid "Sync Certificate %{cert_name} to %{env_name} successfully"
+msgstr "成功複製 %{conf_name} 到 %{node_name}"
+
+#: src/language/constants.ts:38
+#, fuzzy
+msgid "Sync Certificate Error"
+msgstr "更換憑證"
+
+#: src/language/constants.ts:37
+#, fuzzy
+msgid "Sync Certificate Success"
+msgstr "更換憑證"
+
+#: src/views/certificate/CertificateEditor.vue:191
+msgid "Sync to"
+msgstr ""
+
 #: src/routes/index.ts:248
 msgid "System"
 msgstr "系統"
@@ -1675,11 +1727,11 @@ msgstr ""
 msgid "The filename cannot contain the following characters: %{c}"
 msgstr "檔名不能包含以下字元: %{c}"
 
-#: src/views/certificate/CertificateEditor.vue:194
+#: src/views/certificate/CertificateEditor.vue:201
 msgid "The input is not a SSL Certificate"
 msgstr ""
 
-#: src/views/certificate/CertificateEditor.vue:207
+#: src/views/certificate/CertificateEditor.vue:214
 #, fuzzy
 msgid "The input is not a SSL Certificate Key"
 msgstr "SSL 憑證金鑰路徑"
@@ -1690,12 +1742,12 @@ msgid ""
 "dashes, and dots."
 msgstr ""
 
-#: src/views/certificate/CertificateEditor.vue:165
+#: src/views/certificate/CertificateEditor.vue:166
 #, fuzzy
 msgid "The path exists, but the file is not a certificate"
 msgstr "SSL 憑證金鑰路徑"
 
-#: src/views/certificate/CertificateEditor.vue:180
+#: src/views/certificate/CertificateEditor.vue:181
 msgid "The path exists, but the file is not a private key"
 msgstr ""
 
@@ -1728,18 +1780,18 @@ msgstr "此功能在演示中不可用。"
 msgid "The username or password is incorrect"
 msgstr "使用者名稱或密碼不正確"
 
-#: src/views/certificate/CertificateEditor.vue:101
+#: src/views/certificate/CertificateEditor.vue:102
 #, fuzzy
 msgid "This Auto Cert item is invalid, please remove it."
 msgstr "此自動憑證項目無效,請將其移除。"
 
-#: src/views/certificate/CertificateEditor.vue:91
+#: src/views/certificate/CertificateEditor.vue:92
 msgid "This certificate is managed by Nginx UI"
 msgstr ""
 
-#: src/views/certificate/CertificateEditor.vue:149
-#: src/views/certificate/CertificateEditor.vue:163
-#: src/views/certificate/CertificateEditor.vue:178
+#: src/views/certificate/CertificateEditor.vue:150
+#: src/views/certificate/CertificateEditor.vue:164
+#: src/views/certificate/CertificateEditor.vue:179
 msgid "This field is required"
 msgstr ""
 
@@ -1751,7 +1803,7 @@ msgstr "此欄位不應為空"
 msgid "Tips"
 msgstr ""
 
-#: src/views/notification/Notification.vue:18
+#: src/views/notification/Notification.vue:19
 msgid "Title"
 msgstr ""
 
@@ -1774,12 +1826,12 @@ msgid "Trash"
 msgstr ""
 
 #: src/views/certificate/Certificate.vue:37 src/views/config/config.ts:12
-#: src/views/notification/Notification.vue:12
+#: src/views/notification/Notification.vue:13
 msgid "Type"
 msgstr "類型"
 
 #: src/views/certificate/ACMEUser.vue:53
-#: src/views/certificate/DNSCredential.vue:23 src/views/config/config.ts:27
+#: src/views/certificate/DNSCredential.vue:27 src/views/config/config.ts:27
 #: src/views/config/ConfigEdit.vue:121
 #: src/views/domain/components/RightSettings.vue:86
 #: src/views/domain/DomainList.vue:41 src/views/environment/Environment.vue:122
@@ -1797,7 +1849,7 @@ msgstr "更新成功"
 msgid "Upgrade"
 msgstr "升級"
 
-#: src/language/constants.ts:28
+#: src/language/constants.ts:29
 msgid "Upgraded successfully"
 msgstr "升級成功"
 
@@ -1830,11 +1882,11 @@ msgid "Username (*)"
 msgstr "使用者名稱 (*)"
 
 #: src/views/certificate/ACMEUser.vue:87
-#: src/views/certificate/Certificate.vue:92
+#: src/views/certificate/Certificate.vue:97
 msgid "Valid"
 msgstr ""
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:477
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:491
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:103
 msgid "View"
 msgstr "檢視"
@@ -1859,7 +1911,7 @@ msgstr "基本模式"
 msgid "Warning"
 msgstr "警告"
 
-#: src/views/certificate/DNSCredential.vue:52
+#: src/views/certificate/DNSCredential.vue:56
 msgid ""
 "We will add one or more TXT records to the DNS records of your domain for "
 "ownership verification."

+ 1 - 1
app/src/version.json

@@ -1 +1 @@
-{"version":"2.0.0-beta.24","build_id":135,"total_build":339}
+{"version":"2.0.0-beta.25","build_id":137,"total_build":341}

+ 7 - 2
app/src/views/certificate/Certificate.vue

@@ -39,14 +39,19 @@ const columns: Column[] = [{
   customRender: (args: customRender) => {
     const template: JSXElements = []
     const { text } = args
+    const sync = $gettext('Sync Certificate')
     const managed = $gettext('Managed Certificate')
     const general = $gettext('General Certificate')
-    if (text === true || text > 0) {
+    if (text === true || text === 1) {
       template.push(<Tag bordered={false} color="processing">
         { managed }
       </Tag>)
     }
-
+    else if (text === 2) {
+      template.push(<Tag bordered={false} color="success">
+        { sync }
+      </Tag>)
+    }
     else {
       template.push(<Tag bordered={false} color="purple">{
       general }

+ 7 - 0
app/src/views/certificate/CertificateEditor.vue

@@ -9,6 +9,7 @@ import type { Cert } from '@/api/cert'
 import cert from '@/api/cert'
 import FooterToolBar from '@/components/FooterToolbar/FooterToolBar.vue'
 import RenewCert from '@/views/certificate/RenewCert.vue'
+import NodeSelector from '@/components/NodeSelector/NodeSelector.vue'
 
 const route = useRoute()
 
@@ -187,6 +188,12 @@ const isManaged = computed(() => {
               v-model:value="data.ssl_certificate_key_path"
             />
           </AFormItem>
+          <AFormItem :label="$gettext('Sync to')">
+            <NodeSelector
+              v-model:target="data.sync_node_ids"
+              hidden-local
+            />
+          </AFormItem>
           <AFormItem
             :label="$gettext('SSL Certificate Content')"
             :validate-status="errors.ssl_certificate ? 'error' : ''"

+ 2 - 0
app/src/views/notification/Notification.vue

@@ -7,6 +7,7 @@ import type { customRender } from '@/components/StdDesign/StdDataDisplay/StdTabl
 import { datetime, mask } from '@/components/StdDesign/StdDataDisplay/StdTableTransformer'
 import { NotificationType } from '@/constants'
 import { useUserStore } from '@/pinia'
+import { detailRender } from '@/components/Notification/detailRender'
 
 const columns: Column[] = [{
   title: () => $gettext('Type'),
@@ -24,6 +25,7 @@ const columns: Column[] = [{
 }, {
   title: () => $gettext('Details'),
   dataIndex: 'details',
+  customRender: detailRender,
   pithy: true,
 }, {
   title: () => $gettext('Created at'),

+ 1 - 1
app/version.json

@@ -1 +1 @@
-{"version":"2.0.0-beta.24","build_id":135,"total_build":339}
+{"version":"2.0.0-beta.25","build_id":137,"total_build":341}

+ 3 - 0
gen.sh

@@ -0,0 +1,3 @@
+pushd ./cmd/generate || exit
+go run generate.go -config ../../app.ini
+popd || exit

+ 1 - 2
internal/analytic/node_record.go

@@ -29,8 +29,7 @@ func RetrieveNodesStatus() {
 
 	env := query.Environment
 
-	envs, err := env.Find()
-
+	envs, err := env.Where(env.Enabled.Is(true)).Find()
 	if err != nil {
 		logger.Error(err)
 		return

+ 8 - 3
internal/cert/auto_cert.go

@@ -53,14 +53,14 @@ func autoCert(certModel *model.Cert) {
 		return
 	}
 
-	cert, err := GetCertInfo(certModel.SSLCertificatePath)
+	certInfo, err := GetCertInfo(certModel.SSLCertificatePath)
 	if err != nil {
 		// Get certificate info error, ignore this certificate
 		log.Error(errors.Wrap(err, "get certificate info error"))
 		notification.Error("Renew Certificate Error", strings.Join(certModel.Domains, ", "))
 		return
 	}
-	if int(time.Now().Sub(cert.NotBefore).Hours()/24) < settings.ServerSettings.GetCertRenewalInterval() {
+	if int(time.Now().Sub(certInfo.NotBefore).Hours()/24) < settings.ServerSettings.GetCertRenewalInterval() {
 		// not after settings.ServerSettings.CertRenewalInterval, ignore
 		return
 	}
@@ -76,7 +76,7 @@ func autoCert(certModel *model.Cert) {
 		ChallengeMethod: certModel.ChallengeMethod,
 		DNSCredentialID: certModel.DnsCredentialID,
 		KeyType:         certModel.GetKeyType(),
-		NotBefore:       cert.NotBefore,
+		NotBefore:       certInfo.NotBefore,
 	}
 
 	if certModel.Resource != nil {
@@ -106,4 +106,9 @@ func autoCert(certModel *model.Cert) {
 	}
 
 	notification.Success("Renew Certificate Success", strings.Join(payload.ServerName, ", "))
+	err = SyncToRemoteServer(certModel)
+	if err != nil {
+		notification.Error("Sync Certificate Error", err.Error())
+		return
+	}
 }

+ 1 - 1
internal/cert/cert.go

@@ -111,7 +111,7 @@ func IssueCert(payload *ConfigPayload, logChan chan string, errChan chan error)
 			}
 			defer func() {
 				pConfig.CleanEnv()
-				l.Println("[INFO] [Nginx UI] Cleaned environment variables")
+				l.Println("[INFO] [Nginx UI] Environment variables cleaned")
 			}()
 			provider, err := dnsproviders.NewDNSChallengeProviderByName(code)
 			if err != nil {

+ 64 - 20
internal/cert/payload.go

@@ -16,14 +16,17 @@ import (
 )
 
 type ConfigPayload struct {
-	CertID          int                        `json:"cert_id"`
-	ServerName      []string                   `json:"server_name"`
-	ChallengeMethod string                     `json:"challenge_method"`
-	DNSCredentialID int                        `json:"dns_credential_id"`
-	ACMEUserID      int                        `json:"acme_user_id"`
-	KeyType         certcrypto.KeyType         `json:"key_type"`
-	Resource        *model.CertificateResource `json:"resource,omitempty"`
-	NotBefore       time.Time
+	CertID                int                        `json:"cert_id"`
+	ServerName            []string                   `json:"server_name"`
+	ChallengeMethod       string                     `json:"challenge_method"`
+	DNSCredentialID       int                        `json:"dns_credential_id"`
+	ACMEUserID            int                        `json:"acme_user_id"`
+	KeyType               certcrypto.KeyType         `json:"key_type"`
+	Resource              *model.CertificateResource `json:"resource,omitempty"`
+	NotBefore             time.Time                  `json:"-"`
+	CertificateDir        string                     `json:"-"`
+	SSLCertificatePath    string                     `json:"-"`
+	SSLCertificateKeyPath string                     `json:"-"`
 }
 
 func (c *ConfigPayload) GetACMEUser() (user *model.AcmeUser, err error) {
@@ -46,21 +49,38 @@ func (c *ConfigPayload) GetKeyType() certcrypto.KeyType {
 	return helper.GetKeyType(c.KeyType)
 }
 
-func (c *ConfigPayload) WriteFile(l *log.Logger, errChan chan error) {
-	name := strings.Join(c.ServerName, "_")
-	saveDir := nginx.GetConfPath("ssl/" + name + "_" + string(c.KeyType))
-	if _, err := os.Stat(saveDir); os.IsNotExist(err) {
-		err = os.MkdirAll(saveDir, 0755)
-		if err != nil {
-			errChan <- errors.Wrap(err, "mkdir error")
-			return
+func (c *ConfigPayload) mkCertificateDir() (err error) {
+	dir := c.getCertificateDirPath()
+	if _, err = os.Stat(dir); os.IsNotExist(err) {
+		err = os.MkdirAll(dir, 0755)
+		if err == nil {
+			return nil
+		}
+	}
+
+	// For windows, replace # with * (issue #403)
+	c.CertificateDir = strings.ReplaceAll(c.CertificateDir, "#", "*")
+	if _, err = os.Stat(c.CertificateDir); os.IsNotExist(err) {
+		err = os.MkdirAll(c.CertificateDir, 0755)
+		if err == nil {
+			return nil
 		}
 	}
 
+	return
+}
+
+func (c *ConfigPayload) WriteFile(l *log.Logger, errChan chan error) {
+	err := c.mkCertificateDir()
+	if err != nil {
+		errChan <- errors.Wrap(err, "make certificate dir error")
+		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.
 	l.Println("[INFO] [Nginx UI] Writing certificate to disk")
-	err := os.WriteFile(filepath.Join(saveDir, "fullchain.cer"),
+	err = os.WriteFile(c.GetCertificatePath(),
 		c.Resource.Certificate, 0644)
 
 	if err != nil {
@@ -69,7 +89,7 @@ func (c *ConfigPayload) WriteFile(l *log.Logger, errChan chan error) {
 	}
 
 	l.Println("[INFO] [Nginx UI] Writing certificate private key to disk")
-	err = os.WriteFile(filepath.Join(saveDir, "private.key"),
+	err = os.WriteFile(c.GetCertificateKeyPath(),
 		c.Resource.PrivateKey, 0644)
 
 	if err != nil {
@@ -84,7 +104,31 @@ func (c *ConfigPayload) WriteFile(l *log.Logger, errChan chan error) {
 
 	db := model.UseDB()
 	db.Where("id = ?", c.CertID).Updates(&model.Cert{
-		SSLCertificatePath:    filepath.Join(saveDir, "fullchain.cer"),
-		SSLCertificateKeyPath: filepath.Join(saveDir, "private.key"),
+		SSLCertificatePath:    c.GetCertificatePath(),
+		SSLCertificateKeyPath: c.GetCertificateKeyPath(),
 	})
 }
+
+func (c *ConfigPayload) getCertificateDirPath() string {
+	if c.CertificateDir != "" {
+		return c.CertificateDir
+	}
+	c.CertificateDir = nginx.GetConfPath("ssl", strings.Join(c.ServerName, "_")+"_"+string(c.GetKeyType()))
+	return c.CertificateDir
+}
+
+func (c *ConfigPayload) GetCertificatePath() string {
+	if c.SSLCertificatePath != "" {
+		return c.SSLCertificatePath
+	}
+	c.SSLCertificatePath = filepath.Join(c.getCertificateDirPath(), "fullchain.cer")
+	return c.SSLCertificatePath
+}
+
+func (c *ConfigPayload) GetCertificateKeyPath() string {
+	if c.SSLCertificateKeyPath != "" {
+		return c.SSLCertificateKeyPath
+	}
+	c.SSLCertificateKeyPath = filepath.Join(c.getCertificateDirPath(), "private.key")
+	return c.SSLCertificateKeyPath
+}

+ 135 - 0
internal/cert/sync.go

@@ -0,0 +1,135 @@
+package cert
+
+import (
+	"bytes"
+	"crypto/tls"
+	"encoding/json"
+	"fmt"
+	"github.com/0xJacky/Nginx-UI/internal/helper"
+	"github.com/0xJacky/Nginx-UI/internal/logger"
+	"github.com/0xJacky/Nginx-UI/internal/nginx"
+	"github.com/0xJacky/Nginx-UI/internal/notification"
+	"github.com/0xJacky/Nginx-UI/model"
+	"github.com/0xJacky/Nginx-UI/query"
+	"github.com/go-acme/lego/v4/certcrypto"
+	"io"
+	"net/http"
+	"os"
+)
+
+type SyncCertificatePayload struct {
+	Name                  string             `json:"name"`
+	SSLCertificatePath    string             `json:"ssl_certificate_path"`
+	SSLCertificateKeyPath string             `json:"ssl_certificate_key_path"`
+	SSLCertificate        string             `json:"ssl_certificate"`
+	SSLCertificateKey     string             `json:"ssl_certificate_key"`
+	KeyType               certcrypto.KeyType `json:"key_type"`
+}
+
+func SyncToRemoteServer(c *model.Cert) (err error) {
+	if c.SSLCertificatePath == "" || c.SSLCertificateKeyPath == "" || len(c.SyncNodeIds) == 0 {
+		return
+	}
+
+	nginxConfPath := nginx.GetConfPath()
+	if !helper.IsUnderDirectory(c.SSLCertificatePath, nginxConfPath) {
+		return fmt.Errorf("ssl_certificate_path: %s is not under the nginx conf path: %s",
+			c.SSLCertificatePath, nginxConfPath)
+	}
+
+	if !helper.IsUnderDirectory(c.SSLCertificateKeyPath, nginxConfPath) {
+		return fmt.Errorf("ssl_certificate_key_path: %s is not under the nginx conf path: %s",
+			c.SSLCertificateKeyPath, nginxConfPath)
+	}
+
+	certBytes, err := os.ReadFile(c.SSLCertificatePath)
+	if err != nil {
+		return
+	}
+	keyBytes, err := os.ReadFile(c.SSLCertificateKeyPath)
+	if err != nil {
+		return
+	}
+
+	payload := &SyncCertificatePayload{
+		Name:                  c.Name,
+		SSLCertificatePath:    c.SSLCertificatePath,
+		SSLCertificateKeyPath: c.SSLCertificateKeyPath,
+		SSLCertificate:        string(certBytes),
+		SSLCertificateKey:     string(keyBytes),
+		KeyType:               c.KeyType,
+	}
+
+	payloadBytes, err := json.Marshal(payload)
+	if err != nil {
+		return
+	}
+
+	q := query.Environment
+	envs, _ := q.Where(q.ID.In(c.SyncNodeIds...)).Find()
+	for _, env := range envs {
+		go func() {
+			err := deploy(env, c, payloadBytes)
+			if err != nil {
+				logger.Error(err)
+			}
+		}()
+	}
+
+	return
+}
+
+type SyncNotificationPayload struct {
+	StatusCode int    `json:"status_code"`
+	CertName   string `json:"cert_name"`
+	EnvName    string `json:"env_name"`
+	RespBody   string `json:"resp_body"`
+}
+
+func deploy(env *model.Environment, c *model.Cert, payloadBytes []byte) (err error) {
+	client := http.Client{
+		Transport: &http.Transport{
+			TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
+		},
+	}
+	url, err := env.GetUrl("/api/cert_sync")
+	if err != nil {
+		return
+	}
+	req, err := http.NewRequest(http.MethodPut, url, bytes.NewBuffer(payloadBytes))
+	if err != nil {
+		return
+	}
+	req.Header.Set("X-Node-Secret", env.Token)
+	resp, err := client.Do(req)
+	if err != nil {
+		return
+	}
+	defer resp.Body.Close()
+
+	respBody, err := io.ReadAll(resp.Body)
+	if err != nil {
+		return
+	}
+
+	notificationPayload := &SyncNotificationPayload{
+		StatusCode: resp.StatusCode,
+		CertName:   c.Name,
+		EnvName:    env.Name,
+		RespBody:   string(respBody),
+	}
+
+	notificationPayloadBytes, err := json.Marshal(notificationPayload)
+	if err != nil {
+		return
+	}
+
+	if resp.StatusCode != http.StatusOK {
+		notification.Error("Sync Certificate Error", string(notificationPayloadBytes))
+		return
+	}
+
+	notification.Success("Sync Certificate Success", string(notificationPayloadBytes))
+
+	return
+}

+ 18 - 0
internal/cert/write_file.go

@@ -1,6 +1,9 @@
 package cert
 
 import (
+	"fmt"
+	"github.com/0xJacky/Nginx-UI/internal/helper"
+	"github.com/0xJacky/Nginx-UI/internal/nginx"
 	"os"
 	"path/filepath"
 )
@@ -13,6 +16,21 @@ type Content struct {
 }
 
 func (c *Content) WriteFile() (err error) {
+	if c.SSLCertificatePath == "" || c.SSLCertificateKeyPath == "" {
+		return
+	}
+
+	nginxConfPath := nginx.GetConfPath()
+	if !helper.IsUnderDirectory(c.SSLCertificatePath, nginxConfPath) {
+		return fmt.Errorf("ssl_certificate_path: %s is not under the nginx conf path: %s",
+			c.SSLCertificatePath, nginxConfPath)
+	}
+
+	if !helper.IsUnderDirectory(c.SSLCertificateKeyPath, nginxConfPath) {
+		return fmt.Errorf("ssl_certificate_key_path: %s is not under the nginx conf path: %s",
+			c.SSLCertificateKeyPath, nginxConfPath)
+	}
+
 	// MkdirAll creates a directory named path, along with any necessary parents,
 	// and returns nil, or else returns an error.
 	// The permission bits perm (before umask) are used for all directories that MkdirAll creates.

+ 1 - 0
internal/helper/directory.go

@@ -6,6 +6,7 @@ import (
 	"strings"
 )
 
+// IsUnderDirectory checks if the path is under the directory
 func IsUnderDirectory(path, directory string) bool {
 	absPath, err := filepath.Abs(path)
 	if err != nil {

+ 1 - 1
internal/kernal/boot.go

@@ -97,7 +97,7 @@ func InitJsExtensionType() {
 
 func InitCronJobs() {
 	s := gocron.NewScheduler(time.UTC)
-	job, err := s.Every(30).Minute().SingletonMode().Do(cert.AutoCert)
+	job, err := s.Every(6).Hours().SingletonMode().Do(cert.AutoCert)
 
 	if err != nil {
 		logger.Fatalf("AutoCert Job: %v, Err: %v\n", job, err)

+ 2 - 4
model/cert.go

@@ -10,6 +10,7 @@ import (
 )
 
 const (
+	AutoCertSync              = 2
 	AutoCertEnabled           = 1
 	AutoCertDisabled          = -1
 	CertChallengeMethodHTTP01 = "http01"
@@ -42,6 +43,7 @@ type Cert struct {
 	KeyType               certcrypto.KeyType   `json:"key_type"`
 	Log                   string               `json:"log"`
 	Resource              *CertificateResource `json:"-" gorm:"serializer:json"`
+	SyncNodeIds           []int                `json:"sync_node_ids" gorm:"serializer:json"`
 }
 
 func FirstCert(confName string) (c Cert, err error) {
@@ -94,10 +96,6 @@ func (c *Cert) Updates(n *Cert) error {
 	return db.Model(&Cert{}).Where("id", c.ID).Updates(n).Error
 }
 
-func (c *Cert) ClearLog() {
-	db.Model(&Cert{}).Where("id", c.ID).Update("log", "")
-}
-
 func (c *Cert) Remove() error {
 	if c.Filename == "" {
 		return db.Delete(c).Error

+ 19 - 0
model/environment.go

@@ -15,6 +15,25 @@ type Environment struct {
 	SyncApiRegex  string `json:"sync_api_regex"`
 }
 
+func (e *Environment) GetUrl(uri string) (decodedUri string, err error) {
+	baseUrl, err := url.Parse(e.URL)
+	if err != nil {
+		return
+	}
+
+	u, err := url.JoinPath(baseUrl.String(), uri)
+	if err != nil {
+		return
+	}
+
+	decodedUri, err = url.QueryUnescape(u)
+	if err != nil {
+		return
+	}
+
+	return
+}
+
 func (e *Environment) GetWebSocketURL(uri string) (decodedUri string, err error) {
 	baseUrl, err := url.Parse(e.URL)
 	if err != nil {

+ 9 - 1
query/certs.gen.go

@@ -43,6 +43,8 @@ func newCert(db *gorm.DB, opts ...gen.DOOption) cert {
 	_cert.ACMEUserID = field.NewInt(tableName, "acme_user_id")
 	_cert.KeyType = field.NewString(tableName, "key_type")
 	_cert.Log = field.NewString(tableName, "log")
+	_cert.Resource = field.NewField(tableName, "resource")
+	_cert.SyncNodeIds = field.NewField(tableName, "sync_node_ids")
 	_cert.DnsCredential = certBelongsToDnsCredential{
 		db: db.Session(&gorm.Session{}),
 
@@ -79,6 +81,8 @@ type cert struct {
 	ACMEUserID            field.Int
 	KeyType               field.String
 	Log                   field.String
+	Resource              field.Field
+	SyncNodeIds           field.Field
 	DnsCredential         certBelongsToDnsCredential
 
 	ACMEUser certBelongsToACMEUser
@@ -113,6 +117,8 @@ func (c *cert) updateTableName(table string) *cert {
 	c.ACMEUserID = field.NewInt(table, "acme_user_id")
 	c.KeyType = field.NewString(table, "key_type")
 	c.Log = field.NewString(table, "log")
+	c.Resource = field.NewField(table, "resource")
+	c.SyncNodeIds = field.NewField(table, "sync_node_ids")
 
 	c.fillFieldMap()
 
@@ -129,7 +135,7 @@ func (c *cert) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
 }
 
 func (c *cert) fillFieldMap() {
-	c.fieldMap = make(map[string]field.Expr, 17)
+	c.fieldMap = make(map[string]field.Expr, 19)
 	c.fieldMap["id"] = c.ID
 	c.fieldMap["created_at"] = c.CreatedAt
 	c.fieldMap["updated_at"] = c.UpdatedAt
@@ -145,6 +151,8 @@ func (c *cert) fillFieldMap() {
 	c.fieldMap["acme_user_id"] = c.ACMEUserID
 	c.fieldMap["key_type"] = c.KeyType
 	c.fieldMap["log"] = c.Log
+	c.fieldMap["resource"] = c.Resource
+	c.fieldMap["sync_node_ids"] = c.SyncNodeIds
 
 }
 

+ 3 - 3
query/config_backups.gen.go

@@ -33,7 +33,7 @@ func newConfigBackup(db *gorm.DB, opts ...gen.DOOption) configBackup {
 	_configBackup.UpdatedAt = field.NewTime(tableName, "updated_at")
 	_configBackup.DeletedAt = field.NewField(tableName, "deleted_at")
 	_configBackup.Name = field.NewString(tableName, "name")
-	_configBackup.FilePath = field.NewString(tableName, "filepath")
+	_configBackup.FilePath = field.NewString(tableName, "file_path")
 	_configBackup.Content = field.NewString(tableName, "content")
 
 	_configBackup.fillFieldMap()
@@ -73,7 +73,7 @@ func (c *configBackup) updateTableName(table string) *configBackup {
 	c.UpdatedAt = field.NewTime(table, "updated_at")
 	c.DeletedAt = field.NewField(table, "deleted_at")
 	c.Name = field.NewString(table, "name")
-	c.FilePath = field.NewString(table, "filepath")
+	c.FilePath = field.NewString(table, "file_path")
 	c.Content = field.NewString(table, "content")
 
 	c.fillFieldMap()
@@ -97,7 +97,7 @@ func (c *configBackup) fillFieldMap() {
 	c.fieldMap["updated_at"] = c.UpdatedAt
 	c.fieldMap["deleted_at"] = c.DeletedAt
 	c.fieldMap["name"] = c.Name
-	c.fieldMap["filepath"] = c.FilePath
+	c.fieldMap["file_path"] = c.FilePath
 	c.fieldMap["content"] = c.Content
 }
 

+ 5 - 1
query/environments.gen.go

@@ -35,6 +35,7 @@ func newEnvironment(db *gorm.DB, opts ...gen.DOOption) environment {
 	_environment.Name = field.NewString(tableName, "name")
 	_environment.URL = field.NewString(tableName, "url")
 	_environment.Token = field.NewString(tableName, "token")
+	_environment.Enabled = field.NewBool(tableName, "enabled")
 	_environment.OperationSync = field.NewBool(tableName, "operation_sync")
 	_environment.SyncApiRegex = field.NewString(tableName, "sync_api_regex")
 
@@ -54,6 +55,7 @@ type environment struct {
 	Name          field.String
 	URL           field.String
 	Token         field.String
+	Enabled       field.Bool
 	OperationSync field.Bool
 	SyncApiRegex  field.String
 
@@ -79,6 +81,7 @@ func (e *environment) updateTableName(table string) *environment {
 	e.Name = field.NewString(table, "name")
 	e.URL = field.NewString(table, "url")
 	e.Token = field.NewString(table, "token")
+	e.Enabled = field.NewBool(table, "enabled")
 	e.OperationSync = field.NewBool(table, "operation_sync")
 	e.SyncApiRegex = field.NewString(table, "sync_api_regex")
 
@@ -97,7 +100,7 @@ func (e *environment) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
 }
 
 func (e *environment) fillFieldMap() {
-	e.fieldMap = make(map[string]field.Expr, 9)
+	e.fieldMap = make(map[string]field.Expr, 10)
 	e.fieldMap["id"] = e.ID
 	e.fieldMap["created_at"] = e.CreatedAt
 	e.fieldMap["updated_at"] = e.UpdatedAt
@@ -105,6 +108,7 @@ func (e *environment) fillFieldMap() {
 	e.fieldMap["name"] = e.Name
 	e.fieldMap["url"] = e.URL
 	e.fieldMap["token"] = e.Token
+	e.fieldMap["enabled"] = e.Enabled
 	e.fieldMap["operation_sync"] = e.OperationSync
 	e.fieldMap["sync_api_regex"] = e.SyncApiRegex
 }

部分文件因为文件数量过多而无法显示