mirror of
https://github.com/yusing/godoxy.git
synced 2026-04-24 09:48:49 +02:00
refactor: move task, error and testing utils to separte repo; apply gofumpt
This commit is contained in:
@@ -8,13 +8,12 @@ import (
|
|||||||
"github.com/yusing/godoxy/agent/pkg/agent"
|
"github.com/yusing/godoxy/agent/pkg/agent"
|
||||||
"github.com/yusing/godoxy/agent/pkg/env"
|
"github.com/yusing/godoxy/agent/pkg/env"
|
||||||
"github.com/yusing/godoxy/agent/pkg/server"
|
"github.com/yusing/godoxy/agent/pkg/server"
|
||||||
"github.com/yusing/godoxy/internal/gperr"
|
|
||||||
"github.com/yusing/godoxy/internal/metrics/systeminfo"
|
"github.com/yusing/godoxy/internal/metrics/systeminfo"
|
||||||
httpServer "github.com/yusing/godoxy/internal/net/gphttp/server"
|
httpServer "github.com/yusing/godoxy/internal/net/gphttp/server"
|
||||||
"github.com/yusing/godoxy/internal/task"
|
|
||||||
"github.com/yusing/godoxy/pkg"
|
"github.com/yusing/godoxy/pkg"
|
||||||
socketproxy "github.com/yusing/godoxy/socketproxy/pkg"
|
socketproxy "github.com/yusing/godoxy/socketproxy/pkg"
|
||||||
strutils "github.com/yusing/goutils/strings"
|
strutils "github.com/yusing/goutils/strings"
|
||||||
|
"github.com/yusing/goutils/task"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@@ -27,21 +26,21 @@ func main() {
|
|||||||
ca := &agent.PEMPair{}
|
ca := &agent.PEMPair{}
|
||||||
err := ca.Load(env.AgentCACert)
|
err := ca.Load(env.AgentCACert)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gperr.LogFatal("init CA error", err)
|
log.Fatal().Err(err).Msg("init CA error")
|
||||||
}
|
}
|
||||||
caCert, err := ca.ToTLSCert()
|
caCert, err := ca.ToTLSCert()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gperr.LogFatal("init CA error", err)
|
log.Fatal().Err(err).Msg("init CA error")
|
||||||
}
|
}
|
||||||
|
|
||||||
srv := &agent.PEMPair{}
|
srv := &agent.PEMPair{}
|
||||||
srv.Load(env.AgentSSLCert)
|
srv.Load(env.AgentSSLCert)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gperr.LogFatal("init SSL error", err)
|
log.Fatal().Err(err).Msg("init SSL error")
|
||||||
}
|
}
|
||||||
srvCert, err := srv.ToTLSCert()
|
srvCert, err := srv.ToTLSCert()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
gperr.LogFatal("init SSL error", err)
|
log.Fatal().Err(err).Msg("init SSL error")
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info().Msgf("GoDoxy Agent version %s", pkg.GetVersion())
|
log.Info().Msgf("GoDoxy Agent version %s", pkg.GetVersion())
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ require (
|
|||||||
github.com/stretchr/testify v1.11.1
|
github.com/stretchr/testify v1.11.1
|
||||||
github.com/yusing/godoxy v0.18.6
|
github.com/yusing/godoxy v0.18.6
|
||||||
github.com/yusing/godoxy/socketproxy v0.0.0-00010101000000-000000000000
|
github.com/yusing/godoxy/socketproxy v0.0.0-00010101000000-000000000000
|
||||||
github.com/yusing/goutils v0.2.1
|
github.com/yusing/goutils v0.3.1
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
|||||||
@@ -208,8 +208,8 @@ github.com/vincent-petithory/dataurl v1.0.0/go.mod h1:FHafX5vmDzyP+1CQATJn7WFKc9
|
|||||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
github.com/yusing/ds v0.2.0 h1:lPhDU5eA2uvquVrBrzLCrQXRJJgSXlUYA53TbuK2sQY=
|
github.com/yusing/ds v0.2.0 h1:lPhDU5eA2uvquVrBrzLCrQXRJJgSXlUYA53TbuK2sQY=
|
||||||
github.com/yusing/ds v0.2.0/go.mod h1:XhKV4l7cZwBbbl7lRzNC9zX27zvCM0frIwiuD40ULRk=
|
github.com/yusing/ds v0.2.0/go.mod h1:XhKV4l7cZwBbbl7lRzNC9zX27zvCM0frIwiuD40ULRk=
|
||||||
github.com/yusing/goutils v0.2.1 h1:KjoCrNO0otthaPCZPfQY+5GKsqs5+J77CxP+TNHYa/Y=
|
github.com/yusing/goutils v0.3.1 h1:xCPoZ/haI8ZJ0ZaPU4g6+okSPdBczs8o98tIZ/TbpsQ=
|
||||||
github.com/yusing/goutils v0.2.1/go.mod h1:v6RZsMRdzcts4udSg0vqUIFvaD0OaUMPTwYJZ4XnQYo=
|
github.com/yusing/goutils v0.3.1/go.mod h1:meg9GcAU8yvBY21JgYjPuLsXD1Q5VdVHE32A4tG5Y5g=
|
||||||
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
||||||
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||||
|
|||||||
@@ -2,15 +2,16 @@ package agent
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"iter"
|
"iter"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/puzpuzpuz/xsync/v4"
|
"github.com/puzpuzpuz/xsync/v4"
|
||||||
"github.com/yusing/godoxy/internal/common"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var agentPool = xsync.NewMap[string, *AgentConfig](xsync.WithPresize(10))
|
var agentPool = xsync.NewMap[string, *AgentConfig](xsync.WithPresize(10))
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
if common.IsTest {
|
if strings.HasSuffix(os.Args[0], ".test") {
|
||||||
agentPool.Store("test-agent", &AgentConfig{
|
agentPool.Store("test-agent", &AgentConfig{
|
||||||
Addr: "test-agent",
|
Addr: "test-agent",
|
||||||
})
|
})
|
||||||
@@ -63,5 +64,5 @@ func NumAgents() int {
|
|||||||
|
|
||||||
func getAgentByAddr(addr string) (agent *AgentConfig, ok bool) {
|
func getAgentByAddr(addr string) (agent *AgentConfig, ok bool) {
|
||||||
agent, ok = agentPool.Load(addr)
|
agent, ok = agentPool.Load(addr)
|
||||||
return
|
return agent, ok
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ package agent
|
|||||||
import (
|
import (
|
||||||
"crypto/aes"
|
"crypto/aes"
|
||||||
"crypto/cipher"
|
"crypto/cipher"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/elliptic"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
@@ -10,14 +12,11 @@ import (
|
|||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"math/big"
|
"math/big"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"crypto/ecdsa"
|
|
||||||
"crypto/elliptic"
|
|
||||||
"fmt"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -244,5 +243,5 @@ func NewAgent() (ca, srv, client *PEMPair, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
client = toPEMPair(clientCertDER, clientKey)
|
client = toPEMPair(clientCertDER, clientKey)
|
||||||
return
|
return ca, srv, client, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ func proxyConfigFromHeadersLegacy(h http.Header) (cfg Config) {
|
|||||||
cfg.Scheme = "https"
|
cfg.Scheme = "https"
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return cfg
|
||||||
}
|
}
|
||||||
|
|
||||||
func proxyConfigFromHeaders(h http.Header) (cfg Config, err error) {
|
func proxyConfigFromHeaders(h http.Header) (cfg Config, err error) {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import (
|
|||||||
"github.com/yusing/godoxy/agent/pkg/env"
|
"github.com/yusing/godoxy/agent/pkg/env"
|
||||||
"github.com/yusing/godoxy/agent/pkg/handler"
|
"github.com/yusing/godoxy/agent/pkg/handler"
|
||||||
"github.com/yusing/godoxy/internal/net/gphttp/server"
|
"github.com/yusing/godoxy/internal/net/gphttp/server"
|
||||||
"github.com/yusing/godoxy/internal/task"
|
"github.com/yusing/goutils/task"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Options struct {
|
type Options struct {
|
||||||
|
|||||||
@@ -9,15 +9,15 @@ import (
|
|||||||
"github.com/yusing/godoxy/internal/common"
|
"github.com/yusing/godoxy/internal/common"
|
||||||
"github.com/yusing/godoxy/internal/config"
|
"github.com/yusing/godoxy/internal/config"
|
||||||
"github.com/yusing/godoxy/internal/dnsproviders"
|
"github.com/yusing/godoxy/internal/dnsproviders"
|
||||||
"github.com/yusing/godoxy/internal/gperr"
|
|
||||||
"github.com/yusing/godoxy/internal/homepage"
|
"github.com/yusing/godoxy/internal/homepage"
|
||||||
"github.com/yusing/godoxy/internal/logging"
|
"github.com/yusing/godoxy/internal/logging"
|
||||||
"github.com/yusing/godoxy/internal/logging/memlogger"
|
"github.com/yusing/godoxy/internal/logging/memlogger"
|
||||||
"github.com/yusing/godoxy/internal/metrics/systeminfo"
|
"github.com/yusing/godoxy/internal/metrics/systeminfo"
|
||||||
"github.com/yusing/godoxy/internal/metrics/uptime"
|
"github.com/yusing/godoxy/internal/metrics/uptime"
|
||||||
"github.com/yusing/godoxy/internal/net/gphttp/middleware"
|
"github.com/yusing/godoxy/internal/net/gphttp/middleware"
|
||||||
"github.com/yusing/godoxy/internal/task"
|
|
||||||
"github.com/yusing/godoxy/pkg"
|
"github.com/yusing/godoxy/pkg"
|
||||||
|
gperr "github.com/yusing/goutils/errs"
|
||||||
|
"github.com/yusing/goutils/task"
|
||||||
)
|
)
|
||||||
|
|
||||||
func parallel(fns ...func()) {
|
func parallel(fns ...func()) {
|
||||||
|
|||||||
2
go.mod
2
go.mod
@@ -50,7 +50,7 @@ require (
|
|||||||
github.com/yusing/godoxy/agent v0.0.0-20250926130035-55c1c918ba95
|
github.com/yusing/godoxy/agent v0.0.0-20250926130035-55c1c918ba95
|
||||||
github.com/yusing/godoxy/internal/dnsproviders v0.0.0-20250926130035-55c1c918ba95
|
github.com/yusing/godoxy/internal/dnsproviders v0.0.0-20250926130035-55c1c918ba95
|
||||||
github.com/yusing/godoxy/internal/utils v0.1.0
|
github.com/yusing/godoxy/internal/utils v0.1.0
|
||||||
github.com/yusing/goutils v0.2.1
|
github.com/yusing/goutils v0.3.1
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
|||||||
4
go.sum
4
go.sum
@@ -1648,8 +1648,8 @@ github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1
|
|||||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
github.com/yusing/ds v0.2.0 h1:lPhDU5eA2uvquVrBrzLCrQXRJJgSXlUYA53TbuK2sQY=
|
github.com/yusing/ds v0.2.0 h1:lPhDU5eA2uvquVrBrzLCrQXRJJgSXlUYA53TbuK2sQY=
|
||||||
github.com/yusing/ds v0.2.0/go.mod h1:XhKV4l7cZwBbbl7lRzNC9zX27zvCM0frIwiuD40ULRk=
|
github.com/yusing/ds v0.2.0/go.mod h1:XhKV4l7cZwBbbl7lRzNC9zX27zvCM0frIwiuD40ULRk=
|
||||||
github.com/yusing/goutils v0.2.1 h1:KjoCrNO0otthaPCZPfQY+5GKsqs5+J77CxP+TNHYa/Y=
|
github.com/yusing/goutils v0.3.1 h1:xCPoZ/haI8ZJ0ZaPU4g6+okSPdBczs8o98tIZ/TbpsQ=
|
||||||
github.com/yusing/goutils v0.2.1/go.mod h1:v6RZsMRdzcts4udSg0vqUIFvaD0OaUMPTwYJZ4XnQYo=
|
github.com/yusing/goutils v0.3.1/go.mod h1:meg9GcAU8yvBY21JgYjPuLsXD1Q5VdVHE32A4tG5Y5g=
|
||||||
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
||||||
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||||
github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
|
github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
|
||||||
|
|||||||
@@ -7,11 +7,11 @@ import (
|
|||||||
"github.com/puzpuzpuz/xsync/v4"
|
"github.com/puzpuzpuz/xsync/v4"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"github.com/yusing/godoxy/internal/common"
|
"github.com/yusing/godoxy/internal/common"
|
||||||
"github.com/yusing/godoxy/internal/gperr"
|
|
||||||
"github.com/yusing/godoxy/internal/logging/accesslog"
|
"github.com/yusing/godoxy/internal/logging/accesslog"
|
||||||
"github.com/yusing/godoxy/internal/maxmind"
|
"github.com/yusing/godoxy/internal/maxmind"
|
||||||
"github.com/yusing/godoxy/internal/task"
|
|
||||||
"github.com/yusing/godoxy/internal/utils"
|
"github.com/yusing/godoxy/internal/utils"
|
||||||
|
gperr "github.com/yusing/goutils/errs"
|
||||||
|
"github.com/yusing/goutils/task"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/yusing/godoxy/internal/gperr"
|
|
||||||
"github.com/yusing/godoxy/internal/maxmind"
|
"github.com/yusing/godoxy/internal/maxmind"
|
||||||
|
gperr "github.com/yusing/goutils/errs"
|
||||||
)
|
)
|
||||||
|
|
||||||
type MatcherFunc func(*maxmind.IPInfo) bool
|
type MatcherFunc func(*maxmind.IPInfo) bool
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import (
|
|||||||
routeApi "github.com/yusing/godoxy/internal/api/v1/route"
|
routeApi "github.com/yusing/godoxy/internal/api/v1/route"
|
||||||
"github.com/yusing/godoxy/internal/auth"
|
"github.com/yusing/godoxy/internal/auth"
|
||||||
"github.com/yusing/godoxy/internal/common"
|
"github.com/yusing/godoxy/internal/common"
|
||||||
"github.com/yusing/godoxy/internal/gperr"
|
gperr "github.com/yusing/goutils/errs"
|
||||||
)
|
)
|
||||||
|
|
||||||
// @title GoDoxy API
|
// @title GoDoxy API
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package apitypes
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
|
||||||
"github.com/yusing/godoxy/internal/gperr"
|
gperr "github.com/yusing/goutils/errs"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ErrorResponse struct {
|
type ErrorResponse struct {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/yusing/godoxy/agent/pkg/agent"
|
"github.com/yusing/godoxy/agent/pkg/agent"
|
||||||
apitypes "github.com/yusing/godoxy/internal/api/types"
|
apitypes "github.com/yusing/goutils/apitypes"
|
||||||
)
|
)
|
||||||
|
|
||||||
type NewAgentRequest struct {
|
type NewAgentRequest struct {
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ import (
|
|||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/yusing/godoxy/agent/pkg/agent"
|
"github.com/yusing/godoxy/agent/pkg/agent"
|
||||||
"github.com/yusing/godoxy/agent/pkg/certs"
|
"github.com/yusing/godoxy/agent/pkg/certs"
|
||||||
. "github.com/yusing/godoxy/internal/api/types"
|
|
||||||
config "github.com/yusing/godoxy/internal/config/types"
|
config "github.com/yusing/godoxy/internal/config/types"
|
||||||
|
apitypes "github.com/yusing/goutils/apitypes"
|
||||||
)
|
)
|
||||||
|
|
||||||
type VerifyNewAgentRequest struct {
|
type VerifyNewAgentRequest struct {
|
||||||
@@ -35,44 +35,44 @@ type VerifyNewAgentRequest struct {
|
|||||||
func Verify(c *gin.Context) {
|
func Verify(c *gin.Context) {
|
||||||
var request VerifyNewAgentRequest
|
var request VerifyNewAgentRequest
|
||||||
if err := c.ShouldBindJSON(&request); err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
filename, ok := certs.AgentCertsFilepath(request.Host)
|
filename, ok := certs.AgentCertsFilepath(request.Host)
|
||||||
if !ok {
|
if !ok {
|
||||||
c.JSON(http.StatusBadRequest, Error("invalid host", nil))
|
c.JSON(http.StatusBadRequest, apitypes.Error("invalid host", nil))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ca, err := fromEncryptedPEMPairResponse(request.CA)
|
ca, err := fromEncryptedPEMPairResponse(request.CA)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(http.StatusBadRequest, Error("invalid CA", err))
|
c.JSON(http.StatusBadRequest, apitypes.Error("invalid CA", err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
client, err := fromEncryptedPEMPairResponse(request.Client)
|
client, err := fromEncryptedPEMPairResponse(request.Client)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(http.StatusBadRequest, Error("invalid client", err))
|
c.JSON(http.StatusBadRequest, apitypes.Error("invalid client", err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
nRoutesAdded, err := config.GetInstance().VerifyNewAgent(request.Host, ca, client, request.ContainerRuntime)
|
nRoutesAdded, err := config.GetInstance().VerifyNewAgent(request.Host, ca, client, request.ContainerRuntime)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(http.StatusBadRequest, Error("invalid request", err))
|
c.JSON(http.StatusBadRequest, apitypes.Error("invalid request", err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
zip, err := certs.ZipCert(ca.Cert, client.Cert, client.Key)
|
zip, err := certs.ZipCert(ca.Cert, client.Cert, client.Key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Error(InternalServerError(err, "failed to zip certs"))
|
c.Error(apitypes.InternalServerError(err, "failed to zip certs"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := os.WriteFile(filename, zip, 0o600); err != nil {
|
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
|
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"
|
"github.com/rs/zerolog/log"
|
||||||
apitypes "github.com/yusing/godoxy/internal/api/types"
|
apitypes "github.com/yusing/godoxy/internal/api/types"
|
||||||
config "github.com/yusing/godoxy/internal/config/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/logging/memlogger"
|
||||||
"github.com/yusing/godoxy/internal/net/gphttp/websocket"
|
"github.com/yusing/godoxy/internal/net/gphttp/websocket"
|
||||||
|
gperr "github.com/yusing/goutils/errs"
|
||||||
)
|
)
|
||||||
|
|
||||||
// @x-id "renew"
|
// @x-id "renew"
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
|
|
||||||
"github.com/docker/docker/api/types/container"
|
"github.com/docker/docker/api/types/container"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/yusing/godoxy/internal/gperr"
|
gperr "github.com/yusing/goutils/errs"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ContainerState = container.ContainerState // @name ContainerState
|
type ContainerState = container.ContainerState // @name ContainerState
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
|
|
||||||
dockerSystem "github.com/docker/docker/api/types/system"
|
dockerSystem "github.com/docker/docker/api/types/system"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/yusing/godoxy/internal/gperr"
|
gperr "github.com/yusing/goutils/errs"
|
||||||
strutils "github.com/yusing/goutils/strings"
|
strutils "github.com/yusing/goutils/strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import (
|
|||||||
apitypes "github.com/yusing/godoxy/internal/api/types"
|
apitypes "github.com/yusing/godoxy/internal/api/types"
|
||||||
"github.com/yusing/godoxy/internal/docker"
|
"github.com/yusing/godoxy/internal/docker"
|
||||||
"github.com/yusing/godoxy/internal/net/gphttp/websocket"
|
"github.com/yusing/godoxy/internal/net/gphttp/websocket"
|
||||||
"github.com/yusing/godoxy/internal/task"
|
"github.com/yusing/goutils/task"
|
||||||
)
|
)
|
||||||
|
|
||||||
type LogsQueryParams struct {
|
type LogsQueryParams struct {
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ import (
|
|||||||
apitypes "github.com/yusing/godoxy/internal/api/types"
|
apitypes "github.com/yusing/godoxy/internal/api/types"
|
||||||
config "github.com/yusing/godoxy/internal/config/types"
|
config "github.com/yusing/godoxy/internal/config/types"
|
||||||
"github.com/yusing/godoxy/internal/docker"
|
"github.com/yusing/godoxy/internal/docker"
|
||||||
"github.com/yusing/godoxy/internal/gperr"
|
|
||||||
"github.com/yusing/godoxy/internal/net/gphttp/websocket"
|
"github.com/yusing/godoxy/internal/net/gphttp/websocket"
|
||||||
|
gperr "github.com/yusing/goutils/errs"
|
||||||
"github.com/yusing/goutils/http/httpheaders"
|
"github.com/yusing/goutils/http/httpheaders"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ import (
|
|||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
apitypes "github.com/yusing/godoxy/internal/api/types"
|
apitypes "github.com/yusing/godoxy/internal/api/types"
|
||||||
config "github.com/yusing/godoxy/internal/config/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/net/gphttp/middleware"
|
||||||
"github.com/yusing/godoxy/internal/route/provider"
|
"github.com/yusing/godoxy/internal/route/provider"
|
||||||
|
gperr "github.com/yusing/goutils/errs"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ValidateFileRequest struct {
|
type ValidateFileRequest struct {
|
||||||
|
|||||||
@@ -14,10 +14,10 @@ import (
|
|||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"github.com/yusing/godoxy/agent/pkg/agent"
|
"github.com/yusing/godoxy/agent/pkg/agent"
|
||||||
apitypes "github.com/yusing/godoxy/internal/api/types"
|
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/period"
|
||||||
"github.com/yusing/godoxy/internal/metrics/systeminfo"
|
"github.com/yusing/godoxy/internal/metrics/systeminfo"
|
||||||
"github.com/yusing/godoxy/internal/net/gphttp/websocket"
|
"github.com/yusing/godoxy/internal/net/gphttp/websocket"
|
||||||
|
gperr "github.com/yusing/goutils/errs"
|
||||||
"github.com/yusing/goutils/http/httpheaders"
|
"github.com/yusing/goutils/http/httpheaders"
|
||||||
"github.com/yusing/goutils/synk"
|
"github.com/yusing/goutils/synk"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -15,9 +15,9 @@ import (
|
|||||||
"github.com/coreos/go-oidc/v3/oidc"
|
"github.com/coreos/go-oidc/v3/oidc"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"github.com/yusing/godoxy/internal/common"
|
"github.com/yusing/godoxy/internal/common"
|
||||||
"github.com/yusing/godoxy/internal/gperr"
|
|
||||||
"github.com/yusing/godoxy/internal/net/gphttp"
|
"github.com/yusing/godoxy/internal/net/gphttp"
|
||||||
"github.com/yusing/godoxy/internal/utils"
|
"github.com/yusing/godoxy/internal/utils"
|
||||||
|
gperr "github.com/yusing/goutils/errs"
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
"golang.org/x/time/rate"
|
"golang.org/x/time/rate"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import (
|
|||||||
"github.com/yusing/godoxy/internal/common"
|
"github.com/yusing/godoxy/internal/common"
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
|
|
||||||
. "github.com/yusing/godoxy/internal/utils/testing"
|
expect "github.com/yusing/goutils/testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
// setupMockOIDC configures mock OIDC provider for testing.
|
// setupMockOIDC configures mock OIDC provider for testing.
|
||||||
@@ -35,7 +35,7 @@ func setupMockOIDC(t *testing.T) {
|
|||||||
},
|
},
|
||||||
Scopes: []string{oidc.ScopeOpenID, "profile", "email"},
|
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,
|
oidcProvider: provider,
|
||||||
oidcVerifier: provider.Verifier(&oidc.Config{
|
oidcVerifier: provider.Verifier(&oidc.Config{
|
||||||
ClientID: "test-client",
|
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 := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)
|
||||||
token.Header["kid"] = keyID
|
token.Header["kid"] = keyID
|
||||||
signed, err := token.SignedString(j.key)
|
signed, err := token.SignedString(j.key)
|
||||||
ExpectNoError(t, err)
|
expect.NoError(t, err)
|
||||||
return signed
|
return signed
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,7 +84,7 @@ func setupProvider(t *testing.T) *provider {
|
|||||||
|
|
||||||
// Generate an RSA key pair for the test.
|
// Generate an RSA key pair for the test.
|
||||||
privKey, err := rsa.GenerateKey(rand.Reader, 2048)
|
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.
|
// Build the matching public JWK that will be served by the endpoint.
|
||||||
jwk := buildRSAJWK(t, &privKey.PublicKey, keyID)
|
jwk := buildRSAJWK(t, &privKey.PublicKey, keyID)
|
||||||
@@ -227,12 +227,12 @@ func TestOIDCCallbackHandler(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if tt.wantStatus == http.StatusTemporaryRedirect {
|
if tt.wantStatus == http.StatusTemporaryRedirect {
|
||||||
setCookie := Must(http.ParseSetCookie(w.Header().Get("Set-Cookie")))
|
setCookie := expect.Must(http.ParseSetCookie(w.Header().Get("Set-Cookie")))
|
||||||
ExpectEqual(t, setCookie.Name, CookieOauthToken)
|
expect.Equal(t, setCookie.Name, CookieOauthToken)
|
||||||
ExpectTrue(t, setCookie.Value != "")
|
expect.True(t, setCookie.Value != "")
|
||||||
ExpectEqual(t, setCookie.Path, "/")
|
expect.Equal(t, setCookie.Path, "/")
|
||||||
ExpectEqual(t, setCookie.SameSite, http.SameSiteLaxMode)
|
expect.Equal(t, setCookie.SameSite, http.SameSiteLaxMode)
|
||||||
ExpectEqual(t, setCookie.HttpOnly, true)
|
expect.Equal(t, setCookie.HttpOnly, true)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -245,7 +245,7 @@ func TestInitOIDC(t *testing.T) {
|
|||||||
mux := http.NewServeMux()
|
mux := http.NewServeMux()
|
||||||
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set("Content-Type", "application/json")
|
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)
|
server = httptest.NewServer(mux)
|
||||||
t.Cleanup(server.Close)
|
t.Cleanup(server.Close)
|
||||||
@@ -445,9 +445,9 @@ func TestCheckToken(t *testing.T) {
|
|||||||
// Call CheckToken and verify the result.
|
// Call CheckToken and verify the result.
|
||||||
err := auth.CheckToken(req)
|
err := auth.CheckToken(req)
|
||||||
if tc.wantErr == nil {
|
if tc.wantErr == nil {
|
||||||
ExpectNoError(t, err)
|
expect.NoError(t, err)
|
||||||
} else {
|
} 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/golang-jwt/jwt/v5"
|
||||||
"github.com/yusing/godoxy/internal/common"
|
"github.com/yusing/godoxy/internal/common"
|
||||||
"github.com/yusing/godoxy/internal/gperr"
|
|
||||||
"github.com/yusing/godoxy/internal/net/gphttp"
|
"github.com/yusing/godoxy/internal/net/gphttp"
|
||||||
|
gperr "github.com/yusing/goutils/errs"
|
||||||
strutils "github.com/yusing/goutils/strings"
|
strutils "github.com/yusing/goutils/strings"
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -9,14 +9,14 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
. "github.com/yusing/godoxy/internal/utils/testing"
|
expect "github.com/yusing/goutils/testing"
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newMockUserPassAuth() *UserPassAuth {
|
func newMockUserPassAuth() *UserPassAuth {
|
||||||
return &UserPassAuth{
|
return &UserPassAuth{
|
||||||
username: "username",
|
username: "username",
|
||||||
pwdHash: Must(bcrypt.GenerateFromPassword([]byte("password"), bcrypt.DefaultCost)),
|
pwdHash: expect.Must(bcrypt.GenerateFromPassword([]byte("password"), bcrypt.DefaultCost)),
|
||||||
secret: []byte("abcdefghijklmnopqrstuvwxyz"),
|
secret: []byte("abcdefghijklmnopqrstuvwxyz"),
|
||||||
tokenTTL: time.Hour,
|
tokenTTL: time.Hour,
|
||||||
}
|
}
|
||||||
@@ -25,17 +25,17 @@ func newMockUserPassAuth() *UserPassAuth {
|
|||||||
func TestUserPassValidateCredentials(t *testing.T) {
|
func TestUserPassValidateCredentials(t *testing.T) {
|
||||||
auth := newMockUserPassAuth()
|
auth := newMockUserPassAuth()
|
||||||
err := auth.validatePassword("username", "password")
|
err := auth.validatePassword("username", "password")
|
||||||
ExpectNoError(t, err)
|
expect.NoError(t, err)
|
||||||
err = auth.validatePassword("username", "wrong-password")
|
err = auth.validatePassword("username", "wrong-password")
|
||||||
ExpectError(t, ErrInvalidPassword, err)
|
expect.ErrorIs(t, ErrInvalidPassword, err)
|
||||||
err = auth.validatePassword("wrong-username", "password")
|
err = auth.validatePassword("wrong-username", "password")
|
||||||
ExpectError(t, ErrInvalidUsername, err)
|
expect.ErrorIs(t, ErrInvalidUsername, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUserPassCheckToken(t *testing.T) {
|
func TestUserPassCheckToken(t *testing.T) {
|
||||||
auth := newMockUserPassAuth()
|
auth := newMockUserPassAuth()
|
||||||
token, err := auth.NewToken()
|
token, err := auth.NewToken()
|
||||||
ExpectNoError(t, err)
|
expect.NoError(t, err)
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
token string
|
token string
|
||||||
wantErr bool
|
wantErr bool
|
||||||
@@ -60,9 +60,9 @@ func TestUserPassCheckToken(t *testing.T) {
|
|||||||
}
|
}
|
||||||
err = auth.CheckToken(req)
|
err = auth.CheckToken(req)
|
||||||
if tt.wantErr {
|
if tt.wantErr {
|
||||||
ExpectTrue(t, err != nil)
|
expect.True(t, err != nil)
|
||||||
} else {
|
} else {
|
||||||
ExpectNoError(t, err)
|
expect.NoError(t, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -96,20 +96,20 @@ func TestUserPassLoginCallbackHandler(t *testing.T) {
|
|||||||
w := httptest.NewRecorder()
|
w := httptest.NewRecorder()
|
||||||
req := &http.Request{
|
req := &http.Request{
|
||||||
Host: "app.example.com",
|
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)
|
auth.PostAuthCallbackHandler(w, req)
|
||||||
if tt.wantErr {
|
if tt.wantErr {
|
||||||
ExpectEqual(t, w.Code, http.StatusUnauthorized)
|
expect.Equal(t, w.Code, http.StatusUnauthorized)
|
||||||
} else {
|
} else {
|
||||||
setCookie := Must(http.ParseSetCookie(w.Header().Get("Set-Cookie")))
|
setCookie := expect.Must(http.ParseSetCookie(w.Header().Get("Set-Cookie")))
|
||||||
ExpectTrue(t, setCookie.Name == auth.TokenCookieName())
|
expect.True(t, setCookie.Name == auth.TokenCookieName())
|
||||||
ExpectTrue(t, setCookie.Value != "")
|
expect.True(t, setCookie.Value != "")
|
||||||
ExpectEqual(t, setCookie.Domain, "example.com")
|
expect.Equal(t, setCookie.Domain, "example.com")
|
||||||
ExpectEqual(t, setCookie.Path, "/")
|
expect.Equal(t, setCookie.Path, "/")
|
||||||
ExpectEqual(t, setCookie.SameSite, http.SameSiteLaxMode)
|
expect.Equal(t, setCookie.SameSite, http.SameSiteLaxMode)
|
||||||
ExpectEqual(t, setCookie.HttpOnly, true)
|
expect.Equal(t, setCookie.HttpOnly, true)
|
||||||
ExpectEqual(t, w.Code, http.StatusOK)
|
expect.Equal(t, w.Code, http.StatusOK)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/yusing/godoxy/internal/common"
|
"github.com/yusing/godoxy/internal/common"
|
||||||
"github.com/yusing/godoxy/internal/gperr"
|
gperr "github.com/yusing/goutils/errs"
|
||||||
strutils "github.com/yusing/goutils/strings"
|
strutils "github.com/yusing/goutils/strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ import (
|
|||||||
"github.com/go-acme/lego/v4/lego"
|
"github.com/go-acme/lego/v4/lego"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"github.com/yusing/godoxy/internal/common"
|
"github.com/yusing/godoxy/internal/common"
|
||||||
"github.com/yusing/godoxy/internal/gperr"
|
|
||||||
"github.com/yusing/godoxy/internal/utils"
|
"github.com/yusing/godoxy/internal/utils"
|
||||||
|
gperr "github.com/yusing/goutils/errs"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
|
|||||||
@@ -18,10 +18,10 @@ import (
|
|||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"github.com/yusing/godoxy/internal/common"
|
"github.com/yusing/godoxy/internal/common"
|
||||||
"github.com/yusing/godoxy/internal/gperr"
|
|
||||||
"github.com/yusing/godoxy/internal/notif"
|
"github.com/yusing/godoxy/internal/notif"
|
||||||
"github.com/yusing/godoxy/internal/task"
|
gperr "github.com/yusing/goutils/errs"
|
||||||
strutils "github.com/yusing/goutils/strings"
|
strutils "github.com/yusing/goutils/strings"
|
||||||
|
"github.com/yusing/goutils/task"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ package autocert
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/go-acme/lego/v4/challenge"
|
"github.com/go-acme/lego/v4/challenge"
|
||||||
"github.com/yusing/godoxy/internal/gperr"
|
|
||||||
"github.com/yusing/godoxy/internal/serialization"
|
"github.com/yusing/godoxy/internal/serialization"
|
||||||
|
gperr "github.com/yusing/goutils/errs"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Generator func(map[string]any) (challenge.Provider, gperr.Error)
|
type Generator func(map[string]any) (challenge.Provider, gperr.Error)
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package autocert
|
|||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
|
||||||
"github.com/yusing/godoxy/internal/task"
|
"github.com/yusing/goutils/task"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Provider interface {
|
type Provider interface {
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ package config
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/yusing/godoxy/agent/pkg/agent"
|
"github.com/yusing/godoxy/agent/pkg/agent"
|
||||||
"github.com/yusing/godoxy/internal/gperr"
|
|
||||||
"github.com/yusing/godoxy/internal/route/provider"
|
"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) {
|
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"
|
"github.com/yusing/godoxy/internal/common"
|
||||||
config "github.com/yusing/godoxy/internal/config/types"
|
config "github.com/yusing/godoxy/internal/config/types"
|
||||||
"github.com/yusing/godoxy/internal/entrypoint"
|
"github.com/yusing/godoxy/internal/entrypoint"
|
||||||
"github.com/yusing/godoxy/internal/gperr"
|
|
||||||
"github.com/yusing/godoxy/internal/maxmind"
|
"github.com/yusing/godoxy/internal/maxmind"
|
||||||
"github.com/yusing/godoxy/internal/net/gphttp/server"
|
"github.com/yusing/godoxy/internal/net/gphttp/server"
|
||||||
"github.com/yusing/godoxy/internal/notif"
|
"github.com/yusing/godoxy/internal/notif"
|
||||||
"github.com/yusing/godoxy/internal/proxmox"
|
"github.com/yusing/godoxy/internal/proxmox"
|
||||||
proxy "github.com/yusing/godoxy/internal/route/provider"
|
proxy "github.com/yusing/godoxy/internal/route/provider"
|
||||||
"github.com/yusing/godoxy/internal/serialization"
|
"github.com/yusing/godoxy/internal/serialization"
|
||||||
"github.com/yusing/godoxy/internal/task"
|
|
||||||
"github.com/yusing/godoxy/internal/watcher"
|
"github.com/yusing/godoxy/internal/watcher"
|
||||||
"github.com/yusing/godoxy/internal/watcher/events"
|
"github.com/yusing/godoxy/internal/watcher/events"
|
||||||
|
gperr "github.com/yusing/goutils/errs"
|
||||||
"github.com/yusing/goutils/strings/ansi"
|
"github.com/yusing/goutils/strings/ansi"
|
||||||
|
"github.com/yusing/goutils/task"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
|
|||||||
@@ -9,13 +9,13 @@ import (
|
|||||||
"github.com/yusing/godoxy/agent/pkg/agent"
|
"github.com/yusing/godoxy/agent/pkg/agent"
|
||||||
"github.com/yusing/godoxy/internal/acl"
|
"github.com/yusing/godoxy/internal/acl"
|
||||||
"github.com/yusing/godoxy/internal/autocert"
|
"github.com/yusing/godoxy/internal/autocert"
|
||||||
"github.com/yusing/godoxy/internal/gperr"
|
|
||||||
"github.com/yusing/godoxy/internal/logging/accesslog"
|
"github.com/yusing/godoxy/internal/logging/accesslog"
|
||||||
maxmind "github.com/yusing/godoxy/internal/maxmind/types"
|
maxmind "github.com/yusing/godoxy/internal/maxmind/types"
|
||||||
"github.com/yusing/godoxy/internal/notif"
|
"github.com/yusing/godoxy/internal/notif"
|
||||||
"github.com/yusing/godoxy/internal/proxmox"
|
"github.com/yusing/godoxy/internal/proxmox"
|
||||||
"github.com/yusing/godoxy/internal/serialization"
|
"github.com/yusing/godoxy/internal/serialization"
|
||||||
"github.com/yusing/godoxy/internal/types"
|
"github.com/yusing/godoxy/internal/types"
|
||||||
|
gperr "github.com/yusing/goutils/errs"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
package dnsproviders
|
package dnsproviders
|
||||||
|
|
||||||
type DummyConfig struct{}
|
type (
|
||||||
type DummyProvider struct{}
|
DummyConfig struct{}
|
||||||
|
DummyProvider struct{}
|
||||||
|
)
|
||||||
|
|
||||||
func NewDummyDefaultConfig() *DummyConfig {
|
func NewDummyDefaultConfig() *DummyConfig {
|
||||||
return &DummyConfig{}
|
return &DummyConfig{}
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ require (
|
|||||||
github.com/vultr/govultr/v3 v3.24.0 // indirect
|
github.com/vultr/govultr/v3 v3.24.0 // indirect
|
||||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
|
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
|
||||||
github.com/yusing/godoxy/internal/utils v0.1.0 // 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/auto/sdk v1.2.1 // indirect
|
||||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 // indirect
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 // indirect
|
||||||
go.opentelemetry.io/otel v1.38.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.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.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
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.3.1 h1:xCPoZ/haI8ZJ0ZaPU4g6+okSPdBczs8o98tIZ/TbpsQ=
|
||||||
github.com/yusing/goutils v0.2.1/go.mod h1:v6RZsMRdzcts4udSg0vqUIFvaD0OaUMPTwYJZ4XnQYo=
|
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/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
|
||||||
github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA=
|
github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA=
|
||||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import (
|
|||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"github.com/yusing/godoxy/agent/pkg/agent"
|
"github.com/yusing/godoxy/agent/pkg/agent"
|
||||||
"github.com/yusing/godoxy/internal/common"
|
"github.com/yusing/godoxy/internal/common"
|
||||||
"github.com/yusing/godoxy/internal/task"
|
"github.com/yusing/goutils/task"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO: implement reconnect here.
|
// TODO: implement reconnect here.
|
||||||
|
|||||||
@@ -14,10 +14,10 @@ import (
|
|||||||
"github.com/docker/docker/api/types/container"
|
"github.com/docker/docker/api/types/container"
|
||||||
"github.com/docker/go-connections/nat"
|
"github.com/docker/go-connections/nat"
|
||||||
"github.com/yusing/godoxy/agent/pkg/agent"
|
"github.com/yusing/godoxy/agent/pkg/agent"
|
||||||
"github.com/yusing/godoxy/internal/gperr"
|
|
||||||
"github.com/yusing/godoxy/internal/serialization"
|
"github.com/yusing/godoxy/internal/serialization"
|
||||||
"github.com/yusing/godoxy/internal/types"
|
"github.com/yusing/godoxy/internal/types"
|
||||||
"github.com/yusing/godoxy/internal/utils"
|
"github.com/yusing/godoxy/internal/utils"
|
||||||
|
gperr "github.com/yusing/goutils/errs"
|
||||||
)
|
)
|
||||||
|
|
||||||
var DummyContainer = new(types.Container)
|
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 {
|
if res.PrivateHostname == "" && res.PublicHostname == "" && res.Running {
|
||||||
addError(res, ErrNoNetwork)
|
addError(res, ErrNoNetwork)
|
||||||
}
|
}
|
||||||
return
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
func IsBlacklisted(c *types.Container) bool {
|
func IsBlacklisted(c *types.Container) bool {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/docker/docker/api/types/container"
|
"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) {
|
func TestContainerExplicit(t *testing.T) {
|
||||||
@@ -37,7 +37,7 @@ func TestContainerExplicit(t *testing.T) {
|
|||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
c := FromDocker(&container.Summary{Names: []string{"test"}, State: "test", Labels: tt.labels}, "")
|
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 {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
c := FromDocker(tt.container, "")
|
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) {
|
t.Run(tt.full, func(t *testing.T) {
|
||||||
helper := containerHelper{&container.Summary{Image: tt.full}}
|
helper := containerHelper{&container.Summary{Image: tt.full}}
|
||||||
im := helper.parseImage()
|
im := helper.parseImage()
|
||||||
ExpectEqual(t, im.Author, tt.author)
|
expect.Equal(t, im.Author, tt.author)
|
||||||
ExpectEqual(t, im.Name, tt.image)
|
expect.Equal(t, im.Name, tt.image)
|
||||||
ExpectEqual(t, im.Tag, tt.tag)
|
expect.Equal(t, im.Tag, tt.tag)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/goccy/go-yaml"
|
"github.com/goccy/go-yaml"
|
||||||
"github.com/yusing/godoxy/internal/gperr"
|
|
||||||
"github.com/yusing/godoxy/internal/types"
|
"github.com/yusing/godoxy/internal/types"
|
||||||
|
gperr "github.com/yusing/goutils/errs"
|
||||||
strutils "github.com/yusing/goutils/strings"
|
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"
|
||||||
"github.com/yusing/godoxy/internal/net/gphttp/middleware/errorpage"
|
"github.com/yusing/godoxy/internal/net/gphttp/middleware/errorpage"
|
||||||
"github.com/yusing/godoxy/internal/route/routes"
|
"github.com/yusing/godoxy/internal/route/routes"
|
||||||
"github.com/yusing/godoxy/internal/task"
|
|
||||||
"github.com/yusing/godoxy/internal/types"
|
"github.com/yusing/godoxy/internal/types"
|
||||||
|
"github.com/yusing/goutils/task"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Entrypoint struct {
|
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) {
|
func (ep *Entrypoint) SetAccessLogger(parent task.Parent, cfg *accesslog.RequestLoggerConfig) (err error) {
|
||||||
if cfg == nil {
|
if cfg == nil {
|
||||||
ep.accessLogger = nil
|
ep.accessLogger = nil
|
||||||
return
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
ep.accessLogger, err = accesslog.NewAccessLogger(parent, cfg)
|
ep.accessLogger, err = accesslog.NewAccessLogger(parent, cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return err
|
||||||
}
|
}
|
||||||
log.Debug().Msg("entrypoint access logger created")
|
log.Debug().Msg("entrypoint access logger created")
|
||||||
return
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ep *Entrypoint) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
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"
|
||||||
"github.com/yusing/godoxy/internal/route/routes"
|
"github.com/yusing/godoxy/internal/route/routes"
|
||||||
"github.com/yusing/godoxy/internal/task"
|
|
||||||
"github.com/yusing/godoxy/internal/types"
|
"github.com/yusing/godoxy/internal/types"
|
||||||
|
"github.com/yusing/goutils/task"
|
||||||
)
|
)
|
||||||
|
|
||||||
type noopResponseWriter struct {
|
type noopResponseWriter struct {
|
||||||
@@ -24,10 +24,12 @@ type noopResponseWriter struct {
|
|||||||
func (w *noopResponseWriter) Header() http.Header {
|
func (w *noopResponseWriter) Header() http.Header {
|
||||||
return http.Header{}
|
return http.Header{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *noopResponseWriter) Write(b []byte) (int, error) {
|
func (w *noopResponseWriter) Write(b []byte) (int, error) {
|
||||||
w.written = b
|
w.written = b
|
||||||
return len(b), nil
|
return len(b), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *noopResponseWriter) WriteHeader(statusCode int) {
|
func (w *noopResponseWriter) WriteHeader(statusCode int) {
|
||||||
w.statusCode = statusCode
|
w.statusCode = statusCode
|
||||||
}
|
}
|
||||||
@@ -45,7 +47,7 @@ func (t noopTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
|||||||
|
|
||||||
func BenchmarkEntrypointReal(b *testing.B) {
|
func BenchmarkEntrypointReal(b *testing.B) {
|
||||||
var ep Entrypoint
|
var ep Entrypoint
|
||||||
var req = http.Request{
|
req := http.Request{
|
||||||
Method: "GET",
|
Method: "GET",
|
||||||
URL: &url.URL{Path: "/", RawPath: "/"},
|
URL: &url.URL{Path: "/", RawPath: "/"},
|
||||||
Host: "test.domain.tld",
|
Host: "test.domain.tld",
|
||||||
@@ -107,7 +109,7 @@ func BenchmarkEntrypointReal(b *testing.B) {
|
|||||||
|
|
||||||
func BenchmarkEntrypoint(b *testing.B) {
|
func BenchmarkEntrypoint(b *testing.B) {
|
||||||
var ep Entrypoint
|
var ep Entrypoint
|
||||||
var req = http.Request{
|
req := http.Request{
|
||||||
Method: "GET",
|
Method: "GET",
|
||||||
URL: &url.URL{Path: "/", RawPath: "/"},
|
URL: &url.URL{Path: "/", RawPath: "/"},
|
||||||
Host: "test.domain.tld",
|
Host: "test.domain.tld",
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
"github.com/yusing/godoxy/internal/route"
|
"github.com/yusing/godoxy/internal/route"
|
||||||
"github.com/yusing/godoxy/internal/route/routes"
|
"github.com/yusing/godoxy/internal/route/routes"
|
||||||
|
|
||||||
expect "github.com/yusing/godoxy/internal/utils/testing"
|
expect "github.com/yusing/goutils/testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ep = NewEntrypoint()
|
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"
|
"testing"
|
||||||
|
|
||||||
. "github.com/yusing/godoxy/internal/homepage"
|
. "github.com/yusing/godoxy/internal/homepage"
|
||||||
. "github.com/yusing/godoxy/internal/utils/testing"
|
|
||||||
|
expect "github.com/yusing/goutils/testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestOverrideItem(t *testing.T) {
|
func TestOverrideItem(t *testing.T) {
|
||||||
@@ -33,7 +34,7 @@ func TestOverrideItem(t *testing.T) {
|
|||||||
overrides.Initialize()
|
overrides.Initialize()
|
||||||
overrides.OverrideItem(a.Alias, want)
|
overrides.OverrideItem(a.Alias, want)
|
||||||
got := a.GetOverride()
|
got := a.GetOverride()
|
||||||
ExpectEqual(t, got, Item{
|
expect.Equal(t, got, Item{
|
||||||
ItemConfig: want,
|
ItemConfig: want,
|
||||||
Alias: a.Alias,
|
Alias: a.Alias,
|
||||||
})
|
})
|
||||||
@@ -58,8 +59,8 @@ func TestOverrideItem_PreservesURL(t *testing.T) {
|
|||||||
overrides.OverrideItem(a.Alias, wantCfg)
|
overrides.OverrideItem(a.Alias, wantCfg)
|
||||||
|
|
||||||
got := a.GetOverride()
|
got := a.GetOverride()
|
||||||
ExpectEqual(t, got.URL, "http://origin.local")
|
expect.Equal(t, got.URL, "http://origin.local")
|
||||||
ExpectEqual(t, got.Name, "Overridden")
|
expect.Equal(t, got.Name, "Overridden")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestVisibilityFavoriteAndSortOrders(t *testing.T) {
|
func TestVisibilityFavoriteAndSortOrders(t *testing.T) {
|
||||||
@@ -81,11 +82,11 @@ func TestVisibilityFavoriteAndSortOrders(t *testing.T) {
|
|||||||
overrides.SetFavSortOrder(a.Alias, 2)
|
overrides.SetFavSortOrder(a.Alias, 2)
|
||||||
|
|
||||||
got := a.GetOverride()
|
got := a.GetOverride()
|
||||||
ExpectEqual(t, got.Show, false)
|
expect.Equal(t, got.Show, false)
|
||||||
ExpectEqual(t, got.Favorite, true)
|
expect.Equal(t, got.Favorite, true)
|
||||||
ExpectEqual(t, got.SortOrder, 5)
|
expect.Equal(t, got.SortOrder, 5)
|
||||||
ExpectEqual(t, got.AllSortOrder, 9)
|
expect.Equal(t, got.AllSortOrder, 9)
|
||||||
ExpectEqual(t, got.FavSortOrder, 2)
|
expect.Equal(t, got.FavSortOrder, 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCategoryDefaultedWhenEmpty(t *testing.T) {
|
func TestCategoryDefaultedWhenEmpty(t *testing.T) {
|
||||||
@@ -97,7 +98,7 @@ func TestCategoryDefaultedWhenEmpty(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
got := a.GetOverride()
|
got := a.GetOverride()
|
||||||
ExpectEqual(t, got.Category, CategoryOthers)
|
expect.Equal(t, got.Category, CategoryOthers)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestOverrideItems_Bulk(t *testing.T) {
|
func TestOverrideItems_Bulk(t *testing.T) {
|
||||||
@@ -128,9 +129,9 @@ func TestOverrideItems_Bulk(t *testing.T) {
|
|||||||
ga := a.GetOverride()
|
ga := a.GetOverride()
|
||||||
gb := b.GetOverride()
|
gb := b.GetOverride()
|
||||||
|
|
||||||
ExpectEqual(t, ga.Name, "A*")
|
expect.Equal(t, ga.Name, "A*")
|
||||||
ExpectEqual(t, ga.Category, "AX")
|
expect.Equal(t, ga.Category, "AX")
|
||||||
ExpectEqual(t, gb.Name, "B*")
|
expect.Equal(t, gb.Name, "B*")
|
||||||
ExpectEqual(t, gb.Category, "BY")
|
expect.Equal(t, gb.Category, "BY")
|
||||||
ExpectEqual(t, gb.Show, false)
|
expect.Equal(t, gb.Show, false)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ import (
|
|||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"github.com/yusing/godoxy/internal/common"
|
"github.com/yusing/godoxy/internal/common"
|
||||||
"github.com/yusing/godoxy/internal/jsonstore"
|
"github.com/yusing/godoxy/internal/jsonstore"
|
||||||
"github.com/yusing/godoxy/internal/task"
|
|
||||||
"github.com/yusing/godoxy/internal/utils"
|
"github.com/yusing/godoxy/internal/utils"
|
||||||
"github.com/yusing/godoxy/internal/utils/atomic"
|
"github.com/yusing/godoxy/internal/utils/atomic"
|
||||||
|
"github.com/yusing/goutils/task"
|
||||||
)
|
)
|
||||||
|
|
||||||
type cacheEntry struct {
|
type cacheEntry struct {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/yusing/godoxy/internal/gperr"
|
gperr "github.com/yusing/goutils/errs"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
. "github.com/yusing/godoxy/internal/homepage"
|
. "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 {
|
func strPtr(s string) *string {
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
"github.com/yusing/godoxy/internal/gperr"
|
|
||||||
"github.com/yusing/godoxy/internal/homepage/widgets"
|
"github.com/yusing/godoxy/internal/homepage/widgets"
|
||||||
|
gperr "github.com/yusing/goutils/errs"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Client struct {
|
type Client struct {
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ import (
|
|||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"github.com/yusing/godoxy/internal/common"
|
"github.com/yusing/godoxy/internal/common"
|
||||||
"github.com/yusing/godoxy/internal/serialization"
|
"github.com/yusing/godoxy/internal/serialization"
|
||||||
"github.com/yusing/godoxy/internal/task"
|
|
||||||
strutils "github.com/yusing/goutils/strings"
|
strutils "github.com/yusing/goutils/strings"
|
||||||
|
"github.com/yusing/goutils/task"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/yusing/godoxy/internal/gperr"
|
gperr "github.com/yusing/goutils/errs"
|
||||||
)
|
)
|
||||||
|
|
||||||
var HTTPClient = &http.Client{
|
var HTTPClient = &http.Client{
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ package widgets
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/yusing/godoxy/internal/gperr"
|
|
||||||
"github.com/yusing/godoxy/internal/serialization"
|
"github.com/yusing/godoxy/internal/serialization"
|
||||||
|
gperr "github.com/yusing/goutils/errs"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
|
|||||||
@@ -3,10 +3,10 @@ package idlewatcher
|
|||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/yusing/godoxy/internal/gperr"
|
|
||||||
idlewatcher "github.com/yusing/godoxy/internal/idlewatcher/types"
|
idlewatcher "github.com/yusing/godoxy/internal/idlewatcher/types"
|
||||||
"github.com/yusing/godoxy/internal/task"
|
|
||||||
"github.com/yusing/godoxy/internal/types"
|
"github.com/yusing/godoxy/internal/types"
|
||||||
|
gperr "github.com/yusing/goutils/errs"
|
||||||
|
"github.com/yusing/goutils/task"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Start implements health.HealthMonitor.
|
// Start implements health.HealthMonitor.
|
||||||
|
|||||||
@@ -5,10 +5,10 @@ import (
|
|||||||
|
|
||||||
"github.com/docker/docker/api/types/container"
|
"github.com/docker/docker/api/types/container"
|
||||||
"github.com/yusing/godoxy/internal/docker"
|
"github.com/yusing/godoxy/internal/docker"
|
||||||
"github.com/yusing/godoxy/internal/gperr"
|
|
||||||
idlewatcher "github.com/yusing/godoxy/internal/idlewatcher/types"
|
idlewatcher "github.com/yusing/godoxy/internal/idlewatcher/types"
|
||||||
"github.com/yusing/godoxy/internal/types"
|
"github.com/yusing/godoxy/internal/types"
|
||||||
"github.com/yusing/godoxy/internal/watcher"
|
"github.com/yusing/godoxy/internal/watcher"
|
||||||
|
gperr "github.com/yusing/goutils/errs"
|
||||||
)
|
)
|
||||||
|
|
||||||
type DockerProvider struct {
|
type DockerProvider struct {
|
||||||
|
|||||||
@@ -5,12 +5,12 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/yusing/godoxy/internal/gperr"
|
|
||||||
idlewatcher "github.com/yusing/godoxy/internal/idlewatcher/types"
|
idlewatcher "github.com/yusing/godoxy/internal/idlewatcher/types"
|
||||||
"github.com/yusing/godoxy/internal/proxmox"
|
"github.com/yusing/godoxy/internal/proxmox"
|
||||||
"github.com/yusing/godoxy/internal/types"
|
"github.com/yusing/godoxy/internal/types"
|
||||||
"github.com/yusing/godoxy/internal/watcher"
|
"github.com/yusing/godoxy/internal/watcher"
|
||||||
"github.com/yusing/godoxy/internal/watcher/events"
|
"github.com/yusing/godoxy/internal/watcher/events"
|
||||||
|
gperr "github.com/yusing/goutils/errs"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ProxmoxProvider struct {
|
type ProxmoxProvider struct {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package idlewatcher
|
package idlewatcher
|
||||||
|
|
||||||
import "github.com/yusing/godoxy/internal/gperr"
|
import gperr "github.com/yusing/goutils/errs"
|
||||||
|
|
||||||
type ContainerStatus string
|
type ContainerStatus string
|
||||||
|
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ package idlewatcher
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/yusing/godoxy/internal/gperr"
|
|
||||||
"github.com/yusing/godoxy/internal/types"
|
"github.com/yusing/godoxy/internal/types"
|
||||||
"github.com/yusing/godoxy/internal/watcher/events"
|
"github.com/yusing/godoxy/internal/watcher/events"
|
||||||
|
gperr "github.com/yusing/goutils/errs"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Provider interface {
|
type Provider interface {
|
||||||
|
|||||||
@@ -12,18 +12,18 @@ import (
|
|||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"github.com/yusing/ds/ordered"
|
"github.com/yusing/ds/ordered"
|
||||||
"github.com/yusing/godoxy/internal/docker"
|
"github.com/yusing/godoxy/internal/docker"
|
||||||
"github.com/yusing/godoxy/internal/gperr"
|
|
||||||
"github.com/yusing/godoxy/internal/idlewatcher/provider"
|
"github.com/yusing/godoxy/internal/idlewatcher/provider"
|
||||||
idlewatcher "github.com/yusing/godoxy/internal/idlewatcher/types"
|
idlewatcher "github.com/yusing/godoxy/internal/idlewatcher/types"
|
||||||
nettypes "github.com/yusing/godoxy/internal/net/types"
|
nettypes "github.com/yusing/godoxy/internal/net/types"
|
||||||
"github.com/yusing/godoxy/internal/route/routes"
|
"github.com/yusing/godoxy/internal/route/routes"
|
||||||
"github.com/yusing/godoxy/internal/task"
|
|
||||||
"github.com/yusing/godoxy/internal/types"
|
"github.com/yusing/godoxy/internal/types"
|
||||||
U "github.com/yusing/godoxy/internal/utils"
|
U "github.com/yusing/godoxy/internal/utils"
|
||||||
"github.com/yusing/godoxy/internal/utils/atomic"
|
"github.com/yusing/godoxy/internal/utils/atomic"
|
||||||
"github.com/yusing/godoxy/internal/watcher/events"
|
"github.com/yusing/godoxy/internal/watcher/events"
|
||||||
"github.com/yusing/godoxy/internal/watcher/health/monitor"
|
"github.com/yusing/godoxy/internal/watcher/health/monitor"
|
||||||
|
gperr "github.com/yusing/goutils/errs"
|
||||||
"github.com/yusing/goutils/http/reverseproxy"
|
"github.com/yusing/goutils/http/reverseproxy"
|
||||||
|
"github.com/yusing/goutils/task"
|
||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
"golang.org/x/sync/singleflight"
|
"golang.org/x/sync/singleflight"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -10,9 +10,9 @@ import (
|
|||||||
"github.com/puzpuzpuz/xsync/v4"
|
"github.com/puzpuzpuz/xsync/v4"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"github.com/yusing/godoxy/internal/common"
|
"github.com/yusing/godoxy/internal/common"
|
||||||
"github.com/yusing/godoxy/internal/gperr"
|
|
||||||
"github.com/yusing/godoxy/internal/serialization"
|
"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
|
type namespace string
|
||||||
|
|||||||
@@ -9,12 +9,12 @@ import (
|
|||||||
|
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"github.com/yusing/godoxy/internal/gperr"
|
|
||||||
maxmind "github.com/yusing/godoxy/internal/maxmind/types"
|
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"
|
ioutils "github.com/yusing/goutils/io"
|
||||||
strutils "github.com/yusing/goutils/strings"
|
strutils "github.com/yusing/goutils/strings"
|
||||||
"github.com/yusing/goutils/synk"
|
"github.com/yusing/goutils/synk"
|
||||||
|
"github.com/yusing/goutils/task"
|
||||||
"golang.org/x/time/rate"
|
"golang.org/x/time/rate"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
. "github.com/yusing/godoxy/internal/logging/accesslog"
|
. "github.com/yusing/godoxy/internal/logging/accesslog"
|
||||||
"github.com/yusing/godoxy/internal/task"
|
|
||||||
"github.com/yusing/godoxy/internal/utils"
|
"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 (
|
const (
|
||||||
|
|||||||
@@ -9,9 +9,10 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/spf13/afero"
|
"github.com/spf13/afero"
|
||||||
"github.com/yusing/godoxy/internal/task"
|
expect "github.com/yusing/goutils/testing"
|
||||||
expect "github.com/yusing/godoxy/internal/utils/testing"
|
|
||||||
strutils "github.com/yusing/goutils/strings"
|
strutils "github.com/yusing/goutils/strings"
|
||||||
|
"github.com/yusing/goutils/task"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestBackScanner(t *testing.T) {
|
func TestBackScanner(t *testing.T) {
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ package accesslog
|
|||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/yusing/godoxy/internal/gperr"
|
|
||||||
"github.com/yusing/godoxy/internal/serialization"
|
"github.com/yusing/godoxy/internal/serialization"
|
||||||
|
gperr "github.com/yusing/goutils/errs"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
"github.com/yusing/godoxy/internal/docker"
|
"github.com/yusing/godoxy/internal/docker"
|
||||||
. "github.com/yusing/godoxy/internal/logging/accesslog"
|
. "github.com/yusing/godoxy/internal/logging/accesslog"
|
||||||
"github.com/yusing/godoxy/internal/serialization"
|
"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) {
|
func TestNewConfig(t *testing.T) {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
. "github.com/yusing/godoxy/internal/logging/accesslog"
|
. "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,
|
// Cookie header should be removed,
|
||||||
|
|||||||
@@ -6,9 +6,8 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
expect "github.com/yusing/godoxy/internal/utils/testing"
|
"github.com/yusing/goutils/task"
|
||||||
|
expect "github.com/yusing/goutils/testing"
|
||||||
"github.com/yusing/godoxy/internal/task"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestConcurrentFileLoggersShareSameAccessLogIO(t *testing.T) {
|
func TestConcurrentFileLoggersShareSameAccessLogIO(t *testing.T) {
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/yusing/godoxy/internal/gperr"
|
|
||||||
nettypes "github.com/yusing/godoxy/internal/net/types"
|
nettypes "github.com/yusing/godoxy/internal/net/types"
|
||||||
|
gperr "github.com/yusing/goutils/errs"
|
||||||
strutils "github.com/yusing/goutils/strings"
|
strutils "github.com/yusing/goutils/strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ import (
|
|||||||
|
|
||||||
. "github.com/yusing/godoxy/internal/logging/accesslog"
|
. "github.com/yusing/godoxy/internal/logging/accesslog"
|
||||||
nettypes "github.com/yusing/godoxy/internal/net/types"
|
nettypes "github.com/yusing/godoxy/internal/net/types"
|
||||||
expect "github.com/yusing/godoxy/internal/utils/testing"
|
|
||||||
strutils "github.com/yusing/goutils/strings"
|
strutils "github.com/yusing/goutils/strings"
|
||||||
|
expect "github.com/yusing/goutils/testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestStatusCodeFilter(t *testing.T) {
|
func TestStatusCodeFilter(t *testing.T) {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/yusing/godoxy/internal/gperr"
|
gperr "github.com/yusing/goutils/errs"
|
||||||
strutils "github.com/yusing/goutils/strings"
|
strutils "github.com/yusing/goutils/strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -70,7 +70,7 @@ func (r *Retention) Parse(v string) (err error) {
|
|||||||
if !r.IsValid() {
|
if !r.IsValid() {
|
||||||
return ErrZeroValue
|
return ErrZeroValue
|
||||||
}
|
}
|
||||||
return
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Retention) String() string {
|
func (r *Retention) String() string {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
. "github.com/yusing/godoxy/internal/logging/accesslog"
|
. "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) {
|
func TestParseRetention(t *testing.T) {
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
"github.com/yusing/godoxy/internal/gperr"
|
|
||||||
"github.com/yusing/godoxy/internal/utils"
|
"github.com/yusing/godoxy/internal/utils"
|
||||||
|
gperr "github.com/yusing/goutils/errs"
|
||||||
strutils "github.com/yusing/goutils/strings"
|
strutils "github.com/yusing/goutils/strings"
|
||||||
"github.com/yusing/goutils/synk"
|
"github.com/yusing/goutils/synk"
|
||||||
)
|
)
|
||||||
@@ -251,14 +251,14 @@ func rotateLogFileBySize(file supportRotate, config *Retention) (result *RotateR
|
|||||||
// otherwise it returns zero time.
|
// otherwise it returns zero time.
|
||||||
func ParseLogTime(line []byte) (t time.Time) {
|
func ParseLogTime(line []byte) (t time.Time) {
|
||||||
if len(line) == 0 {
|
if len(line) == 0 {
|
||||||
return
|
return t
|
||||||
}
|
}
|
||||||
|
|
||||||
if timeStr := ExtractTime(line); timeStr != nil {
|
if timeStr := ExtractTime(line); timeStr != nil {
|
||||||
t, _ = time.Parse(LogTimeFormat, string(timeStr)) // ignore error
|
t, _ = time.Parse(LogTimeFormat, string(timeStr)) // ignore error
|
||||||
return
|
return t
|
||||||
}
|
}
|
||||||
return
|
return t
|
||||||
}
|
}
|
||||||
|
|
||||||
var timeJSON = []byte(`"time":"`)
|
var timeJSON = []byte(`"time":"`)
|
||||||
@@ -272,8 +272,8 @@ func ExtractTime(line []byte) []byte {
|
|||||||
switch line[0] {
|
switch line[0] {
|
||||||
case '{': // JSON format
|
case '{': // JSON format
|
||||||
if i := bytes.Index(line, timeJSON); i != -1 {
|
if i := bytes.Index(line, timeJSON); i != -1 {
|
||||||
var jsonStart = i + len(`"time":"`)
|
jsonStart := i + len(`"time":"`)
|
||||||
var jsonEnd = i + len(`"time":"`) + len(LogTimeFormat)
|
jsonEnd := i + len(`"time":"`) + len(LogTimeFormat)
|
||||||
if len(line) < jsonEnd {
|
if len(line) < jsonEnd {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,10 +7,10 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
. "github.com/yusing/godoxy/internal/logging/accesslog"
|
. "github.com/yusing/godoxy/internal/logging/accesslog"
|
||||||
"github.com/yusing/godoxy/internal/task"
|
|
||||||
"github.com/yusing/godoxy/internal/utils"
|
"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"
|
strutils "github.com/yusing/goutils/strings"
|
||||||
|
"github.com/yusing/goutils/task"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package accesslog
|
|||||||
import (
|
import (
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/yusing/godoxy/internal/gperr"
|
gperr "github.com/yusing/goutils/errs"
|
||||||
strutils "github.com/yusing/goutils/strings"
|
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)
|
pos, err := m.writeBuf(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// not logging the error here, it will cause Run to be called again = infinite loop
|
// not logging the error here, it will cause Run to be called again = infinite loop
|
||||||
return
|
return n, err
|
||||||
}
|
}
|
||||||
|
|
||||||
m.notifyWS(pos, n)
|
m.notifyWS(pos, n)
|
||||||
return
|
return n, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *memLogger) ServeHTTP(c *gin.Context) {
|
func (m *memLogger) ServeHTTP(c *gin.Context) {
|
||||||
@@ -149,7 +149,7 @@ func (m *memLogger) writeBuf(b []byte) (pos int, err error) {
|
|||||||
defer m.Unlock()
|
defer m.Unlock()
|
||||||
pos = m.Len()
|
pos = m.Len()
|
||||||
_, err = m.Buffer.Write(b)
|
_, err = m.Buffer.Write(b)
|
||||||
return
|
return pos, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *memLogger) events() (logs <-chan []byte, cancel func()) {
|
func (m *memLogger) events() (logs <-chan []byte, cancel func()) {
|
||||||
|
|||||||
@@ -5,9 +5,9 @@ import (
|
|||||||
|
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"github.com/yusing/godoxy/internal/gperr"
|
|
||||||
"github.com/yusing/godoxy/internal/notif"
|
"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
|
var instance *MaxMind
|
||||||
|
|||||||
@@ -15,9 +15,9 @@ import (
|
|||||||
|
|
||||||
"github.com/oschwald/maxminddb-golang"
|
"github.com/oschwald/maxminddb-golang"
|
||||||
"github.com/yusing/godoxy/internal/common"
|
"github.com/yusing/godoxy/internal/common"
|
||||||
"github.com/yusing/godoxy/internal/gperr"
|
|
||||||
maxmind "github.com/yusing/godoxy/internal/maxmind/types"
|
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"
|
"github.com/oschwald/maxminddb-golang"
|
||||||
maxmind "github.com/yusing/godoxy/internal/maxmind/types"
|
maxmind "github.com/yusing/godoxy/internal/maxmind/types"
|
||||||
"github.com/yusing/godoxy/internal/task"
|
"github.com/yusing/goutils/task"
|
||||||
)
|
)
|
||||||
|
|
||||||
func testCfg() *MaxMind {
|
func testCfg() *MaxMind {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package maxmind
|
|||||||
import (
|
import (
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"github.com/yusing/godoxy/internal/gperr"
|
gperr "github.com/yusing/goutils/errs"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
|
|||||||
@@ -11,9 +11,9 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"github.com/yusing/godoxy/internal/gperr"
|
|
||||||
"github.com/yusing/godoxy/internal/task"
|
|
||||||
"github.com/yusing/godoxy/internal/utils/atomic"
|
"github.com/yusing/godoxy/internal/utils/atomic"
|
||||||
|
gperr "github.com/yusing/goutils/errs"
|
||||||
|
"github.com/yusing/goutils/task"
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ import (
|
|||||||
"github.com/shirou/gopsutil/v4/sensors"
|
"github.com/shirou/gopsutil/v4/sensors"
|
||||||
"github.com/shirou/gopsutil/v4/warning"
|
"github.com/shirou/gopsutil/v4/warning"
|
||||||
"github.com/yusing/godoxy/internal/common"
|
"github.com/yusing/godoxy/internal/common"
|
||||||
"github.com/yusing/godoxy/internal/gperr"
|
|
||||||
"github.com/yusing/godoxy/internal/metrics/period"
|
"github.com/yusing/godoxy/internal/metrics/period"
|
||||||
|
gperr "github.com/yusing/goutils/errs"
|
||||||
)
|
)
|
||||||
|
|
||||||
// json tags are left for tests
|
// json tags are left for tests
|
||||||
|
|||||||
@@ -10,96 +10,98 @@ import (
|
|||||||
"github.com/shirou/gopsutil/v4/mem"
|
"github.com/shirou/gopsutil/v4/mem"
|
||||||
"github.com/shirou/gopsutil/v4/net"
|
"github.com/shirou/gopsutil/v4/net"
|
||||||
"github.com/shirou/gopsutil/v4/sensors"
|
"github.com/shirou/gopsutil/v4/sensors"
|
||||||
. "github.com/yusing/godoxy/internal/utils/testing"
|
expect "github.com/yusing/goutils/testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Create test data
|
// Create test data
|
||||||
var cpuAvg = 45.67
|
var (
|
||||||
var testInfo = &SystemInfo{
|
cpuAvg = 45.67
|
||||||
Timestamp: 123456,
|
testInfo = &SystemInfo{
|
||||||
CPUAverage: &cpuAvg,
|
Timestamp: 123456,
|
||||||
Memory: &mem.VirtualMemoryStat{
|
CPUAverage: &cpuAvg,
|
||||||
Total: 16000000000,
|
Memory: &mem.VirtualMemoryStat{
|
||||||
Available: 8000000000,
|
Total: 16000000000,
|
||||||
Used: 8000000000,
|
Available: 8000000000,
|
||||||
UsedPercent: 50.0,
|
Used: 8000000000,
|
||||||
},
|
|
||||||
Disks: map[string]*disk.UsageStat{
|
|
||||||
"sda": {
|
|
||||||
Path: "/",
|
|
||||||
Fstype: "ext4",
|
|
||||||
Total: 500000000000,
|
|
||||||
Free: 250000000000,
|
|
||||||
Used: 250000000000,
|
|
||||||
UsedPercent: 50.0,
|
UsedPercent: 50.0,
|
||||||
},
|
},
|
||||||
"nvme0n1": {
|
Disks: map[string]*disk.UsageStat{
|
||||||
Path: "/",
|
"sda": {
|
||||||
Fstype: "zfs",
|
Path: "/",
|
||||||
Total: 500000000000,
|
Fstype: "ext4",
|
||||||
Free: 250000000000,
|
Total: 500000000000,
|
||||||
Used: 250000000000,
|
Free: 250000000000,
|
||||||
UsedPercent: 50.0,
|
Used: 250000000000,
|
||||||
|
UsedPercent: 50.0,
|
||||||
|
},
|
||||||
|
"nvme0n1": {
|
||||||
|
Path: "/",
|
||||||
|
Fstype: "zfs",
|
||||||
|
Total: 500000000000,
|
||||||
|
Free: 250000000000,
|
||||||
|
Used: 250000000000,
|
||||||
|
UsedPercent: 50.0,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
DisksIO: map[string]*disk.IOCountersStat{
|
||||||
DisksIO: map[string]*disk.IOCountersStat{
|
"media": {
|
||||||
"media": {
|
Name: "media",
|
||||||
Name: "media",
|
ReadBytes: 1000000,
|
||||||
ReadBytes: 1000000,
|
WriteBytes: 2000000,
|
||||||
WriteBytes: 2000000,
|
ReadSpeed: 100.5,
|
||||||
ReadSpeed: 100.5,
|
WriteSpeed: 200.5,
|
||||||
WriteSpeed: 200.5,
|
Iops: 1000,
|
||||||
Iops: 1000,
|
},
|
||||||
|
"nvme0n1": {
|
||||||
|
Name: "nvme0n1",
|
||||||
|
ReadBytes: 1000000,
|
||||||
|
WriteBytes: 2000000,
|
||||||
|
ReadSpeed: 100.5,
|
||||||
|
WriteSpeed: 200.5,
|
||||||
|
Iops: 1000,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
"nvme0n1": {
|
Network: &net.IOCountersStat{
|
||||||
Name: "nvme0n1",
|
BytesSent: 5000000,
|
||||||
ReadBytes: 1000000,
|
BytesRecv: 10000000,
|
||||||
WriteBytes: 2000000,
|
UploadSpeed: 1024.5,
|
||||||
ReadSpeed: 100.5,
|
DownloadSpeed: 2048.5,
|
||||||
WriteSpeed: 200.5,
|
|
||||||
Iops: 1000,
|
|
||||||
},
|
},
|
||||||
},
|
Sensors: []sensors.TemperatureStat{
|
||||||
Network: &net.IOCountersStat{
|
{
|
||||||
BytesSent: 5000000,
|
SensorKey: "cpu_temp",
|
||||||
BytesRecv: 10000000,
|
Temperature: 30.0,
|
||||||
UploadSpeed: 1024.5,
|
High: 40.0,
|
||||||
DownloadSpeed: 2048.5,
|
Critical: 50.0,
|
||||||
},
|
},
|
||||||
Sensors: []sensors.TemperatureStat{
|
{
|
||||||
{
|
SensorKey: "gpu_temp",
|
||||||
SensorKey: "cpu_temp",
|
Temperature: 40.0,
|
||||||
Temperature: 30.0,
|
High: 50.0,
|
||||||
High: 40.0,
|
Critical: 60.0,
|
||||||
Critical: 50.0,
|
},
|
||||||
},
|
},
|
||||||
{
|
}
|
||||||
SensorKey: "gpu_temp",
|
)
|
||||||
Temperature: 40.0,
|
|
||||||
High: 50.0,
|
|
||||||
Critical: 60.0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSystemInfo(t *testing.T) {
|
func TestSystemInfo(t *testing.T) {
|
||||||
// Test marshaling
|
// Test marshaling
|
||||||
data, err := json.Marshal(testInfo)
|
data, err := json.Marshal(testInfo)
|
||||||
ExpectNoError(t, err)
|
expect.NoError(t, err)
|
||||||
|
|
||||||
// Test unmarshaling back
|
// Test unmarshaling back
|
||||||
var decoded SystemInfo
|
var decoded SystemInfo
|
||||||
err = json.Unmarshal(data, &decoded)
|
err = json.Unmarshal(data, &decoded)
|
||||||
ExpectNoError(t, err)
|
expect.NoError(t, err)
|
||||||
|
|
||||||
// Compare original and decoded
|
// Compare original and decoded
|
||||||
ExpectEqual(t, decoded.Timestamp, testInfo.Timestamp)
|
expect.Equal(t, decoded.Timestamp, testInfo.Timestamp)
|
||||||
ExpectEqual(t, *decoded.CPUAverage, *testInfo.CPUAverage)
|
expect.Equal(t, *decoded.CPUAverage, *testInfo.CPUAverage)
|
||||||
ExpectEqual(t, decoded.Memory, testInfo.Memory)
|
expect.Equal(t, decoded.Memory, testInfo.Memory)
|
||||||
ExpectEqual(t, decoded.Disks, testInfo.Disks)
|
expect.Equal(t, decoded.Disks, testInfo.Disks)
|
||||||
ExpectEqual(t, decoded.DisksIO, testInfo.DisksIO)
|
expect.Equal(t, decoded.DisksIO, testInfo.DisksIO)
|
||||||
ExpectEqual(t, decoded.Network, testInfo.Network)
|
expect.Equal(t, decoded.Network, testInfo.Network)
|
||||||
ExpectEqual(t, decoded.Sensors, testInfo.Sensors)
|
expect.Equal(t, decoded.Sensors, testInfo.Sensors)
|
||||||
|
|
||||||
// Test nil fields
|
// Test nil fields
|
||||||
nilInfo := &SystemInfo{
|
nilInfo := &SystemInfo{
|
||||||
@@ -107,18 +109,18 @@ func TestSystemInfo(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
data, err = json.Marshal(nilInfo)
|
data, err = json.Marshal(nilInfo)
|
||||||
ExpectNoError(t, err)
|
expect.NoError(t, err)
|
||||||
|
|
||||||
var decodedNil SystemInfo
|
var decodedNil SystemInfo
|
||||||
err = json.Unmarshal(data, &decodedNil)
|
err = json.Unmarshal(data, &decodedNil)
|
||||||
ExpectNoError(t, err)
|
expect.NoError(t, err)
|
||||||
|
|
||||||
ExpectEqual(t, decodedNil.Timestamp, nilInfo.Timestamp)
|
expect.Equal(t, decodedNil.Timestamp, nilInfo.Timestamp)
|
||||||
ExpectTrue(t, decodedNil.CPUAverage == nil)
|
expect.True(t, decodedNil.CPUAverage == nil)
|
||||||
ExpectTrue(t, decodedNil.Memory == nil)
|
expect.True(t, decodedNil.Memory == nil)
|
||||||
ExpectTrue(t, decodedNil.Disks == nil)
|
expect.True(t, decodedNil.Disks == nil)
|
||||||
ExpectTrue(t, decodedNil.Network == nil)
|
expect.True(t, decodedNil.Network == nil)
|
||||||
ExpectTrue(t, decodedNil.Sensors == nil)
|
expect.True(t, decodedNil.Sensors == nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSerialize(t *testing.T) {
|
func TestSerialize(t *testing.T) {
|
||||||
@@ -130,15 +132,15 @@ func TestSerialize(t *testing.T) {
|
|||||||
t.Run(string(query), func(t *testing.T) {
|
t.Run(string(query), func(t *testing.T) {
|
||||||
_, result := aggregate(entries, url.Values{"aggregate": []string{string(query)}})
|
_, result := aggregate(entries, url.Values{"aggregate": []string{string(query)}})
|
||||||
s, err := result.MarshalJSON()
|
s, err := result.MarshalJSON()
|
||||||
ExpectNoError(t, err)
|
expect.NoError(t, err)
|
||||||
var v []map[string]any
|
var v []map[string]any
|
||||||
ExpectNoError(t, json.Unmarshal(s, &v))
|
expect.NoError(t, json.Unmarshal(s, &v))
|
||||||
ExpectEqual(t, len(v), len(result.Entries))
|
expect.Equal(t, len(v), len(result.Entries))
|
||||||
for i, m := range v {
|
for i, m := range v {
|
||||||
for k, v := range m {
|
for k, v := range m {
|
||||||
// some int64 values are converted to float64 on json.Unmarshal
|
// some int64 values are converted to float64 on json.Unmarshal
|
||||||
vv := reflect.ValueOf(result.Entries[i][k])
|
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"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"net/url"
|
"net/url"
|
||||||
"time"
|
|
||||||
|
|
||||||
"slices"
|
"slices"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/lithammer/fuzzysearch/fuzzy"
|
"github.com/lithammer/fuzzysearch/fuzzy"
|
||||||
config "github.com/yusing/godoxy/internal/config/types"
|
config "github.com/yusing/godoxy/internal/config/types"
|
||||||
|
|||||||
@@ -4,38 +4,38 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
. "github.com/yusing/godoxy/internal/utils/testing"
|
expect "github.com/yusing/goutils/testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestContentTypes(t *testing.T) {
|
func TestContentTypes(t *testing.T) {
|
||||||
ExpectTrue(t, GetContentType(http.Header{"Content-Type": {"text/html"}}).IsHTML())
|
expect.True(t, GetContentType(http.Header{"Content-Type": {"text/html"}}).IsHTML())
|
||||||
ExpectTrue(t, GetContentType(http.Header{"Content-Type": {"text/html; charset=utf-8"}}).IsHTML())
|
expect.True(t, GetContentType(http.Header{"Content-Type": {"text/html; charset=utf-8"}}).IsHTML())
|
||||||
ExpectTrue(t, GetContentType(http.Header{"Content-Type": {"application/xhtml+xml"}}).IsHTML())
|
expect.True(t, GetContentType(http.Header{"Content-Type": {"application/xhtml+xml"}}).IsHTML())
|
||||||
ExpectFalse(t, GetContentType(http.Header{"Content-Type": {"text/plain"}}).IsHTML())
|
expect.False(t, GetContentType(http.Header{"Content-Type": {"text/plain"}}).IsHTML())
|
||||||
|
|
||||||
ExpectTrue(t, GetContentType(http.Header{"Content-Type": {"application/json"}}).IsJSON())
|
expect.True(t, GetContentType(http.Header{"Content-Type": {"application/json"}}).IsJSON())
|
||||||
ExpectTrue(t, GetContentType(http.Header{"Content-Type": {"application/json; charset=utf-8"}}).IsJSON())
|
expect.True(t, GetContentType(http.Header{"Content-Type": {"application/json; charset=utf-8"}}).IsJSON())
|
||||||
ExpectFalse(t, GetContentType(http.Header{"Content-Type": {"text/html"}}).IsJSON())
|
expect.False(t, GetContentType(http.Header{"Content-Type": {"text/html"}}).IsJSON())
|
||||||
|
|
||||||
ExpectTrue(t, GetContentType(http.Header{"Content-Type": {"text/plain"}}).IsPlainText())
|
expect.True(t, GetContentType(http.Header{"Content-Type": {"text/plain"}}).IsPlainText())
|
||||||
ExpectTrue(t, GetContentType(http.Header{"Content-Type": {"text/plain; charset=utf-8"}}).IsPlainText())
|
expect.True(t, GetContentType(http.Header{"Content-Type": {"text/plain; charset=utf-8"}}).IsPlainText())
|
||||||
ExpectFalse(t, GetContentType(http.Header{"Content-Type": {"text/html"}}).IsPlainText())
|
expect.False(t, GetContentType(http.Header{"Content-Type": {"text/html"}}).IsPlainText())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAcceptContentTypes(t *testing.T) {
|
func TestAcceptContentTypes(t *testing.T) {
|
||||||
ExpectTrue(t, GetAccept(http.Header{"Accept": {"text/html", "text/plain"}}).AcceptPlainText())
|
expect.True(t, GetAccept(http.Header{"Accept": {"text/html", "text/plain"}}).AcceptPlainText())
|
||||||
ExpectTrue(t, GetAccept(http.Header{"Accept": {"text/html", "text/plain; charset=utf-8"}}).AcceptPlainText())
|
expect.True(t, GetAccept(http.Header{"Accept": {"text/html", "text/plain; charset=utf-8"}}).AcceptPlainText())
|
||||||
ExpectTrue(t, GetAccept(http.Header{"Accept": {"text/html", "text/plain"}}).AcceptHTML())
|
expect.True(t, GetAccept(http.Header{"Accept": {"text/html", "text/plain"}}).AcceptHTML())
|
||||||
ExpectTrue(t, GetAccept(http.Header{"Accept": {"application/json"}}).AcceptJSON())
|
expect.True(t, GetAccept(http.Header{"Accept": {"application/json"}}).AcceptJSON())
|
||||||
ExpectTrue(t, GetAccept(http.Header{"Accept": {"*/*"}}).AcceptPlainText())
|
expect.True(t, GetAccept(http.Header{"Accept": {"*/*"}}).AcceptPlainText())
|
||||||
ExpectTrue(t, GetAccept(http.Header{"Accept": {"*/*"}}).AcceptHTML())
|
expect.True(t, GetAccept(http.Header{"Accept": {"*/*"}}).AcceptHTML())
|
||||||
ExpectTrue(t, GetAccept(http.Header{"Accept": {"*/*"}}).AcceptJSON())
|
expect.True(t, GetAccept(http.Header{"Accept": {"*/*"}}).AcceptJSON())
|
||||||
ExpectTrue(t, GetAccept(http.Header{"Accept": {"text/*"}}).AcceptPlainText())
|
expect.True(t, GetAccept(http.Header{"Accept": {"text/*"}}).AcceptPlainText())
|
||||||
ExpectTrue(t, GetAccept(http.Header{"Accept": {"text/*"}}).AcceptHTML())
|
expect.True(t, GetAccept(http.Header{"Accept": {"text/*"}}).AcceptHTML())
|
||||||
|
|
||||||
ExpectFalse(t, GetAccept(http.Header{"Accept": {"text/plain"}}).AcceptHTML())
|
expect.False(t, GetAccept(http.Header{"Accept": {"text/plain"}}).AcceptHTML())
|
||||||
ExpectFalse(t, GetAccept(http.Header{"Accept": {"text/plain; charset=utf-8"}}).AcceptHTML())
|
expect.False(t, GetAccept(http.Header{"Accept": {"text/plain; charset=utf-8"}}).AcceptHTML())
|
||||||
ExpectFalse(t, GetAccept(http.Header{"Accept": {"text/html"}}).AcceptPlainText())
|
expect.False(t, GetAccept(http.Header{"Accept": {"text/html"}}).AcceptPlainText())
|
||||||
ExpectFalse(t, GetAccept(http.Header{"Accept": {"text/html"}}).AcceptJSON())
|
expect.False(t, GetAccept(http.Header{"Accept": {"text/html"}}).AcceptJSON())
|
||||||
ExpectFalse(t, GetAccept(http.Header{"Accept": {"text/*"}}).AcceptJSON())
|
expect.False(t, GetAccept(http.Header{"Accept": {"text/*"}}).AcceptJSON())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/yusing/godoxy/internal/gperr"
|
|
||||||
"github.com/yusing/godoxy/internal/net/gphttp/middleware"
|
"github.com/yusing/godoxy/internal/net/gphttp/middleware"
|
||||||
"github.com/yusing/godoxy/internal/types"
|
"github.com/yusing/godoxy/internal/types"
|
||||||
|
gperr "github.com/yusing/goutils/errs"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ipHash struct {
|
type ipHash struct {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user