mirror of
https://github.com/0xJacky/nginx-ui.git
synced 2025-05-11 02:15:48 +02:00
Merge pull request #830 from 0xJacky/enhance/error-handle
enhance: error handle
This commit is contained in:
commit
bbae67939f
94 changed files with 5230 additions and 3740 deletions
21
api/api.go
21
api/api.go
|
@ -1,9 +1,12 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/0xJacky/Nginx-UI/model"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/uozi-tech/cosy"
|
||||
"github.com/uozi-tech/cosy/logger"
|
||||
"gorm.io/gorm"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
|
@ -13,9 +16,21 @@ func CurrentUser(c *gin.Context) *model.User {
|
|||
|
||||
func ErrHandler(c *gin.Context, err error) {
|
||||
logger.GetLogger().Errorln(err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"message": err.Error(),
|
||||
})
|
||||
var cErr *cosy.Error
|
||||
switch {
|
||||
case errors.Is(err, gorm.ErrRecordNotFound):
|
||||
c.JSON(http.StatusNotFound, &cosy.Error{
|
||||
Code: http.StatusNotFound,
|
||||
Message: gorm.ErrRecordNotFound.Error(),
|
||||
})
|
||||
case errors.As(err, &cErr):
|
||||
c.JSON(http.StatusInternalServerError, cErr)
|
||||
default:
|
||||
c.JSON(http.StatusInternalServerError, &cosy.Error{
|
||||
Code: http.StatusInternalServerError,
|
||||
Message: err.Error(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func SetSSEHeaders(c *gin.Context) {
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
package nginx
|
||||
|
||||
import "github.com/gin-gonic/gin"
|
||||
import (
|
||||
"github.com/0xJacky/Nginx-UI/api/nginx_log"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func InitRouter(r *gin.RouterGroup) {
|
||||
r.POST("ngx/build_config", BuildNginxConfig)
|
||||
|
@ -10,10 +13,6 @@ func InitRouter(r *gin.RouterGroup) {
|
|||
r.POST("nginx/restart", Restart)
|
||||
r.POST("nginx/test", Test)
|
||||
r.GET("nginx/status", Status)
|
||||
r.POST("nginx_log", GetNginxLogPage)
|
||||
r.POST("nginx_log", nginx_log.GetNginxLogPage)
|
||||
r.GET("nginx/directives", GetDirectives)
|
||||
}
|
||||
|
||||
func InitNginxLogRouter(r *gin.RouterGroup) {
|
||||
r.GET("nginx_log", Log)
|
||||
}
|
||||
|
|
|
@ -1,12 +1,9 @@
|
|||
package nginx
|
||||
package nginx_log
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/0xJacky/Nginx-UI/internal/cache"
|
||||
"github.com/0xJacky/Nginx-UI/internal/helper"
|
||||
"github.com/0xJacky/Nginx-UI/internal/nginx"
|
||||
"github.com/0xJacky/Nginx-UI/settings"
|
||||
"github.com/0xJacky/Nginx-UI/internal/nginx_log"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/hpcloud/tail"
|
||||
|
@ -70,7 +67,7 @@ func GetNginxLogPage(c *gin.Context) {
|
|||
c.JSON(http.StatusInternalServerError, nginxLogPageResp{
|
||||
Error: "log file is not regular file",
|
||||
})
|
||||
logger.Error("log file is not regular file:", logPath)
|
||||
logger.Errorf("log file is not regular file: %s", logPath)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -132,30 +129,7 @@ func GetNginxLogPage(c *gin.Context) {
|
|||
})
|
||||
}
|
||||
|
||||
// isLogPathUnderWhiteList checks if the log path is under one of the paths in LogDirWhiteList
|
||||
func isLogPathUnderWhiteList(path string) bool {
|
||||
cacheKey := fmt.Sprintf("isLogPathUnderWhiteList:%s", path)
|
||||
res, ok := cache.Get(cacheKey)
|
||||
// no cache, check it
|
||||
if !ok {
|
||||
for _, whitePath := range settings.NginxSettings.LogDirWhiteList {
|
||||
if helper.IsUnderDirectory(path, whitePath) {
|
||||
cache.Set(cacheKey, true, 0)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
return res.(bool)
|
||||
}
|
||||
|
||||
func getLogPath(control *controlStruct) (logPath string, err error) {
|
||||
if len(settings.NginxSettings.LogDirWhiteList) == 0 {
|
||||
err = errors.New("The settings.NginxSettings.LogDirWhiteList has not been configured. " +
|
||||
"For security reasons, please configure a whitelist of log directories. " +
|
||||
"Please visit https://nginxui.com/guide/config-nginx.html for more information.")
|
||||
return
|
||||
}
|
||||
switch control.Type {
|
||||
case "site":
|
||||
var config *nginx.NgxConfig
|
||||
|
@ -167,12 +141,12 @@ func getLogPath(control *controlStruct) (logPath string, err error) {
|
|||
}
|
||||
|
||||
if control.ServerIdx >= len(config.Servers) {
|
||||
err = errors.New("serverIdx out of range")
|
||||
err = nginx_log.ErrServerIdxOutOfRange
|
||||
return
|
||||
}
|
||||
|
||||
if control.DirectiveIdx >= len(config.Servers[control.ServerIdx].Directives) {
|
||||
err = errors.New("DirectiveIdx out of range")
|
||||
err = nginx_log.ErrDirectiveIdxOutOfRange
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -181,12 +155,12 @@ func getLogPath(control *controlStruct) (logPath string, err error) {
|
|||
case "access_log", "error_log":
|
||||
// ok
|
||||
default:
|
||||
err = errors.New("directive.Params neither access_log nor error_log")
|
||||
err = nginx_log.ErrLogDirective
|
||||
return
|
||||
}
|
||||
|
||||
if directive.Params == "" {
|
||||
err = errors.New("directive.Params is empty")
|
||||
err = nginx_log.ErrDirectiveParamsIsEmpty
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -200,8 +174,7 @@ func getLogPath(control *controlStruct) (logPath string, err error) {
|
|||
path := nginx.GetErrorLogPath()
|
||||
|
||||
if path == "" {
|
||||
err = errors.New("settings.NginxLogSettings.ErrorLogPath is empty," +
|
||||
" refer to https://nginxui.com/guide/config-nginx.html for more information")
|
||||
err = nginx_log.ErrErrorLogPathIsEmpty
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -210,8 +183,7 @@ func getLogPath(control *controlStruct) (logPath string, err error) {
|
|||
path := nginx.GetAccessLogPath()
|
||||
|
||||
if path == "" {
|
||||
err = errors.New("settings.NginxLogSettings.AccessLogPath is empty," +
|
||||
" refer to https://nginxui.com/guide/config-nginx.html for more information")
|
||||
err = nginx_log.ErrAccessLogPathIsEmpty
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -219,9 +191,8 @@ func getLogPath(control *controlStruct) (logPath string, err error) {
|
|||
}
|
||||
|
||||
// check if logPath is under one of the paths in LogDirWhiteList
|
||||
if !isLogPathUnderWhiteList(logPath) {
|
||||
err = errors.New("The log path is not under the paths in LogDirWhiteList.")
|
||||
return "", err
|
||||
if !nginx_log.IsLogPathUnderWhiteList(logPath) {
|
||||
return "", nginx_log.ErrLogPathIsNotUnderTheLogDirWhiteList
|
||||
}
|
||||
return
|
||||
}
|
7
api/nginx_log/router.go
Normal file
7
api/nginx_log/router.go
Normal file
|
@ -0,0 +1,7 @@
|
|||
package nginx_log
|
||||
|
||||
import "github.com/gin-gonic/gin"
|
||||
|
||||
func InitRouter(r *gin.RouterGroup) {
|
||||
r.GET("nginx_log", Log)
|
||||
}
|
|
@ -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"
|
||||
|
|
|
@ -2,7 +2,6 @@ package user
|
|||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"github.com/0xJacky/Nginx-UI/api"
|
||||
"github.com/0xJacky/Nginx-UI/internal/cache"
|
||||
"github.com/0xJacky/Nginx-UI/internal/passkey"
|
||||
|
@ -77,23 +76,17 @@ func Start2FASecureSessionByOTP(c *gin.Context) {
|
|||
}
|
||||
u := api.CurrentUser(c)
|
||||
if !u.EnabledOTP() {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"message": "User has not configured OTP as 2FA",
|
||||
})
|
||||
api.ErrHandler(c, user.ErrUserNotEnabledOTPAs2FA)
|
||||
return
|
||||
}
|
||||
|
||||
if json.OTP == "" && json.RecoveryCode == "" {
|
||||
c.JSON(http.StatusBadRequest, LoginResponse{
|
||||
Message: "The user has enabled OTP as 2FA",
|
||||
})
|
||||
api.ErrHandler(c, user.ErrOTPOrRecoveryCodeEmpty)
|
||||
return
|
||||
}
|
||||
|
||||
if err := user.VerifyOTP(u, json.OTP, json.RecoveryCode); err != nil {
|
||||
c.JSON(http.StatusBadRequest, LoginResponse{
|
||||
Message: "Invalid OTP or recovery code",
|
||||
})
|
||||
api.ErrHandler(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -106,7 +99,7 @@ func Start2FASecureSessionByOTP(c *gin.Context) {
|
|||
|
||||
func BeginStart2FASecureSessionByPasskey(c *gin.Context) {
|
||||
if !passkey.Enabled() {
|
||||
api.ErrHandler(c, fmt.Errorf("WebAuthn settings are not configured"))
|
||||
api.ErrHandler(c, user.ErrWebAuthnNotConfigured)
|
||||
return
|
||||
}
|
||||
webauthnInstance := passkey.GetInstance()
|
||||
|
@ -126,13 +119,13 @@ func BeginStart2FASecureSessionByPasskey(c *gin.Context) {
|
|||
|
||||
func FinishStart2FASecureSessionByPasskey(c *gin.Context) {
|
||||
if !passkey.Enabled() {
|
||||
api.ErrHandler(c, fmt.Errorf("WebAuthn settings are not configured"))
|
||||
api.ErrHandler(c, user.ErrWebAuthnNotConfigured)
|
||||
return
|
||||
}
|
||||
passkeySessionID := c.GetHeader("X-Passkey-Session-ID")
|
||||
sessionDataBytes, ok := cache.Get(passkeySessionID)
|
||||
if !ok {
|
||||
api.ErrHandler(c, fmt.Errorf("session not found"))
|
||||
api.ErrHandler(c, user.ErrSessionNotFound)
|
||||
return
|
||||
}
|
||||
sessionData := sessionDataBytes.(*webauthn.SessionData)
|
||||
|
|
|
@ -6,7 +6,7 @@ import (
|
|||
"github.com/0xJacky/Nginx-UI/query"
|
||||
"github.com/0xJacky/Nginx-UI/settings"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/pkg/errors"
|
||||
"errors"
|
||||
"github.com/uozi-tech/cosy"
|
||||
"github.com/uozi-tech/cosy/logger"
|
||||
"math/rand/v2"
|
||||
|
@ -25,12 +25,10 @@ type LoginUser struct {
|
|||
}
|
||||
|
||||
const (
|
||||
ErrPasswordIncorrect = 4031
|
||||
ErrMaxAttempts = 4291
|
||||
ErrUserBanned = 4033
|
||||
Enabled2FA = 199
|
||||
Error2FACode = 4034
|
||||
LoginSuccess = 200
|
||||
ErrMaxAttempts = 4291
|
||||
Enabled2FA = 199
|
||||
Error2FACode = 4034
|
||||
LoginSuccess = 200
|
||||
)
|
||||
|
||||
type LoginResponse struct {
|
||||
|
@ -73,15 +71,9 @@ func Login(c *gin.Context) {
|
|||
time.Sleep(random * time.Second)
|
||||
switch {
|
||||
case errors.Is(err, user.ErrPasswordIncorrect):
|
||||
c.JSON(http.StatusForbidden, LoginResponse{
|
||||
Message: "Password incorrect",
|
||||
Code: ErrPasswordIncorrect,
|
||||
})
|
||||
c.JSON(http.StatusForbidden, user.ErrPasswordIncorrect)
|
||||
case errors.Is(err, user.ErrUserBanned):
|
||||
c.JSON(http.StatusForbidden, LoginResponse{
|
||||
Message: "The user is banned",
|
||||
Code: ErrUserBanned,
|
||||
})
|
||||
c.JSON(http.StatusForbidden, user.ErrUserBanned)
|
||||
default:
|
||||
api.ErrHandler(c, err)
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -53,7 +53,7 @@ func FinishPasskeyRegistration(c *gin.Context) {
|
|||
webauthnInstance := passkey.GetInstance()
|
||||
sessionDataBytes, ok := cache.Get(buildCachePasskeyRegKey(cUser.ID))
|
||||
if !ok {
|
||||
api.ErrHandler(c, fmt.Errorf("session not found"))
|
||||
api.ErrHandler(c, user.ErrSessionNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -87,7 +87,7 @@ func FinishPasskeyRegistration(c *gin.Context) {
|
|||
|
||||
func BeginPasskeyLogin(c *gin.Context) {
|
||||
if !passkey.Enabled() {
|
||||
api.ErrHandler(c, fmt.Errorf("WebAuthn settings are not configured"))
|
||||
api.ErrHandler(c, user.ErrWebAuthnNotConfigured)
|
||||
return
|
||||
}
|
||||
webauthnInstance := passkey.GetInstance()
|
||||
|
@ -107,13 +107,13 @@ func BeginPasskeyLogin(c *gin.Context) {
|
|||
|
||||
func FinishPasskeyLogin(c *gin.Context) {
|
||||
if !passkey.Enabled() {
|
||||
api.ErrHandler(c, fmt.Errorf("WebAuthn settings are not configured"))
|
||||
api.ErrHandler(c, user.ErrWebAuthnNotConfigured)
|
||||
return
|
||||
}
|
||||
sessionId := c.GetHeader("X-Passkey-Session-ID")
|
||||
sessionDataBytes, ok := cache.Get(sessionId)
|
||||
if !ok {
|
||||
api.ErrHandler(c, fmt.Errorf("session not found"))
|
||||
api.ErrHandler(c, user.ErrSessionNotFound)
|
||||
return
|
||||
}
|
||||
webauthnInstance := passkey.GetInstance()
|
||||
|
|
|
@ -28,8 +28,6 @@ function reloadNginx() {
|
|||
message.warn(r.message)
|
||||
else
|
||||
message.error(r.message)
|
||||
}).catch(e => {
|
||||
message.error(`${$gettext('Server error')} ${e?.message}`)
|
||||
}).finally(() => getStatus())
|
||||
}
|
||||
|
||||
|
@ -44,8 +42,6 @@ async function restartNginx() {
|
|||
message.warn(r.message)
|
||||
else
|
||||
message.error(r.message)
|
||||
}).catch(e => {
|
||||
message.error(`${$gettext('Server error')} ${e?.message}`)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
|
|
|
@ -30,7 +30,7 @@ function reconnect() {
|
|||
}
|
||||
|
||||
function newSSE() {
|
||||
const s = new SSE('/api/notifications/live', {
|
||||
const s = new SSE('api/notifications/live', {
|
||||
headers: {
|
||||
Authorization: token.value,
|
||||
},
|
||||
|
@ -67,8 +67,6 @@ function init() {
|
|||
notificationApi.get_list().then(r => {
|
||||
data.value = r.data
|
||||
unreadCount.value = r.pagination?.total || 0
|
||||
}).catch(e => {
|
||||
message.error($gettext(e?.message ?? 'Server error'))
|
||||
}).finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
|
@ -90,8 +88,7 @@ function clear() {
|
|||
message.success($gettext('Cleared successfully'))
|
||||
data.value = []
|
||||
unreadCount.value = 0
|
||||
}).catch(e => {
|
||||
message.error($gettext(e?.message ?? 'Server error'))
|
||||
open.value = false
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -99,8 +96,6 @@ function remove(id: number) {
|
|||
notificationApi.destroy(id).then(() => {
|
||||
message.success($gettext('Removed successfully'))
|
||||
init()
|
||||
}).catch(e => {
|
||||
message.error($gettext(e?.message ?? 'Server error'))
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -48,10 +48,6 @@ async function ok() {
|
|||
emit('save')
|
||||
visible.value = false
|
||||
})
|
||||
.catch(e => {
|
||||
error.value = e.errors
|
||||
message.error($gettext(e?.message) ?? $gettext('Server error'))
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
|
|
|
@ -66,8 +66,6 @@ function onClickApply() {
|
|||
computedActions.value[actionValue.value]?.action(props.selectedRowKeys).then(async () => {
|
||||
message.success($gettext('Apply bulk action successfully'))
|
||||
emit('onSuccess')
|
||||
}).catch(e => {
|
||||
message.error($gettext(e?.message) ?? $gettext('Server error'))
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
|
|
@ -99,7 +99,6 @@ async function ok() {
|
|||
get_list()
|
||||
visible.value = false
|
||||
}).catch(e => {
|
||||
message.error($gettext(e?.message ?? 'Server error'), 5)
|
||||
Object.assign(error, e.errors)
|
||||
})
|
||||
}
|
||||
|
@ -126,8 +125,6 @@ function edit(id: number | string) {
|
|||
visible.value = true
|
||||
modifyMode.value = true
|
||||
editMode.value = 'modify'
|
||||
}).catch(e => {
|
||||
message.error($gettext(e?.message ?? 'Server error'), 5)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -135,8 +132,6 @@ function view(id: number | string) {
|
|||
get(id).then(() => {
|
||||
visible.value = true
|
||||
modifyMode.value = false
|
||||
}).catch(e => {
|
||||
message.error($gettext(e?.message ?? 'Server error'), 5)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -181,8 +181,6 @@ function destroy(id: number | string) {
|
|||
props.api!.destroy(id, { permanent: props.inTrash }).then(() => {
|
||||
get_list()
|
||||
message.success($gettext('Deleted successfully'))
|
||||
}).catch(e => {
|
||||
message.error($gettext(e?.message ?? 'Server error'))
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -190,8 +188,6 @@ function recover(id: number | string) {
|
|||
props.api.recover(id).then(() => {
|
||||
message.success($gettext('Recovered Successfully'))
|
||||
get_list()
|
||||
}).catch(e => {
|
||||
message.error(e?.message ?? $gettext('Server error'))
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -224,8 +220,6 @@ async function _get_list() {
|
|||
|
||||
if (r.pagination)
|
||||
Object.assign(pagination, r.pagination)
|
||||
}).catch(e => {
|
||||
message.error($gettext(e?.message ?? 'Server error'))
|
||||
})
|
||||
|
||||
loading.value = false
|
||||
|
|
|
@ -2,7 +2,6 @@ import type { StdTableProps } from '@/components/StdDesign/StdDataDisplay/types'
|
|||
import type { Column, StdTableResponse } from '@/components/StdDesign/types'
|
||||
import type { ComputedRef } from 'vue'
|
||||
import { downloadCsv } from '@/lib/helper'
|
||||
import { message } from 'ant-design-vue'
|
||||
import dayjs from 'dayjs'
|
||||
import _ from 'lodash'
|
||||
|
||||
|
@ -40,8 +39,7 @@ async function exportCsv(props: StdTableProps, pithyColumns: ComputedRef<Column[
|
|||
return
|
||||
}
|
||||
dataSource.push(...r.data)
|
||||
}).catch((e: { message?: string }) => {
|
||||
message.error(e.message ?? $gettext('Server error'))
|
||||
}).catch(() => {
|
||||
hasMore = false
|
||||
})
|
||||
page += 1
|
||||
|
|
|
@ -119,9 +119,6 @@ function useSortable(props: StdTableProps, randomId: Ref<string>, dataSource: Re
|
|||
affected_ids: changeIds,
|
||||
}).then(() => {
|
||||
message.success($gettext('Updated successfully'))
|
||||
// eslint-disable-next-line ts/no-explicit-any
|
||||
}).catch((e: any) => {
|
||||
message.error(e?.message ?? $gettext('Server error'))
|
||||
})
|
||||
},
|
||||
})
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
import type Curd from '@/api/curd'
|
||||
import type { Column } from '@/components/StdDesign/types'
|
||||
import StdTable from '@/components/StdDesign/StdDataDisplay/StdTable.vue'
|
||||
import { CloseCircleFilled } from '@ant-design/icons-vue'
|
||||
import { watchOnce } from '@vueuse/core'
|
||||
import _ from 'lodash'
|
||||
|
||||
|
@ -134,10 +135,13 @@ async function ok() {
|
|||
M_values.value = _.clone(records.value)
|
||||
}
|
||||
|
||||
// function clear() {
|
||||
// M_values.value = []
|
||||
// emit('update:selectedKey', '')
|
||||
// }
|
||||
function clear() {
|
||||
M_values.value = []
|
||||
if (props.selectionType === 'radio')
|
||||
selectedKey.value = null
|
||||
else
|
||||
selectedKey.value = []
|
||||
}
|
||||
|
||||
defineExpose({ show })
|
||||
</script>
|
||||
|
@ -150,9 +154,8 @@ defineExpose({ show })
|
|||
>
|
||||
<div
|
||||
class="std-selector"
|
||||
@click="show"
|
||||
>
|
||||
<div class="chips-container">
|
||||
<div class="chips-container w-full" @click="show">
|
||||
<div v-if="props.recordValueIndex">
|
||||
<ATag
|
||||
v-for="(chipText, index) in ComputedMValue"
|
||||
|
@ -172,6 +175,10 @@ defineExpose({ show })
|
|||
{{ placeholder }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="close-btn flex text-trueGray-3" @click="clear">
|
||||
<CloseCircleFilled />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<AModal
|
||||
|
@ -211,6 +218,8 @@ defineExpose({ show })
|
|||
align-items: self-start;
|
||||
|
||||
.std-selector {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
overflow-y: auto;
|
||||
box-sizing: border-box;
|
||||
font-variant: tabular-nums;
|
||||
|
@ -228,6 +237,16 @@ defineExpose({ show })
|
|||
//margin: 0 10px 0 0;
|
||||
cursor: pointer;
|
||||
min-width: 180px;
|
||||
|
||||
.close-btn {
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
&:hover {
|
||||
.close-btn {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@ import OTPInput from '@/components/OTPInput/OTPInput.vue'
|
|||
import { useUserStore } from '@/pinia'
|
||||
import { KeyOutlined } from '@ant-design/icons-vue'
|
||||
import { startAuthentication } from '@simplewebauthn/browser'
|
||||
import { message } from 'ant-design-vue'
|
||||
|
||||
defineProps<{
|
||||
twoFAStatus: TwoFAStatusResponse
|
||||
|
@ -44,21 +43,17 @@ defineExpose({
|
|||
|
||||
async function passkeyAuthenticate() {
|
||||
passkeyLoading.value = true
|
||||
try {
|
||||
const begin = await twoFA.begin_start_secure_session_by_passkey()
|
||||
const asseResp = await startAuthentication({ optionsJSON: begin.options.publicKey })
|
||||
|
||||
const r = await twoFA.finish_start_secure_session_by_passkey({
|
||||
session_id: begin.session_id,
|
||||
options: asseResp,
|
||||
})
|
||||
const begin = await twoFA.begin_start_secure_session_by_passkey()
|
||||
const asseResp = await startAuthentication({ optionsJSON: begin.options.publicKey })
|
||||
|
||||
const r = await twoFA.finish_start_secure_session_by_passkey({
|
||||
session_id: begin.session_id,
|
||||
options: asseResp,
|
||||
})
|
||||
|
||||
emit('submitSecureSessionID', r.session_id)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
|
|
8
app/src/constants/errors/cert.ts
Normal file
8
app/src/constants/errors/cert.ts
Normal 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}'),
|
||||
}
|
3
app/src/constants/errors/config.ts
Normal file
3
app/src/constants/errors/config.ts
Normal file
|
@ -0,0 +1,3 @@
|
|||
export default {
|
||||
50006: () => $gettext('Path: {0} is not under the nginx conf dir: {1}'),
|
||||
}
|
4
app/src/constants/errors/crypto.ts
Normal file
4
app/src/constants/errors/crypto.ts
Normal file
|
@ -0,0 +1,4 @@
|
|||
export default {
|
||||
50001: () => $gettext('Plain text is empty'),
|
||||
50002: () => $gettext('Cipher text is too short'),
|
||||
}
|
3
app/src/constants/errors/nginx.ts
Normal file
3
app/src/constants/errors/nginx.ts
Normal file
|
@ -0,0 +1,3 @@
|
|||
export default {
|
||||
50001: () => $gettext('Block is nil'),
|
||||
}
|
9
app/src/constants/errors/nginx_log.ts
Normal file
9
app/src/constants/errors/nginx_log.ts
Normal 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'),
|
||||
}
|
14
app/src/constants/errors/self_check.ts
Normal file
14
app/src/constants/errors/self_check.ts
Normal 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'),
|
||||
}
|
5
app/src/constants/errors/site.ts
Normal file
5
app/src/constants/errors/site.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
export default {
|
||||
40401: () => $gettext('Site not found'),
|
||||
50001: () => $gettext('Destination file already exists'),
|
||||
50002: () => $gettext('Site is enabled'),
|
||||
}
|
10
app/src/constants/errors/user.ts
Normal file
10
app/src/constants/errors/user.ts
Normal 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
|
@ -52,7 +52,6 @@ async function save() {
|
|||
// eslint-disable-next-line ts/no-explicit-any
|
||||
catch (e: any) {
|
||||
errors.value = e.errors
|
||||
message.error($gettext(e?.message ?? 'Server error'))
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
|
|
@ -107,8 +107,6 @@ async function init() {
|
|||
translatedName: () => origName.value,
|
||||
hasChildren: false,
|
||||
}]
|
||||
}).catch(r => {
|
||||
message.error(r.message ?? $gettext('Server error'))
|
||||
})
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -31,8 +31,6 @@ function save() {
|
|||
router.push({
|
||||
path: `/config/${r.path}/edit`,
|
||||
})
|
||||
}).catch(e => {
|
||||
message.error($gettext(e?.message ?? 'Server error'))
|
||||
}).finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
|
|
|
@ -34,8 +34,6 @@ function ok() {
|
|||
|
||||
message.success($gettext('Created successfully'))
|
||||
emit('created')
|
||||
}).catch(e => {
|
||||
message.error(`${$gettext('Server error')} ${e?.message}`)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -41,8 +41,6 @@ function ok() {
|
|||
visible.value = false
|
||||
message.success($gettext('Rename successfully'))
|
||||
emit('renamed')
|
||||
}).catch(e => {
|
||||
message.error(`${$gettext('Server error')} ${e?.message}`)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -3,7 +3,6 @@ import type { Environment } from '@/api/environment'
|
|||
import type { Ref } from 'vue'
|
||||
import upgrade, { type RuntimeInfo } from '@/api/upgrade'
|
||||
import websocket from '@/lib/websocket'
|
||||
import { message } from 'ant-design-vue'
|
||||
import _ from 'lodash'
|
||||
import { marked } from 'marked'
|
||||
import { useRoute } from 'vue-router'
|
||||
|
@ -56,7 +55,6 @@ function getLatestRelease() {
|
|||
data.value = r
|
||||
}).catch(e => {
|
||||
getReleaseError.value = e?.message
|
||||
message.error(e?.message ?? $gettext('Server error'))
|
||||
}).finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
|
|
|
@ -11,8 +11,6 @@ function loadFromSettings() {
|
|||
environment.load_from_settings().then(() => {
|
||||
curd.value.get_list()
|
||||
message.success($gettext('Load successfully'))
|
||||
}).catch(e => {
|
||||
message.error(`${$gettext('Server error')} ${e?.message}`)
|
||||
})
|
||||
}
|
||||
const selectedNodeIds = ref([])
|
||||
|
|
|
@ -13,8 +13,6 @@ function clear() {
|
|||
message.success($gettext('Cleared successfully'))
|
||||
curd.value?.get_list()
|
||||
unreadCount.value = 0
|
||||
}).catch(e => {
|
||||
message.error($gettext(e?.message ?? 'Server error'))
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -66,8 +66,6 @@ function onSubmit() {
|
|||
install.install_nginx_ui(modelRef).then(async () => {
|
||||
message.success($gettext('Install successfully'))
|
||||
await router.push('/login')
|
||||
}).catch(e => {
|
||||
message.error(e.message ?? $gettext('Server error'))
|
||||
}).finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
|
|
|
@ -73,23 +73,8 @@ function onSubmit() {
|
|||
break
|
||||
}
|
||||
}).catch(e => {
|
||||
switch (e.code) {
|
||||
case 4031:
|
||||
message.error($gettext('Incorrect username or password'))
|
||||
break
|
||||
case 4291:
|
||||
message.error($gettext('Too many login failed attempts, please try again later'))
|
||||
break
|
||||
case 4033:
|
||||
message.error($gettext('User is banned'))
|
||||
break
|
||||
case 4034:
|
||||
refOTP.value?.clearInput()
|
||||
message.error($gettext('Invalid 2FA or recovery code'))
|
||||
break
|
||||
default:
|
||||
message.error($gettext(e.message ?? 'Server error'))
|
||||
break
|
||||
if (e.code === 4043) {
|
||||
refOTP.value?.clearInput()
|
||||
}
|
||||
})
|
||||
loading.value = false
|
||||
|
@ -118,9 +103,6 @@ auth.get_casdoor_uri()
|
|||
casdoor_uri.value = r.uri
|
||||
}
|
||||
})
|
||||
.catch(e => {
|
||||
message.error($gettext(e.message ?? 'Server error'))
|
||||
})
|
||||
|
||||
function loginWithCasdoor() {
|
||||
window.location.href = casdoor_uri.value
|
||||
|
@ -134,8 +116,6 @@ if (route.query?.code !== undefined && route.query?.state !== undefined) {
|
|||
const next = (route.query?.next || '').toString() || '/'
|
||||
|
||||
await router.push(next)
|
||||
}).catch(e => {
|
||||
message.error($gettext(e.message ?? 'Server error'))
|
||||
})
|
||||
loading.value = false
|
||||
}
|
||||
|
@ -156,27 +136,23 @@ passkey.get_config_status().then(r => {
|
|||
const passkeyLoginLoading = ref(false)
|
||||
async function handlePasskeyLogin() {
|
||||
passkeyLoginLoading.value = true
|
||||
try {
|
||||
const begin = await auth.begin_passkey_login()
|
||||
const asseResp = await startAuthentication({ optionsJSON: begin.options.publicKey })
|
||||
|
||||
const r = await auth.finish_passkey_login({
|
||||
session_id: begin.session_id,
|
||||
options: asseResp,
|
||||
})
|
||||
const begin = await auth.begin_passkey_login()
|
||||
const asseResp = await startAuthentication({ optionsJSON: begin.options.publicKey })
|
||||
|
||||
if (r.token) {
|
||||
const next = (route.query?.next || '').toString() || '/'
|
||||
const r = await auth.finish_passkey_login({
|
||||
session_id: begin.session_id,
|
||||
options: asseResp,
|
||||
})
|
||||
|
||||
passkeyLogin(asseResp.rawId, r.token)
|
||||
secureSessionId.value = r.secure_session_id
|
||||
await router.push(next)
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line ts/no-explicit-any
|
||||
catch (e: any) {
|
||||
message.error($gettext(e.message ?? 'Server error'))
|
||||
if (r.token) {
|
||||
const next = (route.query?.next || '').toString() || '/'
|
||||
|
||||
passkeyLogin(asseResp.rawId, r.token)
|
||||
secureSessionId.value = r.secure_session_id
|
||||
await router.push(next)
|
||||
}
|
||||
|
||||
passkeyLoginLoading.value = false
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -45,8 +45,6 @@ function removeBannedIP(ip: string) {
|
|||
setting.remove_banned_ip(ip).then(() => {
|
||||
bannedIPs.value = bannedIPs.value.filter(v => v.ip !== ip)
|
||||
message.success($gettext('Remove successfully'))
|
||||
}).catch((e: { message?: string }) => {
|
||||
message.error(e?.message ?? $gettext('Server error'))
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -112,9 +112,6 @@ async function save() {
|
|||
refAuthSettings.value?.getBannedIPs?.()
|
||||
message.success($gettext('Save successfully'))
|
||||
errors.value = {}
|
||||
}).catch(e => {
|
||||
errors.value = e.errors
|
||||
message.error(e?.message ?? $gettext('Server error'))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
|
@ -14,24 +14,18 @@ const passkeyEnabled = ref(false)
|
|||
const regLoading = ref(false)
|
||||
async function registerPasskey() {
|
||||
regLoading.value = true
|
||||
try {
|
||||
const optionsJSON = await passkey.begin_registration()
|
||||
const optionsJSON = await passkey.begin_registration()
|
||||
|
||||
const attestationResponse = await startRegistration({ optionsJSON })
|
||||
const attestationResponse = await startRegistration({ optionsJSON })
|
||||
|
||||
await passkey.finish_registration(attestationResponse, passkeyName.value)
|
||||
await passkey.finish_registration(attestationResponse, passkeyName.value)
|
||||
|
||||
emit('created')
|
||||
emit('created')
|
||||
|
||||
message.success($gettext('Register passkey successfully'))
|
||||
addPasskeyModelOpen.value = false
|
||||
message.success($gettext('Register passkey successfully'))
|
||||
addPasskeyModelOpen.value = false
|
||||
|
||||
user.passkeyRawId = attestationResponse.rawId
|
||||
}
|
||||
// eslint-disable-next-line ts/no-explicit-any
|
||||
catch (e: any) {
|
||||
message.error($gettext(e.message ?? 'Server error'))
|
||||
}
|
||||
user.passkeyRawId = attestationResponse.rawId
|
||||
regLoading.value = false
|
||||
}
|
||||
|
||||
|
|
|
@ -22,8 +22,6 @@ function getList() {
|
|||
getListLoading.value = true
|
||||
passkey.get_list().then(r => {
|
||||
data.value = r
|
||||
}).catch((e: { message?: string }) => {
|
||||
message.error(e?.message ?? $gettext('Server error'))
|
||||
}).finally(() => {
|
||||
getListLoading.value = false
|
||||
})
|
||||
|
@ -39,8 +37,6 @@ function update(id: number, record: Passkey) {
|
|||
getList()
|
||||
modifyIdx.value = -1
|
||||
message.success($gettext('Update successfully'))
|
||||
}).catch((e: { message?: string }) => {
|
||||
message.error(e?.message ?? $gettext('Server error'))
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -52,8 +48,6 @@ function remove(item: Passkey) {
|
|||
// if current passkey is removed, clear it from user store
|
||||
if (user.passkeyLoginAvailable && user.passkeyRawId === item.raw_id)
|
||||
user.passkeyRawId = ''
|
||||
}).catch((e: { message?: string }) => {
|
||||
message.error(e?.message ?? $gettext('Server error'))
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -40,8 +40,6 @@ function generateSecret() {
|
|||
secret.value = r.secret
|
||||
qrCode.value = r.qr_code
|
||||
refOtp.value?.clearInput()
|
||||
}).catch((e: { message?: string }) => {
|
||||
message.error(e.message ?? $gettext('Server error'))
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -52,9 +50,8 @@ function enroll(code: string) {
|
|||
clearGenerateSecretInterval()
|
||||
get2FAStatus()
|
||||
message.success($gettext('Enable 2FA successfully'))
|
||||
}).catch((e: { message?: string }) => {
|
||||
}).catch(() => {
|
||||
refOtp.value?.clearInput()
|
||||
message.error(e.message ?? $gettext('Server error'))
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -79,8 +76,6 @@ function reset2FA() {
|
|||
recoveryCode.value = ''
|
||||
get2FAStatus()
|
||||
clickEnable2FA()
|
||||
}).catch((e: { message?: string }) => {
|
||||
message.error($gettext(e.message ?? 'Server error'))
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -28,8 +28,6 @@ function save() {
|
|||
router.push({
|
||||
path: `/sites/${buffer.value}`,
|
||||
})
|
||||
}).catch(e => {
|
||||
message.error($gettext(e?.message ?? 'Server error'))
|
||||
}).finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
|
|
|
@ -48,8 +48,6 @@ function onSubmit() {
|
|||
message.success($gettext('Duplicate to local successfully'))
|
||||
show.value = false
|
||||
emit('duplicated')
|
||||
}).catch(e => {
|
||||
message.error($gettext(e?.message ?? 'Server error'))
|
||||
})
|
||||
|
||||
loading.value = false
|
||||
|
|
|
@ -35,9 +35,7 @@ onMounted(async () => {
|
|||
return
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line ts/no-explicit-any
|
||||
catch (e: any) {
|
||||
message.error(e?.message ?? $gettext('Server error'))
|
||||
catch {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -68,8 +66,6 @@ function destroy(site_name: string) {
|
|||
table.value.get_list()
|
||||
message.success($gettext('Delete site: %{site_name}', { site_name }))
|
||||
inspect_config.value?.test()
|
||||
}).catch(e => {
|
||||
message.error(e?.message ?? $gettext('Server error'))
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -77,8 +77,6 @@ function destroy(stream_name: string) {
|
|||
table.value.get_list()
|
||||
message.success($gettext('Delete stream: %{stream_name}', { stream_name }))
|
||||
inspect_config.value?.test()
|
||||
}).catch(e => {
|
||||
message.error(e?.message ?? $gettext('Server error'))
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -109,8 +107,6 @@ function handleAddStream() {
|
|||
showAddStream.value = false
|
||||
table.value?.get_list()
|
||||
message.success($gettext('Added successfully'))
|
||||
}).catch(e => {
|
||||
message.error(e?.message ?? $gettext('Server error'))
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -5,7 +5,6 @@ import upgrade from '@/api/upgrade'
|
|||
|
||||
import websocket from '@/lib/websocket'
|
||||
import version from '@/version.json'
|
||||
import { message } from 'ant-design-vue'
|
||||
import dayjs from 'dayjs'
|
||||
import { marked } from 'marked'
|
||||
import { useRoute } from 'vue-router'
|
||||
|
@ -39,7 +38,6 @@ function getLatestRelease() {
|
|||
lastCheck.value = dayjs().format('YYYY-MM-DD HH:mm:ss')
|
||||
}).catch(e => {
|
||||
getReleaseError.value = e?.message
|
||||
message.error(e?.message ?? $gettext('Server error'))
|
||||
}).finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
|
|
7
cmd/errdef/generate.go
Normal file
7
cmd/errdef/generate.go
Normal file
|
@ -0,0 +1,7 @@
|
|||
package main
|
||||
|
||||
import "github.com/uozi-tech/cosy/errdef"
|
||||
|
||||
func main() {
|
||||
errdef.Generate()
|
||||
}
|
|
@ -2,7 +2,7 @@ package main
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
|
@ -15,10 +15,14 @@ type Directive struct {
|
|||
}
|
||||
|
||||
func main() {
|
||||
if len(os.Args) < 2 {
|
||||
log.Println("Usage: go run . <output_file>")
|
||||
}
|
||||
outputPath := os.Args[1]
|
||||
// Fetch page content
|
||||
resp, err := http.Get("https://nginx.org/en/docs/dirindex.html")
|
||||
if err != nil {
|
||||
fmt.Println("Error fetching page:", err)
|
||||
log.Println("[Error] fetching page:", err)
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
@ -26,7 +30,7 @@ func main() {
|
|||
// Parse HTML
|
||||
doc, err := html.Parse(resp.Body)
|
||||
if err != nil {
|
||||
fmt.Println("Error parsing HTML:", err)
|
||||
log.Println("[Error] parsing HTML:", err)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -99,15 +103,15 @@ func main() {
|
|||
// Write results to JSON file
|
||||
jsonData, err := json.MarshalIndent(directives, "", " ")
|
||||
if err != nil {
|
||||
fmt.Println("Error marshaling JSON:", err)
|
||||
log.Println("[Error] marshaling JSON:", err)
|
||||
return
|
||||
}
|
||||
|
||||
err = os.WriteFile("../../internal/nginx/nginx_directives.json", jsonData, 0644)
|
||||
err = os.WriteFile(outputPath, jsonData, 0644)
|
||||
if err != nil {
|
||||
fmt.Println("Error writing file:", err)
|
||||
log.Println("[Error] writing file:", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("Successfully parsed %d directives and saved to nginx_directives.json\n", len(directives))
|
||||
log.Printf("[OK] Successfully parsed %d directives and saved to %s\n", len(directives), outputPath)
|
||||
}
|
7
gen.sh
7
gen.sh
|
@ -1,3 +1,10 @@
|
|||
# generate gen code
|
||||
pushd ./cmd/gen || exit
|
||||
go run generate.go -config ../../app.ini
|
||||
popd || exit
|
||||
|
||||
# generate error definitions
|
||||
go run cmd/errdef/generate.go . ts ./app/src/constants/errors
|
||||
|
||||
# parse nginx directive indexs
|
||||
go run cmd/ngx_dir_index/ngx_dir_index.go ./internal/nginx/nginx_directives.json
|
||||
|
|
4
go.mod
4
go.mod
|
@ -15,7 +15,7 @@ require (
|
|||
github.com/gin-contrib/static v1.1.3
|
||||
github.com/gin-gonic/gin v1.10.0
|
||||
github.com/go-acme/lego/v4 v4.21.0
|
||||
github.com/go-co-op/gocron/v2 v2.14.2
|
||||
github.com/go-co-op/gocron/v2 v2.15.0
|
||||
github.com/go-playground/validator/v10 v10.24.0
|
||||
github.com/go-resty/resty/v2 v2.16.4
|
||||
github.com/go-webauthn/webauthn v0.11.2
|
||||
|
@ -35,7 +35,7 @@ require (
|
|||
github.com/spf13/cast v1.7.1
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/tufanbarisyildirim/gonginx v0.0.0-20250120210832-12a9c7ae0c8a
|
||||
github.com/uozi-tech/cosy v1.14.1
|
||||
github.com/uozi-tech/cosy v1.14.2
|
||||
github.com/uozi-tech/cosy-driver-sqlite v0.2.0
|
||||
go.uber.org/zap v1.27.0
|
||||
golang.org/x/crypto v0.32.0
|
||||
|
|
4
go.sum
4
go.sum
|
@ -1027,6 +1027,8 @@ github.com/go-co-op/gocron/v2 v2.14.1 h1:bwWMkX2rNfS6RqBmUAfkDuOPKl/BRCRCrmuAv8f
|
|||
github.com/go-co-op/gocron/v2 v2.14.1/go.mod h1:ZF70ZwEqz0OO4RBXE1sNxnANy/zvwLcattWEFsqpKig=
|
||||
github.com/go-co-op/gocron/v2 v2.14.2 h1:S6CbI7MVfD3S/aPJNLoSg2YcGyEqzEMwUopDejuT4Oc=
|
||||
github.com/go-co-op/gocron/v2 v2.14.2/go.mod h1:ZF70ZwEqz0OO4RBXE1sNxnANy/zvwLcattWEFsqpKig=
|
||||
github.com/go-co-op/gocron/v2 v2.15.0 h1:Kpvo71VSihE+RImmpA+3ta5CcMhoRzMGw4dJawrj4zo=
|
||||
github.com/go-co-op/gocron/v2 v2.15.0/go.mod h1:ZF70ZwEqz0OO4RBXE1sNxnANy/zvwLcattWEFsqpKig=
|
||||
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
|
||||
github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk=
|
||||
github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
|
||||
|
@ -1941,6 +1943,8 @@ github.com/uozi-tech/cosy v1.13.0 h1:dDnU8f3z3AA2KamqEcpa0ObqY4YVXXsNbdxR9Hv7f0A
|
|||
github.com/uozi-tech/cosy v1.13.0/go.mod h1:DSKLtoVaGLUlJ8KiQ1vWEsnv85epRrAAMXSijuq+asM=
|
||||
github.com/uozi-tech/cosy v1.14.1 h1:Qat6Av9XhYMypeBCcagl3Pfp6LWgDRK0+lHBN/1jFY0=
|
||||
github.com/uozi-tech/cosy v1.14.1/go.mod h1:DSKLtoVaGLUlJ8KiQ1vWEsnv85epRrAAMXSijuq+asM=
|
||||
github.com/uozi-tech/cosy v1.14.2 h1:nWTiBSAYn1yTtQeTZluK/G2OjERGs/SRaK4yVjq1IQ8=
|
||||
github.com/uozi-tech/cosy v1.14.2/go.mod h1:DSKLtoVaGLUlJ8KiQ1vWEsnv85epRrAAMXSijuq+asM=
|
||||
github.com/uozi-tech/cosy-driver-mysql v0.2.2 h1:22S/XNIvuaKGqxQPsYPXN8TZ8hHjCQdcJKVQ83Vzxoo=
|
||||
github.com/uozi-tech/cosy-driver-mysql v0.2.2/go.mod h1:EZnRIbSj1V5U0gEeTobrXai/d1SV11lkl4zP9NFEmyE=
|
||||
github.com/uozi-tech/cosy-driver-postgres v0.2.1 h1:OICakGuT+omva6QOJCxTJ5Lfr7CGXLmk/zD+aS51Z2o=
|
||||
|
|
|
@ -35,7 +35,7 @@ func autoCert(certModel *model.Cert) {
|
|||
defer log.Exit()
|
||||
|
||||
if len(certModel.Filename) == 0 {
|
||||
log.Error(errors.New("filename is empty"))
|
||||
log.Error(ErrCertModelFilenameEmpty)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@ import (
|
|||
"encoding/pem"
|
||||
"github.com/0xJacky/Nginx-UI/internal/helper"
|
||||
"github.com/0xJacky/Nginx-UI/internal/nginx"
|
||||
"github.com/pkg/errors"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
@ -19,24 +18,24 @@ type Info struct {
|
|||
|
||||
func GetCertInfo(sslCertificatePath string) (info *Info, err error) {
|
||||
if !helper.IsUnderDirectory(sslCertificatePath, nginx.GetConfPath()) {
|
||||
err = errors.New("ssl certificate path is not under the nginx conf path")
|
||||
err = ErrCertPathIsNotUnderTheNginxConfDir
|
||||
return
|
||||
}
|
||||
|
||||
certData, err := os.ReadFile(sslCertificatePath)
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "error read certificate")
|
||||
return
|
||||
}
|
||||
|
||||
block, _ := pem.Decode(certData)
|
||||
if block == nil || block.Type != "CERTIFICATE" {
|
||||
err = errors.New("certificate decoding error")
|
||||
err = ErrCertDecode
|
||||
return
|
||||
}
|
||||
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
err = errors.Wrap(err, "certificate parsing error")
|
||||
err = ErrCertParse
|
||||
return
|
||||
}
|
||||
|
||||
|
|
13
internal/cert/errors.go
Normal file
13
internal/cert/errors.go
Normal 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}")
|
||||
)
|
|
@ -10,7 +10,7 @@ import (
|
|||
|
||||
func renew(payload *ConfigPayload, client *lego.Client, l *log.Logger, errChan chan error) {
|
||||
if payload.Resource == nil {
|
||||
errChan <- errors.New("resource is nil")
|
||||
errChan <- ErrPayloadResourceIsNil
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@ package cert
|
|||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/0xJacky/Nginx-UI/internal/helper"
|
||||
"github.com/0xJacky/Nginx-UI/internal/nginx"
|
||||
"github.com/0xJacky/Nginx-UI/internal/notification"
|
||||
|
@ -33,13 +32,11 @@ func SyncToRemoteServer(c *model.Cert) (err error) {
|
|||
|
||||
nginxConfPath := nginx.GetConfPath()
|
||||
if !helper.IsUnderDirectory(c.SSLCertificatePath, nginxConfPath) {
|
||||
return fmt.Errorf("ssl_certificate_path: %s is not under the nginx conf path: %s",
|
||||
c.SSLCertificatePath, nginxConfPath)
|
||||
return e.NewWithParams(50006, ErrPathIsNotUnderTheNginxConfDir.Error(), c.SSLCertificatePath, nginxConfPath)
|
||||
}
|
||||
|
||||
if !helper.IsUnderDirectory(c.SSLCertificateKeyPath, nginxConfPath) {
|
||||
return fmt.Errorf("ssl_certificate_key_path: %s is not under the nginx conf path: %s",
|
||||
c.SSLCertificateKeyPath, nginxConfPath)
|
||||
return e.NewWithParams(50006, ErrPathIsNotUnderTheNginxConfDir.Error(), c.SSLCertificateKeyPath, nginxConfPath)
|
||||
}
|
||||
|
||||
certBytes, err := os.ReadFile(c.SSLCertificatePath)
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package cert
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/0xJacky/Nginx-UI/internal/helper"
|
||||
"github.com/0xJacky/Nginx-UI/internal/nginx"
|
||||
"os"
|
||||
|
@ -22,13 +21,11 @@ func (c *Content) WriteFile() (err error) {
|
|||
|
||||
nginxConfPath := nginx.GetConfPath()
|
||||
if !helper.IsUnderDirectory(c.SSLCertificatePath, nginxConfPath) {
|
||||
return fmt.Errorf("ssl_certificate_path: %s is not under the nginx conf path: %s",
|
||||
c.SSLCertificatePath, nginxConfPath)
|
||||
return e.NewWithParams(50006, ErrPathIsNotUnderTheNginxConfDir.Error(), c.SSLCertificatePath, nginxConfPath)
|
||||
}
|
||||
|
||||
if !helper.IsUnderDirectory(c.SSLCertificateKeyPath, nginxConfPath) {
|
||||
return fmt.Errorf("ssl_certificate_key_path: %s is not under the nginx conf path: %s",
|
||||
c.SSLCertificateKeyPath, nginxConfPath)
|
||||
return e.NewWithParams(50006, ErrPathIsNotUnderTheNginxConfDir.Error(), c.SSLCertificateKeyPath, nginxConfPath)
|
||||
}
|
||||
|
||||
// MkdirAll creates a directory named path, along with any necessary parents,
|
||||
|
|
8
internal/config/errors.go
Normal file
8
internal/config/errors.go
Normal 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}")
|
||||
)
|
|
@ -4,7 +4,6 @@ import (
|
|||
"bytes"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/0xJacky/Nginx-UI/internal/helper"
|
||||
"github.com/0xJacky/Nginx-UI/internal/nginx"
|
||||
"github.com/0xJacky/Nginx-UI/internal/notification"
|
||||
|
@ -35,8 +34,7 @@ func SyncToRemoteServer(c *model.Config) (err error) {
|
|||
|
||||
nginxConfPath := nginx.GetConfPath()
|
||||
if !helper.IsUnderDirectory(c.Filepath, nginxConfPath) {
|
||||
return fmt.Errorf("config: %s is not under the nginx conf path: %s",
|
||||
c.Filepath, nginxConfPath)
|
||||
return e.NewWithParams(50006, ErrPathIsNotUnderTheNginxConfDir.Error(), c.Filepath, nginxConfPath)
|
||||
}
|
||||
|
||||
configBytes, err := os.ReadFile(c.Filepath)
|
||||
|
@ -76,13 +74,11 @@ func SyncRenameOnRemoteServer(origPath, newPath string, syncNodeIds []uint64) (e
|
|||
|
||||
nginxConfPath := nginx.GetConfPath()
|
||||
if !helper.IsUnderDirectory(origPath, nginxConfPath) {
|
||||
return fmt.Errorf("config: %s is not under the nginx conf path: %s",
|
||||
origPath, nginxConfPath)
|
||||
return e.NewWithParams(50006, ErrPathIsNotUnderTheNginxConfDir.Error(), origPath, nginxConfPath)
|
||||
}
|
||||
|
||||
if !helper.IsUnderDirectory(newPath, nginxConfPath) {
|
||||
return fmt.Errorf("config: %s is not under the nginx conf path: %s",
|
||||
newPath, nginxConfPath)
|
||||
return e.NewWithParams(50006, ErrPathIsNotUnderTheNginxConfDir.Error(), newPath, nginxConfPath)
|
||||
}
|
||||
|
||||
payload := &RenameConfigPayload{
|
||||
|
|
|
@ -5,27 +5,25 @@ import (
|
|||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"github.com/0xJacky/Nginx-UI/settings"
|
||||
"github.com/pkg/errors"
|
||||
"io"
|
||||
)
|
||||
|
||||
// AesEncrypt encrypts text and given key with AES.
|
||||
func AesEncrypt(text []byte) ([]byte, error) {
|
||||
if len(text) == 0 {
|
||||
return nil, errors.New("AesEncrypt text is empty")
|
||||
return nil, ErrPlainTextEmpty
|
||||
}
|
||||
block, err := aes.NewCipher(settings.CryptoSettings.GetSecretMd5())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("AesEncrypt invalid key: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b := base64.StdEncoding.EncodeToString(text)
|
||||
ciphertext := make([]byte, aes.BlockSize+len(b))
|
||||
iv := ciphertext[:aes.BlockSize]
|
||||
if _, err = io.ReadFull(rand.Reader, iv); err != nil {
|
||||
return nil, fmt.Errorf("AesEncrypt unable to read IV: %w", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cfb := cipher.NewCFBEncrypter(block, iv)
|
||||
|
@ -42,7 +40,7 @@ func AesDecrypt(text []byte) ([]byte, error) {
|
|||
}
|
||||
|
||||
if len(text) < aes.BlockSize {
|
||||
return nil, errors.New("AesDecrypt ciphertext too short")
|
||||
return nil, ErrCipherTextTooShort
|
||||
}
|
||||
|
||||
iv := text[:aes.BlockSize]
|
||||
|
@ -52,7 +50,7 @@ func AesDecrypt(text []byte) ([]byte, error) {
|
|||
|
||||
data, err := base64.StdEncoding.DecodeString(string(text))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("AesDecrypt invalid decrypted base64 string: %w", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return data, nil
|
||||
|
|
9
internal/crypto/errors.go
Normal file
9
internal/crypto/errors.go
Normal 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")
|
||||
)
|
|
@ -3,7 +3,7 @@ package helper
|
|||
import (
|
||||
"strings"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/pkg/errors"
|
||||
"errors"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ import (
|
|||
"github.com/0xJacky/Nginx-UI/settings"
|
||||
"github.com/caarlos0/env/v11"
|
||||
"github.com/google/uuid"
|
||||
"github.com/pkg/errors"
|
||||
"errors"
|
||||
"github.com/uozi-tech/cosy/logger"
|
||||
cSettings "github.com/uozi-tech/cosy/settings"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
|
|
8
internal/nginx/errors.go
Normal file
8
internal/nginx/errors.go
Normal 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")
|
||||
)
|
|
@ -155,7 +155,7 @@ func buildComment(c []string) string {
|
|||
|
||||
func parse(block config.IBlock, ngxConfig *NgxConfig) (err error) {
|
||||
if block == nil {
|
||||
err = errors.New("block is nil")
|
||||
err = ErrBlockIsNil
|
||||
return
|
||||
}
|
||||
for _, v := range block.GetDirectives() {
|
||||
|
|
14
internal/nginx_log/errors.go
Normal file
14
internal/nginx_log/errors.go
Normal 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")
|
||||
)
|
41
internal/nginx_log/nginx_log.go
Normal file
41
internal/nginx_log/nginx_log.go
Normal 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)
|
||||
}
|
|
@ -11,9 +11,9 @@ var (
|
|||
ErrNginxConfNotIncludeSitesEnabled = e.New(4043, "Nginx conf not include sites-enabled")
|
||||
ErrorNginxConfNoStreamBlock = e.New(4044, "Nginx conf no stream block")
|
||||
ErrNginxConfNotIncludeStreamEnabled = e.New(4045, "Nginx conf not include stream-enabled")
|
||||
ErrFailedToCreateBackup = e.New(5001, "Failed to create backup")
|
||||
ErrFailedToCreateBackup = e.New(5002, "Failed to create backup")
|
||||
ErrSitesAvailableNotExist = e.New(4046, "Sites-available directory not exist")
|
||||
ErrSitesEnabledNotExist = e.New(4047, "Sites-enabled directory not exist")
|
||||
ErrStreamAvailableNotExist = e.New(4048, "Stream-available directory not exist")
|
||||
ErrStreamEnabledNotExist = e.New(4049, "Stream-enabled directory not exist")
|
||||
ErrStreamAvailableNotExist = e.New(4048, "Streams-available directory not exist")
|
||||
ErrStreamEnabledNotExist = e.New(4049, "Streams-enabled directory not exist")
|
||||
)
|
||||
|
|
|
@ -6,7 +6,6 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/0xJacky/Nginx-UI/internal/nginx"
|
||||
"github.com/spf13/cast"
|
||||
"github.com/tufanbarisyildirim/gonginx/config"
|
||||
"github.com/tufanbarisyildirim/gonginx/dumper"
|
||||
"github.com/tufanbarisyildirim/gonginx/parser"
|
||||
|
@ -88,7 +87,7 @@ func FixNginxConfIncludeSites() error {
|
|||
}
|
||||
|
||||
// create a backup file (+.bak.timestamp)
|
||||
backupPath := path + ".bak." + cast.ToString(time.Now().Unix())
|
||||
backupPath := fmt.Sprintf("%s.bak.%d", path, time.Now().Unix())
|
||||
err = os.WriteFile(backupPath, content, 0644)
|
||||
if err != nil {
|
||||
return ErrFailedToCreateBackup
|
||||
|
@ -133,7 +132,7 @@ func FixNginxConfIncludeStreams() error {
|
|||
}
|
||||
|
||||
// create a backup file (+.bak.timestamp)
|
||||
backupPath := path + ".bak." + cast.ToString(time.Now().Unix())
|
||||
backupPath := fmt.Sprintf("%s.bak.%d", path, time.Now().Unix())
|
||||
err = os.WriteFile(backupPath, content, 0644)
|
||||
if err != nil {
|
||||
return ErrFailedToCreateBackup
|
||||
|
|
|
@ -29,11 +29,11 @@ func Delete(name string) (err error) {
|
|||
enabledPath := nginx.GetConfPath("sites-enabled", name)
|
||||
|
||||
if !helper.FileExists(availablePath) {
|
||||
return fmt.Errorf("site not found")
|
||||
return ErrSiteNotFound
|
||||
}
|
||||
|
||||
if helper.FileExists(enabledPath) {
|
||||
return fmt.Errorf("site is enabled")
|
||||
return ErrSiteIsEnabled
|
||||
}
|
||||
|
||||
certModel := model.Cert{Filename: name}
|
||||
|
|
|
@ -3,7 +3,6 @@ package site
|
|||
import (
|
||||
"github.com/0xJacky/Nginx-UI/internal/helper"
|
||||
"github.com/0xJacky/Nginx-UI/internal/nginx"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// Duplicate duplicates a site by copying the file
|
||||
|
@ -12,7 +11,7 @@ func Duplicate(src, dst string) (err error) {
|
|||
dst = nginx.GetConfPath("sites-available", dst)
|
||||
|
||||
if helper.FileExists(dst) {
|
||||
return errors.New("file exists")
|
||||
return ErrDstFileExists
|
||||
}
|
||||
|
||||
_, err = helper.CopyFile(src, dst)
|
||||
|
|
10
internal/site/errors.go
Normal file
10
internal/site/errors.go
Normal 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")
|
||||
)
|
|
@ -24,7 +24,7 @@ func Rename(oldName string, newName string) (err error) {
|
|||
|
||||
// check if dst file exists, do not rename
|
||||
if helper.FileExists(newPath) {
|
||||
return fmt.Errorf("file exists")
|
||||
return ErrDstFileExists
|
||||
}
|
||||
|
||||
s := query.Site
|
||||
|
@ -84,9 +84,9 @@ func syncRename(oldName, newName string) {
|
|||
client.SetBaseURL(node.URL)
|
||||
resp, err := client.R().
|
||||
SetHeader("X-Node-Secret", node.Token).
|
||||
SetBody(map[string]string{
|
||||
"new_name": newName,
|
||||
}).
|
||||
SetBody(map[string]string{
|
||||
"new_name": newName,
|
||||
}).
|
||||
Post(fmt.Sprintf("/api/sites/%s/rename", oldName))
|
||||
if err != nil {
|
||||
notification.Error("Rename Remote Site Error", err.Error())
|
||||
|
|
|
@ -19,7 +19,7 @@ import (
|
|||
func Save(name string, content string, overwrite bool, siteCategoryId uint64, syncNodeIds []uint64) (err error) {
|
||||
path := nginx.GetConfPath("sites-available", name)
|
||||
if !overwrite && helper.FileExists(path) {
|
||||
return fmt.Errorf("file exists")
|
||||
return ErrDstFileExists
|
||||
}
|
||||
|
||||
err = os.WriteFile(path, []byte(content), 0644)
|
||||
|
|
15
internal/user/errors.go
Normal file
15
internal/user/errors.go
Normal 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")
|
||||
)
|
|
@ -1,7 +1,6 @@
|
|||
package user
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/0xJacky/Nginx-UI/model"
|
||||
"github.com/0xJacky/Nginx-UI/query"
|
||||
"github.com/0xJacky/Nginx-UI/settings"
|
||||
|
@ -9,11 +8,6 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrPasswordIncorrect = errors.New("password incorrect")
|
||||
ErrUserBanned = errors.New("user banned")
|
||||
)
|
||||
|
||||
func Login(name string, password string) (user *model.User, err error) {
|
||||
u := query.User
|
||||
|
||||
|
|
|
@ -9,16 +9,10 @@ import (
|
|||
"github.com/0xJacky/Nginx-UI/internal/crypto"
|
||||
"github.com/0xJacky/Nginx-UI/model"
|
||||
"github.com/google/uuid"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/pquerna/otp/totp"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrOTPCode = errors.New("invalid otp code")
|
||||
ErrRecoveryCode = errors.New("invalid recovery code")
|
||||
)
|
||||
|
||||
func VerifyOTP(user *model.User, otp, recoveryCode string) (err error) {
|
||||
if otp != "" {
|
||||
decrypted, err := crypto.AesDecrypt(user.OTPSecret)
|
||||
|
|
2
main.go
2
main.go
|
@ -9,7 +9,7 @@ import (
|
|||
"github.com/0xJacky/Nginx-UI/settings"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/jpillora/overseer"
|
||||
"github.com/pkg/errors"
|
||||
"errors"
|
||||
"github.com/uozi-tech/cosy"
|
||||
cKernel "github.com/uozi-tech/cosy/kernel"
|
||||
"github.com/uozi-tech/cosy/logger"
|
||||
|
|
|
@ -4,7 +4,7 @@ import (
|
|||
"database/sql/driver"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/pkg/errors"
|
||||
"errors"
|
||||
"github.com/sashabaranov/go-openai"
|
||||
)
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"github.com/0xJacky/Nginx-UI/api/cluster"
|
||||
"github.com/0xJacky/Nginx-UI/api/config"
|
||||
"github.com/0xJacky/Nginx-UI/api/nginx"
|
||||
nginxLog "github.com/0xJacky/Nginx-UI/api/nginx_log"
|
||||
"github.com/0xJacky/Nginx-UI/api/notification"
|
||||
"github.com/0xJacky/Nginx-UI/api/openai"
|
||||
"github.com/0xJacky/Nginx-UI/api/public"
|
||||
|
@ -75,7 +76,7 @@ func InitRouter() {
|
|||
{
|
||||
terminal.InitRouter(o)
|
||||
}
|
||||
nginx.InitNginxLogRouter(w)
|
||||
nginxLog.InitRouter(w)
|
||||
upstream.InitRouter(w)
|
||||
system.InitWebSocketRouter(w)
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"github.com/0xJacky/Nginx-UI/settings"
|
||||
"github.com/pkg/errors"
|
||||
"errors"
|
||||
"github.com/sashabaranov/go-openai"
|
||||
"github.com/uozi-tech/cosy/sandbox"
|
||||
"io"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue