mirror of
https://github.com/element-hq/dendrite.git
synced 2025-03-14 14:15:35 +00:00
Compare commits
28 commits
helm-dendr
...
main
Author | SHA1 | Date | |
---|---|---|---|
![]() |
c15dee80f2 | ||
![]() |
06e25ca5ca | ||
![]() |
ad22d950dd | ||
![]() |
57bbba3051 | ||
![]() |
8872299b43 | ||
![]() |
6be7249368 | ||
![]() |
1b8b88cd3d | ||
![]() |
f2a48c352a | ||
![]() |
3d6e6a35ab | ||
![]() |
f0861583f7 | ||
![]() |
f43a426b78 | ||
![]() |
48fb3b923f | ||
![]() |
d285fd880a | ||
![]() |
f4506a0d82 | ||
![]() |
9de3e84fff | ||
![]() |
ceeef81210 | ||
![]() |
3e6835f073 | ||
![]() |
60442bd059 | ||
![]() |
4eb7267d9e | ||
![]() |
afa8fc489e | ||
![]() |
3672df7e38 | ||
![]() |
a41f9cc154 | ||
![]() |
315269d8f9 | ||
![]() |
39bcd5f69d | ||
![]() |
24bd07a1d7 | ||
![]() |
2ab4219ffc | ||
![]() |
829ecafdc4 | ||
![]() |
7f4ba1f6eb |
23 changed files with 681 additions and 542 deletions
2
.github/workflows/dendrite.yml
vendored
2
.github/workflows/dendrite.yml
vendored
|
@ -37,7 +37,7 @@ jobs:
|
|||
cache: true
|
||||
|
||||
- name: Install Node
|
||||
uses: actions/setup-node@v2
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 14
|
||||
|
||||
|
|
16
.github/workflows/docker.yml
vendored
16
.github/workflows/docker.yml
vendored
|
@ -52,7 +52,7 @@ jobs:
|
|||
- name: Build "build" image
|
||||
if: github.ref_name == 'main' || github.event_name == 'release'
|
||||
id: docker_build_cache
|
||||
uses: docker/build-push-action@v3
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
target: build
|
||||
cache-from: type=registry,ref=ghcr.io/${{ env.GHCR_NAMESPACE }}/dendrite-monolith:buildcache
|
||||
|
@ -66,7 +66,7 @@ jobs:
|
|||
- name: Build main monolith image
|
||||
if: github.ref_name == 'main'
|
||||
id: docker_build_monolith
|
||||
uses: docker/build-push-action@v3
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
cache-from: type=registry,ref=ghcr.io/${{ env.GHCR_NAMESPACE }}/dendrite-monolith:buildcache
|
||||
context: .
|
||||
|
@ -79,7 +79,7 @@ jobs:
|
|||
- name: Build release monolith image
|
||||
if: github.event_name == 'release' # Only for GitHub releases
|
||||
id: docker_build_monolith_release
|
||||
uses: docker/build-push-action@v3
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
platforms: ${{ env.PLATFORMS }}
|
||||
|
@ -98,7 +98,7 @@ jobs:
|
|||
output: "trivy-results.sarif"
|
||||
|
||||
- name: Upload Trivy scan results to GitHub Security tab
|
||||
uses: github/codeql-action/upload-sarif@v2
|
||||
uses: github/codeql-action/upload-sarif@v3
|
||||
with:
|
||||
sarif_file: "trivy-results.sarif"
|
||||
|
||||
|
@ -135,7 +135,7 @@ jobs:
|
|||
- name: Build main Pinecone demo image
|
||||
if: github.ref_name == 'main'
|
||||
id: docker_build_demo_pinecone
|
||||
uses: docker/build-push-action@v3
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
cache-from: type=registry,ref=ghcr.io/${{ env.GHCR_NAMESPACE }}/dendrite-monolith:buildcache
|
||||
context: .
|
||||
|
@ -149,7 +149,7 @@ jobs:
|
|||
- name: Build release Pinecone demo image
|
||||
if: github.event_name == 'release' # Only for GitHub releases
|
||||
id: docker_build_demo_pinecone_release
|
||||
uses: docker/build-push-action@v3
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
cache-from: type=registry,ref=ghcr.io/${{ env.GHCR_NAMESPACE }}/dendrite-monolith:buildcache
|
||||
context: .
|
||||
|
@ -195,7 +195,7 @@ jobs:
|
|||
- name: Build main Yggdrasil demo image
|
||||
if: github.ref_name == 'main'
|
||||
id: docker_build_demo_yggdrasil
|
||||
uses: docker/build-push-action@v3
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
cache-from: type=registry,ref=ghcr.io/${{ env.GHCR_NAMESPACE }}/dendrite-monolith:buildcache
|
||||
context: .
|
||||
|
@ -209,7 +209,7 @@ jobs:
|
|||
- name: Build release Yggdrasil demo image
|
||||
if: github.event_name == 'release' # Only for GitHub releases
|
||||
id: docker_build_demo_yggdrasil_release
|
||||
uses: docker/build-push-action@v3
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
cache-from: type=registry,ref=ghcr.io/${{ env.GHCR_NAMESPACE }}/dendrite-monolith:buildcache
|
||||
context: .
|
||||
|
|
4
.github/workflows/gh-pages.yml
vendored
4
.github/workflows/gh-pages.yml
vendored
|
@ -30,7 +30,7 @@ jobs:
|
|||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup Pages
|
||||
uses: actions/configure-pages@v2
|
||||
uses: actions/configure-pages@v5
|
||||
- name: Build with Jekyll
|
||||
uses: actions/jekyll-build-pages@v1
|
||||
with:
|
||||
|
@ -49,4 +49,4 @@ jobs:
|
|||
steps:
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v1
|
||||
uses: actions/deploy-pages@v4
|
||||
|
|
4
.github/workflows/helm.yml
vendored
4
.github/workflows/helm.yml
vendored
|
@ -27,12 +27,12 @@ jobs:
|
|||
git config user.email "$GITHUB_ACTOR@users.noreply.github.com"
|
||||
|
||||
- name: Install Helm
|
||||
uses: azure/setup-helm@v3
|
||||
uses: azure/setup-helm@v4
|
||||
with:
|
||||
version: v3.10.0
|
||||
|
||||
- name: Run chart-releaser
|
||||
uses: helm/chart-releaser-action@v1.6.0
|
||||
uses: helm/chart-releaser-action@v1.7.0
|
||||
env:
|
||||
CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
with:
|
||||
|
|
6
.github/workflows/k8s.yml
vendored
6
.github/workflows/k8s.yml
vendored
|
@ -20,14 +20,14 @@ jobs:
|
|||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: azure/setup-helm@v3
|
||||
- uses: azure/setup-helm@v4
|
||||
with:
|
||||
version: v3.10.0
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: 3.11
|
||||
check-latest: true
|
||||
- uses: helm/chart-testing-action@v2.6.1
|
||||
- uses: helm/chart-testing-action@v2.7.0
|
||||
- name: Get changed status
|
||||
id: list-changed
|
||||
run: |
|
||||
|
@ -62,7 +62,7 @@ jobs:
|
|||
with:
|
||||
python-version: "3.10"
|
||||
- name: Set up chart-testing
|
||||
uses: helm/chart-testing-action@v2.6.1
|
||||
uses: helm/chart-testing-action@v2.7.0
|
||||
- name: Create k3d cluster
|
||||
uses: nolar/setup-k3d-k3s@v1
|
||||
with:
|
||||
|
|
8
.github/workflows/schedules.yaml
vendored
8
.github/workflows/schedules.yaml
vendored
|
@ -254,7 +254,7 @@ jobs:
|
|||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: matrix-org/matrix-react-sdk
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
cache: 'yarn'
|
||||
- name: Fetch layered build
|
||||
|
@ -272,7 +272,7 @@ jobs:
|
|||
run: |
|
||||
sed -i '/HOMESERVER/c\ HOMESERVER: "dendrite",' cypress.config.ts
|
||||
- name: "Run cypress tests"
|
||||
uses: cypress-io/github-action@v4.1.1
|
||||
uses: cypress-io/github-action@v6.7.10
|
||||
with:
|
||||
browser: chrome
|
||||
start: npx serve -p 8080 ./element-web/webapp
|
||||
|
@ -294,7 +294,7 @@ jobs:
|
|||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: matrix-org/matrix-react-sdk
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
cache: 'yarn'
|
||||
- name: Fetch layered build
|
||||
|
@ -312,7 +312,7 @@ jobs:
|
|||
run: |
|
||||
sed -i '/HOMESERVER/c\ HOMESERVER: "dendritePinecone",' cypress.config.ts
|
||||
- name: "Run cypress tests"
|
||||
uses: cypress-io/github-action@v4.1.1
|
||||
uses: cypress-io/github-action@v6.7.10
|
||||
with:
|
||||
browser: chrome
|
||||
start: npx serve -p 8080 ./element-web/webapp
|
||||
|
|
14
README.md
14
README.md
|
@ -125,3 +125,17 @@ If you're new to the project, see our
|
|||
look for [Good First Issues](https://github.com/element-hq/dendrite/labels/good%20first%20issue). If you're
|
||||
familiar with the project, look for [Help Wanted](https://github.com/element-hq/dendrite/labels/help-wanted)
|
||||
issues.
|
||||
|
||||
## Copyright & License
|
||||
|
||||
Copyright 2017 OpenMarket Ltd
|
||||
Copyright 2017 Vector Creations Ltd
|
||||
Copyright 2017-2025 New Vector Ltd
|
||||
|
||||
This software is dual-licensed by New Vector Ltd (Element). It can be used either:
|
||||
|
||||
(1) for free under the terms of the GNU Affero General Public License (as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version); OR
|
||||
|
||||
(2) under the terms of a paid-for Element Commercial License agreement between you and Element (the terms of which may vary depending on what you and Element have agreed to).
|
||||
Unless required by applicable law or agreed to in writing, software distributed under the Licenses is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the Licenses for the specific language governing permissions and limitations under the Licenses.
|
||||
|
||||
|
|
|
@ -7,7 +7,12 @@
|
|||
package routing
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/matrix-org/gomatrixserverlib/fclient"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/element-hq/dendrite/clientapi/auth"
|
||||
"github.com/element-hq/dendrite/clientapi/auth/authtypes"
|
||||
|
@ -23,10 +28,15 @@ type crossSigningRequest struct {
|
|||
Auth newPasswordAuth `json:"auth"`
|
||||
}
|
||||
|
||||
type UploadKeysAPI interface {
|
||||
QueryKeys(ctx context.Context, req *api.QueryKeysRequest, res *api.QueryKeysResponse)
|
||||
api.UploadDeviceKeysAPI
|
||||
}
|
||||
|
||||
func UploadCrossSigningDeviceKeys(
|
||||
req *http.Request, userInteractiveAuth *auth.UserInteractive,
|
||||
keyserverAPI api.ClientKeyAPI, device *api.Device,
|
||||
accountAPI api.ClientUserAPI, cfg *config.ClientAPI,
|
||||
req *http.Request,
|
||||
keyserverAPI UploadKeysAPI, device *api.Device,
|
||||
accountAPI auth.GetAccountByPassword, cfg *config.ClientAPI,
|
||||
) util.JSONResponse {
|
||||
uploadReq := &crossSigningRequest{}
|
||||
uploadRes := &api.PerformUploadDeviceKeysResponse{}
|
||||
|
@ -35,32 +45,59 @@ func UploadCrossSigningDeviceKeys(
|
|||
if resErr != nil {
|
||||
return *resErr
|
||||
}
|
||||
sessionID := uploadReq.Auth.Session
|
||||
if sessionID == "" {
|
||||
sessionID = util.RandomString(sessionIDLength)
|
||||
}
|
||||
if uploadReq.Auth.Type != authtypes.LoginTypePassword {
|
||||
|
||||
// Query existing keys to determine if UIA is required
|
||||
keyResp := api.QueryKeysResponse{}
|
||||
keyserverAPI.QueryKeys(req.Context(), &api.QueryKeysRequest{
|
||||
UserID: device.UserID,
|
||||
UserToDevices: map[string][]string{device.UserID: {device.ID}},
|
||||
Timeout: time.Second * 10,
|
||||
}, &keyResp)
|
||||
|
||||
if keyResp.Error != nil {
|
||||
logrus.WithError(keyResp.Error).Error("Failed to query keys")
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusUnauthorized,
|
||||
JSON: newUserInteractiveResponse(
|
||||
sessionID,
|
||||
[]authtypes.Flow{
|
||||
{
|
||||
Stages: []authtypes.LoginType{authtypes.LoginTypePassword},
|
||||
},
|
||||
},
|
||||
nil,
|
||||
),
|
||||
Code: http.StatusBadRequest,
|
||||
JSON: spec.Unknown(keyResp.Error.Error()),
|
||||
}
|
||||
}
|
||||
typePassword := auth.LoginTypePassword{
|
||||
GetAccountByPassword: accountAPI.QueryAccountByPassword,
|
||||
Config: cfg,
|
||||
|
||||
existingMasterKey, hasMasterKey := keyResp.MasterKeys[device.UserID]
|
||||
requireUIA := false
|
||||
if hasMasterKey {
|
||||
// If we have a master key, check if any of the existing keys differ. If they do,
|
||||
// we need to re-authenticate the user.
|
||||
requireUIA = keysDiffer(existingMasterKey, keyResp, uploadReq, device.UserID)
|
||||
}
|
||||
if _, authErr := typePassword.Login(req.Context(), &uploadReq.Auth.PasswordRequest); authErr != nil {
|
||||
return *authErr
|
||||
|
||||
if requireUIA {
|
||||
sessionID := uploadReq.Auth.Session
|
||||
if sessionID == "" {
|
||||
sessionID = util.RandomString(sessionIDLength)
|
||||
}
|
||||
if uploadReq.Auth.Type != authtypes.LoginTypePassword {
|
||||
return util.JSONResponse{
|
||||
Code: http.StatusUnauthorized,
|
||||
JSON: newUserInteractiveResponse(
|
||||
sessionID,
|
||||
[]authtypes.Flow{
|
||||
{
|
||||
Stages: []authtypes.LoginType{authtypes.LoginTypePassword},
|
||||
},
|
||||
},
|
||||
nil,
|
||||
),
|
||||
}
|
||||
}
|
||||
typePassword := auth.LoginTypePassword{
|
||||
GetAccountByPassword: accountAPI,
|
||||
Config: cfg,
|
||||
}
|
||||
if _, authErr := typePassword.Login(req.Context(), &uploadReq.Auth.PasswordRequest); authErr != nil {
|
||||
return *authErr
|
||||
}
|
||||
sessions.addCompletedSessionStage(sessionID, authtypes.LoginTypePassword)
|
||||
}
|
||||
sessions.addCompletedSessionStage(sessionID, authtypes.LoginTypePassword)
|
||||
|
||||
uploadReq.UserID = device.UserID
|
||||
keyserverAPI.PerformUploadDeviceKeys(req.Context(), &uploadReq.PerformUploadDeviceKeysRequest, uploadRes)
|
||||
|
@ -96,6 +133,21 @@ func UploadCrossSigningDeviceKeys(
|
|||
}
|
||||
}
|
||||
|
||||
func keysDiffer(existingMasterKey fclient.CrossSigningKey, keyResp api.QueryKeysResponse, uploadReq *crossSigningRequest, userID string) bool {
|
||||
masterKeyEqual := existingMasterKey.Equal(&uploadReq.MasterKey)
|
||||
if !masterKeyEqual {
|
||||
return true
|
||||
}
|
||||
existingSelfSigningKey := keyResp.SelfSigningKeys[userID]
|
||||
selfSigningEqual := existingSelfSigningKey.Equal(&uploadReq.SelfSigningKey)
|
||||
if !selfSigningEqual {
|
||||
return true
|
||||
}
|
||||
existingUserSigningKey := keyResp.UserSigningKeys[userID]
|
||||
userSigningEqual := existingUserSigningKey.Equal(&uploadReq.UserSigningKey)
|
||||
return !userSigningEqual
|
||||
}
|
||||
|
||||
func UploadCrossSigningDeviceSignatures(req *http.Request, keyserverAPI api.ClientKeyAPI, device *api.Device) util.JSONResponse {
|
||||
uploadReq := &api.PerformUploadDeviceSignaturesRequest{}
|
||||
uploadRes := &api.PerformUploadDeviceSignaturesResponse{}
|
||||
|
|
316
clientapi/routing/key_crosssigning_test.go
Normal file
316
clientapi/routing/key_crosssigning_test.go
Normal file
|
@ -0,0 +1,316 @@
|
|||
package routing
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/element-hq/dendrite/setup/config"
|
||||
"github.com/element-hq/dendrite/test"
|
||||
"github.com/element-hq/dendrite/test/testrig"
|
||||
"github.com/element-hq/dendrite/userapi/api"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
"github.com/matrix-org/gomatrixserverlib/fclient"
|
||||
"github.com/matrix-org/gomatrixserverlib/spec"
|
||||
)
|
||||
|
||||
type mockKeyAPI struct {
|
||||
t *testing.T
|
||||
userResponses map[string]api.QueryKeysResponse
|
||||
}
|
||||
|
||||
func (m mockKeyAPI) QueryKeys(ctx context.Context, req *api.QueryKeysRequest, res *api.QueryKeysResponse) {
|
||||
res.MasterKeys = m.userResponses[req.UserID].MasterKeys
|
||||
res.SelfSigningKeys = m.userResponses[req.UserID].SelfSigningKeys
|
||||
res.UserSigningKeys = m.userResponses[req.UserID].UserSigningKeys
|
||||
if m.t != nil {
|
||||
m.t.Logf("QueryKeys: %+v => %+v", req, res)
|
||||
}
|
||||
}
|
||||
|
||||
func (m mockKeyAPI) PerformUploadDeviceKeys(ctx context.Context, req *api.PerformUploadDeviceKeysRequest, res *api.PerformUploadDeviceKeysResponse) {
|
||||
// Just a dummy upload which always succeeds
|
||||
}
|
||||
|
||||
func getAccountByPassword(ctx context.Context, req *api.QueryAccountByPasswordRequest, res *api.QueryAccountByPasswordResponse) error {
|
||||
res.Exists = true
|
||||
res.Account = &api.Account{UserID: fmt.Sprintf("@%s:%s", req.Localpart, req.ServerName)}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Tests that if there is no existing master key for the user, the request is allowed
|
||||
func Test_UploadCrossSigningDeviceKeys_ValidRequest(t *testing.T) {
|
||||
req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(`{
|
||||
"master_key": {"user_id": "@user:example.com", "usage": ["master"], "keys": {"ed25519:1": "key1"}},
|
||||
"self_signing_key": {"user_id": "@user:example.com", "usage": ["self_signing"], "keys": {"ed25519:2": "key2"}},
|
||||
"user_signing_key": {"user_id": "@user:example.com", "usage": ["user_signing"], "keys": {"ed25519:3": "key3"}}
|
||||
}`))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
keyserverAPI := &mockKeyAPI{
|
||||
userResponses: map[string]api.QueryKeysResponse{
|
||||
"@user:example.com": {},
|
||||
},
|
||||
}
|
||||
device := &api.Device{UserID: "@user:example.com", ID: "device"}
|
||||
cfg := &config.ClientAPI{}
|
||||
|
||||
res := UploadCrossSigningDeviceKeys(req, keyserverAPI, device, getAccountByPassword, cfg)
|
||||
if res.Code != http.StatusOK {
|
||||
t.Fatalf("expected status %d, got %d", http.StatusOK, res.Code)
|
||||
}
|
||||
}
|
||||
|
||||
// Require UIA if there is an existing master key and there is no auth provided.
|
||||
func Test_UploadCrossSigningDeviceKeys_Unauthorised(t *testing.T) {
|
||||
userID := "@user:example.com"
|
||||
|
||||
// Note that there is no auth field.
|
||||
request := fclient.CrossSigningKeys{
|
||||
MasterKey: fclient.CrossSigningKey{
|
||||
Keys: map[gomatrixserverlib.KeyID]spec.Base64Bytes{"ed25519:1": spec.Base64Bytes("key1")},
|
||||
Usage: []fclient.CrossSigningKeyPurpose{fclient.CrossSigningKeyPurposeMaster},
|
||||
UserID: userID,
|
||||
},
|
||||
SelfSigningKey: fclient.CrossSigningKey{
|
||||
Keys: map[gomatrixserverlib.KeyID]spec.Base64Bytes{"ed25519:1": spec.Base64Bytes("key2")},
|
||||
Usage: []fclient.CrossSigningKeyPurpose{fclient.CrossSigningKeyPurposeSelfSigning},
|
||||
UserID: userID,
|
||||
},
|
||||
UserSigningKey: fclient.CrossSigningKey{
|
||||
Keys: map[gomatrixserverlib.KeyID]spec.Base64Bytes{"ed25519:1": spec.Base64Bytes("key3")},
|
||||
Usage: []fclient.CrossSigningKeyPurpose{fclient.CrossSigningKeyPurposeUserSigning},
|
||||
UserID: userID,
|
||||
},
|
||||
}
|
||||
|
||||
b := bytes.Buffer{}
|
||||
m := json.NewEncoder(&b)
|
||||
err := m.Encode(request)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
req := httptest.NewRequest(http.MethodPost, "/", &b)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
keyserverAPI := &mockKeyAPI{
|
||||
t: t,
|
||||
userResponses: map[string]api.QueryKeysResponse{
|
||||
"@user:example.com": {
|
||||
MasterKeys: map[string]fclient.CrossSigningKey{
|
||||
"@user:example.com": {UserID: "@user:example.com", Usage: []fclient.CrossSigningKeyPurpose{"master"}, Keys: map[gomatrixserverlib.KeyID]spec.Base64Bytes{"ed25519:1": spec.Base64Bytes("key1")}},
|
||||
},
|
||||
SelfSigningKeys: nil,
|
||||
UserSigningKeys: nil,
|
||||
},
|
||||
},
|
||||
}
|
||||
device := &api.Device{UserID: "@user:example.com", ID: "device"}
|
||||
cfg := &config.ClientAPI{}
|
||||
|
||||
res := UploadCrossSigningDeviceKeys(req, keyserverAPI, device, getAccountByPassword, cfg)
|
||||
if res.Code != http.StatusUnauthorized {
|
||||
t.Fatalf("expected status %d, got %d", http.StatusUnauthorized, res.Code)
|
||||
}
|
||||
}
|
||||
|
||||
// Invalid JSON is rejected
|
||||
func Test_UploadCrossSigningDeviceKeys_InvalidJSON(t *testing.T) {
|
||||
req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(`{
|
||||
"auth": {"type": "m.login.password", "session": "session", "user": "user", "password": "password"},
|
||||
"master_key": {"user_id": "@user:example.com", "usage": ["master"], "keys": {"ed25519:1": "key1"}},
|
||||
"self_signing_key": {"user_id": "@user:example.com", "usage": ["self_signing"], "keys": {"ed25519:2": "key2"}},
|
||||
"user_signing_key": {"user_id": "@user:example.com", "usage": ["user_signing"], "keys": {"ed25519:3": "key3"}
|
||||
}`)) // Missing closing brace
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
keyserverAPI := &mockKeyAPI{}
|
||||
device := &api.Device{UserID: "@user:example.com", ID: "device"}
|
||||
cfg := &config.ClientAPI{}
|
||||
|
||||
res := UploadCrossSigningDeviceKeys(req, keyserverAPI, device, getAccountByPassword, cfg)
|
||||
if res.Code != http.StatusBadRequest {
|
||||
t.Fatalf("expected status %d, got %d", http.StatusBadRequest, res.Code)
|
||||
}
|
||||
}
|
||||
|
||||
// Require UIA if an existing master key is present and the keys differ.
|
||||
func Test_UploadCrossSigningDeviceKeys_ExistingKeysMismatch(t *testing.T) {
|
||||
// Again, no auth provided
|
||||
req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(`{
|
||||
"master_key": {"user_id": "@user:example.com", "usage": ["master"], "keys": {"ed25519:1": "key1"}},
|
||||
"self_signing_key": {"user_id": "@user:example.com", "usage": ["self_signing"], "keys": {"ed25519:2": "key2"}},
|
||||
"user_signing_key": {"user_id": "@user:example.com", "usage": ["user_signing"], "keys": {"ed25519:3": "key3"}}
|
||||
}`))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
keyserverAPI := &mockKeyAPI{
|
||||
userResponses: map[string]api.QueryKeysResponse{
|
||||
"@user:example.com": {
|
||||
MasterKeys: map[string]fclient.CrossSigningKey{
|
||||
"@user:example.com": {UserID: "@user:example.com", Usage: []fclient.CrossSigningKeyPurpose{"master"}, Keys: map[gomatrixserverlib.KeyID]spec.Base64Bytes{"ed25519:1": spec.Base64Bytes("different_key")}},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
device := &api.Device{UserID: "@user:example.com", ID: "device"}
|
||||
|
||||
cfg, _, _ := testrig.CreateConfig(t, test.DBTypeSQLite)
|
||||
cfg.Global.ServerName = "example.com"
|
||||
|
||||
res := UploadCrossSigningDeviceKeys(req, keyserverAPI, device, getAccountByPassword, &cfg.ClientAPI)
|
||||
if res.Code != http.StatusUnauthorized {
|
||||
t.Fatalf("expected status %d, got %d", http.StatusUnauthorized, res.Code)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_KeysDiffer_MasterKeyMismatch(t *testing.T) {
|
||||
existingMasterKey := fclient.CrossSigningKey{
|
||||
UserID: "@user:example.com",
|
||||
Usage: []fclient.CrossSigningKeyPurpose{fclient.CrossSigningKeyPurposeMaster},
|
||||
Keys: map[gomatrixserverlib.KeyID]spec.Base64Bytes{"ed25519:1": spec.Base64Bytes("existing_key")},
|
||||
}
|
||||
keyResp := api.QueryKeysResponse{}
|
||||
uploadReq := &crossSigningRequest{
|
||||
PerformUploadDeviceKeysRequest: api.PerformUploadDeviceKeysRequest{
|
||||
CrossSigningKeys: fclient.CrossSigningKeys{
|
||||
MasterKey: fclient.CrossSigningKey{
|
||||
UserID: "@user:example.com",
|
||||
Usage: []fclient.CrossSigningKeyPurpose{fclient.CrossSigningKeyPurposeMaster},
|
||||
Keys: map[gomatrixserverlib.KeyID]spec.Base64Bytes{"ed25519:1": spec.Base64Bytes("new_key")},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
userID := "@user:example.com"
|
||||
|
||||
result := keysDiffer(existingMasterKey, keyResp, uploadReq, userID)
|
||||
if !result {
|
||||
t.Fatalf("expected keys to differ, but they did not")
|
||||
}
|
||||
}
|
||||
|
||||
func Test_KeysDiffer_SelfSigningKeyMismatch(t *testing.T) {
|
||||
existingMasterKey := fclient.CrossSigningKey{
|
||||
UserID: "@user:example.com",
|
||||
Usage: []fclient.CrossSigningKeyPurpose{fclient.CrossSigningKeyPurposeMaster},
|
||||
Keys: map[gomatrixserverlib.KeyID]spec.Base64Bytes{"ed25519:1": spec.Base64Bytes("key")},
|
||||
}
|
||||
keyResp := api.QueryKeysResponse{
|
||||
SelfSigningKeys: map[string]fclient.CrossSigningKey{
|
||||
"@user:example.com": {
|
||||
UserID: "@user:example.com",
|
||||
Usage: []fclient.CrossSigningKeyPurpose{fclient.CrossSigningKeyPurposeSelfSigning},
|
||||
Keys: map[gomatrixserverlib.KeyID]spec.Base64Bytes{"ed25519:2": spec.Base64Bytes("existing_key")},
|
||||
},
|
||||
},
|
||||
}
|
||||
uploadReq := &crossSigningRequest{
|
||||
PerformUploadDeviceKeysRequest: api.PerformUploadDeviceKeysRequest{
|
||||
CrossSigningKeys: fclient.CrossSigningKeys{
|
||||
SelfSigningKey: fclient.CrossSigningKey{
|
||||
UserID: "@user:example.com",
|
||||
Usage: []fclient.CrossSigningKeyPurpose{fclient.CrossSigningKeyPurposeSelfSigning},
|
||||
Keys: map[gomatrixserverlib.KeyID]spec.Base64Bytes{"ed25519:2": spec.Base64Bytes("new_key")},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
userID := "@user:example.com"
|
||||
|
||||
result := keysDiffer(existingMasterKey, keyResp, uploadReq, userID)
|
||||
if !result {
|
||||
t.Fatalf("expected keys to differ, but they did not")
|
||||
}
|
||||
}
|
||||
|
||||
func Test_KeysDiffer_UserSigningKeyMismatch(t *testing.T) {
|
||||
existingMasterKey := fclient.CrossSigningKey{
|
||||
UserID: "@user:example.com",
|
||||
Usage: []fclient.CrossSigningKeyPurpose{fclient.CrossSigningKeyPurposeMaster},
|
||||
Keys: map[gomatrixserverlib.KeyID]spec.Base64Bytes{"ed25519:1": spec.Base64Bytes("key")},
|
||||
}
|
||||
keyResp := api.QueryKeysResponse{
|
||||
UserSigningKeys: map[string]fclient.CrossSigningKey{
|
||||
"@user:example.com": {
|
||||
UserID: "@user:example.com",
|
||||
Usage: []fclient.CrossSigningKeyPurpose{fclient.CrossSigningKeyPurposeUserSigning},
|
||||
Keys: map[gomatrixserverlib.KeyID]spec.Base64Bytes{"ed25519:3": spec.Base64Bytes("existing_key")},
|
||||
},
|
||||
},
|
||||
}
|
||||
uploadReq := &crossSigningRequest{
|
||||
PerformUploadDeviceKeysRequest: api.PerformUploadDeviceKeysRequest{
|
||||
CrossSigningKeys: fclient.CrossSigningKeys{
|
||||
UserSigningKey: fclient.CrossSigningKey{
|
||||
UserID: "@user:example.com",
|
||||
Usage: []fclient.CrossSigningKeyPurpose{fclient.CrossSigningKeyPurposeUserSigning},
|
||||
Keys: map[gomatrixserverlib.KeyID]spec.Base64Bytes{"ed25519:3": spec.Base64Bytes("new_key")},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
userID := "@user:example.com"
|
||||
|
||||
result := keysDiffer(existingMasterKey, keyResp, uploadReq, userID)
|
||||
if !result {
|
||||
t.Fatalf("expected keys to differ, but they did not")
|
||||
}
|
||||
}
|
||||
|
||||
func Test_KeysDiffer_AllKeysMatch(t *testing.T) {
|
||||
existingMasterKey := fclient.CrossSigningKey{
|
||||
UserID: "@user:example.com",
|
||||
Usage: []fclient.CrossSigningKeyPurpose{fclient.CrossSigningKeyPurposeMaster},
|
||||
Keys: map[gomatrixserverlib.KeyID]spec.Base64Bytes{"ed25519:1": spec.Base64Bytes("key")},
|
||||
}
|
||||
keyResp := api.QueryKeysResponse{
|
||||
SelfSigningKeys: map[string]fclient.CrossSigningKey{
|
||||
"@user:example.com": {
|
||||
UserID: "@user:example.com",
|
||||
Usage: []fclient.CrossSigningKeyPurpose{fclient.CrossSigningKeyPurposeSelfSigning},
|
||||
Keys: map[gomatrixserverlib.KeyID]spec.Base64Bytes{"ed25519:2": spec.Base64Bytes("key")},
|
||||
},
|
||||
},
|
||||
UserSigningKeys: map[string]fclient.CrossSigningKey{
|
||||
"@user:example.com": {
|
||||
UserID: "@user:example.com",
|
||||
Usage: []fclient.CrossSigningKeyPurpose{fclient.CrossSigningKeyPurposeUserSigning},
|
||||
Keys: map[gomatrixserverlib.KeyID]spec.Base64Bytes{"ed25519:3": spec.Base64Bytes("key")},
|
||||
},
|
||||
},
|
||||
}
|
||||
uploadReq := &crossSigningRequest{
|
||||
PerformUploadDeviceKeysRequest: api.PerformUploadDeviceKeysRequest{
|
||||
CrossSigningKeys: fclient.CrossSigningKeys{
|
||||
MasterKey: fclient.CrossSigningKey{
|
||||
UserID: "@user:example.com",
|
||||
Usage: []fclient.CrossSigningKeyPurpose{fclient.CrossSigningKeyPurposeMaster},
|
||||
Keys: map[gomatrixserverlib.KeyID]spec.Base64Bytes{"ed25519:1": spec.Base64Bytes("key")},
|
||||
},
|
||||
SelfSigningKey: fclient.CrossSigningKey{
|
||||
UserID: "@user:example.com",
|
||||
Usage: []fclient.CrossSigningKeyPurpose{fclient.CrossSigningKeyPurposeSelfSigning},
|
||||
Keys: map[gomatrixserverlib.KeyID]spec.Base64Bytes{"ed25519:2": spec.Base64Bytes("key")},
|
||||
},
|
||||
UserSigningKey: fclient.CrossSigningKey{
|
||||
UserID: "@user:example.com",
|
||||
Usage: []fclient.CrossSigningKeyPurpose{fclient.CrossSigningKeyPurposeUserSigning},
|
||||
Keys: map[gomatrixserverlib.KeyID]spec.Base64Bytes{"ed25519:3": spec.Base64Bytes("key")},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
userID := "@user:example.com"
|
||||
|
||||
result := keysDiffer(existingMasterKey, keyResp, uploadReq, userID)
|
||||
if result {
|
||||
t.Fatalf("expected keys to match, but they did not")
|
||||
}
|
||||
}
|
|
@ -1441,7 +1441,7 @@ func Setup(
|
|||
// Cross-signing device keys
|
||||
|
||||
postDeviceSigningKeys := httputil.MakeAuthAPI("post_device_signing_keys", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
return UploadCrossSigningDeviceKeys(req, userInteractiveAuth, userAPI, device, userAPI, cfg)
|
||||
return UploadCrossSigningDeviceKeys(req, userAPI, device, userAPI.QueryAccountByPassword, cfg)
|
||||
})
|
||||
|
||||
postDeviceSigningSignatures := httputil.MakeAuthAPI("post_device_signing_signatures", userAPI, func(req *http.Request, device *userapi.Device) util.JSONResponse {
|
||||
|
|
|
@ -138,6 +138,7 @@ func (p *P2PMonolith) SetupDendrite(
|
|||
rsAPI.SetFederationAPI(fsAPI, keyRing)
|
||||
|
||||
userAPI := userapi.NewInternalAPI(processCtx, cfg, cm, &natsInstance, rsAPI, federation, enableMetrics, fsAPI.IsBlacklistedOrBackingOff)
|
||||
rsAPI.SetUserAPI(userAPI)
|
||||
|
||||
asAPI := appservice.NewInternalAPI(processCtx, cfg, &natsInstance, userAPI, rsAPI)
|
||||
|
||||
|
|
|
@ -147,7 +147,7 @@ func (oq *destinationQueue) wakeQueueIfEventsPending(forceWakeup bool) {
|
|||
// or if forceWakeup is true. Otherwise there is no reason to start the
|
||||
// queue goroutine and waste resources.
|
||||
if forceWakeup || eventsPending() {
|
||||
logrus.Info("Starting queue due to pending events or forceWakeup")
|
||||
logrus.Debugf("Starting queue %q -> %q due to pending events or forceWakeup", oq.origin, oq.destination)
|
||||
oq.wakeQueueAndNotify()
|
||||
}
|
||||
}
|
||||
|
|
64
go.mod
64
go.mod
|
@ -4,8 +4,8 @@ require (
|
|||
github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d
|
||||
github.com/DATA-DOG/go-sqlmock v1.5.0
|
||||
github.com/MFAshby/stdemuxerhook v1.0.0
|
||||
github.com/Masterminds/semver/v3 v3.1.1
|
||||
github.com/blevesearch/bleve/v2 v2.4.0
|
||||
github.com/Masterminds/semver/v3 v3.3.1
|
||||
github.com/blevesearch/bleve/v2 v2.4.4
|
||||
github.com/codeclysm/extract v2.2.0+incompatible
|
||||
github.com/coder/websocket v1.8.12
|
||||
github.com/cretz/bine v0.2.0
|
||||
|
@ -29,50 +29,49 @@ require (
|
|||
github.com/matrix-org/pinecone v0.11.1-0.20230810010612-ea4c33717fd7
|
||||
github.com/matrix-org/util v0.0.0-20221111132719-399730281e66
|
||||
github.com/mattn/go-sqlite3 v1.14.24
|
||||
github.com/nats-io/nats-server/v2 v2.10.23
|
||||
github.com/nats-io/nats.go v1.37.0
|
||||
github.com/nats-io/nats-server/v2 v2.10.25
|
||||
github.com/nats-io/nats.go v1.38.0
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
|
||||
github.com/opentracing/opentracing-go v1.2.0
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/prometheus/client_golang v1.19.1
|
||||
github.com/prometheus/client_golang v1.20.5
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/tidwall/gjson v1.18.0
|
||||
github.com/tidwall/sjson v1.2.5
|
||||
github.com/uber/jaeger-client-go v2.30.0+incompatible
|
||||
github.com/uber/jaeger-lib v2.4.1+incompatible
|
||||
github.com/yggdrasil-network/yggdrasil-go v0.5.11
|
||||
github.com/yggdrasil-network/yggdrasil-go v0.5.12
|
||||
github.com/yggdrasil-network/yggquic v0.0.0-20241212194307-0d495106021f
|
||||
go.uber.org/atomic v1.11.0
|
||||
golang.org/x/crypto v0.31.0
|
||||
golang.org/x/crypto v0.32.0
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56
|
||||
golang.org/x/image v0.18.0
|
||||
golang.org/x/image v0.23.0
|
||||
golang.org/x/mobile v0.0.0-20240520174638-fa72addaaa1b
|
||||
golang.org/x/sync v0.10.0
|
||||
golang.org/x/term v0.27.0
|
||||
gopkg.in/h2non/bimg.v1 v1.1.9
|
||||
golang.org/x/term v0.28.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
gotest.tools/v3 v3.4.0
|
||||
gotest.tools/v3 v3.5.1
|
||||
maunium.net/go/mautrix v0.15.1
|
||||
modernc.org/sqlite v1.34.2
|
||||
modernc.org/sqlite v1.34.5
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/Arceliar/ironwood v0.0.0-20241210120540-9deb08d9f8f9 // indirect
|
||||
github.com/Arceliar/ironwood v0.0.0-20241213013129-743fe2fccbd3 // indirect
|
||||
github.com/HdrHistogram/hdrhistogram-go v1.1.2 // indirect
|
||||
github.com/Microsoft/go-winio v0.5.2 // indirect
|
||||
github.com/RoaringBitmap/roaring v1.2.3 // indirect
|
||||
github.com/RoaringBitmap/roaring v1.9.3 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bits-and-blooms/bitset v1.13.0 // indirect
|
||||
github.com/bits-and-blooms/bloom/v3 v3.7.0 // indirect
|
||||
github.com/blevesearch/bleve_index_api v1.1.6 // indirect
|
||||
github.com/blevesearch/bleve_index_api v1.1.12 // indirect
|
||||
github.com/blevesearch/geo v0.1.20 // indirect
|
||||
github.com/blevesearch/go-faiss v1.0.13 // indirect
|
||||
github.com/blevesearch/go-faiss v1.0.24 // indirect
|
||||
github.com/blevesearch/go-porterstemmer v1.0.3 // indirect
|
||||
github.com/blevesearch/gtreap v0.1.1 // indirect
|
||||
github.com/blevesearch/mmap-go v1.0.4 // indirect
|
||||
github.com/blevesearch/scorch_segment_api/v2 v2.2.9 // indirect
|
||||
github.com/blevesearch/scorch_segment_api/v2 v2.2.16 // indirect
|
||||
github.com/blevesearch/segment v0.9.1 // indirect
|
||||
github.com/blevesearch/snowballstem v0.9.0 // indirect
|
||||
github.com/blevesearch/upsidedown_store_api v1.0.2 // indirect
|
||||
|
@ -81,9 +80,9 @@ require (
|
|||
github.com/blevesearch/zapx/v12 v12.3.10 // indirect
|
||||
github.com/blevesearch/zapx/v13 v13.3.10 // indirect
|
||||
github.com/blevesearch/zapx/v14 v14.3.10 // indirect
|
||||
github.com/blevesearch/zapx/v15 v15.3.13 // indirect
|
||||
github.com/blevesearch/zapx/v16 v16.0.12 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/blevesearch/zapx/v15 v15.3.16 // indirect
|
||||
github.com/blevesearch/zapx/v16 v16.1.9-0.20241217210638-a0519e7caf3b // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/containerd/log v0.1.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/distribution/reference v0.6.0 // indirect
|
||||
|
@ -101,7 +100,6 @@ require (
|
|||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd // indirect
|
||||
github.com/h2non/filetype v1.1.3 // indirect
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
||||
github.com/hjson/hjson-go/v4 v4.4.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/juju/errors v1.0.0 // indirect
|
||||
|
@ -115,18 +113,19 @@ require (
|
|||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/morikuni/aec v1.0.0 // indirect
|
||||
github.com/mschoch/smat v0.2.0 // indirect
|
||||
github.com/nats-io/jwt/v2 v2.5.8 // indirect
|
||||
github.com/nats-io/nkeys v0.4.8 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/nats-io/jwt/v2 v2.7.3 // indirect
|
||||
github.com/nats-io/nkeys v0.4.9 // indirect
|
||||
github.com/nats-io/nuid v1.0.1 // indirect
|
||||
github.com/ncruces/go-strftime v0.1.9 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.11.0 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.0.3-0.20211202183452-c5a74bcca799 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/client_model v0.5.0 // indirect
|
||||
github.com/prometheus/common v0.48.0 // indirect
|
||||
github.com/prometheus/procfs v0.12.0 // indirect
|
||||
github.com/quic-go/quic-go v0.46.0 // indirect
|
||||
github.com/prometheus/client_model v0.6.1 // indirect
|
||||
github.com/prometheus/common v0.55.0 // indirect
|
||||
github.com/prometheus/procfs v0.15.1 // indirect
|
||||
github.com/quic-go/quic-go v0.48.2 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/rogpeppe/go-internal v1.13.1 // indirect
|
||||
github.com/rs/zerolog v1.29.1 // indirect
|
||||
|
@ -142,21 +141,18 @@ require (
|
|||
go.opentelemetry.io/otel/trace v1.32.0 // indirect
|
||||
go.uber.org/mock v0.4.0 // indirect
|
||||
golang.org/x/mod v0.19.0 // indirect
|
||||
golang.org/x/net v0.32.0 // indirect
|
||||
golang.org/x/sys v0.28.0 // indirect
|
||||
golang.org/x/net v0.33.0 // indirect
|
||||
golang.org/x/sys v0.29.0 // indirect
|
||||
golang.org/x/text v0.21.0 // indirect
|
||||
golang.org/x/time v0.8.0 // indirect
|
||||
golang.org/x/time v0.9.0 // indirect
|
||||
golang.org/x/tools v0.23.0 // indirect
|
||||
google.golang.org/protobuf v1.35.1 // indirect
|
||||
gopkg.in/macaroon.v2 v2.1.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
maunium.net/go/maulogger/v2 v2.4.1 // indirect
|
||||
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect
|
||||
modernc.org/libc v1.55.3 // indirect
|
||||
modernc.org/mathutil v1.6.0 // indirect
|
||||
modernc.org/memory v1.8.0 // indirect
|
||||
modernc.org/strutil v1.2.0 // indirect
|
||||
modernc.org/token v1.1.0 // indirect
|
||||
nhooyr.io/websocket v1.8.7 // indirect
|
||||
)
|
||||
|
||||
|
|
130
go.sum
130
go.sum
|
@ -1,6 +1,6 @@
|
|||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/Arceliar/ironwood v0.0.0-20241210120540-9deb08d9f8f9 h1:myI8fs7+Iw6g/ywvY9QNQOEzny51AklMz4sF0ErtTm8=
|
||||
github.com/Arceliar/ironwood v0.0.0-20241210120540-9deb08d9f8f9/go.mod h1:SrrElc3FFMpYCODSr11jWbLFeOM8WsY+DbDY/l2AXF0=
|
||||
github.com/Arceliar/ironwood v0.0.0-20241213013129-743fe2fccbd3 h1:d8N0z+udAnbU5PdjpLSNPTWlqeU/nnYsQ42B6+879aw=
|
||||
github.com/Arceliar/ironwood v0.0.0-20241213013129-743fe2fccbd3/go.mod h1:SrrElc3FFMpYCODSr11jWbLFeOM8WsY+DbDY/l2AXF0=
|
||||
github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d h1:UK9fsWbWqwIQkMCz1CP+v5pGbsGoWAw6g4AyvMpm1EM=
|
||||
github.com/Arceliar/phony v0.0.0-20220903101357-530938a4b13d/go.mod h1:BCnxhRf47C/dy/e/D2pmB8NkB3dQVIrkD98b220rx5Q=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
|
||||
|
@ -12,37 +12,37 @@ github.com/HdrHistogram/hdrhistogram-go v1.1.2 h1:5IcZpTvzydCQeHzK4Ef/D5rrSqwxob
|
|||
github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo=
|
||||
github.com/MFAshby/stdemuxerhook v1.0.0 h1:1XFGzakrsHMv76AeanPDL26NOgwjPl/OUxbGhJthwMc=
|
||||
github.com/MFAshby/stdemuxerhook v1.0.0/go.mod h1:nLMI9FUf9Hz98n+yAXsTMUR4RZQy28uCTLG1Fzvj/uY=
|
||||
github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc=
|
||||
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
||||
github.com/Masterminds/semver/v3 v3.3.1 h1:QtNSWtVZ3nBfk8mAOu/B6v7FMJ+NHTIgUPi7rj+4nv4=
|
||||
github.com/Masterminds/semver/v3 v3.3.1/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
|
||||
github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA=
|
||||
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
|
||||
github.com/RoaringBitmap/roaring v1.2.3 h1:yqreLINqIrX22ErkKI0vY47/ivtJr6n+kMhVOVmhWBY=
|
||||
github.com/RoaringBitmap/roaring v1.2.3/go.mod h1:plvDsJQpxOC5bw8LRteu/MLWHsHez/3y6cubLI4/1yE=
|
||||
github.com/RoaringBitmap/roaring v1.9.3 h1:t4EbC5qQwnisr5PrP9nt0IRhRTb9gMUgQF4t4S2OByM=
|
||||
github.com/RoaringBitmap/roaring v1.9.3/go.mod h1:6AXUsoIEzDTFFQCe1RbGA6uFONMhvejWj5rqITANK90=
|
||||
github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
|
||||
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/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
|
||||
github.com/bits-and-blooms/bitset v1.10.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
|
||||
github.com/bits-and-blooms/bitset v1.12.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
|
||||
github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE=
|
||||
github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
|
||||
github.com/bits-and-blooms/bloom/v3 v3.7.0 h1:VfknkqV4xI+PsaDIsoHueyxVDZrfvMn56jeWUzvzdls=
|
||||
github.com/bits-and-blooms/bloom/v3 v3.7.0/go.mod h1:VKlUSvp0lFIYqxJjzdnSsZEw4iHb1kOL2tfHTgyJBHg=
|
||||
github.com/blevesearch/bleve/v2 v2.4.0 h1:2xyg+Wv60CFHYccXc+moGxbL+8QKT/dZK09AewHgKsg=
|
||||
github.com/blevesearch/bleve/v2 v2.4.0/go.mod h1:IhQHoFAbHgWKYavb9rQgQEJJVMuY99cKdQ0wPpst2aY=
|
||||
github.com/blevesearch/bleve_index_api v1.1.6 h1:orkqDFCBuNU2oHW9hN2YEJmet+TE9orml3FCGbl1cKk=
|
||||
github.com/blevesearch/bleve_index_api v1.1.6/go.mod h1:PbcwjIcRmjhGbkS/lJCpfgVSMROV6TRubGGAODaK1W8=
|
||||
github.com/blevesearch/bleve/v2 v2.4.4 h1:RwwLGjUm54SwyyykbrZs4vc1qjzYic4ZnAnY9TwNl60=
|
||||
github.com/blevesearch/bleve/v2 v2.4.4/go.mod h1:fa2Eo6DP7JR+dMFpQe+WiZXINKSunh7WBtlDGbolKXk=
|
||||
github.com/blevesearch/bleve_index_api v1.1.12 h1:P4bw9/G/5rulOF7SJ9l4FsDoo7UFJ+5kexNy1RXfegY=
|
||||
github.com/blevesearch/bleve_index_api v1.1.12/go.mod h1:PbcwjIcRmjhGbkS/lJCpfgVSMROV6TRubGGAODaK1W8=
|
||||
github.com/blevesearch/geo v0.1.20 h1:paaSpu2Ewh/tn5DKn/FB5SzvH0EWupxHEIwbCk/QPqM=
|
||||
github.com/blevesearch/geo v0.1.20/go.mod h1:DVG2QjwHNMFmjo+ZgzrIq2sfCh6rIHzy9d9d0B59I6w=
|
||||
github.com/blevesearch/go-faiss v1.0.13 h1:zfFs7ZYD0NqXVSY37j0JZjZT1BhE9AE4peJfcx/NB4A=
|
||||
github.com/blevesearch/go-faiss v1.0.13/go.mod h1:jrxHrbl42X/RnDPI+wBoZU8joxxuRwedrxqswQ3xfU8=
|
||||
github.com/blevesearch/go-faiss v1.0.24 h1:K79IvKjoKHdi7FdiXEsAhxpMuns0x4fM0BO93bW5jLI=
|
||||
github.com/blevesearch/go-faiss v1.0.24/go.mod h1:OMGQwOaRRYxrmeNdMrXJPvVx8gBnvE5RYrr0BahNnkk=
|
||||
github.com/blevesearch/go-porterstemmer v1.0.3 h1:GtmsqID0aZdCSNiY8SkuPJ12pD4jI+DdXTAn4YRcHCo=
|
||||
github.com/blevesearch/go-porterstemmer v1.0.3/go.mod h1:angGc5Ht+k2xhJdZi511LtmxuEf0OVpvUUNrwmM1P7M=
|
||||
github.com/blevesearch/gtreap v0.1.1 h1:2JWigFrzDMR+42WGIN/V2p0cUvn4UP3C4Q5nmaZGW8Y=
|
||||
github.com/blevesearch/gtreap v0.1.1/go.mod h1:QaQyDRAT51sotthUWAH4Sj08awFSSWzgYICSZ3w0tYk=
|
||||
github.com/blevesearch/mmap-go v1.0.4 h1:OVhDhT5B/M1HNPpYPBKIEJaD0F3Si+CrEKULGCDPWmc=
|
||||
github.com/blevesearch/mmap-go v1.0.4/go.mod h1:EWmEAOmdAS9z/pi/+Toxu99DnsbhG1TIxUoRmJw/pSs=
|
||||
github.com/blevesearch/scorch_segment_api/v2 v2.2.9 h1:3nBaSBRFokjE4FtPW3eUDgcAu3KphBg1GP07zy/6Uyk=
|
||||
github.com/blevesearch/scorch_segment_api/v2 v2.2.9/go.mod h1:ckbeb7knyOOvAdZinn/ASbB7EA3HoagnJkmEV3J7+sg=
|
||||
github.com/blevesearch/scorch_segment_api/v2 v2.2.16 h1:uGvKVvG7zvSxCwcm4/ehBa9cCEuZVE+/zvrSl57QUVY=
|
||||
github.com/blevesearch/scorch_segment_api/v2 v2.2.16/go.mod h1:VF5oHVbIFTu+znY1v30GjSpT5+9YFs9dV2hjvuh34F0=
|
||||
github.com/blevesearch/segment v0.9.1 h1:+dThDy+Lvgj5JMxhmOVlgFfkUtZV2kw49xax4+jTfSU=
|
||||
github.com/blevesearch/segment v0.9.1/go.mod h1:zN21iLm7+GnBHWTao9I+Au/7MBiL8pPFtJBJTsk6kQw=
|
||||
github.com/blevesearch/snowballstem v0.9.0 h1:lMQ189YspGP6sXvZQ4WZ+MLawfV8wOmPoD/iWeNXm8s=
|
||||
|
@ -59,14 +59,14 @@ github.com/blevesearch/zapx/v13 v13.3.10 h1:0KY9tuxg06rXxOZHg3DwPJBjniSlqEgVpxIq
|
|||
github.com/blevesearch/zapx/v13 v13.3.10/go.mod h1:w2wjSDQ/WBVeEIvP0fvMJZAzDwqwIEzVPnCPrz93yAk=
|
||||
github.com/blevesearch/zapx/v14 v14.3.10 h1:SG6xlsL+W6YjhX5N3aEiL/2tcWh3DO75Bnz77pSwwKU=
|
||||
github.com/blevesearch/zapx/v14 v14.3.10/go.mod h1:qqyuR0u230jN1yMmE4FIAuCxmahRQEOehF78m6oTgns=
|
||||
github.com/blevesearch/zapx/v15 v15.3.13 h1:6EkfaZiPlAxqXz0neniq35my6S48QI94W/wyhnpDHHQ=
|
||||
github.com/blevesearch/zapx/v15 v15.3.13/go.mod h1:Turk/TNRKj9es7ZpKK95PS7f6D44Y7fAFy8F4LXQtGg=
|
||||
github.com/blevesearch/zapx/v16 v16.0.12 h1:Uccxvjmn+hQ6ywQP+wIiTpdq9LnAviGoryJOmGwAo/I=
|
||||
github.com/blevesearch/zapx/v16 v16.0.12/go.mod h1:MYnOshRfSm4C4drxx1LGRI+MVFByykJ2anDY1fxdk9Q=
|
||||
github.com/blevesearch/zapx/v15 v15.3.16 h1:Ct3rv7FUJPfPk99TI/OofdC+Kpb4IdyfdMH48sb+FmE=
|
||||
github.com/blevesearch/zapx/v15 v15.3.16/go.mod h1:Turk/TNRKj9es7ZpKK95PS7f6D44Y7fAFy8F4LXQtGg=
|
||||
github.com/blevesearch/zapx/v16 v16.1.9-0.20241217210638-a0519e7caf3b h1:ju9Az5YgrzCeK3M1QwvZIpxYhChkXp7/L0RhDYsxXoE=
|
||||
github.com/blevesearch/zapx/v16 v16.1.9-0.20241217210638-a0519e7caf3b/go.mod h1:BlrYNpOu4BvVRslmIG+rLtKhmjIaRhIbG8sb9scGTwI=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
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/codeclysm/extract v2.2.0+incompatible h1:q3wyckoA30bhUSiwdQezMqVhwd8+WGE64/GL//LtUhI=
|
||||
github.com/codeclysm/extract v2.2.0+incompatible/go.mod h1:2nhFMPHiU9At61hz+12bfrlpXSUrOnK+wR+KlGO4Uks=
|
||||
github.com/coder/websocket v1.8.12 h1:5bUXkEPPIbewrnkU8LTCLVaxi4N4J8ahufH2vlo4NAo=
|
||||
|
@ -197,8 +197,6 @@ github.com/h2non/filetype v1.1.3 h1:FKkx9QbD7HR/zjK1Ia5XiBsq9zdLi5Kf3zGyFTAFkGg=
|
|||
github.com/h2non/filetype v1.1.3/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY=
|
||||
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
|
||||
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
|
||||
github.com/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/hjson/hjson-go/v4 v4.4.0 h1:D/NPvqOCH6/eisTb5/ztuIS8GUvmpHaLOcNk1Bjr298=
|
||||
github.com/hjson/hjson-go/v4 v4.4.0/go.mod h1:KaYt3bTw3zhBjYqnXkYywcYctk0A2nxeEFTse3rH13E=
|
||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
|
@ -221,6 +219,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
|||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
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/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
||||
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
|
||||
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
|
||||
|
@ -266,14 +266,16 @@ github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
|||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||
github.com/mschoch/smat v0.2.0 h1:8imxQsjDm8yFEAVBe7azKmKSgzSkZXDuKkSq9374khM=
|
||||
github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOlotKw=
|
||||
github.com/nats-io/jwt/v2 v2.5.8 h1:uvdSzwWiEGWGXf+0Q+70qv6AQdvcvxrv9hPM0RiPamE=
|
||||
github.com/nats-io/jwt/v2 v2.5.8/go.mod h1:ZdWS1nZa6WMZfFwwgpEaqBV8EPGVgOTDHN/wTbz0Y5A=
|
||||
github.com/nats-io/nats-server/v2 v2.10.23 h1:jvfb9cEi5h8UG6HkZgJGdn9f1UPaX3Dohk0PohEekJI=
|
||||
github.com/nats-io/nats-server/v2 v2.10.23/go.mod h1:hMFnpDT2XUXsvHglABlFl/uroQCCOcW6X/0esW6GpBk=
|
||||
github.com/nats-io/nats.go v1.37.0 h1:07rauXbVnnJvv1gfIyghFEo6lUcYRY0WXc3x7x0vUxE=
|
||||
github.com/nats-io/nats.go v1.37.0/go.mod h1:Ubdu4Nh9exXdSz0RVWRFBbRfrbSxOYd26oF0wkWclB8=
|
||||
github.com/nats-io/nkeys v0.4.8 h1:+wee30071y3vCZAYRsnrmIPaOe47A/SkK/UBDPdIV70=
|
||||
github.com/nats-io/nkeys v0.4.8/go.mod h1:kqXRgRDPlGy7nGaEDMuYzmiJCIAAWDK0IMBtDmGD0nc=
|
||||
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/nats-io/jwt/v2 v2.7.3 h1:6bNPK+FXgBeAqdj4cYQ0F8ViHRbi7woQLq4W29nUAzE=
|
||||
github.com/nats-io/jwt/v2 v2.7.3/go.mod h1:GvkcbHhKquj3pkioy5put1wvPxs78UlZ7D/pY+BgZk4=
|
||||
github.com/nats-io/nats-server/v2 v2.10.25 h1:J0GWLDDXo5HId7ti/lTmBfs+lzhmu8RPkoKl0eSCqwc=
|
||||
github.com/nats-io/nats-server/v2 v2.10.25/go.mod h1:/YYYQO7cuoOBt+A7/8cVjuhWTaTUEAlZbJT+3sMAfFU=
|
||||
github.com/nats-io/nats.go v1.38.0 h1:A7P+g7Wjp4/NWqDOOP/K6hfhr54DvdDQUznt5JFg9XA=
|
||||
github.com/nats-io/nats.go v1.38.0/go.mod h1:IGUM++TwokGnXPs82/wCuiHS02/aKrdYUQkU8If6yjw=
|
||||
github.com/nats-io/nkeys v0.4.9 h1:qe9Faq2Gxwi6RZnZMXfmGMZkg3afLLOtrU+gDZJ35b0=
|
||||
github.com/nats-io/nkeys v0.4.9/go.mod h1:jcMqs+FLG+W5YO36OX6wFIFcmpdAns+w1Wm6D3I/evE=
|
||||
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
||||
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
||||
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
|
||||
|
@ -302,16 +304,16 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
|||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
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/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE=
|
||||
github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=
|
||||
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
|
||||
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
|
||||
github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE=
|
||||
github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
|
||||
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
|
||||
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
|
||||
github.com/quic-go/quic-go v0.46.0 h1:uuwLClEEyk1DNvchH8uCByQVjo3yKL9opKulExNDs7Y=
|
||||
github.com/quic-go/quic-go v0.46.0/go.mod h1:1dLehS7TIR64+vxGR70GDcatWTOtMX2PUtnKsjbTurI=
|
||||
github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
|
||||
github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
|
||||
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
|
||||
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
|
||||
github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc=
|
||||
github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8=
|
||||
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
|
||||
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
|
||||
github.com/quic-go/quic-go v0.48.2 h1:wsKXZPeGWpMpCGSWqOcqpW2wZYic/8T3aqiOID0/KWE=
|
||||
github.com/quic-go/quic-go v0.48.2/go.mod h1:yBgs3rWBOADpga7F+jJsb6Ybg1LSYiQvwWlLX+/6HMs=
|
||||
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/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
|
||||
|
@ -331,8 +333,8 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P
|
|||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
|
||||
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
|
@ -356,8 +358,8 @@ github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0
|
|||
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
|
||||
github.com/wlynxg/anet v0.0.5 h1:J3VJGi1gvo0JwZ/P1/Yc/8p63SoW98B5dHkYDmpgvvU=
|
||||
github.com/wlynxg/anet v0.0.5/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA=
|
||||
github.com/yggdrasil-network/yggdrasil-go v0.5.11 h1:w3IUePelcyBNOMOfYpvM15hPfy+AMGoCXAYgP1UBIug=
|
||||
github.com/yggdrasil-network/yggdrasil-go v0.5.11/go.mod h1:NDqLTXskwOvlsH+PvLpA3M6+2qCy1uKRX6eSnlCrhRc=
|
||||
github.com/yggdrasil-network/yggdrasil-go v0.5.12 h1:SaQ8d59JP+uFy+nOWXTx1ETM5r2uCfe1Gt/d+IodHJw=
|
||||
github.com/yggdrasil-network/yggdrasil-go v0.5.12/go.mod h1:u4DU6dpTfWmVs8r0WjW1T3UpGyeUh9vRrS8zngvncwM=
|
||||
github.com/yggdrasil-network/yggquic v0.0.0-20241212194307-0d495106021f h1:nqinj7N9gyDNKvSAoQK8OTg1RnEE5Bu/01oaC1TMT1o=
|
||||
github.com/yggdrasil-network/yggquic v0.0.0-20241212194307-0d495106021f/go.mod h1:TVCKOUWiXR9cAqr3eDpKvXkVkTph38xwk0wjcvfrtKI=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
|
@ -390,8 +392,8 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U
|
|||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
|
||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
|
||||
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
|
||||
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
|
@ -402,8 +404,8 @@ golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbR
|
|||
golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ=
|
||||
golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E=
|
||||
golang.org/x/image v0.23.0 h1:HseQ7c2OpPKTPVzNjG5fwJsOTCiiwS4QdsYi5XU6H68=
|
||||
golang.org/x/image v0.23.0/go.mod h1:wJJBTdLfCCf3tiHa1fNxpZmUI4mmoZvwMCPP0ddoNKY=
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mobile v0.0.0-20240520174638-fa72addaaa1b h1:WX7nnnLfCEXg+FmdYZPai2XuP3VqCP1HZVMST0n9DF0=
|
||||
golang.org/x/mobile v0.0.0-20240520174638-fa72addaaa1b/go.mod h1:EiXZlVfUTaAyySFVJb9rsODuiO+WXu8HrUuySb7nYFw=
|
||||
|
@ -418,8 +420,8 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL
|
|||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI=
|
||||
golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs=
|
||||
golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I=
|
||||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
|
@ -432,7 +434,6 @@ golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
|
@ -442,11 +443,11 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
|
||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
|
||||
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
|
||||
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
|
||||
golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
|
||||
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
|
@ -454,8 +455,8 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
|
||||
golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
|
||||
golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
|
@ -463,7 +464,6 @@ golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtn
|
|||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg=
|
||||
golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
@ -488,8 +488,6 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
|
|||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/h2non/bimg.v1 v1.1.9 h1:wZIUbeOnwr37Ta4aofhIv8OI8v4ujpjXC9mXnAGpQjM=
|
||||
gopkg.in/h2non/bimg.v1 v1.1.9/go.mod h1:PgsZL7dLwUbsGm1NYps320GxGgvQNTnecMCZqxV11So=
|
||||
gopkg.in/h2non/gock.v1 v1.1.2 h1:jBbHXgGBK/AoPVfJh5x4r/WxIrElvbLel8TCZkkZJoY=
|
||||
gopkg.in/h2non/gock.v1 v1.1.2/go.mod h1:n7UGz/ckNChHiK05rDoiC4MYSunEC/lyaUm2WWaDva0=
|
||||
gopkg.in/macaroon.v2 v2.1.0 h1:HZcsjBCzq9t0eBPMKqTN/uSN6JOm78ZJ2INbqcBQOUI=
|
||||
|
@ -501,8 +499,8 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
|||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o=
|
||||
gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g=
|
||||
gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
|
||||
gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
|
||||
maunium.net/go/maulogger/v2 v2.4.1 h1:N7zSdd0mZkB2m2JtFUsiGTQQAdP0YeFWT7YMc80yAL8=
|
||||
maunium.net/go/maulogger/v2 v2.4.1/go.mod h1:omPuYwYBILeVQobz8uO3XC8DIRuEb5rXYlQSuqrbCho=
|
||||
maunium.net/go/mautrix v0.15.1 h1:pmCtMjYRpd83+2UL+KTRFYQo5to0373yulimvLK+1k0=
|
||||
|
@ -515,8 +513,6 @@ modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE=
|
|||
modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ=
|
||||
modernc.org/gc/v2 v2.4.1 h1:9cNzOqPyMJBvrUipmynX0ZohMhcxPtMccYgGOJdOiBw=
|
||||
modernc.org/gc/v2 v2.4.1/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU=
|
||||
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 h1:5D53IMaUuA5InSeMu9eJtlQXS2NxAhyWQvkKEgXZhHI=
|
||||
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4=
|
||||
modernc.org/libc v1.55.3 h1:AzcW1mhlPNrRtjS5sS+eW2ISCgSOLLNyFzRh/V3Qj/U=
|
||||
modernc.org/libc v1.55.3/go.mod h1:qFXepLhz+JjFThQ4kzwzOjA/y/artDeg+pcYnY+Q83w=
|
||||
modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
|
||||
|
@ -527,8 +523,8 @@ modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
|
|||
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
|
||||
modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc=
|
||||
modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss=
|
||||
modernc.org/sqlite v1.34.2 h1:J9n76TPsfYYkFkZ9Uy1QphILYifiVEwwOT7yP5b++2Y=
|
||||
modernc.org/sqlite v1.34.2/go.mod h1:dnR723UrTtjKpoHCAMN0Q/gZ9MT4r+iRvIBb9umWFkU=
|
||||
modernc.org/sqlite v1.34.5 h1:Bb6SR13/fjp15jt70CL4f18JIN7p7dnMExd+UFnF15g=
|
||||
modernc.org/sqlite v1.34.5/go.mod h1:YLuNmX9NKs8wRNK2ko1LW1NGYcc9FkBO69JOt1AR9JE=
|
||||
modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA=
|
||||
modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0=
|
||||
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
||||
|
|
|
@ -216,13 +216,17 @@ func (t *TxnReq) processEDUs(ctx context.Context) {
|
|||
util.GetLogger(ctx).WithError(err).Debug("Failed to unmarshal typing event")
|
||||
continue
|
||||
}
|
||||
if _, serverName, err := gomatrixserverlib.SplitID('@', typingPayload.UserID); err != nil {
|
||||
_, serverName, err := gomatrixserverlib.SplitID('@', typingPayload.UserID)
|
||||
if err != nil {
|
||||
continue
|
||||
} else if serverName == t.ourServerName {
|
||||
continue
|
||||
} else if serverName != t.Origin {
|
||||
continue
|
||||
}
|
||||
if api.IsServerBannedFromRoom(ctx, t.rsAPI, typingPayload.RoomID, serverName) {
|
||||
continue
|
||||
}
|
||||
if err := t.producer.SendTyping(ctx, typingPayload.UserID, typingPayload.RoomID, typingPayload.Typing, 30*1000); err != nil {
|
||||
util.GetLogger(ctx).WithError(err).Error("Failed to send typing event to JetStream")
|
||||
}
|
||||
|
@ -278,6 +282,9 @@ func (t *TxnReq) processEDUs(ctx context.Context) {
|
|||
util.GetLogger(ctx).Debugf("Dropping receipt event where sender domain (%q) doesn't match origin (%q)", domain, t.Origin)
|
||||
continue
|
||||
}
|
||||
if api.IsServerBannedFromRoom(ctx, t.rsAPI, roomID, domain) {
|
||||
continue
|
||||
}
|
||||
if err := t.processReceiptEvent(ctx, userID, roomID, "m.read", mread.Data.TS, mread.EventIDs); err != nil {
|
||||
util.GetLogger(ctx).WithError(err).WithFields(logrus.Fields{
|
||||
"sender": t.Origin,
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
# Media API
|
||||
|
||||
This server is responsible for serving `/media` requests as per:
|
||||
|
||||
http://matrix.org/docs/spec/client_server/r0.2.0.html#id43
|
||||
|
||||
## Scaling libraries
|
||||
|
||||
### nfnt/resize (default)
|
||||
|
||||
Thumbnailing uses https://github.com/nfnt/resize by default which is a pure golang image scaling library relying on image codecs from the standard library. It is ISC-licensed.
|
||||
|
||||
It is multi-threaded and uses Lanczos3 so produces sharp images. Using Lanczos3 all the way makes it slower than some other approaches like bimg. (~845ms in total for pre-generating 32x32-crop, 96x96-crop, 320x240-scale, 640x480-scale and 800x600-scale from a given JPEG image on a given machine.)
|
||||
|
||||
See the sample below for image quality with nfnt/resize:
|
||||
|
||||

|
||||
|
||||
### bimg (uses libvips C library)
|
||||
|
||||
Alternatively one can use `go build -tags bimg` to use bimg from https://github.com/h2non/bimg (MIT-licensed) which uses libvips from https://github.com/jcupitt/libvips (LGPL v2.1+ -licensed). libvips is a C library and must be installed/built separately. See the github page for details. Also note that libvips in turn has dependencies with a selection of FOSS licenses.
|
||||
|
||||
bimg and libvips have significantly better performance than nfnt/resize but produce slightly less-sharp images. bimg uses a box filter for downscaling to within about 200% of the target scale and then uses Lanczos3 for the last bit. This is a much faster approach but comes at the expense of sharpness. (~295ms in total for pre-generating 32x32-crop, 96x96-crop, 320x240-scale, 640x480-scale and 800x600-scale from a given JPEG image on a given machine.)
|
||||
|
||||
See the sample below for image quality with bimg:
|
||||
|
||||

|
|
@ -7,6 +7,8 @@
|
|||
package mediaapi
|
||||
|
||||
import (
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/element-hq/dendrite/internal/httputil"
|
||||
"github.com/element-hq/dendrite/internal/sqlutil"
|
||||
"github.com/element-hq/dendrite/mediaapi/routing"
|
||||
|
@ -15,7 +17,6 @@ import (
|
|||
userapi "github.com/element-hq/dendrite/userapi/api"
|
||||
"github.com/matrix-org/gomatrixserverlib"
|
||||
"github.com/matrix-org/gomatrixserverlib/fclient"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// AddPublicRoutes sets up and registers HTTP handlers for the MediaAPI component.
|
||||
|
|
|
@ -14,10 +14,11 @@ import (
|
|||
"path/filepath"
|
||||
"sync"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/element-hq/dendrite/mediaapi/storage"
|
||||
"github.com/element-hq/dendrite/mediaapi/types"
|
||||
"github.com/element-hq/dendrite/setup/config"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type thumbnailFitness struct {
|
||||
|
@ -86,7 +87,7 @@ func getActiveThumbnailGeneration(dst types.Path, _ types.ThumbnailSize, activeT
|
|||
activeThumbnailGeneration.Lock()
|
||||
defer activeThumbnailGeneration.Unlock()
|
||||
if activeThumbnailGenerationResult, ok := activeThumbnailGeneration.PathToResult[string(dst)]; ok {
|
||||
logger.Info("Waiting for another goroutine to generate the thumbnail.")
|
||||
logger.Debugf("Waiting for another goroutine to generate the thumbnail %q", dst)
|
||||
|
||||
// NOTE: Wait unlocks and locks again internally. There is still a deferred Unlock() that will unlock this.
|
||||
activeThumbnailGenerationResult.Cond.Wait()
|
||||
|
@ -115,7 +116,7 @@ func broadcastGeneration(dst types.Path, activeThumbnailGeneration *types.Active
|
|||
activeThumbnailGeneration.Lock()
|
||||
defer activeThumbnailGeneration.Unlock()
|
||||
if activeThumbnailGenerationResult, ok := activeThumbnailGeneration.PathToResult[string(dst)]; ok {
|
||||
logger.Info("Signalling other goroutines waiting for this goroutine to generate the thumbnail.")
|
||||
logger.Debugf("Signalling other goroutines waiting for this goroutine to generate the thumbnail %q", dst)
|
||||
// Note: errorReturn is a named return value error that is signalled from here to waiting goroutines
|
||||
activeThumbnailGenerationResult.Err = errorReturn
|
||||
activeThumbnailGenerationResult.Cond.Broadcast()
|
||||
|
@ -136,7 +137,7 @@ func isThumbnailExists(
|
|||
config.Width, config.Height, config.ResizeMethod,
|
||||
)
|
||||
if err != nil {
|
||||
logger.Error("Failed to query database for thumbnail.")
|
||||
logger.Errorf("Failed to query database for thumbnail %q", dst)
|
||||
return false, err
|
||||
}
|
||||
if thumbnailMetadata != nil {
|
||||
|
|
|
@ -1,241 +0,0 @@
|
|||
// Copyright 2024 New Vector Ltd.
|
||||
// Copyright 2017 Vector Creations Ltd
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
//go:build bimg
|
||||
// +build bimg
|
||||
|
||||
package thumbnailer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/element-hq/dendrite/mediaapi/storage"
|
||||
"github.com/element-hq/dendrite/mediaapi/types"
|
||||
"github.com/element-hq/dendrite/setup/config"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"gopkg.in/h2non/bimg.v1"
|
||||
)
|
||||
|
||||
// GenerateThumbnails generates the configured thumbnail sizes for the source file
|
||||
func GenerateThumbnails(
|
||||
ctx context.Context,
|
||||
src types.Path,
|
||||
configs []config.ThumbnailSize,
|
||||
mediaMetadata *types.MediaMetadata,
|
||||
activeThumbnailGeneration *types.ActiveThumbnailGeneration,
|
||||
maxThumbnailGenerators int,
|
||||
db storage.Database,
|
||||
logger *log.Entry,
|
||||
) (busy bool, errorReturn error) {
|
||||
buffer, err := bimg.Read(string(src))
|
||||
if err != nil {
|
||||
logger.WithError(err).WithField("src", src).Error("Failed to read src file")
|
||||
return false, err
|
||||
}
|
||||
img := bimg.NewImage(buffer)
|
||||
for _, config := range configs {
|
||||
// Note: createThumbnail does locking based on activeThumbnailGeneration
|
||||
busy, err = createThumbnail(
|
||||
ctx, src, img, types.ThumbnailSize(config), mediaMetadata, activeThumbnailGeneration,
|
||||
maxThumbnailGenerators, db, logger,
|
||||
)
|
||||
if err != nil {
|
||||
logger.WithError(err).WithField("src", src).Error("Failed to generate thumbnails")
|
||||
return false, err
|
||||
}
|
||||
if busy {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// GenerateThumbnail generates the configured thumbnail size for the source file
|
||||
func GenerateThumbnail(
|
||||
ctx context.Context,
|
||||
src types.Path,
|
||||
config types.ThumbnailSize,
|
||||
mediaMetadata *types.MediaMetadata,
|
||||
activeThumbnailGeneration *types.ActiveThumbnailGeneration,
|
||||
maxThumbnailGenerators int,
|
||||
db storage.Database,
|
||||
logger *log.Entry,
|
||||
) (busy bool, errorReturn error) {
|
||||
buffer, err := bimg.Read(string(src))
|
||||
if err != nil {
|
||||
logger.WithError(err).WithFields(log.Fields{
|
||||
"src": src,
|
||||
}).Error("Failed to read src file")
|
||||
return false, err
|
||||
}
|
||||
img := bimg.NewImage(buffer)
|
||||
// Note: createThumbnail does locking based on activeThumbnailGeneration
|
||||
busy, err = createThumbnail(
|
||||
ctx, src, img, config, mediaMetadata, activeThumbnailGeneration,
|
||||
maxThumbnailGenerators, db, logger,
|
||||
)
|
||||
if err != nil {
|
||||
logger.WithError(err).WithFields(log.Fields{
|
||||
"src": src,
|
||||
}).Error("Failed to generate thumbnails")
|
||||
return false, err
|
||||
}
|
||||
if busy {
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// createThumbnail checks if the thumbnail exists, and if not, generates it
|
||||
// Thumbnail generation is only done once for each non-existing thumbnail.
|
||||
func createThumbnail(
|
||||
ctx context.Context,
|
||||
src types.Path,
|
||||
img *bimg.Image,
|
||||
config types.ThumbnailSize,
|
||||
mediaMetadata *types.MediaMetadata,
|
||||
activeThumbnailGeneration *types.ActiveThumbnailGeneration,
|
||||
maxThumbnailGenerators int,
|
||||
db storage.Database,
|
||||
logger *log.Entry,
|
||||
) (busy bool, errorReturn error) {
|
||||
logger = logger.WithFields(log.Fields{
|
||||
"Width": config.Width,
|
||||
"Height": config.Height,
|
||||
"ResizeMethod": config.ResizeMethod,
|
||||
})
|
||||
|
||||
// Check if request is larger than original
|
||||
if isLargerThanOriginal(config, img) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
dst := GetThumbnailPath(src, config)
|
||||
|
||||
// Note: getActiveThumbnailGeneration uses mutexes and conditions from activeThumbnailGeneration
|
||||
isActive, busy, err := getActiveThumbnailGeneration(dst, config, activeThumbnailGeneration, maxThumbnailGenerators, logger)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if busy {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
if isActive {
|
||||
// Note: This is an active request that MUST broadcastGeneration to wake up waiting goroutines!
|
||||
// Note: broadcastGeneration uses mutexes and conditions from activeThumbnailGeneration
|
||||
defer func() {
|
||||
// Note: errorReturn is the named return variable so we wrap this in a closure to re-evaluate the arguments at defer-time
|
||||
if err := recover(); err != nil {
|
||||
broadcastGeneration(dst, activeThumbnailGeneration, config, err.(error), logger)
|
||||
panic(err)
|
||||
}
|
||||
broadcastGeneration(dst, activeThumbnailGeneration, config, errorReturn, logger)
|
||||
}()
|
||||
}
|
||||
|
||||
exists, err := isThumbnailExists(ctx, dst, config, mediaMetadata, db, logger)
|
||||
if err != nil || exists {
|
||||
return false, err
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
width, height, err := resize(dst, img, config.Width, config.Height, config.ResizeMethod == "crop", logger)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
logger.WithFields(log.Fields{
|
||||
"ActualWidth": width,
|
||||
"ActualHeight": height,
|
||||
"processTime": time.Now().Sub(start),
|
||||
}).Info("Generated thumbnail")
|
||||
|
||||
stat, err := os.Stat(string(dst))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
thumbnailMetadata := &types.ThumbnailMetadata{
|
||||
MediaMetadata: &types.MediaMetadata{
|
||||
MediaID: mediaMetadata.MediaID,
|
||||
Origin: mediaMetadata.Origin,
|
||||
// Note: the code currently always creates a JPEG thumbnail
|
||||
ContentType: types.ContentType("image/jpeg"),
|
||||
FileSizeBytes: types.FileSizeBytes(stat.Size()),
|
||||
},
|
||||
ThumbnailSize: types.ThumbnailSize{
|
||||
Width: config.Width,
|
||||
Height: config.Height,
|
||||
ResizeMethod: config.ResizeMethod,
|
||||
},
|
||||
}
|
||||
|
||||
err = db.StoreThumbnail(ctx, thumbnailMetadata)
|
||||
if err != nil {
|
||||
logger.WithError(err).WithFields(log.Fields{
|
||||
"ActualWidth": width,
|
||||
"ActualHeight": height,
|
||||
}).Error("Failed to store thumbnail metadata in database.")
|
||||
return false, err
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func isLargerThanOriginal(config types.ThumbnailSize, img *bimg.Image) bool {
|
||||
imgSize, err := img.Size()
|
||||
if err == nil && config.Width >= imgSize.Width && config.Height >= imgSize.Height {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// resize scales an image to fit within the provided width and height
|
||||
// If the source aspect ratio is different to the target dimensions, one edge will be smaller than requested
|
||||
// If crop is set to true, the image will be scaled to fill the width and height with any excess being cropped off
|
||||
func resize(dst types.Path, inImage *bimg.Image, w, h int, crop bool, logger *log.Entry) (int, int, error) {
|
||||
inSize, err := inImage.Size()
|
||||
if err != nil {
|
||||
return -1, -1, err
|
||||
}
|
||||
|
||||
options := bimg.Options{
|
||||
Type: bimg.JPEG,
|
||||
Quality: 85,
|
||||
}
|
||||
if crop {
|
||||
options.Width = w
|
||||
options.Height = h
|
||||
options.Crop = true
|
||||
} else {
|
||||
inAR := float64(inSize.Width) / float64(inSize.Height)
|
||||
outAR := float64(w) / float64(h)
|
||||
|
||||
if inAR > outAR {
|
||||
// input has wider AR than requested output so use requested width and calculate height to match input AR
|
||||
options.Width = w
|
||||
options.Height = int(float64(w) / inAR)
|
||||
} else {
|
||||
// input has narrower AR than requested output so use requested height and calculate width to match input AR
|
||||
options.Width = int(float64(h) * inAR)
|
||||
options.Height = h
|
||||
}
|
||||
}
|
||||
|
||||
newImage, err := inImage.Process(options)
|
||||
if err != nil {
|
||||
return -1, -1, err
|
||||
}
|
||||
|
||||
if err = bimg.Write(string(dst), newImage); err != nil {
|
||||
logger.WithError(err).Error("Failed to resize image")
|
||||
return -1, -1, err
|
||||
}
|
||||
|
||||
return options.Width, options.Height, nil
|
||||
}
|
|
@ -4,9 +4,6 @@
|
|||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
|
||||
//go:build !bimg
|
||||
// +build !bimg
|
||||
|
||||
package thumbnailer
|
||||
|
||||
import (
|
||||
|
@ -20,18 +17,18 @@ import (
|
|||
|
||||
// Imported for png codec
|
||||
_ "image/png"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
// Imported for webp codec
|
||||
_ "golang.org/x/image/webp"
|
||||
|
||||
"os"
|
||||
"time"
|
||||
"github.com/nfnt/resize"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/element-hq/dendrite/mediaapi/storage"
|
||||
"github.com/element-hq/dendrite/mediaapi/types"
|
||||
"github.com/element-hq/dendrite/setup/config"
|
||||
"github.com/nfnt/resize"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// GenerateThumbnails generates the configured thumbnail sizes for the source file
|
||||
|
@ -191,7 +188,7 @@ func createThumbnail(
|
|||
"ActualWidth": width,
|
||||
"ActualHeight": height,
|
||||
"processTime": time.Since(start),
|
||||
}).Info("Generated thumbnail")
|
||||
}).Debugf("Generated thumbnail %q", dst)
|
||||
|
||||
stat, err := os.Stat(string(dst))
|
||||
if err != nil {
|
||||
|
|
|
@ -15,6 +15,8 @@ type JetStream struct {
|
|||
// The prefix to use for stream names for this homeserver - really only
|
||||
// useful if running more than one Dendrite on the same NATS deployment.
|
||||
TopicPrefix string `yaml:"topic_prefix"`
|
||||
// The JetStream domain, if needed.
|
||||
JetStreamDomain string `yaml:"js_domain"`
|
||||
// Keep all storage in memory. This is mostly useful for unit tests.
|
||||
InMemory bool `yaml:"in_memory"`
|
||||
// Disable logging. This is mostly useful for unit tests.
|
||||
|
|
|
@ -22,7 +22,7 @@ func JetStreamConsumer(
|
|||
f func(ctx context.Context, msgs []*nats.Msg) bool,
|
||||
opts ...nats.SubOpt,
|
||||
) error {
|
||||
defer func() {
|
||||
defer func(durable string) {
|
||||
// If there are existing consumers from before they were pull
|
||||
// consumers, we need to clean up the old push consumers. However,
|
||||
// in order to not affect the interest-based policies, we need to
|
||||
|
@ -33,86 +33,93 @@ func JetStreamConsumer(
|
|||
logrus.WithContext(ctx).Warnf("Failed to clean up old consumer %q", durable)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}(durable)
|
||||
|
||||
name := durable + "Pull"
|
||||
sub, err := js.PullSubscribe(subj, name, opts...)
|
||||
durable = durable + "Pull"
|
||||
sub, err := js.PullSubscribe(subj, durable, opts...)
|
||||
if err != nil {
|
||||
sentry.CaptureException(err)
|
||||
return fmt.Errorf("nats.SubscribeSync: %w", err)
|
||||
logrus.WithContext(ctx).WithError(err).Warnf("Failed to configure durable %q", durable)
|
||||
return err
|
||||
}
|
||||
go func() {
|
||||
for {
|
||||
// If the parent context has given up then there's no point in
|
||||
// carrying on doing anything, so stop the listener.
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
if err := sub.Unsubscribe(); err != nil {
|
||||
logrus.WithContext(ctx).Warnf("Failed to unsubscribe %q", durable)
|
||||
}
|
||||
return
|
||||
default:
|
||||
}
|
||||
// The context behaviour here is surprising — we supply a context
|
||||
// so that we can interrupt the fetch if we want, but NATS will still
|
||||
// enforce its own deadline (roughly 5 seconds by default). Therefore
|
||||
// it is our responsibility to check whether our context expired or
|
||||
// not when a context error is returned. Footguns. Footguns everywhere.
|
||||
msgs, err := sub.Fetch(batch, nats.Context(ctx))
|
||||
if err != nil {
|
||||
if err == context.Canceled || err == context.DeadlineExceeded {
|
||||
// Work out whether it was the JetStream context that expired
|
||||
// or whether it was our supplied context.
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
// The supplied context expired, so we want to stop the
|
||||
// consumer altogether.
|
||||
return
|
||||
default:
|
||||
// The JetStream context expired, so the fetch probably
|
||||
// just timed out and we should try again.
|
||||
continue
|
||||
}
|
||||
} else if errors.Is(err, nats.ErrConsumerDeleted) {
|
||||
// The consumer was deleted so stop.
|
||||
go jetStreamConsumerWorker(ctx, sub, subj, batch, f)
|
||||
return nil
|
||||
}
|
||||
|
||||
func jetStreamConsumerWorker(
|
||||
ctx context.Context, sub *nats.Subscription, subj string, batch int,
|
||||
f func(ctx context.Context, msgs []*nats.Msg) bool,
|
||||
) {
|
||||
for {
|
||||
// If the parent context has given up then there's no point in
|
||||
// carrying on doing anything, so stop the listener.
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
default:
|
||||
}
|
||||
// The context behaviour here is surprising — we supply a context
|
||||
// so that we can interrupt the fetch if we want, but NATS will still
|
||||
// enforce its own deadline (roughly 5 seconds by default). Therefore
|
||||
// it is our responsibility to check whether our context expired or
|
||||
// not when a context error is returned. Footguns. Footguns everywhere.
|
||||
msgs, err := sub.Fetch(batch, nats.Context(ctx))
|
||||
if err != nil {
|
||||
if err == context.Canceled || err == context.DeadlineExceeded {
|
||||
// Work out whether it was the JetStream context that expired
|
||||
// or whether it was our supplied context.
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
// The supplied context expired, so we want to stop the
|
||||
// consumer altogether.
|
||||
return
|
||||
} else {
|
||||
// Unfortunately, there's no ErrServerShutdown or similar, so we need to compare the string
|
||||
if err.Error() == "nats: Server Shutdown" {
|
||||
logrus.WithContext(ctx).Warn("nats server shutting down")
|
||||
return
|
||||
}
|
||||
// Something else went wrong, so we'll panic.
|
||||
sentry.CaptureException(err)
|
||||
logrus.WithContext(ctx).WithField("subject", subj).Fatal(err)
|
||||
}
|
||||
}
|
||||
if len(msgs) < 1 {
|
||||
continue
|
||||
}
|
||||
for _, msg := range msgs {
|
||||
if err = msg.InProgress(nats.Context(ctx)); err != nil {
|
||||
logrus.WithContext(ctx).WithField("subject", subj).Warn(fmt.Errorf("msg.InProgress: %w", err))
|
||||
sentry.CaptureException(err)
|
||||
default:
|
||||
// The JetStream context expired, so the fetch probably
|
||||
// just timed out and we should try again.
|
||||
continue
|
||||
}
|
||||
}
|
||||
if f(ctx, msgs) {
|
||||
for _, msg := range msgs {
|
||||
if err = msg.AckSync(nats.Context(ctx)); err != nil {
|
||||
logrus.WithContext(ctx).WithField("subject", subj).Warn(fmt.Errorf("msg.AckSync: %w", err))
|
||||
sentry.CaptureException(err)
|
||||
}
|
||||
}
|
||||
} else if errors.Is(err, nats.ErrTimeout) {
|
||||
// Pull request was invalidated, try again.
|
||||
continue
|
||||
} else if errors.Is(err, nats.ErrConsumerLeadershipChanged) {
|
||||
// Leadership changed so pending pull requests became invalidated,
|
||||
// just try again.
|
||||
continue
|
||||
} else if err.Error() == "nats: Server Shutdown" {
|
||||
// The server is shutting down, but we'll rely on reconnect
|
||||
// behaviour to try and either connect us to another node (if
|
||||
// clustered) or to reconnect when the server comes back up.
|
||||
continue
|
||||
} else {
|
||||
for _, msg := range msgs {
|
||||
if err = msg.Nak(nats.Context(ctx)); err != nil {
|
||||
logrus.WithContext(ctx).WithField("subject", subj).Warn(fmt.Errorf("msg.Nak: %w", err))
|
||||
sentry.CaptureException(err)
|
||||
}
|
||||
// Something else went wrong.
|
||||
logrus.WithContext(ctx).WithField("subject", subj).WithError(err).Warn("Error on pull subscriber fetch")
|
||||
return
|
||||
}
|
||||
}
|
||||
if len(msgs) < 1 {
|
||||
continue
|
||||
}
|
||||
for _, msg := range msgs {
|
||||
if err = msg.InProgress(nats.Context(ctx)); err != nil {
|
||||
logrus.WithContext(ctx).WithField("subject", subj).Warn(fmt.Errorf("msg.InProgress: %w", err))
|
||||
sentry.CaptureException(err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
if f(ctx, msgs) {
|
||||
for _, msg := range msgs {
|
||||
if err = msg.AckSync(nats.Context(ctx)); err != nil {
|
||||
logrus.WithContext(ctx).WithField("subject", subj).Warn(fmt.Errorf("msg.AckSync: %w", err))
|
||||
sentry.CaptureException(err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for _, msg := range msgs {
|
||||
if err = msg.Nak(nats.Context(ctx)); err != nil {
|
||||
logrus.WithContext(ctx).WithField("subject", subj).Warn(fmt.Errorf("msg.Nak: %w", err))
|
||||
sentry.CaptureException(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/getsentry/sentry-go"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/element-hq/dendrite/setup/config"
|
||||
|
@ -36,17 +35,20 @@ func DeleteAllStreams(js natsclient.JetStreamContext, cfg *config.JetStream) {
|
|||
func (s *NATSInstance) Prepare(process *process.ProcessContext, cfg *config.JetStream) (natsclient.JetStreamContext, *natsclient.Conn) {
|
||||
natsLock.Lock()
|
||||
defer natsLock.Unlock()
|
||||
// check if we need an in-process NATS Server
|
||||
if len(cfg.Addresses) != 0 {
|
||||
// reuse existing connections
|
||||
if s.nc != nil {
|
||||
return s.js, s.nc
|
||||
}
|
||||
var err error
|
||||
|
||||
// If an existing connection exists, return it.
|
||||
if s.nc != nil && s.js != nil {
|
||||
return s.js, s.nc
|
||||
}
|
||||
|
||||
// For connecting to an external NATS server.
|
||||
if len(cfg.Addresses) > 0 {
|
||||
s.js, s.nc = setupNATS(process, cfg, nil)
|
||||
return s.js, s.nc
|
||||
}
|
||||
if s.Server == nil {
|
||||
var err error
|
||||
|
||||
if len(cfg.Addresses) == 0 && s.Server == nil {
|
||||
opts := &natsserver.Options{
|
||||
ServerName: "monolith",
|
||||
DontListen: true,
|
||||
|
@ -58,46 +60,61 @@ func (s *NATSInstance) Prepare(process *process.ProcessContext, cfg *config.JetS
|
|||
NoLog: cfg.NoLog,
|
||||
SyncAlways: true,
|
||||
}
|
||||
s.Server, err = natsserver.NewServer(opts)
|
||||
if err != nil {
|
||||
if s.Server, err = natsserver.NewServer(opts); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if !cfg.NoLog {
|
||||
s.SetLogger(NewLogAdapter(), opts.Debug, opts.Trace)
|
||||
}
|
||||
go func() {
|
||||
process.ComponentStarted()
|
||||
s.Start()
|
||||
}()
|
||||
process.ComponentStarted()
|
||||
go s.Start()
|
||||
go func() {
|
||||
<-process.WaitForShutdown()
|
||||
s.Shutdown()
|
||||
s.WaitForShutdown()
|
||||
process.ComponentFinished()
|
||||
}()
|
||||
if !s.ReadyForConnections(time.Second * 60) {
|
||||
logrus.Fatalln("NATS did not start in time, shutting down")
|
||||
process.ShutdownDendrite()
|
||||
s.Shutdown()
|
||||
s.WaitForShutdown()
|
||||
process.ComponentFinished()
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
if !s.ReadyForConnections(time.Second * 60) {
|
||||
logrus.Fatalln("NATS did not start in time")
|
||||
}
|
||||
// reuse existing connections
|
||||
if s.nc != nil {
|
||||
return s.js, s.nc
|
||||
}
|
||||
nc, err := natsclient.Connect("", natsclient.InProcessServer(s))
|
||||
if err != nil {
|
||||
|
||||
// No existing process connection, create a new one.
|
||||
if s.nc, err = natsclient.Connect("", natsclient.InProcessServer(s.Server)); err != nil {
|
||||
logrus.Fatalln("Failed to create NATS client")
|
||||
}
|
||||
js, _ := setupNATS(process, cfg, nc)
|
||||
s.js = js
|
||||
s.nc = nc
|
||||
return js, nc
|
||||
s.js, s.nc = setupNATS(process, cfg, s.nc)
|
||||
return s.js, s.nc
|
||||
}
|
||||
|
||||
// nolint:gocyclo
|
||||
func setupNATS(process *process.ProcessContext, cfg *config.JetStream, nc *natsclient.Conn) (natsclient.JetStreamContext, *natsclient.Conn) {
|
||||
jsOpts := []natsclient.JSOpt{}
|
||||
if cfg.JetStreamDomain != "" {
|
||||
jsOpts = append(jsOpts, natsclient.Domain(cfg.JetStreamDomain))
|
||||
}
|
||||
|
||||
if nc == nil {
|
||||
var err error
|
||||
opts := []natsclient.Option{}
|
||||
opts := []natsclient.Option{
|
||||
natsclient.Name("Dendrite"),
|
||||
natsclient.MaxReconnects(-1), // Try forever
|
||||
natsclient.ReconnectJitter(time.Second, time.Second),
|
||||
natsclient.ReconnectWait(time.Second * 10),
|
||||
natsclient.ReconnectHandler(func(c *natsclient.Conn) {
|
||||
js, jerr := c.JetStream(jsOpts...)
|
||||
if jerr != nil {
|
||||
logrus.WithError(jerr).Panic("Unable to get JetStream context in reconnect handler")
|
||||
return
|
||||
}
|
||||
checkAndConfigureStreams(process, cfg, js)
|
||||
}),
|
||||
}
|
||||
if cfg.DisableTLSValidation {
|
||||
opts = append(opts, natsclient.Secure(&tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
|
@ -113,15 +130,19 @@ func setupNATS(process *process.ProcessContext, cfg *config.JetStream, nc *natsc
|
|||
}
|
||||
}
|
||||
|
||||
s, err := nc.JetStream()
|
||||
js, err := nc.JetStream(jsOpts...)
|
||||
if err != nil {
|
||||
logrus.WithError(err).Panic("Unable to get JetStream context")
|
||||
return nil, nil
|
||||
}
|
||||
checkAndConfigureStreams(process, cfg, js)
|
||||
return js, nc
|
||||
}
|
||||
|
||||
func checkAndConfigureStreams(process *process.ProcessContext, cfg *config.JetStream, js natsclient.JetStreamContext) {
|
||||
for _, stream := range streams { // streams are defined in streams.go
|
||||
name := cfg.Prefixed(stream.Name)
|
||||
info, err := s.StreamInfo(name)
|
||||
info, err := js.StreamInfo(name)
|
||||
if err != nil && err != natsclient.ErrStreamNotFound {
|
||||
logrus.WithError(err).Fatal("Unable to get stream info")
|
||||
}
|
||||
|
@ -153,11 +174,11 @@ func setupNATS(process *process.ProcessContext, cfg *config.JetStream, nc *natsc
|
|||
case info.Config.MaxAge != stream.MaxAge:
|
||||
// Try updating the stream first, as many things can be updated
|
||||
// non-destructively.
|
||||
if info, err = s.UpdateStream(stream); err != nil {
|
||||
if info, err = js.UpdateStream(stream); err != nil {
|
||||
logrus.WithError(err).Warnf("Unable to update stream %q, recreating...", name)
|
||||
// We failed to update the stream, this is a last attempt to get
|
||||
// things working but may result in data loss.
|
||||
if err = s.DeleteStream(name); err != nil {
|
||||
if err = js.DeleteStream(name); err != nil {
|
||||
logrus.WithError(err).Fatalf("Unable to delete stream %q", name)
|
||||
}
|
||||
info = nil
|
||||
|
@ -176,7 +197,7 @@ func setupNATS(process *process.ProcessContext, cfg *config.JetStream, nc *natsc
|
|||
namespaced := *stream
|
||||
namespaced.Name = name
|
||||
namespaced.Subjects = subjects
|
||||
if _, err = s.AddStream(&namespaced); err != nil {
|
||||
if _, err = js.AddStream(&namespaced); err != nil {
|
||||
logger := logrus.WithError(err).WithFields(logrus.Fields{
|
||||
"stream": namespaced.Name,
|
||||
"subjects": namespaced.Subjects,
|
||||
|
@ -193,10 +214,9 @@ func setupNATS(process *process.ProcessContext, cfg *config.JetStream, nc *natsc
|
|||
// we can't recover anything that was queued on the disk but we
|
||||
// will still be able to start and run hopefully in the meantime.
|
||||
logger.WithError(err).Error("Unable to add stream")
|
||||
sentry.CaptureException(fmt.Errorf("Unable to add stream %q: %w", namespaced.Name, err))
|
||||
|
||||
namespaced.Storage = natsclient.MemoryStorage
|
||||
if _, err = s.AddStream(&namespaced); err != nil {
|
||||
if _, err = js.AddStream(&namespaced); err != nil {
|
||||
// We tried to add the stream in-memory instead but something
|
||||
// went wrong. That's an unrecoverable situation so we will
|
||||
// give up at this point.
|
||||
|
@ -208,7 +228,6 @@ func setupNATS(process *process.ProcessContext, cfg *config.JetStream, nc *natsc
|
|||
// disk will be left alone, but our ability to recover from a
|
||||
// future crash will be limited. Yell about it.
|
||||
err := fmt.Errorf("Stream %q is running in-memory; this may be due to data corruption in the JetStream storage directory", namespaced.Name)
|
||||
sentry.CaptureException(err)
|
||||
process.Degraded(err)
|
||||
}
|
||||
}
|
||||
|
@ -229,15 +248,13 @@ func setupNATS(process *process.ProcessContext, cfg *config.JetStream, nc *natsc
|
|||
streamName := cfg.Matrix.JetStream.Prefixed(stream)
|
||||
for _, consumer := range consumers {
|
||||
consumerName := cfg.Matrix.JetStream.Prefixed(consumer) + "Pull"
|
||||
consumerInfo, err := s.ConsumerInfo(streamName, consumerName)
|
||||
consumerInfo, err := js.ConsumerInfo(streamName, consumerName)
|
||||
if err != nil || consumerInfo == nil {
|
||||
continue
|
||||
}
|
||||
if err = s.DeleteConsumer(streamName, consumerName); err != nil {
|
||||
if err = js.DeleteConsumer(streamName, consumerName); err != nil {
|
||||
logrus.WithError(err).Errorf("Unable to clean up old consumer %q for stream %q", consumer, stream)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return s, nc
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue