diff --git a/Dockerfile b/Dockerfile index 7e0074e..ce7d473 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,9 +3,7 @@ FROM alpine:3.21@sha256:21dc6063fd678b478f57c0e13f47560d0ea4eeba26dfc947b2a4f81f # Create non-root user and set up permissions in a single layer RUN adduser -k /dev/null -u 10001 -D gorge \ && chgrp 0 /home/gorge \ - && chmod -R g+rwX /home/gorge \ - # Add additional security hardening - && chmod 755 /gorge + && chmod -R g+rwX /home/gorge # Copy application binary with explicit permissions COPY --chmod=755 gorge / diff --git a/README.md b/README.md index 9dec623..cb4c2e1 100644 --- a/README.md +++ b/README.md @@ -90,6 +90,7 @@ Flags: --bind string host to listen to (default "127.0.0.1") --cache-max-age int max number of seconds responses should be cached (default 86400) --cache-prefixes string url prefixes to cache (default "/v3/files") + --cache-by-full-request-uri will cache responses by the full request URI (incl. query fragments) instead of only the request path --cors string allowed cors origins separated by comma (default "*") --dev enables dev mode --drop-privileges drops privileges to the given user/group @@ -207,6 +208,7 @@ GORGE_BACKEND=filesystem GORGE_BIND=127.0.0.1 GORGE_CACHE_MAX_AGE=86400 GORGE_CACHE_PREFIXES=/v3/files +GORGE_CACHE_BY_FULL_REQUEST_URI=false GORGE_CORS="*" GORGE_DEV=false GORGE_DROP_PRIVILEGES=false diff --git a/cmd/serve.go b/cmd/serve.go index f25f668..ab47623 100644 --- a/cmd/serve.go +++ b/cmd/serve.go @@ -159,7 +159,13 @@ You can also enable the caching functionality to speed things up.`, log.Log.Debug("Setting up cache middleware") customKeyFunc := func(r *http.Request) uint64 { token := r.Header.Get("Authorization") - return stampede.StringToHash(r.Method, r.URL.Path, strings.ToLower(token)) + requestURI := r.URL.Path + + if config.CacheByFullRequestURI { + requestURI = r.URL.RequestURI() + } + + return stampede.StringToHash(r.Method, requestURI, strings.ToLower(token)) } cachedMiddleware := stampede.HandlerWithKey( @@ -209,10 +215,14 @@ You can also enable the caching functionality to speed things up.`, slices.Reverse(proxies) for _, proxy := range proxies { - r.Use(customMiddleware.ProxyFallback(proxy, func(status int) bool { - return status == http.StatusNotFound - }, + r.Use(customMiddleware.ProxyFallback( + proxy, + func(status int) bool { + return status == http.StatusNotFound + }, func(r *http.Response) { + r.Header.Add("X-Proxied-To", proxy) + if config.ImportProxiedReleases && strings.HasPrefix(r.Request.URL.Path, "/v3/files/") && r.StatusCode == http.StatusOK { body, err := io.ReadAll(r.Body) if err != nil { @@ -228,6 +238,7 @@ You can also enable the caching functionality to speed things up.`, log.Log.Error(err) return } + log.Log.Infof("Imported release %s\n", release.Slug) } }, @@ -379,4 +390,5 @@ func init() { serveCmd.Flags().Int64Var(&config.CacheMaxAge, "cache-max-age", 86400, "max number of seconds responses should be cached") serveCmd.Flags().BoolVar(&config.NoCache, "no-cache", false, "disables the caching functionality") serveCmd.Flags().BoolVar(&config.ImportProxiedReleases, "import-proxied-releases", false, "add every proxied modules to local store") + serveCmd.Flags().BoolVar(&config.CacheByFullRequestURI, "cache-by-full-request-uri", false, "will cache responses by the full request URI (incl. query fragments) instead of only the request path") } diff --git a/internal/config/config.go b/internal/config/config.go index b2ae88c..d6b721d 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -16,6 +16,7 @@ var ( FallbackProxyUrl string NoCache bool CachePrefixes string + CacheByFullRequestURI bool CacheMaxAge int64 ImportProxiedReleases bool JwtSecret string diff --git a/internal/middleware/stats.go b/internal/middleware/stats.go index a0cdcd6..fc4d4fb 100644 --- a/internal/middleware/stats.go +++ b/internal/middleware/stats.go @@ -7,29 +7,33 @@ import ( ) type Statistics struct { - ActiveConnections int - TotalConnections int - TotalResponseTime time.Duration - TotalCacheHits int - TotalCacheMisses int - ConnectionsPerEndpoint map[string]int - ResponseTimePerEndpoint map[string]time.Duration - CacheHitsPerEndpoint map[string]int - CacheMissesPerEndpoint map[string]int - Mutex sync.Mutex + ActiveConnections int + TotalConnections int + TotalResponseTime time.Duration + TotalCacheHits int + TotalCacheMisses int + ConnectionsPerEndpoint map[string]int + ResponseTimePerEndpoint map[string]time.Duration + CacheHitsPerEndpoint map[string]int + CacheMissesPerEndpoint map[string]int + Mutex sync.Mutex + ProxiedConnections int + ProxiedConnectionsPerEndpoint map[string]int } func NewStatistics() *Statistics { return &Statistics{ - ActiveConnections: 0, - TotalConnections: 0, - TotalResponseTime: 0, - TotalCacheHits: 0, - TotalCacheMisses: 0, - ConnectionsPerEndpoint: make(map[string]int), - CacheHitsPerEndpoint: make(map[string]int), - CacheMissesPerEndpoint: make(map[string]int), - ResponseTimePerEndpoint: make(map[string]time.Duration), + ActiveConnections: 0, + TotalConnections: 0, + TotalResponseTime: 0, + TotalCacheHits: 0, + TotalCacheMisses: 0, + ConnectionsPerEndpoint: make(map[string]int), + CacheHitsPerEndpoint: make(map[string]int), + CacheMissesPerEndpoint: make(map[string]int), + ResponseTimePerEndpoint: make(map[string]time.Duration), + ProxiedConnections: 0, + ProxiedConnectionsPerEndpoint: make(map[string]int), } } @@ -59,6 +63,12 @@ func StatisticsMiddleware(stats *Statistics) func(next http.Handler) http.Handle stats.TotalResponseTime += duration stats.ResponseTimePerEndpoint[r.URL.Path] += duration + + if w.Header().Get("X-Proxied-To") != "" { + stats.ProxiedConnections++ + stats.ProxiedConnectionsPerEndpoint[r.URL.Path]++ + } + stats.Mutex.Unlock() }() diff --git a/internal/v3/api/release.go b/internal/v3/api/release.go index 88a0236..e18bd3a 100644 --- a/internal/v3/api/release.go +++ b/internal/v3/api/release.go @@ -13,6 +13,7 @@ import ( "strings" "github.com/dadav/gorge/internal/config" + "github.com/dadav/gorge/internal/log" "github.com/dadav/gorge/internal/v3/backend" "github.com/dadav/gorge/internal/v3/utils" gen "github.com/dadav/gorge/pkg/gen/v3/openapi" @@ -114,7 +115,6 @@ type GetFile400Response struct { // GetFile - Download module release func (s *ReleaseOperationsApi) GetFile(ctx context.Context, filename string) (gen.ImplResponse, error) { - if filename == "" { return gen.Response(400, gen.GetFile400Response{ Message: "No filename provided", @@ -261,50 +261,113 @@ func (s *ReleaseOperationsApi) GetReleases(ctx context.Context, limit int32, off } results := []gen.Release{} - filtered := []gen.Release{} + filtered := []*gen.Release{} allReleases, _ := backend.ConfiguredBackend.GetAllReleases() + + base, _ := url.Parse("/v3/releases") params := url.Values{} + + filterSet := false + + if module != "" { + filterSet = true + params.Add("module", module) + } + + if owner != "" { + filterSet = true + params.Add("owner", owner) + } + params.Add("offset", strconv.Itoa(int(offset))) params.Add("limit", strconv.Itoa(int(limit))) - if int(offset)+1 > len(allReleases) { - return gen.Response(404, GetRelease404Response{ - Message: "Invalid offset", - Errors: []string{"The given offset is larger than the total number of modules."}, + base.RawQuery = params.Encode() + currentInf := interface{}(base.String()) + + // We know there's no releases and a fallback proxy, so we should return a 404 to let the proxy handle it + if config.FallbackProxyUrl != "" && len(allReleases) == 0 { + log.Log.Debugln("Could not find *any* releases in the backend, returning 404 so we can proxy if desired") + + return gen.Response(http.StatusNotFound, GetRelease404Response{ + Message: "No releases found", + Errors: []string{"Did not retrieve any releases from the backend."}, }), nil } - for _, r := range allReleases[offset:] { - var filterMatched, filterSet bool - - if module != "" && r.Module.Slug != module { - filterSet = true - filterMatched = r.Module.Slug == module - params.Add("module", module) + if module != "" { + // Perform an early query to see if the module even exists in the backend, optimization for instances with _many_ modules + _, err := backend.ConfiguredBackend.GetModuleBySlug(module) + if err != nil { + log.Log.Debugf("Could not find module with slug '%s' in backend, returning 404 so we can proxy if desired\n", module) + + if config.FallbackProxyUrl != "" { + return gen.Response(http.StatusNotFound, GetRelease404Response{ + Message: "No releases found", + Errors: []string{"No module(s) found for given query."}, + }), nil + } else { + return gen.Response(http.StatusOK, gen.GetReleases200Response{ + Pagination: gen.GetReleases200ResponsePagination{ + Limit: limit, + Offset: offset, + First: ¤tInf, + Previous: nil, + Current: ¤tInf, + Next: nil, + Total: 0, + }, + Results: []gen.Release{}, + }), nil + } } - if owner != "" && r.Module.Owner.Slug != owner { - filterSet = true - filterMatched = r.Module.Owner.Slug == owner - params.Add("owner", owner) + } + + if filterSet { + // We search through all available releases to see if they match the filter + for _, r := range allReleases { + if module != "" && r.Module.Slug != module { + continue + } + + if owner != "" && r.Module.Owner.Slug != owner { + continue + } + + filtered = append(filtered, r) } + } else { + filtered = allReleases + } + + if len(filtered) > int(offset) { + i := 1 + for _, release := range filtered[offset:] { + if i > int(limit) { + break + } - if !filterSet || filterMatched { - filtered = append(filtered, *r) + results = append(results, *release) + i++ } } - i := 1 - for _, release := range filtered { - if i > int(limit) { - break + // If we're using a fallback-proxy, we should return a 404 so the proxy can handle the request + if config.FallbackProxyUrl != "" && len(results) == 0 { + if module != "" { + log.Log.Debugf("No releases for '%s' found in backend\n", module) + } else { + log.Log.Debugln("No releases found in backend") } - results = append(results, release) - i++ + + return gen.Response(http.StatusNotFound, GetRelease404Response{ + Message: "No releases found", + Errors: []string{"No release(s) found for given query."}, + }), nil } - base, _ := url.Parse("/v3/releases") base.RawQuery = params.Encode() - currentInf := interface{}(base.String()) + currentInf = interface{}(base.String()) params.Set("offset", "0") firstInf := interface{}(base.String()) diff --git a/internal/v3/ui/components/statistics.templ b/internal/v3/ui/components/statistics.templ index 6146ca7..c1bc867 100644 --- a/internal/v3/ui/components/statistics.templ +++ b/internal/v3/ui/components/statistics.templ @@ -10,6 +10,7 @@ templ StatisticsView(stats *customMiddleware.Statistics) {

Statistics

ActiveConnections: { strconv.Itoa(stats.ActiveConnections) }

+

ProxiedConnections: { strconv.Itoa(stats.ProxiedConnections) }

TotalConnections: { strconv.Itoa(stats.TotalConnections) }

TotalResponseTime: { stats.TotalResponseTime.String() }

TotalCacheHits: { strconv.Itoa(stats.TotalCacheHits) }

@@ -19,6 +20,7 @@ templ StatisticsView(stats *customMiddleware.Statistics) { Path Connections + Proxied Connections Average ResponseTime Total ResponseTime Cache (Hits/Misses) @@ -29,6 +31,7 @@ templ StatisticsView(stats *customMiddleware.Statistics) { { path } { strconv.Itoa(connections) } + { strconv.Itoa(stats.ProxiedConnectionsPerEndpoint[path]) } { (stats.ResponseTimePerEndpoint[path] / time.Duration(connections)).String() } { stats.ResponseTimePerEndpoint[path].String() } if stats.CacheHitsPerEndpoint[path] > 0 || stats.CacheMissesPerEndpoint[path] > 0 { diff --git a/internal/v3/ui/components/statistics_templ.go b/internal/v3/ui/components/statistics_templ.go index 534b1bc..3ecaa96 100644 --- a/internal/v3/ui/components/statistics_templ.go +++ b/internal/v3/ui/components/statistics_templ.go @@ -45,59 +45,72 @@ func StatisticsView(stats *customMiddleware.Statistics) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("

TotalConnections: ") + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("

ProxiedConnections: ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } var templ_7745c5c3_Var3 string - templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(stats.TotalConnections)) + templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(stats.ProxiedConnections)) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/v3/ui/components/statistics.templ`, Line: 13, Col: 61} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/v3/ui/components/statistics.templ`, Line: 13, Col: 65} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("

TotalResponseTime: ") + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("

TotalConnections: ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } var templ_7745c5c3_Var4 string - templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(stats.TotalResponseTime.String()) + templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(stats.TotalConnections)) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/v3/ui/components/statistics.templ`, Line: 14, Col: 58} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/v3/ui/components/statistics.templ`, Line: 14, Col: 61} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("

TotalCacheHits: ") + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("

TotalResponseTime: ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } var templ_7745c5c3_Var5 string - templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(stats.TotalCacheHits)) + templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(stats.TotalResponseTime.String()) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/v3/ui/components/statistics.templ`, Line: 15, Col: 57} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/v3/ui/components/statistics.templ`, Line: 15, Col: 58} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("

TotalCacheMisses: ") + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("

TotalCacheHits: ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } var templ_7745c5c3_Var6 string - templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(stats.TotalCacheMisses)) + templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(stats.TotalCacheHits)) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/v3/ui/components/statistics.templ`, Line: 16, Col: 61} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/v3/ui/components/statistics.templ`, Line: 16, Col: 57} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("

") + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("

TotalCacheMisses: ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var7 string + templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(stats.TotalCacheMisses)) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/v3/ui/components/statistics.templ`, Line: 17, Col: 61} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("

PathConnectionsAverage ResponseTimeTotal ResponseTimeCache (Hits/Misses)
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -106,12 +119,12 @@ func StatisticsView(stats *customMiddleware.Statistics) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var7 string - templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(path) + var templ_7745c5c3_Var8 string + templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(path) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/v3/ui/components/statistics.templ`, Line: 30, Col: 16} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/v3/ui/components/statistics.templ`, Line: 32, Col: 16} } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7)) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -119,12 +132,12 @@ func StatisticsView(stats *customMiddleware.Statistics) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var8 string - templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(connections)) + var templ_7745c5c3_Var9 string + templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(connections)) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/v3/ui/components/statistics.templ`, Line: 31, Col: 37} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/v3/ui/components/statistics.templ`, Line: 33, Col: 37} } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8)) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -132,12 +145,12 @@ func StatisticsView(stats *customMiddleware.Statistics) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var9 string - templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs((stats.ResponseTimePerEndpoint[path] / time.Duration(connections)).String()) + var templ_7745c5c3_Var10 string + templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(stats.ProxiedConnectionsPerEndpoint[path])) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/v3/ui/components/statistics.templ`, Line: 32, Col: 87} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/v3/ui/components/statistics.templ`, Line: 34, Col: 67} } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9)) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -145,12 +158,25 @@ func StatisticsView(stats *customMiddleware.Statistics) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var10 string - templ_7745c5c3_Var10, templ_7745c5c3_Err = templ.JoinStringErrs(stats.ResponseTimePerEndpoint[path].String()) + var templ_7745c5c3_Var11 string + templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs((stats.ResponseTimePerEndpoint[path] / time.Duration(connections)).String()) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/v3/ui/components/statistics.templ`, Line: 33, Col: 56} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/v3/ui/components/statistics.templ`, Line: 35, Col: 87} } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var10)) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
PathConnectionsProxied ConnectionsAverage ResponseTimeTotal ResponseTimeCache (Hits/Misses)
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var12 string + templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(stats.ResponseTimePerEndpoint[path].String()) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/v3/ui/components/statistics.templ`, Line: 36, Col: 56} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -163,12 +189,12 @@ func StatisticsView(stats *customMiddleware.Statistics) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var11 string - templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(stats.CacheHitsPerEndpoint[path])) + var templ_7745c5c3_Var13 string + templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(stats.CacheHitsPerEndpoint[path])) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/v3/ui/components/statistics.templ`, Line: 35, Col: 59} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/v3/ui/components/statistics.templ`, Line: 38, Col: 59} } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11)) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -176,12 +202,12 @@ func StatisticsView(stats *customMiddleware.Statistics) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var12 string - templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(stats.CacheMissesPerEndpoint[path])) + var templ_7745c5c3_Var14 string + templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(stats.CacheMissesPerEndpoint[path])) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/v3/ui/components/statistics.templ`, Line: 35, Col: 112} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `internal/v3/ui/components/statistics.templ`, Line: 38, Col: 112} } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12)) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err }