Browse Source

enhance(wip): error handle

Jacky 3 months ago
parent
commit
650196d06a
93 changed files with 2749 additions and 1911 deletions
  1. 18 3
      api/api.go
  2. 5 6
      api/nginx/router.go
  3. 11 40
      api/nginx_log/nginx_log.go
  4. 7 0
      api/nginx_log/router.go
  5. 1 1
      api/openai/openai.go
  6. 6 13
      api/user/2fa.go
  7. 7 15
      api/user/auth.go
  8. 1 1
      api/user/casdoor.go
  9. 4 4
      api/user/passkey.go
  10. 0 4
      app/src/components/NginxControl/NginxControl.vue
  11. 1 6
      app/src/components/Notification/Notification.vue
  12. 0 4
      app/src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue
  13. 0 2
      app/src/components/StdDesign/StdDataDisplay/StdBulkActions.vue
  14. 0 5
      app/src/components/StdDesign/StdDataDisplay/StdCurd.vue
  15. 0 6
      app/src/components/StdDesign/StdDataDisplay/StdTable.vue
  16. 1 3
      app/src/components/StdDesign/StdDataDisplay/methods/exportCsv.ts
  17. 0 3
      app/src/components/StdDesign/StdDataDisplay/methods/sortable.ts
  18. 25 6
      app/src/components/StdDesign/StdDataEntry/components/StdSelector.vue
  19. 11 16
      app/src/components/TwoFA/Authorization.vue
  20. 8 0
      app/src/constants/errors/cert.ts
  21. 3 0
      app/src/constants/errors/config.ts
  22. 4 0
      app/src/constants/errors/crypto.ts
  23. 3 0
      app/src/constants/errors/nginx.ts
  24. 9 0
      app/src/constants/errors/nginx_log.ts
  25. 14 0
      app/src/constants/errors/self_check.ts
  26. 5 0
      app/src/constants/errors/site.ts
  27. 10 0
      app/src/constants/errors/user.ts
  28. 225 152
      app/src/language/ar/app.po
  29. 212 140
      app/src/language/en/app.po
  30. 214 141
      app/src/language/es/app.po
  31. 211 141
      app/src/language/fr_FR/app.po
  32. 213 142
      app/src/language/ko_KR/app.po
  33. 223 155
      app/src/language/messages.pot
  34. 214 141
      app/src/language/ru_RU/app.po
  35. 214 141
      app/src/language/tr_TR/app.po
  36. 212 141
      app/src/language/vi_VN/app.po
  37. 220 152
      app/src/language/zh_CN/app.po
  38. 225 152
      app/src/language/zh_TW/app.po
  39. 0 1
      app/src/views/certificate/CertificateEditor.vue
  40. 0 2
      app/src/views/config/ConfigEditor.vue
  41. 0 2
      app/src/views/config/components/ConfigName.vue
  42. 0 2
      app/src/views/config/components/Mkdir.vue
  43. 0 2
      app/src/views/config/components/Rename.vue
  44. 0 2
      app/src/views/environment/BatchUpgrader.vue
  45. 0 2
      app/src/views/environment/Environment.vue
  46. 0 2
      app/src/views/notification/Notification.vue
  47. 0 2
      app/src/views/other/Install.vue
  48. 15 39
      app/src/views/other/Login.vue
  49. 0 2
      app/src/views/preference/AuthSettings.vue
  50. 0 3
      app/src/views/preference/Preference.vue
  51. 7 13
      app/src/views/preference/components/AddPasskey.vue
  52. 0 6
      app/src/views/preference/components/Passkey.vue
  53. 1 6
      app/src/views/preference/components/TOTP.vue
  54. 0 2
      app/src/views/site/site_edit/components/ConfigName.vue
  55. 0 2
      app/src/views/site/site_list/SiteDuplicate.vue
  56. 1 5
      app/src/views/site/site_list/SiteList.vue
  57. 0 4
      app/src/views/stream/StreamList.vue
  58. 0 2
      app/src/views/system/Upgrade.vue
  59. 7 0
      cmd/errdef/generate.go
  60. 11 7
      cmd/ngx_dir_index/ngx_dir_index.go
  61. 7 0
      gen.sh
  62. 2 2
      go.mod
  63. 4 0
      go.sum
  64. 1 1
      internal/cert/auto_cert.go
  65. 4 5
      internal/cert/cert_info.go
  66. 13 0
      internal/cert/errors.go
  67. 1 1
      internal/cert/renew.go
  68. 2 5
      internal/cert/sync.go
  69. 2 5
      internal/cert/write_file.go
  70. 8 0
      internal/config/errors.go
  71. 3 7
      internal/config/sync.go
  72. 5 7
      internal/crypto/aes.go
  73. 9 0
      internal/crypto/errors.go
  74. 1 1
      internal/helper/websocket_error.go
  75. 1 1
      internal/kernel/skip_install.go
  76. 8 0
      internal/nginx/errors.go
  77. 1 1
      internal/nginx/parse.go
  78. 14 0
      internal/nginx_log/errors.go
  79. 41 0
      internal/nginx_log/nginx_log.go
  80. 3 3
      internal/self_check/errors.go
  81. 2 3
      internal/self_check/nginx_conf.go
  82. 2 2
      internal/site/delete.go
  83. 1 2
      internal/site/duplicate.go
  84. 10 0
      internal/site/errors.go
  85. 4 4
      internal/site/rename.go
  86. 1 1
      internal/site/save.go
  87. 15 0
      internal/user/errors.go
  88. 0 6
      internal/user/login.go
  89. 0 6
      internal/user/otp.go
  90. 1 1
      main.go
  91. 1 1
      model/chatgpt_log.go
  92. 2 1
      router/routers.go
  93. 1 1
      test/chatgpt_test.go

+ 18 - 3
api/api.go

@@ -1,9 +1,12 @@
 package api
 
 import (
+	"errors"
 	"github.com/0xJacky/Nginx-UI/model"
 	"github.com/gin-gonic/gin"
+	"github.com/uozi-tech/cosy"
 	"github.com/uozi-tech/cosy/logger"
+	"gorm.io/gorm"
 	"net/http"
 )
 
@@ -13,9 +16,21 @@ func CurrentUser(c *gin.Context) *model.User {
 
 func ErrHandler(c *gin.Context, err error) {
 	logger.GetLogger().Errorln(err)
-	c.JSON(http.StatusInternalServerError, gin.H{
-		"message": err.Error(),
-	})
+	var cErr *cosy.Error
+	switch {
+	case errors.Is(err, gorm.ErrRecordNotFound):
+		c.JSON(http.StatusNotFound, &cosy.Error{
+			Code:    http.StatusNotFound,
+			Message: gorm.ErrRecordNotFound.Error(),
+		})
+	case errors.As(err, &cErr):
+		c.JSON(http.StatusInternalServerError, cErr)
+	default:
+		c.JSON(http.StatusInternalServerError, &cosy.Error{
+			Code:    http.StatusInternalServerError,
+			Message: err.Error(),
+		})
+	}
 }
 
 func SetSSEHeaders(c *gin.Context) {

+ 5 - 6
api/nginx/router.go

@@ -1,6 +1,9 @@
 package nginx
 
-import "github.com/gin-gonic/gin"
+import (
+	"github.com/0xJacky/Nginx-UI/api/nginx_log"
+	"github.com/gin-gonic/gin"
+)
 
 func InitRouter(r *gin.RouterGroup) {
 	r.POST("ngx/build_config", BuildNginxConfig)
@@ -10,10 +13,6 @@ func InitRouter(r *gin.RouterGroup) {
 	r.POST("nginx/restart", Restart)
 	r.POST("nginx/test", Test)
 	r.GET("nginx/status", Status)
-	r.POST("nginx_log", GetNginxLogPage)
+	r.POST("nginx_log", nginx_log.GetNginxLogPage)
 	r.GET("nginx/directives", GetDirectives)
 }
-
-func InitNginxLogRouter(r *gin.RouterGroup) {
-	r.GET("nginx_log", Log)
-}

+ 11 - 40
api/nginx/nginx_log.go → api/nginx_log/nginx_log.go

@@ -1,12 +1,9 @@
-package nginx
+package nginx_log
 
 import (
 	"encoding/json"
-	"fmt"
-	"github.com/0xJacky/Nginx-UI/internal/cache"
-	"github.com/0xJacky/Nginx-UI/internal/helper"
 	"github.com/0xJacky/Nginx-UI/internal/nginx"
-	"github.com/0xJacky/Nginx-UI/settings"
+	"github.com/0xJacky/Nginx-UI/internal/nginx_log"
 	"github.com/gin-gonic/gin"
 	"github.com/gorilla/websocket"
 	"github.com/hpcloud/tail"
@@ -70,7 +67,7 @@ func GetNginxLogPage(c *gin.Context) {
 		c.JSON(http.StatusInternalServerError, nginxLogPageResp{
 			Error: "log file is not regular file",
 		})
-		logger.Error("log file is not regular file:", logPath)
+		logger.Errorf("log file is not regular file: %s", logPath)
 		return
 	}
 
@@ -132,30 +129,7 @@ func GetNginxLogPage(c *gin.Context) {
 	})
 }
 
-// isLogPathUnderWhiteList checks if the log path is under one of the paths in LogDirWhiteList
-func isLogPathUnderWhiteList(path string) bool {
-	cacheKey := fmt.Sprintf("isLogPathUnderWhiteList:%s", path)
-	res, ok := cache.Get(cacheKey)
-	// no cache, check it
-	if !ok {
-		for _, whitePath := range settings.NginxSettings.LogDirWhiteList {
-			if helper.IsUnderDirectory(path, whitePath) {
-				cache.Set(cacheKey, true, 0)
-				return true
-			}
-		}
-		return false
-	}
-	return res.(bool)
-}
-
 func getLogPath(control *controlStruct) (logPath string, err error) {
-	if len(settings.NginxSettings.LogDirWhiteList) == 0 {
-		err = errors.New("The settings.NginxSettings.LogDirWhiteList has not been configured. " +
-			"For security reasons, please configure a whitelist of log directories. " +
-			"Please visit https://nginxui.com/guide/config-nginx.html for more information.")
-		return
-	}
 	switch control.Type {
 	case "site":
 		var config *nginx.NgxConfig
@@ -167,12 +141,12 @@ func getLogPath(control *controlStruct) (logPath string, err error) {
 		}
 
 		if control.ServerIdx >= len(config.Servers) {
-			err = errors.New("serverIdx out of range")
+			err = nginx_log.ErrServerIdxOutOfRange
 			return
 		}
 
 		if control.DirectiveIdx >= len(config.Servers[control.ServerIdx].Directives) {
-			err = errors.New("DirectiveIdx out of range")
+			err = nginx_log.ErrDirectiveIdxOutOfRange
 			return
 		}
 
@@ -181,12 +155,12 @@ func getLogPath(control *controlStruct) (logPath string, err error) {
 		case "access_log", "error_log":
 			// ok
 		default:
-			err = errors.New("directive.Params neither access_log nor error_log")
+			err = nginx_log.ErrLogDirective
 			return
 		}
 
 		if directive.Params == "" {
-			err = errors.New("directive.Params is empty")
+			err = nginx_log.ErrDirectiveParamsIsEmpty
 			return
 		}
 
@@ -200,8 +174,7 @@ func getLogPath(control *controlStruct) (logPath string, err error) {
 		path := nginx.GetErrorLogPath()
 
 		if path == "" {
-			err = errors.New("settings.NginxLogSettings.ErrorLogPath is empty," +
-				" refer to https://nginxui.com/guide/config-nginx.html for more information")
+			err = nginx_log.ErrErrorLogPathIsEmpty
 			return
 		}
 
@@ -210,8 +183,7 @@ func getLogPath(control *controlStruct) (logPath string, err error) {
 		path := nginx.GetAccessLogPath()
 
 		if path == "" {
-			err = errors.New("settings.NginxLogSettings.AccessLogPath is empty," +
-				" refer to https://nginxui.com/guide/config-nginx.html for more information")
+			err = nginx_log.ErrAccessLogPathIsEmpty
 			return
 		}
 
@@ -219,9 +191,8 @@ func getLogPath(control *controlStruct) (logPath string, err error) {
 	}
 
 	// check if logPath is under one of the paths in LogDirWhiteList
-	if !isLogPathUnderWhiteList(logPath) {
-		err = errors.New("The log path is not under the paths in LogDirWhiteList.")
-		return "", err
+	if !nginx_log.IsLogPathUnderWhiteList(logPath) {
+		return "", nginx_log.ErrLogPathIsNotUnderTheLogDirWhiteList
 	}
 	return
 }

+ 7 - 0
api/nginx_log/router.go

@@ -0,0 +1,7 @@
+package nginx_log
+
+import "github.com/gin-gonic/gin"
+
+func InitRouter(r *gin.RouterGroup) {
+	r.GET("nginx_log", Log)
+}

+ 1 - 1
api/openai/openai.go

@@ -6,7 +6,7 @@ import (
 	"github.com/0xJacky/Nginx-UI/internal/chatbot"
 	"github.com/0xJacky/Nginx-UI/settings"
 	"github.com/gin-gonic/gin"
-	"github.com/pkg/errors"
+	"errors"
 	"github.com/sashabaranov/go-openai"
 	"github.com/uozi-tech/cosy"
 	"github.com/uozi-tech/cosy/logger"

+ 6 - 13
api/user/2fa.go

@@ -2,7 +2,6 @@ package user
 
 import (
 	"encoding/base64"
-	"fmt"
 	"github.com/0xJacky/Nginx-UI/api"
 	"github.com/0xJacky/Nginx-UI/internal/cache"
 	"github.com/0xJacky/Nginx-UI/internal/passkey"
@@ -77,23 +76,17 @@ func Start2FASecureSessionByOTP(c *gin.Context) {
 	}
 	u := api.CurrentUser(c)
 	if !u.EnabledOTP() {
-		c.JSON(http.StatusBadRequest, gin.H{
-			"message": "User has not configured OTP as 2FA",
-		})
+		api.ErrHandler(c, user.ErrUserNotEnabledOTPAs2FA)
 		return
 	}
 
 	if json.OTP == "" && json.RecoveryCode == "" {
-		c.JSON(http.StatusBadRequest, LoginResponse{
-			Message: "The user has enabled OTP as 2FA",
-		})
+		api.ErrHandler(c, user.ErrOTPOrRecoveryCodeEmpty)
 		return
 	}
 
 	if err := user.VerifyOTP(u, json.OTP, json.RecoveryCode); err != nil {
-		c.JSON(http.StatusBadRequest, LoginResponse{
-			Message: "Invalid OTP or recovery code",
-		})
+		api.ErrHandler(c, err)
 		return
 	}
 
@@ -106,7 +99,7 @@ func Start2FASecureSessionByOTP(c *gin.Context) {
 
 func BeginStart2FASecureSessionByPasskey(c *gin.Context) {
 	if !passkey.Enabled() {
-		api.ErrHandler(c, fmt.Errorf("WebAuthn settings are not configured"))
+		api.ErrHandler(c, user.ErrWebAuthnNotConfigured)
 		return
 	}
 	webauthnInstance := passkey.GetInstance()
@@ -126,13 +119,13 @@ func BeginStart2FASecureSessionByPasskey(c *gin.Context) {
 
 func FinishStart2FASecureSessionByPasskey(c *gin.Context) {
 	if !passkey.Enabled() {
-		api.ErrHandler(c, fmt.Errorf("WebAuthn settings are not configured"))
+		api.ErrHandler(c, user.ErrWebAuthnNotConfigured)
 		return
 	}
 	passkeySessionID := c.GetHeader("X-Passkey-Session-ID")
 	sessionDataBytes, ok := cache.Get(passkeySessionID)
 	if !ok {
-		api.ErrHandler(c, fmt.Errorf("session not found"))
+		api.ErrHandler(c, user.ErrSessionNotFound)
 		return
 	}
 	sessionData := sessionDataBytes.(*webauthn.SessionData)

+ 7 - 15
api/user/auth.go

@@ -6,7 +6,7 @@ import (
 	"github.com/0xJacky/Nginx-UI/query"
 	"github.com/0xJacky/Nginx-UI/settings"
 	"github.com/gin-gonic/gin"
-	"github.com/pkg/errors"
+	"errors"
 	"github.com/uozi-tech/cosy"
 	"github.com/uozi-tech/cosy/logger"
 	"math/rand/v2"
@@ -25,12 +25,10 @@ type LoginUser struct {
 }
 
 const (
-	ErrPasswordIncorrect = 4031
-	ErrMaxAttempts       = 4291
-	ErrUserBanned        = 4033
-	Enabled2FA           = 199
-	Error2FACode         = 4034
-	LoginSuccess         = 200
+	ErrMaxAttempts = 4291
+	Enabled2FA     = 199
+	Error2FACode   = 4034
+	LoginSuccess   = 200
 )
 
 type LoginResponse struct {
@@ -73,15 +71,9 @@ func Login(c *gin.Context) {
 		time.Sleep(random * time.Second)
 		switch {
 		case errors.Is(err, user.ErrPasswordIncorrect):
-			c.JSON(http.StatusForbidden, LoginResponse{
-				Message: "Password incorrect",
-				Code:    ErrPasswordIncorrect,
-			})
+			c.JSON(http.StatusForbidden, user.ErrPasswordIncorrect)
 		case errors.Is(err, user.ErrUserBanned):
-			c.JSON(http.StatusForbidden, LoginResponse{
-				Message: "The user is banned",
-				Code:    ErrUserBanned,
-			})
+			c.JSON(http.StatusForbidden, user.ErrUserBanned)
 		default:
 			api.ErrHandler(c, err)
 		}

+ 1 - 1
api/user/casdoor.go

@@ -7,7 +7,7 @@ import (
 	"github.com/0xJacky/Nginx-UI/settings"
 	"github.com/casdoor/casdoor-go-sdk/casdoorsdk"
 	"github.com/gin-gonic/gin"
-	"github.com/pkg/errors"
+	"errors"
 	"github.com/uozi-tech/cosy"
 	"gorm.io/gorm"
 	"net/http"

+ 4 - 4
api/user/passkey.go

@@ -53,7 +53,7 @@ func FinishPasskeyRegistration(c *gin.Context) {
 	webauthnInstance := passkey.GetInstance()
 	sessionDataBytes, ok := cache.Get(buildCachePasskeyRegKey(cUser.ID))
 	if !ok {
-		api.ErrHandler(c, fmt.Errorf("session not found"))
+		api.ErrHandler(c, user.ErrSessionNotFound)
 		return
 	}
 
@@ -87,7 +87,7 @@ func FinishPasskeyRegistration(c *gin.Context) {
 
 func BeginPasskeyLogin(c *gin.Context) {
 	if !passkey.Enabled() {
-		api.ErrHandler(c, fmt.Errorf("WebAuthn settings are not configured"))
+		api.ErrHandler(c, user.ErrWebAuthnNotConfigured)
 		return
 	}
 	webauthnInstance := passkey.GetInstance()
@@ -107,13 +107,13 @@ func BeginPasskeyLogin(c *gin.Context) {
 
 func FinishPasskeyLogin(c *gin.Context) {
 	if !passkey.Enabled() {
-		api.ErrHandler(c, fmt.Errorf("WebAuthn settings are not configured"))
+		api.ErrHandler(c, user.ErrWebAuthnNotConfigured)
 		return
 	}
 	sessionId := c.GetHeader("X-Passkey-Session-ID")
 	sessionDataBytes, ok := cache.Get(sessionId)
 	if !ok {
-		api.ErrHandler(c, fmt.Errorf("session not found"))
+		api.ErrHandler(c, user.ErrSessionNotFound)
 		return
 	}
 	webauthnInstance := passkey.GetInstance()

+ 0 - 4
app/src/components/NginxControl/NginxControl.vue

@@ -28,8 +28,6 @@ function reloadNginx() {
       message.warn(r.message)
     else
       message.error(r.message)
-  }).catch(e => {
-    message.error(`${$gettext('Server error')} ${e?.message}`)
   }).finally(() => getStatus())
 }
 
@@ -44,8 +42,6 @@ async function restartNginx() {
       message.warn(r.message)
     else
       message.error(r.message)
-  }).catch(e => {
-    message.error(`${$gettext('Server error')} ${e?.message}`)
   })
 }
 

+ 1 - 6
app/src/components/Notification/Notification.vue

@@ -67,8 +67,6 @@ function init() {
   notificationApi.get_list().then(r => {
     data.value = r.data
     unreadCount.value = r.pagination?.total || 0
-  }).catch(e => {
-    message.error($gettext(e?.message ?? 'Server error'))
   }).finally(() => {
     loading.value = false
   })
@@ -90,8 +88,7 @@ function clear() {
     message.success($gettext('Cleared successfully'))
     data.value = []
     unreadCount.value = 0
-  }).catch(e => {
-    message.error($gettext(e?.message ?? 'Server error'))
+    open.value = false
   })
 }
 
@@ -99,8 +96,6 @@ function remove(id: number) {
   notificationApi.destroy(id).then(() => {
     message.success($gettext('Removed successfully'))
     init()
-  }).catch(e => {
-    message.error($gettext(e?.message ?? 'Server error'))
   })
 }
 

+ 0 - 4
app/src/components/StdDesign/StdDataDisplay/StdBatchEdit.vue

@@ -48,10 +48,6 @@ async function ok() {
       emit('save')
       visible.value = false
     })
-    .catch(e => {
-      error.value = e.errors
-      message.error($gettext(e?.message) ?? $gettext('Server error'))
-    })
     .finally(() => {
       loading.value = false
     })

+ 0 - 2
app/src/components/StdDesign/StdDataDisplay/StdBulkActions.vue

@@ -66,8 +66,6 @@ function onClickApply() {
       computedActions.value[actionValue.value]?.action(props.selectedRowKeys).then(async () => {
         message.success($gettext('Apply bulk action successfully'))
         emit('onSuccess')
-      }).catch(e => {
-        message.error($gettext(e?.message) ?? $gettext('Server error'))
       }),
     )
   })

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

@@ -99,7 +99,6 @@ async function ok() {
       get_list()
       visible.value = false
     }).catch(e => {
-      message.error($gettext(e?.message ?? 'Server error'), 5)
       Object.assign(error, e.errors)
     })
   }
@@ -126,8 +125,6 @@ function edit(id: number | string) {
     visible.value = true
     modifyMode.value = true
     editMode.value = 'modify'
-  }).catch(e => {
-    message.error($gettext(e?.message ?? 'Server error'), 5)
   })
 }
 
@@ -135,8 +132,6 @@ function view(id: number | string) {
   get(id).then(() => {
     visible.value = true
     modifyMode.value = false
-  }).catch(e => {
-    message.error($gettext(e?.message ?? 'Server error'), 5)
   })
 }
 

+ 0 - 6
app/src/components/StdDesign/StdDataDisplay/StdTable.vue

@@ -181,8 +181,6 @@ function destroy(id: number | string) {
   props.api!.destroy(id, { permanent: props.inTrash }).then(() => {
     get_list()
     message.success($gettext('Deleted successfully'))
-  }).catch(e => {
-    message.error($gettext(e?.message ?? 'Server error'))
   })
 }
 
@@ -190,8 +188,6 @@ function recover(id: number | string) {
   props.api.recover(id).then(() => {
     message.success($gettext('Recovered Successfully'))
     get_list()
-  }).catch(e => {
-    message.error(e?.message ?? $gettext('Server error'))
   })
 }
 
@@ -224,8 +220,6 @@ async function _get_list() {
 
     if (r.pagination)
       Object.assign(pagination, r.pagination)
-  }).catch(e => {
-    message.error($gettext(e?.message ?? 'Server error'))
   })
 
   loading.value = false

+ 1 - 3
app/src/components/StdDesign/StdDataDisplay/methods/exportCsv.ts

@@ -2,7 +2,6 @@ import type { StdTableProps } from '@/components/StdDesign/StdDataDisplay/types'
 import type { Column, StdTableResponse } from '@/components/StdDesign/types'
 import type { ComputedRef } from 'vue'
 import { downloadCsv } from '@/lib/helper'
-import { message } from 'ant-design-vue'
 import dayjs from 'dayjs'
 import _ from 'lodash'
 
@@ -40,8 +39,7 @@ async function exportCsv(props: StdTableProps, pithyColumns: ComputedRef<Column[
         return
       }
       dataSource.push(...r.data)
-    }).catch((e: { message?: string }) => {
-      message.error(e.message ?? $gettext('Server error'))
+    }).catch(() => {
       hasMore = false
     })
     page += 1

+ 0 - 3
app/src/components/StdDesign/StdDataDisplay/methods/sortable.ts

@@ -119,9 +119,6 @@ function useSortable(props: StdTableProps, randomId: Ref<string>, dataSource: Re
         affected_ids: changeIds,
       }).then(() => {
         message.success($gettext('Updated successfully'))
-        // eslint-disable-next-line ts/no-explicit-any
-      }).catch((e: any) => {
-        message.error(e?.message ?? $gettext('Server error'))
       })
     },
   })

+ 25 - 6
app/src/components/StdDesign/StdDataEntry/components/StdSelector.vue

@@ -2,6 +2,7 @@
 import type Curd from '@/api/curd'
 import type { Column } from '@/components/StdDesign/types'
 import StdTable from '@/components/StdDesign/StdDataDisplay/StdTable.vue'
+import { CloseCircleFilled } from '@ant-design/icons-vue'
 import { watchOnce } from '@vueuse/core'
 import _ from 'lodash'
 
@@ -134,10 +135,13 @@ async function ok() {
   M_values.value = _.clone(records.value)
 }
 
-// function clear() {
-//   M_values.value = []
-//   emit('update:selectedKey', '')
-// }
+function clear() {
+  M_values.value = []
+  if (props.selectionType === 'radio')
+    selectedKey.value = null
+  else
+    selectedKey.value = []
+}
 
 defineExpose({ show })
 </script>
@@ -150,9 +154,8 @@ defineExpose({ show })
     >
       <div
         class="std-selector"
-        @click="show"
       >
-        <div class="chips-container">
+        <div class="chips-container w-full" @click="show">
           <div v-if="props.recordValueIndex">
             <ATag
               v-for="(chipText, index) in ComputedMValue"
@@ -172,6 +175,10 @@ defineExpose({ show })
             {{ placeholder }}
           </div>
         </div>
+
+        <div class="close-btn flex text-trueGray-3" @click="clear">
+          <CloseCircleFilled />
+        </div>
       </div>
     </div>
     <AModal
@@ -211,6 +218,8 @@ defineExpose({ show })
   align-items: self-start;
 
   .std-selector {
+    display: flex;
+    justify-content: space-between;
     overflow-y: auto;
     box-sizing: border-box;
     font-variant: tabular-nums;
@@ -228,6 +237,16 @@ defineExpose({ show })
     //margin: 0 10px 0 0;
     cursor: pointer;
     min-width: 180px;
+
+    .close-btn {
+      opacity: 0;
+      transition: opacity 0.3s;
+    }
+    &:hover {
+      .close-btn {
+        opacity: 1;
+      }
+    }
   }
 }
 

+ 11 - 16
app/src/components/TwoFA/Authorization.vue

@@ -5,7 +5,6 @@ import OTPInput from '@/components/OTPInput/OTPInput.vue'
 import { useUserStore } from '@/pinia'
 import { KeyOutlined } from '@ant-design/icons-vue'
 import { startAuthentication } from '@simplewebauthn/browser'
-import { message } from 'ant-design-vue'
 
 defineProps<{
   twoFAStatus: TwoFAStatusResponse
@@ -44,21 +43,17 @@ defineExpose({
 
 async function passkeyAuthenticate() {
   passkeyLoading.value = true
-  try {
-    const begin = await twoFA.begin_start_secure_session_by_passkey()
-    const asseResp = await startAuthentication({ optionsJSON: begin.options.publicKey })
-
-    const r = await twoFA.finish_start_secure_session_by_passkey({
-      session_id: begin.session_id,
-      options: asseResp,
-    })
-
-    emit('submitSecureSessionID', r.session_id)
-  }
-  // eslint-disable-next-line ts/no-explicit-any
-  catch (e: any) {
-    message.error($gettext(e.message ?? 'Server error'))
-  }
+
+  const begin = await twoFA.begin_start_secure_session_by_passkey()
+  const asseResp = await startAuthentication({ optionsJSON: begin.options.publicKey })
+
+  const r = await twoFA.finish_start_secure_session_by_passkey({
+    session_id: begin.session_id,
+    options: asseResp,
+  })
+
+  emit('submitSecureSessionID', r.session_id)
+
   passkeyLoading.value = false
 }
 

+ 8 - 0
app/src/constants/errors/cert.ts

@@ -0,0 +1,8 @@
+export default {
+  50001: () => $gettext('Filename is empty'),
+  50002: () => $gettext('Cert path is not under the nginx conf dir'),
+  50003: () => $gettext('Certificate decode error'),
+  50004: () => $gettext('Certificate parse error'),
+  50005: () => $gettext('Payload resource is nil'),
+  50006: () => $gettext('Path: {0} is not under the nginx conf dir: {1}'),
+}

+ 3 - 0
app/src/constants/errors/config.ts

@@ -0,0 +1,3 @@
+export default {
+  50006: () => $gettext('Path: {0} is not under the nginx conf dir: {1}'),
+}

+ 4 - 0
app/src/constants/errors/crypto.ts

@@ -0,0 +1,4 @@
+export default {
+  50001: () => $gettext('Plain text is empty'),
+  50002: () => $gettext('Cipher text is too short'),
+}

+ 3 - 0
app/src/constants/errors/nginx.ts

@@ -0,0 +1,3 @@
+export default {
+  50001: () => $gettext('Block is nil'),
+}

+ 9 - 0
app/src/constants/errors/nginx_log.ts

@@ -0,0 +1,9 @@
+export default {
+  50001: () => $gettext('The log path is not under the paths in settings.NginxSettings.LogDirWhiteList'),
+  50002: () => $gettext('ServerIdx out of range'),
+  50003: () => $gettext('DirectiveIdx out of range'),
+  50004: () => $gettext('Directive.Params neither access_log nor error_log'),
+  50005: () => $gettext('Directive params is empty'),
+  50006: () => $gettext('Settings.NginxLogSettings.ErrorLogPath is empty, refer to https://nginxui.com/guide/config-nginx.html for more information'),
+  50007: () => $gettext('Settings.NginxLogSettings.AccessLogPath is empty, refer to https://nginxui.com/guide/config-nginx.html for more information'),
+}

+ 14 - 0
app/src/constants/errors/self_check.ts

@@ -0,0 +1,14 @@
+export default {
+  4040: () => $gettext('Task not found'),
+  4041: () => $gettext('Failed to read nginx.conf'),
+  5001: () => $gettext('Failed to parse nginx.conf'),
+  4042: () => $gettext('Nginx conf no http block'),
+  4043: () => $gettext('Nginx conf not include sites-enabled'),
+  4044: () => $gettext('Nginx conf no stream block'),
+  4045: () => $gettext('Nginx conf not include stream-enabled'),
+  5002: () => $gettext('Failed to create backup'),
+  4046: () => $gettext('Sites-available directory not exist'),
+  4047: () => $gettext('Sites-enabled directory not exist'),
+  4048: () => $gettext('Streams-available directory not exist'),
+  4049: () => $gettext('Streams-enabled directory not exist'),
+}

+ 5 - 0
app/src/constants/errors/site.ts

@@ -0,0 +1,5 @@
+export default {
+  40401: () => $gettext('Site not found'),
+  50001: () => $gettext('Destination file already exists'),
+  50002: () => $gettext('Site is enabled'),
+}

+ 10 - 0
app/src/constants/errors/user.ts

@@ -0,0 +1,10 @@
+export default {
+  40301: () => $gettext('Password incorrect'),
+  40303: () => $gettext('User banned'),
+  40304: () => $gettext('Invalid otp code'),
+  40305: () => $gettext('Invalid recovery code'),
+  50000: () => $gettext('WebAuthn settings are not configured'),
+  50001: () => $gettext('User not enabled otp as 2fa'),
+  50002: () => $gettext('Otp or recovery code empty'),
+  40401: () => $gettext('Session not found'),
+}

File diff suppressed because it is too large
+ 225 - 152
app/src/language/ar/app.po


File diff suppressed because it is too large
+ 212 - 140
app/src/language/en/app.po


File diff suppressed because it is too large
+ 214 - 141
app/src/language/es/app.po


File diff suppressed because it is too large
+ 211 - 141
app/src/language/fr_FR/app.po


File diff suppressed because it is too large
+ 213 - 142
app/src/language/ko_KR/app.po


File diff suppressed because it is too large
+ 223 - 155
app/src/language/messages.pot


File diff suppressed because it is too large
+ 214 - 141
app/src/language/ru_RU/app.po


File diff suppressed because it is too large
+ 214 - 141
app/src/language/tr_TR/app.po


File diff suppressed because it is too large
+ 212 - 141
app/src/language/vi_VN/app.po


File diff suppressed because it is too large
+ 220 - 152
app/src/language/zh_CN/app.po


File diff suppressed because it is too large
+ 225 - 152
app/src/language/zh_TW/app.po


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

@@ -52,7 +52,6 @@ async function save() {
   // eslint-disable-next-line ts/no-explicit-any
   catch (e: any) {
     errors.value = e.errors
-    message.error($gettext(e?.message ?? 'Server error'))
     throw e
   }
 }

+ 0 - 2
app/src/views/config/ConfigEditor.vue

@@ -107,8 +107,6 @@ async function init() {
         translatedName: () => origName.value,
         hasChildren: false,
       }]
-    }).catch(r => {
-      message.error(r.message ?? $gettext('Server error'))
     })
   }
   else {

+ 0 - 2
app/src/views/config/components/ConfigName.vue

@@ -31,8 +31,6 @@ function save() {
       router.push({
         path: `/config/${r.path}/edit`,
       })
-    }).catch(e => {
-      message.error($gettext(e?.message ?? 'Server error'))
     }).finally(() => {
       loading.value = false
     })

+ 0 - 2
app/src/views/config/components/Mkdir.vue

@@ -34,8 +34,6 @@ function ok() {
 
         message.success($gettext('Created successfully'))
         emit('created')
-      }).catch(e => {
-        message.error(`${$gettext('Server error')} ${e?.message}`)
       })
     })
   })

+ 0 - 2
app/src/views/config/components/Rename.vue

@@ -41,8 +41,6 @@ function ok() {
         visible.value = false
         message.success($gettext('Rename successfully'))
         emit('renamed')
-      }).catch(e => {
-        message.error(`${$gettext('Server error')} ${e?.message}`)
       })
     })
   })

+ 0 - 2
app/src/views/environment/BatchUpgrader.vue

@@ -3,7 +3,6 @@ import type { Environment } from '@/api/environment'
 import type { Ref } from 'vue'
 import upgrade, { type RuntimeInfo } from '@/api/upgrade'
 import websocket from '@/lib/websocket'
-import { message } from 'ant-design-vue'
 import _ from 'lodash'
 import { marked } from 'marked'
 import { useRoute } from 'vue-router'
@@ -56,7 +55,6 @@ function getLatestRelease() {
     data.value = r
   }).catch(e => {
     getReleaseError.value = e?.message
-    message.error(e?.message ?? $gettext('Server error'))
   }).finally(() => {
     loading.value = false
   })

+ 0 - 2
app/src/views/environment/Environment.vue

@@ -11,8 +11,6 @@ function loadFromSettings() {
   environment.load_from_settings().then(() => {
     curd.value.get_list()
     message.success($gettext('Load successfully'))
-  }).catch(e => {
-    message.error(`${$gettext('Server error')} ${e?.message}`)
   })
 }
 const selectedNodeIds = ref([])

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

@@ -13,8 +13,6 @@ function clear() {
     message.success($gettext('Cleared successfully'))
     curd.value?.get_list()
     unreadCount.value = 0
-  }).catch(e => {
-    message.error($gettext(e?.message ?? 'Server error'))
   })
 }
 

+ 0 - 2
app/src/views/other/Install.vue

@@ -66,8 +66,6 @@ function onSubmit() {
     install.install_nginx_ui(modelRef).then(async () => {
       message.success($gettext('Install successfully'))
       await router.push('/login')
-    }).catch(e => {
-      message.error(e.message ?? $gettext('Server error'))
     }).finally(() => {
       loading.value = false
     })

+ 15 - 39
app/src/views/other/Login.vue

@@ -73,23 +73,8 @@ function onSubmit() {
           break
       }
     }).catch(e => {
-      switch (e.code) {
-        case 4031:
-          message.error($gettext('Incorrect username or password'))
-          break
-        case 4291:
-          message.error($gettext('Too many login failed attempts, please try again later'))
-          break
-        case 4033:
-          message.error($gettext('User is banned'))
-          break
-        case 4034:
-          refOTP.value?.clearInput()
-          message.error($gettext('Invalid 2FA or recovery code'))
-          break
-        default:
-          message.error($gettext(e.message ?? 'Server error'))
-          break
+      if (e.code === 4043) {
+        refOTP.value?.clearInput()
       }
     })
     loading.value = false
@@ -118,9 +103,6 @@ auth.get_casdoor_uri()
       casdoor_uri.value = r.uri
     }
   })
-  .catch(e => {
-    message.error($gettext(e.message ?? 'Server error'))
-  })
 
 function loginWithCasdoor() {
   window.location.href = casdoor_uri.value
@@ -134,8 +116,6 @@ if (route.query?.code !== undefined && route.query?.state !== undefined) {
     const next = (route.query?.next || '').toString() || '/'
 
     await router.push(next)
-  }).catch(e => {
-    message.error($gettext(e.message ?? 'Server error'))
   })
   loading.value = false
 }
@@ -156,27 +136,23 @@ passkey.get_config_status().then(r => {
 const passkeyLoginLoading = ref(false)
 async function handlePasskeyLogin() {
   passkeyLoginLoading.value = true
-  try {
-    const begin = await auth.begin_passkey_login()
-    const asseResp = await startAuthentication({ optionsJSON: begin.options.publicKey })
 
-    const r = await auth.finish_passkey_login({
-      session_id: begin.session_id,
-      options: asseResp,
-    })
+  const begin = await auth.begin_passkey_login()
+  const asseResp = await startAuthentication({ optionsJSON: begin.options.publicKey })
 
-    if (r.token) {
-      const next = (route.query?.next || '').toString() || '/'
+  const r = await auth.finish_passkey_login({
+    session_id: begin.session_id,
+    options: asseResp,
+  })
 
-      passkeyLogin(asseResp.rawId, r.token)
-      secureSessionId.value = r.secure_session_id
-      await router.push(next)
-    }
-  }
-  // eslint-disable-next-line ts/no-explicit-any
-  catch (e: any) {
-    message.error($gettext(e.message ?? 'Server error'))
+  if (r.token) {
+    const next = (route.query?.next || '').toString() || '/'
+
+    passkeyLogin(asseResp.rawId, r.token)
+    secureSessionId.value = r.secure_session_id
+    await router.push(next)
   }
+
   passkeyLoginLoading.value = false
 }
 </script>

+ 0 - 2
app/src/views/preference/AuthSettings.vue

@@ -45,8 +45,6 @@ function removeBannedIP(ip: string) {
   setting.remove_banned_ip(ip).then(() => {
     bannedIPs.value = bannedIPs.value.filter(v => v.ip !== ip)
     message.success($gettext('Remove successfully'))
-  }).catch((e: { message?: string }) => {
-    message.error(e?.message ?? $gettext('Server error'))
   })
 }
 </script>

+ 0 - 3
app/src/views/preference/Preference.vue

@@ -112,9 +112,6 @@ async function save() {
       refAuthSettings.value?.getBannedIPs?.()
       message.success($gettext('Save successfully'))
       errors.value = {}
-    }).catch(e => {
-      errors.value = e.errors
-      message.error(e?.message ?? $gettext('Server error'))
     })
   })
 }

+ 7 - 13
app/src/views/preference/components/AddPasskey.vue

@@ -14,24 +14,18 @@ const passkeyEnabled = ref(false)
 const regLoading = ref(false)
 async function registerPasskey() {
   regLoading.value = true
-  try {
-    const optionsJSON = await passkey.begin_registration()
+  const optionsJSON = await passkey.begin_registration()
 
-    const attestationResponse = await startRegistration({ optionsJSON })
+  const attestationResponse = await startRegistration({ optionsJSON })
 
-    await passkey.finish_registration(attestationResponse, passkeyName.value)
+  await passkey.finish_registration(attestationResponse, passkeyName.value)
 
-    emit('created')
+  emit('created')
 
-    message.success($gettext('Register passkey successfully'))
-    addPasskeyModelOpen.value = false
+  message.success($gettext('Register passkey successfully'))
+  addPasskeyModelOpen.value = false
 
-    user.passkeyRawId = attestationResponse.rawId
-  }
-  // eslint-disable-next-line ts/no-explicit-any
-  catch (e: any) {
-    message.error($gettext(e.message ?? 'Server error'))
-  }
+  user.passkeyRawId = attestationResponse.rawId
   regLoading.value = false
 }
 

+ 0 - 6
app/src/views/preference/components/Passkey.vue

@@ -22,8 +22,6 @@ function getList() {
   getListLoading.value = true
   passkey.get_list().then(r => {
     data.value = r
-  }).catch((e: { message?: string }) => {
-    message.error(e?.message ?? $gettext('Server error'))
   }).finally(() => {
     getListLoading.value = false
   })
@@ -39,8 +37,6 @@ function update(id: number, record: Passkey) {
     getList()
     modifyIdx.value = -1
     message.success($gettext('Update successfully'))
-  }).catch((e: { message?: string }) => {
-    message.error(e?.message ?? $gettext('Server error'))
   })
 }
 
@@ -52,8 +48,6 @@ function remove(item: Passkey) {
     // if current passkey is removed, clear it from user store
     if (user.passkeyLoginAvailable && user.passkeyRawId === item.raw_id)
       user.passkeyRawId = ''
-  }).catch((e: { message?: string }) => {
-    message.error(e?.message ?? $gettext('Server error'))
   })
 }
 </script>

+ 1 - 6
app/src/views/preference/components/TOTP.vue

@@ -40,8 +40,6 @@ function generateSecret() {
     secret.value = r.secret
     qrCode.value = r.qr_code
     refOtp.value?.clearInput()
-  }).catch((e: { message?: string }) => {
-    message.error(e.message ?? $gettext('Server error'))
   })
 }
 
@@ -52,9 +50,8 @@ function enroll(code: string) {
     clearGenerateSecretInterval()
     get2FAStatus()
     message.success($gettext('Enable 2FA successfully'))
-  }).catch((e: { message?: string }) => {
+  }).catch(() => {
     refOtp.value?.clearInput()
-    message.error(e.message ?? $gettext('Server error'))
   })
 }
 
@@ -79,8 +76,6 @@ function reset2FA() {
     recoveryCode.value = ''
     get2FAStatus()
     clickEnable2FA()
-  }).catch((e: { message?: string }) => {
-    message.error($gettext(e.message ?? 'Server error'))
   })
 }
 </script>

+ 0 - 2
app/src/views/site/site_edit/components/ConfigName.vue

@@ -28,8 +28,6 @@ function save() {
     router.push({
       path: `/sites/${buffer.value}`,
     })
-  }).catch(e => {
-    message.error($gettext(e?.message ?? 'Server error'))
   }).finally(() => {
     loading.value = false
   })

+ 0 - 2
app/src/views/site/site_list/SiteDuplicate.vue

@@ -48,8 +48,6 @@ function onSubmit() {
       message.success($gettext('Duplicate to local successfully'))
       show.value = false
       emit('duplicated')
-    }).catch(e => {
-      message.error($gettext(e?.message ?? 'Server error'))
     })
 
     loading.value = false

+ 1 - 5
app/src/views/site/site_list/SiteList.vue

@@ -35,9 +35,7 @@ onMounted(async () => {
         return
       }
     }
-    // eslint-disable-next-line ts/no-explicit-any
-    catch (e: any) {
-      message.error(e?.message ?? $gettext('Server error'))
+    catch {
       return
     }
   }
@@ -68,8 +66,6 @@ function destroy(site_name: string) {
     table.value.get_list()
     message.success($gettext('Delete site: %{site_name}', { site_name }))
     inspect_config.value?.test()
-  }).catch(e => {
-    message.error(e?.message ?? $gettext('Server error'))
   })
 }
 

+ 0 - 4
app/src/views/stream/StreamList.vue

@@ -77,8 +77,6 @@ function destroy(stream_name: string) {
     table.value.get_list()
     message.success($gettext('Delete stream: %{stream_name}', { stream_name }))
     inspect_config.value?.test()
-  }).catch(e => {
-    message.error(e?.message ?? $gettext('Server error'))
   })
 }
 
@@ -109,8 +107,6 @@ function handleAddStream() {
     showAddStream.value = false
     table.value?.get_list()
     message.success($gettext('Added successfully'))
-  }).catch(e => {
-    message.error(e?.message ?? $gettext('Server error'))
   })
 }
 </script>

+ 0 - 2
app/src/views/system/Upgrade.vue

@@ -5,7 +5,6 @@ import upgrade from '@/api/upgrade'
 
 import websocket from '@/lib/websocket'
 import version from '@/version.json'
-import { message } from 'ant-design-vue'
 import dayjs from 'dayjs'
 import { marked } from 'marked'
 import { useRoute } from 'vue-router'
@@ -39,7 +38,6 @@ function getLatestRelease() {
     lastCheck.value = dayjs().format('YYYY-MM-DD HH:mm:ss')
   }).catch(e => {
     getReleaseError.value = e?.message
-    message.error(e?.message ?? $gettext('Server error'))
   }).finally(() => {
     loading.value = false
   })

+ 7 - 0
cmd/errdef/generate.go

@@ -0,0 +1,7 @@
+package main
+
+import "github.com/uozi-tech/cosy/errdef"
+
+func main() {
+	errdef.Generate()
+}

+ 11 - 7
cmd/parse_ngx_index/parse_ngx_index.go → cmd/ngx_dir_index/ngx_dir_index.go

@@ -2,7 +2,7 @@ package main
 
 import (
 	"encoding/json"
-	"fmt"
+	"log"
 	"net/http"
 	"os"
 	"strings"
@@ -15,10 +15,14 @@ type Directive struct {
 }
 
 func main() {
+	if len(os.Args) < 2 {
+		log.Println("Usage: go run . <output_file>")
+	}
+	outputPath := os.Args[1]
 	// Fetch page content
 	resp, err := http.Get("https://nginx.org/en/docs/dirindex.html")
 	if err != nil {
-		fmt.Println("Error fetching page:", err)
+		log.Println("[Error] fetching page:", err)
 		return
 	}
 	defer resp.Body.Close()
@@ -26,7 +30,7 @@ func main() {
 	// Parse HTML
 	doc, err := html.Parse(resp.Body)
 	if err != nil {
-		fmt.Println("Error parsing HTML:", err)
+		log.Println("[Error] parsing HTML:", err)
 		return
 	}
 
@@ -99,15 +103,15 @@ func main() {
 	// Write results to JSON file
 	jsonData, err := json.MarshalIndent(directives, "", "  ")
 	if err != nil {
-		fmt.Println("Error marshaling JSON:", err)
+		log.Println("[Error] marshaling JSON:", err)
 		return
 	}
 
-	err = os.WriteFile("../../internal/nginx/nginx_directives.json", jsonData, 0644)
+	err = os.WriteFile(outputPath, jsonData, 0644)
 	if err != nil {
-		fmt.Println("Error writing file:", err)
+		log.Println("[Error] writing file:", err)
 		return
 	}
 
-	fmt.Printf("Successfully parsed %d directives and saved to nginx_directives.json\n", len(directives))
+	log.Printf("[OK] Successfully parsed %d directives and saved to %s\n", len(directives), outputPath)
 }

+ 7 - 0
gen.sh

@@ -1,3 +1,10 @@
+# generate gen code
 pushd ./cmd/gen || exit
 go run generate.go -config ../../app.ini
 popd || exit
+
+# generate error definitions
+go run cmd/errdef/generate.go . ts ./app/src/constants/errors
+
+# parse nginx directive indexs
+go run cmd/ngx_dir_index/ngx_dir_index.go ./internal/nginx/nginx_directives.json

+ 2 - 2
go.mod

@@ -15,7 +15,7 @@ require (
 	github.com/gin-contrib/static v1.1.3
 	github.com/gin-gonic/gin v1.10.0
 	github.com/go-acme/lego/v4 v4.21.0
-	github.com/go-co-op/gocron/v2 v2.14.2
+	github.com/go-co-op/gocron/v2 v2.15.0
 	github.com/go-playground/validator/v10 v10.24.0
 	github.com/go-resty/resty/v2 v2.16.4
 	github.com/go-webauthn/webauthn v0.11.2
@@ -35,7 +35,7 @@ require (
 	github.com/spf13/cast v1.7.1
 	github.com/stretchr/testify v1.10.0
 	github.com/tufanbarisyildirim/gonginx v0.0.0-20250120210832-12a9c7ae0c8a
-	github.com/uozi-tech/cosy v1.14.1
+	github.com/uozi-tech/cosy v1.14.2
 	github.com/uozi-tech/cosy-driver-sqlite v0.2.0
 	go.uber.org/zap v1.27.0
 	golang.org/x/crypto v0.32.0

+ 4 - 0
go.sum

@@ -1027,6 +1027,8 @@ github.com/go-co-op/gocron/v2 v2.14.1 h1:bwWMkX2rNfS6RqBmUAfkDuOPKl/BRCRCrmuAv8f
 github.com/go-co-op/gocron/v2 v2.14.1/go.mod h1:ZF70ZwEqz0OO4RBXE1sNxnANy/zvwLcattWEFsqpKig=
 github.com/go-co-op/gocron/v2 v2.14.2 h1:S6CbI7MVfD3S/aPJNLoSg2YcGyEqzEMwUopDejuT4Oc=
 github.com/go-co-op/gocron/v2 v2.14.2/go.mod h1:ZF70ZwEqz0OO4RBXE1sNxnANy/zvwLcattWEFsqpKig=
+github.com/go-co-op/gocron/v2 v2.15.0 h1:Kpvo71VSihE+RImmpA+3ta5CcMhoRzMGw4dJawrj4zo=
+github.com/go-co-op/gocron/v2 v2.15.0/go.mod h1:ZF70ZwEqz0OO4RBXE1sNxnANy/zvwLcattWEFsqpKig=
 github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
 github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk=
 github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
@@ -1941,6 +1943,8 @@ github.com/uozi-tech/cosy v1.13.0 h1:dDnU8f3z3AA2KamqEcpa0ObqY4YVXXsNbdxR9Hv7f0A
 github.com/uozi-tech/cosy v1.13.0/go.mod h1:DSKLtoVaGLUlJ8KiQ1vWEsnv85epRrAAMXSijuq+asM=
 github.com/uozi-tech/cosy v1.14.1 h1:Qat6Av9XhYMypeBCcagl3Pfp6LWgDRK0+lHBN/1jFY0=
 github.com/uozi-tech/cosy v1.14.1/go.mod h1:DSKLtoVaGLUlJ8KiQ1vWEsnv85epRrAAMXSijuq+asM=
+github.com/uozi-tech/cosy v1.14.2 h1:nWTiBSAYn1yTtQeTZluK/G2OjERGs/SRaK4yVjq1IQ8=
+github.com/uozi-tech/cosy v1.14.2/go.mod h1:DSKLtoVaGLUlJ8KiQ1vWEsnv85epRrAAMXSijuq+asM=
 github.com/uozi-tech/cosy-driver-mysql v0.2.2 h1:22S/XNIvuaKGqxQPsYPXN8TZ8hHjCQdcJKVQ83Vzxoo=
 github.com/uozi-tech/cosy-driver-mysql v0.2.2/go.mod h1:EZnRIbSj1V5U0gEeTobrXai/d1SV11lkl4zP9NFEmyE=
 github.com/uozi-tech/cosy-driver-postgres v0.2.1 h1:OICakGuT+omva6QOJCxTJ5Lfr7CGXLmk/zD+aS51Z2o=

+ 1 - 1
internal/cert/auto_cert.go

@@ -35,7 +35,7 @@ func autoCert(certModel *model.Cert) {
 	defer log.Exit()
 
 	if len(certModel.Filename) == 0 {
-		log.Error(errors.New("filename is empty"))
+		log.Error(ErrCertModelFilenameEmpty)
 		return
 	}
 

+ 4 - 5
internal/cert/cert_info.go

@@ -5,7 +5,6 @@ import (
 	"encoding/pem"
 	"github.com/0xJacky/Nginx-UI/internal/helper"
 	"github.com/0xJacky/Nginx-UI/internal/nginx"
-	"github.com/pkg/errors"
 	"os"
 	"time"
 )
@@ -19,24 +18,24 @@ type Info struct {
 
 func GetCertInfo(sslCertificatePath string) (info *Info, err error) {
 	if !helper.IsUnderDirectory(sslCertificatePath, nginx.GetConfPath()) {
-		err = errors.New("ssl certificate path is not under the nginx conf path")
+		err = ErrCertPathIsNotUnderTheNginxConfDir
 		return
 	}
+
 	certData, err := os.ReadFile(sslCertificatePath)
 	if err != nil {
-		err = errors.Wrap(err, "error read certificate")
 		return
 	}
 
 	block, _ := pem.Decode(certData)
 	if block == nil || block.Type != "CERTIFICATE" {
-		err = errors.New("certificate decoding error")
+		err = ErrCertDecode
 		return
 	}
 
 	cert, err := x509.ParseCertificate(block.Bytes)
 	if err != nil {
-		err = errors.Wrap(err, "certificate parsing error")
+		err = ErrCertParse
 		return
 	}
 

+ 13 - 0
internal/cert/errors.go

@@ -0,0 +1,13 @@
+package cert
+
+import "github.com/uozi-tech/cosy"
+
+var (
+	e                                    = cosy.NewErrorScope("cert")
+	ErrCertModelFilenameEmpty            = e.New(50001, "filename is empty")
+	ErrCertPathIsNotUnderTheNginxConfDir = e.New(50002, "cert path is not under the nginx conf dir")
+	ErrCertDecode                        = e.New(50003, "certificate decode error")
+	ErrCertParse                         = e.New(50004, "certificate parse error")
+	ErrPayloadResourceIsNil              = e.New(50005, "payload resource is nil")
+	ErrPathIsNotUnderTheNginxConfDir     = e.New(50006, "path: {0} is not under the nginx conf dir: {1}")
+)

+ 1 - 1
internal/cert/renew.go

@@ -10,7 +10,7 @@ import (
 
 func renew(payload *ConfigPayload, client *lego.Client, l *log.Logger, errChan chan error) {
 	if payload.Resource == nil {
-		errChan <- errors.New("resource is nil")
+		errChan <- ErrPayloadResourceIsNil
 		return
 	}
 

+ 2 - 5
internal/cert/sync.go

@@ -3,7 +3,6 @@ package cert
 import (
 	"bytes"
 	"encoding/json"
-	"fmt"
 	"github.com/0xJacky/Nginx-UI/internal/helper"
 	"github.com/0xJacky/Nginx-UI/internal/nginx"
 	"github.com/0xJacky/Nginx-UI/internal/notification"
@@ -33,13 +32,11 @@ func SyncToRemoteServer(c *model.Cert) (err error) {
 
 	nginxConfPath := nginx.GetConfPath()
 	if !helper.IsUnderDirectory(c.SSLCertificatePath, nginxConfPath) {
-		return fmt.Errorf("ssl_certificate_path: %s is not under the nginx conf path: %s",
-			c.SSLCertificatePath, nginxConfPath)
+		return e.NewWithParams(50006, ErrPathIsNotUnderTheNginxConfDir.Error(), c.SSLCertificatePath, nginxConfPath)
 	}
 
 	if !helper.IsUnderDirectory(c.SSLCertificateKeyPath, nginxConfPath) {
-		return fmt.Errorf("ssl_certificate_key_path: %s is not under the nginx conf path: %s",
-			c.SSLCertificateKeyPath, nginxConfPath)
+		return e.NewWithParams(50006, ErrPathIsNotUnderTheNginxConfDir.Error(), c.SSLCertificateKeyPath, nginxConfPath)
 	}
 
 	certBytes, err := os.ReadFile(c.SSLCertificatePath)

+ 2 - 5
internal/cert/write_file.go

@@ -1,7 +1,6 @@
 package cert
 
 import (
-	"fmt"
 	"github.com/0xJacky/Nginx-UI/internal/helper"
 	"github.com/0xJacky/Nginx-UI/internal/nginx"
 	"os"
@@ -22,13 +21,11 @@ func (c *Content) WriteFile() (err error) {
 
 	nginxConfPath := nginx.GetConfPath()
 	if !helper.IsUnderDirectory(c.SSLCertificatePath, nginxConfPath) {
-		return fmt.Errorf("ssl_certificate_path: %s is not under the nginx conf path: %s",
-			c.SSLCertificatePath, nginxConfPath)
+		return e.NewWithParams(50006, ErrPathIsNotUnderTheNginxConfDir.Error(), c.SSLCertificatePath, nginxConfPath)
 	}
 
 	if !helper.IsUnderDirectory(c.SSLCertificateKeyPath, nginxConfPath) {
-		return fmt.Errorf("ssl_certificate_key_path: %s is not under the nginx conf path: %s",
-			c.SSLCertificateKeyPath, nginxConfPath)
+		return e.NewWithParams(50006, ErrPathIsNotUnderTheNginxConfDir.Error(), c.SSLCertificateKeyPath, nginxConfPath)
 	}
 
 	// MkdirAll creates a directory named path, along with any necessary parents,

+ 8 - 0
internal/config/errors.go

@@ -0,0 +1,8 @@
+package config
+
+import "github.com/uozi-tech/cosy"
+
+var (
+	e                                = cosy.NewErrorScope("config")
+	ErrPathIsNotUnderTheNginxConfDir = e.New(50006, "path: {0} is not under the nginx conf dir: {1}")
+)

+ 3 - 7
internal/config/sync.go

@@ -4,7 +4,6 @@ import (
 	"bytes"
 	"crypto/tls"
 	"encoding/json"
-	"fmt"
 	"github.com/0xJacky/Nginx-UI/internal/helper"
 	"github.com/0xJacky/Nginx-UI/internal/nginx"
 	"github.com/0xJacky/Nginx-UI/internal/notification"
@@ -35,8 +34,7 @@ func SyncToRemoteServer(c *model.Config) (err error) {
 
 	nginxConfPath := nginx.GetConfPath()
 	if !helper.IsUnderDirectory(c.Filepath, nginxConfPath) {
-		return fmt.Errorf("config: %s is not under the nginx conf path: %s",
-			c.Filepath, nginxConfPath)
+		return e.NewWithParams(50006, ErrPathIsNotUnderTheNginxConfDir.Error(), c.Filepath, nginxConfPath)
 	}
 
 	configBytes, err := os.ReadFile(c.Filepath)
@@ -76,13 +74,11 @@ func SyncRenameOnRemoteServer(origPath, newPath string, syncNodeIds []uint64) (e
 
 	nginxConfPath := nginx.GetConfPath()
 	if !helper.IsUnderDirectory(origPath, nginxConfPath) {
-		return fmt.Errorf("config: %s is not under the nginx conf path: %s",
-			origPath, nginxConfPath)
+		return e.NewWithParams(50006, ErrPathIsNotUnderTheNginxConfDir.Error(), origPath, nginxConfPath)
 	}
 
 	if !helper.IsUnderDirectory(newPath, nginxConfPath) {
-		return fmt.Errorf("config: %s is not under the nginx conf path: %s",
-			newPath, nginxConfPath)
+		return e.NewWithParams(50006, ErrPathIsNotUnderTheNginxConfDir.Error(), newPath, nginxConfPath)
 	}
 
 	payload := &RenameConfigPayload{

+ 5 - 7
internal/crypto/aes.go

@@ -5,27 +5,25 @@ import (
 	"crypto/cipher"
 	"crypto/rand"
 	"encoding/base64"
-	"fmt"
 	"github.com/0xJacky/Nginx-UI/settings"
-	"github.com/pkg/errors"
 	"io"
 )
 
 // AesEncrypt encrypts text and given key with AES.
 func AesEncrypt(text []byte) ([]byte, error) {
 	if len(text) == 0 {
-		return nil, errors.New("AesEncrypt text is empty")
+		return nil, ErrPlainTextEmpty
 	}
 	block, err := aes.NewCipher(settings.CryptoSettings.GetSecretMd5())
 	if err != nil {
-		return nil, fmt.Errorf("AesEncrypt invalid key: %v", err)
+		return nil, err
 	}
 
 	b := base64.StdEncoding.EncodeToString(text)
 	ciphertext := make([]byte, aes.BlockSize+len(b))
 	iv := ciphertext[:aes.BlockSize]
 	if _, err = io.ReadFull(rand.Reader, iv); err != nil {
-		return nil, fmt.Errorf("AesEncrypt unable to read IV: %w", err)
+		return nil, err
 	}
 
 	cfb := cipher.NewCFBEncrypter(block, iv)
@@ -42,7 +40,7 @@ func AesDecrypt(text []byte) ([]byte, error) {
 	}
 
 	if len(text) < aes.BlockSize {
-		return nil, errors.New("AesDecrypt ciphertext too short")
+		return nil, ErrCipherTextTooShort
 	}
 
 	iv := text[:aes.BlockSize]
@@ -52,7 +50,7 @@ func AesDecrypt(text []byte) ([]byte, error) {
 
 	data, err := base64.StdEncoding.DecodeString(string(text))
 	if err != nil {
-		return nil, fmt.Errorf("AesDecrypt invalid decrypted base64 string: %w", err)
+		return nil, err
 	}
 
 	return data, nil

+ 9 - 0
internal/crypto/errors.go

@@ -0,0 +1,9 @@
+package crypto
+
+import "github.com/uozi-tech/cosy"
+
+var (
+	e                     = cosy.NewErrorScope("crypto")
+	ErrPlainTextEmpty     = e.New(50001, "plain text is empty")
+	ErrCipherTextTooShort = e.New(50002, "cipher text is too short")
+)

+ 1 - 1
internal/helper/websocket_error.go

@@ -3,7 +3,7 @@ package helper
 import (
 	"strings"
 	"github.com/gorilla/websocket"
-	"github.com/pkg/errors"
+	"errors"
 	"syscall"
 )
 

+ 1 - 1
internal/kernel/skip_install.go

@@ -6,7 +6,7 @@ import (
 	"github.com/0xJacky/Nginx-UI/settings"
 	"github.com/caarlos0/env/v11"
 	"github.com/google/uuid"
-	"github.com/pkg/errors"
+	"errors"
 	"github.com/uozi-tech/cosy/logger"
 	cSettings "github.com/uozi-tech/cosy/settings"
 	"golang.org/x/crypto/bcrypt"

+ 8 - 0
internal/nginx/errors.go

@@ -0,0 +1,8 @@
+package nginx
+
+import "github.com/uozi-tech/cosy"
+
+var (
+	e             = cosy.NewErrorScope("nginx")
+	ErrBlockIsNil = e.New(50001, "block is nil")
+)

+ 1 - 1
internal/nginx/parse.go

@@ -155,7 +155,7 @@ func buildComment(c []string) string {
 
 func parse(block config.IBlock, ngxConfig *NgxConfig) (err error) {
 	if block == nil {
-		err = errors.New("block is nil")
+		err = ErrBlockIsNil
 		return
 	}
 	for _, v := range block.GetDirectives() {

+ 14 - 0
internal/nginx_log/errors.go

@@ -0,0 +1,14 @@
+package nginx_log
+
+import "github.com/uozi-tech/cosy"
+
+var (
+	e                                      = cosy.NewErrorScope("nginx_log")
+	ErrLogPathIsNotUnderTheLogDirWhiteList = e.New(50001, "the log path is not under the paths in settings.NginxSettings.LogDirWhiteList")
+	ErrServerIdxOutOfRange                 = e.New(50002, "serverIdx out of range")
+	ErrDirectiveIdxOutOfRange              = e.New(50003, "directiveIdx out of range")
+	ErrLogDirective                        = e.New(50004, "directive.Params neither access_log nor error_log")
+	ErrDirectiveParamsIsEmpty              = e.New(50005, "directive params is empty")
+	ErrErrorLogPathIsEmpty                 = e.New(50006, "settings.NginxLogSettings.ErrorLogPath is empty, refer to https://nginxui.com/guide/config-nginx.html for more information")
+	ErrAccessLogPathIsEmpty                = e.New(50007, "settings.NginxLogSettings.AccessLogPath is empty, refer to https://nginxui.com/guide/config-nginx.html for more information")
+)

+ 41 - 0
internal/nginx_log/nginx_log.go

@@ -0,0 +1,41 @@
+package nginx_log
+
+import (
+	"fmt"
+	"github.com/0xJacky/Nginx-UI/internal/cache"
+	"github.com/0xJacky/Nginx-UI/internal/helper"
+	"github.com/0xJacky/Nginx-UI/internal/nginx"
+	"github.com/0xJacky/Nginx-UI/settings"
+	"path/filepath"
+)
+
+// IsLogPathUnderWhiteList checks if the log path is under one of the paths in LogDirWhiteList
+func IsLogPathUnderWhiteList(path string) bool {
+	cacheKey := fmt.Sprintf("isLogPathUnderWhiteList:%s", path)
+	res, ok := cache.Get(cacheKey)
+
+	// deep copy
+	logDirWhiteList := append([]string{}, settings.NginxSettings.LogDirWhiteList...)
+
+	accessLogPath := nginx.GetAccessLogPath()
+	errorLogPath := nginx.GetErrorLogPath()
+
+	if accessLogPath != "" {
+		logDirWhiteList = append(logDirWhiteList, filepath.Dir(accessLogPath))
+	}
+	if errorLogPath != "" {
+		logDirWhiteList = append(logDirWhiteList, filepath.Dir(errorLogPath))
+	}
+
+	// no cache, check it
+	if !ok {
+		for _, whitePath := range logDirWhiteList {
+			if helper.IsUnderDirectory(path, whitePath) {
+				cache.Set(cacheKey, true, 0)
+				return true
+			}
+		}
+		return false
+	}
+	return res.(bool)
+}

+ 3 - 3
internal/self_check/errors.go

@@ -11,9 +11,9 @@ var (
 	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")
+	ErrFailedToCreateBackup             = e.New(5002, "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")
+	ErrStreamAvailableNotExist          = e.New(4048, "Streams-available directory not exist")
+	ErrStreamEnabledNotExist            = e.New(4049, "Streams-enabled directory not exist")
 )

+ 2 - 3
internal/self_check/nginx_conf.go

@@ -6,7 +6,6 @@ import (
 	"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"
@@ -88,7 +87,7 @@ func FixNginxConfIncludeSites() error {
 	}
 
 	// create a backup file (+.bak.timestamp)
-	backupPath := path + ".bak." + cast.ToString(time.Now().Unix())
+	backupPath := fmt.Sprintf("%s.bak.%d", path, time.Now().Unix())
 	err = os.WriteFile(backupPath, content, 0644)
 	if err != nil {
 		return ErrFailedToCreateBackup
@@ -133,7 +132,7 @@ func FixNginxConfIncludeStreams() error {
 	}
 
 	// create a backup file (+.bak.timestamp)
-	backupPath := path + ".bak." + cast.ToString(time.Now().Unix())
+	backupPath := fmt.Sprintf("%s.bak.%d", path, time.Now().Unix())
 	err = os.WriteFile(backupPath, content, 0644)
 	if err != nil {
 		return ErrFailedToCreateBackup

+ 2 - 2
internal/site/delete.go

@@ -29,11 +29,11 @@ func Delete(name string) (err error) {
 	enabledPath := nginx.GetConfPath("sites-enabled", name)
 
 	if !helper.FileExists(availablePath) {
-		return fmt.Errorf("site not found")
+		return ErrSiteNotFound
 	}
 
 	if helper.FileExists(enabledPath) {
-		return fmt.Errorf("site is enabled")
+		return ErrSiteIsEnabled
 	}
 
 	certModel := model.Cert{Filename: name}

+ 1 - 2
internal/site/duplicate.go

@@ -3,7 +3,6 @@ package site
 import (
 	"github.com/0xJacky/Nginx-UI/internal/helper"
 	"github.com/0xJacky/Nginx-UI/internal/nginx"
-	"github.com/pkg/errors"
 )
 
 // Duplicate duplicates a site by copying the file
@@ -12,7 +11,7 @@ func Duplicate(src, dst string) (err error) {
 	dst = nginx.GetConfPath("sites-available", dst)
 
 	if helper.FileExists(dst) {
-		return errors.New("file exists")
+		return ErrDstFileExists
 	}
 
 	_, err = helper.CopyFile(src, dst)

+ 10 - 0
internal/site/errors.go

@@ -0,0 +1,10 @@
+package site
+
+import "github.com/uozi-tech/cosy"
+
+var (
+	e                = cosy.NewErrorScope("site")
+	ErrSiteNotFound  = e.New(40401, "site not found")
+	ErrDstFileExists = e.New(50001, "destination file already exists")
+	ErrSiteIsEnabled = e.New(50002, "site is enabled")
+)

+ 4 - 4
internal/site/rename.go

@@ -24,7 +24,7 @@ func Rename(oldName string, newName string) (err error) {
 
 	// check if dst file exists, do not rename
 	if helper.FileExists(newPath) {
-		return fmt.Errorf("file exists")
+		return ErrDstFileExists
 	}
 
 	s := query.Site
@@ -84,9 +84,9 @@ func syncRename(oldName, newName string) {
 			client.SetBaseURL(node.URL)
 			resp, err := client.R().
 				SetHeader("X-Node-Secret", node.Token).
-					SetBody(map[string]string{
-						"new_name": newName,
-					}).
+				SetBody(map[string]string{
+					"new_name": newName,
+				}).
 				Post(fmt.Sprintf("/api/sites/%s/rename", oldName))
 			if err != nil {
 				notification.Error("Rename Remote Site Error", err.Error())

+ 1 - 1
internal/site/save.go

@@ -19,7 +19,7 @@ import (
 func Save(name string, content string, overwrite bool, siteCategoryId uint64, syncNodeIds []uint64) (err error) {
 	path := nginx.GetConfPath("sites-available", name)
 	if !overwrite && helper.FileExists(path) {
-		return fmt.Errorf("file exists")
+		return ErrDstFileExists
 	}
 
 	err = os.WriteFile(path, []byte(content), 0644)

+ 15 - 0
internal/user/errors.go

@@ -0,0 +1,15 @@
+package user
+
+import "github.com/uozi-tech/cosy"
+
+var (
+	e                         = cosy.NewErrorScope("user")
+	ErrPasswordIncorrect      = e.New(40301, "password incorrect")
+	ErrUserBanned             = e.New(40303, "user banned")
+	ErrOTPCode                = e.New(40304, "invalid otp code")
+	ErrRecoveryCode           = e.New(40305, "invalid recovery code")
+	ErrWebAuthnNotConfigured  = e.New(50000, "WebAuthn settings are not configured")
+	ErrUserNotEnabledOTPAs2FA = e.New(50001, "user not enabled otp as 2fa")
+	ErrOTPOrRecoveryCodeEmpty = e.New(50002, "otp or recovery code empty")
+	ErrSessionNotFound        = e.New(40401, "session not found")
+)

+ 0 - 6
internal/user/login.go

@@ -1,7 +1,6 @@
 package user
 
 import (
-	"errors"
 	"github.com/0xJacky/Nginx-UI/model"
 	"github.com/0xJacky/Nginx-UI/query"
 	"github.com/0xJacky/Nginx-UI/settings"
@@ -9,11 +8,6 @@ import (
 	"time"
 )
 
-var (
-	ErrPasswordIncorrect = errors.New("password incorrect")
-	ErrUserBanned        = errors.New("user banned")
-)
-
 func Login(name string, password string) (user *model.User, err error) {
 	u := query.User
 

+ 0 - 6
internal/user/otp.go

@@ -9,16 +9,10 @@ import (
 	"github.com/0xJacky/Nginx-UI/internal/crypto"
 	"github.com/0xJacky/Nginx-UI/model"
 	"github.com/google/uuid"
-	"github.com/pkg/errors"
 	"github.com/pquerna/otp/totp"
 	"time"
 )
 
-var (
-	ErrOTPCode      = errors.New("invalid otp code")
-	ErrRecoveryCode = errors.New("invalid recovery code")
-)
-
 func VerifyOTP(user *model.User, otp, recoveryCode string) (err error) {
 	if otp != "" {
 		decrypted, err := crypto.AesDecrypt(user.OTPSecret)

+ 1 - 1
main.go

@@ -9,7 +9,7 @@ import (
 	"github.com/0xJacky/Nginx-UI/settings"
 	"github.com/gin-gonic/gin"
 	"github.com/jpillora/overseer"
-	"github.com/pkg/errors"
+	"errors"
 	"github.com/uozi-tech/cosy"
 	cKernel "github.com/uozi-tech/cosy/kernel"
 	"github.com/uozi-tech/cosy/logger"

+ 1 - 1
model/chatgpt_log.go

@@ -4,7 +4,7 @@ import (
 	"database/sql/driver"
 	"encoding/json"
 	"fmt"
-	"github.com/pkg/errors"
+	"errors"
 	"github.com/sashabaranov/go-openai"
 )
 

+ 2 - 1
router/routers.go

@@ -6,6 +6,7 @@ import (
 	"github.com/0xJacky/Nginx-UI/api/cluster"
 	"github.com/0xJacky/Nginx-UI/api/config"
 	"github.com/0xJacky/Nginx-UI/api/nginx"
+	nginxLog "github.com/0xJacky/Nginx-UI/api/nginx_log"
 	"github.com/0xJacky/Nginx-UI/api/notification"
 	"github.com/0xJacky/Nginx-UI/api/openai"
 	"github.com/0xJacky/Nginx-UI/api/public"
@@ -75,7 +76,7 @@ func InitRouter() {
 			{
 				terminal.InitRouter(o)
 			}
-			nginx.InitNginxLogRouter(w)
+			nginxLog.InitRouter(w)
 			upstream.InitRouter(w)
 			system.InitWebSocketRouter(w)
 		}

+ 1 - 1
test/chatgpt_test.go

@@ -4,7 +4,7 @@ import (
 	"context"
 	"fmt"
 	"github.com/0xJacky/Nginx-UI/settings"
-	"github.com/pkg/errors"
+	"errors"
 	"github.com/sashabaranov/go-openai"
 	"github.com/uozi-tech/cosy/sandbox"
 	"io"

Some files were not shown because too many files changed in this diff