mirror of
https://github.com/yusing/godoxy.git
synced 2025-05-20 04:42:33 +02:00
Merge remote-tracking branch 'origin/main' into feat/rootless
This commit is contained in:
commit
4e19c0992a
21 changed files with 248 additions and 460 deletions
13
agent/go.mod
13
agent/go.mod
|
@ -9,7 +9,7 @@ require (
|
|||
github.com/docker/docker v28.1.1+incompatible
|
||||
github.com/rs/zerolog v1.34.0
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/yusing/go-proxy v0.11.1
|
||||
github.com/yusing/go-proxy v0.11.5
|
||||
)
|
||||
|
||||
replace github.com/docker/docker => github.com/godoxy-app/docker v0.0.0-20250418000134-7af8fd7b079e
|
||||
|
@ -18,12 +18,15 @@ require (
|
|||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
github.com/PuerkitoBio/goquery v1.10.3 // indirect
|
||||
github.com/andybalholm/cascadia v1.3.3 // indirect
|
||||
github.com/buger/goterm v1.0.4 // indirect
|
||||
github.com/bytedance/sonic v1.13.2 // indirect
|
||||
github.com/bytedance/sonic/loader v0.2.4 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||
github.com/cloudwego/base64x v0.1.5 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/diskfs/go-diskfs v1.6.0 // indirect
|
||||
github.com/distribution/reference v0.6.0 // indirect
|
||||
github.com/djherbis/times v1.6.0 // indirect
|
||||
github.com/docker/cli v28.1.1+incompatible // indirect
|
||||
github.com/docker/go-connections v0.5.0 // indirect
|
||||
github.com/docker/go-units v0.5.0 // indirect
|
||||
|
@ -40,11 +43,15 @@ require (
|
|||
github.com/goccy/go-yaml v1.17.1 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/google/pprof v0.0.0-20250423184734-337e5dd93bb4 // indirect
|
||||
github.com/gotify/server/v2 v2.6.1 // indirect
|
||||
github.com/gorilla/websocket v1.5.3 // indirect
|
||||
github.com/gotify/server/v2 v2.6.3 // indirect
|
||||
github.com/jinzhu/copier v0.4.0 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.10 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/lithammer/fuzzysearch v1.1.8 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35 // indirect
|
||||
github.com/luthermonson/go-proxmox v0.2.2 // indirect
|
||||
github.com/magefile/mage v1.15.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/miekg/dns v1.1.65 // indirect
|
||||
|
@ -59,7 +66,7 @@ require (
|
|||
github.com/puzpuzpuz/xsync/v3 v3.5.1 // indirect
|
||||
github.com/quic-go/qpack v0.5.1 // indirect
|
||||
github.com/quic-go/quic-go v0.51.0 // indirect
|
||||
github.com/samber/lo v1.49.1 // indirect
|
||||
github.com/samber/lo v1.50.0 // indirect
|
||||
github.com/samber/slog-common v0.18.1 // indirect
|
||||
github.com/samber/slog-zerolog/v2 v2.7.3 // indirect
|
||||
github.com/shirou/gopsutil/v4 v4.25.3 // indirect
|
||||
|
|
26
agent/go.sum
26
agent/go.sum
|
@ -43,6 +43,8 @@ github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4
|
|||
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I=
|
||||
github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
||||
github.com/elliotwutingfeng/asciiset v0.0.0-20230602022725-51bbb787efab h1:h1UgjJdAAhj+uPL68n7XASS6bU+07ZX1WJvVS2eyoeY=
|
||||
github.com/elliotwutingfeng/asciiset v0.0.0-20230602022725-51bbb787efab/go.mod h1:GLo/8fDswSAniFG+BFIaiSPcK610jyzgEhWYPQwuQdw=
|
||||
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
||||
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||
github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY=
|
||||
|
@ -68,6 +70,8 @@ github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc
|
|||
github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||
github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
|
||||
github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
|
||||
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
||||
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||
github.com/goccy/go-yaml v1.17.1 h1:LI34wktB2xEE3ONG/2Ar54+/HJVBriAGJ55PHls4YuY=
|
||||
|
@ -88,14 +92,20 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
|||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gotify/server/v2 v2.6.1 h1:Kf7v5fzBxzELzZa/jonWfwJMkqYqh1LBzBpCmt5QIAI=
|
||||
github.com/gotify/server/v2 v2.6.1/go.mod h1:Dk8HLyTVDqmXM8YEg6tjROBen6mxyHZFRggJFHTwZLc=
|
||||
github.com/gotify/server/v2 v2.6.3 h1:2sLDRsQ/No1+hcFwFDvjNtwKepfCSIR8L3BkXl/Vz1I=
|
||||
github.com/gotify/server/v2 v2.6.3/go.mod h1:IyeQ/iL3vetcuqUAzkCMVObIMGGJx4zb13/mVatIwE8=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 h1:e9Rjr40Z98/clHv5Yg79Is0NtosR5LXRvdr7o/6NwbA=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1/go.mod h1:tIxuGz/9mpox++sgp9fJjHO0+q1X9/UOWd798aAm22M=
|
||||
github.com/h2non/gock v1.2.0 h1:K6ol8rfrRkUOefooBC8elXoaNGYkpp7y2qcxGG6BzUE=
|
||||
github.com/h2non/gock v1.2.0/go.mod h1:tNhoxHYW2W42cYkYb1WqzdbYIieALC99kpYr7rH/BQk=
|
||||
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
|
||||
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
|
||||
github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8=
|
||||
github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
|
||||
github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||
|
@ -143,8 +153,12 @@ github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJw
|
|||
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
|
||||
github.com/oschwald/maxminddb-golang v1.13.1 h1:G3wwjdN9JmIK2o/ermkHM+98oX5fS+k5MbwsmL4MRQE=
|
||||
github.com/oschwald/maxminddb-golang v1.13.1/go.mod h1:K4pgV9N/GcK694KSTmVSDTODk4IsCNThNdTmnaBZ/F8=
|
||||
github.com/pierrec/lz4/v4 v4.1.17 h1:kV4Ip+/hUBC+8T6+2EgburRtkE9ef4nbY3f4dFhGjMc=
|
||||
github.com/pierrec/lz4/v4 v4.1.17/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/xattr v0.4.9 h1:5883YPCtkSd8LFbs13nXplj9g9tlrwoJRjgpgMu1/fE=
|
||||
github.com/pkg/xattr v0.4.9/go.mod h1:di8WF84zAKk8jzR1UBTEWh9AUlIZZ7M/JNt8e9B6ktU=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
|
@ -161,8 +175,8 @@ github.com/quic-go/quic-go v0.51.0/go.mod h1:MFlGGpcpJqRAfmYi6NC2cptDPSxRWTOGNuP
|
|||
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
|
||||
github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY=
|
||||
github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ=
|
||||
github.com/samber/lo v1.49.1 h1:4BIFyVfuQSEpluc7Fua+j1NolZHiEHEpaSEKdsH0tew=
|
||||
github.com/samber/lo v1.49.1/go.mod h1:dO6KHFzUKXgP8LDhU0oI8d2hekjXnGOu0DB8Jecxd6o=
|
||||
github.com/samber/lo v1.50.0 h1:XrG0xOeHs+4FQ8gJR97zDz5uOFMW7OwFWiFVzqopKgY=
|
||||
github.com/samber/lo v1.50.0/go.mod h1:RjZyNk6WSnUFRKK6EyOhsRJMqft3G+pg7dCWHQCWvsc=
|
||||
github.com/samber/slog-common v0.18.1 h1:c0EipD/nVY9HG5shgm/XAs67mgpWDMF+MmtptdJNCkQ=
|
||||
github.com/samber/slog-common v0.18.1/go.mod h1:QNZiNGKakvrfbJ2YglQXLCZauzkI9xZBjOhWFKS3IKk=
|
||||
github.com/samber/slog-zerolog/v2 v2.7.3 h1:/MkPDl/tJhijN2GvB1MWwBn2FU8RiL3rQ8gpXkQm2EY=
|
||||
|
@ -188,6 +202,8 @@ github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfj
|
|||
github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8=
|
||||
github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/vincent-petithory/dataurl v1.0.0 h1:cXw+kPto8NLuJtlMsI152irrVw9fRDX8AbShPRpg2CI=
|
||||
github.com/vincent-petithory/dataurl v1.0.0/go.mod h1:FHafX5vmDzyP+1CQATJn7WFKc9CvnvxyvZy6I1MrG/U=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
|
@ -271,8 +287,10 @@ golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210331175145-43e1dd70ce54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
|
|
|
@ -6,13 +6,13 @@ import (
|
|||
"os"
|
||||
"sync"
|
||||
|
||||
"github.com/yusing/go-proxy/internal"
|
||||
"github.com/yusing/go-proxy/internal/api/v1/query"
|
||||
"github.com/yusing/go-proxy/internal/auth"
|
||||
"github.com/yusing/go-proxy/internal/common"
|
||||
"github.com/yusing/go-proxy/internal/config"
|
||||
"github.com/yusing/go-proxy/internal/dnsproviders"
|
||||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
"github.com/yusing/go-proxy/internal/homepage"
|
||||
"github.com/yusing/go-proxy/internal/logging"
|
||||
"github.com/yusing/go-proxy/internal/logging/memlogger"
|
||||
"github.com/yusing/go-proxy/internal/metrics/systeminfo"
|
||||
|
@ -50,7 +50,7 @@ func main() {
|
|||
rawLogger.Println("ok")
|
||||
return
|
||||
case common.CommandListIcons:
|
||||
icons, err := internal.ListAvailableIcons()
|
||||
icons, err := homepage.ListAvailableIcons()
|
||||
if err != nil {
|
||||
rawLogger.Fatal(err)
|
||||
}
|
||||
|
@ -79,7 +79,7 @@ func main() {
|
|||
logging.Info().Msgf("GoDoxy version %s", pkg.GetVersion())
|
||||
logging.Trace().Msg("trace enabled")
|
||||
parallel(
|
||||
internal.InitIconListCache,
|
||||
homepage.InitIconListCache,
|
||||
systeminfo.Poller.Start,
|
||||
)
|
||||
|
||||
|
|
14
go.mod
14
go.mod
|
@ -15,7 +15,7 @@ require (
|
|||
github.com/go-acme/lego/v4 v4.23.1 // acme client
|
||||
github.com/go-playground/validator/v10 v10.26.0 // validator
|
||||
github.com/gobwas/glob v0.2.3 // glob matcher for route rules
|
||||
github.com/gotify/server/v2 v2.6.1 // reference the Message struct for json response
|
||||
github.com/gotify/server/v2 v2.6.3 // reference the Message struct for json response
|
||||
github.com/lithammer/fuzzysearch v1.1.8 // fuzzy search for searching icons and filtering metrics
|
||||
github.com/puzpuzpuz/xsync/v3 v3.5.1 // lock free map for concurrent operations
|
||||
github.com/rs/zerolog v1.34.0 // logging
|
||||
|
@ -41,13 +41,13 @@ require (
|
|||
github.com/samber/slog-zerolog/v2 v2.7.3
|
||||
github.com/spf13/afero v1.14.0
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/yusing/go-proxy/agent v0.0.0-00010101000000-000000000000
|
||||
github.com/yusing/go-proxy/internal/dnsproviders v0.0.0-00010101000000-000000000000
|
||||
github.com/yusing/go-proxy/agent v0.0.0-20250428032249-8da63daf0202
|
||||
github.com/yusing/go-proxy/internal/dnsproviders v0.0.0-20250428032249-8da63daf0202
|
||||
go.uber.org/atomic v1.11.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.5.0 // indirect
|
||||
)
|
||||
|
@ -131,7 +131,7 @@ require (
|
|||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
|
||||
github.com/hashicorp/go-uuid v1.0.3 // indirect
|
||||
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.146 // indirect
|
||||
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.147 // indirect
|
||||
github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df // indirect
|
||||
github.com/infobloxopen/infoblox-go-client/v2 v2.10.0 // indirect
|
||||
github.com/jinzhu/copier v0.4.0 // indirect
|
||||
|
@ -191,7 +191,7 @@ require (
|
|||
github.com/sacloud/iaas-api-go v1.14.0 // indirect
|
||||
github.com/sacloud/packages-go v0.0.11 // indirect
|
||||
github.com/sagikazarmark/locafero v0.9.0 // indirect
|
||||
github.com/samber/lo v1.49.1 // indirect
|
||||
github.com/samber/lo v1.50.0 // indirect
|
||||
github.com/samber/slog-common v0.18.1 // indirect
|
||||
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.33 // indirect
|
||||
github.com/selectel/domains-go v1.1.0 // indirect
|
||||
|
@ -207,7 +207,7 @@ require (
|
|||
github.com/spf13/pflag v1.0.6 // indirect
|
||||
github.com/spf13/viper v1.20.1 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1151 // indirect
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1154 // indirect
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1136 // indirect
|
||||
github.com/tjfoc/gmsm v1.4.1 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.15 // indirect
|
||||
|
|
19
go.sum
19
go.sum
|
@ -1082,16 +1082,19 @@ github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/z
|
|||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gotify/server/v2 v2.6.1 h1:Kf7v5fzBxzELzZa/jonWfwJMkqYqh1LBzBpCmt5QIAI=
|
||||
github.com/gotify/server/v2 v2.6.1/go.mod h1:Dk8HLyTVDqmXM8YEg6tjROBen6mxyHZFRggJFHTwZLc=
|
||||
github.com/gotify/server/v2 v2.6.3 h1:2sLDRsQ/No1+hcFwFDvjNtwKepfCSIR8L3BkXl/Vz1I=
|
||||
github.com/gotify/server/v2 v2.6.3/go.mod h1:IyeQ/iL3vetcuqUAzkCMVObIMGGJx4zb13/mVatIwE8=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 h1:e9Rjr40Z98/clHv5Yg79Is0NtosR5LXRvdr7o/6NwbA=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1/go.mod h1:tIxuGz/9mpox++sgp9fJjHO0+q1X9/UOWd798aAm22M=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI=
|
||||
github.com/h2non/gock v1.2.0 h1:K6ol8rfrRkUOefooBC8elXoaNGYkpp7y2qcxGG6BzUE=
|
||||
github.com/h2non/gock v1.2.0/go.mod h1:tNhoxHYW2W42cYkYb1WqzdbYIieALC99kpYr7rH/BQk=
|
||||
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
|
||||
|
@ -1147,8 +1150,8 @@ github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/J
|
|||
github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk=
|
||||
github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.146 h1:ld5s5UeA9zgyFsZskVD2Tr6k6VnJWkvaLm5nqvfOEf4=
|
||||
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.146/go.mod h1:Y/+YLCFCJtS29i2MbYPTUlNNfwXvkzEsZKR0imY/2aY=
|
||||
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.147 h1:ip9+1n9+THhYgChlQpgDLVDVTv4LVJ7AoyPBJBaX2MY=
|
||||
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.147/go.mod h1:Y/+YLCFCJtS29i2MbYPTUlNNfwXvkzEsZKR0imY/2aY=
|
||||
github.com/hudl/fargo v1.4.0/go.mod h1:9Ai6uvFy5fQNq6VPKtg+Ceq1+eTY4nKUlR2JElEOcDo=
|
||||
github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
|
@ -1517,8 +1520,8 @@ github.com/sacloud/packages-go v0.0.11/go.mod h1:XNF5MCTWcHo9NiqWnYctVbASSSZR3ZO
|
|||
github.com/sagikazarmark/crypt v0.10.0/go.mod h1:gwTNHQVoOS3xp9Xvz5LLR+1AauC5M6880z5NWzdhOyQ=
|
||||
github.com/sagikazarmark/locafero v0.9.0 h1:GbgQGNtTrEmddYDSAH9QLRyfAHY12md+8YFTqyMTC9k=
|
||||
github.com/sagikazarmark/locafero v0.9.0/go.mod h1:UBUyz37V+EdMS3hDF3QWIiVr/2dPrx49OMO0Bn0hJqk=
|
||||
github.com/samber/lo v1.49.1 h1:4BIFyVfuQSEpluc7Fua+j1NolZHiEHEpaSEKdsH0tew=
|
||||
github.com/samber/lo v1.49.1/go.mod h1:dO6KHFzUKXgP8LDhU0oI8d2hekjXnGOu0DB8Jecxd6o=
|
||||
github.com/samber/lo v1.50.0 h1:XrG0xOeHs+4FQ8gJR97zDz5uOFMW7OwFWiFVzqopKgY=
|
||||
github.com/samber/lo v1.50.0/go.mod h1:RjZyNk6WSnUFRKK6EyOhsRJMqft3G+pg7dCWHQCWvsc=
|
||||
github.com/samber/slog-common v0.18.1 h1:c0EipD/nVY9HG5shgm/XAs67mgpWDMF+MmtptdJNCkQ=
|
||||
github.com/samber/slog-common v0.18.1/go.mod h1:QNZiNGKakvrfbJ2YglQXLCZauzkI9xZBjOhWFKS3IKk=
|
||||
github.com/samber/slog-zerolog/v2 v2.7.3 h1:/MkPDl/tJhijN2GvB1MWwBn2FU8RiL3rQ8gpXkQm2EY=
|
||||
|
@ -1615,8 +1618,8 @@ github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNG
|
|||
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1136/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1151 h1:SBbEaeCwhqmyAEEF5ubpg/2vv3RO6SdBsOSYhpnJaL4=
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1151/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1154 h1:tc2GXLGwpjaZdapd7pEpUjoeWU5gl3XUuZzDEyes7fg=
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1154/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1136 h1:kMIdSU5IvpOROh27ToVQ3hlm6ym3lCRs9tnGCOBoZqk=
|
||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1136/go.mod h1:FpyIz3mymKaExVs6Fz27kxDBS42jqZn7vbACtxdeEH4=
|
||||
github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho=
|
||||
|
|
|
@ -6,9 +6,9 @@ import (
|
|||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/yusing/go-proxy/internal"
|
||||
"github.com/yusing/go-proxy/internal/common"
|
||||
config "github.com/yusing/go-proxy/internal/config/types"
|
||||
"github.com/yusing/go-proxy/internal/homepage"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/middleware"
|
||||
"github.com/yusing/go-proxy/internal/route/routes"
|
||||
|
@ -67,7 +67,7 @@ func List(cfg config.ConfigInstance, w http.ResponseWriter, r *http.Request) {
|
|||
if err != nil {
|
||||
limit = 0
|
||||
}
|
||||
icons, err := internal.SearchIcons(r.FormValue("keyword"), limit)
|
||||
icons, err := homepage.SearchIcons(r.FormValue("keyword"), limit)
|
||||
if err != nil {
|
||||
gphttp.ClientError(w, err)
|
||||
return
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/yusing/go-proxy/internal/common"
|
||||
|
@ -38,17 +39,35 @@ func IsOIDCEnabled() bool {
|
|||
return common.OIDCIssuerURL != ""
|
||||
}
|
||||
|
||||
type nextHandler struct{}
|
||||
|
||||
var nextHandlerContextKey = nextHandler{}
|
||||
|
||||
func RequireAuth(next http.HandlerFunc) http.HandlerFunc {
|
||||
if IsEnabled() {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
if err := defaultAuth.CheckToken(r); err != nil {
|
||||
gphttp.ClientError(w, err, http.StatusUnauthorized)
|
||||
} else {
|
||||
next(w, r)
|
||||
}
|
||||
}
|
||||
if !IsEnabled() {
|
||||
return next
|
||||
}
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
if err := defaultAuth.CheckToken(r); err != nil {
|
||||
if IsFrontend(r) {
|
||||
r = r.WithContext(context.WithValue(r.Context(), nextHandlerContextKey, next))
|
||||
defaultAuth.LoginHandler(w, r)
|
||||
} else {
|
||||
gphttp.ClientError(w, err, http.StatusUnauthorized)
|
||||
}
|
||||
return
|
||||
}
|
||||
next(w, r)
|
||||
}
|
||||
}
|
||||
|
||||
func ProceedNext(w http.ResponseWriter, r *http.Request) {
|
||||
next, ok := r.Context().Value(nextHandlerContextKey).(http.HandlerFunc)
|
||||
if ok {
|
||||
next(w, r)
|
||||
} else {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
return next
|
||||
}
|
||||
|
||||
func AuthCheckHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
|
@ -19,6 +21,10 @@ type oauthRefreshToken struct {
|
|||
Username string `json:"username"`
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
Expiry time.Time `json:"expiry"`
|
||||
|
||||
result *refreshResult
|
||||
err error
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
type Session struct {
|
||||
|
@ -27,6 +33,12 @@ type Session struct {
|
|||
Groups []string `json:"groups"`
|
||||
}
|
||||
|
||||
type refreshResult struct {
|
||||
newSession Session
|
||||
jwt string
|
||||
jwtExpiry time.Time
|
||||
}
|
||||
|
||||
type sessionClaims struct {
|
||||
Session
|
||||
jwt.RegisteredClaims
|
||||
|
@ -34,11 +46,12 @@ type sessionClaims struct {
|
|||
|
||||
type sessionID string
|
||||
|
||||
var oauthRefreshTokens jsonstore.MapStore[oauthRefreshToken]
|
||||
var oauthRefreshTokens jsonstore.MapStore[*oauthRefreshToken]
|
||||
|
||||
var (
|
||||
defaultRefreshTokenExpiry = 30 * 24 * time.Hour // 1 month
|
||||
refreshBefore = 30 * time.Second
|
||||
sessionInvalidateDelay = 3 * time.Second
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -50,7 +63,7 @@ const sessionTokenIssuer = "GoDoxy"
|
|||
|
||||
func init() {
|
||||
if IsOIDCEnabled() {
|
||||
oauthRefreshTokens = jsonstore.Store[oauthRefreshToken]("oauth_refresh_tokens")
|
||||
oauthRefreshTokens = jsonstore.Store[*oauthRefreshToken]("oauth_refresh_tokens")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -61,7 +74,7 @@ func (token *oauthRefreshToken) expired() bool {
|
|||
func newSessionID() sessionID {
|
||||
b := make([]byte, 32)
|
||||
_, _ = rand.Read(b)
|
||||
return sessionID(base64.StdEncoding.EncodeToString(b))
|
||||
return sessionID(hex.EncodeToString(b))
|
||||
}
|
||||
|
||||
func newSession(username string, groups []string) Session {
|
||||
|
@ -72,26 +85,28 @@ func newSession(username string, groups []string) Session {
|
|||
}
|
||||
}
|
||||
|
||||
// getOnceOAuthRefreshToken returns the refresh token for the given session.
|
||||
// getOAuthRefreshToken returns the refresh token for the given session.
|
||||
//
|
||||
// The token is removed from the store after retrieval.
|
||||
func getOnceOAuthRefreshToken(claims *Session) (*oauthRefreshToken, bool) {
|
||||
func getOAuthRefreshToken(claims *Session) (*oauthRefreshToken, bool) {
|
||||
token, ok := oauthRefreshTokens.Load(string(claims.SessionID))
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
invalidateOAuthRefreshToken(claims.SessionID)
|
||||
|
||||
if token.expired() {
|
||||
invalidateOAuthRefreshToken(claims.SessionID)
|
||||
return nil, false
|
||||
}
|
||||
|
||||
if claims.Username != token.Username {
|
||||
return nil, false
|
||||
}
|
||||
return &token, true
|
||||
return token, true
|
||||
}
|
||||
|
||||
func storeOAuthRefreshToken(sessionID sessionID, username, token string) {
|
||||
oauthRefreshTokens.Store(string(sessionID), oauthRefreshToken{
|
||||
oauthRefreshTokens.Store(string(sessionID), &oauthRefreshToken{
|
||||
Username: username,
|
||||
RefreshToken: token,
|
||||
Expiry: time.Now().Add(defaultRefreshTokenExpiry),
|
||||
|
@ -135,51 +150,75 @@ func (auth *OIDCProvider) parseSessionJWT(sessionJWT string) (claims *sessionCla
|
|||
return claims, sessionToken.Valid && claims.Issuer == sessionTokenIssuer, nil
|
||||
}
|
||||
|
||||
func (auth *OIDCProvider) TryRefreshToken(w http.ResponseWriter, r *http.Request, sessionJWT string) error {
|
||||
func (auth *OIDCProvider) TryRefreshToken(ctx context.Context, sessionJWT string) (*refreshResult, error) {
|
||||
// verify the session cookie
|
||||
claims, valid, err := auth.parseSessionJWT(sessionJWT)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%w: %w", ErrInvalidSessionToken, err)
|
||||
return nil, fmt.Errorf("session: %s - %w: %w", claims.SessionID, ErrInvalidSessionToken, err)
|
||||
}
|
||||
if !valid {
|
||||
return ErrInvalidSessionToken
|
||||
return nil, ErrInvalidSessionToken
|
||||
}
|
||||
|
||||
// check if refresh is possible
|
||||
refreshToken, ok := getOnceOAuthRefreshToken(&claims.Session)
|
||||
refreshToken, ok := getOAuthRefreshToken(&claims.Session)
|
||||
if !ok {
|
||||
return errNoRefreshToken
|
||||
return nil, errNoRefreshToken
|
||||
}
|
||||
|
||||
if !auth.checkAllowed(claims.Username, claims.Groups) {
|
||||
return ErrUserNotAllowed
|
||||
return nil, ErrUserNotAllowed
|
||||
}
|
||||
|
||||
return auth.doRefreshToken(ctx, refreshToken, &claims.Session)
|
||||
}
|
||||
|
||||
func (auth *OIDCProvider) doRefreshToken(ctx context.Context, refreshToken *oauthRefreshToken, claims *Session) (*refreshResult, error) {
|
||||
refreshToken.mu.Lock()
|
||||
defer refreshToken.mu.Unlock()
|
||||
|
||||
// already refreshed
|
||||
// this must be called after refresh but before invalidate
|
||||
if refreshToken.result != nil || refreshToken.err != nil {
|
||||
return refreshToken.result, refreshToken.err
|
||||
}
|
||||
|
||||
// this step refreshes the token
|
||||
// see https://cs.opensource.google/go/x/oauth2/+/refs/tags/v0.29.0:oauth2.go;l=313
|
||||
newToken, err := auth.oauthConfig.TokenSource(r.Context(), &oauth2.Token{
|
||||
newToken, err := auth.oauthConfig.TokenSource(ctx, &oauth2.Token{
|
||||
RefreshToken: refreshToken.RefreshToken,
|
||||
}).Token()
|
||||
if err != nil {
|
||||
return fmt.Errorf("%w: %w", ErrRefreshTokenFailure, err)
|
||||
refreshToken.err = fmt.Errorf("session: %s - %w: %w", claims.SessionID, ErrRefreshTokenFailure, err)
|
||||
return nil, refreshToken.err
|
||||
}
|
||||
|
||||
idTokenJWT, idToken, err := auth.getIdToken(r.Context(), newToken)
|
||||
idTokenJWT, idToken, err := auth.getIdToken(ctx, newToken)
|
||||
if err != nil {
|
||||
return err
|
||||
refreshToken.err = fmt.Errorf("session: %s - %w: %w", claims.SessionID, ErrRefreshTokenFailure, err)
|
||||
return nil, refreshToken.err
|
||||
}
|
||||
|
||||
// in case there're multiple requests for the same session to refresh
|
||||
// invalidate the token after a short delay
|
||||
go func() {
|
||||
<-time.After(sessionInvalidateDelay)
|
||||
invalidateOAuthRefreshToken(claims.SessionID)
|
||||
}()
|
||||
|
||||
sessionID := newSessionID()
|
||||
|
||||
logging.Debug().Str("username", claims.Username).Time("expiry", newToken.Expiry).Msg("refreshed token")
|
||||
storeOAuthRefreshToken(sessionID, claims.Username, newToken.RefreshToken)
|
||||
|
||||
// set new idToken and new sessionToken
|
||||
auth.setIDTokenCookie(w, r, idTokenJWT, time.Until(idToken.Expiry))
|
||||
auth.setSessionTokenCookie(w, r, Session{
|
||||
SessionID: sessionID,
|
||||
Username: claims.Username,
|
||||
Groups: claims.Groups,
|
||||
})
|
||||
return nil
|
||||
refreshToken.result = &refreshResult{
|
||||
newSession: Session{
|
||||
SessionID: sessionID,
|
||||
Username: claims.Username,
|
||||
Groups: claims.Groups,
|
||||
},
|
||||
jwt: idTokenJWT,
|
||||
jwtExpiry: idToken.Expiry,
|
||||
}
|
||||
return refreshToken.result, nil
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
|
||||
"github.com/coreos/go-oidc/v3/oidc"
|
||||
"github.com/yusing/go-proxy/internal/common"
|
||||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
"github.com/yusing/go-proxy/internal/logging"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp"
|
||||
"github.com/yusing/go-proxy/internal/utils"
|
||||
|
@ -47,7 +48,12 @@ const (
|
|||
OIDCLogoutPath = "/auth/logout"
|
||||
)
|
||||
|
||||
var errMissingIDToken = errors.New("missing id_token field from oauth token")
|
||||
var (
|
||||
errMissingIDToken = errors.New("missing id_token field from oauth token")
|
||||
|
||||
ErrMissingOAuthToken = gperr.New("missing oauth token")
|
||||
ErrInvalidOAuthToken = gperr.New("invalid oauth token")
|
||||
)
|
||||
|
||||
// generateState generates a random string for OIDC state.
|
||||
const oidcStateLength = 32
|
||||
|
@ -148,12 +154,19 @@ func (auth *OIDCProvider) HandleAuth(w http.ResponseWriter, r *http.Request) {
|
|||
func (auth *OIDCProvider) LoginHandler(w http.ResponseWriter, r *http.Request) {
|
||||
// check for session token
|
||||
sessionToken, err := r.Cookie(CookieOauthSessionToken)
|
||||
if err == nil {
|
||||
err = auth.TryRefreshToken(w, r, sessionToken.Value)
|
||||
if err != nil {
|
||||
logging.Debug().Err(err).Msg("failed to refresh token")
|
||||
auth.clearCookie(w, r)
|
||||
if err == nil { // session token exists
|
||||
result, err := auth.TryRefreshToken(r.Context(), sessionToken.Value)
|
||||
// redirect back to where they requested
|
||||
// when token refresh is ok
|
||||
if err == nil {
|
||||
auth.setIDTokenCookie(w, r, result.jwt, time.Until(result.jwtExpiry))
|
||||
auth.setSessionTokenCookie(w, r, result.newSession)
|
||||
ProceedNext(w, r)
|
||||
return
|
||||
}
|
||||
// clear cookies then redirect to home
|
||||
logging.Err(err).Msg("failed to refresh token")
|
||||
auth.clearCookie(w, r)
|
||||
http.Redirect(w, r, "/", http.StatusFound)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -10,22 +10,21 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
ErrMissingOAuthToken = gperr.New("missing oauth token")
|
||||
ErrMissingSessionToken = gperr.New("missing session token")
|
||||
ErrInvalidOAuthToken = gperr.New("invalid oauth token")
|
||||
ErrInvalidSessionToken = gperr.New("invalid session token")
|
||||
ErrUserNotAllowed = gperr.New("user not allowed")
|
||||
)
|
||||
|
||||
func IsFrontend(r *http.Request) bool {
|
||||
return r.Host == common.APIHTTPAddr
|
||||
}
|
||||
|
||||
func requestHost(r *http.Request) string {
|
||||
// check if it's from backend
|
||||
switch r.Host {
|
||||
case common.APIHTTPAddr:
|
||||
// use XFH
|
||||
if IsFrontend(r) {
|
||||
return r.Header.Get("X-Forwarded-Host")
|
||||
default:
|
||||
return r.Host
|
||||
}
|
||||
return r.Host
|
||||
}
|
||||
|
||||
// cookieDomain returns the fully qualified domain name of the request host
|
||||
|
|
|
@ -16,7 +16,6 @@ const (
|
|||
ConfigPath = ConfigBasePath + "/" + ConfigFileName
|
||||
|
||||
IconListCachePath = ConfigBasePath + "/.icon_list_cache.json"
|
||||
IconCachePath = ConfigBasePath + "/.icon_cache.json"
|
||||
|
||||
NamespaceHomepageOverrides = ".homepage"
|
||||
NamespaceIconCache = ".icon_cache"
|
||||
|
|
|
@ -18,6 +18,7 @@ import (
|
|||
"github.com/yusing/go-proxy/internal/logging"
|
||||
"github.com/yusing/go-proxy/internal/net/gphttp/server"
|
||||
"github.com/yusing/go-proxy/internal/notif"
|
||||
"github.com/yusing/go-proxy/internal/proxmox"
|
||||
proxy "github.com/yusing/go-proxy/internal/route/provider"
|
||||
"github.com/yusing/go-proxy/internal/task"
|
||||
"github.com/yusing/go-proxy/internal/utils"
|
||||
|
@ -229,6 +230,7 @@ func (cfg *Config) load() gperr.Error {
|
|||
errs.Add(cfg.entrypoint.SetAccessLogger(cfg.task, model.Entrypoint.AccessLog))
|
||||
cfg.initNotification(model.Providers.Notification)
|
||||
errs.Add(cfg.initAutoCert(model.AutoCert))
|
||||
errs.Add(cfg.initProxmox(model.Providers.Proxmox))
|
||||
errs.Add(cfg.loadRouteProviders(&model.Providers))
|
||||
|
||||
cfg.value = model
|
||||
|
@ -278,6 +280,17 @@ func (cfg *Config) initAutoCert(autocertCfg *autocert.Config) gperr.Error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (cfg *Config) initProxmox(proxmoxCfg []proxmox.Config) gperr.Error {
|
||||
proxmox.Clients.Clear()
|
||||
var errs = gperr.NewBuilder()
|
||||
for _, cfg := range proxmoxCfg {
|
||||
if err := cfg.Init(); err != nil {
|
||||
errs.Add(err.Subject(cfg.URL))
|
||||
}
|
||||
}
|
||||
return errs.Error()
|
||||
}
|
||||
|
||||
func (cfg *Config) errIfExists(p *proxy.Provider) gperr.Error {
|
||||
if _, ok := cfg.providers.Load(p.String()); ok {
|
||||
return gperr.Errorf("provider %s already exists", p.String())
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
"github.com/yusing/go-proxy/internal/logging/accesslog"
|
||||
"github.com/yusing/go-proxy/internal/notif"
|
||||
"github.com/yusing/go-proxy/internal/proxmox"
|
||||
"github.com/yusing/go-proxy/internal/utils"
|
||||
)
|
||||
|
||||
|
@ -30,6 +31,7 @@ type (
|
|||
Docker map[string]string `json:"docker" yaml:"docker,omitempty" validate:"non_empty_docker_keys,dive,unix_addr|url"`
|
||||
Agents []*agent.AgentConfig `json:"agents" yaml:"agents,omitempty"`
|
||||
Notification []notif.NotificationConfig `json:"notification" yaml:"notification,omitempty"`
|
||||
Proxmox []proxmox.Config `json:"proxmox" yaml:"proxmox,omitempty"`
|
||||
}
|
||||
Entrypoint struct {
|
||||
Middlewares []map[string]any `json:"middlewares"`
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/yusing/go-proxy/internal/common"
|
||||
"github.com/yusing/go-proxy/internal/jsonstore"
|
||||
"github.com/yusing/go-proxy/internal/logging"
|
||||
"github.com/yusing/go-proxy/internal/task"
|
||||
"github.com/yusing/go-proxy/internal/utils"
|
||||
|
@ -15,34 +16,24 @@ import (
|
|||
|
||||
type cacheEntry struct {
|
||||
Icon []byte `json:"icon"`
|
||||
ContentType string `json:"content_type"`
|
||||
ContentType string `json:"content_type,omitempty"`
|
||||
LastAccess atomic.Value[time.Time] `json:"last_access"`
|
||||
}
|
||||
|
||||
// cache key can be absolute url or route name.
|
||||
var (
|
||||
iconCache = make(map[string]*cacheEntry)
|
||||
iconCacheMu sync.RWMutex
|
||||
iconCache = jsonstore.Store[*cacheEntry](common.NamespaceIconCache)
|
||||
iconMu sync.RWMutex
|
||||
)
|
||||
|
||||
const (
|
||||
iconCacheTTL = 3 * 24 * time.Hour
|
||||
cleanUpInterval = time.Minute
|
||||
maxCacheSize = 1024 * 1024 // 1MB
|
||||
maxIconSize = 1024 * 1024 // 1MB
|
||||
maxCacheEntries = 100
|
||||
)
|
||||
|
||||
func InitIconCache() {
|
||||
iconCacheMu.Lock()
|
||||
defer iconCacheMu.Unlock()
|
||||
|
||||
err := utils.LoadJSONIfExist(common.IconCachePath, &iconCache)
|
||||
if err != nil {
|
||||
logging.Error().Err(err).Msg("failed to load icon cache")
|
||||
} else if len(iconCache) > 0 {
|
||||
logging.Info().Int("count", len(iconCache)).Msg("icon cache loaded")
|
||||
}
|
||||
|
||||
func init() {
|
||||
go func() {
|
||||
cleanupTicker := time.NewTicker(cleanUpInterval)
|
||||
defer cleanupTicker.Stop()
|
||||
|
@ -55,36 +46,21 @@ func InitIconCache() {
|
|||
}
|
||||
}
|
||||
}()
|
||||
|
||||
task.OnProgramExit("save_favicon_cache", func() {
|
||||
iconCacheMu.Lock()
|
||||
defer iconCacheMu.Unlock()
|
||||
|
||||
if len(iconCache) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if err := utils.SaveJSON(common.IconCachePath, &iconCache, 0o644); err != nil {
|
||||
logging.Error().Err(err).Msg("failed to save icon cache")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func pruneExpiredIconCache() {
|
||||
iconCacheMu.Lock()
|
||||
defer iconCacheMu.Unlock()
|
||||
|
||||
nPruned := 0
|
||||
for key, icon := range iconCache {
|
||||
for key, icon := range iconCache.Range {
|
||||
if icon.IsExpired() {
|
||||
delete(iconCache, key)
|
||||
iconCache.Delete(key)
|
||||
nPruned++
|
||||
}
|
||||
}
|
||||
if len(iconCache) > maxCacheEntries {
|
||||
if iconCache.Size() > maxCacheEntries {
|
||||
iconCache.Clear()
|
||||
newIconCache := make(map[string]*cacheEntry, maxCacheEntries)
|
||||
i := 0
|
||||
for key, icon := range iconCache {
|
||||
for key, icon := range iconCache.Range {
|
||||
if i == maxCacheEntries {
|
||||
break
|
||||
}
|
||||
|
@ -93,7 +69,9 @@ func pruneExpiredIconCache() {
|
|||
i++
|
||||
}
|
||||
}
|
||||
iconCache = newIconCache
|
||||
for key, icon := range newIconCache {
|
||||
iconCache.Store(key, icon)
|
||||
}
|
||||
}
|
||||
if nPruned > 0 {
|
||||
logging.Info().Int("pruned", nPruned).Msg("pruned expired icon cache")
|
||||
|
@ -101,21 +79,18 @@ func pruneExpiredIconCache() {
|
|||
}
|
||||
|
||||
func PruneRouteIconCache(route route) {
|
||||
iconCacheMu.Lock()
|
||||
defer iconCacheMu.Unlock()
|
||||
delete(iconCache, route.Key())
|
||||
iconCache.Delete(route.Key())
|
||||
}
|
||||
|
||||
func loadIconCache(key string) *FetchResult {
|
||||
iconCacheMu.RLock()
|
||||
defer iconCacheMu.RUnlock()
|
||||
|
||||
icon, ok := iconCache[key]
|
||||
iconMu.RLock()
|
||||
defer iconMu.RUnlock()
|
||||
icon, ok := iconCache.Load(key)
|
||||
if ok && len(icon.Icon) > 0 {
|
||||
logging.Debug().
|
||||
Str("key", key).
|
||||
Msg("icon found in cache")
|
||||
icon.LastAccess.Store(time.Now())
|
||||
icon.LastAccess.Store(utils.TimeNow())
|
||||
return &FetchResult{Icon: icon.Icon, contentType: icon.ContentType}
|
||||
}
|
||||
return nil
|
||||
|
@ -123,15 +98,17 @@ func loadIconCache(key string) *FetchResult {
|
|||
|
||||
func storeIconCache(key string, result *FetchResult) {
|
||||
icon := result.Icon
|
||||
if len(icon) > maxCacheSize {
|
||||
if len(icon) > maxIconSize {
|
||||
logging.Debug().Int("size", len(icon)).Msg("icon cache size exceeds max cache size")
|
||||
return
|
||||
}
|
||||
iconCacheMu.Lock()
|
||||
defer iconCacheMu.Unlock()
|
||||
|
||||
iconMu.Lock()
|
||||
defer iconMu.Unlock()
|
||||
|
||||
entry := &cacheEntry{Icon: icon, ContentType: result.contentType}
|
||||
entry.LastAccess.Store(time.Now())
|
||||
iconCache[key] = entry
|
||||
iconCache.Store(key, entry)
|
||||
logging.Debug().Str("key", key).Int("size", len(icon)).Msg("stored icon cache")
|
||||
}
|
||||
|
||||
|
@ -140,12 +117,20 @@ func (e *cacheEntry) IsExpired() bool {
|
|||
}
|
||||
|
||||
func (e *cacheEntry) UnmarshalJSON(data []byte) error {
|
||||
var tmp struct {
|
||||
Icon []byte `json:"icon"`
|
||||
ContentType string `json:"content_type,omitempty"`
|
||||
LastAccess time.Time `json:"last_access"`
|
||||
}
|
||||
// check if data is json
|
||||
if json.Valid(data) {
|
||||
err := json.Unmarshal(data, &e)
|
||||
err := json.Unmarshal(data, &tmp)
|
||||
// return only if unmarshal is successful
|
||||
// otherwise fallback to base64
|
||||
if err == nil {
|
||||
e.Icon = tmp.Icon
|
||||
e.ContentType = tmp.ContentType
|
||||
e.LastAccess.Store(tmp.LastAccess)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import (
|
|||
"github.com/lithammer/fuzzysearch/fuzzy"
|
||||
"github.com/yusing/go-proxy/internal/common"
|
||||
"github.com/yusing/go-proxy/internal/logging"
|
||||
"github.com/yusing/go-proxy/internal/task"
|
||||
"github.com/yusing/go-proxy/internal/utils"
|
||||
)
|
||||
|
||||
|
@ -68,6 +69,10 @@ func InitIconListCache() {
|
|||
Int("display_names", len(iconsCache.DisplayNames)).
|
||||
Msg("icon list cache loaded")
|
||||
}
|
||||
|
||||
task.OnProgramExit("save_icon_list_cache", func() {
|
||||
utils.SaveJSON(common.IconListCachePath, iconsCache, 0o644)
|
||||
})
|
||||
}
|
||||
|
||||
func ListAvailableIcons() (*Cache, error) {
|
||||
|
|
|
@ -131,7 +131,7 @@ func NewWatcher(parent task.Parent, r routes.Route) (*Watcher, error) {
|
|||
case routes.StreamRoute:
|
||||
w.stream = r
|
||||
default:
|
||||
return nil, gperr.New("unexpected route type")
|
||||
return nil, gperr.Errorf("unexpected route type: %T", r)
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(parent.Context(), reqTimeout)
|
||||
|
|
|
@ -1,297 +0,0 @@
|
|||
package internal
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/lithammer/fuzzysearch/fuzzy"
|
||||
"github.com/yusing/go-proxy/internal/common"
|
||||
"github.com/yusing/go-proxy/internal/logging"
|
||||
"github.com/yusing/go-proxy/internal/utils"
|
||||
)
|
||||
|
||||
type GitHubContents struct { //! keep this, may reuse in future
|
||||
Type string `json:"type"`
|
||||
Path string `json:"path"`
|
||||
Name string `json:"name"`
|
||||
Sha string `json:"sha"`
|
||||
Size int `json:"size"`
|
||||
}
|
||||
|
||||
type (
|
||||
IconsMap map[string]map[string]struct{}
|
||||
IconList []string
|
||||
Cache struct {
|
||||
WalkxCode, Selfhst IconsMap
|
||||
DisplayNames ReferenceDisplayNameMap
|
||||
IconList IconList // combined into a single list
|
||||
}
|
||||
ReferenceDisplayNameMap map[string]string
|
||||
)
|
||||
|
||||
func (icons *Cache) needUpdate() bool {
|
||||
return len(icons.WalkxCode) == 0 || len(icons.Selfhst) == 0 || len(icons.IconList) == 0 || len(icons.DisplayNames) == 0
|
||||
}
|
||||
|
||||
const updateInterval = 2 * time.Hour
|
||||
|
||||
var (
|
||||
iconsCache *Cache
|
||||
iconsCahceMu sync.RWMutex
|
||||
lastUpdate time.Time
|
||||
)
|
||||
|
||||
const (
|
||||
walkxcodeIcons = "https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/tree.json"
|
||||
selfhstIcons = "https://cdn.selfh.st/directory/icons.json"
|
||||
)
|
||||
|
||||
func InitIconListCache() {
|
||||
iconsCahceMu.Lock()
|
||||
defer iconsCahceMu.Unlock()
|
||||
|
||||
iconsCache = &Cache{
|
||||
WalkxCode: make(IconsMap),
|
||||
Selfhst: make(IconsMap),
|
||||
DisplayNames: make(ReferenceDisplayNameMap),
|
||||
IconList: []string{},
|
||||
}
|
||||
err := utils.LoadJSONIfExist(common.IconListCachePath, iconsCache)
|
||||
if err != nil {
|
||||
logging.Error().Err(err).Msg("failed to load icon list cache config")
|
||||
} else if len(iconsCache.IconList) > 0 {
|
||||
logging.Info().
|
||||
Int("icons", len(iconsCache.IconList)).
|
||||
Int("display_names", len(iconsCache.DisplayNames)).
|
||||
Msg("icon list cache loaded")
|
||||
}
|
||||
}
|
||||
|
||||
func ListAvailableIcons() (*Cache, error) {
|
||||
iconsCahceMu.RLock()
|
||||
if time.Since(lastUpdate) < updateInterval {
|
||||
if !iconsCache.needUpdate() {
|
||||
iconsCahceMu.RUnlock()
|
||||
return iconsCache, nil
|
||||
}
|
||||
}
|
||||
iconsCahceMu.RUnlock()
|
||||
|
||||
iconsCahceMu.Lock()
|
||||
defer iconsCahceMu.Unlock()
|
||||
|
||||
logging.Info().Msg("updating icon data")
|
||||
icons, err := fetchIconData()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
logging.Info().
|
||||
Int("icons", len(icons.IconList)).
|
||||
Int("display_names", len(icons.DisplayNames)).
|
||||
Msg("icons list updated")
|
||||
|
||||
iconsCache = icons
|
||||
lastUpdate = time.Now()
|
||||
|
||||
err = utils.SaveJSON(common.IconListCachePath, iconsCache, 0o644)
|
||||
if err != nil {
|
||||
logging.Warn().Err(err).Msg("failed to save icon list cache")
|
||||
}
|
||||
return icons, nil
|
||||
}
|
||||
|
||||
func SearchIcons(keyword string, limit int) ([]string, error) {
|
||||
icons, err := ListAvailableIcons()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if keyword == "" {
|
||||
return utils.Slice(icons.IconList, limit), nil
|
||||
}
|
||||
return utils.Slice(fuzzy.Find(keyword, icons.IconList), limit), nil
|
||||
}
|
||||
|
||||
func HasWalkxCodeIcon(name string, filetype string) bool {
|
||||
icons, err := ListAvailableIcons()
|
||||
if err != nil {
|
||||
logging.Error().Err(err).Msg("failed to list icons")
|
||||
return false
|
||||
}
|
||||
if _, ok := icons.WalkxCode[filetype]; !ok {
|
||||
return false
|
||||
}
|
||||
_, ok := icons.WalkxCode[filetype][name+"."+filetype]
|
||||
return ok
|
||||
}
|
||||
|
||||
func HasSelfhstIcon(name string, filetype string) bool {
|
||||
icons, err := ListAvailableIcons()
|
||||
if err != nil {
|
||||
logging.Error().Err(err).Msg("failed to list icons")
|
||||
return false
|
||||
}
|
||||
if _, ok := icons.Selfhst[filetype]; !ok {
|
||||
return false
|
||||
}
|
||||
_, ok := icons.Selfhst[filetype][name+"."+filetype]
|
||||
return ok
|
||||
}
|
||||
|
||||
func GetDisplayName(reference string) (string, bool) {
|
||||
icons, err := ListAvailableIcons()
|
||||
if err != nil {
|
||||
logging.Error().Err(err).Msg("failed to list icons")
|
||||
return "", false
|
||||
}
|
||||
displayName, ok := icons.DisplayNames[reference]
|
||||
return displayName, ok
|
||||
}
|
||||
|
||||
func fetchIconData() (*Cache, error) {
|
||||
walkxCodeIconMap, walkxCodeIconList, err := fetchWalkxCodeIcons()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
n := 0
|
||||
for _, items := range walkxCodeIconMap {
|
||||
n += len(items)
|
||||
}
|
||||
|
||||
selfhstIconMap, selfhstIconList, referenceToNames, err := fetchSelfhstIcons()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Cache{
|
||||
WalkxCode: walkxCodeIconMap,
|
||||
Selfhst: selfhstIconMap,
|
||||
DisplayNames: referenceToNames,
|
||||
IconList: append(walkxCodeIconList, selfhstIconList...),
|
||||
}, nil
|
||||
}
|
||||
|
||||
/*
|
||||
format:
|
||||
|
||||
{
|
||||
"png": [
|
||||
"*.png",
|
||||
],
|
||||
"svg": [
|
||||
"*.svg",
|
||||
],
|
||||
"webp": [
|
||||
"*.webp",
|
||||
]
|
||||
}
|
||||
*/
|
||||
func fetchWalkxCodeIcons() (IconsMap, IconList, error) {
|
||||
req, err := http.NewRequest(http.MethodGet, walkxcodeIcons, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
data := make(map[string][]string)
|
||||
err = json.Unmarshal(body, &data)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
icons := make(IconsMap, len(data))
|
||||
iconList := make(IconList, 0, 2000)
|
||||
for fileType, files := range data {
|
||||
icons[fileType] = make(map[string]struct{}, len(files))
|
||||
for _, icon := range files {
|
||||
icons[fileType][icon] = struct{}{}
|
||||
iconList = append(iconList, "@walkxcode/"+icon)
|
||||
}
|
||||
}
|
||||
return icons, iconList, nil
|
||||
}
|
||||
|
||||
/*
|
||||
format:
|
||||
|
||||
{
|
||||
"Name": "2FAuth",
|
||||
"Reference": "2fauth",
|
||||
"SVG": "Yes",
|
||||
"PNG": "Yes",
|
||||
"WebP": "Yes",
|
||||
"Light": "Yes",
|
||||
"Category": "Self-Hosted",
|
||||
"CreatedAt": "2024-08-16 00:27:23+00:00"
|
||||
}
|
||||
*/
|
||||
func fetchSelfhstIcons() (IconsMap, IconList, ReferenceDisplayNameMap, error) {
|
||||
type SelfhStIcon struct {
|
||||
Name string `json:"Name"`
|
||||
Reference string `json:"Reference"`
|
||||
SVG string `json:"SVG"`
|
||||
PNG string `json:"PNG"`
|
||||
WebP string `json:"WebP"`
|
||||
// Light string
|
||||
// Category string
|
||||
// CreatedAt string
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, selfhstIcons, nil)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
data := make([]SelfhStIcon, 0, 2000)
|
||||
err = json.Unmarshal(body, &data)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
iconList := make(IconList, 0, len(data)*3)
|
||||
icons := make(IconsMap)
|
||||
icons["svg"] = make(map[string]struct{}, len(data))
|
||||
icons["png"] = make(map[string]struct{}, len(data))
|
||||
icons["webp"] = make(map[string]struct{}, len(data))
|
||||
|
||||
referenceToNames := make(ReferenceDisplayNameMap, len(data))
|
||||
|
||||
for _, item := range data {
|
||||
if item.SVG == "Yes" {
|
||||
icons["svg"][item.Reference+".svg"] = struct{}{}
|
||||
iconList = append(iconList, "@selfhst/"+item.Reference+".svg")
|
||||
}
|
||||
if item.PNG == "Yes" {
|
||||
icons["png"][item.Reference+".png"] = struct{}{}
|
||||
iconList = append(iconList, "@selfhst/"+item.Reference+".png")
|
||||
}
|
||||
if item.WebP == "Yes" {
|
||||
icons["webp"][item.Reference+".webp"] = struct{}{}
|
||||
iconList = append(iconList, "@selfhst/"+item.Reference+".webp")
|
||||
}
|
||||
referenceToNames[item.Reference] = item.Name
|
||||
}
|
||||
|
||||
return icons, iconList, referenceToNames, nil
|
||||
}
|
|
@ -133,11 +133,6 @@ func (lb *LoadBalancer) AddServer(srv Server) {
|
|||
|
||||
lb.rebalance()
|
||||
lb.impl.OnAddServer(srv)
|
||||
|
||||
lb.l.Debug().
|
||||
Str("action", "add").
|
||||
Str("server", srv.Name()).
|
||||
Msgf("%d servers available", lb.pool.Size())
|
||||
}
|
||||
|
||||
func (lb *LoadBalancer) RemoveServer(srv Server) {
|
||||
|
|
|
@ -12,19 +12,13 @@ import (
|
|||
type EventHandler struct {
|
||||
provider *Provider
|
||||
|
||||
errs *gperr.Builder
|
||||
added *gperr.Builder
|
||||
removed *gperr.Builder
|
||||
updated *gperr.Builder
|
||||
errs *gperr.Builder
|
||||
}
|
||||
|
||||
func (p *Provider) newEventHandler() *EventHandler {
|
||||
return &EventHandler{
|
||||
provider: p,
|
||||
errs: gperr.NewBuilder("event errors"),
|
||||
added: gperr.NewBuilder("added"),
|
||||
removed: gperr.NewBuilder("removed"),
|
||||
updated: gperr.NewBuilder("updated"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -88,15 +82,12 @@ func (handler *EventHandler) Add(parent task.Parent, route *route.Route) {
|
|||
err := handler.provider.startRoute(parent, route)
|
||||
if err != nil {
|
||||
handler.errs.Add(err.Subject("add"))
|
||||
} else {
|
||||
handler.added.Adds(route.Alias)
|
||||
}
|
||||
}
|
||||
|
||||
func (handler *EventHandler) Remove(route *route.Route) {
|
||||
route.Finish("route removed")
|
||||
delete(handler.provider.routes, route.Alias)
|
||||
handler.removed.Adds(route.Alias)
|
||||
}
|
||||
|
||||
func (handler *EventHandler) Update(parent task.Parent, oldRoute *route.Route, newRoute *route.Route) {
|
||||
|
@ -104,18 +95,11 @@ func (handler *EventHandler) Update(parent task.Parent, oldRoute *route.Route, n
|
|||
err := handler.provider.startRoute(parent, newRoute)
|
||||
if err != nil {
|
||||
handler.errs.Add(err.Subject("update"))
|
||||
} else {
|
||||
handler.updated.Adds(newRoute.Alias)
|
||||
}
|
||||
}
|
||||
|
||||
func (handler *EventHandler) Log() {
|
||||
results := gperr.NewBuilder("event occurred")
|
||||
results.AddFrom(handler.added, false)
|
||||
results.AddFrom(handler.removed, false)
|
||||
results.AddFrom(handler.updated, false)
|
||||
results.AddFrom(handler.errs, false)
|
||||
if result := results.String(); result != "" {
|
||||
handler.provider.Logger().Info().Msg(result)
|
||||
if err := handler.errs.Error(); err != nil {
|
||||
handler.provider.Logger().Info().Msg(err.Error())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,19 +22,19 @@ import (
|
|||
"github.com/yusing/go-proxy/internal/watcher/health/monitor"
|
||||
)
|
||||
|
||||
type (
|
||||
ReveseProxyRoute struct {
|
||||
*Route
|
||||
type ReveseProxyRoute struct {
|
||||
*Route
|
||||
|
||||
HealthMon health.HealthMonitor `json:"health,omitempty"`
|
||||
HealthMon health.HealthMonitor `json:"health,omitempty"`
|
||||
|
||||
loadBalancer *loadbalancer.LoadBalancer
|
||||
handler http.Handler
|
||||
rp *reverseproxy.ReverseProxy
|
||||
loadBalancer *loadbalancer.LoadBalancer
|
||||
handler http.Handler
|
||||
rp *reverseproxy.ReverseProxy
|
||||
|
||||
task *task.Task
|
||||
}
|
||||
)
|
||||
task *task.Task
|
||||
}
|
||||
|
||||
var _ routes.ReverseProxyRoute = (*ReveseProxyRoute)(nil)
|
||||
|
||||
// var globalMux = http.NewServeMux() // TODO: support regex subdomain matching.
|
||||
|
||||
|
@ -88,6 +88,11 @@ func NewReverseProxyRoute(base *Route) (*ReveseProxyRoute, gperr.Error) {
|
|||
return r, nil
|
||||
}
|
||||
|
||||
// ReverseProxy implements routes.ReverseProxyRoute.
|
||||
func (r *ReveseProxyRoute) ReverseProxy() *reverseproxy.ReverseProxy {
|
||||
return r.rp
|
||||
}
|
||||
|
||||
// Start implements task.TaskStarter.
|
||||
func (r *ReveseProxyRoute) Start(parent task.Parent) gperr.Error {
|
||||
if existing, ok := routes.HTTP.Get(r.Key()); ok && !r.UseLoadBalance() {
|
||||
|
|
|
@ -8,7 +8,6 @@ import (
|
|||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/yusing/go-proxy/agent/pkg/agent"
|
||||
"github.com/yusing/go-proxy/internal"
|
||||
"github.com/yusing/go-proxy/internal/docker"
|
||||
"github.com/yusing/go-proxy/internal/gperr"
|
||||
"github.com/yusing/go-proxy/internal/homepage"
|
||||
|
@ -484,7 +483,7 @@ func (r *Route) FinalizeHomepageConfig() {
|
|||
} else {
|
||||
key = r.Alias
|
||||
}
|
||||
displayName, ok := internal.GetDisplayName(key)
|
||||
displayName, ok := homepage.GetDisplayName(key)
|
||||
if ok {
|
||||
hp.Name = displayName
|
||||
} else {
|
||||
|
|
Loading…
Add table
Reference in a new issue