Browse Source

feat: add notifications for renew certs #192

0xJacky 1 year ago
parent
commit
994b9087ae
45 changed files with 1971 additions and 594 deletions
  1. 12 0
      .idea/dataSources.xml
  2. 6 0
      api/cosy/cosy.go
  3. 4 0
      api/cosy/delete.go
  4. 58 0
      api/notification/notification.go
  5. 10 0
      api/notification/router.go
  6. 3 0
      app/components.d.ts
  7. 3 0
      app/gettext.config.cjs
  8. 19 0
      app/src/api/notification.ts
  9. 164 0
      app/src/components/Notification/Notification.vue
  10. 9 5
      app/src/components/StdDesign/StdDataDisplay/StdCurd.vue
  11. 15 9
      app/src/components/StdDesign/StdDataDisplay/StdTable.vue
  12. 17 0
      app/src/constants/index.ts
  13. 3 0
      app/src/language/constants.ts
  14. 143 69
      app/src/language/en/app.po
  15. 143 68
      app/src/language/es/app.po
  16. 143 68
      app/src/language/fr_FR/app.po
  17. 138 65
      app/src/language/messages.pot
  18. 143 69
      app/src/language/ru_RU/app.po
  19. BIN
      app/src/language/zh_CN/app.mo
  20. 133 68
      app/src/language/zh_CN/app.po
  21. 143 68
      app/src/language/zh_TW/app.po
  22. 3 0
      app/src/layouts/HeaderLayout.vue
  23. 1 0
      app/src/pinia/moudule/user.ts
  24. 9 0
      app/src/routes/index.ts
  25. 12 4
      app/src/views/certificate/Certificate.vue
  26. 40 12
      app/src/views/certificate/CertificateEditor.vue
  27. 0 1
      app/src/views/domain/DomainList.vue
  28. 87 0
      app/src/views/notification/Notification.vue
  29. 1 1
      cmd/generate/generate.go
  30. 28 12
      internal/cert/auto_cert.go
  31. 32 0
      internal/notification/notification.go
  32. 3 6
      model/model.go
  33. 17 0
      model/notification.go
  34. 2 5
      query/auth_tokens.gen.go
  35. 2 5
      query/auths.gen.go
  36. 31 34
      query/certs.gen.go
  37. 2 5
      query/chat_gpt_logs.gen.go
  38. 2 5
      query/config_backups.gen.go
  39. 2 5
      query/dns_credentials.gen.go
  40. 2 5
      query/environments.gen.go
  41. 8 0
      query/gen.go
  42. 374 0
      query/notifications.gen.go
  43. 2 5
      query/sites.gen.go
  44. BIN
      resources/demo/demo.db
  45. 2 0
      router/routers.go

+ 12 - 0
.idea/dataSources.xml

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="DataSourceManagerImpl" format="xml" multifile-model="true">
+    <data-source source="LOCAL" name="database" uuid="31ab1005-9ff6-47e2-9df8-6c521ae42819">
+      <driver-ref>sqlite.xerial</driver-ref>
+      <synchronize>true</synchronize>
+      <jdbc-driver>org.sqlite.JDBC</jdbc-driver>
+      <jdbc-url>jdbc:sqlite:$PROJECT_DIR$/database.db</jdbc-url>
+      <working-dir>$ProjectFileDir$</working-dir>
+    </data-source>
+  </component>
+</project>

+ 6 - 0
api/cosy/cosy.go

@@ -27,6 +27,7 @@ type Ctx[T any] struct {
 	preloads              []string
 	preloads              []string
 	scan                  func(tx *gorm.DB) any
 	scan                  func(tx *gorm.DB) any
 	transformer           func(*T) any
 	transformer           func(*T) any
+	permanentlyDelete     bool
 	SelectedFields        []string
 	SelectedFields        []string
 }
 }
 
 
@@ -128,6 +129,11 @@ func (c *Ctx[T]) Abort() {
 	c.abort = true
 	c.abort = true
 }
 }
 
 
+func (c *Ctx[T]) PermanentlyDelete() *Ctx[T] {
+	c.permanentlyDelete = true
+	return c
+}
+
 func (c *Ctx[T]) GormScope(hook func(tx *gorm.DB) *gorm.DB) *Ctx[T] {
 func (c *Ctx[T]) GormScope(hook func(tx *gorm.DB) *gorm.DB) *Ctx[T] {
 	c.gormScopes = append(c.gormScopes, hook)
 	c.gormScopes = append(c.gormScopes, hook)
 	return c
 	return c

+ 4 - 0
api/cosy/delete.go

@@ -29,6 +29,10 @@ func (c *Ctx[T]) Destroy() {
 		return
 		return
 	}
 	}
 
 
+	if c.permanentlyDelete {
+		result = result.Unscoped()
+	}
+
 	err = result.Delete(&dbModel).Error
 	err = result.Delete(&dbModel).Error
 	if err != nil {
 	if err != nil {
 		errHandler(c.ctx, err)
 		errHandler(c.ctx, err)

+ 58 - 0
api/notification/notification.go

@@ -0,0 +1,58 @@
+package notification
+
+import (
+	"github.com/0xJacky/Nginx-UI/api"
+	"github.com/0xJacky/Nginx-UI/api/cosy"
+	"github.com/0xJacky/Nginx-UI/model"
+	"github.com/0xJacky/Nginx-UI/query"
+	"github.com/gin-gonic/gin"
+	"github.com/spf13/cast"
+	"net/http"
+)
+
+func Get(c *gin.Context) {
+	n := query.Notification
+
+	id := cast.ToInt(c.Param("id"))
+
+	data, err := n.FirstByID(id)
+
+	if err != nil {
+		api.ErrHandler(c, err)
+		return
+	}
+
+	c.JSON(http.StatusOK, data)
+}
+
+func GetList(c *gin.Context) {
+	cosy.Core[model.Notification](c).PagingList()
+}
+
+func Destroy(c *gin.Context) {
+	cosy.Core[model.Notification](c).
+		PermanentlyDelete().
+		Destroy()
+}
+
+func DestroyAll(c *gin.Context) {
+	db := model.UseDB()
+	// remove all records
+	err := db.Exec("DELETE FROM notifications").Error
+
+	if err != nil {
+		api.ErrHandler(c, err)
+		return
+	}
+	// reset auto increment
+	err = db.Exec("UPDATE sqlite_sequence SET seq = 0 WHERE name = 'notifications';").Error
+
+	if err != nil {
+		api.ErrHandler(c, err)
+		return
+	}
+
+	c.JSON(http.StatusOK, gin.H{
+		"message": "ok",
+	})
+}

+ 10 - 0
api/notification/router.go

@@ -0,0 +1,10 @@
+package notification
+
+import "github.com/gin-gonic/gin"
+
+func InitRouter(r *gin.RouterGroup) {
+	r.GET("notifications", GetList)
+	r.GET("notification/:id", Get)
+	r.DELETE("notification/:id", Destroy)
+	r.DELETE("notifications", DestroyAll)
+}

+ 3 - 0
app/components.d.ts

@@ -40,6 +40,7 @@ declare module 'vue' {
     AListItem: typeof import('ant-design-vue/es')['ListItem']
     AListItem: typeof import('ant-design-vue/es')['ListItem']
     AListItemMeta: typeof import('ant-design-vue/es')['ListItemMeta']
     AListItemMeta: typeof import('ant-design-vue/es')['ListItemMeta']
     AMenu: typeof import('ant-design-vue/es')['Menu']
     AMenu: typeof import('ant-design-vue/es')['Menu']
+    AMenuDivider: typeof import('ant-design-vue/es')['MenuDivider']
     AMenuItem: typeof import('ant-design-vue/es')['MenuItem']
     AMenuItem: typeof import('ant-design-vue/es')['MenuItem']
     AModal: typeof import('ant-design-vue/es')['Modal']
     AModal: typeof import('ant-design-vue/es')['Modal']
     APagination: typeof import('ant-design-vue/es')['Pagination']
     APagination: typeof import('ant-design-vue/es')['Pagination']
@@ -73,6 +74,8 @@ declare module 'vue' {
     LogoLogo: typeof import('./src/components/Logo/Logo.vue')['default']
     LogoLogo: typeof import('./src/components/Logo/Logo.vue')['default']
     NginxControlNginxControl: typeof import('./src/components/NginxControl/NginxControl.vue')['default']
     NginxControlNginxControl: typeof import('./src/components/NginxControl/NginxControl.vue')['default']
     NodeSelectorNodeSelector: typeof import('./src/components/NodeSelector/NodeSelector.vue')['default']
     NodeSelectorNodeSelector: typeof import('./src/components/NodeSelector/NodeSelector.vue')['default']
+    NotificationClearNotifications: typeof import('./src/components/Notification/ClearNotifications.vue')['default']
+    NotificationNotification: typeof import('./src/components/Notification/Notification.vue')['default']
     PageHeaderPageHeader: typeof import('./src/components/PageHeader/PageHeader.vue')['default']
     PageHeaderPageHeader: typeof import('./src/components/PageHeader/PageHeader.vue')['default']
     RouterLink: typeof import('vue-router')['RouterLink']
     RouterLink: typeof import('vue-router')['RouterLink']
     RouterView: typeof import('vue-router')['RouterView']
     RouterView: typeof import('vue-router')['RouterView']

+ 3 - 0
app/gettext.config.cjs

@@ -2,6 +2,9 @@
 const i18n = require('./i18n.json')
 const i18n = require('./i18n.json')
 
 
 module.exports = {
 module.exports = {
+  input: {
+    include: ["**/*.js", "**/*.ts", "**/*.vue", "**/*.jsx", "**/*.tsx"]
+  },
   output: {
   output: {
     locales: Object.keys(i18n),
     locales: Object.keys(i18n),
   },
   },

+ 19 - 0
app/src/api/notification.ts

@@ -0,0 +1,19 @@
+import type { ModelBase } from '@/api/curd'
+import Curd from '@/api/curd'
+import http from '@/lib/http'
+
+export interface Notification extends ModelBase {
+  type: string
+  title: string
+  details: string
+}
+
+class NotificationCurd extends Curd<Notification> {
+  public clear() {
+    return http.delete(this.plural)
+  }
+}
+
+const notification = new NotificationCurd('/notification')
+
+export default notification

+ 164 - 0
app/src/components/Notification/Notification.vue

@@ -0,0 +1,164 @@
+<script setup lang="ts">
+import { BellOutlined, CheckCircleOutlined, CloseCircleOutlined, DeleteOutlined, InfoCircleOutlined, WarningOutlined } from '@ant-design/icons-vue'
+import { useGettext } from 'vue3-gettext'
+import type { Ref } from 'vue'
+import { message } from 'ant-design-vue'
+import notification from '@/api/notification'
+import type { Notification } from '@/api/notification'
+import { NotificationTypeT } from '@/constants'
+import { useUserStore } from '@/pinia'
+
+const { $gettext } = useGettext()
+const loading = ref(false)
+
+const { unreadCount } = storeToRefs(useUserStore())
+
+const data = ref([]) as Ref<Notification[]>
+function init() {
+  loading.value = true
+  notification.get_list().then(r => {
+    data.value = r.data
+    unreadCount.value = r.pagination.total
+  }).catch(e => {
+    message.error($gettext(e?.message ?? 'Server error'))
+  }).finally(() => {
+    loading.value = false
+  })
+}
+
+onMounted(() => {
+  init()
+})
+
+const open = ref(false)
+
+watch(open, v => {
+  if (v)
+    init()
+})
+
+function clear() {
+  notification.clear().then(() => {
+    message.success($gettext('Cleared successfully'))
+    data.value = []
+    unreadCount.value = 0
+  }).catch(e => {
+    message.error($gettext(e?.message ?? 'Server error'))
+  })
+}
+
+function remove(id: number) {
+  notification.destroy(id).then(() => {
+    message.success($gettext('Removed successfully'))
+    init()
+  }).catch(e => {
+    message.error($gettext(e?.message ?? 'Server error'))
+  })
+}
+
+const router = useRouter()
+function viewAll() {
+  router.push('/notifications')
+  open.value = false
+}
+</script>
+
+<template>
+  <span class="cursor-pointer">
+    <APopover
+      v-model:open="open"
+      placement="bottomRight"
+      overlay-class-name="notification-popover"
+      trigger="click"
+    >
+      <ABadge
+        :count="unreadCount"
+        dot
+      >
+        <BellOutlined />
+      </ABadge>
+      <template #content>
+        <div class="flex justify-between items-center p-2">
+          <h3 class="mb-0">{{ $gettext('Notifications') }}</h3>
+          <APopconfirm
+            :cancel-text="$gettext('No')"
+            :ok-text="$gettext('OK')"
+            :title="$gettext('Are you sure you want to clear all notifications?')"
+            placement="bottomRight"
+            @confirm="clear"
+          >
+            <a>
+              {{ $gettext('Clear') }}
+            </a>
+          </APopconfirm>
+        </div>
+
+        <ADivider class="mt-2 mb-2" />
+
+        <AList
+          :data-source="data"
+          class="max-h-96 overflow-scroll"
+        >
+          <template #renderItem="{ item }">
+            <AListItem>
+              <template #actions>
+                <span
+                  key="list-loadmore-remove"
+                  class="cursor-pointer"
+                  @click="remove(item.id)"
+                >
+                  <DeleteOutlined />
+                </span>
+              </template>
+              <AListItemMeta
+                :title="item.title"
+                :description="item.details"
+              >
+                <template #avatar>
+                  <div>
+                    <CloseCircleOutlined
+                      v-if="item.type === NotificationTypeT.Error"
+                      class="text-red-500"
+                    />
+                    <WarningOutlined
+                      v-else-if="item.type === NotificationTypeT.Warning"
+                      class="text-orange-400"
+                    />
+                    <InfoCircleOutlined
+                      v-else-if="item.type === NotificationTypeT.Info"
+                      class="text-blue-500"
+                    />
+                    <CheckCircleOutlined
+                      v-else-if="item.type === NotificationTypeT.Success"
+                      class="text-green-500"
+                    />
+                  </div>
+                </template>
+              </AListItemMeta>
+            </AListItem>
+          </template>
+        </AList>
+        <ADivider class="m-0 mb-2" />
+        <div class="flex justify-center p-2">
+          <a @click="viewAll">{{ $gettext('View all notifications') }}</a>
+        </div>
+      </template>
+    </APopover>
+  </span>
+</template>
+
+<style lang="less">
+.notification-popover {
+  width: 400px;
+}
+</style>
+
+<style scoped lang="less">
+:deep(.ant-list-item-meta) {
+  align-items: center !important;
+}
+
+:deep(.ant-list-item-meta-avatar) {
+  font-size: 24px;
+}
+</style>

+ 9 - 5
app/src/components/StdDesign/StdDataDisplay/StdCurd.vue

@@ -107,11 +107,15 @@ const selectedRowKeys = ref([])
 <template>
 <template>
   <div class="std-curd">
   <div class="std-curd">
     <ACard :title="title || $gettext('Table')">
     <ACard :title="title || $gettext('Table')">
-      <template
-        v-if="!disableAdd"
-        #extra
-      >
-        <a @click="add">{{ $gettext('Add') }}</a>
+      <template #extra>
+        <ASpace>
+          <a
+            v-if="!disableAdd"
+            @click="add"
+          >{{ $gettext('Add') }}</a>
+
+          <slot name="extra" />
+        </ASpace>
       </template>
       </template>
 
 
       <StdTable
       <StdTable

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

@@ -327,20 +327,26 @@ function export_csv() {
           {{ text }}
           {{ text }}
         </template>
         </template>
         <template v-if="column.dataIndex === 'action'">
         <template v-if="column.dataIndex === 'action'">
-          <AButton
-            v-if="!props.disabledModify"
-            type="link"
-            size="small"
-            @click="$emit('clickEdit', record[props.rowKey], record)"
-          >
-            {{ $gettext('Modify') }}
-          </AButton>
+          <template v-if="!props.disabledModify">
+            <AButton
+              type="link"
+              size="small"
+              @click="$emit('clickEdit', record[props.rowKey], record)"
+            >
+              {{ $gettext('Modify') }}
+            </AButton>
+            <ADivider
+              v-if="!props.disableDelete"
+              type="vertical"
+            />
+          </template>
+
           <slot
           <slot
             name="actions"
             name="actions"
             :record="record"
             :record="record"
           />
           />
+
           <template v-if="!props.disableDelete">
           <template v-if="!props.disableDelete">
-            <ADivider type="vertical" />
             <APopconfirm
             <APopconfirm
               :cancel-text="$gettext('No')"
               :cancel-text="$gettext('No')"
               :ok-text="$gettext('OK')"
               :ok-text="$gettext('OK')"

+ 17 - 0
app/src/constants/index.ts

@@ -1,4 +1,21 @@
+import gettext from '@/gettext'
+
+const { $gettext } = gettext
 export enum AutoCertState {
 export enum AutoCertState {
   Disable = 0,
   Disable = 0,
   Enable = 1,
   Enable = 1,
 }
 }
+
+export enum NotificationTypeT {
+  Error,
+  Warning,
+  Info,
+  Success,
+}
+
+export const NotificationType = {
+  [NotificationTypeT.Error]: () => $gettext('Error'),
+  [NotificationTypeT.Warning]: () => $gettext('Warning'),
+  [NotificationTypeT.Info]: () => $gettext('Info'),
+  [NotificationTypeT.Success]: () => $gettext('Success'),
+} as const

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

@@ -33,4 +33,7 @@ export const msg = [
 
 
   $gettext('File exists'),
   $gettext('File exists'),
   $gettext('Requested with wrong parameters'),
   $gettext('Requested with wrong parameters'),
+
+  $gettext('Renew Certificate Success'),
+  $gettext('Renew Certificate Error'),
 ]
 ]

+ 143 - 69
app/src/language/en/app.po

@@ -9,30 +9,30 @@ msgstr ""
 "Content-Transfer-Encoding: 8bit\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 
 
-#: src/routes/index.ts:199
+#: src/routes/index.ts:208
 msgid "About"
 msgid "About"
 msgstr "About"
 msgstr "About"
 
 
-#: src/routes/index.ts:151 src/views/domain/ngx_conf/LogEntry.vue:78
+#: src/routes/index.ts:152 src/views/domain/ngx_conf/LogEntry.vue:78
 msgid "Access Logs"
 msgid "Access Logs"
 msgstr ""
 msgstr ""
 
 
-#: src/views/certificate/Certificate.vue:96
+#: src/views/certificate/Certificate.vue:104
 #: src/views/certificate/DNSCredential.vue:32 src/views/config/config.ts:36
 #: src/views/certificate/DNSCredential.vue:32 src/views/config/config.ts:36
 #: src/views/domain/DomainList.vue:50 src/views/environment/Environment.vue:105
 #: src/views/domain/DomainList.vue:50 src/views/environment/Environment.vue:105
-#: src/views/user/User.vue:46
+#: src/views/notification/Notification.vue:38 src/views/user/User.vue:46
 msgid "Action"
 msgid "Action"
 msgstr "Action"
 msgstr "Action"
 
 
-#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:114
-#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:137
+#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:115
+#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:141
+#: src/views/certificate/Certificate.vue:120
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:115
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:115
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:308
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:308
 msgid "Add"
 msgid "Add"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:122 src/views/certificate/Certificate.vue:115
-#: src/views/certificate/CertificateEditor.vue:76
+#: src/routes/index.ts:123 src/views/certificate/CertificateEditor.vue:84
 #, fuzzy
 #, fuzzy
 msgid "Add Certificate"
 msgid "Add Certificate"
 msgstr "Certificate Status"
 msgstr "Certificate Status"
@@ -46,7 +46,7 @@ msgstr "Add Directive Below"
 msgid "Add Location"
 msgid "Add Location"
 msgstr "Add Location"
 msgstr "Add Location"
 
 
-#: src/routes/index.ts:70 src/views/domain/DomainAdd.vue:91
+#: src/routes/index.ts:71 src/views/domain/DomainAdd.vue:91
 msgid "Add Site"
 msgid "Add Site"
 msgstr "Add Site"
 msgstr "Add Site"
 
 
@@ -75,13 +75,19 @@ msgstr ""
 msgid "Arch"
 msgid "Arch"
 msgstr ""
 msgstr ""
 
 
+#: src/components/Notification/Notification.vue:86
+#: src/views/notification/Notification.vue:73
+#, fuzzy
+msgid "Are you sure you want to clear all notifications?"
+msgstr "Are you sure you want to remove this directive?"
+
 #: src/components/ChatGPT/ChatGPT.vue:279
 #: src/components/ChatGPT/ChatGPT.vue:279
 #, fuzzy
 #, fuzzy
 msgid "Are you sure you want to clear the record of chat?"
 msgid "Are you sure you want to clear the record of chat?"
 msgstr "Are you sure you want to remove this directive?"
 msgstr "Are you sure you want to remove this directive?"
 
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:347
-#: src/views/domain/DomainList.vue:145
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:353
+#: src/views/domain/DomainList.vue:144
 #, fuzzy
 #, fuzzy
 msgid "Are you sure you want to delete?"
 msgid "Are you sure you want to delete?"
 msgstr "Are you sure you want to remove this directive?"
 msgstr "Are you sure you want to remove this directive?"
@@ -124,7 +130,7 @@ msgstr "Auto-renewal disabled for %{name}"
 msgid "Auto-renewal enabled for %{name}"
 msgid "Auto-renewal enabled for %{name}"
 msgstr "Auto-renewal enabled for %{name}"
 msgstr "Auto-renewal enabled for %{name}"
 
 
-#: src/views/certificate/CertificateEditor.vue:168
+#: src/views/certificate/CertificateEditor.vue:205
 #: src/views/config/Config.vue:75 src/views/config/ConfigEdit.vue:89
 #: src/views/config/Config.vue:75 src/views/config/ConfigEdit.vue:89
 #: src/views/domain/DomainEdit.vue:261 src/views/nginx_log/NginxLog.vue:170
 #: src/views/domain/DomainEdit.vue:261 src/views/nginx_log/NginxLog.vue:170
 msgid "Back"
 msgid "Back"
@@ -166,7 +172,7 @@ msgstr ""
 
 
 #: src/components/ChatGPT/ChatGPT.vue:260
 #: src/components/ChatGPT/ChatGPT.vue:260
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:58
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:58
-#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:139
+#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:143
 #: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:103
 #: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:103
 #: src/views/domain/cert/components/ObtainCert.vue:150
 #: src/views/domain/cert/components/ObtainCert.vue:150
 #: src/views/domain/components/Deploy.vue:24
 #: src/views/domain/components/Deploy.vue:24
@@ -184,17 +190,17 @@ msgstr "Certificate has expired"
 msgid "Certificate is valid"
 msgid "Certificate is valid"
 msgstr "Certificate is valid"
 msgstr "Certificate is valid"
 
 
-#: src/views/certificate/CertificateEditor.vue:119
+#: src/views/certificate/CertificateEditor.vue:127
 #: src/views/domain/cert/Cert.vue:35
 #: src/views/domain/cert/Cert.vue:35
 msgid "Certificate Status"
 msgid "Certificate Status"
 msgstr "Certificate Status"
 msgstr "Certificate Status"
 
 
-#: src/routes/index.ts:100 src/views/certificate/Certificate.vue:109
+#: src/routes/index.ts:101 src/views/certificate/Certificate.vue:117
 #, fuzzy
 #, fuzzy
 msgid "Certificates"
 msgid "Certificates"
 msgstr "Certificate Status"
 msgstr "Certificate Status"
 
 
-#: src/routes/index.ts:109
+#: src/routes/index.ts:110
 #, fuzzy
 #, fuzzy
 msgid "Certificates List"
 msgid "Certificates List"
 msgstr "Certificate is valid"
 msgstr "Certificate is valid"
@@ -230,9 +236,17 @@ msgid "Cleaning environment variables"
 msgstr ""
 msgstr ""
 
 
 #: src/components/ChatGPT/ChatGPT.vue:283
 #: src/components/ChatGPT/ChatGPT.vue:283
+#: src/components/Notification/Notification.vue:91
+#: src/views/notification/Notification.vue:78
 msgid "Clear"
 msgid "Clear"
 msgstr ""
 msgstr ""
 
 
+#: src/components/Notification/Notification.vue:42
+#: src/views/notification/Notification.vue:47
+#, fuzzy
+msgid "Cleared successfully"
+msgstr "Disabled successfully"
+
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:97
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:97
 #: src/views/domain/ngx_conf/LocationEditor.vue:121
 #: src/views/domain/ngx_conf/LocationEditor.vue:121
 #: src/views/domain/ngx_conf/LocationEditor.vue:90
 #: src/views/domain/ngx_conf/LocationEditor.vue:90
@@ -287,7 +301,7 @@ msgstr "CPU:"
 msgid "Create Another"
 msgid "Create Another"
 msgstr "Create Another"
 msgstr "Create Another"
 
 
-#: src/views/user/User.vue:34
+#: src/views/notification/Notification.vue:32 src/views/user/User.vue:34
 msgid "Created at"
 msgid "Created at"
 msgstr "Created at"
 msgstr "Created at"
 
 
@@ -312,7 +326,7 @@ msgstr ""
 msgid "Custom"
 msgid "Custom"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:51
+#: src/routes/index.ts:52
 msgid "Dashboard"
 msgid "Dashboard"
 msgstr "Dashboard"
 msgstr "Dashboard"
 
 
@@ -320,7 +334,7 @@ msgstr "Dashboard"
 msgid "Database (Optional, default: database)"
 msgid "Database (Optional, default: database)"
 msgstr "Database (Optional, default: database)"
 msgstr "Database (Optional, default: database)"
 
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:354
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:360
 #: src/views/domain/DomainList.vue:153
 #: src/views/domain/DomainList.vue:153
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:259
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:259
 msgid "Delete"
 msgid "Delete"
@@ -358,6 +372,10 @@ msgstr "Saved successfully"
 msgid "Description"
 msgid "Description"
 msgstr ""
 msgstr ""
 
 
+#: src/views/notification/Notification.vue:28
+msgid "Details"
+msgstr ""
+
 #: src/views/system/About.vue:25
 #: src/views/system/About.vue:25
 msgid "Development Mode"
 msgid "Development Mode"
 msgstr "Development Mode"
 msgstr "Development Mode"
@@ -385,7 +403,7 @@ msgid "Disable auto-renewal failed for %{name}"
 msgstr "Disable auto-renewal failed for %{name}"
 msgstr "Disable auto-renewal failed for %{name}"
 
 
 #: src/views/domain/cert/ChangeCert.vue:48 src/views/domain/DomainEdit.vue:190
 #: src/views/domain/cert/ChangeCert.vue:48 src/views/domain/DomainEdit.vue:190
-#: src/views/domain/DomainList.vue:123
+#: src/views/domain/DomainList.vue:122
 msgid "Disabled"
 msgid "Disabled"
 msgstr "Disabled"
 msgstr "Disabled"
 
 
@@ -398,7 +416,7 @@ msgstr "Disabled successfully"
 msgid "Disk IO"
 msgid "Disk IO"
 msgstr "Disk IO"
 msgstr "Disk IO"
 
 
-#: src/routes/index.ts:130 src/views/certificate/DNSCredential.vue:39
+#: src/routes/index.ts:131 src/views/certificate/DNSCredential.vue:39
 msgid "DNS Credentials"
 msgid "DNS Credentials"
 msgstr ""
 msgstr ""
 
 
@@ -446,7 +464,7 @@ msgstr "Are you sure you want to remove this directive?"
 msgid "Domain Config Created Successfully"
 msgid "Domain Config Created Successfully"
 msgstr "Domain Config Created Successfully"
 msgstr "Domain Config Created Successfully"
 
 
-#: src/views/certificate/CertificateEditor.vue:103
+#: src/views/certificate/CertificateEditor.vue:111
 msgid "Domains list is empty, try to reopen Auto Cert for %{config}"
 msgid "Domains list is empty, try to reopen Auto Cert for %{config}"
 msgstr ""
 msgstr ""
 
 
@@ -463,7 +481,7 @@ msgid "Dry run mode enabled"
 msgstr ""
 msgstr ""
 
 
 #: src/views/domain/components/SiteDuplicate.vue:128
 #: src/views/domain/components/SiteDuplicate.vue:128
-#: src/views/domain/DomainList.vue:139
+#: src/views/domain/DomainList.vue:138
 msgid "Duplicate"
 msgid "Duplicate"
 msgstr ""
 msgstr ""
 
 
@@ -491,11 +509,11 @@ msgstr "Saved successfully"
 msgid "Edit %{n}"
 msgid "Edit %{n}"
 msgstr "Edit %{n}"
 msgstr "Edit %{n}"
 
 
-#: src/routes/index.ts:92 src/views/config/ConfigEdit.vue:83
+#: src/routes/index.ts:93 src/views/config/ConfigEdit.vue:83
 msgid "Edit Configuration"
 msgid "Edit Configuration"
 msgstr "Edit Configuration"
 msgstr "Edit Configuration"
 
 
-#: src/routes/index.ts:74
+#: src/routes/index.ts:75
 msgid "Edit Site"
 msgid "Edit Site"
 msgstr "Edit Site"
 msgstr "Edit Site"
 
 
@@ -536,7 +554,7 @@ msgstr "Enable TLS"
 #: src/views/domain/cert/ChangeCert.vue:44
 #: src/views/domain/cert/ChangeCert.vue:44
 #: src/views/domain/components/Deploy.vue:89
 #: src/views/domain/components/Deploy.vue:89
 #: src/views/domain/components/RightSettings.vue:75
 #: src/views/domain/components/RightSettings.vue:75
-#: src/views/domain/DomainEdit.vue:184 src/views/domain/DomainList.vue:131
+#: src/views/domain/DomainEdit.vue:184 src/views/domain/DomainList.vue:130
 msgid "Enabled"
 msgid "Enabled"
 msgstr "Enabled"
 msgstr "Enabled"
 
 
@@ -550,7 +568,7 @@ msgstr "Enabled successfully"
 msgid "Encrypt website with Let's Encrypt"
 msgid "Encrypt website with Let's Encrypt"
 msgstr "Encrypt website with Let's Encrypt"
 msgstr "Encrypt website with Let's Encrypt"
 
 
-#: src/routes/index.ts:168 src/views/environment/Environment.vue:113
+#: src/routes/index.ts:169 src/views/environment/Environment.vue:113
 msgid "Environment"
 msgid "Environment"
 msgstr ""
 msgstr ""
 
 
@@ -559,11 +577,11 @@ msgstr ""
 msgid "Environments"
 msgid "Environments"
 msgstr "Comments"
 msgstr "Comments"
 
 
-#: src/views/config/InspectConfig.vue:47
+#: src/constants/index.ts:17 src/views/config/InspectConfig.vue:47
 msgid "Error"
 msgid "Error"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:155 src/views/domain/ngx_conf/LogEntry.vue:86
+#: src/routes/index.ts:156 src/views/domain/ngx_conf/LogEntry.vue:86
 msgid "Error Logs"
 msgid "Error Logs"
 msgstr ""
 msgstr ""
 
 
@@ -571,7 +589,7 @@ msgstr ""
 msgid "Executable Path"
 msgid "Executable Path"
 msgstr ""
 msgstr ""
 
 
-#: src/views/certificate/Certificate.vue:84
+#: src/views/certificate/Certificate.vue:92
 msgid "Expired"
 msgid "Expired"
 msgstr ""
 msgstr ""
 
 
@@ -637,6 +655,11 @@ msgstr "Save error %{msg}"
 msgid "Format successfully"
 msgid "Format successfully"
 msgstr "Saved successfully"
 msgstr "Saved successfully"
 
 
+#: src/views/certificate/Certificate.vue:45
+#, fuzzy
+msgid "General Certificate"
+msgstr "Certificate is valid"
+
 #: src/components/StdDesign/StdDataEntry/components/StdPassword.vue:60
 #: src/components/StdDesign/StdDataEntry/components/StdPassword.vue:60
 msgid "Generate"
 msgid "Generate"
 msgstr ""
 msgstr ""
@@ -674,7 +697,7 @@ msgstr ""
 msgid "GPT-4-Turbo"
 msgid "GPT-4-Turbo"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:44
+#: src/routes/index.ts:45
 msgid "Home"
 msgid "Home"
 msgstr "Home"
 msgstr "Home"
 
 
@@ -694,6 +717,10 @@ msgstr ""
 msgid "HTTP01"
 msgid "HTTP01"
 msgstr ""
 msgstr ""
 
 
+#: src/constants/index.ts:19
+msgid "Info"
+msgstr ""
+
 #: src/language/constants.ts:27
 #: src/language/constants.ts:27
 msgid "Initial core upgrader error"
 msgid "Initial core upgrader error"
 msgstr ""
 msgstr ""
@@ -702,7 +729,7 @@ msgstr ""
 msgid "Initialing core upgrader"
 msgid "Initialing core upgrader"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:211 src/views/other/Install.vue:139
+#: src/routes/index.ts:220 src/views/other/Install.vue:139
 msgid "Install"
 msgid "Install"
 msgstr "Install"
 msgstr "Install"
 
 
@@ -764,12 +791,12 @@ msgstr "Location"
 msgid "Locations"
 msgid "Locations"
 msgstr "Locations"
 msgstr "Locations"
 
 
-#: src/views/certificate/CertificateEditor.vue:156
+#: src/views/certificate/CertificateEditor.vue:193
 #, fuzzy
 #, fuzzy
 msgid "Log"
 msgid "Log"
 msgstr "Login"
 msgstr "Login"
 
 
-#: src/routes/index.ts:217 src/views/other/Login.vue:147
+#: src/routes/index.ts:226 src/views/other/Login.vue:147
 msgid "Login"
 msgid "Login"
 msgstr "Login"
 msgstr "Login"
 
 
@@ -777,7 +804,7 @@ msgstr "Login"
 msgid "Login successful"
 msgid "Login successful"
 msgstr "Login successful"
 msgstr "Login successful"
 
 
-#: src/layouts/HeaderLayout.vue:21
+#: src/layouts/HeaderLayout.vue:22
 msgid "Logout successful"
 msgid "Logout successful"
 msgstr "Logout successful"
 msgstr "Logout successful"
 
 
@@ -790,18 +817,23 @@ msgstr ""
 "Make sure you have configured a reverse proxy for .well-known directory to "
 "Make sure you have configured a reverse proxy for .well-known directory to "
 "HTTPChallengePort (default: 9180) before getting the certificate."
 "HTTPChallengePort (default: 9180) before getting the certificate."
 
 
-#: src/routes/index.ts:83
+#: src/routes/index.ts:84
 msgid "Manage Configs"
 msgid "Manage Configs"
 msgstr "Manage Configs"
 msgstr "Manage Configs"
 
 
-#: src/routes/index.ts:58 src/views/domain/DomainList.vue:102
+#: src/routes/index.ts:59 src/views/domain/DomainList.vue:102
 msgid "Manage Sites"
 msgid "Manage Sites"
 msgstr "Manage Sites"
 msgstr "Manage Sites"
 
 
-#: src/routes/index.ts:176 src/views/user/User.vue:53
+#: src/routes/index.ts:185 src/views/user/User.vue:53
 msgid "Manage Users"
 msgid "Manage Users"
 msgstr "Manage Users"
 msgstr "Manage Users"
 
 
+#: src/views/certificate/Certificate.vue:44
+#, fuzzy
+msgid "Managed Certificate"
+msgstr "Certificate is valid"
+
 #: src/views/dashboard/ServerAnalytic.vue:220
 #: src/views/dashboard/ServerAnalytic.vue:220
 msgid "Memory"
 msgid "Memory"
 msgstr "Memory"
 msgstr "Memory"
@@ -811,13 +843,13 @@ msgid "Memory and Storage"
 msgstr "Memory and Storage"
 msgstr "Memory and Storage"
 
 
 #: src/components/ChatGPT/ChatGPT.vue:256
 #: src/components/ChatGPT/ChatGPT.vue:256
-#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:137
+#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:141
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:336
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:336
 #, fuzzy
 #, fuzzy
 msgid "Modify"
 msgid "Modify"
 msgstr "Modify Config"
 msgstr "Modify Config"
 
 
-#: src/routes/index.ts:114 src/views/certificate/CertificateEditor.vue:76
+#: src/routes/index.ts:115 src/views/certificate/CertificateEditor.vue:84
 #, fuzzy
 #, fuzzy
 msgid "Modify Certificate"
 msgid "Modify Certificate"
 msgstr "Certificate Status"
 msgstr "Certificate Status"
@@ -832,6 +864,7 @@ msgid "Multi-line Directive"
 msgstr "Single Directive"
 msgstr "Single Directive"
 
 
 #: src/views/certificate/Certificate.vue:22
 #: src/views/certificate/Certificate.vue:22
+#: src/views/certificate/CertificateEditor.vue:145
 #: src/views/certificate/DNSCredential.vue:13 src/views/config/config.ts:9
 #: src/views/certificate/DNSCredential.vue:13 src/views/config/config.ts:9
 #: src/views/domain/cert/ChangeCert.vue:21
 #: src/views/domain/cert/ChangeCert.vue:21
 #: src/views/domain/components/RightSettings.vue:81
 #: src/views/domain/components/RightSettings.vue:81
@@ -886,7 +919,7 @@ msgstr ""
 msgid "Nginx Error Log Path"
 msgid "Nginx Error Log Path"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:145 src/views/nginx_log/NginxLog.vue:145
+#: src/routes/index.ts:146 src/views/nginx_log/NginxLog.vue:145
 msgid "Nginx Log"
 msgid "Nginx Log"
 msgstr ""
 msgstr ""
 
 
@@ -901,10 +934,12 @@ msgid "Nginx restarted successfully"
 msgstr "Saved successfully"
 msgstr "Saved successfully"
 
 
 #: src/components/ChatGPT/ChatGPT.vue:277
 #: src/components/ChatGPT/ChatGPT.vue:277
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:345
-#: src/views/domain/DomainList.vue:143
+#: src/components/Notification/Notification.vue:84
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:351
+#: src/views/domain/DomainList.vue:142
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:81
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:81
 #: src/views/domain/ngx_conf/LocationEditor.vue:76
 #: src/views/domain/ngx_conf/LocationEditor.vue:76
+#: src/views/notification/Notification.vue:71
 msgid "No"
 msgid "No"
 msgstr "No"
 msgstr "No"
 
 
@@ -912,11 +947,11 @@ msgstr "No"
 msgid "Node Secret"
 msgid "Node Secret"
 msgstr ""
 msgstr ""
 
 
-#: src/views/certificate/Certificate.vue:90
+#: src/views/certificate/Certificate.vue:98
 msgid "Not After"
 msgid "Not After"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:223 src/routes/index.ts:225
+#: src/routes/index.ts:232 src/routes/index.ts:234
 msgid "Not Found"
 msgid "Not Found"
 msgstr "Not Found"
 msgstr "Not Found"
 
 
@@ -929,6 +964,16 @@ msgstr "Not Valid Before: %{date}"
 msgid "Note"
 msgid "Note"
 msgstr ""
 msgstr ""
 
 
+#: src/views/notification/Notification.vue:63
+#, fuzzy
+msgid "Notification"
+msgstr "Certificate is valid"
+
+#: src/components/Notification/Notification.vue:82 src/routes/index.ts:177
+#, fuzzy
+msgid "Notifications"
+msgstr "Certificate is valid"
+
 #: src/views/domain/cert/components/ObtainCert.vue:191
 #: src/views/domain/cert/components/ObtainCert.vue:191
 #, fuzzy
 #, fuzzy
 msgid "Obtain certificate"
 msgid "Obtain certificate"
@@ -945,16 +990,18 @@ msgid "Offline"
 msgstr ""
 msgstr ""
 
 
 #: src/components/ChatGPT/ChatGPT.vue:278
 #: src/components/ChatGPT/ChatGPT.vue:278
+#: src/components/Notification/Notification.vue:85
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:59
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:59
-#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:140
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:346
+#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:144
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:352
 #: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:104
 #: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:104
 #: src/views/domain/cert/components/ObtainCert.vue:149
 #: src/views/domain/cert/components/ObtainCert.vue:149
 #: src/views/domain/components/Deploy.vue:23
 #: src/views/domain/components/Deploy.vue:23
 #: src/views/domain/components/RightSettings.vue:51
 #: src/views/domain/components/RightSettings.vue:51
-#: src/views/domain/DomainList.vue:144
+#: src/views/domain/DomainList.vue:143
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:215
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:215
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:47
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:47
+#: src/views/notification/Notification.vue:72
 msgid "OK"
 msgid "OK"
 msgstr ""
 msgstr ""
 
 
@@ -1059,7 +1106,7 @@ msgstr ""
 msgid "Pre-release"
 msgid "Pre-release"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:184 src/views/preference/Preference.vue:85
+#: src/routes/index.ts:193 src/views/preference/Preference.vue:85
 msgid "Preference"
 msgid "Preference"
 msgstr ""
 msgstr ""
 
 
@@ -1124,12 +1171,27 @@ msgstr ""
 msgid "Reloading nginx"
 msgid "Reloading nginx"
 msgstr ""
 msgstr ""
 
 
+#: src/components/Notification/Notification.vue:52
+#, fuzzy
+msgid "Removed successfully"
+msgstr "Saved successfully"
+
 #: src/views/certificate/RenewCert.vue:43
 #: src/views/certificate/RenewCert.vue:43
 #: src/views/certificate/RenewCert.vue:47
 #: src/views/certificate/RenewCert.vue:47
 #, fuzzy
 #, fuzzy
 msgid "Renew Certificate"
 msgid "Renew Certificate"
 msgstr "Certificate is valid"
 msgstr "Certificate is valid"
 
 
+#: src/language/constants.ts:38
+#, fuzzy
+msgid "Renew Certificate Error"
+msgstr "Certificate is valid"
+
+#: src/language/constants.ts:37
+#, fuzzy
+msgid "Renew Certificate Success"
+msgstr "Certificate is valid"
+
 #: src/views/certificate/RenewCert.vue:25
 #: src/views/certificate/RenewCert.vue:25
 #, fuzzy
 #, fuzzy
 msgid "Renew successfully"
 msgid "Renew successfully"
@@ -1161,7 +1223,7 @@ msgid "Running"
 msgstr ""
 msgstr ""
 
 
 #: src/components/ChatGPT/ChatGPT.vue:259
 #: src/components/ChatGPT/ChatGPT.vue:259
-#: src/views/certificate/CertificateEditor.vue:175
+#: src/views/certificate/CertificateEditor.vue:212
 #: src/views/config/ConfigEdit.vue:98 src/views/domain/DomainEdit.vue:268
 #: src/views/config/ConfigEdit.vue:98 src/views/domain/DomainEdit.vue:268
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:111
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:111
 #: src/views/preference/Preference.vue:113
 #: src/views/preference/Preference.vue:113
@@ -1178,7 +1240,7 @@ msgid "Save error %{msg}"
 msgstr "Save error %{msg}"
 msgstr "Save error %{msg}"
 
 
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:42
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:42
-#: src/views/certificate/CertificateEditor.vue:46
+#: src/views/certificate/CertificateEditor.vue:47
 #: src/views/preference/Preference.vue:58
 #: src/views/preference/Preference.vue:58
 #, fuzzy
 #, fuzzy
 msgid "Save successfully"
 msgid "Save successfully"
@@ -1245,31 +1307,33 @@ msgstr ""
 msgid "Single Directive"
 msgid "Single Directive"
 msgstr "Single Directive"
 msgstr "Single Directive"
 
 
-#: src/routes/index.ts:159
+#: src/routes/index.ts:160
 #, fuzzy
 #, fuzzy
 msgid "Site Logs"
 msgid "Site Logs"
 msgstr "Sites List"
 msgstr "Sites List"
 
 
-#: src/routes/index.ts:66
+#: src/routes/index.ts:67
 msgid "Sites List"
 msgid "Sites List"
 msgstr "Sites List"
 msgstr "Sites List"
 
 
-#: src/views/certificate/CertificateEditor.vue:135
+#: src/views/certificate/CertificateEditor.vue:172
 #, fuzzy
 #, fuzzy
 msgid "SSL Certificate Content"
 msgid "SSL Certificate Content"
 msgstr "Certificate Status"
 msgstr "Certificate Status"
 
 
-#: src/views/certificate/CertificateEditor.vue:142
+#: src/views/certificate/CertificateEditor.vue:179
 #, fuzzy
 #, fuzzy
 msgid "SSL Certificate Key Content"
 msgid "SSL Certificate Key Content"
 msgstr "Certificate Status"
 msgstr "Certificate Status"
 
 
-#: src/views/certificate/Certificate.vue:63
+#: src/views/certificate/Certificate.vue:71
+#: src/views/certificate/CertificateEditor.vue:163
 #, fuzzy
 #, fuzzy
 msgid "SSL Certificate Key Path"
 msgid "SSL Certificate Key Path"
 msgstr "Certificate Status"
 msgstr "Certificate Status"
 
 
-#: src/views/certificate/Certificate.vue:55
+#: src/views/certificate/Certificate.vue:63
+#: src/views/certificate/CertificateEditor.vue:154
 #, fuzzy
 #, fuzzy
 msgid "SSL Certificate Path"
 msgid "SSL Certificate Path"
 msgstr "Certificate Status"
 msgstr "Certificate Status"
@@ -1284,7 +1348,7 @@ msgstr "Login"
 msgid "Stable"
 msgid "Stable"
 msgstr "Enabled"
 msgstr "Enabled"
 
 
-#: src/views/certificate/Certificate.vue:71 src/views/domain/DomainList.vue:25
+#: src/views/certificate/Certificate.vue:79 src/views/domain/DomainList.vue:25
 #: src/views/environment/Environment.vue:78
 #: src/views/environment/Environment.vue:78
 msgid "Status"
 msgid "Status"
 msgstr "Status"
 msgstr "Status"
@@ -1302,6 +1366,10 @@ msgstr "Storage"
 msgid "Subject Name: %{subject}"
 msgid "Subject Name: %{subject}"
 msgstr "Subject Name: %{name}"
 msgstr "Subject Name: %{name}"
 
 
+#: src/constants/index.ts:20
+msgid "Success"
+msgstr ""
+
 #: src/views/dashboard/ServerAnalytic.vue:233
 #: src/views/dashboard/ServerAnalytic.vue:233
 msgid "Swap"
 msgid "Swap"
 msgstr "Swap"
 msgstr "Swap"
@@ -1314,7 +1382,7 @@ msgstr ""
 msgid "Switch to light theme"
 msgid "Switch to light theme"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:192
+#: src/routes/index.ts:201
 msgid "System"
 msgid "System"
 msgstr ""
 msgstr ""
 
 
@@ -1327,7 +1395,7 @@ msgstr "Enabled"
 msgid "Target"
 msgid "Target"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:137 src/views/pty/Terminal.vue:95
+#: src/routes/index.ts:138 src/views/pty/Terminal.vue:95
 msgid "Terminal"
 msgid "Terminal"
 msgstr "Terminal"
 msgstr "Terminal"
 
 
@@ -1361,11 +1429,11 @@ msgstr ""
 msgid "The username or password is incorrect"
 msgid "The username or password is incorrect"
 msgstr ""
 msgstr ""
 
 
-#: src/views/certificate/CertificateEditor.vue:93
+#: src/views/certificate/CertificateEditor.vue:101
 msgid "This Auto Cert item is invalid, please remove it."
 msgid "This Auto Cert item is invalid, please remove it."
 msgstr ""
 msgstr ""
 
 
-#: src/views/certificate/CertificateEditor.vue:83
+#: src/views/certificate/CertificateEditor.vue:91
 msgid "This certificate is managed by Nginx UI"
 msgid "This certificate is managed by Nginx UI"
 msgstr ""
 msgstr ""
 
 
@@ -1373,6 +1441,10 @@ msgstr ""
 msgid "This field should not be empty"
 msgid "This field should not be empty"
 msgstr ""
 msgstr ""
 
 
+#: src/views/notification/Notification.vue:21
+msgid "Title"
+msgstr ""
+
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:42
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:42
 msgid ""
 msgid ""
 "To make sure the certification auto-renewal can work normally, we need to "
 "To make sure the certification auto-renewal can work normally, we need to "
@@ -1382,6 +1454,7 @@ msgid ""
 msgstr ""
 msgstr ""
 
 
 #: src/views/certificate/Certificate.vue:39 src/views/config/config.ts:14
 #: src/views/certificate/Certificate.vue:39 src/views/config/config.ts:14
+#: src/views/notification/Notification.vue:15
 msgid "Type"
 msgid "Type"
 msgstr ""
 msgstr ""
 
 
@@ -1398,7 +1471,7 @@ msgstr "Updated at"
 msgid "Updated successfully"
 msgid "Updated successfully"
 msgstr "Saved successfully"
 msgstr "Saved successfully"
 
 
-#: src/routes/index.ts:203 src/views/system/Upgrade.vue:143
+#: src/routes/index.ts:212 src/views/system/Upgrade.vue:143
 #: src/views/system/Upgrade.vue:235
 #: src/views/system/Upgrade.vue:235
 msgid "Upgrade"
 msgid "Upgrade"
 msgstr ""
 msgstr ""
@@ -1433,7 +1506,7 @@ msgstr "Username"
 msgid "Username (*)"
 msgid "Username (*)"
 msgstr "Username (*)"
 msgstr "Username (*)"
 
 
-#: src/views/certificate/Certificate.vue:80
+#: src/views/certificate/Certificate.vue:88
 msgid "Valid"
 msgid "Valid"
 msgstr ""
 msgstr ""
 
 
@@ -1441,7 +1514,12 @@ msgstr ""
 msgid "View"
 msgid "View"
 msgstr ""
 msgstr ""
 
 
-#: src/views/config/InspectConfig.vue:36
+#: src/components/Notification/Notification.vue:143
+#, fuzzy
+msgid "View all notifications"
+msgstr "Certificate is valid"
+
+#: src/constants/index.ts:18 src/views/config/InspectConfig.vue:36
 #: src/views/domain/cert/components/AutoCertStepOne.vue:23
 #: src/views/domain/cert/components/AutoCertStepOne.vue:23
 #: src/views/domain/DomainAdd.vue:114
 #: src/views/domain/DomainAdd.vue:114
 msgid "Warning"
 msgid "Warning"
@@ -1493,10 +1571,6 @@ msgstr ""
 #~ msgid "Config Name"
 #~ msgid "Config Name"
 #~ msgstr "Configuration Name"
 #~ msgstr "Configuration Name"
 
 
-#, fuzzy
-#~ msgid "Certification"
-#~ msgstr "Certificate is valid"
-
 #, fuzzy
 #, fuzzy
 #~ msgid "Inspect Configurations"
 #~ msgid "Inspect Configurations"
 #~ msgstr "Edit Configuration"
 #~ msgstr "Edit Configuration"

+ 143 - 68
app/src/language/es/app.po

@@ -14,30 +14,30 @@ msgstr ""
 "Plural-Forms: nplurals=2; plural=n != 1;\n"
 "Plural-Forms: nplurals=2; plural=n != 1;\n"
 "X-Generator: Weblate 5.0\n"
 "X-Generator: Weblate 5.0\n"
 
 
-#: src/routes/index.ts:199
+#: src/routes/index.ts:208
 msgid "About"
 msgid "About"
 msgstr "Acerca de"
 msgstr "Acerca de"
 
 
-#: src/routes/index.ts:151 src/views/domain/ngx_conf/LogEntry.vue:78
+#: src/routes/index.ts:152 src/views/domain/ngx_conf/LogEntry.vue:78
 msgid "Access Logs"
 msgid "Access Logs"
 msgstr "Registros de acceso"
 msgstr "Registros de acceso"
 
 
-#: src/views/certificate/Certificate.vue:96
+#: src/views/certificate/Certificate.vue:104
 #: src/views/certificate/DNSCredential.vue:32 src/views/config/config.ts:36
 #: src/views/certificate/DNSCredential.vue:32 src/views/config/config.ts:36
 #: src/views/domain/DomainList.vue:50 src/views/environment/Environment.vue:105
 #: src/views/domain/DomainList.vue:50 src/views/environment/Environment.vue:105
-#: src/views/user/User.vue:46
+#: src/views/notification/Notification.vue:38 src/views/user/User.vue:46
 msgid "Action"
 msgid "Action"
 msgstr "Acción"
 msgstr "Acción"
 
 
-#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:114
-#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:137
+#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:115
+#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:141
+#: src/views/certificate/Certificate.vue:120
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:115
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:115
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:308
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:308
 msgid "Add"
 msgid "Add"
 msgstr "Agregar"
 msgstr "Agregar"
 
 
-#: src/routes/index.ts:122 src/views/certificate/Certificate.vue:115
-#: src/views/certificate/CertificateEditor.vue:76
+#: src/routes/index.ts:123 src/views/certificate/CertificateEditor.vue:84
 #, fuzzy
 #, fuzzy
 msgid "Add Certificate"
 msgid "Add Certificate"
 msgstr "Estado del Certificado"
 msgstr "Estado del Certificado"
@@ -51,7 +51,7 @@ msgstr "Añadir directiva a continuación"
 msgid "Add Location"
 msgid "Add Location"
 msgstr "Agregar Ubicación"
 msgstr "Agregar Ubicación"
 
 
-#: src/routes/index.ts:70 src/views/domain/DomainAdd.vue:91
+#: src/routes/index.ts:71 src/views/domain/DomainAdd.vue:91
 msgid "Add Site"
 msgid "Add Site"
 msgstr "Agregar Sitio"
 msgstr "Agregar Sitio"
 
 
@@ -79,12 +79,18 @@ msgstr "Token de la API"
 msgid "Arch"
 msgid "Arch"
 msgstr "Arquitectura"
 msgstr "Arquitectura"
 
 
+#: src/components/Notification/Notification.vue:86
+#: src/views/notification/Notification.vue:73
+#, fuzzy
+msgid "Are you sure you want to clear all notifications?"
+msgstr "¿Está seguro de que desea borrar el registro del chat?"
+
 #: src/components/ChatGPT/ChatGPT.vue:279
 #: src/components/ChatGPT/ChatGPT.vue:279
 msgid "Are you sure you want to clear the record of chat?"
 msgid "Are you sure you want to clear the record of chat?"
 msgstr "¿Está seguro de que desea borrar el registro del chat?"
 msgstr "¿Está seguro de que desea borrar el registro del chat?"
 
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:347
-#: src/views/domain/DomainList.vue:145
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:353
+#: src/views/domain/DomainList.vue:144
 msgid "Are you sure you want to delete?"
 msgid "Are you sure you want to delete?"
 msgstr "¿Está seguro de que quiere borrar?"
 msgstr "¿Está seguro de que quiere borrar?"
 
 
@@ -125,7 +131,7 @@ msgstr "Renovación automática deshabilitada por %{name}"
 msgid "Auto-renewal enabled for %{name}"
 msgid "Auto-renewal enabled for %{name}"
 msgstr "Renovación automática habilitada por %{name}"
 msgstr "Renovación automática habilitada por %{name}"
 
 
-#: src/views/certificate/CertificateEditor.vue:168
+#: src/views/certificate/CertificateEditor.vue:205
 #: src/views/config/Config.vue:75 src/views/config/ConfigEdit.vue:89
 #: src/views/config/Config.vue:75 src/views/config/ConfigEdit.vue:89
 #: src/views/domain/DomainEdit.vue:261 src/views/nginx_log/NginxLog.vue:170
 #: src/views/domain/DomainEdit.vue:261 src/views/nginx_log/NginxLog.vue:170
 msgid "Back"
 msgid "Back"
@@ -164,7 +170,7 @@ msgstr "Directorio CA"
 
 
 #: src/components/ChatGPT/ChatGPT.vue:260
 #: src/components/ChatGPT/ChatGPT.vue:260
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:58
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:58
-#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:139
+#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:143
 #: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:103
 #: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:103
 #: src/views/domain/cert/components/ObtainCert.vue:150
 #: src/views/domain/cert/components/ObtainCert.vue:150
 #: src/views/domain/components/Deploy.vue:24
 #: src/views/domain/components/Deploy.vue:24
@@ -182,17 +188,17 @@ msgstr "El certificado expiró"
 msgid "Certificate is valid"
 msgid "Certificate is valid"
 msgstr "El certificado es válido"
 msgstr "El certificado es válido"
 
 
-#: src/views/certificate/CertificateEditor.vue:119
+#: src/views/certificate/CertificateEditor.vue:127
 #: src/views/domain/cert/Cert.vue:35
 #: src/views/domain/cert/Cert.vue:35
 msgid "Certificate Status"
 msgid "Certificate Status"
 msgstr "Estado del Certificado"
 msgstr "Estado del Certificado"
 
 
-#: src/routes/index.ts:100 src/views/certificate/Certificate.vue:109
+#: src/routes/index.ts:101 src/views/certificate/Certificate.vue:117
 #, fuzzy
 #, fuzzy
 msgid "Certificates"
 msgid "Certificates"
 msgstr "Estado del Certificado"
 msgstr "Estado del Certificado"
 
 
-#: src/routes/index.ts:109
+#: src/routes/index.ts:110
 #, fuzzy
 #, fuzzy
 msgid "Certificates List"
 msgid "Certificates List"
 msgstr "Lista de Certificados"
 msgstr "Lista de Certificados"
@@ -227,9 +233,17 @@ msgid "Cleaning environment variables"
 msgstr "Borrar las variables de entorno"
 msgstr "Borrar las variables de entorno"
 
 
 #: src/components/ChatGPT/ChatGPT.vue:283
 #: src/components/ChatGPT/ChatGPT.vue:283
+#: src/components/Notification/Notification.vue:91
+#: src/views/notification/Notification.vue:78
 msgid "Clear"
 msgid "Clear"
 msgstr "Limpiar"
 msgstr "Limpiar"
 
 
+#: src/components/Notification/Notification.vue:42
+#: src/views/notification/Notification.vue:47
+#, fuzzy
+msgid "Cleared successfully"
+msgstr "Desactivado con éxito"
+
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:97
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:97
 #: src/views/domain/ngx_conf/LocationEditor.vue:121
 #: src/views/domain/ngx_conf/LocationEditor.vue:121
 #: src/views/domain/ngx_conf/LocationEditor.vue:90
 #: src/views/domain/ngx_conf/LocationEditor.vue:90
@@ -283,7 +297,7 @@ msgstr "CPU:"
 msgid "Create Another"
 msgid "Create Another"
 msgstr "Crear otro"
 msgstr "Crear otro"
 
 
-#: src/views/user/User.vue:34
+#: src/views/notification/Notification.vue:32 src/views/user/User.vue:34
 msgid "Created at"
 msgid "Created at"
 msgstr "Creado el"
 msgstr "Creado el"
 
 
@@ -308,7 +322,7 @@ msgstr "Versión actual"
 msgid "Custom"
 msgid "Custom"
 msgstr "Personalizado"
 msgstr "Personalizado"
 
 
-#: src/routes/index.ts:51
+#: src/routes/index.ts:52
 msgid "Dashboard"
 msgid "Dashboard"
 msgstr "Panel"
 msgstr "Panel"
 
 
@@ -316,7 +330,7 @@ msgstr "Panel"
 msgid "Database (Optional, default: database)"
 msgid "Database (Optional, default: database)"
 msgstr "Base de datos (Opcional, default: database)"
 msgstr "Base de datos (Opcional, default: database)"
 
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:354
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:360
 #: src/views/domain/DomainList.vue:153
 #: src/views/domain/DomainList.vue:153
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:259
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:259
 msgid "Delete"
 msgid "Delete"
@@ -353,6 +367,10 @@ msgstr "Desplegado con éxito"
 msgid "Description"
 msgid "Description"
 msgstr "Descripción"
 msgstr "Descripción"
 
 
+#: src/views/notification/Notification.vue:28
+msgid "Details"
+msgstr ""
+
 #: src/views/system/About.vue:25
 #: src/views/system/About.vue:25
 msgid "Development Mode"
 msgid "Development Mode"
 msgstr "Modo de desarrollo"
 msgstr "Modo de desarrollo"
@@ -380,7 +398,7 @@ msgid "Disable auto-renewal failed for %{name}"
 msgstr "No se pudo desactivar la renovación automática por %{name}"
 msgstr "No se pudo desactivar la renovación automática por %{name}"
 
 
 #: src/views/domain/cert/ChangeCert.vue:48 src/views/domain/DomainEdit.vue:190
 #: src/views/domain/cert/ChangeCert.vue:48 src/views/domain/DomainEdit.vue:190
-#: src/views/domain/DomainList.vue:123
+#: src/views/domain/DomainList.vue:122
 msgid "Disabled"
 msgid "Disabled"
 msgstr "Desactivado"
 msgstr "Desactivado"
 
 
@@ -393,7 +411,7 @@ msgstr "Desactivado con éxito"
 msgid "Disk IO"
 msgid "Disk IO"
 msgstr "I/O del disco"
 msgstr "I/O del disco"
 
 
-#: src/routes/index.ts:130 src/views/certificate/DNSCredential.vue:39
+#: src/routes/index.ts:131 src/views/certificate/DNSCredential.vue:39
 msgid "DNS Credentials"
 msgid "DNS Credentials"
 msgstr "Credenciales de DNS"
 msgstr "Credenciales de DNS"
 
 
@@ -436,7 +454,7 @@ msgstr "¿Quieres eliminar este servidor?"
 msgid "Domain Config Created Successfully"
 msgid "Domain Config Created Successfully"
 msgstr "Configuración de dominio creada con éxito"
 msgstr "Configuración de dominio creada con éxito"
 
 
-#: src/views/certificate/CertificateEditor.vue:103
+#: src/views/certificate/CertificateEditor.vue:111
 #, fuzzy
 #, fuzzy
 msgid "Domains list is empty, try to reopen Auto Cert for %{config}"
 msgid "Domains list is empty, try to reopen Auto Cert for %{config}"
 msgstr ""
 msgstr ""
@@ -456,7 +474,7 @@ msgid "Dry run mode enabled"
 msgstr "Modo de ejecución de prueba habilitado"
 msgstr "Modo de ejecución de prueba habilitado"
 
 
 #: src/views/domain/components/SiteDuplicate.vue:128
 #: src/views/domain/components/SiteDuplicate.vue:128
-#: src/views/domain/DomainList.vue:139
+#: src/views/domain/DomainList.vue:138
 msgid "Duplicate"
 msgid "Duplicate"
 msgstr "Duplicar"
 msgstr "Duplicar"
 
 
@@ -480,11 +498,11 @@ msgstr "Duplicado con éxito a local"
 msgid "Edit %{n}"
 msgid "Edit %{n}"
 msgstr "Editar %{n}"
 msgstr "Editar %{n}"
 
 
-#: src/routes/index.ts:92 src/views/config/ConfigEdit.vue:83
+#: src/routes/index.ts:93 src/views/config/ConfigEdit.vue:83
 msgid "Edit Configuration"
 msgid "Edit Configuration"
 msgstr "Editar Configuración"
 msgstr "Editar Configuración"
 
 
-#: src/routes/index.ts:74
+#: src/routes/index.ts:75
 msgid "Edit Site"
 msgid "Edit Site"
 msgstr "Editar Sitio"
 msgstr "Editar Sitio"
 
 
@@ -524,7 +542,7 @@ msgstr "Habilitar TLS"
 #: src/views/domain/cert/ChangeCert.vue:44
 #: src/views/domain/cert/ChangeCert.vue:44
 #: src/views/domain/components/Deploy.vue:89
 #: src/views/domain/components/Deploy.vue:89
 #: src/views/domain/components/RightSettings.vue:75
 #: src/views/domain/components/RightSettings.vue:75
-#: src/views/domain/DomainEdit.vue:184 src/views/domain/DomainList.vue:131
+#: src/views/domain/DomainEdit.vue:184 src/views/domain/DomainList.vue:130
 msgid "Enabled"
 msgid "Enabled"
 msgstr "Habilitado"
 msgstr "Habilitado"
 
 
@@ -538,7 +556,7 @@ msgstr "Habilitado con éxito"
 msgid "Encrypt website with Let's Encrypt"
 msgid "Encrypt website with Let's Encrypt"
 msgstr "Encriptar sitio web con Let's Encrypt"
 msgstr "Encriptar sitio web con Let's Encrypt"
 
 
-#: src/routes/index.ts:168 src/views/environment/Environment.vue:113
+#: src/routes/index.ts:169 src/views/environment/Environment.vue:113
 msgid "Environment"
 msgid "Environment"
 msgstr "Entorno"
 msgstr "Entorno"
 
 
@@ -546,11 +564,11 @@ msgstr "Entorno"
 msgid "Environments"
 msgid "Environments"
 msgstr "Entornos"
 msgstr "Entornos"
 
 
-#: src/views/config/InspectConfig.vue:47
+#: src/constants/index.ts:17 src/views/config/InspectConfig.vue:47
 msgid "Error"
 msgid "Error"
 msgstr "Error"
 msgstr "Error"
 
 
-#: src/routes/index.ts:155 src/views/domain/ngx_conf/LogEntry.vue:86
+#: src/routes/index.ts:156 src/views/domain/ngx_conf/LogEntry.vue:86
 msgid "Error Logs"
 msgid "Error Logs"
 msgstr "Registros de acceso"
 msgstr "Registros de acceso"
 
 
@@ -558,7 +576,7 @@ msgstr "Registros de acceso"
 msgid "Executable Path"
 msgid "Executable Path"
 msgstr "Ruta ejecutable"
 msgstr "Ruta ejecutable"
 
 
-#: src/views/certificate/Certificate.vue:84
+#: src/views/certificate/Certificate.vue:92
 msgid "Expired"
 msgid "Expired"
 msgstr ""
 msgstr ""
 
 
@@ -623,6 +641,11 @@ msgstr "Error de formato %{msg}"
 msgid "Format successfully"
 msgid "Format successfully"
 msgstr "Formateado correctamente"
 msgstr "Formateado correctamente"
 
 
+#: src/views/certificate/Certificate.vue:45
+#, fuzzy
+msgid "General Certificate"
+msgstr "Cambiar Certificado"
+
 #: src/components/StdDesign/StdDataEntry/components/StdPassword.vue:60
 #: src/components/StdDesign/StdDataEntry/components/StdPassword.vue:60
 msgid "Generate"
 msgid "Generate"
 msgstr "Generar"
 msgstr "Generar"
@@ -660,7 +683,7 @@ msgstr "GPT-4-32K"
 msgid "GPT-4-Turbo"
 msgid "GPT-4-Turbo"
 msgstr "GPT-3.5-Turbo"
 msgstr "GPT-3.5-Turbo"
 
 
-#: src/routes/index.ts:44
+#: src/routes/index.ts:45
 msgid "Home"
 msgid "Home"
 msgstr "Inicio"
 msgstr "Inicio"
 
 
@@ -680,6 +703,10 @@ msgstr "Puerto HTTP"
 msgid "HTTP01"
 msgid "HTTP01"
 msgstr "HTTP01"
 msgstr "HTTP01"
 
 
+#: src/constants/index.ts:19
+msgid "Info"
+msgstr ""
+
 #: src/language/constants.ts:27
 #: src/language/constants.ts:27
 msgid "Initial core upgrader error"
 msgid "Initial core upgrader error"
 msgstr "Error de actualización de kernel inicial"
 msgstr "Error de actualización de kernel inicial"
@@ -688,7 +715,7 @@ msgstr "Error de actualización de kernel inicial"
 msgid "Initialing core upgrader"
 msgid "Initialing core upgrader"
 msgstr "Inicializando la actualización del kernel"
 msgstr "Inicializando la actualización del kernel"
 
 
-#: src/routes/index.ts:211 src/views/other/Install.vue:139
+#: src/routes/index.ts:220 src/views/other/Install.vue:139
 msgid "Install"
 msgid "Install"
 msgstr "Instalar"
 msgstr "Instalar"
 
 
@@ -747,12 +774,12 @@ msgstr "Ubicación"
 msgid "Locations"
 msgid "Locations"
 msgstr "Ubicaciones"
 msgstr "Ubicaciones"
 
 
-#: src/views/certificate/CertificateEditor.vue:156
+#: src/views/certificate/CertificateEditor.vue:193
 #, fuzzy
 #, fuzzy
 msgid "Log"
 msgid "Log"
 msgstr "Acceso"
 msgstr "Acceso"
 
 
-#: src/routes/index.ts:217 src/views/other/Login.vue:147
+#: src/routes/index.ts:226 src/views/other/Login.vue:147
 msgid "Login"
 msgid "Login"
 msgstr "Acceso"
 msgstr "Acceso"
 
 
@@ -760,7 +787,7 @@ msgstr "Acceso"
 msgid "Login successful"
 msgid "Login successful"
 msgstr "Acceso exitoso"
 msgstr "Acceso exitoso"
 
 
-#: src/layouts/HeaderLayout.vue:21
+#: src/layouts/HeaderLayout.vue:22
 msgid "Logout successful"
 msgid "Logout successful"
 msgstr "Cierre de sesión exitoso"
 msgstr "Cierre de sesión exitoso"
 
 
@@ -772,18 +799,23 @@ msgstr ""
 "Asegúrese de haber configurado un proxy reverso para el directorio .well-"
 "Asegúrese de haber configurado un proxy reverso para el directorio .well-"
 "known en HTTPChallengePort antes de obtener el certificado."
 "known en HTTPChallengePort antes de obtener el certificado."
 
 
-#: src/routes/index.ts:83
+#: src/routes/index.ts:84
 msgid "Manage Configs"
 msgid "Manage Configs"
 msgstr "Administrar configuraciones"
 msgstr "Administrar configuraciones"
 
 
-#: src/routes/index.ts:58 src/views/domain/DomainList.vue:102
+#: src/routes/index.ts:59 src/views/domain/DomainList.vue:102
 msgid "Manage Sites"
 msgid "Manage Sites"
 msgstr "Administrar sitios"
 msgstr "Administrar sitios"
 
 
-#: src/routes/index.ts:176 src/views/user/User.vue:53
+#: src/routes/index.ts:185 src/views/user/User.vue:53
 msgid "Manage Users"
 msgid "Manage Users"
 msgstr "Administrar usuarios"
 msgstr "Administrar usuarios"
 
 
+#: src/views/certificate/Certificate.vue:44
+#, fuzzy
+msgid "Managed Certificate"
+msgstr "Cambiar Certificado"
+
 #: src/views/dashboard/ServerAnalytic.vue:220
 #: src/views/dashboard/ServerAnalytic.vue:220
 msgid "Memory"
 msgid "Memory"
 msgstr "Memoria"
 msgstr "Memoria"
@@ -793,12 +825,12 @@ msgid "Memory and Storage"
 msgstr "Memoria y almacenamiento"
 msgstr "Memoria y almacenamiento"
 
 
 #: src/components/ChatGPT/ChatGPT.vue:256
 #: src/components/ChatGPT/ChatGPT.vue:256
-#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:137
+#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:141
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:336
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:336
 msgid "Modify"
 msgid "Modify"
 msgstr "Modificar"
 msgstr "Modificar"
 
 
-#: src/routes/index.ts:114 src/views/certificate/CertificateEditor.vue:76
+#: src/routes/index.ts:115 src/views/certificate/CertificateEditor.vue:84
 #, fuzzy
 #, fuzzy
 msgid "Modify Certificate"
 msgid "Modify Certificate"
 msgstr "Estado del Certificado"
 msgstr "Estado del Certificado"
@@ -812,6 +844,7 @@ msgid "Multi-line Directive"
 msgstr "Directiva multilínea"
 msgstr "Directiva multilínea"
 
 
 #: src/views/certificate/Certificate.vue:22
 #: src/views/certificate/Certificate.vue:22
+#: src/views/certificate/CertificateEditor.vue:145
 #: src/views/certificate/DNSCredential.vue:13 src/views/config/config.ts:9
 #: src/views/certificate/DNSCredential.vue:13 src/views/config/config.ts:9
 #: src/views/domain/cert/ChangeCert.vue:21
 #: src/views/domain/cert/ChangeCert.vue:21
 #: src/views/domain/components/RightSettings.vue:81
 #: src/views/domain/components/RightSettings.vue:81
@@ -865,7 +898,7 @@ msgstr "Control de Nginx"
 msgid "Nginx Error Log Path"
 msgid "Nginx Error Log Path"
 msgstr "Ruta de registro de errores de Nginx"
 msgstr "Ruta de registro de errores de Nginx"
 
 
-#: src/routes/index.ts:145 src/views/nginx_log/NginxLog.vue:145
+#: src/routes/index.ts:146 src/views/nginx_log/NginxLog.vue:145
 msgid "Nginx Log"
 msgid "Nginx Log"
 msgstr "Registro Nginx"
 msgstr "Registro Nginx"
 
 
@@ -878,10 +911,12 @@ msgid "Nginx restarted successfully"
 msgstr "Nginx reiniciado con éxito"
 msgstr "Nginx reiniciado con éxito"
 
 
 #: src/components/ChatGPT/ChatGPT.vue:277
 #: src/components/ChatGPT/ChatGPT.vue:277
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:345
-#: src/views/domain/DomainList.vue:143
+#: src/components/Notification/Notification.vue:84
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:351
+#: src/views/domain/DomainList.vue:142
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:81
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:81
 #: src/views/domain/ngx_conf/LocationEditor.vue:76
 #: src/views/domain/ngx_conf/LocationEditor.vue:76
+#: src/views/notification/Notification.vue:71
 msgid "No"
 msgid "No"
 msgstr "No"
 msgstr "No"
 
 
@@ -889,11 +924,11 @@ msgstr "No"
 msgid "Node Secret"
 msgid "Node Secret"
 msgstr "Secreto del nodo"
 msgstr "Secreto del nodo"
 
 
-#: src/views/certificate/Certificate.vue:90
+#: src/views/certificate/Certificate.vue:98
 msgid "Not After"
 msgid "Not After"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:223 src/routes/index.ts:225
+#: src/routes/index.ts:232 src/routes/index.ts:234
 msgid "Not Found"
 msgid "Not Found"
 msgstr "No encontrado"
 msgstr "No encontrado"
 
 
@@ -906,6 +941,16 @@ msgstr "No válido antes: %{date}"
 msgid "Note"
 msgid "Note"
 msgstr "Nota"
 msgstr "Nota"
 
 
+#: src/views/notification/Notification.vue:63
+#, fuzzy
+msgid "Notification"
+msgstr "Certificación"
+
+#: src/components/Notification/Notification.vue:82 src/routes/index.ts:177
+#, fuzzy
+msgid "Notifications"
+msgstr "Certificación"
+
 #: src/views/domain/cert/components/ObtainCert.vue:191
 #: src/views/domain/cert/components/ObtainCert.vue:191
 msgid "Obtain certificate"
 msgid "Obtain certificate"
 msgstr "Obtener certificado"
 msgstr "Obtener certificado"
@@ -921,16 +966,18 @@ msgid "Offline"
 msgstr "Desconectado"
 msgstr "Desconectado"
 
 
 #: src/components/ChatGPT/ChatGPT.vue:278
 #: src/components/ChatGPT/ChatGPT.vue:278
+#: src/components/Notification/Notification.vue:85
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:59
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:59
-#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:140
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:346
+#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:144
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:352
 #: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:104
 #: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:104
 #: src/views/domain/cert/components/ObtainCert.vue:149
 #: src/views/domain/cert/components/ObtainCert.vue:149
 #: src/views/domain/components/Deploy.vue:23
 #: src/views/domain/components/Deploy.vue:23
 #: src/views/domain/components/RightSettings.vue:51
 #: src/views/domain/components/RightSettings.vue:51
-#: src/views/domain/DomainList.vue:144
+#: src/views/domain/DomainList.vue:143
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:215
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:215
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:47
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:47
+#: src/views/notification/Notification.vue:72
 msgid "OK"
 msgid "OK"
 msgstr "OK"
 msgstr "OK"
 
 
@@ -1040,7 +1087,7 @@ msgstr "¡Seleccione al menos un nodo!"
 msgid "Pre-release"
 msgid "Pre-release"
 msgstr "Prelanzamiento"
 msgstr "Prelanzamiento"
 
 
-#: src/routes/index.ts:184 src/views/preference/Preference.vue:85
+#: src/routes/index.ts:193 src/views/preference/Preference.vue:85
 msgid "Preference"
 msgid "Preference"
 msgstr "Configuración"
 msgstr "Configuración"
 
 
@@ -1103,12 +1150,27 @@ msgstr "Recargando"
 msgid "Reloading nginx"
 msgid "Reloading nginx"
 msgstr "Recargando Nginx"
 msgstr "Recargando Nginx"
 
 
+#: src/components/Notification/Notification.vue:52
+#, fuzzy
+msgid "Removed successfully"
+msgstr "Guardado con éxito"
+
 #: src/views/certificate/RenewCert.vue:43
 #: src/views/certificate/RenewCert.vue:43
 #: src/views/certificate/RenewCert.vue:47
 #: src/views/certificate/RenewCert.vue:47
 #, fuzzy
 #, fuzzy
 msgid "Renew Certificate"
 msgid "Renew Certificate"
 msgstr "Cambiar Certificado"
 msgstr "Cambiar Certificado"
 
 
+#: src/language/constants.ts:38
+#, fuzzy
+msgid "Renew Certificate Error"
+msgstr "Cambiar Certificado"
+
+#: src/language/constants.ts:37
+#, fuzzy
+msgid "Renew Certificate Success"
+msgstr "Cambiar Certificado"
+
 #: src/views/certificate/RenewCert.vue:25
 #: src/views/certificate/RenewCert.vue:25
 #, fuzzy
 #, fuzzy
 msgid "Renew successfully"
 msgid "Renew successfully"
@@ -1139,7 +1201,7 @@ msgid "Running"
 msgstr "Corriendo"
 msgstr "Corriendo"
 
 
 #: src/components/ChatGPT/ChatGPT.vue:259
 #: src/components/ChatGPT/ChatGPT.vue:259
-#: src/views/certificate/CertificateEditor.vue:175
+#: src/views/certificate/CertificateEditor.vue:212
 #: src/views/config/ConfigEdit.vue:98 src/views/domain/DomainEdit.vue:268
 #: src/views/config/ConfigEdit.vue:98 src/views/domain/DomainEdit.vue:268
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:111
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:111
 #: src/views/preference/Preference.vue:113
 #: src/views/preference/Preference.vue:113
@@ -1156,7 +1218,7 @@ msgid "Save error %{msg}"
 msgstr "Error al guardar %{msg}"
 msgstr "Error al guardar %{msg}"
 
 
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:42
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:42
-#: src/views/certificate/CertificateEditor.vue:46
+#: src/views/certificate/CertificateEditor.vue:47
 #: src/views/preference/Preference.vue:58
 #: src/views/preference/Preference.vue:58
 msgid "Save successfully"
 msgid "Save successfully"
 msgstr "Guardado con éxito"
 msgstr "Guardado con éxito"
@@ -1223,29 +1285,31 @@ msgstr "Usando el proveedor de desafíos HTTP01"
 msgid "Single Directive"
 msgid "Single Directive"
 msgstr "Directiva de una sola línea"
 msgstr "Directiva de una sola línea"
 
 
-#: src/routes/index.ts:159
+#: src/routes/index.ts:160
 msgid "Site Logs"
 msgid "Site Logs"
 msgstr "Registros del sitio"
 msgstr "Registros del sitio"
 
 
-#: src/routes/index.ts:66
+#: src/routes/index.ts:67
 msgid "Sites List"
 msgid "Sites List"
 msgstr "Lista de sitios"
 msgstr "Lista de sitios"
 
 
-#: src/views/certificate/CertificateEditor.vue:135
+#: src/views/certificate/CertificateEditor.vue:172
 #, fuzzy
 #, fuzzy
 msgid "SSL Certificate Content"
 msgid "SSL Certificate Content"
 msgstr "Contenido de certificado SSL"
 msgstr "Contenido de certificado SSL"
 
 
-#: src/views/certificate/CertificateEditor.vue:142
+#: src/views/certificate/CertificateEditor.vue:179
 #, fuzzy
 #, fuzzy
 msgid "SSL Certificate Key Content"
 msgid "SSL Certificate Key Content"
 msgstr "Contenido de la llave del certificado SSL"
 msgstr "Contenido de la llave del certificado SSL"
 
 
-#: src/views/certificate/Certificate.vue:63
+#: src/views/certificate/Certificate.vue:71
+#: src/views/certificate/CertificateEditor.vue:163
 msgid "SSL Certificate Key Path"
 msgid "SSL Certificate Key Path"
 msgstr "Ruta de la llave del certificado SSL"
 msgstr "Ruta de la llave del certificado SSL"
 
 
-#: src/views/certificate/Certificate.vue:55
+#: src/views/certificate/Certificate.vue:63
+#: src/views/certificate/CertificateEditor.vue:154
 msgid "SSL Certificate Path"
 msgid "SSL Certificate Path"
 msgstr "Ruta del certificado SSL"
 msgstr "Ruta del certificado SSL"
 
 
@@ -1258,7 +1322,7 @@ msgstr "Acceso"
 msgid "Stable"
 msgid "Stable"
 msgstr "Estable"
 msgstr "Estable"
 
 
-#: src/views/certificate/Certificate.vue:71 src/views/domain/DomainList.vue:25
+#: src/views/certificate/Certificate.vue:79 src/views/domain/DomainList.vue:25
 #: src/views/environment/Environment.vue:78
 #: src/views/environment/Environment.vue:78
 msgid "Status"
 msgid "Status"
 msgstr "Estado"
 msgstr "Estado"
@@ -1276,6 +1340,10 @@ msgstr "Almacenamiento"
 msgid "Subject Name: %{subject}"
 msgid "Subject Name: %{subject}"
 msgstr "Nombre del asunto: %{name}"
 msgstr "Nombre del asunto: %{name}"
 
 
+#: src/constants/index.ts:20
+msgid "Success"
+msgstr ""
+
 #: src/views/dashboard/ServerAnalytic.vue:233
 #: src/views/dashboard/ServerAnalytic.vue:233
 msgid "Swap"
 msgid "Swap"
 msgstr "Swap"
 msgstr "Swap"
@@ -1288,7 +1356,7 @@ msgstr ""
 msgid "Switch to light theme"
 msgid "Switch to light theme"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:192
+#: src/routes/index.ts:201
 msgid "System"
 msgid "System"
 msgstr "Sistema"
 msgstr "Sistema"
 
 
@@ -1300,7 +1368,7 @@ msgstr "Tabla"
 msgid "Target"
 msgid "Target"
 msgstr "Objetivo"
 msgstr "Objetivo"
 
 
-#: src/routes/index.ts:137 src/views/pty/Terminal.vue:95
+#: src/routes/index.ts:138 src/views/pty/Terminal.vue:95
 msgid "Terminal"
 msgid "Terminal"
 msgstr "Terminal"
 msgstr "Terminal"
 
 
@@ -1335,12 +1403,12 @@ msgstr ""
 msgid "The username or password is incorrect"
 msgid "The username or password is incorrect"
 msgstr "El nombre de usuario o contraseña son incorrectos"
 msgstr "El nombre de usuario o contraseña son incorrectos"
 
 
-#: src/views/certificate/CertificateEditor.vue:93
+#: src/views/certificate/CertificateEditor.vue:101
 #, fuzzy
 #, fuzzy
 msgid "This Auto Cert item is invalid, please remove it."
 msgid "This Auto Cert item is invalid, please remove it."
 msgstr "Este elemento de auto-cert es inválido, elimínelo por favor."
 msgstr "Este elemento de auto-cert es inválido, elimínelo por favor."
 
 
-#: src/views/certificate/CertificateEditor.vue:83
+#: src/views/certificate/CertificateEditor.vue:91
 msgid "This certificate is managed by Nginx UI"
 msgid "This certificate is managed by Nginx UI"
 msgstr ""
 msgstr ""
 
 
@@ -1348,6 +1416,10 @@ msgstr ""
 msgid "This field should not be empty"
 msgid "This field should not be empty"
 msgstr "Este campo no debe estar vacío"
 msgstr "Este campo no debe estar vacío"
 
 
+#: src/views/notification/Notification.vue:21
+msgid "Title"
+msgstr ""
+
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:42
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:42
 msgid ""
 msgid ""
 "To make sure the certification auto-renewal can work normally, we need to "
 "To make sure the certification auto-renewal can work normally, we need to "
@@ -1361,6 +1433,7 @@ msgstr ""
 "Nginx. ¿Estás seguro de que quieres continuar?"
 "Nginx. ¿Estás seguro de que quieres continuar?"
 
 
 #: src/views/certificate/Certificate.vue:39 src/views/config/config.ts:14
 #: src/views/certificate/Certificate.vue:39 src/views/config/config.ts:14
+#: src/views/notification/Notification.vue:15
 msgid "Type"
 msgid "Type"
 msgstr "Tipo"
 msgstr "Tipo"
 
 
@@ -1376,7 +1449,7 @@ msgstr "Actualizado a"
 msgid "Updated successfully"
 msgid "Updated successfully"
 msgstr "Actualización exitosa"
 msgstr "Actualización exitosa"
 
 
-#: src/routes/index.ts:203 src/views/system/Upgrade.vue:143
+#: src/routes/index.ts:212 src/views/system/Upgrade.vue:143
 #: src/views/system/Upgrade.vue:235
 #: src/views/system/Upgrade.vue:235
 msgid "Upgrade"
 msgid "Upgrade"
 msgstr "Actualizar"
 msgstr "Actualizar"
@@ -1409,7 +1482,7 @@ msgstr "Nombre de usuario"
 msgid "Username (*)"
 msgid "Username (*)"
 msgstr "Nombre de usuario (*)"
 msgstr "Nombre de usuario (*)"
 
 
-#: src/views/certificate/Certificate.vue:80
+#: src/views/certificate/Certificate.vue:88
 msgid "Valid"
 msgid "Valid"
 msgstr ""
 msgstr ""
 
 
@@ -1417,7 +1490,12 @@ msgstr ""
 msgid "View"
 msgid "View"
 msgstr "Ver"
 msgstr "Ver"
 
 
-#: src/views/config/InspectConfig.vue:36
+#: src/components/Notification/Notification.vue:143
+#, fuzzy
+msgid "View all notifications"
+msgstr "Certificación"
+
+#: src/constants/index.ts:18 src/views/config/InspectConfig.vue:36
 #: src/views/domain/cert/components/AutoCertStepOne.vue:23
 #: src/views/domain/cert/components/AutoCertStepOne.vue:23
 #: src/views/domain/DomainAdd.vue:114
 #: src/views/domain/DomainAdd.vue:114
 msgid "Warning"
 msgid "Warning"
@@ -1498,9 +1576,6 @@ msgstr "Puede consultar la actualización de Nginx UI en esta página."
 #~ "verificación, se eliminarán los registros. Tenga en cuenta que las "
 #~ "verificación, se eliminarán los registros. Tenga en cuenta que las "
 #~ "configuraciones de tiempo que aparecen debajo están todas en segundos."
 #~ "configuraciones de tiempo que aparecen debajo están todas en segundos."
 
 
-#~ msgid "Certification"
-#~ msgstr "Certificación"
-
 #~ msgid "Delete ID: %{id}"
 #~ msgid "Delete ID: %{id}"
 #~ msgstr "Eliminar ID: %{id}"
 #~ msgstr "Eliminar ID: %{id}"
 
 

+ 143 - 68
app/src/language/fr_FR/app.po

@@ -11,30 +11,30 @@ msgstr ""
 "Content-Transfer-Encoding: 8bit\n"
 "Content-Transfer-Encoding: 8bit\n"
 "X-Generator: Poedit 3.3\n"
 "X-Generator: Poedit 3.3\n"
 
 
-#: src/routes/index.ts:199
+#: src/routes/index.ts:208
 msgid "About"
 msgid "About"
 msgstr "À propos"
 msgstr "À propos"
 
 
-#: src/routes/index.ts:151 src/views/domain/ngx_conf/LogEntry.vue:78
+#: src/routes/index.ts:152 src/views/domain/ngx_conf/LogEntry.vue:78
 msgid "Access Logs"
 msgid "Access Logs"
 msgstr "Journaux d'accès"
 msgstr "Journaux d'accès"
 
 
-#: src/views/certificate/Certificate.vue:96
+#: src/views/certificate/Certificate.vue:104
 #: src/views/certificate/DNSCredential.vue:32 src/views/config/config.ts:36
 #: src/views/certificate/DNSCredential.vue:32 src/views/config/config.ts:36
 #: src/views/domain/DomainList.vue:50 src/views/environment/Environment.vue:105
 #: src/views/domain/DomainList.vue:50 src/views/environment/Environment.vue:105
-#: src/views/user/User.vue:46
+#: src/views/notification/Notification.vue:38 src/views/user/User.vue:46
 msgid "Action"
 msgid "Action"
 msgstr "Action"
 msgstr "Action"
 
 
-#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:114
-#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:137
+#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:115
+#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:141
+#: src/views/certificate/Certificate.vue:120
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:115
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:115
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:308
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:308
 msgid "Add"
 msgid "Add"
 msgstr "Ajouter"
 msgstr "Ajouter"
 
 
-#: src/routes/index.ts:122 src/views/certificate/Certificate.vue:115
-#: src/views/certificate/CertificateEditor.vue:76
+#: src/routes/index.ts:123 src/views/certificate/CertificateEditor.vue:84
 #, fuzzy
 #, fuzzy
 msgid "Add Certificate"
 msgid "Add Certificate"
 msgstr "État du certificat"
 msgstr "État du certificat"
@@ -48,7 +48,7 @@ msgstr "Ajouter une directive"
 msgid "Add Location"
 msgid "Add Location"
 msgstr "Ajouter une localisation"
 msgstr "Ajouter une localisation"
 
 
-#: src/routes/index.ts:70 src/views/domain/DomainAdd.vue:91
+#: src/routes/index.ts:71 src/views/domain/DomainAdd.vue:91
 msgid "Add Site"
 msgid "Add Site"
 msgstr "Ajouter un site"
 msgstr "Ajouter un site"
 
 
@@ -78,12 +78,18 @@ msgstr "Jeton d'API"
 msgid "Arch"
 msgid "Arch"
 msgstr "Arch"
 msgstr "Arch"
 
 
+#: src/components/Notification/Notification.vue:86
+#: src/views/notification/Notification.vue:73
+#, fuzzy
+msgid "Are you sure you want to clear all notifications?"
+msgstr "Voulez-vous vraiment effacer l'historique du chat ?"
+
 #: src/components/ChatGPT/ChatGPT.vue:279
 #: src/components/ChatGPT/ChatGPT.vue:279
 msgid "Are you sure you want to clear the record of chat?"
 msgid "Are you sure you want to clear the record of chat?"
 msgstr "Voulez-vous vraiment effacer l'historique du chat ?"
 msgstr "Voulez-vous vraiment effacer l'historique du chat ?"
 
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:347
-#: src/views/domain/DomainList.vue:145
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:353
+#: src/views/domain/DomainList.vue:144
 msgid "Are you sure you want to delete?"
 msgid "Are you sure you want to delete?"
 msgstr "Etes-vous sûr que vous voulez supprimer ?"
 msgstr "Etes-vous sûr que vous voulez supprimer ?"
 
 
@@ -125,7 +131,7 @@ msgstr "Renouvellement automatique désactivé pour %{name}"
 msgid "Auto-renewal enabled for %{name}"
 msgid "Auto-renewal enabled for %{name}"
 msgstr "Renouvellement automatique activé pour %{name}"
 msgstr "Renouvellement automatique activé pour %{name}"
 
 
-#: src/views/certificate/CertificateEditor.vue:168
+#: src/views/certificate/CertificateEditor.vue:205
 #: src/views/config/Config.vue:75 src/views/config/ConfigEdit.vue:89
 #: src/views/config/Config.vue:75 src/views/config/ConfigEdit.vue:89
 #: src/views/domain/DomainEdit.vue:261 src/views/nginx_log/NginxLog.vue:170
 #: src/views/domain/DomainEdit.vue:261 src/views/nginx_log/NginxLog.vue:170
 msgid "Back"
 msgid "Back"
@@ -165,7 +171,7 @@ msgstr ""
 
 
 #: src/components/ChatGPT/ChatGPT.vue:260
 #: src/components/ChatGPT/ChatGPT.vue:260
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:58
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:58
-#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:139
+#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:143
 #: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:103
 #: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:103
 #: src/views/domain/cert/components/ObtainCert.vue:150
 #: src/views/domain/cert/components/ObtainCert.vue:150
 #: src/views/domain/components/Deploy.vue:24
 #: src/views/domain/components/Deploy.vue:24
@@ -183,17 +189,17 @@ msgstr "Le certificat a expiré"
 msgid "Certificate is valid"
 msgid "Certificate is valid"
 msgstr "Le certificat est valide"
 msgstr "Le certificat est valide"
 
 
-#: src/views/certificate/CertificateEditor.vue:119
+#: src/views/certificate/CertificateEditor.vue:127
 #: src/views/domain/cert/Cert.vue:35
 #: src/views/domain/cert/Cert.vue:35
 msgid "Certificate Status"
 msgid "Certificate Status"
 msgstr "État du certificat"
 msgstr "État du certificat"
 
 
-#: src/routes/index.ts:100 src/views/certificate/Certificate.vue:109
+#: src/routes/index.ts:101 src/views/certificate/Certificate.vue:117
 #, fuzzy
 #, fuzzy
 msgid "Certificates"
 msgid "Certificates"
 msgstr "État du certificat"
 msgstr "État du certificat"
 
 
-#: src/routes/index.ts:109
+#: src/routes/index.ts:110
 #, fuzzy
 #, fuzzy
 msgid "Certificates List"
 msgid "Certificates List"
 msgstr "Liste des certifications"
 msgstr "Liste des certifications"
@@ -228,9 +234,17 @@ msgid "Cleaning environment variables"
 msgstr "Nettoyage des variables d'environnement"
 msgstr "Nettoyage des variables d'environnement"
 
 
 #: src/components/ChatGPT/ChatGPT.vue:283
 #: src/components/ChatGPT/ChatGPT.vue:283
+#: src/components/Notification/Notification.vue:91
+#: src/views/notification/Notification.vue:78
 msgid "Clear"
 msgid "Clear"
 msgstr "Effacer"
 msgstr "Effacer"
 
 
+#: src/components/Notification/Notification.vue:42
+#: src/views/notification/Notification.vue:47
+#, fuzzy
+msgid "Cleared successfully"
+msgstr "Désactivé avec succès"
+
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:97
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:97
 #: src/views/domain/ngx_conf/LocationEditor.vue:121
 #: src/views/domain/ngx_conf/LocationEditor.vue:121
 #: src/views/domain/ngx_conf/LocationEditor.vue:90
 #: src/views/domain/ngx_conf/LocationEditor.vue:90
@@ -284,7 +298,7 @@ msgstr "CPU :"
 msgid "Create Another"
 msgid "Create Another"
 msgstr "Créer un autre"
 msgstr "Créer un autre"
 
 
-#: src/views/user/User.vue:34
+#: src/views/notification/Notification.vue:32 src/views/user/User.vue:34
 msgid "Created at"
 msgid "Created at"
 msgstr "Créé le"
 msgstr "Créé le"
 
 
@@ -309,7 +323,7 @@ msgstr "Version actuelle"
 msgid "Custom"
 msgid "Custom"
 msgstr "Custom"
 msgstr "Custom"
 
 
-#: src/routes/index.ts:51
+#: src/routes/index.ts:52
 msgid "Dashboard"
 msgid "Dashboard"
 msgstr "Dashboard"
 msgstr "Dashboard"
 
 
@@ -317,7 +331,7 @@ msgstr "Dashboard"
 msgid "Database (Optional, default: database)"
 msgid "Database (Optional, default: database)"
 msgstr "Base de données (Facultatif, par défaut : database)"
 msgstr "Base de données (Facultatif, par défaut : database)"
 
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:354
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:360
 #: src/views/domain/DomainList.vue:153
 #: src/views/domain/DomainList.vue:153
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:259
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:259
 msgid "Delete"
 msgid "Delete"
@@ -355,6 +369,10 @@ msgstr "Sauvegarde réussie"
 msgid "Description"
 msgid "Description"
 msgstr "Description"
 msgstr "Description"
 
 
+#: src/views/notification/Notification.vue:28
+msgid "Details"
+msgstr ""
+
 #: src/views/system/About.vue:25
 #: src/views/system/About.vue:25
 msgid "Development Mode"
 msgid "Development Mode"
 msgstr "Mode développement"
 msgstr "Mode développement"
@@ -382,7 +400,7 @@ msgid "Disable auto-renewal failed for %{name}"
 msgstr "La désactivation du renouvellement automatique a échoué pour %{name}"
 msgstr "La désactivation du renouvellement automatique a échoué pour %{name}"
 
 
 #: src/views/domain/cert/ChangeCert.vue:48 src/views/domain/DomainEdit.vue:190
 #: src/views/domain/cert/ChangeCert.vue:48 src/views/domain/DomainEdit.vue:190
-#: src/views/domain/DomainList.vue:123
+#: src/views/domain/DomainList.vue:122
 msgid "Disabled"
 msgid "Disabled"
 msgstr "Désactivé"
 msgstr "Désactivé"
 
 
@@ -395,7 +413,7 @@ msgstr "Désactivé avec succès"
 msgid "Disk IO"
 msgid "Disk IO"
 msgstr "E/S disque"
 msgstr "E/S disque"
 
 
-#: src/routes/index.ts:130 src/views/certificate/DNSCredential.vue:39
+#: src/routes/index.ts:131 src/views/certificate/DNSCredential.vue:39
 msgid "DNS Credentials"
 msgid "DNS Credentials"
 msgstr "Identifiants DNS"
 msgstr "Identifiants DNS"
 
 
@@ -439,7 +457,7 @@ msgstr "Voulez-vous supprimer ce serveur ?"
 msgid "Domain Config Created Successfully"
 msgid "Domain Config Created Successfully"
 msgstr "La configuration du domaine a été créée avec succès"
 msgstr "La configuration du domaine a été créée avec succès"
 
 
-#: src/views/certificate/CertificateEditor.vue:103
+#: src/views/certificate/CertificateEditor.vue:111
 #, fuzzy
 #, fuzzy
 msgid "Domains list is empty, try to reopen Auto Cert for %{config}"
 msgid "Domains list is empty, try to reopen Auto Cert for %{config}"
 msgstr ""
 msgstr ""
@@ -459,7 +477,7 @@ msgid "Dry run mode enabled"
 msgstr ""
 msgstr ""
 
 
 #: src/views/domain/components/SiteDuplicate.vue:128
 #: src/views/domain/components/SiteDuplicate.vue:128
-#: src/views/domain/DomainList.vue:139
+#: src/views/domain/DomainList.vue:138
 msgid "Duplicate"
 msgid "Duplicate"
 msgstr "Dupliquer"
 msgstr "Dupliquer"
 
 
@@ -487,11 +505,11 @@ msgstr "Dupliqué avec succès"
 msgid "Edit %{n}"
 msgid "Edit %{n}"
 msgstr "Modifier %{n}"
 msgstr "Modifier %{n}"
 
 
-#: src/routes/index.ts:92 src/views/config/ConfigEdit.vue:83
+#: src/routes/index.ts:93 src/views/config/ConfigEdit.vue:83
 msgid "Edit Configuration"
 msgid "Edit Configuration"
 msgstr "Modifier la configuration"
 msgstr "Modifier la configuration"
 
 
-#: src/routes/index.ts:74
+#: src/routes/index.ts:75
 msgid "Edit Site"
 msgid "Edit Site"
 msgstr "Modifier le site"
 msgstr "Modifier le site"
 
 
@@ -532,7 +550,7 @@ msgstr "Activer TLS"
 #: src/views/domain/cert/ChangeCert.vue:44
 #: src/views/domain/cert/ChangeCert.vue:44
 #: src/views/domain/components/Deploy.vue:89
 #: src/views/domain/components/Deploy.vue:89
 #: src/views/domain/components/RightSettings.vue:75
 #: src/views/domain/components/RightSettings.vue:75
-#: src/views/domain/DomainEdit.vue:184 src/views/domain/DomainList.vue:131
+#: src/views/domain/DomainEdit.vue:184 src/views/domain/DomainList.vue:130
 msgid "Enabled"
 msgid "Enabled"
 msgstr "Activé"
 msgstr "Activé"
 
 
@@ -546,7 +564,7 @@ msgstr "Activé avec succès"
 msgid "Encrypt website with Let's Encrypt"
 msgid "Encrypt website with Let's Encrypt"
 msgstr "Crypter le site Web avec Let's Encrypt"
 msgstr "Crypter le site Web avec Let's Encrypt"
 
 
-#: src/routes/index.ts:168 src/views/environment/Environment.vue:113
+#: src/routes/index.ts:169 src/views/environment/Environment.vue:113
 msgid "Environment"
 msgid "Environment"
 msgstr ""
 msgstr ""
 
 
@@ -555,11 +573,11 @@ msgstr ""
 msgid "Environments"
 msgid "Environments"
 msgstr "Commentaires"
 msgstr "Commentaires"
 
 
-#: src/views/config/InspectConfig.vue:47
+#: src/constants/index.ts:17 src/views/config/InspectConfig.vue:47
 msgid "Error"
 msgid "Error"
 msgstr "Erreur"
 msgstr "Erreur"
 
 
-#: src/routes/index.ts:155 src/views/domain/ngx_conf/LogEntry.vue:86
+#: src/routes/index.ts:156 src/views/domain/ngx_conf/LogEntry.vue:86
 msgid "Error Logs"
 msgid "Error Logs"
 msgstr "Journaux d'erreurs"
 msgstr "Journaux d'erreurs"
 
 
@@ -567,7 +585,7 @@ msgstr "Journaux d'erreurs"
 msgid "Executable Path"
 msgid "Executable Path"
 msgstr "Chemin exécutable"
 msgstr "Chemin exécutable"
 
 
-#: src/views/certificate/Certificate.vue:84
+#: src/views/certificate/Certificate.vue:92
 msgid "Expired"
 msgid "Expired"
 msgstr ""
 msgstr ""
 
 
@@ -633,6 +651,11 @@ msgstr "Erreur de format %{msg}"
 msgid "Format successfully"
 msgid "Format successfully"
 msgstr "Formaté avec succès"
 msgstr "Formaté avec succès"
 
 
+#: src/views/certificate/Certificate.vue:45
+#, fuzzy
+msgid "General Certificate"
+msgstr "Changer de certificat"
+
 #: src/components/StdDesign/StdDataEntry/components/StdPassword.vue:60
 #: src/components/StdDesign/StdDataEntry/components/StdPassword.vue:60
 msgid "Generate"
 msgid "Generate"
 msgstr "Générer"
 msgstr "Générer"
@@ -670,7 +693,7 @@ msgstr "GPT-4-32K"
 msgid "GPT-4-Turbo"
 msgid "GPT-4-Turbo"
 msgstr "GPT-3.5-Turbo"
 msgstr "GPT-3.5-Turbo"
 
 
-#: src/routes/index.ts:44
+#: src/routes/index.ts:45
 msgid "Home"
 msgid "Home"
 msgstr "Menu principal"
 msgstr "Menu principal"
 
 
@@ -690,6 +713,10 @@ msgstr "Port HTTP"
 msgid "HTTP01"
 msgid "HTTP01"
 msgstr "HTTP01"
 msgstr "HTTP01"
 
 
+#: src/constants/index.ts:19
+msgid "Info"
+msgstr ""
+
 #: src/language/constants.ts:27
 #: src/language/constants.ts:27
 msgid "Initial core upgrader error"
 msgid "Initial core upgrader error"
 msgstr "Erreur du programme de mise à niveau initial du core"
 msgstr "Erreur du programme de mise à niveau initial du core"
@@ -698,7 +725,7 @@ msgstr "Erreur du programme de mise à niveau initial du core"
 msgid "Initialing core upgrader"
 msgid "Initialing core upgrader"
 msgstr "Initialisation du programme de mise à niveau du core"
 msgstr "Initialisation du programme de mise à niveau du core"
 
 
-#: src/routes/index.ts:211 src/views/other/Install.vue:139
+#: src/routes/index.ts:220 src/views/other/Install.vue:139
 msgid "Install"
 msgid "Install"
 msgstr "Installer"
 msgstr "Installer"
 
 
@@ -760,12 +787,12 @@ msgstr "Localisation"
 msgid "Locations"
 msgid "Locations"
 msgstr "Localisations"
 msgstr "Localisations"
 
 
-#: src/views/certificate/CertificateEditor.vue:156
+#: src/views/certificate/CertificateEditor.vue:193
 #, fuzzy
 #, fuzzy
 msgid "Log"
 msgid "Log"
 msgstr "Connexion"
 msgstr "Connexion"
 
 
-#: src/routes/index.ts:217 src/views/other/Login.vue:147
+#: src/routes/index.ts:226 src/views/other/Login.vue:147
 msgid "Login"
 msgid "Login"
 msgstr "Connexion"
 msgstr "Connexion"
 
 
@@ -773,7 +800,7 @@ msgstr "Connexion"
 msgid "Login successful"
 msgid "Login successful"
 msgstr "Connexion réussie"
 msgstr "Connexion réussie"
 
 
-#: src/layouts/HeaderLayout.vue:21
+#: src/layouts/HeaderLayout.vue:22
 msgid "Logout successful"
 msgid "Logout successful"
 msgstr "Déconnexion réussie"
 msgstr "Déconnexion réussie"
 
 
@@ -786,18 +813,23 @@ msgstr ""
 "Assurez vous d'avoir configuré un reverse proxy pour le répertoire .well-"
 "Assurez vous d'avoir configuré un reverse proxy pour le répertoire .well-"
 "known vers HTTPChallengePort avant d'obtenir le certificat."
 "known vers HTTPChallengePort avant d'obtenir le certificat."
 
 
-#: src/routes/index.ts:83
+#: src/routes/index.ts:84
 msgid "Manage Configs"
 msgid "Manage Configs"
 msgstr "Gérer les configurations"
 msgstr "Gérer les configurations"
 
 
-#: src/routes/index.ts:58 src/views/domain/DomainList.vue:102
+#: src/routes/index.ts:59 src/views/domain/DomainList.vue:102
 msgid "Manage Sites"
 msgid "Manage Sites"
 msgstr "Gérer les sites"
 msgstr "Gérer les sites"
 
 
-#: src/routes/index.ts:176 src/views/user/User.vue:53
+#: src/routes/index.ts:185 src/views/user/User.vue:53
 msgid "Manage Users"
 msgid "Manage Users"
 msgstr "Gérer les utilisateurs"
 msgstr "Gérer les utilisateurs"
 
 
+#: src/views/certificate/Certificate.vue:44
+#, fuzzy
+msgid "Managed Certificate"
+msgstr "Changer de certificat"
+
 #: src/views/dashboard/ServerAnalytic.vue:220
 #: src/views/dashboard/ServerAnalytic.vue:220
 msgid "Memory"
 msgid "Memory"
 msgstr "Mémoire"
 msgstr "Mémoire"
@@ -807,12 +839,12 @@ msgid "Memory and Storage"
 msgstr "Mémoire et stockage"
 msgstr "Mémoire et stockage"
 
 
 #: src/components/ChatGPT/ChatGPT.vue:256
 #: src/components/ChatGPT/ChatGPT.vue:256
-#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:137
+#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:141
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:336
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:336
 msgid "Modify"
 msgid "Modify"
 msgstr "Modifier"
 msgstr "Modifier"
 
 
-#: src/routes/index.ts:114 src/views/certificate/CertificateEditor.vue:76
+#: src/routes/index.ts:115 src/views/certificate/CertificateEditor.vue:84
 #, fuzzy
 #, fuzzy
 msgid "Modify Certificate"
 msgid "Modify Certificate"
 msgstr "État du certificat"
 msgstr "État du certificat"
@@ -826,6 +858,7 @@ msgid "Multi-line Directive"
 msgstr "Directive multiligne"
 msgstr "Directive multiligne"
 
 
 #: src/views/certificate/Certificate.vue:22
 #: src/views/certificate/Certificate.vue:22
+#: src/views/certificate/CertificateEditor.vue:145
 #: src/views/certificate/DNSCredential.vue:13 src/views/config/config.ts:9
 #: src/views/certificate/DNSCredential.vue:13 src/views/config/config.ts:9
 #: src/views/domain/cert/ChangeCert.vue:21
 #: src/views/domain/cert/ChangeCert.vue:21
 #: src/views/domain/components/RightSettings.vue:81
 #: src/views/domain/components/RightSettings.vue:81
@@ -880,7 +913,7 @@ msgstr "Contrôle Nginx"
 msgid "Nginx Error Log Path"
 msgid "Nginx Error Log Path"
 msgstr "Chemin du journal des erreurs Nginx"
 msgstr "Chemin du journal des erreurs Nginx"
 
 
-#: src/routes/index.ts:145 src/views/nginx_log/NginxLog.vue:145
+#: src/routes/index.ts:146 src/views/nginx_log/NginxLog.vue:145
 msgid "Nginx Log"
 msgid "Nginx Log"
 msgstr "Journal Nginx"
 msgstr "Journal Nginx"
 
 
@@ -893,10 +926,12 @@ msgid "Nginx restarted successfully"
 msgstr "Nginx a redémarré avec succès"
 msgstr "Nginx a redémarré avec succès"
 
 
 #: src/components/ChatGPT/ChatGPT.vue:277
 #: src/components/ChatGPT/ChatGPT.vue:277
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:345
-#: src/views/domain/DomainList.vue:143
+#: src/components/Notification/Notification.vue:84
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:351
+#: src/views/domain/DomainList.vue:142
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:81
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:81
 #: src/views/domain/ngx_conf/LocationEditor.vue:76
 #: src/views/domain/ngx_conf/LocationEditor.vue:76
+#: src/views/notification/Notification.vue:71
 msgid "No"
 msgid "No"
 msgstr "Non"
 msgstr "Non"
 
 
@@ -905,11 +940,11 @@ msgstr "Non"
 msgid "Node Secret"
 msgid "Node Secret"
 msgstr "Secret Jwt"
 msgstr "Secret Jwt"
 
 
-#: src/views/certificate/Certificate.vue:90
+#: src/views/certificate/Certificate.vue:98
 msgid "Not After"
 msgid "Not After"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:223 src/routes/index.ts:225
+#: src/routes/index.ts:232 src/routes/index.ts:234
 msgid "Not Found"
 msgid "Not Found"
 msgstr "Introuvable"
 msgstr "Introuvable"
 
 
@@ -922,6 +957,16 @@ msgstr "Non valide avant : %{date}"
 msgid "Note"
 msgid "Note"
 msgstr "Note"
 msgstr "Note"
 
 
+#: src/views/notification/Notification.vue:63
+#, fuzzy
+msgid "Notification"
+msgstr "Certification"
+
+#: src/components/Notification/Notification.vue:82 src/routes/index.ts:177
+#, fuzzy
+msgid "Notifications"
+msgstr "Certification"
+
 #: src/views/domain/cert/components/ObtainCert.vue:191
 #: src/views/domain/cert/components/ObtainCert.vue:191
 msgid "Obtain certificate"
 msgid "Obtain certificate"
 msgstr "Obtenir un certificat"
 msgstr "Obtenir un certificat"
@@ -937,16 +982,18 @@ msgid "Offline"
 msgstr ""
 msgstr ""
 
 
 #: src/components/ChatGPT/ChatGPT.vue:278
 #: src/components/ChatGPT/ChatGPT.vue:278
+#: src/components/Notification/Notification.vue:85
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:59
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:59
-#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:140
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:346
+#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:144
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:352
 #: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:104
 #: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:104
 #: src/views/domain/cert/components/ObtainCert.vue:149
 #: src/views/domain/cert/components/ObtainCert.vue:149
 #: src/views/domain/components/Deploy.vue:23
 #: src/views/domain/components/Deploy.vue:23
 #: src/views/domain/components/RightSettings.vue:51
 #: src/views/domain/components/RightSettings.vue:51
-#: src/views/domain/DomainList.vue:144
+#: src/views/domain/DomainList.vue:143
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:215
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:215
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:47
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:47
+#: src/views/notification/Notification.vue:72
 msgid "OK"
 msgid "OK"
 msgstr "OK"
 msgstr "OK"
 
 
@@ -1057,7 +1104,7 @@ msgstr ""
 msgid "Pre-release"
 msgid "Pre-release"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:184 src/views/preference/Preference.vue:85
+#: src/routes/index.ts:193 src/views/preference/Preference.vue:85
 msgid "Preference"
 msgid "Preference"
 msgstr "Préférence"
 msgstr "Préférence"
 
 
@@ -1123,12 +1170,27 @@ msgstr "Rechargement"
 msgid "Reloading nginx"
 msgid "Reloading nginx"
 msgstr "Rechargement de nginx"
 msgstr "Rechargement de nginx"
 
 
+#: src/components/Notification/Notification.vue:52
+#, fuzzy
+msgid "Removed successfully"
+msgstr "Enregistré avec succès"
+
 #: src/views/certificate/RenewCert.vue:43
 #: src/views/certificate/RenewCert.vue:43
 #: src/views/certificate/RenewCert.vue:47
 #: src/views/certificate/RenewCert.vue:47
 #, fuzzy
 #, fuzzy
 msgid "Renew Certificate"
 msgid "Renew Certificate"
 msgstr "Changer de certificat"
 msgstr "Changer de certificat"
 
 
+#: src/language/constants.ts:38
+#, fuzzy
+msgid "Renew Certificate Error"
+msgstr "Changer de certificat"
+
+#: src/language/constants.ts:37
+#, fuzzy
+msgid "Renew Certificate Success"
+msgstr "Changer de certificat"
+
 #: src/views/certificate/RenewCert.vue:25
 #: src/views/certificate/RenewCert.vue:25
 #, fuzzy
 #, fuzzy
 msgid "Renew successfully"
 msgid "Renew successfully"
@@ -1159,7 +1221,7 @@ msgid "Running"
 msgstr "En cours d'éxécution"
 msgstr "En cours d'éxécution"
 
 
 #: src/components/ChatGPT/ChatGPT.vue:259
 #: src/components/ChatGPT/ChatGPT.vue:259
-#: src/views/certificate/CertificateEditor.vue:175
+#: src/views/certificate/CertificateEditor.vue:212
 #: src/views/config/ConfigEdit.vue:98 src/views/domain/DomainEdit.vue:268
 #: src/views/config/ConfigEdit.vue:98 src/views/domain/DomainEdit.vue:268
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:111
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:111
 #: src/views/preference/Preference.vue:113
 #: src/views/preference/Preference.vue:113
@@ -1176,7 +1238,7 @@ msgid "Save error %{msg}"
 msgstr "Enregistrer l'erreur %{msg}"
 msgstr "Enregistrer l'erreur %{msg}"
 
 
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:42
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:42
-#: src/views/certificate/CertificateEditor.vue:46
+#: src/views/certificate/CertificateEditor.vue:47
 #: src/views/preference/Preference.vue:58
 #: src/views/preference/Preference.vue:58
 msgid "Save successfully"
 msgid "Save successfully"
 msgstr "Sauvegarde réussie"
 msgstr "Sauvegarde réussie"
@@ -1243,29 +1305,31 @@ msgstr "Utilisation du fournisseur de challenge HTTP01"
 msgid "Single Directive"
 msgid "Single Directive"
 msgstr "Directive unique"
 msgstr "Directive unique"
 
 
-#: src/routes/index.ts:159
+#: src/routes/index.ts:160
 msgid "Site Logs"
 msgid "Site Logs"
 msgstr "Journaux du site"
 msgstr "Journaux du site"
 
 
-#: src/routes/index.ts:66
+#: src/routes/index.ts:67
 msgid "Sites List"
 msgid "Sites List"
 msgstr "Liste des sites"
 msgstr "Liste des sites"
 
 
-#: src/views/certificate/CertificateEditor.vue:135
+#: src/views/certificate/CertificateEditor.vue:172
 #, fuzzy
 #, fuzzy
 msgid "SSL Certificate Content"
 msgid "SSL Certificate Content"
 msgstr "Contenu de la certification SSL"
 msgstr "Contenu de la certification SSL"
 
 
-#: src/views/certificate/CertificateEditor.vue:142
+#: src/views/certificate/CertificateEditor.vue:179
 #, fuzzy
 #, fuzzy
 msgid "SSL Certificate Key Content"
 msgid "SSL Certificate Key Content"
 msgstr "Contenu de la clé de certification SSL"
 msgstr "Contenu de la clé de certification SSL"
 
 
-#: src/views/certificate/Certificate.vue:63
+#: src/views/certificate/Certificate.vue:71
+#: src/views/certificate/CertificateEditor.vue:163
 msgid "SSL Certificate Key Path"
 msgid "SSL Certificate Key Path"
 msgstr "Chemin de la clé du certificat SSL"
 msgstr "Chemin de la clé du certificat SSL"
 
 
-#: src/views/certificate/Certificate.vue:55
+#: src/views/certificate/Certificate.vue:63
+#: src/views/certificate/CertificateEditor.vue:154
 msgid "SSL Certificate Path"
 msgid "SSL Certificate Path"
 msgstr "Chemin du certificat SSL"
 msgstr "Chemin du certificat SSL"
 
 
@@ -1279,7 +1343,7 @@ msgstr "Connexion"
 msgid "Stable"
 msgid "Stable"
 msgstr "Tableau"
 msgstr "Tableau"
 
 
-#: src/views/certificate/Certificate.vue:71 src/views/domain/DomainList.vue:25
+#: src/views/certificate/Certificate.vue:79 src/views/domain/DomainList.vue:25
 #: src/views/environment/Environment.vue:78
 #: src/views/environment/Environment.vue:78
 msgid "Status"
 msgid "Status"
 msgstr "Statut"
 msgstr "Statut"
@@ -1297,6 +1361,10 @@ msgstr "Stockage"
 msgid "Subject Name: %{subject}"
 msgid "Subject Name: %{subject}"
 msgstr "Nom du sujet : %{name}"
 msgstr "Nom du sujet : %{name}"
 
 
+#: src/constants/index.ts:20
+msgid "Success"
+msgstr ""
+
 #: src/views/dashboard/ServerAnalytic.vue:233
 #: src/views/dashboard/ServerAnalytic.vue:233
 #, fuzzy
 #, fuzzy
 msgid "Swap"
 msgid "Swap"
@@ -1310,7 +1378,7 @@ msgstr ""
 msgid "Switch to light theme"
 msgid "Switch to light theme"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:192
+#: src/routes/index.ts:201
 msgid "System"
 msgid "System"
 msgstr "Système"
 msgstr "Système"
 
 
@@ -1322,7 +1390,7 @@ msgstr "Tableau"
 msgid "Target"
 msgid "Target"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:137 src/views/pty/Terminal.vue:95
+#: src/routes/index.ts:138 src/views/pty/Terminal.vue:95
 msgid "Terminal"
 msgid "Terminal"
 msgstr "Terminal"
 msgstr "Terminal"
 
 
@@ -1357,14 +1425,14 @@ msgstr ""
 msgid "The username or password is incorrect"
 msgid "The username or password is incorrect"
 msgstr "Le pseudo ou mot de passe est incorect"
 msgstr "Le pseudo ou mot de passe est incorect"
 
 
-#: src/views/certificate/CertificateEditor.vue:93
+#: src/views/certificate/CertificateEditor.vue:101
 #, fuzzy
 #, fuzzy
 msgid "This Auto Cert item is invalid, please remove it."
 msgid "This Auto Cert item is invalid, please remove it."
 msgstr ""
 msgstr ""
 "Cet élément de certification automatique n'est pas valide, veuillez le "
 "Cet élément de certification automatique n'est pas valide, veuillez le "
 "supprimer."
 "supprimer."
 
 
-#: src/views/certificate/CertificateEditor.vue:83
+#: src/views/certificate/CertificateEditor.vue:91
 msgid "This certificate is managed by Nginx UI"
 msgid "This certificate is managed by Nginx UI"
 msgstr ""
 msgstr ""
 
 
@@ -1372,6 +1440,10 @@ msgstr ""
 msgid "This field should not be empty"
 msgid "This field should not be empty"
 msgstr ""
 msgstr ""
 
 
+#: src/views/notification/Notification.vue:21
+msgid "Title"
+msgstr ""
+
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:42
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:42
 msgid ""
 msgid ""
 "To make sure the certification auto-renewal can work normally, we need to "
 "To make sure the certification auto-renewal can work normally, we need to "
@@ -1385,6 +1457,7 @@ msgstr ""
 "ce fichier et recharger le Nginx. Êtes-vous sûr de vouloir continuer?"
 "ce fichier et recharger le Nginx. Êtes-vous sûr de vouloir continuer?"
 
 
 #: src/views/certificate/Certificate.vue:39 src/views/config/config.ts:14
 #: src/views/certificate/Certificate.vue:39 src/views/config/config.ts:14
+#: src/views/notification/Notification.vue:15
 msgid "Type"
 msgid "Type"
 msgstr "Type"
 msgstr "Type"
 
 
@@ -1400,7 +1473,7 @@ msgstr "Mis à jour le"
 msgid "Updated successfully"
 msgid "Updated successfully"
 msgstr "Mis à jour avec succés"
 msgstr "Mis à jour avec succés"
 
 
-#: src/routes/index.ts:203 src/views/system/Upgrade.vue:143
+#: src/routes/index.ts:212 src/views/system/Upgrade.vue:143
 #: src/views/system/Upgrade.vue:235
 #: src/views/system/Upgrade.vue:235
 msgid "Upgrade"
 msgid "Upgrade"
 msgstr "Mettre à niveau"
 msgstr "Mettre à niveau"
@@ -1434,7 +1507,7 @@ msgstr "Nom d'utilisateur"
 msgid "Username (*)"
 msgid "Username (*)"
 msgstr "Nom d'utilisateur (*)"
 msgstr "Nom d'utilisateur (*)"
 
 
-#: src/views/certificate/Certificate.vue:80
+#: src/views/certificate/Certificate.vue:88
 msgid "Valid"
 msgid "Valid"
 msgstr ""
 msgstr ""
 
 
@@ -1442,7 +1515,12 @@ msgstr ""
 msgid "View"
 msgid "View"
 msgstr "Voir"
 msgstr "Voir"
 
 
-#: src/views/config/InspectConfig.vue:36
+#: src/components/Notification/Notification.vue:143
+#, fuzzy
+msgid "View all notifications"
+msgstr "Certification"
+
+#: src/constants/index.ts:18 src/views/config/InspectConfig.vue:36
 #: src/views/domain/cert/components/AutoCertStepOne.vue:23
 #: src/views/domain/cert/components/AutoCertStepOne.vue:23
 #: src/views/domain/DomainAdd.vue:114
 #: src/views/domain/DomainAdd.vue:114
 msgid "Warning"
 msgid "Warning"
@@ -1524,9 +1602,6 @@ msgstr "Vous pouvez vérifier la mise à niveau de Nginx UI sur cette page."
 #~ "supprimés. Veuillez noter que les configurations de temps ci-dessous sont "
 #~ "supprimés. Veuillez noter que les configurations de temps ci-dessous sont "
 #~ "toutes en secondes."
 #~ "toutes en secondes."
 
 
-#~ msgid "Certification"
-#~ msgstr "Certification"
-
 #~ msgid "Delete ID: %{id}"
 #~ msgid "Delete ID: %{id}"
 #~ msgstr "Supprimer l'identifiant : %{id}"
 #~ msgstr "Supprimer l'identifiant : %{id}"
 
 

+ 138 - 65
app/src/language/messages.pot

@@ -2,34 +2,35 @@ msgid ""
 msgstr ""
 msgstr ""
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 
 
-#: src/routes/index.ts:199
+#: src/routes/index.ts:208
 msgid "About"
 msgid "About"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:151
+#: src/routes/index.ts:152
 #: src/views/domain/ngx_conf/LogEntry.vue:78
 #: src/views/domain/ngx_conf/LogEntry.vue:78
 msgid "Access Logs"
 msgid "Access Logs"
 msgstr ""
 msgstr ""
 
 
-#: src/views/certificate/Certificate.vue:96
+#: src/views/certificate/Certificate.vue:104
 #: src/views/certificate/DNSCredential.vue:32
 #: src/views/certificate/DNSCredential.vue:32
 #: src/views/config/config.ts:36
 #: src/views/config/config.ts:36
 #: src/views/domain/DomainList.vue:50
 #: src/views/domain/DomainList.vue:50
 #: src/views/environment/Environment.vue:105
 #: src/views/environment/Environment.vue:105
+#: src/views/notification/Notification.vue:38
 #: src/views/user/User.vue:46
 #: src/views/user/User.vue:46
 msgid "Action"
 msgid "Action"
 msgstr ""
 msgstr ""
 
 
-#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:114
-#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:137
+#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:115
+#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:141
+#: src/views/certificate/Certificate.vue:120
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:115
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:115
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:308
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:308
 msgid "Add"
 msgid "Add"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:122
-#: src/views/certificate/Certificate.vue:115
-#: src/views/certificate/CertificateEditor.vue:76
+#: src/routes/index.ts:123
+#: src/views/certificate/CertificateEditor.vue:84
 msgid "Add Certificate"
 msgid "Add Certificate"
 msgstr ""
 msgstr ""
 
 
@@ -42,7 +43,7 @@ msgstr ""
 msgid "Add Location"
 msgid "Add Location"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:70
+#: src/routes/index.ts:71
 #: src/views/domain/DomainAdd.vue:91
 #: src/views/domain/DomainAdd.vue:91
 msgid "Add Site"
 msgid "Add Site"
 msgstr ""
 msgstr ""
@@ -71,12 +72,17 @@ msgstr ""
 msgid "Arch"
 msgid "Arch"
 msgstr ""
 msgstr ""
 
 
+#: src/components/Notification/Notification.vue:86
+#: src/views/notification/Notification.vue:73
+msgid "Are you sure you want to clear all notifications?"
+msgstr ""
+
 #: src/components/ChatGPT/ChatGPT.vue:279
 #: src/components/ChatGPT/ChatGPT.vue:279
 msgid "Are you sure you want to clear the record of chat?"
 msgid "Are you sure you want to clear the record of chat?"
 msgstr ""
 msgstr ""
 
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:347
-#: src/views/domain/DomainList.vue:145
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:353
+#: src/views/domain/DomainList.vue:144
 msgid "Are you sure you want to delete?"
 msgid "Are you sure you want to delete?"
 msgstr ""
 msgstr ""
 
 
@@ -117,7 +123,7 @@ msgstr ""
 msgid "Auto-renewal enabled for %{name}"
 msgid "Auto-renewal enabled for %{name}"
 msgstr ""
 msgstr ""
 
 
-#: src/views/certificate/CertificateEditor.vue:168
+#: src/views/certificate/CertificateEditor.vue:205
 #: src/views/config/Config.vue:75
 #: src/views/config/Config.vue:75
 #: src/views/config/ConfigEdit.vue:89
 #: src/views/config/ConfigEdit.vue:89
 #: src/views/domain/DomainEdit.vue:261
 #: src/views/domain/DomainEdit.vue:261
@@ -158,7 +164,7 @@ msgstr ""
 
 
 #: src/components/ChatGPT/ChatGPT.vue:260
 #: src/components/ChatGPT/ChatGPT.vue:260
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:58
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:58
-#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:139
+#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:143
 #: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:103
 #: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:103
 #: src/views/domain/cert/components/ObtainCert.vue:150
 #: src/views/domain/cert/components/ObtainCert.vue:150
 #: src/views/domain/components/Deploy.vue:24
 #: src/views/domain/components/Deploy.vue:24
@@ -176,17 +182,17 @@ msgstr ""
 msgid "Certificate is valid"
 msgid "Certificate is valid"
 msgstr ""
 msgstr ""
 
 
-#: src/views/certificate/CertificateEditor.vue:119
+#: src/views/certificate/CertificateEditor.vue:127
 #: src/views/domain/cert/Cert.vue:35
 #: src/views/domain/cert/Cert.vue:35
 msgid "Certificate Status"
 msgid "Certificate Status"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:100
-#: src/views/certificate/Certificate.vue:109
+#: src/routes/index.ts:101
+#: src/views/certificate/Certificate.vue:117
 msgid "Certificates"
 msgid "Certificates"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:109
+#: src/routes/index.ts:110
 msgid "Certificates List"
 msgid "Certificates List"
 msgstr ""
 msgstr ""
 
 
@@ -220,9 +226,16 @@ msgid "Cleaning environment variables"
 msgstr ""
 msgstr ""
 
 
 #: src/components/ChatGPT/ChatGPT.vue:283
 #: src/components/ChatGPT/ChatGPT.vue:283
+#: src/components/Notification/Notification.vue:91
+#: src/views/notification/Notification.vue:78
 msgid "Clear"
 msgid "Clear"
 msgstr ""
 msgstr ""
 
 
+#: src/components/Notification/Notification.vue:42
+#: src/views/notification/Notification.vue:47
+msgid "Cleared successfully"
+msgstr ""
+
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:97
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:97
 #: src/views/domain/ngx_conf/LocationEditor.vue:121
 #: src/views/domain/ngx_conf/LocationEditor.vue:121
 #: src/views/domain/ngx_conf/LocationEditor.vue:90
 #: src/views/domain/ngx_conf/LocationEditor.vue:90
@@ -276,6 +289,7 @@ msgstr ""
 msgid "Create Another"
 msgid "Create Another"
 msgstr ""
 msgstr ""
 
 
+#: src/views/notification/Notification.vue:32
 #: src/views/user/User.vue:34
 #: src/views/user/User.vue:34
 msgid "Created at"
 msgid "Created at"
 msgstr ""
 msgstr ""
@@ -301,7 +315,7 @@ msgstr ""
 msgid "Custom"
 msgid "Custom"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:51
+#: src/routes/index.ts:52
 msgid "Dashboard"
 msgid "Dashboard"
 msgstr ""
 msgstr ""
 
 
@@ -309,7 +323,7 @@ msgstr ""
 msgid "Database (Optional, default: database)"
 msgid "Database (Optional, default: database)"
 msgstr ""
 msgstr ""
 
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:354
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:360
 #: src/views/domain/DomainList.vue:153
 #: src/views/domain/DomainList.vue:153
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:259
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:259
 msgid "Delete"
 msgid "Delete"
@@ -345,6 +359,10 @@ msgstr ""
 msgid "Description"
 msgid "Description"
 msgstr ""
 msgstr ""
 
 
+#: src/views/notification/Notification.vue:28
+msgid "Details"
+msgstr ""
+
 #: src/views/system/About.vue:25
 #: src/views/system/About.vue:25
 msgid "Development Mode"
 msgid "Development Mode"
 msgstr ""
 msgstr ""
@@ -371,7 +389,7 @@ msgstr ""
 
 
 #: src/views/domain/cert/ChangeCert.vue:48
 #: src/views/domain/cert/ChangeCert.vue:48
 #: src/views/domain/DomainEdit.vue:190
 #: src/views/domain/DomainEdit.vue:190
-#: src/views/domain/DomainList.vue:123
+#: src/views/domain/DomainList.vue:122
 msgid "Disabled"
 msgid "Disabled"
 msgstr ""
 msgstr ""
 
 
@@ -384,7 +402,7 @@ msgstr ""
 msgid "Disk IO"
 msgid "Disk IO"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:130
+#: src/routes/index.ts:131
 #: src/views/certificate/DNSCredential.vue:39
 #: src/views/certificate/DNSCredential.vue:39
 msgid "DNS Credentials"
 msgid "DNS Credentials"
 msgstr ""
 msgstr ""
@@ -428,7 +446,7 @@ msgstr ""
 msgid "Domain Config Created Successfully"
 msgid "Domain Config Created Successfully"
 msgstr ""
 msgstr ""
 
 
-#: src/views/certificate/CertificateEditor.vue:103
+#: src/views/certificate/CertificateEditor.vue:111
 msgid "Domains list is empty, try to reopen Auto Cert for %{config}"
 msgid "Domains list is empty, try to reopen Auto Cert for %{config}"
 msgstr ""
 msgstr ""
 
 
@@ -445,7 +463,7 @@ msgid "Dry run mode enabled"
 msgstr ""
 msgstr ""
 
 
 #: src/views/domain/components/SiteDuplicate.vue:128
 #: src/views/domain/components/SiteDuplicate.vue:128
-#: src/views/domain/DomainList.vue:139
+#: src/views/domain/DomainList.vue:138
 msgid "Duplicate"
 msgid "Duplicate"
 msgstr ""
 msgstr ""
 
 
@@ -469,12 +487,12 @@ msgstr ""
 msgid "Edit %{n}"
 msgid "Edit %{n}"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:92
+#: src/routes/index.ts:93
 #: src/views/config/ConfigEdit.vue:83
 #: src/views/config/ConfigEdit.vue:83
 msgid "Edit Configuration"
 msgid "Edit Configuration"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:74
+#: src/routes/index.ts:75
 msgid "Edit Site"
 msgid "Edit Site"
 msgstr ""
 msgstr ""
 
 
@@ -514,7 +532,7 @@ msgstr ""
 #: src/views/domain/components/Deploy.vue:89
 #: src/views/domain/components/Deploy.vue:89
 #: src/views/domain/components/RightSettings.vue:75
 #: src/views/domain/components/RightSettings.vue:75
 #: src/views/domain/DomainEdit.vue:184
 #: src/views/domain/DomainEdit.vue:184
-#: src/views/domain/DomainList.vue:131
+#: src/views/domain/DomainList.vue:130
 msgid "Enabled"
 msgid "Enabled"
 msgstr ""
 msgstr ""
 
 
@@ -529,7 +547,7 @@ msgstr ""
 msgid "Encrypt website with Let's Encrypt"
 msgid "Encrypt website with Let's Encrypt"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:168
+#: src/routes/index.ts:169
 #: src/views/environment/Environment.vue:113
 #: src/views/environment/Environment.vue:113
 msgid "Environment"
 msgid "Environment"
 msgstr ""
 msgstr ""
@@ -538,11 +556,12 @@ msgstr ""
 msgid "Environments"
 msgid "Environments"
 msgstr ""
 msgstr ""
 
 
+#: src/constants/index.ts:17
 #: src/views/config/InspectConfig.vue:47
 #: src/views/config/InspectConfig.vue:47
 msgid "Error"
 msgid "Error"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:155
+#: src/routes/index.ts:156
 #: src/views/domain/ngx_conf/LogEntry.vue:86
 #: src/views/domain/ngx_conf/LogEntry.vue:86
 msgid "Error Logs"
 msgid "Error Logs"
 msgstr ""
 msgstr ""
@@ -551,7 +570,7 @@ msgstr ""
 msgid "Executable Path"
 msgid "Executable Path"
 msgstr ""
 msgstr ""
 
 
-#: src/views/certificate/Certificate.vue:84
+#: src/views/certificate/Certificate.vue:92
 msgid "Expired"
 msgid "Expired"
 msgstr ""
 msgstr ""
 
 
@@ -615,6 +634,10 @@ msgstr ""
 msgid "Format successfully"
 msgid "Format successfully"
 msgstr ""
 msgstr ""
 
 
+#: src/views/certificate/Certificate.vue:45
+msgid "General Certificate"
+msgstr ""
+
 #: src/components/StdDesign/StdDataEntry/components/StdPassword.vue:60
 #: src/components/StdDesign/StdDataEntry/components/StdPassword.vue:60
 msgid "Generate"
 msgid "Generate"
 msgstr ""
 msgstr ""
@@ -651,7 +674,7 @@ msgstr ""
 msgid "GPT-4-Turbo"
 msgid "GPT-4-Turbo"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:44
+#: src/routes/index.ts:45
 msgid "Home"
 msgid "Home"
 msgstr ""
 msgstr ""
 
 
@@ -671,6 +694,10 @@ msgstr ""
 msgid "HTTP01"
 msgid "HTTP01"
 msgstr ""
 msgstr ""
 
 
+#: src/constants/index.ts:19
+msgid "Info"
+msgstr ""
+
 #: src/language/constants.ts:27
 #: src/language/constants.ts:27
 msgid "Initial core upgrader error"
 msgid "Initial core upgrader error"
 msgstr ""
 msgstr ""
@@ -679,7 +706,7 @@ msgstr ""
 msgid "Initialing core upgrader"
 msgid "Initialing core upgrader"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:211
+#: src/routes/index.ts:220
 #: src/views/other/Install.vue:139
 #: src/views/other/Install.vue:139
 msgid "Install"
 msgid "Install"
 msgstr ""
 msgstr ""
@@ -737,11 +764,11 @@ msgstr ""
 msgid "Locations"
 msgid "Locations"
 msgstr ""
 msgstr ""
 
 
-#: src/views/certificate/CertificateEditor.vue:156
+#: src/views/certificate/CertificateEditor.vue:193
 msgid "Log"
 msgid "Log"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:217
+#: src/routes/index.ts:226
 #: src/views/other/Login.vue:147
 #: src/views/other/Login.vue:147
 msgid "Login"
 msgid "Login"
 msgstr ""
 msgstr ""
@@ -751,7 +778,7 @@ msgstr ""
 msgid "Login successful"
 msgid "Login successful"
 msgstr ""
 msgstr ""
 
 
-#: src/layouts/HeaderLayout.vue:21
+#: src/layouts/HeaderLayout.vue:22
 msgid "Logout successful"
 msgid "Logout successful"
 msgstr ""
 msgstr ""
 
 
@@ -759,20 +786,24 @@ msgstr ""
 msgid "Make sure you have configured a reverse proxy for .well-known directory to HTTPChallengePort before obtaining the certificate."
 msgid "Make sure you have configured a reverse proxy for .well-known directory to HTTPChallengePort before obtaining the certificate."
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:83
+#: src/routes/index.ts:84
 msgid "Manage Configs"
 msgid "Manage Configs"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:58
+#: src/routes/index.ts:59
 #: src/views/domain/DomainList.vue:102
 #: src/views/domain/DomainList.vue:102
 msgid "Manage Sites"
 msgid "Manage Sites"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:176
+#: src/routes/index.ts:185
 #: src/views/user/User.vue:53
 #: src/views/user/User.vue:53
 msgid "Manage Users"
 msgid "Manage Users"
 msgstr ""
 msgstr ""
 
 
+#: src/views/certificate/Certificate.vue:44
+msgid "Managed Certificate"
+msgstr ""
+
 #: src/views/dashboard/ServerAnalytic.vue:220
 #: src/views/dashboard/ServerAnalytic.vue:220
 msgid "Memory"
 msgid "Memory"
 msgstr ""
 msgstr ""
@@ -782,13 +813,13 @@ msgid "Memory and Storage"
 msgstr ""
 msgstr ""
 
 
 #: src/components/ChatGPT/ChatGPT.vue:256
 #: src/components/ChatGPT/ChatGPT.vue:256
-#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:137
+#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:141
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:336
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:336
 msgid "Modify"
 msgid "Modify"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:114
-#: src/views/certificate/CertificateEditor.vue:76
+#: src/routes/index.ts:115
+#: src/views/certificate/CertificateEditor.vue:84
 msgid "Modify Certificate"
 msgid "Modify Certificate"
 msgstr ""
 msgstr ""
 
 
@@ -801,6 +832,7 @@ msgid "Multi-line Directive"
 msgstr ""
 msgstr ""
 
 
 #: src/views/certificate/Certificate.vue:22
 #: src/views/certificate/Certificate.vue:22
+#: src/views/certificate/CertificateEditor.vue:145
 #: src/views/certificate/DNSCredential.vue:13
 #: src/views/certificate/DNSCredential.vue:13
 #: src/views/config/config.ts:9
 #: src/views/config/config.ts:9
 #: src/views/domain/cert/ChangeCert.vue:21
 #: src/views/domain/cert/ChangeCert.vue:21
@@ -856,7 +888,7 @@ msgstr ""
 msgid "Nginx Error Log Path"
 msgid "Nginx Error Log Path"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:145
+#: src/routes/index.ts:146
 #: src/views/nginx_log/NginxLog.vue:145
 #: src/views/nginx_log/NginxLog.vue:145
 msgid "Nginx Log"
 msgid "Nginx Log"
 msgstr ""
 msgstr ""
@@ -870,10 +902,12 @@ msgid "Nginx restarted successfully"
 msgstr ""
 msgstr ""
 
 
 #: src/components/ChatGPT/ChatGPT.vue:277
 #: src/components/ChatGPT/ChatGPT.vue:277
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:345
-#: src/views/domain/DomainList.vue:143
+#: src/components/Notification/Notification.vue:84
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:351
+#: src/views/domain/DomainList.vue:142
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:81
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:81
 #: src/views/domain/ngx_conf/LocationEditor.vue:76
 #: src/views/domain/ngx_conf/LocationEditor.vue:76
+#: src/views/notification/Notification.vue:71
 msgid "No"
 msgid "No"
 msgstr ""
 msgstr ""
 
 
@@ -881,12 +915,12 @@ msgstr ""
 msgid "Node Secret"
 msgid "Node Secret"
 msgstr ""
 msgstr ""
 
 
-#: src/views/certificate/Certificate.vue:90
+#: src/views/certificate/Certificate.vue:98
 msgid "Not After"
 msgid "Not After"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:223
-#: src/routes/index.ts:225
+#: src/routes/index.ts:232
+#: src/routes/index.ts:234
 msgid "Not Found"
 msgid "Not Found"
 msgstr ""
 msgstr ""
 
 
@@ -899,6 +933,15 @@ msgstr ""
 msgid "Note"
 msgid "Note"
 msgstr ""
 msgstr ""
 
 
+#: src/views/notification/Notification.vue:63
+msgid "Notification"
+msgstr ""
+
+#: src/components/Notification/Notification.vue:82
+#: src/routes/index.ts:177
+msgid "Notifications"
+msgstr ""
+
 #: src/views/domain/cert/components/ObtainCert.vue:191
 #: src/views/domain/cert/components/ObtainCert.vue:191
 msgid "Obtain certificate"
 msgid "Obtain certificate"
 msgstr ""
 msgstr ""
@@ -914,16 +957,18 @@ msgid "Offline"
 msgstr ""
 msgstr ""
 
 
 #: src/components/ChatGPT/ChatGPT.vue:278
 #: src/components/ChatGPT/ChatGPT.vue:278
+#: src/components/Notification/Notification.vue:85
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:59
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:59
-#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:140
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:346
+#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:144
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:352
 #: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:104
 #: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:104
 #: src/views/domain/cert/components/ObtainCert.vue:149
 #: src/views/domain/cert/components/ObtainCert.vue:149
 #: src/views/domain/components/Deploy.vue:23
 #: src/views/domain/components/Deploy.vue:23
 #: src/views/domain/components/RightSettings.vue:51
 #: src/views/domain/components/RightSettings.vue:51
-#: src/views/domain/DomainList.vue:144
+#: src/views/domain/DomainList.vue:143
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:215
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:215
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:47
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:47
+#: src/views/notification/Notification.vue:72
 msgid "OK"
 msgid "OK"
 msgstr ""
 msgstr ""
 
 
@@ -1024,7 +1069,7 @@ msgstr ""
 msgid "Pre-release"
 msgid "Pre-release"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:184
+#: src/routes/index.ts:193
 #: src/views/preference/Preference.vue:85
 #: src/views/preference/Preference.vue:85
 msgid "Preference"
 msgid "Preference"
 msgstr ""
 msgstr ""
@@ -1088,11 +1133,23 @@ msgstr ""
 msgid "Reloading nginx"
 msgid "Reloading nginx"
 msgstr ""
 msgstr ""
 
 
+#: src/components/Notification/Notification.vue:52
+msgid "Removed successfully"
+msgstr ""
+
 #: src/views/certificate/RenewCert.vue:43
 #: src/views/certificate/RenewCert.vue:43
 #: src/views/certificate/RenewCert.vue:47
 #: src/views/certificate/RenewCert.vue:47
 msgid "Renew Certificate"
 msgid "Renew Certificate"
 msgstr ""
 msgstr ""
 
 
+#: src/language/constants.ts:38
+msgid "Renew Certificate Error"
+msgstr ""
+
+#: src/language/constants.ts:37
+msgid "Renew Certificate Success"
+msgstr ""
+
 #: src/views/certificate/RenewCert.vue:25
 #: src/views/certificate/RenewCert.vue:25
 msgid "Renew successfully"
 msgid "Renew successfully"
 msgstr ""
 msgstr ""
@@ -1122,7 +1179,7 @@ msgid "Running"
 msgstr ""
 msgstr ""
 
 
 #: src/components/ChatGPT/ChatGPT.vue:259
 #: src/components/ChatGPT/ChatGPT.vue:259
-#: src/views/certificate/CertificateEditor.vue:175
+#: src/views/certificate/CertificateEditor.vue:212
 #: src/views/config/ConfigEdit.vue:98
 #: src/views/config/ConfigEdit.vue:98
 #: src/views/domain/DomainEdit.vue:268
 #: src/views/domain/DomainEdit.vue:268
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:111
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:111
@@ -1141,7 +1198,7 @@ msgid "Save error %{msg}"
 msgstr ""
 msgstr ""
 
 
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:42
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:42
-#: src/views/certificate/CertificateEditor.vue:46
+#: src/views/certificate/CertificateEditor.vue:47
 #: src/views/preference/Preference.vue:58
 #: src/views/preference/Preference.vue:58
 msgid "Save successfully"
 msgid "Save successfully"
 msgstr ""
 msgstr ""
@@ -1209,27 +1266,29 @@ msgstr ""
 msgid "Single Directive"
 msgid "Single Directive"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:159
+#: src/routes/index.ts:160
 msgid "Site Logs"
 msgid "Site Logs"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:66
+#: src/routes/index.ts:67
 msgid "Sites List"
 msgid "Sites List"
 msgstr ""
 msgstr ""
 
 
-#: src/views/certificate/CertificateEditor.vue:135
+#: src/views/certificate/CertificateEditor.vue:172
 msgid "SSL Certificate Content"
 msgid "SSL Certificate Content"
 msgstr ""
 msgstr ""
 
 
-#: src/views/certificate/CertificateEditor.vue:142
+#: src/views/certificate/CertificateEditor.vue:179
 msgid "SSL Certificate Key Content"
 msgid "SSL Certificate Key Content"
 msgstr ""
 msgstr ""
 
 
-#: src/views/certificate/Certificate.vue:63
+#: src/views/certificate/Certificate.vue:71
+#: src/views/certificate/CertificateEditor.vue:163
 msgid "SSL Certificate Key Path"
 msgid "SSL Certificate Key Path"
 msgstr ""
 msgstr ""
 
 
-#: src/views/certificate/Certificate.vue:55
+#: src/views/certificate/Certificate.vue:63
+#: src/views/certificate/CertificateEditor.vue:154
 msgid "SSL Certificate Path"
 msgid "SSL Certificate Path"
 msgstr ""
 msgstr ""
 
 
@@ -1242,7 +1301,7 @@ msgstr ""
 msgid "Stable"
 msgid "Stable"
 msgstr ""
 msgstr ""
 
 
-#: src/views/certificate/Certificate.vue:71
+#: src/views/certificate/Certificate.vue:79
 #: src/views/domain/DomainList.vue:25
 #: src/views/domain/DomainList.vue:25
 #: src/views/environment/Environment.vue:78
 #: src/views/environment/Environment.vue:78
 msgid "Status"
 msgid "Status"
@@ -1260,6 +1319,10 @@ msgstr ""
 msgid "Subject Name: %{subject}"
 msgid "Subject Name: %{subject}"
 msgstr ""
 msgstr ""
 
 
+#: src/constants/index.ts:20
+msgid "Success"
+msgstr ""
+
 #: src/views/dashboard/ServerAnalytic.vue:233
 #: src/views/dashboard/ServerAnalytic.vue:233
 msgid "Swap"
 msgid "Swap"
 msgstr ""
 msgstr ""
@@ -1272,7 +1335,7 @@ msgstr ""
 msgid "Switch to light theme"
 msgid "Switch to light theme"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:192
+#: src/routes/index.ts:201
 msgid "System"
 msgid "System"
 msgstr ""
 msgstr ""
 
 
@@ -1284,7 +1347,7 @@ msgstr ""
 msgid "Target"
 msgid "Target"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:137
+#: src/routes/index.ts:138
 #: src/views/pty/Terminal.vue:95
 #: src/views/pty/Terminal.vue:95
 msgid "Terminal"
 msgid "Terminal"
 msgstr ""
 msgstr ""
@@ -1309,11 +1372,11 @@ msgstr ""
 msgid "The username or password is incorrect"
 msgid "The username or password is incorrect"
 msgstr ""
 msgstr ""
 
 
-#: src/views/certificate/CertificateEditor.vue:93
+#: src/views/certificate/CertificateEditor.vue:101
 msgid "This Auto Cert item is invalid, please remove it."
 msgid "This Auto Cert item is invalid, please remove it."
 msgstr ""
 msgstr ""
 
 
-#: src/views/certificate/CertificateEditor.vue:83
+#: src/views/certificate/CertificateEditor.vue:91
 msgid "This certificate is managed by Nginx UI"
 msgid "This certificate is managed by Nginx UI"
 msgstr ""
 msgstr ""
 
 
@@ -1321,12 +1384,17 @@ msgstr ""
 msgid "This field should not be empty"
 msgid "This field should not be empty"
 msgstr ""
 msgstr ""
 
 
+#: src/views/notification/Notification.vue:21
+msgid "Title"
+msgstr ""
+
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:42
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:42
 msgid "To make sure the certification auto-renewal can work normally, we need to add a location which can proxy the request from authority to backend, and we need to save this file and reload the Nginx. Are you sure you want to continue?"
 msgid "To make sure the certification auto-renewal can work normally, we need to add a location which can proxy the request from authority to backend, and we need to save this file and reload the Nginx. Are you sure you want to continue?"
 msgstr ""
 msgstr ""
 
 
 #: src/views/certificate/Certificate.vue:39
 #: src/views/certificate/Certificate.vue:39
 #: src/views/config/config.ts:14
 #: src/views/config/config.ts:14
+#: src/views/notification/Notification.vue:15
 msgid "Type"
 msgid "Type"
 msgstr ""
 msgstr ""
 
 
@@ -1344,7 +1412,7 @@ msgstr ""
 msgid "Updated successfully"
 msgid "Updated successfully"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:203
+#: src/routes/index.ts:212
 #: src/views/system/Upgrade.vue:143
 #: src/views/system/Upgrade.vue:143
 #: src/views/system/Upgrade.vue:235
 #: src/views/system/Upgrade.vue:235
 msgid "Upgrade"
 msgid "Upgrade"
@@ -1379,7 +1447,7 @@ msgstr ""
 msgid "Username (*)"
 msgid "Username (*)"
 msgstr ""
 msgstr ""
 
 
-#: src/views/certificate/Certificate.vue:80
+#: src/views/certificate/Certificate.vue:88
 msgid "Valid"
 msgid "Valid"
 msgstr ""
 msgstr ""
 
 
@@ -1387,6 +1455,11 @@ msgstr ""
 msgid "View"
 msgid "View"
 msgstr ""
 msgstr ""
 
 
+#: src/components/Notification/Notification.vue:143
+msgid "View all notifications"
+msgstr ""
+
+#: src/constants/index.ts:18
 #: src/views/config/InspectConfig.vue:36
 #: src/views/config/InspectConfig.vue:36
 #: src/views/domain/cert/components/AutoCertStepOne.vue:23
 #: src/views/domain/cert/components/AutoCertStepOne.vue:23
 #: src/views/domain/DomainAdd.vue:114
 #: src/views/domain/DomainAdd.vue:114

+ 143 - 69
app/src/language/ru_RU/app.po

@@ -9,30 +9,30 @@ msgstr ""
 "Content-Transfer-Encoding: 8bit\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 
 
-#: src/routes/index.ts:199
+#: src/routes/index.ts:208
 msgid "About"
 msgid "About"
 msgstr "О проекте"
 msgstr "О проекте"
 
 
-#: src/routes/index.ts:151 src/views/domain/ngx_conf/LogEntry.vue:78
+#: src/routes/index.ts:152 src/views/domain/ngx_conf/LogEntry.vue:78
 msgid "Access Logs"
 msgid "Access Logs"
 msgstr "Журнал доступа"
 msgstr "Журнал доступа"
 
 
-#: src/views/certificate/Certificate.vue:96
+#: src/views/certificate/Certificate.vue:104
 #: src/views/certificate/DNSCredential.vue:32 src/views/config/config.ts:36
 #: src/views/certificate/DNSCredential.vue:32 src/views/config/config.ts:36
 #: src/views/domain/DomainList.vue:50 src/views/environment/Environment.vue:105
 #: src/views/domain/DomainList.vue:50 src/views/environment/Environment.vue:105
-#: src/views/user/User.vue:46
+#: src/views/notification/Notification.vue:38 src/views/user/User.vue:46
 msgid "Action"
 msgid "Action"
 msgstr "Действие"
 msgstr "Действие"
 
 
-#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:114
-#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:137
+#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:115
+#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:141
+#: src/views/certificate/Certificate.vue:120
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:115
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:115
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:308
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:308
 msgid "Add"
 msgid "Add"
 msgstr "Добавить"
 msgstr "Добавить"
 
 
-#: src/routes/index.ts:122 src/views/certificate/Certificate.vue:115
-#: src/views/certificate/CertificateEditor.vue:76
+#: src/routes/index.ts:123 src/views/certificate/CertificateEditor.vue:84
 #, fuzzy
 #, fuzzy
 msgid "Add Certificate"
 msgid "Add Certificate"
 msgstr "Статус сертификата"
 msgstr "Статус сертификата"
@@ -46,7 +46,7 @@ msgstr "Добавить директиву ниже"
 msgid "Add Location"
 msgid "Add Location"
 msgstr "Добавить Location"
 msgstr "Добавить Location"
 
 
-#: src/routes/index.ts:70 src/views/domain/DomainAdd.vue:91
+#: src/routes/index.ts:71 src/views/domain/DomainAdd.vue:91
 msgid "Add Site"
 msgid "Add Site"
 msgstr "Добавть Сайт"
 msgstr "Добавть Сайт"
 
 
@@ -75,13 +75,19 @@ msgstr ""
 msgid "Arch"
 msgid "Arch"
 msgstr ""
 msgstr ""
 
 
+#: src/components/Notification/Notification.vue:86
+#: src/views/notification/Notification.vue:73
+#, fuzzy
+msgid "Are you sure you want to clear all notifications?"
+msgstr "Вы уверены, что хотите удалить эту директиву?"
+
 #: src/components/ChatGPT/ChatGPT.vue:279
 #: src/components/ChatGPT/ChatGPT.vue:279
 #, fuzzy
 #, fuzzy
 msgid "Are you sure you want to clear the record of chat?"
 msgid "Are you sure you want to clear the record of chat?"
 msgstr "Вы уверены, что хотите удалить эту директиву?"
 msgstr "Вы уверены, что хотите удалить эту директиву?"
 
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:347
-#: src/views/domain/DomainList.vue:145
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:353
+#: src/views/domain/DomainList.vue:144
 #, fuzzy
 #, fuzzy
 msgid "Are you sure you want to delete?"
 msgid "Are you sure you want to delete?"
 msgstr "Вы уверены, что хотите удалить эту директиву?"
 msgstr "Вы уверены, что хотите удалить эту директиву?"
@@ -124,7 +130,7 @@ msgstr "Автообновление отключено для %{name}"
 msgid "Auto-renewal enabled for %{name}"
 msgid "Auto-renewal enabled for %{name}"
 msgstr "Автообновление включено для %{name}"
 msgstr "Автообновление включено для %{name}"
 
 
-#: src/views/certificate/CertificateEditor.vue:168
+#: src/views/certificate/CertificateEditor.vue:205
 #: src/views/config/Config.vue:75 src/views/config/ConfigEdit.vue:89
 #: src/views/config/Config.vue:75 src/views/config/ConfigEdit.vue:89
 #: src/views/domain/DomainEdit.vue:261 src/views/nginx_log/NginxLog.vue:170
 #: src/views/domain/DomainEdit.vue:261 src/views/nginx_log/NginxLog.vue:170
 msgid "Back"
 msgid "Back"
@@ -166,7 +172,7 @@ msgstr ""
 
 
 #: src/components/ChatGPT/ChatGPT.vue:260
 #: src/components/ChatGPT/ChatGPT.vue:260
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:58
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:58
-#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:139
+#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:143
 #: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:103
 #: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:103
 #: src/views/domain/cert/components/ObtainCert.vue:150
 #: src/views/domain/cert/components/ObtainCert.vue:150
 #: src/views/domain/components/Deploy.vue:24
 #: src/views/domain/components/Deploy.vue:24
@@ -184,17 +190,17 @@ msgstr "Срок действия сертификата истек"
 msgid "Certificate is valid"
 msgid "Certificate is valid"
 msgstr "Сертификат действителен"
 msgstr "Сертификат действителен"
 
 
-#: src/views/certificate/CertificateEditor.vue:119
+#: src/views/certificate/CertificateEditor.vue:127
 #: src/views/domain/cert/Cert.vue:35
 #: src/views/domain/cert/Cert.vue:35
 msgid "Certificate Status"
 msgid "Certificate Status"
 msgstr "Статус сертификата"
 msgstr "Статус сертификата"
 
 
-#: src/routes/index.ts:100 src/views/certificate/Certificate.vue:109
+#: src/routes/index.ts:101 src/views/certificate/Certificate.vue:117
 #, fuzzy
 #, fuzzy
 msgid "Certificates"
 msgid "Certificates"
 msgstr "Статус сертификата"
 msgstr "Статус сертификата"
 
 
-#: src/routes/index.ts:109
+#: src/routes/index.ts:110
 #, fuzzy
 #, fuzzy
 msgid "Certificates List"
 msgid "Certificates List"
 msgstr "Список"
 msgstr "Список"
@@ -230,9 +236,17 @@ msgid "Cleaning environment variables"
 msgstr "Очистка переменных среды"
 msgstr "Очистка переменных среды"
 
 
 #: src/components/ChatGPT/ChatGPT.vue:283
 #: src/components/ChatGPT/ChatGPT.vue:283
+#: src/components/Notification/Notification.vue:91
+#: src/views/notification/Notification.vue:78
 msgid "Clear"
 msgid "Clear"
 msgstr "Очистить"
 msgstr "Очистить"
 
 
+#: src/components/Notification/Notification.vue:42
+#: src/views/notification/Notification.vue:47
+#, fuzzy
+msgid "Cleared successfully"
+msgstr "Отключено успешно"
+
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:97
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:97
 #: src/views/domain/ngx_conf/LocationEditor.vue:121
 #: src/views/domain/ngx_conf/LocationEditor.vue:121
 #: src/views/domain/ngx_conf/LocationEditor.vue:90
 #: src/views/domain/ngx_conf/LocationEditor.vue:90
@@ -287,7 +301,7 @@ msgstr "CPU:"
 msgid "Create Another"
 msgid "Create Another"
 msgstr "Создать еще"
 msgstr "Создать еще"
 
 
-#: src/views/user/User.vue:34
+#: src/views/notification/Notification.vue:32 src/views/user/User.vue:34
 msgid "Created at"
 msgid "Created at"
 msgstr "Создан в"
 msgstr "Создан в"
 
 
@@ -312,7 +326,7 @@ msgstr "Текущяя версия"
 msgid "Custom"
 msgid "Custom"
 msgstr "Пользовательский"
 msgstr "Пользовательский"
 
 
-#: src/routes/index.ts:51
+#: src/routes/index.ts:52
 msgid "Dashboard"
 msgid "Dashboard"
 msgstr "Доска"
 msgstr "Доска"
 
 
@@ -320,7 +334,7 @@ msgstr "Доска"
 msgid "Database (Optional, default: database)"
 msgid "Database (Optional, default: database)"
 msgstr "База данных (Опционально, по умолчанию: database)"
 msgstr "База данных (Опционально, по умолчанию: database)"
 
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:354
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:360
 #: src/views/domain/DomainList.vue:153
 #: src/views/domain/DomainList.vue:153
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:259
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:259
 msgid "Delete"
 msgid "Delete"
@@ -358,6 +372,10 @@ msgstr "Saved successfully"
 msgid "Description"
 msgid "Description"
 msgstr "Описание"
 msgstr "Описание"
 
 
+#: src/views/notification/Notification.vue:28
+msgid "Details"
+msgstr ""
+
 #: src/views/system/About.vue:25
 #: src/views/system/About.vue:25
 msgid "Development Mode"
 msgid "Development Mode"
 msgstr "Режим разработки"
 msgstr "Режим разработки"
@@ -385,7 +403,7 @@ msgid "Disable auto-renewal failed for %{name}"
 msgstr "Не удалось отключить автоматическое продление для %{name}"
 msgstr "Не удалось отключить автоматическое продление для %{name}"
 
 
 #: src/views/domain/cert/ChangeCert.vue:48 src/views/domain/DomainEdit.vue:190
 #: src/views/domain/cert/ChangeCert.vue:48 src/views/domain/DomainEdit.vue:190
-#: src/views/domain/DomainList.vue:123
+#: src/views/domain/DomainList.vue:122
 msgid "Disabled"
 msgid "Disabled"
 msgstr "Отключено"
 msgstr "Отключено"
 
 
@@ -398,7 +416,7 @@ msgstr "Отключено успешно"
 msgid "Disk IO"
 msgid "Disk IO"
 msgstr "Нагрузка на Диск IO"
 msgstr "Нагрузка на Диск IO"
 
 
-#: src/routes/index.ts:130 src/views/certificate/DNSCredential.vue:39
+#: src/routes/index.ts:131 src/views/certificate/DNSCredential.vue:39
 msgid "DNS Credentials"
 msgid "DNS Credentials"
 msgstr ""
 msgstr ""
 
 
@@ -446,7 +464,7 @@ msgstr "Вы хотите удалить этот сервер?"
 msgid "Domain Config Created Successfully"
 msgid "Domain Config Created Successfully"
 msgstr "Конфигурация домена успешно создана"
 msgstr "Конфигурация домена успешно создана"
 
 
-#: src/views/certificate/CertificateEditor.vue:103
+#: src/views/certificate/CertificateEditor.vue:111
 #, fuzzy
 #, fuzzy
 msgid "Domains list is empty, try to reopen Auto Cert for %{config}"
 msgid "Domains list is empty, try to reopen Auto Cert for %{config}"
 msgstr ""
 msgstr ""
@@ -465,7 +483,7 @@ msgid "Dry run mode enabled"
 msgstr "Включен пробный режим"
 msgstr "Включен пробный режим"
 
 
 #: src/views/domain/components/SiteDuplicate.vue:128
 #: src/views/domain/components/SiteDuplicate.vue:128
-#: src/views/domain/DomainList.vue:139
+#: src/views/domain/DomainList.vue:138
 msgid "Duplicate"
 msgid "Duplicate"
 msgstr "Дублировать"
 msgstr "Дублировать"
 
 
@@ -493,11 +511,11 @@ msgstr "Saved successfully"
 msgid "Edit %{n}"
 msgid "Edit %{n}"
 msgstr "Редактировать %{n}"
 msgstr "Редактировать %{n}"
 
 
-#: src/routes/index.ts:92 src/views/config/ConfigEdit.vue:83
+#: src/routes/index.ts:93 src/views/config/ConfigEdit.vue:83
 msgid "Edit Configuration"
 msgid "Edit Configuration"
 msgstr "Редактировать Конфигурацию"
 msgstr "Редактировать Конфигурацию"
 
 
-#: src/routes/index.ts:74
+#: src/routes/index.ts:75
 msgid "Edit Site"
 msgid "Edit Site"
 msgstr "Редактировать Сайт"
 msgstr "Редактировать Сайт"
 
 
@@ -538,7 +556,7 @@ msgstr "Включить TLS"
 #: src/views/domain/cert/ChangeCert.vue:44
 #: src/views/domain/cert/ChangeCert.vue:44
 #: src/views/domain/components/Deploy.vue:89
 #: src/views/domain/components/Deploy.vue:89
 #: src/views/domain/components/RightSettings.vue:75
 #: src/views/domain/components/RightSettings.vue:75
-#: src/views/domain/DomainEdit.vue:184 src/views/domain/DomainList.vue:131
+#: src/views/domain/DomainEdit.vue:184 src/views/domain/DomainList.vue:130
 msgid "Enabled"
 msgid "Enabled"
 msgstr "Включено"
 msgstr "Включено"
 
 
@@ -552,7 +570,7 @@ msgstr "Активировано успешно"
 msgid "Encrypt website with Let's Encrypt"
 msgid "Encrypt website with Let's Encrypt"
 msgstr "Использовать для сайта Let's Encrypt"
 msgstr "Использовать для сайта Let's Encrypt"
 
 
-#: src/routes/index.ts:168 src/views/environment/Environment.vue:113
+#: src/routes/index.ts:169 src/views/environment/Environment.vue:113
 msgid "Environment"
 msgid "Environment"
 msgstr "Окружение"
 msgstr "Окружение"
 
 
@@ -561,11 +579,11 @@ msgstr "Окружение"
 msgid "Environments"
 msgid "Environments"
 msgstr "Комментарии"
 msgstr "Комментарии"
 
 
-#: src/views/config/InspectConfig.vue:47
+#: src/constants/index.ts:17 src/views/config/InspectConfig.vue:47
 msgid "Error"
 msgid "Error"
 msgstr "Ошибка"
 msgstr "Ошибка"
 
 
-#: src/routes/index.ts:155 src/views/domain/ngx_conf/LogEntry.vue:86
+#: src/routes/index.ts:156 src/views/domain/ngx_conf/LogEntry.vue:86
 msgid "Error Logs"
 msgid "Error Logs"
 msgstr "Ошибка логирования"
 msgstr "Ошибка логирования"
 
 
@@ -573,7 +591,7 @@ msgstr "Ошибка логирования"
 msgid "Executable Path"
 msgid "Executable Path"
 msgstr "Исполняемый путь"
 msgstr "Исполняемый путь"
 
 
-#: src/views/certificate/Certificate.vue:84
+#: src/views/certificate/Certificate.vue:92
 msgid "Expired"
 msgid "Expired"
 msgstr ""
 msgstr ""
 
 
@@ -639,6 +657,11 @@ msgstr "Ошибка форматирования %{msg}"
 msgid "Format successfully"
 msgid "Format successfully"
 msgstr "Форматирование успешно"
 msgstr "Форматирование успешно"
 
 
+#: src/views/certificate/Certificate.vue:45
+#, fuzzy
+msgid "General Certificate"
+msgstr "Сертификат действителен"
+
 #: src/components/StdDesign/StdDataEntry/components/StdPassword.vue:60
 #: src/components/StdDesign/StdDataEntry/components/StdPassword.vue:60
 msgid "Generate"
 msgid "Generate"
 msgstr "Сгенерировать"
 msgstr "Сгенерировать"
@@ -676,7 +699,7 @@ msgstr ""
 msgid "GPT-4-Turbo"
 msgid "GPT-4-Turbo"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:44
+#: src/routes/index.ts:45
 msgid "Home"
 msgid "Home"
 msgstr "Главная"
 msgstr "Главная"
 
 
@@ -696,6 +719,10 @@ msgstr "Порт HTTP"
 msgid "HTTP01"
 msgid "HTTP01"
 msgstr ""
 msgstr ""
 
 
+#: src/constants/index.ts:19
+msgid "Info"
+msgstr ""
+
 #: src/language/constants.ts:27
 #: src/language/constants.ts:27
 msgid "Initial core upgrader error"
 msgid "Initial core upgrader error"
 msgstr "Ошибка первоначального обновления ядра"
 msgstr "Ошибка первоначального обновления ядра"
@@ -704,7 +731,7 @@ msgstr "Ошибка первоначального обновления ядр
 msgid "Initialing core upgrader"
 msgid "Initialing core upgrader"
 msgstr "Инициализация программы обновления ядра"
 msgstr "Инициализация программы обновления ядра"
 
 
-#: src/routes/index.ts:211 src/views/other/Install.vue:139
+#: src/routes/index.ts:220 src/views/other/Install.vue:139
 msgid "Install"
 msgid "Install"
 msgstr "Установить"
 msgstr "Установить"
 
 
@@ -766,12 +793,12 @@ msgstr "Location"
 msgid "Locations"
 msgid "Locations"
 msgstr "Locations"
 msgstr "Locations"
 
 
-#: src/views/certificate/CertificateEditor.vue:156
+#: src/views/certificate/CertificateEditor.vue:193
 #, fuzzy
 #, fuzzy
 msgid "Log"
 msgid "Log"
 msgstr "Логин"
 msgstr "Логин"
 
 
-#: src/routes/index.ts:217 src/views/other/Login.vue:147
+#: src/routes/index.ts:226 src/views/other/Login.vue:147
 msgid "Login"
 msgid "Login"
 msgstr "Логин"
 msgstr "Логин"
 
 
@@ -779,7 +806,7 @@ msgstr "Логин"
 msgid "Login successful"
 msgid "Login successful"
 msgstr "Авторизация успешна"
 msgstr "Авторизация успешна"
 
 
-#: src/layouts/HeaderLayout.vue:21
+#: src/layouts/HeaderLayout.vue:22
 msgid "Logout successful"
 msgid "Logout successful"
 msgstr "Выход выполнен успешно"
 msgstr "Выход выполнен успешно"
 
 
@@ -792,18 +819,23 @@ msgstr ""
 "Убедитесь, что вы настроили обратный прокси-сервер для каталога .well-known "
 "Убедитесь, что вы настроили обратный прокси-сервер для каталога .well-known "
 "на HTTPChallengePort перед получением сертификата»."
 "на HTTPChallengePort перед получением сертификата»."
 
 
-#: src/routes/index.ts:83
+#: src/routes/index.ts:84
 msgid "Manage Configs"
 msgid "Manage Configs"
 msgstr "Конфигурации"
 msgstr "Конфигурации"
 
 
-#: src/routes/index.ts:58 src/views/domain/DomainList.vue:102
+#: src/routes/index.ts:59 src/views/domain/DomainList.vue:102
 msgid "Manage Sites"
 msgid "Manage Sites"
 msgstr "Сайты"
 msgstr "Сайты"
 
 
-#: src/routes/index.ts:176 src/views/user/User.vue:53
+#: src/routes/index.ts:185 src/views/user/User.vue:53
 msgid "Manage Users"
 msgid "Manage Users"
 msgstr "Пользователи"
 msgstr "Пользователи"
 
 
+#: src/views/certificate/Certificate.vue:44
+#, fuzzy
+msgid "Managed Certificate"
+msgstr "Сертификат действителен"
+
 #: src/views/dashboard/ServerAnalytic.vue:220
 #: src/views/dashboard/ServerAnalytic.vue:220
 msgid "Memory"
 msgid "Memory"
 msgstr "Память"
 msgstr "Память"
@@ -813,13 +845,13 @@ msgid "Memory and Storage"
 msgstr "Память и хранилище"
 msgstr "Память и хранилище"
 
 
 #: src/components/ChatGPT/ChatGPT.vue:256
 #: src/components/ChatGPT/ChatGPT.vue:256
-#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:137
+#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:141
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:336
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:336
 #, fuzzy
 #, fuzzy
 msgid "Modify"
 msgid "Modify"
 msgstr "Изменить"
 msgstr "Изменить"
 
 
-#: src/routes/index.ts:114 src/views/certificate/CertificateEditor.vue:76
+#: src/routes/index.ts:115 src/views/certificate/CertificateEditor.vue:84
 #, fuzzy
 #, fuzzy
 msgid "Modify Certificate"
 msgid "Modify Certificate"
 msgstr "Статус сертификата"
 msgstr "Статус сертификата"
@@ -834,6 +866,7 @@ msgid "Multi-line Directive"
 msgstr "Одиночная директива"
 msgstr "Одиночная директива"
 
 
 #: src/views/certificate/Certificate.vue:22
 #: src/views/certificate/Certificate.vue:22
+#: src/views/certificate/CertificateEditor.vue:145
 #: src/views/certificate/DNSCredential.vue:13 src/views/config/config.ts:9
 #: src/views/certificate/DNSCredential.vue:13 src/views/config/config.ts:9
 #: src/views/domain/cert/ChangeCert.vue:21
 #: src/views/domain/cert/ChangeCert.vue:21
 #: src/views/domain/components/RightSettings.vue:81
 #: src/views/domain/components/RightSettings.vue:81
@@ -889,7 +922,7 @@ msgstr "Управление Nginx"
 msgid "Nginx Error Log Path"
 msgid "Nginx Error Log Path"
 msgstr "Путь для Nginx Error Log"
 msgstr "Путь для Nginx Error Log"
 
 
-#: src/routes/index.ts:145 src/views/nginx_log/NginxLog.vue:145
+#: src/routes/index.ts:146 src/views/nginx_log/NginxLog.vue:145
 msgid "Nginx Log"
 msgid "Nginx Log"
 msgstr "Журнал"
 msgstr "Журнал"
 
 
@@ -904,10 +937,12 @@ msgid "Nginx restarted successfully"
 msgstr "Nginx успешно перезапущен"
 msgstr "Nginx успешно перезапущен"
 
 
 #: src/components/ChatGPT/ChatGPT.vue:277
 #: src/components/ChatGPT/ChatGPT.vue:277
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:345
-#: src/views/domain/DomainList.vue:143
+#: src/components/Notification/Notification.vue:84
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:351
+#: src/views/domain/DomainList.vue:142
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:81
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:81
 #: src/views/domain/ngx_conf/LocationEditor.vue:76
 #: src/views/domain/ngx_conf/LocationEditor.vue:76
+#: src/views/notification/Notification.vue:71
 msgid "No"
 msgid "No"
 msgstr "Нет"
 msgstr "Нет"
 
 
@@ -915,11 +950,11 @@ msgstr "Нет"
 msgid "Node Secret"
 msgid "Node Secret"
 msgstr ""
 msgstr ""
 
 
-#: src/views/certificate/Certificate.vue:90
+#: src/views/certificate/Certificate.vue:98
 msgid "Not After"
 msgid "Not After"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:223 src/routes/index.ts:225
+#: src/routes/index.ts:232 src/routes/index.ts:234
 msgid "Not Found"
 msgid "Not Found"
 msgstr "Не найден"
 msgstr "Не найден"
 
 
@@ -932,6 +967,16 @@ msgstr "Недействительно до: %{date}"
 msgid "Note"
 msgid "Note"
 msgstr "Заметка"
 msgstr "Заметка"
 
 
+#: src/views/notification/Notification.vue:63
+#, fuzzy
+msgid "Notification"
+msgstr "Сертификат"
+
+#: src/components/Notification/Notification.vue:82 src/routes/index.ts:177
+#, fuzzy
+msgid "Notifications"
+msgstr "Сертификат"
+
 #: src/views/domain/cert/components/ObtainCert.vue:191
 #: src/views/domain/cert/components/ObtainCert.vue:191
 #, fuzzy
 #, fuzzy
 msgid "Obtain certificate"
 msgid "Obtain certificate"
@@ -948,16 +993,18 @@ msgid "Offline"
 msgstr ""
 msgstr ""
 
 
 #: src/components/ChatGPT/ChatGPT.vue:278
 #: src/components/ChatGPT/ChatGPT.vue:278
+#: src/components/Notification/Notification.vue:85
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:59
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:59
-#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:140
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:346
+#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:144
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:352
 #: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:104
 #: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:104
 #: src/views/domain/cert/components/ObtainCert.vue:149
 #: src/views/domain/cert/components/ObtainCert.vue:149
 #: src/views/domain/components/Deploy.vue:23
 #: src/views/domain/components/Deploy.vue:23
 #: src/views/domain/components/RightSettings.vue:51
 #: src/views/domain/components/RightSettings.vue:51
-#: src/views/domain/DomainList.vue:144
+#: src/views/domain/DomainList.vue:143
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:215
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:215
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:47
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:47
+#: src/views/notification/Notification.vue:72
 msgid "OK"
 msgid "OK"
 msgstr ""
 msgstr ""
 
 
@@ -1064,7 +1111,7 @@ msgstr ""
 msgid "Pre-release"
 msgid "Pre-release"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:184 src/views/preference/Preference.vue:85
+#: src/routes/index.ts:193 src/views/preference/Preference.vue:85
 msgid "Preference"
 msgid "Preference"
 msgstr "Настройки"
 msgstr "Настройки"
 
 
@@ -1129,12 +1176,27 @@ msgstr "Перезагружается"
 msgid "Reloading nginx"
 msgid "Reloading nginx"
 msgstr "Перезагружается nginx"
 msgstr "Перезагружается nginx"
 
 
+#: src/components/Notification/Notification.vue:52
+#, fuzzy
+msgid "Removed successfully"
+msgstr "Успешно сохранено"
+
 #: src/views/certificate/RenewCert.vue:43
 #: src/views/certificate/RenewCert.vue:43
 #: src/views/certificate/RenewCert.vue:47
 #: src/views/certificate/RenewCert.vue:47
 #, fuzzy
 #, fuzzy
 msgid "Renew Certificate"
 msgid "Renew Certificate"
 msgstr "Сертификат действителен"
 msgstr "Сертификат действителен"
 
 
+#: src/language/constants.ts:38
+#, fuzzy
+msgid "Renew Certificate Error"
+msgstr "Сертификат действителен"
+
+#: src/language/constants.ts:37
+#, fuzzy
+msgid "Renew Certificate Success"
+msgstr "Сертификат действителен"
+
 #: src/views/certificate/RenewCert.vue:25
 #: src/views/certificate/RenewCert.vue:25
 #, fuzzy
 #, fuzzy
 msgid "Renew successfully"
 msgid "Renew successfully"
@@ -1166,7 +1228,7 @@ msgid "Running"
 msgstr "Выполняется"
 msgstr "Выполняется"
 
 
 #: src/components/ChatGPT/ChatGPT.vue:259
 #: src/components/ChatGPT/ChatGPT.vue:259
-#: src/views/certificate/CertificateEditor.vue:175
+#: src/views/certificate/CertificateEditor.vue:212
 #: src/views/config/ConfigEdit.vue:98 src/views/domain/DomainEdit.vue:268
 #: src/views/config/ConfigEdit.vue:98 src/views/domain/DomainEdit.vue:268
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:111
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:111
 #: src/views/preference/Preference.vue:113
 #: src/views/preference/Preference.vue:113
@@ -1183,7 +1245,7 @@ msgid "Save error %{msg}"
 msgstr "Ошибка сохранения %{msg}"
 msgstr "Ошибка сохранения %{msg}"
 
 
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:42
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:42
-#: src/views/certificate/CertificateEditor.vue:46
+#: src/views/certificate/CertificateEditor.vue:47
 #: src/views/preference/Preference.vue:58
 #: src/views/preference/Preference.vue:58
 #, fuzzy
 #, fuzzy
 msgid "Save successfully"
 msgid "Save successfully"
@@ -1250,31 +1312,33 @@ msgstr ""
 msgid "Single Directive"
 msgid "Single Directive"
 msgstr "Одиночная Директива"
 msgstr "Одиночная Директива"
 
 
-#: src/routes/index.ts:159
+#: src/routes/index.ts:160
 #, fuzzy
 #, fuzzy
 msgid "Site Logs"
 msgid "Site Logs"
 msgstr "Логи сайтов"
 msgstr "Логи сайтов"
 
 
-#: src/routes/index.ts:66
+#: src/routes/index.ts:67
 msgid "Sites List"
 msgid "Sites List"
 msgstr "Список сайтов"
 msgstr "Список сайтов"
 
 
-#: src/views/certificate/CertificateEditor.vue:135
+#: src/views/certificate/CertificateEditor.vue:172
 #, fuzzy
 #, fuzzy
 msgid "SSL Certificate Content"
 msgid "SSL Certificate Content"
 msgstr "Содержание сертификата SSL"
 msgstr "Содержание сертификата SSL"
 
 
-#: src/views/certificate/CertificateEditor.vue:142
+#: src/views/certificate/CertificateEditor.vue:179
 #, fuzzy
 #, fuzzy
 msgid "SSL Certificate Key Content"
 msgid "SSL Certificate Key Content"
 msgstr "Содержание ключа сертификата SSL"
 msgstr "Содержание ключа сертификата SSL"
 
 
-#: src/views/certificate/Certificate.vue:63
+#: src/views/certificate/Certificate.vue:71
+#: src/views/certificate/CertificateEditor.vue:163
 #, fuzzy
 #, fuzzy
 msgid "SSL Certificate Key Path"
 msgid "SSL Certificate Key Path"
 msgstr "Путь к ключу сертификата SSL"
 msgstr "Путь к ключу сертификата SSL"
 
 
-#: src/views/certificate/Certificate.vue:55
+#: src/views/certificate/Certificate.vue:63
+#: src/views/certificate/CertificateEditor.vue:154
 #, fuzzy
 #, fuzzy
 msgid "SSL Certificate Path"
 msgid "SSL Certificate Path"
 msgstr "Путь к сертификату SSL"
 msgstr "Путь к сертификату SSL"
@@ -1289,7 +1353,7 @@ msgstr "Логин"
 msgid "Stable"
 msgid "Stable"
 msgstr "Таблица"
 msgstr "Таблица"
 
 
-#: src/views/certificate/Certificate.vue:71 src/views/domain/DomainList.vue:25
+#: src/views/certificate/Certificate.vue:79 src/views/domain/DomainList.vue:25
 #: src/views/environment/Environment.vue:78
 #: src/views/environment/Environment.vue:78
 msgid "Status"
 msgid "Status"
 msgstr "Статус"
 msgstr "Статус"
@@ -1307,6 +1371,10 @@ msgstr "Хранилище"
 msgid "Subject Name: %{subject}"
 msgid "Subject Name: %{subject}"
 msgstr "Название темы: %{name}"
 msgstr "Название темы: %{name}"
 
 
+#: src/constants/index.ts:20
+msgid "Success"
+msgstr ""
+
 #: src/views/dashboard/ServerAnalytic.vue:233
 #: src/views/dashboard/ServerAnalytic.vue:233
 msgid "Swap"
 msgid "Swap"
 msgstr "Своп"
 msgstr "Своп"
@@ -1319,7 +1387,7 @@ msgstr ""
 msgid "Switch to light theme"
 msgid "Switch to light theme"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:192
+#: src/routes/index.ts:201
 msgid "System"
 msgid "System"
 msgstr "Система"
 msgstr "Система"
 
 
@@ -1332,7 +1400,7 @@ msgstr "Таблица"
 msgid "Target"
 msgid "Target"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:137 src/views/pty/Terminal.vue:95
+#: src/routes/index.ts:138 src/views/pty/Terminal.vue:95
 msgid "Terminal"
 msgid "Terminal"
 msgstr "Терминал"
 msgstr "Терминал"
 
 
@@ -1366,12 +1434,12 @@ msgstr ""
 msgid "The username or password is incorrect"
 msgid "The username or password is incorrect"
 msgstr "Имя пользователя или пароль неверны"
 msgstr "Имя пользователя или пароль неверны"
 
 
-#: src/views/certificate/CertificateEditor.vue:93
+#: src/views/certificate/CertificateEditor.vue:101
 #, fuzzy
 #, fuzzy
 msgid "This Auto Cert item is invalid, please remove it."
 msgid "This Auto Cert item is invalid, please remove it."
 msgstr "Этот элемент автосертификата недействителен, удалите его.."
 msgstr "Этот элемент автосертификата недействителен, удалите его.."
 
 
-#: src/views/certificate/CertificateEditor.vue:83
+#: src/views/certificate/CertificateEditor.vue:91
 msgid "This certificate is managed by Nginx UI"
 msgid "This certificate is managed by Nginx UI"
 msgstr ""
 msgstr ""
 
 
@@ -1379,6 +1447,10 @@ msgstr ""
 msgid "This field should not be empty"
 msgid "This field should not be empty"
 msgstr ""
 msgstr ""
 
 
+#: src/views/notification/Notification.vue:21
+msgid "Title"
+msgstr ""
+
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:42
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:42
 msgid ""
 msgid ""
 "To make sure the certification auto-renewal can work normally, we need to "
 "To make sure the certification auto-renewal can work normally, we need to "
@@ -1388,6 +1460,7 @@ msgid ""
 msgstr ""
 msgstr ""
 
 
 #: src/views/certificate/Certificate.vue:39 src/views/config/config.ts:14
 #: src/views/certificate/Certificate.vue:39 src/views/config/config.ts:14
+#: src/views/notification/Notification.vue:15
 msgid "Type"
 msgid "Type"
 msgstr "Тип"
 msgstr "Тип"
 
 
@@ -1404,7 +1477,7 @@ msgstr "Обновлено в"
 msgid "Updated successfully"
 msgid "Updated successfully"
 msgstr "Обновлено успешно"
 msgstr "Обновлено успешно"
 
 
-#: src/routes/index.ts:203 src/views/system/Upgrade.vue:143
+#: src/routes/index.ts:212 src/views/system/Upgrade.vue:143
 #: src/views/system/Upgrade.vue:235
 #: src/views/system/Upgrade.vue:235
 msgid "Upgrade"
 msgid "Upgrade"
 msgstr "Обновление"
 msgstr "Обновление"
@@ -1439,7 +1512,7 @@ msgstr "Имя пользователя"
 msgid "Username (*)"
 msgid "Username (*)"
 msgstr "Имя пользователя (*)"
 msgstr "Имя пользователя (*)"
 
 
-#: src/views/certificate/Certificate.vue:80
+#: src/views/certificate/Certificate.vue:88
 msgid "Valid"
 msgid "Valid"
 msgstr ""
 msgstr ""
 
 
@@ -1447,7 +1520,12 @@ msgstr ""
 msgid "View"
 msgid "View"
 msgstr "Просмотр"
 msgstr "Просмотр"
 
 
-#: src/views/config/InspectConfig.vue:36
+#: src/components/Notification/Notification.vue:143
+#, fuzzy
+msgid "View all notifications"
+msgstr "Сертификат"
+
+#: src/constants/index.ts:18 src/views/config/InspectConfig.vue:36
 #: src/views/domain/cert/components/AutoCertStepOne.vue:23
 #: src/views/domain/cert/components/AutoCertStepOne.vue:23
 #: src/views/domain/DomainAdd.vue:114
 #: src/views/domain/DomainAdd.vue:114
 msgid "Warning"
 msgid "Warning"
@@ -1510,10 +1588,6 @@ msgstr "Вы можете проверить обновление Nginx UI на
 #~ msgid "Auto cert is enabled, please do not modify this certification."
 #~ msgid "Auto cert is enabled, please do not modify this certification."
 #~ msgstr "Включено автомотическое получения сертификата. Не правте руками."
 #~ msgstr "Включено автомотическое получения сертификата. Не правте руками."
 
 
-#, fuzzy
-#~ msgid "Certification"
-#~ msgstr "Сертификат"
-
 #~ msgid "Delete ID: %{id}"
 #~ msgid "Delete ID: %{id}"
 #~ msgstr "Удалить ID: %{id}"
 #~ msgstr "Удалить ID: %{id}"
 
 

BIN
app/src/language/zh_CN/app.mo


+ 133 - 68
app/src/language/zh_CN/app.po

@@ -13,30 +13,30 @@ msgstr ""
 "Generated-By: easygettext\n"
 "Generated-By: easygettext\n"
 "X-Generator: Poedit 3.4.1\n"
 "X-Generator: Poedit 3.4.1\n"
 
 
-#: src/routes/index.ts:199
+#: src/routes/index.ts:208
 msgid "About"
 msgid "About"
 msgstr "关于"
 msgstr "关于"
 
 
-#: src/routes/index.ts:151 src/views/domain/ngx_conf/LogEntry.vue:78
+#: src/routes/index.ts:152 src/views/domain/ngx_conf/LogEntry.vue:78
 msgid "Access Logs"
 msgid "Access Logs"
 msgstr "访问日志"
 msgstr "访问日志"
 
 
-#: src/views/certificate/Certificate.vue:96
+#: src/views/certificate/Certificate.vue:104
 #: src/views/certificate/DNSCredential.vue:32 src/views/config/config.ts:36
 #: src/views/certificate/DNSCredential.vue:32 src/views/config/config.ts:36
 #: src/views/domain/DomainList.vue:50 src/views/environment/Environment.vue:105
 #: src/views/domain/DomainList.vue:50 src/views/environment/Environment.vue:105
-#: src/views/user/User.vue:46
+#: src/views/notification/Notification.vue:38 src/views/user/User.vue:46
 msgid "Action"
 msgid "Action"
 msgstr "操作"
 msgstr "操作"
 
 
-#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:114
-#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:137
+#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:115
+#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:141
+#: src/views/certificate/Certificate.vue:120
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:115
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:115
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:308
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:308
 msgid "Add"
 msgid "Add"
 msgstr "添加"
 msgstr "添加"
 
 
-#: src/routes/index.ts:122 src/views/certificate/Certificate.vue:115
-#: src/views/certificate/CertificateEditor.vue:76
+#: src/routes/index.ts:123 src/views/certificate/CertificateEditor.vue:84
 msgid "Add Certificate"
 msgid "Add Certificate"
 msgstr "添加证书"
 msgstr "添加证书"
 
 
@@ -49,7 +49,7 @@ msgstr "在下面添加指令"
 msgid "Add Location"
 msgid "Add Location"
 msgstr "添加 Location"
 msgstr "添加 Location"
 
 
-#: src/routes/index.ts:70 src/views/domain/DomainAdd.vue:91
+#: src/routes/index.ts:71 src/views/domain/DomainAdd.vue:91
 msgid "Add Site"
 msgid "Add Site"
 msgstr "添加站点"
 msgstr "添加站点"
 
 
@@ -77,12 +77,17 @@ msgstr "API Token"
 msgid "Arch"
 msgid "Arch"
 msgstr "架构"
 msgstr "架构"
 
 
+#: src/components/Notification/Notification.vue:86
+#: src/views/notification/Notification.vue:73
+msgid "Are you sure you want to clear all notifications?"
+msgstr "您确定要清除所有通知吗?"
+
 #: src/components/ChatGPT/ChatGPT.vue:279
 #: src/components/ChatGPT/ChatGPT.vue:279
 msgid "Are you sure you want to clear the record of chat?"
 msgid "Are you sure you want to clear the record of chat?"
 msgstr "你确定你要清除聊天记录吗?"
 msgstr "你确定你要清除聊天记录吗?"
 
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:347
-#: src/views/domain/DomainList.vue:145
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:353
+#: src/views/domain/DomainList.vue:144
 msgid "Are you sure you want to delete?"
 msgid "Are you sure you want to delete?"
 msgstr "您确定要删除吗?"
 msgstr "您确定要删除吗?"
 
 
@@ -123,7 +128,7 @@ msgstr "成功关闭 %{name} 自动续签"
 msgid "Auto-renewal enabled for %{name}"
 msgid "Auto-renewal enabled for %{name}"
 msgstr "成功启用 %{name} 自动续签"
 msgstr "成功启用 %{name} 自动续签"
 
 
-#: src/views/certificate/CertificateEditor.vue:168
+#: src/views/certificate/CertificateEditor.vue:205
 #: src/views/config/Config.vue:75 src/views/config/ConfigEdit.vue:89
 #: src/views/config/Config.vue:75 src/views/config/ConfigEdit.vue:89
 #: src/views/domain/DomainEdit.vue:261 src/views/nginx_log/NginxLog.vue:170
 #: src/views/domain/DomainEdit.vue:261 src/views/nginx_log/NginxLog.vue:170
 msgid "Back"
 msgid "Back"
@@ -162,7 +167,7 @@ msgstr "CADir"
 
 
 #: src/components/ChatGPT/ChatGPT.vue:260
 #: src/components/ChatGPT/ChatGPT.vue:260
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:58
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:58
-#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:139
+#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:143
 #: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:103
 #: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:103
 #: src/views/domain/cert/components/ObtainCert.vue:150
 #: src/views/domain/cert/components/ObtainCert.vue:150
 #: src/views/domain/components/Deploy.vue:24
 #: src/views/domain/components/Deploy.vue:24
@@ -180,16 +185,16 @@ msgstr "此证书已过期"
 msgid "Certificate is valid"
 msgid "Certificate is valid"
 msgstr "此证书有效"
 msgstr "此证书有效"
 
 
-#: src/views/certificate/CertificateEditor.vue:119
+#: src/views/certificate/CertificateEditor.vue:127
 #: src/views/domain/cert/Cert.vue:35
 #: src/views/domain/cert/Cert.vue:35
 msgid "Certificate Status"
 msgid "Certificate Status"
 msgstr "证书状态"
 msgstr "证书状态"
 
 
-#: src/routes/index.ts:100 src/views/certificate/Certificate.vue:109
+#: src/routes/index.ts:101 src/views/certificate/Certificate.vue:117
 msgid "Certificates"
 msgid "Certificates"
 msgstr "证书"
 msgstr "证书"
 
 
-#: src/routes/index.ts:109
+#: src/routes/index.ts:110
 msgid "Certificates List"
 msgid "Certificates List"
 msgstr "证书列表"
 msgstr "证书列表"
 
 
@@ -223,9 +228,16 @@ msgid "Cleaning environment variables"
 msgstr "正在清理环境变量"
 msgstr "正在清理环境变量"
 
 
 #: src/components/ChatGPT/ChatGPT.vue:283
 #: src/components/ChatGPT/ChatGPT.vue:283
+#: src/components/Notification/Notification.vue:91
+#: src/views/notification/Notification.vue:78
 msgid "Clear"
 msgid "Clear"
 msgstr "清空"
 msgstr "清空"
 
 
+#: src/components/Notification/Notification.vue:42
+#: src/views/notification/Notification.vue:47
+msgid "Cleared successfully"
+msgstr "清除成功"
+
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:97
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:97
 #: src/views/domain/ngx_conf/LocationEditor.vue:121
 #: src/views/domain/ngx_conf/LocationEditor.vue:121
 #: src/views/domain/ngx_conf/LocationEditor.vue:90
 #: src/views/domain/ngx_conf/LocationEditor.vue:90
@@ -279,7 +291,7 @@ msgstr "CPU:"
 msgid "Create Another"
 msgid "Create Another"
 msgstr "再创建一个"
 msgstr "再创建一个"
 
 
-#: src/views/user/User.vue:34
+#: src/views/notification/Notification.vue:32 src/views/user/User.vue:34
 msgid "Created at"
 msgid "Created at"
 msgstr "创建时间"
 msgstr "创建时间"
 
 
@@ -304,7 +316,7 @@ msgstr "当前版本"
 msgid "Custom"
 msgid "Custom"
 msgstr "自定义"
 msgstr "自定义"
 
 
-#: src/routes/index.ts:51
+#: src/routes/index.ts:52
 msgid "Dashboard"
 msgid "Dashboard"
 msgstr "仪表盘"
 msgstr "仪表盘"
 
 
@@ -312,7 +324,7 @@ msgstr "仪表盘"
 msgid "Database (Optional, default: database)"
 msgid "Database (Optional, default: database)"
 msgstr "数据库 (可选,默认: database)"
 msgstr "数据库 (可选,默认: database)"
 
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:354
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:360
 #: src/views/domain/DomainList.vue:153
 #: src/views/domain/DomainList.vue:153
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:259
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:259
 msgid "Delete"
 msgid "Delete"
@@ -348,6 +360,10 @@ msgstr "部署成功"
 msgid "Description"
 msgid "Description"
 msgstr "描述"
 msgstr "描述"
 
 
+#: src/views/notification/Notification.vue:28
+msgid "Details"
+msgstr "详情"
+
 #: src/views/system/About.vue:25
 #: src/views/system/About.vue:25
 msgid "Development Mode"
 msgid "Development Mode"
 msgstr "开发模式"
 msgstr "开发模式"
@@ -373,7 +389,7 @@ msgid "Disable auto-renewal failed for %{name}"
 msgstr "关闭 %{name} 自动续签失败"
 msgstr "关闭 %{name} 自动续签失败"
 
 
 #: src/views/domain/cert/ChangeCert.vue:48 src/views/domain/DomainEdit.vue:190
 #: src/views/domain/cert/ChangeCert.vue:48 src/views/domain/DomainEdit.vue:190
-#: src/views/domain/DomainList.vue:123
+#: src/views/domain/DomainList.vue:122
 msgid "Disabled"
 msgid "Disabled"
 msgstr "禁用"
 msgstr "禁用"
 
 
@@ -386,7 +402,7 @@ msgstr "禁用成功"
 msgid "Disk IO"
 msgid "Disk IO"
 msgstr "磁盘 IO"
 msgstr "磁盘 IO"
 
 
-#: src/routes/index.ts:130 src/views/certificate/DNSCredential.vue:39
+#: src/routes/index.ts:131 src/views/certificate/DNSCredential.vue:39
 msgid "DNS Credentials"
 msgid "DNS Credentials"
 msgstr "DNS 凭证"
 msgstr "DNS 凭证"
 
 
@@ -428,7 +444,7 @@ msgstr "你想删除这个服务器吗?"
 msgid "Domain Config Created Successfully"
 msgid "Domain Config Created Successfully"
 msgstr "域名配置文件创建成功"
 msgstr "域名配置文件创建成功"
 
 
-#: src/views/certificate/CertificateEditor.vue:103
+#: src/views/certificate/CertificateEditor.vue:111
 msgid "Domains list is empty, try to reopen Auto Cert for %{config}"
 msgid "Domains list is empty, try to reopen Auto Cert for %{config}"
 msgstr "域名列表为空,请尝试为 %{config} 重新打开证书自动续期。"
 msgstr "域名列表为空,请尝试为 %{config} 重新打开证书自动续期。"
 
 
@@ -445,7 +461,7 @@ msgid "Dry run mode enabled"
 msgstr "试运行模式已启动"
 msgstr "试运行模式已启动"
 
 
 #: src/views/domain/components/SiteDuplicate.vue:128
 #: src/views/domain/components/SiteDuplicate.vue:128
-#: src/views/domain/DomainList.vue:139
+#: src/views/domain/DomainList.vue:138
 msgid "Duplicate"
 msgid "Duplicate"
 msgstr "复制"
 msgstr "复制"
 
 
@@ -469,11 +485,11 @@ msgstr "成功复制到本地"
 msgid "Edit %{n}"
 msgid "Edit %{n}"
 msgstr "编辑 %{n}"
 msgstr "编辑 %{n}"
 
 
-#: src/routes/index.ts:92 src/views/config/ConfigEdit.vue:83
+#: src/routes/index.ts:93 src/views/config/ConfigEdit.vue:83
 msgid "Edit Configuration"
 msgid "Edit Configuration"
 msgstr "编辑配置"
 msgstr "编辑配置"
 
 
-#: src/routes/index.ts:74
+#: src/routes/index.ts:75
 msgid "Edit Site"
 msgid "Edit Site"
 msgstr "编辑站点"
 msgstr "编辑站点"
 
 
@@ -512,7 +528,7 @@ msgstr "启用 TLS"
 #: src/views/domain/cert/ChangeCert.vue:44
 #: src/views/domain/cert/ChangeCert.vue:44
 #: src/views/domain/components/Deploy.vue:89
 #: src/views/domain/components/Deploy.vue:89
 #: src/views/domain/components/RightSettings.vue:75
 #: src/views/domain/components/RightSettings.vue:75
-#: src/views/domain/DomainEdit.vue:184 src/views/domain/DomainList.vue:131
+#: src/views/domain/DomainEdit.vue:184 src/views/domain/DomainList.vue:130
 msgid "Enabled"
 msgid "Enabled"
 msgstr "启用"
 msgstr "启用"
 
 
@@ -526,7 +542,7 @@ msgstr "启用成功"
 msgid "Encrypt website with Let's Encrypt"
 msgid "Encrypt website with Let's Encrypt"
 msgstr "用 Let's Encrypt 对网站进行加密"
 msgstr "用 Let's Encrypt 对网站进行加密"
 
 
-#: src/routes/index.ts:168 src/views/environment/Environment.vue:113
+#: src/routes/index.ts:169 src/views/environment/Environment.vue:113
 msgid "Environment"
 msgid "Environment"
 msgstr "环境"
 msgstr "环境"
 
 
@@ -534,11 +550,11 @@ msgstr "环境"
 msgid "Environments"
 msgid "Environments"
 msgstr "环境"
 msgstr "环境"
 
 
-#: src/views/config/InspectConfig.vue:47
+#: src/constants/index.ts:17 src/views/config/InspectConfig.vue:47
 msgid "Error"
 msgid "Error"
 msgstr "错误"
 msgstr "错误"
 
 
-#: src/routes/index.ts:155 src/views/domain/ngx_conf/LogEntry.vue:86
+#: src/routes/index.ts:156 src/views/domain/ngx_conf/LogEntry.vue:86
 msgid "Error Logs"
 msgid "Error Logs"
 msgstr "错误日志"
 msgstr "错误日志"
 
 
@@ -546,7 +562,7 @@ msgstr "错误日志"
 msgid "Executable Path"
 msgid "Executable Path"
 msgstr "可执行文件路径"
 msgstr "可执行文件路径"
 
 
-#: src/views/certificate/Certificate.vue:84
+#: src/views/certificate/Certificate.vue:92
 msgid "Expired"
 msgid "Expired"
 msgstr "已过期"
 msgstr "已过期"
 
 
@@ -609,6 +625,10 @@ msgstr "保存错误 %{msg}"
 msgid "Format successfully"
 msgid "Format successfully"
 msgstr "格式化成功"
 msgstr "格式化成功"
 
 
+#: src/views/certificate/Certificate.vue:45
+msgid "General Certificate"
+msgstr "普通证书"
+
 #: src/components/StdDesign/StdDataEntry/components/StdPassword.vue:60
 #: src/components/StdDesign/StdDataEntry/components/StdPassword.vue:60
 msgid "Generate"
 msgid "Generate"
 msgstr "生成"
 msgstr "生成"
@@ -645,7 +665,7 @@ msgstr "GPT-4-32K"
 msgid "GPT-4-Turbo"
 msgid "GPT-4-Turbo"
 msgstr "GPT-4-Turbo"
 msgstr "GPT-4-Turbo"
 
 
-#: src/routes/index.ts:44
+#: src/routes/index.ts:45
 msgid "Home"
 msgid "Home"
 msgstr "首页"
 msgstr "首页"
 
 
@@ -665,6 +685,10 @@ msgstr "HTTP 监听端口"
 msgid "HTTP01"
 msgid "HTTP01"
 msgstr "HTTP01"
 msgstr "HTTP01"
 
 
+#: src/constants/index.ts:19
+msgid "Info"
+msgstr "信息"
+
 #: src/language/constants.ts:27
 #: src/language/constants.ts:27
 msgid "Initial core upgrader error"
 msgid "Initial core upgrader error"
 msgstr "初始化核心升级程序错误"
 msgstr "初始化核心升级程序错误"
@@ -673,7 +697,7 @@ msgstr "初始化核心升级程序错误"
 msgid "Initialing core upgrader"
 msgid "Initialing core upgrader"
 msgstr "初始化核心升级器"
 msgstr "初始化核心升级器"
 
 
-#: src/routes/index.ts:211 src/views/other/Install.vue:139
+#: src/routes/index.ts:220 src/views/other/Install.vue:139
 msgid "Install"
 msgid "Install"
 msgstr "安装"
 msgstr "安装"
 
 
@@ -730,11 +754,11 @@ msgstr "Location"
 msgid "Locations"
 msgid "Locations"
 msgstr "Locations"
 msgstr "Locations"
 
 
-#: src/views/certificate/CertificateEditor.vue:156
+#: src/views/certificate/CertificateEditor.vue:193
 msgid "Log"
 msgid "Log"
 msgstr "日志"
 msgstr "日志"
 
 
-#: src/routes/index.ts:217 src/views/other/Login.vue:147
+#: src/routes/index.ts:226 src/views/other/Login.vue:147
 msgid "Login"
 msgid "Login"
 msgstr "登录"
 msgstr "登录"
 
 
@@ -742,7 +766,7 @@ msgstr "登录"
 msgid "Login successful"
 msgid "Login successful"
 msgstr "登录成功"
 msgstr "登录成功"
 
 
-#: src/layouts/HeaderLayout.vue:21
+#: src/layouts/HeaderLayout.vue:22
 msgid "Logout successful"
 msgid "Logout successful"
 msgstr "登出成功"
 msgstr "登出成功"
 
 
@@ -754,18 +778,22 @@ msgstr ""
 "在获取签发证书前,请确保配置文件中已将 .well-known 目录反向代理到 "
 "在获取签发证书前,请确保配置文件中已将 .well-known 目录反向代理到 "
 "HTTPChallengePort。"
 "HTTPChallengePort。"
 
 
-#: src/routes/index.ts:83
+#: src/routes/index.ts:84
 msgid "Manage Configs"
 msgid "Manage Configs"
 msgstr "配置管理"
 msgstr "配置管理"
 
 
-#: src/routes/index.ts:58 src/views/domain/DomainList.vue:102
+#: src/routes/index.ts:59 src/views/domain/DomainList.vue:102
 msgid "Manage Sites"
 msgid "Manage Sites"
 msgstr "网站管理"
 msgstr "网站管理"
 
 
-#: src/routes/index.ts:176 src/views/user/User.vue:53
+#: src/routes/index.ts:185 src/views/user/User.vue:53
 msgid "Manage Users"
 msgid "Manage Users"
 msgstr "用户管理"
 msgstr "用户管理"
 
 
+#: src/views/certificate/Certificate.vue:44
+msgid "Managed Certificate"
+msgstr "托管证书"
+
 #: src/views/dashboard/ServerAnalytic.vue:220
 #: src/views/dashboard/ServerAnalytic.vue:220
 msgid "Memory"
 msgid "Memory"
 msgstr "内存"
 msgstr "内存"
@@ -775,12 +803,12 @@ msgid "Memory and Storage"
 msgstr "内存与存储"
 msgstr "内存与存储"
 
 
 #: src/components/ChatGPT/ChatGPT.vue:256
 #: src/components/ChatGPT/ChatGPT.vue:256
-#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:137
+#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:141
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:336
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:336
 msgid "Modify"
 msgid "Modify"
 msgstr "修改"
 msgstr "修改"
 
 
-#: src/routes/index.ts:114 src/views/certificate/CertificateEditor.vue:76
+#: src/routes/index.ts:115 src/views/certificate/CertificateEditor.vue:84
 msgid "Modify Certificate"
 msgid "Modify Certificate"
 msgstr "修改证书"
 msgstr "修改证书"
 
 
@@ -793,6 +821,7 @@ msgid "Multi-line Directive"
 msgstr "多行指令"
 msgstr "多行指令"
 
 
 #: src/views/certificate/Certificate.vue:22
 #: src/views/certificate/Certificate.vue:22
+#: src/views/certificate/CertificateEditor.vue:145
 #: src/views/certificate/DNSCredential.vue:13 src/views/config/config.ts:9
 #: src/views/certificate/DNSCredential.vue:13 src/views/config/config.ts:9
 #: src/views/domain/cert/ChangeCert.vue:21
 #: src/views/domain/cert/ChangeCert.vue:21
 #: src/views/domain/components/RightSettings.vue:81
 #: src/views/domain/components/RightSettings.vue:81
@@ -846,7 +875,7 @@ msgstr "控制 Nginx"
 msgid "Nginx Error Log Path"
 msgid "Nginx Error Log Path"
 msgstr "Nginx 错误日志路径"
 msgstr "Nginx 错误日志路径"
 
 
-#: src/routes/index.ts:145 src/views/nginx_log/NginxLog.vue:145
+#: src/routes/index.ts:146 src/views/nginx_log/NginxLog.vue:145
 msgid "Nginx Log"
 msgid "Nginx Log"
 msgstr "Nginx 日志"
 msgstr "Nginx 日志"
 
 
@@ -859,10 +888,12 @@ msgid "Nginx restarted successfully"
 msgstr "Nginx 重启成功"
 msgstr "Nginx 重启成功"
 
 
 #: src/components/ChatGPT/ChatGPT.vue:277
 #: src/components/ChatGPT/ChatGPT.vue:277
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:345
-#: src/views/domain/DomainList.vue:143
+#: src/components/Notification/Notification.vue:84
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:351
+#: src/views/domain/DomainList.vue:142
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:81
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:81
 #: src/views/domain/ngx_conf/LocationEditor.vue:76
 #: src/views/domain/ngx_conf/LocationEditor.vue:76
+#: src/views/notification/Notification.vue:71
 msgid "No"
 msgid "No"
 msgstr "取消"
 msgstr "取消"
 
 
@@ -870,11 +901,11 @@ msgstr "取消"
 msgid "Node Secret"
 msgid "Node Secret"
 msgstr "节点密钥"
 msgstr "节点密钥"
 
 
-#: src/views/certificate/Certificate.vue:90
+#: src/views/certificate/Certificate.vue:98
 msgid "Not After"
 msgid "Not After"
 msgstr "有效期"
 msgstr "有效期"
 
 
-#: src/routes/index.ts:223 src/routes/index.ts:225
+#: src/routes/index.ts:232 src/routes/index.ts:234
 msgid "Not Found"
 msgid "Not Found"
 msgstr "找不到页面"
 msgstr "找不到页面"
 
 
@@ -887,6 +918,14 @@ msgstr "此前无效: %{date}"
 msgid "Note"
 msgid "Note"
 msgstr "注意"
 msgstr "注意"
 
 
+#: src/views/notification/Notification.vue:63
+msgid "Notification"
+msgstr "通知"
+
+#: src/components/Notification/Notification.vue:82 src/routes/index.ts:177
+msgid "Notifications"
+msgstr "通知"
+
 #: src/views/domain/cert/components/ObtainCert.vue:191
 #: src/views/domain/cert/components/ObtainCert.vue:191
 msgid "Obtain certificate"
 msgid "Obtain certificate"
 msgstr "获取证书"
 msgstr "获取证书"
@@ -902,16 +941,18 @@ msgid "Offline"
 msgstr "离线"
 msgstr "离线"
 
 
 #: src/components/ChatGPT/ChatGPT.vue:278
 #: src/components/ChatGPT/ChatGPT.vue:278
+#: src/components/Notification/Notification.vue:85
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:59
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:59
-#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:140
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:346
+#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:144
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:352
 #: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:104
 #: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:104
 #: src/views/domain/cert/components/ObtainCert.vue:149
 #: src/views/domain/cert/components/ObtainCert.vue:149
 #: src/views/domain/components/Deploy.vue:23
 #: src/views/domain/components/Deploy.vue:23
 #: src/views/domain/components/RightSettings.vue:51
 #: src/views/domain/components/RightSettings.vue:51
-#: src/views/domain/DomainList.vue:144
+#: src/views/domain/DomainList.vue:143
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:215
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:215
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:47
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:47
+#: src/views/notification/Notification.vue:72
 msgid "OK"
 msgid "OK"
 msgstr "确定"
 msgstr "确定"
 
 
@@ -1017,7 +1058,7 @@ msgstr "请至少选择一个节点!"
 msgid "Pre-release"
 msgid "Pre-release"
 msgstr "预发布"
 msgstr "预发布"
 
 
-#: src/routes/index.ts:184 src/views/preference/Preference.vue:85
+#: src/routes/index.ts:193 src/views/preference/Preference.vue:85
 msgid "Preference"
 msgid "Preference"
 msgstr "偏好设置"
 msgstr "偏好设置"
 
 
@@ -1080,11 +1121,23 @@ msgstr "重载中"
 msgid "Reloading nginx"
 msgid "Reloading nginx"
 msgstr "正在重载 Nginx"
 msgstr "正在重载 Nginx"
 
 
+#: src/components/Notification/Notification.vue:52
+msgid "Removed successfully"
+msgstr "删除成功"
+
 #: src/views/certificate/RenewCert.vue:43
 #: src/views/certificate/RenewCert.vue:43
 #: src/views/certificate/RenewCert.vue:47
 #: src/views/certificate/RenewCert.vue:47
 msgid "Renew Certificate"
 msgid "Renew Certificate"
 msgstr "更新证书"
 msgstr "更新证书"
 
 
+#: src/language/constants.ts:38
+msgid "Renew Certificate Error"
+msgstr "证书续期错误"
+
+#: src/language/constants.ts:37
+msgid "Renew Certificate Success"
+msgstr "证书续期成功"
+
 #: src/views/certificate/RenewCert.vue:25
 #: src/views/certificate/RenewCert.vue:25
 msgid "Renew successfully"
 msgid "Renew successfully"
 msgstr "更新成功"
 msgstr "更新成功"
@@ -1114,7 +1167,7 @@ msgid "Running"
 msgstr "运行中"
 msgstr "运行中"
 
 
 #: src/components/ChatGPT/ChatGPT.vue:259
 #: src/components/ChatGPT/ChatGPT.vue:259
-#: src/views/certificate/CertificateEditor.vue:175
+#: src/views/certificate/CertificateEditor.vue:212
 #: src/views/config/ConfigEdit.vue:98 src/views/domain/DomainEdit.vue:268
 #: src/views/config/ConfigEdit.vue:98 src/views/domain/DomainEdit.vue:268
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:111
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:111
 #: src/views/preference/Preference.vue:113
 #: src/views/preference/Preference.vue:113
@@ -1131,7 +1184,7 @@ msgid "Save error %{msg}"
 msgstr "保存错误 %{msg}"
 msgstr "保存错误 %{msg}"
 
 
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:42
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:42
-#: src/views/certificate/CertificateEditor.vue:46
+#: src/views/certificate/CertificateEditor.vue:47
 #: src/views/preference/Preference.vue:58
 #: src/views/preference/Preference.vue:58
 msgid "Save successfully"
 msgid "Save successfully"
 msgstr "保存成功"
 msgstr "保存成功"
@@ -1196,27 +1249,29 @@ msgstr "使用 HTTP01 challenge provider"
 msgid "Single Directive"
 msgid "Single Directive"
 msgstr "单行指令"
 msgstr "单行指令"
 
 
-#: src/routes/index.ts:159
+#: src/routes/index.ts:160
 msgid "Site Logs"
 msgid "Site Logs"
 msgstr "站点列表"
 msgstr "站点列表"
 
 
-#: src/routes/index.ts:66
+#: src/routes/index.ts:67
 msgid "Sites List"
 msgid "Sites List"
 msgstr "站点列表"
 msgstr "站点列表"
 
 
-#: src/views/certificate/CertificateEditor.vue:135
+#: src/views/certificate/CertificateEditor.vue:172
 msgid "SSL Certificate Content"
 msgid "SSL Certificate Content"
 msgstr "SSL 证书内容"
 msgstr "SSL 证书内容"
 
 
-#: src/views/certificate/CertificateEditor.vue:142
+#: src/views/certificate/CertificateEditor.vue:179
 msgid "SSL Certificate Key Content"
 msgid "SSL Certificate Key Content"
 msgstr "SSL 证书密钥内容"
 msgstr "SSL 证书密钥内容"
 
 
-#: src/views/certificate/Certificate.vue:63
+#: src/views/certificate/Certificate.vue:71
+#: src/views/certificate/CertificateEditor.vue:163
 msgid "SSL Certificate Key Path"
 msgid "SSL Certificate Key Path"
 msgstr "SSL证书密钥路径"
 msgstr "SSL证书密钥路径"
 
 
-#: src/views/certificate/Certificate.vue:55
+#: src/views/certificate/Certificate.vue:63
+#: src/views/certificate/CertificateEditor.vue:154
 msgid "SSL Certificate Path"
 msgid "SSL Certificate Path"
 msgstr "SSL证书路径"
 msgstr "SSL证书路径"
 
 
@@ -1228,7 +1283,7 @@ msgstr "SSO 登录"
 msgid "Stable"
 msgid "Stable"
 msgstr "稳定"
 msgstr "稳定"
 
 
-#: src/views/certificate/Certificate.vue:71 src/views/domain/DomainList.vue:25
+#: src/views/certificate/Certificate.vue:79 src/views/domain/DomainList.vue:25
 #: src/views/environment/Environment.vue:78
 #: src/views/environment/Environment.vue:78
 msgid "Status"
 msgid "Status"
 msgstr "状态"
 msgstr "状态"
@@ -1245,6 +1300,10 @@ msgstr "存储"
 msgid "Subject Name: %{subject}"
 msgid "Subject Name: %{subject}"
 msgstr "主体名称: %{subject}"
 msgstr "主体名称: %{subject}"
 
 
+#: src/constants/index.ts:20
+msgid "Success"
+msgstr "成功"
+
 #: src/views/dashboard/ServerAnalytic.vue:233
 #: src/views/dashboard/ServerAnalytic.vue:233
 msgid "Swap"
 msgid "Swap"
 msgstr "Swap"
 msgstr "Swap"
@@ -1257,7 +1316,7 @@ msgstr "切换到深色主题"
 msgid "Switch to light theme"
 msgid "Switch to light theme"
 msgstr "切换到浅色"
 msgstr "切换到浅色"
 
 
-#: src/routes/index.ts:192
+#: src/routes/index.ts:201
 msgid "System"
 msgid "System"
 msgstr "系统"
 msgstr "系统"
 
 
@@ -1269,7 +1328,7 @@ msgstr "列表"
 msgid "Target"
 msgid "Target"
 msgstr "目标"
 msgstr "目标"
 
 
-#: src/routes/index.ts:137 src/views/pty/Terminal.vue:95
+#: src/routes/index.ts:138 src/views/pty/Terminal.vue:95
 msgid "Terminal"
 msgid "Terminal"
 msgstr "终端"
 msgstr "终端"
 
 
@@ -1299,11 +1358,11 @@ msgstr "当前配置中的 server_name 必须是获取证书所需的域名,
 msgid "The username or password is incorrect"
 msgid "The username or password is incorrect"
 msgstr "用户名或密码错误"
 msgstr "用户名或密码错误"
 
 
-#: src/views/certificate/CertificateEditor.vue:93
+#: src/views/certificate/CertificateEditor.vue:101
 msgid "This Auto Cert item is invalid, please remove it."
 msgid "This Auto Cert item is invalid, please remove it."
 msgstr "这个证书自动续期项目是无效的,请删除。"
 msgstr "这个证书自动续期项目是无效的,请删除。"
 
 
-#: src/views/certificate/CertificateEditor.vue:83
+#: src/views/certificate/CertificateEditor.vue:91
 msgid "This certificate is managed by Nginx UI"
 msgid "This certificate is managed by Nginx UI"
 msgstr "该证书由 Nginx UI 托管"
 msgstr "该证书由 Nginx UI 托管"
 
 
@@ -1311,6 +1370,10 @@ msgstr "该证书由 Nginx UI 托管"
 msgid "This field should not be empty"
 msgid "This field should not be empty"
 msgstr "该字段不能为空"
 msgstr "该字段不能为空"
 
 
+#: src/views/notification/Notification.vue:21
+msgid "Title"
+msgstr "标题"
+
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:42
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:42
 msgid ""
 msgid ""
 "To make sure the certification auto-renewal can work normally, we need to "
 "To make sure the certification auto-renewal can work normally, we need to "
@@ -1322,6 +1385,7 @@ msgstr ""
 "请求的 Location,并且我们需要保存这个文件并重新加载Nginx。你确定要继续吗?"
 "请求的 Location,并且我们需要保存这个文件并重新加载Nginx。你确定要继续吗?"
 
 
 #: src/views/certificate/Certificate.vue:39 src/views/config/config.ts:14
 #: src/views/certificate/Certificate.vue:39 src/views/config/config.ts:14
+#: src/views/notification/Notification.vue:15
 msgid "Type"
 msgid "Type"
 msgstr "类型"
 msgstr "类型"
 
 
@@ -1337,7 +1401,7 @@ msgstr "修改时间"
 msgid "Updated successfully"
 msgid "Updated successfully"
 msgstr "更新成功"
 msgstr "更新成功"
 
 
-#: src/routes/index.ts:203 src/views/system/Upgrade.vue:143
+#: src/routes/index.ts:212 src/views/system/Upgrade.vue:143
 #: src/views/system/Upgrade.vue:235
 #: src/views/system/Upgrade.vue:235
 msgid "Upgrade"
 msgid "Upgrade"
 msgstr "升级"
 msgstr "升级"
@@ -1370,7 +1434,7 @@ msgstr "用户名"
 msgid "Username (*)"
 msgid "Username (*)"
 msgstr "用户名 (*)"
 msgstr "用户名 (*)"
 
 
-#: src/views/certificate/Certificate.vue:80
+#: src/views/certificate/Certificate.vue:88
 msgid "Valid"
 msgid "Valid"
 msgstr "有效的"
 msgstr "有效的"
 
 
@@ -1378,7 +1442,11 @@ msgstr "有效的"
 msgid "View"
 msgid "View"
 msgstr "查看"
 msgstr "查看"
 
 
-#: src/views/config/InspectConfig.vue:36
+#: src/components/Notification/Notification.vue:143
+msgid "View all notifications"
+msgstr "查看全部通知"
+
+#: src/constants/index.ts:18 src/views/config/InspectConfig.vue:36
 #: src/views/domain/cert/components/AutoCertStepOne.vue:23
 #: src/views/domain/cert/components/AutoCertStepOne.vue:23
 #: src/views/domain/DomainAdd.vue:114
 #: src/views/domain/DomainAdd.vue:114
 msgid "Warning"
 msgid "Warning"
@@ -1450,9 +1518,6 @@ msgstr "你可以在这个页面检查Nginx UI的升级。"
 #~ "或多个TXT记录,以进行所有权验证。一旦验证完成,这些记录将被删除。请注意,"
 #~ "或多个TXT记录,以进行所有权验证。一旦验证完成,这些记录将被删除。请注意,"
 #~ "下面的时间配置都是以秒为单位。"
 #~ "下面的时间配置都是以秒为单位。"
 
 
-#~ msgid "Certification"
-#~ msgstr "证书"
-
 #~ msgid "Delete ID: %{id}"
 #~ msgid "Delete ID: %{id}"
 #~ msgstr "删除 ID: %{id}"
 #~ msgstr "删除 ID: %{id}"
 
 

+ 143 - 68
app/src/language/zh_TW/app.po

@@ -14,30 +14,30 @@ msgstr ""
 "Generated-By: easygettext\n"
 "Generated-By: easygettext\n"
 "X-Generator: Poedit 3.4.1\n"
 "X-Generator: Poedit 3.4.1\n"
 
 
-#: src/routes/index.ts:199
+#: src/routes/index.ts:208
 msgid "About"
 msgid "About"
 msgstr "關於"
 msgstr "關於"
 
 
-#: src/routes/index.ts:151 src/views/domain/ngx_conf/LogEntry.vue:78
+#: src/routes/index.ts:152 src/views/domain/ngx_conf/LogEntry.vue:78
 msgid "Access Logs"
 msgid "Access Logs"
 msgstr "存取日誌"
 msgstr "存取日誌"
 
 
-#: src/views/certificate/Certificate.vue:96
+#: src/views/certificate/Certificate.vue:104
 #: src/views/certificate/DNSCredential.vue:32 src/views/config/config.ts:36
 #: src/views/certificate/DNSCredential.vue:32 src/views/config/config.ts:36
 #: src/views/domain/DomainList.vue:50 src/views/environment/Environment.vue:105
 #: src/views/domain/DomainList.vue:50 src/views/environment/Environment.vue:105
-#: src/views/user/User.vue:46
+#: src/views/notification/Notification.vue:38 src/views/user/User.vue:46
 msgid "Action"
 msgid "Action"
 msgstr "操作"
 msgstr "操作"
 
 
-#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:114
-#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:137
+#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:115
+#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:141
+#: src/views/certificate/Certificate.vue:120
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:115
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:115
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:308
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:308
 msgid "Add"
 msgid "Add"
 msgstr "新增"
 msgstr "新增"
 
 
-#: src/routes/index.ts:122 src/views/certificate/Certificate.vue:115
-#: src/views/certificate/CertificateEditor.vue:76
+#: src/routes/index.ts:123 src/views/certificate/CertificateEditor.vue:84
 #, fuzzy
 #, fuzzy
 msgid "Add Certificate"
 msgid "Add Certificate"
 msgstr "憑證狀態"
 msgstr "憑證狀態"
@@ -51,7 +51,7 @@ msgstr "在下方新增指令"
 msgid "Add Location"
 msgid "Add Location"
 msgstr "新增 Location"
 msgstr "新增 Location"
 
 
-#: src/routes/index.ts:70 src/views/domain/DomainAdd.vue:91
+#: src/routes/index.ts:71 src/views/domain/DomainAdd.vue:91
 msgid "Add Site"
 msgid "Add Site"
 msgstr "新增網站"
 msgstr "新增網站"
 
 
@@ -79,12 +79,18 @@ msgstr "API Token"
 msgid "Arch"
 msgid "Arch"
 msgstr "架構"
 msgstr "架構"
 
 
+#: src/components/Notification/Notification.vue:86
+#: src/views/notification/Notification.vue:73
+#, fuzzy
+msgid "Are you sure you want to clear all notifications?"
+msgstr "您確定要清除聊天記錄嗎?"
+
 #: src/components/ChatGPT/ChatGPT.vue:279
 #: src/components/ChatGPT/ChatGPT.vue:279
 msgid "Are you sure you want to clear the record of chat?"
 msgid "Are you sure you want to clear the record of chat?"
 msgstr "您確定要清除聊天記錄嗎?"
 msgstr "您確定要清除聊天記錄嗎?"
 
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:347
-#: src/views/domain/DomainList.vue:145
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:353
+#: src/views/domain/DomainList.vue:144
 msgid "Are you sure you want to delete?"
 msgid "Are you sure you want to delete?"
 msgstr "您確定要刪除嗎?"
 msgstr "您確定要刪除嗎?"
 
 
@@ -125,7 +131,7 @@ msgstr "已關閉 %{name} 的自動續簽"
 msgid "Auto-renewal enabled for %{name}"
 msgid "Auto-renewal enabled for %{name}"
 msgstr "已啟用 %{name} 的自動續簽"
 msgstr "已啟用 %{name} 的自動續簽"
 
 
-#: src/views/certificate/CertificateEditor.vue:168
+#: src/views/certificate/CertificateEditor.vue:205
 #: src/views/config/Config.vue:75 src/views/config/ConfigEdit.vue:89
 #: src/views/config/Config.vue:75 src/views/config/ConfigEdit.vue:89
 #: src/views/domain/DomainEdit.vue:261 src/views/nginx_log/NginxLog.vue:170
 #: src/views/domain/DomainEdit.vue:261 src/views/nginx_log/NginxLog.vue:170
 msgid "Back"
 msgid "Back"
@@ -164,7 +170,7 @@ msgstr "CADir"
 
 
 #: src/components/ChatGPT/ChatGPT.vue:260
 #: src/components/ChatGPT/ChatGPT.vue:260
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:58
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:58
-#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:139
+#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:143
 #: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:103
 #: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:103
 #: src/views/domain/cert/components/ObtainCert.vue:150
 #: src/views/domain/cert/components/ObtainCert.vue:150
 #: src/views/domain/components/Deploy.vue:24
 #: src/views/domain/components/Deploy.vue:24
@@ -182,17 +188,17 @@ msgstr "此憑證已過期"
 msgid "Certificate is valid"
 msgid "Certificate is valid"
 msgstr "此憑證有效"
 msgstr "此憑證有效"
 
 
-#: src/views/certificate/CertificateEditor.vue:119
+#: src/views/certificate/CertificateEditor.vue:127
 #: src/views/domain/cert/Cert.vue:35
 #: src/views/domain/cert/Cert.vue:35
 msgid "Certificate Status"
 msgid "Certificate Status"
 msgstr "憑證狀態"
 msgstr "憑證狀態"
 
 
-#: src/routes/index.ts:100 src/views/certificate/Certificate.vue:109
+#: src/routes/index.ts:101 src/views/certificate/Certificate.vue:117
 #, fuzzy
 #, fuzzy
 msgid "Certificates"
 msgid "Certificates"
 msgstr "憑證狀態"
 msgstr "憑證狀態"
 
 
-#: src/routes/index.ts:109
+#: src/routes/index.ts:110
 #, fuzzy
 #, fuzzy
 msgid "Certificates List"
 msgid "Certificates List"
 msgstr "憑證清單"
 msgstr "憑證清單"
@@ -227,9 +233,17 @@ msgid "Cleaning environment variables"
 msgstr "清理環境變數"
 msgstr "清理環境變數"
 
 
 #: src/components/ChatGPT/ChatGPT.vue:283
 #: src/components/ChatGPT/ChatGPT.vue:283
+#: src/components/Notification/Notification.vue:91
+#: src/views/notification/Notification.vue:78
 msgid "Clear"
 msgid "Clear"
 msgstr "清除"
 msgstr "清除"
 
 
+#: src/components/Notification/Notification.vue:42
+#: src/views/notification/Notification.vue:47
+#, fuzzy
+msgid "Cleared successfully"
+msgstr "成功停用"
+
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:97
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:97
 #: src/views/domain/ngx_conf/LocationEditor.vue:121
 #: src/views/domain/ngx_conf/LocationEditor.vue:121
 #: src/views/domain/ngx_conf/LocationEditor.vue:90
 #: src/views/domain/ngx_conf/LocationEditor.vue:90
@@ -283,7 +297,7 @@ msgstr "中央處理器:"
 msgid "Create Another"
 msgid "Create Another"
 msgstr "再建立一個"
 msgstr "再建立一個"
 
 
-#: src/views/user/User.vue:34
+#: src/views/notification/Notification.vue:32 src/views/user/User.vue:34
 msgid "Created at"
 msgid "Created at"
 msgstr "建立時間"
 msgstr "建立時間"
 
 
@@ -308,7 +322,7 @@ msgstr "目前版本"
 msgid "Custom"
 msgid "Custom"
 msgstr "自訂"
 msgstr "自訂"
 
 
-#: src/routes/index.ts:51
+#: src/routes/index.ts:52
 msgid "Dashboard"
 msgid "Dashboard"
 msgstr "儀表板"
 msgstr "儀表板"
 
 
@@ -316,7 +330,7 @@ msgstr "儀表板"
 msgid "Database (Optional, default: database)"
 msgid "Database (Optional, default: database)"
 msgstr "資料庫 (可選,預設: database)"
 msgstr "資料庫 (可選,預設: database)"
 
 
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:354
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:360
 #: src/views/domain/DomainList.vue:153
 #: src/views/domain/DomainList.vue:153
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:259
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:259
 msgid "Delete"
 msgid "Delete"
@@ -353,6 +367,10 @@ msgstr "部署成功"
 msgid "Description"
 msgid "Description"
 msgstr "描述"
 msgstr "描述"
 
 
+#: src/views/notification/Notification.vue:28
+msgid "Details"
+msgstr ""
+
 #: src/views/system/About.vue:25
 #: src/views/system/About.vue:25
 msgid "Development Mode"
 msgid "Development Mode"
 msgstr "開發模式"
 msgstr "開發模式"
@@ -379,7 +397,7 @@ msgid "Disable auto-renewal failed for %{name}"
 msgstr "關閉 %{name} 自動續簽失敗"
 msgstr "關閉 %{name} 自動續簽失敗"
 
 
 #: src/views/domain/cert/ChangeCert.vue:48 src/views/domain/DomainEdit.vue:190
 #: src/views/domain/cert/ChangeCert.vue:48 src/views/domain/DomainEdit.vue:190
-#: src/views/domain/DomainList.vue:123
+#: src/views/domain/DomainList.vue:122
 msgid "Disabled"
 msgid "Disabled"
 msgstr "停用"
 msgstr "停用"
 
 
@@ -392,7 +410,7 @@ msgstr "成功停用"
 msgid "Disk IO"
 msgid "Disk IO"
 msgstr "磁碟 IO"
 msgstr "磁碟 IO"
 
 
-#: src/routes/index.ts:130 src/views/certificate/DNSCredential.vue:39
+#: src/routes/index.ts:131 src/views/certificate/DNSCredential.vue:39
 msgid "DNS Credentials"
 msgid "DNS Credentials"
 msgstr "DNS 認證"
 msgstr "DNS 認證"
 
 
@@ -434,7 +452,7 @@ msgstr "您要移除此伺服器嗎?"
 msgid "Domain Config Created Successfully"
 msgid "Domain Config Created Successfully"
 msgstr "網域設定檔成功建立"
 msgstr "網域設定檔成功建立"
 
 
-#: src/views/certificate/CertificateEditor.vue:103
+#: src/views/certificate/CertificateEditor.vue:111
 #, fuzzy
 #, fuzzy
 msgid "Domains list is empty, try to reopen Auto Cert for %{config}"
 msgid "Domains list is empty, try to reopen Auto Cert for %{config}"
 msgstr "網域列表為空,請嘗試重新開啟 %{config} 的自動憑證"
 msgstr "網域列表為空,請嘗試重新開啟 %{config} 的自動憑證"
@@ -452,7 +470,7 @@ msgid "Dry run mode enabled"
 msgstr "試運轉模式已啟用"
 msgstr "試運轉模式已啟用"
 
 
 #: src/views/domain/components/SiteDuplicate.vue:128
 #: src/views/domain/components/SiteDuplicate.vue:128
-#: src/views/domain/DomainList.vue:139
+#: src/views/domain/DomainList.vue:138
 msgid "Duplicate"
 msgid "Duplicate"
 msgstr "複製"
 msgstr "複製"
 
 
@@ -476,11 +494,11 @@ msgstr "成功複製至本機"
 msgid "Edit %{n}"
 msgid "Edit %{n}"
 msgstr "編輯 %{n}"
 msgstr "編輯 %{n}"
 
 
-#: src/routes/index.ts:92 src/views/config/ConfigEdit.vue:83
+#: src/routes/index.ts:93 src/views/config/ConfigEdit.vue:83
 msgid "Edit Configuration"
 msgid "Edit Configuration"
 msgstr "編輯設定"
 msgstr "編輯設定"
 
 
-#: src/routes/index.ts:74
+#: src/routes/index.ts:75
 msgid "Edit Site"
 msgid "Edit Site"
 msgstr "編輯網站"
 msgstr "編輯網站"
 
 
@@ -519,7 +537,7 @@ msgstr "啟用 TLS"
 #: src/views/domain/cert/ChangeCert.vue:44
 #: src/views/domain/cert/ChangeCert.vue:44
 #: src/views/domain/components/Deploy.vue:89
 #: src/views/domain/components/Deploy.vue:89
 #: src/views/domain/components/RightSettings.vue:75
 #: src/views/domain/components/RightSettings.vue:75
-#: src/views/domain/DomainEdit.vue:184 src/views/domain/DomainList.vue:131
+#: src/views/domain/DomainEdit.vue:184 src/views/domain/DomainList.vue:130
 msgid "Enabled"
 msgid "Enabled"
 msgstr "已啟用"
 msgstr "已啟用"
 
 
@@ -533,7 +551,7 @@ msgstr "成功啟用"
 msgid "Encrypt website with Let's Encrypt"
 msgid "Encrypt website with Let's Encrypt"
 msgstr "用 Let's Encrypt 對網站進行加密"
 msgstr "用 Let's Encrypt 對網站進行加密"
 
 
-#: src/routes/index.ts:168 src/views/environment/Environment.vue:113
+#: src/routes/index.ts:169 src/views/environment/Environment.vue:113
 msgid "Environment"
 msgid "Environment"
 msgstr "環境"
 msgstr "環境"
 
 
@@ -541,11 +559,11 @@ msgstr "環境"
 msgid "Environments"
 msgid "Environments"
 msgstr "環境"
 msgstr "環境"
 
 
-#: src/views/config/InspectConfig.vue:47
+#: src/constants/index.ts:17 src/views/config/InspectConfig.vue:47
 msgid "Error"
 msgid "Error"
 msgstr "錯誤"
 msgstr "錯誤"
 
 
-#: src/routes/index.ts:155 src/views/domain/ngx_conf/LogEntry.vue:86
+#: src/routes/index.ts:156 src/views/domain/ngx_conf/LogEntry.vue:86
 msgid "Error Logs"
 msgid "Error Logs"
 msgstr "錯誤日誌"
 msgstr "錯誤日誌"
 
 
@@ -553,7 +571,7 @@ msgstr "錯誤日誌"
 msgid "Executable Path"
 msgid "Executable Path"
 msgstr "可執行檔路徑"
 msgstr "可執行檔路徑"
 
 
-#: src/views/certificate/Certificate.vue:84
+#: src/views/certificate/Certificate.vue:92
 msgid "Expired"
 msgid "Expired"
 msgstr ""
 msgstr ""
 
 
@@ -617,6 +635,11 @@ msgstr "格式錯誤 %{msg}"
 msgid "Format successfully"
 msgid "Format successfully"
 msgstr "成功格式化"
 msgstr "成功格式化"
 
 
+#: src/views/certificate/Certificate.vue:45
+#, fuzzy
+msgid "General Certificate"
+msgstr "更換憑證"
+
 #: src/components/StdDesign/StdDataEntry/components/StdPassword.vue:60
 #: src/components/StdDesign/StdDataEntry/components/StdPassword.vue:60
 msgid "Generate"
 msgid "Generate"
 msgstr "產生"
 msgstr "產生"
@@ -653,7 +676,7 @@ msgstr "GPT-4-32K"
 msgid "GPT-4-Turbo"
 msgid "GPT-4-Turbo"
 msgstr "GPT-4-Turbo"
 msgstr "GPT-4-Turbo"
 
 
-#: src/routes/index.ts:44
+#: src/routes/index.ts:45
 msgid "Home"
 msgid "Home"
 msgstr "首頁"
 msgstr "首頁"
 
 
@@ -673,6 +696,10 @@ msgstr "HTTP 監聽埠"
 msgid "HTTP01"
 msgid "HTTP01"
 msgstr "HTTP01"
 msgstr "HTTP01"
 
 
+#: src/constants/index.ts:19
+msgid "Info"
+msgstr ""
+
 #: src/language/constants.ts:27
 #: src/language/constants.ts:27
 msgid "Initial core upgrader error"
 msgid "Initial core upgrader error"
 msgstr "初始化核心升級程式錯誤"
 msgstr "初始化核心升級程式錯誤"
@@ -681,7 +708,7 @@ msgstr "初始化核心升級程式錯誤"
 msgid "Initialing core upgrader"
 msgid "Initialing core upgrader"
 msgstr "正在初始化核心升級程式"
 msgstr "正在初始化核心升級程式"
 
 
-#: src/routes/index.ts:211 src/views/other/Install.vue:139
+#: src/routes/index.ts:220 src/views/other/Install.vue:139
 msgid "Install"
 msgid "Install"
 msgstr "安裝"
 msgstr "安裝"
 
 
@@ -740,12 +767,12 @@ msgstr "Location"
 msgid "Locations"
 msgid "Locations"
 msgstr "Locations"
 msgstr "Locations"
 
 
-#: src/views/certificate/CertificateEditor.vue:156
+#: src/views/certificate/CertificateEditor.vue:193
 #, fuzzy
 #, fuzzy
 msgid "Log"
 msgid "Log"
 msgstr "登入"
 msgstr "登入"
 
 
-#: src/routes/index.ts:217 src/views/other/Login.vue:147
+#: src/routes/index.ts:226 src/views/other/Login.vue:147
 msgid "Login"
 msgid "Login"
 msgstr "登入"
 msgstr "登入"
 
 
@@ -753,7 +780,7 @@ msgstr "登入"
 msgid "Login successful"
 msgid "Login successful"
 msgstr "登入成功"
 msgstr "登入成功"
 
 
-#: src/layouts/HeaderLayout.vue:21
+#: src/layouts/HeaderLayout.vue:22
 msgid "Logout successful"
 msgid "Logout successful"
 msgstr "登出成功"
 msgstr "登出成功"
 
 
@@ -764,18 +791,23 @@ msgid ""
 msgstr ""
 msgstr ""
 "在取得憑證前,請確保您已將 .well-known 目錄反向代理到 HTTPChallengePort。"
 "在取得憑證前,請確保您已將 .well-known 目錄反向代理到 HTTPChallengePort。"
 
 
-#: src/routes/index.ts:83
+#: src/routes/index.ts:84
 msgid "Manage Configs"
 msgid "Manage Configs"
 msgstr "管理設定"
 msgstr "管理設定"
 
 
-#: src/routes/index.ts:58 src/views/domain/DomainList.vue:102
+#: src/routes/index.ts:59 src/views/domain/DomainList.vue:102
 msgid "Manage Sites"
 msgid "Manage Sites"
 msgstr "管理網站"
 msgstr "管理網站"
 
 
-#: src/routes/index.ts:176 src/views/user/User.vue:53
+#: src/routes/index.ts:185 src/views/user/User.vue:53
 msgid "Manage Users"
 msgid "Manage Users"
 msgstr "管理使用者"
 msgstr "管理使用者"
 
 
+#: src/views/certificate/Certificate.vue:44
+#, fuzzy
+msgid "Managed Certificate"
+msgstr "更換憑證"
+
 #: src/views/dashboard/ServerAnalytic.vue:220
 #: src/views/dashboard/ServerAnalytic.vue:220
 msgid "Memory"
 msgid "Memory"
 msgstr "記憶體"
 msgstr "記憶體"
@@ -785,12 +817,12 @@ msgid "Memory and Storage"
 msgstr "記憶體與儲存"
 msgstr "記憶體與儲存"
 
 
 #: src/components/ChatGPT/ChatGPT.vue:256
 #: src/components/ChatGPT/ChatGPT.vue:256
-#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:137
+#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:141
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:336
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:336
 msgid "Modify"
 msgid "Modify"
 msgstr "修改"
 msgstr "修改"
 
 
-#: src/routes/index.ts:114 src/views/certificate/CertificateEditor.vue:76
+#: src/routes/index.ts:115 src/views/certificate/CertificateEditor.vue:84
 #, fuzzy
 #, fuzzy
 msgid "Modify Certificate"
 msgid "Modify Certificate"
 msgstr "憑證狀態"
 msgstr "憑證狀態"
@@ -804,6 +836,7 @@ msgid "Multi-line Directive"
 msgstr "多行指令"
 msgstr "多行指令"
 
 
 #: src/views/certificate/Certificate.vue:22
 #: src/views/certificate/Certificate.vue:22
+#: src/views/certificate/CertificateEditor.vue:145
 #: src/views/certificate/DNSCredential.vue:13 src/views/config/config.ts:9
 #: src/views/certificate/DNSCredential.vue:13 src/views/config/config.ts:9
 #: src/views/domain/cert/ChangeCert.vue:21
 #: src/views/domain/cert/ChangeCert.vue:21
 #: src/views/domain/components/RightSettings.vue:81
 #: src/views/domain/components/RightSettings.vue:81
@@ -857,7 +890,7 @@ msgstr "Nginx 控制元件"
 msgid "Nginx Error Log Path"
 msgid "Nginx Error Log Path"
 msgstr "Nginx 錯誤日誌路徑"
 msgstr "Nginx 錯誤日誌路徑"
 
 
-#: src/routes/index.ts:145 src/views/nginx_log/NginxLog.vue:145
+#: src/routes/index.ts:146 src/views/nginx_log/NginxLog.vue:145
 msgid "Nginx Log"
 msgid "Nginx Log"
 msgstr "Nginx 日誌"
 msgstr "Nginx 日誌"
 
 
@@ -870,10 +903,12 @@ msgid "Nginx restarted successfully"
 msgstr "Nginx 重啟成功"
 msgstr "Nginx 重啟成功"
 
 
 #: src/components/ChatGPT/ChatGPT.vue:277
 #: src/components/ChatGPT/ChatGPT.vue:277
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:345
-#: src/views/domain/DomainList.vue:143
+#: src/components/Notification/Notification.vue:84
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:351
+#: src/views/domain/DomainList.vue:142
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:81
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:81
 #: src/views/domain/ngx_conf/LocationEditor.vue:76
 #: src/views/domain/ngx_conf/LocationEditor.vue:76
+#: src/views/notification/Notification.vue:71
 msgid "No"
 msgid "No"
 msgstr "取消"
 msgstr "取消"
 
 
@@ -881,11 +916,11 @@ msgstr "取消"
 msgid "Node Secret"
 msgid "Node Secret"
 msgstr "Node Secret"
 msgstr "Node Secret"
 
 
-#: src/views/certificate/Certificate.vue:90
+#: src/views/certificate/Certificate.vue:98
 msgid "Not After"
 msgid "Not After"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:223 src/routes/index.ts:225
+#: src/routes/index.ts:232 src/routes/index.ts:234
 msgid "Not Found"
 msgid "Not Found"
 msgstr "找不到頁面"
 msgstr "找不到頁面"
 
 
@@ -898,6 +933,16 @@ msgstr "此前無效: %{date}"
 msgid "Note"
 msgid "Note"
 msgstr "備註"
 msgstr "備註"
 
 
+#: src/views/notification/Notification.vue:63
+#, fuzzy
+msgid "Notification"
+msgstr "憑證"
+
+#: src/components/Notification/Notification.vue:82 src/routes/index.ts:177
+#, fuzzy
+msgid "Notifications"
+msgstr "憑證"
+
 #: src/views/domain/cert/components/ObtainCert.vue:191
 #: src/views/domain/cert/components/ObtainCert.vue:191
 msgid "Obtain certificate"
 msgid "Obtain certificate"
 msgstr "取得憑證"
 msgstr "取得憑證"
@@ -913,16 +958,18 @@ msgid "Offline"
 msgstr "離線"
 msgstr "離線"
 
 
 #: src/components/ChatGPT/ChatGPT.vue:278
 #: src/components/ChatGPT/ChatGPT.vue:278
+#: src/components/Notification/Notification.vue:85
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:59
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:59
-#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:140
-#: src/components/StdDesign/StdDataDisplay/StdTable.vue:346
+#: src/components/StdDesign/StdDataDisplay/StdCurd.vue:144
+#: src/components/StdDesign/StdDataDisplay/StdTable.vue:352
 #: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:104
 #: src/components/StdDesign/StdDataEntry/components/StdSelector.vue:104
 #: src/views/domain/cert/components/ObtainCert.vue:149
 #: src/views/domain/cert/components/ObtainCert.vue:149
 #: src/views/domain/components/Deploy.vue:23
 #: src/views/domain/components/Deploy.vue:23
 #: src/views/domain/components/RightSettings.vue:51
 #: src/views/domain/components/RightSettings.vue:51
-#: src/views/domain/DomainList.vue:144
+#: src/views/domain/DomainList.vue:143
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:215
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:215
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:47
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:47
+#: src/views/notification/Notification.vue:72
 msgid "OK"
 msgid "OK"
 msgstr "確定"
 msgstr "確定"
 
 
@@ -1029,7 +1076,7 @@ msgstr "請至少選擇一個節點!"
 msgid "Pre-release"
 msgid "Pre-release"
 msgstr "預先發布"
 msgstr "預先發布"
 
 
-#: src/routes/index.ts:184 src/views/preference/Preference.vue:85
+#: src/routes/index.ts:193 src/views/preference/Preference.vue:85
 msgid "Preference"
 msgid "Preference"
 msgstr "偏好設定"
 msgstr "偏好設定"
 
 
@@ -1092,12 +1139,27 @@ msgstr "重新載入中"
 msgid "Reloading nginx"
 msgid "Reloading nginx"
 msgstr "正在重新載入 Nginx"
 msgstr "正在重新載入 Nginx"
 
 
+#: src/components/Notification/Notification.vue:52
+#, fuzzy
+msgid "Removed successfully"
+msgstr "儲存成功"
+
 #: src/views/certificate/RenewCert.vue:43
 #: src/views/certificate/RenewCert.vue:43
 #: src/views/certificate/RenewCert.vue:47
 #: src/views/certificate/RenewCert.vue:47
 #, fuzzy
 #, fuzzy
 msgid "Renew Certificate"
 msgid "Renew Certificate"
 msgstr "更換憑證"
 msgstr "更換憑證"
 
 
+#: src/language/constants.ts:38
+#, fuzzy
+msgid "Renew Certificate Error"
+msgstr "更換憑證"
+
+#: src/language/constants.ts:37
+#, fuzzy
+msgid "Renew Certificate Success"
+msgstr "更換憑證"
+
 #: src/views/certificate/RenewCert.vue:25
 #: src/views/certificate/RenewCert.vue:25
 #, fuzzy
 #, fuzzy
 msgid "Renew successfully"
 msgid "Renew successfully"
@@ -1128,7 +1190,7 @@ msgid "Running"
 msgstr "執行中"
 msgstr "執行中"
 
 
 #: src/components/ChatGPT/ChatGPT.vue:259
 #: src/components/ChatGPT/ChatGPT.vue:259
-#: src/views/certificate/CertificateEditor.vue:175
+#: src/views/certificate/CertificateEditor.vue:212
 #: src/views/config/ConfigEdit.vue:98 src/views/domain/DomainEdit.vue:268
 #: src/views/config/ConfigEdit.vue:98 src/views/domain/DomainEdit.vue:268
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:111
 #: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:111
 #: src/views/preference/Preference.vue:113
 #: src/views/preference/Preference.vue:113
@@ -1145,7 +1207,7 @@ msgid "Save error %{msg}"
 msgstr "儲存錯誤 %{msg}"
 msgstr "儲存錯誤 %{msg}"
 
 
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:42
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:42
-#: src/views/certificate/CertificateEditor.vue:46
+#: src/views/certificate/CertificateEditor.vue:47
 #: src/views/preference/Preference.vue:58
 #: src/views/preference/Preference.vue:58
 msgid "Save successfully"
 msgid "Save successfully"
 msgstr "儲存成功"
 msgstr "儲存成功"
@@ -1212,29 +1274,31 @@ msgstr "使用 HTTP01 挑戰提供者"
 msgid "Single Directive"
 msgid "Single Directive"
 msgstr "單一指令"
 msgstr "單一指令"
 
 
-#: src/routes/index.ts:159
+#: src/routes/index.ts:160
 msgid "Site Logs"
 msgid "Site Logs"
 msgstr "網站日誌"
 msgstr "網站日誌"
 
 
-#: src/routes/index.ts:66
+#: src/routes/index.ts:67
 msgid "Sites List"
 msgid "Sites List"
 msgstr "網站列表"
 msgstr "網站列表"
 
 
-#: src/views/certificate/CertificateEditor.vue:135
+#: src/views/certificate/CertificateEditor.vue:172
 #, fuzzy
 #, fuzzy
 msgid "SSL Certificate Content"
 msgid "SSL Certificate Content"
 msgstr "SSL 認證內容"
 msgstr "SSL 認證內容"
 
 
-#: src/views/certificate/CertificateEditor.vue:142
+#: src/views/certificate/CertificateEditor.vue:179
 #, fuzzy
 #, fuzzy
 msgid "SSL Certificate Key Content"
 msgid "SSL Certificate Key Content"
 msgstr "SSL 憑證金鑰內容"
 msgstr "SSL 憑證金鑰內容"
 
 
-#: src/views/certificate/Certificate.vue:63
+#: src/views/certificate/Certificate.vue:71
+#: src/views/certificate/CertificateEditor.vue:163
 msgid "SSL Certificate Key Path"
 msgid "SSL Certificate Key Path"
 msgstr "SSL 憑證金鑰路徑"
 msgstr "SSL 憑證金鑰路徑"
 
 
-#: src/views/certificate/Certificate.vue:55
+#: src/views/certificate/Certificate.vue:63
+#: src/views/certificate/CertificateEditor.vue:154
 msgid "SSL Certificate Path"
 msgid "SSL Certificate Path"
 msgstr "SSL 憑證路徑"
 msgstr "SSL 憑證路徑"
 
 
@@ -1247,7 +1311,7 @@ msgstr "登入"
 msgid "Stable"
 msgid "Stable"
 msgstr "穩定"
 msgstr "穩定"
 
 
-#: src/views/certificate/Certificate.vue:71 src/views/domain/DomainList.vue:25
+#: src/views/certificate/Certificate.vue:79 src/views/domain/DomainList.vue:25
 #: src/views/environment/Environment.vue:78
 #: src/views/environment/Environment.vue:78
 msgid "Status"
 msgid "Status"
 msgstr "狀態"
 msgstr "狀態"
@@ -1265,6 +1329,10 @@ msgstr "儲存空間"
 msgid "Subject Name: %{subject}"
 msgid "Subject Name: %{subject}"
 msgstr "主體名稱: %{name}"
 msgstr "主體名稱: %{name}"
 
 
+#: src/constants/index.ts:20
+msgid "Success"
+msgstr ""
+
 #: src/views/dashboard/ServerAnalytic.vue:233
 #: src/views/dashboard/ServerAnalytic.vue:233
 msgid "Swap"
 msgid "Swap"
 msgstr "交換空間"
 msgstr "交換空間"
@@ -1277,7 +1345,7 @@ msgstr ""
 msgid "Switch to light theme"
 msgid "Switch to light theme"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:192
+#: src/routes/index.ts:201
 msgid "System"
 msgid "System"
 msgstr "系統"
 msgstr "系統"
 
 
@@ -1289,7 +1357,7 @@ msgstr "表格"
 msgid "Target"
 msgid "Target"
 msgstr "目標"
 msgstr "目標"
 
 
-#: src/routes/index.ts:137 src/views/pty/Terminal.vue:95
+#: src/routes/index.ts:138 src/views/pty/Terminal.vue:95
 msgid "Terminal"
 msgid "Terminal"
 msgstr "終端機"
 msgstr "終端機"
 
 
@@ -1321,12 +1389,12 @@ msgstr "注意:目前設定中的 server_name 必須為需要申請憑證的
 msgid "The username or password is incorrect"
 msgid "The username or password is incorrect"
 msgstr "使用者名稱或密碼不正確"
 msgstr "使用者名稱或密碼不正確"
 
 
-#: src/views/certificate/CertificateEditor.vue:93
+#: src/views/certificate/CertificateEditor.vue:101
 #, fuzzy
 #, fuzzy
 msgid "This Auto Cert item is invalid, please remove it."
 msgid "This Auto Cert item is invalid, please remove it."
 msgstr "此自動憑證項目無效,請將其移除。"
 msgstr "此自動憑證項目無效,請將其移除。"
 
 
-#: src/views/certificate/CertificateEditor.vue:83
+#: src/views/certificate/CertificateEditor.vue:91
 msgid "This certificate is managed by Nginx UI"
 msgid "This certificate is managed by Nginx UI"
 msgstr ""
 msgstr ""
 
 
@@ -1334,6 +1402,10 @@ msgstr ""
 msgid "This field should not be empty"
 msgid "This field should not be empty"
 msgstr "此欄位不應為空"
 msgstr "此欄位不應為空"
 
 
+#: src/views/notification/Notification.vue:21
+msgid "Title"
+msgstr ""
+
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:42
 #: src/views/domain/ngx_conf/NgxConfigEditor.vue:42
 msgid ""
 msgid ""
 "To make sure the certification auto-renewal can work normally, we need to "
 "To make sure the certification auto-renewal can work normally, we need to "
@@ -1345,6 +1417,7 @@ msgstr ""
 "的請求,我們需要儲存這個檔案並重新載入 Nginx。你確定你要繼續嗎?"
 "的請求,我們需要儲存這個檔案並重新載入 Nginx。你確定你要繼續嗎?"
 
 
 #: src/views/certificate/Certificate.vue:39 src/views/config/config.ts:14
 #: src/views/certificate/Certificate.vue:39 src/views/config/config.ts:14
+#: src/views/notification/Notification.vue:15
 msgid "Type"
 msgid "Type"
 msgstr "類型"
 msgstr "類型"
 
 
@@ -1360,7 +1433,7 @@ msgstr "更新時間"
 msgid "Updated successfully"
 msgid "Updated successfully"
 msgstr "更新成功"
 msgstr "更新成功"
 
 
-#: src/routes/index.ts:203 src/views/system/Upgrade.vue:143
+#: src/routes/index.ts:212 src/views/system/Upgrade.vue:143
 #: src/views/system/Upgrade.vue:235
 #: src/views/system/Upgrade.vue:235
 msgid "Upgrade"
 msgid "Upgrade"
 msgstr "升級"
 msgstr "升級"
@@ -1393,7 +1466,7 @@ msgstr "使用者名稱"
 msgid "Username (*)"
 msgid "Username (*)"
 msgstr "使用者名稱 (*)"
 msgstr "使用者名稱 (*)"
 
 
-#: src/views/certificate/Certificate.vue:80
+#: src/views/certificate/Certificate.vue:88
 msgid "Valid"
 msgid "Valid"
 msgstr ""
 msgstr ""
 
 
@@ -1401,7 +1474,12 @@ msgstr ""
 msgid "View"
 msgid "View"
 msgstr "檢視"
 msgstr "檢視"
 
 
-#: src/views/config/InspectConfig.vue:36
+#: src/components/Notification/Notification.vue:143
+#, fuzzy
+msgid "View all notifications"
+msgstr "憑證"
+
+#: src/constants/index.ts:18 src/views/config/InspectConfig.vue:36
 #: src/views/domain/cert/components/AutoCertStepOne.vue:23
 #: src/views/domain/cert/components/AutoCertStepOne.vue:23
 #: src/views/domain/DomainAdd.vue:114
 #: src/views/domain/DomainAdd.vue:114
 msgid "Warning"
 msgid "Warning"
@@ -1478,9 +1556,6 @@ msgstr "您可以在此頁面檢查 Nginx UI 的升級。"
 #~ "新增到您網域的 DNS 記錄中以進行所有權驗證。驗證完成後,記錄將被刪除。請注"
 #~ "新增到您網域的 DNS 記錄中以進行所有權驗證。驗證完成後,記錄將被刪除。請注"
 #~ "意,以下時間設定均以秒為單位。"
 #~ "意,以下時間設定均以秒為單位。"
 
 
-#~ msgid "Certification"
-#~ msgstr "憑證"
-
 #~ msgid "Delete ID: %{id}"
 #~ msgid "Delete ID: %{id}"
 #~ msgstr "刪除 ID: %{id}"
 #~ msgstr "刪除 ID: %{id}"
 
 

+ 3 - 0
app/src/layouts/HeaderLayout.vue

@@ -7,6 +7,7 @@ import gettext from '@/gettext'
 import auth from '@/api/auth'
 import auth from '@/api/auth'
 import NginxControl from '@/components/NginxControl/NginxControl.vue'
 import NginxControl from '@/components/NginxControl/NginxControl.vue'
 import SwitchAppearance from '@/components/SwitchAppearance/SwitchAppearance.vue'
 import SwitchAppearance from '@/components/SwitchAppearance/SwitchAppearance.vue'
+import Notification from '@/components/Notification/Notification.vue'
 
 
 const emit = defineEmits<{
 const emit = defineEmits<{
   clickUnFold: [void]
   clickUnFold: [void]
@@ -39,6 +40,8 @@ function logout() {
 
 
       <SwitchAppearance />
       <SwitchAppearance />
 
 
+      <Notification />
+
       <a href="/">
       <a href="/">
         <HomeOutlined />
         <HomeOutlined />
       </a>
       </a>

+ 1 - 0
app/src/pinia/moudule/user.ts

@@ -3,6 +3,7 @@ import { defineStore } from 'pinia'
 export const useUserStore = defineStore('user', {
 export const useUserStore = defineStore('user', {
   state: () => ({
   state: () => ({
     token: '',
     token: '',
+    unreadCount: 0,
   }),
   }),
   getters: {
   getters: {
     is_login(state): boolean {
     is_login(state): boolean {

+ 9 - 0
app/src/routes/index.ts

@@ -2,6 +2,7 @@ import { createRouter, createWebHashHistory } from 'vue-router'
 import type { AntDesignOutlinedIconType } from '@ant-design/icons-vue/lib/icons/AntDesignOutlined'
 import type { AntDesignOutlinedIconType } from '@ant-design/icons-vue/lib/icons/AntDesignOutlined'
 
 
 import {
 import {
+  BellOutlined,
   CloudOutlined,
   CloudOutlined,
   CodeOutlined,
   CodeOutlined,
   DatabaseOutlined,
   DatabaseOutlined,
@@ -171,6 +172,14 @@ export const routes: Route[] = [
           icon: DatabaseOutlined,
           icon: DatabaseOutlined,
         },
         },
       },
       },
+      {
+        path: 'notifications',
+        name: () => $gettext('Notifications'),
+        component: () => import('@/views/notification/Notification.vue'),
+        meta: {
+          icon: BellOutlined,
+        },
+      },
       {
       {
         path: 'user',
         path: 'user',
         name: () => $gettext('Manage Users'),
         name: () => $gettext('Manage Users'),

+ 12 - 4
app/src/views/certificate/Certificate.vue

@@ -41,11 +41,19 @@ const columns: Column[] = [{
   customRender: (args: customRender) => {
   customRender: (args: customRender) => {
     const template = []
     const template = []
     const { text } = args
     const { text } = args
-    if (text === true || text > 0)
-      template.push(<Tag bordered={false} color="processing">{$gettext('Managed Certificate')}</Tag>)
+    const managed = $gettext('Managed Certificate')
+    const general = $gettext('General Certificate')
+    if (text === true || text > 0) {
+      template.push(<Tag bordered={false} color="processing">
+        { managed }
+      </Tag>)
+    }
 
 
-    else
-      template.push(<Tag bordered={false} color="purple">{$gettext('General Certificate')}</Tag>)
+    else {
+      template.push(<Tag bordered={false} color="purple">{
+      general }
+      </Tag>)
+    }
 
 
     return h('div', template)
     return h('div', template)
   },
   },

+ 40 - 12
app/src/views/certificate/CertificateEditor.vue

@@ -40,10 +40,14 @@ onMounted(() => {
   init()
   init()
 })
 })
 
 
+const router = useRouter()
 function save() {
 function save() {
   cert.save(data.value.id, data.value).then(r => {
   cert.save(data.value.id, data.value).then(r => {
     data.value = r
     data.value = r
     message.success($gettext('Save successfully'))
     message.success($gettext('Save successfully'))
+    router.push(`/certificates/${r.id}`)
+  }).catch(e => {
+    message.error($gettext(e?.message ?? 'Server error'))
   })
   })
 }
 }
 
 
@@ -70,12 +74,16 @@ const log = computed(() => {
 
 
   return logs.join('\n')
   return logs.join('\n')
 })
 })
+
+const isManaged = computed(() => {
+  return data.value.auto_cert === AutoCertState.Enable
+})
 </script>
 </script>
 
 
 <template>
 <template>
   <ACard :title="id > 0 ? $gettext('Modify Certificate') : $gettext('Add Certificate')">
   <ACard :title="id > 0 ? $gettext('Modify Certificate') : $gettext('Add Certificate')">
     <div
     <div
-      v-if="data.auto_cert === AutoCertState.Enable"
+      v-if="isManaged"
       class="mb-4"
       class="mb-4"
     >
     >
       <div class="mb-2">
       <div class="mb-2">
@@ -121,25 +129,45 @@ const log = computed(() => {
           </AFormItem>
           </AFormItem>
         </AForm>
         </AForm>
 
 
-        <RenewCert
-          v-if="data.auto_cert === AutoCertState.Enable"
-          @renewed="init"
-        />
+        <template v-if="isManaged">
+          <RenewCert @renewed="init" />
+
+          <AutoCertStepOne
+            style="max-width: 600px"
+            hide-note
+          />
+        </template>
 
 
-        <AutoCertStepOne
-          v-if="data.auto_cert === AutoCertState.Enable"
-          style="max-width: 600px"
-          hide-note
-        />
         <AForm
         <AForm
           layout="vertical"
           layout="vertical"
           style="max-width: 600px"
           style="max-width: 600px"
         >
         >
+          <AFormItem :label="$gettext('Name')">
+            <p v-if="isManaged">
+              {{ data.name }}
+            </p>
+            <AInput
+              v-else
+              v-model:value="data.name"
+            />
+          </AFormItem>
           <AFormItem :label="$gettext('SSL Certificate Path')">
           <AFormItem :label="$gettext('SSL Certificate Path')">
-            <AInput v-model:value="data.ssl_certificate_path" />
+            <p v-if="isManaged">
+              {{ data.ssl_certificate_path }}
+            </p>
+            <AInput
+              v-else
+              v-model:value="data.ssl_certificate_path"
+            />
           </AFormItem>
           </AFormItem>
           <AFormItem :label="$gettext('SSL Certificate Key Path')">
           <AFormItem :label="$gettext('SSL Certificate Key Path')">
-            <AInput v-model:value="data.ssl_certificate_key_path" />
+            <p v-if="isManaged">
+              {{ data.ssl_certificate_key_path }}
+            </p>
+            <AInput
+              v-else
+              v-model:value="data.ssl_certificate_key_path"
+            />
           </AFormItem>
           </AFormItem>
           <AFormItem :label="$gettext('SSL Certificate Content')">
           <AFormItem :label="$gettext('SSL Certificate Content')">
             <CodeEditor
             <CodeEditor

+ 0 - 1
app/src/views/domain/DomainList.vue

@@ -113,7 +113,6 @@ watch(route, () => {
       })"
       })"
     >
     >
       <template #actions="{ record }">
       <template #actions="{ record }">
-        <ADivider type="vertical" />
         <AButton
         <AButton
           v-if="record.enabled"
           v-if="record.enabled"
           type="link"
           type="link"

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

@@ -0,0 +1,87 @@
+<script setup lang="ts">
+import { useGettext } from 'vue3-gettext'
+import { message } from 'ant-design-vue'
+import StdCurd from '@/components/StdDesign/StdDataDisplay/StdCurd.vue'
+import notification from '@/api/notification'
+import type { Column } from '@/components/StdDesign/types'
+import type { customRender } from '@/components/StdDesign/StdDataDisplay/StdTableTransformer'
+import { datetime, mask } from '@/components/StdDesign/StdDataDisplay/StdTableTransformer'
+import { NotificationType } from '@/constants'
+import { useUserStore } from '@/pinia'
+
+const { $gettext } = useGettext()
+
+const columns: Column[] = [{
+  title: () => $gettext('Type'),
+  dataIndex: 'type',
+  customRender: (args: customRender) => mask(args, NotificationType),
+  sortable: true,
+  pithy: true,
+}, {
+  title: () => $gettext('Title'),
+  dataIndex: 'title',
+  customRender: (args: customRender) => {
+    return h('span', $gettext(args.text))
+  },
+  pithy: true,
+}, {
+  title: () => $gettext('Details'),
+  dataIndex: 'details',
+  pithy: true,
+}, {
+  title: () => $gettext('Created at'),
+  dataIndex: 'created_at',
+  sortable: true,
+  customRender: datetime,
+  pithy: true,
+}, {
+  title: () => $gettext('Action'),
+  dataIndex: 'action',
+}]
+
+const { unreadCount } = storeToRefs(useUserStore())
+
+const curd = ref()
+function clear() {
+  notification.clear().then(() => {
+    message.success($gettext('Cleared successfully'))
+    curd.value.get_list()
+    unreadCount.value = 0
+  }).catch(e => {
+    message.error($gettext(e?.message ?? 'Server error'))
+  })
+}
+
+watch(unreadCount, () => {
+  curd.value.get_list()
+})
+</script>
+
+<template>
+  <StdCurd
+    ref="curd"
+    :title="$gettext('Notification')"
+    :columns="columns"
+    :api="notification"
+    disabled-modify
+    disable-add
+  >
+    <template #extra>
+      <APopconfirm
+        :cancel-text="$gettext('No')"
+        :ok-text="$gettext('OK')"
+        :title="$gettext('Are you sure you want to clear all notifications?')"
+        placement="bottomRight"
+        @confirm="clear"
+      >
+        <a>
+          {{ $gettext('Clear') }}
+        </a>
+      </APopconfirm>
+    </template>
+  </StdCurd>
+</template>
+
+<style scoped lang="less">
+
+</style>

+ 1 - 1
cmd/generate/generate.go

@@ -17,7 +17,7 @@ func main() {
 	// specify the output directory (default: "./query")
 	// specify the output directory (default: "./query")
 	// ### if you want to query without context constrain, set mode gen.WithoutContext ###
 	// ### if you want to query without context constrain, set mode gen.WithoutContext ###
 	g := gen.NewGenerator(gen.Config{
 	g := gen.NewGenerator(gen.Config{
-		OutPath: "../../server/query",
+		OutPath: "../../query",
 		Mode:    gen.WithoutContext | gen.WithDefaultQuery,
 		Mode:    gen.WithoutContext | gen.WithDefaultQuery,
 		//if you want the nullable field generation property to be pointer type, set FieldNullable true
 		//if you want the nullable field generation property to be pointer type, set FieldNullable true
 		FieldNullable: true,
 		FieldNullable: true,

+ 28 - 12
internal/cert/auto_cert.go

@@ -2,8 +2,10 @@ package cert
 
 
 import (
 import (
 	"github.com/0xJacky/Nginx-UI/internal/logger"
 	"github.com/0xJacky/Nginx-UI/internal/logger"
+	"github.com/0xJacky/Nginx-UI/internal/notification"
 	"github.com/0xJacky/Nginx-UI/model"
 	"github.com/0xJacky/Nginx-UI/model"
 	"github.com/pkg/errors"
 	"github.com/pkg/errors"
+	"runtime"
 	"strings"
 	"strings"
 	"time"
 	"time"
 )
 )
@@ -11,7 +13,9 @@ import (
 func AutoObtain() {
 func AutoObtain() {
 	defer func() {
 	defer func() {
 		if err := recover(); err != nil {
 		if err := recover(); err != nil {
-			logger.Error("AutoCert Recover", err)
+			buf := make([]byte, 1024)
+			runtime.Stack(buf, false)
+			logger.Error("AutoCert Recover", err, string(buf))
 		}
 		}
 	}()
 	}()
 	logger.Info("AutoCert Worker Started")
 	logger.Info("AutoCert Worker Started")
@@ -38,21 +42,29 @@ func renew(certModel *model.Cert) {
 	if len(certModel.Domains) == 0 {
 	if len(certModel.Domains) == 0 {
 		log.Error(errors.New("domains list is empty, " +
 		log.Error(errors.New("domains list is empty, " +
 			"try to reopen auto-cert for this config:" + confName))
 			"try to reopen auto-cert for this config:" + confName))
+		notification.Error("Renew Certificate Error", confName)
 		return
 		return
 	}
 	}
 
 
-	if certModel.SSLCertificatePath != "" {
-		cert, err := GetCertInfo(certModel.SSLCertificatePath)
-		if err != nil {
-			// Get certificate info error, ignore this certificate
-			log.Error(errors.Wrap(err, "get certificate info error"))
-			return
-		}
-		if time.Now().Sub(cert.NotBefore).Hours()/24 < 7 {
-			// not between 1 week, ignore this certificate
-			return
-		}
+	if certModel.SSLCertificatePath == "" {
+		log.Error(errors.New("ssl certificate path is empty, " +
+			"try to reopen auto-cert for this config:" + confName))
+		notification.Error("Renew Certificate Error", confName)
+		return
+	}
+
+	cert, 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 time.Now().Sub(cert.NotBefore).Hours()/24 < 7 {
+		// not between 1 week, ignore this certificate
+		return
 	}
 	}
+
 	// after 1 mo, reissue certificate
 	// after 1 mo, reissue certificate
 	logChan := make(chan string, 1)
 	logChan := make(chan string, 1)
 	errChan := make(chan error, 1)
 	errChan := make(chan error, 1)
@@ -76,5 +88,9 @@ func renew(certModel *model.Cert) {
 	// block, unless errChan closed
 	// block, unless errChan closed
 	for err := range errChan {
 	for err := range errChan {
 		log.Error(err)
 		log.Error(err)
+		notification.Error("Renew Certificate Error", strings.Join(payload.ServerName, ", "))
+		return
 	}
 	}
+
+	notification.Success("Renew Certificate Success", strings.Join(payload.ServerName, ", "))
 }
 }

+ 32 - 0
internal/notification/notification.go

@@ -0,0 +1,32 @@
+package notification
+
+import (
+	"github.com/0xJacky/Nginx-UI/model"
+	"github.com/0xJacky/Nginx-UI/query"
+)
+
+func Info(title string, details string) {
+	push(model.NotificationInfo, title, details)
+}
+
+func Error(title string, details string) {
+	push(model.NotificationError, title, details)
+}
+
+func Warning(title string, details string) {
+	push(model.NotificationWarning, title, details)
+}
+
+func Success(title string, details string) {
+	push(model.NotificationSuccess, title, details)
+}
+
+func push(nType model.NotificationType, title string, details string) {
+	n := query.Notification
+
+	_ = n.Create(&model.Notification{
+		Type:    nType,
+		Title:   title,
+		Details: details,
+	})
+}

+ 3 - 6
model/model.go

@@ -34,6 +34,7 @@ func GenerateAllModel() []any {
 		Site{},
 		Site{},
 		DnsCredential{},
 		DnsCredential{},
 		Environment{},
 		Environment{},
+		Notification{},
 	}
 	}
 }
 }
 
 
@@ -99,12 +100,8 @@ func OrderAndPaginate(c *gin.Context) func(db *gorm.DB) *gorm.DB {
 	return func(db *gorm.DB) *gorm.DB {
 	return func(db *gorm.DB) *gorm.DB {
 		sort := c.DefaultQuery("order", "desc")
 		sort := c.DefaultQuery("order", "desc")
 
 
-		sortBy := DefaultQuery(c, "sort_by", "")
-
-		if sortBy != "" {
-			order := fmt.Sprintf("`%s` %s", DefaultQuery(c, "sort_by", "id"), sort)
-			db = db.Order(order)
-		}
+		order := fmt.Sprintf("`%s` %s", DefaultQuery(c, "sort_by", "id"), sort)
+		db = db.Order(order)
 
 
 		page := cast.ToInt(c.Query("page"))
 		page := cast.ToInt(c.Query("page"))
 		if page == 0 {
 		if page == 0 {

+ 17 - 0
model/notification.go

@@ -0,0 +1,17 @@
+package model
+
+type NotificationType int
+
+const (
+	NotificationError NotificationType = iota
+	NotificationWarning
+	NotificationInfo
+	NotificationSuccess
+)
+
+type Notification struct {
+	Model
+	Type    NotificationType `json:"type"`
+	Title   string           `json:"title"`
+	Details string           `json:"details"`
+}

+ 2 - 5
query/auth_tokens.gen.go

@@ -6,7 +6,6 @@ package query
 
 
 import (
 import (
 	"context"
 	"context"
-	"github.com/0xJacky/Nginx-UI/model"
 	"strings"
 	"strings"
 
 
 	"gorm.io/gorm"
 	"gorm.io/gorm"
@@ -17,6 +16,8 @@ import (
 	"gorm.io/gen/field"
 	"gorm.io/gen/field"
 
 
 	"gorm.io/plugin/dbresolver"
 	"gorm.io/plugin/dbresolver"
+
+	"github.com/0xJacky/Nginx-UI/model"
 )
 )
 
 
 func newAuthToken(db *gorm.DB, opts ...gen.DOOption) authToken {
 func newAuthToken(db *gorm.DB, opts ...gen.DOOption) authToken {
@@ -162,10 +163,6 @@ func (a authTokenDo) Where(conds ...gen.Condition) *authTokenDo {
 	return a.withDO(a.DO.Where(conds...))
 	return a.withDO(a.DO.Where(conds...))
 }
 }
 
 
-func (a authTokenDo) Exists(subquery interface{ UnderlyingDB() *gorm.DB }) *authTokenDo {
-	return a.Where(field.CompareSubQuery(field.ExistsOp, nil, subquery.UnderlyingDB()))
-}
-
 func (a authTokenDo) Order(conds ...field.Expr) *authTokenDo {
 func (a authTokenDo) Order(conds ...field.Expr) *authTokenDo {
 	return a.withDO(a.DO.Order(conds...))
 	return a.withDO(a.DO.Order(conds...))
 }
 }

+ 2 - 5
query/auths.gen.go

@@ -6,7 +6,6 @@ package query
 
 
 import (
 import (
 	"context"
 	"context"
-	"github.com/0xJacky/Nginx-UI/model"
 	"strings"
 	"strings"
 
 
 	"gorm.io/gorm"
 	"gorm.io/gorm"
@@ -17,6 +16,8 @@ import (
 	"gorm.io/gen/field"
 	"gorm.io/gen/field"
 
 
 	"gorm.io/plugin/dbresolver"
 	"gorm.io/plugin/dbresolver"
+
+	"github.com/0xJacky/Nginx-UI/model"
 )
 )
 
 
 func newAuth(db *gorm.DB, opts ...gen.DOOption) auth {
 func newAuth(db *gorm.DB, opts ...gen.DOOption) auth {
@@ -182,10 +183,6 @@ func (a authDo) Where(conds ...gen.Condition) *authDo {
 	return a.withDO(a.DO.Where(conds...))
 	return a.withDO(a.DO.Where(conds...))
 }
 }
 
 
-func (a authDo) Exists(subquery interface{ UnderlyingDB() *gorm.DB }) *authDo {
-	return a.Where(field.CompareSubQuery(field.ExistsOp, nil, subquery.UnderlyingDB()))
-}
-
 func (a authDo) Order(conds ...field.Expr) *authDo {
 func (a authDo) Order(conds ...field.Expr) *authDo {
 	return a.withDO(a.DO.Order(conds...))
 	return a.withDO(a.DO.Order(conds...))
 }
 }

+ 31 - 34
query/certs.gen.go

@@ -6,7 +6,6 @@ package query
 
 
 import (
 import (
 	"context"
 	"context"
-	model2 "github.com/0xJacky/Nginx-UI/model"
 	"strings"
 	"strings"
 
 
 	"gorm.io/gorm"
 	"gorm.io/gorm"
@@ -17,13 +16,15 @@ import (
 	"gorm.io/gen/field"
 	"gorm.io/gen/field"
 
 
 	"gorm.io/plugin/dbresolver"
 	"gorm.io/plugin/dbresolver"
+
+	"github.com/0xJacky/Nginx-UI/model"
 )
 )
 
 
 func newCert(db *gorm.DB, opts ...gen.DOOption) cert {
 func newCert(db *gorm.DB, opts ...gen.DOOption) cert {
 	_cert := cert{}
 	_cert := cert{}
 
 
 	_cert.certDo.UseDB(db, opts...)
 	_cert.certDo.UseDB(db, opts...)
-	_cert.certDo.UseModel(&model2.Cert{})
+	_cert.certDo.UseModel(&model.Cert{})
 
 
 	tableName := _cert.certDo.TableName()
 	tableName := _cert.certDo.TableName()
 	_cert.ALL = field.NewAsterisk(tableName)
 	_cert.ALL = field.NewAsterisk(tableName)
@@ -67,8 +68,8 @@ type cert struct {
 	AutoCert              field.Int
 	AutoCert              field.Int
 	ChallengeMethod       field.String
 	ChallengeMethod       field.String
 	DnsCredentialID       field.Int
 	DnsCredentialID       field.Int
-	Log           field.String
-	DnsCredential certBelongsToDnsCredential
+	Log                   field.String
+	DnsCredential         certBelongsToDnsCredential
 
 
 	fieldMap map[string]field.Expr
 	fieldMap map[string]field.Expr
 }
 }
@@ -170,17 +171,17 @@ func (a certBelongsToDnsCredential) Session(session *gorm.Session) *certBelongsT
 	return &a
 	return &a
 }
 }
 
 
-func (a certBelongsToDnsCredential) Model(m *model2.Cert) *certBelongsToDnsCredentialTx {
+func (a certBelongsToDnsCredential) Model(m *model.Cert) *certBelongsToDnsCredentialTx {
 	return &certBelongsToDnsCredentialTx{a.db.Model(m).Association(a.Name())}
 	return &certBelongsToDnsCredentialTx{a.db.Model(m).Association(a.Name())}
 }
 }
 
 
 type certBelongsToDnsCredentialTx struct{ tx *gorm.Association }
 type certBelongsToDnsCredentialTx struct{ tx *gorm.Association }
 
 
-func (a certBelongsToDnsCredentialTx) Find() (result *model2.DnsCredential, err error) {
+func (a certBelongsToDnsCredentialTx) Find() (result *model.DnsCredential, err error) {
 	return result, a.tx.Find(&result)
 	return result, a.tx.Find(&result)
 }
 }
 
 
-func (a certBelongsToDnsCredentialTx) Append(values ...*model2.DnsCredential) (err error) {
+func (a certBelongsToDnsCredentialTx) Append(values ...*model.DnsCredential) (err error) {
 	targetValues := make([]interface{}, len(values))
 	targetValues := make([]interface{}, len(values))
 	for i, v := range values {
 	for i, v := range values {
 		targetValues[i] = v
 		targetValues[i] = v
@@ -188,7 +189,7 @@ func (a certBelongsToDnsCredentialTx) Append(values ...*model2.DnsCredential) (e
 	return a.tx.Append(targetValues...)
 	return a.tx.Append(targetValues...)
 }
 }
 
 
-func (a certBelongsToDnsCredentialTx) Replace(values ...*model2.DnsCredential) (err error) {
+func (a certBelongsToDnsCredentialTx) Replace(values ...*model.DnsCredential) (err error) {
 	targetValues := make([]interface{}, len(values))
 	targetValues := make([]interface{}, len(values))
 	for i, v := range values {
 	for i, v := range values {
 		targetValues[i] = v
 		targetValues[i] = v
@@ -196,7 +197,7 @@ func (a certBelongsToDnsCredentialTx) Replace(values ...*model2.DnsCredential) (
 	return a.tx.Replace(targetValues...)
 	return a.tx.Replace(targetValues...)
 }
 }
 
 
-func (a certBelongsToDnsCredentialTx) Delete(values ...*model2.DnsCredential) (err error) {
+func (a certBelongsToDnsCredentialTx) Delete(values ...*model.DnsCredential) (err error) {
 	targetValues := make([]interface{}, len(values))
 	targetValues := make([]interface{}, len(values))
 	for i, v := range values {
 	for i, v := range values {
 		targetValues[i] = v
 		targetValues[i] = v
@@ -215,7 +216,7 @@ func (a certBelongsToDnsCredentialTx) Count() int64 {
 type certDo struct{ gen.DO }
 type certDo struct{ gen.DO }
 
 
 // FirstByID Where("id=@id")
 // FirstByID Where("id=@id")
-func (c certDo) FirstByID(id int) (result *model2.Cert, err error) {
+func (c certDo) FirstByID(id int) (result *model.Cert, err error) {
 	var params []interface{}
 	var params []interface{}
 
 
 	var generateSQL strings.Builder
 	var generateSQL strings.Builder
@@ -288,10 +289,6 @@ func (c certDo) Where(conds ...gen.Condition) *certDo {
 	return c.withDO(c.DO.Where(conds...))
 	return c.withDO(c.DO.Where(conds...))
 }
 }
 
 
-func (c certDo) Exists(subquery interface{ UnderlyingDB() *gorm.DB }) *certDo {
-	return c.Where(field.CompareSubQuery(field.ExistsOp, nil, subquery.UnderlyingDB()))
-}
-
 func (c certDo) Order(conds ...field.Expr) *certDo {
 func (c certDo) Order(conds ...field.Expr) *certDo {
 	return c.withDO(c.DO.Order(conds...))
 	return c.withDO(c.DO.Order(conds...))
 }
 }
@@ -340,57 +337,57 @@ func (c certDo) Unscoped() *certDo {
 	return c.withDO(c.DO.Unscoped())
 	return c.withDO(c.DO.Unscoped())
 }
 }
 
 
-func (c certDo) Create(values ...*model2.Cert) error {
+func (c certDo) Create(values ...*model.Cert) error {
 	if len(values) == 0 {
 	if len(values) == 0 {
 		return nil
 		return nil
 	}
 	}
 	return c.DO.Create(values)
 	return c.DO.Create(values)
 }
 }
 
 
-func (c certDo) CreateInBatches(values []*model2.Cert, batchSize int) error {
+func (c certDo) CreateInBatches(values []*model.Cert, batchSize int) error {
 	return c.DO.CreateInBatches(values, batchSize)
 	return c.DO.CreateInBatches(values, batchSize)
 }
 }
 
 
 // Save : !!! underlying implementation is different with GORM
 // Save : !!! underlying implementation is different with GORM
 // The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values)
 // The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values)
-func (c certDo) Save(values ...*model2.Cert) error {
+func (c certDo) Save(values ...*model.Cert) error {
 	if len(values) == 0 {
 	if len(values) == 0 {
 		return nil
 		return nil
 	}
 	}
 	return c.DO.Save(values)
 	return c.DO.Save(values)
 }
 }
 
 
-func (c certDo) First() (*model2.Cert, error) {
+func (c certDo) First() (*model.Cert, error) {
 	if result, err := c.DO.First(); err != nil {
 	if result, err := c.DO.First(); err != nil {
 		return nil, err
 		return nil, err
 	} else {
 	} else {
-		return result.(*model2.Cert), nil
+		return result.(*model.Cert), nil
 	}
 	}
 }
 }
 
 
-func (c certDo) Take() (*model2.Cert, error) {
+func (c certDo) Take() (*model.Cert, error) {
 	if result, err := c.DO.Take(); err != nil {
 	if result, err := c.DO.Take(); err != nil {
 		return nil, err
 		return nil, err
 	} else {
 	} else {
-		return result.(*model2.Cert), nil
+		return result.(*model.Cert), nil
 	}
 	}
 }
 }
 
 
-func (c certDo) Last() (*model2.Cert, error) {
+func (c certDo) Last() (*model.Cert, error) {
 	if result, err := c.DO.Last(); err != nil {
 	if result, err := c.DO.Last(); err != nil {
 		return nil, err
 		return nil, err
 	} else {
 	} else {
-		return result.(*model2.Cert), nil
+		return result.(*model.Cert), nil
 	}
 	}
 }
 }
 
 
-func (c certDo) Find() ([]*model2.Cert, error) {
+func (c certDo) Find() ([]*model.Cert, error) {
 	result, err := c.DO.Find()
 	result, err := c.DO.Find()
-	return result.([]*model2.Cert), err
+	return result.([]*model.Cert), err
 }
 }
 
 
-func (c certDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model2.Cert, err error) {
-	buf := make([]*model2.Cert, 0, batchSize)
+func (c certDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.Cert, err error) {
+	buf := make([]*model.Cert, 0, batchSize)
 	err = c.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error {
 	err = c.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error {
 		defer func() { results = append(results, buf...) }()
 		defer func() { results = append(results, buf...) }()
 		return fc(tx, batch)
 		return fc(tx, batch)
@@ -398,7 +395,7 @@ func (c certDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error)
 	return results, err
 	return results, err
 }
 }
 
 
-func (c certDo) FindInBatches(result *[]*model2.Cert, batchSize int, fc func(tx gen.Dao, batch int) error) error {
+func (c certDo) FindInBatches(result *[]*model.Cert, batchSize int, fc func(tx gen.Dao, batch int) error) error {
 	return c.DO.FindInBatches(result, batchSize, fc)
 	return c.DO.FindInBatches(result, batchSize, fc)
 }
 }
 
 
@@ -424,23 +421,23 @@ func (c certDo) Preload(fields ...field.RelationField) *certDo {
 	return &c
 	return &c
 }
 }
 
 
-func (c certDo) FirstOrInit() (*model2.Cert, error) {
+func (c certDo) FirstOrInit() (*model.Cert, error) {
 	if result, err := c.DO.FirstOrInit(); err != nil {
 	if result, err := c.DO.FirstOrInit(); err != nil {
 		return nil, err
 		return nil, err
 	} else {
 	} else {
-		return result.(*model2.Cert), nil
+		return result.(*model.Cert), nil
 	}
 	}
 }
 }
 
 
-func (c certDo) FirstOrCreate() (*model2.Cert, error) {
+func (c certDo) FirstOrCreate() (*model.Cert, error) {
 	if result, err := c.DO.FirstOrCreate(); err != nil {
 	if result, err := c.DO.FirstOrCreate(); err != nil {
 		return nil, err
 		return nil, err
 	} else {
 	} else {
-		return result.(*model2.Cert), nil
+		return result.(*model.Cert), nil
 	}
 	}
 }
 }
 
 
-func (c certDo) FindByPage(offset int, limit int) (result []*model2.Cert, count int64, err error) {
+func (c certDo) FindByPage(offset int, limit int) (result []*model.Cert, count int64, err error) {
 	result, err = c.Offset(offset).Limit(limit).Find()
 	result, err = c.Offset(offset).Limit(limit).Find()
 	if err != nil {
 	if err != nil {
 		return
 		return
@@ -469,7 +466,7 @@ func (c certDo) Scan(result interface{}) (err error) {
 	return c.DO.Scan(result)
 	return c.DO.Scan(result)
 }
 }
 
 
-func (c certDo) Delete(models ...*model2.Cert) (result gen.ResultInfo, err error) {
+func (c certDo) Delete(models ...*model.Cert) (result gen.ResultInfo, err error) {
 	return c.DO.Delete(models)
 	return c.DO.Delete(models)
 }
 }
 
 

+ 2 - 5
query/chat_gpt_logs.gen.go

@@ -6,7 +6,6 @@ package query
 
 
 import (
 import (
 	"context"
 	"context"
-	"github.com/0xJacky/Nginx-UI/model"
 	"strings"
 	"strings"
 
 
 	"gorm.io/gorm"
 	"gorm.io/gorm"
@@ -17,6 +16,8 @@ import (
 	"gorm.io/gen/field"
 	"gorm.io/gen/field"
 
 
 	"gorm.io/plugin/dbresolver"
 	"gorm.io/plugin/dbresolver"
+
+	"github.com/0xJacky/Nginx-UI/model"
 )
 )
 
 
 func newChatGPTLog(db *gorm.DB, opts ...gen.DOOption) chatGPTLog {
 func newChatGPTLog(db *gorm.DB, opts ...gen.DOOption) chatGPTLog {
@@ -166,10 +167,6 @@ func (c chatGPTLogDo) Where(conds ...gen.Condition) *chatGPTLogDo {
 	return c.withDO(c.DO.Where(conds...))
 	return c.withDO(c.DO.Where(conds...))
 }
 }
 
 
-func (c chatGPTLogDo) Exists(subquery interface{ UnderlyingDB() *gorm.DB }) *chatGPTLogDo {
-	return c.Where(field.CompareSubQuery(field.ExistsOp, nil, subquery.UnderlyingDB()))
-}
-
 func (c chatGPTLogDo) Order(conds ...field.Expr) *chatGPTLogDo {
 func (c chatGPTLogDo) Order(conds ...field.Expr) *chatGPTLogDo {
 	return c.withDO(c.DO.Order(conds...))
 	return c.withDO(c.DO.Order(conds...))
 }
 }

+ 2 - 5
query/config_backups.gen.go

@@ -6,7 +6,6 @@ package query
 
 
 import (
 import (
 	"context"
 	"context"
-	"github.com/0xJacky/Nginx-UI/model"
 	"strings"
 	"strings"
 
 
 	"gorm.io/gorm"
 	"gorm.io/gorm"
@@ -17,6 +16,8 @@ import (
 	"gorm.io/gen/field"
 	"gorm.io/gen/field"
 
 
 	"gorm.io/plugin/dbresolver"
 	"gorm.io/plugin/dbresolver"
+
+	"github.com/0xJacky/Nginx-UI/model"
 )
 )
 
 
 func newConfigBackup(db *gorm.DB, opts ...gen.DOOption) configBackup {
 func newConfigBackup(db *gorm.DB, opts ...gen.DOOption) configBackup {
@@ -186,10 +187,6 @@ func (c configBackupDo) Where(conds ...gen.Condition) *configBackupDo {
 	return c.withDO(c.DO.Where(conds...))
 	return c.withDO(c.DO.Where(conds...))
 }
 }
 
 
-func (c configBackupDo) Exists(subquery interface{ UnderlyingDB() *gorm.DB }) *configBackupDo {
-	return c.Where(field.CompareSubQuery(field.ExistsOp, nil, subquery.UnderlyingDB()))
-}
-
 func (c configBackupDo) Order(conds ...field.Expr) *configBackupDo {
 func (c configBackupDo) Order(conds ...field.Expr) *configBackupDo {
 	return c.withDO(c.DO.Order(conds...))
 	return c.withDO(c.DO.Order(conds...))
 }
 }

+ 2 - 5
query/dns_credentials.gen.go

@@ -6,7 +6,6 @@ package query
 
 
 import (
 import (
 	"context"
 	"context"
-	"github.com/0xJacky/Nginx-UI/model"
 	"strings"
 	"strings"
 
 
 	"gorm.io/gorm"
 	"gorm.io/gorm"
@@ -17,6 +16,8 @@ import (
 	"gorm.io/gen/field"
 	"gorm.io/gen/field"
 
 
 	"gorm.io/plugin/dbresolver"
 	"gorm.io/plugin/dbresolver"
+
+	"github.com/0xJacky/Nginx-UI/model"
 )
 )
 
 
 func newDnsCredential(db *gorm.DB, opts ...gen.DOOption) dnsCredential {
 func newDnsCredential(db *gorm.DB, opts ...gen.DOOption) dnsCredential {
@@ -186,10 +187,6 @@ func (d dnsCredentialDo) Where(conds ...gen.Condition) *dnsCredentialDo {
 	return d.withDO(d.DO.Where(conds...))
 	return d.withDO(d.DO.Where(conds...))
 }
 }
 
 
-func (d dnsCredentialDo) Exists(subquery interface{ UnderlyingDB() *gorm.DB }) *dnsCredentialDo {
-	return d.Where(field.CompareSubQuery(field.ExistsOp, nil, subquery.UnderlyingDB()))
-}
-
 func (d dnsCredentialDo) Order(conds ...field.Expr) *dnsCredentialDo {
 func (d dnsCredentialDo) Order(conds ...field.Expr) *dnsCredentialDo {
 	return d.withDO(d.DO.Order(conds...))
 	return d.withDO(d.DO.Order(conds...))
 }
 }

+ 2 - 5
query/environments.gen.go

@@ -6,7 +6,6 @@ package query
 
 
 import (
 import (
 	"context"
 	"context"
-	"github.com/0xJacky/Nginx-UI/model"
 	"strings"
 	"strings"
 
 
 	"gorm.io/gorm"
 	"gorm.io/gorm"
@@ -17,6 +16,8 @@ import (
 	"gorm.io/gen/field"
 	"gorm.io/gen/field"
 
 
 	"gorm.io/plugin/dbresolver"
 	"gorm.io/plugin/dbresolver"
+
+	"github.com/0xJacky/Nginx-UI/model"
 )
 )
 
 
 func newEnvironment(db *gorm.DB, opts ...gen.DOOption) environment {
 func newEnvironment(db *gorm.DB, opts ...gen.DOOption) environment {
@@ -194,10 +195,6 @@ func (e environmentDo) Where(conds ...gen.Condition) *environmentDo {
 	return e.withDO(e.DO.Where(conds...))
 	return e.withDO(e.DO.Where(conds...))
 }
 }
 
 
-func (e environmentDo) Exists(subquery interface{ UnderlyingDB() *gorm.DB }) *environmentDo {
-	return e.Where(field.CompareSubQuery(field.ExistsOp, nil, subquery.UnderlyingDB()))
-}
-
 func (e environmentDo) Order(conds ...field.Expr) *environmentDo {
 func (e environmentDo) Order(conds ...field.Expr) *environmentDo {
 	return e.withDO(e.DO.Order(conds...))
 	return e.withDO(e.DO.Order(conds...))
 }
 }

+ 8 - 0
query/gen.go

@@ -24,6 +24,7 @@ var (
 	ConfigBackup  *configBackup
 	ConfigBackup  *configBackup
 	DnsCredential *dnsCredential
 	DnsCredential *dnsCredential
 	Environment   *environment
 	Environment   *environment
+	Notification  *notification
 	Site          *site
 	Site          *site
 )
 )
 
 
@@ -36,6 +37,7 @@ func SetDefault(db *gorm.DB, opts ...gen.DOOption) {
 	ConfigBackup = &Q.ConfigBackup
 	ConfigBackup = &Q.ConfigBackup
 	DnsCredential = &Q.DnsCredential
 	DnsCredential = &Q.DnsCredential
 	Environment = &Q.Environment
 	Environment = &Q.Environment
+	Notification = &Q.Notification
 	Site = &Q.Site
 	Site = &Q.Site
 }
 }
 
 
@@ -49,6 +51,7 @@ func Use(db *gorm.DB, opts ...gen.DOOption) *Query {
 		ConfigBackup:  newConfigBackup(db, opts...),
 		ConfigBackup:  newConfigBackup(db, opts...),
 		DnsCredential: newDnsCredential(db, opts...),
 		DnsCredential: newDnsCredential(db, opts...),
 		Environment:   newEnvironment(db, opts...),
 		Environment:   newEnvironment(db, opts...),
+		Notification:  newNotification(db, opts...),
 		Site:          newSite(db, opts...),
 		Site:          newSite(db, opts...),
 	}
 	}
 }
 }
@@ -63,6 +66,7 @@ type Query struct {
 	ConfigBackup  configBackup
 	ConfigBackup  configBackup
 	DnsCredential dnsCredential
 	DnsCredential dnsCredential
 	Environment   environment
 	Environment   environment
+	Notification  notification
 	Site          site
 	Site          site
 }
 }
 
 
@@ -78,6 +82,7 @@ func (q *Query) clone(db *gorm.DB) *Query {
 		ConfigBackup:  q.ConfigBackup.clone(db),
 		ConfigBackup:  q.ConfigBackup.clone(db),
 		DnsCredential: q.DnsCredential.clone(db),
 		DnsCredential: q.DnsCredential.clone(db),
 		Environment:   q.Environment.clone(db),
 		Environment:   q.Environment.clone(db),
+		Notification:  q.Notification.clone(db),
 		Site:          q.Site.clone(db),
 		Site:          q.Site.clone(db),
 	}
 	}
 }
 }
@@ -100,6 +105,7 @@ func (q *Query) ReplaceDB(db *gorm.DB) *Query {
 		ConfigBackup:  q.ConfigBackup.replaceDB(db),
 		ConfigBackup:  q.ConfigBackup.replaceDB(db),
 		DnsCredential: q.DnsCredential.replaceDB(db),
 		DnsCredential: q.DnsCredential.replaceDB(db),
 		Environment:   q.Environment.replaceDB(db),
 		Environment:   q.Environment.replaceDB(db),
+		Notification:  q.Notification.replaceDB(db),
 		Site:          q.Site.replaceDB(db),
 		Site:          q.Site.replaceDB(db),
 	}
 	}
 }
 }
@@ -112,6 +118,7 @@ type queryCtx struct {
 	ConfigBackup  *configBackupDo
 	ConfigBackup  *configBackupDo
 	DnsCredential *dnsCredentialDo
 	DnsCredential *dnsCredentialDo
 	Environment   *environmentDo
 	Environment   *environmentDo
+	Notification  *notificationDo
 	Site          *siteDo
 	Site          *siteDo
 }
 }
 
 
@@ -124,6 +131,7 @@ func (q *Query) WithContext(ctx context.Context) *queryCtx {
 		ConfigBackup:  q.ConfigBackup.WithContext(ctx),
 		ConfigBackup:  q.ConfigBackup.WithContext(ctx),
 		DnsCredential: q.DnsCredential.WithContext(ctx),
 		DnsCredential: q.DnsCredential.WithContext(ctx),
 		Environment:   q.Environment.WithContext(ctx),
 		Environment:   q.Environment.WithContext(ctx),
+		Notification:  q.Notification.WithContext(ctx),
 		Site:          q.Site.WithContext(ctx),
 		Site:          q.Site.WithContext(ctx),
 	}
 	}
 }
 }

+ 374 - 0
query/notifications.gen.go

@@ -0,0 +1,374 @@
+// Code generated by gorm.io/gen. DO NOT EDIT.
+// Code generated by gorm.io/gen. DO NOT EDIT.
+// Code generated by gorm.io/gen. DO NOT EDIT.
+
+package query
+
+import (
+	"context"
+	"strings"
+
+	"gorm.io/gorm"
+	"gorm.io/gorm/clause"
+	"gorm.io/gorm/schema"
+
+	"gorm.io/gen"
+	"gorm.io/gen/field"
+
+	"gorm.io/plugin/dbresolver"
+
+	"github.com/0xJacky/Nginx-UI/model"
+)
+
+func newNotification(db *gorm.DB, opts ...gen.DOOption) notification {
+	_notification := notification{}
+
+	_notification.notificationDo.UseDB(db, opts...)
+	_notification.notificationDo.UseModel(&model.Notification{})
+
+	tableName := _notification.notificationDo.TableName()
+	_notification.ALL = field.NewAsterisk(tableName)
+	_notification.ID = field.NewInt(tableName, "id")
+	_notification.CreatedAt = field.NewTime(tableName, "created_at")
+	_notification.UpdatedAt = field.NewTime(tableName, "updated_at")
+	_notification.DeletedAt = field.NewField(tableName, "deleted_at")
+	_notification.Type = field.NewInt(tableName, "type")
+	_notification.Title = field.NewString(tableName, "title")
+	_notification.Details = field.NewString(tableName, "details")
+
+	_notification.fillFieldMap()
+
+	return _notification
+}
+
+type notification struct {
+	notificationDo
+
+	ALL       field.Asterisk
+	ID        field.Int
+	CreatedAt field.Time
+	UpdatedAt field.Time
+	DeletedAt field.Field
+	Type      field.Int
+	Title     field.String
+	Details   field.String
+
+	fieldMap map[string]field.Expr
+}
+
+func (n notification) Table(newTableName string) *notification {
+	n.notificationDo.UseTable(newTableName)
+	return n.updateTableName(newTableName)
+}
+
+func (n notification) As(alias string) *notification {
+	n.notificationDo.DO = *(n.notificationDo.As(alias).(*gen.DO))
+	return n.updateTableName(alias)
+}
+
+func (n *notification) updateTableName(table string) *notification {
+	n.ALL = field.NewAsterisk(table)
+	n.ID = field.NewInt(table, "id")
+	n.CreatedAt = field.NewTime(table, "created_at")
+	n.UpdatedAt = field.NewTime(table, "updated_at")
+	n.DeletedAt = field.NewField(table, "deleted_at")
+	n.Type = field.NewInt(table, "type")
+	n.Title = field.NewString(table, "title")
+	n.Details = field.NewString(table, "details")
+
+	n.fillFieldMap()
+
+	return n
+}
+
+func (n *notification) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
+	_f, ok := n.fieldMap[fieldName]
+	if !ok || _f == nil {
+		return nil, false
+	}
+	_oe, ok := _f.(field.OrderExpr)
+	return _oe, ok
+}
+
+func (n *notification) fillFieldMap() {
+	n.fieldMap = make(map[string]field.Expr, 7)
+	n.fieldMap["id"] = n.ID
+	n.fieldMap["created_at"] = n.CreatedAt
+	n.fieldMap["updated_at"] = n.UpdatedAt
+	n.fieldMap["deleted_at"] = n.DeletedAt
+	n.fieldMap["type"] = n.Type
+	n.fieldMap["title"] = n.Title
+	n.fieldMap["details"] = n.Details
+}
+
+func (n notification) clone(db *gorm.DB) notification {
+	n.notificationDo.ReplaceConnPool(db.Statement.ConnPool)
+	return n
+}
+
+func (n notification) replaceDB(db *gorm.DB) notification {
+	n.notificationDo.ReplaceDB(db)
+	return n
+}
+
+type notificationDo struct{ gen.DO }
+
+// FirstByID Where("id=@id")
+func (n notificationDo) FirstByID(id int) (result *model.Notification, err error) {
+	var params []interface{}
+
+	var generateSQL strings.Builder
+	params = append(params, id)
+	generateSQL.WriteString("id=? ")
+
+	var executeSQL *gorm.DB
+	executeSQL = n.UnderlyingDB().Where(generateSQL.String(), params...).Take(&result) // ignore_security_alert
+	err = executeSQL.Error
+
+	return
+}
+
+// DeleteByID update @@table set deleted_at=strftime('%Y-%m-%d %H:%M:%S','now') where id=@id
+func (n notificationDo) DeleteByID(id int) (err error) {
+	var params []interface{}
+
+	var generateSQL strings.Builder
+	params = append(params, id)
+	generateSQL.WriteString("update notifications set deleted_at=strftime('%Y-%m-%d %H:%M:%S','now') where id=? ")
+
+	var executeSQL *gorm.DB
+	executeSQL = n.UnderlyingDB().Exec(generateSQL.String(), params...) // ignore_security_alert
+	err = executeSQL.Error
+
+	return
+}
+
+func (n notificationDo) Debug() *notificationDo {
+	return n.withDO(n.DO.Debug())
+}
+
+func (n notificationDo) WithContext(ctx context.Context) *notificationDo {
+	return n.withDO(n.DO.WithContext(ctx))
+}
+
+func (n notificationDo) ReadDB() *notificationDo {
+	return n.Clauses(dbresolver.Read)
+}
+
+func (n notificationDo) WriteDB() *notificationDo {
+	return n.Clauses(dbresolver.Write)
+}
+
+func (n notificationDo) Session(config *gorm.Session) *notificationDo {
+	return n.withDO(n.DO.Session(config))
+}
+
+func (n notificationDo) Clauses(conds ...clause.Expression) *notificationDo {
+	return n.withDO(n.DO.Clauses(conds...))
+}
+
+func (n notificationDo) Returning(value interface{}, columns ...string) *notificationDo {
+	return n.withDO(n.DO.Returning(value, columns...))
+}
+
+func (n notificationDo) Not(conds ...gen.Condition) *notificationDo {
+	return n.withDO(n.DO.Not(conds...))
+}
+
+func (n notificationDo) Or(conds ...gen.Condition) *notificationDo {
+	return n.withDO(n.DO.Or(conds...))
+}
+
+func (n notificationDo) Select(conds ...field.Expr) *notificationDo {
+	return n.withDO(n.DO.Select(conds...))
+}
+
+func (n notificationDo) Where(conds ...gen.Condition) *notificationDo {
+	return n.withDO(n.DO.Where(conds...))
+}
+
+func (n notificationDo) Order(conds ...field.Expr) *notificationDo {
+	return n.withDO(n.DO.Order(conds...))
+}
+
+func (n notificationDo) Distinct(cols ...field.Expr) *notificationDo {
+	return n.withDO(n.DO.Distinct(cols...))
+}
+
+func (n notificationDo) Omit(cols ...field.Expr) *notificationDo {
+	return n.withDO(n.DO.Omit(cols...))
+}
+
+func (n notificationDo) Join(table schema.Tabler, on ...field.Expr) *notificationDo {
+	return n.withDO(n.DO.Join(table, on...))
+}
+
+func (n notificationDo) LeftJoin(table schema.Tabler, on ...field.Expr) *notificationDo {
+	return n.withDO(n.DO.LeftJoin(table, on...))
+}
+
+func (n notificationDo) RightJoin(table schema.Tabler, on ...field.Expr) *notificationDo {
+	return n.withDO(n.DO.RightJoin(table, on...))
+}
+
+func (n notificationDo) Group(cols ...field.Expr) *notificationDo {
+	return n.withDO(n.DO.Group(cols...))
+}
+
+func (n notificationDo) Having(conds ...gen.Condition) *notificationDo {
+	return n.withDO(n.DO.Having(conds...))
+}
+
+func (n notificationDo) Limit(limit int) *notificationDo {
+	return n.withDO(n.DO.Limit(limit))
+}
+
+func (n notificationDo) Offset(offset int) *notificationDo {
+	return n.withDO(n.DO.Offset(offset))
+}
+
+func (n notificationDo) Scopes(funcs ...func(gen.Dao) gen.Dao) *notificationDo {
+	return n.withDO(n.DO.Scopes(funcs...))
+}
+
+func (n notificationDo) Unscoped() *notificationDo {
+	return n.withDO(n.DO.Unscoped())
+}
+
+func (n notificationDo) Create(values ...*model.Notification) error {
+	if len(values) == 0 {
+		return nil
+	}
+	return n.DO.Create(values)
+}
+
+func (n notificationDo) CreateInBatches(values []*model.Notification, batchSize int) error {
+	return n.DO.CreateInBatches(values, batchSize)
+}
+
+// Save : !!! underlying implementation is different with GORM
+// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values)
+func (n notificationDo) Save(values ...*model.Notification) error {
+	if len(values) == 0 {
+		return nil
+	}
+	return n.DO.Save(values)
+}
+
+func (n notificationDo) First() (*model.Notification, error) {
+	if result, err := n.DO.First(); err != nil {
+		return nil, err
+	} else {
+		return result.(*model.Notification), nil
+	}
+}
+
+func (n notificationDo) Take() (*model.Notification, error) {
+	if result, err := n.DO.Take(); err != nil {
+		return nil, err
+	} else {
+		return result.(*model.Notification), nil
+	}
+}
+
+func (n notificationDo) Last() (*model.Notification, error) {
+	if result, err := n.DO.Last(); err != nil {
+		return nil, err
+	} else {
+		return result.(*model.Notification), nil
+	}
+}
+
+func (n notificationDo) Find() ([]*model.Notification, error) {
+	result, err := n.DO.Find()
+	return result.([]*model.Notification), err
+}
+
+func (n notificationDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.Notification, err error) {
+	buf := make([]*model.Notification, 0, batchSize)
+	err = n.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error {
+		defer func() { results = append(results, buf...) }()
+		return fc(tx, batch)
+	})
+	return results, err
+}
+
+func (n notificationDo) FindInBatches(result *[]*model.Notification, batchSize int, fc func(tx gen.Dao, batch int) error) error {
+	return n.DO.FindInBatches(result, batchSize, fc)
+}
+
+func (n notificationDo) Attrs(attrs ...field.AssignExpr) *notificationDo {
+	return n.withDO(n.DO.Attrs(attrs...))
+}
+
+func (n notificationDo) Assign(attrs ...field.AssignExpr) *notificationDo {
+	return n.withDO(n.DO.Assign(attrs...))
+}
+
+func (n notificationDo) Joins(fields ...field.RelationField) *notificationDo {
+	for _, _f := range fields {
+		n = *n.withDO(n.DO.Joins(_f))
+	}
+	return &n
+}
+
+func (n notificationDo) Preload(fields ...field.RelationField) *notificationDo {
+	for _, _f := range fields {
+		n = *n.withDO(n.DO.Preload(_f))
+	}
+	return &n
+}
+
+func (n notificationDo) FirstOrInit() (*model.Notification, error) {
+	if result, err := n.DO.FirstOrInit(); err != nil {
+		return nil, err
+	} else {
+		return result.(*model.Notification), nil
+	}
+}
+
+func (n notificationDo) FirstOrCreate() (*model.Notification, error) {
+	if result, err := n.DO.FirstOrCreate(); err != nil {
+		return nil, err
+	} else {
+		return result.(*model.Notification), nil
+	}
+}
+
+func (n notificationDo) FindByPage(offset int, limit int) (result []*model.Notification, count int64, err error) {
+	result, err = n.Offset(offset).Limit(limit).Find()
+	if err != nil {
+		return
+	}
+
+	if size := len(result); 0 < limit && 0 < size && size < limit {
+		count = int64(size + offset)
+		return
+	}
+
+	count, err = n.Offset(-1).Limit(-1).Count()
+	return
+}
+
+func (n notificationDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) {
+	count, err = n.Count()
+	if err != nil {
+		return
+	}
+
+	err = n.Offset(offset).Limit(limit).Scan(result)
+	return
+}
+
+func (n notificationDo) Scan(result interface{}) (err error) {
+	return n.DO.Scan(result)
+}
+
+func (n notificationDo) Delete(models ...*model.Notification) (result gen.ResultInfo, err error) {
+	return n.DO.Delete(models)
+}
+
+func (n *notificationDo) withDO(do gen.Dao) *notificationDo {
+	n.DO = *do.(*gen.DO)
+	return n
+}

+ 2 - 5
query/sites.gen.go

@@ -6,7 +6,6 @@ package query
 
 
 import (
 import (
 	"context"
 	"context"
-	"github.com/0xJacky/Nginx-UI/model"
 	"strings"
 	"strings"
 
 
 	"gorm.io/gorm"
 	"gorm.io/gorm"
@@ -17,6 +16,8 @@ import (
 	"gorm.io/gen/field"
 	"gorm.io/gen/field"
 
 
 	"gorm.io/plugin/dbresolver"
 	"gorm.io/plugin/dbresolver"
+
+	"github.com/0xJacky/Nginx-UI/model"
 )
 )
 
 
 func newSite(db *gorm.DB, opts ...gen.DOOption) site {
 func newSite(db *gorm.DB, opts ...gen.DOOption) site {
@@ -182,10 +183,6 @@ func (s siteDo) Where(conds ...gen.Condition) *siteDo {
 	return s.withDO(s.DO.Where(conds...))
 	return s.withDO(s.DO.Where(conds...))
 }
 }
 
 
-func (s siteDo) Exists(subquery interface{ UnderlyingDB() *gorm.DB }) *siteDo {
-	return s.Where(field.CompareSubQuery(field.ExistsOp, nil, subquery.UnderlyingDB()))
-}
-
 func (s siteDo) Order(conds ...field.Expr) *siteDo {
 func (s siteDo) Order(conds ...field.Expr) *siteDo {
 	return s.withDO(s.DO.Order(conds...))
 	return s.withDO(s.DO.Order(conds...))
 }
 }

BIN
resources/demo/demo.db


+ 2 - 0
router/routers.go

@@ -6,6 +6,7 @@ import (
 	"github.com/0xJacky/Nginx-UI/api/cluster"
 	"github.com/0xJacky/Nginx-UI/api/cluster"
 	"github.com/0xJacky/Nginx-UI/api/config"
 	"github.com/0xJacky/Nginx-UI/api/config"
 	"github.com/0xJacky/Nginx-UI/api/nginx"
 	"github.com/0xJacky/Nginx-UI/api/nginx"
+	"github.com/0xJacky/Nginx-UI/api/notification"
 	"github.com/0xJacky/Nginx-UI/api/openai"
 	"github.com/0xJacky/Nginx-UI/api/openai"
 	"github.com/0xJacky/Nginx-UI/api/sites"
 	"github.com/0xJacky/Nginx-UI/api/sites"
 	"github.com/0xJacky/Nginx-UI/api/system"
 	"github.com/0xJacky/Nginx-UI/api/system"
@@ -54,6 +55,7 @@ func InitRouter() *gin.Engine {
 			system.InitPrivateRouter(g)
 			system.InitPrivateRouter(g)
 			openai.InitRouter(g)
 			openai.InitRouter(g)
 			cluster.InitRouter(g)
 			cluster.InitRouter(g)
+			notification.InitRouter(g)
 		}
 		}
 
 
 		// Authorization required and websocket request
 		// Authorization required and websocket request