diff --git a/.drone-manifest.yml b/.drone-manifest.yml
new file mode 100644
index 0000000..5ca6313
--- /dev/null
+++ b/.drone-manifest.yml
@@ -0,0 +1,22 @@
+image: happydomain/checker-srv:{{#if build.tag}}{{trimPrefix "v" build.tag}}{{else}}latest{{/if}}
+{{#if build.tags}}
+tags:
+{{#each build.tags}}
+ - {{this}}
+{{/each}}
+{{/if}}
+manifests:
+ - image: happydomain/checker-srv:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-amd64
+ platform:
+ architecture: amd64
+ os: linux
+ - image: happydomain/checker-srv:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm64
+ platform:
+ architecture: arm64
+ os: linux
+ variant: v8
+ - image: happydomain/checker-srv:{{#if build.tag}}{{trimPrefix "v" build.tag}}-{{/if}}linux-arm
+ platform:
+ architecture: arm
+ os: linux
+ variant: v7
diff --git a/.drone.yml b/.drone.yml
new file mode 100644
index 0000000..9689361
--- /dev/null
+++ b/.drone.yml
@@ -0,0 +1,187 @@
+---
+kind: pipeline
+type: docker
+name: build-amd64
+
+platform:
+ os: linux
+ arch: amd64
+
+steps:
+ - name: checker build
+ image: golang:1-alpine
+ commands:
+ - apk add --no-cache git make
+ - make
+ environment:
+ CHECKER_VERSION: "${DRONE_BRANCH}-${DRONE_COMMIT}"
+ CGO_ENABLED: 0
+ when:
+ event:
+ exclude:
+ - tag
+
+ - name: checker build tag
+ image: golang:1-alpine
+ commands:
+ - apk add --no-cache git make
+ - make
+ environment:
+ CHECKER_VERSION: "${DRONE_SEMVER}"
+ CGO_ENABLED: 0
+ when:
+ event:
+ - tag
+
+ - name: publish on Docker Hub
+ image: plugins/docker
+ settings:
+ repo: happydomain/checker-srv
+ auto_tag: true
+ auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH}
+ dockerfile: Dockerfile
+ build_args:
+ - CHECKER_VERSION=${DRONE_BRANCH}-${DRONE_COMMIT}
+ username:
+ from_secret: docker_username
+ password:
+ from_secret: docker_password
+ when:
+ event:
+ exclude:
+ - tag
+
+ - name: publish on Docker Hub (tag)
+ image: plugins/docker
+ settings:
+ repo: happydomain/checker-srv
+ auto_tag: true
+ auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH}
+ dockerfile: Dockerfile
+ build_args:
+ - CHECKER_VERSION=${DRONE_SEMVER}
+ username:
+ from_secret: docker_username
+ password:
+ from_secret: docker_password
+ when:
+ event:
+ - tag
+
+trigger:
+ branch:
+ exclude:
+ - renovate/*
+ event:
+ - cron
+ - push
+ - tag
+
+---
+kind: pipeline
+type: docker
+name: build-arm64
+
+platform:
+ os: linux
+ arch: arm64
+
+steps:
+ - name: checker build
+ image: golang:1-alpine
+ commands:
+ - apk add --no-cache git make
+ - make
+ environment:
+ CHECKER_VERSION: "${DRONE_BRANCH}-${DRONE_COMMIT}"
+ CGO_ENABLED: 0
+ when:
+ event:
+ exclude:
+ - tag
+
+ - name: checker build tag
+ image: golang:1-alpine
+ commands:
+ - apk add --no-cache git make
+ - make
+ environment:
+ CHECKER_VERSION: "${DRONE_SEMVER}"
+ CGO_ENABLED: 0
+ when:
+ event:
+ - tag
+
+ - name: publish on Docker Hub
+ image: plugins/docker
+ settings:
+ repo: happydomain/checker-srv
+ auto_tag: true
+ auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH}
+ dockerfile: Dockerfile
+ build_args:
+ - CHECKER_VERSION=${DRONE_BRANCH}-${DRONE_COMMIT}
+ username:
+ from_secret: docker_username
+ password:
+ from_secret: docker_password
+ when:
+ event:
+ exclude:
+ - tag
+
+ - name: publish on Docker Hub (tag)
+ image: plugins/docker
+ settings:
+ repo: happydomain/checker-srv
+ auto_tag: true
+ auto_tag_suffix: ${DRONE_STAGE_OS}-${DRONE_STAGE_ARCH}
+ dockerfile: Dockerfile
+ build_args:
+ - CHECKER_VERSION=${DRONE_SEMVER}
+ username:
+ from_secret: docker_username
+ password:
+ from_secret: docker_password
+ when:
+ event:
+ - tag
+
+trigger:
+ event:
+ - cron
+ - push
+ - tag
+
+---
+kind: pipeline
+name: docker-manifest
+
+platform:
+ os: linux
+ arch: arm64
+
+steps:
+ - name: publish on Docker Hub
+ image: plugins/manifest
+ settings:
+ auto_tag: true
+ ignore_missing: true
+ spec: .drone-manifest.yml
+ username:
+ from_secret: docker_username
+ password:
+ from_secret: docker_password
+
+trigger:
+ branch:
+ exclude:
+ - renovate/*
+ event:
+ - cron
+ - push
+ - tag
+
+depends_on:
+ - build-amd64
+ - build-arm64
diff --git a/README.md b/README.md
index 38e3ede..c5b791c 100644
--- a/README.md
+++ b/README.md
@@ -64,53 +64,19 @@ make docker CHECKER_VERSION=1.2.3
Set the `endpoint` admin option for the SRV checker to the URL of the running checker-srv server (e.g., `http://checker-srv:8080`). happyDomain will delegate observation collection to this endpoint.
-## Protocol
+## Rules
-### POST /collect
-
-Request:
-```json
-{
- "key": "srv_records",
- "target": {"userId": "...", "domainId": "...", "serviceId": "..."},
- "options": {
- "service": {"_svctype": "svcs.UnknownSRV", "Service": {"srv": [ /* dns.SRV records */ ]}},
- "subdomain": "_sip._tcp",
- "domain": "example.com",
- "tcpTimeout": 3000,
- "udpTimeout": 2000
- }
-}
-```
-
-Response:
-```json
-{
- "data": {
- "serviceDomain": "_sip._tcp.example.com",
- "records": [
- {
- "service": "sip",
- "proto": "tcp",
- "owner": "_sip._tcp.example.com",
- "target": "sip1.example.com",
- "port": 5061,
- "priority": 10,
- "weight": 20,
- "addresses": ["203.0.113.5"],
- "probes": [
- {
- "address": "203.0.113.5:5061",
- "proto": "tcp",
- "connected": true,
- "latencyMs": 12.4
- }
- ]
- }
- ]
- }
-}
-```
+| Code | Description | Severity |
+|-------------------------------|---------------------------------------------------------------------------------------------------|---------------------|
+| `srv_records_present` | At least one SRV record is published for this service. | CRITICAL |
+| `srv_null_target` | Detects SRV records with target `.`, signaling the service is intentionally not available. | WARNING |
+| `srv_target_not_cname` | RFC 2782: SRV targets must resolve directly to A/AAAA, not through a CNAME. | WARNING |
+| `srv_targets_resolve` | Every SRV target resolves to at least one A/AAAA address. | CRITICAL |
+| `srv_port_valid` | SRV records advertise a non-zero port that clients can actually connect to. | CRITICAL |
+| `srv_priority_weight_sanity` | Priority/weight values follow RFC 2782 conventions (failover present, weights meaningful). | WARNING |
+| `srv_tcp_reachable` | Every TCP SRV `target:port` accepts a TCP connection. | CRITICAL |
+| `srv_udp_reachable` | UDP SRV targets do not return ICMP port-unreachable. | WARNING |
+| `srv_redundancy` | At least two distinct SRV targets exist (avoids single point of failure). | INFO |
## Discovered TLS endpoints
@@ -132,18 +98,6 @@ so that the TLS checker can probe the targets without re-parsing SRV data:
Null targets (`.`) and unknown service names are skipped. SNI is set to
the SRV target hostname.
-## Rules
-
-| Name | Description |
-|---|---|
-| `srv_records_present` | At least one SRV record is published. |
-| `srv_null_target` | Detects `"."` targets (RFC 2782, service intentionally unavailable). |
-| `srv_target_not_cname` | Warns when an SRV target is a CNAME (RFC 2782 violation). |
-| `srv_targets_resolve` | Every target resolves to at least one A/AAAA. |
-| `srv_tcp_reachable` | Every `_tcp` SRV `target:port` accepts a TCP connection. |
-| `srv_udp_reachable` | UDP targets do not return ICMP port-unreachable. |
-| `srv_redundancy` | At least two distinct targets exist (no single point of failure). |
-
## License & licensing roadmap
This project is currently licensed under the **GNU Affero General Public
diff --git a/checker/collect.go b/checker/collect.go
index 4a410fb..83f37df 100644
--- a/checker/collect.go
+++ b/checker/collect.go
@@ -86,7 +86,10 @@ func (p *srvProvider) Collect(ctx context.Context, opts sdk.CheckerOptions) (any
}
for _, r := range payload.Records {
- owner := strings.TrimSuffix(r.Hdr.Name, ".")
+ // Hdr.Name is relative to the service location (serviceDomain =
+ // subdomain.domain), so we join it with serviceDomain before
+ // treating as FQDN.
+ owner := sdk.JoinRelative(r.Hdr.Name, serviceDomain)
svc, proto := parseOwner(owner, serviceDomain)
rec := SRVRecord{
diff --git a/checker/report.go b/checker/report.go
index 8cdb3c2..5d44efb 100644
--- a/checker/report.go
+++ b/checker/report.go
@@ -256,52 +256,79 @@ func (p *srvProvider) GetHTMLReport(ctx sdk.ReportContext) (string, error) {
rd.Records = append(rd.Records, rec)
}
- if len(resolveFails) > 0 {
- rd.Alerts = append(rd.Alerts, reportAlert{
- Severity: "crit",
- Title: fmt.Sprintf("DNS resolution failed for %d SRV target(s)", len(resolveFails)),
- Body: template.HTML(fmt.Sprintf(
- "%s
Clients will not be able to reach the service. Fix: either publish A/AAAA records for the target(s), or remove the broken SRV record.",
- strings.Join(resolveFails, "
"))),
- })
- }
- if len(cnames) > 0 {
- rd.Alerts = append(rd.Alerts, reportAlert{
- Severity: "warn",
- Title: "SRV target is a CNAME (RFC 2782 violation)",
- Body: template.HTML(fmt.Sprintf(
- "Target(s): %s
RFC 2782 requires SRV targets to resolve directly to A/AAAA. "+
- "Some clients will refuse to follow the CNAME. Fix: point the SRV record to a hostname with A/AAAA records, "+
- "or replace the CNAME with an ALIAS/ANAME at the DNS provider.",
- ""+strings.Join(cnames, ", ")+"")),
- })
- }
- if len(tcpDown) > 0 {
- var items []string
- for _, f := range tcpDown {
- items = append(items, fmt.Sprintf("%s (%s): %s",
- template.HTMLEscapeString(f.address),
- template.HTMLEscapeString(f.owner),
- template.HTMLEscapeString(f.err)))
+ // Build alerts from rule states when available; fall back to raw-data
+ // analysis when the host hasn't threaded rule output through yet.
+ states := ctx.States()
+ if len(states) > 0 {
+ for _, st := range states {
+ sev := ""
+ switch st.Status {
+ case sdk.StatusCrit, sdk.StatusError:
+ sev = "crit"
+ case sdk.StatusWarn:
+ sev = "warn"
+ case sdk.StatusInfo:
+ sev = "info"
+ default:
+ continue
+ }
+ alert := reportAlert{
+ Severity: sev,
+ Title: st.Message,
+ }
+ if fix, ok := st.Meta["fix"].(string); ok && fix != "" {
+ alert.Body = template.HTML(template.HTMLEscapeString(fix))
+ }
+ rd.Alerts = append(rd.Alerts, alert)
+ }
+ } else {
+ if len(resolveFails) > 0 {
+ rd.Alerts = append(rd.Alerts, reportAlert{
+ Severity: "crit",
+ Title: fmt.Sprintf("DNS resolution failed for %d SRV target(s)", len(resolveFails)),
+ Body: template.HTML(fmt.Sprintf(
+ "%s
Clients will not be able to reach the service. Fix: either publish A/AAAA records for the target(s), or remove the broken SRV record.",
+ strings.Join(resolveFails, "
"))),
+ })
+ }
+ if len(cnames) > 0 {
+ rd.Alerts = append(rd.Alerts, reportAlert{
+ Severity: "warn",
+ Title: "SRV target is a CNAME (RFC 2782 violation)",
+ Body: template.HTML(fmt.Sprintf(
+ "Target(s): %s
RFC 2782 requires SRV targets to resolve directly to A/AAAA. "+
+ "Some clients will refuse to follow the CNAME. Fix: point the SRV record to a hostname with A/AAAA records, "+
+ "or replace the CNAME with an ALIAS/ANAME at the DNS provider.",
+ ""+strings.Join(cnames, ", ")+"")),
+ })
+ }
+ if len(tcpDown) > 0 {
+ var items []string
+ for _, f := range tcpDown {
+ items = append(items, fmt.Sprintf("%s (%s): %s",
+ template.HTMLEscapeString(f.address),
+ template.HTMLEscapeString(f.owner),
+ template.HTMLEscapeString(f.err)))
+ }
+ rd.Alerts = append(rd.Alerts, reportAlert{
+ Severity: "crit",
+ Title: fmt.Sprintf("%d target(s) unreachable on their advertised TCP port", len(tcpDown)),
+ Body: template.HTML(strings.Join(items, "
") +
+ "
Check: (1) the server is running and bound to the right port; " +
+ "(2) firewall/security-group allows inbound TCP to that port; " +
+ "(3) the SRV record is not pointing at an old IP."),
+ })
+ }
+ if len(nulls) > 0 && len(nulls) == len(d.Records) {
+ rd.Alerts = append(rd.Alerts, reportAlert{
+ Severity: "warn",
+ Title: "All SRV records use the null target (\".\"): service is explicitly disabled",
+ Body: template.HTML(
+ "RFC 2782 defines a single SRV record with target \".\" to signal that the service is " +
+ "intentionally not available. If this is what you want, the configuration is correct. " +
+ "If you expected clients to reach this service, replace the null target with a real hostname."),
+ })
}
- rd.Alerts = append(rd.Alerts, reportAlert{
- Severity: "crit",
- Title: fmt.Sprintf("%d target(s) unreachable on their advertised TCP port", len(tcpDown)),
- Body: template.HTML(strings.Join(items, "
") +
- "
Check: (1) the server is running and bound to the right port; " +
- "(2) firewall/security-group allows inbound TCP to that port; " +
- "(3) the SRV record is not pointing at an old IP."),
- })
- }
- if len(nulls) > 0 && len(nulls) == len(d.Records) {
- rd.Alerts = append(rd.Alerts, reportAlert{
- Severity: "warn",
- Title: "All SRV records use the null target (\".\"): service is explicitly disabled",
- Body: template.HTML(
- "RFC 2782 defines a single SRV record with target \".\" to signal that the service is " +
- "intentionally not available. If this is what you want, the configuration is correct. " +
- "If you expected clients to reach this service, replace the null target with a real hostname."),
- })
}
var buf strings.Builder
diff --git a/go.mod b/go.mod
index 851c290..c53916b 100644
--- a/go.mod
+++ b/go.mod
@@ -3,44 +3,44 @@ module git.happydns.org/checker-srv
go 1.25.0
require (
- git.happydns.org/checker-sdk-go v1.5.0
- git.happydns.org/checker-tls v0.6.2
+ git.happydns.org/checker-sdk-go v1.7.0
+ git.happydns.org/checker-tls v0.7.0
git.happydns.org/happyDomain v0.7.0
)
require (
- github.com/bytedance/gopkg v0.1.3 // indirect
- github.com/bytedance/sonic v1.15.0 // indirect
- github.com/bytedance/sonic/loader v0.5.0 // indirect
- github.com/cloudwego/base64x v0.1.6 // indirect
+ github.com/bytedance/gopkg v0.1.4 // indirect
+ github.com/bytedance/sonic v1.15.1 // indirect
+ github.com/bytedance/sonic/loader v0.5.1 // indirect
+ github.com/cloudwego/base64x v0.1.7 // indirect
github.com/gabriel-vasile/mimetype v1.4.13 // indirect
- github.com/gin-contrib/sse v1.1.0 // indirect
+ github.com/gin-contrib/sse v1.1.1 // indirect
github.com/gin-gonic/gin v1.12.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
- github.com/go-playground/validator/v10 v10.30.1 // indirect
- github.com/goccy/go-json v0.10.5 // indirect
+ github.com/go-playground/validator/v10 v10.30.2 // indirect
+ github.com/goccy/go-json v0.10.6 // indirect
github.com/goccy/go-yaml v1.19.2 // indirect
github.com/json-iterator/go v1.1.13-0.20220915233716-71ac16282d12 // indirect
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
- github.com/mattn/go-isatty v0.0.20 // indirect
+ github.com/mattn/go-isatty v0.0.22 // indirect
github.com/miekg/dns v1.1.72 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
- github.com/pelletier/go-toml/v2 v2.2.4 // indirect
+ github.com/pelletier/go-toml/v2 v2.3.1 // indirect
github.com/quic-go/qpack v0.6.0 // indirect
github.com/quic-go/quic-go v0.59.0 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.3.1 // indirect
- go.mongodb.org/mongo-driver/v2 v2.5.0 // indirect
- golang.org/x/arch v0.24.0 // indirect
- golang.org/x/crypto v0.49.0 // indirect
- golang.org/x/mod v0.33.0 // indirect
- golang.org/x/net v0.51.0 // indirect
+ go.mongodb.org/mongo-driver/v2 v2.6.0 // indirect
+ golang.org/x/arch v0.27.0 // indirect
+ golang.org/x/crypto v0.51.0 // indirect
+ golang.org/x/mod v0.36.0 // indirect
+ golang.org/x/net v0.54.0 // indirect
golang.org/x/sync v0.20.0 // indirect
- golang.org/x/sys v0.42.0 // indirect
- golang.org/x/text v0.35.0 // indirect
- golang.org/x/tools v0.42.0 // indirect
+ golang.org/x/sys v0.44.0 // indirect
+ golang.org/x/text v0.37.0 // indirect
+ golang.org/x/tools v0.45.0 // indirect
google.golang.org/protobuf v1.36.11 // indirect
)
diff --git a/go.sum b/go.sum
index 4bf62a6..3664c74 100644
--- a/go.sum
+++ b/go.sum
@@ -1,25 +1,25 @@
-git.happydns.org/checker-sdk-go v1.5.0 h1:5uD5Cm6xJ+lwnhbJ09iCXGHbYS9zRh+Yh0NeBHkAPBY=
-git.happydns.org/checker-sdk-go v1.5.0/go.mod h1:aNAcfYFfbhvH9kJhE0Njp5GX0dQbxdRB0rJ0KvSC5nI=
-git.happydns.org/checker-tls v0.6.2 h1:8oKia1XlD+tklyqrwzmUgFH1Kw8VLSLLF9suZ7Qr14E=
-git.happydns.org/checker-tls v0.6.2/go.mod h1:9tpnxg0iOwS+7If64DRG1jqYonUAgxOBuxwfF5mVkL4=
+git.happydns.org/checker-sdk-go v1.7.0 h1:dSgo2js5mfXluLc6x0WWZ0MQULd9XV2GI9z0Usk+Qgw=
+git.happydns.org/checker-sdk-go v1.7.0/go.mod h1:aNAcfYFfbhvH9kJhE0Njp5GX0dQbxdRB0rJ0KvSC5nI=
+git.happydns.org/checker-tls v0.7.0 h1:mfNHYbHMGS40y+N2rudC2svT/xLK7KCiSa7V8/RhcTM=
+git.happydns.org/checker-tls v0.7.0/go.mod h1:wlY4UI3owvqMAtOcXLmskpTpZ7xPjuiV6M42+rFZDQo=
git.happydns.org/happyDomain v0.7.0 h1:NV82/NbcSeRm0+IUZqaK3Vu9Ovl5+vv4AigUJZMdwws=
git.happydns.org/happyDomain v0.7.0/go.mod h1:5tgkmqFE65kK359rY49V++49wgZ0gco+Gh9X6tbL+bY=
-github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M=
-github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM=
-github.com/bytedance/sonic v1.15.0 h1:/PXeWFaR5ElNcVE84U0dOHjiMHQOwNIx3K4ymzh/uSE=
-github.com/bytedance/sonic v1.15.0/go.mod h1:tFkWrPz0/CUCLEF4ri4UkHekCIcdnkqXw9VduqpJh0k=
-github.com/bytedance/sonic/loader v0.5.0 h1:gXH3KVnatgY7loH5/TkeVyXPfESoqSBSBEiDd5VjlgE=
-github.com/bytedance/sonic/loader v0.5.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo=
-github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M=
-github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU=
+github.com/bytedance/gopkg v0.1.4 h1:oZnQwnX82KAIWb7033bEwtxvTqXcYMxDBaQxo5JJHWM=
+github.com/bytedance/gopkg v0.1.4/go.mod h1:v1zWfPm21Fb+OsyXN2VAHdL6TBb2L88anLQgdyje6R4=
+github.com/bytedance/sonic v1.15.1 h1:nJD5PmM0vY7J8CT6MxoqbVAAMhkSmV2HgRAUrrpLoOw=
+github.com/bytedance/sonic v1.15.1/go.mod h1:mT2NbXunuaEbnZ+mRIX/vYqKISmgEuHFDI4UzmKx2SA=
+github.com/bytedance/sonic/loader v0.5.1 h1:Ygpfa9zwRCCKSlrp5bBP/b/Xzc3VxsAW+5NIYXrOOpI=
+github.com/bytedance/sonic/loader v0.5.1/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo=
+github.com/cloudwego/base64x v0.1.7 h1:NppS+Fgzg5ovhn4NkUXaDT3x9jldgH5ToMCqzBSi2zI=
+github.com/cloudwego/base64x v0.1.7/go.mod h1:Cu1PV9zfrSf7ET2tIbWbbEy7jO7HHJ13q4X2SQ8aWYg=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gabriel-vasile/mimetype v1.4.13 h1:46nXokslUBsAJE/wMsp5gtO500a4F3Nkz9Ufpk2AcUM=
github.com/gabriel-vasile/mimetype v1.4.13/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
-github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
-github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
+github.com/gin-contrib/sse v1.1.1 h1:uGYpNwTacv5R68bSGMapo62iLTRa9l5zxGCps4hK6ko=
+github.com/gin-contrib/sse v1.1.1/go.mod h1:QXzuVkA0YO7o/gun03UI1Q+FTI8ZV/n5t03kIQAI89s=
github.com/gin-gonic/gin v1.12.0 h1:b3YAbrZtnf8N//yjKeU2+MQsh2mY5htkZidOM7O0wG8=
github.com/gin-gonic/gin v1.12.0/go.mod h1:VxccKfsSllpKshkBWgVgRniFFAzFb9csfngsqANjnLc=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
@@ -28,10 +28,10 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
-github.com/go-playground/validator/v10 v10.30.1 h1:f3zDSN/zOma+w6+1Wswgd9fLkdwy06ntQJp0BBvFG0w=
-github.com/go-playground/validator/v10 v10.30.1/go.mod h1:oSuBIQzuJxL//3MelwSLD5hc2Tu889bF0Idm9Dg26cM=
-github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
-github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
+github.com/go-playground/validator/v10 v10.30.2 h1:JiFIMtSSHb2/XBUbWM4i/MpeQm9ZK2xqPNk8vgvu5JQ=
+github.com/go-playground/validator/v10 v10.30.2/go.mod h1:mAf2pIOVXjTEBrwUMGKkCWKKPs9NheYGabeB04txQSc=
+github.com/goccy/go-json v0.10.6 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU=
+github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM=
github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
@@ -43,8 +43,8 @@ github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzh
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
-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-isatty v0.0.22 h1:j8l17JJ9i6VGPUFUYoTUKPSgKe/83EYU2zBC7YNKMw4=
+github.com/mattn/go-isatty v0.0.22/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4=
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/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -52,8 +52,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
-github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
-github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
+github.com/pelletier/go-toml/v2 v2.3.1 h1:MYEvvGnQjeNkRF1qUuGolNtNExTDwct51yp7olPtrEc=
+github.com/pelletier/go-toml/v2 v2.3.1/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
@@ -75,27 +75,26 @@ github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY=
github.com/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
-go.mongodb.org/mongo-driver/v2 v2.5.0 h1:yXUhImUjjAInNcpTcAlPHiT7bIXhshCTL3jVBkF3xaE=
-go.mongodb.org/mongo-driver/v2 v2.5.0/go.mod h1:yOI9kBsufol30iFsl1slpdq1I0eHPzybRWdyYUs8K/0=
+go.mongodb.org/mongo-driver/v2 v2.6.0 h1:b9sJOYrkmt4l8bY43ZenFBcPlhYIjaOfYHLtbB/5qi8=
+go.mongodb.org/mongo-driver/v2 v2.6.0/go.mod h1:yOI9kBsufol30iFsl1slpdq1I0eHPzybRWdyYUs8K/0=
go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
-golang.org/x/arch v0.24.0 h1:qlJ3M9upxvFfwRM51tTg3Yl+8CP9vCC1E7vlFpgv99Y=
-golang.org/x/arch v0.24.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A=
-golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4=
-golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA=
-golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8=
-golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w=
-golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo=
-golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y=
+golang.org/x/arch v0.27.0 h1:0WNVcR8u9yFz8j5FvdHpgwNp3FS5U4guYdzHwEiGjoU=
+golang.org/x/arch v0.27.0/go.mod h1:0X+GdSIP+kL5wPmpK7sdkEVTt2XoYP0cSjQSbZBwOi8=
+golang.org/x/crypto v0.51.0 h1:IBPXwPfKxY7cWQZ38ZCIRPI50YLeevDLlLnyC5wRGTI=
+golang.org/x/crypto v0.51.0/go.mod h1:8AdwkbraGNABw2kOX6YFPs3WM22XqI4EXEd8g+x7Oc8=
+golang.org/x/mod v0.36.0 h1:JJjpVx6myfUsUdAzZuOSTTmRE0PfZeNWzzvKrP7amb4=
+golang.org/x/mod v0.36.0/go.mod h1:moc6ELqsWcOw5Ef3xVprK5ul/MvtVvkIXLziUOICjUQ=
+golang.org/x/net v0.54.0 h1:2zJIZAxAHV/OHCDTCOHAYehQzLfSXuf/5SoL/Dv6w/w=
+golang.org/x/net v0.54.0/go.mod h1:Sj4oj8jK6XmHpBZU/zWHw3BV3abl4Kvi+Ut7cQcY+cQ=
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
-golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
-golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
-golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
-golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
-golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k=
-golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0=
+golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ=
+golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
+golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
+golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
+golang.org/x/tools v0.45.0 h1:18qN3FAooORvApf5XjCXgsuayZOEtXf6JK18I3+ONa8=
+golang.org/x/tools v0.45.0/go.mod h1:LuUGqqaXcXMEFEruIVJVm5mgDD8vww/z/SR1gQ4uE/0=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=