Parcourir la source

refactor: replace api error handler with cosy err handler

Jacky il y a 1 mois
Parent
commit
b469ca7961

+ 0 - 24
api/api.go

@@ -1,38 +1,14 @@
 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"
 )
 
 func CurrentUser(c *gin.Context) *model.User {
 	return c.MustGet("user").(*model.User)
 }
 
-func ErrHandler(c *gin.Context, err error) {
-	logger.GetLogger().Errorln(err)
-	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) {
 	c.Header("Content-Type", "text/event-stream")
 	c.Header("Cache-Control", "no-cache")

+ 6 - 6
api/certificate/acme_user.go

@@ -1,14 +1,14 @@
 package certificate
 
 import (
-	"github.com/0xJacky/Nginx-UI/api"
+	"net/http"
+
 	"github.com/0xJacky/Nginx-UI/model"
 	"github.com/0xJacky/Nginx-UI/query"
 	"github.com/0xJacky/Nginx-UI/settings"
 	"github.com/gin-gonic/gin"
 	"github.com/spf13/cast"
 	"github.com/uozi-tech/cosy"
-	"net/http"
 )
 
 func GetAcmeUser(c *gin.Context) {
@@ -16,7 +16,7 @@ func GetAcmeUser(c *gin.Context) {
 	id := cast.ToUint64(c.Param("id"))
 	user, err := u.FirstByID(id)
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 	c.JSON(http.StatusOK, user)
@@ -83,17 +83,17 @@ func RegisterAcmeUser(c *gin.Context) {
 	u := query.AcmeUser
 	user, err := u.FirstByID(id)
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 	err = user.Register()
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 	_, err = u.Where(u.ID.Eq(id)).Updates(user)
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 	c.JSON(http.StatusOK, user)

+ 11 - 11
api/certificate/certificate.go

@@ -1,7 +1,9 @@
 package certificate
 
 import (
-	"github.com/0xJacky/Nginx-UI/api"
+	"net/http"
+	"os"
+
 	"github.com/0xJacky/Nginx-UI/internal/cert"
 	"github.com/0xJacky/Nginx-UI/internal/helper"
 	"github.com/0xJacky/Nginx-UI/internal/nginx"
@@ -12,8 +14,6 @@ import (
 	"github.com/go-acme/lego/v4/certcrypto"
 	"github.com/spf13/cast"
 	"github.com/uozi-tech/cosy"
-	"net/http"
-	"os"
 )
 
 type APICertificate struct {
@@ -74,7 +74,7 @@ func GetCert(c *gin.Context) {
 	certModel, err := q.FirstByID(cast.ToUint64(c.Param("id")))
 
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 
@@ -114,7 +114,7 @@ func AddCert(c *gin.Context) {
 
 	err := certModel.Insert()
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 
@@ -127,7 +127,7 @@ func AddCert(c *gin.Context) {
 
 	err = content.WriteFile()
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 
@@ -153,7 +153,7 @@ func ModifyCert(c *gin.Context) {
 
 	certModel, err := q.FirstByID(id)
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 
@@ -169,7 +169,7 @@ func ModifyCert(c *gin.Context) {
 	})
 
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 
@@ -182,7 +182,7 @@ func ModifyCert(c *gin.Context) {
 
 	err = content.WriteFile()
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 
@@ -218,7 +218,7 @@ func SyncCertificate(c *gin.Context) {
 
 	err := db.Where(certModel).FirstOrCreate(certModel).Error
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 
@@ -231,7 +231,7 @@ func SyncCertificate(c *gin.Context) {
 
 	err = content.WriteFile()
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 

+ 6 - 6
api/certificate/dns_credential.go

@@ -1,14 +1,14 @@
 package certificate
 
 import (
-	"github.com/0xJacky/Nginx-UI/api"
+	"net/http"
+
 	"github.com/0xJacky/Nginx-UI/internal/cert/dns"
 	"github.com/0xJacky/Nginx-UI/model"
 	"github.com/0xJacky/Nginx-UI/query"
 	"github.com/gin-gonic/gin"
 	"github.com/spf13/cast"
 	"github.com/uozi-tech/cosy"
-	"net/http"
 )
 
 func GetDnsCredential(c *gin.Context) {
@@ -18,7 +18,7 @@ func GetDnsCredential(c *gin.Context) {
 
 	dnsCredential, err := d.FirstByID(id)
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 	type apiDnsCredential struct {
@@ -62,7 +62,7 @@ func AddDnsCredential(c *gin.Context) {
 
 	err := d.Create(&dnsCredential)
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 
@@ -81,7 +81,7 @@ func EditDnsCredential(c *gin.Context) {
 
 	dnsCredential, err := d.FirstByID(id)
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 
@@ -93,7 +93,7 @@ func EditDnsCredential(c *gin.Context) {
 	})
 
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 

+ 2 - 2
api/cluster/environment.go

@@ -27,7 +27,7 @@ func GetEnvironment(c *gin.Context) {
 
 	env, err := envQuery.FirstByID(id)
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 
@@ -161,7 +161,7 @@ func DeleteEnvironment(c *gin.Context) {
 func LoadEnvironmentFromSettings(c *gin.Context) {
 	err := settings.ReloadCluster()
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 

+ 2 - 2
api/cluster/node.go

@@ -3,7 +3,6 @@ package cluster
 import (
 	"net/http"
 
-	"github.com/0xJacky/Nginx-UI/api"
 	analytic2 "github.com/0xJacky/Nginx-UI/internal/analytic"
 	"github.com/0xJacky/Nginx-UI/internal/upgrader"
 	"github.com/0xJacky/Nginx-UI/internal/version"
@@ -11,6 +10,7 @@ import (
 	"github.com/gin-gonic/gin"
 	"github.com/shirou/gopsutil/v4/cpu"
 	"github.com/shirou/gopsutil/v4/disk"
+	"github.com/uozi-tech/cosy"
 )
 
 func GetCurrentNode(c *gin.Context) {
@@ -23,7 +23,7 @@ func GetCurrentNode(c *gin.Context) {
 
 	runtimeInfo, err := upgrader.GetRuntimeInfo()
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 	cpuInfo, _ := cpu.Info()

+ 10 - 10
api/config/add.go

@@ -1,7 +1,11 @@
 package config
 
 import (
-	"github.com/0xJacky/Nginx-UI/api"
+	"net/http"
+	"os"
+	"path/filepath"
+	"time"
+
 	"github.com/0xJacky/Nginx-UI/internal/config"
 	"github.com/0xJacky/Nginx-UI/internal/helper"
 	"github.com/0xJacky/Nginx-UI/internal/nginx"
@@ -10,10 +14,6 @@ import (
 	"github.com/gin-gonic/gin"
 	"github.com/sashabaranov/go-openai"
 	"github.com/uozi-tech/cosy"
-	"net/http"
-	"os"
-	"path/filepath"
-	"time"
 )
 
 func AddConfig(c *gin.Context) {
@@ -48,14 +48,14 @@ func AddConfig(c *gin.Context) {
 	if !helper.FileExists(dir) {
 		err := os.MkdirAll(dir, 0755)
 		if err != nil {
-			api.ErrHandler(c, err)
+			cosy.ErrHandler(c, err)
 			return
 		}
 	}
 
 	err := os.WriteFile(path, []byte(content), 0644)
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 
@@ -70,7 +70,7 @@ func AddConfig(c *gin.Context) {
 	q := query.Config
 	_, err = q.Where(q.Filepath.Eq(path)).Delete()
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 
@@ -83,13 +83,13 @@ func AddConfig(c *gin.Context) {
 
 	err = q.Create(cfg)
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 
 	err = config.SyncToRemoteServer(cfg)
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 

+ 9 - 8
api/config/get.go

@@ -1,16 +1,17 @@
 package config
 
 import (
-	"github.com/0xJacky/Nginx-UI/api"
+	"net/http"
+	"os"
+	"path/filepath"
+
 	"github.com/0xJacky/Nginx-UI/internal/config"
 	"github.com/0xJacky/Nginx-UI/internal/helper"
 	"github.com/0xJacky/Nginx-UI/internal/nginx"
 	"github.com/0xJacky/Nginx-UI/query"
 	"github.com/gin-gonic/gin"
 	"github.com/sashabaranov/go-openai"
-	"net/http"
-	"os"
-	"path/filepath"
+	"github.com/uozi-tech/cosy"
 )
 
 type APIConfigResp struct {
@@ -32,20 +33,20 @@ func GetConfig(c *gin.Context) {
 
 	stat, err := os.Stat(absPath)
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 
 	content, err := os.ReadFile(absPath)
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 	q := query.Config
 	g := query.ChatGPTLog
 	chatgpt, err := g.Where(g.Name.Eq(absPath)).FirstOrCreate()
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 
@@ -55,7 +56,7 @@ func GetConfig(c *gin.Context) {
 
 	cfg, err := q.Where(q.Filepath.Eq(absPath)).FirstOrInit()
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 

+ 6 - 5
api/config/list.go

@@ -1,14 +1,15 @@
 package config
 
 import (
-	"github.com/0xJacky/Nginx-UI/api"
+	"net/http"
+	"os"
+	"strings"
+
 	"github.com/0xJacky/Nginx-UI/internal/config"
 	"github.com/0xJacky/Nginx-UI/internal/nginx"
 	"github.com/gin-gonic/gin"
+	"github.com/uozi-tech/cosy"
 	"github.com/uozi-tech/cosy/logger"
-	"net/http"
-	"os"
-	"strings"
 )
 
 func GetConfigs(c *gin.Context) {
@@ -19,7 +20,7 @@ func GetConfigs(c *gin.Context) {
 
 	configFiles, err := os.ReadDir(nginx.GetConfPath(dir))
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 

+ 4 - 4
api/config/mkdir.go

@@ -1,13 +1,13 @@
 package config
 
 import (
-	"github.com/0xJacky/Nginx-UI/api"
+	"net/http"
+	"os"
+
 	"github.com/0xJacky/Nginx-UI/internal/helper"
 	"github.com/0xJacky/Nginx-UI/internal/nginx"
 	"github.com/gin-gonic/gin"
 	"github.com/uozi-tech/cosy"
-	"net/http"
-	"os"
 )
 
 func Mkdir(c *gin.Context) {
@@ -28,7 +28,7 @@ func Mkdir(c *gin.Context) {
 	}
 	err := os.Mkdir(fullPath, 0755)
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 

+ 11 - 11
api/config/modify.go

@@ -1,7 +1,11 @@
 package config
 
 import (
-	"github.com/0xJacky/Nginx-UI/api"
+	"net/http"
+	"os"
+	"path/filepath"
+	"time"
+
 	"github.com/0xJacky/Nginx-UI/internal/config"
 	"github.com/0xJacky/Nginx-UI/internal/helper"
 	"github.com/0xJacky/Nginx-UI/internal/nginx"
@@ -11,10 +15,6 @@ import (
 	"github.com/sashabaranov/go-openai"
 	"github.com/uozi-tech/cosy"
 	"gorm.io/gen/field"
-	"net/http"
-	"os"
-	"path/filepath"
-	"time"
 )
 
 type EditConfigJson struct {
@@ -43,14 +43,14 @@ func EditConfig(c *gin.Context) {
 	content := json.Content
 	origContent, err := os.ReadFile(absPath)
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 
 	if content != "" && content != string(origContent) {
 		err = os.WriteFile(absPath, []byte(content), 0644)
 		if err != nil {
-			api.ErrHandler(c, err)
+			cosy.ErrHandler(c, err)
 			return
 		}
 	}
@@ -60,7 +60,7 @@ func EditConfig(c *gin.Context) {
 		Name: filepath.Base(absPath),
 	})).Where(q.Filepath.Eq(absPath)).FirstOrCreate()
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 
@@ -71,7 +71,7 @@ func EditConfig(c *gin.Context) {
 			SyncOverwrite: json.SyncOverwrite,
 		})
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 
@@ -82,7 +82,7 @@ func EditConfig(c *gin.Context) {
 	g := query.ChatGPTLog
 	err = config.SyncToRemoteServer(cfg)
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 
@@ -96,7 +96,7 @@ func EditConfig(c *gin.Context) {
 
 	chatgpt, err := g.Where(g.Name.Eq(absPath)).FirstOrCreate()
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 

+ 10 - 10
api/config/rename.go

@@ -1,7 +1,11 @@
 package config
 
 import (
-	"github.com/0xJacky/Nginx-UI/api"
+	"net/http"
+	"os"
+	"path/filepath"
+	"strings"
+
 	"github.com/0xJacky/Nginx-UI/internal/config"
 	"github.com/0xJacky/Nginx-UI/internal/helper"
 	"github.com/0xJacky/Nginx-UI/internal/nginx"
@@ -9,10 +13,6 @@ import (
 	"github.com/0xJacky/Nginx-UI/query"
 	"github.com/gin-gonic/gin"
 	"github.com/uozi-tech/cosy"
-	"net/http"
-	"os"
-	"path/filepath"
-	"strings"
 )
 
 func Rename(c *gin.Context) {
@@ -45,7 +45,7 @@ func Rename(c *gin.Context) {
 
 	stat, err := os.Stat(origFullPath)
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 
@@ -58,7 +58,7 @@ func Rename(c *gin.Context) {
 
 	err = os.Rename(origFullPath, newFullPath)
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 
@@ -67,7 +67,7 @@ func Rename(c *gin.Context) {
 	q := query.Config
 	cfg, err := q.Where(q.Filepath.Eq(origFullPath)).FirstOrInit()
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 	if !stat.IsDir() {
@@ -85,14 +85,14 @@ func Rename(c *gin.Context) {
 		Name:     json.NewName,
 	})
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 
 	if len(json.SyncNodeIds) > 0 {
 		err = config.SyncRenameOnRemoteServer(origFullPath, newFullPath, json.SyncNodeIds)
 		if err != nil {
-			api.ErrHandler(c, err)
+			cosy.ErrHandler(c, err)
 			return
 		}
 	}

+ 2 - 2
api/crypto/crypto.go

@@ -3,16 +3,16 @@ package crypto
 import (
 	"net/http"
 
-	"github.com/0xJacky/Nginx-UI/api"
 	"github.com/0xJacky/Nginx-UI/internal/crypto"
 	"github.com/gin-gonic/gin"
+	"github.com/uozi-tech/cosy"
 )
 
 // GetPublicKey generates a new ED25519 key pair and registers it in the cache
 func GetPublicKey(c *gin.Context) {
 	params, err := crypto.GetCryptoParams()
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 

+ 5 - 5
api/nginx/nginx.go

@@ -1,11 +1,11 @@
 package nginx
 
 import (
-	"github.com/0xJacky/Nginx-UI/api"
+	"net/http"
+
 	"github.com/0xJacky/Nginx-UI/internal/nginx"
 	"github.com/gin-gonic/gin"
 	"github.com/uozi-tech/cosy"
-	"net/http"
 )
 
 func BuildNginxConfig(c *gin.Context) {
@@ -15,7 +15,7 @@ func BuildNginxConfig(c *gin.Context) {
 	}
 	content, err := ngxConf.BuildConfig()
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 	c.JSON(http.StatusOK, gin.H{
@@ -34,7 +34,7 @@ func TokenizeNginxConfig(c *gin.Context) {
 
 	ngxConfig, err := nginx.ParseNgxConfigByContent(json.Content)
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 	c.JSON(http.StatusOK, ngxConfig)
@@ -50,7 +50,7 @@ func FormatNginxConfig(c *gin.Context) {
 	}
 	content, err := nginx.FmtCode(json.Content)
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 	c.JSON(http.StatusOK, gin.H{

+ 5 - 5
api/notification/notification.go

@@ -1,13 +1,13 @@
 package notification
 
 import (
-	"github.com/0xJacky/Nginx-UI/api"
+	"net/http"
+
 	"github.com/0xJacky/Nginx-UI/model"
 	"github.com/0xJacky/Nginx-UI/query"
 	"github.com/gin-gonic/gin"
 	"github.com/spf13/cast"
 	"github.com/uozi-tech/cosy"
-	"net/http"
 )
 
 func Get(c *gin.Context) {
@@ -18,7 +18,7 @@ func Get(c *gin.Context) {
 	data, err := n.FirstByID(id)
 
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 
@@ -39,14 +39,14 @@ func DestroyAll(c *gin.Context) {
 	err := db.Exec("DELETE FROM notifications").Error
 
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 	// reset auto increment
 	err = db.Exec("UPDATE sqlite_sequence SET seq = 0 WHERE name = 'notifications';").Error
 
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 

+ 4 - 4
api/openai/store.go

@@ -1,13 +1,13 @@
 package openai
 
 import (
-	"github.com/0xJacky/Nginx-UI/api"
+	"net/http"
+
 	"github.com/0xJacky/Nginx-UI/model"
 	"github.com/0xJacky/Nginx-UI/query"
 	"github.com/gin-gonic/gin"
 	"github.com/sashabaranov/go-openai"
 	"github.com/uozi-tech/cosy"
-	"net/http"
 )
 
 func StoreChatGPTRecord(c *gin.Context) {
@@ -25,7 +25,7 @@ func StoreChatGPTRecord(c *gin.Context) {
 	_, err := g.Where(g.Name.Eq(name)).FirstOrCreate()
 
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 
@@ -35,7 +35,7 @@ func StoreChatGPTRecord(c *gin.Context) {
 	})
 
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 

+ 3 - 3
api/settings/settings.go

@@ -2,14 +2,14 @@ package settings
 
 import (
 	"fmt"
-	"github.com/0xJacky/Nginx-UI/api"
+	"net/http"
+
 	"github.com/0xJacky/Nginx-UI/internal/cron"
 	"github.com/0xJacky/Nginx-UI/internal/nginx"
 	"github.com/0xJacky/Nginx-UI/settings"
 	"github.com/gin-gonic/gin"
 	"github.com/uozi-tech/cosy"
 	cSettings "github.com/uozi-tech/cosy/settings"
-	"net/http"
 )
 
 func GetServerName(c *gin.Context) {
@@ -87,7 +87,7 @@ func SaveSettings(c *gin.Context) {
 
 	err := settings.Save()
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 

+ 4 - 4
api/sites/advance.go

@@ -1,12 +1,12 @@
 package sites
 
 import (
-	"github.com/0xJacky/Nginx-UI/api"
+	"net/http"
+
 	"github.com/0xJacky/Nginx-UI/internal/nginx"
 	"github.com/0xJacky/Nginx-UI/query"
 	"github.com/gin-gonic/gin"
 	"github.com/uozi-tech/cosy"
-	"net/http"
 )
 
 func DomainEditByAdvancedMode(c *gin.Context) {
@@ -25,14 +25,14 @@ func DomainEditByAdvancedMode(c *gin.Context) {
 
 	_, err := s.Where(s.Path.Eq(path)).FirstOrCreate()
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 
 	_, err = s.Where(s.Path.Eq(path)).Update(s.Advanced, json.Advanced)
 
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 

+ 6 - 6
api/sites/auto_cert.go

@@ -1,13 +1,13 @@
 package sites
 
 import (
-	"github.com/0xJacky/Nginx-UI/api"
+	"net/http"
+
 	"github.com/0xJacky/Nginx-UI/internal/helper"
 	"github.com/0xJacky/Nginx-UI/model"
 	"github.com/gin-gonic/gin"
 	"github.com/go-acme/lego/v4/certcrypto"
 	"github.com/uozi-tech/cosy"
-	"net/http"
 )
 
 func AddDomainToAutoCert(c *gin.Context) {
@@ -27,7 +27,7 @@ func AddDomainToAutoCert(c *gin.Context) {
 	certModel, err := model.FirstOrCreateCert(name, helper.GetKeyType(json.KeyType))
 
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 
@@ -40,7 +40,7 @@ func AddDomainToAutoCert(c *gin.Context) {
 	})
 
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 
@@ -52,7 +52,7 @@ func RemoveDomainFromAutoCert(c *gin.Context) {
 	certModel, err := model.FirstCert(name)
 
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 
@@ -61,7 +61,7 @@ func RemoveDomainFromAutoCert(c *gin.Context) {
 	})
 
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 	c.JSON(http.StatusOK, nil)

+ 3 - 3
api/sites/duplicate.go

@@ -1,11 +1,11 @@
 package sites
 
 import (
-	"github.com/0xJacky/Nginx-UI/api"
+	"net/http"
+
 	"github.com/0xJacky/Nginx-UI/internal/site"
 	"github.com/gin-gonic/gin"
 	"github.com/uozi-tech/cosy"
-	"net/http"
 )
 
 func DuplicateSite(c *gin.Context) {
@@ -23,7 +23,7 @@ func DuplicateSite(c *gin.Context) {
 
 	err := site.Duplicate(src, json.Name)
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 

+ 9 - 8
api/sites/list.go

@@ -1,7 +1,11 @@
 package sites
 
 import (
-	"github.com/0xJacky/Nginx-UI/api"
+	"net/http"
+	"os"
+	"path/filepath"
+	"strings"
+
 	"github.com/0xJacky/Nginx-UI/internal/config"
 	"github.com/0xJacky/Nginx-UI/internal/nginx"
 	"github.com/0xJacky/Nginx-UI/model"
@@ -9,10 +13,7 @@ import (
 	"github.com/gin-gonic/gin"
 	"github.com/samber/lo"
 	"github.com/spf13/cast"
-	"net/http"
-	"os"
-	"path/filepath"
-	"strings"
+	"github.com/uozi-tech/cosy"
 )
 
 func GetSiteList(c *gin.Context) {
@@ -24,13 +25,13 @@ func GetSiteList(c *gin.Context) {
 
 	configFiles, err := os.ReadDir(nginx.GetConfPath("sites-available"))
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 
 	enabledConfig, err := os.ReadDir(nginx.GetConfPath("sites-enabled"))
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 
@@ -41,7 +42,7 @@ func GetSiteList(c *gin.Context) {
 	}
 	sites, err := sTx.Find()
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 	sitesMap := lo.SliceToMap(sites, func(item *model.Site) (string, *model.Site) {

+ 12 - 12
api/sites/site.go

@@ -1,7 +1,9 @@
 package sites
 
 import (
-	"github.com/0xJacky/Nginx-UI/api"
+	"net/http"
+	"os"
+
 	"github.com/0xJacky/Nginx-UI/internal/cert"
 	"github.com/0xJacky/Nginx-UI/internal/nginx"
 	"github.com/0xJacky/Nginx-UI/internal/site"
@@ -12,8 +14,6 @@ import (
 	"github.com/uozi-tech/cosy"
 	"github.com/uozi-tech/cosy/logger"
 	"gorm.io/gorm/clause"
-	"net/http"
-	"os"
 )
 
 func GetSite(c *gin.Context) {
@@ -36,7 +36,7 @@ func GetSite(c *gin.Context) {
 	g := query.ChatGPTLog
 	chatgpt, err := g.Where(g.Name.Eq(path)).FirstOrCreate()
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 
@@ -47,7 +47,7 @@ func GetSite(c *gin.Context) {
 	s := query.Site
 	siteModel, err := s.Where(s.Path.Eq(path)).FirstOrCreate()
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 
@@ -59,7 +59,7 @@ func GetSite(c *gin.Context) {
 	if siteModel.Advanced {
 		origContent, err := os.ReadFile(path)
 		if err != nil {
-			api.ErrHandler(c, err)
+			cosy.ErrHandler(c, err)
 			return
 		}
 
@@ -78,7 +78,7 @@ func GetSite(c *gin.Context) {
 
 	nginxConfig, err := nginx.ParseNgxConfig(path)
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 
@@ -126,7 +126,7 @@ func SaveSite(c *gin.Context) {
 
 	err := site.Save(name, json.Content, json.Overwrite, json.SiteCategoryID, json.SyncNodeIDs)
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 
@@ -144,7 +144,7 @@ func RenameSite(c *gin.Context) {
 
 	err := site.Rename(oldName, json.NewName)
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 
@@ -156,7 +156,7 @@ func RenameSite(c *gin.Context) {
 func EnableSite(c *gin.Context) {
 	err := site.Enable(c.Param("name"))
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 
@@ -168,7 +168,7 @@ func EnableSite(c *gin.Context) {
 func DisableSite(c *gin.Context) {
 	err := site.Disable(c.Param("name"))
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 
@@ -180,7 +180,7 @@ func DisableSite(c *gin.Context) {
 func DeleteSite(c *gin.Context) {
 	err := site.Delete(c.Param("name"))
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 

+ 2 - 3
api/streams/advance.go

@@ -3,7 +3,6 @@ package streams
 import (
 	"net/http"
 
-	"github.com/0xJacky/Nginx-UI/api"
 	"github.com/0xJacky/Nginx-UI/internal/nginx"
 	"github.com/0xJacky/Nginx-UI/query"
 	"github.com/gin-gonic/gin"
@@ -26,14 +25,14 @@ func AdvancedEdit(c *gin.Context) {
 
 	_, err := s.Where(s.Path.Eq(path)).FirstOrCreate()
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 
 	_, err = s.Where(s.Path.Eq(path)).Update(s.Advanced, json.Advanced)
 
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 

+ 3 - 3
api/streams/duplicate.go

@@ -1,12 +1,12 @@
 package streams
 
 import (
-	"github.com/0xJacky/Nginx-UI/api"
+	"net/http"
+
 	"github.com/0xJacky/Nginx-UI/internal/helper"
 	"github.com/0xJacky/Nginx-UI/internal/nginx"
 	"github.com/gin-gonic/gin"
 	"github.com/uozi-tech/cosy"
-	"net/http"
 )
 
 func Duplicate(c *gin.Context) {
@@ -35,7 +35,7 @@ func Duplicate(c *gin.Context) {
 	_, err := helper.CopyFile(src, dst)
 
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 

+ 11 - 12
api/streams/streams.go

@@ -6,7 +6,6 @@ import (
 	"strings"
 	"time"
 
-	"github.com/0xJacky/Nginx-UI/api"
 	"github.com/0xJacky/Nginx-UI/internal/config"
 	"github.com/0xJacky/Nginx-UI/internal/nginx"
 	"github.com/0xJacky/Nginx-UI/internal/stream"
@@ -35,13 +34,13 @@ func GetStreams(c *gin.Context) {
 
 	configFiles, err := os.ReadDir(nginx.GetConfPath("streams-available"))
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 
 	enabledConfig, err := os.ReadDir(nginx.GetConfPath("streams-enabled"))
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 
@@ -98,7 +97,7 @@ func GetStream(c *gin.Context) {
 	chatgpt, err := g.Where(g.Name.Eq(path)).FirstOrCreate()
 
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 
@@ -109,14 +108,14 @@ func GetStream(c *gin.Context) {
 	s := query.Stream
 	streamModel, err := s.Where(s.Path.Eq(path)).FirstOrCreate()
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 
 	if streamModel.Advanced {
 		origContent, err := os.ReadFile(path)
 		if err != nil {
-			api.ErrHandler(c, err)
+			cosy.ErrHandler(c, err)
 			return
 		}
 
@@ -136,7 +135,7 @@ func GetStream(c *gin.Context) {
 	nginxConfig, err := nginx.ParseNgxConfig(path)
 
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 
@@ -168,7 +167,7 @@ func SaveStream(c *gin.Context) {
 
 	err := stream.Save(name, json.Content, json.Overwrite, json.SyncNodeIDs)
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 
@@ -178,7 +177,7 @@ func SaveStream(c *gin.Context) {
 func EnableStream(c *gin.Context) {
 	err := stream.Enable(c.Param("name"))
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 
@@ -190,7 +189,7 @@ func EnableStream(c *gin.Context) {
 func DisableStream(c *gin.Context) {
 	err := stream.Disable(c.Param("name"))
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 
@@ -202,7 +201,7 @@ func DisableStream(c *gin.Context) {
 func DeleteStream(c *gin.Context) {
 	err := stream.Delete(c.Param("name"))
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 
@@ -222,7 +221,7 @@ func RenameStream(c *gin.Context) {
 
 	err := stream.Rename(oldName, json.NewName)
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 

+ 13 - 14
api/system/backup.go

@@ -9,7 +9,6 @@ import (
 	"strings"
 	"time"
 
-	"github.com/0xJacky/Nginx-UI/api"
 	"github.com/0xJacky/Nginx-UI/internal/backup"
 	"github.com/gin-gonic/gin"
 	"github.com/jpillora/overseer"
@@ -18,9 +17,9 @@ import (
 
 // RestoreResponse contains the response data for restore operation
 type RestoreResponse struct {
-	NginxUIRestored bool   `json:"nginx_ui_restored"`
-	NginxRestored   bool   `json:"nginx_restored"`
-	HashMatch       bool   `json:"hash_match"`
+	NginxUIRestored bool `json:"nginx_ui_restored"`
+	NginxRestored   bool `json:"nginx_restored"`
+	HashMatch       bool `json:"hash_match"`
 }
 
 // CreateBackup creates a backup of nginx-ui and nginx configurations
@@ -28,7 +27,7 @@ type RestoreResponse struct {
 func CreateBackup(c *gin.Context) {
 	result, err := backup.Backup()
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 
@@ -65,20 +64,20 @@ func RestoreBackup(c *gin.Context) {
 	// Get backup file
 	backupFile, err := c.FormFile("backup_file")
 	if err != nil {
-		api.ErrHandler(c, cosy.WrapErrorWithParams(backup.ErrBackupFileNotFound, err.Error()))
+		cosy.ErrHandler(c, cosy.WrapErrorWithParams(backup.ErrBackupFileNotFound, err.Error()))
 		return
 	}
 
 	// Validate security token
 	if securityToken == "" {
-		api.ErrHandler(c, backup.ErrInvalidSecurityToken)
+		cosy.ErrHandler(c, backup.ErrInvalidSecurityToken)
 		return
 	}
 
 	// Split security token to get Key and IV
 	parts := strings.Split(securityToken, ":")
 	if len(parts) != 2 {
-		api.ErrHandler(c, backup.ErrInvalidSecurityToken)
+		cosy.ErrHandler(c, backup.ErrInvalidSecurityToken)
 		return
 	}
 
@@ -88,20 +87,20 @@ func RestoreBackup(c *gin.Context) {
 	// Decode Key and IV from base64
 	key, err := base64.StdEncoding.DecodeString(aesKey)
 	if err != nil {
-		api.ErrHandler(c, cosy.WrapErrorWithParams(backup.ErrInvalidAESKey, err.Error()))
+		cosy.ErrHandler(c, cosy.WrapErrorWithParams(backup.ErrInvalidAESKey, err.Error()))
 		return
 	}
 
 	iv, err := base64.StdEncoding.DecodeString(aesIv)
 	if err != nil {
-		api.ErrHandler(c, cosy.WrapErrorWithParams(backup.ErrInvalidAESIV, err.Error()))
+		cosy.ErrHandler(c, cosy.WrapErrorWithParams(backup.ErrInvalidAESIV, err.Error()))
 		return
 	}
 
 	// Create temporary directory for files
 	tempDir, err := os.MkdirTemp("", "nginx-ui-restore-upload-*")
 	if err != nil {
-		api.ErrHandler(c, cosy.WrapErrorWithParams(backup.ErrCreateTempDir, err.Error()))
+		cosy.ErrHandler(c, cosy.WrapErrorWithParams(backup.ErrCreateTempDir, err.Error()))
 		return
 	}
 	defer os.RemoveAll(tempDir)
@@ -109,14 +108,14 @@ func RestoreBackup(c *gin.Context) {
 	// Save backup file
 	backupPath := filepath.Join(tempDir, backupFile.Filename)
 	if err := c.SaveUploadedFile(backupFile, backupPath); err != nil {
-		api.ErrHandler(c, cosy.WrapErrorWithParams(backup.ErrCreateBackupFile, err.Error()))
+		cosy.ErrHandler(c, cosy.WrapErrorWithParams(backup.ErrCreateBackupFile, err.Error()))
 		return
 	}
 
 	// Create temporary directory for restore operation
 	restoreDir, err := os.MkdirTemp("", "nginx-ui-restore-*")
 	if err != nil {
-		api.ErrHandler(c, cosy.WrapErrorWithParams(backup.ErrCreateRestoreDir, err.Error()))
+		cosy.ErrHandler(c, cosy.WrapErrorWithParams(backup.ErrCreateRestoreDir, err.Error()))
 		return
 	}
 
@@ -136,7 +135,7 @@ func RestoreBackup(c *gin.Context) {
 	if err != nil {
 		// Clean up temporary directory on error
 		os.RemoveAll(restoreDir)
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 

+ 35 - 5
api/system/install.go

@@ -1,8 +1,11 @@
 package system
 
 import (
-	"github.com/0xJacky/Nginx-UI/api"
+	"net/http"
+	"time"
+
 	"github.com/0xJacky/Nginx-UI/internal/kernel"
+	"github.com/0xJacky/Nginx-UI/internal/system"
 	"github.com/0xJacky/Nginx-UI/model"
 	"github.com/0xJacky/Nginx-UI/query"
 	"github.com/0xJacky/Nginx-UI/settings"
@@ -11,16 +14,36 @@ import (
 	"github.com/uozi-tech/cosy"
 	cSettings "github.com/uozi-tech/cosy/settings"
 	"golang.org/x/crypto/bcrypt"
-	"net/http"
 )
 
+// System startup time
+var startupTime time.Time
+
+func init() {
+	// Record system startup time
+	startupTime = time.Now()
+}
+
 func installLockStatus() bool {
 	return settings.NodeSettings.SkipInstallation || "" != cSettings.AppSettings.JwtSecret
 }
 
+// Check if installation time limit (10 minutes) is exceeded
+func isInstallTimeoutExceeded() bool {
+	return time.Since(startupTime) > 10*time.Minute
+}
+
 func InstallLockCheck(c *gin.Context) {
+	locked := installLockStatus()
+	timeout := false
+
+	if !locked {
+		timeout = isInstallTimeoutExceeded()
+	}
+
 	c.JSON(http.StatusOK, gin.H{
-		"lock": installLockStatus(),
+		"lock":    locked,
+		"timeout": timeout,
 	})
 }
 
@@ -39,6 +62,13 @@ func InstallNginxUI(c *gin.Context) {
 		})
 		return
 	}
+
+	// Check if installation time limit (10 minutes) is exceeded
+	if isInstallTimeoutExceeded() {
+		cosy.ErrHandler(c, system.ErrInstallTimeout)
+		return
+	}
+
 	var json InstallJson
 	ok := cosy.BindAndValid(c, &json)
 	if !ok {
@@ -54,7 +84,7 @@ func InstallNginxUI(c *gin.Context) {
 
 	err := settings.Save()
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 
@@ -70,7 +100,7 @@ func InstallNginxUI(c *gin.Context) {
 	})
 
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 

+ 3 - 3
api/system/upgrade.go

@@ -4,24 +4,24 @@ import (
 	"net/http"
 	"os"
 
-	"github.com/0xJacky/Nginx-UI/api"
 	"github.com/0xJacky/Nginx-UI/internal/upgrader"
 	"github.com/0xJacky/Nginx-UI/internal/version"
 	"github.com/0xJacky/Nginx-UI/settings"
 	"github.com/gin-gonic/gin"
 	"github.com/gorilla/websocket"
+	"github.com/uozi-tech/cosy"
 	"github.com/uozi-tech/cosy/logger"
 )
 
 func GetRelease(c *gin.Context) {
 	data, err := upgrader.GetRelease(c.Query("channel"))
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 	runtimeInfo, err := upgrader.GetRuntimeInfo()
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 	type resp struct {

+ 8 - 6
api/template/template.go

@@ -1,11 +1,13 @@
 package template
 
 import (
-	"github.com/0xJacky/Nginx-UI/api"
+	"net/http"
+
 	"github.com/0xJacky/Nginx-UI/internal/nginx"
 	"github.com/0xJacky/Nginx-UI/internal/template"
 	"github.com/gin-gonic/gin"
-	"net/http"
+
+	"github.com/uozi-tech/cosy"
 )
 
 func GetDefaultSiteTemplate(c *gin.Context) {
@@ -40,7 +42,7 @@ func GetDefaultSiteTemplate(c *gin.Context) {
 
 	content, err := ngxConfig.BuildConfig()
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 
@@ -55,7 +57,7 @@ func GetTemplateConfList(c *gin.Context) {
 	configList, err := template.GetTemplateList("conf")
 
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 
@@ -67,7 +69,7 @@ func GetTemplateConfList(c *gin.Context) {
 func GetTemplateBlockList(c *gin.Context) {
 	configList, err := template.GetTemplateList("block")
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 
@@ -91,7 +93,7 @@ func GetTemplateBlock(c *gin.Context) {
 
 	detail, err := template.ParseTemplate("block", c.Param("name"), bindData)
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 	info.Variables = bindData

+ 8 - 8
api/user/2fa.go

@@ -81,17 +81,17 @@ func Start2FASecureSessionByOTP(c *gin.Context) {
 	}
 	u := api.CurrentUser(c)
 	if !u.EnabledOTP() {
-		api.ErrHandler(c, user.ErrUserNotEnabledOTPAs2FA)
+		cosy.ErrHandler(c, user.ErrUserNotEnabledOTPAs2FA)
 		return
 	}
 
 	if json.OTP == "" && json.RecoveryCode == "" {
-		api.ErrHandler(c, user.ErrOTPOrRecoveryCodeEmpty)
+		cosy.ErrHandler(c, user.ErrOTPOrRecoveryCodeEmpty)
 		return
 	}
 
 	if err := user.VerifyOTP(u, json.OTP, json.RecoveryCode); err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 
@@ -104,14 +104,14 @@ func Start2FASecureSessionByOTP(c *gin.Context) {
 
 func BeginStart2FASecureSessionByPasskey(c *gin.Context) {
 	if !passkey.Enabled() {
-		api.ErrHandler(c, user.ErrWebAuthnNotConfigured)
+		cosy.ErrHandler(c, user.ErrWebAuthnNotConfigured)
 		return
 	}
 	webauthnInstance := passkey.GetInstance()
 	u := api.CurrentUser(c)
 	options, sessionData, err := webauthnInstance.BeginLogin(u)
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 	passkeySessionID := uuid.NewString()
@@ -124,13 +124,13 @@ func BeginStart2FASecureSessionByPasskey(c *gin.Context) {
 
 func FinishStart2FASecureSessionByPasskey(c *gin.Context) {
 	if !passkey.Enabled() {
-		api.ErrHandler(c, user.ErrWebAuthnNotConfigured)
+		cosy.ErrHandler(c, user.ErrWebAuthnNotConfigured)
 		return
 	}
 	passkeySessionID := c.GetHeader("X-Passkey-Session-ID")
 	sessionDataBytes, ok := cache.Get(passkeySessionID)
 	if !ok {
-		api.ErrHandler(c, user.ErrSessionNotFound)
+		cosy.ErrHandler(c, user.ErrSessionNotFound)
 		return
 	}
 	sessionData := sessionDataBytes.(*webauthn.SessionData)
@@ -138,7 +138,7 @@ func FinishStart2FASecureSessionByPasskey(c *gin.Context) {
 	u := api.CurrentUser(c)
 	credential, err := webauthnInstance.FinishLogin(u, *sessionData, c.Request)
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 	rawID := strings.TrimRight(base64.StdEncoding.EncodeToString(credential.ID), "=")

+ 7 - 7
api/user/auth.go

@@ -1,18 +1,18 @@
 package user
 
 import (
-	"github.com/0xJacky/Nginx-UI/api"
+	"errors"
+	"math/rand/v2"
+	"net/http"
+	"sync"
+	"time"
+
 	"github.com/0xJacky/Nginx-UI/internal/user"
 	"github.com/0xJacky/Nginx-UI/query"
 	"github.com/0xJacky/Nginx-UI/settings"
 	"github.com/gin-gonic/gin"
-	"errors"
 	"github.com/uozi-tech/cosy"
 	"github.com/uozi-tech/cosy/logger"
-	"math/rand/v2"
-	"net/http"
-	"sync"
-	"time"
 )
 
 var mutex = &sync.Mutex{}
@@ -75,7 +75,7 @@ func Login(c *gin.Context) {
 		case errors.Is(err, user.ErrUserBanned):
 			c.JSON(http.StatusForbidden, user.ErrUserBanned)
 		default:
-			api.ErrHandler(c, err)
+			cosy.ErrHandler(c, err)
 		}
 		user.BanIP(clientIP)
 		return

+ 10 - 10
api/user/casdoor.go

@@ -1,18 +1,18 @@
 package user
 
 import (
+	"errors"
 	"fmt"
-	"github.com/0xJacky/Nginx-UI/api"
+	"net/http"
+	"net/url"
+	"os"
+
 	"github.com/0xJacky/Nginx-UI/internal/user"
 	"github.com/0xJacky/Nginx-UI/settings"
 	"github.com/casdoor/casdoor-go-sdk/casdoorsdk"
 	"github.com/gin-gonic/gin"
-	"errors"
 	"github.com/uozi-tech/cosy"
 	"gorm.io/gorm"
-	"net/http"
-	"net/url"
-	"os"
 )
 
 type CasdoorLoginUser struct {
@@ -44,7 +44,7 @@ func CasdoorCallback(c *gin.Context) {
 
 	certBytes, err := os.ReadFile(certificatePath)
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 
@@ -52,13 +52,13 @@ func CasdoorCallback(c *gin.Context) {
 
 	token, err := casdoorsdk.GetOAuthToken(loginUser.Code, loginUser.State)
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 
 	claims, err := casdoorsdk.ParseJwtToken(token.AccessToken)
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 
@@ -69,14 +69,14 @@ func CasdoorCallback(c *gin.Context) {
 				"message": "User not exist",
 			})
 		} else {
-			api.ErrHandler(c, err)
+			cosy.ErrHandler(c, err)
 		}
 		return
 	}
 
 	userToken, err := user.GenerateJWT(u)
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 

+ 5 - 5
api/user/otp.go

@@ -32,7 +32,7 @@ func GenerateTOTP(c *gin.Context) {
 	}
 	otpKey, err := totp.Generate(otpOpts)
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 
@@ -75,14 +75,14 @@ func EnrollTOTP(c *gin.Context) {
 
 	ciphertext, err := crypto.AesEncrypt([]byte(twoFA.Secret))
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 
 	u := query.User
 	_, err = u.Where(u.ID.Eq(cUser.ID)).Update(u.OTPSecret, ciphertext)
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 
@@ -91,7 +91,7 @@ func EnrollTOTP(c *gin.Context) {
 	cUser.RecoveryCodes = recoveryCodes
 	_, err = u.Where(u.ID.Eq(cUser.ID)).Updates(cUser)
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 
@@ -106,7 +106,7 @@ func ResetOTP(c *gin.Context) {
 	u := query.User
 	_, err := u.Where(u.ID.Eq(cUser.ID)).UpdateSimple(u.OTPSecret.Null(), u.RecoveryCodes.Null())
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 

+ 10 - 10
api/user/passkey.go

@@ -40,7 +40,7 @@ func BeginPasskeyRegistration(c *gin.Context) {
 
 	options, sessionData, err := webauthnInstance.BeginRegistration(u)
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 	cache.Set(buildCachePasskeyRegKey(u.ID), sessionData, passkeyTimeout)
@@ -53,14 +53,14 @@ func FinishPasskeyRegistration(c *gin.Context) {
 	webauthnInstance := passkey.GetInstance()
 	sessionDataBytes, ok := cache.Get(buildCachePasskeyRegKey(cUser.ID))
 	if !ok {
-		api.ErrHandler(c, user.ErrSessionNotFound)
+		cosy.ErrHandler(c, user.ErrSessionNotFound)
 		return
 	}
 
 	sessionData := sessionDataBytes.(*webauthn.SessionData)
 	credential, err := webauthnInstance.FinishRegistration(cUser, *sessionData, c.Request)
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 	cache.Del(buildCachePasskeyRegKey(cUser.ID))
@@ -76,7 +76,7 @@ func FinishPasskeyRegistration(c *gin.Context) {
 		LastUsedAt: time.Now().Unix(),
 	})
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 
@@ -87,13 +87,13 @@ func FinishPasskeyRegistration(c *gin.Context) {
 
 func BeginPasskeyLogin(c *gin.Context) {
 	if !passkey.Enabled() {
-		api.ErrHandler(c, user.ErrWebAuthnNotConfigured)
+		cosy.ErrHandler(c, user.ErrWebAuthnNotConfigured)
 		return
 	}
 	webauthnInstance := passkey.GetInstance()
 	options, sessionData, err := webauthnInstance.BeginDiscoverableLogin()
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 	sessionID := uuid.NewString()
@@ -107,13 +107,13 @@ func BeginPasskeyLogin(c *gin.Context) {
 
 func FinishPasskeyLogin(c *gin.Context) {
 	if !passkey.Enabled() {
-		api.ErrHandler(c, user.ErrWebAuthnNotConfigured)
+		cosy.ErrHandler(c, user.ErrWebAuthnNotConfigured)
 		return
 	}
 	sessionId := c.GetHeader("X-Passkey-Session-ID")
 	sessionDataBytes, ok := cache.Get(sessionId)
 	if !ok {
-		api.ErrHandler(c, user.ErrSessionNotFound)
+		cosy.ErrHandler(c, user.ErrSessionNotFound)
 		return
 	}
 	webauthnInstance := passkey.GetInstance()
@@ -134,7 +134,7 @@ func FinishPasskeyLogin(c *gin.Context) {
 			return outUser, err
 		}, *sessionData, c.Request)
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 
@@ -167,7 +167,7 @@ func GetPasskeyList(c *gin.Context) {
 	p := query.Passkey
 	passkeys, err := p.Where(p.UserID.Eq(u.ID)).Find()
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 

+ 3 - 2
api/user/recovery.go

@@ -10,6 +10,7 @@ import (
 	"github.com/0xJacky/Nginx-UI/model"
 	"github.com/0xJacky/Nginx-UI/query"
 	"github.com/gin-gonic/gin"
+	"github.com/uozi-tech/cosy"
 )
 
 type RecoveryCodesResponse struct {
@@ -41,7 +42,7 @@ func ViewRecoveryCodes(c *gin.Context) {
 	user.RecoveryCodes.LastViewed = &t
 	_, err := u.Where(u.ID.Eq(user.ID)).Updates(user)
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 
@@ -61,7 +62,7 @@ func GenerateRecoveryCodes(c *gin.Context) {
 	u := query.User
 	_, err := u.Where(u.ID.Eq(user.ID)).Updates(user)
 	if err != nil {
-		api.ErrHandler(c, err)
+		cosy.ErrHandler(c, err)
 		return
 	}
 

+ 9 - 9
app/src/views/system/Backup/BackupCreator.vue

@@ -165,14 +165,14 @@ function handleCopy(copy) {
   line-height: 1.6;
 }
 
-/* 暗夜模式优化 */
+/* Dark mode optimization */
 :deep(.backup-token-modal) {
-  /* 模态框背景 */
+  /* Modal background */
   .ant-modal-content {
     background-color: #1f1f1f;
   }
 
-  /* 模态框标题 */
+  /* Modal title */
   .ant-modal-header {
     background-color: #1f1f1f;
     border-bottom: 1px solid #303030;
@@ -182,24 +182,24 @@ function handleCopy(copy) {
     color: #e6e6e6;
   }
 
-  /* 模态框内容 */
+  /* Modal content */
   .ant-modal-body {
     color: #e6e6e6;
   }
 
-  /* 模态框底部 */
+  /* Modal footer */
   .ant-modal-footer {
     border-top: 1px solid #303030;
     background-color: #1f1f1f;
   }
 
-  /* 关闭按钮 */
+  /* Close button */
   .ant-modal-close-x {
     color: #e6e6e6;
   }
 }
 
-/* 令牌容器暗夜模式样式 */
+/* Token container dark mode styles */
 .dark {
   .token-container {
     background-color: #262626 !important;
@@ -211,7 +211,7 @@ function handleCopy(copy) {
     color: #d9d9d9;
   }
 
-  /* 警告框暗夜模式 */
+  /* Warning box dark mode */
   .warning-box {
     background-color: rgba(255, 77, 79, 0.1);
     border-color: rgba(255, 77, 79, 0.3);
@@ -222,7 +222,7 @@ function handleCopy(copy) {
   }
 }
 
-/* 媒体查询方式添加暗夜模式支持 */
+/* Dark mode support via media query */
 @media (prefers-color-scheme: dark) {
   .token-container {
     background-color: #262626 !important;

+ 9 - 0
internal/system/errors.go

@@ -0,0 +1,9 @@
+package system
+
+import "github.com/uozi-tech/cosy"
+
+// System error definitions
+var (
+	e                 = cosy.NewErrorScope("system")
+	ErrInstallTimeout = e.New(40308, "installation is not allowed after 10 minutes of system startup")
+)