Date: Thu, 3 Apr 2025 13:35:34 +0800
Subject: [PATCH 04/16] Fixed #201 and #608
- Added domain / host name specific statistics
- Added upstream forward request count
---
src/def.go | 2 +-
src/mod/dynamicproxy/dpcore/dpcore.go | 13 ---
src/mod/dynamicproxy/proxyRequestHandler.go | 1 +
src/mod/statistic/analytic/utils.go | 10 ++
src/mod/statistic/statistic.go | 53 ++++++---
src/mod/statistic/structconv.go | 113 +++++++++-----------
src/web/components/stats.html | 97 ++++++++++++++++-
7 files changed, 198 insertions(+), 91 deletions(-)
diff --git a/src/def.go b/src/def.go
index a5fba8e..9b717ef 100644
--- a/src/def.go
+++ b/src/def.go
@@ -45,7 +45,7 @@ const (
/* Build Constants */
SYSTEM_NAME = "Zoraxy"
SYSTEM_VERSION = "3.2.1"
- DEVELOPMENT_BUILD = false /* Development: Set to false to use embedded web fs */
+ DEVELOPMENT_BUILD = true /* Development: Set to false to use embedded web fs */
/* System Constants */
TMP_FOLDER = "./tmp"
diff --git a/src/mod/dynamicproxy/dpcore/dpcore.go b/src/mod/dynamicproxy/dpcore/dpcore.go
index 7eef37e..8f858e4 100644
--- a/src/mod/dynamicproxy/dpcore/dpcore.go
+++ b/src/mod/dynamicproxy/dpcore/dpcore.go
@@ -105,7 +105,6 @@ func NewDynamicProxyCore(target *url.URL, prepender string, dpcOptions *DpcoreOp
thisTransporter := http.DefaultTransport
//Hack the default transporter to handle more connections
-
optimalConcurrentConnection := 256
if dpcOptions.MaxConcurrentConnection > 0 {
optimalConcurrentConnection = dpcOptions.MaxConcurrentConnection
@@ -137,18 +136,6 @@ func NewDynamicProxyCore(target *url.URL, prepender string, dpcOptions *DpcoreOp
}
}
-func singleJoiningSlash(a, b string) string {
- aslash := strings.HasSuffix(a, "/")
- bslash := strings.HasPrefix(b, "/")
- switch {
- case aslash && bslash:
- return a + b[1:]
- case !aslash && !bslash:
- return a + "/" + b
- }
- return a + b
-}
-
func joinURLPath(a, b *url.URL) (path, rawpath string) {
apath, bpath := a.EscapedPath(), b.EscapedPath()
aslash, bslash := strings.HasSuffix(apath, "/"), strings.HasPrefix(bpath, "/")
diff --git a/src/mod/dynamicproxy/proxyRequestHandler.go b/src/mod/dynamicproxy/proxyRequestHandler.go
index fffc303..36e22df 100644
--- a/src/mod/dynamicproxy/proxyRequestHandler.go
+++ b/src/mod/dynamicproxy/proxyRequestHandler.go
@@ -320,6 +320,7 @@ func (router *Router) logRequest(r *http.Request, succ bool, statusCode int, for
UserAgent: r.UserAgent(),
RequestURL: r.Host + r.RequestURI,
Target: originalHostname,
+ Upstream: upstreamHostname,
}
router.Option.StatisticCollector.RecordRequest(requestInfo)
}()
diff --git a/src/mod/statistic/analytic/utils.go b/src/mod/statistic/analytic/utils.go
index a373abb..f5b174a 100644
--- a/src/mod/statistic/analytic/utils.go
+++ b/src/mod/statistic/analytic/utils.go
@@ -36,6 +36,8 @@ func mergeDailySummaryExports(exports []*statistic.DailySummaryExport) *statisti
Referer: make(map[string]int),
UserAgent: make(map[string]int),
RequestURL: make(map[string]int),
+ Downstreams: make(map[string]int),
+ Upstreams: make(map[string]int),
}
for _, export := range exports {
@@ -66,6 +68,14 @@ func mergeDailySummaryExports(exports []*statistic.DailySummaryExport) *statisti
for key, value := range export.RequestURL {
mergedExport.RequestURL[key] += value
}
+
+ for key, value := range export.Downstreams {
+ mergedExport.Downstreams[key] += value
+ }
+
+ for key, value := range export.Upstreams {
+ mergedExport.Upstreams[key] += value
+ }
}
return mergedExport
diff --git a/src/mod/statistic/statistic.go b/src/mod/statistic/statistic.go
index e9c7a4f..c5c8647 100644
--- a/src/mod/statistic/statistic.go
+++ b/src/mod/statistic/statistic.go
@@ -24,12 +24,14 @@ type DailySummary struct {
ErrorRequest int64 //Invalid request of the day, including error or not found
ValidRequest int64 //Valid request of the day
//Type counters
- ForwardTypes *sync.Map //Map that hold the forward types
- RequestOrigin *sync.Map //Map that hold [country ISO code]: visitor counter
- RequestClientIp *sync.Map //Map that hold all unique request IPs
- Referer *sync.Map //Map that store where the user was refered from
- UserAgent *sync.Map //Map that store the useragent of the request
- RequestURL *sync.Map //Request URL of the request object
+ ForwardTypes *sync.Map //Map that hold the forward types
+ RequestOrigin *sync.Map //Map that hold [country ISO code]: visitor counter
+ RequestClientIp *sync.Map //Map that hold all unique request IPs
+ Referer *sync.Map //Map that store where the user was refered from
+ UserAgent *sync.Map //Map that store the useragent of the request
+ RequestURL *sync.Map //Request URL of the request object
+ DownstreamHostnames *sync.Map //Request count of downstream hostname
+ UpstreamHostnames *sync.Map //Forwarded request count of upstream hostname
}
type RequestInfo struct {
@@ -42,6 +44,7 @@ type RequestInfo struct {
UserAgent string //UserAgent of the downstream request
RequestURL string //Request URL
Target string //Target domain or hostname
+ Upstream string ////Upstream domain or hostname, if the request is forwarded to upstream
}
type CollectorOption struct {
@@ -233,6 +236,24 @@ func (c *Collector) RecordRequest(ri RequestInfo) {
} else {
c.DailySummary.RequestURL.Store(ri.RequestURL, ru.(int)+1)
}
+
+ //Record the downstream hostname
+ //This is the hostname that the user visited, not the target domain
+ ds, ok := c.DailySummary.DownstreamHostnames.Load(ri.Target)
+ if !ok {
+ c.DailySummary.DownstreamHostnames.Store(ri.Target, 1)
+ } else {
+ c.DailySummary.DownstreamHostnames.Store(ri.Target, ds.(int)+1)
+ }
+
+ //Record the upstream hostname
+ //This is the selected load balancer upstream hostname or ip
+ us, ok := c.DailySummary.UpstreamHostnames.Load(ri.Upstream)
+ if !ok {
+ c.DailySummary.UpstreamHostnames.Store(ri.Upstream, 1)
+ } else {
+ c.DailySummary.UpstreamHostnames.Store(ri.Upstream, us.(int)+1)
+ }
}()
//ADD MORE HERE IF NEEDED
@@ -271,15 +292,17 @@ func (c *Collector) ScheduleResetRealtimeStats() chan bool {
func NewDailySummary() *DailySummary {
return &DailySummary{
- TotalRequest: 0,
- ErrorRequest: 0,
- ValidRequest: 0,
- ForwardTypes: &sync.Map{},
- RequestOrigin: &sync.Map{},
- RequestClientIp: &sync.Map{},
- Referer: &sync.Map{},
- UserAgent: &sync.Map{},
- RequestURL: &sync.Map{},
+ TotalRequest: 0,
+ ErrorRequest: 0,
+ ValidRequest: 0,
+ ForwardTypes: &sync.Map{},
+ RequestOrigin: &sync.Map{},
+ RequestClientIp: &sync.Map{},
+ Referer: &sync.Map{},
+ UserAgent: &sync.Map{},
+ RequestURL: &sync.Map{},
+ DownstreamHostnames: &sync.Map{},
+ UpstreamHostnames: &sync.Map{},
}
}
diff --git a/src/mod/statistic/structconv.go b/src/mod/statistic/structconv.go
index 7de5c98..f36f0f8 100644
--- a/src/mod/statistic/structconv.go
+++ b/src/mod/statistic/structconv.go
@@ -13,6 +13,21 @@ type DailySummaryExport struct {
Referer map[string]int
UserAgent map[string]int
RequestURL map[string]int
+ Downstreams map[string]int
+ Upstreams map[string]int
+}
+
+func SyncMapToMapStringInt(syncMap *sync.Map) map[string]int {
+ result := make(map[string]int)
+ syncMap.Range(func(key, value interface{}) bool {
+ strKey, okKey := key.(string)
+ intValue, okValue := value.(int)
+ if okKey && okValue {
+ result[strKey] = intValue
+ }
+ return true
+ })
+ return result
}
func DailySummaryToExport(summary DailySummary) DailySummaryExport {
@@ -26,77 +41,53 @@ func DailySummaryToExport(summary DailySummary) DailySummaryExport {
Referer: make(map[string]int),
UserAgent: make(map[string]int),
RequestURL: make(map[string]int),
+ Downstreams: make(map[string]int),
+ Upstreams: make(map[string]int),
}
- summary.ForwardTypes.Range(func(key, value interface{}) bool {
- export.ForwardTypes[key.(string)] = value.(int)
- return true
- })
-
- summary.RequestOrigin.Range(func(key, value interface{}) bool {
- export.RequestOrigin[key.(string)] = value.(int)
- return true
- })
-
- summary.RequestClientIp.Range(func(key, value interface{}) bool {
- export.RequestClientIp[key.(string)] = value.(int)
- return true
- })
-
- summary.Referer.Range(func(key, value interface{}) bool {
- export.Referer[key.(string)] = value.(int)
- return true
- })
-
- summary.UserAgent.Range(func(key, value interface{}) bool {
- export.UserAgent[key.(string)] = value.(int)
- return true
- })
-
- summary.RequestURL.Range(func(key, value interface{}) bool {
- export.RequestURL[key.(string)] = value.(int)
- return true
- })
+ export.ForwardTypes = SyncMapToMapStringInt(summary.ForwardTypes)
+ export.RequestOrigin = SyncMapToMapStringInt(summary.RequestOrigin)
+ export.RequestClientIp = SyncMapToMapStringInt(summary.RequestClientIp)
+ export.Referer = SyncMapToMapStringInt(summary.Referer)
+ export.UserAgent = SyncMapToMapStringInt(summary.UserAgent)
+ export.RequestURL = SyncMapToMapStringInt(summary.RequestURL)
+ export.Downstreams = SyncMapToMapStringInt(summary.DownstreamHostnames)
+ export.Upstreams = SyncMapToMapStringInt(summary.UpstreamHostnames)
return export
}
+func MapStringIntToSyncMap(m map[string]int) *sync.Map {
+ syncMap := &sync.Map{}
+ for k, v := range m {
+ syncMap.Store(k, v)
+ }
+ return syncMap
+}
+
func DailySummaryExportToSummary(export DailySummaryExport) DailySummary {
summary := DailySummary{
- TotalRequest: export.TotalRequest,
- ErrorRequest: export.ErrorRequest,
- ValidRequest: export.ValidRequest,
- ForwardTypes: &sync.Map{},
- RequestOrigin: &sync.Map{},
- RequestClientIp: &sync.Map{},
- Referer: &sync.Map{},
- UserAgent: &sync.Map{},
- RequestURL: &sync.Map{},
+ TotalRequest: export.TotalRequest,
+ ErrorRequest: export.ErrorRequest,
+ ValidRequest: export.ValidRequest,
+ ForwardTypes: &sync.Map{},
+ RequestOrigin: &sync.Map{},
+ RequestClientIp: &sync.Map{},
+ Referer: &sync.Map{},
+ UserAgent: &sync.Map{},
+ RequestURL: &sync.Map{},
+ DownstreamHostnames: &sync.Map{},
+ UpstreamHostnames: &sync.Map{},
}
- for k, v := range export.ForwardTypes {
- summary.ForwardTypes.Store(k, v)
- }
-
- for k, v := range export.RequestOrigin {
- summary.RequestOrigin.Store(k, v)
- }
-
- for k, v := range export.RequestClientIp {
- summary.RequestClientIp.Store(k, v)
- }
-
- for k, v := range export.Referer {
- summary.Referer.Store(k, v)
- }
-
- for k, v := range export.UserAgent {
- summary.UserAgent.Store(k, v)
- }
-
- for k, v := range export.RequestURL {
- summary.RequestURL.Store(k, v)
- }
+ summary.ForwardTypes = MapStringIntToSyncMap(export.ForwardTypes)
+ summary.RequestOrigin = MapStringIntToSyncMap(export.RequestOrigin)
+ summary.RequestClientIp = MapStringIntToSyncMap(export.RequestClientIp)
+ summary.Referer = MapStringIntToSyncMap(export.Referer)
+ summary.UserAgent = MapStringIntToSyncMap(export.UserAgent)
+ summary.RequestURL = MapStringIntToSyncMap(export.RequestURL)
+ summary.DownstreamHostnames = MapStringIntToSyncMap(export.Downstreams)
+ summary.UpstreamHostnames = MapStringIntToSyncMap(export.Upstreams)
return summary
}
diff --git a/src/web/components/stats.html b/src/web/components/stats.html
index 4f650b0..3d78467 100644
--- a/src/web/components/stats.html
+++ b/src/web/components/stats.html
@@ -184,7 +184,46 @@
-
+
+
+
+
+
Requested Hostnames
+
Most requested hostnames from downstream
+
+
+
+
+
+ Hostname
+ Requests
+
+
+
+
+
+
+
+
+
+
Forwarded Upstreams
+
The Top 100 upstreams where the requests are forwarded to
+
+
+
+
+
+ Upstream Endpoint
+ Requests
+
+
+
+
+
+
+
+
+
Visitor Trend Analysis
@@ -263,6 +302,22 @@
//Render Referer header
renderRefererTable(data.Referer);
+ if (data.Downstreams == null){
+ //No downstream data to show
+ $("#stats_downstreamTable").html("No data ");
+ }else{
+ //Render the downstream table
+ renderDownstreamTable(data.Downstreams);
+ }
+
+ if (data.Upstreams == null){
+ //No upstream data to show
+ $("#stats_upstreamTable").html("No data ");
+ }else{
+ //Render the upstream table
+ renderUpstreamTable(data.Upstreams);
+ }
+
//Hide the trend graphs
$("#trendGraphs").hide();
});
@@ -410,6 +465,46 @@
}
}
+ function renderDownstreamTable(downstreamList){
+ const sortedEntries = Object.entries(downstreamList).sort(([, valueA], [, valueB]) => valueB - valueA);
+ $("#stats_downstreamTable").html("");
+ let endStop = 100;
+ if (sortedEntries.length < 100){
+ endStop = sortedEntries.length;
+ }
+ for (var i = 0; i < endStop; i++) {
+ let referer = (decodeURIComponent(sortedEntries[i][0])).replace(/(<([^>]+)>)/ig,"");
+ if (sortedEntries[i][0] == ""){
+ //Root
+ referer = `( Unknown or Hidden) `;
+ }
+ $("#stats_downstreamTable").append(`
+ ${referer}
+ ${sortedEntries[i][1]}
+ `);
+ }
+ }
+
+ function renderUpstreamTable(upstreamList){
+ const sortedEntries = Object.entries(upstreamList).sort(([, valueA], [, valueB]) => valueB - valueA);
+ $("#stats_upstreamTable").html("");
+ let endStop = 100;
+ if (sortedEntries.length < 100){
+ endStop = sortedEntries.length;
+ }
+ for (var i = 0; i < endStop; i++) {
+ let referer = (decodeURIComponent(sortedEntries[i][0])).replace(/(<([^>]+)>)/ig,"");
+ if (sortedEntries[i][0] == ""){
+ //Root
+ referer = `( Unknown or Hidden) `;
+ }
+ $("#stats_upstreamTable").append(`
+ ${referer}
+ ${sortedEntries[i][1]}
+ `);
+ }
+ }
+
function renderFileTypeGraph(requestURLs){
//Create the device chart
let fileExtensions = {};
From b9b992a81726a8aec3658559fd1327109d99c097 Mon Sep 17 00:00:00 2001
From: Toby Chui
Date: Sun, 6 Apr 2025 16:49:44 +0800
Subject: [PATCH 05/16] Fixed #626
- Added checks for port in hostname redirection in dpcore util
---
src/mod/dynamicproxy/dpcore/dpcore.go | 1 -
src/mod/dynamicproxy/dpcore/utils.go | 18 ++++++++++++++++++
2 files changed, 18 insertions(+), 1 deletion(-)
diff --git a/src/mod/dynamicproxy/dpcore/dpcore.go b/src/mod/dynamicproxy/dpcore/dpcore.go
index 8f858e4..eb0a489 100644
--- a/src/mod/dynamicproxy/dpcore/dpcore.go
+++ b/src/mod/dynamicproxy/dpcore/dpcore.go
@@ -339,7 +339,6 @@ func (p *ReverseProxy) ProxyHTTP(rw http.ResponseWriter, req *http.Request, rrr
}
} else if strings.HasPrefix(originLocation, "/") && rrr.PathPrefix != "" {
//Back to the root of this proxy object
- //fmt.Println(rrr.ProxyDomain, rrr.OriginalHost)
locationRewrite = strings.TrimSuffix(rrr.PathPrefix, "/") + originLocation
} else {
//Relative path. Do not modifiy location header
diff --git a/src/mod/dynamicproxy/dpcore/utils.go b/src/mod/dynamicproxy/dpcore/utils.go
index ab878e2..edac154 100644
--- a/src/mod/dynamicproxy/dpcore/utils.go
+++ b/src/mod/dynamicproxy/dpcore/utils.go
@@ -36,6 +36,24 @@ func replaceLocationHost(urlString string, rrr *ResponseRewriteRuleSet, useTLS b
//Do not modify location header
return urlString, nil
}
+
+ //Issue #626: Check if the location header is another subdomain with port
+ //E.g. Proxy config: blog.example.com -> 127.0.0.1:80
+ //Check if it is actually redirecting to (*.)blog.example.com:8080 instead of current domain
+ //like Location: http://x.blog.example.com:1234/
+ _, newLocationPort, err := net.SplitHostPort(u.Host)
+ if (newLocationPort == "80" || newLocationPort == "443") && err == nil {
+ //Port 80 or 443, some web server use this to switch between http and https
+ //E.g. http://example.com:80 -> https://example.com:443
+ //E.g. http://example.com:443 -> https://example.com:80
+ //That usually means the user have invalidly configured the web server to use port 80 or 443
+ //for http or https. We should not modify the location header in this case.
+
+ } else {
+ //Other port numbers. Do not modify location header
+ return urlString, nil
+ }
+
u.Host = rrr.OriginalHost
if strings.Contains(rrr.ProxyDomain, "/") {
From a3cccee16248bd130d5c3a6da984c197bcef3b13 Mon Sep 17 00:00:00 2001
From: Toby Chui
Date: Sun, 6 Apr 2025 16:50:59 +0800
Subject: [PATCH 06/16] Added "blocks common exploits" module
- Added blocks common exploits prototype
- Added bot detection function (not included in dpcore yet) #615
---
src/go.mod | 18 +---
src/go.sum | 79 +---------------
src/mod/dynamicproxy/exploits/exploits.go | 108 ++++++++++++++++++++++
3 files changed, 113 insertions(+), 92 deletions(-)
create mode 100644 src/mod/dynamicproxy/exploits/exploits.go
diff --git a/src/go.mod b/src/go.mod
index cad79ff..e722aa9 100644
--- a/src/go.mod
+++ b/src/go.mod
@@ -5,11 +5,11 @@ go 1.22.0
toolchain go1.22.2
require (
+ github.com/armon/go-radix v1.0.0
github.com/boltdb/bolt v1.3.1
github.com/docker/docker v27.0.0+incompatible
github.com/go-acme/lego/v4 v4.21.0
github.com/go-ping/ping v1.1.0
- github.com/go-session/session v3.1.2+incompatible
github.com/google/uuid v1.6.0
github.com/gorilla/sessions v1.2.2
github.com/gorilla/websocket v1.5.1
@@ -19,7 +19,6 @@ require (
github.com/shirou/gopsutil/v4 v4.25.1
github.com/syndtr/goleveldb v1.0.0
golang.org/x/net v0.33.0
- golang.org/x/sys v0.28.0
golang.org/x/text v0.21.0
)
@@ -27,30 +26,22 @@ require (
cloud.google.com/go/auth v0.13.0 // indirect
cloud.google.com/go/auth/oauth2adapt v0.2.6 // indirect
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resourcegraph/armresourcegraph v0.9.0 // indirect
- github.com/armon/go-radix v1.0.0 // indirect
github.com/benbjohnson/clock v1.3.0 // indirect
github.com/ebitengine/purego v0.8.2 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
- github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
github.com/golang/snappy v0.0.1 // indirect
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.128 // indirect
+ github.com/monperrus/crawler-user-agents v1.1.0 // indirect
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect
github.com/peterhellberg/link v1.2.0 // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
github.com/shopspring/decimal v1.3.1 // indirect
- github.com/tidwall/btree v0.0.0-20191029221954-400434d76274 // indirect
- github.com/tidwall/buntdb v1.1.2 // indirect
- github.com/tidwall/gjson v1.12.1 // indirect
- github.com/tidwall/grect v0.0.0-20161006141115-ba9a043346eb // indirect
- github.com/tidwall/match v1.1.1 // indirect
- github.com/tidwall/pretty v1.2.0 // indirect
- github.com/tidwall/rtree v0.0.0-20180113144539-6cd427091e0e // indirect
- github.com/tidwall/tinyqueue v0.0.0-20180302190814-1e39f5511563 // indirect
github.com/tjfoc/gmsm v1.4.1 // indirect
github.com/vultr/govultr/v3 v3.9.1 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect
go.mongodb.org/mongo-driver v1.12.0 // indirect
+ golang.org/x/sys v0.28.0 // indirect
)
require (
@@ -111,11 +102,9 @@ require (
github.com/go-jose/go-jose/v4 v4.0.4 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
- github.com/go-oauth2/oauth2/v4 v4.5.2
github.com/go-resty/resty/v2 v2.16.2 // indirect
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
github.com/goccy/go-json v0.10.4 // indirect
- github.com/gofrs/uuid v4.4.0+incompatible
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt/v4 v4.5.1 // indirect
github.com/google/go-querystring v1.1.0 // indirect
@@ -187,7 +176,6 @@ require (
github.com/transip/gotransip/v6 v6.26.0 // indirect
github.com/ultradns/ultradns-go-sdk v1.8.0-20241010134910-243eeec // indirect
github.com/vinyldns/go-vinyldns v0.9.16 // indirect
- github.com/xlzd/gotp v0.1.0
github.com/yandex-cloud/go-genproto v0.0.0-20241220122821-aeb3b05efd1c // indirect
github.com/yandex-cloud/go-sdk v0.0.0-20241220131134-2393e243c134 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect
diff --git a/src/go.sum b/src/go.sum
index 6a52464..64d276a 100644
--- a/src/go.sum
+++ b/src/go.sum
@@ -76,15 +76,11 @@ github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jB
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87 h1:xPMsUicZ3iosVPSIP7bW5EcGUzjiiMl1OYTe14y/R24=
github.com/OpenDNS/vegadns2client v0.0.0-20180418235048-a3fa4a771d87/go.mod h1:iGLljf5n9GjT6kc0HBvyI1nOKnGQbNB66VzSNbK5iks=
-github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
-github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/aliyun/alibaba-cloud-sdk-go v1.63.72 h1:HvFZUzEbNvfe8F2Mg0wBGv90bPhWDxgVtDHR5zoBOU0=
github.com/aliyun/alibaba-cloud-sdk-go v1.63.72/go.mod h1:SOSDHfe1kX91v3W5QiBsWSLqeLxImobbMX1mxrFHsVQ=
-github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
-github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
@@ -186,7 +182,6 @@ github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
-github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
@@ -202,8 +197,6 @@ github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
-github.com/gavv/httpexpect v2.0.0+incompatible h1:1X9kcRshkSKEjNJJxX9Y9mQ5BRfbxU5kORdjhlA1yX8=
-github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc=
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-acme/lego/v4 v4.21.0 h1:arEW+8o5p7VI8Bk1kr/PDlgD1DrxtTH1gJ4b7mehL8o=
@@ -222,16 +215,12 @@ github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
-github.com/go-oauth2/oauth2/v4 v4.5.2 h1:CuZhD3lhGuI6aNLyUbRHXsgG2RwGRBOuCBfd4WQKqBQ=
-github.com/go-oauth2/oauth2/v4 v4.5.2/go.mod h1:wk/2uLImWIa9VVQDgxz99H2GDbhmfi/9/Xr+GvkSUSQ=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-ping/ping v1.1.0 h1:3MCGhVX4fyEUuhsfwPrsEdQw6xspHkv5zHsiSoDFZYw=
github.com/go-ping/ping v1.1.0/go.mod h1:xIFjORFzTxqIV/tDVGO4eDy/bLuSyawEeojSm3GfRGk=
github.com/go-resty/resty/v2 v2.16.2 h1:CpRqTjIzq/rweXUt9+GxzzQdlkqMdt8Lm/fuK/CAbAg=
github.com/go-resty/resty/v2 v2.16.2/go.mod h1:0fHAoK7JoBy/Ch36N8VFeMsK7xQOHhvWaC3iOktwmIU=
-github.com/go-session/session v3.1.2+incompatible h1:yStchEObKg4nk2F7JGE7KoFIrA/1Y078peagMWcrncg=
-github.com/go-session/session v3.1.2+incompatible/go.mod h1:8B3iivBQjrz/JtC68Np2T1yBBLxTan3mn/3OM0CyRt0=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
@@ -242,16 +231,11 @@ github.com/gobs/pretty v0.0.0-20180724170744-09732c25a95b h1:/vQ+oYKu+JoyaMPDsv5
github.com/gobs/pretty v0.0.0-20180724170744-09732c25a95b/go.mod h1:Xo4aNUOrJnVruqWQJBtW6+bTBDTniY8yZum5rF3b5jw=
github.com/goccy/go-json v0.10.4 h1:JSwxQzIqKfmFX1swYPpUThQZp/Ka4wzJdK0LWVytLPM=
github.com/goccy/go-json v0.10.4/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
-github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA=
-github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=
-github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
-github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
-github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
@@ -307,7 +291,6 @@ github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLe
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/s2a-go v0.1.8 h1:zZDs9gcbt9ZPLV0ndSyQk6Kacx2g/X+SKYovpnz3SMM=
github.com/google/s2a-go v0.1.8/go.mod h1:6iNWHTpQ+nfNRN5E00MSdfDwVesa8hhS32PhPO8deJA=
-github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
@@ -320,7 +303,6 @@ github.com/googleapis/gax-go/v2 v2.14.0 h1:f+jMrjBPl+DL9nI4IQzLUxMq7XrAqFYB7hBPq
github.com/googleapis/gax-go/v2 v2.14.0/go.mod h1:lhBCnjdLrWRaPvLWhmc8IS24m9mr07qSYnHncrgo+zk=
github.com/gophercloud/gophercloud v1.14.1 h1:DTCNaTVGl8/cFu58O1JwWgis9gtISAFONqpMKNg/Vpw=
github.com/gophercloud/gophercloud v1.14.1/go.mod h1:aAVqcocTSXh2vYFZ1JTvx4EQmfgzxRcNupUfxZbBNDM=
-github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/csrf v1.7.2 h1:oTUjx0vyf2T+wkrx09Trsev1TE+/EbDAeHtSTbtC2eI=
github.com/gorilla/csrf v1.7.2/go.mod h1:F1Fj3KG23WYHE6gozCmBAezKookxbIvUJT+121wTuLk=
@@ -377,8 +359,6 @@ github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.128/go.mod h1:JWz2ujO9X3oU5wb6
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df h1:MZf03xP9WdakyXhOWuAD5uPK3wHh96wCsqe3hCMKh8E=
github.com/iij/doapi v0.0.0-20190504054126-0bbf12d6d7df/go.mod h1:QMZY7/J/KSQEhKWFeDesPjMj+wCHReeknARU3wqlyN4=
-github.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk=
-github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/infobloxopen/infoblox-go-client v1.1.1 h1:728A6LbLjptj/7kZjHyIxQnm768PWHfGFm0HH8FnbtU=
github.com/infobloxopen/infoblox-go-client v1.1.1/go.mod h1:BXiw7S2b9qJoM8MS40vfgCNB2NLHGusk1DtO16BD9zI=
@@ -395,11 +375,9 @@ github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/u
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
-github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
-github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k=
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 h1:qGQQKEcAR99REcMpsXCp3lJ03zYT1PkRd3kQGPn9GVg=
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw=
github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6 h1:IsMZxCuZqKuao2vNdfD82fjjgPLfyHLpR41Z88viRWs=
@@ -408,8 +386,6 @@ github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvW
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.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
-github.com/klauspost/compress v1.15.0 h1:xqfchp4whNFxn5A4XFyyYtitiWI8Hy5EW59jEwcyL6U=
-github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b h1:udzkj9S/zlT5X367kqJis0QP7YMxobob6zhzq6Yre00=
github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b/go.mod h1:pcaDhQK0/NJZEvtCO0qQPPropqV0sJOJ6YW7X+9kRwM=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
@@ -493,11 +469,11 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
+github.com/monperrus/crawler-user-agents v1.1.0 h1:Xy8ZrhizT+y2FONWFFdKOP+3BhH97BDLuG7W/MswoGI=
+github.com/monperrus/crawler-user-agents v1.1.0/go.mod h1:GfRyKbsbxSrRxTPYnVi4U/0stQd6BcFCxDy6i6IxQ0M=
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
-github.com/moul/http2curl v1.0.0 h1:dRMWoAtb+ePxMlLkrCbAqh4TlPHXvoGUSQ323/9Zahs=
-github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04 h1:o6uBwrhM5C8Ll3MAAxrQxRHEu7FkapwTuI2WmL1rw4g=
github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04/go.mod h1:5sN+Lt1CaY4wsPvgQH/jsuJi4XO2ssZbdsIizr4CVC8=
@@ -531,7 +507,6 @@ github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
-github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0=
github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
@@ -609,10 +584,7 @@ github.com/sacloud/packages-go v0.0.10 h1:UiQGjy8LretewkRhsuna1TBM9Vz/l9FoYpQx+D
github.com/sacloud/packages-go v0.0.10/go.mod h1:f8QITBh9z4IZc4yE9j21Q8b0sXEMwRlRmhhjWeDVTYs=
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.30 h1:yoKAVkEVwAqbGbR8n87rHQ1dulL25rKloGadb3vm770=
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.30/go.mod h1:sH0u6fq6x4R5M7WxkoQFY/o7UaiItec0o1LinLCJNq8=
-github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
-github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
-github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/shirou/gopsutil/v4 v4.25.1 h1:QSWkTc+fu9LTAWfkZwZ6j8MSUk4A2LV7rbH0ZqmLjXs=
github.com/shirou/gopsutil/v4 v4.25.1/go.mod h1:RoUCUpndaJFtT+2zsZzzmhvbfGoDCJ7nFXKJf8GqJbI=
github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
@@ -628,7 +600,6 @@ github.com/smartystreets/assertions v1.0.1 h1:voD4ITNjPL5jjBfgR/r8fPIIBrliWrWHei
github.com/smartystreets/assertions v1.0.1/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM=
github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9 h1:hp2CYQUINdZMHdvTdXtPOY2ainKl4IoMcpAXEf2xj3Q=
github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM=
-github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/smartystreets/gunit v1.0.4 h1:tpTjnuH7MLlqhoD21vRoMZbMIi5GmBsAJDFyF67GhZA=
github.com/smartystreets/gunit v1.0.4/go.mod h1:EH5qMBab2UclzXUcpR8b93eHsIlp9u+pDQIRp5DZNzQ=
@@ -661,7 +632,6 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
-github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
@@ -675,25 +645,6 @@ github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1065 h1:krc
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1065/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1065 h1:aEFtLD1ceyeljQXB1S2BjN0zjTkf0X3XmpuxFIiC29w=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1065/go.mod h1:HWvwy09hFSMXrj9SMvVRWV4U7rZO3l+WuogyNuxiT3M=
-github.com/tidwall/btree v0.0.0-20191029221954-400434d76274 h1:G6Z6HvJuPjG6XfNGi/feOATzeJrfgTNJY+rGrHbA04E=
-github.com/tidwall/btree v0.0.0-20191029221954-400434d76274/go.mod h1:huei1BkDWJ3/sLXmO+bsCNELL+Bp2Kks9OLyQFkzvA8=
-github.com/tidwall/buntdb v1.1.2 h1:noCrqQXL9EKMtcdwJcmuVKSEjqu1ua99RHHgbLTEHRo=
-github.com/tidwall/buntdb v1.1.2/go.mod h1:xAzi36Hir4FarpSHyfuZ6JzPJdjRZ8QlLZSntE2mqlI=
-github.com/tidwall/gjson v1.3.4/go.mod h1:P256ACg0Mn+j1RXIDXoss50DeIABTYK1PULOJHhxOls=
-github.com/tidwall/gjson v1.12.1 h1:ikuZsLdhr8Ws0IdROXUS1Gi4v9Z4pGqpX/CvJkxvfpo=
-github.com/tidwall/gjson v1.12.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
-github.com/tidwall/grect v0.0.0-20161006141115-ba9a043346eb h1:5NSYaAdrnblKByzd7XByQEJVT8+9v0W/tIY0Oo4OwrE=
-github.com/tidwall/grect v0.0.0-20161006141115-ba9a043346eb/go.mod h1:lKYYLFIr9OIgdgrtgkZ9zgRxRdvPYsExnYBsEAd8W5M=
-github.com/tidwall/match v1.0.1/go.mod h1:LujAq0jyVjBy028G1WhWfIzbpQfMO8bBZ6Tyb0+pL9E=
-github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
-github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
-github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
-github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
-github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
-github.com/tidwall/rtree v0.0.0-20180113144539-6cd427091e0e h1:+NL1GDIUOKxVfbp2KoJQD9cTQ6dyP2co9q4yzmT9FZo=
-github.com/tidwall/rtree v0.0.0-20180113144539-6cd427091e0e/go.mod h1:/h+UnNGt0IhNNJLkGikcdcJqm66zGD/uJGMRxK/9+Ao=
-github.com/tidwall/tinyqueue v0.0.0-20180302190814-1e39f5511563 h1:Otn9S136ELckZ3KKDyCkxapfufrqDqwmGjcHfAyXRrE=
-github.com/tidwall/tinyqueue v0.0.0-20180302190814-1e39f5511563/go.mod h1:mLqSmt7Dv/CNneF2wfcChfN1rvapyQr01LGKnKex0DQ=
github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho=
github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
@@ -705,11 +656,6 @@ github.com/uber/jaeger-lib v2.4.1+incompatible h1:td4jdvLcExb4cBISKIpHuGoVXh+dVK
github.com/uber/jaeger-lib v2.4.1+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U=
github.com/ultradns/ultradns-go-sdk v1.8.0-20241010134910-243eeec h1:2s/ghQ8wKE+UzD/hf3P4Gd1j0JI9ncbxv+nsypPoUYI=
github.com/ultradns/ultradns-go-sdk v1.8.0-20241010134910-243eeec/go.mod h1:BZr7Qs3ku1ckpqed8tCRSqTlp8NAeZfAVpfx4OzXMss=
-github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
-github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
-github.com/valyala/fasthttp v1.34.0 h1:d3AAQJ2DRcxJYHm7OXNXtXt2as1vMDfxeIcFvhmGGm4=
-github.com/valyala/fasthttp v1.34.0/go.mod h1:epZA5N+7pY6ZaEKRmstzOuYJx9HI8DI1oaCGZpdH4h0=
-github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
github.com/vinyldns/go-vinyldns v0.9.16 h1:GZJStDkcCk1F1AcRc64LuuMh+ENL8pHA0CVd4ulRMcQ=
github.com/vinyldns/go-vinyldns v0.9.16/go.mod h1:5qIJOdmzAnatKjurI+Tl4uTus7GJKJxb+zitufjHs3Q=
github.com/vultr/govultr/v3 v3.9.1 h1:uxSIb8Miel7tqTs3ee+z3t+JelZikwqBBsZzCOPBy/8=
@@ -717,27 +663,12 @@ github.com/vultr/govultr/v3 v3.9.1/go.mod h1:Rd8ebpXm7jxH3MDmhnEs+zrlYW212ouhx+H
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
-github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
-github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
-github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
-github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
-github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
-github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
-github.com/xlzd/gotp v0.1.0 h1:37blvlKCh38s+fkem+fFh7sMnceltoIEBYTVXyoa5Po=
-github.com/xlzd/gotp v0.1.0/go.mod h1:ndLJ3JKzi3xLmUProq4LLxCuECL93dG9WASNLpHz8qg=
-github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0 h1:6fRhSjgLCkTD3JnJxvaJ4Sj+TYblw757bqYgZaOq5ZY=
-github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=
github.com/yandex-cloud/go-genproto v0.0.0-20241220122821-aeb3b05efd1c h1:Rnr+lDYXVkP+3eT8/d68iq4G/UeIhyCQk+HKa8toTvg=
github.com/yandex-cloud/go-genproto v0.0.0-20241220122821-aeb3b05efd1c/go.mod h1:0LDD/IZLIUIV4iPH+YcF+jysO3jkSvADFGm4dCAuwQo=
github.com/yandex-cloud/go-sdk v0.0.0-20241220131134-2393e243c134 h1:qmpz0Kvr9GAng8LAhRcKIpY71CEAcL3EBkftVlsP5Cw=
github.com/yandex-cloud/go-sdk v0.0.0-20241220131134-2393e243c134/go.mod h1:KgZCJrxdhdw/sKhTQ/M3S9WOLri2PCnBlc4C3s+PfKY=
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
-github.com/yudai/gojsondiff v1.0.0 h1:27cbfqXLVEJ1o8I6v3y9lg8Ydm53EKqHXAOMxEGlCOA=
-github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=
-github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M=
-github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=
-github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
@@ -783,7 +714,6 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
@@ -861,7 +791,6 @@ golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE=
golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -903,7 +832,6 @@ golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200918174421-af09f7315aff/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -919,7 +847,6 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/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-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -1009,7 +936,6 @@ google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9Ywl
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
-google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
@@ -1071,7 +997,6 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
diff --git a/src/mod/dynamicproxy/exploits/exploits.go b/src/mod/dynamicproxy/exploits/exploits.go
new file mode 100644
index 0000000..ff5e6dd
--- /dev/null
+++ b/src/mod/dynamicproxy/exploits/exploits.go
@@ -0,0 +1,108 @@
+package exploits
+
+/*
+ exploits.go
+
+ This file is used to define routing rules that blocks common exploits.
+
+*/
+
+import (
+ _ "embed"
+ "net/http"
+ "regexp"
+
+ agents "github.com/monperrus/crawler-user-agents"
+)
+
+type Detector struct {
+}
+
+func NewExploitDetector() *Detector {
+ return &Detector{}
+}
+
+// RequestContainCommonExploits checks if the request contains common exploits
+// such as SQL injection, file injection, and other common attack patterns.
+func (d *Detector) RequestContainCommonExploits(r *http.Request) bool {
+ query := r.URL.RawQuery
+ userAgent := r.UserAgent()
+
+ // Block SQL injections
+ sqlInjectionPatterns := []string{
+ `union.*select.*\(`,
+ `union.*all.*select.*`,
+ `concat.*\(`,
+ }
+ for _, pattern := range sqlInjectionPatterns {
+ if match, _ := regexp.MatchString(pattern, query); match {
+ return true
+ }
+ }
+
+ // Block file injections
+ fileInjectionPatterns := []string{
+ `[a-zA-Z0-9_]=http://`,
+ `[a-zA-Z0-9_]=(\.\.//?)+`,
+ `[a-zA-Z0-9_]=/([a-z0-9_.]//?)+`,
+ }
+ for _, pattern := range fileInjectionPatterns {
+ if match, _ := regexp.MatchString(pattern, query); match {
+ return true
+ }
+ }
+
+ // Block common exploits
+ commonExploitPatterns := []string{
+ `(<|%3C).*script.*(>|%3E)`,
+ `GLOBALS(=|\[|\%[0-9A-Z]{0,2})`,
+ `_REQUEST(=|\[|\%[0-9A-Z]{0,2})`,
+ `proc/self/environ`,
+ `mosConfig_[a-zA-Z_]{1,21}(=|\%3D)`,
+ `base64_(en|de)code\(.*\)`,
+ }
+ for _, pattern := range commonExploitPatterns {
+ if match, _ := regexp.MatchString(pattern, query); match {
+ return true
+ }
+ }
+
+ // Block spam
+ spamPatterns := []string{
+ `\b(ultram|unicauca|valium|viagra|vicodin|xanax|ypxaieo)\b`,
+ `\b(erections|hoodia|huronriveracres|impotence|levitra|libido)\b`,
+ `\b(ambien|blue\spill|cialis|cocaine|ejaculation|erectile)\b`,
+ `\b(lipitor|phentermin|pro[sz]ac|sandyauer|tramadol|troyhamby)\b`,
+ }
+ for _, pattern := range spamPatterns {
+ if match, _ := regexp.MatchString(pattern, query); match {
+ return true
+ }
+ }
+
+ // Block user agents
+ userAgentPatterns := []string{
+ `Indy Library`,
+ `libwww-perl`,
+ `GetRight`,
+ `GetWeb!`,
+ `Go!Zilla`,
+ `Download Demon`,
+ `Go-Ahead-Got-It`,
+ `TurnitinBot`,
+ `GrabNet`,
+ }
+ for _, pattern := range userAgentPatterns {
+ if match, _ := regexp.MatchString(pattern, userAgent); match {
+ return true
+ }
+ }
+
+ return false
+}
+
+// RequestIsMadeByBots checks if the request is made by bots or crawlers
+func (d *Detector) RequestIsMadeByBots(r *http.Request) bool {
+ userAgent := r.UserAgent()
+ return agents.IsCrawler(userAgent)
+}
From ca7cd0476c06b6285de27b4cf15fa643e4b31a2a Mon Sep 17 00:00:00 2001
From: Toby Chui
Date: Sun, 6 Apr 2025 17:05:30 +0800
Subject: [PATCH 07/16] Updated location rewrite logic
- Updated to a much more relax logic for handling domain with port redirection
---
src/mod/dynamicproxy/dpcore/utils.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/mod/dynamicproxy/dpcore/utils.go b/src/mod/dynamicproxy/dpcore/utils.go
index edac154..a30f084 100644
--- a/src/mod/dynamicproxy/dpcore/utils.go
+++ b/src/mod/dynamicproxy/dpcore/utils.go
@@ -49,7 +49,7 @@ func replaceLocationHost(urlString string, rrr *ResponseRewriteRuleSet, useTLS b
//That usually means the user have invalidly configured the web server to use port 80 or 443
//for http or https. We should not modify the location header in this case.
- } else {
+ } else if strings.Contains(u.Host, ":") && err == nil {
//Other port numbers. Do not modify location header
return urlString, nil
}
From b863a9720f2edc573566e515b6081be938a5ab81 Mon Sep 17 00:00:00 2001
From: Toby Chui
Date: Mon, 7 Apr 2025 20:02:04 +0800
Subject: [PATCH 08/16] Fixed #629
- Added $remote_ip to remote port number from remote address
---
src/mod/dynamicproxy/rewrite/headervars.go | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/src/mod/dynamicproxy/rewrite/headervars.go b/src/mod/dynamicproxy/rewrite/headervars.go
index d57c9cc..d7bd44d 100644
--- a/src/mod/dynamicproxy/rewrite/headervars.go
+++ b/src/mod/dynamicproxy/rewrite/headervars.go
@@ -2,6 +2,7 @@ package rewrite
import (
"fmt"
+ "net"
"net/http"
"strings"
)
@@ -14,6 +15,11 @@ func GetHeaderVariableValuesFromRequest(r *http.Request) map[string]string {
// Request-specific variables
vars["$host"] = r.Host
vars["$remote_addr"] = r.RemoteAddr
+ remoteIP, _, err := net.SplitHostPort(r.RemoteAddr)
+ if err != nil {
+ remoteIP = r.RemoteAddr // Fallback to the full RemoteAddr if parsing fails
+ }
+ vars["$remote_ip"] = remoteIP
vars["$request_uri"] = r.RequestURI
vars["$request_method"] = r.Method
vars["$content_length"] = fmt.Sprintf("%d", r.ContentLength)
From e961e52dea70a4e8831e4fbb174b10df4db4480d Mon Sep 17 00:00:00 2001
From: Niklas Roth <36939232+Nirostar@users.noreply.github.com>
Date: Tue, 8 Apr 2025 14:44:11 +0200
Subject: [PATCH 09/16] Fix IPv6 whitelisting for Link-Local addresses by
removing the scope ID
---
src/mod/netutils/ipmatch.go | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/src/mod/netutils/ipmatch.go b/src/mod/netutils/ipmatch.go
index a9733c9..d969454 100644
--- a/src/mod/netutils/ipmatch.go
+++ b/src/mod/netutils/ipmatch.go
@@ -58,6 +58,11 @@ func GetRequesterIP(r *http.Request) string {
requesterRawIp = requesterRawIp[1 : len(requesterRawIp)-1]
}
+ // Trim away scope ID if present (e.g. %eth0 in IPv6)
+ if i := strings.Index(requesterRawIp, "%"); i != -1 {
+ requesterRawIp = requesterRawIp[:i]
+ }
+
return requesterRawIp
}
From 23eeeee7015abe7b43797f7a9c1ce666994bb04d Mon Sep 17 00:00:00 2001
From: Niklas Roth <36939232+Nirostar@users.noreply.github.com>
Date: Tue, 8 Apr 2025 15:06:20 +0200
Subject: [PATCH 10/16] Move Scope ID handling into CIDR check
---
src/mod/netutils/ipmatch.go | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/src/mod/netutils/ipmatch.go b/src/mod/netutils/ipmatch.go
index d969454..b5f6ff4 100644
--- a/src/mod/netutils/ipmatch.go
+++ b/src/mod/netutils/ipmatch.go
@@ -57,12 +57,7 @@ func GetRequesterIP(r *http.Request) string {
//e.g. [15c4:cbb4:cc98:4291:ffc1:3a46:06a1:51a7]
requesterRawIp = requesterRawIp[1 : len(requesterRawIp)-1]
}
-
- // Trim away scope ID if present (e.g. %eth0 in IPv6)
- if i := strings.Index(requesterRawIp, "%"); i != -1 {
- requesterRawIp = requesterRawIp[:i]
- }
-
+
return requesterRawIp
}
@@ -92,6 +87,11 @@ func MatchIpWildcard(ipAddress, wildcard string) bool {
// Match ip address with CIDR
func MatchIpCIDR(ip string, cidr string) bool {
+ // Trim away scope ID if present in IP (e.g. fe80::1%eth0)
+ if i := strings.Index(ip, "%"); i != -1 {
+ ip = ip[:i]
+ }
+
// parse the CIDR string
_, cidrnet, err := net.ParseCIDR(cidr)
if err != nil {
From b23b967165247dfe94d5dbccd0d77f2212533a7a Mon Sep 17 00:00:00 2001
From: WHFo <996579747@qq.com>
Date: Fri, 11 Apr 2025 12:02:28 +0800
Subject: [PATCH 11/16] Update httprp.html
Prevent the browser from filling the saved Zoraxy login account into the input searchInput
---
src/web/components/httprp.html | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/web/components/httprp.html b/src/web/components/httprp.html
index 485148f..272a420 100644
--- a/src/web/components/httprp.html
+++ b/src/web/components/httprp.html
@@ -48,6 +48,8 @@
+
+
@@ -782,4 +784,4 @@
filterProxyList();
});
});
-
\ No newline at end of file
+
From 0c8dfd8aa0fdac6e8592a52a6c9db84cde1bfe3c Mon Sep 17 00:00:00 2001
From: Toby Chui
Date: Sat, 12 Apr 2025 11:32:22 +0800
Subject: [PATCH 12/16] Added http proxy list link port
- Added port number and http proto to http proxy list link #635
---
src/mod/netutils/ipmatch.go | 4 ++--
src/web/components/httprp.html | 21 ++++++++++++++++-----
src/web/components/status.html | 1 -
3 files changed, 18 insertions(+), 8 deletions(-)
diff --git a/src/mod/netutils/ipmatch.go b/src/mod/netutils/ipmatch.go
index b5f6ff4..942bf56 100644
--- a/src/mod/netutils/ipmatch.go
+++ b/src/mod/netutils/ipmatch.go
@@ -57,7 +57,7 @@ func GetRequesterIP(r *http.Request) string {
//e.g. [15c4:cbb4:cc98:4291:ffc1:3a46:06a1:51a7]
requesterRawIp = requesterRawIp[1 : len(requesterRawIp)-1]
}
-
+
return requesterRawIp
}
@@ -91,7 +91,7 @@ func MatchIpCIDR(ip string, cidr string) bool {
if i := strings.Index(ip, "%"); i != -1 {
ip = ip[:i]
}
-
+
// parse the CIDR string
_, cidrnet, err := net.ParseCIDR(cidr)
if err != nil {
diff --git a/src/web/components/httprp.html b/src/web/components/httprp.html
index 272a420..1a9d653 100644
--- a/src/web/components/httprp.html
+++ b/src/web/components/httprp.html
@@ -47,10 +47,10 @@
-
@@ -144,12 +144,23 @@
if (subd.Disabled){
enableChecked = "";
}
-
+ let httpProto = "http://";
+ if ($("#tls").checkbox("is checked")) {
+ httpProto = "https://";
+ } else {
+ httpProto = "http://";
+ }
+ let hostnameRedirectPort = currentListeningPort;
+ if (hostnameRedirectPort == 80 || hostnameRedirectPort == 443){
+ hostnameRedirectPort = "";
+ }else{
+ hostnameRedirectPort = ":" + hostnameRedirectPort;
+ }
let aliasDomains = ``;
if (subd.MatchingDomainAlias != undefined && subd.MatchingDomainAlias.length > 0){
aliasDomains = `Alias: `;
subd.MatchingDomainAlias.forEach(alias => {
- aliasDomains += `${alias} , `;
+ aliasDomains += `${alias} , `;
});
aliasDomains = aliasDomains.substr(0, aliasDomains.length - 2); //Remove the last tailing seperator
aliasDomains += ` `;
@@ -157,7 +168,7 @@
$("#httpProxyList").append(`
- ${subd.RootOrMatchingDomain} ${inboundTlsIcon}
+ ${subd.RootOrMatchingDomain} ${inboundTlsIcon}
${aliasDomains}
diff --git a/src/web/components/status.html b/src/web/components/status.html
index affaaf8..09910ea 100644
--- a/src/web/components/status.html
+++ b/src/web/components/status.html
@@ -524,7 +524,6 @@
$("#tls").checkbox("set checked");
}else{
$(".tlsEnabledOnly").addClass('disabled');
- $(".tlsEnabledOnly").addClass('disabled');
}
//Initiate the input listener on the checkbox
From 4f026e8c07a1678956fec9663423b2be7864ab9f Mon Sep 17 00:00:00 2001
From: James Elliott
Date: Mon, 21 Apr 2025 12:57:59 +1000
Subject: [PATCH 13/16] fix(authelia): original headers
This fixes the original headers imparted to authelia.
---
src/mod/auth/sso/authelia/authelia.go | 54 ++++++++++++++++++++-------
1 file changed, 41 insertions(+), 13 deletions(-)
diff --git a/src/mod/auth/sso/authelia/authelia.go b/src/mod/auth/sso/authelia/authelia.go
index 075e97f..d86f374 100644
--- a/src/mod/auth/sso/authelia/authelia.go
+++ b/src/mod/auth/sso/authelia/authelia.go
@@ -3,9 +3,10 @@ package authelia
import (
"encoding/json"
"errors"
- "fmt"
+ "net"
"net/http"
"net/url"
+ "strings"
"imuslab.com/zoraxy/mod/database"
"imuslab.com/zoraxy/mod/info/logger"
@@ -93,25 +94,20 @@ func (ar *AutheliaRouter) HandleAutheliaAuth(w http.ResponseWriter, r *http.Requ
protocol = "https"
}
- autheliaBaseURL := protocol + "://" + ar.options.AutheliaURL
- //Remove tailing slash if any
- if autheliaBaseURL[len(autheliaBaseURL)-1] == '/' {
- autheliaBaseURL = autheliaBaseURL[:len(autheliaBaseURL)-1]
+ autheliaURL := &url.URL{
+ Scheme: protocol,
+ Host: ar.options.AutheliaURL,
}
//Make a request to Authelia to verify the request
- req, err := http.NewRequest("POST", autheliaBaseURL+"/api/verify", nil)
+ req, err := http.NewRequest("POST", autheliaURL.JoinPath("api", "verify").String(), nil)
if err != nil {
ar.options.Logger.PrintAndLog("Authelia", "Unable to create request", err)
w.WriteHeader(401)
return errors.New("unauthorized")
}
- scheme := "http"
- if r.TLS != nil {
- scheme = "https"
- }
- req.Header.Add("X-Original-URL", fmt.Sprintf("%s://%s", scheme, r.Host))
+ originalURL := rOriginalHeaders(r, req)
// Copy cookies from the incoming request
for _, cookie := range r.Cookies() {
@@ -127,10 +123,42 @@ func (ar *AutheliaRouter) HandleAutheliaAuth(w http.ResponseWriter, r *http.Requ
}
if resp.StatusCode != 200 {
- redirectURL := autheliaBaseURL + "/?rd=" + url.QueryEscape(scheme+"://"+r.Host+r.URL.String()) + "&rm=" + r.Method
- http.Redirect(w, r, redirectURL, http.StatusSeeOther)
+ redirectURL := autheliaURL.JoinPath()
+
+ query := redirectURL.Query()
+
+ query.Set("rd", originalURL.String())
+ query.Set("rm", r.Method)
+
+ http.Redirect(w, r, redirectURL.String(), http.StatusSeeOther)
return errors.New("unauthorized")
}
return nil
}
+
+func rOriginalHeaders(r, req *http.Request) *url.URL {
+ if r.RemoteAddr != "" {
+ before, _, _ := strings.Cut(r.RemoteAddr, ":")
+
+ if ip := net.ParseIP(before); ip != nil {
+ req.Header.Set("X-Forwarded-For", ip.String())
+ }
+ }
+
+ originalURL := &url.URL{
+ Scheme: "http",
+ Host: r.Host,
+ Path: r.URL.Path,
+ RawPath: r.URL.RawPath,
+ }
+
+ if r.TLS != nil {
+ originalURL.Scheme = "https"
+ }
+
+ req.Header.Add("X-Forwarded-Method", r.Method)
+ req.Header.Add("X-Original-URL", originalURL.String())
+
+ return originalURL
+}
From 6750c7fe3d5b4ae221c863cddfe9f514aa273a07 Mon Sep 17 00:00:00 2001
From: Toby Chui
Date: Tue, 22 Apr 2025 07:15:30 +0800
Subject: [PATCH 14/16] Added wip plugin store
- Added plugin store snippet
- Added plugin list sync functions
- Work in progress install / uninstall plugin function
---
src/api.go | 3 +
src/mod/plugins/handler.go | 2 +
src/mod/plugins/plugins.go | 2 +-
src/mod/plugins/store.go | 118 ++++++++++++++
src/mod/plugins/store_test.go | 52 +++++++
src/mod/plugins/typdef.go | 6 +
src/start.go | 18 ++-
src/web/components/plugins.html | 7 +
src/web/main.css | 6 +
src/web/snippet/pluginstore.html | 258 +++++++++++++++++++++++++++++++
10 files changed, 469 insertions(+), 3 deletions(-)
create mode 100644 src/mod/plugins/store.go
create mode 100644 src/mod/plugins/store_test.go
create mode 100644 src/web/snippet/pluginstore.html
diff --git a/src/api.go b/src/api.go
index 0991823..52f6499 100644
--- a/src/api.go
+++ b/src/api.go
@@ -234,6 +234,9 @@ func RegisterPluginAPIs(authRouter *auth.RouterDef) {
authRouter.HandleFunc("/api/plugins/groups/add", pluginManager.HandleAddPluginToGroup)
authRouter.HandleFunc("/api/plugins/groups/remove", pluginManager.HandleRemovePluginFromGroup)
authRouter.HandleFunc("/api/plugins/groups/deleteTag", pluginManager.HandleRemovePluginGroup)
+
+ authRouter.HandleFunc("/api/plugins/store/list", pluginManager.HandleListDownloadablePlugins)
+ authRouter.HandleFunc("/api/plugins/store/resync", pluginManager.HandleResyncPluginList)
}
// Register the APIs for Auth functions, due to scoping issue some functions are defined here
diff --git a/src/mod/plugins/handler.go b/src/mod/plugins/handler.go
index 8e95805..93930ab 100644
--- a/src/mod/plugins/handler.go
+++ b/src/mod/plugins/handler.go
@@ -249,3 +249,5 @@ func (m *Manager) HandleDisablePlugin(w http.ResponseWriter, r *http.Request) {
utils.SendOK(w)
}
+
+/* Plugin Store */
diff --git a/src/mod/plugins/plugins.go b/src/mod/plugins/plugins.go
index bb4c64b..5971ea6 100644
--- a/src/mod/plugins/plugins.go
+++ b/src/mod/plugins/plugins.go
@@ -82,7 +82,7 @@ func (m *Manager) LoadPluginsFromDisk() error {
m.Log("Loaded plugin: "+thisPlugin.Spec.Name, nil)
// If the plugin was enabled, start it now
- fmt.Println("Plugin enabled state", m.GetPluginPreviousEnableState(thisPlugin.Spec.ID))
+ //fmt.Println("Plugin enabled state", m.GetPluginPreviousEnableState(thisPlugin.Spec.ID))
if m.GetPluginPreviousEnableState(thisPlugin.Spec.ID) {
err = m.StartPlugin(thisPlugin.Spec.ID)
if err != nil {
diff --git a/src/mod/plugins/store.go b/src/mod/plugins/store.go
new file mode 100644
index 0000000..eff3fcd
--- /dev/null
+++ b/src/mod/plugins/store.go
@@ -0,0 +1,118 @@
+package plugins
+
+import (
+ "encoding/json"
+ "fmt"
+ "io"
+ "net/http"
+ "strings"
+ "time"
+
+ "imuslab.com/zoraxy/mod/plugins/zoraxy_plugin"
+ "imuslab.com/zoraxy/mod/utils"
+)
+
+/*
+ Plugin Store
+*/
+
+// See https://github.com/aroz-online/zoraxy-official-plugins/blob/main/directories/index.json for the standard format
+
+type Checksums struct {
+ LinuxAmd64 string `json:"linux_amd64"`
+ Linux386 string `json:"linux_386"`
+ LinuxArm string `json:"linux_arm"`
+ LinuxArm64 string `json:"linux_arm64"`
+ LinuxMipsle string `json:"linux_mipsle"`
+ LinuxRiscv64 string `json:"linux_riscv64"`
+ WindowsAmd64 string `json:"windows_amd64"`
+}
+
+type DownloadablePlugin struct {
+ IconPath string
+ PluginIntroSpect zoraxy_plugin.IntroSpect //Plugin introspect information
+ ChecksumsSHA256 Checksums //Checksums for the plugin binary
+ DownloadURLs map[string]string //Download URLs for different platforms
+}
+
+/* Plugin Store Index List Sync */
+//Update the plugin list from the plugin store URLs
+func (m *Manager) UpdateDownloadablePluginList() error {
+ //Get downloadable plugins from each of the plugin store URLS
+ m.Options.DownloadablePluginCache = []*DownloadablePlugin{}
+ for _, url := range m.Options.PluginStoreURLs {
+ pluginList, err := m.getPluginListFromURL(url)
+ if err != nil {
+ return fmt.Errorf("failed to get plugin list from %s: %w", url, err)
+ }
+ m.Options.DownloadablePluginCache = append(m.Options.DownloadablePluginCache, pluginList...)
+ }
+
+ m.Options.LastSuccPluginSyncTime = time.Now().Unix()
+
+ return nil
+}
+
+// Get the plugin list from the URL
+func (m *Manager) getPluginListFromURL(url string) ([]*DownloadablePlugin, error) {
+ //Get the plugin list from the URL
+ resp, err := http.Get(url)
+ if err != nil {
+ return nil, err
+ }
+ defer resp.Body.Close()
+
+ if resp.StatusCode != http.StatusOK {
+ return nil, fmt.Errorf("failed to get plugin list from %s: %s", url, resp.Status)
+ }
+
+ var pluginList []*DownloadablePlugin
+ content, err := io.ReadAll(resp.Body)
+ if err != nil {
+ return nil, fmt.Errorf("failed to read plugin list from %s: %w", url, err)
+ }
+ content = []byte(strings.TrimSpace(string(content)))
+
+ err = json.Unmarshal(content, &pluginList)
+ if err != nil {
+ return nil, fmt.Errorf("failed to unmarshal plugin list from %s: %w", url, err)
+ }
+
+ return pluginList, nil
+}
+
+func (m *Manager) ListDownloadablePlugins() []*DownloadablePlugin {
+ //List all downloadable plugins
+ if len(m.Options.DownloadablePluginCache) == 0 {
+ return []*DownloadablePlugin{}
+ }
+ return m.Options.DownloadablePluginCache
+}
+
+/*
+ Handlers for Plugin Store
+*/
+
+func (m *Manager) HandleListDownloadablePlugins(w http.ResponseWriter, r *http.Request) {
+ //List all downloadable plugins
+ plugins := m.ListDownloadablePlugins()
+ js, _ := json.Marshal(plugins)
+ utils.SendJSONResponse(w, string(js))
+}
+
+// HandleResyncPluginList is the handler for resyncing the plugin list from the plugin store URLs
+func (m *Manager) HandleResyncPluginList(w http.ResponseWriter, r *http.Request) {
+ if r.Method != http.MethodPost {
+ //Make sure this function require csrf token
+ utils.SendErrorResponse(w, "Method not allowed")
+ return
+ }
+
+ //Resync the plugin list from the plugin store URLs
+ err := m.UpdateDownloadablePluginList()
+ if err != nil {
+ utils.SendErrorResponse(w, "Failed to resync plugin list: "+err.Error())
+ return
+ }
+ utils.SendOK(w)
+}
diff --git a/src/mod/plugins/store_test.go b/src/mod/plugins/store_test.go
new file mode 100644
index 0000000..058648a
--- /dev/null
+++ b/src/mod/plugins/store_test.go
@@ -0,0 +1,52 @@
+package plugins
+
+import (
+ "testing"
+)
+
+func TestUpdateDownloadablePluginList(t *testing.T) {
+ mockManager := &Manager{
+ Options: &ManagerOptions{
+ DownloadablePluginCache: []*DownloadablePlugin{},
+ PluginStoreURLs: []string{},
+ },
+ }
+
+ //Inject a mock URL for testing
+ mockManager.Options.PluginStoreURLs = []string{"https://raw.githubusercontent.com/aroz-online/zoraxy-official-plugins/refs/heads/main/directories/index.json"}
+
+ err := mockManager.UpdateDownloadablePluginList()
+ if err != nil {
+ t.Fatalf("expected no error, got %v", err)
+ }
+
+ if len(mockManager.Options.DownloadablePluginCache) == 0 {
+ t.Fatalf("expected plugin cache to be updated, but it was empty")
+ }
+
+ if mockManager.Options.LastSuccPluginSyncTime == 0 {
+ t.Fatalf("expected LastSuccPluginSyncTime to be updated, but it was not")
+ }
+}
+
+func TestGetPluginListFromURL(t *testing.T) {
+ mockManager := &Manager{
+ Options: &ManagerOptions{
+ DownloadablePluginCache: []*DownloadablePlugin{},
+ PluginStoreURLs: []string{},
+ },
+ }
+
+ pluginList, err := mockManager.getPluginListFromURL("https://raw.githubusercontent.com/aroz-online/zoraxy-official-plugins/refs/heads/main/directories/index.json")
+ if err != nil {
+ t.Fatalf("expected no error, got %v", err)
+ }
+
+ if len(pluginList) == 0 {
+ t.Fatalf("expected plugin list to be populated, but it was empty")
+ }
+
+ for _, plugin := range pluginList {
+ t.Logf("Plugin: %+v", plugin)
+ }
+}
diff --git a/src/mod/plugins/typdef.go b/src/mod/plugins/typdef.go
index 991651c..23ad083 100644
--- a/src/mod/plugins/typdef.go
+++ b/src/mod/plugins/typdef.go
@@ -29,10 +29,16 @@ type Plugin struct {
}
type ManagerOptions struct {
+ /* Plugins */
PluginDir string //The directory where the plugins are stored
PluginGroups map[string][]string //The plugin groups,key is the tag name and the value is an array of plugin IDs
PluginGroupsConfig string //The group / tag configuration file, if set the plugin groups will be loaded from this file
+ /* Plugin Downloader */
+ PluginStoreURLs []string //The plugin store URLs, used to download the plugins
+ DownloadablePluginCache []*DownloadablePlugin //The cache for the downloadable plugins, key is the plugin ID and value is the DownloadablePlugin struct
+ LastSuccPluginSyncTime int64 //The last sync time for the plugin store URLs, used to check if the plugin store URLs need to be synced again
+
/* Runtime */
SystemConst *zoraxyPlugin.RuntimeConstantValue //The system constant value
CSRFTokenGen func(*http.Request) string `json:"-"` //The CSRF token generator function
diff --git a/src/start.go b/src/start.go
index 2f3c79e..83ed7f1 100644
--- a/src/start.go
+++ b/src/start.go
@@ -1,7 +1,6 @@
package main
import (
- "imuslab.com/zoraxy/mod/auth/sso/authentik"
"log"
"net/http"
"os"
@@ -10,6 +9,8 @@ import (
"strings"
"time"
+ "imuslab.com/zoraxy/mod/auth/sso/authentik"
+
"github.com/gorilla/csrf"
"imuslab.com/zoraxy/mod/access"
"imuslab.com/zoraxy/mod/acme"
@@ -322,6 +323,9 @@ func startupSequence() {
ZoraxyUUID: nodeUUID,
DevelopmentBuild: DEVELOPMENT_BUILD,
},
+ PluginStoreURLs: []string{
+ "https://raw.githubusercontent.com/aroz-online/zoraxy-official-plugins/refs/heads/main/directories/index.json",
+ },
Database: sysdb,
Logger: SystemWideLogger,
PluginGroupsConfig: CONF_PLUGIN_GROUPS,
@@ -330,9 +334,19 @@ func startupSequence() {
},
})
+ //Sync latest plugin list from the plugin store
+ go func() {
+ err = pluginManager.UpdateDownloadablePluginList()
+ if err != nil {
+ SystemWideLogger.PrintAndLog("plugin-manager", "Failed to sync plugin list from plugin store", err)
+ } else {
+ SystemWideLogger.PrintAndLog("plugin-manager", "Plugin list synced from plugin store", nil)
+ }
+ }()
+
err = pluginManager.LoadPluginsFromDisk()
if err != nil {
- SystemWideLogger.PrintAndLog("Plugin Manager", "Failed to load plugins", err)
+ SystemWideLogger.PrintAndLog("plugin-manager", "Failed to load plugins", err)
}
/* Docker UX Optimizer */
diff --git a/src/web/components/plugins.html b/src/web/components/plugins.html
index bf2d9ef..bae23ba 100644
--- a/src/web/components/plugins.html
+++ b/src/web/components/plugins.html
@@ -185,6 +185,8 @@
+
+ Get More Plugins!
diff --git a/src/web/main.css b/src/web/main.css
index 713cb12..7359506 100644
--- a/src/web/main.css
+++ b/src/web/main.css
@@ -197,6 +197,12 @@ body.darkTheme .menubar{
max-width: calc(80% - 1em);
}
+@media screen and (max-width: 478px) {
+ .sideWrapper.extendedMode {
+ max-width: calc(100% - 1em);
+ }
+}
+
.sideWrapper .content{
height: 100%;
width: 100%;
diff --git a/src/web/snippet/pluginstore.html b/src/web/snippet/pluginstore.html
new file mode 100644
index 0000000..a7a4b91
--- /dev/null
+++ b/src/web/snippet/pluginstore.html
@@ -0,0 +1,258 @@
+
+
+
+
+
+
+ Plugin Store
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Update Plugin List
+
+
+
+
+
+ Advance Settings
+
+
+
Plugin Store URLs
+
+
+
+
+
+
+
+ Close
+
+
+
+
+
+
\ No newline at end of file
From ffc67ede129ed6aabfaf0eb241e8ed13c30c756b Mon Sep 17 00:00:00 2001
From: Toby Chui
Date: Thu, 24 Apr 2025 21:19:16 +0800
Subject: [PATCH 15/16] Added working plugin store prototype
- Added plugin install and remove api
---
src/api.go | 2 +
src/mod/plugins/lifecycle.go | 7 +-
src/mod/plugins/plugins.go | 58 ++++++++
src/mod/plugins/store.go | 238 +++++++++++++++++++++++++++++++
src/web/components/plugins.html | 32 ++++-
src/web/snippet/pluginstore.html | 57 +++++---
6 files changed, 362 insertions(+), 32 deletions(-)
diff --git a/src/api.go b/src/api.go
index 52f6499..1caedeb 100644
--- a/src/api.go
+++ b/src/api.go
@@ -237,6 +237,8 @@ func RegisterPluginAPIs(authRouter *auth.RouterDef) {
authRouter.HandleFunc("/api/plugins/store/list", pluginManager.HandleListDownloadablePlugins)
authRouter.HandleFunc("/api/plugins/store/resync", pluginManager.HandleResyncPluginList)
+ authRouter.HandleFunc("/api/plugins/store/install", pluginManager.HandleInstallPlugin)
+ authRouter.HandleFunc("/api/plugins/store/uninstall", pluginManager.HandleUninstallPlugin)
}
// Register the APIs for Auth functions, due to scoping issue some functions are defined here
diff --git a/src/mod/plugins/lifecycle.go b/src/mod/plugins/lifecycle.go
index e7b2161..914acf7 100644
--- a/src/mod/plugins/lifecycle.go
+++ b/src/mod/plugins/lifecycle.go
@@ -274,13 +274,10 @@ func (m *Manager) StopPlugin(pluginID string) error {
}
// Check if the plugin is still running
-func (m *Manager) PluginStillRunning(pluginID string) bool {
+func (m *Manager) PluginIsRunning(pluginID string) bool {
plugin, err := m.GetPluginByID(pluginID)
if err != nil {
return false
}
- if plugin.process == nil {
- return false
- }
- return plugin.process.ProcessState == nil
+ return plugin.IsRunning()
}
diff --git a/src/mod/plugins/plugins.go b/src/mod/plugins/plugins.go
index 5971ea6..039eaa1 100644
--- a/src/mod/plugins/plugins.go
+++ b/src/mod/plugins/plugins.go
@@ -58,6 +58,59 @@ func NewPluginManager(options *ManagerOptions) *Manager {
}
}
+// Reload all plugins from disk
+func (m *Manager) ReloadPluginFromDisk() {
+ //Check each of the current plugins if the directory exists
+ //If not, remove the plugin from the loaded plugins list
+ m.loadedPluginsMutex.Lock()
+ for pluginID, plugin := range m.LoadedPlugins {
+ if !utils.FileExists(plugin.RootDir) {
+ m.Log("Plugin directory not found, removing plugin from runtime: "+pluginID, nil)
+ delete(m.LoadedPlugins, pluginID)
+ //Remove the plugin enable state from the database
+ m.Options.Database.Delete("plugins", pluginID)
+ }
+ }
+
+ m.loadedPluginsMutex.Unlock()
+
+ //Scan the plugin directory for new plugins
+ foldersInPluginDir, err := os.ReadDir(m.Options.PluginDir)
+ if err != nil {
+ m.Log("Failed to read plugin directory", err)
+ return
+ }
+
+ for _, folder := range foldersInPluginDir {
+ if folder.IsDir() {
+ pluginPath := filepath.Join(m.Options.PluginDir, folder.Name())
+ thisPlugin, err := m.LoadPluginSpec(pluginPath)
+ if err != nil {
+ m.Log("Failed to load plugin: "+filepath.Base(pluginPath), err)
+ continue
+ }
+
+ //Check if the plugin id is already loaded into the runtime
+ m.loadedPluginsMutex.RLock()
+ _, ok := m.LoadedPlugins[thisPlugin.Spec.ID]
+ m.loadedPluginsMutex.RUnlock()
+ if ok {
+ //Plugin already loaded, skip it
+ continue
+ }
+
+ thisPlugin.RootDir = filepath.ToSlash(pluginPath)
+ thisPlugin.staticRouteProxy = make(map[string]*dpcore.ReverseProxy)
+ m.loadedPluginsMutex.Lock()
+ m.LoadedPlugins[thisPlugin.Spec.ID] = thisPlugin
+ m.loadedPluginsMutex.Unlock()
+ m.Log("Added new plugin: "+thisPlugin.Spec.Name, nil)
+
+ // The default state of the plugin is disabled, so no need to start it
+ }
+ }
+}
+
// LoadPluginsFromDisk loads all plugins from the plugin directory
func (m *Manager) LoadPluginsFromDisk() error {
// Load all plugins from the plugin directory
@@ -258,3 +311,8 @@ func (p *Plugin) HandleStaticRoute(w http.ResponseWriter, r *http.Request, longe
})
}
+
+// IsRunning checks if the plugin is currently running
+func (p *Plugin) IsRunning() bool {
+ return p.process != nil && p.process.Process != nil
+}
diff --git a/src/mod/plugins/store.go b/src/mod/plugins/store.go
index eff3fcd..fc17833 100644
--- a/src/mod/plugins/store.go
+++ b/src/mod/plugins/store.go
@@ -1,10 +1,14 @@
package plugins
import (
+ "crypto/sha256"
"encoding/json"
"fmt"
"io"
"net/http"
+ "os"
+ "path/filepath"
+ "runtime"
"strings"
"time"
@@ -89,6 +93,180 @@ func (m *Manager) ListDownloadablePlugins() []*DownloadablePlugin {
return m.Options.DownloadablePluginCache
}
+// InstallPlugin installs the given plugin by moving it to the PluginDir.
+func (m *Manager) InstallPlugin(plugin *DownloadablePlugin) error {
+ pluginDir := filepath.Join(m.Options.PluginDir, plugin.PluginIntroSpect.Name)
+ pluginFile := plugin.PluginIntroSpect.Name
+ if runtime.GOOS == "windows" {
+ pluginFile += ".exe"
+ }
+
+ //Check if the plugin id already exists in runtime plugin map
+ if _, ok := m.LoadedPlugins[plugin.PluginIntroSpect.ID]; ok {
+ return fmt.Errorf("plugin already installed: %s", plugin.PluginIntroSpect.ID)
+ }
+
+ // Create the plugin directory if it doesn't exist
+ err := os.MkdirAll(pluginDir, os.ModePerm)
+ if err != nil {
+ return fmt.Errorf("failed to create plugin directory: %w", err)
+ }
+
+ // Download the plugin binary
+ downloadURL, ok := plugin.DownloadURLs[runtime.GOOS+"_"+runtime.GOARCH]
+ if !ok {
+ return fmt.Errorf("no download URL available for the current platform")
+ }
+
+ resp, err := http.Get(downloadURL)
+ if err != nil {
+ return fmt.Errorf("failed to download plugin: %w", err)
+ }
+ defer resp.Body.Close()
+
+ if resp.StatusCode != http.StatusOK {
+ return fmt.Errorf("failed to download plugin: %s", resp.Status)
+ }
+
+ // Write the plugin binary to the plugin directory
+ pluginPath := filepath.Join(pluginDir, pluginFile)
+ out, err := os.Create(pluginPath)
+ if err != nil {
+ return fmt.Errorf("failed to create plugin file: %w", err)
+ }
+
+ _, err = io.Copy(out, resp.Body)
+ if err != nil {
+ out.Close()
+ return fmt.Errorf("failed to write plugin file: %w", err)
+ }
+
+ // Make the plugin executable
+ err = os.Chmod(pluginPath, 0755)
+ if err != nil {
+ out.Close()
+ return fmt.Errorf("failed to set executable permissions: %w", err)
+ }
+
+ // Verify the checksum of the downloaded plugin binary
+ checksums, err := plugin.ChecksumsSHA256.GetCurrentPlatformChecksum()
+ if err == nil {
+ if !verifyChecksumForFile(pluginPath, checksums) {
+ out.Close()
+ return fmt.Errorf("checksum verification failed for plugin binary")
+ }
+ }
+
+ //Ok, also download the icon if exists
+ if plugin.IconPath != "" {
+ iconURL := strings.TrimSpace(plugin.IconPath)
+ if iconURL != "" {
+ resp, err := http.Get(iconURL)
+ if err != nil {
+ return fmt.Errorf("failed to download plugin icon: %w", err)
+ }
+ defer resp.Body.Close()
+
+ //Save the icon to the plugin directory
+ iconPath := filepath.Join(pluginDir, "icon.png")
+ out, err := os.Create(iconPath)
+ if err != nil {
+ return fmt.Errorf("failed to create plugin icon file: %w", err)
+ }
+ defer out.Close()
+
+ io.Copy(out, resp.Body)
+ }
+ }
+ //Close the plugin exeutable
+ out.Close()
+
+ //Reload the plugin list
+ m.ReloadPluginFromDisk()
+
+ return nil
+}
+
+// UninstallPlugin uninstalls the plugin by removing its directory.
+func (m *Manager) UninstallPlugin(pluginID string) error {
+
+ //Stop the plugin process if it's running
+ plugin, ok := m.LoadedPlugins[pluginID]
+ if !ok {
+ return fmt.Errorf("plugin not found: %s", pluginID)
+ }
+
+ if plugin.IsRunning() {
+ err := m.StopPlugin(plugin.Spec.ID)
+ if err != nil {
+ return fmt.Errorf("failed to stop plugin: %w", err)
+ }
+ }
+
+ //Make sure the plugin process is stopped
+ m.Options.Logger.PrintAndLog("plugin-manager", "Removing plugin in 3 seconds...", nil)
+ time.Sleep(3 * time.Second)
+
+ // Remove the plugin directory
+ err := os.RemoveAll(plugin.RootDir)
+ if err != nil {
+ return fmt.Errorf("failed to remove plugin directory: %w", err)
+ }
+
+ //Reload the plugin list
+ m.ReloadPluginFromDisk()
+ return nil
+}
+
+// GetCurrentPlatformChecksum returns the checksum for the current platform
+func (c *Checksums) GetCurrentPlatformChecksum() (string, error) {
+ switch runtime.GOOS {
+ case "linux":
+ switch runtime.GOARCH {
+ case "amd64":
+ return c.LinuxAmd64, nil
+ case "386":
+ return c.Linux386, nil
+ case "arm":
+ return c.LinuxArm, nil
+ case "arm64":
+ return c.LinuxArm64, nil
+ case "mipsle":
+ return c.LinuxMipsle, nil
+ case "riscv64":
+ return c.LinuxRiscv64, nil
+ default:
+ return "", fmt.Errorf("unsupported architecture: %s", runtime.GOARCH)
+ }
+ case "windows":
+ switch runtime.GOARCH {
+ case "amd64":
+ return c.WindowsAmd64, nil
+ default:
+ return "", fmt.Errorf("unsupported architecture: %s", runtime.GOARCH)
+ }
+ default:
+ return "", fmt.Errorf("unsupported operating system: %s", runtime.GOOS)
+ }
+}
+
+// VerifyChecksum verifies the checksum of the downloaded plugin binary.
+func verifyChecksumForFile(filePath string, checksum string) bool {
+ file, err := os.Open(filePath)
+ if err != nil {
+ return false
+ }
+ defer file.Close()
+
+ hash := sha256.New()
+ if _, err := io.Copy(hash, file); err != nil {
+ return false
+ }
+ calculatedChecksum := fmt.Sprintf("%x", hash.Sum(nil))
+
+ return calculatedChecksum == checksum
+}
+
/*
Handlers for Plugin Store
*/
@@ -116,3 +294,63 @@ func (m *Manager) HandleResyncPluginList(w http.ResponseWriter, r *http.Request)
}
utils.SendOK(w)
}
+
+// HandleInstallPlugin is the handler for installing a plugin
+func (m *Manager) HandleInstallPlugin(w http.ResponseWriter, r *http.Request) {
+ if r.Method != http.MethodPost {
+ utils.SendErrorResponse(w, "Method not allowed")
+ return
+ }
+
+ pluginID, err := utils.PostPara(r, "pluginID")
+ if err != nil {
+ utils.SendErrorResponse(w, "pluginID is required")
+ return
+ }
+
+ // Find the plugin info from cache
+ var plugin *DownloadablePlugin
+ for _, p := range m.Options.DownloadablePluginCache {
+ if p.PluginIntroSpect.ID == pluginID {
+ plugin = p
+ break
+ }
+ }
+
+ if plugin == nil {
+ utils.SendErrorResponse(w, "Plugin not found")
+ return
+ }
+
+ // Install the plugin (implementation depends on your system)
+ err = m.InstallPlugin(plugin)
+ if err != nil {
+ utils.SendErrorResponse(w, "Failed to install plugin: "+err.Error())
+ return
+ }
+
+ utils.SendOK(w)
+}
+
+// HandleUninstallPlugin is the handler for uninstalling a plugin
+func (m *Manager) HandleUninstallPlugin(w http.ResponseWriter, r *http.Request) {
+ if r.Method != http.MethodPost {
+ utils.SendErrorResponse(w, "Method not allowed")
+ return
+ }
+
+ pluginID, err := utils.PostPara(r, "pluginID")
+ if err != nil {
+ utils.SendErrorResponse(w, "pluginID is required")
+ return
+ }
+
+ // Uninstall the plugin (implementation depends on your system)
+ err = m.UninstallPlugin(pluginID)
+ if err != nil {
+ utils.SendErrorResponse(w, "Failed to uninstall plugin: "+err.Error())
+ return
+ }
+
+ utils.SendOK(w)
+}
diff --git a/src/web/components/plugins.html b/src/web/components/plugins.html
index bae23ba..9592723 100644
--- a/src/web/components/plugins.html
+++ b/src/web/components/plugins.html
@@ -186,7 +186,7 @@
- Get More Plugins!
+ Plugin Store (Experimental)
diff --git a/src/web/snippet/pluginstore.html b/src/web/snippet/pluginstore.html
index a7a4b91..3f479d4 100644
--- a/src/web/snippet/pluginstore.html
+++ b/src/web/snippet/pluginstore.html
@@ -43,6 +43,10 @@
+
+
+
The Plugin Store is an experimental feature. Use it at your own risk.
+
@@ -53,7 +57,7 @@
Update Plugin List
-
+
Close
@@ -126,7 +131,7 @@
const name = item.querySelector('.header').textContent.toLowerCase();
const description = item.querySelector('.description p').textContent.toLowerCase();
const author = item.querySelector('.meta span:nth-child(2)').textContent.toLowerCase();
- const id = item.querySelector('.extra button').getAttribute('onclick').match(/'(.*?)'/)[1].toLowerCase();
+ const id = item.getAttribute('plugin_id').toLowerCase();
if (name.includes(query) || description.includes(query) || author.includes(query) || id.includes(query)) {
item.style.display = '';
@@ -145,6 +150,8 @@
});
function forceResyncPlugins() {
+ parent.msgbox("Updating plugin list...", true);
+ document.getElementById('searchInput').value = '';
$.cjax({
url: '/api/plugins/store/resync',
type: 'POST',
@@ -181,52 +188,58 @@
${plugin.PluginIntroSpect.description}
${thisPluginIsInstalled
- ? ` Remove `
- : ` Install `}
+ ? `Installed `
+ : ` Install `}
`;
$('#pluginList').append(item);
});
+
+ // Reapply search filter if there's a query in the search bar
+ const searchQuery = document.getElementById('searchInput').value.toLowerCase();
+ if (searchQuery.trim() !== '') {
+ searchPlugins();
+ }
+
}
/* Plugin Actions */
- function installPlugin(pluginId) {
+ function installPlugin(pluginId, btn=undefined) {
+ if (btn !== undefined) {
+ $(btn).addClass('loading').prop('disabled', true);
+ }
$.cjax({
url: '/api/plugins/store/install',
type: 'POST',
- data: { pluginId },
+ data: { "pluginID": pluginId },
success: function(data) {
+ if (btn !== undefined) {
+ $(btn).removeClass('loading').prop('disabled', false);
+ }
if (data.error != undefined) {
parent.msgbox(data.error, false);
} else {
parent.msgbox("Plugin installed successfully", true);
initStoreList();
- }
- }
- });
- }
- function uninstallPlugin(pluginId) {
- $.cjax({
- url: '/api/plugins/store/uninstall',
- type: 'POST',
- data: { pluginId },
- success: function(data) {
- if (data.error != undefined) {
- parent.msgbox(data.error, false);
- } else {
- parent.msgbox("Plugin uninstalled successfully", true);
- initStoreList();
+ //Also reload the parent plugin list
+ parent.initiatePluginList();
}
+ },
+ error: function() {
+ if (btn !== undefined) {
+ $(btn).removeClass('loading').prop('disabled', false);
+ }
+ parent.msgbox("An error occurred while installing the plugin", false);
}
});
}
From 97817359833fb9bc0f6367be3bed44e1bbb6442b Mon Sep 17 00:00:00 2001
From: Toby Chui
Date: Sun, 27 Apr 2025 13:55:54 +0800
Subject: [PATCH 16/16] Moved dev settings to flag
- Moved internal DEVELOPMENT_MODE flag to start parameters
---
src/api.go | 2 +-
src/def.go | 10 +++++-----
src/router.go | 2 +-
src/start.go | 4 ++--
src/web/snippet/pluginstore.html | 9 ++++-----
src/wrappers.go | 2 +-
6 files changed, 14 insertions(+), 15 deletions(-)
diff --git a/src/api.go b/src/api.go
index 1caedeb..01adf20 100644
--- a/src/api.go
+++ b/src/api.go
@@ -324,7 +324,7 @@ func initAPIs(targetMux *http.ServeMux) {
// Register the standard web services URLs
var staticWebRes http.Handler
- if DEVELOPMENT_BUILD {
+ if *development_build {
staticWebRes = http.FileServer(http.Dir("web/"))
} else {
subFS, err := fs.Sub(webres, "web")
diff --git a/src/def.go b/src/def.go
index 9b717ef..7efd638 100644
--- a/src/def.go
+++ b/src/def.go
@@ -43,9 +43,8 @@ import (
const (
/* Build Constants */
- SYSTEM_NAME = "Zoraxy"
- SYSTEM_VERSION = "3.2.1"
- DEVELOPMENT_BUILD = true /* Development: Set to false to use embedded web fs */
+ SYSTEM_NAME = "Zoraxy"
+ SYSTEM_VERSION = "3.2.1"
/* System Constants */
TMP_FOLDER = "./tmp"
@@ -101,8 +100,9 @@ var (
path_webserver = flag.String("webroot", "./www", "Static web server root folder. Only allow change in start paramters")
path_plugin = flag.String("plugin", "./plugins", "Plugin folder path")
- /* Maintaince Function Flags */
- geoDbUpdate = flag.Bool("update_geoip", false, "Download the latest GeoIP data and exit")
+ /* Maintaince & Development Function Flags */
+ geoDbUpdate = flag.Bool("update_geoip", false, "Download the latest GeoIP data and exit")
+ development_build = flag.Bool("dev", false, "Use external web folder for UI development")
)
/* Global Variables and Handlers */
diff --git a/src/router.go b/src/router.go
index 05f91dd..064ed27 100644
--- a/src/router.go
+++ b/src/router.go
@@ -101,7 +101,7 @@ func handleInjectHTML(w http.ResponseWriter, r *http.Request, relativeFilepath s
if len(relativeFilepath) > 0 && relativeFilepath[len(relativeFilepath)-1:] == "/" {
relativeFilepath = relativeFilepath + "index.html"
}
- if DEVELOPMENT_BUILD {
+ if *development_build {
//Load from disk
targetFilePath := strings.ReplaceAll(filepath.Join("web/", relativeFilepath), "\\", "/")
content, err = os.ReadFile(targetFilePath)
diff --git a/src/start.go b/src/start.go
index 83ed7f1..0fadc05 100644
--- a/src/start.go
+++ b/src/start.go
@@ -100,7 +100,7 @@ func startupSequence() {
})
//Create a TLS certificate manager
- tlsCertManager, err = tlscert.NewManager(CONF_CERT_STORE, DEVELOPMENT_BUILD, SystemWideLogger)
+ tlsCertManager, err = tlscert.NewManager(CONF_CERT_STORE, *development_build, SystemWideLogger)
if err != nil {
panic(err)
}
@@ -321,7 +321,7 @@ func startupSequence() {
SystemConst: &zoraxy_plugin.RuntimeConstantValue{
ZoraxyVersion: SYSTEM_VERSION,
ZoraxyUUID: nodeUUID,
- DevelopmentBuild: DEVELOPMENT_BUILD,
+ DevelopmentBuild: *development_build,
},
PluginStoreURLs: []string{
"https://raw.githubusercontent.com/aroz-online/zoraxy-official-plugins/refs/heads/main/directories/index.json",
diff --git a/src/web/snippet/pluginstore.html b/src/web/snippet/pluginstore.html
index 3f479d4..e7205f0 100644
--- a/src/web/snippet/pluginstore.html
+++ b/src/web/snippet/pluginstore.html
@@ -130,7 +130,8 @@
items.forEach(item => {
const name = item.querySelector('.header').textContent.toLowerCase();
const description = item.querySelector('.description p').textContent.toLowerCase();
- const author = item.querySelector('.meta span:nth-child(2)').textContent.toLowerCase();
+ const authorElement = item.querySelector('.plugin_author');
+ const author = authorElement ? authorElement.textContent.toLowerCase() : '';
const id = item.getAttribute('plugin_id').toLowerCase();
if (name.includes(query) || description.includes(query) || author.includes(query) || id.includes(query)) {
@@ -184,11 +185,9 @@
-
+
${plugin.PluginIntroSpect.description}
diff --git a/src/wrappers.go b/src/wrappers.go
index 41d8133..81783a7 100644
--- a/src/wrappers.go
+++ b/src/wrappers.go
@@ -345,7 +345,7 @@ func HandleZoraxyInfo(w http.ResponseWriter, r *http.Request) {
info := ZoraxyInfo{
Version: SYSTEM_VERSION,
NodeUUID: displayUUID,
- Development: DEVELOPMENT_BUILD,
+ Development: *development_build,
BootTime: displayBootTime,
EnableSshLoopback: displayAllowSSHLB,
}