enhance(wip): error handle

This commit is contained in:
Jacky 2025-01-24 16:37:45 +08:00
parent 3f95ae5d90
commit 650196d06a
No known key found for this signature in database
GPG key ID: 215C21B10DF38B4D
93 changed files with 5228 additions and 3738 deletions

View file

@ -1,9 +1,12 @@
package api package api
import ( import (
"errors"
"github.com/0xJacky/Nginx-UI/model" "github.com/0xJacky/Nginx-UI/model"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/uozi-tech/cosy"
"github.com/uozi-tech/cosy/logger" "github.com/uozi-tech/cosy/logger"
"gorm.io/gorm"
"net/http" "net/http"
) )
@ -13,9 +16,21 @@ func CurrentUser(c *gin.Context) *model.User {
func ErrHandler(c *gin.Context, err error) { func ErrHandler(c *gin.Context, err error) {
logger.GetLogger().Errorln(err) logger.GetLogger().Errorln(err)
c.JSON(http.StatusInternalServerError, gin.H{ var cErr *cosy.Error
"message": err.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) { func SetSSEHeaders(c *gin.Context) {

View file

@ -1,6 +1,9 @@
package nginx 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) { func InitRouter(r *gin.RouterGroup) {
r.POST("ngx/build_config", BuildNginxConfig) r.POST("ngx/build_config", BuildNginxConfig)
@ -10,10 +13,6 @@ func InitRouter(r *gin.RouterGroup) {
r.POST("nginx/restart", Restart) r.POST("nginx/restart", Restart)
r.POST("nginx/test", Test) r.POST("nginx/test", Test)
r.GET("nginx/status", Status) r.GET("nginx/status", Status)
r.POST("nginx_log", GetNginxLogPage) r.POST("nginx_log", nginx_log.GetNginxLogPage)
r.GET("nginx/directives", GetDirectives) r.GET("nginx/directives", GetDirectives)
} }
func InitNginxLogRouter(r *gin.RouterGroup) {
r.GET("nginx_log", Log)
}

View file

@ -1,12 +1,9 @@
package nginx package nginx_log
import ( import (
"encoding/json" "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/internal/nginx"
"github.com/0xJacky/Nginx-UI/settings" "github.com/0xJacky/Nginx-UI/internal/nginx_log"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
"github.com/hpcloud/tail" "github.com/hpcloud/tail"
@ -70,7 +67,7 @@ func GetNginxLogPage(c *gin.Context) {
c.JSON(http.StatusInternalServerError, nginxLogPageResp{ c.JSON(http.StatusInternalServerError, nginxLogPageResp{
Error: "log file is not regular file", 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 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) { 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 { switch control.Type {
case "site": case "site":
var config *nginx.NgxConfig var config *nginx.NgxConfig
@ -167,12 +141,12 @@ func getLogPath(control *controlStruct) (logPath string, err error) {
} }
if control.ServerIdx >= len(config.Servers) { if control.ServerIdx >= len(config.Servers) {
err = errors.New("serverIdx out of range") err = nginx_log.ErrServerIdxOutOfRange
return return
} }
if control.DirectiveIdx >= len(config.Servers[control.ServerIdx].Directives) { if control.DirectiveIdx >= len(config.Servers[control.ServerIdx].Directives) {
err = errors.New("DirectiveIdx out of range") err = nginx_log.ErrDirectiveIdxOutOfRange
return return
} }
@ -181,12 +155,12 @@ func getLogPath(control *controlStruct) (logPath string, err error) {
case "access_log", "error_log": case "access_log", "error_log":
// ok // ok
default: default:
err = errors.New("directive.Params neither access_log nor error_log") err = nginx_log.ErrLogDirective
return return
} }
if directive.Params == "" { if directive.Params == "" {
err = errors.New("directive.Params is empty") err = nginx_log.ErrDirectiveParamsIsEmpty
return return
} }
@ -200,8 +174,7 @@ func getLogPath(control *controlStruct) (logPath string, err error) {
path := nginx.GetErrorLogPath() path := nginx.GetErrorLogPath()
if path == "" { if path == "" {
err = errors.New("settings.NginxLogSettings.ErrorLogPath is empty," + err = nginx_log.ErrErrorLogPathIsEmpty
" refer to https://nginxui.com/guide/config-nginx.html for more information")
return return
} }
@ -210,8 +183,7 @@ func getLogPath(control *controlStruct) (logPath string, err error) {
path := nginx.GetAccessLogPath() path := nginx.GetAccessLogPath()
if path == "" { if path == "" {
err = errors.New("settings.NginxLogSettings.AccessLogPath is empty," + err = nginx_log.ErrAccessLogPathIsEmpty
" refer to https://nginxui.com/guide/config-nginx.html for more information")
return return
} }
@ -219,9 +191,8 @@ func getLogPath(control *controlStruct) (logPath string, err error) {
} }
// check if logPath is under one of the paths in LogDirWhiteList // check if logPath is under one of the paths in LogDirWhiteList
if !isLogPathUnderWhiteList(logPath) { if !nginx_log.IsLogPathUnderWhiteList(logPath) {
err = errors.New("The log path is not under the paths in LogDirWhiteList.") return "", nginx_log.ErrLogPathIsNotUnderTheLogDirWhiteList
return "", err
} }
return return
} }

7
api/nginx_log/router.go Normal file
View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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}'),
}

View file

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

View file

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

View file

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

View file

@ -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'),
}

View file

@ -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'),
}

View file

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

View file

@ -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 Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -73,23 +73,8 @@ function onSubmit() {
break break
} }
}).catch(e => { }).catch(e => {
switch (e.code) { if (e.code === 4043) {
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() refOTP.value?.clearInput()
message.error($gettext('Invalid 2FA or recovery code'))
break
default:
message.error($gettext(e.message ?? 'Server error'))
break
} }
}) })
loading.value = false loading.value = false
@ -118,9 +103,6 @@ auth.get_casdoor_uri()
casdoor_uri.value = r.uri casdoor_uri.value = r.uri
} }
}) })
.catch(e => {
message.error($gettext(e.message ?? 'Server error'))
})
function loginWithCasdoor() { function loginWithCasdoor() {
window.location.href = casdoor_uri.value 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() || '/' const next = (route.query?.next || '').toString() || '/'
await router.push(next) await router.push(next)
}).catch(e => {
message.error($gettext(e.message ?? 'Server error'))
}) })
loading.value = false loading.value = false
} }
@ -156,7 +136,7 @@ passkey.get_config_status().then(r => {
const passkeyLoginLoading = ref(false) const passkeyLoginLoading = ref(false)
async function handlePasskeyLogin() { async function handlePasskeyLogin() {
passkeyLoginLoading.value = true passkeyLoginLoading.value = true
try {
const begin = await auth.begin_passkey_login() const begin = await auth.begin_passkey_login()
const asseResp = await startAuthentication({ optionsJSON: begin.options.publicKey }) const asseResp = await startAuthentication({ optionsJSON: begin.options.publicKey })
@ -172,11 +152,7 @@ async function handlePasskeyLogin() {
secureSessionId.value = r.secure_session_id secureSessionId.value = r.secure_session_id
await router.push(next) await router.push(next)
} }
}
// eslint-disable-next-line ts/no-explicit-any
catch (e: any) {
message.error($gettext(e.message ?? 'Server error'))
}
passkeyLoginLoading.value = false passkeyLoginLoading.value = false
} }
</script> </script>

View file

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

View file

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

View file

@ -14,7 +14,6 @@ const passkeyEnabled = ref(false)
const regLoading = ref(false) const regLoading = ref(false)
async function registerPasskey() { async function registerPasskey() {
regLoading.value = true 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 })
@ -27,11 +26,6 @@ async function registerPasskey() {
addPasskeyModelOpen.value = false addPasskeyModelOpen.value = false
user.passkeyRawId = attestationResponse.rawId user.passkeyRawId = attestationResponse.rawId
}
// eslint-disable-next-line ts/no-explicit-any
catch (e: any) {
message.error($gettext(e.message ?? 'Server error'))
}
regLoading.value = false regLoading.value = false
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

7
cmd/errdef/generate.go Normal file
View file

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

View file

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

@ -1,3 +1,10 @@
# generate gen code
pushd ./cmd/gen || exit pushd ./cmd/gen || exit
go run generate.go -config ../../app.ini go run generate.go -config ../../app.ini
popd || exit 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

4
go.mod
View file

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

4
go.sum
View file

@ -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.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 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.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.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 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk=
github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= 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.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 h1:Qat6Av9XhYMypeBCcagl3Pfp6LWgDRK0+lHBN/1jFY0=
github.com/uozi-tech/cosy v1.14.1/go.mod h1:DSKLtoVaGLUlJ8KiQ1vWEsnv85epRrAAMXSijuq+asM= 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 h1:22S/XNIvuaKGqxQPsYPXN8TZ8hHjCQdcJKVQ83Vzxoo=
github.com/uozi-tech/cosy-driver-mysql v0.2.2/go.mod h1:EZnRIbSj1V5U0gEeTobrXai/d1SV11lkl4zP9NFEmyE= 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= github.com/uozi-tech/cosy-driver-postgres v0.2.1 h1:OICakGuT+omva6QOJCxTJ5Lfr7CGXLmk/zD+aS51Z2o=

View file

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

View file

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

13
internal/cert/errors.go Normal file
View file

@ -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}")
)

View file

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

View file

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

View file

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

View file

@ -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}")
)

View file

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

View file

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

View file

@ -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")
)

View file

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

View file

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

8
internal/nginx/errors.go Normal file
View file

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

View file

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

View file

@ -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")
)

View file

@ -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)
}

View file

@ -11,9 +11,9 @@ var (
ErrNginxConfNotIncludeSitesEnabled = e.New(4043, "Nginx conf not include sites-enabled") ErrNginxConfNotIncludeSitesEnabled = e.New(4043, "Nginx conf not include sites-enabled")
ErrorNginxConfNoStreamBlock = e.New(4044, "Nginx conf no stream block") ErrorNginxConfNoStreamBlock = e.New(4044, "Nginx conf no stream block")
ErrNginxConfNotIncludeStreamEnabled = e.New(4045, "Nginx conf not include stream-enabled") 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") ErrSitesAvailableNotExist = e.New(4046, "Sites-available directory not exist")
ErrSitesEnabledNotExist = e.New(4047, "Sites-enabled directory not exist") ErrSitesEnabledNotExist = e.New(4047, "Sites-enabled directory not exist")
ErrStreamAvailableNotExist = e.New(4048, "Stream-available directory not exist") ErrStreamAvailableNotExist = e.New(4048, "Streams-available directory not exist")
ErrStreamEnabledNotExist = e.New(4049, "Stream-enabled directory not exist") ErrStreamEnabledNotExist = e.New(4049, "Streams-enabled directory not exist")
) )

View file

@ -6,7 +6,6 @@ import (
"time" "time"
"github.com/0xJacky/Nginx-UI/internal/nginx" "github.com/0xJacky/Nginx-UI/internal/nginx"
"github.com/spf13/cast"
"github.com/tufanbarisyildirim/gonginx/config" "github.com/tufanbarisyildirim/gonginx/config"
"github.com/tufanbarisyildirim/gonginx/dumper" "github.com/tufanbarisyildirim/gonginx/dumper"
"github.com/tufanbarisyildirim/gonginx/parser" "github.com/tufanbarisyildirim/gonginx/parser"
@ -88,7 +87,7 @@ func FixNginxConfIncludeSites() error {
} }
// create a backup file (+.bak.timestamp) // 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) err = os.WriteFile(backupPath, content, 0644)
if err != nil { if err != nil {
return ErrFailedToCreateBackup return ErrFailedToCreateBackup
@ -133,7 +132,7 @@ func FixNginxConfIncludeStreams() error {
} }
// create a backup file (+.bak.timestamp) // 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) err = os.WriteFile(backupPath, content, 0644)
if err != nil { if err != nil {
return ErrFailedToCreateBackup return ErrFailedToCreateBackup

View file

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

View file

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

10
internal/site/errors.go Normal file
View file

@ -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")
)

View file

@ -24,7 +24,7 @@ func Rename(oldName string, newName string) (err error) {
// check if dst file exists, do not rename // check if dst file exists, do not rename
if helper.FileExists(newPath) { if helper.FileExists(newPath) {
return fmt.Errorf("file exists") return ErrDstFileExists
} }
s := query.Site s := query.Site

View file

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

15
internal/user/errors.go Normal file
View file

@ -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")
)

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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