Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

enhance: error handle #830

Merged
merged 2 commits into from
Jan 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 18 additions & 3 deletions api/api.go
Original file line number Diff line number Diff line change
@@ -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"
)

Expand All @@ -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) {
Expand Down
11 changes: 5 additions & 6 deletions api/nginx/router.go
Original file line number Diff line number Diff line change
@@ -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)
Expand All @@ -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)
}
51 changes: 11 additions & 40 deletions api/nginx/nginx_log.go → api/nginx_log/nginx_log.go
Original file line number Diff line number Diff line change
@@ -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"
Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -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
Expand All @@ -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
}

Expand All @@ -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
}

Expand All @@ -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
}

Expand All @@ -210,18 +183,16 @@ 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
}

logPath = path
}

// 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
}
Expand Down
7 changes: 7 additions & 0 deletions api/nginx_log/router.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package nginx_log

import "github.com/gin-gonic/gin"

func InitRouter(r *gin.RouterGroup) {
r.GET("nginx_log", Log)
}
2 changes: 1 addition & 1 deletion api/openai/openai.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
19 changes: 6 additions & 13 deletions api/user/2fa.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
}

Expand All @@ -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()
Expand All @@ -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)
Expand Down
22 changes: 7 additions & 15 deletions api/user/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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 {
Expand Down Expand Up @@ -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)
}
Expand Down
2 changes: 1 addition & 1 deletion api/user/casdoor.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
8 changes: 4 additions & 4 deletions api/user/passkey.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down Expand Up @@ -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()
Expand All @@ -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()
Expand Down
4 changes: 0 additions & 4 deletions app/src/components/NginxControl/NginxControl.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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())
}

Expand All @@ -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}`)
})
}

Expand Down
2 changes: 1 addition & 1 deletion app/src/components/NodeSelector/NodeSelector.vue
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ function reconnect() {
}

function newSSE() {
const s = new SSE('/api/environments/enabled', {
const s = new SSE('api/environments/enabled', {
headers: {
Authorization: token.value,
},
Expand Down
Loading
Loading