浏览代码

Merge pull request #826 from 0xJacky/feat/self-check

Self Check
Jacky 3 月之前
父节点
当前提交
987a7397af
共有 42 个文件被更改,包括 1551 次插入296 次删除
  1. 1 0
      README-es.md
  2. 1 0
      README-vi_VN.md
  3. 1 0
      README-zh_CN.md
  4. 1 0
      README-zh_TW.md
  5. 1 0
      README.md
  6. 3 0
      api/system/router.go
  7. 41 0
      api/system/self_check.go
  8. 0 1
      app/package.json
  9. 0 226
      app/pnpm-lock.yaml
  10. 22 0
      app/src/api/self_check.ts
  11. 65 6
      app/src/language/ar/app.po
  12. 64 6
      app/src/language/en/app.po
  13. 65 6
      app/src/language/es/app.po
  14. 64 6
      app/src/language/fr_FR/app.po
  15. 65 6
      app/src/language/ko_KR/app.po
  16. 55 5
      app/src/language/messages.pot
  17. 65 6
      app/src/language/ru_RU/app.po
  18. 65 6
      app/src/language/tr_TR/app.po
  19. 64 6
      app/src/language/vi_VN/app.po
  20. 65 6
      app/src/language/zh_CN/app.po
  21. 65 6
      app/src/language/zh_TW/app.po
  22. 59 1
      app/src/lib/http/index.ts
  23. 7 0
      app/src/routes/index.ts
  24. 99 0
      app/src/views/system/SelfCheck/SelfCheck.vue
  25. 20 0
      app/src/views/system/SelfCheck/tasks.ts
  26. 1 0
      docs/guide/about.md
  27. 1 0
      docs/zh_CN/guide/about.md
  28. 1 0
      docs/zh_TW/guide/about.md
  29. 20 3
      internal/nginx/config_args.go
  30. 67 0
      internal/self_check/directory.go
  31. 19 0
      internal/self_check/errors.go
  32. 168 0
      internal/self_check/nginx_conf.go
  33. 123 0
      internal/self_check/nginx_conf_test.go
  34. 74 0
      internal/self_check/self_check.go
  35. 1 0
      internal/self_check/test_cases/5001.conf
  36. 14 0
      internal/self_check/test_cases/no-http-block-fixed.conf
  37. 10 0
      internal/self_check/test_cases/no-http-block.conf
  38. 23 0
      internal/self_check/test_cases/no-http-sites-enabled-fixed.conf
  39. 34 0
      internal/self_check/test_cases/no-http-sites-enabled.conf
  40. 35 0
      internal/self_check/test_cases/ok.conf
  41. 1 0
      internal/self_check/websocket.go
  42. 1 0
      settings/nginx.go

+ 1 - 0
README-es.md

@@ -109,6 +109,7 @@ Aceptamos traducciones a cualquier idioma.
 - [vue3-gettext](https://github.com/jshmrtn/vue3-gettext)
 - [vue3-gettext](https://github.com/jshmrtn/vue3-gettext)
 - [vue3-ace-editor](https://github.com/CarterLi/vue3-ace-editor)
 - [vue3-ace-editor](https://github.com/CarterLi/vue3-ace-editor)
 - [Gonginx](https://github.com/tufanbarisyildirim/gonginx)
 - [Gonginx](https://github.com/tufanbarisyildirim/gonginx)
+- [lego](https://github.com/go-acme/lego)
 
 
 ## Cómo empezar
 ## Cómo empezar
 
 

+ 1 - 0
README-vi_VN.md

@@ -121,6 +121,7 @@ Chúng tôi hoan nghênh bản dịch sang bất kỳ ngôn ngữ nào.
 - [vue3-gettext](https://github.com/jshmrtn/vue3-gettext)
 - [vue3-gettext](https://github.com/jshmrtn/vue3-gettext)
 - [vue3-ace-editor](https://github.com/CarterLi/vue3-ace-editor)
 - [vue3-ace-editor](https://github.com/CarterLi/vue3-ace-editor)
 - [Gonginx](https://github.com/tufanbarisyildirim/gonginx)
 - [Gonginx](https://github.com/tufanbarisyildirim/gonginx)
+- [lego](https://github.com/go-acme/lego)
 
 
 ## Bắt đầu
 ## Bắt đầu
 
 

+ 1 - 0
README-zh_CN.md

@@ -102,6 +102,7 @@ Nginx 网络管理界面,由  [0xJacky](https://jackyu.cn/) 与 [Hintay](https
 - [vue3-gettext](https://github.com/jshmrtn/vue3-gettext)
 - [vue3-gettext](https://github.com/jshmrtn/vue3-gettext)
 - [vue3-ace-editor](https://github.com/CarterLi/vue3-ace-editor)
 - [vue3-ace-editor](https://github.com/CarterLi/vue3-ace-editor)
 - [Gonginx](https://github.com/tufanbarisyildirim/gonginx)
 - [Gonginx](https://github.com/tufanbarisyildirim/gonginx)
+- [lego](https://github.com/go-acme/lego)
 
 
 ## 入门指南
 ## 入门指南
 
 

+ 1 - 0
README-zh_TW.md

@@ -105,6 +105,7 @@ Nginx 網路管理介面,由 [0xJacky](https://jackyu.cn/) 與 [Hintay](https:
 - [vue3-gettext](https://github.com/jshmrtn/vue3-gettext)
 - [vue3-gettext](https://github.com/jshmrtn/vue3-gettext)
 - [vue3-ace-editor](https://github.com/CarterLi/vue3-ace-editor)
 - [vue3-ace-editor](https://github.com/CarterLi/vue3-ace-editor)
 - [Gonginx](https://github.com/tufanbarisyildirim/gonginx)
 - [Gonginx](https://github.com/tufanbarisyildirim/gonginx)
+- [lego](https://github.com/go-acme/lego)
 
 
 ## 入門指南
 ## 入門指南
 
 

+ 1 - 0
README.md

@@ -120,6 +120,7 @@ We welcome translations into any language.
 - [vue3-gettext](https://github.com/jshmrtn/vue3-gettext)
 - [vue3-gettext](https://github.com/jshmrtn/vue3-gettext)
 - [vue3-ace-editor](https://github.com/CarterLi/vue3-ace-editor)
 - [vue3-ace-editor](https://github.com/CarterLi/vue3-ace-editor)
 - [Gonginx](https://github.com/tufanbarisyildirim/gonginx)
 - [Gonginx](https://github.com/tufanbarisyildirim/gonginx)
+- [lego](https://github.com/go-acme/lego)
 
 
 ## Getting Started
 ## Getting Started
 
 

+ 3 - 0
api/system/router.go

@@ -13,6 +13,9 @@ func InitPublicRouter(r *gin.RouterGroup) {
 func InitPrivateRouter(r *gin.RouterGroup) {
 func InitPrivateRouter(r *gin.RouterGroup) {
 	r.GET("upgrade/release", GetRelease)
 	r.GET("upgrade/release", GetRelease)
 	r.GET("upgrade/current", GetCurrentVersion)
 	r.GET("upgrade/current", GetCurrentVersion)
+	r.GET("self_check", SelfCheck)
+	r.POST("self_check/:name/fix", SelfCheckFix)
+	r.GET("self_check/websocket", CheckWebSocket)
 }
 }
 
 
 func InitWebSocketRouter(r *gin.RouterGroup) {
 func InitWebSocketRouter(r *gin.RouterGroup) {

+ 41 - 0
api/system/self_check.go

@@ -0,0 +1,41 @@
+package system
+
+import (
+	"github.com/gorilla/websocket"
+	"github.com/uozi-tech/cosy/logger"
+	"net/http"
+
+	"github.com/0xJacky/Nginx-UI/internal/self_check"
+	"github.com/gin-gonic/gin"
+)
+
+func SelfCheck(c *gin.Context) {
+	report := self_check.Run()
+	c.JSON(http.StatusOK, report)
+}
+
+func SelfCheckFix(c *gin.Context) {
+	result := self_check.AttemptFix(c.Param("name"))
+	c.JSON(http.StatusOK, result)
+}
+
+func CheckWebSocket(c *gin.Context) {
+	var upgrader = websocket.Upgrader{
+		CheckOrigin: func(r *http.Request) bool {
+			return true
+		},
+	}
+	ws, err := upgrader.Upgrade(c.Writer, c.Request, nil)
+	if err != nil {
+		logger.Error(err)
+		return
+	}
+	defer ws.Close()
+	err = ws.WriteJSON(gin.H{
+		"message": "ok",
+	})
+	if err != nil {
+		logger.Error(err)
+		return
+	}
+}

+ 0 - 1
app/package.json

@@ -72,7 +72,6 @@
     "eslint-plugin-sonarjs": "^3.0.1",
     "eslint-plugin-sonarjs": "^3.0.1",
     "less": "^4.2.1",
     "less": "^4.2.1",
     "postcss": "^8.5.1",
     "postcss": "^8.5.1",
-    "tailwindcss": "^3.4.17",
     "typescript": "5.7.3",
     "typescript": "5.7.3",
     "unplugin-auto-import": "^19.0.0",
     "unplugin-auto-import": "^19.0.0",
     "unplugin-vue-components": "^28.0.0",
     "unplugin-vue-components": "^28.0.0",

+ 0 - 226
app/pnpm-lock.yaml

@@ -180,9 +180,6 @@ importers:
       postcss:
       postcss:
         specifier: ^8.5.1
         specifier: ^8.5.1
         version: 8.5.1
         version: 8.5.1
-      tailwindcss:
-        specifier: ^3.4.17
-        version: 3.4.17
       typescript:
       typescript:
         specifier: 5.7.3
         specifier: 5.7.3
         version: 5.7.3
         version: 5.7.3
@@ -210,10 +207,6 @@ packages:
   '@0xjacky/vue-github-button@3.1.1':
   '@0xjacky/vue-github-button@3.1.1':
     resolution: {integrity: sha512-fD9IWHGe0V6+7SR/5rbL/mrq+B6u3tDktZrAFYfKNgS9JUM/OBQAvKG+i8e39iq5invDhzHyLHAkzff9rT0j5g==}
     resolution: {integrity: sha512-fD9IWHGe0V6+7SR/5rbL/mrq+B6u3tDktZrAFYfKNgS9JUM/OBQAvKG+i8e39iq5invDhzHyLHAkzff9rT0j5g==}
 
 
-  '@alloc/quick-lru@5.2.0':
-    resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==}
-    engines: {node: '>=10'}
-
   '@ampproject/remapping@2.3.0':
   '@ampproject/remapping@2.3.0':
     resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==}
     resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==}
     engines: {node: '>=6.0.0'}
     engines: {node: '>=6.0.0'}
@@ -1892,9 +1885,6 @@ packages:
     peerDependencies:
     peerDependencies:
       vue: '>=3.2.0'
       vue: '>=3.2.0'
 
 
-  any-promise@1.3.0:
-    resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==}
-
   anymatch@3.1.3:
   anymatch@3.1.3:
     resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
     resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
     engines: {node: '>= 8'}
     engines: {node: '>= 8'}
@@ -1906,9 +1896,6 @@ packages:
     resolution: {integrity: sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig==}
     resolution: {integrity: sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig==}
     engines: {node: '>=14'}
     engines: {node: '>=14'}
 
 
-  arg@5.0.2:
-    resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==}
-
   argparse@2.0.1:
   argparse@2.0.1:
     resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
     resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
 
 
@@ -2051,10 +2038,6 @@ packages:
     resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
     resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
     engines: {node: '>=6'}
     engines: {node: '>=6'}
 
 
-  camelcase-css@2.0.1:
-    resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==}
-    engines: {node: '>= 6'}
-
   caniuse-lite@1.0.30001688:
   caniuse-lite@1.0.30001688:
     resolution: {integrity: sha512-Nmqpru91cuABu/DTCXbM2NSRHzM2uVHfPnhJ/1zEAJx/ILBRVmz3pzH4N7DZqbdG0gWClsCC05Oj0mJ/1AWMbA==}
     resolution: {integrity: sha512-Nmqpru91cuABu/DTCXbM2NSRHzM2uVHfPnhJ/1zEAJx/ILBRVmz3pzH4N7DZqbdG0gWClsCC05Oj0mJ/1AWMbA==}
 
 
@@ -2123,10 +2106,6 @@ packages:
     resolution: {integrity: sha512-H4UfQhZyakIjC74I9d34fGYDwk3XpSr17QhEd0Q3I9Xq1CETHo4Hcuo87WyWHpAF1aSLjLRf5lD9ZGX2qStUvg==}
     resolution: {integrity: sha512-H4UfQhZyakIjC74I9d34fGYDwk3XpSr17QhEd0Q3I9Xq1CETHo4Hcuo87WyWHpAF1aSLjLRf5lD9ZGX2qStUvg==}
     engines: {node: '>=4.0.0'}
     engines: {node: '>=4.0.0'}
 
 
-  commander@4.1.1:
-    resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==}
-    engines: {node: '>= 6'}
-
   commander@7.2.0:
   commander@7.2.0:
     resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==}
     resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==}
     engines: {node: '>= 10'}
     engines: {node: '>= 10'}
@@ -2296,15 +2275,9 @@ packages:
   devlop@1.1.0:
   devlop@1.1.0:
     resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==}
     resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==}
 
 
-  didyoumean@1.2.2:
-    resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==}
-
   diff3@0.0.3:
   diff3@0.0.3:
     resolution: {integrity: sha512-iSq8ngPOt0K53A6eVr4d5Kn6GNrM2nQZtC740pzIriHtn4pOQ2lyzEXQMBeVcWERN0ye7fhBsk9PbLLQOnUx/g==}
     resolution: {integrity: sha512-iSq8ngPOt0K53A6eVr4d5Kn6GNrM2nQZtC740pzIriHtn4pOQ2lyzEXQMBeVcWERN0ye7fhBsk9PbLLQOnUx/g==}
 
 
-  dlv@1.1.3:
-    resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==}
-
   doctrine@3.0.0:
   doctrine@3.0.0:
     resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==}
     resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==}
     engines: {node: '>=6.0.0'}
     engines: {node: '>=6.0.0'}
@@ -2654,10 +2627,6 @@ packages:
   fast-deep-equal@3.1.3:
   fast-deep-equal@3.1.3:
     resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
     resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
 
 
-  fast-glob@3.3.2:
-    resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==}
-    engines: {node: '>=8.6.0'}
-
   fast-glob@3.3.3:
   fast-glob@3.3.3:
     resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==}
     resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==}
     engines: {node: '>=8.6.0'}
     engines: {node: '>=8.6.0'}
@@ -3094,10 +3063,6 @@ packages:
   jackspeak@3.4.3:
   jackspeak@3.4.3:
     resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==}
     resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==}
 
 
-  jiti@1.21.6:
-    resolution: {integrity: sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==}
-    hasBin: true
-
   jiti@2.4.1:
   jiti@2.4.1:
     resolution: {integrity: sha512-yPBThwecp1wS9DmoA4x4KR2h3QoslacnDR8ypuFM962kI4/456Iy1oHx2RAgh4jfZNdn0bctsdadceiBUgpU1g==}
     resolution: {integrity: sha512-yPBThwecp1wS9DmoA4x4KR2h3QoslacnDR8ypuFM962kI4/456Iy1oHx2RAgh4jfZNdn0bctsdadceiBUgpU1g==}
     hasBin: true
     hasBin: true
@@ -3177,10 +3142,6 @@ packages:
     resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
     resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
     engines: {node: '>= 0.8.0'}
     engines: {node: '>= 0.8.0'}
 
 
-  lilconfig@3.1.3:
-    resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==}
-    engines: {node: '>=14'}
-
   lines-and-columns@1.2.4:
   lines-and-columns@1.2.4:
     resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
     resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
 
 
@@ -3480,9 +3441,6 @@ packages:
   muggle-string@0.4.1:
   muggle-string@0.4.1:
     resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==}
     resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==}
 
 
-  mz@2.7.0:
-    resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==}
-
   nanoid@3.3.8:
   nanoid@3.3.8:
     resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==}
     resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==}
     engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
     engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
@@ -3539,14 +3497,6 @@ packages:
     engines: {node: ^14.16.0 || >=16.10.0}
     engines: {node: ^14.16.0 || >=16.10.0}
     hasBin: true
     hasBin: true
 
 
-  object-assign@4.1.1:
-    resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
-    engines: {node: '>=0.10.0'}
-
-  object-hash@3.0.0:
-    resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==}
-    engines: {node: '>= 6'}
-
   object-inspect@1.13.3:
   object-inspect@1.13.3:
     resolution: {integrity: sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==}
     resolution: {integrity: sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==}
     engines: {node: '>= 0.4'}
     engines: {node: '>= 0.4'}
@@ -3700,10 +3650,6 @@ packages:
     resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==}
     resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==}
     engines: {node: '>=12'}
     engines: {node: '>=12'}
 
 
-  pify@2.3.0:
-    resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==}
-    engines: {node: '>=0.10.0'}
-
   pify@4.0.1:
   pify@4.0.1:
     resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==}
     resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==}
     engines: {node: '>=6'}
     engines: {node: '>=6'}
@@ -3728,10 +3674,6 @@ packages:
       typescript:
       typescript:
         optional: true
         optional: true
 
 
-  pirates@4.0.6:
-    resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==}
-    engines: {node: '>= 6'}
-
   pkg-types@1.2.1:
   pkg-types@1.2.1:
     resolution: {integrity: sha512-sQoqa8alT3nHjGuTjuKgOnvjo4cljkufdtLMnO2LBP/wRwuDlo1tkaEdMxCRhyGRPacv/ztlZgDPm2b7FAmEvw==}
     resolution: {integrity: sha512-sQoqa8alT3nHjGuTjuKgOnvjo4cljkufdtLMnO2LBP/wRwuDlo1tkaEdMxCRhyGRPacv/ztlZgDPm2b7FAmEvw==}
 
 
@@ -3752,36 +3694,6 @@ packages:
     resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==}
     resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==}
     engines: {node: '>= 0.4'}
     engines: {node: '>= 0.4'}
 
 
-  postcss-import@15.1.0:
-    resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==}
-    engines: {node: '>=14.0.0'}
-    peerDependencies:
-      postcss: ^8.0.0
-
-  postcss-js@4.0.1:
-    resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==}
-    engines: {node: ^12 || ^14 || >= 16}
-    peerDependencies:
-      postcss: ^8.4.21
-
-  postcss-load-config@4.0.2:
-    resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==}
-    engines: {node: '>= 14'}
-    peerDependencies:
-      postcss: '>=8.0.9'
-      ts-node: '>=9.0.0'
-    peerDependenciesMeta:
-      postcss:
-        optional: true
-      ts-node:
-        optional: true
-
-  postcss-nested@6.2.0:
-    resolution: {integrity: sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==}
-    engines: {node: '>=12.0'}
-    peerDependencies:
-      postcss: ^8.2.14
-
   postcss-selector-parser@6.1.2:
   postcss-selector-parser@6.1.2:
     resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==}
     resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==}
     engines: {node: '>=4'}
     engines: {node: '>=4'}
@@ -3816,9 +3728,6 @@ packages:
   rc9@2.1.2:
   rc9@2.1.2:
     resolution: {integrity: sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==}
     resolution: {integrity: sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==}
 
 
-  read-cache@1.0.0:
-    resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==}
-
   read-pkg-up@7.0.1:
   read-pkg-up@7.0.1:
     resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==}
     resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==}
     engines: {node: '>=8'}
     engines: {node: '>=8'}
@@ -4110,11 +4019,6 @@ packages:
   stylis@4.3.4:
   stylis@4.3.4:
     resolution: {integrity: sha512-osIBl6BGUmSfDkyH2mB7EFvCJntXDrLhKjHTRj/rK6xLH0yuPrHULDRQzKokSOD4VoorhtKpfcfW1GAntu8now==}
     resolution: {integrity: sha512-osIBl6BGUmSfDkyH2mB7EFvCJntXDrLhKjHTRj/rK6xLH0yuPrHULDRQzKokSOD4VoorhtKpfcfW1GAntu8now==}
 
 
-  sucrase@3.35.0:
-    resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==}
-    engines: {node: '>=16 || 14 >=14.17'}
-    hasBin: true
-
   supports-color@7.2.0:
   supports-color@7.2.0:
     resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
     resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
     engines: {node: '>=8'}
     engines: {node: '>=8'}
@@ -4139,11 +4043,6 @@ packages:
     resolution: {integrity: sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==}
     resolution: {integrity: sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==}
     engines: {node: ^14.18.0 || >=16.0.0}
     engines: {node: ^14.18.0 || >=16.0.0}
 
 
-  tailwindcss@3.4.17:
-    resolution: {integrity: sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==}
-    engines: {node: '>=14.0.0'}
-    hasBin: true
-
   tapable@2.2.1:
   tapable@2.2.1:
     resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==}
     resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==}
     engines: {node: '>=6'}
     engines: {node: '>=6'}
@@ -4152,13 +4051,6 @@ packages:
     resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==}
     resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==}
     engines: {node: '>=10'}
     engines: {node: '>=10'}
 
 
-  thenify-all@1.6.0:
-    resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==}
-    engines: {node: '>=0.8'}
-
-  thenify@3.3.1:
-    resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==}
-
   throttle-debounce@5.0.2:
   throttle-debounce@5.0.2:
     resolution: {integrity: sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==}
     resolution: {integrity: sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==}
     engines: {node: '>=12.22'}
     engines: {node: '>=12.22'}
@@ -4191,9 +4083,6 @@ packages:
     peerDependencies:
     peerDependencies:
       typescript: '>=4.8.4'
       typescript: '>=4.8.4'
 
 
-  ts-interface-checker@0.1.13:
-    resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==}
-
   tslib@2.8.1:
   tslib@2.8.1:
     resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
     resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
 
 
@@ -4601,8 +4490,6 @@ snapshots:
     dependencies:
     dependencies:
       github-buttons: 2.29.1
       github-buttons: 2.29.1
 
 
-  '@alloc/quick-lru@5.2.0': {}
-
   '@ampproject/remapping@2.3.0':
   '@ampproject/remapping@2.3.0':
     dependencies:
     dependencies:
       '@jridgewell/gen-mapping': 0.3.8
       '@jridgewell/gen-mapping': 0.3.8
@@ -6474,8 +6361,6 @@ snapshots:
       vue-types: 3.0.2(vue@3.5.13(typescript@5.7.3))
       vue-types: 3.0.2(vue@3.5.13(typescript@5.7.3))
       warning: 4.0.3
       warning: 4.0.3
 
 
-  any-promise@1.3.0: {}
-
   anymatch@3.1.3:
   anymatch@3.1.3:
     dependencies:
     dependencies:
       normalize-path: 3.0.0
       normalize-path: 3.0.0
@@ -6492,8 +6377,6 @@ snapshots:
 
 
   are-docs-informative@0.0.2: {}
   are-docs-informative@0.0.2: {}
 
 
-  arg@5.0.2: {}
-
   argparse@2.0.1: {}
   argparse@2.0.1: {}
 
 
   array-back@3.1.0: {}
   array-back@3.1.0: {}
@@ -6667,8 +6550,6 @@ snapshots:
 
 
   callsites@3.1.0: {}
   callsites@3.1.0: {}
 
 
-  camelcase-css@2.0.1: {}
-
   caniuse-lite@1.0.30001688: {}
   caniuse-lite@1.0.30001688: {}
 
 
   ccount@2.0.1: {}
   ccount@2.0.1: {}
@@ -6758,8 +6639,6 @@ snapshots:
       lodash.camelcase: 4.3.0
       lodash.camelcase: 4.3.0
       typical: 4.0.0
       typical: 4.0.0
 
 
-  commander@4.1.1: {}
-
   commander@7.2.0: {}
   commander@7.2.0: {}
 
 
   comment-parser@1.4.1: {}
   comment-parser@1.4.1: {}
@@ -6908,12 +6787,8 @@ snapshots:
     dependencies:
     dependencies:
       dequal: 2.0.3
       dequal: 2.0.3
 
 
-  didyoumean@1.2.2: {}
-
   diff3@0.0.3: {}
   diff3@0.0.3: {}
 
 
-  dlv@1.1.3: {}
-
   doctrine@3.0.0:
   doctrine@3.0.0:
     dependencies:
     dependencies:
       esutils: 2.0.3
       esutils: 2.0.3
@@ -7473,14 +7348,6 @@ snapshots:
 
 
   fast-deep-equal@3.1.3: {}
   fast-deep-equal@3.1.3: {}
 
 
-  fast-glob@3.3.2:
-    dependencies:
-      '@nodelib/fs.stat': 2.0.5
-      '@nodelib/fs.walk': 1.2.8
-      glob-parent: 5.1.2
-      merge2: 1.4.1
-      micromatch: 4.0.8
-
   fast-glob@3.3.3:
   fast-glob@3.3.3:
     dependencies:
     dependencies:
       '@nodelib/fs.stat': 2.0.5
       '@nodelib/fs.stat': 2.0.5
@@ -7924,8 +7791,6 @@ snapshots:
     optionalDependencies:
     optionalDependencies:
       '@pkgjs/parseargs': 0.11.0
       '@pkgjs/parseargs': 0.11.0
 
 
-  jiti@1.21.6: {}
-
   jiti@2.4.1: {}
   jiti@2.4.1: {}
 
 
   js-tokens@4.0.0: {}
   js-tokens@4.0.0: {}
@@ -7997,8 +7862,6 @@ snapshots:
       prelude-ls: 1.2.1
       prelude-ls: 1.2.1
       type-check: 0.4.0
       type-check: 0.4.0
 
 
-  lilconfig@3.1.3: {}
-
   lines-and-columns@1.2.4: {}
   lines-and-columns@1.2.4: {}
 
 
   load-tsconfig@0.2.5: {}
   load-tsconfig@0.2.5: {}
@@ -8440,12 +8303,6 @@ snapshots:
 
 
   muggle-string@0.4.1: {}
   muggle-string@0.4.1: {}
 
 
-  mz@2.7.0:
-    dependencies:
-      any-promise: 1.3.0
-      object-assign: 4.1.1
-      thenify-all: 1.6.0
-
   nanoid@3.3.8: {}
   nanoid@3.3.8: {}
 
 
   nanopop@2.4.2: {}
   nanopop@2.4.2: {}
@@ -8496,10 +8353,6 @@ snapshots:
       pkg-types: 1.3.0
       pkg-types: 1.3.0
       ufo: 1.5.4
       ufo: 1.5.4
 
 
-  object-assign@4.1.1: {}
-
-  object-hash@3.0.0: {}
-
   object-inspect@1.13.3: {}
   object-inspect@1.13.3: {}
 
 
   object-keys@1.1.1: {}
   object-keys@1.1.1: {}
@@ -8640,8 +8493,6 @@ snapshots:
 
 
   picomatch@4.0.2: {}
   picomatch@4.0.2: {}
 
 
-  pify@2.3.0: {}
-
   pify@4.0.1: {}
   pify@4.0.1: {}
 
 
   pinia-plugin-persistedstate@4.2.0(pinia@2.3.0(typescript@5.7.3)(vue@3.5.13(typescript@5.7.3)))(rollup@4.28.1):
   pinia-plugin-persistedstate@4.2.0(pinia@2.3.0(typescript@5.7.3)(vue@3.5.13(typescript@5.7.3)))(rollup@4.28.1):
@@ -8667,8 +8518,6 @@ snapshots:
     transitivePeerDependencies:
     transitivePeerDependencies:
       - '@vue/composition-api'
       - '@vue/composition-api'
 
 
-  pirates@4.0.6: {}
-
   pkg-types@1.2.1:
   pkg-types@1.2.1:
     dependencies:
     dependencies:
       confbox: 0.1.8
       confbox: 0.1.8
@@ -8689,30 +8538,6 @@ snapshots:
 
 
   possible-typed-array-names@1.0.0: {}
   possible-typed-array-names@1.0.0: {}
 
 
-  postcss-import@15.1.0(postcss@8.5.1):
-    dependencies:
-      postcss: 8.5.1
-      postcss-value-parser: 4.2.0
-      read-cache: 1.0.0
-      resolve: 1.22.9
-
-  postcss-js@4.0.1(postcss@8.5.1):
-    dependencies:
-      camelcase-css: 2.0.1
-      postcss: 8.5.1
-
-  postcss-load-config@4.0.2(postcss@8.5.1):
-    dependencies:
-      lilconfig: 3.1.3
-      yaml: 2.6.1
-    optionalDependencies:
-      postcss: 8.5.1
-
-  postcss-nested@6.2.0(postcss@8.5.1):
-    dependencies:
-      postcss: 8.5.1
-      postcss-selector-parser: 6.1.2
-
   postcss-selector-parser@6.1.2:
   postcss-selector-parser@6.1.2:
     dependencies:
     dependencies:
       cssesc: 3.0.0
       cssesc: 3.0.0
@@ -8747,10 +8572,6 @@ snapshots:
       defu: 6.1.4
       defu: 6.1.4
       destr: 2.0.3
       destr: 2.0.3
 
 
-  read-cache@1.0.0:
-    dependencies:
-      pify: 2.3.0
-
   read-pkg-up@7.0.1:
   read-pkg-up@7.0.1:
     dependencies:
     dependencies:
       find-up: 4.1.0
       find-up: 4.1.0
@@ -9096,16 +8917,6 @@ snapshots:
 
 
   stylis@4.3.4: {}
   stylis@4.3.4: {}
 
 
-  sucrase@3.35.0:
-    dependencies:
-      '@jridgewell/gen-mapping': 0.3.8
-      commander: 4.1.1
-      glob: 10.4.5
-      lines-and-columns: 1.2.4
-      mz: 2.7.0
-      pirates: 4.0.6
-      ts-interface-checker: 0.1.13
-
   supports-color@7.2.0:
   supports-color@7.2.0:
     dependencies:
     dependencies:
       has-flag: 4.0.0
       has-flag: 4.0.0
@@ -9133,33 +8944,6 @@ snapshots:
       '@pkgr/core': 0.1.1
       '@pkgr/core': 0.1.1
       tslib: 2.8.1
       tslib: 2.8.1
 
 
-  tailwindcss@3.4.17:
-    dependencies:
-      '@alloc/quick-lru': 5.2.0
-      arg: 5.0.2
-      chokidar: 3.6.0
-      didyoumean: 1.2.2
-      dlv: 1.1.3
-      fast-glob: 3.3.2
-      glob-parent: 6.0.2
-      is-glob: 4.0.3
-      jiti: 1.21.6
-      lilconfig: 3.1.3
-      micromatch: 4.0.8
-      normalize-path: 3.0.0
-      object-hash: 3.0.0
-      picocolors: 1.1.1
-      postcss: 8.5.1
-      postcss-import: 15.1.0(postcss@8.5.1)
-      postcss-js: 4.0.1(postcss@8.5.1)
-      postcss-load-config: 4.0.2(postcss@8.5.1)
-      postcss-nested: 6.2.0(postcss@8.5.1)
-      postcss-selector-parser: 6.1.2
-      resolve: 1.22.9
-      sucrase: 3.35.0
-    transitivePeerDependencies:
-      - ts-node
-
   tapable@2.2.1: {}
   tapable@2.2.1: {}
 
 
   tar@6.2.1:
   tar@6.2.1:
@@ -9171,14 +8955,6 @@ snapshots:
       mkdirp: 1.0.4
       mkdirp: 1.0.4
       yallist: 4.0.0
       yallist: 4.0.0
 
 
-  thenify-all@1.6.0:
-    dependencies:
-      thenify: 3.3.1
-
-  thenify@3.3.1:
-    dependencies:
-      any-promise: 1.3.0
-
   throttle-debounce@5.0.2: {}
   throttle-debounce@5.0.2: {}
 
 
   tinyexec@0.3.1: {}
   tinyexec@0.3.1: {}
@@ -9204,8 +8980,6 @@ snapshots:
     dependencies:
     dependencies:
       typescript: 5.7.3
       typescript: 5.7.3
 
 
-  ts-interface-checker@0.1.13: {}
-
   tslib@2.8.1: {}
   tslib@2.8.1: {}
 
 
   tsx@4.19.2:
   tsx@4.19.2:

+ 22 - 0
app/src/api/self_check.ts

@@ -0,0 +1,22 @@
+import type { CosyError } from '@/lib/http'
+import http from '@/lib/http'
+import ws from '@/lib/websocket'
+
+export interface Report {
+  name: string
+  err?: CosyError
+}
+
+const selfCheck = {
+  run(): Promise<Report[]> {
+    return http.get('/self_check')
+  },
+  fix(taskName: string) {
+    return http.post(`/self_check/${taskName}/fix`)
+  },
+  websocket() {
+    return ws('/api/self_check/websocket', false)
+  },
+}
+
+export default selfCheck

+ 65 - 6
app/src/language/ar/app.po

@@ -21,7 +21,7 @@ msgstr "المصادقة الثنائية"
 msgid "2FA Settings"
 msgid "2FA Settings"
 msgstr "إعدادات المصادقة الثنائية"
 msgstr "إعدادات المصادقة الثنائية"
 
 
-#: src/routes/index.ts:283
+#: src/routes/index.ts:290
 msgid "About"
 msgid "About"
 msgstr "عن"
 msgstr "عن"
 
 
@@ -199,6 +199,11 @@ msgstr "اطلب المساعدة من ChatGPT"
 msgid "Assistant"
 msgid "Assistant"
 msgstr "المساعد"
 msgstr "المساعد"
 
 
+#: src/views/system/SelfCheck/SelfCheck.vue:54
+#, fuzzy
+msgid "Attempt to fix"
+msgstr "محاولات"
+
 #: src/views/preference/AuthSettings.vue:17
 #: src/views/preference/AuthSettings.vue:17
 msgid "Attempts"
 msgid "Attempts"
 msgstr "محاولات"
 msgstr "محاولات"
@@ -389,6 +394,26 @@ msgstr "قناة"
 msgid "Check again"
 msgid "Check again"
 msgstr "تحقق مرة أخرى"
 msgstr "تحقق مرة أخرى"
 
 
+#: src/views/system/SelfCheck/tasks.ts:12
+msgid "Check if the nginx.conf includes the sites-enabled directory."
+msgstr ""
+
+#: src/views/system/SelfCheck/tasks.ts:16
+msgid "Check if the nginx.conf includes the streams-enabled directory."
+msgstr ""
+
+#: src/views/system/SelfCheck/tasks.ts:4
+msgid ""
+"Check if the sites-available and sites-enabled directories are under the "
+"nginx configuration directory."
+msgstr ""
+
+#: src/views/system/SelfCheck/tasks.ts:8
+msgid ""
+"Check if the streams-available and streams-enabled directories are under the "
+"nginx configuration directory."
+msgstr ""
+
 #: src/language/constants.ts:13
 #: src/language/constants.ts:13
 msgid "Cleaning environment variables"
 msgid "Cleaning environment variables"
 msgstr "تنظيف متغيرات البيئة"
 msgstr "تنظيف متغيرات البيئة"
@@ -1132,7 +1157,7 @@ msgstr "أدخل الرمز من التطبيق:"
 msgid "Input the recovery code:"
 msgid "Input the recovery code:"
 msgstr "أدخل رمز الاسترداد:"
 msgstr "أدخل رمز الاسترداد:"
 
 
-#: src/routes/index.ts:305 src/views/other/Install.vue:136
+#: src/routes/index.ts:312 src/views/other/Install.vue:136
 msgid "Install"
 msgid "Install"
 msgstr "تثبيت"
 msgstr "تثبيت"
 
 
@@ -1269,7 +1294,7 @@ msgstr "أماكن"
 msgid "Log"
 msgid "Log"
 msgstr "سجل"
 msgstr "سجل"
 
 
-#: src/routes/index.ts:311 src/views/other/Login.vue:246
+#: src/routes/index.ts:318 src/views/other/Login.vue:246
 msgid "Login"
 msgid "Login"
 msgstr "تسجيل الدخول"
 msgstr "تسجيل الدخول"
 
 
@@ -1434,6 +1459,14 @@ msgstr "Nginx"
 msgid "Nginx Access Log Path"
 msgid "Nginx Access Log Path"
 msgstr "مسار سجل الوصول لـ Nginx"
 msgstr "مسار سجل الوصول لـ Nginx"
 
 
+#: src/views/system/SelfCheck/tasks.ts:11
+msgid "Nginx Conf Include Sites Enabled"
+msgstr ""
+
+#: src/views/system/SelfCheck/tasks.ts:15
+msgid "Nginx Conf Include Streams Enabled"
+msgstr ""
+
 #: src/views/site/site_edit/SiteEdit.vue:223
 #: src/views/site/site_edit/SiteEdit.vue:223
 #: src/views/stream/StreamEdit.vue:207
 #: src/views/stream/StreamEdit.vue:207
 msgid "Nginx Configuration Parse Error"
 msgid "Nginx Configuration Parse Error"
@@ -1512,7 +1545,7 @@ msgstr "سر العقدة"
 msgid "Not After"
 msgid "Not After"
 msgstr "ليس بعد"
 msgstr "ليس بعد"
 
 
-#: src/routes/index.ts:317
+#: src/routes/index.ts:324
 msgid "Not Found"
 msgid "Not Found"
 msgstr "غير موجود"
 msgstr "غير موجود"
 
 
@@ -1813,6 +1846,10 @@ msgstr "يقرأ"
 msgid "Receive"
 msgid "Receive"
 msgstr "يستقبل"
 msgstr "يستقبل"
 
 
+#: src/views/system/SelfCheck/SelfCheck.vue:47
+msgid "Recheck"
+msgstr ""
+
 #: src/components/StdDesign/StdDataDisplay/StdBulkActions.vue:43
 #: src/components/StdDesign/StdDataDisplay/StdBulkActions.vue:43
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:544
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:544
 msgid "Recover"
 msgid "Recover"
@@ -2102,6 +2139,10 @@ msgstr "تم نسخ السر"
 msgid "Selector"
 msgid "Selector"
 msgstr "المحدد"
 msgstr "المحدد"
 
 
+#: src/routes/index.ts:283 src/views/system/SelfCheck/SelfCheck.vue:42
+msgid "Self Check"
+msgstr ""
+
 #: src/views/dashboard/ServerAnalytic.vue:348
 #: src/views/dashboard/ServerAnalytic.vue:348
 #: src/views/dashboard/ServerAnalytic.vue:35
 #: src/views/dashboard/ServerAnalytic.vue:35
 msgid "Send"
 msgid "Send"
@@ -2114,7 +2155,7 @@ msgstr "إرسال"
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:53
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:53
 #: src/components/StdDesign/StdDataDisplay/StdBulkActions.vue:70
 #: src/components/StdDesign/StdDataDisplay/StdBulkActions.vue:70
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:194
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:194
-#: src/views/config/components/Mkdir.vue:38
+#: src/lib/http/index.ts:128 src/views/config/components/Mkdir.vue:38
 #: src/views/config/components/Rename.vue:45
 #: src/views/config/components/Rename.vue:45
 #: src/views/config/ConfigEditor.vue:111
 #: src/views/config/ConfigEditor.vue:111
 #: src/views/environment/BatchUpgrader.vue:59
 #: src/views/environment/BatchUpgrader.vue:59
@@ -2184,6 +2225,11 @@ msgstr "فئات الموقع"
 msgid "Site Logs"
 msgid "Site Logs"
 msgstr "سجلات الموقع"
 msgstr "سجلات الموقع"
 
 
+#: src/views/system/SelfCheck/tasks.ts:3
+#, fuzzy
+msgid "Sites Directory"
+msgstr "مجلد"
+
 #: src/routes/index.ts:56
 #: src/routes/index.ts:56
 msgid "Sites List"
 msgid "Sites List"
 msgstr "قائمة المواقع"
 msgstr "قائمة المواقع"
@@ -2230,10 +2276,23 @@ msgstr "متوقف"
 msgid "Storage"
 msgid "Storage"
 msgstr "تخزين"
 msgstr "تخزين"
 
 
+#: src/views/system/SelfCheck/tasks.ts:7
+#, fuzzy
+msgid "Streams Directory"
+msgstr "مجلد"
+
 #: src/constants/index.ts:19 src/views/notification/notificationColumns.tsx:36
 #: src/constants/index.ts:19 src/views/notification/notificationColumns.tsx:36
 msgid "Success"
 msgid "Success"
 msgstr "نجاح"
 msgstr "نجاح"
 
 
+#: src/views/system/SelfCheck/SelfCheck.vue:78
+msgid ""
+"Support communication with the backend through the WebSocket protocol. If "
+"your Nginx UI is being used via an Nginx reverse proxy, please refer to this "
+"link to write the corresponding configuration file: https://nginxui.com/"
+"guide/nginx-proxy-example.html"
+msgstr ""
+
 #: src/views/dashboard/ServerAnalytic.vue:240
 #: src/views/dashboard/ServerAnalytic.vue:240
 #: src/views/dashboard/ServerAnalytic.vue:241
 #: src/views/dashboard/ServerAnalytic.vue:241
 msgid "Swap"
 msgid "Swap"
@@ -2590,7 +2649,7 @@ msgstr "محدث في"
 msgid "Updated successfully"
 msgid "Updated successfully"
 msgstr "تم التحديث بنجاح"
 msgstr "تم التحديث بنجاح"
 
 
-#: src/routes/index.ts:290 src/views/environment/Environment.vue:55
+#: src/routes/index.ts:297 src/views/environment/Environment.vue:55
 #: src/views/system/Upgrade.vue:145 src/views/system/Upgrade.vue:228
 #: src/views/system/Upgrade.vue:145 src/views/system/Upgrade.vue:228
 msgid "Upgrade"
 msgid "Upgrade"
 msgstr "ترقية"
 msgstr "ترقية"

+ 64 - 6
app/src/language/en/app.po

@@ -17,7 +17,7 @@ msgstr ""
 msgid "2FA Settings"
 msgid "2FA Settings"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:283
+#: src/routes/index.ts:290
 msgid "About"
 msgid "About"
 msgstr "About"
 msgstr "About"
 
 
@@ -209,6 +209,10 @@ msgstr ""
 msgid "Assistant"
 msgid "Assistant"
 msgstr ""
 msgstr ""
 
 
+#: src/views/system/SelfCheck/SelfCheck.vue:54
+msgid "Attempt to fix"
+msgstr ""
+
 #: src/views/preference/AuthSettings.vue:17
 #: src/views/preference/AuthSettings.vue:17
 msgid "Attempts"
 msgid "Attempts"
 msgstr ""
 msgstr ""
@@ -401,6 +405,26 @@ msgstr ""
 msgid "Check again"
 msgid "Check again"
 msgstr ""
 msgstr ""
 
 
+#: src/views/system/SelfCheck/tasks.ts:12
+msgid "Check if the nginx.conf includes the sites-enabled directory."
+msgstr ""
+
+#: src/views/system/SelfCheck/tasks.ts:16
+msgid "Check if the nginx.conf includes the streams-enabled directory."
+msgstr ""
+
+#: src/views/system/SelfCheck/tasks.ts:4
+msgid ""
+"Check if the sites-available and sites-enabled directories are under the "
+"nginx configuration directory."
+msgstr ""
+
+#: src/views/system/SelfCheck/tasks.ts:8
+msgid ""
+"Check if the streams-available and streams-enabled directories are under the "
+"nginx configuration directory."
+msgstr ""
+
 #: src/language/constants.ts:13
 #: src/language/constants.ts:13
 msgid "Cleaning environment variables"
 msgid "Cleaning environment variables"
 msgstr ""
 msgstr ""
@@ -1175,7 +1199,7 @@ msgstr ""
 msgid "Input the recovery code:"
 msgid "Input the recovery code:"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:305 src/views/other/Install.vue:136
+#: src/routes/index.ts:312 src/views/other/Install.vue:136
 msgid "Install"
 msgid "Install"
 msgstr "Install"
 msgstr "Install"
 
 
@@ -1327,7 +1351,7 @@ msgstr "Locations"
 msgid "Log"
 msgid "Log"
 msgstr "Login"
 msgstr "Login"
 
 
-#: src/routes/index.ts:311 src/views/other/Login.vue:246
+#: src/routes/index.ts:318 src/views/other/Login.vue:246
 msgid "Login"
 msgid "Login"
 msgstr "Login"
 msgstr "Login"
 
 
@@ -1496,6 +1520,14 @@ msgstr ""
 msgid "Nginx Access Log Path"
 msgid "Nginx Access Log Path"
 msgstr ""
 msgstr ""
 
 
+#: src/views/system/SelfCheck/tasks.ts:11
+msgid "Nginx Conf Include Sites Enabled"
+msgstr ""
+
+#: src/views/system/SelfCheck/tasks.ts:15
+msgid "Nginx Conf Include Streams Enabled"
+msgstr ""
+
 #: src/views/site/site_edit/SiteEdit.vue:223
 #: src/views/site/site_edit/SiteEdit.vue:223
 #: src/views/stream/StreamEdit.vue:207
 #: src/views/stream/StreamEdit.vue:207
 #, fuzzy
 #, fuzzy
@@ -1579,7 +1611,7 @@ msgstr ""
 msgid "Not After"
 msgid "Not After"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:317
+#: src/routes/index.ts:324
 msgid "Not Found"
 msgid "Not Found"
 msgstr "Not Found"
 msgstr "Not Found"
 
 
@@ -1878,6 +1910,10 @@ msgstr "Reads"
 msgid "Receive"
 msgid "Receive"
 msgstr "Receive"
 msgstr "Receive"
 
 
+#: src/views/system/SelfCheck/SelfCheck.vue:47
+msgid "Recheck"
+msgstr ""
+
 #: src/components/StdDesign/StdDataDisplay/StdBulkActions.vue:43
 #: src/components/StdDesign/StdDataDisplay/StdBulkActions.vue:43
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:544
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:544
 msgid "Recover"
 msgid "Recover"
@@ -2193,6 +2229,10 @@ msgstr ""
 msgid "Selector"
 msgid "Selector"
 msgstr ""
 msgstr ""
 
 
+#: src/routes/index.ts:283 src/views/system/SelfCheck/SelfCheck.vue:42
+msgid "Self Check"
+msgstr ""
+
 #: src/views/dashboard/ServerAnalytic.vue:348
 #: src/views/dashboard/ServerAnalytic.vue:348
 #: src/views/dashboard/ServerAnalytic.vue:35
 #: src/views/dashboard/ServerAnalytic.vue:35
 msgid "Send"
 msgid "Send"
@@ -2205,7 +2245,7 @@ msgstr "Send"
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:53
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:53
 #: src/components/StdDesign/StdDataDisplay/StdBulkActions.vue:70
 #: src/components/StdDesign/StdDataDisplay/StdBulkActions.vue:70
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:194
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:194
-#: src/views/config/components/Mkdir.vue:38
+#: src/lib/http/index.ts:128 src/views/config/components/Mkdir.vue:38
 #: src/views/config/components/Rename.vue:45
 #: src/views/config/components/Rename.vue:45
 #: src/views/config/ConfigEditor.vue:111
 #: src/views/config/ConfigEditor.vue:111
 #: src/views/environment/BatchUpgrader.vue:59
 #: src/views/environment/BatchUpgrader.vue:59
@@ -2274,6 +2314,11 @@ msgstr ""
 msgid "Site Logs"
 msgid "Site Logs"
 msgstr "Sites List"
 msgstr "Sites List"
 
 
+#: src/views/system/SelfCheck/tasks.ts:3
+#, fuzzy
+msgid "Sites Directory"
+msgstr "Directive"
+
 #: src/routes/index.ts:56
 #: src/routes/index.ts:56
 msgid "Sites List"
 msgid "Sites List"
 msgstr "Sites List"
 msgstr "Sites List"
@@ -2326,10 +2371,23 @@ msgstr ""
 msgid "Storage"
 msgid "Storage"
 msgstr "Storage"
 msgstr "Storage"
 
 
+#: src/views/system/SelfCheck/tasks.ts:7
+#, fuzzy
+msgid "Streams Directory"
+msgstr "Directive"
+
 #: src/constants/index.ts:19 src/views/notification/notificationColumns.tsx:36
 #: src/constants/index.ts:19 src/views/notification/notificationColumns.tsx:36
 msgid "Success"
 msgid "Success"
 msgstr ""
 msgstr ""
 
 
+#: src/views/system/SelfCheck/SelfCheck.vue:78
+msgid ""
+"Support communication with the backend through the WebSocket protocol. If "
+"your Nginx UI is being used via an Nginx reverse proxy, please refer to this "
+"link to write the corresponding configuration file: https://nginxui.com/"
+"guide/nginx-proxy-example.html"
+msgstr ""
+
 #: src/views/dashboard/ServerAnalytic.vue:240
 #: src/views/dashboard/ServerAnalytic.vue:240
 #: src/views/dashboard/ServerAnalytic.vue:241
 #: src/views/dashboard/ServerAnalytic.vue:241
 msgid "Swap"
 msgid "Swap"
@@ -2671,7 +2729,7 @@ msgstr "Updated at"
 msgid "Updated successfully"
 msgid "Updated successfully"
 msgstr "Saved successfully"
 msgstr "Saved successfully"
 
 
-#: src/routes/index.ts:290 src/views/environment/Environment.vue:55
+#: src/routes/index.ts:297 src/views/environment/Environment.vue:55
 #: src/views/system/Upgrade.vue:145 src/views/system/Upgrade.vue:228
 #: src/views/system/Upgrade.vue:145 src/views/system/Upgrade.vue:228
 msgid "Upgrade"
 msgid "Upgrade"
 msgstr ""
 msgstr ""

+ 65 - 6
app/src/language/es/app.po

@@ -24,7 +24,7 @@ msgstr "2FA"
 msgid "2FA Settings"
 msgid "2FA Settings"
 msgstr "Configuración de 2FA"
 msgstr "Configuración de 2FA"
 
 
-#: src/routes/index.ts:283
+#: src/routes/index.ts:290
 msgid "About"
 msgid "About"
 msgstr "Acerca de"
 msgstr "Acerca de"
 
 
@@ -204,6 +204,11 @@ msgstr "Preguntar por ayuda a ChatGPT"
 msgid "Assistant"
 msgid "Assistant"
 msgstr "Asistente"
 msgstr "Asistente"
 
 
+#: src/views/system/SelfCheck/SelfCheck.vue:54
+#, fuzzy
+msgid "Attempt to fix"
+msgstr "Intentos"
+
 #: src/views/preference/AuthSettings.vue:17
 #: src/views/preference/AuthSettings.vue:17
 msgid "Attempts"
 msgid "Attempts"
 msgstr "Intentos"
 msgstr "Intentos"
@@ -388,6 +393,26 @@ msgstr "Canal"
 msgid "Check again"
 msgid "Check again"
 msgstr "Intentar nuevamente"
 msgstr "Intentar nuevamente"
 
 
+#: src/views/system/SelfCheck/tasks.ts:12
+msgid "Check if the nginx.conf includes the sites-enabled directory."
+msgstr ""
+
+#: src/views/system/SelfCheck/tasks.ts:16
+msgid "Check if the nginx.conf includes the streams-enabled directory."
+msgstr ""
+
+#: src/views/system/SelfCheck/tasks.ts:4
+msgid ""
+"Check if the sites-available and sites-enabled directories are under the "
+"nginx configuration directory."
+msgstr ""
+
+#: src/views/system/SelfCheck/tasks.ts:8
+msgid ""
+"Check if the streams-available and streams-enabled directories are under the "
+"nginx configuration directory."
+msgstr ""
+
 #: src/language/constants.ts:13
 #: src/language/constants.ts:13
 msgid "Cleaning environment variables"
 msgid "Cleaning environment variables"
 msgstr "Borrar las variables de entorno"
 msgstr "Borrar las variables de entorno"
@@ -1139,7 +1164,7 @@ msgstr "Ingrese el código de la aplicación:"
 msgid "Input the recovery code:"
 msgid "Input the recovery code:"
 msgstr "Ingrese el código de recuperación:"
 msgstr "Ingrese el código de recuperación:"
 
 
-#: src/routes/index.ts:305 src/views/other/Install.vue:136
+#: src/routes/index.ts:312 src/views/other/Install.vue:136
 msgid "Install"
 msgid "Install"
 msgstr "Instalar"
 msgstr "Instalar"
 
 
@@ -1278,7 +1303,7 @@ msgstr "Ubicaciones"
 msgid "Log"
 msgid "Log"
 msgstr "Registro"
 msgstr "Registro"
 
 
-#: src/routes/index.ts:311 src/views/other/Login.vue:246
+#: src/routes/index.ts:318 src/views/other/Login.vue:246
 msgid "Login"
 msgid "Login"
 msgstr "Acceso"
 msgstr "Acceso"
 
 
@@ -1444,6 +1469,14 @@ msgstr "Nginx"
 msgid "Nginx Access Log Path"
 msgid "Nginx Access Log Path"
 msgstr "Ruta de registro de acceso de Nginx"
 msgstr "Ruta de registro de acceso de Nginx"
 
 
+#: src/views/system/SelfCheck/tasks.ts:11
+msgid "Nginx Conf Include Sites Enabled"
+msgstr ""
+
+#: src/views/system/SelfCheck/tasks.ts:15
+msgid "Nginx Conf Include Streams Enabled"
+msgstr ""
+
 #: src/views/site/site_edit/SiteEdit.vue:223
 #: src/views/site/site_edit/SiteEdit.vue:223
 #: src/views/stream/StreamEdit.vue:207
 #: src/views/stream/StreamEdit.vue:207
 msgid "Nginx Configuration Parse Error"
 msgid "Nginx Configuration Parse Error"
@@ -1526,7 +1559,7 @@ msgstr "Secreto del nodo"
 msgid "Not After"
 msgid "Not After"
 msgstr "No después de"
 msgstr "No después de"
 
 
-#: src/routes/index.ts:317
+#: src/routes/index.ts:324
 msgid "Not Found"
 msgid "Not Found"
 msgstr "No encontrado"
 msgstr "No encontrado"
 
 
@@ -1845,6 +1878,10 @@ msgstr "Lecturas"
 msgid "Receive"
 msgid "Receive"
 msgstr "Recibido"
 msgstr "Recibido"
 
 
+#: src/views/system/SelfCheck/SelfCheck.vue:47
+msgid "Recheck"
+msgstr ""
+
 #: src/components/StdDesign/StdDataDisplay/StdBulkActions.vue:43
 #: src/components/StdDesign/StdDataDisplay/StdBulkActions.vue:43
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:544
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:544
 msgid "Recover"
 msgid "Recover"
@@ -2150,6 +2187,10 @@ msgstr "El secreto ha sido copiado"
 msgid "Selector"
 msgid "Selector"
 msgstr "Seleccionador"
 msgstr "Seleccionador"
 
 
+#: src/routes/index.ts:283 src/views/system/SelfCheck/SelfCheck.vue:42
+msgid "Self Check"
+msgstr ""
+
 #: src/views/dashboard/ServerAnalytic.vue:348
 #: src/views/dashboard/ServerAnalytic.vue:348
 #: src/views/dashboard/ServerAnalytic.vue:35
 #: src/views/dashboard/ServerAnalytic.vue:35
 msgid "Send"
 msgid "Send"
@@ -2162,7 +2203,7 @@ msgstr "Enviado"
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:53
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:53
 #: src/components/StdDesign/StdDataDisplay/StdBulkActions.vue:70
 #: src/components/StdDesign/StdDataDisplay/StdBulkActions.vue:70
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:194
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:194
-#: src/views/config/components/Mkdir.vue:38
+#: src/lib/http/index.ts:128 src/views/config/components/Mkdir.vue:38
 #: src/views/config/components/Rename.vue:45
 #: src/views/config/components/Rename.vue:45
 #: src/views/config/ConfigEditor.vue:111
 #: src/views/config/ConfigEditor.vue:111
 #: src/views/environment/BatchUpgrader.vue:59
 #: src/views/environment/BatchUpgrader.vue:59
@@ -2232,6 +2273,11 @@ msgstr "Categorías del sitio"
 msgid "Site Logs"
 msgid "Site Logs"
 msgstr "Registros del sitio"
 msgstr "Registros del sitio"
 
 
+#: src/views/system/SelfCheck/tasks.ts:3
+#, fuzzy
+msgid "Sites Directory"
+msgstr "Directorio"
+
 #: src/routes/index.ts:56
 #: src/routes/index.ts:56
 msgid "Sites List"
 msgid "Sites List"
 msgstr "Lista de sitios"
 msgstr "Lista de sitios"
@@ -2278,10 +2324,23 @@ msgstr "Detenido"
 msgid "Storage"
 msgid "Storage"
 msgstr "Almacenamiento"
 msgstr "Almacenamiento"
 
 
+#: src/views/system/SelfCheck/tasks.ts:7
+#, fuzzy
+msgid "Streams Directory"
+msgstr "Directorio"
+
 #: src/constants/index.ts:19 src/views/notification/notificationColumns.tsx:36
 #: src/constants/index.ts:19 src/views/notification/notificationColumns.tsx:36
 msgid "Success"
 msgid "Success"
 msgstr "Éxito"
 msgstr "Éxito"
 
 
+#: src/views/system/SelfCheck/SelfCheck.vue:78
+msgid ""
+"Support communication with the backend through the WebSocket protocol. If "
+"your Nginx UI is being used via an Nginx reverse proxy, please refer to this "
+"link to write the corresponding configuration file: https://nginxui.com/"
+"guide/nginx-proxy-example.html"
+msgstr ""
+
 #: src/views/dashboard/ServerAnalytic.vue:240
 #: src/views/dashboard/ServerAnalytic.vue:240
 #: src/views/dashboard/ServerAnalytic.vue:241
 #: src/views/dashboard/ServerAnalytic.vue:241
 msgid "Swap"
 msgid "Swap"
@@ -2658,7 +2717,7 @@ msgstr "Actualizado a"
 msgid "Updated successfully"
 msgid "Updated successfully"
 msgstr "Actualización exitosa"
 msgstr "Actualización exitosa"
 
 
-#: src/routes/index.ts:290 src/views/environment/Environment.vue:55
+#: src/routes/index.ts:297 src/views/environment/Environment.vue:55
 #: src/views/system/Upgrade.vue:145 src/views/system/Upgrade.vue:228
 #: src/views/system/Upgrade.vue:145 src/views/system/Upgrade.vue:228
 msgid "Upgrade"
 msgid "Upgrade"
 msgstr "Actualizar"
 msgstr "Actualizar"

+ 64 - 6
app/src/language/fr_FR/app.po

@@ -19,7 +19,7 @@ msgstr ""
 msgid "2FA Settings"
 msgid "2FA Settings"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:283
+#: src/routes/index.ts:290
 msgid "About"
 msgid "About"
 msgstr "À propos"
 msgstr "À propos"
 
 
@@ -212,6 +212,10 @@ msgstr "Modèle ChatGPT"
 msgid "Assistant"
 msgid "Assistant"
 msgstr ""
 msgstr ""
 
 
+#: src/views/system/SelfCheck/SelfCheck.vue:54
+msgid "Attempt to fix"
+msgstr ""
+
 #: src/views/preference/AuthSettings.vue:17
 #: src/views/preference/AuthSettings.vue:17
 msgid "Attempts"
 msgid "Attempts"
 msgstr ""
 msgstr ""
@@ -404,6 +408,26 @@ msgstr ""
 msgid "Check again"
 msgid "Check again"
 msgstr "Revérifier"
 msgstr "Revérifier"
 
 
+#: src/views/system/SelfCheck/tasks.ts:12
+msgid "Check if the nginx.conf includes the sites-enabled directory."
+msgstr ""
+
+#: src/views/system/SelfCheck/tasks.ts:16
+msgid "Check if the nginx.conf includes the streams-enabled directory."
+msgstr ""
+
+#: src/views/system/SelfCheck/tasks.ts:4
+msgid ""
+"Check if the sites-available and sites-enabled directories are under the "
+"nginx configuration directory."
+msgstr ""
+
+#: src/views/system/SelfCheck/tasks.ts:8
+msgid ""
+"Check if the streams-available and streams-enabled directories are under the "
+"nginx configuration directory."
+msgstr ""
+
 #: src/language/constants.ts:13
 #: src/language/constants.ts:13
 msgid "Cleaning environment variables"
 msgid "Cleaning environment variables"
 msgstr "Nettoyage des variables d'environnement"
 msgstr "Nettoyage des variables d'environnement"
@@ -1180,7 +1204,7 @@ msgstr ""
 msgid "Input the recovery code:"
 msgid "Input the recovery code:"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:305 src/views/other/Install.vue:136
+#: src/routes/index.ts:312 src/views/other/Install.vue:136
 msgid "Install"
 msgid "Install"
 msgstr "Installer"
 msgstr "Installer"
 
 
@@ -1331,7 +1355,7 @@ msgstr "Localisations"
 msgid "Log"
 msgid "Log"
 msgstr "Connexion"
 msgstr "Connexion"
 
 
-#: src/routes/index.ts:311 src/views/other/Login.vue:246
+#: src/routes/index.ts:318 src/views/other/Login.vue:246
 msgid "Login"
 msgid "Login"
 msgstr "Connexion"
 msgstr "Connexion"
 
 
@@ -1499,6 +1523,14 @@ msgstr "Journal Nginx"
 msgid "Nginx Access Log Path"
 msgid "Nginx Access Log Path"
 msgstr "Chemin du journal d'accès Nginx"
 msgstr "Chemin du journal d'accès Nginx"
 
 
+#: src/views/system/SelfCheck/tasks.ts:11
+msgid "Nginx Conf Include Sites Enabled"
+msgstr ""
+
+#: src/views/system/SelfCheck/tasks.ts:15
+msgid "Nginx Conf Include Streams Enabled"
+msgstr ""
+
 #: src/views/site/site_edit/SiteEdit.vue:223
 #: src/views/site/site_edit/SiteEdit.vue:223
 #: src/views/stream/StreamEdit.vue:207
 #: src/views/stream/StreamEdit.vue:207
 msgid "Nginx Configuration Parse Error"
 msgid "Nginx Configuration Parse Error"
@@ -1582,7 +1614,7 @@ msgstr "Secret Jwt"
 msgid "Not After"
 msgid "Not After"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:317
+#: src/routes/index.ts:324
 msgid "Not Found"
 msgid "Not Found"
 msgstr "Introuvable"
 msgstr "Introuvable"
 
 
@@ -1892,6 +1924,10 @@ msgstr "Lectures"
 msgid "Receive"
 msgid "Receive"
 msgstr "Recevoir"
 msgstr "Recevoir"
 
 
+#: src/views/system/SelfCheck/SelfCheck.vue:47
+msgid "Recheck"
+msgstr ""
+
 #: src/components/StdDesign/StdDataDisplay/StdBulkActions.vue:43
 #: src/components/StdDesign/StdDataDisplay/StdBulkActions.vue:43
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:544
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:544
 msgid "Recover"
 msgid "Recover"
@@ -2208,6 +2244,10 @@ msgstr ""
 msgid "Selector"
 msgid "Selector"
 msgstr "Sélecteur"
 msgstr "Sélecteur"
 
 
+#: src/routes/index.ts:283 src/views/system/SelfCheck/SelfCheck.vue:42
+msgid "Self Check"
+msgstr ""
+
 #: src/views/dashboard/ServerAnalytic.vue:348
 #: src/views/dashboard/ServerAnalytic.vue:348
 #: src/views/dashboard/ServerAnalytic.vue:35
 #: src/views/dashboard/ServerAnalytic.vue:35
 msgid "Send"
 msgid "Send"
@@ -2220,7 +2260,7 @@ msgstr "Envoyer"
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:53
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:53
 #: src/components/StdDesign/StdDataDisplay/StdBulkActions.vue:70
 #: src/components/StdDesign/StdDataDisplay/StdBulkActions.vue:70
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:194
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:194
-#: src/views/config/components/Mkdir.vue:38
+#: src/lib/http/index.ts:128 src/views/config/components/Mkdir.vue:38
 #: src/views/config/components/Rename.vue:45
 #: src/views/config/components/Rename.vue:45
 #: src/views/config/ConfigEditor.vue:111
 #: src/views/config/ConfigEditor.vue:111
 #: src/views/environment/BatchUpgrader.vue:59
 #: src/views/environment/BatchUpgrader.vue:59
@@ -2290,6 +2330,11 @@ msgstr ""
 msgid "Site Logs"
 msgid "Site Logs"
 msgstr "Journaux du site"
 msgstr "Journaux du site"
 
 
+#: src/views/system/SelfCheck/tasks.ts:3
+#, fuzzy
+msgid "Sites Directory"
+msgstr "Directive"
+
 #: src/routes/index.ts:56
 #: src/routes/index.ts:56
 msgid "Sites List"
 msgid "Sites List"
 msgstr "Liste des sites"
 msgstr "Liste des sites"
@@ -2340,10 +2385,23 @@ msgstr "Arrêté"
 msgid "Storage"
 msgid "Storage"
 msgstr "Stockage"
 msgstr "Stockage"
 
 
+#: src/views/system/SelfCheck/tasks.ts:7
+#, fuzzy
+msgid "Streams Directory"
+msgstr "Directive"
+
 #: src/constants/index.ts:19 src/views/notification/notificationColumns.tsx:36
 #: src/constants/index.ts:19 src/views/notification/notificationColumns.tsx:36
 msgid "Success"
 msgid "Success"
 msgstr ""
 msgstr ""
 
 
+#: src/views/system/SelfCheck/SelfCheck.vue:78
+msgid ""
+"Support communication with the backend through the WebSocket protocol. If "
+"your Nginx UI is being used via an Nginx reverse proxy, please refer to this "
+"link to write the corresponding configuration file: https://nginxui.com/"
+"guide/nginx-proxy-example.html"
+msgstr ""
+
 #: src/views/dashboard/ServerAnalytic.vue:240
 #: src/views/dashboard/ServerAnalytic.vue:240
 #: src/views/dashboard/ServerAnalytic.vue:241
 #: src/views/dashboard/ServerAnalytic.vue:241
 #, fuzzy
 #, fuzzy
@@ -2693,7 +2751,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:290 src/views/environment/Environment.vue:55
+#: src/routes/index.ts:297 src/views/environment/Environment.vue:55
 #: src/views/system/Upgrade.vue:145 src/views/system/Upgrade.vue:228
 #: src/views/system/Upgrade.vue:145 src/views/system/Upgrade.vue:228
 msgid "Upgrade"
 msgid "Upgrade"
 msgstr "Mettre à niveau"
 msgstr "Mettre à niveau"

+ 65 - 6
app/src/language/ko_KR/app.po

@@ -22,7 +22,7 @@ msgstr "2FA"
 msgid "2FA Settings"
 msgid "2FA Settings"
 msgstr "2FA 설정"
 msgstr "2FA 설정"
 
 
-#: src/routes/index.ts:283
+#: src/routes/index.ts:290
 msgid "About"
 msgid "About"
 msgstr "대하여"
 msgstr "대하여"
 
 
@@ -201,6 +201,11 @@ msgstr "ChatGPT에게 도움 요청"
 msgid "Assistant"
 msgid "Assistant"
 msgstr "조수"
 msgstr "조수"
 
 
+#: src/views/system/SelfCheck/SelfCheck.vue:54
+#, fuzzy
+msgid "Attempt to fix"
+msgstr "시도 횟수"
+
 #: src/views/preference/AuthSettings.vue:17
 #: src/views/preference/AuthSettings.vue:17
 msgid "Attempts"
 msgid "Attempts"
 msgstr "시도 횟수"
 msgstr "시도 횟수"
@@ -386,6 +391,26 @@ msgstr "채널"
 msgid "Check again"
 msgid "Check again"
 msgstr "다시 확인"
 msgstr "다시 확인"
 
 
+#: src/views/system/SelfCheck/tasks.ts:12
+msgid "Check if the nginx.conf includes the sites-enabled directory."
+msgstr ""
+
+#: src/views/system/SelfCheck/tasks.ts:16
+msgid "Check if the nginx.conf includes the streams-enabled directory."
+msgstr ""
+
+#: src/views/system/SelfCheck/tasks.ts:4
+msgid ""
+"Check if the sites-available and sites-enabled directories are under the "
+"nginx configuration directory."
+msgstr ""
+
+#: src/views/system/SelfCheck/tasks.ts:8
+msgid ""
+"Check if the streams-available and streams-enabled directories are under the "
+"nginx configuration directory."
+msgstr ""
+
 #: src/language/constants.ts:13
 #: src/language/constants.ts:13
 msgid "Cleaning environment variables"
 msgid "Cleaning environment variables"
 msgstr "환경 변수 정리"
 msgstr "환경 변수 정리"
@@ -1140,7 +1165,7 @@ msgstr ""
 msgid "Input the recovery code:"
 msgid "Input the recovery code:"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:305 src/views/other/Install.vue:136
+#: src/routes/index.ts:312 src/views/other/Install.vue:136
 msgid "Install"
 msgid "Install"
 msgstr "설치"
 msgstr "설치"
 
 
@@ -1292,7 +1317,7 @@ msgstr "위치들"
 msgid "Log"
 msgid "Log"
 msgstr "로그인"
 msgstr "로그인"
 
 
-#: src/routes/index.ts:311 src/views/other/Login.vue:246
+#: src/routes/index.ts:318 src/views/other/Login.vue:246
 msgid "Login"
 msgid "Login"
 msgstr "로그인"
 msgstr "로그인"
 
 
@@ -1466,6 +1491,14 @@ msgstr "Nginx"
 msgid "Nginx Access Log Path"
 msgid "Nginx Access Log Path"
 msgstr "Nginx 접근 로그 경로"
 msgstr "Nginx 접근 로그 경로"
 
 
+#: src/views/system/SelfCheck/tasks.ts:11
+msgid "Nginx Conf Include Sites Enabled"
+msgstr ""
+
+#: src/views/system/SelfCheck/tasks.ts:15
+msgid "Nginx Conf Include Streams Enabled"
+msgstr ""
+
 #: src/views/site/site_edit/SiteEdit.vue:223
 #: src/views/site/site_edit/SiteEdit.vue:223
 #: src/views/stream/StreamEdit.vue:207
 #: src/views/stream/StreamEdit.vue:207
 #, fuzzy
 #, fuzzy
@@ -1551,7 +1584,7 @@ msgstr "노드 시크릿"
 msgid "Not After"
 msgid "Not After"
 msgstr "만료일"
 msgstr "만료일"
 
 
-#: src/routes/index.ts:317
+#: src/routes/index.ts:324
 msgid "Not Found"
 msgid "Not Found"
 msgstr "찾을 수 없음"
 msgstr "찾을 수 없음"
 
 
@@ -1855,6 +1888,10 @@ msgstr "읽기"
 msgid "Receive"
 msgid "Receive"
 msgstr "수신"
 msgstr "수신"
 
 
+#: src/views/system/SelfCheck/SelfCheck.vue:47
+msgid "Recheck"
+msgstr ""
+
 #: src/components/StdDesign/StdDataDisplay/StdBulkActions.vue:43
 #: src/components/StdDesign/StdDataDisplay/StdBulkActions.vue:43
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:544
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:544
 msgid "Recover"
 msgid "Recover"
@@ -2174,6 +2211,10 @@ msgstr ""
 msgid "Selector"
 msgid "Selector"
 msgstr "선택"
 msgstr "선택"
 
 
+#: src/routes/index.ts:283 src/views/system/SelfCheck/SelfCheck.vue:42
+msgid "Self Check"
+msgstr ""
+
 #: src/views/dashboard/ServerAnalytic.vue:348
 #: src/views/dashboard/ServerAnalytic.vue:348
 #: src/views/dashboard/ServerAnalytic.vue:35
 #: src/views/dashboard/ServerAnalytic.vue:35
 msgid "Send"
 msgid "Send"
@@ -2186,7 +2227,7 @@ msgstr "보내기"
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:53
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:53
 #: src/components/StdDesign/StdDataDisplay/StdBulkActions.vue:70
 #: src/components/StdDesign/StdDataDisplay/StdBulkActions.vue:70
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:194
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:194
-#: src/views/config/components/Mkdir.vue:38
+#: src/lib/http/index.ts:128 src/views/config/components/Mkdir.vue:38
 #: src/views/config/components/Rename.vue:45
 #: src/views/config/components/Rename.vue:45
 #: src/views/config/ConfigEditor.vue:111
 #: src/views/config/ConfigEditor.vue:111
 #: src/views/environment/BatchUpgrader.vue:59
 #: src/views/environment/BatchUpgrader.vue:59
@@ -2255,6 +2296,11 @@ msgstr ""
 msgid "Site Logs"
 msgid "Site Logs"
 msgstr "사이트 로그"
 msgstr "사이트 로그"
 
 
+#: src/views/system/SelfCheck/tasks.ts:3
+#, fuzzy
+msgid "Sites Directory"
+msgstr "디렉토리"
+
 #: src/routes/index.ts:56
 #: src/routes/index.ts:56
 msgid "Sites List"
 msgid "Sites List"
 msgstr "사이트 목록"
 msgstr "사이트 목록"
@@ -2306,10 +2352,23 @@ msgstr "정지됨"
 msgid "Storage"
 msgid "Storage"
 msgstr "저장소"
 msgstr "저장소"
 
 
+#: src/views/system/SelfCheck/tasks.ts:7
+#, fuzzy
+msgid "Streams Directory"
+msgstr "디렉토리"
+
 #: src/constants/index.ts:19 src/views/notification/notificationColumns.tsx:36
 #: src/constants/index.ts:19 src/views/notification/notificationColumns.tsx:36
 msgid "Success"
 msgid "Success"
 msgstr "성공"
 msgstr "성공"
 
 
+#: src/views/system/SelfCheck/SelfCheck.vue:78
+msgid ""
+"Support communication with the backend through the WebSocket protocol. If "
+"your Nginx UI is being used via an Nginx reverse proxy, please refer to this "
+"link to write the corresponding configuration file: https://nginxui.com/"
+"guide/nginx-proxy-example.html"
+msgstr ""
+
 #: src/views/dashboard/ServerAnalytic.vue:240
 #: src/views/dashboard/ServerAnalytic.vue:240
 #: src/views/dashboard/ServerAnalytic.vue:241
 #: src/views/dashboard/ServerAnalytic.vue:241
 msgid "Swap"
 msgid "Swap"
@@ -2658,7 +2717,7 @@ msgstr "업데이트됨"
 msgid "Updated successfully"
 msgid "Updated successfully"
 msgstr "성공적으로 저장되었습니다"
 msgstr "성공적으로 저장되었습니다"
 
 
-#: src/routes/index.ts:290 src/views/environment/Environment.vue:55
+#: src/routes/index.ts:297 src/views/environment/Environment.vue:55
 #: src/views/system/Upgrade.vue:145 src/views/system/Upgrade.vue:228
 #: src/views/system/Upgrade.vue:145 src/views/system/Upgrade.vue:228
 msgid "Upgrade"
 msgid "Upgrade"
 msgstr "업그레이드"
 msgstr "업그레이드"

+ 55 - 5
app/src/language/messages.pot

@@ -10,7 +10,7 @@ msgstr ""
 msgid "2FA Settings"
 msgid "2FA Settings"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:283
+#: src/routes/index.ts:290
 msgid "About"
 msgid "About"
 msgstr ""
 msgstr ""
 
 
@@ -190,6 +190,10 @@ msgstr ""
 msgid "Assistant"
 msgid "Assistant"
 msgstr ""
 msgstr ""
 
 
+#: src/views/system/SelfCheck/SelfCheck.vue:54
+msgid "Attempt to fix"
+msgstr ""
+
 #: src/views/preference/AuthSettings.vue:17
 #: src/views/preference/AuthSettings.vue:17
 msgid "Attempts"
 msgid "Attempts"
 msgstr ""
 msgstr ""
@@ -374,6 +378,22 @@ msgstr ""
 msgid "Check again"
 msgid "Check again"
 msgstr ""
 msgstr ""
 
 
+#: src/views/system/SelfCheck/tasks.ts:12
+msgid "Check if the nginx.conf includes the sites-enabled directory."
+msgstr ""
+
+#: src/views/system/SelfCheck/tasks.ts:16
+msgid "Check if the nginx.conf includes the streams-enabled directory."
+msgstr ""
+
+#: src/views/system/SelfCheck/tasks.ts:4
+msgid "Check if the sites-available and sites-enabled directories are under the nginx configuration directory."
+msgstr ""
+
+#: src/views/system/SelfCheck/tasks.ts:8
+msgid "Check if the streams-available and streams-enabled directories are under the nginx configuration directory."
+msgstr ""
+
 #: src/language/constants.ts:13
 #: src/language/constants.ts:13
 msgid "Cleaning environment variables"
 msgid "Cleaning environment variables"
 msgstr ""
 msgstr ""
@@ -1105,7 +1125,7 @@ msgstr ""
 msgid "Input the recovery code:"
 msgid "Input the recovery code:"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:305
+#: src/routes/index.ts:312
 #: src/views/other/Install.vue:136
 #: src/views/other/Install.vue:136
 msgid "Install"
 msgid "Install"
 msgstr ""
 msgstr ""
@@ -1243,7 +1263,7 @@ msgstr ""
 msgid "Log"
 msgid "Log"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:311
+#: src/routes/index.ts:318
 #: src/views/other/Login.vue:246
 #: src/views/other/Login.vue:246
 msgid "Login"
 msgid "Login"
 msgstr ""
 msgstr ""
@@ -1403,6 +1423,14 @@ msgstr ""
 msgid "Nginx Access Log Path"
 msgid "Nginx Access Log Path"
 msgstr ""
 msgstr ""
 
 
+#: src/views/system/SelfCheck/tasks.ts:11
+msgid "Nginx Conf Include Sites Enabled"
+msgstr ""
+
+#: src/views/system/SelfCheck/tasks.ts:15
+msgid "Nginx Conf Include Streams Enabled"
+msgstr ""
+
 #: src/views/site/site_edit/SiteEdit.vue:223
 #: src/views/site/site_edit/SiteEdit.vue:223
 #: src/views/stream/StreamEdit.vue:207
 #: src/views/stream/StreamEdit.vue:207
 msgid "Nginx Configuration Parse Error"
 msgid "Nginx Configuration Parse Error"
@@ -1482,7 +1510,7 @@ msgstr ""
 msgid "Not After"
 msgid "Not After"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:317
+#: src/routes/index.ts:324
 msgid "Not Found"
 msgid "Not Found"
 msgstr ""
 msgstr ""
 
 
@@ -1762,6 +1790,10 @@ msgstr ""
 msgid "Receive"
 msgid "Receive"
 msgstr ""
 msgstr ""
 
 
+#: src/views/system/SelfCheck/SelfCheck.vue:47
+msgid "Recheck"
+msgstr ""
+
 #: src/components/StdDesign/StdDataDisplay/StdBulkActions.vue:43
 #: src/components/StdDesign/StdDataDisplay/StdBulkActions.vue:43
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:544
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:544
 msgid "Recover"
 msgid "Recover"
@@ -2045,6 +2077,11 @@ msgstr ""
 msgid "Selector"
 msgid "Selector"
 msgstr ""
 msgstr ""
 
 
+#: src/routes/index.ts:283
+#: src/views/system/SelfCheck/SelfCheck.vue:42
+msgid "Self Check"
+msgstr ""
+
 #: src/views/dashboard/ServerAnalytic.vue:348
 #: src/views/dashboard/ServerAnalytic.vue:348
 #: src/views/dashboard/ServerAnalytic.vue:35
 #: src/views/dashboard/ServerAnalytic.vue:35
 msgid "Send"
 msgid "Send"
@@ -2057,6 +2094,7 @@ msgstr ""
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:53
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:53
 #: src/components/StdDesign/StdDataDisplay/StdBulkActions.vue:70
 #: src/components/StdDesign/StdDataDisplay/StdBulkActions.vue:70
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:194
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:194
+#: src/lib/http/index.ts:128
 #: src/views/config/components/Mkdir.vue:38
 #: src/views/config/components/Mkdir.vue:38
 #: src/views/config/components/Rename.vue:45
 #: src/views/config/components/Rename.vue:45
 #: src/views/config/ConfigEditor.vue:111
 #: src/views/config/ConfigEditor.vue:111
@@ -2127,6 +2165,10 @@ msgstr ""
 msgid "Site Logs"
 msgid "Site Logs"
 msgstr ""
 msgstr ""
 
 
+#: src/views/system/SelfCheck/tasks.ts:3
+msgid "Sites Directory"
+msgstr ""
+
 #: src/routes/index.ts:56
 #: src/routes/index.ts:56
 msgid "Sites List"
 msgid "Sites List"
 msgstr ""
 msgstr ""
@@ -2175,11 +2217,19 @@ msgstr ""
 msgid "Storage"
 msgid "Storage"
 msgstr ""
 msgstr ""
 
 
+#: src/views/system/SelfCheck/tasks.ts:7
+msgid "Streams Directory"
+msgstr ""
+
 #: src/constants/index.ts:19
 #: src/constants/index.ts:19
 #: src/views/notification/notificationColumns.tsx:36
 #: src/views/notification/notificationColumns.tsx:36
 msgid "Success"
 msgid "Success"
 msgstr ""
 msgstr ""
 
 
+#: src/views/system/SelfCheck/SelfCheck.vue:78
+msgid "Support communication with the backend through the WebSocket protocol. If your Nginx UI is being used via an Nginx reverse proxy, please refer to this link to write the corresponding configuration file: https://nginxui.com/guide/nginx-proxy-example.html"
+msgstr ""
+
 #: src/views/dashboard/ServerAnalytic.vue:240
 #: src/views/dashboard/ServerAnalytic.vue:240
 #: src/views/dashboard/ServerAnalytic.vue:241
 #: src/views/dashboard/ServerAnalytic.vue:241
 msgid "Swap"
 msgid "Swap"
@@ -2468,7 +2518,7 @@ msgstr ""
 msgid "Updated successfully"
 msgid "Updated successfully"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:290
+#: src/routes/index.ts:297
 #: src/views/environment/Environment.vue:55
 #: src/views/environment/Environment.vue:55
 #: src/views/system/Upgrade.vue:145
 #: src/views/system/Upgrade.vue:145
 #: src/views/system/Upgrade.vue:228
 #: src/views/system/Upgrade.vue:228

+ 65 - 6
app/src/language/ru_RU/app.po

@@ -23,7 +23,7 @@ msgstr "2FA"
 msgid "2FA Settings"
 msgid "2FA Settings"
 msgstr "Настройки 2FA"
 msgstr "Настройки 2FA"
 
 
-#: src/routes/index.ts:283
+#: src/routes/index.ts:290
 msgid "About"
 msgid "About"
 msgstr "О проекте"
 msgstr "О проекте"
 
 
@@ -203,6 +203,11 @@ msgstr "Обратитесь за помощью к ChatGPT"
 msgid "Assistant"
 msgid "Assistant"
 msgstr "Ассистент"
 msgstr "Ассистент"
 
 
+#: src/views/system/SelfCheck/SelfCheck.vue:54
+#, fuzzy
+msgid "Attempt to fix"
+msgstr "Попытки"
+
 #: src/views/preference/AuthSettings.vue:17
 #: src/views/preference/AuthSettings.vue:17
 msgid "Attempts"
 msgid "Attempts"
 msgstr "Попытки"
 msgstr "Попытки"
@@ -387,6 +392,26 @@ msgstr "Канал"
 msgid "Check again"
 msgid "Check again"
 msgstr "Проверить повторно"
 msgstr "Проверить повторно"
 
 
+#: src/views/system/SelfCheck/tasks.ts:12
+msgid "Check if the nginx.conf includes the sites-enabled directory."
+msgstr ""
+
+#: src/views/system/SelfCheck/tasks.ts:16
+msgid "Check if the nginx.conf includes the streams-enabled directory."
+msgstr ""
+
+#: src/views/system/SelfCheck/tasks.ts:4
+msgid ""
+"Check if the sites-available and sites-enabled directories are under the "
+"nginx configuration directory."
+msgstr ""
+
+#: src/views/system/SelfCheck/tasks.ts:8
+msgid ""
+"Check if the streams-available and streams-enabled directories are under the "
+"nginx configuration directory."
+msgstr ""
+
 #: src/language/constants.ts:13
 #: src/language/constants.ts:13
 msgid "Cleaning environment variables"
 msgid "Cleaning environment variables"
 msgstr "Очистка переменных среды"
 msgstr "Очистка переменных среды"
@@ -1146,7 +1171,7 @@ msgstr "Введите код из приложения:"
 msgid "Input the recovery code:"
 msgid "Input the recovery code:"
 msgstr "Введите код восстановления:"
 msgstr "Введите код восстановления:"
 
 
-#: src/routes/index.ts:305 src/views/other/Install.vue:136
+#: src/routes/index.ts:312 src/views/other/Install.vue:136
 msgid "Install"
 msgid "Install"
 msgstr "Установить"
 msgstr "Установить"
 
 
@@ -1285,7 +1310,7 @@ msgstr "Локации"
 msgid "Log"
 msgid "Log"
 msgstr "Журнал"
 msgstr "Журнал"
 
 
-#: src/routes/index.ts:311 src/views/other/Login.vue:246
+#: src/routes/index.ts:318 src/views/other/Login.vue:246
 msgid "Login"
 msgid "Login"
 msgstr "Логин"
 msgstr "Логин"
 
 
@@ -1450,6 +1475,14 @@ msgstr "Nginx"
 msgid "Nginx Access Log Path"
 msgid "Nginx Access Log Path"
 msgstr "Путь для Nginx Access Log"
 msgstr "Путь для Nginx Access Log"
 
 
+#: src/views/system/SelfCheck/tasks.ts:11
+msgid "Nginx Conf Include Sites Enabled"
+msgstr ""
+
+#: src/views/system/SelfCheck/tasks.ts:15
+msgid "Nginx Conf Include Streams Enabled"
+msgstr ""
+
 #: src/views/site/site_edit/SiteEdit.vue:223
 #: src/views/site/site_edit/SiteEdit.vue:223
 #: src/views/stream/StreamEdit.vue:207
 #: src/views/stream/StreamEdit.vue:207
 msgid "Nginx Configuration Parse Error"
 msgid "Nginx Configuration Parse Error"
@@ -1532,7 +1565,7 @@ msgstr ""
 msgid "Not After"
 msgid "Not After"
 msgstr "Не позднее"
 msgstr "Не позднее"
 
 
-#: src/routes/index.ts:317
+#: src/routes/index.ts:324
 msgid "Not Found"
 msgid "Not Found"
 msgstr "Не найден"
 msgstr "Не найден"
 
 
@@ -1840,6 +1873,10 @@ msgstr "Чтение"
 msgid "Receive"
 msgid "Receive"
 msgstr "Принято"
 msgstr "Принято"
 
 
+#: src/views/system/SelfCheck/SelfCheck.vue:47
+msgid "Recheck"
+msgstr ""
+
 #: src/components/StdDesign/StdDataDisplay/StdBulkActions.vue:43
 #: src/components/StdDesign/StdDataDisplay/StdBulkActions.vue:43
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:544
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:544
 msgid "Recover"
 msgid "Recover"
@@ -2144,6 +2181,10 @@ msgstr ""
 msgid "Selector"
 msgid "Selector"
 msgstr "Выбор"
 msgstr "Выбор"
 
 
+#: src/routes/index.ts:283 src/views/system/SelfCheck/SelfCheck.vue:42
+msgid "Self Check"
+msgstr ""
+
 #: src/views/dashboard/ServerAnalytic.vue:348
 #: src/views/dashboard/ServerAnalytic.vue:348
 #: src/views/dashboard/ServerAnalytic.vue:35
 #: src/views/dashboard/ServerAnalytic.vue:35
 msgid "Send"
 msgid "Send"
@@ -2156,7 +2197,7 @@ msgstr "Отправлено"
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:53
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:53
 #: src/components/StdDesign/StdDataDisplay/StdBulkActions.vue:70
 #: src/components/StdDesign/StdDataDisplay/StdBulkActions.vue:70
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:194
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:194
-#: src/views/config/components/Mkdir.vue:38
+#: src/lib/http/index.ts:128 src/views/config/components/Mkdir.vue:38
 #: src/views/config/components/Rename.vue:45
 #: src/views/config/components/Rename.vue:45
 #: src/views/config/ConfigEditor.vue:111
 #: src/views/config/ConfigEditor.vue:111
 #: src/views/environment/BatchUpgrader.vue:59
 #: src/views/environment/BatchUpgrader.vue:59
@@ -2226,6 +2267,11 @@ msgstr ""
 msgid "Site Logs"
 msgid "Site Logs"
 msgstr "Журналы сайта"
 msgstr "Журналы сайта"
 
 
+#: src/views/system/SelfCheck/tasks.ts:3
+#, fuzzy
+msgid "Sites Directory"
+msgstr "Каталог"
+
 #: src/routes/index.ts:56
 #: src/routes/index.ts:56
 msgid "Sites List"
 msgid "Sites List"
 msgstr "Список сайтов"
 msgstr "Список сайтов"
@@ -2272,10 +2318,23 @@ msgstr "Остановлен"
 msgid "Storage"
 msgid "Storage"
 msgstr "Хранилище"
 msgstr "Хранилище"
 
 
+#: src/views/system/SelfCheck/tasks.ts:7
+#, fuzzy
+msgid "Streams Directory"
+msgstr "Каталог"
+
 #: src/constants/index.ts:19 src/views/notification/notificationColumns.tsx:36
 #: src/constants/index.ts:19 src/views/notification/notificationColumns.tsx:36
 msgid "Success"
 msgid "Success"
 msgstr "Успех"
 msgstr "Успех"
 
 
+#: src/views/system/SelfCheck/SelfCheck.vue:78
+msgid ""
+"Support communication with the backend through the WebSocket protocol. If "
+"your Nginx UI is being used via an Nginx reverse proxy, please refer to this "
+"link to write the corresponding configuration file: https://nginxui.com/"
+"guide/nginx-proxy-example.html"
+msgstr ""
+
 #: src/views/dashboard/ServerAnalytic.vue:240
 #: src/views/dashboard/ServerAnalytic.vue:240
 #: src/views/dashboard/ServerAnalytic.vue:241
 #: src/views/dashboard/ServerAnalytic.vue:241
 msgid "Swap"
 msgid "Swap"
@@ -2642,7 +2701,7 @@ msgstr "Обновлено в"
 msgid "Updated successfully"
 msgid "Updated successfully"
 msgstr "Успешно обновлено"
 msgstr "Успешно обновлено"
 
 
-#: src/routes/index.ts:290 src/views/environment/Environment.vue:55
+#: src/routes/index.ts:297 src/views/environment/Environment.vue:55
 #: src/views/system/Upgrade.vue:145 src/views/system/Upgrade.vue:228
 #: src/views/system/Upgrade.vue:145 src/views/system/Upgrade.vue:228
 msgid "Upgrade"
 msgid "Upgrade"
 msgstr "Обновление"
 msgstr "Обновление"

+ 65 - 6
app/src/language/tr_TR/app.po

@@ -20,7 +20,7 @@ msgstr "İki aşamalı kimlik doğrulaması(2FA)"
 msgid "2FA Settings"
 msgid "2FA Settings"
 msgstr "2FA Ayarları"
 msgstr "2FA Ayarları"
 
 
-#: src/routes/index.ts:283
+#: src/routes/index.ts:290
 msgid "About"
 msgid "About"
 msgstr "Hakkında"
 msgstr "Hakkında"
 
 
@@ -199,6 +199,11 @@ msgstr "ChatGPT'den Yardım İsteyin"
 msgid "Assistant"
 msgid "Assistant"
 msgstr "Asistan"
 msgstr "Asistan"
 
 
+#: src/views/system/SelfCheck/SelfCheck.vue:54
+#, fuzzy
+msgid "Attempt to fix"
+msgstr "Girişimler"
+
 #: src/views/preference/AuthSettings.vue:17
 #: src/views/preference/AuthSettings.vue:17
 msgid "Attempts"
 msgid "Attempts"
 msgstr "Girişimler"
 msgstr "Girişimler"
@@ -381,6 +386,26 @@ msgstr "Kanal"
 msgid "Check again"
 msgid "Check again"
 msgstr "Tekrar kontrol et"
 msgstr "Tekrar kontrol et"
 
 
+#: src/views/system/SelfCheck/tasks.ts:12
+msgid "Check if the nginx.conf includes the sites-enabled directory."
+msgstr ""
+
+#: src/views/system/SelfCheck/tasks.ts:16
+msgid "Check if the nginx.conf includes the streams-enabled directory."
+msgstr ""
+
+#: src/views/system/SelfCheck/tasks.ts:4
+msgid ""
+"Check if the sites-available and sites-enabled directories are under the "
+"nginx configuration directory."
+msgstr ""
+
+#: src/views/system/SelfCheck/tasks.ts:8
+msgid ""
+"Check if the streams-available and streams-enabled directories are under the "
+"nginx configuration directory."
+msgstr ""
+
 #: src/language/constants.ts:13
 #: src/language/constants.ts:13
 msgid "Cleaning environment variables"
 msgid "Cleaning environment variables"
 msgstr "Ortam değişkenlerini temizleme"
 msgstr "Ortam değişkenlerini temizleme"
@@ -1148,7 +1173,7 @@ msgstr "Uygulamadan kodu girin:"
 msgid "Input the recovery code:"
 msgid "Input the recovery code:"
 msgstr "Kurtarma kodunu girin:"
 msgstr "Kurtarma kodunu girin:"
 
 
-#: src/routes/index.ts:305 src/views/other/Install.vue:136
+#: src/routes/index.ts:312 src/views/other/Install.vue:136
 msgid "Install"
 msgid "Install"
 msgstr "Yükle"
 msgstr "Yükle"
 
 
@@ -1286,7 +1311,7 @@ msgstr "Konumlar"
 msgid "Log"
 msgid "Log"
 msgstr "Günlük"
 msgstr "Günlük"
 
 
-#: src/routes/index.ts:311 src/views/other/Login.vue:246
+#: src/routes/index.ts:318 src/views/other/Login.vue:246
 msgid "Login"
 msgid "Login"
 msgstr "Giriş"
 msgstr "Giriş"
 
 
@@ -1478,6 +1503,14 @@ msgstr "Nginx"
 msgid "Nginx Access Log Path"
 msgid "Nginx Access Log Path"
 msgstr "Nginx Erişim Günlüğü Yolu"
 msgstr "Nginx Erişim Günlüğü Yolu"
 
 
+#: src/views/system/SelfCheck/tasks.ts:11
+msgid "Nginx Conf Include Sites Enabled"
+msgstr ""
+
+#: src/views/system/SelfCheck/tasks.ts:15
+msgid "Nginx Conf Include Streams Enabled"
+msgstr ""
+
 #: src/views/site/site_edit/SiteEdit.vue:223
 #: src/views/site/site_edit/SiteEdit.vue:223
 #: src/views/stream/StreamEdit.vue:207
 #: src/views/stream/StreamEdit.vue:207
 #, fuzzy
 #, fuzzy
@@ -1570,7 +1603,7 @@ msgstr "Düğüm Sırrı"
 msgid "Not After"
 msgid "Not After"
 msgstr "Sonra değil"
 msgstr "Sonra değil"
 
 
-#: src/routes/index.ts:317
+#: src/routes/index.ts:324
 #, fuzzy
 #, fuzzy
 msgid "Not Found"
 msgid "Not Found"
 msgstr "Bulunamadı"
 msgstr "Bulunamadı"
@@ -1935,6 +1968,10 @@ msgstr "Okumalar"
 msgid "Receive"
 msgid "Receive"
 msgstr "Teslim almak"
 msgstr "Teslim almak"
 
 
+#: src/views/system/SelfCheck/SelfCheck.vue:47
+msgid "Recheck"
+msgstr ""
+
 #: src/components/StdDesign/StdDataDisplay/StdBulkActions.vue:43
 #: src/components/StdDesign/StdDataDisplay/StdBulkActions.vue:43
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:544
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:544
 #, fuzzy
 #, fuzzy
@@ -2289,6 +2326,10 @@ msgstr "Sır kopyalandı"
 msgid "Selector"
 msgid "Selector"
 msgstr "Selektör"
 msgstr "Selektör"
 
 
+#: src/routes/index.ts:283 src/views/system/SelfCheck/SelfCheck.vue:42
+msgid "Self Check"
+msgstr ""
+
 #: src/views/dashboard/ServerAnalytic.vue:348
 #: src/views/dashboard/ServerAnalytic.vue:348
 #: src/views/dashboard/ServerAnalytic.vue:35
 #: src/views/dashboard/ServerAnalytic.vue:35
 #, fuzzy
 #, fuzzy
@@ -2302,7 +2343,7 @@ msgstr "Gönder"
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:53
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:53
 #: src/components/StdDesign/StdDataDisplay/StdBulkActions.vue:70
 #: src/components/StdDesign/StdDataDisplay/StdBulkActions.vue:70
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:194
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:194
-#: src/views/config/components/Mkdir.vue:38
+#: src/lib/http/index.ts:128 src/views/config/components/Mkdir.vue:38
 #: src/views/config/components/Rename.vue:45
 #: src/views/config/components/Rename.vue:45
 #: src/views/config/ConfigEditor.vue:111
 #: src/views/config/ConfigEditor.vue:111
 #: src/views/environment/BatchUpgrader.vue:59
 #: src/views/environment/BatchUpgrader.vue:59
@@ -2384,6 +2425,11 @@ msgstr ""
 msgid "Site Logs"
 msgid "Site Logs"
 msgstr "Site Günlükleri"
 msgstr "Site Günlükleri"
 
 
+#: src/views/system/SelfCheck/tasks.ts:3
+#, fuzzy
+msgid "Sites Directory"
+msgstr "Dizin"
+
 #: src/routes/index.ts:56
 #: src/routes/index.ts:56
 #, fuzzy
 #, fuzzy
 msgid "Sites List"
 msgid "Sites List"
@@ -2440,11 +2486,24 @@ msgstr "Durduruldu"
 msgid "Storage"
 msgid "Storage"
 msgstr "Depolama"
 msgstr "Depolama"
 
 
+#: src/views/system/SelfCheck/tasks.ts:7
+#, fuzzy
+msgid "Streams Directory"
+msgstr "Dizin"
+
 #: src/constants/index.ts:19 src/views/notification/notificationColumns.tsx:36
 #: src/constants/index.ts:19 src/views/notification/notificationColumns.tsx:36
 #, fuzzy
 #, fuzzy
 msgid "Success"
 msgid "Success"
 msgstr "Başarılı"
 msgstr "Başarılı"
 
 
+#: src/views/system/SelfCheck/SelfCheck.vue:78
+msgid ""
+"Support communication with the backend through the WebSocket protocol. If "
+"your Nginx UI is being used via an Nginx reverse proxy, please refer to this "
+"link to write the corresponding configuration file: https://nginxui.com/"
+"guide/nginx-proxy-example.html"
+msgstr ""
+
 #: src/views/dashboard/ServerAnalytic.vue:240
 #: src/views/dashboard/ServerAnalytic.vue:240
 #: src/views/dashboard/ServerAnalytic.vue:241
 #: src/views/dashboard/ServerAnalytic.vue:241
 #, fuzzy
 #, fuzzy
@@ -2867,7 +2926,7 @@ msgstr "Güncelleme"
 msgid "Updated successfully"
 msgid "Updated successfully"
 msgstr "Başarıyla güncellendi"
 msgstr "Başarıyla güncellendi"
 
 
-#: src/routes/index.ts:290 src/views/environment/Environment.vue:55
+#: src/routes/index.ts:297 src/views/environment/Environment.vue:55
 #: src/views/system/Upgrade.vue:145 src/views/system/Upgrade.vue:228
 #: src/views/system/Upgrade.vue:145 src/views/system/Upgrade.vue:228
 #, fuzzy
 #, fuzzy
 msgid "Upgrade"
 msgid "Upgrade"

+ 64 - 6
app/src/language/vi_VN/app.po

@@ -17,7 +17,7 @@ msgstr ""
 msgid "2FA Settings"
 msgid "2FA Settings"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:283
+#: src/routes/index.ts:290
 msgid "About"
 msgid "About"
 msgstr "Tác giả"
 msgstr "Tác giả"
 
 
@@ -210,6 +210,10 @@ msgstr "Hỏi ChatGPT"
 msgid "Assistant"
 msgid "Assistant"
 msgstr "Trợ lý"
 msgstr "Trợ lý"
 
 
+#: src/views/system/SelfCheck/SelfCheck.vue:54
+msgid "Attempt to fix"
+msgstr ""
+
 #: src/views/preference/AuthSettings.vue:17
 #: src/views/preference/AuthSettings.vue:17
 msgid "Attempts"
 msgid "Attempts"
 msgstr ""
 msgstr ""
@@ -405,6 +409,26 @@ msgstr "Kênh"
 msgid "Check again"
 msgid "Check again"
 msgstr "Kiểm tra lại"
 msgstr "Kiểm tra lại"
 
 
+#: src/views/system/SelfCheck/tasks.ts:12
+msgid "Check if the nginx.conf includes the sites-enabled directory."
+msgstr ""
+
+#: src/views/system/SelfCheck/tasks.ts:16
+msgid "Check if the nginx.conf includes the streams-enabled directory."
+msgstr ""
+
+#: src/views/system/SelfCheck/tasks.ts:4
+msgid ""
+"Check if the sites-available and sites-enabled directories are under the "
+"nginx configuration directory."
+msgstr ""
+
+#: src/views/system/SelfCheck/tasks.ts:8
+msgid ""
+"Check if the streams-available and streams-enabled directories are under the "
+"nginx configuration directory."
+msgstr ""
+
 #: src/language/constants.ts:13
 #: src/language/constants.ts:13
 msgid "Cleaning environment variables"
 msgid "Cleaning environment variables"
 msgstr "Xoá các biến môi trường"
 msgstr "Xoá các biến môi trường"
@@ -1186,7 +1210,7 @@ msgstr ""
 msgid "Input the recovery code:"
 msgid "Input the recovery code:"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:305 src/views/other/Install.vue:136
+#: src/routes/index.ts:312 src/views/other/Install.vue:136
 msgid "Install"
 msgid "Install"
 msgstr "Cài đặt"
 msgstr "Cài đặt"
 
 
@@ -1339,7 +1363,7 @@ msgstr "Locations"
 msgid "Log"
 msgid "Log"
 msgstr "Log"
 msgstr "Log"
 
 
-#: src/routes/index.ts:311 src/views/other/Login.vue:246
+#: src/routes/index.ts:318 src/views/other/Login.vue:246
 msgid "Login"
 msgid "Login"
 msgstr "Đăng nhập"
 msgstr "Đăng nhập"
 
 
@@ -1507,6 +1531,14 @@ msgstr ""
 msgid "Nginx Access Log Path"
 msgid "Nginx Access Log Path"
 msgstr "Vị trí lưu log truy cập (Access log) của Nginx"
 msgstr "Vị trí lưu log truy cập (Access log) của Nginx"
 
 
+#: src/views/system/SelfCheck/tasks.ts:11
+msgid "Nginx Conf Include Sites Enabled"
+msgstr ""
+
+#: src/views/system/SelfCheck/tasks.ts:15
+msgid "Nginx Conf Include Streams Enabled"
+msgstr ""
+
 #: src/views/site/site_edit/SiteEdit.vue:223
 #: src/views/site/site_edit/SiteEdit.vue:223
 #: src/views/stream/StreamEdit.vue:207
 #: src/views/stream/StreamEdit.vue:207
 #, fuzzy
 #, fuzzy
@@ -1591,7 +1623,7 @@ msgstr ""
 msgid "Not After"
 msgid "Not After"
 msgstr "Không phải sau khi"
 msgstr "Không phải sau khi"
 
 
-#: src/routes/index.ts:317
+#: src/routes/index.ts:324
 msgid "Not Found"
 msgid "Not Found"
 msgstr "Không tìm thấy"
 msgstr "Không tìm thấy"
 
 
@@ -1896,6 +1928,10 @@ msgstr "Đọc"
 msgid "Receive"
 msgid "Receive"
 msgstr "Nhận"
 msgstr "Nhận"
 
 
+#: src/views/system/SelfCheck/SelfCheck.vue:47
+msgid "Recheck"
+msgstr ""
+
 #: src/components/StdDesign/StdDataDisplay/StdBulkActions.vue:43
 #: src/components/StdDesign/StdDataDisplay/StdBulkActions.vue:43
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:544
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:544
 msgid "Recover"
 msgid "Recover"
@@ -2215,6 +2251,10 @@ msgstr ""
 msgid "Selector"
 msgid "Selector"
 msgstr "Bộ chọn"
 msgstr "Bộ chọn"
 
 
+#: src/routes/index.ts:283 src/views/system/SelfCheck/SelfCheck.vue:42
+msgid "Self Check"
+msgstr ""
+
 #: src/views/dashboard/ServerAnalytic.vue:348
 #: src/views/dashboard/ServerAnalytic.vue:348
 #: src/views/dashboard/ServerAnalytic.vue:35
 #: src/views/dashboard/ServerAnalytic.vue:35
 msgid "Send"
 msgid "Send"
@@ -2227,7 +2267,7 @@ msgstr "Gửi"
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:53
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:53
 #: src/components/StdDesign/StdDataDisplay/StdBulkActions.vue:70
 #: src/components/StdDesign/StdDataDisplay/StdBulkActions.vue:70
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:194
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:194
-#: src/views/config/components/Mkdir.vue:38
+#: src/lib/http/index.ts:128 src/views/config/components/Mkdir.vue:38
 #: src/views/config/components/Rename.vue:45
 #: src/views/config/components/Rename.vue:45
 #: src/views/config/ConfigEditor.vue:111
 #: src/views/config/ConfigEditor.vue:111
 #: src/views/environment/BatchUpgrader.vue:59
 #: src/views/environment/BatchUpgrader.vue:59
@@ -2297,6 +2337,11 @@ msgstr ""
 msgid "Site Logs"
 msgid "Site Logs"
 msgstr "Logs"
 msgstr "Logs"
 
 
+#: src/views/system/SelfCheck/tasks.ts:3
+#, fuzzy
+msgid "Sites Directory"
+msgstr "Thư mục"
+
 #: src/routes/index.ts:56
 #: src/routes/index.ts:56
 msgid "Sites List"
 msgid "Sites List"
 msgstr "Danh sách Website"
 msgstr "Danh sách Website"
@@ -2344,10 +2389,23 @@ msgstr "Đã dừng"
 msgid "Storage"
 msgid "Storage"
 msgstr "Storage"
 msgstr "Storage"
 
 
+#: src/views/system/SelfCheck/tasks.ts:7
+#, fuzzy
+msgid "Streams Directory"
+msgstr "Thư mục"
+
 #: src/constants/index.ts:19 src/views/notification/notificationColumns.tsx:36
 #: src/constants/index.ts:19 src/views/notification/notificationColumns.tsx:36
 msgid "Success"
 msgid "Success"
 msgstr "Thành công"
 msgstr "Thành công"
 
 
+#: src/views/system/SelfCheck/SelfCheck.vue:78
+msgid ""
+"Support communication with the backend through the WebSocket protocol. If "
+"your Nginx UI is being used via an Nginx reverse proxy, please refer to this "
+"link to write the corresponding configuration file: https://nginxui.com/"
+"guide/nginx-proxy-example.html"
+msgstr ""
+
 #: src/views/dashboard/ServerAnalytic.vue:240
 #: src/views/dashboard/ServerAnalytic.vue:240
 #: src/views/dashboard/ServerAnalytic.vue:241
 #: src/views/dashboard/ServerAnalytic.vue:241
 msgid "Swap"
 msgid "Swap"
@@ -2692,7 +2750,7 @@ msgstr "Ngày cập nhật"
 msgid "Updated successfully"
 msgid "Updated successfully"
 msgstr "Cập nhật thành công"
 msgstr "Cập nhật thành công"
 
 
-#: src/routes/index.ts:290 src/views/environment/Environment.vue:55
+#: src/routes/index.ts:297 src/views/environment/Environment.vue:55
 #: src/views/system/Upgrade.vue:145 src/views/system/Upgrade.vue:228
 #: src/views/system/Upgrade.vue:145 src/views/system/Upgrade.vue:228
 msgid "Upgrade"
 msgid "Upgrade"
 msgstr "Cập nhật"
 msgstr "Cập nhật"

+ 65 - 6
app/src/language/zh_CN/app.po

@@ -21,7 +21,7 @@ msgstr "2FA"
 msgid "2FA Settings"
 msgid "2FA Settings"
 msgstr "2FA 设置"
 msgstr "2FA 设置"
 
 
-#: src/routes/index.ts:283
+#: src/routes/index.ts:290
 msgid "About"
 msgid "About"
 msgstr "关于"
 msgstr "关于"
 
 
@@ -196,6 +196,10 @@ msgstr "与ChatGPT聊天"
 msgid "Assistant"
 msgid "Assistant"
 msgstr "助手"
 msgstr "助手"
 
 
+#: src/views/system/SelfCheck/SelfCheck.vue:54
+msgid "Attempt to fix"
+msgstr "尝试修复"
+
 #: src/views/preference/AuthSettings.vue:17
 #: src/views/preference/AuthSettings.vue:17
 msgid "Attempts"
 msgid "Attempts"
 msgstr "尝试次数"
 msgstr "尝试次数"
@@ -375,6 +379,27 @@ msgstr "通道"
 msgid "Check again"
 msgid "Check again"
 msgstr "重新检查"
 msgstr "重新检查"
 
 
+#: src/views/system/SelfCheck/tasks.ts:12
+msgid "Check if the nginx.conf includes the sites-enabled directory."
+msgstr "检查 nginx.conf 是否包含 sites-enabled 的目录。"
+
+#: src/views/system/SelfCheck/tasks.ts:16
+msgid "Check if the nginx.conf includes the streams-enabled directory."
+msgstr "检查 nginx.conf 是否包含 streams-enabled 的目录。"
+
+#: src/views/system/SelfCheck/tasks.ts:4
+msgid ""
+"Check if the sites-available and sites-enabled directories are under the "
+"nginx configuration directory."
+msgstr "检查 sites-available 和 sites-enabled 目录是否位于 nginx 配置目录下。"
+
+#: src/views/system/SelfCheck/tasks.ts:8
+msgid ""
+"Check if the streams-available and streams-enabled directories are under the "
+"nginx configuration directory."
+msgstr ""
+"检查 nginx 配置目录下是否有 streams-available 和 streams-enabled 目录。"
+
 #: src/language/constants.ts:13
 #: src/language/constants.ts:13
 msgid "Cleaning environment variables"
 msgid "Cleaning environment variables"
 msgstr "正在清理环境变量"
 msgstr "正在清理环境变量"
@@ -1102,7 +1127,7 @@ msgstr "输入应用程序中的代码:"
 msgid "Input the recovery code:"
 msgid "Input the recovery code:"
 msgstr "输入恢复代码:"
 msgstr "输入恢复代码:"
 
 
-#: src/routes/index.ts:305 src/views/other/Install.vue:136
+#: src/routes/index.ts:312 src/views/other/Install.vue:136
 msgid "Install"
 msgid "Install"
 msgstr "安装"
 msgstr "安装"
 
 
@@ -1239,7 +1264,7 @@ msgstr "Locations"
 msgid "Log"
 msgid "Log"
 msgstr "日志"
 msgstr "日志"
 
 
-#: src/routes/index.ts:311 src/views/other/Login.vue:246
+#: src/routes/index.ts:318 src/views/other/Login.vue:246
 msgid "Login"
 msgid "Login"
 msgstr "登录"
 msgstr "登录"
 
 
@@ -1402,6 +1427,14 @@ msgstr "Nginx"
 msgid "Nginx Access Log Path"
 msgid "Nginx Access Log Path"
 msgstr "Nginx 访问日志路径"
 msgstr "Nginx 访问日志路径"
 
 
+#: src/views/system/SelfCheck/tasks.ts:11
+msgid "Nginx Conf Include Sites Enabled"
+msgstr "Nginx Conf 中引用启用站点目录"
+
+#: src/views/system/SelfCheck/tasks.ts:15
+msgid "Nginx Conf Include Streams Enabled"
+msgstr "Nginx Conf 中引用启用 Streams 目录"
+
 #: src/views/site/site_edit/SiteEdit.vue:223
 #: src/views/site/site_edit/SiteEdit.vue:223
 #: src/views/stream/StreamEdit.vue:207
 #: src/views/stream/StreamEdit.vue:207
 msgid "Nginx Configuration Parse Error"
 msgid "Nginx Configuration Parse Error"
@@ -1480,7 +1513,7 @@ msgstr "节点密钥"
 msgid "Not After"
 msgid "Not After"
 msgstr "有效期"
 msgstr "有效期"
 
 
-#: src/routes/index.ts:317
+#: src/routes/index.ts:324
 msgid "Not Found"
 msgid "Not Found"
 msgstr "找不到页面"
 msgstr "找不到页面"
 
 
@@ -1775,6 +1808,10 @@ msgstr "读"
 msgid "Receive"
 msgid "Receive"
 msgstr "下载"
 msgstr "下载"
 
 
+#: src/views/system/SelfCheck/SelfCheck.vue:47
+msgid "Recheck"
+msgstr "重新检查"
+
 #: src/components/StdDesign/StdDataDisplay/StdBulkActions.vue:43
 #: src/components/StdDesign/StdDataDisplay/StdBulkActions.vue:43
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:544
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:544
 msgid "Recover"
 msgid "Recover"
@@ -2061,6 +2098,10 @@ msgstr "密钥已复制"
 msgid "Selector"
 msgid "Selector"
 msgstr "选择器"
 msgstr "选择器"
 
 
+#: src/routes/index.ts:283 src/views/system/SelfCheck/SelfCheck.vue:42
+msgid "Self Check"
+msgstr "自我检查"
+
 #: src/views/dashboard/ServerAnalytic.vue:348
 #: src/views/dashboard/ServerAnalytic.vue:348
 #: src/views/dashboard/ServerAnalytic.vue:35
 #: src/views/dashboard/ServerAnalytic.vue:35
 msgid "Send"
 msgid "Send"
@@ -2073,7 +2114,7 @@ msgstr "上传"
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:53
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:53
 #: src/components/StdDesign/StdDataDisplay/StdBulkActions.vue:70
 #: src/components/StdDesign/StdDataDisplay/StdBulkActions.vue:70
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:194
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:194
-#: src/views/config/components/Mkdir.vue:38
+#: src/lib/http/index.ts:128 src/views/config/components/Mkdir.vue:38
 #: src/views/config/components/Rename.vue:45
 #: src/views/config/components/Rename.vue:45
 #: src/views/config/ConfigEditor.vue:111
 #: src/views/config/ConfigEditor.vue:111
 #: src/views/environment/BatchUpgrader.vue:59
 #: src/views/environment/BatchUpgrader.vue:59
@@ -2141,6 +2182,10 @@ msgstr "网站分类"
 msgid "Site Logs"
 msgid "Site Logs"
 msgstr "站点列表"
 msgstr "站点列表"
 
 
+#: src/views/system/SelfCheck/tasks.ts:3
+msgid "Sites Directory"
+msgstr "网站目录"
+
 #: src/routes/index.ts:56
 #: src/routes/index.ts:56
 msgid "Sites List"
 msgid "Sites List"
 msgstr "站点列表"
 msgstr "站点列表"
@@ -2187,10 +2232,24 @@ msgstr "已停止"
 msgid "Storage"
 msgid "Storage"
 msgstr "存储"
 msgstr "存储"
 
 
+#: src/views/system/SelfCheck/tasks.ts:7
+msgid "Streams Directory"
+msgstr "Streams 目录"
+
 #: src/constants/index.ts:19 src/views/notification/notificationColumns.tsx:36
 #: src/constants/index.ts:19 src/views/notification/notificationColumns.tsx:36
 msgid "Success"
 msgid "Success"
 msgstr "成功"
 msgstr "成功"
 
 
+#: src/views/system/SelfCheck/SelfCheck.vue:78
+msgid ""
+"Support communication with the backend through the WebSocket protocol. If "
+"your Nginx UI is being used via an Nginx reverse proxy, please refer to this "
+"link to write the corresponding configuration file: https://nginxui.com/"
+"guide/nginx-proxy-example.html"
+msgstr ""
+"支持通过 WebSocket 协议与后端通信,如果您正在使用 Nginx 反向代理了 Nginx UI "
+"请参考:https://nginxui.com/guide/nginx-proxy-example.html 编写配置文件"
+
 #: src/views/dashboard/ServerAnalytic.vue:240
 #: src/views/dashboard/ServerAnalytic.vue:240
 #: src/views/dashboard/ServerAnalytic.vue:241
 #: src/views/dashboard/ServerAnalytic.vue:241
 msgid "Swap"
 msgid "Swap"
@@ -2523,7 +2582,7 @@ msgstr "修改时间"
 msgid "Updated successfully"
 msgid "Updated successfully"
 msgstr "更新成功"
 msgstr "更新成功"
 
 
-#: src/routes/index.ts:290 src/views/environment/Environment.vue:55
+#: src/routes/index.ts:297 src/views/environment/Environment.vue:55
 #: src/views/system/Upgrade.vue:145 src/views/system/Upgrade.vue:228
 #: src/views/system/Upgrade.vue:145 src/views/system/Upgrade.vue:228
 msgid "Upgrade"
 msgid "Upgrade"
 msgstr "升级"
 msgstr "升级"

+ 65 - 6
app/src/language/zh_TW/app.po

@@ -25,7 +25,7 @@ msgstr "多重要素驗證"
 msgid "2FA Settings"
 msgid "2FA Settings"
 msgstr "多重要素驗證設定"
 msgstr "多重要素驗證設定"
 
 
-#: src/routes/index.ts:283
+#: src/routes/index.ts:290
 msgid "About"
 msgid "About"
 msgstr "關於"
 msgstr "關於"
 
 
@@ -204,6 +204,11 @@ msgstr "向 ChatGPT 尋求幫助"
 msgid "Assistant"
 msgid "Assistant"
 msgstr "助理"
 msgstr "助理"
 
 
+#: src/views/system/SelfCheck/SelfCheck.vue:54
+#, fuzzy
+msgid "Attempt to fix"
+msgstr "嘗試次數"
+
 #: src/views/preference/AuthSettings.vue:17
 #: src/views/preference/AuthSettings.vue:17
 msgid "Attempts"
 msgid "Attempts"
 msgstr "嘗試次數"
 msgstr "嘗試次數"
@@ -385,6 +390,26 @@ msgstr "通道"
 msgid "Check again"
 msgid "Check again"
 msgstr "再次檢查"
 msgstr "再次檢查"
 
 
+#: src/views/system/SelfCheck/tasks.ts:12
+msgid "Check if the nginx.conf includes the sites-enabled directory."
+msgstr ""
+
+#: src/views/system/SelfCheck/tasks.ts:16
+msgid "Check if the nginx.conf includes the streams-enabled directory."
+msgstr ""
+
+#: src/views/system/SelfCheck/tasks.ts:4
+msgid ""
+"Check if the sites-available and sites-enabled directories are under the "
+"nginx configuration directory."
+msgstr ""
+
+#: src/views/system/SelfCheck/tasks.ts:8
+msgid ""
+"Check if the streams-available and streams-enabled directories are under the "
+"nginx configuration directory."
+msgstr ""
+
 #: src/language/constants.ts:13
 #: src/language/constants.ts:13
 msgid "Cleaning environment variables"
 msgid "Cleaning environment variables"
 msgstr "清理環境變數"
 msgstr "清理環境變數"
@@ -1125,7 +1150,7 @@ msgstr "請輸入應用程式中的代碼:"
 msgid "Input the recovery code:"
 msgid "Input the recovery code:"
 msgstr "輸入恢復碼:"
 msgstr "輸入恢復碼:"
 
 
-#: src/routes/index.ts:305 src/views/other/Install.vue:136
+#: src/routes/index.ts:312 src/views/other/Install.vue:136
 msgid "Install"
 msgid "Install"
 msgstr "安裝"
 msgstr "安裝"
 
 
@@ -1265,7 +1290,7 @@ msgstr "Locations"
 msgid "Log"
 msgid "Log"
 msgstr "日誌"
 msgstr "日誌"
 
 
-#: src/routes/index.ts:311 src/views/other/Login.vue:246
+#: src/routes/index.ts:318 src/views/other/Login.vue:246
 msgid "Login"
 msgid "Login"
 msgstr "登入"
 msgstr "登入"
 
 
@@ -1427,6 +1452,14 @@ msgstr "Nginx"
 msgid "Nginx Access Log Path"
 msgid "Nginx Access Log Path"
 msgstr "Nginx 存取日誌路徑"
 msgstr "Nginx 存取日誌路徑"
 
 
+#: src/views/system/SelfCheck/tasks.ts:11
+msgid "Nginx Conf Include Sites Enabled"
+msgstr ""
+
+#: src/views/system/SelfCheck/tasks.ts:15
+msgid "Nginx Conf Include Streams Enabled"
+msgstr ""
+
 #: src/views/site/site_edit/SiteEdit.vue:223
 #: src/views/site/site_edit/SiteEdit.vue:223
 #: src/views/stream/StreamEdit.vue:207
 #: src/views/stream/StreamEdit.vue:207
 msgid "Nginx Configuration Parse Error"
 msgid "Nginx Configuration Parse Error"
@@ -1509,7 +1542,7 @@ msgstr "Node Secret"
 msgid "Not After"
 msgid "Not After"
 msgstr "不晚於"
 msgstr "不晚於"
 
 
-#: src/routes/index.ts:317
+#: src/routes/index.ts:324
 msgid "Not Found"
 msgid "Not Found"
 msgstr "找不到頁面"
 msgstr "找不到頁面"
 
 
@@ -1808,6 +1841,10 @@ msgstr "讀取"
 msgid "Receive"
 msgid "Receive"
 msgstr "接收"
 msgstr "接收"
 
 
+#: src/views/system/SelfCheck/SelfCheck.vue:47
+msgid "Recheck"
+msgstr ""
+
 #: src/components/StdDesign/StdDataDisplay/StdBulkActions.vue:43
 #: src/components/StdDesign/StdDataDisplay/StdBulkActions.vue:43
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:544
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:544
 msgid "Recover"
 msgid "Recover"
@@ -2107,6 +2144,10 @@ msgstr ""
 msgid "Selector"
 msgid "Selector"
 msgstr "選擇器"
 msgstr "選擇器"
 
 
+#: src/routes/index.ts:283 src/views/system/SelfCheck/SelfCheck.vue:42
+msgid "Self Check"
+msgstr ""
+
 #: src/views/dashboard/ServerAnalytic.vue:348
 #: src/views/dashboard/ServerAnalytic.vue:348
 #: src/views/dashboard/ServerAnalytic.vue:35
 #: src/views/dashboard/ServerAnalytic.vue:35
 msgid "Send"
 msgid "Send"
@@ -2119,7 +2160,7 @@ msgstr "傳送"
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:53
 #: src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue:53
 #: src/components/StdDesign/StdDataDisplay/StdBulkActions.vue:70
 #: src/components/StdDesign/StdDataDisplay/StdBulkActions.vue:70
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:194
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:194
-#: src/views/config/components/Mkdir.vue:38
+#: src/lib/http/index.ts:128 src/views/config/components/Mkdir.vue:38
 #: src/views/config/components/Rename.vue:45
 #: src/views/config/components/Rename.vue:45
 #: src/views/config/ConfigEditor.vue:111
 #: src/views/config/ConfigEditor.vue:111
 #: src/views/environment/BatchUpgrader.vue:59
 #: src/views/environment/BatchUpgrader.vue:59
@@ -2187,6 +2228,11 @@ msgstr ""
 msgid "Site Logs"
 msgid "Site Logs"
 msgstr "網站日誌"
 msgstr "網站日誌"
 
 
+#: src/views/system/SelfCheck/tasks.ts:3
+#, fuzzy
+msgid "Sites Directory"
+msgstr "目錄"
+
 #: src/routes/index.ts:56
 #: src/routes/index.ts:56
 msgid "Sites List"
 msgid "Sites List"
 msgstr "網站列表"
 msgstr "網站列表"
@@ -2233,10 +2279,23 @@ msgstr "已停止"
 msgid "Storage"
 msgid "Storage"
 msgstr "儲存空間"
 msgstr "儲存空間"
 
 
+#: src/views/system/SelfCheck/tasks.ts:7
+#, fuzzy
+msgid "Streams Directory"
+msgstr "目錄"
+
 #: src/constants/index.ts:19 src/views/notification/notificationColumns.tsx:36
 #: src/constants/index.ts:19 src/views/notification/notificationColumns.tsx:36
 msgid "Success"
 msgid "Success"
 msgstr "成功"
 msgstr "成功"
 
 
+#: src/views/system/SelfCheck/SelfCheck.vue:78
+msgid ""
+"Support communication with the backend through the WebSocket protocol. If "
+"your Nginx UI is being used via an Nginx reverse proxy, please refer to this "
+"link to write the corresponding configuration file: https://nginxui.com/"
+"guide/nginx-proxy-example.html"
+msgstr ""
+
 #: src/views/dashboard/ServerAnalytic.vue:240
 #: src/views/dashboard/ServerAnalytic.vue:240
 #: src/views/dashboard/ServerAnalytic.vue:241
 #: src/views/dashboard/ServerAnalytic.vue:241
 msgid "Swap"
 msgid "Swap"
@@ -2574,7 +2633,7 @@ msgstr "更新時間"
 msgid "Updated successfully"
 msgid "Updated successfully"
 msgstr "更新成功"
 msgstr "更新成功"
 
 
-#: src/routes/index.ts:290 src/views/environment/Environment.vue:55
+#: src/routes/index.ts:297 src/views/environment/Environment.vue:55
 #: src/views/system/Upgrade.vue:145 src/views/system/Upgrade.vue:228
 #: src/views/system/Upgrade.vue:145 src/views/system/Upgrade.vue:228
 msgid "Upgrade"
 msgid "Upgrade"
 msgstr "升級"
 msgstr "升級"

+ 59 - 1
app/src/lib/http/index.ts

@@ -3,8 +3,8 @@ import use2FAModal from '@/components/TwoFA/use2FAModal'
 import { useNProgress } from '@/lib/nprogress/nprogress'
 import { useNProgress } from '@/lib/nprogress/nprogress'
 import { useSettingsStore, useUserStore } from '@/pinia'
 import { useSettingsStore, useUserStore } from '@/pinia'
 import router from '@/routes'
 import router from '@/routes'
+import { message } from 'ant-design-vue'
 import axios from 'axios'
 import axios from 'axios'
-
 import { storeToRefs } from 'pinia'
 import { storeToRefs } from 'pinia'
 import 'nprogress/nprogress.css'
 import 'nprogress/nprogress.css'
 
 
@@ -12,6 +12,23 @@ const user = useUserStore()
 const settings = useSettingsStore()
 const settings = useSettingsStore()
 const { token, secureSessionId } = storeToRefs(user)
 const { token, secureSessionId } = storeToRefs(user)
 
 
+// server response
+export interface CosyError {
+  scope?: string
+  code: string
+  message: string
+  params?: string[]
+}
+
+// code, message translation
+export type CosyErrorRecord = Record<number, () => string>
+
+const errors: Record<string, CosyErrorRecord> = {}
+
+function registerError(scope: string, record: CosyErrorRecord) {
+  errors[scope] = record
+}
+
 const instance = axios.create({
 const instance = axios.create({
   baseURL: import.meta.env.VITE_API_ROOT,
   baseURL: import.meta.env.VITE_API_ROOT,
   timeout: 50000,
   timeout: 50000,
@@ -59,6 +76,7 @@ instance.interceptors.response.use(
 
 
     return Promise.resolve(response.data)
     return Promise.resolve(response.data)
   },
   },
+  // eslint-disable-next-line sonarjs/cognitive-complexity
   async error => {
   async error => {
     nprogress.done()
     nprogress.done()
 
 
@@ -74,6 +92,46 @@ instance.interceptors.response.use(
         break
         break
     }
     }
 
 
+    const err = error.response.data as CosyError
+
+    if (err?.scope) {
+      // check if already register
+      if (!errors[err.scope]) {
+        try {
+          const error = await import(`@/constants/errors/${err.scope}.ts`)
+
+          registerError(err.scope, error.default)
+        }
+        catch {
+          /* empty */
+        }
+      }
+
+      const msg = errors?.[err.scope]?.[err.code]
+
+      if (msg) {
+        // if err has parmas
+        if (err?.params && err.params.length > 0) {
+          let res = msg()
+
+          err.params.forEach((param, index) => {
+            res = res.replaceAll(`{${index}}`, param)
+          })
+
+          message.error(res, 5)
+        }
+        else {
+          message.error(msg(), 5)
+        }
+      }
+      else {
+        message.error($gettext('Server error'))
+      }
+    }
+    else {
+      message.error($gettext(err?.message ?? 'Server error'))
+    }
+
     return Promise.reject(error.response.data)
     return Promise.reject(error.response.data)
   },
   },
 )
 )

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

@@ -276,6 +276,13 @@ export const routes: RouteRecordRaw[] = [
           icon: InfoCircleOutlined,
           icon: InfoCircleOutlined,
         },
         },
         children: [{
         children: [{
+          path: 'self_check',
+          name: 'Self Check',
+          component: () => import('@/views/system/SelfCheck/SelfCheck.vue'),
+          meta: {
+            name: () => $gettext('Self Check'),
+          },
+        }, {
           path: 'about',
           path: 'about',
           name: 'About',
           name: 'About',
           component: () => import('@/views/system/About.vue'),
           component: () => import('@/views/system/About.vue'),

+ 99 - 0
app/src/views/system/SelfCheck/SelfCheck.vue

@@ -0,0 +1,99 @@
+<script setup lang="ts">
+import type { Report } from '@/api/self_check'
+import selfCheck from '@/api/self_check'
+import { CheckCircleOutlined, CloseCircleOutlined } from '@ant-design/icons-vue'
+import tasks from './tasks'
+
+const data = ref<Report[]>()
+
+const loading = ref(false)
+const supportWebsocket = ref(false)
+
+function check() {
+  loading.value = true
+  selfCheck.run().then(r => {
+    data.value = r
+  }).finally(() => {
+    loading.value = false
+  })
+  const ws = selfCheck.websocket()
+  ws.onopen = () => {
+    supportWebsocket.value = true
+  }
+}
+
+onMounted(() => {
+  check()
+})
+
+const fixing = reactive({})
+
+function fix(taskName: string) {
+  fixing[taskName] = true
+  selfCheck.fix(taskName).then(() => {
+    check()
+  }).finally(() => {
+    fixing[taskName] = false
+  })
+}
+</script>
+
+<template>
+  <ACard :title="$gettext('Self Check')">
+    <template #extra>
+      <AButton
+        type="link" size="small" :loading @click="check"
+      >
+        {{ $gettext('Recheck') }}
+      </AButton>
+    </template>
+    <AList>
+      <AListItem v-for="(item, index) in data" :key="index">
+        <template v-if="item.err" #actions>
+          <AButton type="link" size="small" :loading="fixing[item.name]" @click="fix(item.name)">
+            {{ $gettext('Attempt to fix') }}
+          </AButton>
+        </template>
+        <AListItemMeta>
+          <template #title>
+            {{ tasks?.[item.name]?.name?.() }}
+          </template>
+          <template #description>
+            {{ tasks?.[item.name]?.description?.() }}
+          </template>
+          <template #avatar>
+            <div class="text-23px">
+              <CheckCircleOutlined v-if="!item.err" class="text-green" />
+              <CloseCircleOutlined v-else class="text-red" />
+            </div>
+          </template>
+        </AListItemMeta>
+      </AListItem>
+      <AListItem>
+        <AListItemMeta>
+          <template #title>
+            WebSocket
+          </template>
+          <template #description>
+            {{ $gettext('Support communication with the backend through the WebSocket protocol. '
+              + 'If your Nginx UI is being used via an Nginx reverse proxy, '
+              + 'please refer to this link to write the corresponding configuration file: '
+              + 'https://nginxui.com/guide/nginx-proxy-example.html') }}
+          </template>
+          <template #avatar>
+            <div class="text-23px">
+              <CheckCircleOutlined v-if="supportWebsocket" class="text-green" />
+              <CloseCircleOutlined v-else class="text-red" />
+            </div>
+          </template>
+        </AListItemMeta>
+      </AListItem>
+    </AList>
+  </ACard>
+</template>
+
+<style scoped lang="less">
+:deep(.ant-list-item-meta) {
+  align-items: center !important;
+}
+</style>

+ 20 - 0
app/src/views/system/SelfCheck/tasks.ts

@@ -0,0 +1,20 @@
+const tasks = {
+  'Directory-Sites': {
+    name: () => $gettext('Sites Directory'),
+    description: () => $gettext('Check if the sites-available and sites-enabled directories are under the nginx configuration directory.'),
+  },
+  'Directory-Streams': {
+    name: () => $gettext('Streams Directory'),
+    description: () => $gettext('Check if the streams-available and streams-enabled directories are under the nginx configuration directory.'),
+  },
+  'NginxConf-Sites-Enabled': {
+    name: () => $gettext('Nginx Conf Include Sites Enabled'),
+    description: () => $gettext('Check if the nginx.conf includes the sites-enabled directory.'),
+  },
+  'NginxConf-Streams-Enabled': {
+    name: () => $gettext('Nginx Conf Include Streams Enabled'),
+    description: () => $gettext('Check if the nginx.conf includes the streams-enabled directory.'),
+  },
+}
+
+export default tasks

+ 1 - 0
docs/guide/about.md

@@ -92,3 +92,4 @@ We welcome translations into any language.
 - [vue3-gettext](https://github.com/jshmrtn/vue3-gettext)
 - [vue3-gettext](https://github.com/jshmrtn/vue3-gettext)
 - [vue3-ace-editor](https://github.com/CarterLi/vue3-ace-editor)
 - [vue3-ace-editor](https://github.com/CarterLi/vue3-ace-editor)
 - [Gonginx](https://github.com/tufanbarisyildirim/gonginx)
 - [Gonginx](https://github.com/tufanbarisyildirim/gonginx)
+- [lego](https://github.com/go-acme/lego)

+ 1 - 0
docs/zh_CN/guide/about.md

@@ -89,3 +89,4 @@ Nginx UI 可在以下平台中使用:
 - [vue3-gettext](https://github.com/jshmrtn/vue3-gettext)
 - [vue3-gettext](https://github.com/jshmrtn/vue3-gettext)
 - [vue3-ace-editor](https://github.com/CarterLi/vue3-ace-editor)
 - [vue3-ace-editor](https://github.com/CarterLi/vue3-ace-editor)
 - [Gonginx](https://github.com/tufanbarisyildirim/gonginx)
 - [Gonginx](https://github.com/tufanbarisyildirim/gonginx)
+- [lego](https://github.com/go-acme/lego)

+ 1 - 0
docs/zh_TW/guide/about.md

@@ -89,3 +89,4 @@ Nginx UI 可在以下作業系統中使用:
 - [vue3-gettext](https://github.com/jshmrtn/vue3-gettext)
 - [vue3-gettext](https://github.com/jshmrtn/vue3-gettext)
 - [vue3-ace-editor](https://github.com/CarterLi/vue3-ace-editor)
 - [vue3-ace-editor](https://github.com/CarterLi/vue3-ace-editor)
 - [Gonginx](https://github.com/tufanbarisyildirim/gonginx)
 - [Gonginx](https://github.com/tufanbarisyildirim/gonginx)
+- [lego](https://github.com/go-acme/lego)

+ 20 - 3
internal/nginx/config_args.go

@@ -39,6 +39,23 @@ func GetConfPath(dir ...string) (confPath string) {
 	return joined
 	return joined
 }
 }
 
 
+func GetConfEntryPath() (path string) {
+	if settings.NginxSettings.ConfigPath == "" {
+		out := getNginxV()
+		r, _ := regexp.Compile("--conf-path=(.*.conf)")
+		match := r.FindStringSubmatch(out)
+		if len(match) < 1 {
+			logger.Error("nginx.GetConfEntryPath len(match) < 1")
+			return ""
+		}
+		path = match[1]
+	} else {
+		path = settings.NginxSettings.ConfigPath
+	}
+
+	return
+}
+
 func GetPIDPath() (path string) {
 func GetPIDPath() (path string) {
 	if settings.NginxSettings.PIDPath == "" {
 	if settings.NginxSettings.PIDPath == "" {
 		out := getNginxV()
 		out := getNginxV()
@@ -53,7 +70,7 @@ func GetPIDPath() (path string) {
 		path = settings.NginxSettings.PIDPath
 		path = settings.NginxSettings.PIDPath
 	}
 	}
 
 
-	return path
+	return
 }
 }
 
 
 func GetSbinPath() (path string) {
 func GetSbinPath() (path string) {
@@ -66,7 +83,7 @@ func GetSbinPath() (path string) {
 	}
 	}
 	path = match[1]
 	path = match[1]
 
 
-	return path
+	return
 }
 }
 
 
 func GetAccessLogPath() (path string) {
 func GetAccessLogPath() (path string) {
@@ -83,7 +100,7 @@ func GetAccessLogPath() (path string) {
 		path = settings.NginxSettings.AccessLogPath
 		path = settings.NginxSettings.AccessLogPath
 	}
 	}
 
 
-	return path
+	return
 }
 }
 
 
 func GetErrorLogPath() string {
 func GetErrorLogPath() string {

+ 67 - 0
internal/self_check/directory.go

@@ -0,0 +1,67 @@
+package self_check
+
+import (
+	"os"
+
+	"github.com/0xJacky/Nginx-UI/internal/nginx"
+)
+
+// CheckSitesDirectory checks if sites-available/sites-enabled directory exists
+func CheckSitesDirectory() error {
+	// check sites-available directory
+	if _, err := os.Stat(nginx.GetConfPath("sites-available")); os.IsNotExist(err) {
+		return ErrSitesAvailableNotExist
+	}
+
+	// check sites-enabled directory
+	if _, err := os.Stat(nginx.GetConfPath("sites-enabled")); os.IsNotExist(err) {
+		return ErrSitesEnabledNotExist
+	}
+
+	return nil
+}
+
+// CheckStreamDirectory checks if stream-available/stream-enabled directory exists
+func CheckStreamDirectory() error {
+	// check stream-available directory
+	if _, err := os.Stat(nginx.GetConfPath("streams-available")); os.IsNotExist(err) {
+		return ErrStreamAvailableNotExist
+	}
+
+	// check stream-enabled directory
+	if _, err := os.Stat(nginx.GetConfPath("streams-enabled")); os.IsNotExist(err) {
+		return ErrStreamEnabledNotExist
+	}
+
+	return nil
+}
+
+// FixSitesDirectory creates sites-available/sites-enabled directory
+func FixSitesDirectory() error {
+	// create sites-available directory
+	if err := os.MkdirAll(nginx.GetConfPath("sites-available"), 0755); err != nil {
+		return err
+	}
+
+	// create sites-enabled directory
+	if err := os.MkdirAll(nginx.GetConfPath("sites-enabled"), 0755); err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// FixStreamDirectory creates stream-available/stream-enabled directory
+func FixStreamDirectory() error {
+	// create stream-available directory
+	if err := os.MkdirAll(nginx.GetConfPath("streams-available"), 0755); err != nil {
+		return err
+	}
+
+	// create stream-enabled directory
+	if err := os.MkdirAll(nginx.GetConfPath("streams-enabled"), 0755); err != nil {
+		return err
+	}
+
+	return nil
+}

+ 19 - 0
internal/self_check/errors.go

@@ -0,0 +1,19 @@
+package self_check
+
+import "github.com/uozi-tech/cosy"
+
+var (
+	e                                   = cosy.NewErrorScope("self_check")
+	ErrTaskNotFound                     = e.New(4040, "Task not found")
+	ErrFailedToReadNginxConf            = e.New(4041, "Failed to read nginx.conf")
+	ErrParseNginxConf                   = e.New(5001, "Failed to parse nginx.conf")
+	ErrNginxConfNoHttpBlock             = e.New(4042, "Nginx conf no http block")
+	ErrNginxConfNotIncludeSitesEnabled  = e.New(4043, "Nginx conf not include sites-enabled")
+	ErrorNginxConfNoStreamBlock         = e.New(4044, "Nginx conf no stream block")
+	ErrNginxConfNotIncludeStreamEnabled = e.New(4045, "Nginx conf not include stream-enabled")
+	ErrFailedToCreateBackup             = e.New(5001, "Failed to create backup")
+	ErrSitesAvailableNotExist           = e.New(4046, "Sites-available directory not exist")
+	ErrSitesEnabledNotExist             = e.New(4047, "Sites-enabled directory not exist")
+	ErrStreamAvailableNotExist          = e.New(4048, "Stream-available directory not exist")
+	ErrStreamEnabledNotExist            = e.New(4049, "Stream-enabled directory not exist")
+)

+ 168 - 0
internal/self_check/nginx_conf.go

@@ -0,0 +1,168 @@
+package self_check
+
+import (
+	"fmt"
+	"os"
+	"time"
+
+	"github.com/0xJacky/Nginx-UI/internal/nginx"
+	"github.com/spf13/cast"
+	"github.com/tufanbarisyildirim/gonginx/config"
+	"github.com/tufanbarisyildirim/gonginx/dumper"
+	"github.com/tufanbarisyildirim/gonginx/parser"
+)
+
+// CheckNginxConfIncludeSites checks if nginx.conf include sites-enabled
+func CheckNginxConfIncludeSites() error {
+	path := nginx.GetConfEntryPath()
+
+	content, err := os.ReadFile(path)
+	if err != nil {
+		return ErrFailedToReadNginxConf
+	}
+
+	// parse nginx.conf
+	p := parser.NewStringParser(string(content), parser.WithSkipValidDirectivesErr())
+	c, err := p.Parse()
+	if err != nil {
+		return ErrParseNginxConf
+	}
+
+	// find http block
+	for _, v := range c.Block.Directives {
+		if v.GetName() == "http" {
+			// find include sites-enabled
+			for _, directive := range v.GetBlock().GetDirectives() {
+				if directive.GetName() == "include" && len(directive.GetParameters()) > 0 &&
+					directive.GetParameters()[0] == nginx.GetConfPath("sites-enabled/*") {
+					return nil
+				}
+			}
+			return ErrNginxConfNotIncludeSitesEnabled
+		}
+	}
+
+	return ErrNginxConfNoHttpBlock
+}
+
+// CheckNginxConfIncludeStreams checks if nginx.conf include streams-enabled
+func CheckNginxConfIncludeStreams() error {
+	path := nginx.GetConfEntryPath()
+
+	content, err := os.ReadFile(path)
+	if err != nil {
+		return ErrFailedToReadNginxConf
+	}
+
+	// parse nginx.conf
+	p := parser.NewStringParser(string(content), parser.WithSkipValidDirectivesErr())
+	c, err := p.Parse()
+	if err != nil {
+		return ErrParseNginxConf
+	}
+
+	// find http block
+	for _, v := range c.Block.Directives {
+		if v.GetName() == "stream" {
+			// find include sites-enabled
+			for _, directive := range v.GetBlock().GetDirectives() {
+				if directive.GetName() == "include" && len(directive.GetParameters()) > 0 &&
+					directive.GetParameters()[0] == nginx.GetConfPath("streams-enabled/*") {
+					return nil
+				}
+			}
+			return ErrNginxConfNotIncludeStreamEnabled
+		}
+	}
+
+	return ErrorNginxConfNoStreamBlock
+}
+
+// FixNginxConfIncludeSites attempts to fix nginx.conf include sites-enabled
+func FixNginxConfIncludeSites() error {
+	path := nginx.GetConfEntryPath()
+
+	content, err := os.ReadFile(path)
+	if err != nil {
+		return ErrFailedToReadNginxConf
+	}
+
+	// create a backup file (+.bak.timestamp)
+	backupPath := path + ".bak." + cast.ToString(time.Now().Unix())
+	err = os.WriteFile(backupPath, content, 0644)
+	if err != nil {
+		return ErrFailedToCreateBackup
+	}
+
+	// parse nginx.conf
+	p := parser.NewStringParser(string(content), parser.WithSkipValidDirectivesErr())
+	c, err := p.Parse()
+	if err != nil {
+		return ErrParseNginxConf
+	}
+
+	// find http block
+	for _, v := range c.Block.Directives {
+		if v.GetName() == "http" {
+			// add include sites-enabled/* to http block
+			includeDirective := &config.Directive{
+				Name:       "include",
+				Parameters: []string{nginx.GetConfPath("sites-enabled/*")},
+			}
+
+			realBlock := v.GetBlock().(*config.HTTP)
+			realBlock.Directives = append(realBlock.Directives, includeDirective)
+
+			// write to file
+			return os.WriteFile(path, []byte(dumper.DumpBlock(c.Block, dumper.IndentedStyle)), 0644)
+		}
+	}
+
+	// if no http block, append http block with include sites-enabled/*
+	content = append(content, []byte(fmt.Sprintf("\nhttp {\n\tinclude %s;\n}\n", nginx.GetConfPath("sites-enabled/*")))...)
+	return os.WriteFile(path, content, 0644)
+}
+
+// FixNginxConfIncludeStreams attempts to fix nginx.conf include streams-enabled
+func FixNginxConfIncludeStreams() error {
+	path := nginx.GetConfEntryPath()
+
+	content, err := os.ReadFile(path)
+	if err != nil {
+		return ErrFailedToReadNginxConf
+	}
+
+	// create a backup file (+.bak.timestamp)
+	backupPath := path + ".bak." + cast.ToString(time.Now().Unix())
+	err = os.WriteFile(backupPath, content, 0644)
+	if err != nil {
+		return ErrFailedToCreateBackup
+	}
+
+	// parse nginx.conf
+	p := parser.NewStringParser(string(content), parser.WithSkipValidDirectivesErr())
+	c, err := p.Parse()
+	if err != nil {
+		return ErrParseNginxConf
+	}
+
+	// find stream block
+	for _, v := range c.Block.Directives {
+		if v.GetName() == "stream" {
+			// add include streams-enabled/* to stream block
+			includeDirective := &config.Directive{
+				Name:       "include",
+				Parameters: []string{nginx.GetConfPath("streams-enabled/*")},
+			}
+			realBlock := v.GetBlock().(*config.Block)
+			realBlock.Directives = append(realBlock.Directives, includeDirective)
+
+			// write to file
+			return os.WriteFile(path, []byte(dumper.DumpBlock(c.Block, dumper.IndentedStyle)), 0644)
+		}
+	}
+
+	// if no stream block, append stream block with include streams-enabled/*
+	content = append(content, []byte(fmt.Sprintf("\nstream {\n\tinclude %s;\n}\n", nginx.GetConfPath("streams-enabled/*")))...)
+	return os.WriteFile(path, content, 0644)
+}

+ 123 - 0
internal/self_check/nginx_conf_test.go

@@ -0,0 +1,123 @@
+package self_check
+
+import (
+	"errors"
+	"os"
+	"strings"
+	"testing"
+
+	"github.com/0xJacky/Nginx-UI/settings"
+	"github.com/stretchr/testify/assert"
+	"github.com/uozi-tech/cosy"
+	"github.com/uozi-tech/cosy/logger"
+)
+
+func TestCheckNginxConfIncludeSites(t *testing.T) {
+	// test ok
+	logger.Init("debug")
+	settings.NginxSettings.ConfigDir = "/etc/nginx"
+	settings.NginxSettings.ConfigPath = "./test_cases/ok.conf"
+	var result *cosy.Error
+	errors.As(CheckNginxConfIncludeSites(), &result)
+	assert.Nil(t, result)
+
+	// test 4041 nginx.conf not found
+	settings.NginxSettings.ConfigDir = "/etc/nginx"
+	settings.NginxSettings.ConfigPath = "./test_cases/4041.conf"
+	errors.As(CheckNginxConfIncludeSites(), &result)
+	assert.Equal(t, int32(4041), result.Code)
+
+	// test 5001 nginx.conf parse error
+	settings.NginxSettings.ConfigDir = "/etc/nginx"
+	settings.NginxSettings.ConfigPath = "./test_cases/5001.conf"
+	errors.As(CheckNginxConfIncludeSites(), &result)
+	assert.Equal(t, int32(5001), result.Code)
+
+	// test 4042 nginx.conf no http block
+	settings.NginxSettings.ConfigDir = "/etc/nginx"
+	settings.NginxSettings.ConfigPath = "./test_cases/no-http-block.conf"
+	errors.As(CheckNginxConfIncludeSites(), &result)
+	assert.Equal(t, int32(4042), result.Code)
+
+	// test 4043 nginx.conf not include sites-enabled
+	settings.NginxSettings.ConfigDir = "/etc/nginx"
+	settings.NginxSettings.ConfigPath = "./test_cases/no-http-sites-enabled.conf"
+	errors.As(CheckNginxConfIncludeSites(), &result)
+	assert.Equal(t, int32(4043), result.Code)
+}
+
+func TestCheckNginxConfIncludeStreams(t *testing.T) {
+	// test ok
+	logger.Init("debug")
+	settings.NginxSettings.ConfigDir = "/etc/nginx"
+	settings.NginxSettings.ConfigPath = "./test_cases/ok.conf"
+	var result *cosy.Error
+	errors.As(CheckNginxConfIncludeStreams(), &result)
+	assert.Nil(t, result)
+
+	// test 4041 nginx.conf not found
+	settings.NginxSettings.ConfigDir = "/etc/nginx"
+	settings.NginxSettings.ConfigPath = "./test_cases/4041.conf"
+	errors.As(CheckNginxConfIncludeStreams(), &result)
+	assert.Equal(t, int32(4041), result.Code)
+
+	// test 5001 nginx.conf parse error
+	settings.NginxSettings.ConfigDir = "/etc/nginx"
+	settings.NginxSettings.ConfigPath = "./test_cases/5001.conf"
+	errors.As(CheckNginxConfIncludeStreams(), &result)
+	assert.Equal(t, int32(5001), result.Code)
+
+	// test 4044 nginx.conf no stream block
+	settings.NginxSettings.ConfigDir = "/etc/nginx"
+	settings.NginxSettings.ConfigPath = "./test_cases/no-http-block.conf"
+	errors.As(CheckNginxConfIncludeStreams(), &result)
+	assert.Equal(t, int32(4044), result.Code)
+
+	// test 4045 nginx.conf not include stream-enabled
+	settings.NginxSettings.ConfigDir = "/etc/nginx"
+	settings.NginxSettings.ConfigPath = "./test_cases/no-http-sites-enabled.conf"
+	errors.As(CheckNginxConfIncludeStreams(), &result)
+	assert.Equal(t, int32(4045), result.Code)
+}
+
+func TestFixNginxConfIncludeSites(t *testing.T) {
+	logger.Init("debug")
+	settings.NginxSettings.ConfigDir = "/etc/nginx"
+
+	// copy file
+	content, err := os.ReadFile("./test_cases/no-http-block.conf")
+	assert.Nil(t, err)
+
+	err = os.WriteFile("./test_cases/no-http-block-fixed.conf", content, 0644)
+	assert.Nil(t, err)
+
+	settings.NginxSettings.ConfigPath = "./test_cases/no-http-block-fixed.conf"
+	var result *cosy.Error
+	errors.As(FixNginxConfIncludeSites(), &result)
+	assert.Nil(t, result)
+
+	// copy file
+	content, err = os.ReadFile("./test_cases/no-http-sites-enabled.conf")
+	assert.Nil(t, err)
+	err = os.WriteFile("./test_cases/no-http-sites-enabled-fixed.conf", content, 0644)
+	assert.Nil(t, err)
+
+	settings.NginxSettings.ConfigPath = "./test_cases/no-http-sites-enabled-fixed.conf"
+	errors.As(FixNginxConfIncludeSites(), &result)
+	assert.Nil(t, result)
+
+	settings.NginxSettings.ConfigPath = "./test_cases/no-http-sites-enabled-fixed.conf"
+	errors.As(FixNginxConfIncludeStreams(), &result)
+	assert.Nil(t, result)
+
+	// remove backup files (./test_cases/*.bak.*)
+	files, err := os.ReadDir("./test_cases")
+	assert.Nil(t, err)
+
+	for _, file := range files {
+		if strings.Contains(file.Name(), ".bak.") {
+			err = os.Remove("./test_cases/" + file.Name())
+			assert.Nil(t, err)
+		}
+	}
+}

+ 74 - 0
internal/self_check/self_check.go

@@ -0,0 +1,74 @@
+package self_check
+
+import (
+	"errors"
+
+	"github.com/uozi-tech/cosy"
+)
+
+type Task struct {
+	Name      string
+	CheckFunc func() error
+	FixFunc   func() error
+}
+
+type Report struct {
+	Name string      `json:"name"`
+	Err  *cosy.Error `json:"err,omitempty"`
+}
+
+type Reports []*Report
+
+var selfCheckTasks = []*Task{
+	{
+		Name:      "Directory-Sites",
+		CheckFunc: CheckSitesDirectory,
+		FixFunc:   FixSitesDirectory,
+	},
+	{
+		Name:      "Directory-Streams",
+		CheckFunc: CheckStreamDirectory,
+		FixFunc:   FixStreamDirectory,
+	},
+	{
+		Name:      "NginxConf-Sites-Enabled",
+		CheckFunc: CheckNginxConfIncludeSites,
+		FixFunc:   FixNginxConfIncludeSites,
+	},
+	{
+		Name:      "NginxConf-Streams-Enabled",
+		CheckFunc: CheckNginxConfIncludeStreams,
+		FixFunc:   FixNginxConfIncludeStreams,
+	},
+}
+
+var selfCheckTaskMap = make(map[string]*Task)
+
+func init() {
+	for _, task := range selfCheckTasks {
+		selfCheckTaskMap[task.Name] = task
+	}
+}
+
+func Run() (reports Reports) {
+	reports = make(Reports, 0)
+	for _, task := range selfCheckTasks {
+		var cErr *cosy.Error
+		if err := task.CheckFunc(); err != nil {
+			errors.As(err, &cErr)
+		}
+		reports = append(reports, &Report{
+			Name: task.Name,
+			Err:  cErr,
+		})
+	}
+	return
+}
+
+func AttemptFix(taskName string) (err error) {
+	task, ok := selfCheckTaskMap[taskName]
+	if !ok {
+		return ErrTaskNotFound
+	}
+	return task.FixFunc()
+}

+ 1 - 0
internal/self_check/test_cases/5001.conf

@@ -0,0 +1 @@
+5001.conf

+ 14 - 0
internal/self_check/test_cases/no-http-block-fixed.conf

@@ -0,0 +1,14 @@
+user  nginx;
+worker_processes  auto;
+
+error_log  /var/log/nginx/error.log notice;
+error_log  /var/log/nginx/error.local.log notice;
+pid        /var/run/nginx.pid;
+
+events {
+    worker_connections  1024;
+}
+
+http {
+	include /etc/nginx/sites-enabled/*;
+}

+ 10 - 0
internal/self_check/test_cases/no-http-block.conf

@@ -0,0 +1,10 @@
+user  nginx;
+worker_processes  auto;
+
+error_log  /var/log/nginx/error.log notice;
+error_log  /var/log/nginx/error.local.log notice;
+pid        /var/run/nginx.pid;
+
+events {
+    worker_connections  1024;
+}

+ 23 - 0
internal/self_check/test_cases/no-http-sites-enabled-fixed.conf

@@ -0,0 +1,23 @@
+user nginx;
+worker_processes auto;
+error_log /var/log/nginx/error.log notice;
+error_log /var/log/nginx/error.local.log notice;
+pid /var/run/nginx.pid;
+events {
+    worker_connections 1024;
+}
+stream {
+    include /etc/nginx/streams-enabled/*;
+}
+http {
+    include /etc/nginx/mime.types;
+    default_type application/octet-stream;
+    log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"';
+    access_log /var/log/nginx/access.log main;
+    sendfile on;
+    #tcp_nopush     on;
+    keepalive_timeout 65;
+    gzip on;
+    include /etc/nginx/conf.d/*.conf;
+    include /etc/nginx/sites-enabled/*;
+}

+ 34 - 0
internal/self_check/test_cases/no-http-sites-enabled.conf

@@ -0,0 +1,34 @@
+user  nginx;
+worker_processes  auto;
+
+error_log  /var/log/nginx/error.log notice;
+error_log  /var/log/nginx/error.local.log notice;
+pid        /var/run/nginx.pid;
+
+events {
+    worker_connections  1024;
+}
+
+stream {
+
+}
+
+http {
+    include       /etc/nginx/mime.types;
+    default_type  application/octet-stream;
+
+    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
+                      '$status $body_bytes_sent "$http_referer" '
+                      '"$http_user_agent" "$http_x_forwarded_for"';
+
+    access_log  /var/log/nginx/access.log  main;
+
+    sendfile        on;
+    #tcp_nopush     on;
+
+    keepalive_timeout  65;
+
+    gzip  on;
+
+    include /etc/nginx/conf.d/*.conf;
+}

+ 35 - 0
internal/self_check/test_cases/ok.conf

@@ -0,0 +1,35 @@
+user  nginx;
+worker_processes  auto;
+
+error_log  /var/log/nginx/error.log notice;
+error_log  /var/log/nginx/error.local.log notice;
+pid        /var/run/nginx.pid;
+
+events {
+    worker_connections  1024;
+}
+
+stream {
+    include /etc/nginx/streams-enabled/*;
+}
+
+http {
+    include       /etc/nginx/mime.types;
+    default_type  application/octet-stream;
+
+    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
+                      '$status $body_bytes_sent "$http_referer" '
+                      '"$http_user_agent" "$http_x_forwarded_for"';
+
+    access_log  /var/log/nginx/access.log  main;
+
+    sendfile        on;
+    #tcp_nopush     on;
+
+    keepalive_timeout  65;
+
+    gzip  on;
+
+    include /etc/nginx/conf.d/*.conf;
+    include /etc/nginx/sites-enabled/*;
+}

+ 1 - 0
internal/self_check/websocket.go

@@ -0,0 +1 @@
+package self_check

+ 1 - 0
settings/nginx.go

@@ -5,6 +5,7 @@ type Nginx struct {
 	ErrorLogPath    string   `json:"error_log_path" protected:"true"`
 	ErrorLogPath    string   `json:"error_log_path" protected:"true"`
 	LogDirWhiteList []string `json:"log_dir_white_list" protected:"true"`
 	LogDirWhiteList []string `json:"log_dir_white_list" protected:"true"`
 	ConfigDir       string   `json:"config_dir" protected:"true"`
 	ConfigDir       string   `json:"config_dir" protected:"true"`
+	ConfigPath      string   `json:"config_path" protected:"true"`
 	PIDPath         string   `json:"pid_path" protected:"true"`
 	PIDPath         string   `json:"pid_path" protected:"true"`
 	TestConfigCmd   string   `json:"test_config_cmd" protected:"true"`
 	TestConfigCmd   string   `json:"test_config_cmd" protected:"true"`
 	ReloadCmd       string   `json:"reload_cmd" protected:"true"`
 	ReloadCmd       string   `json:"reload_cmd" protected:"true"`