mirror of
https://github.com/TwiN/gatus.git
synced 2026-08-22 15:16:04 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c4900ced6c | ||
|
|
433e59b0ce | ||
|
|
f09d959c97 | ||
|
|
e5b4e0d381 | ||
|
|
e580cd362a | ||
|
|
b65b4d83e1 | ||
|
|
618892c248 | ||
|
|
77cedb080d | ||
|
|
786af2dee4 | ||
|
|
48531911bb | ||
|
|
720888009e | ||
|
|
961bffd8e7 | ||
|
|
309d68e622 | ||
|
|
ce269905b7 | ||
|
|
d8724d683e | ||
|
|
0f22e863a9 | ||
|
|
7bb959e072 | ||
|
|
1095deb3c6 | ||
|
|
c93a321073 | ||
|
|
1bf05d6bf9 |
@@ -22,7 +22,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: 1.24.4
|
||||
go-version: 1.25.5
|
||||
repository: "${{ github.event.inputs.repository || 'TwiN/gatus' }}"
|
||||
ref: "${{ github.event.inputs.ref || 'master' }}"
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
@@ -83,20 +83,21 @@ jobs:
|
||||
with:
|
||||
script: |
|
||||
const status = '${{ needs.regenerate-static-assets.outputs.status }}';
|
||||
let reaction = '';
|
||||
if (status === 'success') {
|
||||
reaction = 'hooray';
|
||||
} else if (status === 'no_changes') {
|
||||
reaction = '+1';
|
||||
let reaction = 'hooray';
|
||||
let message = '';
|
||||
var workflowUrl = `${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}`;
|
||||
if (status === 'no_changes') {
|
||||
message = `@${context.actor} No changes to commit ([ref](${workflowUrl})).`;
|
||||
} else {
|
||||
reaction = '-1';
|
||||
var workflowUrl = `${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}`;
|
||||
var body = '⚠️ There was an issue regenerating static assets. Please check the [workflow run logs](' + workflowUrl + ') for more details.';
|
||||
message = `@${context.actor} There was an issue regenerating static assets. Please check the [workflow run logs](${workflowUrl}) for more details.`;
|
||||
}
|
||||
if (message.length) {
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.issue.number,
|
||||
body: body
|
||||
body: message
|
||||
});
|
||||
}
|
||||
await github.rest.reactions.createForIssueComment({
|
||||
|
||||
@@ -18,7 +18,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: 1.24.4
|
||||
go-version: 1.25.5
|
||||
- uses: actions/checkout@v5
|
||||
- name: Build binary to make sure it works
|
||||
run: go build
|
||||
|
||||
@@ -303,6 +303,7 @@ You can then configure alerts to be triggered when an endpoint is unhealthy once
|
||||
| `endpoints[].ui.hide-url` | Whether to hide the URL from the results. Useful if the URL contains a token. | `false` |
|
||||
| `endpoints[].ui.hide-errors` | Whether to hide errors from the results. | `false` |
|
||||
| `endpoints[].ui.dont-resolve-failed-conditions` | Whether to resolve failed conditions for the UI. | `false` |
|
||||
| `endpoints[].ui.resolve-successful-conditions` | Whether to resolve successful conditions for the UI (helpful to expose body assertions even when checks pass). | `false` |
|
||||
| `endpoints[].ui.badge.response-time` | List of response time thresholds. Each time a threshold is reached, the badge has a different color. | `[50, 200, 300, 500, 750]` |
|
||||
| `endpoints[].extra-labels` | Extra labels to add to the metrics. Useful for grouping endpoints together. | `{}` |
|
||||
| `endpoints[].always-run` | (SUITES ONLY) Whether to execute this endpoint even if previous endpoints in the suite failed. | `false` |
|
||||
@@ -538,6 +539,9 @@ Allows you to configure the application wide defaults for the dashboard's UI. So
|
||||
| `ui.header` | Header at the top of the dashboard. | `Gatus` |
|
||||
| `ui.logo` | URL to the logo to display. | `""` |
|
||||
| `ui.link` | Link to open when the logo is clicked. | `""` |
|
||||
| `ui.favicon.default` | Favourite default icon to display in web browser tab or address bar. | `/favicon.ico` |
|
||||
| `ui.favicon.size16x16` | Favourite icon to display in web browser for 16x16 size. | `/favicon-16x16.png` |
|
||||
| `ui.favicon.size32x32` | Favourite icon to display in web browser for 32x32 size. | `/favicon-32x32.png` |
|
||||
| `ui.buttons` | List of buttons to display below the header. | `[]` |
|
||||
| `ui.buttons[].name` | Text to display on the button. | Required `""` |
|
||||
| `ui.buttons[].link` | Link to open when the button is clicked. | Required `""` |
|
||||
@@ -3252,7 +3256,7 @@ endpoints:
|
||||
|
||||
|
||||
### Concurrency
|
||||
By default, Gatus allows up to 5 endpoints/suites to be monitored concurrently. This provides a balance between performance and resource usage while maintaining accurate response time measurements.
|
||||
By default, Gatus allows up to 3 endpoints/suites to be monitored concurrently. This provides a balance between performance and resource usage while maintaining accurate response time measurements.
|
||||
|
||||
You can configure the concurrency level using the `concurrency` parameter:
|
||||
|
||||
|
||||
@@ -12,6 +12,18 @@ import (
|
||||
"github.com/TwiN/gatus/v5/test"
|
||||
)
|
||||
|
||||
// isIgnorableTestError checks if an error is expected during testing when making API calls with dummy credentials
|
||||
func isIgnorableTestError(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
errStr := err.Error()
|
||||
return strings.Contains(errStr, "user does not exist") ||
|
||||
strings.Contains(errStr, "no such host") ||
|
||||
strings.Contains(errStr, "invalid username, password or token") ||
|
||||
strings.Contains(errStr, "dial tcp")
|
||||
}
|
||||
|
||||
func TestAlertProvider_Validate(t *testing.T) {
|
||||
scenarios := []struct {
|
||||
Name string
|
||||
@@ -50,7 +62,7 @@ func TestAlertProvider_Validate(t *testing.T) {
|
||||
if scenario.ExpectedError && err == nil {
|
||||
t.Error("expected error, got none")
|
||||
}
|
||||
if !scenario.ExpectedError && err != nil && !strings.Contains(err.Error(), "user does not exist") && !strings.Contains(err.Error(), "no such host") {
|
||||
if !scenario.ExpectedError && err != nil && !isIgnorableTestError(err) {
|
||||
t.Error("expected no error, got", err.Error())
|
||||
}
|
||||
})
|
||||
@@ -87,7 +99,7 @@ func TestAlertProvider_Send(t *testing.T) {
|
||||
for _, scenario := range scenarios {
|
||||
t.Run(scenario.Name, func(t *testing.T) {
|
||||
cfg, err := scenario.Provider.GetConfig("", &scenario.Alert)
|
||||
if err != nil && !strings.Contains(err.Error(), "user does not exist") && !strings.Contains(err.Error(), "no such host") {
|
||||
if err != nil && !isIgnorableTestError(err) {
|
||||
t.Error("expected no error, got", err.Error())
|
||||
}
|
||||
cfg.giteaClient, _ = gitea.NewClient("https://gitea.com")
|
||||
@@ -203,7 +215,7 @@ func TestAlertProvider_GetConfig(t *testing.T) {
|
||||
for _, scenario := range scenarios {
|
||||
t.Run(scenario.Name, func(t *testing.T) {
|
||||
got, err := scenario.Provider.GetConfig("", &scenario.InputAlert)
|
||||
if err != nil && !strings.Contains(err.Error(), "user does not exist") && !strings.Contains(err.Error(), "no such host") {
|
||||
if err != nil && !isIgnorableTestError(err) {
|
||||
t.Fatalf("unexpected error: %s", err)
|
||||
}
|
||||
if got.RepositoryURL != scenario.ExpectedOutput.RepositoryURL {
|
||||
@@ -221,7 +233,7 @@ func TestAlertProvider_GetConfig(t *testing.T) {
|
||||
}
|
||||
}
|
||||
// Test ValidateOverrides as well, since it really just calls GetConfig
|
||||
if err = scenario.Provider.ValidateOverrides("", &scenario.InputAlert); err != nil && !strings.Contains(err.Error(), "user does not exist") {
|
||||
if err = scenario.Provider.ValidateOverrides("", &scenario.InputAlert); err != nil && !isIgnorableTestError(err) {
|
||||
t.Errorf("unexpected error: %s", err)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"maps"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -170,9 +171,7 @@ func (provider *AlertProvider) buildRequestBody(cfg *Config, ep *endpoint.Endpoi
|
||||
// Merge metadata: cfg.Metadata + ep.ExtraLabels (if present)
|
||||
mergedMetadata := map[string]interface{}{}
|
||||
// Copy cfg.Metadata
|
||||
for k, v := range cfg.Metadata {
|
||||
mergedMetadata[k] = v
|
||||
}
|
||||
maps.Copy(mergedMetadata, cfg.Metadata)
|
||||
// Add extra labels from endpoint (if present)
|
||||
if ep.ExtraLabels != nil && len(ep.ExtraLabels) > 0 {
|
||||
for k, v := range ep.ExtraLabels {
|
||||
|
||||
+1
-1
@@ -299,7 +299,7 @@ func getBadgeColorFromResponseTime(responseTime int, key string, cfg *config.Con
|
||||
thresholds = endpoint.UIConfig.Badge.ResponseTime.Thresholds
|
||||
}
|
||||
// the threshold config requires 5 values, so we can be sure it's set here
|
||||
for i := 0; i < 5; i++ {
|
||||
for i := range 5 {
|
||||
if responseTime <= thresholds[i] {
|
||||
return badgeColors[i]
|
||||
}
|
||||
|
||||
+2
-1
@@ -6,6 +6,7 @@ import (
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -136,7 +137,7 @@ func (config *Config) GetUniqueExtraMetricLabels() []string {
|
||||
continue
|
||||
}
|
||||
for label := range ep.ExtraLabels {
|
||||
if contains(labels, label) {
|
||||
if slices.Contains(labels, label) {
|
||||
continue
|
||||
}
|
||||
labels = append(labels, label)
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -2052,7 +2053,7 @@ func TestConfig_GetUniqueExtraMetricLabels(t *testing.T) {
|
||||
t.Errorf("expected %d labels, got %d", len(tt.expected), len(labels))
|
||||
}
|
||||
for _, label := range tt.expected {
|
||||
if !contains(labels, label) {
|
||||
if !slices.Contains(labels, label) {
|
||||
t.Errorf("expected label %s to be present", label)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ type Condition string
|
||||
// Validate checks if the Condition is valid
|
||||
func (c Condition) Validate() error {
|
||||
r := &Result{}
|
||||
c.evaluate(r, false, nil)
|
||||
c.evaluate(r, false, false, nil)
|
||||
if len(r.Errors) != 0 {
|
||||
return errors.New(r.Errors[0])
|
||||
}
|
||||
@@ -34,44 +34,50 @@ func (c Condition) Validate() error {
|
||||
}
|
||||
|
||||
// evaluate the Condition with the Result and an optional context
|
||||
func (c Condition) evaluate(result *Result, dontResolveFailedConditions bool, context *gontext.Gontext) bool {
|
||||
func (c Condition) evaluate(result *Result, dontResolveFailedConditions bool, resolveSuccessfulConditions bool, context *gontext.Gontext) bool {
|
||||
condition := string(c)
|
||||
success := false
|
||||
conditionToDisplay := condition
|
||||
shouldResolveCondition := func(success bool) bool {
|
||||
if success {
|
||||
return resolveSuccessfulConditions
|
||||
}
|
||||
return !dontResolveFailedConditions
|
||||
}
|
||||
if strings.Contains(condition, " == ") {
|
||||
parameters, resolvedParameters := sanitizeAndResolveWithContext(strings.Split(condition, " == "), result, context)
|
||||
success = isEqual(resolvedParameters[0], resolvedParameters[1])
|
||||
if !success && !dontResolveFailedConditions {
|
||||
if shouldResolveCondition(success) {
|
||||
conditionToDisplay = prettify(parameters, resolvedParameters, "==")
|
||||
}
|
||||
} else if strings.Contains(condition, " != ") {
|
||||
parameters, resolvedParameters := sanitizeAndResolveWithContext(strings.Split(condition, " != "), result, context)
|
||||
success = !isEqual(resolvedParameters[0], resolvedParameters[1])
|
||||
if !success && !dontResolveFailedConditions {
|
||||
if shouldResolveCondition(success) {
|
||||
conditionToDisplay = prettify(parameters, resolvedParameters, "!=")
|
||||
}
|
||||
} else if strings.Contains(condition, " <= ") {
|
||||
parameters, resolvedParameters := sanitizeAndResolveNumericalWithContext(strings.Split(condition, " <= "), result, context)
|
||||
success = resolvedParameters[0] <= resolvedParameters[1]
|
||||
if !success && !dontResolveFailedConditions {
|
||||
if shouldResolveCondition(success) {
|
||||
conditionToDisplay = prettifyNumericalParameters(parameters, resolvedParameters, "<=")
|
||||
}
|
||||
} else if strings.Contains(condition, " >= ") {
|
||||
parameters, resolvedParameters := sanitizeAndResolveNumericalWithContext(strings.Split(condition, " >= "), result, context)
|
||||
success = resolvedParameters[0] >= resolvedParameters[1]
|
||||
if !success && !dontResolveFailedConditions {
|
||||
if shouldResolveCondition(success) {
|
||||
conditionToDisplay = prettifyNumericalParameters(parameters, resolvedParameters, ">=")
|
||||
}
|
||||
} else if strings.Contains(condition, " > ") {
|
||||
parameters, resolvedParameters := sanitizeAndResolveNumericalWithContext(strings.Split(condition, " > "), result, context)
|
||||
success = resolvedParameters[0] > resolvedParameters[1]
|
||||
if !success && !dontResolveFailedConditions {
|
||||
if shouldResolveCondition(success) {
|
||||
conditionToDisplay = prettifyNumericalParameters(parameters, resolvedParameters, ">")
|
||||
}
|
||||
} else if strings.Contains(condition, " < ") {
|
||||
parameters, resolvedParameters := sanitizeAndResolveNumericalWithContext(strings.Split(condition, " < "), result, context)
|
||||
success = resolvedParameters[0] < resolvedParameters[1]
|
||||
if !success && !dontResolveFailedConditions {
|
||||
if shouldResolveCondition(success) {
|
||||
conditionToDisplay = prettifyNumericalParameters(parameters, resolvedParameters, "<")
|
||||
}
|
||||
} else {
|
||||
@@ -210,7 +216,7 @@ func sanitizeAndResolveNumericalWithContext(list []string, result *Result, conte
|
||||
|
||||
func prettifyNumericalParameters(parameters []string, resolvedParameters []int64, operator string) string {
|
||||
resolvedStrings := make([]string, 2)
|
||||
for i := 0; i < 2; i++ {
|
||||
for i := range 2 {
|
||||
// Check if the parameter is a certificate or domain expiration placeholder
|
||||
if parameters[i] == CertificateExpirationPlaceholder || parameters[i] == DomainExpirationPlaceholder {
|
||||
// Format as duration string (convert milliseconds back to duration)
|
||||
|
||||
@@ -8,7 +8,7 @@ func BenchmarkCondition_evaluateWithBodyStringAny(b *testing.B) {
|
||||
condition := Condition("[BODY].name == any(john.doe, jane.doe)")
|
||||
for n := 0; n < b.N; n++ {
|
||||
result := &Result{Body: []byte("{\"name\": \"john.doe\"}")}
|
||||
condition.evaluate(result, false, nil)
|
||||
condition.evaluate(result, false, false, nil)
|
||||
}
|
||||
b.ReportAllocs()
|
||||
}
|
||||
@@ -17,7 +17,7 @@ func BenchmarkCondition_evaluateWithBodyStringAnyFailure(b *testing.B) {
|
||||
condition := Condition("[BODY].name == any(john.doe, jane.doe)")
|
||||
for n := 0; n < b.N; n++ {
|
||||
result := &Result{Body: []byte("{\"name\": \"bob.doe\"}")}
|
||||
condition.evaluate(result, false, nil)
|
||||
condition.evaluate(result, false, false, nil)
|
||||
}
|
||||
b.ReportAllocs()
|
||||
}
|
||||
@@ -26,7 +26,7 @@ func BenchmarkCondition_evaluateWithBodyString(b *testing.B) {
|
||||
condition := Condition("[BODY].name == john.doe")
|
||||
for n := 0; n < b.N; n++ {
|
||||
result := &Result{Body: []byte("{\"name\": \"john.doe\"}")}
|
||||
condition.evaluate(result, false, nil)
|
||||
condition.evaluate(result, false, false, nil)
|
||||
}
|
||||
b.ReportAllocs()
|
||||
}
|
||||
@@ -35,7 +35,7 @@ func BenchmarkCondition_evaluateWithBodyStringFailure(b *testing.B) {
|
||||
condition := Condition("[BODY].name == john.doe")
|
||||
for n := 0; n < b.N; n++ {
|
||||
result := &Result{Body: []byte("{\"name\": \"bob.doe\"}")}
|
||||
condition.evaluate(result, false, nil)
|
||||
condition.evaluate(result, false, false, nil)
|
||||
}
|
||||
b.ReportAllocs()
|
||||
}
|
||||
@@ -44,7 +44,7 @@ func BenchmarkCondition_evaluateWithBodyStringFailureInvalidPath(b *testing.B) {
|
||||
condition := Condition("[BODY].user.name == bob.doe")
|
||||
for n := 0; n < b.N; n++ {
|
||||
result := &Result{Body: []byte("{\"name\": \"bob.doe\"}")}
|
||||
condition.evaluate(result, false, nil)
|
||||
condition.evaluate(result, false, false, nil)
|
||||
}
|
||||
b.ReportAllocs()
|
||||
}
|
||||
@@ -53,7 +53,7 @@ func BenchmarkCondition_evaluateWithBodyStringLen(b *testing.B) {
|
||||
condition := Condition("len([BODY].name) == 8")
|
||||
for n := 0; n < b.N; n++ {
|
||||
result := &Result{Body: []byte("{\"name\": \"john.doe\"}")}
|
||||
condition.evaluate(result, false, nil)
|
||||
condition.evaluate(result, false, false, nil)
|
||||
}
|
||||
b.ReportAllocs()
|
||||
}
|
||||
@@ -62,7 +62,7 @@ func BenchmarkCondition_evaluateWithBodyStringLenFailure(b *testing.B) {
|
||||
condition := Condition("len([BODY].name) == 8")
|
||||
for n := 0; n < b.N; n++ {
|
||||
result := &Result{Body: []byte("{\"name\": \"bob.doe\"}")}
|
||||
condition.evaluate(result, false, nil)
|
||||
condition.evaluate(result, false, false, nil)
|
||||
}
|
||||
b.ReportAllocs()
|
||||
}
|
||||
@@ -71,7 +71,7 @@ func BenchmarkCondition_evaluateWithStatus(b *testing.B) {
|
||||
condition := Condition("[STATUS] == 200")
|
||||
for n := 0; n < b.N; n++ {
|
||||
result := &Result{HTTPStatus: 200}
|
||||
condition.evaluate(result, false, nil)
|
||||
condition.evaluate(result, false, false, nil)
|
||||
}
|
||||
b.ReportAllocs()
|
||||
}
|
||||
@@ -80,7 +80,7 @@ func BenchmarkCondition_evaluateWithStatusFailure(b *testing.B) {
|
||||
condition := Condition("[STATUS] == 200")
|
||||
for n := 0; n < b.N; n++ {
|
||||
result := &Result{HTTPStatus: 400}
|
||||
condition.evaluate(result, false, nil)
|
||||
condition.evaluate(result, false, false, nil)
|
||||
}
|
||||
b.ReportAllocs()
|
||||
}
|
||||
|
||||
@@ -62,6 +62,7 @@ func TestCondition_evaluate(t *testing.T) {
|
||||
Condition Condition
|
||||
Result *Result
|
||||
DontResolveFailedConditions bool
|
||||
ResolveSuccessfulConditions bool
|
||||
ExpectedSuccess bool
|
||||
ExpectedOutput string
|
||||
}{
|
||||
@@ -184,6 +185,14 @@ func TestCondition_evaluate(t *testing.T) {
|
||||
ExpectedSuccess: true,
|
||||
ExpectedOutput: "[BODY] == test",
|
||||
},
|
||||
{
|
||||
Name: "body-resolved-on-success",
|
||||
Condition: Condition("[BODY].status == UP"),
|
||||
Result: &Result{Body: []byte("{\"status\":\"UP\"}")},
|
||||
ResolveSuccessfulConditions: true,
|
||||
ExpectedSuccess: true,
|
||||
ExpectedOutput: "[BODY].status (UP) == UP",
|
||||
},
|
||||
{
|
||||
Name: "body-numerical-equal",
|
||||
Condition: Condition("[BODY] == 123"),
|
||||
@@ -757,7 +766,7 @@ func TestCondition_evaluate(t *testing.T) {
|
||||
}
|
||||
for _, scenario := range scenarios {
|
||||
t.Run(scenario.Name, func(t *testing.T) {
|
||||
scenario.Condition.evaluate(scenario.Result, scenario.DontResolveFailedConditions, nil)
|
||||
scenario.Condition.evaluate(scenario.Result, scenario.DontResolveFailedConditions, scenario.ResolveSuccessfulConditions, nil)
|
||||
if scenario.Result.ConditionResults[0].Success != scenario.ExpectedSuccess {
|
||||
t.Errorf("Condition '%s' should have been success=%v", scenario.Condition, scenario.ExpectedSuccess)
|
||||
}
|
||||
@@ -771,7 +780,7 @@ func TestCondition_evaluate(t *testing.T) {
|
||||
func TestCondition_evaluateWithInvalidOperator(t *testing.T) {
|
||||
condition := Condition("[STATUS] ? 201")
|
||||
result := &Result{HTTPStatus: 201}
|
||||
condition.evaluate(result, false, nil)
|
||||
condition.evaluate(result, false, false, nil)
|
||||
if result.Success {
|
||||
t.Error("condition was invalid, result should've been a failure")
|
||||
}
|
||||
@@ -791,7 +800,7 @@ func TestConditionEvaluateWithInvalidContextPlaceholder(t *testing.T) {
|
||||
"max_response_time": 5000,
|
||||
})
|
||||
// Simulate suite endpoint evaluation with context
|
||||
success := condition.evaluate(result, false, ctx) // false = don't skip resolution (default)
|
||||
success := condition.evaluate(result, false, false, ctx) // false = don't skip resolution (default)
|
||||
if success {
|
||||
t.Error("Condition should have failed because [CONTEXT].expected_statusz doesn't exist")
|
||||
}
|
||||
@@ -814,7 +823,7 @@ func TestConditionEvaluateWithValidContextPlaceholder(t *testing.T) {
|
||||
"expected_status": 200,
|
||||
})
|
||||
// Simulate suite endpoint evaluation with context
|
||||
success := condition.evaluate(result, false, ctx)
|
||||
success := condition.evaluate(result, false, false, ctx)
|
||||
if !success {
|
||||
t.Error("Condition should have succeeded")
|
||||
}
|
||||
@@ -839,7 +848,7 @@ func TestConditionEvaluateWithMixedValidAndInvalidContext(t *testing.T) {
|
||||
"valid_key": 5000,
|
||||
})
|
||||
// Simulate suite endpoint evaluation with context
|
||||
success := condition.evaluate(result, false, ctx)
|
||||
success := condition.evaluate(result, false, false, ctx)
|
||||
if success {
|
||||
t.Error("Condition should have failed because [CONTEXT].invalid_key doesn't exist")
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"maps"
|
||||
"math/rand"
|
||||
"net"
|
||||
"net/http"
|
||||
@@ -221,12 +222,12 @@ func (e *Endpoint) ValidateAndSetDefaults() error {
|
||||
e.Headers = make(map[string]string)
|
||||
}
|
||||
// Automatically add user agent header if there isn't one specified in the endpoint configuration
|
||||
if _, userAgentHeaderExists := e.Headers[UserAgentHeader]; !userAgentHeaderExists {
|
||||
if !hasHeader(e.Headers, UserAgentHeader) {
|
||||
e.Headers[UserAgentHeader] = GatusUserAgent
|
||||
}
|
||||
// Automatically add "Content-Type: application/json" header if there's no Content-Type set
|
||||
// and endpoint.GraphQL is set to true
|
||||
if _, contentTypeHeaderExists := e.Headers[ContentTypeHeader]; !contentTypeHeaderExists && e.GraphQL {
|
||||
if !hasHeader(e.Headers, ContentTypeHeader) && e.GraphQL {
|
||||
e.Headers[ContentTypeHeader] = "application/json"
|
||||
}
|
||||
if len(e.Conditions) == 0 {
|
||||
@@ -332,7 +333,7 @@ func (e *Endpoint) EvaluateHealthWithContext(context *gontext.Gontext) *Result {
|
||||
}
|
||||
// Evaluate the conditions
|
||||
for _, condition := range processedEndpoint.Conditions {
|
||||
success := condition.evaluate(result, processedEndpoint.UIConfig.DontResolveFailedConditions, context)
|
||||
success := condition.evaluate(result, processedEndpoint.UIConfig.DontResolveFailedConditions, processedEndpoint.UIConfig.ResolveSuccessfulConditions, context)
|
||||
if !success {
|
||||
result.Success = false
|
||||
}
|
||||
@@ -489,12 +490,10 @@ func (e *Endpoint) call(result *Result) {
|
||||
} else if endpointType == TypeWS {
|
||||
wsHeaders := map[string]string{}
|
||||
if e.Headers != nil {
|
||||
for k, v := range e.Headers {
|
||||
wsHeaders[k] = v
|
||||
}
|
||||
maps.Copy(wsHeaders, e.Headers)
|
||||
}
|
||||
if _, exists := wsHeaders["User-Agent"]; !exists {
|
||||
wsHeaders["User-Agent"] = GatusUserAgent
|
||||
if !hasHeader(wsHeaders, UserAgentHeader) {
|
||||
wsHeaders[UserAgentHeader] = GatusUserAgent
|
||||
}
|
||||
result.Connected, result.Body, err = client.QueryWebSocket(e.URL, e.getParsedBody(), wsHeaders, e.ClientConfig)
|
||||
if err != nil {
|
||||
@@ -582,7 +581,7 @@ func (e *Endpoint) buildHTTPRequest() *http.Request {
|
||||
request, _ := http.NewRequest(e.Method, e.URL, bodyBuffer)
|
||||
for k, v := range e.Headers {
|
||||
request.Header.Set(k, v)
|
||||
if k == HostHeader {
|
||||
if strings.EqualFold(k, HostHeader) {
|
||||
request.Host = v
|
||||
}
|
||||
}
|
||||
@@ -626,3 +625,13 @@ func (e *Endpoint) needsToRetrieveIP() bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// hasHeader checks if a header exists in the map using a case-insensitive lookup
|
||||
func hasHeader(headers map[string]string, name string) bool {
|
||||
for k := range headers {
|
||||
if strings.EqualFold(k, name) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -21,6 +21,30 @@ import (
|
||||
"github.com/TwiN/gatus/v5/test"
|
||||
)
|
||||
|
||||
func TestHasHeader(t *testing.T) {
|
||||
scenarios := []struct {
|
||||
name string
|
||||
headers map[string]string
|
||||
lookup string
|
||||
expected bool
|
||||
}{
|
||||
{name: "exact-match", headers: map[string]string{"User-Agent": "test"}, lookup: "User-Agent", expected: true},
|
||||
{name: "lowercase-lookup", headers: map[string]string{"User-Agent": "test"}, lookup: "user-agent", expected: true},
|
||||
{name: "uppercase-lookup", headers: map[string]string{"user-agent": "test"}, lookup: "USER-AGENT", expected: true},
|
||||
{name: "mixed-case", headers: map[string]string{"UsEr-AgEnT": "test"}, lookup: "uSeR-aGeNt", expected: true},
|
||||
{name: "not-found", headers: map[string]string{"Content-Type": "test"}, lookup: "User-Agent", expected: false},
|
||||
{name: "empty-headers", headers: map[string]string{}, lookup: "User-Agent", expected: false},
|
||||
{name: "nil-headers", headers: nil, lookup: "User-Agent", expected: false},
|
||||
}
|
||||
for _, scenario := range scenarios {
|
||||
t.Run(scenario.name, func(t *testing.T) {
|
||||
if result := hasHeader(scenario.headers, scenario.lookup); result != scenario.expected {
|
||||
t.Errorf("expected %v, got %v", scenario.expected, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestEndpoint(t *testing.T) {
|
||||
defer client.InjectHTTPClient(nil)
|
||||
scenarios := []struct {
|
||||
@@ -268,6 +292,31 @@ func TestEndpoint(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestEndpoint_ResolveSuccessfulConditions(t *testing.T) {
|
||||
defer client.InjectHTTPClient(nil)
|
||||
endpoint := Endpoint{
|
||||
Name: "test-endpoint",
|
||||
URL: "https://example.com/health",
|
||||
Conditions: []Condition{"[BODY].status == UP"},
|
||||
UIConfig: &ui.Config{ResolveSuccessfulConditions: true},
|
||||
}
|
||||
mockResponse := test.MockRoundTripper(func(r *http.Request) *http.Response {
|
||||
return &http.Response{StatusCode: http.StatusOK, Body: io.NopCloser(bytes.NewBufferString(`{"status":"UP"}`))}
|
||||
})
|
||||
client.InjectHTTPClient(&http.Client{Transport: mockResponse})
|
||||
if err := endpoint.ValidateAndSetDefaults(); err != nil {
|
||||
t.Fatalf("ValidateAndSetDefaults failed: %v", err)
|
||||
}
|
||||
result := endpoint.EvaluateHealth()
|
||||
if len(result.ConditionResults) != 1 {
|
||||
t.Fatalf("expected 1 condition result, got %d", len(result.ConditionResults))
|
||||
}
|
||||
expectedCondition := "[BODY].status (UP) == UP"
|
||||
if result.ConditionResults[0].Condition != expectedCondition {
|
||||
t.Errorf("expected condition to be '%s', got '%s'", expectedCondition, result.ConditionResults[0].Condition)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEndpoint_IsEnabled(t *testing.T) {
|
||||
if !(&Endpoint{Enabled: nil}).IsEnabled() {
|
||||
t.Error("endpoint.IsEnabled() should've returned true, because Enabled was set to nil")
|
||||
@@ -697,6 +746,76 @@ func TestEndpoint_buildHTTPRequestWithHostHeader(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestEndpoint_buildHTTPRequestWithLowercaseUserAgent(t *testing.T) {
|
||||
condition := Condition("[STATUS] == 200")
|
||||
endpoint := Endpoint{
|
||||
Name: "website-health",
|
||||
URL: "https://twin.sh/health",
|
||||
Conditions: []Condition{condition},
|
||||
Headers: map[string]string{
|
||||
"user-agent": "CustomAgent/1.0",
|
||||
},
|
||||
}
|
||||
err := endpoint.ValidateAndSetDefaults()
|
||||
if err != nil {
|
||||
t.Fatal("did not expect an error, got", err)
|
||||
}
|
||||
if _, exists := endpoint.Headers[UserAgentHeader]; exists {
|
||||
t.Error("User-Agent header should not have been added since user-agent was already specified")
|
||||
}
|
||||
request := endpoint.buildHTTPRequest()
|
||||
if userAgent := request.Header.Get("User-Agent"); userAgent != "CustomAgent/1.0" {
|
||||
t.Errorf("request.Header.Get(User-Agent) should've been CustomAgent/1.0, but was %s", userAgent)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEndpoint_buildHTTPRequestWithLowercaseContentType(t *testing.T) {
|
||||
condition := Condition("[STATUS] == 200")
|
||||
endpoint := Endpoint{
|
||||
Name: "website-graphql",
|
||||
URL: "https://twin.sh/graphql",
|
||||
Method: "POST",
|
||||
Conditions: []Condition{condition},
|
||||
GraphQL: true,
|
||||
Headers: map[string]string{
|
||||
"content-type": "application/graphql",
|
||||
},
|
||||
Body: `{ users { id } }`,
|
||||
}
|
||||
err := endpoint.ValidateAndSetDefaults()
|
||||
if err != nil {
|
||||
t.Fatal("did not expect an error, got", err)
|
||||
}
|
||||
if _, exists := endpoint.Headers[ContentTypeHeader]; exists {
|
||||
t.Error("Content-Type header should not have been added since content-type was already specified")
|
||||
}
|
||||
request := endpoint.buildHTTPRequest()
|
||||
if contentType := request.Header.Get("Content-Type"); contentType != "application/graphql" {
|
||||
t.Errorf("request.Header.Get(Content-Type) should've been application/graphql, but was %s", contentType)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEndpoint_buildHTTPRequestWithLowercaseHostHeader(t *testing.T) {
|
||||
condition := Condition("[STATUS] == 200")
|
||||
endpoint := Endpoint{
|
||||
Name: "website-health",
|
||||
URL: "https://twin.sh/health",
|
||||
Method: "POST",
|
||||
Conditions: []Condition{condition},
|
||||
Headers: map[string]string{
|
||||
"host": "example.com",
|
||||
},
|
||||
}
|
||||
err := endpoint.ValidateAndSetDefaults()
|
||||
if err != nil {
|
||||
t.Fatal("did not expect an error, got", err)
|
||||
}
|
||||
request := endpoint.buildHTTPRequest()
|
||||
if request.Host != "example.com" {
|
||||
t.Error("request.Host should've been example.com, but was", request.Host)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEndpoint_buildHTTPRequestWithGraphQLEnabled(t *testing.T) {
|
||||
condition := Condition("[STATUS] == 200")
|
||||
endpoint := Endpoint{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package endpoint
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -66,11 +67,7 @@ type Result struct {
|
||||
// AddError adds an error to the result's list of errors.
|
||||
// It also ensures that there are no duplicates.
|
||||
func (r *Result) AddError(error string) {
|
||||
for _, resultError := range r.Errors {
|
||||
if resultError == error {
|
||||
// If the error already exists, don't add it
|
||||
return
|
||||
}
|
||||
if !slices.Contains(r.Errors, error) {
|
||||
r.Errors = append(r.Errors, error+"")
|
||||
}
|
||||
r.Errors = append(r.Errors, error+"")
|
||||
}
|
||||
|
||||
@@ -22,6 +22,9 @@ type Config struct {
|
||||
// DontResolveFailedConditions whether to resolve failed conditions in the Result for display in the UI
|
||||
DontResolveFailedConditions bool `yaml:"dont-resolve-failed-conditions"`
|
||||
|
||||
// ResolveSuccessfulConditions whether to resolve successful conditions in the Result for display in the UI
|
||||
ResolveSuccessfulConditions bool `yaml:"resolve-successful-conditions"`
|
||||
|
||||
// Badge is the configuration for the badges generated
|
||||
Badge *Badge `yaml:"badge"`
|
||||
}
|
||||
@@ -63,6 +66,7 @@ func GetDefaultConfig() *Config {
|
||||
HidePort: false,
|
||||
HideErrors: false,
|
||||
DontResolveFailedConditions: false,
|
||||
ResolveSuccessfulConditions: false,
|
||||
HideConditions: false,
|
||||
Badge: &Badge{
|
||||
ResponseTime: &ResponseTime{
|
||||
|
||||
@@ -340,9 +340,9 @@ func TestGontext_ConcurrentAccess(t *testing.T) {
|
||||
done := make(chan bool, 10)
|
||||
|
||||
// Start 5 goroutines that read values
|
||||
for i := 0; i < 5; i++ {
|
||||
for i := range 5 {
|
||||
go func(id int) {
|
||||
for j := 0; j < 100; j++ {
|
||||
for range 100 {
|
||||
_, err := ctx.Get("counter")
|
||||
if err != nil {
|
||||
t.Errorf("Reader %d error: %v", id, err)
|
||||
@@ -353,9 +353,9 @@ func TestGontext_ConcurrentAccess(t *testing.T) {
|
||||
}
|
||||
|
||||
// Start 5 goroutines that write values
|
||||
for i := 0; i < 5; i++ {
|
||||
for i := range 5 {
|
||||
go func(id int) {
|
||||
for j := 0; j < 100; j++ {
|
||||
for j := range 100 {
|
||||
err := ctx.Set("counter", id*1000+j)
|
||||
if err != nil {
|
||||
t.Errorf("Writer %d error: %v", id, err)
|
||||
@@ -366,7 +366,7 @@ func TestGontext_ConcurrentAccess(t *testing.T) {
|
||||
}
|
||||
|
||||
// Wait for all goroutines to complete
|
||||
for i := 0; i < 10; i++ {
|
||||
for range 10 {
|
||||
<-done
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package maintenance
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -70,13 +71,7 @@ func (c *Config) ValidateAndSetDefaults() error {
|
||||
return nil
|
||||
}
|
||||
for _, day := range c.Every {
|
||||
isDayValid := false
|
||||
for _, longDayName := range longDayNames {
|
||||
if day == longDayName {
|
||||
isDayValid = true
|
||||
break
|
||||
}
|
||||
}
|
||||
isDayValid := slices.Contains(longDayNames, day)
|
||||
if !isDayValid {
|
||||
return errInvalidDayName
|
||||
}
|
||||
@@ -118,7 +113,7 @@ func (c *Config) IsUnderMaintenance() bool {
|
||||
// Set to midnight prior to adding duration
|
||||
dayWhereMaintenancePeriodWouldStart := time.Date(now.Year(), now.Month(), adjustedDate, 0, 0, 0, 0, now.Location())
|
||||
hasMaintenanceEveryDay := len(c.Every) == 0
|
||||
hasMaintenancePeriodScheduledToStartOnThatWeekday := c.hasDay(dayWhereMaintenancePeriodWouldStart.Weekday().String())
|
||||
hasMaintenancePeriodScheduledToStartOnThatWeekday := slices.Contains(c.Every, dayWhereMaintenancePeriodWouldStart.Weekday().String())
|
||||
if !hasMaintenanceEveryDay && !hasMaintenancePeriodScheduledToStartOnThatWeekday {
|
||||
// The day when the maintenance period would start is not scheduled
|
||||
// to have any maintenance, so we can just return false.
|
||||
@@ -129,15 +124,6 @@ func (c *Config) IsUnderMaintenance() bool {
|
||||
return now.After(startOfMaintenancePeriod) && now.Before(endOfMaintenancePeriod)
|
||||
}
|
||||
|
||||
func (c *Config) hasDay(day string) bool {
|
||||
for _, d := range c.Every {
|
||||
if d == day {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func hhmmToDuration(s string) (time.Duration, error) {
|
||||
if len(s) != 5 {
|
||||
return 0, errInvalidMaintenanceStartFormat
|
||||
|
||||
@@ -133,7 +133,7 @@ func (t *SSHTunnel) Dial(network, addr string) (net.Conn, error) {
|
||||
const maxRetries = 3
|
||||
const baseDelay = 500 * time.Millisecond
|
||||
var lastErr error
|
||||
for attempt := 0; attempt < maxRetries; attempt++ {
|
||||
for attempt := range maxRetries {
|
||||
if attempt > 0 {
|
||||
// Exponential backoff: 500ms, 1s, 2s
|
||||
delay := baseDelay << (attempt - 1)
|
||||
|
||||
@@ -17,6 +17,9 @@ const (
|
||||
defaultDashboardSubheading = "Monitor the health of your endpoints in real-time"
|
||||
defaultLogo = ""
|
||||
defaultLink = ""
|
||||
defaultFavicon = "/favicon.ico"
|
||||
defaultFavicon16 = "/favicon-16x16.png"
|
||||
defaultFavicon32 = "/favicon-32x32.png"
|
||||
defaultCustomCSS = ""
|
||||
defaultSortBy = "name"
|
||||
defaultFilterBy = "none"
|
||||
@@ -39,6 +42,7 @@ type Config struct {
|
||||
Header string `yaml:"header,omitempty"` // Header is the text at the top of the page
|
||||
Logo string `yaml:"logo,omitempty"` // Logo to display on the page
|
||||
Link string `yaml:"link,omitempty"` // Link to open when clicking on the logo
|
||||
Favicon Favicon `yaml:"favicon,omitempty"` // Favourite icon to display in web browser tab or address bar
|
||||
Buttons []Button `yaml:"buttons,omitempty"` // Buttons to display below the header
|
||||
CustomCSS string `yaml:"custom-css,omitempty"` // Custom CSS to include in the page
|
||||
DarkMode *bool `yaml:"dark-mode,omitempty"` // DarkMode is a flag to enable dark mode by default
|
||||
@@ -71,6 +75,12 @@ func (btn *Button) Validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type Favicon struct {
|
||||
Default string `yaml:"default,omitempty"` // URL or path to default favourite icon.
|
||||
Size16x16 string `yaml:"size16x16,omitempty"` // URL or path to favourite icon for 16x16 size.
|
||||
Size32x32 string `yaml:"size32x32,omitempty"` // URL or path to favourite icon for 32x32 size.
|
||||
}
|
||||
|
||||
// GetDefaultConfig returns a Config struct with the default values
|
||||
func GetDefaultConfig() *Config {
|
||||
return &Config{
|
||||
@@ -86,6 +96,11 @@ func GetDefaultConfig() *Config {
|
||||
DefaultSortBy: defaultSortBy,
|
||||
DefaultFilterBy: defaultFilterBy,
|
||||
MaximumNumberOfResults: storage.DefaultMaximumNumberOfResults,
|
||||
Favicon: Favicon{
|
||||
Default: defaultFavicon,
|
||||
Size16x16: defaultFavicon16,
|
||||
Size32x32: defaultFavicon32,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,6 +143,15 @@ func (cfg *Config) ValidateAndSetDefaults() error {
|
||||
} else if cfg.DefaultFilterBy != "none" && cfg.DefaultFilterBy != "failing" && cfg.DefaultFilterBy != "unstable" {
|
||||
return ErrInvalidDefaultFilterBy
|
||||
}
|
||||
if len(cfg.Favicon.Default) == 0 {
|
||||
cfg.Favicon.Default = defaultFavicon
|
||||
}
|
||||
if len(cfg.Favicon.Size16x16) == 0 {
|
||||
cfg.Favicon.Size16x16 = defaultFavicon16
|
||||
}
|
||||
if len(cfg.Favicon.Size32x32) == 0 {
|
||||
cfg.Favicon.Size32x32 = defaultFavicon32
|
||||
}
|
||||
for _, btn := range cfg.Buttons {
|
||||
if err := btn.Validate(); err != nil {
|
||||
return err
|
||||
|
||||
@@ -41,6 +41,15 @@ func TestConfig_ValidateAndSetDefaults(t *testing.T) {
|
||||
if cfg.DefaultFilterBy != defaultFilterBy {
|
||||
t.Errorf("expected defaultFilterBy to be %s, got %s", defaultFilterBy, cfg.DefaultFilterBy)
|
||||
}
|
||||
if cfg.Favicon.Default != defaultFavicon {
|
||||
t.Errorf("expected favicon to be %s, got %s", defaultFavicon, cfg.Favicon.Default)
|
||||
}
|
||||
if cfg.Favicon.Size16x16 != defaultFavicon16 {
|
||||
t.Errorf("expected favicon to be %s, got %s", defaultFavicon16, cfg.Favicon.Size16x16)
|
||||
}
|
||||
if cfg.Favicon.Size32x32 != defaultFavicon32 {
|
||||
t.Errorf("expected favicon to be %s, got %s", defaultFavicon32, cfg.Favicon.Size32x32)
|
||||
}
|
||||
})
|
||||
t.Run("custom-values", func(t *testing.T) {
|
||||
cfg := &Config{
|
||||
|
||||
@@ -4,13 +4,3 @@ package config
|
||||
func toPtr[T any](value T) *T {
|
||||
return &value
|
||||
}
|
||||
|
||||
// contains checks if a key exists in the slice
|
||||
func contains[T comparable](slice []T, key T) bool {
|
||||
for _, item := range slice {
|
||||
if item == key {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -1,63 +1,63 @@
|
||||
module github.com/TwiN/gatus/v5
|
||||
|
||||
go 1.24.4
|
||||
|
||||
toolchain go1.24.7
|
||||
go 1.25.5
|
||||
|
||||
require (
|
||||
code.gitea.io/sdk/gitea v0.22.1
|
||||
code.gitea.io/sdk/gitea v0.23.2
|
||||
github.com/TwiN/deepmerge v0.2.2
|
||||
github.com/TwiN/g8/v2 v2.0.0
|
||||
github.com/TwiN/gocache/v2 v2.4.0
|
||||
github.com/TwiN/health v1.6.0
|
||||
github.com/TwiN/logr v0.3.1
|
||||
github.com/TwiN/whois v1.2.0
|
||||
github.com/aws/aws-sdk-go-v2 v1.40.0
|
||||
github.com/aws/aws-sdk-go-v2/config v1.32.2
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.2
|
||||
github.com/aws/aws-sdk-go-v2/service/ses v1.34.7
|
||||
github.com/coreos/go-oidc/v3 v3.16.0
|
||||
github.com/gofiber/fiber/v2 v2.52.9
|
||||
github.com/TwiN/whois v1.3.0
|
||||
github.com/aws/aws-sdk-go-v2 v1.41.1
|
||||
github.com/aws/aws-sdk-go-v2/config v1.32.7
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.7
|
||||
github.com/aws/aws-sdk-go-v2/service/ses v1.34.18
|
||||
github.com/coreos/go-oidc/v3 v3.17.0
|
||||
github.com/gofiber/fiber/v2 v2.52.11
|
||||
github.com/google/go-github/v48 v48.2.0
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/gorilla/websocket v1.5.3
|
||||
github.com/ishidawataru/sctp v0.0.0-20230406120618-7ff4192f6ff2
|
||||
github.com/lib/pq v1.10.9
|
||||
github.com/miekg/dns v1.1.68
|
||||
github.com/prometheus-community/pro-bing v0.7.0
|
||||
github.com/ishidawataru/sctp v0.0.0-20251114114122-19ddcbc6aae2
|
||||
github.com/lib/pq v1.11.1
|
||||
github.com/miekg/dns v1.1.72
|
||||
github.com/prometheus-community/pro-bing v0.8.0
|
||||
github.com/prometheus/client_golang v1.23.2
|
||||
github.com/registrobr/rdap v1.1.8
|
||||
github.com/valyala/fasthttp v1.67.0
|
||||
github.com/valyala/fasthttp v1.69.0
|
||||
github.com/wcharczuk/go-chart/v2 v2.1.2
|
||||
golang.org/x/crypto v0.45.0
|
||||
golang.org/x/oauth2 v0.32.0
|
||||
golang.org/x/sync v0.18.0
|
||||
google.golang.org/api v0.252.0
|
||||
google.golang.org/grpc v1.77.0
|
||||
golang.org/x/crypto v0.47.0
|
||||
golang.org/x/oauth2 v0.35.0
|
||||
golang.org/x/sync v0.19.0
|
||||
google.golang.org/api v0.265.0
|
||||
google.golang.org/grpc v1.78.0
|
||||
gopkg.in/mail.v2 v2.3.1
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
modernc.org/sqlite v1.39.1
|
||||
modernc.org/sqlite v1.44.3
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go/auth v0.17.0 // indirect
|
||||
cloud.google.com/go/auth v0.18.1 // indirect
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.9.0 // indirect
|
||||
github.com/42wim/httpsig v1.2.3 // indirect
|
||||
github.com/andybalholm/brotli v1.2.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.14 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.14 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.14 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.17 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.17 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.17 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.14 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/signin v1.0.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.10 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.41.2 // indirect
|
||||
github.com/aws/smithy-go v1.23.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.17 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/signin v1.0.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.9 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.13 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.41.6 // indirect
|
||||
github.com/aws/smithy-go v1.24.0 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/clipperhouse/stringish v0.1.1 // indirect
|
||||
github.com/clipperhouse/uax29/v2 v2.4.0 // indirect
|
||||
github.com/davidmz/go-pageant v1.0.2 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
@@ -68,39 +68,38 @@ require (
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
github.com/google/s2a-go v0.1.9 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.15.0 // indirect
|
||||
github.com/hashicorp/go-version v1.7.0 // indirect
|
||||
github.com/klauspost/compress v1.18.0 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.11 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.16.0 // indirect
|
||||
github.com/hashicorp/go-version v1.8.0 // indirect
|
||||
github.com/klauspost/compress v1.18.3 // indirect
|
||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.16 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.19 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/ncruces/go-strftime v0.1.9 // indirect
|
||||
github.com/ncruces/go-strftime v1.0.0 // indirect
|
||||
github.com/prometheus/client_model v0.6.2 // indirect
|
||||
github.com/prometheus/common v0.66.1 // indirect
|
||||
github.com/prometheus/procfs v0.16.1 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
|
||||
go.opentelemetry.io/otel v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel v1.39.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.39.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.39.0 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.2 // indirect
|
||||
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b // indirect
|
||||
golang.org/x/image v0.18.0 // indirect
|
||||
golang.org/x/mod v0.29.0 // indirect
|
||||
golang.org/x/net v0.47.0 // indirect
|
||||
golang.org/x/sys v0.38.0 // indirect
|
||||
golang.org/x/text v0.31.0 // indirect
|
||||
golang.org/x/tools v0.38.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8 // indirect
|
||||
google.golang.org/protobuf v1.36.10 // indirect
|
||||
golang.org/x/exp v0.0.0-20260112195511-716be5621a96 // indirect
|
||||
golang.org/x/image v0.35.0 // indirect
|
||||
golang.org/x/mod v0.32.0 // indirect
|
||||
golang.org/x/net v0.49.0 // indirect
|
||||
golang.org/x/sys v0.40.0 // indirect
|
||||
golang.org/x/text v0.33.0 // indirect
|
||||
golang.org/x/tools v0.41.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 // indirect
|
||||
google.golang.org/protobuf v1.36.11 // indirect
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||
modernc.org/libc v1.66.10 // indirect
|
||||
modernc.org/libc v1.67.7 // indirect
|
||||
modernc.org/mathutil v1.7.1 // indirect
|
||||
modernc.org/memory v1.11.0 // indirect
|
||||
)
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
cloud.google.com/go/auth v0.17.0 h1:74yCm7hCj2rUyyAocqnFzsAYXgJhrG26XCFimrc/Kz4=
|
||||
cloud.google.com/go/auth v0.17.0/go.mod h1:6wv/t5/6rOPAX4fJiRjKkJCvswLwdet7G8+UGXt7nCQ=
|
||||
cloud.google.com/go/auth v0.18.1 h1:IwTEx92GFUo2pJ6Qea0EU3zYvKnTAeRCODxfA/G5UWs=
|
||||
cloud.google.com/go/auth v0.18.1/go.mod h1:GfTYoS9G3CWpRA3Va9doKN9mjPGRS+v41jmZAhBzbrA=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c=
|
||||
cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs=
|
||||
cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10=
|
||||
code.gitea.io/sdk/gitea v0.22.1 h1:7K05KjRORyTcTYULQ/AwvlVS6pawLcWyXZcTr7gHFyA=
|
||||
code.gitea.io/sdk/gitea v0.22.1/go.mod h1:yyF5+GhljqvA30sRDreoyHILruNiy4ASufugzYg0VHM=
|
||||
code.gitea.io/sdk/gitea v0.23.2 h1:iJB1FDmLegwfwjX8gotBDHdPSbk/ZR8V9VmEJaVsJYg=
|
||||
code.gitea.io/sdk/gitea v0.23.2/go.mod h1:yyF5+GhljqvA30sRDreoyHILruNiy4ASufugzYg0VHM=
|
||||
github.com/42wim/httpsig v1.2.3 h1:xb0YyWhkYj57SPtfSttIobJUPJZB9as1nsfo7KWVcEs=
|
||||
github.com/42wim/httpsig v1.2.3/go.mod h1:nZq9OlYKDrUBhptd77IHx4/sZZD+IxTBADvAPI9G/EM=
|
||||
github.com/TwiN/deepmerge v0.2.2 h1:FUG9QMIYg/j2aQyPPhA3XTFJwXSNHI/swaR4Lbyxwg4=
|
||||
@@ -18,48 +18,52 @@ github.com/TwiN/health v1.6.0 h1:L2ks575JhRgQqWWOfKjw9B0ec172hx7GdToqkYUycQM=
|
||||
github.com/TwiN/health v1.6.0/go.mod h1:Z6TszwQPMvtSiVx1QMidVRgvVr4KZGfiwqcD7/Z+3iw=
|
||||
github.com/TwiN/logr v0.3.1 h1:CfTKA83jUmsAoxqrr3p4JxEkqXOBnEE9/f35L5MODy4=
|
||||
github.com/TwiN/logr v0.3.1/go.mod h1:BZgZFYq6fQdU3KtR8qYato3zUEw53yQDaIuujHb55Jw=
|
||||
github.com/TwiN/whois v1.2.0 h1:/Z22SrS3Z0FQgMl1+4bKSu9UmEQTfGx9i9J4Hn18eQk=
|
||||
github.com/TwiN/whois v1.2.0/go.mod h1:TjipCMpJRAJYKmtz/rXQBU6UGxMh6bk8SHazu7OMnQE=
|
||||
github.com/TwiN/whois v1.3.0 h1:V2+IUh5OGim8F3axTOMSlVmxNRaBrgpyiv5U0Dvbt5I=
|
||||
github.com/TwiN/whois v1.3.0/go.mod h1:TjipCMpJRAJYKmtz/rXQBU6UGxMh6bk8SHazu7OMnQE=
|
||||
github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
|
||||
github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
|
||||
github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b h1:uUXgbcPDK3KpW29o4iy7GtuappbWT0l5NaMo9H9pJDw=
|
||||
github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
|
||||
github.com/aws/aws-sdk-go-v2 v1.40.0 h1:/WMUA0kjhZExjOQN2z3oLALDREea1A7TobfuiBrKlwc=
|
||||
github.com/aws/aws-sdk-go-v2 v1.40.0/go.mod h1:c9pm7VwuW0UPxAEYGyTmyurVcNrbF6Rt/wixFqDhcjE=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.32.2 h1:4liUsdEpUUPZs5WVapsJLx5NPmQhQdez7nYFcovrytk=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.32.2/go.mod h1:l0hs06IFz1eCT+jTacU/qZtC33nvcnLADAPL/XyrkZI=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.2 h1:qZry8VUyTK4VIo5aEdUcBjPZHL2v4FyQ3QEOaWcFLu4=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.2/go.mod h1:YUqm5a1/kBnoK+/NY5WEiMocZihKSo15/tJdmdXnM5g=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.14 h1:WZVR5DbDgxzA0BJeudId89Kmgy6DIU4ORpxwsVHz0qA=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.14/go.mod h1:Dadl9QO0kHgbrH1GRqGiZdYtW5w+IXXaBNCHTIaheM4=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.14 h1:PZHqQACxYb8mYgms4RZbhZG0a7dPW06xOjmaH0EJC/I=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.14/go.mod h1:VymhrMJUWs69D8u0/lZ7jSB6WgaG/NqHi3gX0aYf6U0=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.14 h1:bOS19y6zlJwagBfHxs0ESzr1XCOU2KXJCWcq3E2vfjY=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.14/go.mod h1:1ipeGBMAxZ0xcTm6y6paC2C/J6f6OO7LBODV9afuAyM=
|
||||
github.com/aws/aws-sdk-go-v2 v1.41.1 h1:ABlyEARCDLN034NhxlRUSZr4l71mh+T5KAeGh6cerhU=
|
||||
github.com/aws/aws-sdk-go-v2 v1.41.1/go.mod h1:MayyLB8y+buD9hZqkCW3kX1AKq07Y5pXxtgB+rRFhz0=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.32.7 h1:vxUyWGUwmkQ2g19n7JY/9YL8MfAIl7bTesIUykECXmY=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.32.7/go.mod h1:2/Qm5vKUU/r7Y+zUk/Ptt2MDAEKAfUtKc1+3U1Mo3oY=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.7 h1:tHK47VqqtJxOymRrNtUXN5SP/zUTvZKeLx4tH6PGQc8=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.7/go.mod h1:qOZk8sPDrxhf+4Wf4oT2urYJrYt3RejHSzgAquYeppw=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.17 h1:I0GyV8wiYrP8XpA70g1HBcQO1JlQxCMTW9npl5UbDHY=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.17/go.mod h1:tyw7BOl5bBe/oqvoIeECFJjMdzXoa/dfVz3QQ5lgHGA=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.17 h1:xOLELNKGp2vsiteLsvLPwxC+mYmO6OZ8PYgiuPJzF8U=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.17/go.mod h1:5M5CI3D12dNOtH3/mk6minaRwI2/37ifCURZISxA/IQ=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.17 h1:WWLqlh79iO48yLkj1v3ISRNiv+3KdQoZ6JWyfcsyQik=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.17/go.mod h1:EhG22vHRrvF8oXSTYStZhJc1aUgKtnJe+aOiFEV90cM=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 h1:WKuaxf++XKWlHWu9ECbMlha8WOEGm0OUEZqm4K/Gcfk=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4/go.mod h1:ZWy7j6v1vWGmPReu0iSGvRiise4YI5SkR3OHKTZ6Wuc=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.3 h1:x2Ibm/Af8Fi+BH+Hsn9TXGdT+hKbDd5XOTZxTMxDk7o=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.3/go.mod h1:IW1jwyrQgMdhisceG8fQLmQIydcT/jWY21rFhzgaKwo=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.14 h1:FIouAnCE46kyYqyhs0XEBDFFSREtdnr8HQuLPQPLCrY=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.14/go.mod h1:UTwDc5COa5+guonQU8qBikJo1ZJ4ln2r1MkF7Dqag1E=
|
||||
github.com/aws/aws-sdk-go-v2/service/ses v1.34.7 h1:As+tSgow5R48+hFwkgtEBOFYFtvSEqMal2QSMRccWgw=
|
||||
github.com/aws/aws-sdk-go-v2/service/ses v1.34.7/go.mod h1:DgSdE3UxodNau1977aGbt4YixHYESTNAEStFo9qJ2TQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/signin v1.0.2 h1:MxMBdKTYBjPQChlJhi4qlEueqB1p1KcbTEa7tD5aqPs=
|
||||
github.com/aws/aws-sdk-go-v2/service/signin v1.0.2/go.mod h1:iS6EPmNeqCsGo+xQmXv0jIMjyYtQfnwg36zl2FwEouk=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.5 h1:ksUT5KtgpZd3SAiFJNJ0AFEJVva3gjBmN7eXUZjzUwQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.5/go.mod h1:av+ArJpoYf3pgyrj6tcehSFW+y9/QvAY8kMooR9bZCw=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.10 h1:GtsxyiF3Nd3JahRBJbxLCCdYW9ltGQYrFWg8XdkGDd8=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.10/go.mod h1:/j67Z5XBVDx8nZVp9EuFM9/BS5dvBznbqILGuu73hug=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.41.2 h1:a5UTtD4mHBU3t0o6aHQZFJTNKVfxFWfPX7J0Lr7G+uY=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.41.2/go.mod h1:6TxbXoDSgBQ225Qd8Q+MbxUxUh6TtNKwbRt/EPS9xso=
|
||||
github.com/aws/smithy-go v1.23.2 h1:Crv0eatJUQhaManss33hS5r40CG3ZFH+21XSkqMrIUM=
|
||||
github.com/aws/smithy-go v1.23.2/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4 h1:0ryTNEdJbzUCEWkVXEXoqlXV72J5keC1GvILMOuD00E=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4/go.mod h1:HQ4qwNZh32C3CBeO6iJLQlgtMzqeG17ziAA/3KDJFow=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.17 h1:RuNSMoozM8oXlgLG/n6WLaFGoea7/CddrCfIiSA+xdY=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.17/go.mod h1:F2xxQ9TZz5gDWsclCtPQscGpP0VUOc8RqgFM3vDENmU=
|
||||
github.com/aws/aws-sdk-go-v2/service/ses v1.34.18 h1:2Lnd3ZNTyWpFJJM55y0mP0aESovm+vFuFEwLijucUL8=
|
||||
github.com/aws/aws-sdk-go-v2/service/ses v1.34.18/go.mod h1:BLwHw6wdkA6NfnW/cFaVcvpwdIXHLAkpe6nsLF9BVww=
|
||||
github.com/aws/aws-sdk-go-v2/service/signin v1.0.5 h1:VrhDvQib/i0lxvr3zqlUwLwJP4fpmpyD9wYG1vfSu+Y=
|
||||
github.com/aws/aws-sdk-go-v2/service/signin v1.0.5/go.mod h1:k029+U8SY30/3/ras4G/Fnv/b88N4mAfliNn08Dem4M=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.9 h1:v6EiMvhEYBoHABfbGB4alOYmCIrcgyPPiBE1wZAEbqk=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.9/go.mod h1:yifAsgBxgJWn3ggx70A3urX2AN49Y5sJTD1UQFlfqBw=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.13 h1:gd84Omyu9JLriJVCbGApcLzVR3XtmC4ZDPcAI6Ftvds=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.13/go.mod h1:sTGThjphYE4Ohw8vJiRStAcu3rbjtXRsdNB0TvZ5wwo=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.41.6 h1:5fFjR/ToSOzB2OQ/XqWpZBmNvmP/pJ1jOWYlFDJTjRQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.41.6/go.mod h1:qgFDZQSD/Kys7nJnVqYlWKnh0SSdMjAi0uSwON4wgYQ=
|
||||
github.com/aws/smithy-go v1.24.0 h1:LpilSUItNPFr1eY85RYgTIg5eIEPtvFbskaFcmmIUnk=
|
||||
github.com/aws/smithy-go v1.24.0/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/coreos/go-oidc/v3 v3.16.0 h1:qRQUCFstKpXwmEjDQTIbyY/5jF00+asXzSkmkoa/mow=
|
||||
github.com/coreos/go-oidc/v3 v3.16.0/go.mod h1:wqPbKFrVnE90vty060SB40FCJ8fTHTxSwyXJqZH+sI8=
|
||||
github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs=
|
||||
github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA=
|
||||
github.com/clipperhouse/uax29/v2 v2.4.0 h1:RXqE/l5EiAbA4u97giimKNlmpvkmz+GrBVTelsoXy9g=
|
||||
github.com/clipperhouse/uax29/v2 v2.4.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g=
|
||||
github.com/coreos/go-oidc/v3 v3.17.0 h1:hWBGaQfbi0iVviX4ibC7bk8OKT5qNr4klBaCHVNvehc=
|
||||
github.com/coreos/go-oidc/v3 v3.17.0/go.mod h1:wqPbKFrVnE90vty060SB40FCJ8fTHTxSwyXJqZH+sI8=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davidmz/go-pageant v1.0.2 h1:bPblRCh5jGU+Uptpz6LgMZGD5hJoOt7otgT454WvHn0=
|
||||
@@ -77,8 +81,8 @@ github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/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/gofiber/fiber/v2 v2.52.9 h1:YjKl5DOiyP3j0mO61u3NTmK7or8GzzWzCFzkboyP5cw=
|
||||
github.com/gofiber/fiber/v2 v2.52.9/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw=
|
||||
github.com/gofiber/fiber/v2 v2.52.11 h1:5f4yzKLcBcF8ha1GQTWB+mpblWz3Vz6nSAbTL31HkWs=
|
||||
github.com/gofiber/fiber/v2 v2.52.11/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw=
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
@@ -97,43 +101,44 @@ github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
|
||||
github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU9uHLo7OnF5tL52HFAgMmyrf4=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA=
|
||||
github.com/googleapis/gax-go/v2 v2.15.0 h1:SyjDc1mGgZU5LncH8gimWo9lW1DtIfPibOG81vgd/bo=
|
||||
github.com/googleapis/gax-go/v2 v2.15.0/go.mod h1:zVVkkxAQHa1RQpg9z2AUCMnKhi0Qld9rcmyfL1OZhoc=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.11 h1:vAe81Msw+8tKUxi2Dqh/NZMz7475yUvmRIkXr4oN2ao=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.11/go.mod h1:RFV7MUdlb7AgEq2v7FmMCfeSMCllAzWxFgRdusoGks8=
|
||||
github.com/googleapis/gax-go/v2 v2.16.0 h1:iHbQmKLLZrexmb0OSsNGTeSTS0HO4YvFOG8g5E4Zd0Y=
|
||||
github.com/googleapis/gax-go/v2 v2.16.0/go.mod h1:o1vfQjjNZn4+dPnRdl/4ZD7S9414Y4xA+a/6Icj6l14=
|
||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY=
|
||||
github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/ishidawataru/sctp v0.0.0-20230406120618-7ff4192f6ff2 h1:i2fYnDurfLlJH8AyyMOnkLHnHeP8Ff/DDpuZA/D3bPo=
|
||||
github.com/ishidawataru/sctp v0.0.0-20230406120618-7ff4192f6ff2/go.mod h1:co9pwDoBCm1kGxawmb4sPq0cSIOOWNPT4KnHotMP1Zg=
|
||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||
github.com/hashicorp/go-version v1.8.0 h1:KAkNb1HAiZd1ukkxDFGmokVZe1Xy9HG6NUp+bPle2i4=
|
||||
github.com/hashicorp/go-version v1.8.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
||||
github.com/ishidawataru/sctp v0.0.0-20251114114122-19ddcbc6aae2 h1:36qep4gxKs+JgeHGWeQ040RyZdt9kQlLglL1rFVn/oQ=
|
||||
github.com/ishidawataru/sctp v0.0.0-20251114114122-19ddcbc6aae2/go.mod h1:co9pwDoBCm1kGxawmb4sPq0cSIOOWNPT4KnHotMP1Zg=
|
||||
github.com/klauspost/compress v1.18.3 h1:9PJRvfbmTabkOX8moIpXPbMMbYN60bWImDDU7L+/6zw=
|
||||
github.com/klauspost/compress v1.18.3/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/lib/pq v1.11.1 h1:wuChtj2hfsGmmx3nf1m7xC2XpK6OtelS2shMY+bGMtI=
|
||||
github.com/lib/pq v1.11.1/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA=
|
||||
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
|
||||
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/miekg/dns v1.1.68 h1:jsSRkNozw7G/mnmXULynzMNIsgY2dHC8LO6U6Ij2JEA=
|
||||
github.com/miekg/dns v1.1.68/go.mod h1:fujopn7TB3Pu3JM69XaawiU0wqjpL9/8xGop5UrTPps=
|
||||
github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw=
|
||||
github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
|
||||
github.com/miekg/dns v1.1.72 h1:vhmr+TF2A3tuoGNkLDFK9zi36F2LS+hKTRW0Uf8kbzI=
|
||||
github.com/miekg/dns v1.1.72/go.mod h1:+EuEPhdHOsfk6Wk5TT2CzssZdqkmFhf8r+aVyDEToIs=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
|
||||
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||
github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w=
|
||||
github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus-community/pro-bing v0.7.0 h1:KFYFbxC2f2Fp6c+TyxbCOEarf7rbnzr9Gw8eIb0RfZA=
|
||||
github.com/prometheus-community/pro-bing v0.7.0/go.mod h1:Moob9dvlY50Bfq6i88xIwfyw7xLFHH69LUgx9n5zqCE=
|
||||
github.com/prometheus-community/pro-bing v0.8.0 h1:CEY/g1/AgERRDjxw5P32ikcOgmrSuXs7xon7ovx6mNc=
|
||||
github.com/prometheus-community/pro-bing v0.8.0/go.mod h1:Idyxz8raDO6TgkUN6ByiEGvWJNyQd40kN9ZUeho3lN0=
|
||||
github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
|
||||
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
|
||||
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
|
||||
@@ -146,17 +151,14 @@ github.com/registrobr/rdap v1.1.8 h1:7egYAM8MsuencdP9mvF/892f8OjXvUFSyp5cT1Lg45U
|
||||
github.com/registrobr/rdap v1.1.8/go.mod h1:VY2DVrpsJpUfy9gj2QvurGymCgZV11/11cxQz5CxO+w=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
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.67.0 h1:tqKlJMUP6iuNG8hGjK/s9J4kadH7HLV4ijEcPGsezac=
|
||||
github.com/valyala/fasthttp v1.67.0/go.mod h1:qYSIpqt/0XNmShgo/8Aq8E3UYWVVwNS2QYmzd8WIEPM=
|
||||
github.com/valyala/fasthttp v1.69.0 h1:fNLLESD2SooWeh2cidsuFtOcrEi4uB4m1mPrkJMZyVI=
|
||||
github.com/valyala/fasthttp v1.69.0/go.mod h1:4wA4PfAraPlAsJ5jMSqCE2ug5tqUPwKXxVj8oNECGcw=
|
||||
github.com/wcharczuk/go-chart/v2 v2.1.2 h1:Y17/oYNuXwZg6TFag06qe8sBajwwsuvPiJJXcUcLL6E=
|
||||
github.com/wcharczuk/go-chart/v2 v2.1.2/go.mod h1:Zi4hbaqlWpYajnXB2K22IUYVXRXaLfSGNNR7P4ukyyQ=
|
||||
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
|
||||
@@ -168,16 +170,16 @@ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.6
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0/go.mod h1:snMWehoOh2wsEwnvvwtDyFCxVeDAODenXHtn5vzrKjo=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q=
|
||||
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
|
||||
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
|
||||
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
|
||||
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=
|
||||
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E=
|
||||
go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg=
|
||||
go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48=
|
||||
go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8=
|
||||
go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0=
|
||||
go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs=
|
||||
go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18=
|
||||
go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA=
|
||||
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
|
||||
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
|
||||
go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI=
|
||||
go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
|
||||
@@ -189,19 +191,20 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
|
||||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
|
||||
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
|
||||
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o=
|
||||
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8=
|
||||
golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ=
|
||||
golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=
|
||||
golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=
|
||||
golang.org/x/exp v0.0.0-20260112195511-716be5621a96 h1:Z/6YuSHTLOHfNFdb8zVZomZr7cqNgTJvA8+Qz75D8gU=
|
||||
golang.org/x/exp v0.0.0-20260112195511-716be5621a96/go.mod h1:nzimsREAkjBCIEFtHiYkrJyT+2uy9YZJB7H1k68CXZU=
|
||||
golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E=
|
||||
golang.org/x/image v0.35.0 h1:LKjiHdgMtO8z7Fh18nGY6KDcoEtVfsgLDPeLyguqb7I=
|
||||
golang.org/x/image v0.35.0/go.mod h1:MwPLTVgvxSASsxdLzKrl8BRFuyqMyGhLwmC+TO1Sybk=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA=
|
||||
golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w=
|
||||
golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c=
|
||||
golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
@@ -211,33 +214,32 @@ golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
||||
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
||||
golang.org/x/oauth2 v0.32.0 h1:jsCblLleRMDrxMN29H3z/k1KliIvpLgCkE6R8FXXNgY=
|
||||
golang.org/x/oauth2 v0.32.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
||||
golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=
|
||||
golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=
|
||||
golang.org/x/oauth2 v0.35.0 h1:Mv2mzuHuZuY2+bkyWXIHMfhNdJAdwW3FuWeCPYN5GVQ=
|
||||
golang.org/x/oauth2 v0.35.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
|
||||
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
||||
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
|
||||
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
@@ -246,8 +248,8 @@ golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
||||
golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
|
||||
golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=
|
||||
golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY=
|
||||
golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
@@ -257,30 +259,30 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
|
||||
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
|
||||
golang.org/x/time v0.13.0 h1:eUlYslOIt32DgYD6utsuUeHs4d7AsEYLuIAdg7FlYgI=
|
||||
golang.org/x/time v0.13.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
|
||||
golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=
|
||||
golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
|
||||
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
|
||||
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=
|
||||
golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=
|
||||
golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc=
|
||||
golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||
google.golang.org/api v0.252.0 h1:xfKJeAJaMwb8OC9fesr369rjciQ704AjU/psjkKURSI=
|
||||
google.golang.org/api v0.252.0/go.mod h1:dnHOv81x5RAmumZ7BWLShB/u7JZNeyalImxHmtTHxqw=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8 h1:M1rk8KBnUsBDg1oPGHNCxG4vc1f49epmTO7xscSajMk=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
|
||||
google.golang.org/grpc v1.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM=
|
||||
google.golang.org/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig=
|
||||
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
|
||||
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
google.golang.org/api v0.265.0 h1:FZvfUdI8nfmuNrE34aOWFPmLC+qRBEiNm3JdivTvAAU=
|
||||
google.golang.org/api v0.265.0/go.mod h1:uAvfEl3SLUj/7n6k+lJutcswVojHPp2Sp08jWCu8hLY=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 h1:H86B94AW+VfJWDqFeEbBPhEtHzJwJfTbgE2lZa54ZAQ=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
|
||||
google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc=
|
||||
google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
@@ -290,18 +292,20 @@ gopkg.in/mail.v2 v2.3.1 h1:WYFn/oANrAGP2C0dcV6/pbkPzv8yGzqTjPmTeO7qoXk=
|
||||
gopkg.in/mail.v2 v2.3.1/go.mod h1:htwXN1Qh09vZJ1NVKxQqHPBaCBbzKhp5GzuJEA4VJWw=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
modernc.org/cc/v4 v4.26.5 h1:xM3bX7Mve6G8K8b+T11ReenJOT+BmVqQj0FY5T4+5Y4=
|
||||
modernc.org/cc/v4 v4.26.5/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
|
||||
modernc.org/ccgo/v4 v4.28.1 h1:wPKYn5EC/mYTqBO373jKjvX2n+3+aK7+sICCv4Fjy1A=
|
||||
modernc.org/ccgo/v4 v4.28.1/go.mod h1:uD+4RnfrVgE6ec9NGguUNdhqzNIeeomeXf6CL0GTE5Q=
|
||||
modernc.org/cc/v4 v4.27.1 h1:9W30zRlYrefrDV2JE2O8VDtJ1yPGownxciz5rrbQZis=
|
||||
modernc.org/cc/v4 v4.27.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
|
||||
modernc.org/ccgo/v4 v4.30.1 h1:4r4U1J6Fhj98NKfSjnPUN7Ze2c6MnAdL0hWw6+LrJpc=
|
||||
modernc.org/ccgo/v4 v4.30.1/go.mod h1:bIOeI1JL54Utlxn+LwrFyjCx2n2RDiYEaJVSrgdrRfM=
|
||||
modernc.org/fileutil v1.3.40 h1:ZGMswMNc9JOCrcrakF1HrvmergNLAmxOPjizirpfqBA=
|
||||
modernc.org/fileutil v1.3.40/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc=
|
||||
modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
|
||||
modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
|
||||
modernc.org/gc/v3 v3.1.1 h1:k8T3gkXWY9sEiytKhcgyiZ2L0DTyCQ/nvX+LoCljoRE=
|
||||
modernc.org/gc/v3 v3.1.1/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY=
|
||||
modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks=
|
||||
modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI=
|
||||
modernc.org/libc v1.66.10 h1:yZkb3YeLx4oynyR+iUsXsybsX4Ubx7MQlSYEw4yj59A=
|
||||
modernc.org/libc v1.66.10/go.mod h1:8vGSEwvoUoltr4dlywvHqjtAqHBaw0j1jI7iFBTAr2I=
|
||||
modernc.org/libc v1.67.7 h1:H+gYQw2PyidyxwxQsGTwQw6+6H+xUk+plvOKW7+d3TI=
|
||||
modernc.org/libc v1.67.7/go.mod h1:UjCSJFl2sYbJbReVQeVpq/MgzlbmDM4cRHIYFelnaDk=
|
||||
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
|
||||
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
|
||||
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
|
||||
@@ -310,8 +314,8 @@ modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
|
||||
modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
|
||||
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
|
||||
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
|
||||
modernc.org/sqlite v1.39.1 h1:H+/wGFzuSCIEVCvXYVHX5RQglwhMOvtHSv+VtidL2r4=
|
||||
modernc.org/sqlite v1.39.1/go.mod h1:9fjQZ0mB1LLP0GYrp39oOJXx/I2sxEnZtzCmEQIKvGE=
|
||||
modernc.org/sqlite v1.44.3 h1:+39JvV/HWMcYslAwRxHb8067w+2zowvFOUrOWIy9PjY=
|
||||
modernc.org/sqlite v1.44.3/go.mod h1:CzbrU2lSB1DKUusvwGz7rqEKIq+NUd8GWuBBZDs9/nA=
|
||||
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
|
||||
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
|
||||
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package memory
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"sort"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -237,13 +238,7 @@ func (s *Store) InsertSuiteResult(su *suite.Suite, result *suite.Result) error {
|
||||
func (s *Store) DeleteAllEndpointStatusesNotInKeys(keys []string) int {
|
||||
var keysToDelete []string
|
||||
for _, existingKey := range s.endpointCache.GetKeysByPattern("*", 0) {
|
||||
shouldDelete := true
|
||||
for _, k := range keys {
|
||||
if existingKey == k {
|
||||
shouldDelete = false
|
||||
break
|
||||
}
|
||||
}
|
||||
shouldDelete := !slices.Contains(keys, existingKey)
|
||||
if shouldDelete {
|
||||
keysToDelete = append(keysToDelete, existingKey)
|
||||
}
|
||||
|
||||
@@ -797,7 +797,7 @@ func TestSuiteResultOrdering(t *testing.T) {
|
||||
baseTime := time.Now().Add(-5 * time.Hour)
|
||||
timestamps := make([]time.Time, 5)
|
||||
|
||||
for i := 0; i < 5; i++ {
|
||||
for i := range 5 {
|
||||
timestamp := baseTime.Add(time.Duration(i) * time.Hour)
|
||||
timestamps[i] = timestamp
|
||||
result := &suite.Result{
|
||||
@@ -878,7 +878,7 @@ func TestSuiteResultOrdering(t *testing.T) {
|
||||
smallSuite := &suite.Suite{Name: "small-suite", Group: "test"}
|
||||
|
||||
// Insert 6 results, should keep only the newest 3
|
||||
for i := 0; i < 6; i++ {
|
||||
for i := range 6 {
|
||||
result := &suite.Result{
|
||||
Name: smallSuite.Name,
|
||||
Group: smallSuite.Group,
|
||||
@@ -925,7 +925,7 @@ func TestStore_ConcurrentAccess(t *testing.T) {
|
||||
|
||||
// Create endpoints for concurrent testing
|
||||
endpoints := make([]*endpoint.Endpoint, numGoroutines)
|
||||
for i := 0; i < numGoroutines; i++ {
|
||||
for i := range numGoroutines {
|
||||
endpoints[i] = &endpoint.Endpoint{
|
||||
Name: "endpoint-" + string(rune('A'+i)),
|
||||
Group: "concurrent",
|
||||
@@ -934,12 +934,12 @@ func TestStore_ConcurrentAccess(t *testing.T) {
|
||||
}
|
||||
|
||||
// Concurrently insert results for different endpoints
|
||||
for i := 0; i < numGoroutines; i++ {
|
||||
for i := range numGoroutines {
|
||||
wg.Add(1)
|
||||
go func(endpointIndex int) {
|
||||
defer wg.Done()
|
||||
ep := endpoints[endpointIndex]
|
||||
for j := 0; j < resultsPerGoroutine; j++ {
|
||||
for j := range resultsPerGoroutine {
|
||||
result := &endpoint.Result{
|
||||
Success: j%2 == 0,
|
||||
Timestamp: time.Now().Add(time.Duration(j) * time.Minute),
|
||||
@@ -978,7 +978,7 @@ func TestStore_ConcurrentAccess(t *testing.T) {
|
||||
|
||||
// Create suites for concurrent testing
|
||||
suites := make([]*suite.Suite, numGoroutines)
|
||||
for i := 0; i < numGoroutines; i++ {
|
||||
for i := range numGoroutines {
|
||||
suites[i] = &suite.Suite{
|
||||
Name: "suite-" + string(rune('A'+i)),
|
||||
Group: "concurrent",
|
||||
@@ -986,12 +986,12 @@ func TestStore_ConcurrentAccess(t *testing.T) {
|
||||
}
|
||||
|
||||
// Concurrently insert results for different suites
|
||||
for i := 0; i < numGoroutines; i++ {
|
||||
for i := range numGoroutines {
|
||||
wg.Add(1)
|
||||
go func(suiteIndex int) {
|
||||
defer wg.Done()
|
||||
su := suites[suiteIndex]
|
||||
for j := 0; j < resultsPerGoroutine; j++ {
|
||||
for j := range resultsPerGoroutine {
|
||||
result := &suite.Result{
|
||||
Name: su.Name,
|
||||
Group: su.Group,
|
||||
@@ -1036,7 +1036,7 @@ func TestStore_ConcurrentAccess(t *testing.T) {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for i := 0; i < 5; i++ {
|
||||
for i := range 5 {
|
||||
result := &endpoint.Result{
|
||||
Success: true,
|
||||
Timestamp: time.Now(),
|
||||
@@ -1050,7 +1050,7 @@ func TestStore_ConcurrentAccess(t *testing.T) {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for i := 0; i < 5; i++ {
|
||||
for i := range 5 {
|
||||
result := &suite.Result{
|
||||
Name: testSuite.Name,
|
||||
Group: testSuite.Group,
|
||||
@@ -1066,7 +1066,7 @@ func TestStore_ConcurrentAccess(t *testing.T) {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for i := 0; i < 10; i++ {
|
||||
for range 10 {
|
||||
store.GetAllEndpointStatuses(&paging.EndpointStatusParams{})
|
||||
store.GetAllSuiteStatuses(&paging.SuiteStatusParams{})
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
|
||||
@@ -11,10 +11,10 @@ import (
|
||||
func BenchmarkShallowCopyEndpointStatus(b *testing.B) {
|
||||
ep := &testEndpoint
|
||||
status := endpoint.NewStatus(ep.Group, ep.Name)
|
||||
for i := 0; i < storage.DefaultMaximumNumberOfResults; i++ {
|
||||
for range storage.DefaultMaximumNumberOfResults {
|
||||
AddResult(status, &testSuccessfulResult, storage.DefaultMaximumNumberOfResults, storage.DefaultMaximumNumberOfEvents)
|
||||
}
|
||||
for n := 0; n < b.N; n++ {
|
||||
for b.Loop() {
|
||||
ShallowCopyEndpointStatus(status, paging.NewEndpointStatusParams().WithResults(1, 20))
|
||||
}
|
||||
b.ReportAllocs()
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
func TestAddResult(t *testing.T) {
|
||||
ep := &endpoint.Endpoint{Name: "name", Group: "group"}
|
||||
endpointStatus := endpoint.NewStatus(ep.Group, ep.Name)
|
||||
for i := 0; i < (storage.DefaultMaximumNumberOfResults+storage.DefaultMaximumNumberOfEvents)*2; i++ {
|
||||
for i := range (storage.DefaultMaximumNumberOfResults + storage.DefaultMaximumNumberOfEvents) * 2 {
|
||||
AddResult(endpointStatus, &endpoint.Result{Success: i%2 == 0, Timestamp: time.Now()}, storage.DefaultMaximumNumberOfResults, storage.DefaultMaximumNumberOfEvents)
|
||||
}
|
||||
if len(endpointStatus.Results) != storage.DefaultMaximumNumberOfResults {
|
||||
@@ -30,7 +30,7 @@ func TestShallowCopyEndpointStatus(t *testing.T) {
|
||||
ep := &endpoint.Endpoint{Name: "name", Group: "group"}
|
||||
endpointStatus := endpoint.NewStatus(ep.Group, ep.Name)
|
||||
ts := time.Now().Add(-25 * time.Hour)
|
||||
for i := 0; i < 25; i++ {
|
||||
for i := range 25 {
|
||||
AddResult(endpointStatus, &endpoint.Result{Success: i%2 == 0, Timestamp: ts}, storage.DefaultMaximumNumberOfResults, storage.DefaultMaximumNumberOfEvents)
|
||||
ts = ts.Add(time.Hour)
|
||||
}
|
||||
@@ -74,9 +74,9 @@ func TestShallowCopySuiteStatus(t *testing.T) {
|
||||
Key: testSuite.Key(),
|
||||
Results: []*suite.Result{},
|
||||
}
|
||||
|
||||
|
||||
ts := time.Now().Add(-25 * time.Hour)
|
||||
for i := 0; i < 25; i++ {
|
||||
for i := range 25 {
|
||||
result := &suite.Result{
|
||||
Name: testSuite.Name,
|
||||
Group: testSuite.Group,
|
||||
@@ -169,4 +169,3 @@ func TestShallowCopySuiteStatus(t *testing.T) {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -900,7 +900,7 @@ func TestEventOrderingFix(t *testing.T) {
|
||||
}
|
||||
// Create many events over time
|
||||
baseTime := time.Now().Add(-100 * time.Hour) // Start 100 hours ago
|
||||
for i := 0; i < 50; i++ {
|
||||
for i := range 50 {
|
||||
result := &endpoint.Result{
|
||||
Success: i%2 == 0, // Alternate between true/false to create events
|
||||
Timestamp: baseTime.Add(time.Duration(i) * time.Hour),
|
||||
|
||||
@@ -53,11 +53,11 @@ func BenchmarkStore_GetAllEndpointStatuses(b *testing.B) {
|
||||
numberOfEndpoints := []int{10, 25, 50, 100}
|
||||
for _, numberOfEndpointsToCreate := range numberOfEndpoints {
|
||||
// Create endpoints and insert results
|
||||
for i := 0; i < numberOfEndpointsToCreate; i++ {
|
||||
for i := range numberOfEndpointsToCreate {
|
||||
ep := testEndpoint
|
||||
ep.Name = "endpoint" + strconv.Itoa(i)
|
||||
// InsertEndpointResult 20 results for each endpoint
|
||||
for j := 0; j < 20; j++ {
|
||||
for range 20 {
|
||||
scenario.Store.InsertEndpointResult(&ep, &testSuccessfulResult)
|
||||
}
|
||||
}
|
||||
@@ -191,7 +191,7 @@ func BenchmarkStore_GetEndpointStatusByKey(b *testing.B) {
|
||||
},
|
||||
}
|
||||
for _, scenario := range scenarios {
|
||||
for i := 0; i < 50; i++ {
|
||||
for range 50 {
|
||||
scenario.Store.InsertEndpointResult(&testEndpoint, &testSuccessfulResult)
|
||||
scenario.Store.InsertEndpointResult(&testEndpoint, &testUnsuccessfulResult)
|
||||
}
|
||||
|
||||
@@ -19,10 +19,10 @@
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="{{ .UI.Favicon.Size32x32 }}" />
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="{{ .UI.Favicon.Size16x16 }}" />
|
||||
<link rel="manifest" href="/manifest.json" crossorigin="use-credentials" />
|
||||
<link rel="shortcut icon" href="/favicon.ico" />
|
||||
<link rel="shortcut icon" href="{{ .UI.Favicon.Default }}" />
|
||||
<link rel="stylesheet" href="/css/custom.css" />
|
||||
<meta name="description" content="{{ .UI.Description }}" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
@click="showAverageResponseTime = !showAverageResponseTime"
|
||||
@click="toggleShowAverageResponseTime"
|
||||
:title="showAverageResponseTime ? 'Show min-max response time' : 'Show average response time'"
|
||||
>
|
||||
<Activity v-if="showAverageResponseTime" class="h-5 w-5" />
|
||||
@@ -224,7 +224,7 @@ const events = ref([])
|
||||
const currentPage = ref(1)
|
||||
const resultPageSize = 50
|
||||
const showResponseTimeChartAndBadges = ref(false)
|
||||
const showAverageResponseTime = ref(false)
|
||||
const showAverageResponseTime = ref(localStorage.getItem('gatus:show-average-response-time') !== 'false')
|
||||
const selectedChartDuration = ref('24h')
|
||||
const isRefreshing = ref(false)
|
||||
|
||||
@@ -245,6 +245,11 @@ const hostname = computed(() => {
|
||||
return latestResult.value?.hostname || null
|
||||
})
|
||||
|
||||
const toggleShowAverageResponseTime = () => {
|
||||
showAverageResponseTime.value = !showAverageResponseTime.value
|
||||
localStorage.setItem('gatus:show-average-response-time', showAverageResponseTime.value ? 'true' : 'false')
|
||||
}
|
||||
|
||||
const pageAverageResponseTime = computed(() => {
|
||||
// Use endpointStatus for current page's average response time
|
||||
if (!endpointStatus.value || !endpointStatus.value.results || endpointStatus.value.results.length === 0) {
|
||||
|
||||
@@ -219,7 +219,7 @@ const itemsPerPage = 96
|
||||
const searchQuery = ref('')
|
||||
const showOnlyFailing = ref(false)
|
||||
const showRecentFailures = ref(false)
|
||||
const showAverageResponseTime = ref(true)
|
||||
const showAverageResponseTime = ref(localStorage.getItem('gatus:show-average-response-time') !== 'false')
|
||||
const groupByGroup = ref(false)
|
||||
const sortBy = ref(localStorage.getItem('gatus:sort-by') || 'name')
|
||||
const uncollapsedGroups = ref(new Set())
|
||||
@@ -483,6 +483,7 @@ const goToPage = (page) => {
|
||||
|
||||
const toggleShowAverageResponseTime = () => {
|
||||
showAverageResponseTime.value = !showAverageResponseTime.value
|
||||
localStorage.setItem('gatus:show-average-response-time', showAverageResponseTime.value ? 'true' : 'false')
|
||||
}
|
||||
|
||||
const showTooltip = (result, event, action = 'hover') => {
|
||||
|
||||
@@ -8,4 +8,4 @@
|
||||
} else {
|
||||
document.documentElement.classList.remove('dark');
|
||||
}
|
||||
})();</script><title>{{ .UI.Title }}</title><meta http-equiv="X-UA-Compatible" content="IE=edge"/><meta name="viewport" content="width=device-width,initial-scale=1"/><link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png"/><link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png"/><link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png"/><link rel="manifest" href="/manifest.json" crossorigin="use-credentials"/><link rel="shortcut icon" href="/favicon.ico"/><link rel="stylesheet" href="/css/custom.css"/><meta name="description" content="{{ .UI.Description }}"/><meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"/><meta name="apple-mobile-web-app-title" content="{{ .UI.Title }}"/><meta name="application-name" content="{{ .UI.Title }}"/><meta name="theme-color" content="#f7f9fb"/><script defer="defer" src="/js/chunk-vendors.js"></script><script defer="defer" src="/js/app.js"></script><link href="/css/app.css" rel="stylesheet"></head><body><noscript><strong>Enable JavaScript to view this page.</strong></noscript><div id="app"></div></body></html>
|
||||
})();</script><title>{{ .UI.Title }}</title><meta http-equiv="X-UA-Compatible" content="IE=edge"/><meta name="viewport" content="width=device-width,initial-scale=1"/><link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png"/><link rel="icon" type="image/png" sizes="32x32" href="{{ .UI.Favicon.Size32x32 }}"/><link rel="icon" type="image/png" sizes="16x16" href="{{ .UI.Favicon.Size16x16 }}"/><link rel="manifest" href="/manifest.json" crossorigin="use-credentials"/><link rel="shortcut icon" href="{{ .UI.Favicon.Default }}"/><link rel="stylesheet" href="/css/custom.css"/><meta name="description" content="{{ .UI.Description }}"/><meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"/><meta name="apple-mobile-web-app-title" content="{{ .UI.Title }}"/><meta name="application-name" content="{{ .UI.Title }}"/><meta name="theme-color" content="#f7f9fb"/><script defer="defer" src="/js/chunk-vendors.js"></script><script defer="defer" src="/js/app.js"></script><link href="/css/app.css" rel="stylesheet"></head><body><noscript><strong>Enable JavaScript to view this page.</strong></noscript><div id="app"></div></body></html>
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user