mirror of
https://github.com/yusing/godoxy.git
synced 2026-04-18 14:39:49 +02:00
refactor: move task, error and testing utils to separte repo; apply gofumpt
This commit is contained in:
@@ -7,11 +7,11 @@ import (
|
||||
"github.com/puzpuzpuz/xsync/v4"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/yusing/godoxy/internal/common"
|
||||
"github.com/yusing/godoxy/internal/gperr"
|
||||
"github.com/yusing/godoxy/internal/logging/accesslog"
|
||||
"github.com/yusing/godoxy/internal/maxmind"
|
||||
"github.com/yusing/godoxy/internal/task"
|
||||
"github.com/yusing/godoxy/internal/utils"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
"github.com/yusing/goutils/task"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
|
||||
@@ -4,8 +4,8 @@ import (
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/yusing/godoxy/internal/gperr"
|
||||
"github.com/yusing/godoxy/internal/maxmind"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
)
|
||||
|
||||
type MatcherFunc func(*maxmind.IPInfo) bool
|
||||
|
||||
@@ -20,7 +20,7 @@ import (
|
||||
routeApi "github.com/yusing/godoxy/internal/api/v1/route"
|
||||
"github.com/yusing/godoxy/internal/auth"
|
||||
"github.com/yusing/godoxy/internal/common"
|
||||
"github.com/yusing/godoxy/internal/gperr"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
)
|
||||
|
||||
// @title GoDoxy API
|
||||
|
||||
@@ -3,7 +3,7 @@ package apitypes
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/yusing/godoxy/internal/gperr"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
)
|
||||
|
||||
type ErrorResponse struct {
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/yusing/godoxy/agent/pkg/agent"
|
||||
apitypes "github.com/yusing/godoxy/internal/api/types"
|
||||
apitypes "github.com/yusing/goutils/apitypes"
|
||||
)
|
||||
|
||||
type NewAgentRequest struct {
|
||||
|
||||
@@ -8,8 +8,8 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/yusing/godoxy/agent/pkg/agent"
|
||||
"github.com/yusing/godoxy/agent/pkg/certs"
|
||||
. "github.com/yusing/godoxy/internal/api/types"
|
||||
config "github.com/yusing/godoxy/internal/config/types"
|
||||
apitypes "github.com/yusing/goutils/apitypes"
|
||||
)
|
||||
|
||||
type VerifyNewAgentRequest struct {
|
||||
@@ -35,44 +35,44 @@ type VerifyNewAgentRequest struct {
|
||||
func Verify(c *gin.Context) {
|
||||
var request VerifyNewAgentRequest
|
||||
if err := c.ShouldBindJSON(&request); err != nil {
|
||||
c.JSON(http.StatusBadRequest, Error("invalid request", err))
|
||||
c.JSON(http.StatusBadRequest, apitypes.Error("invalid request", err))
|
||||
return
|
||||
}
|
||||
|
||||
filename, ok := certs.AgentCertsFilepath(request.Host)
|
||||
if !ok {
|
||||
c.JSON(http.StatusBadRequest, Error("invalid host", nil))
|
||||
c.JSON(http.StatusBadRequest, apitypes.Error("invalid host", nil))
|
||||
return
|
||||
}
|
||||
|
||||
ca, err := fromEncryptedPEMPairResponse(request.CA)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, Error("invalid CA", err))
|
||||
c.JSON(http.StatusBadRequest, apitypes.Error("invalid CA", err))
|
||||
return
|
||||
}
|
||||
|
||||
client, err := fromEncryptedPEMPairResponse(request.Client)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, Error("invalid client", err))
|
||||
c.JSON(http.StatusBadRequest, apitypes.Error("invalid client", err))
|
||||
return
|
||||
}
|
||||
|
||||
nRoutesAdded, err := config.GetInstance().VerifyNewAgent(request.Host, ca, client, request.ContainerRuntime)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, Error("invalid request", err))
|
||||
c.JSON(http.StatusBadRequest, apitypes.Error("invalid request", err))
|
||||
return
|
||||
}
|
||||
|
||||
zip, err := certs.ZipCert(ca.Cert, client.Cert, client.Key)
|
||||
if err != nil {
|
||||
c.Error(InternalServerError(err, "failed to zip certs"))
|
||||
c.Error(apitypes.InternalServerError(err, "failed to zip certs"))
|
||||
return
|
||||
}
|
||||
|
||||
if err := os.WriteFile(filename, zip, 0o600); err != nil {
|
||||
c.Error(InternalServerError(err, "failed to write certs"))
|
||||
c.Error(apitypes.InternalServerError(err, "failed to write certs"))
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, Success(fmt.Sprintf("Added %d routes", nRoutesAdded)))
|
||||
c.JSON(http.StatusOK, apitypes.Success(fmt.Sprintf("Added %d routes", nRoutesAdded)))
|
||||
}
|
||||
|
||||
@@ -8,9 +8,9 @@ import (
|
||||
"github.com/rs/zerolog/log"
|
||||
apitypes "github.com/yusing/godoxy/internal/api/types"
|
||||
config "github.com/yusing/godoxy/internal/config/types"
|
||||
"github.com/yusing/godoxy/internal/gperr"
|
||||
"github.com/yusing/godoxy/internal/logging/memlogger"
|
||||
"github.com/yusing/godoxy/internal/net/gphttp/websocket"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
)
|
||||
|
||||
// @x-id "renew"
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/yusing/godoxy/internal/gperr"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
)
|
||||
|
||||
type ContainerState = container.ContainerState // @name ContainerState
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
|
||||
dockerSystem "github.com/docker/docker/api/types/system"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/yusing/godoxy/internal/gperr"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
strutils "github.com/yusing/goutils/strings"
|
||||
)
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
apitypes "github.com/yusing/godoxy/internal/api/types"
|
||||
"github.com/yusing/godoxy/internal/docker"
|
||||
"github.com/yusing/godoxy/internal/net/gphttp/websocket"
|
||||
"github.com/yusing/godoxy/internal/task"
|
||||
"github.com/yusing/goutils/task"
|
||||
)
|
||||
|
||||
type LogsQueryParams struct {
|
||||
|
||||
@@ -10,8 +10,8 @@ import (
|
||||
apitypes "github.com/yusing/godoxy/internal/api/types"
|
||||
config "github.com/yusing/godoxy/internal/config/types"
|
||||
"github.com/yusing/godoxy/internal/docker"
|
||||
"github.com/yusing/godoxy/internal/gperr"
|
||||
"github.com/yusing/godoxy/internal/net/gphttp/websocket"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
"github.com/yusing/goutils/http/httpheaders"
|
||||
)
|
||||
|
||||
|
||||
@@ -6,9 +6,9 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
apitypes "github.com/yusing/godoxy/internal/api/types"
|
||||
config "github.com/yusing/godoxy/internal/config/types"
|
||||
"github.com/yusing/godoxy/internal/gperr"
|
||||
"github.com/yusing/godoxy/internal/net/gphttp/middleware"
|
||||
"github.com/yusing/godoxy/internal/route/provider"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
)
|
||||
|
||||
type ValidateFileRequest struct {
|
||||
|
||||
@@ -14,10 +14,10 @@ import (
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/yusing/godoxy/agent/pkg/agent"
|
||||
apitypes "github.com/yusing/godoxy/internal/api/types"
|
||||
"github.com/yusing/godoxy/internal/gperr"
|
||||
"github.com/yusing/godoxy/internal/metrics/period"
|
||||
"github.com/yusing/godoxy/internal/metrics/systeminfo"
|
||||
"github.com/yusing/godoxy/internal/net/gphttp/websocket"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
"github.com/yusing/goutils/http/httpheaders"
|
||||
"github.com/yusing/goutils/synk"
|
||||
)
|
||||
|
||||
@@ -15,9 +15,9 @@ import (
|
||||
"github.com/coreos/go-oidc/v3/oidc"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/yusing/godoxy/internal/common"
|
||||
"github.com/yusing/godoxy/internal/gperr"
|
||||
"github.com/yusing/godoxy/internal/net/gphttp"
|
||||
"github.com/yusing/godoxy/internal/utils"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
"golang.org/x/oauth2"
|
||||
"golang.org/x/time/rate"
|
||||
)
|
||||
|
||||
@@ -16,7 +16,7 @@ import (
|
||||
"github.com/yusing/godoxy/internal/common"
|
||||
"golang.org/x/oauth2"
|
||||
|
||||
. "github.com/yusing/godoxy/internal/utils/testing"
|
||||
expect "github.com/yusing/goutils/testing"
|
||||
)
|
||||
|
||||
// setupMockOIDC configures mock OIDC provider for testing.
|
||||
@@ -35,7 +35,7 @@ func setupMockOIDC(t *testing.T) {
|
||||
},
|
||||
Scopes: []string{oidc.ScopeOpenID, "profile", "email"},
|
||||
},
|
||||
endSessionURL: Must(url.Parse("http://mock-provider/logout")),
|
||||
endSessionURL: expect.Must(url.Parse("http://mock-provider/logout")),
|
||||
oidcProvider: provider,
|
||||
oidcVerifier: provider.Verifier(&oidc.Config{
|
||||
ClientID: "test-client",
|
||||
@@ -75,7 +75,7 @@ func (j *provider) SignClaims(t *testing.T, claims jwt.Claims) string {
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
|
||||
token.Header["kid"] = keyID
|
||||
signed, err := token.SignedString(j.key)
|
||||
ExpectNoError(t, err)
|
||||
expect.NoError(t, err)
|
||||
return signed
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ func setupProvider(t *testing.T) *provider {
|
||||
|
||||
// Generate an RSA key pair for the test.
|
||||
privKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
ExpectNoError(t, err)
|
||||
expect.NoError(t, err)
|
||||
|
||||
// Build the matching public JWK that will be served by the endpoint.
|
||||
jwk := buildRSAJWK(t, &privKey.PublicKey, keyID)
|
||||
@@ -227,12 +227,12 @@ func TestOIDCCallbackHandler(t *testing.T) {
|
||||
}
|
||||
|
||||
if tt.wantStatus == http.StatusTemporaryRedirect {
|
||||
setCookie := Must(http.ParseSetCookie(w.Header().Get("Set-Cookie")))
|
||||
ExpectEqual(t, setCookie.Name, CookieOauthToken)
|
||||
ExpectTrue(t, setCookie.Value != "")
|
||||
ExpectEqual(t, setCookie.Path, "/")
|
||||
ExpectEqual(t, setCookie.SameSite, http.SameSiteLaxMode)
|
||||
ExpectEqual(t, setCookie.HttpOnly, true)
|
||||
setCookie := expect.Must(http.ParseSetCookie(w.Header().Get("Set-Cookie")))
|
||||
expect.Equal(t, setCookie.Name, CookieOauthToken)
|
||||
expect.True(t, setCookie.Value != "")
|
||||
expect.Equal(t, setCookie.Path, "/")
|
||||
expect.Equal(t, setCookie.SameSite, http.SameSiteLaxMode)
|
||||
expect.Equal(t, setCookie.HttpOnly, true)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -245,7 +245,7 @@ func TestInitOIDC(t *testing.T) {
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
ExpectNoError(t, json.NewEncoder(w).Encode(discoveryDocument(t, server)))
|
||||
expect.NoError(t, json.NewEncoder(w).Encode(discoveryDocument(t, server)))
|
||||
})
|
||||
server = httptest.NewServer(mux)
|
||||
t.Cleanup(server.Close)
|
||||
@@ -445,9 +445,9 @@ func TestCheckToken(t *testing.T) {
|
||||
// Call CheckToken and verify the result.
|
||||
err := auth.CheckToken(req)
|
||||
if tc.wantErr == nil {
|
||||
ExpectNoError(t, err)
|
||||
expect.NoError(t, err)
|
||||
} else {
|
||||
ExpectError(t, tc.wantErr, err)
|
||||
expect.ErrorIs(t, tc.wantErr, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -8,8 +8,8 @@ import (
|
||||
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"github.com/yusing/godoxy/internal/common"
|
||||
"github.com/yusing/godoxy/internal/gperr"
|
||||
"github.com/yusing/godoxy/internal/net/gphttp"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
strutils "github.com/yusing/goutils/strings"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
@@ -9,14 +9,14 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
. "github.com/yusing/godoxy/internal/utils/testing"
|
||||
expect "github.com/yusing/goutils/testing"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
func newMockUserPassAuth() *UserPassAuth {
|
||||
return &UserPassAuth{
|
||||
username: "username",
|
||||
pwdHash: Must(bcrypt.GenerateFromPassword([]byte("password"), bcrypt.DefaultCost)),
|
||||
pwdHash: expect.Must(bcrypt.GenerateFromPassword([]byte("password"), bcrypt.DefaultCost)),
|
||||
secret: []byte("abcdefghijklmnopqrstuvwxyz"),
|
||||
tokenTTL: time.Hour,
|
||||
}
|
||||
@@ -25,17 +25,17 @@ func newMockUserPassAuth() *UserPassAuth {
|
||||
func TestUserPassValidateCredentials(t *testing.T) {
|
||||
auth := newMockUserPassAuth()
|
||||
err := auth.validatePassword("username", "password")
|
||||
ExpectNoError(t, err)
|
||||
expect.NoError(t, err)
|
||||
err = auth.validatePassword("username", "wrong-password")
|
||||
ExpectError(t, ErrInvalidPassword, err)
|
||||
expect.ErrorIs(t, ErrInvalidPassword, err)
|
||||
err = auth.validatePassword("wrong-username", "password")
|
||||
ExpectError(t, ErrInvalidUsername, err)
|
||||
expect.ErrorIs(t, ErrInvalidUsername, err)
|
||||
}
|
||||
|
||||
func TestUserPassCheckToken(t *testing.T) {
|
||||
auth := newMockUserPassAuth()
|
||||
token, err := auth.NewToken()
|
||||
ExpectNoError(t, err)
|
||||
expect.NoError(t, err)
|
||||
tests := []struct {
|
||||
token string
|
||||
wantErr bool
|
||||
@@ -60,9 +60,9 @@ func TestUserPassCheckToken(t *testing.T) {
|
||||
}
|
||||
err = auth.CheckToken(req)
|
||||
if tt.wantErr {
|
||||
ExpectTrue(t, err != nil)
|
||||
expect.True(t, err != nil)
|
||||
} else {
|
||||
ExpectNoError(t, err)
|
||||
expect.NoError(t, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -96,20 +96,20 @@ func TestUserPassLoginCallbackHandler(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
req := &http.Request{
|
||||
Host: "app.example.com",
|
||||
Body: io.NopCloser(bytes.NewReader(Must(json.Marshal(tt.creds)))),
|
||||
Body: io.NopCloser(bytes.NewReader(expect.Must(json.Marshal(tt.creds)))),
|
||||
}
|
||||
auth.PostAuthCallbackHandler(w, req)
|
||||
if tt.wantErr {
|
||||
ExpectEqual(t, w.Code, http.StatusUnauthorized)
|
||||
expect.Equal(t, w.Code, http.StatusUnauthorized)
|
||||
} else {
|
||||
setCookie := Must(http.ParseSetCookie(w.Header().Get("Set-Cookie")))
|
||||
ExpectTrue(t, setCookie.Name == auth.TokenCookieName())
|
||||
ExpectTrue(t, setCookie.Value != "")
|
||||
ExpectEqual(t, setCookie.Domain, "example.com")
|
||||
ExpectEqual(t, setCookie.Path, "/")
|
||||
ExpectEqual(t, setCookie.SameSite, http.SameSiteLaxMode)
|
||||
ExpectEqual(t, setCookie.HttpOnly, true)
|
||||
ExpectEqual(t, w.Code, http.StatusOK)
|
||||
setCookie := expect.Must(http.ParseSetCookie(w.Header().Get("Set-Cookie")))
|
||||
expect.True(t, setCookie.Name == auth.TokenCookieName())
|
||||
expect.True(t, setCookie.Value != "")
|
||||
expect.Equal(t, setCookie.Domain, "example.com")
|
||||
expect.Equal(t, setCookie.Path, "/")
|
||||
expect.Equal(t, setCookie.SameSite, http.SameSiteLaxMode)
|
||||
expect.Equal(t, setCookie.HttpOnly, true)
|
||||
expect.Equal(t, w.Code, http.StatusOK)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/yusing/godoxy/internal/common"
|
||||
"github.com/yusing/godoxy/internal/gperr"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
strutils "github.com/yusing/goutils/strings"
|
||||
)
|
||||
|
||||
|
||||
@@ -15,8 +15,8 @@ import (
|
||||
"github.com/go-acme/lego/v4/lego"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/yusing/godoxy/internal/common"
|
||||
"github.com/yusing/godoxy/internal/gperr"
|
||||
"github.com/yusing/godoxy/internal/utils"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
|
||||
@@ -18,10 +18,10 @@ import (
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/yusing/godoxy/internal/common"
|
||||
"github.com/yusing/godoxy/internal/gperr"
|
||||
"github.com/yusing/godoxy/internal/notif"
|
||||
"github.com/yusing/godoxy/internal/task"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
strutils "github.com/yusing/goutils/strings"
|
||||
"github.com/yusing/goutils/task"
|
||||
)
|
||||
|
||||
type (
|
||||
|
||||
@@ -2,8 +2,8 @@ package autocert
|
||||
|
||||
import (
|
||||
"github.com/go-acme/lego/v4/challenge"
|
||||
"github.com/yusing/godoxy/internal/gperr"
|
||||
"github.com/yusing/godoxy/internal/serialization"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
)
|
||||
|
||||
type Generator func(map[string]any) (challenge.Provider, gperr.Error)
|
||||
|
||||
@@ -3,7 +3,7 @@ package autocert
|
||||
import (
|
||||
"crypto/tls"
|
||||
|
||||
"github.com/yusing/godoxy/internal/task"
|
||||
"github.com/yusing/goutils/task"
|
||||
)
|
||||
|
||||
type Provider interface {
|
||||
|
||||
@@ -2,8 +2,8 @@ package config
|
||||
|
||||
import (
|
||||
"github.com/yusing/godoxy/agent/pkg/agent"
|
||||
"github.com/yusing/godoxy/internal/gperr"
|
||||
"github.com/yusing/godoxy/internal/route/provider"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
)
|
||||
|
||||
func (cfg *Config) VerifyNewAgent(host string, ca agent.PEMPair, client agent.PEMPair, containerRuntime agent.ContainerRuntime) (int, gperr.Error) {
|
||||
|
||||
@@ -18,17 +18,17 @@ import (
|
||||
"github.com/yusing/godoxy/internal/common"
|
||||
config "github.com/yusing/godoxy/internal/config/types"
|
||||
"github.com/yusing/godoxy/internal/entrypoint"
|
||||
"github.com/yusing/godoxy/internal/gperr"
|
||||
"github.com/yusing/godoxy/internal/maxmind"
|
||||
"github.com/yusing/godoxy/internal/net/gphttp/server"
|
||||
"github.com/yusing/godoxy/internal/notif"
|
||||
"github.com/yusing/godoxy/internal/proxmox"
|
||||
proxy "github.com/yusing/godoxy/internal/route/provider"
|
||||
"github.com/yusing/godoxy/internal/serialization"
|
||||
"github.com/yusing/godoxy/internal/task"
|
||||
"github.com/yusing/godoxy/internal/watcher"
|
||||
"github.com/yusing/godoxy/internal/watcher/events"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
"github.com/yusing/goutils/strings/ansi"
|
||||
"github.com/yusing/goutils/task"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
|
||||
@@ -9,13 +9,13 @@ import (
|
||||
"github.com/yusing/godoxy/agent/pkg/agent"
|
||||
"github.com/yusing/godoxy/internal/acl"
|
||||
"github.com/yusing/godoxy/internal/autocert"
|
||||
"github.com/yusing/godoxy/internal/gperr"
|
||||
"github.com/yusing/godoxy/internal/logging/accesslog"
|
||||
maxmind "github.com/yusing/godoxy/internal/maxmind/types"
|
||||
"github.com/yusing/godoxy/internal/notif"
|
||||
"github.com/yusing/godoxy/internal/proxmox"
|
||||
"github.com/yusing/godoxy/internal/serialization"
|
||||
"github.com/yusing/godoxy/internal/types"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
)
|
||||
|
||||
type (
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package dnsproviders
|
||||
|
||||
type DummyConfig struct{}
|
||||
type DummyProvider struct{}
|
||||
type (
|
||||
DummyConfig struct{}
|
||||
DummyProvider struct{}
|
||||
)
|
||||
|
||||
func NewDummyDefaultConfig() *DummyConfig {
|
||||
return &DummyConfig{}
|
||||
|
||||
@@ -148,7 +148,7 @@ require (
|
||||
github.com/vultr/govultr/v3 v3.24.0 // indirect
|
||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
|
||||
github.com/yusing/godoxy/internal/utils v0.1.0 // indirect
|
||||
github.com/yusing/goutils v0.2.1 // indirect
|
||||
github.com/yusing/goutils v0.3.1 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 // indirect
|
||||
go.opentelemetry.io/otel v1.38.0 // indirect
|
||||
|
||||
@@ -1513,8 +1513,8 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yusing/goutils v0.2.1 h1:KjoCrNO0otthaPCZPfQY+5GKsqs5+J77CxP+TNHYa/Y=
|
||||
github.com/yusing/goutils v0.2.1/go.mod h1:v6RZsMRdzcts4udSg0vqUIFvaD0OaUMPTwYJZ4XnQYo=
|
||||
github.com/yusing/goutils v0.3.1 h1:xCPoZ/haI8ZJ0ZaPU4g6+okSPdBczs8o98tIZ/TbpsQ=
|
||||
github.com/yusing/goutils v0.3.1/go.mod h1:meg9GcAU8yvBY21JgYjPuLsXD1Q5VdVHE32A4tG5Y5g=
|
||||
github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
|
||||
github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA=
|
||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
|
||||
@@ -16,7 +16,7 @@ import (
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/yusing/godoxy/agent/pkg/agent"
|
||||
"github.com/yusing/godoxy/internal/common"
|
||||
"github.com/yusing/godoxy/internal/task"
|
||||
"github.com/yusing/goutils/task"
|
||||
)
|
||||
|
||||
// TODO: implement reconnect here.
|
||||
|
||||
@@ -14,10 +14,10 @@ import (
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/go-connections/nat"
|
||||
"github.com/yusing/godoxy/agent/pkg/agent"
|
||||
"github.com/yusing/godoxy/internal/gperr"
|
||||
"github.com/yusing/godoxy/internal/serialization"
|
||||
"github.com/yusing/godoxy/internal/types"
|
||||
"github.com/yusing/godoxy/internal/utils"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
)
|
||||
|
||||
var DummyContainer = new(types.Container)
|
||||
@@ -84,7 +84,7 @@ func FromDocker(c *container.Summary, dockerHost string) (res *types.Container)
|
||||
if res.PrivateHostname == "" && res.PublicHostname == "" && res.Running {
|
||||
addError(res, ErrNoNetwork)
|
||||
}
|
||||
return
|
||||
return res
|
||||
}
|
||||
|
||||
func IsBlacklisted(c *types.Container) bool {
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
. "github.com/yusing/godoxy/internal/utils/testing"
|
||||
expect "github.com/yusing/goutils/testing"
|
||||
)
|
||||
|
||||
func TestContainerExplicit(t *testing.T) {
|
||||
@@ -37,7 +37,7 @@ func TestContainerExplicit(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := FromDocker(&container.Summary{Names: []string{"test"}, State: "test", Labels: tt.labels}, "")
|
||||
ExpectEqual(t, c.IsExplicit, tt.isExplicit)
|
||||
expect.Equal(t, c.IsExplicit, tt.isExplicit)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -74,7 +74,7 @@ func TestContainerHostNetworkMode(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := FromDocker(tt.container, "")
|
||||
ExpectEqual(t, c.IsHostNetworkMode, tt.isHostNetworkMode)
|
||||
expect.Equal(t, c.IsHostNetworkMode, tt.isHostNetworkMode)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -109,9 +109,9 @@ func TestImageNameParsing(t *testing.T) {
|
||||
t.Run(tt.full, func(t *testing.T) {
|
||||
helper := containerHelper{&container.Summary{Image: tt.full}}
|
||||
im := helper.parseImage()
|
||||
ExpectEqual(t, im.Author, tt.author)
|
||||
ExpectEqual(t, im.Name, tt.image)
|
||||
ExpectEqual(t, im.Tag, tt.tag)
|
||||
expect.Equal(t, im.Author, tt.author)
|
||||
expect.Equal(t, im.Name, tt.image)
|
||||
expect.Equal(t, im.Tag, tt.tag)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,8 +5,8 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/goccy/go-yaml"
|
||||
"github.com/yusing/godoxy/internal/gperr"
|
||||
"github.com/yusing/godoxy/internal/types"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
strutils "github.com/yusing/goutils/strings"
|
||||
)
|
||||
|
||||
|
||||
@@ -11,8 +11,8 @@ import (
|
||||
"github.com/yusing/godoxy/internal/net/gphttp/middleware"
|
||||
"github.com/yusing/godoxy/internal/net/gphttp/middleware/errorpage"
|
||||
"github.com/yusing/godoxy/internal/route/routes"
|
||||
"github.com/yusing/godoxy/internal/task"
|
||||
"github.com/yusing/godoxy/internal/types"
|
||||
"github.com/yusing/goutils/task"
|
||||
)
|
||||
|
||||
type Entrypoint struct {
|
||||
@@ -56,15 +56,15 @@ func (ep *Entrypoint) SetMiddlewares(mws []map[string]any) error {
|
||||
func (ep *Entrypoint) SetAccessLogger(parent task.Parent, cfg *accesslog.RequestLoggerConfig) (err error) {
|
||||
if cfg == nil {
|
||||
ep.accessLogger = nil
|
||||
return
|
||||
return err
|
||||
}
|
||||
|
||||
ep.accessLogger, err = accesslog.NewAccessLogger(parent, cfg)
|
||||
if err != nil {
|
||||
return
|
||||
return err
|
||||
}
|
||||
log.Debug().Msg("entrypoint access logger created")
|
||||
return
|
||||
return err
|
||||
}
|
||||
|
||||
func (ep *Entrypoint) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
@@ -12,8 +12,8 @@ import (
|
||||
|
||||
"github.com/yusing/godoxy/internal/route"
|
||||
"github.com/yusing/godoxy/internal/route/routes"
|
||||
"github.com/yusing/godoxy/internal/task"
|
||||
"github.com/yusing/godoxy/internal/types"
|
||||
"github.com/yusing/goutils/task"
|
||||
)
|
||||
|
||||
type noopResponseWriter struct {
|
||||
@@ -24,10 +24,12 @@ type noopResponseWriter struct {
|
||||
func (w *noopResponseWriter) Header() http.Header {
|
||||
return http.Header{}
|
||||
}
|
||||
|
||||
func (w *noopResponseWriter) Write(b []byte) (int, error) {
|
||||
w.written = b
|
||||
return len(b), nil
|
||||
}
|
||||
|
||||
func (w *noopResponseWriter) WriteHeader(statusCode int) {
|
||||
w.statusCode = statusCode
|
||||
}
|
||||
@@ -45,7 +47,7 @@ func (t noopTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
|
||||
func BenchmarkEntrypointReal(b *testing.B) {
|
||||
var ep Entrypoint
|
||||
var req = http.Request{
|
||||
req := http.Request{
|
||||
Method: "GET",
|
||||
URL: &url.URL{Path: "/", RawPath: "/"},
|
||||
Host: "test.domain.tld",
|
||||
@@ -107,7 +109,7 @@ func BenchmarkEntrypointReal(b *testing.B) {
|
||||
|
||||
func BenchmarkEntrypoint(b *testing.B) {
|
||||
var ep Entrypoint
|
||||
var req = http.Request{
|
||||
req := http.Request{
|
||||
Method: "GET",
|
||||
URL: &url.URL{Path: "/", RawPath: "/"},
|
||||
Host: "test.domain.tld",
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"github.com/yusing/godoxy/internal/route"
|
||||
"github.com/yusing/godoxy/internal/route/routes"
|
||||
|
||||
expect "github.com/yusing/godoxy/internal/utils/testing"
|
||||
expect "github.com/yusing/goutils/testing"
|
||||
)
|
||||
|
||||
var ep = NewEntrypoint()
|
||||
|
||||
@@ -1,106 +0,0 @@
|
||||
# gperr
|
||||
|
||||
gperr is an error interface that supports nested structure and subject highlighting.
|
||||
|
||||
## Usage
|
||||
|
||||
### gperr.Error
|
||||
|
||||
The error interface.
|
||||
|
||||
### gperr.New
|
||||
|
||||
Like `errors.New`, but returns a `gperr.Error`.
|
||||
|
||||
### gperr.Wrap
|
||||
|
||||
Like `fmt.Errorf("%s: %w", message, err)`, but returns a `gperr.Error`.
|
||||
|
||||
### gperr.Error.Subject
|
||||
|
||||
Returns a new error with the subject prepended to the error message. The main subject is highlighted.
|
||||
|
||||
```go
|
||||
err := gperr.New("error message")
|
||||
err = err.Subject("bar")
|
||||
err = err.Subject("foo")
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
<code>foo > <span style="color: red;">bar</span>: error message</code>
|
||||
|
||||
### gperr.Error.Subjectf
|
||||
|
||||
Like `gperr.Error.Subject`, but formats the subject with `fmt.Sprintf`.
|
||||
|
||||
### gperr.PrependSubject
|
||||
|
||||
Prepends the subject to the error message like `gperr.Error.Subject`.
|
||||
|
||||
```go
|
||||
err := gperr.New("error message")
|
||||
err = gperr.PrependSubject(err, "foo")
|
||||
err = gperr.PrependSubject(err, "bar")
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
<code>bar > <span style="color: red;">foo</span>: error message</code>
|
||||
|
||||
### gperr.Error.With
|
||||
|
||||
Adds a new error to the error chain.
|
||||
|
||||
```go
|
||||
err := gperr.New("error message")
|
||||
err = err.With(gperr.New("inner error"))
|
||||
err = err.With(gperr.New("inner error 2").With(gperr.New("inner inner error")))
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```
|
||||
error message:
|
||||
• inner error
|
||||
• inner error 2
|
||||
• inner inner error
|
||||
```
|
||||
|
||||
### gperr.Error.Withf
|
||||
|
||||
Like `gperr.Error.With`, but formats the error with `fmt.Errorf`.
|
||||
|
||||
### gperr.Error.Is
|
||||
|
||||
Returns true if the error is equal to the given error.
|
||||
|
||||
### gperr.Builder
|
||||
|
||||
A builder for `gperr.Error`.
|
||||
|
||||
```go
|
||||
builder := gperr.NewBuilder("foo")
|
||||
builder.Add(gperr.New("error message"))
|
||||
builder.Addf("error message: %s", "foo")
|
||||
builder.AddRange(gperr.New("error message 1"), gperr.New("error message 2"))
|
||||
```
|
||||
|
||||
Output:
|
||||
|
||||
```
|
||||
foo:
|
||||
• error message
|
||||
• error message: foo
|
||||
• error message 1
|
||||
• error message 2
|
||||
```
|
||||
|
||||
### gperr.Builder.Build
|
||||
|
||||
Builds a `gperr.Error` from the builder.
|
||||
|
||||
## When to return gperr.Error
|
||||
|
||||
- When you want to return multiple errors
|
||||
- When the error has a subject
|
||||
@@ -1,75 +0,0 @@
|
||||
package gperr
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// baseError is an immutable wrapper around an error.
|
||||
//
|
||||
//nolint:recvcheck
|
||||
type baseError struct {
|
||||
Err error `json:"err"`
|
||||
}
|
||||
|
||||
func (err *baseError) Unwrap() error {
|
||||
return err.Err
|
||||
}
|
||||
|
||||
func (err *baseError) Is(other error) bool {
|
||||
if other, ok := other.(*baseError); ok {
|
||||
return errors.Is(err.Err, other.Err)
|
||||
}
|
||||
return errors.Is(err.Err, other)
|
||||
}
|
||||
|
||||
func (err baseError) Subject(subject string) Error {
|
||||
err.Err = PrependSubject(subject, err.Err)
|
||||
return &err
|
||||
}
|
||||
|
||||
func (err *baseError) Subjectf(format string, args ...any) Error {
|
||||
if len(args) > 0 {
|
||||
return err.Subject(fmt.Sprintf(format, args...))
|
||||
}
|
||||
return err.Subject(format)
|
||||
}
|
||||
|
||||
func (err *baseError) With(extra error) Error {
|
||||
if extra == nil {
|
||||
return err
|
||||
}
|
||||
return &nestedError{&baseError{err.Err}, []error{extra}}
|
||||
}
|
||||
|
||||
func (err baseError) Withf(format string, args ...any) Error {
|
||||
return &nestedError{&err, []error{fmt.Errorf(format, args...)}}
|
||||
}
|
||||
|
||||
func (err *baseError) Error() string {
|
||||
return err.Err.Error()
|
||||
}
|
||||
|
||||
// MarshalJSON implements the json.Marshaler interface.
|
||||
func (err *baseError) MarshalJSON() ([]byte, error) {
|
||||
//nolint:errorlint
|
||||
switch err := err.Err.(type) {
|
||||
case Error, *withSubject:
|
||||
return json.Marshal(err)
|
||||
case json.Marshaler:
|
||||
return err.MarshalJSON()
|
||||
case interface{ MarshalText() ([]byte, error) }:
|
||||
return err.MarshalText()
|
||||
default:
|
||||
return json.Marshal(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func (err *baseError) Plain() []byte {
|
||||
return Plain(err.Err)
|
||||
}
|
||||
|
||||
func (err *baseError) Markdown() []byte {
|
||||
return Markdown(err.Err)
|
||||
}
|
||||
@@ -1,161 +0,0 @@
|
||||
package gperr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type noLock struct{}
|
||||
|
||||
func (noLock) Lock() {}
|
||||
func (noLock) Unlock() {}
|
||||
func (noLock) RLock() {}
|
||||
func (noLock) RUnlock() {}
|
||||
|
||||
type rwLock interface {
|
||||
sync.Locker
|
||||
RLock()
|
||||
RUnlock()
|
||||
}
|
||||
|
||||
type Builder struct {
|
||||
about string
|
||||
errs []error
|
||||
rwLock
|
||||
}
|
||||
|
||||
// NewBuilder creates a new Builder.
|
||||
//
|
||||
// If about is not provided, the Builder will not have a subject
|
||||
// and will expand when adding to another builder.
|
||||
func NewBuilder(about ...string) *Builder {
|
||||
if len(about) == 0 {
|
||||
return &Builder{rwLock: noLock{}}
|
||||
}
|
||||
return &Builder{about: about[0], rwLock: noLock{}}
|
||||
}
|
||||
|
||||
func NewBuilderWithConcurrency(about ...string) *Builder {
|
||||
if len(about) == 0 {
|
||||
return &Builder{rwLock: new(sync.RWMutex)}
|
||||
}
|
||||
return &Builder{about: about[0], rwLock: new(sync.RWMutex)}
|
||||
}
|
||||
|
||||
func (b *Builder) EnableConcurrency() {
|
||||
b.rwLock = new(sync.RWMutex)
|
||||
}
|
||||
|
||||
func (b *Builder) About() string {
|
||||
return b.about
|
||||
}
|
||||
|
||||
func (b *Builder) HasError() bool {
|
||||
// no need to lock, when this is called, the Builder is not used anymore
|
||||
return len(b.errs) > 0
|
||||
}
|
||||
|
||||
func (b *Builder) Error() Error {
|
||||
if len(b.errs) == 0 {
|
||||
return nil
|
||||
}
|
||||
if len(b.errs) == 1 && b.about == "" {
|
||||
return wrap(b.errs[0])
|
||||
}
|
||||
return &nestedError{Err: New(b.about), Extras: b.errs}
|
||||
}
|
||||
|
||||
func (b *Builder) String() string {
|
||||
err := b.Error()
|
||||
if err == nil {
|
||||
return ""
|
||||
}
|
||||
return err.Error()
|
||||
}
|
||||
|
||||
// Add adds an error to the Builder.
|
||||
//
|
||||
// adding nil is no-op.
|
||||
func (b *Builder) Add(err error) {
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
|
||||
b.Lock()
|
||||
defer b.Unlock()
|
||||
|
||||
b.add(err)
|
||||
}
|
||||
|
||||
func (b *Builder) Adds(err string) {
|
||||
b.Lock()
|
||||
defer b.Unlock()
|
||||
b.errs = append(b.errs, newError(err))
|
||||
}
|
||||
|
||||
func (b *Builder) Addf(format string, args ...any) {
|
||||
if len(args) > 0 {
|
||||
b.Lock()
|
||||
defer b.Unlock()
|
||||
b.errs = append(b.errs, fmt.Errorf(format, args...))
|
||||
} else {
|
||||
b.Adds(format)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Builder) AddFrom(other *Builder, flatten bool) {
|
||||
if other == nil || !other.HasError() {
|
||||
return
|
||||
}
|
||||
|
||||
b.Lock()
|
||||
defer b.Unlock()
|
||||
if flatten {
|
||||
b.errs = append(b.errs, other.errs...)
|
||||
} else {
|
||||
b.errs = append(b.errs, other.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Builder) AddRange(errs ...error) {
|
||||
nonNilErrs := make([]error, 0, len(errs))
|
||||
for _, err := range errs {
|
||||
if err != nil {
|
||||
nonNilErrs = append(nonNilErrs, err)
|
||||
}
|
||||
}
|
||||
|
||||
b.Lock()
|
||||
defer b.Unlock()
|
||||
|
||||
for _, err := range nonNilErrs {
|
||||
b.add(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Builder) ForEach(fn func(error)) {
|
||||
b.RLock()
|
||||
errs := b.errs
|
||||
b.RUnlock()
|
||||
|
||||
for _, err := range errs {
|
||||
fn(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Builder) add(err error) {
|
||||
switch err := err.(type) { //nolint:errorlint
|
||||
case *baseError:
|
||||
b.errs = append(b.errs, err.Err)
|
||||
case *nestedError:
|
||||
if err.Err == nil {
|
||||
b.errs = append(b.errs, err.Extras...)
|
||||
} else {
|
||||
b.errs = append(b.errs, err)
|
||||
}
|
||||
case *MultilineError:
|
||||
b.add(&err.nestedError)
|
||||
default:
|
||||
b.errs = append(b.errs, err)
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
package gperr_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
. "github.com/yusing/godoxy/internal/gperr"
|
||||
. "github.com/yusing/godoxy/internal/utils/testing"
|
||||
)
|
||||
|
||||
func TestBuilderEmpty(t *testing.T) {
|
||||
eb := NewBuilder("foo")
|
||||
ExpectTrue(t, errors.Is(eb.Error(), nil))
|
||||
ExpectFalse(t, eb.HasError())
|
||||
}
|
||||
|
||||
func TestBuilderAddNil(t *testing.T) {
|
||||
eb := NewBuilder("foo")
|
||||
var err Error
|
||||
for range 3 {
|
||||
eb.Add(nil)
|
||||
}
|
||||
for range 3 {
|
||||
eb.Add(err)
|
||||
}
|
||||
eb.AddRange(nil, nil, err)
|
||||
ExpectFalse(t, eb.HasError())
|
||||
ExpectTrue(t, eb.Error() == nil)
|
||||
}
|
||||
|
||||
func TestBuilderIs(t *testing.T) {
|
||||
eb := NewBuilder("foo")
|
||||
eb.Add(context.Canceled)
|
||||
eb.Add(io.ErrShortBuffer)
|
||||
ExpectTrue(t, eb.HasError())
|
||||
ExpectError(t, io.ErrShortBuffer, eb.Error())
|
||||
ExpectError(t, context.Canceled, eb.Error())
|
||||
}
|
||||
|
||||
func TestBuilderNested(t *testing.T) {
|
||||
eb := NewBuilder("action failed")
|
||||
eb.Add(New("Action 1").Withf("Inner: 1").Withf("Inner: 2"))
|
||||
eb.Add(New("Action 2").Withf("Inner: 3"))
|
||||
|
||||
got := eb.String()
|
||||
expected := `action failed
|
||||
• Action 1
|
||||
• Inner: 1
|
||||
• Inner: 2
|
||||
• Action 2
|
||||
• Inner: 3
|
||||
`
|
||||
ExpectEqual(t, got, expected)
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
package gperr
|
||||
|
||||
type Error interface {
|
||||
error
|
||||
|
||||
// Is is a wrapper for errors.Is when there is no sub-error.
|
||||
//
|
||||
// When there are sub-errors, they will also be checked.
|
||||
Is(other error) bool
|
||||
// With appends a sub-error to the error.
|
||||
With(extra error) Error
|
||||
// Withf is a wrapper for With(fmt.Errorf(format, args...)).
|
||||
Withf(format string, args ...any) Error
|
||||
// Subject prepends the given subject with a colon and space to the error message.
|
||||
//
|
||||
// If there is already a subject in the error message, the subject will be
|
||||
// prepended to the existing subject with " > ".
|
||||
//
|
||||
// Subject empty string is ignored.
|
||||
Subject(subject string) Error
|
||||
// Subjectf is a wrapper for Subject(fmt.Sprintf(format, args...)).
|
||||
Subjectf(format string, args ...any) Error
|
||||
PlainError
|
||||
MarkdownError
|
||||
}
|
||||
|
||||
type PlainError interface {
|
||||
Plain() []byte
|
||||
}
|
||||
|
||||
type MarkdownError interface {
|
||||
Markdown() []byte
|
||||
}
|
||||
|
||||
// this makes JSON marshaling work,
|
||||
// as the builtin one doesn't.
|
||||
//
|
||||
//nolint:errname
|
||||
type errStr string
|
||||
|
||||
func (err errStr) Error() string {
|
||||
return string(err)
|
||||
}
|
||||
@@ -1,159 +0,0 @@
|
||||
package gperr
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
expect "github.com/yusing/godoxy/internal/utils/testing"
|
||||
"github.com/yusing/goutils/strings/ansi"
|
||||
)
|
||||
|
||||
func TestBaseString(t *testing.T) {
|
||||
expect.Equal(t, New("error").Error(), "error")
|
||||
}
|
||||
|
||||
func TestBaseWithSubject(t *testing.T) {
|
||||
err := New("error")
|
||||
withSubject := err.Subject("foo")
|
||||
withSubjectf := err.Subjectf("%s %s", "foo", "bar")
|
||||
|
||||
expect.ErrorIs(t, err, withSubject)
|
||||
expect.Equal(t, ansi.StripANSI(withSubject.Error()), "foo: error")
|
||||
expect.True(t, withSubject.Is(err))
|
||||
|
||||
expect.ErrorIs(t, err, withSubjectf)
|
||||
expect.Equal(t, ansi.StripANSI(withSubjectf.Error()), "foo bar: error")
|
||||
expect.True(t, withSubjectf.Is(err))
|
||||
}
|
||||
|
||||
func TestBaseWithExtra(t *testing.T) {
|
||||
err := New("error")
|
||||
extra := New("bar").Subject("baz")
|
||||
withExtra := err.With(extra)
|
||||
|
||||
expect.True(t, withExtra.Is(extra))
|
||||
expect.True(t, withExtra.Is(err))
|
||||
|
||||
expect.True(t, errors.Is(withExtra, extra))
|
||||
expect.True(t, errors.Is(withExtra, err))
|
||||
|
||||
expect.True(t, strings.Contains(withExtra.Error(), err.Error()))
|
||||
expect.True(t, strings.Contains(withExtra.Error(), extra.Error()))
|
||||
expect.True(t, strings.Contains(withExtra.Error(), "baz"))
|
||||
}
|
||||
|
||||
func TestBaseUnwrap(t *testing.T) {
|
||||
err := errors.New("err")
|
||||
wrapped := Wrap(err)
|
||||
|
||||
expect.ErrorIs(t, err, errors.Unwrap(wrapped))
|
||||
}
|
||||
|
||||
func TestNestedUnwrap(t *testing.T) {
|
||||
err := errors.New("err")
|
||||
err2 := New("err2")
|
||||
wrapped := Wrap(err).Subject("foo").With(err2.Subject("bar"))
|
||||
|
||||
unwrapper, ok := wrapped.(interface{ Unwrap() []error })
|
||||
expect.True(t, ok)
|
||||
|
||||
expect.ErrorIs(t, err, wrapped)
|
||||
expect.ErrorIs(t, err2, wrapped)
|
||||
expect.Equal(t, len(unwrapper.Unwrap()), 2)
|
||||
}
|
||||
|
||||
func TestErrorIs(t *testing.T) {
|
||||
from := errors.New("error")
|
||||
err := Wrap(from)
|
||||
expect.ErrorIs(t, from, err)
|
||||
|
||||
expect.True(t, err.Is(from))
|
||||
expect.False(t, err.Is(New("error")))
|
||||
|
||||
expect.True(t, errors.Is(err.Subject("foo"), from))
|
||||
expect.True(t, errors.Is(err.Withf("foo"), from))
|
||||
expect.True(t, errors.Is(err.Subject("foo").Withf("bar"), from))
|
||||
}
|
||||
|
||||
func TestErrorImmutability(t *testing.T) {
|
||||
err := New("err")
|
||||
err2 := New("err2")
|
||||
|
||||
for range 3 {
|
||||
// t.Logf("%d: %v %T %s", i, errors.Unwrap(err), err, err)
|
||||
_ = err.Subject("foo")
|
||||
expect.False(t, strings.Contains(err.Error(), "foo"))
|
||||
|
||||
_ = err.With(err2)
|
||||
expect.False(t, strings.Contains(err.Error(), "extra"))
|
||||
expect.False(t, err.Is(err2))
|
||||
|
||||
err = err.Subject("bar").Withf("baz")
|
||||
expect.True(t, err != nil)
|
||||
}
|
||||
}
|
||||
|
||||
func TestErrorWith(t *testing.T) {
|
||||
err1 := New("err1")
|
||||
err2 := New("err2")
|
||||
|
||||
err3 := err1.With(err2)
|
||||
|
||||
expect.True(t, err3.Is(err1))
|
||||
expect.True(t, err3.Is(err2))
|
||||
|
||||
_ = err2.Subject("foo")
|
||||
|
||||
expect.True(t, err3.Is(err1))
|
||||
expect.True(t, err3.Is(err2))
|
||||
|
||||
// check if err3 is affected by err2.Subject
|
||||
expect.False(t, strings.Contains(err3.Error(), "foo"))
|
||||
}
|
||||
|
||||
func TestErrorStringSimple(t *testing.T) {
|
||||
errFailure := New("generic failure")
|
||||
ne := errFailure.Subject("foo bar")
|
||||
expect.Equal(t, ansi.StripANSI(ne.Error()), "foo bar: generic failure")
|
||||
ne = ne.Subject("baz")
|
||||
expect.Equal(t, ansi.StripANSI(ne.Error()), "baz > foo bar: generic failure")
|
||||
}
|
||||
|
||||
func TestErrorStringNested(t *testing.T) {
|
||||
errFailure := New("generic failure")
|
||||
inner := errFailure.Subject("inner").
|
||||
Withf("1").
|
||||
Withf("1")
|
||||
inner2 := errFailure.Subject("inner2").
|
||||
Subject("action 2").
|
||||
Withf("2").
|
||||
Withf("2")
|
||||
inner3 := errFailure.Subject("inner3").
|
||||
Subject("action 3").
|
||||
Withf("3").
|
||||
Withf("3")
|
||||
ne := errFailure.
|
||||
Subject("foo").
|
||||
Withf("bar").
|
||||
Withf("baz").
|
||||
With(inner).
|
||||
With(inner.With(inner2.With(inner3)))
|
||||
want := `foo: generic failure
|
||||
• bar
|
||||
• baz
|
||||
• inner: generic failure
|
||||
• 1
|
||||
• 1
|
||||
• inner: generic failure
|
||||
• 1
|
||||
• 1
|
||||
• action 2 > inner2: generic failure
|
||||
• 2
|
||||
• 2
|
||||
• action 3 > inner3: generic failure
|
||||
• 3
|
||||
• 3
|
||||
`
|
||||
expect.Equal(t, ansi.StripANSI(ne.Error()), want)
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
package gperr
|
||||
|
||||
import "github.com/yusing/goutils/strings/ansi"
|
||||
|
||||
type Hint struct {
|
||||
Prefix string
|
||||
Message string
|
||||
Suffix string
|
||||
}
|
||||
|
||||
var _ PlainError = (*Hint)(nil)
|
||||
var _ MarkdownError = (*Hint)(nil)
|
||||
|
||||
func (h *Hint) Error() string {
|
||||
return h.Prefix + ansi.Info(h.Message) + h.Suffix
|
||||
}
|
||||
|
||||
func (h *Hint) Plain() []byte {
|
||||
return []byte(h.Prefix + h.Message + h.Suffix)
|
||||
}
|
||||
|
||||
func (h *Hint) Markdown() []byte {
|
||||
return []byte(h.Prefix + "**" + h.Message + "**" + h.Suffix)
|
||||
}
|
||||
|
||||
func (h *Hint) MarshalText() ([]byte, error) {
|
||||
return h.Plain(), nil
|
||||
}
|
||||
|
||||
func (h *Hint) String() string {
|
||||
return h.Error()
|
||||
}
|
||||
|
||||
func DoYouMean(s string) error {
|
||||
if s == "" {
|
||||
return nil
|
||||
}
|
||||
return &Hint{
|
||||
Prefix: "Do you mean ",
|
||||
Message: s,
|
||||
Suffix: "?",
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
package gperr
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
zerologlog "github.com/rs/zerolog/log"
|
||||
"github.com/yusing/godoxy/internal/common"
|
||||
)
|
||||
|
||||
func log(msg string, err error, level zerolog.Level, logger ...*zerolog.Logger) {
|
||||
var l *zerolog.Logger
|
||||
if len(logger) > 0 {
|
||||
l = logger[0]
|
||||
} else {
|
||||
l = &zerologlog.Logger
|
||||
}
|
||||
l.WithLevel(level).Msg(New(highlightANSI(msg)).With(err).Error())
|
||||
switch level {
|
||||
case zerolog.FatalLevel:
|
||||
os.Exit(1)
|
||||
case zerolog.PanicLevel:
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func LogFatal(msg string, err error, logger ...*zerolog.Logger) {
|
||||
if common.IsDebug {
|
||||
LogPanic(msg, err, logger...)
|
||||
}
|
||||
log(msg, err, zerolog.FatalLevel, logger...)
|
||||
}
|
||||
|
||||
func LogError(msg string, err error, logger ...*zerolog.Logger) {
|
||||
log(msg, err, zerolog.ErrorLevel, logger...)
|
||||
}
|
||||
|
||||
func LogWarn(msg string, err error, logger ...*zerolog.Logger) {
|
||||
log(msg, err, zerolog.WarnLevel, logger...)
|
||||
}
|
||||
|
||||
func LogPanic(msg string, err error, logger ...*zerolog.Logger) {
|
||||
log(msg, err, zerolog.PanicLevel, logger...)
|
||||
}
|
||||
|
||||
func LogDebug(msg string, err error, logger ...*zerolog.Logger) {
|
||||
log(msg, err, zerolog.DebugLevel, logger...)
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
package gperr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type MultilineError struct {
|
||||
nestedError
|
||||
}
|
||||
|
||||
func Multiline() *MultilineError {
|
||||
return &MultilineError{}
|
||||
}
|
||||
|
||||
func (m *MultilineError) add(err error) {
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
m.Extras = append(m.Extras, err)
|
||||
}
|
||||
|
||||
func (m *MultilineError) Addf(format string, args ...any) *MultilineError {
|
||||
m.add(fmt.Errorf(format, args...))
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *MultilineError) Adds(s string) *MultilineError {
|
||||
m.add(newError(s))
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *MultilineError) AddLines(lines ...any) *MultilineError {
|
||||
v := reflect.ValueOf(lines)
|
||||
if v.Kind() == reflect.Slice {
|
||||
for i := range v.Len() {
|
||||
switch v := v.Index(i).Interface().(type) {
|
||||
case string:
|
||||
m.add(newError(v))
|
||||
case error:
|
||||
m.add(v)
|
||||
default:
|
||||
m.add(fmt.Errorf("%v", v))
|
||||
}
|
||||
}
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *MultilineError) AddLinesString(lines ...string) *MultilineError {
|
||||
for _, line := range lines {
|
||||
m.add(newError(line))
|
||||
}
|
||||
return m
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
package gperr
|
||||
|
||||
import (
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
expect "github.com/yusing/godoxy/internal/utils/testing"
|
||||
)
|
||||
|
||||
func TestWrapMultiline(t *testing.T) {
|
||||
multiline := Multiline()
|
||||
var wrapper error = wrap(multiline)
|
||||
_, ok := wrapper.(*MultilineError)
|
||||
if !ok {
|
||||
t.Errorf("wrapper is not a MultilineError")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrependSubjectMultiline(t *testing.T) {
|
||||
multiline := Multiline()
|
||||
multiline.Addf("line 1 %s", "test")
|
||||
multiline.Adds("line 2")
|
||||
multiline.AddLines([]any{1, "2", 3.0, net.IPv4(127, 0, 0, 1)})
|
||||
multiline.Subject("subject")
|
||||
|
||||
builder := NewBuilder()
|
||||
builder.Add(multiline)
|
||||
expect.Equal(t, len(multiline.Extras), len(builder.errs))
|
||||
}
|
||||
@@ -1,152 +0,0 @@
|
||||
package gperr
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
//nolint:recvcheck
|
||||
type nestedError struct {
|
||||
Err error `json:"err"`
|
||||
Extras []error `json:"extras"`
|
||||
}
|
||||
|
||||
var emptyError = errStr("")
|
||||
|
||||
func (err nestedError) Subject(subject string) Error {
|
||||
if err.Err == nil {
|
||||
err.Err = PrependSubject(subject, emptyError)
|
||||
} else {
|
||||
err.Err = PrependSubject(subject, err.Err)
|
||||
}
|
||||
return &err
|
||||
}
|
||||
|
||||
func (err *nestedError) Subjectf(format string, args ...any) Error {
|
||||
if len(args) > 0 {
|
||||
return err.Subject(fmt.Sprintf(format, args...))
|
||||
}
|
||||
return err.Subject(format)
|
||||
}
|
||||
|
||||
func (err nestedError) With(extra error) Error {
|
||||
if extra != nil {
|
||||
err.Extras = append(err.Extras, extra)
|
||||
}
|
||||
return &err
|
||||
}
|
||||
|
||||
func (err nestedError) Withf(format string, args ...any) Error {
|
||||
if len(args) > 0 {
|
||||
err.Extras = append(err.Extras, fmt.Errorf(format, args...))
|
||||
} else {
|
||||
err.Extras = append(err.Extras, newError(format))
|
||||
}
|
||||
return &err
|
||||
}
|
||||
|
||||
func (err *nestedError) Unwrap() []error {
|
||||
if err.Err == nil {
|
||||
if len(err.Extras) == 0 {
|
||||
return nil
|
||||
}
|
||||
return err.Extras
|
||||
}
|
||||
return append([]error{err.Err}, err.Extras...)
|
||||
}
|
||||
|
||||
func (err *nestedError) Is(other error) bool {
|
||||
if errors.Is(err.Err, other) {
|
||||
return true
|
||||
}
|
||||
for _, e := range err.Extras {
|
||||
if errors.Is(e, other) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
var nilError = newError("<nil>")
|
||||
var bulletPrefix = []byte("• ")
|
||||
var markdownBulletPrefix = []byte("- ")
|
||||
var spaces = []byte(" ")
|
||||
|
||||
type appendLineFunc func(buf []byte, err error, level int) []byte
|
||||
|
||||
func (err *nestedError) fmtError(appendLine appendLineFunc) []byte {
|
||||
if err == nil {
|
||||
return appendLine(nil, nilError, 0)
|
||||
}
|
||||
if err.Err != nil {
|
||||
buf := appendLine(nil, err.Err, 0)
|
||||
if len(err.Extras) > 0 {
|
||||
buf = append(buf, '\n')
|
||||
buf = appendLines(buf, err.Extras, 1, appendLine)
|
||||
}
|
||||
return buf
|
||||
}
|
||||
return appendLines(nil, err.Extras, 0, appendLine)
|
||||
}
|
||||
|
||||
func (err *nestedError) Error() string {
|
||||
return string(err.fmtError(appendLineNormal))
|
||||
}
|
||||
|
||||
func (err *nestedError) Plain() []byte {
|
||||
return err.fmtError(appendLinePlain)
|
||||
}
|
||||
|
||||
func (err *nestedError) Markdown() []byte {
|
||||
return err.fmtError(appendLineMd)
|
||||
}
|
||||
|
||||
func appendLine(buf []byte, err error, level int, prefix []byte, format func(err error) []byte) []byte {
|
||||
if err == nil {
|
||||
return appendLine(buf, nilError, level, prefix, format)
|
||||
}
|
||||
if level == 0 {
|
||||
return append(buf, format(err)...)
|
||||
}
|
||||
buf = append(buf, spaces[:2*level]...)
|
||||
buf = append(buf, prefix...)
|
||||
buf = append(buf, format(err)...)
|
||||
return buf
|
||||
}
|
||||
|
||||
func appendLineNormal(buf []byte, err error, level int) []byte {
|
||||
return appendLine(buf, err, level, bulletPrefix, Normal)
|
||||
}
|
||||
|
||||
func appendLinePlain(buf []byte, err error, level int) []byte {
|
||||
return appendLine(buf, err, level, bulletPrefix, Plain)
|
||||
}
|
||||
|
||||
func appendLineMd(buf []byte, err error, level int) []byte {
|
||||
return appendLine(buf, err, level, markdownBulletPrefix, Markdown)
|
||||
}
|
||||
|
||||
func appendLines(buf []byte, errs []error, level int, appendLine appendLineFunc) []byte {
|
||||
if len(errs) == 0 {
|
||||
return buf
|
||||
}
|
||||
for _, err := range errs {
|
||||
switch err := wrap(err).(type) {
|
||||
case *nestedError:
|
||||
if err.Err != nil {
|
||||
buf = appendLine(buf, err.Err, level)
|
||||
buf = append(buf, '\n')
|
||||
buf = appendLines(buf, err.Extras, level+1, appendLine)
|
||||
} else {
|
||||
buf = appendLines(buf, err.Extras, level, appendLine)
|
||||
}
|
||||
default:
|
||||
if err == nil {
|
||||
continue
|
||||
}
|
||||
buf = appendLine(buf, err, level)
|
||||
buf = append(buf, '\n')
|
||||
}
|
||||
}
|
||||
return buf
|
||||
}
|
||||
@@ -1,144 +0,0 @@
|
||||
package gperr
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"slices"
|
||||
|
||||
"github.com/yusing/goutils/strings/ansi"
|
||||
)
|
||||
|
||||
//nolint:errname
|
||||
type withSubject struct {
|
||||
Subjects []string
|
||||
Err error
|
||||
|
||||
pendingSubject string
|
||||
}
|
||||
|
||||
const subjectSep = " > "
|
||||
|
||||
type highlightFunc func(subject string) string
|
||||
|
||||
var _ PlainError = (*withSubject)(nil)
|
||||
var _ MarkdownError = (*withSubject)(nil)
|
||||
|
||||
func highlightANSI(subject string) string {
|
||||
return ansi.HighlightRed + subject + ansi.Reset
|
||||
}
|
||||
|
||||
func highlightMarkdown(subject string) string {
|
||||
return "**" + subject + "**"
|
||||
}
|
||||
|
||||
func noHighlight(subject string) string {
|
||||
return subject
|
||||
}
|
||||
|
||||
func PrependSubject(subject string, err error) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
//nolint:errorlint
|
||||
switch err := err.(type) {
|
||||
case *withSubject:
|
||||
return err.Prepend(subject)
|
||||
case *wrappedError:
|
||||
return &wrappedError{
|
||||
Err: PrependSubject(subject, err.Err),
|
||||
Message: err.Message,
|
||||
}
|
||||
case Error:
|
||||
return err.Subject(subject)
|
||||
}
|
||||
return &withSubject{[]string{subject}, err, ""}
|
||||
}
|
||||
|
||||
func (err *withSubject) Prepend(subject string) *withSubject {
|
||||
if subject == "" {
|
||||
return err
|
||||
}
|
||||
|
||||
clone := *err
|
||||
switch subject[0] {
|
||||
case '[', '(', '{':
|
||||
// since prepend is called in depth-first order,
|
||||
// the subject of the index is not yet seen
|
||||
// add it when the next subject is seen
|
||||
clone.pendingSubject += subject
|
||||
default:
|
||||
clone.Subjects = append(clone.Subjects, subject)
|
||||
if clone.pendingSubject != "" {
|
||||
clone.Subjects[len(clone.Subjects)-1] = subject + clone.pendingSubject
|
||||
clone.pendingSubject = ""
|
||||
}
|
||||
}
|
||||
return &clone
|
||||
}
|
||||
|
||||
func (err *withSubject) Is(other error) bool {
|
||||
return errors.Is(other, err.Err)
|
||||
}
|
||||
|
||||
func (err *withSubject) Unwrap() error {
|
||||
return err.Err
|
||||
}
|
||||
|
||||
func (err *withSubject) Error() string {
|
||||
return string(err.fmtError(highlightANSI))
|
||||
}
|
||||
|
||||
func (err *withSubject) Plain() []byte {
|
||||
return err.fmtError(noHighlight)
|
||||
}
|
||||
|
||||
func (err *withSubject) Markdown() []byte {
|
||||
return err.fmtError(highlightMarkdown)
|
||||
}
|
||||
|
||||
func (err *withSubject) fmtError(highlight highlightFunc) []byte {
|
||||
// subject is in reversed order
|
||||
size := 0
|
||||
errStr := err.Err.Error()
|
||||
subjects := err.Subjects
|
||||
if err.pendingSubject != "" {
|
||||
subjects = append(subjects, err.pendingSubject)
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
for _, s := range subjects {
|
||||
size += len(s)
|
||||
}
|
||||
n := len(subjects)
|
||||
buf.Grow(size + 2 + n*len(subjectSep) + len(errStr) + len(highlight("")))
|
||||
|
||||
for i := n - 1; i > 0; i-- {
|
||||
buf.WriteString(subjects[i])
|
||||
buf.WriteString(subjectSep)
|
||||
}
|
||||
buf.WriteString(highlight(subjects[0]))
|
||||
if errStr != "" {
|
||||
buf.WriteString(": ")
|
||||
buf.WriteString(errStr)
|
||||
}
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
// MarshalJSON implements the json.Marshaler interface.
|
||||
func (err *withSubject) MarshalJSON() ([]byte, error) {
|
||||
subjects := slices.Clone(err.Subjects)
|
||||
slices.Reverse(subjects)
|
||||
reversed := struct {
|
||||
Subjects []string `json:"subjects"`
|
||||
Err error `json:"err"`
|
||||
}{
|
||||
Subjects: subjects,
|
||||
Err: err.Err,
|
||||
}
|
||||
if err.pendingSubject != "" {
|
||||
reversed.Subjects = append(reversed.Subjects, err.pendingSubject)
|
||||
}
|
||||
|
||||
return json.Marshal(reversed)
|
||||
}
|
||||
@@ -1,133 +0,0 @@
|
||||
package gperr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func newError(message string) error {
|
||||
return errStr(message)
|
||||
}
|
||||
|
||||
func New(message string) Error {
|
||||
if message == "" {
|
||||
return nil
|
||||
}
|
||||
return &baseError{newError(message)}
|
||||
}
|
||||
|
||||
func Errorf(format string, args ...any) Error {
|
||||
return &baseError{fmt.Errorf(format, args...)}
|
||||
}
|
||||
|
||||
// Wrap wraps message in front of the error message.
|
||||
func Wrap(err error, message ...string) Error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
if len(message) == 0 || message[0] == "" {
|
||||
return wrap(err)
|
||||
}
|
||||
//nolint:errorlint
|
||||
switch err := err.(type) {
|
||||
case *baseError:
|
||||
err.Err = &wrappedError{err.Err, message[0]}
|
||||
return err
|
||||
case *nestedError:
|
||||
err.Err = &wrappedError{err.Err, message[0]}
|
||||
return err
|
||||
}
|
||||
return &baseError{&wrappedError{err, message[0]}}
|
||||
}
|
||||
|
||||
func Unwrap(err error) Error {
|
||||
//nolint:errorlint
|
||||
switch err := err.(type) {
|
||||
case interface{ Unwrap() []error }:
|
||||
return &nestedError{Extras: err.Unwrap()}
|
||||
case interface{ Unwrap() error }:
|
||||
return &baseError{err.Unwrap()}
|
||||
default:
|
||||
return &baseError{err}
|
||||
}
|
||||
}
|
||||
|
||||
func wrap(err error) Error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
//nolint:errorlint
|
||||
switch err := err.(type) {
|
||||
case Error:
|
||||
return err
|
||||
}
|
||||
return &baseError{err}
|
||||
}
|
||||
|
||||
func Join(errors ...error) Error {
|
||||
n := 0
|
||||
for _, err := range errors {
|
||||
if err != nil {
|
||||
n++
|
||||
}
|
||||
}
|
||||
if n == 0 {
|
||||
return nil
|
||||
}
|
||||
errs := make([]error, n)
|
||||
i := 0
|
||||
for _, err := range errors {
|
||||
if err != nil {
|
||||
errs[i] = err
|
||||
i++
|
||||
}
|
||||
}
|
||||
return &nestedError{Extras: errs}
|
||||
}
|
||||
|
||||
func JoinLines(main error, errors ...string) Error {
|
||||
errs := make([]error, len(errors))
|
||||
for i, err := range errors {
|
||||
if err == "" {
|
||||
continue
|
||||
}
|
||||
errs[i] = newError(err)
|
||||
}
|
||||
return &nestedError{Err: main, Extras: errs}
|
||||
}
|
||||
|
||||
func Collect[T any, Err error, Arg any, Func func(Arg) (T, Err)](eb *Builder, fn Func, arg Arg) T {
|
||||
result, err := fn(arg)
|
||||
eb.Add(err)
|
||||
return result
|
||||
}
|
||||
|
||||
func Normal(err error) []byte {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return []byte(err.Error())
|
||||
}
|
||||
|
||||
func Plain(err error) []byte {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
if p, ok := err.(PlainError); ok {
|
||||
return p.Plain()
|
||||
}
|
||||
return []byte(err.Error())
|
||||
}
|
||||
|
||||
func Markdown(err error) []byte {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
switch err := err.(type) {
|
||||
case MarkdownError:
|
||||
return err.Markdown()
|
||||
case interface{ Unwrap() []error }:
|
||||
return appendLines(nil, err.Unwrap(), 0, appendLineMd)
|
||||
default:
|
||||
return []byte(err.Error())
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
package gperr
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
type testErr struct{}
|
||||
|
||||
func (e testErr) Error() string {
|
||||
return "test error"
|
||||
}
|
||||
|
||||
func (e testErr) Plain() []byte {
|
||||
return []byte("test error")
|
||||
}
|
||||
|
||||
func (e testErr) Markdown() []byte {
|
||||
return []byte("**test error**")
|
||||
}
|
||||
|
||||
type testMultiErr struct {
|
||||
errors []error
|
||||
}
|
||||
|
||||
func (e testMultiErr) Error() string {
|
||||
return Join(e.errors...).Error()
|
||||
}
|
||||
|
||||
func (e testMultiErr) Unwrap() []error {
|
||||
return e.errors
|
||||
}
|
||||
|
||||
func TestFormatting(t *testing.T) {
|
||||
err := testErr{}
|
||||
plain := Plain(err)
|
||||
if string(plain) != "test error" {
|
||||
t.Errorf("expected test error, got %s", string(plain))
|
||||
}
|
||||
md := Markdown(err)
|
||||
if string(md) != "**test error**" {
|
||||
t.Errorf("expected test error, got %s", string(md))
|
||||
}
|
||||
}
|
||||
|
||||
func TestMultiError(t *testing.T) {
|
||||
err := testMultiErr{[]error{testErr{}, testErr{}}}
|
||||
plain := Plain(err)
|
||||
if string(plain) != "test error\ntest error\n" {
|
||||
t.Errorf("expected test error, got %s", string(plain))
|
||||
}
|
||||
md := Markdown(err)
|
||||
if string(md) != "**test error**\n**test error**\n" {
|
||||
t.Errorf("expected test error, got %s", string(md))
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
package gperr
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type wrappedError struct {
|
||||
Err error
|
||||
Message string
|
||||
}
|
||||
|
||||
var _ PlainError = (*wrappedError)(nil)
|
||||
var _ MarkdownError = (*wrappedError)(nil)
|
||||
|
||||
func (e *wrappedError) Error() string {
|
||||
return fmt.Sprintf("%s: %s", e.Message, e.Err.Error())
|
||||
}
|
||||
|
||||
func (e *wrappedError) Plain() []byte {
|
||||
return fmt.Appendf(nil, "%s: %s", e.Message, e.Err.Error())
|
||||
}
|
||||
|
||||
func (e *wrappedError) Markdown() []byte {
|
||||
return fmt.Appendf(nil, "**%s**: %s", e.Message, e.Err.Error())
|
||||
}
|
||||
|
||||
func (e *wrappedError) Unwrap() error {
|
||||
return e.Err
|
||||
}
|
||||
|
||||
func (e *wrappedError) Is(target error) bool {
|
||||
return errors.Is(e.Err, target)
|
||||
}
|
||||
@@ -4,7 +4,8 @@ import (
|
||||
"testing"
|
||||
|
||||
. "github.com/yusing/godoxy/internal/homepage"
|
||||
. "github.com/yusing/godoxy/internal/utils/testing"
|
||||
|
||||
expect "github.com/yusing/goutils/testing"
|
||||
)
|
||||
|
||||
func TestOverrideItem(t *testing.T) {
|
||||
@@ -33,7 +34,7 @@ func TestOverrideItem(t *testing.T) {
|
||||
overrides.Initialize()
|
||||
overrides.OverrideItem(a.Alias, want)
|
||||
got := a.GetOverride()
|
||||
ExpectEqual(t, got, Item{
|
||||
expect.Equal(t, got, Item{
|
||||
ItemConfig: want,
|
||||
Alias: a.Alias,
|
||||
})
|
||||
@@ -58,8 +59,8 @@ func TestOverrideItem_PreservesURL(t *testing.T) {
|
||||
overrides.OverrideItem(a.Alias, wantCfg)
|
||||
|
||||
got := a.GetOverride()
|
||||
ExpectEqual(t, got.URL, "http://origin.local")
|
||||
ExpectEqual(t, got.Name, "Overridden")
|
||||
expect.Equal(t, got.URL, "http://origin.local")
|
||||
expect.Equal(t, got.Name, "Overridden")
|
||||
}
|
||||
|
||||
func TestVisibilityFavoriteAndSortOrders(t *testing.T) {
|
||||
@@ -81,11 +82,11 @@ func TestVisibilityFavoriteAndSortOrders(t *testing.T) {
|
||||
overrides.SetFavSortOrder(a.Alias, 2)
|
||||
|
||||
got := a.GetOverride()
|
||||
ExpectEqual(t, got.Show, false)
|
||||
ExpectEqual(t, got.Favorite, true)
|
||||
ExpectEqual(t, got.SortOrder, 5)
|
||||
ExpectEqual(t, got.AllSortOrder, 9)
|
||||
ExpectEqual(t, got.FavSortOrder, 2)
|
||||
expect.Equal(t, got.Show, false)
|
||||
expect.Equal(t, got.Favorite, true)
|
||||
expect.Equal(t, got.SortOrder, 5)
|
||||
expect.Equal(t, got.AllSortOrder, 9)
|
||||
expect.Equal(t, got.FavSortOrder, 2)
|
||||
}
|
||||
|
||||
func TestCategoryDefaultedWhenEmpty(t *testing.T) {
|
||||
@@ -97,7 +98,7 @@ func TestCategoryDefaultedWhenEmpty(t *testing.T) {
|
||||
},
|
||||
}
|
||||
got := a.GetOverride()
|
||||
ExpectEqual(t, got.Category, CategoryOthers)
|
||||
expect.Equal(t, got.Category, CategoryOthers)
|
||||
}
|
||||
|
||||
func TestOverrideItems_Bulk(t *testing.T) {
|
||||
@@ -128,9 +129,9 @@ func TestOverrideItems_Bulk(t *testing.T) {
|
||||
ga := a.GetOverride()
|
||||
gb := b.GetOverride()
|
||||
|
||||
ExpectEqual(t, ga.Name, "A*")
|
||||
ExpectEqual(t, ga.Category, "AX")
|
||||
ExpectEqual(t, gb.Name, "B*")
|
||||
ExpectEqual(t, gb.Category, "BY")
|
||||
ExpectEqual(t, gb.Show, false)
|
||||
expect.Equal(t, ga.Name, "A*")
|
||||
expect.Equal(t, ga.Category, "AX")
|
||||
expect.Equal(t, gb.Name, "B*")
|
||||
expect.Equal(t, gb.Category, "BY")
|
||||
expect.Equal(t, gb.Show, false)
|
||||
}
|
||||
|
||||
@@ -9,9 +9,9 @@ import (
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/yusing/godoxy/internal/common"
|
||||
"github.com/yusing/godoxy/internal/jsonstore"
|
||||
"github.com/yusing/godoxy/internal/task"
|
||||
"github.com/yusing/godoxy/internal/utils"
|
||||
"github.com/yusing/godoxy/internal/utils/atomic"
|
||||
"github.com/yusing/goutils/task"
|
||||
)
|
||||
|
||||
type cacheEntry struct {
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/yusing/godoxy/internal/gperr"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
)
|
||||
|
||||
type (
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"testing"
|
||||
|
||||
. "github.com/yusing/godoxy/internal/homepage"
|
||||
expect "github.com/yusing/godoxy/internal/utils/testing"
|
||||
expect "github.com/yusing/goutils/testing"
|
||||
)
|
||||
|
||||
func strPtr(s string) *string {
|
||||
|
||||
@@ -7,8 +7,8 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/yusing/godoxy/internal/gperr"
|
||||
"github.com/yusing/godoxy/internal/homepage/widgets"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
|
||||
@@ -15,8 +15,8 @@ import (
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/yusing/godoxy/internal/common"
|
||||
"github.com/yusing/godoxy/internal/serialization"
|
||||
"github.com/yusing/godoxy/internal/task"
|
||||
strutils "github.com/yusing/goutils/strings"
|
||||
"github.com/yusing/goutils/task"
|
||||
)
|
||||
|
||||
type (
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/yusing/godoxy/internal/gperr"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
)
|
||||
|
||||
var HTTPClient = &http.Client{
|
||||
|
||||
@@ -3,8 +3,8 @@ package widgets
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/yusing/godoxy/internal/gperr"
|
||||
"github.com/yusing/godoxy/internal/serialization"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
)
|
||||
|
||||
type (
|
||||
|
||||
@@ -3,10 +3,10 @@ package idlewatcher
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/yusing/godoxy/internal/gperr"
|
||||
idlewatcher "github.com/yusing/godoxy/internal/idlewatcher/types"
|
||||
"github.com/yusing/godoxy/internal/task"
|
||||
"github.com/yusing/godoxy/internal/types"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
"github.com/yusing/goutils/task"
|
||||
)
|
||||
|
||||
// Start implements health.HealthMonitor.
|
||||
|
||||
@@ -5,10 +5,10 @@ import (
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/yusing/godoxy/internal/docker"
|
||||
"github.com/yusing/godoxy/internal/gperr"
|
||||
idlewatcher "github.com/yusing/godoxy/internal/idlewatcher/types"
|
||||
"github.com/yusing/godoxy/internal/types"
|
||||
"github.com/yusing/godoxy/internal/watcher"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
)
|
||||
|
||||
type DockerProvider struct {
|
||||
|
||||
@@ -5,12 +5,12 @@ import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/yusing/godoxy/internal/gperr"
|
||||
idlewatcher "github.com/yusing/godoxy/internal/idlewatcher/types"
|
||||
"github.com/yusing/godoxy/internal/proxmox"
|
||||
"github.com/yusing/godoxy/internal/types"
|
||||
"github.com/yusing/godoxy/internal/watcher"
|
||||
"github.com/yusing/godoxy/internal/watcher/events"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
)
|
||||
|
||||
type ProxmoxProvider struct {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package idlewatcher
|
||||
|
||||
import "github.com/yusing/godoxy/internal/gperr"
|
||||
import gperr "github.com/yusing/goutils/errs"
|
||||
|
||||
type ContainerStatus string
|
||||
|
||||
|
||||
@@ -3,9 +3,9 @@ package idlewatcher
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/yusing/godoxy/internal/gperr"
|
||||
"github.com/yusing/godoxy/internal/types"
|
||||
"github.com/yusing/godoxy/internal/watcher/events"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
)
|
||||
|
||||
type Provider interface {
|
||||
|
||||
@@ -12,18 +12,18 @@ import (
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/yusing/ds/ordered"
|
||||
"github.com/yusing/godoxy/internal/docker"
|
||||
"github.com/yusing/godoxy/internal/gperr"
|
||||
"github.com/yusing/godoxy/internal/idlewatcher/provider"
|
||||
idlewatcher "github.com/yusing/godoxy/internal/idlewatcher/types"
|
||||
nettypes "github.com/yusing/godoxy/internal/net/types"
|
||||
"github.com/yusing/godoxy/internal/route/routes"
|
||||
"github.com/yusing/godoxy/internal/task"
|
||||
"github.com/yusing/godoxy/internal/types"
|
||||
U "github.com/yusing/godoxy/internal/utils"
|
||||
"github.com/yusing/godoxy/internal/utils/atomic"
|
||||
"github.com/yusing/godoxy/internal/watcher/events"
|
||||
"github.com/yusing/godoxy/internal/watcher/health/monitor"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
"github.com/yusing/goutils/http/reverseproxy"
|
||||
"github.com/yusing/goutils/task"
|
||||
"golang.org/x/sync/errgroup"
|
||||
"golang.org/x/sync/singleflight"
|
||||
)
|
||||
|
||||
@@ -10,9 +10,9 @@ import (
|
||||
"github.com/puzpuzpuz/xsync/v4"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/yusing/godoxy/internal/common"
|
||||
"github.com/yusing/godoxy/internal/gperr"
|
||||
"github.com/yusing/godoxy/internal/serialization"
|
||||
"github.com/yusing/godoxy/internal/task"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
"github.com/yusing/goutils/task"
|
||||
)
|
||||
|
||||
type namespace string
|
||||
|
||||
@@ -9,12 +9,12 @@ import (
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/yusing/godoxy/internal/gperr"
|
||||
maxmind "github.com/yusing/godoxy/internal/maxmind/types"
|
||||
"github.com/yusing/godoxy/internal/task"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
ioutils "github.com/yusing/goutils/io"
|
||||
strutils "github.com/yusing/goutils/strings"
|
||||
"github.com/yusing/goutils/synk"
|
||||
"github.com/yusing/goutils/task"
|
||||
"golang.org/x/time/rate"
|
||||
)
|
||||
|
||||
|
||||
@@ -9,9 +9,9 @@ import (
|
||||
"time"
|
||||
|
||||
. "github.com/yusing/godoxy/internal/logging/accesslog"
|
||||
"github.com/yusing/godoxy/internal/task"
|
||||
"github.com/yusing/godoxy/internal/utils"
|
||||
expect "github.com/yusing/godoxy/internal/utils/testing"
|
||||
"github.com/yusing/goutils/task"
|
||||
expect "github.com/yusing/goutils/testing"
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
@@ -9,9 +9,10 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/afero"
|
||||
"github.com/yusing/godoxy/internal/task"
|
||||
expect "github.com/yusing/godoxy/internal/utils/testing"
|
||||
expect "github.com/yusing/goutils/testing"
|
||||
|
||||
strutils "github.com/yusing/goutils/strings"
|
||||
"github.com/yusing/goutils/task"
|
||||
)
|
||||
|
||||
func TestBackScanner(t *testing.T) {
|
||||
|
||||
@@ -3,8 +3,8 @@ package accesslog
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/yusing/godoxy/internal/gperr"
|
||||
"github.com/yusing/godoxy/internal/serialization"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
)
|
||||
|
||||
type (
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"github.com/yusing/godoxy/internal/docker"
|
||||
. "github.com/yusing/godoxy/internal/logging/accesslog"
|
||||
"github.com/yusing/godoxy/internal/serialization"
|
||||
expect "github.com/yusing/godoxy/internal/utils/testing"
|
||||
expect "github.com/yusing/goutils/testing"
|
||||
)
|
||||
|
||||
func TestNewConfig(t *testing.T) {
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"testing"
|
||||
|
||||
. "github.com/yusing/godoxy/internal/logging/accesslog"
|
||||
expect "github.com/yusing/godoxy/internal/utils/testing"
|
||||
expect "github.com/yusing/goutils/testing"
|
||||
)
|
||||
|
||||
// Cookie header should be removed,
|
||||
|
||||
@@ -6,9 +6,8 @@ import (
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
expect "github.com/yusing/godoxy/internal/utils/testing"
|
||||
|
||||
"github.com/yusing/godoxy/internal/task"
|
||||
"github.com/yusing/goutils/task"
|
||||
expect "github.com/yusing/goutils/testing"
|
||||
)
|
||||
|
||||
func TestConcurrentFileLoggersShareSameAccessLogIO(t *testing.T) {
|
||||
|
||||
@@ -5,8 +5,8 @@ import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/yusing/godoxy/internal/gperr"
|
||||
nettypes "github.com/yusing/godoxy/internal/net/types"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
strutils "github.com/yusing/goutils/strings"
|
||||
)
|
||||
|
||||
|
||||
@@ -7,8 +7,8 @@ import (
|
||||
|
||||
. "github.com/yusing/godoxy/internal/logging/accesslog"
|
||||
nettypes "github.com/yusing/godoxy/internal/net/types"
|
||||
expect "github.com/yusing/godoxy/internal/utils/testing"
|
||||
strutils "github.com/yusing/goutils/strings"
|
||||
expect "github.com/yusing/goutils/testing"
|
||||
)
|
||||
|
||||
func TestStatusCodeFilter(t *testing.T) {
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/yusing/godoxy/internal/gperr"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
strutils "github.com/yusing/goutils/strings"
|
||||
)
|
||||
|
||||
@@ -70,7 +70,7 @@ func (r *Retention) Parse(v string) (err error) {
|
||||
if !r.IsValid() {
|
||||
return ErrZeroValue
|
||||
}
|
||||
return
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *Retention) String() string {
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"testing"
|
||||
|
||||
. "github.com/yusing/godoxy/internal/logging/accesslog"
|
||||
expect "github.com/yusing/godoxy/internal/utils/testing"
|
||||
expect "github.com/yusing/goutils/testing"
|
||||
)
|
||||
|
||||
func TestParseRetention(t *testing.T) {
|
||||
|
||||
@@ -6,8 +6,8 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/yusing/godoxy/internal/gperr"
|
||||
"github.com/yusing/godoxy/internal/utils"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
strutils "github.com/yusing/goutils/strings"
|
||||
"github.com/yusing/goutils/synk"
|
||||
)
|
||||
@@ -251,14 +251,14 @@ func rotateLogFileBySize(file supportRotate, config *Retention) (result *RotateR
|
||||
// otherwise it returns zero time.
|
||||
func ParseLogTime(line []byte) (t time.Time) {
|
||||
if len(line) == 0 {
|
||||
return
|
||||
return t
|
||||
}
|
||||
|
||||
if timeStr := ExtractTime(line); timeStr != nil {
|
||||
t, _ = time.Parse(LogTimeFormat, string(timeStr)) // ignore error
|
||||
return
|
||||
return t
|
||||
}
|
||||
return
|
||||
return t
|
||||
}
|
||||
|
||||
var timeJSON = []byte(`"time":"`)
|
||||
@@ -272,8 +272,8 @@ func ExtractTime(line []byte) []byte {
|
||||
switch line[0] {
|
||||
case '{': // JSON format
|
||||
if i := bytes.Index(line, timeJSON); i != -1 {
|
||||
var jsonStart = i + len(`"time":"`)
|
||||
var jsonEnd = i + len(`"time":"`) + len(LogTimeFormat)
|
||||
jsonStart := i + len(`"time":"`)
|
||||
jsonEnd := i + len(`"time":"`) + len(LogTimeFormat)
|
||||
if len(line) < jsonEnd {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -7,10 +7,10 @@ import (
|
||||
"time"
|
||||
|
||||
. "github.com/yusing/godoxy/internal/logging/accesslog"
|
||||
"github.com/yusing/godoxy/internal/task"
|
||||
"github.com/yusing/godoxy/internal/utils"
|
||||
expect "github.com/yusing/godoxy/internal/utils/testing"
|
||||
expect "github.com/yusing/goutils/testing"
|
||||
strutils "github.com/yusing/goutils/strings"
|
||||
"github.com/yusing/goutils/task"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@@ -3,7 +3,7 @@ package accesslog
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/yusing/godoxy/internal/gperr"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
strutils "github.com/yusing/goutils/strings"
|
||||
)
|
||||
|
||||
|
||||
@@ -61,11 +61,11 @@ func (m *memLogger) Write(p []byte) (n int, err error) {
|
||||
pos, err := m.writeBuf(p)
|
||||
if err != nil {
|
||||
// not logging the error here, it will cause Run to be called again = infinite loop
|
||||
return
|
||||
return n, err
|
||||
}
|
||||
|
||||
m.notifyWS(pos, n)
|
||||
return
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (m *memLogger) ServeHTTP(c *gin.Context) {
|
||||
@@ -149,7 +149,7 @@ func (m *memLogger) writeBuf(b []byte) (pos int, err error) {
|
||||
defer m.Unlock()
|
||||
pos = m.Len()
|
||||
_, err = m.Buffer.Write(b)
|
||||
return
|
||||
return pos, err
|
||||
}
|
||||
|
||||
func (m *memLogger) events() (logs <-chan []byte, cancel func()) {
|
||||
|
||||
@@ -5,9 +5,9 @@ import (
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/yusing/godoxy/internal/gperr"
|
||||
"github.com/yusing/godoxy/internal/notif"
|
||||
"github.com/yusing/godoxy/internal/task"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
"github.com/yusing/goutils/task"
|
||||
)
|
||||
|
||||
var instance *MaxMind
|
||||
|
||||
@@ -15,9 +15,9 @@ import (
|
||||
|
||||
"github.com/oschwald/maxminddb-golang"
|
||||
"github.com/yusing/godoxy/internal/common"
|
||||
"github.com/yusing/godoxy/internal/gperr"
|
||||
maxmind "github.com/yusing/godoxy/internal/maxmind/types"
|
||||
"github.com/yusing/godoxy/internal/task"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
"github.com/yusing/goutils/task"
|
||||
)
|
||||
|
||||
/*
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
|
||||
"github.com/oschwald/maxminddb-golang"
|
||||
maxmind "github.com/yusing/godoxy/internal/maxmind/types"
|
||||
"github.com/yusing/godoxy/internal/task"
|
||||
"github.com/yusing/goutils/task"
|
||||
)
|
||||
|
||||
func testCfg() *MaxMind {
|
||||
|
||||
@@ -3,7 +3,7 @@ package maxmind
|
||||
import (
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/yusing/godoxy/internal/gperr"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
)
|
||||
|
||||
type (
|
||||
|
||||
@@ -11,9 +11,9 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/yusing/godoxy/internal/gperr"
|
||||
"github.com/yusing/godoxy/internal/task"
|
||||
"github.com/yusing/godoxy/internal/utils/atomic"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
"github.com/yusing/goutils/task"
|
||||
)
|
||||
|
||||
type (
|
||||
|
||||
@@ -16,8 +16,8 @@ import (
|
||||
"github.com/shirou/gopsutil/v4/sensors"
|
||||
"github.com/shirou/gopsutil/v4/warning"
|
||||
"github.com/yusing/godoxy/internal/common"
|
||||
"github.com/yusing/godoxy/internal/gperr"
|
||||
"github.com/yusing/godoxy/internal/metrics/period"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
)
|
||||
|
||||
// json tags are left for tests
|
||||
|
||||
@@ -10,96 +10,98 @@ import (
|
||||
"github.com/shirou/gopsutil/v4/mem"
|
||||
"github.com/shirou/gopsutil/v4/net"
|
||||
"github.com/shirou/gopsutil/v4/sensors"
|
||||
. "github.com/yusing/godoxy/internal/utils/testing"
|
||||
expect "github.com/yusing/goutils/testing"
|
||||
)
|
||||
|
||||
// Create test data
|
||||
var cpuAvg = 45.67
|
||||
var testInfo = &SystemInfo{
|
||||
Timestamp: 123456,
|
||||
CPUAverage: &cpuAvg,
|
||||
Memory: &mem.VirtualMemoryStat{
|
||||
Total: 16000000000,
|
||||
Available: 8000000000,
|
||||
Used: 8000000000,
|
||||
UsedPercent: 50.0,
|
||||
},
|
||||
Disks: map[string]*disk.UsageStat{
|
||||
"sda": {
|
||||
Path: "/",
|
||||
Fstype: "ext4",
|
||||
Total: 500000000000,
|
||||
Free: 250000000000,
|
||||
Used: 250000000000,
|
||||
var (
|
||||
cpuAvg = 45.67
|
||||
testInfo = &SystemInfo{
|
||||
Timestamp: 123456,
|
||||
CPUAverage: &cpuAvg,
|
||||
Memory: &mem.VirtualMemoryStat{
|
||||
Total: 16000000000,
|
||||
Available: 8000000000,
|
||||
Used: 8000000000,
|
||||
UsedPercent: 50.0,
|
||||
},
|
||||
"nvme0n1": {
|
||||
Path: "/",
|
||||
Fstype: "zfs",
|
||||
Total: 500000000000,
|
||||
Free: 250000000000,
|
||||
Used: 250000000000,
|
||||
UsedPercent: 50.0,
|
||||
Disks: map[string]*disk.UsageStat{
|
||||
"sda": {
|
||||
Path: "/",
|
||||
Fstype: "ext4",
|
||||
Total: 500000000000,
|
||||
Free: 250000000000,
|
||||
Used: 250000000000,
|
||||
UsedPercent: 50.0,
|
||||
},
|
||||
"nvme0n1": {
|
||||
Path: "/",
|
||||
Fstype: "zfs",
|
||||
Total: 500000000000,
|
||||
Free: 250000000000,
|
||||
Used: 250000000000,
|
||||
UsedPercent: 50.0,
|
||||
},
|
||||
},
|
||||
},
|
||||
DisksIO: map[string]*disk.IOCountersStat{
|
||||
"media": {
|
||||
Name: "media",
|
||||
ReadBytes: 1000000,
|
||||
WriteBytes: 2000000,
|
||||
ReadSpeed: 100.5,
|
||||
WriteSpeed: 200.5,
|
||||
Iops: 1000,
|
||||
DisksIO: map[string]*disk.IOCountersStat{
|
||||
"media": {
|
||||
Name: "media",
|
||||
ReadBytes: 1000000,
|
||||
WriteBytes: 2000000,
|
||||
ReadSpeed: 100.5,
|
||||
WriteSpeed: 200.5,
|
||||
Iops: 1000,
|
||||
},
|
||||
"nvme0n1": {
|
||||
Name: "nvme0n1",
|
||||
ReadBytes: 1000000,
|
||||
WriteBytes: 2000000,
|
||||
ReadSpeed: 100.5,
|
||||
WriteSpeed: 200.5,
|
||||
Iops: 1000,
|
||||
},
|
||||
},
|
||||
"nvme0n1": {
|
||||
Name: "nvme0n1",
|
||||
ReadBytes: 1000000,
|
||||
WriteBytes: 2000000,
|
||||
ReadSpeed: 100.5,
|
||||
WriteSpeed: 200.5,
|
||||
Iops: 1000,
|
||||
Network: &net.IOCountersStat{
|
||||
BytesSent: 5000000,
|
||||
BytesRecv: 10000000,
|
||||
UploadSpeed: 1024.5,
|
||||
DownloadSpeed: 2048.5,
|
||||
},
|
||||
},
|
||||
Network: &net.IOCountersStat{
|
||||
BytesSent: 5000000,
|
||||
BytesRecv: 10000000,
|
||||
UploadSpeed: 1024.5,
|
||||
DownloadSpeed: 2048.5,
|
||||
},
|
||||
Sensors: []sensors.TemperatureStat{
|
||||
{
|
||||
SensorKey: "cpu_temp",
|
||||
Temperature: 30.0,
|
||||
High: 40.0,
|
||||
Critical: 50.0,
|
||||
Sensors: []sensors.TemperatureStat{
|
||||
{
|
||||
SensorKey: "cpu_temp",
|
||||
Temperature: 30.0,
|
||||
High: 40.0,
|
||||
Critical: 50.0,
|
||||
},
|
||||
{
|
||||
SensorKey: "gpu_temp",
|
||||
Temperature: 40.0,
|
||||
High: 50.0,
|
||||
Critical: 60.0,
|
||||
},
|
||||
},
|
||||
{
|
||||
SensorKey: "gpu_temp",
|
||||
Temperature: 40.0,
|
||||
High: 50.0,
|
||||
Critical: 60.0,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
func TestSystemInfo(t *testing.T) {
|
||||
// Test marshaling
|
||||
data, err := json.Marshal(testInfo)
|
||||
ExpectNoError(t, err)
|
||||
expect.NoError(t, err)
|
||||
|
||||
// Test unmarshaling back
|
||||
var decoded SystemInfo
|
||||
err = json.Unmarshal(data, &decoded)
|
||||
ExpectNoError(t, err)
|
||||
expect.NoError(t, err)
|
||||
|
||||
// Compare original and decoded
|
||||
ExpectEqual(t, decoded.Timestamp, testInfo.Timestamp)
|
||||
ExpectEqual(t, *decoded.CPUAverage, *testInfo.CPUAverage)
|
||||
ExpectEqual(t, decoded.Memory, testInfo.Memory)
|
||||
ExpectEqual(t, decoded.Disks, testInfo.Disks)
|
||||
ExpectEqual(t, decoded.DisksIO, testInfo.DisksIO)
|
||||
ExpectEqual(t, decoded.Network, testInfo.Network)
|
||||
ExpectEqual(t, decoded.Sensors, testInfo.Sensors)
|
||||
expect.Equal(t, decoded.Timestamp, testInfo.Timestamp)
|
||||
expect.Equal(t, *decoded.CPUAverage, *testInfo.CPUAverage)
|
||||
expect.Equal(t, decoded.Memory, testInfo.Memory)
|
||||
expect.Equal(t, decoded.Disks, testInfo.Disks)
|
||||
expect.Equal(t, decoded.DisksIO, testInfo.DisksIO)
|
||||
expect.Equal(t, decoded.Network, testInfo.Network)
|
||||
expect.Equal(t, decoded.Sensors, testInfo.Sensors)
|
||||
|
||||
// Test nil fields
|
||||
nilInfo := &SystemInfo{
|
||||
@@ -107,18 +109,18 @@ func TestSystemInfo(t *testing.T) {
|
||||
}
|
||||
|
||||
data, err = json.Marshal(nilInfo)
|
||||
ExpectNoError(t, err)
|
||||
expect.NoError(t, err)
|
||||
|
||||
var decodedNil SystemInfo
|
||||
err = json.Unmarshal(data, &decodedNil)
|
||||
ExpectNoError(t, err)
|
||||
expect.NoError(t, err)
|
||||
|
||||
ExpectEqual(t, decodedNil.Timestamp, nilInfo.Timestamp)
|
||||
ExpectTrue(t, decodedNil.CPUAverage == nil)
|
||||
ExpectTrue(t, decodedNil.Memory == nil)
|
||||
ExpectTrue(t, decodedNil.Disks == nil)
|
||||
ExpectTrue(t, decodedNil.Network == nil)
|
||||
ExpectTrue(t, decodedNil.Sensors == nil)
|
||||
expect.Equal(t, decodedNil.Timestamp, nilInfo.Timestamp)
|
||||
expect.True(t, decodedNil.CPUAverage == nil)
|
||||
expect.True(t, decodedNil.Memory == nil)
|
||||
expect.True(t, decodedNil.Disks == nil)
|
||||
expect.True(t, decodedNil.Network == nil)
|
||||
expect.True(t, decodedNil.Sensors == nil)
|
||||
}
|
||||
|
||||
func TestSerialize(t *testing.T) {
|
||||
@@ -130,15 +132,15 @@ func TestSerialize(t *testing.T) {
|
||||
t.Run(string(query), func(t *testing.T) {
|
||||
_, result := aggregate(entries, url.Values{"aggregate": []string{string(query)}})
|
||||
s, err := result.MarshalJSON()
|
||||
ExpectNoError(t, err)
|
||||
expect.NoError(t, err)
|
||||
var v []map[string]any
|
||||
ExpectNoError(t, json.Unmarshal(s, &v))
|
||||
ExpectEqual(t, len(v), len(result.Entries))
|
||||
expect.NoError(t, json.Unmarshal(s, &v))
|
||||
expect.Equal(t, len(v), len(result.Entries))
|
||||
for i, m := range v {
|
||||
for k, v := range m {
|
||||
// some int64 values are converted to float64 on json.Unmarshal
|
||||
vv := reflect.ValueOf(result.Entries[i][k])
|
||||
ExpectEqual(t, reflect.ValueOf(v).Convert(vv.Type()).Interface(), vv.Interface())
|
||||
expect.Equal(t, reflect.ValueOf(v).Convert(vv.Type()).Interface(), vv.Interface())
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -4,9 +4,8 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"slices"
|
||||
"time"
|
||||
|
||||
"github.com/lithammer/fuzzysearch/fuzzy"
|
||||
config "github.com/yusing/godoxy/internal/config/types"
|
||||
|
||||
@@ -4,38 +4,38 @@ import (
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
. "github.com/yusing/godoxy/internal/utils/testing"
|
||||
expect "github.com/yusing/goutils/testing"
|
||||
)
|
||||
|
||||
func TestContentTypes(t *testing.T) {
|
||||
ExpectTrue(t, GetContentType(http.Header{"Content-Type": {"text/html"}}).IsHTML())
|
||||
ExpectTrue(t, GetContentType(http.Header{"Content-Type": {"text/html; charset=utf-8"}}).IsHTML())
|
||||
ExpectTrue(t, GetContentType(http.Header{"Content-Type": {"application/xhtml+xml"}}).IsHTML())
|
||||
ExpectFalse(t, GetContentType(http.Header{"Content-Type": {"text/plain"}}).IsHTML())
|
||||
expect.True(t, GetContentType(http.Header{"Content-Type": {"text/html"}}).IsHTML())
|
||||
expect.True(t, GetContentType(http.Header{"Content-Type": {"text/html; charset=utf-8"}}).IsHTML())
|
||||
expect.True(t, GetContentType(http.Header{"Content-Type": {"application/xhtml+xml"}}).IsHTML())
|
||||
expect.False(t, GetContentType(http.Header{"Content-Type": {"text/plain"}}).IsHTML())
|
||||
|
||||
ExpectTrue(t, GetContentType(http.Header{"Content-Type": {"application/json"}}).IsJSON())
|
||||
ExpectTrue(t, GetContentType(http.Header{"Content-Type": {"application/json; charset=utf-8"}}).IsJSON())
|
||||
ExpectFalse(t, GetContentType(http.Header{"Content-Type": {"text/html"}}).IsJSON())
|
||||
expect.True(t, GetContentType(http.Header{"Content-Type": {"application/json"}}).IsJSON())
|
||||
expect.True(t, GetContentType(http.Header{"Content-Type": {"application/json; charset=utf-8"}}).IsJSON())
|
||||
expect.False(t, GetContentType(http.Header{"Content-Type": {"text/html"}}).IsJSON())
|
||||
|
||||
ExpectTrue(t, GetContentType(http.Header{"Content-Type": {"text/plain"}}).IsPlainText())
|
||||
ExpectTrue(t, GetContentType(http.Header{"Content-Type": {"text/plain; charset=utf-8"}}).IsPlainText())
|
||||
ExpectFalse(t, GetContentType(http.Header{"Content-Type": {"text/html"}}).IsPlainText())
|
||||
expect.True(t, GetContentType(http.Header{"Content-Type": {"text/plain"}}).IsPlainText())
|
||||
expect.True(t, GetContentType(http.Header{"Content-Type": {"text/plain; charset=utf-8"}}).IsPlainText())
|
||||
expect.False(t, GetContentType(http.Header{"Content-Type": {"text/html"}}).IsPlainText())
|
||||
}
|
||||
|
||||
func TestAcceptContentTypes(t *testing.T) {
|
||||
ExpectTrue(t, GetAccept(http.Header{"Accept": {"text/html", "text/plain"}}).AcceptPlainText())
|
||||
ExpectTrue(t, GetAccept(http.Header{"Accept": {"text/html", "text/plain; charset=utf-8"}}).AcceptPlainText())
|
||||
ExpectTrue(t, GetAccept(http.Header{"Accept": {"text/html", "text/plain"}}).AcceptHTML())
|
||||
ExpectTrue(t, GetAccept(http.Header{"Accept": {"application/json"}}).AcceptJSON())
|
||||
ExpectTrue(t, GetAccept(http.Header{"Accept": {"*/*"}}).AcceptPlainText())
|
||||
ExpectTrue(t, GetAccept(http.Header{"Accept": {"*/*"}}).AcceptHTML())
|
||||
ExpectTrue(t, GetAccept(http.Header{"Accept": {"*/*"}}).AcceptJSON())
|
||||
ExpectTrue(t, GetAccept(http.Header{"Accept": {"text/*"}}).AcceptPlainText())
|
||||
ExpectTrue(t, GetAccept(http.Header{"Accept": {"text/*"}}).AcceptHTML())
|
||||
expect.True(t, GetAccept(http.Header{"Accept": {"text/html", "text/plain"}}).AcceptPlainText())
|
||||
expect.True(t, GetAccept(http.Header{"Accept": {"text/html", "text/plain; charset=utf-8"}}).AcceptPlainText())
|
||||
expect.True(t, GetAccept(http.Header{"Accept": {"text/html", "text/plain"}}).AcceptHTML())
|
||||
expect.True(t, GetAccept(http.Header{"Accept": {"application/json"}}).AcceptJSON())
|
||||
expect.True(t, GetAccept(http.Header{"Accept": {"*/*"}}).AcceptPlainText())
|
||||
expect.True(t, GetAccept(http.Header{"Accept": {"*/*"}}).AcceptHTML())
|
||||
expect.True(t, GetAccept(http.Header{"Accept": {"*/*"}}).AcceptJSON())
|
||||
expect.True(t, GetAccept(http.Header{"Accept": {"text/*"}}).AcceptPlainText())
|
||||
expect.True(t, GetAccept(http.Header{"Accept": {"text/*"}}).AcceptHTML())
|
||||
|
||||
ExpectFalse(t, GetAccept(http.Header{"Accept": {"text/plain"}}).AcceptHTML())
|
||||
ExpectFalse(t, GetAccept(http.Header{"Accept": {"text/plain; charset=utf-8"}}).AcceptHTML())
|
||||
ExpectFalse(t, GetAccept(http.Header{"Accept": {"text/html"}}).AcceptPlainText())
|
||||
ExpectFalse(t, GetAccept(http.Header{"Accept": {"text/html"}}).AcceptJSON())
|
||||
ExpectFalse(t, GetAccept(http.Header{"Accept": {"text/*"}}).AcceptJSON())
|
||||
expect.False(t, GetAccept(http.Header{"Accept": {"text/plain"}}).AcceptHTML())
|
||||
expect.False(t, GetAccept(http.Header{"Accept": {"text/plain; charset=utf-8"}}).AcceptHTML())
|
||||
expect.False(t, GetAccept(http.Header{"Accept": {"text/html"}}).AcceptPlainText())
|
||||
expect.False(t, GetAccept(http.Header{"Accept": {"text/html"}}).AcceptJSON())
|
||||
expect.False(t, GetAccept(http.Header{"Accept": {"text/*"}}).AcceptJSON())
|
||||
}
|
||||
|
||||
@@ -6,9 +6,9 @@ import (
|
||||
"net/http"
|
||||
"sync"
|
||||
|
||||
"github.com/yusing/godoxy/internal/gperr"
|
||||
"github.com/yusing/godoxy/internal/net/gphttp/middleware"
|
||||
"github.com/yusing/godoxy/internal/types"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
)
|
||||
|
||||
type ipHash struct {
|
||||
|
||||
@@ -8,11 +8,11 @@ import (
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/yusing/godoxy/internal/gperr"
|
||||
"github.com/yusing/godoxy/internal/task"
|
||||
"github.com/yusing/godoxy/internal/types"
|
||||
"github.com/yusing/godoxy/internal/utils/pool"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
"github.com/yusing/goutils/http/httpheaders"
|
||||
"github.com/yusing/goutils/task"
|
||||
)
|
||||
|
||||
// TODO: stats of each server.
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/yusing/godoxy/internal/types"
|
||||
. "github.com/yusing/godoxy/internal/utils/testing"
|
||||
expect "github.com/yusing/goutils/testing"
|
||||
)
|
||||
|
||||
func TestRebalance(t *testing.T) {
|
||||
@@ -15,7 +15,7 @@ func TestRebalance(t *testing.T) {
|
||||
lb.AddServer(TestNewServer(0))
|
||||
}
|
||||
lb.rebalance()
|
||||
ExpectEqual(t, lb.sumWeight, maxWeight)
|
||||
expect.Equal(t, lb.sumWeight, maxWeight)
|
||||
})
|
||||
t.Run("less", func(t *testing.T) {
|
||||
lb := New(new(types.LoadBalancerConfig))
|
||||
@@ -26,7 +26,7 @@ func TestRebalance(t *testing.T) {
|
||||
lb.AddServer(TestNewServer(float64(maxWeight) * .1))
|
||||
lb.rebalance()
|
||||
// t.Logf("%s", U.Must(json.MarshalIndent(lb.pool, "", " ")))
|
||||
ExpectEqual(t, lb.sumWeight, maxWeight)
|
||||
expect.Equal(t, lb.sumWeight, maxWeight)
|
||||
})
|
||||
t.Run("more", func(t *testing.T) {
|
||||
lb := New(new(types.LoadBalancerConfig))
|
||||
@@ -39,6 +39,6 @@ func TestRebalance(t *testing.T) {
|
||||
lb.AddServer(TestNewServer(float64(maxWeight) * .1))
|
||||
lb.rebalance()
|
||||
// t.Logf("%s", U.Must(json.MarshalIndent(lb.pool, "", " ")))
|
||||
ExpectEqual(t, lb.sumWeight, maxWeight)
|
||||
expect.Equal(t, lb.sumWeight, maxWeight)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -14,9 +14,9 @@ import (
|
||||
. "github.com/yusing/godoxy/internal/net/gphttp/middleware"
|
||||
"github.com/yusing/godoxy/internal/route"
|
||||
routeTypes "github.com/yusing/godoxy/internal/route/types"
|
||||
"github.com/yusing/godoxy/internal/task"
|
||||
expect "github.com/yusing/godoxy/internal/utils/testing"
|
||||
"github.com/yusing/goutils/http/reverseproxy"
|
||||
"github.com/yusing/goutils/task"
|
||||
expect "github.com/yusing/goutils/testing"
|
||||
)
|
||||
|
||||
func noOpHandler(w http.ResponseWriter, r *http.Request) {}
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
|
||||
_ "embed"
|
||||
|
||||
"github.com/yusing/godoxy/internal/gperr"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
)
|
||||
|
||||
type HcaptchaProvider struct {
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/yusing/godoxy/internal/gperr"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
)
|
||||
|
||||
type Provider interface {
|
||||
@@ -16,6 +16,4 @@ type Provider interface {
|
||||
FormHTML() string
|
||||
}
|
||||
|
||||
var (
|
||||
ErrCaptchaVerificationFailed = gperr.New("captcha verification failed")
|
||||
)
|
||||
var ErrCaptchaVerificationFailed = gperr.New("captcha verification failed")
|
||||
|
||||
@@ -7,9 +7,9 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/yusing/godoxy/internal/gperr"
|
||||
"github.com/yusing/godoxy/internal/serialization"
|
||||
. "github.com/yusing/godoxy/internal/utils/testing"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
expect "github.com/yusing/goutils/testing"
|
||||
)
|
||||
|
||||
//go:embed test_data/cidr_whitelist_test.yml
|
||||
@@ -23,32 +23,32 @@ func TestCIDRWhitelistValidation(t *testing.T) {
|
||||
"allow": []string{"192.168.2.100/32"},
|
||||
"message": testMessage,
|
||||
})
|
||||
ExpectNoError(t, err)
|
||||
expect.NoError(t, err)
|
||||
_, err = CIDRWhiteList.New(OptionsRaw{
|
||||
"allow": []string{"192.168.2.100/32"},
|
||||
"message": testMessage,
|
||||
"status": 403,
|
||||
})
|
||||
ExpectNoError(t, err)
|
||||
expect.NoError(t, err)
|
||||
_, err = CIDRWhiteList.New(OptionsRaw{
|
||||
"allow": []string{"192.168.2.100/32"},
|
||||
"message": testMessage,
|
||||
"status_code": 403,
|
||||
})
|
||||
ExpectNoError(t, err)
|
||||
expect.NoError(t, err)
|
||||
})
|
||||
t.Run("missing allow", func(t *testing.T) {
|
||||
_, err := CIDRWhiteList.New(OptionsRaw{
|
||||
"message": testMessage,
|
||||
})
|
||||
ExpectError(t, serialization.ErrValidationError, err)
|
||||
expect.ErrorIs(t, serialization.ErrValidationError, err)
|
||||
})
|
||||
t.Run("invalid cidr", func(t *testing.T) {
|
||||
_, err := CIDRWhiteList.New(OptionsRaw{
|
||||
"allow": []string{"192.168.2.100/123"},
|
||||
"message": testMessage,
|
||||
})
|
||||
ExpectErrorT[*net.ParseError](t, err)
|
||||
expect.ErrorT[*net.ParseError](t, err)
|
||||
})
|
||||
t.Run("invalid status code", func(t *testing.T) {
|
||||
_, err := CIDRWhiteList.New(OptionsRaw{
|
||||
@@ -56,14 +56,14 @@ func TestCIDRWhitelistValidation(t *testing.T) {
|
||||
"status_code": 600,
|
||||
"message": testMessage,
|
||||
})
|
||||
ExpectError(t, serialization.ErrValidationError, err)
|
||||
expect.ErrorIs(t, serialization.ErrValidationError, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestCIDRWhitelist(t *testing.T) {
|
||||
errs := gperr.NewBuilder("")
|
||||
mids := BuildMiddlewaresFromYAML("", testCIDRWhitelistCompose, errs)
|
||||
ExpectNoError(t, errs.Error())
|
||||
expect.NoError(t, errs.Error())
|
||||
deny = mids["deny@file"]
|
||||
accept = mids["accept@file"]
|
||||
if deny == nil || accept == nil {
|
||||
@@ -74,9 +74,9 @@ func TestCIDRWhitelist(t *testing.T) {
|
||||
t.Parallel()
|
||||
for range 10 {
|
||||
result, err := newMiddlewareTest(deny, nil)
|
||||
ExpectNoError(t, err)
|
||||
ExpectEqual(t, result.ResponseStatus, cidrWhitelistDefaults.StatusCode)
|
||||
ExpectEqual(t, strings.TrimSpace(string(result.Data)), cidrWhitelistDefaults.Message)
|
||||
expect.NoError(t, err)
|
||||
expect.Equal(t, result.ResponseStatus, cidrWhitelistDefaults.StatusCode)
|
||||
expect.Equal(t, strings.TrimSpace(string(result.Data)), cidrWhitelistDefaults.Message)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -84,8 +84,8 @@ func TestCIDRWhitelist(t *testing.T) {
|
||||
t.Parallel()
|
||||
for range 10 {
|
||||
result, err := newMiddlewareTest(accept, nil)
|
||||
ExpectNoError(t, err)
|
||||
ExpectEqual(t, result.ResponseStatus, http.StatusOK)
|
||||
expect.NoError(t, err)
|
||||
expect.Equal(t, result.ResponseStatus, http.StatusOK)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -63,14 +63,14 @@ func (cri *cloudflareRealIP) before(w http.ResponseWriter, r *http.Request) bool
|
||||
|
||||
func tryFetchCFCIDR() (cfCIDRs []*nettypes.CIDR) {
|
||||
if time.Since(cfCIDRsLastUpdate.Load()) < cfCIDRsUpdateInterval {
|
||||
return
|
||||
return cfCIDRs
|
||||
}
|
||||
|
||||
cfCIDRsMu.Lock()
|
||||
defer cfCIDRsMu.Unlock()
|
||||
|
||||
if time.Since(cfCIDRsLastUpdate.Load()) < cfCIDRsUpdateInterval {
|
||||
return
|
||||
return cfCIDRs
|
||||
}
|
||||
|
||||
if common.IsTest {
|
||||
@@ -93,7 +93,7 @@ func tryFetchCFCIDR() (cfCIDRs []*nettypes.CIDR) {
|
||||
|
||||
cfCIDRsLastUpdate.Store(time.Now())
|
||||
log.Info().Msg("cloudflare CIDR range updated")
|
||||
return
|
||||
return cfCIDRs
|
||||
}
|
||||
|
||||
func fetchUpdateCFIPRange(endpoint string, cfCIDRs *[]*nettypes.CIDR) error {
|
||||
|
||||
@@ -9,11 +9,11 @@ import (
|
||||
"github.com/puzpuzpuz/xsync/v4"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/yusing/godoxy/internal/common"
|
||||
"github.com/yusing/godoxy/internal/gperr"
|
||||
"github.com/yusing/godoxy/internal/task"
|
||||
"github.com/yusing/godoxy/internal/utils"
|
||||
"github.com/yusing/godoxy/internal/watcher"
|
||||
"github.com/yusing/godoxy/internal/watcher/events"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
"github.com/yusing/goutils/task"
|
||||
)
|
||||
|
||||
const errPagesBasePath = common.ErrorPagesBasePath
|
||||
@@ -42,7 +42,7 @@ func GetErrorPageByStatus(statusCode int) (content []byte, ok bool) {
|
||||
if !ok && statusCode != 404 {
|
||||
return fileContentMap.Load("404.html")
|
||||
}
|
||||
return
|
||||
return content, ok
|
||||
}
|
||||
|
||||
func loadContent() {
|
||||
|
||||
@@ -10,9 +10,9 @@ import (
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/yusing/godoxy/internal/gperr"
|
||||
gphttp "github.com/yusing/godoxy/internal/net/gphttp"
|
||||
"github.com/yusing/godoxy/internal/serialization"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
"github.com/yusing/goutils/http/reverseproxy"
|
||||
)
|
||||
|
||||
@@ -207,10 +207,10 @@ func PatchReverseProxy(rp *ReverseProxy, middlewaresMap map[string]OptionsRaw) (
|
||||
var middlewares []*Middleware
|
||||
middlewares, err = compileMiddlewares(middlewaresMap)
|
||||
if err != nil {
|
||||
return
|
||||
return err
|
||||
}
|
||||
patchReverseProxy(rp, middlewares)
|
||||
return
|
||||
return err
|
||||
}
|
||||
|
||||
func patchReverseProxy(rp *ReverseProxy, middlewares []*Middleware) {
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"sort"
|
||||
|
||||
"github.com/goccy/go-yaml"
|
||||
"github.com/yusing/godoxy/internal/gperr"
|
||||
gperr "github.com/yusing/goutils/errs"
|
||||
)
|
||||
|
||||
var ErrMissingMiddlewareUse = gperr.New("missing middleware 'use' field")
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user