mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2025-10-29 07:22:24 -05:00
Compare commits
212 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5fd52369c9 | ||
|
|
baf2c54730 | ||
|
|
b012a81f66 | ||
|
|
c6044d0142 | ||
|
|
03fc6eaf39 | ||
|
|
5b95636993 | ||
|
|
259fa1ffac | ||
|
|
57cb4fe748 | ||
|
|
ff950e94bb | ||
|
|
e7cd8bb43e | ||
|
|
bd1c43d55e | ||
|
|
dfdf06e4ad | ||
|
|
3db2d42247 | ||
|
|
121677754c | ||
|
|
602022701b | ||
|
|
82216281ce | ||
|
|
6607e1c944 | ||
|
|
e81bcb5171 | ||
|
|
6801ce299a | ||
|
|
247733aef4 | ||
|
|
5533fbc1f8 | ||
|
|
11f39bead0 | ||
|
|
0a32fc0980 | ||
|
|
10b8d270f2 | ||
|
|
882d07db5f | ||
|
|
d9ee6cddea | ||
|
|
f9cb086c53 | ||
|
|
754b7be9cf | ||
|
|
33fed81a8d | ||
|
|
c949b9f2d1 | ||
|
|
a6429b5410 | ||
|
|
9b7db51436 | ||
|
|
4b6c65fc15 | ||
|
|
19784e381b | ||
|
|
ed3035699f | ||
|
|
78defcd916 | ||
|
|
5a54e7156b | ||
|
|
9cb177cd8a | ||
|
|
3efb338ca9 | ||
|
|
380bfd0289 | ||
|
|
383e41e3e5 | ||
|
|
a79f83cbde | ||
|
|
8c619d51b5 | ||
|
|
1144ac037f | ||
|
|
3fad524089 | ||
|
|
a6bb45e5e4 | ||
|
|
e9b7e977a5 | ||
|
|
12e3cb71c1 | ||
|
|
8e76c9ab57 | ||
|
|
fb2ef90ec5 | ||
|
|
7f8cb204cd | ||
|
|
1edc0f7b3c | ||
|
|
660cf2c94c | ||
|
|
cead741c16 | ||
|
|
e565855540 | ||
|
|
67100809b3 | ||
|
|
7af9117e0d | ||
|
|
96c05a90a2 | ||
|
|
f301bd5abf | ||
|
|
7e45168d33 | ||
|
|
ac032ff6ab | ||
|
|
e3dfd88893 | ||
|
|
30cd1cd9eb | ||
|
|
c00cad2ceb | ||
|
|
eb60081985 | ||
|
|
a4b54aa935 | ||
|
|
94cbe1120e | ||
|
|
a97a15fd10 | ||
|
|
352353ce7a | ||
|
|
dcfc9b7885 | ||
|
|
c8a4ce9a88 | ||
|
|
07f61a13de | ||
|
|
66e1ec14aa | ||
|
|
79914bdbf7 | ||
|
|
bf46b87343 | ||
|
|
7a5144b4cc | ||
|
|
4f2aa792b3 | ||
|
|
8b0ea56027 | ||
|
|
7712885038 | ||
|
|
5fbaf5b7be | ||
|
|
39b11dbfb6 | ||
|
|
700bd69828 | ||
|
|
996da6e029 | ||
|
|
9d5af6c3dc | ||
|
|
56d1248a85 | ||
|
|
80191348ba | ||
|
|
38ff88f006 | ||
|
|
bfc8c31e5f | ||
|
|
29d481d769 | ||
|
|
8b615391e5 | ||
|
|
b1c0eca1d8 | ||
|
|
7d50aec6dc | ||
|
|
a9b2d4ee35 | ||
|
|
76e7fd62d2 | ||
|
|
726584287a | ||
|
|
5717ce6f3c | ||
|
|
fc074d2357 | ||
|
|
933a9091eb | ||
|
|
e87681d433 | ||
|
|
d7f967cbb5 | ||
|
|
1dc79c9586 | ||
|
|
4ad17788cd | ||
|
|
d3f2c2c765 | ||
|
|
153f6feecd | ||
|
|
37f9a9fa94 | ||
|
|
3f1c3c0dac | ||
|
|
54a0af34fb | ||
|
|
bd4fe7081e | ||
|
|
01e3765268 | ||
|
|
32738d83a8 | ||
|
|
b13a6437ff | ||
|
|
77eddea3af | ||
|
|
a37dd59d1f | ||
|
|
118d4e4d03 | ||
|
|
43f1c6d872 | ||
|
|
be6d80c020 | ||
|
|
6d511d696b | ||
|
|
5097e6d278 | ||
|
|
ee180a2359 | ||
|
|
025ca487cf | ||
|
|
faed35c938 | ||
|
|
a82d574acc | ||
|
|
3b46eb6a9e | ||
|
|
d9e8d844e1 | ||
|
|
0e698a49fb | ||
|
|
3ff6f6e421 | ||
|
|
2c73bb3602 | ||
|
|
6c1b674278 | ||
|
|
f3c4ea0106 | ||
|
|
143febb318 | ||
|
|
311d9a1697 | ||
|
|
fd64a1e264 | ||
|
|
326e04283a | ||
|
|
b6ff55662e | ||
|
|
20aad9be0f | ||
|
|
00e58c60cd | ||
|
|
d5c9c4adc1 | ||
|
|
ec4d4d0115 | ||
|
|
3a29a59e55 | ||
|
|
3cff4b2d7d | ||
|
|
cfe6336b6e | ||
|
|
3ba49aed45 | ||
|
|
ca12742a7a | ||
|
|
5925644ad3 | ||
|
|
3fedff3a5a | ||
|
|
d3c67ad642 | ||
|
|
8480a75808 | ||
|
|
6b6946f42a | ||
|
|
61137e6c41 | ||
|
|
7bd4bed558 | ||
|
|
d2f13e7564 | ||
|
|
8c1511a494 | ||
|
|
cfa83fb44c | ||
|
|
700b7eaab7 | ||
|
|
4c96e2571d | ||
|
|
1f0c261fd2 | ||
|
|
7e105f98ed | ||
|
|
b2a469d6b6 | ||
|
|
6acf56cde9 | ||
|
|
7d74548a91 | ||
|
|
ebf64eb0db | ||
|
|
8264b63337 | ||
|
|
e464de1322 | ||
|
|
cf93d3af0a | ||
|
|
57fc267b5c | ||
|
|
90a5425fe9 | ||
|
|
4a6b357501 | ||
|
|
73aa62581e | ||
|
|
ecbdc4227b | ||
|
|
ad71066973 | ||
|
|
83184da6e7 | ||
|
|
89df9f3b21 | ||
|
|
f3f1854359 | ||
|
|
056c67f396 | ||
|
|
211192c482 | ||
|
|
c15002d76e | ||
|
|
bad427e7f0 | ||
|
|
4d6408015b | ||
|
|
6c87918635 | ||
|
|
bf10ca0203 | ||
|
|
1480f22aea | ||
|
|
1bad36b637 | ||
|
|
90f5004db0 | ||
|
|
ac01652de9 | ||
|
|
ef0f8a55c6 | ||
|
|
31628019fe | ||
|
|
14f15b321b | ||
|
|
4fe5dfae56 | ||
|
|
efc79528d2 | ||
|
|
d8c4d9fc5a | ||
|
|
2b82fa7481 | ||
|
|
2d91eed4ed | ||
|
|
4c8ff7d1b2 | ||
|
|
afb4279d27 | ||
|
|
436765a6a2 | ||
|
|
457ca3c9d3 | ||
|
|
7dae3364fa | ||
|
|
8bc0b06105 | ||
|
|
d3e9a9e563 | ||
|
|
d5c3f44bb9 | ||
|
|
9b92175239 | ||
|
|
93c3c153f0 | ||
|
|
5bfccdad3c | ||
|
|
346962beb5 | ||
|
|
ab10266bff | ||
|
|
152bcb43b6 | ||
|
|
85eb192669 |
||
|
|
f7323c065a |
||
|
|
6a6a499333 |
||
|
|
ffde1b150f |
||
|
|
68ed7aba25 |
||
|
|
768ee70b88 |
2402 changed files with 398323 additions and 151979 deletions
2
.github/README.md
vendored
2
.github/README.md
vendored
|
|
@ -1,3 +1,5 @@
|
|||
# GoToSocial
|
||||
|
||||
This is a mirror. You can find us on https://codeberg.org/superseriousbusiness/gotosocial.
|
||||
|
||||
We will **stop mirroring** to Github after 0.20 is released. This will likely be sometime **in August**.
|
||||
|
|
|
|||
7
.gitignore
vendored
7
.gitignore
vendored
|
|
@ -4,6 +4,10 @@
|
|||
# exclude built documentation, since readthedocs will build it for us anyway
|
||||
/docs/_build
|
||||
|
||||
# exclude kim's commonly used
|
||||
# test stdout file location
|
||||
test.out
|
||||
|
||||
# exclude coverage report
|
||||
cp.out
|
||||
|
||||
|
|
@ -19,6 +23,9 @@ dist/
|
|||
# exclude the copy of swagger.yaml moved into assets during packaging
|
||||
web/assets/swagger.yaml
|
||||
|
||||
# exclude the copy of all_licenses.txt moved into assets during packaging
|
||||
web/assets/all_licenses.txt
|
||||
|
||||
# exludes docker-volume from exemple/docker-compose
|
||||
example/docker-compose/docker-volume
|
||||
|
||||
|
|
|
|||
151
.golangci.yml
151
.golangci.yml
|
|
@ -4,91 +4,98 @@
|
|||
#
|
||||
# For GoToSocial we mostly take the default linters, but we add a few to catch style issues as well.
|
||||
|
||||
version: "2"
|
||||
# options for analysis running
|
||||
run:
|
||||
# include test files or not, default is true
|
||||
tests: false
|
||||
# timeout for analysis, e.g. 30s, 5m, default is 1m
|
||||
timeout: 5m
|
||||
|
||||
linters:
|
||||
# enable some extra linters, see here for the list: https://golangci-lint.run/usage/linters/
|
||||
enable:
|
||||
- gocritic
|
||||
- gofmt
|
||||
- goheader
|
||||
- gosec
|
||||
- nilerr
|
||||
- revive
|
||||
# https://golangci-lint.run/usage/linters/#linters-configuration
|
||||
settings:
|
||||
# https://golangci-lint.run/usage/linters/#goheader
|
||||
goheader:
|
||||
template: |-
|
||||
GoToSocial
|
||||
Copyright (C) GoToSocial Authors admin@gotosocial.org
|
||||
SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
# https://golangci-lint.run/usage/linters/#linters-configuration
|
||||
linters-settings:
|
||||
# https://golangci-lint.run/usage/linters/#goheader
|
||||
goheader:
|
||||
template: |-
|
||||
GoToSocial
|
||||
Copyright (C) GoToSocial Authors admin@gotosocial.org
|
||||
SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it 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.
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it 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.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
# https://golangci-lint.run/usage/linters/#govet
|
||||
govet:
|
||||
disable:
|
||||
- composites
|
||||
# https://golangci-lint.run/usage/linters/#revive
|
||||
revive:
|
||||
rules:
|
||||
# Enable most default rules.
|
||||
# See: https://github.com/mgechev/revive/blob/master/defaults.toml
|
||||
- name: blank-imports
|
||||
- name: context-as-argument
|
||||
- name: context-keys-type
|
||||
- name: dot-imports
|
||||
- name: error-naming
|
||||
- name: error-return
|
||||
- name: error-strings
|
||||
- name: exported
|
||||
- name: if-return
|
||||
- name: increment-decrement
|
||||
- name: var-naming
|
||||
- name: var-declaration
|
||||
- name: package-comments
|
||||
- name: range
|
||||
- name: receiver-naming
|
||||
- name: time-naming
|
||||
- name: unexported-return
|
||||
- name: indent-error-flow
|
||||
- name: errorf
|
||||
- name: empty-block
|
||||
- name: superfluous-else
|
||||
- name: unreachable-code
|
||||
# Disable below rules.
|
||||
- name: redefines-builtin-id
|
||||
disabled: true # This one is just annoying.
|
||||
- name: unused-parameter
|
||||
disabled: true # We often pass parameters to fulfil interfaces.
|
||||
# https://golangci-lint.run/usage/linters/#staticcheck
|
||||
staticcheck:
|
||||
# Enable all checks, but disable SA1012: nil context passing.
|
||||
# See: https://staticcheck.io/docs/configuration/options/#checks
|
||||
checks: ["all", "-SA1012"]
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
# https://golangci-lint.run/usage/linters/#govet
|
||||
govet:
|
||||
disable:
|
||||
- composites
|
||||
# https://golangci-lint.run/usage/linters/#revive
|
||||
revive:
|
||||
rules:
|
||||
# Enable most default rules.
|
||||
# See: https://github.com/mgechev/revive/blob/master/defaults.toml
|
||||
- name: blank-imports
|
||||
- name: context-as-argument
|
||||
- name: context-keys-type
|
||||
- name: dot-imports
|
||||
- name: error-naming
|
||||
- name: error-return
|
||||
- name: error-strings
|
||||
- name: exported
|
||||
- name: if-return
|
||||
- name: increment-decrement
|
||||
- name: var-naming
|
||||
- name: var-declaration
|
||||
- name: package-comments
|
||||
- name: range
|
||||
- name: receiver-naming
|
||||
- name: time-naming
|
||||
- name: unexported-return
|
||||
- name: indent-error-flow
|
||||
- name: errorf
|
||||
- name: empty-block
|
||||
- name: superfluous-else
|
||||
- name: unreachable-code
|
||||
# Disable below rules.
|
||||
- name: redefines-builtin-id
|
||||
disabled: true
|
||||
- name: unused-parameter
|
||||
disabled: true
|
||||
- name: var-naming
|
||||
arguments:
|
||||
- []
|
||||
- []
|
||||
- - skip-package-name-checks: true
|
||||
|
||||
issues:
|
||||
exclude-rules:
|
||||
# Exclude VSCode custom folding region comments in files that use them.
|
||||
# Already fixed in go-critic and can be removed next time go-critic is updated.
|
||||
- linters:
|
||||
- gocritic
|
||||
path: internal/db/filter.go
|
||||
text: 'commentFormatting: put a space between `//` and comment text'
|
||||
# https://golangci-lint.run/usage/linters/#staticcheck
|
||||
staticcheck:
|
||||
# Enable all checks, but disable SA1012: nil context passing.
|
||||
# See: https://staticcheck.io/docs/configuration/options/#checks
|
||||
checks:
|
||||
- SA*
|
||||
- -SA1012
|
||||
exclusions:
|
||||
generated: lax
|
||||
presets:
|
||||
- comments
|
||||
- common-false-positives
|
||||
- legacy
|
||||
- std-error-handling
|
||||
formatters:
|
||||
enable:
|
||||
- gofmt
|
||||
exclusions:
|
||||
generated: lax
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ before:
|
|||
- yarn --cwd ./web/source install
|
||||
- yarn --cwd ./web/source ts-patch install # https://typia.io/docs/setup/#manual-setup
|
||||
- yarn --cwd ./web/source build
|
||||
# Bundle all licenses into web/assets/all_licenses.txt
|
||||
- ./scripts/bundle_licenses.sh
|
||||
|
||||
# https://goreleaser.com/customization/build/
|
||||
builds:
|
||||
|
|
|
|||
|
|
@ -16,16 +16,13 @@ steps:
|
|||
# https://woodpecker-ci.org/docs/usage/environment#built-in-environment-variables
|
||||
- evaluate: >-
|
||||
(not ("CI_PIPELINE_FILES" in $env)) ||
|
||||
len(CI_PIPELINE_FILES) == 0 ||
|
||||
any(fromJSON(CI_PIPELINE_FILES), {
|
||||
# startsWith "internal/" ||
|
||||
# startsWith "cmd/" ||
|
||||
# startsWith "testrig/"
|
||||
})
|
||||
CI_PIPELINE_FILES == "[]" ||
|
||||
any(fromJSON(CI_PIPELINE_FILES), { # startsWith "internal/" || # startsWith "cmd/" || # startsWith "testrig/" }) ||
|
||||
len(fromJSON(CI_PIPELINE_FILES)) == 0
|
||||
|
||||
# We use golangci-lint for linting.
|
||||
# See: https://golangci-lint.run/
|
||||
image: golangci/golangci-lint:v1.62.0
|
||||
image: golangci/golangci-lint:v2.3.1
|
||||
pull: true
|
||||
|
||||
# https://woodpecker-ci.org/docs/administration/configuration/backends/docker#run-user
|
||||
|
|
@ -60,15 +57,11 @@ steps:
|
|||
# https://woodpecker-ci.org/docs/usage/environment#built-in-environment-variables
|
||||
- evaluate: >-
|
||||
(not ("CI_PIPELINE_FILES" in $env)) ||
|
||||
len(CI_PIPELINE_FILES) == 0 ||
|
||||
any(fromJSON(CI_PIPELINE_FILES), {
|
||||
# startsWith "internal/" ||
|
||||
# startsWith "cmd/" ||
|
||||
# startsWith "testrig/" ||
|
||||
# startsWith "vendor/"
|
||||
})
|
||||
CI_PIPELINE_FILES == "[]" ||
|
||||
any(fromJSON(CI_PIPELINE_FILES), { # startsWith "internal/" || # startsWith "cmd/" || # startsWith "testrig/" || # startsWith "vendor/" }) ||
|
||||
len(fromJSON(CI_PIPELINE_FILES)) == 0
|
||||
|
||||
image: golang:1.23-alpine
|
||||
image: golang:1.24-alpine
|
||||
pull: true
|
||||
|
||||
# https://woodpecker-ci.org/docs/administration/configuration/backends/docker#run-user
|
||||
|
|
@ -99,9 +92,6 @@ steps:
|
|||
./...
|
||||
- ./test/envparsing.sh
|
||||
- ./test/swagger.sh
|
||||
|
||||
# https://woodpecker-ci.org/docs/usage/workflow-syntax#depends_on
|
||||
depends_on: [lint]
|
||||
|
||||
# Validate the web code only
|
||||
# if web source has changed.
|
||||
|
|
@ -116,8 +106,9 @@ steps:
|
|||
# https://woodpecker-ci.org/docs/usage/environment#built-in-environment-variables
|
||||
- evaluate: >-
|
||||
(not ("CI_PIPELINE_FILES" in $env)) ||
|
||||
len(CI_PIPELINE_FILES) == 0 ||
|
||||
any(fromJSON(CI_PIPELINE_FILES), { # startsWith "web/source/" })
|
||||
CI_PIPELINE_FILES == "[]" ||
|
||||
any(fromJSON(CI_PIPELINE_FILES), { # startsWith "web/source/" }) ||
|
||||
len(fromJSON(CI_PIPELINE_FILES)) == 0
|
||||
|
||||
image: node:lts-alpine
|
||||
pull: true
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ clone:
|
|||
steps:
|
||||
release:
|
||||
# https://codeberg.org/superseriousbusiness/gotosocial-woodpecker-build
|
||||
image: superseriousbusiness/gotosocial-woodpecker-build:0.11.0
|
||||
image: superseriousbusiness/gotosocial-woodpecker-build:0.12.1
|
||||
pull: true
|
||||
|
||||
# https://woodpecker-ci.org/docs/usage/volumes
|
||||
|
|
|
|||
|
|
@ -24,17 +24,12 @@ steps:
|
|||
# https://woodpecker-ci.org/docs/usage/environment#built-in-environment-variables
|
||||
- evaluate: >-
|
||||
(not ("CI_PIPELINE_FILES" in $env)) ||
|
||||
any(fromJSON(CI_PIPELINE_FILES), {
|
||||
# startsWith "internal/" ||
|
||||
# startsWith "cmd/" ||
|
||||
# startsWith "testrig/" ||
|
||||
# startsWith "vendor/" ||
|
||||
# startsWith "web/" ||
|
||||
# == "Dockerfile"
|
||||
})
|
||||
CI_PIPELINE_FILES == "[]" ||
|
||||
any(fromJSON(CI_PIPELINE_FILES), { # startsWith "internal/" || # startsWith "cmd/" || # startsWith "testrig/" || # startsWith "vendor/" || # startsWith "web/" || # == "Dockerfile" }) ||
|
||||
len(fromJSON(CI_PIPELINE_FILES)) == 0
|
||||
|
||||
# https://codeberg.org/superseriousbusiness/gotosocial-woodpecker-build
|
||||
image: superseriousbusiness/gotosocial-woodpecker-build:0.11.0
|
||||
image: superseriousbusiness/gotosocial-woodpecker-build:0.12.1
|
||||
pull: true
|
||||
|
||||
# https://woodpecker-ci.org/docs/usage/volumes
|
||||
|
|
|
|||
|
|
@ -18,20 +18,23 @@ These contribution guidelines were adapted from / inspired by those of Gitea (ht
|
|||
- [Docker](#docker)
|
||||
- [With GoReleaser](#with-goreleaser)
|
||||
- [Manually](#manually)
|
||||
- [Stylesheet / Web dev](#stylesheet--web-dev)
|
||||
- [Stylesheet / Web dev](#stylesheet-web-dev)
|
||||
- [Live Reloading](#live-reloading)
|
||||
- [Project Structure](#project-structure)
|
||||
- [Finding your way around the code](#finding-your-way-around-the-code)
|
||||
- [Style / Linting / Formatting](#style--linting--formatting)
|
||||
- [Style / Linting / Formatting](#style-linting-formatting)
|
||||
- [Testing](#testing)
|
||||
- [Standalone Testrig with Pinafore](#standalone-testrig-with-pinafore)
|
||||
- [Configuring the Standalone Testrig](#configuring-the-standalone-testrig)
|
||||
- [Running automated tests](#running-automated-tests)
|
||||
- [SQLite](#sqlite)
|
||||
- [Postgres](#postgres)
|
||||
- [CLI Tests](#cli-tests)
|
||||
- [Federation](#federation)
|
||||
- [Updating Swagger docs](#updating-swagger-docs)
|
||||
- [CI/CD configuration](#cicd-configuration)
|
||||
- [CI/CD configuration](#ci-cd-configuration)
|
||||
- [Other Useful Stuff](#other-useful-stuff)
|
||||
- [Running migrations on a Postgres DB backup locally](#running-migrations-on-a-postgres-db-backup-locally)
|
||||
|
||||
## Introduction
|
||||
|
||||
|
|
@ -182,7 +185,7 @@ Normally, these processes are handled by Drone (see CI/CD above). However, you c
|
|||
|
||||
To do this, first [install GoReleaser](https://goreleaser.com/install/).
|
||||
|
||||
Then install Node and Yarn as described in [Stylesheet / Web dev](#stylesheet--web-dev).
|
||||
Then install Node and Yarn as described in [Stylesheet / Web dev](#stylesheet-web-dev).
|
||||
|
||||
Finally, to create a snapshot build, do:
|
||||
|
||||
|
|
@ -422,10 +425,35 @@ At the login screen, enter the email address `zork@example.org` and password `pa
|
|||
|
||||
Note the following constraints:
|
||||
|
||||
- Since the testrig uses an in-memory database, the database will be destroyed when the testrig is stopped.
|
||||
- If you stop the testrig and start it again, any tokens or applications you created during your tests will also be removed. As such, you need to log out and in again every time you stop/start the rig.
|
||||
- Since the testrig uses an in-memory database by default, the database will be destroyed when the testrig is stopped.
|
||||
- If you stop the testrig and start it again, by default any tokens or applications you created during your tests will also be removed. As such, you need to log out and in again every time you stop/start the rig.
|
||||
- The testrig does not make any actual external HTTP calls, so federation will not work from a testrig.
|
||||
|
||||
##### Configuring the Standalone Testrig
|
||||
|
||||
By default the standalone testrig uses an in-memory SQLite database, which is filled with test data when starting up, and is cleared when shutting down, but you can tweak this (and a few other settings) with environment variables:
|
||||
|
||||
- `GTS_LOG_LEVEL` - you can set this to `trace` if you want to see all DB queries.
|
||||
- `GTS_TESTRIG_SKIP_DB_SETUP` - set this to any value to skip the creation of tables and population of test data when the testrig starts.
|
||||
- `GTS_TESTRIG_SKIP_DB_TEARDOWN` - set this to any value to skip the deletion of tables and test data when the testrig stops.
|
||||
- `GTS_STORAGE_BACKEND` - this uses in-memory storage by default, but you can set this to `s3` to use a locally-running Minio etc for testing.
|
||||
- `GTS_DB_TYPE` - you can change this to `postgres` to test against a locally-running Postgres intance.
|
||||
- `GTS_DB_ADDRESS` - this is set to `:memory:` by default. You can change this to use an sqlite.db file somewhere, or set it to a Postgres address.
|
||||
- `GTS_DB_PORT`, `GTS_DB_USER`, `GTS_DB_PASSWORD`, `GTS_DB_DATABASE`, `GTS_DB_TLS_MODE`, `GTS_DB_TLS_CA_CERT` - you can set these if you change `GTS_DB_ADDRESS` to `postgres` and don't use `GTS_DB_POSTGRES_CONNECTION_STRING`.
|
||||
- `GTS_DB_POSTGRES_CONNECTION_STRING` - use this to provide a Postgres connection string if you don't want to set all the db env variables mentioned in the previous point.
|
||||
|
||||
Using these variables you can also (albeit awkwardly) test migrations from one schema to another.
|
||||
|
||||
For example, to test SQLite migrations:
|
||||
|
||||
1. Switch to main branch.
|
||||
2. Build the debug binary, and then start the testrig with `DEBUG=1 GTS_LOG_LEVEL=trace GTS_DB_ADDRESS=./sqlite.test.db GTS_TESTRIG_SKIP_DB_TEARDOWN=1 ./gotosocial testrig start`. This instructs the testrig to use trace logging, use an actual file for the SQLite db, and to skip tearing it down when finished.
|
||||
3. Stop the testrig.
|
||||
4. The file `sqlite.test.db` now contains the schema and test models from the main branch.
|
||||
5. Switch to the branch with the migration you want to test.
|
||||
6. Build the debug binary, and then start the testrig with `DEBUG=1 GTS_LOG_LEVEL=trace GTS_DB_ADDRESS=./sqlite.test.db GTS_TESTRIG_SKIP_DB_SETUP=1 ./gotosocial testrig start`. This instructs the testrig to use trace logging, and to use the already-populated sqlite.test.db file.
|
||||
7. You should see logging for migrations.
|
||||
|
||||
#### Running automated tests
|
||||
|
||||
Tests can be run against both SQLite and Postgres.
|
||||
|
|
@ -499,3 +527,38 @@ The `woodpecker` pipeline files are in the `.woodpecker` directory of this repos
|
|||
The Woodpecker instance for GoToSocial is [here](https://woodpecker.superseriousbusiness.org/repos/2).
|
||||
|
||||
Documentation for Woodpecker is [here](https://woodpecker-ci.org/docs/intro).
|
||||
|
||||
## Other Useful Stuff
|
||||
|
||||
Various bits and bobs.
|
||||
|
||||
### Running migrations on a Postgres DB backup locally
|
||||
|
||||
It may be useful when testing or debugging migrations to be able to run them against a copy of a real instance's Postgres database locally.
|
||||
|
||||
Basic steps for this:
|
||||
|
||||
First dump the Postgres database on the remote machine, and copy the dump over to your development machine.
|
||||
|
||||
Now create a local Postgres container and mount the dump into it with, for example:
|
||||
|
||||
```bash
|
||||
docker run -it --name postgres --network host -e POSTGRES_PASSWORD=postgres -v /path/to/db_dump:/db_dump postgres
|
||||
```
|
||||
|
||||
In a separate terminal window, execute a command inside the running container to load the dump into the "postgres" database:
|
||||
|
||||
```bash
|
||||
docker exec -it --user postgres postgres psql -X -f /db_dump postgres
|
||||
```
|
||||
|
||||
With the Postgres container still running, run GoToSocial and point it towards the container. Use the appropriate `GTS_HOST` (and `GTS_ACCOUNT_DOMAIN`) values for the instance you dumped:
|
||||
|
||||
```bash
|
||||
GTS_HOST=example.org \
|
||||
GTS_DB_TYPE=postgres \
|
||||
GTS_DB_POSTGRES_CONNECTION_STRING=postgres://postgres:postgres@localhost:5432/postgres \
|
||||
./gotosocial migrations run
|
||||
```
|
||||
|
||||
When you're done messing around, don't forget to remove any containers that you started up, and remove any lingering volumes with `docker volume prune`, else you might end up filling your disk with unused temporary volumes.
|
||||
|
|
|
|||
35
README.md
35
README.md
|
|
@ -36,7 +36,7 @@ Here's a screenshot of the instance landing page! Check out the project's [offic
|
|||
- [Rich text formatting](#rich-text-formatting)
|
||||
- [Themes and custom CSS](#themes-and-custom-css)
|
||||
- [Easy to run](#easy-to-run)
|
||||
- [Safety + security features](#safety--security-features)
|
||||
- [Safety + security features](#safety-security-features)
|
||||
- [Various federation modes](#various-federation-modes)
|
||||
- [OIDC integration](#oidc-integration)
|
||||
- [Backend-first design](#backend-first-design)
|
||||
|
|
@ -51,7 +51,7 @@ Here's a screenshot of the instance landing page! Check out the project's [offic
|
|||
- [Stable Releases](#stable-releases)
|
||||
- [Snapshot Releases](#snapshot-releases)
|
||||
- [Docker](#docker)
|
||||
- [Binary release .tar.gz](#binary-release-targz)
|
||||
- [Binary release .tar.gz](#binary-release-tar-gz)
|
||||
- [From Source](#from-source)
|
||||
- [Third-party Packaging](#third-party-packaging)
|
||||
- [Contributing](#contributing)
|
||||
|
|
@ -61,7 +61,7 @@ Here's a screenshot of the instance landing page! Check out the project's [offic
|
|||
- [Image Attribution and Licensing](#image-attribution-and-licensing)
|
||||
- [Team](#team)
|
||||
- [Special Thanks](#special-thanks)
|
||||
- [Sponsorship + Funding](#sponsorship--funding)
|
||||
- [Sponsorship + Funding](#sponsorship-funding)
|
||||
- [Crowdfunding](#crowdfunding)
|
||||
- [Corporate Sponsorship](#corporate-sponsorship)
|
||||
- [NLnet](#nlnet)
|
||||
|
|
@ -134,7 +134,7 @@ GoToSocial lets you choose who can reply to your posts, via [interaction policie
|
|||
|
||||
### Local-only posting
|
||||
|
||||
Sometimes you only want to talk to people you share an instance with. GoToSocial supports this via local-only posting, which ensures that your post stays on your instance only. (Local-only posting is currently dependent on client support.)
|
||||
Sometimes you only want to talk to people you share an instance with. GoToSocial supports this via local-only posting, which ensures that your post stays on your instance only, even if it gets boosted. They also do not show up in the web interface, and are not accessible publicly via URL. (Local-only posting is currently dependent on client support.)
|
||||
|
||||
### RSS feed
|
||||
|
||||
|
|
@ -213,6 +213,26 @@ It's also easy for admins to [add their own custom themes](https://docs.gotosoci
|
|||
<figcaption>Sunset</figcaption>
|
||||
</figure>
|
||||
<hr/>
|
||||
<figure>
|
||||
<img src="https://codeberg.org/superseriousbusiness/gotosocial/raw/branch/main/docs/overrides/public/theme-hacker-dark.png"/>
|
||||
<figcaption>Hacker dark</figcaption>
|
||||
</figure>
|
||||
<hr/>
|
||||
<figure>
|
||||
<img src="https://codeberg.org/superseriousbusiness/gotosocial/raw/branch/main/docs/overrides/public/theme-hacker-light.png"/>
|
||||
<figcaption>Hacker light</figcaption>
|
||||
</figure>
|
||||
<hr/>
|
||||
<figure>
|
||||
<img src="https://codeberg.org/superseriousbusiness/gotosocial/raw/branch/main/docs/overrides/public/theme-programmer-socks-dark.png"/>
|
||||
<figcaption>Programmer socks dark</figcaption>
|
||||
</figure>
|
||||
<hr/>
|
||||
<figure>
|
||||
<img src="https://codeberg.org/superseriousbusiness/gotosocial/raw/branch/main/docs/overrides/public/theme-programmer-socks-light.png"/>
|
||||
<figcaption>Programmer socks light</figcaption>
|
||||
</figure>
|
||||
<hr/>
|
||||
</details>
|
||||
|
||||
### Easy to run
|
||||
|
|
@ -272,9 +292,9 @@ Don't like GtS but still want to use the fediverse? Like GtS but prefer not to u
|
|||
|
||||
## Known Issues
|
||||
|
||||
Since GoToSocial is still in beta, there are plenty of bugs. We use [Codeberg issues](https://codeberg.org/superseriousbusiness/gotosocial/issues?q=is%3Aissue+is%3Aopen+label%3Abug) to track these.
|
||||
Since GoToSocial is still in beta, there are plenty of bugs. We use [Codeberg issues](https://codeberg.org/superseriousbusiness/gotosocial/issues?labels=378161) to track these.
|
||||
|
||||
Since every ActivityPub server implementation has a slightly different interpretation of the protocol, some servers don't quite federate properly with GoToSocial yet. We're tracking these issues [in this project](https://codeberg.org/superseriousbusiness/gotosocial/projects/4). Eventually, we want to make sure that any implementation that can federate nicely with Mastodon should also be able to federate with GoToSocial.
|
||||
Since every ActivityPub server implementation has a slightly different interpretation of the protocol, some servers don't quite federate properly with GoToSocial yet. We're tracking these issues [with the federation label](https://codeberg.org/superseriousbusiness/gotosocial/issues?labels=378188). Eventually, we want to make sure that any implementation that can federate nicely with Mastodon should also be able to federate with GoToSocial.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -308,7 +328,7 @@ This is the current status of support offered by GoToSocial for different platfo
|
|||
|
||||
Notes on 64-bit CPU feature requirements:
|
||||
|
||||
- x86_64 requires the SSE4.1 instruction set. (CPUs manufactured after ~2010)
|
||||
- x86_64 requires the [x86-64-v2](https://en.wikipedia.org/wiki/X86-64-v2) level instruction sets. (CPUs manufactured after ~2010)
|
||||
|
||||
- ARM64 requires no specific features, ARMv8 CPUs (and later) have all required features.
|
||||
|
||||
|
|
@ -424,6 +444,7 @@ The following open source libraries, frameworks, and tools are used by GoToSocia
|
|||
- [gruf/go-mutexes](https://codeberg.org/gruf/go-mutexes); safemutex & mutex map. [MIT License](https://spdx.org/licenses/MIT.html).
|
||||
- [gruf/go-runners](https://codeberg.org/gruf/go-runners); synchronization utilities. [MIT License](https://spdx.org/licenses/MIT.html).
|
||||
- [gruf/go-sched](https://codeberg.org/gruf/go-sched); task scheduler. [MIT License](https://spdx.org/licenses/MIT.html).
|
||||
- [gruf/go-split](https://codeberg.org/gruf/go-split); configuration string handling. [MIT License](https://spdx.org/licenses/MIT.html).
|
||||
- [gruf/go-storage](https://codeberg.org/gruf/go-storage); file storage backend (local & s3). [MIT License](https://spdx.org/licenses/MIT.html).
|
||||
- [gruf/go-structr](https://codeberg.org/gruf/go-structr); struct caching + queueing with automated indexing by field. [MIT License](https://spdx.org/licenses/MIT.html).
|
||||
- jackc:
|
||||
|
|
|
|||
|
|
@ -13,10 +13,10 @@ Big thank you to all of our [Open Collective](https://opencollective.com/gotosoc
|
|||
- [Beta Aims](#beta-aims)
|
||||
- [Timeline](#timeline)
|
||||
- [Mid 2023](#mid-2023)
|
||||
- [Mid/late 2023](#midlate-2023)
|
||||
- [Mid/late 2023](#mid-late-2023)
|
||||
- [Early 2024](#early-2024)
|
||||
- [BETA milestone](#beta-milestone)
|
||||
- [Remainder 2024 - early 2025](#remainder-2024---early-2025)
|
||||
- [Remainder 2024 - early 2025](#remainder-2024-early-2025)
|
||||
- [On the way out of BETA to STABLE RELEASE](#on-the-way-out-of-beta-to-stable-release)
|
||||
- [Wishlist](#wishlist)
|
||||
|
||||
|
|
|
|||
|
|
@ -87,8 +87,8 @@ Thirdly, we want to make GtS as customizable as possible by allowing admins to e
|
|||
|
||||
The main technical challenges we foresee on the project are:
|
||||
|
||||
1. Ensuring compatibility with other AP servers (see here: https://codeberg.org/superseriousbusiness/gotosocial/projects/4).
|
||||
2. Ensuring compatibility with clients that use the Mastodon API (see here: https://codeberg.org/superseriousbusiness/gotosocial/projects/5).
|
||||
1. Ensuring compatibility with other AP servers (see federation issues: https://codeberg.org/superseriousbusiness/gotosocial/issues?labels=378188).
|
||||
2. Ensuring compatibility with clients that use the Mastodon API (see client compatibility issues: https://codeberg.org/superseriousbusiness/gotosocial/issues?labels=378194).
|
||||
3. Designing nuanced federation safety features that allow instance admins to screen federation without totally breaking it. This will require careful design discussions and lots of testing.
|
||||
4. Implementing our own open-source http signature library with a reference implementation of the latest draft of the http signature proposal: https://httpwg.org/http-extensions/draft-ietf-httpbis-message-signatures.html.
|
||||
5. Writing + maintaining our own extensions to the AP protocol (see below).
|
||||
|
|
|
|||
|
|
@ -83,8 +83,8 @@ Thirdly, we want to make GtS as customizable as possible by allowing admins to e
|
|||
|
||||
The main technical challenges we foresee on the project are:
|
||||
|
||||
1. Ensuring compatibility with other AP servers (see here: https://codeberg.org/superseriousbusiness/gotosocial/projects/4).
|
||||
2. Ensuring compatibility with clients that use the Mastodon API (see here: https://codeberg.org/superseriousbusiness/gotosocial/projects/5).
|
||||
1. Ensuring compatibility with other AP servers (see federation issues: https://codeberg.org/superseriousbusiness/gotosocial/issues?labels=378188).
|
||||
2. Ensuring compatibility with clients that use the Mastodon API (see client compatibility issues: https://codeberg.org/superseriousbusiness/gotosocial/issues?labels=378194).
|
||||
3. Designing nuanced federation safety features that allow instance admins to screen federation without totally breaking it. This will require careful design discussions and lots of testing.
|
||||
4. Implementing our own open-source http signature library with a reference implementation of the latest draft of the http signature proposal: https://httpwg.org/http-extensions/draft-ietf-httpbis-message-signatures.html.
|
||||
5. Writing + maintaining our own extensions to the AP protocol (see below).
|
||||
|
|
|
|||
|
|
@ -29,12 +29,25 @@ import (
|
|||
"code.superseriousbusiness.org/gotosocial/internal/db/bundb"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/gtsmodel"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/log"
|
||||
userprocessor "code.superseriousbusiness.org/gotosocial/internal/processing/user"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/state"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/util"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/validate"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
var (
|
||||
// check function conformance
|
||||
_ action.GTSAction = Create
|
||||
_ action.GTSAction = List
|
||||
_ action.GTSAction = Confirm
|
||||
_ action.GTSAction = Promote
|
||||
_ action.GTSAction = Demote
|
||||
_ action.GTSAction = Enable
|
||||
_ action.GTSAction = Disable
|
||||
_ action.GTSAction = Password
|
||||
)
|
||||
|
||||
func initState(ctx context.Context) (*state.State, error) {
|
||||
var state state.State
|
||||
state.Caches.Init()
|
||||
|
|
@ -61,7 +74,7 @@ func stopState(state *state.State) error {
|
|||
|
||||
// Create creates a new account and user
|
||||
// in the database using the provided flags.
|
||||
var Create action.GTSAction = func(ctx context.Context) error {
|
||||
func Create(ctx context.Context) error {
|
||||
state, err := initState(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -118,7 +131,7 @@ var Create action.GTSAction = func(ctx context.Context) error {
|
|||
}
|
||||
|
||||
// List returns all existing local accounts.
|
||||
var List action.GTSAction = func(ctx context.Context) error {
|
||||
func List(ctx context.Context) error {
|
||||
state, err := initState(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -156,7 +169,7 @@ var List action.GTSAction = func(ctx context.Context) error {
|
|||
|
||||
// Confirm sets a user to Approved, sets Email to the current
|
||||
// UnconfirmedEmail value, and sets ConfirmedAt to now.
|
||||
var Confirm action.GTSAction = func(ctx context.Context) error {
|
||||
func Confirm(ctx context.Context) error {
|
||||
state, err := initState(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -198,7 +211,7 @@ var Confirm action.GTSAction = func(ctx context.Context) error {
|
|||
}
|
||||
|
||||
// Promote sets admin + moderator flags on a user to true.
|
||||
var Promote action.GTSAction = func(ctx context.Context) error {
|
||||
func Promote(ctx context.Context) error {
|
||||
state, err := initState(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -235,7 +248,7 @@ var Promote action.GTSAction = func(ctx context.Context) error {
|
|||
}
|
||||
|
||||
// Demote sets admin + moderator flags on a user to false.
|
||||
var Demote action.GTSAction = func(ctx context.Context) error {
|
||||
func Demote(ctx context.Context) error {
|
||||
state, err := initState(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -272,7 +285,7 @@ var Demote action.GTSAction = func(ctx context.Context) error {
|
|||
}
|
||||
|
||||
// Disable sets Disabled to true on a user.
|
||||
var Disable action.GTSAction = func(ctx context.Context) error {
|
||||
func Disable(ctx context.Context) error {
|
||||
state, err := initState(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -308,7 +321,7 @@ var Disable action.GTSAction = func(ctx context.Context) error {
|
|||
}
|
||||
|
||||
// Enable sets Disabled to false on a user.
|
||||
var Enable action.GTSAction = func(ctx context.Context) error {
|
||||
func Enable(ctx context.Context) error {
|
||||
state, err := initState(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -344,7 +357,7 @@ var Enable action.GTSAction = func(ctx context.Context) error {
|
|||
}
|
||||
|
||||
// Password sets the password of target account.
|
||||
var Password action.GTSAction = func(ctx context.Context) error {
|
||||
func Password(ctx context.Context) error {
|
||||
state, err := initState(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -389,3 +402,41 @@ var Password action.GTSAction = func(ctx context.Context) error {
|
|||
"encrypted_password",
|
||||
)
|
||||
}
|
||||
|
||||
// Disable2FA disables 2FA for target account.
|
||||
func Disable2FA(ctx context.Context) error {
|
||||
state, err := initState(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
// Ensure state gets stopped on return.
|
||||
if err := stopState(state); err != nil {
|
||||
log.Error(ctx, err)
|
||||
}
|
||||
}()
|
||||
|
||||
username := config.GetAdminAccountUsername()
|
||||
if err := validate.Username(username); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
account, err := state.DB.GetAccountByUsernameDomain(ctx, username, "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
user, err := state.DB.GetUserByAccountID(ctx, account.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = userprocessor.TwoFactorDisable(ctx, state, user)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("2fa disabled\n")
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,12 +18,10 @@
|
|||
package media
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"code.superseriousbusiness.org/gotosocial/cmd/gotosocial/action"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/config"
|
||||
|
|
@ -33,80 +31,168 @@ import (
|
|||
"code.superseriousbusiness.org/gotosocial/internal/log"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/paging"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/state"
|
||||
"codeberg.org/gruf/go-byteutil"
|
||||
"codeberg.org/gruf/go-fastpath/v2"
|
||||
)
|
||||
|
||||
// check function conformance.
|
||||
var _ action.GTSAction = ListAttachments
|
||||
var _ action.GTSAction = ListEmojis
|
||||
|
||||
// ListAttachments lists local, remote, or all attachment paths.
|
||||
func ListAttachments(ctx context.Context) error {
|
||||
list, err := setupList(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
// Ensure lister gets shutdown on exit.
|
||||
if err := list.shutdown(); err != nil {
|
||||
log.Errorf(ctx, "error shutting down: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
// List attachment media paths from db.
|
||||
return list.ListAttachmentPaths(ctx)
|
||||
}
|
||||
|
||||
// ListEmojis lists local, remote, or all emoji filepaths.
|
||||
func ListEmojis(ctx context.Context) error {
|
||||
list, err := setupList(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
// Ensure lister gets shutdown on exit.
|
||||
if err := list.shutdown(); err != nil {
|
||||
log.Errorf(ctx, "error shutting down: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
// List emoji media paths from db.
|
||||
return list.ListEmojiPaths(ctx)
|
||||
}
|
||||
|
||||
type list struct {
|
||||
dbService db.DB
|
||||
state *state.State
|
||||
page paging.Page
|
||||
localOnly bool
|
||||
remoteOnly bool
|
||||
out *bufio.Writer
|
||||
}
|
||||
|
||||
// Get a list of attachment using a custom filter
|
||||
func (l *list) GetAllAttachmentPaths(ctx context.Context, filter func(*gtsmodel.MediaAttachment) string) ([]string, error) {
|
||||
res := make([]string, 0, 100)
|
||||
func (l *list) ListAttachmentPaths(ctx context.Context) error {
|
||||
// Page reused for iterative
|
||||
// attachment queries, with
|
||||
// predefined limit.
|
||||
var page paging.Page
|
||||
page.Limit = 500
|
||||
|
||||
// Storage base path, used for path building.
|
||||
basePath := config.GetStorageLocalBasePath()
|
||||
|
||||
for {
|
||||
// Get the next page of media attachments up to max ID.
|
||||
attachments, err := l.dbService.GetAttachments(ctx, &l.page)
|
||||
// Get next page of media attachments up to max ID.
|
||||
medias, err := l.state.DB.GetAttachments(ctx, &page)
|
||||
if err != nil && !errors.Is(err, db.ErrNoEntries) {
|
||||
return nil, fmt.Errorf("failed to retrieve media metadata from database: %w", err)
|
||||
return fmt.Errorf("failed to fetch media from database: %w", err)
|
||||
}
|
||||
|
||||
// Get current max ID.
|
||||
maxID := l.page.Max.Value
|
||||
maxID := page.Max.Value
|
||||
|
||||
// If no attachments or the same group is returned, we reached the end.
|
||||
if len(attachments) == 0 || maxID == attachments[len(attachments)-1].ID {
|
||||
// If no media or the same group is returned, we reached end.
|
||||
if len(medias) == 0 || maxID == medias[len(medias)-1].ID {
|
||||
break
|
||||
}
|
||||
|
||||
// Use last ID as the next 'maxID' value.
|
||||
maxID = attachments[len(attachments)-1].ID
|
||||
l.page.Max = paging.MaxID(maxID)
|
||||
// Use last ID as the next 'maxID'.
|
||||
maxID = medias[len(medias)-1].ID
|
||||
page.Max.Value = maxID
|
||||
|
||||
for _, a := range attachments {
|
||||
v := filter(a)
|
||||
if v != "" {
|
||||
res = append(res, v)
|
||||
switch {
|
||||
case l.localOnly:
|
||||
// Only print local media paths.
|
||||
for _, media := range medias {
|
||||
if media.RemoteURL == "" {
|
||||
printMediaPaths(basePath, media)
|
||||
}
|
||||
}
|
||||
|
||||
case l.remoteOnly:
|
||||
// Only print remote media paths.
|
||||
for _, media := range medias {
|
||||
if media.RemoteURL != "" {
|
||||
printMediaPaths(basePath, media)
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
// Print all known media paths.
|
||||
for _, media := range medias {
|
||||
printMediaPaths(basePath, media)
|
||||
}
|
||||
}
|
||||
}
|
||||
return res, nil
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get a list of emojis using a custom filter
|
||||
func (l *list) GetAllEmojisPaths(ctx context.Context, filter func(*gtsmodel.Emoji) string) ([]string, error) {
|
||||
res := make([]string, 0, 100)
|
||||
func (l *list) ListEmojiPaths(ctx context.Context) error {
|
||||
// Page reused for iterative
|
||||
// attachment queries, with
|
||||
// predefined limit.
|
||||
var page paging.Page
|
||||
page.Limit = 500
|
||||
|
||||
// Storage base path, used for path building.
|
||||
basePath := config.GetStorageLocalBasePath()
|
||||
|
||||
for {
|
||||
// Get the next page of emoji media up to max ID.
|
||||
attachments, err := l.dbService.GetEmojis(ctx, &l.page)
|
||||
emojis, err := l.state.DB.GetEmojis(ctx, &page)
|
||||
if err != nil && !errors.Is(err, db.ErrNoEntries) {
|
||||
return nil, fmt.Errorf("failed to retrieve media metadata from database: %w", err)
|
||||
return fmt.Errorf("failed to fetch emojis from database: %w", err)
|
||||
}
|
||||
|
||||
// Get current max ID.
|
||||
maxID := l.page.Max.Value
|
||||
maxID := page.Max.Value
|
||||
|
||||
// If no attachments or the same group is returned, we reached the end.
|
||||
if len(attachments) == 0 || maxID == attachments[len(attachments)-1].ID {
|
||||
// If no emojis or the same group is returned, we reached end.
|
||||
if len(emojis) == 0 || maxID == emojis[len(emojis)-1].ID {
|
||||
break
|
||||
}
|
||||
|
||||
// Use last ID as the next 'maxID' value.
|
||||
maxID = attachments[len(attachments)-1].ID
|
||||
l.page.Max = paging.MaxID(maxID)
|
||||
// Use last ID as the next 'maxID'.
|
||||
maxID = emojis[len(emojis)-1].ID
|
||||
page.Max.Value = maxID
|
||||
|
||||
for _, a := range attachments {
|
||||
v := filter(a)
|
||||
if v != "" {
|
||||
res = append(res, v)
|
||||
switch {
|
||||
case l.localOnly:
|
||||
// Only print local emoji paths.
|
||||
for _, emoji := range emojis {
|
||||
if emoji.ImageRemoteURL == "" {
|
||||
printEmojiPaths(basePath, emoji)
|
||||
}
|
||||
}
|
||||
|
||||
case l.remoteOnly:
|
||||
// Only print remote emoji paths.
|
||||
for _, emoji := range emojis {
|
||||
if emoji.ImageRemoteURL != "" {
|
||||
printEmojiPaths(basePath, emoji)
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
// Print all known emoji paths.
|
||||
for _, emoji := range emojis {
|
||||
printEmojiPaths(basePath, emoji)
|
||||
}
|
||||
}
|
||||
}
|
||||
return res, nil
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func setupList(ctx context.Context) (*list, error) {
|
||||
|
|
@ -124,150 +210,84 @@ func setupList(ctx context.Context) (*list, error) {
|
|||
)
|
||||
}
|
||||
|
||||
// Initialize caches.
|
||||
state.Caches.Init()
|
||||
|
||||
// Ensure background cache tasks are running.
|
||||
if err := state.Caches.Start(); err != nil {
|
||||
return nil, fmt.Errorf("error starting caches: %w", err)
|
||||
}
|
||||
|
||||
var err error
|
||||
|
||||
// Only set state DB connection.
|
||||
// Don't need Actions or Workers for this.
|
||||
dbService, err := bundb.NewBunDBService(ctx, &state)
|
||||
state.DB, err = bundb.NewBunDBService(ctx, &state)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating dbservice: %w", err)
|
||||
}
|
||||
state.DB = dbService
|
||||
|
||||
return &list{
|
||||
dbService: dbService,
|
||||
state: &state,
|
||||
page: paging.Page{Limit: 200},
|
||||
localOnly: localOnly,
|
||||
remoteOnly: remoteOnly,
|
||||
out: bufio.NewWriter(os.Stdout),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (l *list) shutdown() error {
|
||||
l.out.Flush()
|
||||
err := l.dbService.Close()
|
||||
err := l.state.DB.Close()
|
||||
l.state.Caches.Stop()
|
||||
return err
|
||||
}
|
||||
|
||||
// ListAttachments lists local, remote, or all attachment paths.
|
||||
var ListAttachments action.GTSAction = func(ctx context.Context) error {
|
||||
list, err := setupList(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
// reusable path building buffer,
|
||||
// only usable here as we're not
|
||||
// performing concurrent writes.
|
||||
var pb fastpath.Builder
|
||||
|
||||
// reusable string output buffer,
|
||||
// only usable here as we're not
|
||||
// performing concurrent writes.
|
||||
var outbuf byteutil.Buffer
|
||||
|
||||
func printMediaPaths(basePath string, media *gtsmodel.MediaAttachment) {
|
||||
// Append file path if present.
|
||||
if media.File.Path != "" {
|
||||
path := pb.Join(basePath, media.File.Path)
|
||||
_, _ = outbuf.WriteString(path + "\n")
|
||||
}
|
||||
|
||||
defer func() {
|
||||
// Ensure lister gets shutdown on exit.
|
||||
if err := list.shutdown(); err != nil {
|
||||
log.Error(ctx, err)
|
||||
}
|
||||
}()
|
||||
|
||||
var (
|
||||
mediaPath = config.GetStorageLocalBasePath()
|
||||
filter func(*gtsmodel.MediaAttachment) string
|
||||
)
|
||||
|
||||
switch {
|
||||
case list.localOnly:
|
||||
filter = func(m *gtsmodel.MediaAttachment) string {
|
||||
if m.RemoteURL != "" {
|
||||
// Remote, not
|
||||
// interested.
|
||||
return ""
|
||||
}
|
||||
|
||||
return path.Join(mediaPath, m.File.Path)
|
||||
}
|
||||
|
||||
case list.remoteOnly:
|
||||
filter = func(m *gtsmodel.MediaAttachment) string {
|
||||
if m.RemoteURL == "" {
|
||||
// Local, not
|
||||
// interested.
|
||||
return ""
|
||||
}
|
||||
|
||||
return path.Join(mediaPath, m.File.Path)
|
||||
}
|
||||
|
||||
default:
|
||||
filter = func(m *gtsmodel.MediaAttachment) string {
|
||||
return path.Join(mediaPath, m.File.Path)
|
||||
}
|
||||
// Append thumb path if present.
|
||||
if media.Thumbnail.Path != "" {
|
||||
path := pb.Join(basePath, media.Thumbnail.Path)
|
||||
_, _ = outbuf.WriteString(path + "\n")
|
||||
}
|
||||
|
||||
attachments, err := list.GetAllAttachmentPaths(ctx, filter)
|
||||
if err != nil {
|
||||
return err
|
||||
// Only write if any
|
||||
// string was prepared.
|
||||
if outbuf.Len() > 0 {
|
||||
_, _ = os.Stdout.Write(outbuf.B)
|
||||
outbuf.Reset()
|
||||
}
|
||||
|
||||
for _, a := range attachments {
|
||||
_, _ = list.out.WriteString(a + "\n")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ListEmojis lists local, remote, or all emoji filepaths.
|
||||
var ListEmojis action.GTSAction = func(ctx context.Context) error {
|
||||
list, err := setupList(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
func printEmojiPaths(basePath string, emoji *gtsmodel.Emoji) {
|
||||
// Append image path if present.
|
||||
if emoji.ImagePath != "" {
|
||||
path := pb.Join(basePath, emoji.ImagePath)
|
||||
_, _ = outbuf.WriteString(path + "\n")
|
||||
}
|
||||
|
||||
defer func() {
|
||||
// Ensure lister gets shutdown on exit.
|
||||
if err := list.shutdown(); err != nil {
|
||||
log.Error(ctx, err)
|
||||
}
|
||||
}()
|
||||
|
||||
var (
|
||||
mediaPath = config.GetStorageLocalBasePath()
|
||||
filter func(*gtsmodel.Emoji) string
|
||||
)
|
||||
|
||||
switch {
|
||||
case list.localOnly:
|
||||
filter = func(e *gtsmodel.Emoji) string {
|
||||
if e.ImageRemoteURL != "" {
|
||||
// Remote, not
|
||||
// interested.
|
||||
return ""
|
||||
}
|
||||
|
||||
return path.Join(mediaPath, e.ImagePath)
|
||||
}
|
||||
|
||||
case list.remoteOnly:
|
||||
filter = func(e *gtsmodel.Emoji) string {
|
||||
if e.ImageRemoteURL == "" {
|
||||
// Local, not
|
||||
// interested.
|
||||
return ""
|
||||
}
|
||||
|
||||
return path.Join(mediaPath, e.ImagePath)
|
||||
}
|
||||
|
||||
default:
|
||||
filter = func(e *gtsmodel.Emoji) string {
|
||||
return path.Join(mediaPath, e.ImagePath)
|
||||
}
|
||||
// Append static path if present.
|
||||
if emoji.ImageStaticPath != "" {
|
||||
path := pb.Join(basePath, emoji.ImageStaticPath)
|
||||
_, _ = outbuf.WriteString(path + "\n")
|
||||
}
|
||||
|
||||
emojis, err := list.GetAllEmojisPaths(ctx, filter)
|
||||
if err != nil {
|
||||
return err
|
||||
// Only write if any
|
||||
// string was prepared.
|
||||
if outbuf.Len() > 0 {
|
||||
_, _ = os.Stdout.Write(outbuf.B)
|
||||
outbuf.Reset()
|
||||
}
|
||||
|
||||
for _, e := range emojis {
|
||||
_, _ = list.out.WriteString(e + "\n")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,8 +26,11 @@ import (
|
|||
"code.superseriousbusiness.org/gotosocial/internal/log"
|
||||
)
|
||||
|
||||
// check function conformance.
|
||||
var _ action.GTSAction = All
|
||||
|
||||
// All performs all media clean actions
|
||||
var All action.GTSAction = func(ctx context.Context) error {
|
||||
func All(ctx context.Context) error {
|
||||
// Setup pruning utilities.
|
||||
prune, err := setupPrune(ctx)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -26,8 +26,11 @@ import (
|
|||
"code.superseriousbusiness.org/gotosocial/internal/log"
|
||||
)
|
||||
|
||||
// check function conformance.
|
||||
var _ action.GTSAction = Orphaned
|
||||
|
||||
// Orphaned prunes orphaned media from storage.
|
||||
var Orphaned action.GTSAction = func(ctx context.Context) error {
|
||||
func Orphaned(ctx context.Context) error {
|
||||
// Setup pruning utilities.
|
||||
prune, err := setupPrune(ctx)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -27,8 +27,11 @@ import (
|
|||
"code.superseriousbusiness.org/gotosocial/internal/log"
|
||||
)
|
||||
|
||||
// check function conformance.
|
||||
var _ action.GTSAction = Remote
|
||||
|
||||
// Remote prunes old and/or unused remote media.
|
||||
var Remote action.GTSAction = func(ctx context.Context) error {
|
||||
func Remote(ctx context.Context) error {
|
||||
// Setup pruning utilities.
|
||||
prune, err := setupPrune(ctx)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -29,8 +29,11 @@ import (
|
|||
"code.superseriousbusiness.org/gotosocial/internal/trans"
|
||||
)
|
||||
|
||||
// check function conformance.
|
||||
var _ action.GTSAction = Export
|
||||
|
||||
// Export exports info from the database into a file
|
||||
var Export action.GTSAction = func(ctx context.Context) error {
|
||||
func Export(ctx context.Context) error {
|
||||
var state state.State
|
||||
|
||||
// Only set state DB connection.
|
||||
|
|
|
|||
|
|
@ -29,8 +29,11 @@ import (
|
|||
"code.superseriousbusiness.org/gotosocial/internal/trans"
|
||||
)
|
||||
|
||||
// check function conformance.
|
||||
var _ action.GTSAction = Import
|
||||
|
||||
// Import imports info from a file into the database
|
||||
var Import action.GTSAction = func(ctx context.Context) error {
|
||||
func Import(ctx context.Context) error {
|
||||
var state state.State
|
||||
|
||||
// Only set state DB connection.
|
||||
|
|
|
|||
|
|
@ -26,17 +26,17 @@ import (
|
|||
"code.superseriousbusiness.org/gotosocial/internal/config"
|
||||
)
|
||||
|
||||
// check function conformance.
|
||||
var _ action.GTSAction = Config
|
||||
|
||||
// Config just prints the collated config out to stdout as json.
|
||||
var Config action.GTSAction = func(ctx context.Context) (err error) {
|
||||
func Config(ctx context.Context) (err error) {
|
||||
var raw map[string]interface{}
|
||||
|
||||
// Marshal configuration to a raw JSON map
|
||||
config.Config(func(cfg *config.Configuration) {
|
||||
raw, err = cfg.MarshalMap()
|
||||
raw = cfg.MarshalMap()
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
enc := json.NewEncoder(os.Stdout)
|
||||
enc.SetIndent("", " ")
|
||||
|
|
|
|||
68
cmd/gotosocial/action/migration/run.go
Normal file
68
cmd/gotosocial/action/migration/run.go
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
// GoToSocial
|
||||
// Copyright (C) GoToSocial Authors admin@gotosocial.org
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it 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.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package migration
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"code.superseriousbusiness.org/gotosocial/cmd/gotosocial/action"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/db/bundb"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/log"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/state"
|
||||
)
|
||||
|
||||
// check function conformance.
|
||||
var _ action.GTSAction = Run
|
||||
|
||||
// Run will initialize the database, running any available migrations.
|
||||
func Run(ctx context.Context) error {
|
||||
var state state.State
|
||||
|
||||
defer func() {
|
||||
if state.DB != nil {
|
||||
// Lastly, if database service was started,
|
||||
// ensure it gets closed now all else stopped.
|
||||
if err := state.DB.Close(); err != nil {
|
||||
log.Errorf(ctx, "error stopping database: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Finally reached end of shutdown.
|
||||
log.Info(ctx, "done! exiting...")
|
||||
}()
|
||||
|
||||
// Initialize caches
|
||||
state.Caches.Init()
|
||||
if err := state.Caches.Start(); err != nil {
|
||||
return fmt.Errorf("error starting caches: %w", err)
|
||||
}
|
||||
|
||||
log.Info(ctx, "starting db service...")
|
||||
|
||||
// Open connection to the database now caches started.
|
||||
dbService, err := bundb.NewBunDBService(ctx, &state)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating dbservice: %s", err)
|
||||
}
|
||||
|
||||
// Set DB on state.
|
||||
state.DB = dbService
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -41,7 +41,9 @@ import (
|
|||
"code.superseriousbusiness.org/gotosocial/internal/federation"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/federation/federatingdb"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/filter/interaction"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/filter/mutes"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/filter/spam"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/filter/status"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/filter/visibility"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/gtserror"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/httpclient"
|
||||
|
|
@ -55,12 +57,10 @@ import (
|
|||
"code.superseriousbusiness.org/gotosocial/internal/observability"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/oidc"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/processing"
|
||||
tlprocessor "code.superseriousbusiness.org/gotosocial/internal/processing/timeline"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/router"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/state"
|
||||
gtsstorage "code.superseriousbusiness.org/gotosocial/internal/storage"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/subscriptions"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/timeline"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/transport"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/typeutils"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/web"
|
||||
|
|
@ -70,9 +70,13 @@ import (
|
|||
"go.uber.org/automaxprocs/maxprocs"
|
||||
)
|
||||
|
||||
// check function conformance.
|
||||
var _ action.GTSAction = Maintenance
|
||||
var _ action.GTSAction = Start
|
||||
|
||||
// Maintenance starts and creates a GoToSocial server
|
||||
// in maintenance mode (returns 503 for most requests).
|
||||
var Maintenance action.GTSAction = func(ctx context.Context) error {
|
||||
func Maintenance(ctx context.Context) error {
|
||||
route, err := router.New(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating maintenance router: %w", err)
|
||||
|
|
@ -101,7 +105,7 @@ var Maintenance action.GTSAction = func(ctx context.Context) error {
|
|||
}
|
||||
|
||||
// Start creates and starts a gotosocial server
|
||||
var Start action.GTSAction = func(ctx context.Context) error {
|
||||
func Start(ctx context.Context) error {
|
||||
// Set GOMAXPROCS / GOMEMLIMIT
|
||||
// to match container limits.
|
||||
setLimits(ctx)
|
||||
|
|
@ -139,20 +143,6 @@ var Start action.GTSAction = func(ctx context.Context) error {
|
|||
// Noop on unstarted workers.
|
||||
state.Workers.Stop()
|
||||
|
||||
if state.Timelines.Home != nil {
|
||||
// Home timeline mgr was setup, ensure it gets stopped.
|
||||
if err := state.Timelines.Home.Stop(); err != nil {
|
||||
log.Errorf(ctx, "error stopping home timeline: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if state.Timelines.List != nil {
|
||||
// List timeline mgr was setup, ensure it gets stopped.
|
||||
if err := state.Timelines.List.Stop(); err != nil {
|
||||
log.Errorf(ctx, "error stopping list timeline: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if process != nil {
|
||||
const timeout = time.Minute
|
||||
|
||||
|
|
@ -200,7 +190,7 @@ var Start action.GTSAction = func(ctx context.Context) error {
|
|||
}
|
||||
|
||||
// Initialize tracing (noop if not enabled).
|
||||
if err := observability.InitializeTracing(); err != nil {
|
||||
if err := observability.InitializeTracing(ctx); err != nil {
|
||||
return fmt.Errorf("error initializing tracing: %w", err)
|
||||
}
|
||||
|
||||
|
|
@ -284,10 +274,12 @@ var Start action.GTSAction = func(ctx context.Context) error {
|
|||
)
|
||||
typeConverter := typeutils.NewConverter(state)
|
||||
visFilter := visibility.NewFilter(state)
|
||||
muteFilter := mutes.NewFilter(state)
|
||||
intFilter := interaction.NewFilter(state)
|
||||
statusFilter := status.NewFilter(state)
|
||||
spamFilter := spam.NewFilter(state)
|
||||
federatingDB := federatingdb.New(state, typeConverter, visFilter, intFilter, spamFilter)
|
||||
transportController := transport.NewController(state, federatingDB, &federation.Clock{}, client)
|
||||
transportController := transport.NewController(state, federatingDB, client)
|
||||
federator := federation.NewFederator(
|
||||
state,
|
||||
federatingDB,
|
||||
|
|
@ -323,26 +315,6 @@ var Start action.GTSAction = func(ctx context.Context) error {
|
|||
// Create a Web Push notification sender.
|
||||
webPushSender := webpush.NewSender(client, state, typeConverter)
|
||||
|
||||
// Initialize both home / list timelines.
|
||||
state.Timelines.Home = timeline.NewManager(
|
||||
tlprocessor.HomeTimelineGrab(state),
|
||||
tlprocessor.HomeTimelineFilter(state, visFilter),
|
||||
tlprocessor.HomeTimelineStatusPrepare(state, typeConverter),
|
||||
tlprocessor.SkipInsert(),
|
||||
)
|
||||
if err := state.Timelines.Home.Start(); err != nil {
|
||||
return fmt.Errorf("error starting home timeline: %s", err)
|
||||
}
|
||||
state.Timelines.List = timeline.NewManager(
|
||||
tlprocessor.ListTimelineGrab(state),
|
||||
tlprocessor.ListTimelineFilter(state, visFilter),
|
||||
tlprocessor.ListTimelineStatusPrepare(state, typeConverter),
|
||||
tlprocessor.SkipInsert(),
|
||||
)
|
||||
if err := state.Timelines.List.Start(); err != nil {
|
||||
return fmt.Errorf("error starting list timeline: %s", err)
|
||||
}
|
||||
|
||||
// Start the job scheduler
|
||||
// (this is required for cleaner).
|
||||
state.Workers.StartScheduler()
|
||||
|
|
@ -384,7 +356,9 @@ var Start action.GTSAction = func(ctx context.Context) error {
|
|||
emailSender,
|
||||
webPushSender,
|
||||
visFilter,
|
||||
muteFilter,
|
||||
intFilter,
|
||||
statusFilter,
|
||||
)
|
||||
|
||||
// Schedule background cleaning tasks.
|
||||
|
|
@ -412,8 +386,13 @@ var Start action.GTSAction = func(ctx context.Context) error {
|
|||
return fmt.Errorf("error scheduling poll expiries: %w", err)
|
||||
}
|
||||
|
||||
// schedule publication tasks for all scheduled statuses.
|
||||
if err := process.Status().ScheduledStatusesScheduleAll(ctx); err != nil {
|
||||
return fmt.Errorf("error scheduling status publications: %w", err)
|
||||
}
|
||||
|
||||
// Initialize metrics.
|
||||
if err := observability.InitializeMetrics(state.DB); err != nil {
|
||||
if err := observability.InitializeMetrics(ctx, state); err != nil {
|
||||
return fmt.Errorf("error initializing metrics: %w", err)
|
||||
}
|
||||
|
||||
|
|
@ -514,17 +493,19 @@ var Start action.GTSAction = func(ctx context.Context) error {
|
|||
return fmt.Errorf("error generating session name for session middleware: %w", err)
|
||||
}
|
||||
|
||||
// Configure our instance cookie policy.
|
||||
cookiePolicy := apiutil.NewCookiePolicy()
|
||||
|
||||
var (
|
||||
authModule = api.NewAuth(state, process, idp, routerSession, sessionName) // auth/oauth paths
|
||||
clientModule = api.NewClient(state, process) // api client endpoints
|
||||
metricsModule = api.NewMetrics() // Metrics endpoints
|
||||
healthModule = api.NewHealth(dbService.Ready) // Health check endpoints
|
||||
fileserverModule = api.NewFileserver(process) // fileserver endpoints
|
||||
robotsModule = api.NewRobots() // robots.txt endpoint
|
||||
wellKnownModule = api.NewWellKnown(process) // .well-known endpoints
|
||||
nodeInfoModule = api.NewNodeInfo(process) // nodeinfo endpoint
|
||||
activityPubModule = api.NewActivityPub(dbService, process) // ActivityPub endpoints
|
||||
webModule = web.New(dbService, process) // web pages + user profiles + settings panels etc
|
||||
authModule = api.NewAuth(state, process, idp, routerSession, sessionName, cookiePolicy) // auth/oauth paths
|
||||
clientModule = api.NewClient(state, process) // api client endpoints
|
||||
healthModule = api.NewHealth(dbService.Ready) // Health check endpoints
|
||||
fileserverModule = api.NewFileserver(process) // fileserver endpoints
|
||||
robotsModule = api.NewRobots() // robots.txt endpoint
|
||||
wellKnownModule = api.NewWellKnown(process) // .well-known endpoints
|
||||
nodeInfoModule = api.NewNodeInfo(process) // nodeinfo endpoint
|
||||
activityPubModule = api.NewActivityPub(dbService, process) // ActivityPub endpoints
|
||||
webModule = web.New(dbService, process, cookiePolicy) // web pages + user profiles + settings panels etc
|
||||
)
|
||||
|
||||
// Create per-route / per-grouping middlewares.
|
||||
|
|
@ -571,7 +552,6 @@ var Start action.GTSAction = func(ctx context.Context) error {
|
|||
// apply throttling *after* rate limiting
|
||||
authModule.Route(route, clLimit, clThrottle, robotsDisallowAll, gzip)
|
||||
clientModule.Route(route, clLimit, clThrottle, robotsDisallowAll, gzip)
|
||||
metricsModule.Route(route, clLimit, clThrottle, robotsDisallowAIOnly)
|
||||
healthModule.Route(route, clLimit, clThrottle, robotsDisallowAIOnly)
|
||||
fileserverModule.Route(route, fsMainLimit, fsThrottle, robotsDisallowAIOnly)
|
||||
fileserverModule.RouteEmojis(route, instanceAccount.ID, fsEmojiLimit, fsThrottle, robotsDisallowAIOnly)
|
||||
|
|
@ -652,14 +632,14 @@ func parseClientRanges() (
|
|||
|
||||
allowIPs := config.GetHTTPClientAllowIPs()
|
||||
allowRanges := make([]netip.Prefix, len(allowIPs))
|
||||
allowFlag := config.HTTPClientAllowIPsFlag()
|
||||
allowFlag := config.HTTPClientAllowIPsFlag
|
||||
if err := parseF(allowIPs, allowRanges, allowFlag); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
blockIPs := config.GetHTTPClientBlockIPs()
|
||||
blockRanges := make([]netip.Prefix, len(blockIPs))
|
||||
blockFlag := config.HTTPClientBlockIPsFlag()
|
||||
blockFlag := config.HTTPClientBlockIPsFlag
|
||||
if err := parseF(blockIPs, blockRanges, blockFlag); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,8 +19,15 @@
|
|||
|
||||
package testrig
|
||||
|
||||
import "code.superseriousbusiness.org/gotosocial/cmd/gotosocial/action"
|
||||
import (
|
||||
"context"
|
||||
|
||||
"code.superseriousbusiness.org/gotosocial/cmd/gotosocial/action"
|
||||
)
|
||||
|
||||
// check function conformance.
|
||||
var _ action.GTSAction = Start
|
||||
|
||||
// Start creates and starts a gotosocial testrig server.
|
||||
// This is only enabled in debug builds, else is nil.
|
||||
var Start action.GTSAction
|
||||
func Start(context.Context) error { return nil }
|
||||
|
|
|
|||
|
|
@ -34,28 +34,28 @@ import (
|
|||
apiutil "code.superseriousbusiness.org/gotosocial/internal/api/util"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/cleaner"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/config"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/filter/visibility"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/gtserror"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/language"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/log"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/middleware"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/observability"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/oidc"
|
||||
tlprocessor "code.superseriousbusiness.org/gotosocial/internal/processing/timeline"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/router"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/state"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/storage"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/subscriptions"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/timeline"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/typeutils"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/web"
|
||||
"code.superseriousbusiness.org/gotosocial/testrig"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// check function conformance.
|
||||
var _ action.GTSAction = Start
|
||||
|
||||
// Start creates and starts a gotosocial testrig server.
|
||||
// This is only enabled in debug builds, else is nil.
|
||||
var Start action.GTSAction = func(ctx context.Context) error {
|
||||
func Start(ctx context.Context) error {
|
||||
testrig.InitTestConfig()
|
||||
testrig.InitTestLog()
|
||||
|
||||
|
|
@ -89,29 +89,20 @@ var Start action.GTSAction = func(ctx context.Context) error {
|
|||
// tasks from being executed.
|
||||
testrig.StopWorkers(state)
|
||||
|
||||
if state.Timelines.Home != nil {
|
||||
// Home timeline mgr was setup, ensure it gets stopped.
|
||||
if err := state.Timelines.Home.Stop(); err != nil {
|
||||
log.Errorf(ctx, "error stopping home timeline: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if state.Timelines.List != nil {
|
||||
// List timeline mgr was setup, ensure it gets stopped.
|
||||
if err := state.Timelines.List.Stop(); err != nil {
|
||||
log.Errorf(ctx, "error stopping list timeline: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if state.Storage != nil {
|
||||
// If storage was created, ensure torn down.
|
||||
testrig.StandardStorageTeardown(state.Storage)
|
||||
}
|
||||
|
||||
if state.DB != nil {
|
||||
// Clean up database by
|
||||
// dropping tables if required.
|
||||
if !config.GetTestrigSkipDBTeardown() {
|
||||
testrig.StandardDBTeardown(state.DB)
|
||||
}
|
||||
|
||||
// Lastly, if database service was started,
|
||||
// ensure it gets closed now all else stopped.
|
||||
testrig.StandardDBTeardown(state.DB)
|
||||
if err := state.DB.Close(); err != nil {
|
||||
log.Errorf(ctx, "error stopping database: %v", err)
|
||||
}
|
||||
|
|
@ -127,7 +118,7 @@ var Start action.GTSAction = func(ctx context.Context) error {
|
|||
}
|
||||
config.SetInstanceLanguages(parsedLangs)
|
||||
|
||||
if err := observability.InitializeTracing(); err != nil {
|
||||
if err := observability.InitializeTracing(ctx); err != nil {
|
||||
return fmt.Errorf("error initializing tracing: %w", err)
|
||||
}
|
||||
|
||||
|
|
@ -142,7 +133,10 @@ var Start action.GTSAction = func(ctx context.Context) error {
|
|||
// that twice, we can just start the initialized caches.
|
||||
state.Caches.Start()
|
||||
|
||||
testrig.StandardDBSetup(state.DB, nil)
|
||||
// Populate database tables + data if required.
|
||||
if !config.GetTestrigSkipDBSetup() {
|
||||
testrig.StandardDBSetup(state.DB, nil)
|
||||
}
|
||||
|
||||
// Get the instance account (we'll need this later).
|
||||
instanceAccount, err := state.DB.GetInstanceAccount(ctx, "")
|
||||
|
|
@ -170,27 +164,6 @@ var Start action.GTSAction = func(ctx context.Context) error {
|
|||
emailSender := testrig.NewEmailSender("./web/template/", nil)
|
||||
webPushSender := testrig.NewWebPushMockSender()
|
||||
typeConverter := typeutils.NewConverter(state)
|
||||
filter := visibility.NewFilter(state)
|
||||
|
||||
// Initialize both home / list timelines.
|
||||
state.Timelines.Home = timeline.NewManager(
|
||||
tlprocessor.HomeTimelineGrab(state),
|
||||
tlprocessor.HomeTimelineFilter(state, filter),
|
||||
tlprocessor.HomeTimelineStatusPrepare(state, typeConverter),
|
||||
tlprocessor.SkipInsert(),
|
||||
)
|
||||
if err := state.Timelines.Home.Start(); err != nil {
|
||||
return fmt.Errorf("error starting home timeline: %s", err)
|
||||
}
|
||||
state.Timelines.List = timeline.NewManager(
|
||||
tlprocessor.ListTimelineGrab(state),
|
||||
tlprocessor.ListTimelineFilter(state, filter),
|
||||
tlprocessor.ListTimelineStatusPrepare(state, typeConverter),
|
||||
tlprocessor.SkipInsert(),
|
||||
)
|
||||
if err := state.Timelines.List.Start(); err != nil {
|
||||
return fmt.Errorf("error starting list timeline: %s", err)
|
||||
}
|
||||
|
||||
processor := testrig.NewTestProcessor(state, federator, emailSender, webPushSender, mediaManager)
|
||||
|
||||
|
|
@ -199,7 +172,7 @@ var Start action.GTSAction = func(ctx context.Context) error {
|
|||
defer testrig.StopWorkers(state)
|
||||
|
||||
// Initialize metrics.
|
||||
if err := observability.InitializeMetrics(state.DB); err != nil {
|
||||
if err := observability.InitializeMetrics(ctx, state); err != nil {
|
||||
return fmt.Errorf("error initializing metrics: %w", err)
|
||||
}
|
||||
|
||||
|
|
@ -282,23 +255,24 @@ var Start action.GTSAction = func(ctx context.Context) error {
|
|||
return fmt.Errorf("error generating session name for session middleware: %w", err)
|
||||
}
|
||||
|
||||
// Configure our instance cookie policy.
|
||||
cookiePolicy := apiutil.NewCookiePolicy()
|
||||
|
||||
var (
|
||||
authModule = api.NewAuth(state, processor, idp, routerSession, sessionName) // auth/oauth paths
|
||||
clientModule = api.NewClient(state, processor) // api client endpoints
|
||||
metricsModule = api.NewMetrics() // Metrics endpoints
|
||||
healthModule = api.NewHealth(state.DB.Ready) // Health check endpoints
|
||||
fileserverModule = api.NewFileserver(processor) // fileserver endpoints
|
||||
robotsModule = api.NewRobots() // robots.txt endpoint
|
||||
wellKnownModule = api.NewWellKnown(processor) // .well-known endpoints
|
||||
nodeInfoModule = api.NewNodeInfo(processor) // nodeinfo endpoint
|
||||
activityPubModule = api.NewActivityPub(state.DB, processor) // ActivityPub endpoints
|
||||
webModule = web.New(state.DB, processor) // web pages + user profiles + settings panels etc
|
||||
authModule = api.NewAuth(state, processor, idp, routerSession, sessionName, cookiePolicy) // auth/oauth paths
|
||||
clientModule = api.NewClient(state, processor) // api client endpoints
|
||||
healthModule = api.NewHealth(state.DB.Ready) // Health check endpoints
|
||||
fileserverModule = api.NewFileserver(processor) // fileserver endpoints
|
||||
robotsModule = api.NewRobots() // robots.txt endpoint
|
||||
wellKnownModule = api.NewWellKnown(processor) // .well-known endpoints
|
||||
nodeInfoModule = api.NewNodeInfo(processor) // nodeinfo endpoint
|
||||
activityPubModule = api.NewActivityPub(state.DB, processor) // ActivityPub endpoints
|
||||
webModule = web.New(state.DB, processor, cookiePolicy) // web pages + user profiles + settings panels etc
|
||||
)
|
||||
|
||||
// these should be routed in order
|
||||
authModule.Route(route)
|
||||
clientModule.Route(route)
|
||||
metricsModule.Route(route)
|
||||
healthModule.Route(route)
|
||||
fileserverModule.Route(route)
|
||||
fileserverModule.RouteEmojis(route, instanceAccount.ID)
|
||||
|
|
|
|||
|
|
@ -146,6 +146,19 @@ func adminCommands() *cobra.Command {
|
|||
config.AddAdminAccountPassword(adminAccountPasswordCmd)
|
||||
adminAccountCmd.AddCommand(adminAccountPasswordCmd)
|
||||
|
||||
adminAccountDisable2FACmd := &cobra.Command{
|
||||
Use: "disable-2fa",
|
||||
Short: "disable 2fa for the given local account",
|
||||
PreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
return preRun(preRunArgs{cmd: cmd})
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return run(cmd.Context(), account.Disable2FA)
|
||||
},
|
||||
}
|
||||
config.AddAdminAccount(adminAccountDisable2FACmd)
|
||||
adminAccountCmd.AddCommand(adminAccountDisable2FACmd)
|
||||
|
||||
adminCmd.AddCommand(adminAccountCmd)
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -43,16 +43,16 @@ type preRunArgs struct {
|
|||
// env vars or cli flag.
|
||||
func preRun(a preRunArgs) error {
|
||||
if err := config.BindFlags(a.cmd); err != nil {
|
||||
return fmt.Errorf("error binding flags: %s", err)
|
||||
return fmt.Errorf("error binding flags: %w", err)
|
||||
}
|
||||
|
||||
if err := config.Reload(); err != nil {
|
||||
return fmt.Errorf("error reloading config: %s", err)
|
||||
if err := config.LoadConfigFile(); err != nil {
|
||||
return fmt.Errorf("error loading config file: %w", err)
|
||||
}
|
||||
|
||||
if !a.skipValidation {
|
||||
if err := config.Validate(); err != nil {
|
||||
return fmt.Errorf("invalid config: %s", err)
|
||||
return fmt.Errorf("invalid config: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -64,11 +64,17 @@ func preRun(a preRunArgs) error {
|
|||
// context, after initializing any last-minute things like loggers etc.
|
||||
func run(ctx context.Context, action action.GTSAction) error {
|
||||
log.SetTimeFormat(config.GetLogTimestampFormat())
|
||||
// Set the global log level from configuration
|
||||
|
||||
// Set the global log level from configuration.
|
||||
if err := log.ParseLevel(config.GetLogLevel()); err != nil {
|
||||
return fmt.Errorf("error parsing log level: %w", err)
|
||||
}
|
||||
|
||||
// Set global log output format from configuration.
|
||||
if err := log.ParseFormat(config.GetLogFormat()); err != nil {
|
||||
return fmt.Errorf("error parsing log format: %w", err)
|
||||
}
|
||||
|
||||
if config.GetSyslogEnabled() {
|
||||
// Enable logging to syslog
|
||||
if err := log.EnableSyslog(
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ package main
|
|||
|
||||
import (
|
||||
configaction "code.superseriousbusiness.org/gotosocial/cmd/gotosocial/action/debug/config"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/config"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
|
|
@ -39,7 +38,6 @@ func debugCommands() *cobra.Command {
|
|||
return run(cmd.Context(), configaction.Config)
|
||||
},
|
||||
}
|
||||
config.AddServerFlags(debugConfigCmd)
|
||||
debugCmd.AddCommand(debugConfigCmd)
|
||||
return debugCmd
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,10 +23,9 @@ import (
|
|||
godebug "runtime/debug"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
_ "code.superseriousbusiness.org/gotosocial/docs"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/config"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// Version is the version of GoToSocial being used.
|
||||
|
|
@ -41,27 +40,22 @@ func main() {
|
|||
// override version in config store
|
||||
config.SetSoftwareVersion(version)
|
||||
|
||||
// instantiate the root command
|
||||
rootCmd := &cobra.Command{
|
||||
Use: "gotosocial",
|
||||
Short: "GoToSocial - a fediverse social media server",
|
||||
Long: "GoToSocial - a fediverse social media server\n\nFor help, see: https://docs.gotosocial.org.\n\nCode: https://codeberg.org/superseriousbusiness/gotosocial",
|
||||
Version: version,
|
||||
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
// before running any other cmd funcs, we must load config-path
|
||||
return config.LoadEarlyFlags(cmd)
|
||||
},
|
||||
SilenceErrors: true,
|
||||
SilenceUsage: true,
|
||||
}
|
||||
rootCmd := new(cobra.Command)
|
||||
rootCmd.Use = "gotosocial"
|
||||
rootCmd.Short = "GoToSocial - a fediverse social media server"
|
||||
rootCmd.Long = "GoToSocial - a fediverse social media server\n\nFor help, see: https://docs.gotosocial.org.\n\nCode: https://codeberg.org/superseriousbusiness/gotosocial"
|
||||
rootCmd.Version = version
|
||||
rootCmd.SilenceErrors = true
|
||||
rootCmd.SilenceUsage = true
|
||||
|
||||
// attach global flags to the root command so that they can be accessed from any subcommand
|
||||
config.AddGlobalFlags(rootCmd)
|
||||
// Register global flags with root.
|
||||
config.RegisterGlobalFlags(rootCmd)
|
||||
|
||||
// add subcommands
|
||||
// Add subcommands with their flags.
|
||||
rootCmd.AddCommand(serverCommands())
|
||||
rootCmd.AddCommand(debugCommands())
|
||||
rootCmd.AddCommand(adminCommands())
|
||||
rootCmd.AddCommand(migrationCommands())
|
||||
|
||||
// Testrigcmd will only be set when debug is enabled.
|
||||
if testrigCmd := testrigCommands(); testrigCmd != nil {
|
||||
|
|
@ -70,7 +64,7 @@ func main() {
|
|||
log.Fatal("gotosocial must be built and run with the DEBUG enviroment variable set to enable and access testrig")
|
||||
}
|
||||
|
||||
// run
|
||||
// Run the prepared root command.
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
log.Fatalf("error executing command: %s", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,24 +15,29 @@
|
|||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package v1
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
apimodel "code.superseriousbusiness.org/gotosocial/internal/api/model"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/gtserror"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/gtsmodel"
|
||||
"code.superseriousbusiness.org/gotosocial/cmd/gotosocial/action/migration"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// apiFilter is a shortcut to return the API v1 filter version of the given
|
||||
// filter keyword, or return an appropriate error if conversion fails.
|
||||
func (p *Processor) apiFilter(ctx context.Context, filterKeyword *gtsmodel.FilterKeyword) (*apimodel.FilterV1, gtserror.WithCode) {
|
||||
apiFilter, err := p.converter.FilterKeywordToAPIFilterV1(ctx, filterKeyword)
|
||||
if err != nil {
|
||||
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting filter keyword to API v1 filter: %w", err))
|
||||
// migrationCommands returns the 'migrations' subcommand
|
||||
func migrationCommands() *cobra.Command {
|
||||
migrationCmd := &cobra.Command{
|
||||
Use: "migrations",
|
||||
Short: "gotosocial migrations-related tasks",
|
||||
}
|
||||
|
||||
return apiFilter, nil
|
||||
migrationRunCmd := &cobra.Command{
|
||||
Use: "run",
|
||||
Short: "starts and stops the database, running any outstanding migrations",
|
||||
PreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
return preRun(preRunArgs{cmd: cmd})
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return run(cmd.Context(), migration.Run)
|
||||
},
|
||||
}
|
||||
migrationCmd.AddCommand(migrationRunCmd)
|
||||
return migrationCmd
|
||||
}
|
||||
|
|
@ -19,7 +19,6 @@ package main
|
|||
|
||||
import (
|
||||
"code.superseriousbusiness.org/gotosocial/cmd/gotosocial/action/server"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/config"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
|
|
@ -39,7 +38,6 @@ func serverCommands() *cobra.Command {
|
|||
return run(cmd.Context(), server.Start)
|
||||
},
|
||||
}
|
||||
config.AddServerFlags(serverStartCmd)
|
||||
serverCmd.AddCommand(serverStartCmd)
|
||||
|
||||
serverMaintenanceCmd := &cobra.Command{
|
||||
|
|
@ -52,7 +50,6 @@ func serverCommands() *cobra.Command {
|
|||
return run(cmd.Context(), server.Maintenance)
|
||||
},
|
||||
}
|
||||
config.AddServerFlags(serverMaintenanceCmd)
|
||||
serverCmd.AddCommand(serverMaintenanceCmd)
|
||||
|
||||
return serverCmd
|
||||
|
|
|
|||
|
|
@ -19,11 +19,13 @@ package main
|
|||
|
||||
import (
|
||||
"code.superseriousbusiness.org/gotosocial/cmd/gotosocial/action/testrig"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/config"
|
||||
"codeberg.org/gruf/go-debug"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func testrigCommands() *cobra.Command {
|
||||
if testrig.Start != nil {
|
||||
if debug.DEBUG {
|
||||
testrigCmd := &cobra.Command{
|
||||
Use: "testrig",
|
||||
Short: "gotosocial testrig-related tasks",
|
||||
|
|
@ -38,6 +40,7 @@ func testrigCommands() *cobra.Command {
|
|||
}
|
||||
|
||||
testrigCmd.AddCommand(testrigStartCmd)
|
||||
config.AddTestrig(testrigCmd)
|
||||
return testrigCmd
|
||||
}
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -41,9 +41,9 @@ With this in mind, you should only ever treat domain blocking as *one layer* of
|
|||
|
||||
Unfortunately, the Fediverse has its share of trolls, many of whom see domain blocking as an adversary to be defeated. To achieve this, they often target instances which use domain blocks to protect users.
|
||||
|
||||
As such, there are bots on the Fediverse which scrape instance domain blocks and announce any discovered blocks to the followers of the bot, opening the admin of the blocking instance up to harassment. These bots use the `api/v1/instance/peers?filter=suspended` endpoint of GoToSocial instances to gather domain block information.
|
||||
As such, there are bots on the Fediverse which scrape instance domain blocks and announce any discovered blocks to the followers of the bot, opening the admin of the blocking instance up to harassment. These bots use the `api/v1/instance/peers?filter=suspended`, `api/v1/instance/peers?filter=blocked`, and/or `api/v1/instance/domain_blocks` endpoints of GoToSocial instances to gather domain block information.
|
||||
|
||||
By default, GoToSocial does not expose this endpoint publicly, so your instance will be safe from such scraping. However, if you set `instance-expose-suspended` to `true` in your config.yaml file, you may find that this endpoint gets scraped occasionally, and you may see your blocks being announced by troll bots.
|
||||
By default, GoToSocial does not expose these endpoints publicly, so your instance will be safe from such scraping. However, if you set `instance-expose-blocklist` to `true` in your config.yaml file, you may find that these endpoints gets scraped occasionally, and you may see your blocks being announced by troll bots.
|
||||
|
||||
## What are the side effects of creating a domain block
|
||||
|
||||
|
|
|
|||
|
|
@ -21,9 +21,28 @@ The subscription with the higher priority is the one that now creates and manage
|
|||
|
||||
If the subscription with the higher priority is removed, then the next time all the subscriptions are fetched, "Less Important List" will create (or take ownership of) the domain allow instead.
|
||||
|
||||
## Retractions
|
||||
|
||||
Sometimes, an entry that was present on a subscribed block or allow list will be removed later by the curator(s) of that list. When this happens, the removed domain permission entry can be said to have been "retracted".
|
||||
|
||||
For example, say your instance subscribes to one block list, and that block list contains an entry for `baddies.example.org`. A corresponding domain block for `baddies.example.org` has therefore been created in your database, with the subscription ID of your block list. In other words, the domain block is in force, and is managed by your block list subscription.
|
||||
|
||||
At some point, your instance fetches the list again, and this time it sees that the entry for `baddies.example.org` is no longer present in the list, because it has been removed by the list curator(s) (perhaps the admins turned their policies around, or the instance was shut down, etc). Thus, according to your instance, the block for `baddies.example.org` is now a "retracted" domain permission entry.
|
||||
|
||||
If the domain permission subscription is set to "Remove retracted permissions," then the now-retracted domain block will be removed from the database, and will no longer be enforced. In this example, that means your instance will start federating (again) with `baddies.example.org`.
|
||||
|
||||
If the domain permission subscription is *not* set to "Remove retracted permissions," then instead of the retracted block being removed from the database, it will be kept in the database but "orphaned" -- ie., it will still be in force, but it will be marked as no longer being managed by the subscription. In this example, that means your instance will keep blocking `baddies.example.org`.
|
||||
|
||||
!!!! Note "Retracted permissions and other subscriptions"
|
||||
When a permission is retracted and removed from the database, but an entry for it exists on the list of *another* subscription of a lower priority than the one it was retracted from, then the permission will be recreated as an entry managed by the lower priority list.
|
||||
|
||||
For example, say you subscribe to List1 at priority 255, and List2 at priority 128, and `baddies.example.org` is present on both lists. That means the domain block entry will be managed by List1. If List1 later *retracts* the entry, it will be removed from your database (assuming you have "Remove retracted permissions" set). However, as soon as List2 is checked (usually seconds after List1), then an entry for `baddies.example.org` will be created again, but managed by List2 this time.
|
||||
|
||||
In other words, it is only when an entry is retracted from *every list you subscribe to* that it will truly be removed.
|
||||
|
||||
## Orphan Permissions
|
||||
|
||||
Domain permissions (blocks or allows) that are not currently managed by a domain permission subscription are considered "orphan" permissions. This includes permissions that an admin created in the settings panel by hand, or which were imported manually via the import/export page.
|
||||
Domain permissions (blocks or allows) that are not currently managed by a domain permission subscription are considered "orphan" permissions. This includes permissions that an admin created in the settings panel by hand, entries which were imported manually via the import/export page, or entries that belonged to a subscription but have since been retracted but not removed.
|
||||
|
||||
If you wish, when creating a domain permission subscription, you can set ["adopt orphans"](./settings.md#adopt-orphan-permissions) to true for that subscription. If a domain permission subscription that is set to adopt orphans encounters an orphan permission which is *also present on the list at the subscription's URI*, then it will "adopt" the orphan by setting the orphan's subscription ID to its own ID.
|
||||
|
||||
|
|
|
|||
|
|
@ -10,8 +10,6 @@ You can allow or disallow crawlers from collecting stats about your instance fro
|
|||
|
||||
The AI scrapers come from a [community maintained repository][airobots]. It's manually kept in sync for the time being. If you know of any missing robots, please send them a PR!
|
||||
|
||||
A number of AI scrapers are known to ignore entries in `robots.txt` even if it explicitly matches their User-Agent. This means the `robots.txt` file is not a foolproof way of ensuring AI scrapers don't grab your content.
|
||||
|
||||
If you want to block these things fully, you'll need to block based on the User-Agent header in a reverse proxy until GoToSocial can filter requests by User-Agent header.
|
||||
A number of AI scrapers are known to ignore entries in `robots.txt` even if it explicitly matches their User-Agent. This means the `robots.txt` file is not a foolproof way of ensuring AI scrapers don't grab your content. In addition to this you might want to look into blocking User-Agents via [requester header filtering](request_filtering_modes.md).
|
||||
|
||||
[airobots]: https://github.com/ai-robots-txt/ai.robots.txt/
|
||||
|
|
|
|||
|
|
@ -123,7 +123,13 @@ If you check this box, then any existing domain permissions will become managed
|
|||
1. They don't already have a subscription ID (ie., they're not managed by any domain permission subscription).
|
||||
2. They match a domain permission included in the list at the URL of this subscription.
|
||||
|
||||
For more information on orphan permissions, please see the separate [domain permission subscriptions](./domain_permission_subscriptions.md) document.
|
||||
For more information on orphan permissions, please see the separate [domain permission subscriptions](./domain_permission_subscriptions.md#orphan-permissions) document.
|
||||
|
||||
##### Remove Retracted Permissions
|
||||
|
||||
This setting controls how retractions are handled by this domain permission subscription: if "Remove retracted permissions" is checked, retracted entries will be removed from the database; if "Remove retracted permissions" is not checked, retracted entries will just be orphaned instead.
|
||||
|
||||
For more detail on how retractions work, with examples, please see the separate [domain permission subscriptions](./domain_permission_subscriptions.md#retractions) document.
|
||||
|
||||
##### Create Permissions as Drafts
|
||||
|
||||
|
|
|
|||
43
docs/admin/slow_hardware.md
Normal file
43
docs/admin/slow_hardware.md
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
# Managing GtS on slow hardware
|
||||
|
||||
While GoToSocial runs great on lower-end hardware, some operations are not practical on it, especially
|
||||
instances with the database on slow storage (think anything that is not an SSD). This document
|
||||
offers some suggestions on how to work around common issues when running GtS on slow hardware.
|
||||
|
||||
## Running database migrations on a different machine
|
||||
|
||||
Sometimes a database migration will need to do operations that are taxing on the database's storage.
|
||||
These operations can take days if the database resides on a hard disk or SD card. If your
|
||||
database is on slow storage, it can save a lot of time to follow the following procedure:
|
||||
|
||||
!!! danger
|
||||
|
||||
It might seem tempting to keep GtS running while you run the migrations on another machine, but
|
||||
doing this will lead to all the posts that are received during the migration post disappearing
|
||||
once the migrated database is re-imported.
|
||||
|
||||
1. Shut down GtS
|
||||
2. Take a [backup](backup_and_restore.md#what-to-backup-database) of the database
|
||||
3. Import the database on faster hardware
|
||||
4. Run the GtS migration on the faster hardware
|
||||
5. Take a backup of the resultant database
|
||||
6. Import the resultant backup and overwrite the old database
|
||||
7. Start GtS with the new version
|
||||
|
||||
### Running GtS migrations separately
|
||||
|
||||
After you import the database on the faster hardware, you can run the migration without starting
|
||||
GtS by downloading the *target* GtS version from the [releases](https://codeberg.org/superseriousbusiness/gotosocial/releases) page.
|
||||
For instance, if you are running `v0.19.2` and you want to upgrade to `v0.20.0-rc1`, download the
|
||||
latter version. Once you have the binary, set it to executable by running `chmod u+x /path/to/gotosocial`. Afterwards, copy the configuration of the original server, and alter
|
||||
it with the location of the new database. We copy the configuration in case variables like
|
||||
the hostname is used in the migration, we want to keep that consistent.
|
||||
Once everything is in place, you can run the migration like this:
|
||||
|
||||
```sh
|
||||
$ /path/to/gotosocial --config-path /path/to/config migrations run
|
||||
```
|
||||
|
||||
This will run all the migrations, just like GtS would if it was started normally. Once this is done
|
||||
you can copy the result to the original instance and start the new GtS version there as well, which
|
||||
will see that everything is migrated and that there's nothing to do except run as expected.
|
||||
|
|
@ -1,32 +1,89 @@
|
|||
# Metrics
|
||||
|
||||
GoToSocial comes with [OpenTelemetry][otel] based metrics. The metrics are exposed using the [Prometheus exposition format][prom] on the `/metrics` path. The configuration settings are documented in the [Observability configuration reference][obs].
|
||||
GoToSocial uses the [OpenTelemetry][otel] Go SDK to enable instance admins to expose runtime metrics in the Prometheus metrics format.
|
||||
|
||||
Currently the following metrics are collected:
|
||||
Currently, the following metrics are collected:
|
||||
|
||||
* Go performance and runtime metrics
|
||||
* Gin (HTTP) metrics
|
||||
* Gin (HTTP server) metrics
|
||||
* Bun (database) metrics
|
||||
|
||||
Metrics can be enable with the following configuration:
|
||||
## Enabling metrics
|
||||
|
||||
To enable metrics, first set the `metrics-enabled` configuration value to `true` in your config.yaml file:
|
||||
|
||||
```yaml
|
||||
metrics-enabled: true
|
||||
```
|
||||
|
||||
Though metrics do not contain anything privacy sensitive, you may not want to allow just anyone to view and scrape operational metrics of your instance.
|
||||
Then, you will need to set some additional environment variables on the GoToSocial process in order to configure OpenTelemetry to expose metrics in the Prometheus format:
|
||||
|
||||
```env
|
||||
OTEL_METRICS_PRODUCERS=prometheus
|
||||
OTEL_METRICS_EXPORTER=prometheus
|
||||
```
|
||||
|
||||
By default, this configuration will instantiate an additional HTTP server running alongside the standard GoToSocial HTTP server, which exposes a Prometheus metrics endpoint at `localhost:9464/metrics`.
|
||||
|
||||
!!! tip
|
||||
If you are running GoToSocial using the [example systemd service definition](../../example/gotosocial.service), you can easily set these environment variables by uncommenting the relevant two lines in that file, and reloading + restarting the service.
|
||||
|
||||
If you wish, you can further customize this metrics HTTP server by using the following environment variables to change the host and port:
|
||||
|
||||
```env
|
||||
OTEL_EXPORTER_PROMETHEUS_HOST=example.org
|
||||
OTEL_EXPORTER_PROMETHEUS_PORT=9999
|
||||
```
|
||||
|
||||
## Serving metrics to the outside world
|
||||
|
||||
If you have deployed GoToSocial in a "bare-metal" fashion without a reverse proxy, you can expose the metrics endpoint to the outside world by setting `OTEL_EXPORTER_PROMETHEUS_HOST` to your host value. For example, if your GtS instance `host` configuration value is set to `example.org`, you should set `OTEL_EXPORTER_PROMETHEUS_HOST=example.org`. You should then be able to access your metrics at `http://example.org:9464/metrics`. GoToSocial running in this fashion will not serve LetsEncrypt certificates at the metrics endpoint, so you will be limited to using HTTP rather than HTTPS.
|
||||
|
||||
If you are using a reverse proxy like Nginx, you can expose the metrics endpoint to the outside world with HTTPS certificates, by putting an additional location stanza in your Nginx configuration above the catch-all `location /` stanza:
|
||||
|
||||
```nginx
|
||||
location /metrics {
|
||||
proxy_pass http://127.0.0.1:9464;
|
||||
}
|
||||
```
|
||||
|
||||
This will instruct Nginx to forward requests to `example.org/metrics` to the separate Prometheus server running on port 9464.
|
||||
|
||||
## Enabling basic authentication
|
||||
|
||||
You can enable basic authentication for the metrics endpoint. On the GoToSocial, side you'll need the following configuration:
|
||||
Although there is no sensitive data contained in the OTEL runtime statistics exported by Prometheus, you may nevertheless wish to gate access to the `/metrics` endpoint behind some kind of authentication, to prevent every Tom, Dick, and Harry from looking at your runtime stats.
|
||||
|
||||
```yaml
|
||||
metrics-auth-enabled: true
|
||||
metrics-auth-username: some_username
|
||||
metrics-auth-password: some_password
|
||||
You can do this by configuring your reverse proxy to require basic authentication for access to `/metrics`.
|
||||
|
||||
In Nginx, for example, you could do this by creating an `htpasswd` file alongside your site in the `sites-available` directory of Nginx, and instructing Nginx to use that file to gate access.
|
||||
|
||||
Assuming you followed the [guide for setting up Nginx as your reverse proxy](../getting_started/reverse_proxy/nginx.md), you will already have a file for your Nginx service definition at `/etc/nginx/sites-available/example.org`, where `example.org` is the hostname of your instance.
|
||||
|
||||
You can create an `htpasswd` file alongside this file using the following command:
|
||||
|
||||
```bash
|
||||
htpasswd -c /etc/nginx/sites-available/example.org.htpasswd username
|
||||
```
|
||||
|
||||
You can scrape that endpoint with a Prometheus instance using the following configuration in your `scrape_configs`:
|
||||
In the command, be sure to replace `example.org` with your hostname, and `username` with whatever username you want to use.
|
||||
|
||||
Now, edit `/etc/nginx/sites-available/example.org` and update your `/metrics` stanza to use the `httpasswd` file. After editing it should look something like this:
|
||||
|
||||
```nginx
|
||||
location /metrics {
|
||||
proxy_pass http://127.0.0.1:9464;
|
||||
auth_basic "Metrics";
|
||||
auth_basic_user_file /etc/nginx/sites-available/example.org.htpasswd;
|
||||
}
|
||||
```
|
||||
|
||||
Again, replace `example.org` in that snippet with your instance hostname.
|
||||
|
||||
When you're finished editing, reload + restart Nginx, and you should see a basic authentication prompt when visiting the `/metrics` endpoint of your instance in your browser.
|
||||
|
||||
## Prometheus scrape configuration
|
||||
|
||||
You can scrape your `/metrics` endpoint with a Prometheus instance using the following configuration in your `scrape_configs`:
|
||||
|
||||
```yaml
|
||||
- job_name: gotosocial
|
||||
|
|
@ -40,18 +97,12 @@ You can scrape that endpoint with a Prometheus instance using the following conf
|
|||
- example.org
|
||||
```
|
||||
|
||||
## Blocking external scraping
|
||||
Change `example.org` to your hostname in the above snippet. If you are not using HTTPS, change the `scheme` value to `http`. If you are not using basic authentication, you can remove the `basic_auth` section. If you are not using a reverse proxy, and metrics are exposed on port 9464, add the port to the host (eg., `example.org` -> `example.org:9464`).
|
||||
|
||||
When running with a reverse proxy you can use it to block external access to metrics. You can use this approach if your Prometheus scraper runs on the same machine as your GoToSocial instance and can thus access it internally.
|
||||
## Viewing metrics on Grafana
|
||||
|
||||
For example with nginx, block the `/metrics` endpoint by returning a 404:
|
||||
|
||||
```nginx
|
||||
location /metrics {
|
||||
return 404;
|
||||
}
|
||||
```
|
||||
Instructions on how to set up Grafana are beyond the scope of this document. However, once you have set up a Grafana to pull from your Prometheus instance, you can import the [example Grafana dashboard](https://codeberg.org/superseriousbusiness/gotosocial/raw/branch/main/example/metrics/gotosocial_grafana_dashboard.json) into your Grafana frontend to easily view GoToSocial Go runtime and HTTP metrics.
|
||||
|
||||
[otel]: https://opentelemetry.io/
|
||||
[prom]: https://prometheus.io/docs/instrumenting/exposition_formats/
|
||||
[obs]: ../configuration/observability.md
|
||||
[obs]: ../configuration/observability_and_metrics.md
|
||||
|
|
|
|||
|
|
@ -1,25 +1,20 @@
|
|||
# Tracing
|
||||
|
||||
GoToSocial comes with [OpenTelemetry][otel] based tracing built-in. It's not wired through every function, but our HTTP handlers and database library will create spans. How to configure tracing is explained in the [Observability configuration reference][obs].
|
||||
GoToSocial comes with [OpenTelemetry][otel] based tracing built-in. It's not wired through every function, but our HTTP handlers and database library will create spans that may help you debug issues.
|
||||
|
||||
## Enabling tracing
|
||||
|
||||
To enable tracing on your instance, you must set `tracing-enabled` to `true` in your config.yaml file. Then, you must set the environment variable `OTEL_TRACES_EXPORTER` to your desired tracing format. A list of available options is available [here](https://opentelemetry.io/docs/languages/sdk-configuration/general/#otel_traces_exporter). Once you have changed your config and set the environment variable, restart your instance.
|
||||
|
||||
If necessary, you can do further configuration of tracing using the other environment variables listed [here](https://opentelemetry.io/docs/languages/sdk-configuration/general/).
|
||||
|
||||
## Ingesting traces
|
||||
|
||||
In order to receive the traces, you need something to ingest them and then visualise them. There are many options available including self-hosted and commercial options.
|
||||
|
||||
We provide an example of how to do this using [Grafana Tempo][tempo] to ingest the spans and [Grafana][grafana] to explore them. Please beware that the configuration we provide is not suitable for a production setup. It can be used safely for local development and can provide a good starting point for setting up your own tracing infrastructure.
|
||||
In [`example/tracing`][ext] we provide an example of how to do this using [Grafana Tempo][tempo] to ingest the spans and [Grafana][grafana] to explore them. You can use the files with `docker-compose up -d` to get Tempo and Grafana running.
|
||||
|
||||
You'll need the files in [`example/tracing`][ext]. Once you have those you can run `docker-compose up -d` to get Tempo and Grafana running. With both services running, you can add the following to your GoToSocial configuration and restart your instance:
|
||||
|
||||
```yaml
|
||||
tracing-enabled: true
|
||||
tracing-transport: "grpc"
|
||||
tracing-endpoint: "localhost:4317"
|
||||
tracing-insecure-transport: true
|
||||
```
|
||||
|
||||
[otel]: https://opentelemetry.io/
|
||||
[obs]: ../configuration/observability.md
|
||||
[tempo]: https://grafana.com/oss/tempo/
|
||||
[grafana]: https://grafana.com/oss/grafana/
|
||||
[ext]: https://codeberg.org/superseriousbusiness/gotosocial/tree/main/example/tracing
|
||||
Please be aware that while the example configuration we provide can be used safely for local development and can provide a good starting point for setting up your own tracing infrastructure, it is not suitable for a so-called "production" setup.
|
||||
|
||||
## Querying and visualising traces
|
||||
|
||||
|
|
@ -27,18 +22,23 @@ Once you execute a few queries against your instance, you'll be able to find the
|
|||
|
||||
Using TraceQL, a simple query to find all traces related to requests to `/api/v1/instance` would look like this:
|
||||
|
||||
```
|
||||
```traceql
|
||||
{.http.route = "/api/v1/instance"}
|
||||
```
|
||||
|
||||
If you wanted to see all GoToSocial traces, you could instead run:
|
||||
|
||||
```
|
||||
```traceql
|
||||
{.service.name = "GoToSocial"}
|
||||
```
|
||||
|
||||
Once you select a trace, a second panel will open up visualising the span. You can drill down from there, by clicking into every sub-span to see what it was doing.
|
||||
|
||||

|
||||

|
||||
|
||||
[traceql]: https://grafana.com/docs/tempo/latest/traceql/
|
||||
[otel]: https://opentelemetry.io/
|
||||
[obs]: ../configuration/observability_and_metrics.md
|
||||
[tempo]: https://grafana.com/oss/tempo/
|
||||
[grafana]: https://grafana.com/oss/grafana/
|
||||
[ext]: https://codeberg.org/superseriousbusiness/gotosocial/src/branch/main/example/tracing
|
||||
|
|
|
|||
|
|
@ -130,6 +130,69 @@ definitions:
|
|||
title: NodeInfoUsers represents aggregate information about the users on the server.
|
||||
type: object
|
||||
x-go-package: code.superseriousbusiness.org/gotosocial/internal/api/model
|
||||
ScheduledStatusParams:
|
||||
properties:
|
||||
application_id:
|
||||
type: string
|
||||
x-go-name: ApplicationID
|
||||
content_type:
|
||||
type: string
|
||||
x-go-name: ContentType
|
||||
in_reply_to_id:
|
||||
type: string
|
||||
x-go-name: InReplyToID
|
||||
interaction_policy:
|
||||
$ref: '#/definitions/interactionPolicy'
|
||||
language:
|
||||
type: string
|
||||
x-go-name: Language
|
||||
local_only:
|
||||
type: boolean
|
||||
x-go-name: LocalOnly
|
||||
media_ids:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
x-go-name: MediaIDs
|
||||
poll:
|
||||
$ref: '#/definitions/ScheduledStatusParamsPoll'
|
||||
scheduled_at:
|
||||
type: string
|
||||
x-go-name: ScheduledAt
|
||||
sensitive:
|
||||
type: boolean
|
||||
x-go-name: Sensitive
|
||||
spoiler_text:
|
||||
type: string
|
||||
x-go-name: SpoilerText
|
||||
text:
|
||||
type: string
|
||||
x-go-name: Text
|
||||
visibility:
|
||||
type: string
|
||||
x-go-name: Visibility
|
||||
title: StatusParams represents parameters for a scheduled status.
|
||||
type: object
|
||||
x-go-package: code.superseriousbusiness.org/gotosocial/internal/api/model
|
||||
ScheduledStatusParamsPoll:
|
||||
properties:
|
||||
expires_in:
|
||||
format: int64
|
||||
type: integer
|
||||
x-go-name: ExpiresIn
|
||||
hide_totals:
|
||||
type: boolean
|
||||
x-go-name: HideTotals
|
||||
multiple:
|
||||
type: boolean
|
||||
x-go-name: Multiple
|
||||
options:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
x-go-name: Options
|
||||
type: object
|
||||
x-go-package: code.superseriousbusiness.org/gotosocial/internal/api/model
|
||||
Source:
|
||||
description: Returned as an additional entity when verifying and updated credentials, as an attribute of Account.
|
||||
properties:
|
||||
|
|
@ -1101,7 +1164,7 @@ definitions:
|
|||
properties:
|
||||
comment:
|
||||
description: |-
|
||||
If the domain is blocked, what's the publicly-stated reason for the block.
|
||||
If the domain is blocked or allowed, what's the publicly-stated reason (if any).
|
||||
Alternative to `public_comment` to be used when serializing/deserializing via /api/v1/instance.
|
||||
example: they smell
|
||||
type: string
|
||||
|
|
@ -1113,11 +1176,17 @@ definitions:
|
|||
x-go-name: Domain
|
||||
public_comment:
|
||||
description: |-
|
||||
If the domain is blocked, what's the publicly-stated reason for the block.
|
||||
If the domain is blocked or allowed, what's the publicly-stated reason (if any).
|
||||
Alternative to `comment` to be used when serializing/deserializing NOT via /api/v1/instance.
|
||||
example: they smell
|
||||
type: string
|
||||
x-go-name: PublicComment
|
||||
severity:
|
||||
description: |-
|
||||
Severity of this entry.
|
||||
Only ever set for domain blocks, and if set, always="suspend".
|
||||
type: string
|
||||
x-go-name: Severity
|
||||
silenced_at:
|
||||
description: Time at which this domain was silenced. Key will not be present on open domains.
|
||||
example: "2021-07-30T09:20:25+00:00"
|
||||
|
|
@ -1135,7 +1204,7 @@ definitions:
|
|||
properties:
|
||||
comment:
|
||||
description: |-
|
||||
If the domain is blocked, what's the publicly-stated reason for the block.
|
||||
If the domain is blocked or allowed, what's the publicly-stated reason (if any).
|
||||
Alternative to `public_comment` to be used when serializing/deserializing via /api/v1/instance.
|
||||
example: they smell
|
||||
type: string
|
||||
|
|
@ -1179,11 +1248,17 @@ definitions:
|
|||
x-go-name: PrivateComment
|
||||
public_comment:
|
||||
description: |-
|
||||
If the domain is blocked, what's the publicly-stated reason for the block.
|
||||
If the domain is blocked or allowed, what's the publicly-stated reason (if any).
|
||||
Alternative to `comment` to be used when serializing/deserializing NOT via /api/v1/instance.
|
||||
example: they smell
|
||||
type: string
|
||||
x-go-name: PublicComment
|
||||
severity:
|
||||
description: |-
|
||||
Severity of this entry.
|
||||
Only ever set for domain blocks, and if set, always="suspend".
|
||||
type: string
|
||||
x-go-name: Severity
|
||||
silenced_at:
|
||||
description: Time at which this domain was silenced. Key will not be present on open domains.
|
||||
example: "2021-07-30T09:20:25+00:00"
|
||||
|
|
@ -1277,6 +1352,11 @@ definitions:
|
|||
format: uint8
|
||||
type: integer
|
||||
x-go-name: Priority
|
||||
remove_retracted:
|
||||
description: If true, then when a list is processed, if the list does *not* contain entries that it *did* contain previously, ie., retracted entries, then domain permissions corresponding to those entries will be removed. If false, they will just be orphaned instead.
|
||||
example: true
|
||||
type: boolean
|
||||
x-go-name: RemoveRetracted
|
||||
successfully_fetched_at:
|
||||
description: Time of the most recent successful fetch (ISO 8601 Datetime).
|
||||
example: "2021-07-30T09:20:25+00:00"
|
||||
|
|
@ -1581,9 +1661,7 @@ definitions:
|
|||
type: integer
|
||||
x-go-name: MaxFeaturedTags
|
||||
max_profile_fields:
|
||||
description: |-
|
||||
The maximum number of profile fields allowed for each account.
|
||||
Currently not configurable, so this is hardcoded to 6. (https://codeberg.org/superseriousbusiness/gotosocial/issues/1876)
|
||||
description: The maximum number of profile fields allowed for each account.
|
||||
format: int64
|
||||
type: integer
|
||||
x-go-name: MaxProfileFields
|
||||
|
|
@ -2098,6 +2176,20 @@ definitions:
|
|||
example: <p>Registrations are currently closed on example.org because of spam bots!</p>
|
||||
type: string
|
||||
x-go-name: Message
|
||||
min_age:
|
||||
description: |-
|
||||
A minimum age required to register, if configured.
|
||||
Currently not implemented: will always be null.
|
||||
format: int64
|
||||
type: integer
|
||||
x-go-name: MinAge
|
||||
reason_required:
|
||||
description: |-
|
||||
Whether registrations require the user to provide a reason for joining.
|
||||
Only applicable when ApprovalRequired is true.
|
||||
example: true
|
||||
type: boolean
|
||||
x-go-name: ReasonRequired
|
||||
title: Information about registering for this instance.
|
||||
type: object
|
||||
x-go-name: InstanceV2Registrations
|
||||
|
|
@ -2168,11 +2260,26 @@ definitions:
|
|||
x-go-package: code.superseriousbusiness.org/gotosocial/internal/api/model
|
||||
instanceV2URLs:
|
||||
properties:
|
||||
about:
|
||||
description: Address of the server about page.
|
||||
example: https://example.org/about
|
||||
type: string
|
||||
x-go-name: About
|
||||
privacy_policy:
|
||||
description: Address of the server privacy policy, if any.
|
||||
example: https://example.org/about#rules
|
||||
type: string
|
||||
x-go-name: PrivacyPolicy
|
||||
streaming:
|
||||
description: Websockets address for status and notification streaming.
|
||||
example: wss://example.org
|
||||
type: string
|
||||
x-go-name: Streaming
|
||||
terms_of_service:
|
||||
description: Address of the server current terms of service, if any.
|
||||
example: https://example.org/about#privacy_policy
|
||||
type: string
|
||||
x-go-name: TermsOfService
|
||||
title: InstanceV2URLs models instance-relevant URLs for client application consumption.
|
||||
type: object
|
||||
x-go-name: InstanceV2URLs
|
||||
|
|
@ -2214,13 +2321,29 @@ definitions:
|
|||
interactionPolicyRules:
|
||||
properties:
|
||||
always:
|
||||
description: Policy entries for accounts that can always do this type of interaction.
|
||||
description: |-
|
||||
Policy entries for accounts that can always do this type of interaction.
|
||||
Deprecated: Use "automatic_approval" instead.
|
||||
items:
|
||||
$ref: '#/definitions/interactionPolicyValue'
|
||||
type: array
|
||||
x-go-name: Always
|
||||
automatic_approval:
|
||||
description: Policy entries for accounts that will receive automatic approval for this type of interaction.
|
||||
items:
|
||||
$ref: '#/definitions/interactionPolicyValue'
|
||||
type: array
|
||||
x-go-name: AutomaticApproval
|
||||
manual_approval:
|
||||
description: Policy entries for accounts that require manual approval for this type of interaction.
|
||||
items:
|
||||
$ref: '#/definitions/interactionPolicyValue'
|
||||
type: array
|
||||
x-go-name: ManualApproval
|
||||
with_approval:
|
||||
description: Policy entries for accounts that require approval to do this type of interaction.
|
||||
description: |-
|
||||
Policy entries for accounts that require approval to do this type of interaction.
|
||||
Deprecated: Use "manual_approval" instead.
|
||||
items:
|
||||
$ref: '#/definitions/interactionPolicyValue'
|
||||
type: array
|
||||
|
|
@ -2279,10 +2402,6 @@ definitions:
|
|||
`reblog` - Someone reblogged / boosted a status.
|
||||
type: string
|
||||
x-go-name: Type
|
||||
uri:
|
||||
description: URI of the Accept or Reject. Only set if accepted_at or rejected_at is set, else omitted.
|
||||
type: string
|
||||
x-go-name: URI
|
||||
title: InteractionRequest represents a pending, approved, or rejected interaction of type favourite, reply, or reblog.
|
||||
type: object
|
||||
x-go-name: InteractionRequest
|
||||
|
|
@ -2849,6 +2968,25 @@ definitions:
|
|||
type: object
|
||||
x-go-name: Report
|
||||
x-go-package: code.superseriousbusiness.org/gotosocial/internal/api/model
|
||||
scheduledStatus:
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
x-go-name: ID
|
||||
media_attachments:
|
||||
items:
|
||||
$ref: '#/definitions/attachment'
|
||||
type: array
|
||||
x-go-name: MediaAttachments
|
||||
params:
|
||||
$ref: '#/definitions/ScheduledStatusParams'
|
||||
scheduled_at:
|
||||
type: string
|
||||
x-go-name: ScheduledAt
|
||||
title: ScheduledStatus represents a status that will be published at a future scheduled date.
|
||||
type: object
|
||||
x-go-name: ScheduledStatus
|
||||
x-go-package: code.superseriousbusiness.org/gotosocial/internal/api/model
|
||||
searchResult:
|
||||
properties:
|
||||
accounts:
|
||||
|
|
@ -3726,6 +3864,7 @@ info:
|
|||
read:applications: grants read access to user-managed applications
|
||||
read:blocks: grants read access to blocks
|
||||
read:bookmarks: grants read access to bookmarks
|
||||
read:custom_emojis: grants read access to custom emojis
|
||||
read:favourites: grants read access to accounts
|
||||
read:filters: grants read access to filters
|
||||
read:follows: grants read access to follows
|
||||
|
|
@ -6695,6 +6834,11 @@ paths:
|
|||
in: formData
|
||||
name: adopt_orphans
|
||||
type: boolean
|
||||
- default: true
|
||||
description: If true, then when a list is processed, if the list does *not* contain entries that it *did* contain previously, ie., retracted entries, then domain permissions corresponding to those entries will be removed. If false, they will just be orphaned instead.
|
||||
in: formData
|
||||
name: remove_retracted
|
||||
type: boolean
|
||||
- description: URI to call in order to fetch the permissions list.
|
||||
in: formData
|
||||
name: uri
|
||||
|
|
@ -6774,6 +6918,11 @@ paths:
|
|||
in: formData
|
||||
name: adopt_orphans
|
||||
type: boolean
|
||||
- default: true
|
||||
description: If true, then when a list is processed, if the list does *not* contain entries that it *did* contain previously, ie., retracted entries, then domain permissions corresponding to those entries will be removed. If false, they will just be orphaned instead.
|
||||
in: formData
|
||||
name: remove_retracted
|
||||
type: boolean
|
||||
- description: MIME content type to use when parsing the permissions list. One of "text/plain", "text/csv", and "application/json".
|
||||
in: formData
|
||||
name: content_type
|
||||
|
|
@ -8100,6 +8249,7 @@ paths:
|
|||
- conversations
|
||||
/api/v1/custom_emojis:
|
||||
get:
|
||||
description: If the instance config setting `instance-expose-custom-emojis` is `true` then authentication is not required.
|
||||
operationId: customEmojisGet
|
||||
produces:
|
||||
- application/json
|
||||
|
|
@ -8117,7 +8267,8 @@ paths:
|
|||
"500":
|
||||
description: internal server error
|
||||
security:
|
||||
- OAuth2 Bearer: []
|
||||
- OAuth2 Bearer:
|
||||
- read:custom_emojis
|
||||
summary: Get an array of custom emojis available on the instance.
|
||||
tags:
|
||||
- custom_emojis
|
||||
|
|
@ -8719,6 +8870,65 @@ paths:
|
|||
summary: Reject/deny follow request from the given account ID.
|
||||
tags:
|
||||
- follow_requests
|
||||
/api/v1/follow_requests/outgoing:
|
||||
get:
|
||||
description: |-
|
||||
The next and previous queries can be parsed from the returned Link header.
|
||||
Example:
|
||||
|
||||
```
|
||||
<https://example.org/api/v1/follow_requests/outgoing?limit=80&max_id=01FC0SKA48HNSVR6YKZCQGS2V8>; rel="next", <https://example.org/api/v1/follow_requests/outgoing?limit=80&min_id=01FC0SKW5JK2Q4EVAV2B462YY0>; rel="prev"
|
||||
````
|
||||
operationId: getOutgoingFollowRequests
|
||||
parameters:
|
||||
- description: 'Return only follow requested accounts *OLDER* than the given max ID. The follow requestee with the specified ID will not be included in the response. NOTE: the ID is of the internal follow request, NOT any of the returned accounts.'
|
||||
in: query
|
||||
name: max_id
|
||||
type: string
|
||||
- description: 'Return only follow requested accounts *NEWER* than the given since ID. The follow requestee with the specified ID will not be included in the response. NOTE: the ID is of the internal follow request, NOT any of the returned accounts.'
|
||||
in: query
|
||||
name: since_id
|
||||
type: string
|
||||
- description: 'Return only follow requested accounts *IMMEDIATELY NEWER* than the given min ID. The follow requestee with the specified ID will not be included in the response. NOTE: the ID is of the internal follow request, NOT any of the returned accounts.'
|
||||
in: query
|
||||
name: min_id
|
||||
type: string
|
||||
- default: 40
|
||||
description: Number of follow requested accounts to return.
|
||||
in: query
|
||||
maximum: 80
|
||||
minimum: 1
|
||||
name: limit
|
||||
type: integer
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: ""
|
||||
headers:
|
||||
Link:
|
||||
description: Links to the next and previous queries.
|
||||
type: string
|
||||
schema:
|
||||
items:
|
||||
$ref: '#/definitions/account'
|
||||
type: array
|
||||
"400":
|
||||
description: bad request
|
||||
"401":
|
||||
description: unauthorized
|
||||
"404":
|
||||
description: not found
|
||||
"406":
|
||||
description: not acceptable
|
||||
"500":
|
||||
description: internal server error
|
||||
security:
|
||||
- OAuth2 Bearer:
|
||||
- read:follows
|
||||
summary: Get an array of accounts that you have requested to follow.
|
||||
tags:
|
||||
- follow_requests
|
||||
/api/v1/followed_tags:
|
||||
get:
|
||||
operationId: getFollowedTags
|
||||
|
|
@ -8914,37 +9124,105 @@ paths:
|
|||
summary: Update your instance information and/or upload a new avatar/header for the instance.
|
||||
tags:
|
||||
- instance
|
||||
/api/v1/instance/domain_allows:
|
||||
get:
|
||||
description: OAuth token may need to be provided depending on setting `instance-expose-allowlist`.
|
||||
operationId: instanceDomainAllowsGet
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: List of explicitly allowed domains.
|
||||
schema:
|
||||
items:
|
||||
$ref: '#/definitions/domain'
|
||||
type: array
|
||||
"400":
|
||||
description: bad request
|
||||
"401":
|
||||
description: unauthorized
|
||||
"403":
|
||||
description: forbidden
|
||||
"404":
|
||||
description: not found
|
||||
"406":
|
||||
description: not acceptable
|
||||
"500":
|
||||
description: internal server error
|
||||
security:
|
||||
- OAuth2 Bearer: []
|
||||
summary: List explicitly allowed domains.
|
||||
tags:
|
||||
- instance
|
||||
/api/v1/instance/domain_blocks:
|
||||
get:
|
||||
description: OAuth token may need to be provided depending on setting `instance-expose-blocklist`.
|
||||
operationId: instanceDomainBlocksGet
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: List of blocked domains.
|
||||
schema:
|
||||
items:
|
||||
$ref: '#/definitions/domain'
|
||||
type: array
|
||||
"400":
|
||||
description: bad request
|
||||
"401":
|
||||
description: unauthorized
|
||||
"403":
|
||||
description: forbidden
|
||||
"404":
|
||||
description: not found
|
||||
"406":
|
||||
description: not acceptable
|
||||
"500":
|
||||
description: internal server error
|
||||
security:
|
||||
- OAuth2 Bearer: []
|
||||
summary: List blocked domains.
|
||||
tags:
|
||||
- instance
|
||||
/api/v1/instance/peers:
|
||||
get:
|
||||
operationId: instancePeersGet
|
||||
parameters:
|
||||
- default: open
|
||||
- default: flat
|
||||
description: |-
|
||||
Comma-separated list of filters to apply to results. Recognized filters are:
|
||||
- `open` -- include peers that are not suspended or silenced
|
||||
- `suspended` -- include peers that have been suspended.
|
||||
- `open` -- include known domains that are not in the domain blocklist
|
||||
- `allowed` -- include domains that are in the domain allowlist
|
||||
- `blocked` -- include domains that are in the domain blocklist
|
||||
- `suspended` -- DEPRECATED! Use `blocked` instead. Same as `blocked`: include domains that are in the domain blocklist;
|
||||
|
||||
If filter is `open`, only instances that haven't been suspended or silenced will be returned.
|
||||
If filter is `open`, only domains that aren't in the blocklist will be shown.
|
||||
|
||||
If filter is `suspended`, only suspended instances will be shown.
|
||||
If filter is `blocked`, only domains that *are* in the blocklist will be shown.
|
||||
|
||||
If filter is `open,suspended`, then all known instances will be returned.
|
||||
If filter is `allowed`, only domains that are in the allowlist will be shown.
|
||||
|
||||
If filter is `open,blocked`, then blocked domains and known domains not on the blocklist will be shown.
|
||||
|
||||
If filter is `open,allowed`, then allowed domains and known domains not on the blocklist will be shown.
|
||||
|
||||
If filter is an empty string or not set, then `open` will be assumed as the default.
|
||||
in: query
|
||||
name: filter
|
||||
type: string
|
||||
- default: false
|
||||
description: If true, a "flat" array of strings will be returned corresponding to just domain names.
|
||||
in: query
|
||||
name: flat
|
||||
type: boolean
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: |-
|
||||
If no filter parameter is provided, or filter is empty, then a legacy, Mastodon-API compatible response will be returned. This will consist of just a 'flat' array of strings like `["example.com", "example.org"]`, which corresponds to domains this instance peers with.
|
||||
|
||||
If a filter parameter is provided, then an array of objects with at least a `domain` key set on each object will be returned.
|
||||
|
||||
If no filter parameter is provided, or filter is empty, then a legacy, Mastodon-API compatible response will be returned. This will consist of just a 'flat' array of strings like `["example.com", "example.org"]`, which corresponds to setting a filter of `open` and flat=true.
|
||||
If a filter parameter is provided and flat is not true, then an array of objects with at least a `domain` key set on each object will be returned.
|
||||
Domains that are silenced or suspended will also have a key `suspended_at` or `silenced_at` that contains an iso8601 date string. If one of these keys is not present on the domain object, it is open. Suspended instances may in some cases be obfuscated, which means they will have some letters replaced by `*` to make it more difficult for bad actors to target instances with harassment.
|
||||
|
||||
Whether a flat response or a more detailed response is returned, domains will be sorted alphabetically by hostname.
|
||||
schema:
|
||||
items:
|
||||
|
|
@ -8964,6 +9242,7 @@ paths:
|
|||
description: internal server error
|
||||
security:
|
||||
- OAuth2 Bearer: []
|
||||
summary: List peer domains.
|
||||
tags:
|
||||
- instance
|
||||
/api/v1/instance/rules:
|
||||
|
|
@ -9354,7 +9633,7 @@ paths:
|
|||
security:
|
||||
- OAuth2 Bearer:
|
||||
- read:lists
|
||||
summary: Get all lists for owned by authorized user.
|
||||
summary: Get all lists owned by authorized user.
|
||||
tags:
|
||||
- lists
|
||||
post:
|
||||
|
|
@ -10669,6 +10948,159 @@ paths:
|
|||
summary: Get one report with the given id.
|
||||
tags:
|
||||
- reports
|
||||
/api/v1/scheduled_statuses:
|
||||
get:
|
||||
operationId: getScheduledStatuses
|
||||
parameters:
|
||||
- description: Return only statuses *OLDER* than the given max status ID. The status with the specified ID will not be included in the response.
|
||||
in: query
|
||||
name: max_id
|
||||
type: string
|
||||
- description: Return only statuses *newer* than the given since status ID. The status with the specified ID will not be included in the response.
|
||||
in: query
|
||||
name: since_id
|
||||
type: string
|
||||
- description: Return only statuses *immediately newer* than the given min ID. The status with the specified ID will not be included in the response.
|
||||
in: query
|
||||
name: min_id
|
||||
type: string
|
||||
- default: 20
|
||||
description: Number of scheduled statuses to return.
|
||||
in: query
|
||||
name: limit
|
||||
type: integer
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: ""
|
||||
headers:
|
||||
Link:
|
||||
description: Links to the next and previous queries.
|
||||
type: string
|
||||
schema:
|
||||
items:
|
||||
$ref: '#/definitions/scheduledStatus'
|
||||
type: array
|
||||
"400":
|
||||
description: bad request
|
||||
"401":
|
||||
description: unauthorized
|
||||
"404":
|
||||
description: not found
|
||||
"406":
|
||||
description: not acceptable
|
||||
"500":
|
||||
description: internal server error
|
||||
security:
|
||||
- OAuth2 Bearer:
|
||||
- read:statuses
|
||||
summary: Get an array of statuses scheduled by authorized user.
|
||||
tags:
|
||||
- scheduled_statuses
|
||||
/api/v1/scheduled_statuses/{id}:
|
||||
delete:
|
||||
operationId: deleteScheduledStatus
|
||||
parameters:
|
||||
- description: ID of the status
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: status canceled
|
||||
"400":
|
||||
description: bad request
|
||||
"401":
|
||||
description: unauthorized
|
||||
"404":
|
||||
description: not found
|
||||
"406":
|
||||
description: not acceptable
|
||||
"500":
|
||||
description: internal server error
|
||||
security:
|
||||
- OAuth2 Bearer:
|
||||
- write:statuses
|
||||
summary: Cancel a scheduled status with the given id.
|
||||
tags:
|
||||
- scheduled_statuses
|
||||
get:
|
||||
operationId: getScheduledStatus
|
||||
parameters:
|
||||
- description: ID of the status
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
type: string
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: ""
|
||||
schema:
|
||||
$ref: '#/definitions/scheduledStatus'
|
||||
"400":
|
||||
description: bad request
|
||||
"401":
|
||||
description: unauthorized
|
||||
"404":
|
||||
description: not found
|
||||
"406":
|
||||
description: not acceptable
|
||||
"500":
|
||||
description: internal server error
|
||||
security:
|
||||
- OAuth2 Bearer:
|
||||
- read:statuses
|
||||
summary: Get a scheduled status with the given id.
|
||||
tags:
|
||||
- scheduled_statuses
|
||||
put:
|
||||
operationId: updateScheduledStatus
|
||||
parameters:
|
||||
- description: ID of the status
|
||||
in: path
|
||||
name: id
|
||||
required: true
|
||||
type: string
|
||||
- description: |-
|
||||
ISO 8601 Datetime at which to schedule a status.
|
||||
|
||||
Must be at least 5 minutes in the future.
|
||||
format: date-time
|
||||
in: formData
|
||||
name: scheduled_at
|
||||
type: string
|
||||
x-go-name: ScheduledAt
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: ""
|
||||
schema:
|
||||
$ref: '#/definitions/scheduledStatus'
|
||||
"400":
|
||||
description: bad request
|
||||
"401":
|
||||
description: unauthorized
|
||||
"404":
|
||||
description: not found
|
||||
"406":
|
||||
description: not acceptable
|
||||
"422":
|
||||
description: unprocessable content
|
||||
"500":
|
||||
description: internal server error
|
||||
security:
|
||||
- OAuth2 Bearer:
|
||||
- write:statuses
|
||||
summary: Update a scheduled status's publishing date.
|
||||
tags:
|
||||
- scheduled_statuses
|
||||
/api/v1/statuses:
|
||||
post:
|
||||
consumes:
|
||||
|
|
@ -10793,7 +11225,6 @@ paths:
|
|||
|
||||
Providing this parameter with a *future* time will cause ScheduledStatus to be returned instead of Status.
|
||||
Must be at least 5 minutes in the future.
|
||||
This feature isn't implemented yet.
|
||||
|
||||
Providing this parameter with a *past* time will cause the status to be backdated,
|
||||
and will not push it to the user's followers. This is intended for importing old statuses.
|
||||
|
|
@ -10856,10 +11287,10 @@ paths:
|
|||
description: not found
|
||||
"406":
|
||||
description: not acceptable
|
||||
"422":
|
||||
description: unprocessable content
|
||||
"500":
|
||||
description: internal server error
|
||||
"501":
|
||||
description: scheduled_at was set, but this feature is not yet implemented
|
||||
security:
|
||||
- OAuth2 Bearer:
|
||||
- write:statuses
|
||||
|
|
@ -11146,7 +11577,7 @@ paths:
|
|||
description: internal server error
|
||||
security:
|
||||
- OAuth2 Bearer:
|
||||
- write:statuses
|
||||
- write:favourites
|
||||
summary: Star/like/favourite the given status, if permitted.
|
||||
tags:
|
||||
- statuses
|
||||
|
|
@ -11684,6 +12115,32 @@ paths:
|
|||
summary: Initiate a websocket connection for live streaming of statuses and notifications.
|
||||
tags:
|
||||
- streaming
|
||||
/api/v1/suggestions:
|
||||
get:
|
||||
description: 'THIS ENDPOINT IS CURRENTLY NOT FULLY IMPLEMENTED: it will always return an empty array.'
|
||||
operationId: getSuggestions
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: ""
|
||||
schema:
|
||||
items:
|
||||
type: object
|
||||
maxItems: 0
|
||||
type: array
|
||||
"401":
|
||||
description: unauthorized
|
||||
"403":
|
||||
description: forbidden
|
||||
"406":
|
||||
description: not acceptable
|
||||
security:
|
||||
- OAuth2 Bearer:
|
||||
- read
|
||||
summary: Accounts that are promoted by staff, or that the user has had past positive interactions with, but is not yet following.
|
||||
tags:
|
||||
- suggestions
|
||||
/api/v1/tags/{tag_name}:
|
||||
get:
|
||||
description: If the tag does not exist, this method will not create it in the database.
|
||||
|
|
@ -12136,6 +12593,63 @@ paths:
|
|||
summary: Invalidate the target token, removing it from the database and making it unusable.
|
||||
tags:
|
||||
- tokens
|
||||
/api/v1/trends/links:
|
||||
get:
|
||||
description: 'THIS ENDPOINT IS CURRENTLY NOT FULLY IMPLEMENTED: it will always return an empty array.'
|
||||
operationId: getTrendingLinks
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: ""
|
||||
schema:
|
||||
items:
|
||||
type: object
|
||||
maxItems: 0
|
||||
type: array
|
||||
"406":
|
||||
description: not acceptable
|
||||
summary: Links that have been shared more than others.
|
||||
tags:
|
||||
- trends
|
||||
/api/v1/trends/statuses:
|
||||
get:
|
||||
description: 'THIS ENDPOINT IS CURRENTLY NOT FULLY IMPLEMENTED: it will always return an empty array.'
|
||||
operationId: getTrendingStatuses
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: ""
|
||||
schema:
|
||||
items:
|
||||
type: object
|
||||
maxItems: 0
|
||||
type: array
|
||||
"406":
|
||||
description: not acceptable
|
||||
summary: Statuses that have been interacted with more than others.
|
||||
tags:
|
||||
- trends
|
||||
/api/v1/trends/tags:
|
||||
get:
|
||||
description: 'THIS ENDPOINT IS CURRENTLY NOT FULLY IMPLEMENTED: it will always return an empty array.'
|
||||
operationId: getTrendingTags
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: ""
|
||||
schema:
|
||||
items:
|
||||
type: object
|
||||
maxItems: 0
|
||||
type: array
|
||||
"406":
|
||||
description: not acceptable
|
||||
summary: View hashtags that are currently being used more frequently than usual.
|
||||
tags:
|
||||
- trends
|
||||
/api/v1/user:
|
||||
get:
|
||||
operationId: getUser
|
||||
|
|
@ -13434,6 +13948,7 @@ securityDefinitions:
|
|||
read:applications: grants read access to user-managed applications
|
||||
read:blocks: grants read access to blocks
|
||||
read:bookmarks: grants read access to bookmarks
|
||||
read:custom_emojis: grants read access to custom emojis
|
||||
read:favourites: grants read access to accounts
|
||||
read:filters: grants read access to filters
|
||||
read:follows: grants read access to follows
|
||||
|
|
|
|||
|
|
@ -64,4 +64,12 @@ accounts-allow-custom-css: false
|
|||
# Examples: [500, 5000, 9999]
|
||||
# Default: 10000
|
||||
accounts-custom-css-length: 10000
|
||||
|
||||
# Int. The maximum number of profile fields allowed for each account.
|
||||
#
|
||||
# Note that going way higher than the default might break federation.
|
||||
#
|
||||
# Examples: [4, 6, 12]
|
||||
# Default: 6
|
||||
accounts-max-profile-fields: 6
|
||||
```
|
||||
|
|
|
|||
|
|
@ -2,21 +2,21 @@
|
|||
|
||||
GoToSocial stores statuses, accounts, etc, in a database. This can be either [SQLite](https://sqlite.org/index.html) or [Postgres](https://www.postgresql.org/).
|
||||
|
||||
By default, GoToSocial will use Postgres, but this is easy to change.
|
||||
GoToSocial has no default configured database type or address. In most situations, we recommend the use of SQLite.
|
||||
|
||||
## SQLite
|
||||
|
||||
SQLite, as the name implies, is the lightest database type that GoToSocial can use. It stores entries in a simple file format, usually in the same directory as the GoToSocial binary itself. SQLite is great for small instances and single-board computers, where a dedicated database would be overkill.
|
||||
|
||||
To configure GoToSocial to use SQLite, change `db-type` to `sqlite`. The `address` setting will then be a filename instead of an address, so you will want to change it to `sqlite.db` or something similar.
|
||||
To use SQLite, keep the default `db-type` as `sqlite`. By default the database will be kept in the same directory as the GoToSocial binary, ie., `./sqlite.db`, but you can change this using `db-address` if you want to keep it somewhere else. In the same directory as the `sqlite.db` file, the SQLite driver will also create files `sqlite.db-wal` and `sqlite.db-shm` for temporary storage.
|
||||
|
||||
Note that the `:memory:` setting will use an *in-memory database* which will be wiped when your GoToSocial instance stops running. This is for testing only and is absolutely not suitable for running a proper instance, so *don't do this*.
|
||||
|
||||
## Postgres
|
||||
|
||||
Postgres is a heavier database format, which is useful for larger instances where you need to scale performance, or where you need to run your database on a dedicated machine separate from your GoToSocial instance (or do funky stuff like run a database cluster).
|
||||
Postgres (aka PostgreSQL) is a heavier database which is useful for larger instances where you need to run your database on a dedicated machine separate from your GoToSocial instance (or do funky stuff like run a database cluster).
|
||||
|
||||
You can connect to Postgres using either a Unix socket connection, or via TCP, depending on what you've set as your `db-address` value.
|
||||
To configure GoToSocial to use Postgres, change `db-type` to `postgres`. The `db-address` setting will then be either a Unix socket connection or a hostname.
|
||||
|
||||
GoToSocial also supports connecting to Postgres using SSL/TLS over TCP. If you're running Postgres on a different machine from GoToSocial, and connecting to it via an IP address or hostname (as opposed to just running on localhost), then SSL/TLS is **CRUCIAL** to avoid leaking data all over the place!
|
||||
|
||||
|
|
@ -71,8 +71,8 @@ db-postgres-connection-string: 'postgres://myUser:myPass@localhost/db?search_pat
|
|||
|
||||
# String. Database type.
|
||||
# Options: ["postgres","sqlite"]
|
||||
# Default: "postgres"
|
||||
db-type: "postgres"
|
||||
# Default: ""
|
||||
db-type: "sqlite"
|
||||
|
||||
# String. Database address or parameters.
|
||||
#
|
||||
|
|
@ -87,29 +87,29 @@ db-type: "postgres"
|
|||
#
|
||||
# Examples: ["localhost","my.db.host","127.0.0.1","192.111.39.110",":memory:", "sqlite.db"]
|
||||
# Default: ""
|
||||
db-address: ""
|
||||
db-address: "sqlite.db"
|
||||
|
||||
# Int. Port for database connection.
|
||||
# Int. Port for postgres database connection; ignored for sqlite.
|
||||
# Examples: [5432, 1234, 6969]
|
||||
# Default: 5432
|
||||
db-port: 5432
|
||||
|
||||
# String. Username for the database connection.
|
||||
# String. Username for postgres database connection.
|
||||
# Examples: ["mydbuser","postgres","gotosocial"]
|
||||
# Default: ""
|
||||
db-user: ""
|
||||
|
||||
# String. Password to use for the database connection
|
||||
# String. Password to use for postgres database connection.
|
||||
# Examples: ["password123","verysafepassword","postgres"]
|
||||
# Default: ""
|
||||
db-password: ""
|
||||
|
||||
# String. Name of the database to use within the provided database type.
|
||||
# String. Name of the database to use for postgres database connection.
|
||||
# Examples: ["mydb","postgres","gotosocial"]
|
||||
# Default: "gotosocial"
|
||||
db-database: "gotosocial"
|
||||
|
||||
# String. Disable, enable, or require SSL/TLS connection to the database.
|
||||
# String. Disable, enable, or require SSL/TLS connection to postgres.
|
||||
# If "disable" then no TLS connection will be attempted.
|
||||
# If "enable" then TLS will be tried, but the database certificate won't be checked (for self-signed certs).
|
||||
# If "require" then TLS will be required to make a connection, and a valid certificate must be presented.
|
||||
|
|
|
|||
|
|
@ -26,6 +26,19 @@ log-db-queries: false
|
|||
# Default: true
|
||||
log-client-ip: true
|
||||
|
||||
# String. Format to use for formatting log entries.
|
||||
# Supports "logfmt" and "json", with examples below:
|
||||
#
|
||||
# logfmt:
|
||||
# func=router.(*Router).Start.func1 level=INFO msg="listening on 127.0.0.1:8080"
|
||||
#
|
||||
# json:
|
||||
# {"func":"router.(*Router).Start.func1", "level":"INFO", "msg":"listening on 127.0.0.1:8080"}
|
||||
#
|
||||
# Examples: ["logfmt", "json"]
|
||||
# Default: "logfmt"
|
||||
log-format: "logfmt"
|
||||
|
||||
# String. Format to use for the timestamp in log lines.
|
||||
# If set to the empty string, the timestamp will be
|
||||
# ommitted from the logs entirely.
|
||||
|
|
@ -94,14 +107,16 @@ account-domain: ""
|
|||
# Default: "https"
|
||||
protocol: "https"
|
||||
|
||||
# String. Address to bind the GoToSocial server to.
|
||||
# This can be an IPv4 address or an IPv6 address (surrounded in square brackets), or a hostname.
|
||||
# String. Address to bind the GoToSocial HTTP server to.
|
||||
# This can be an IPv4 address, an IPv6 address, or a hostname.
|
||||
#
|
||||
# The default value will bind to all interfaces, which makes the server
|
||||
# accessible by other machines. For most setups there is no need to change this.
|
||||
# If you are using GoToSocial in a reverse proxy setup with the proxy running on
|
||||
# the same machine, you will want to set this to "localhost" or an equivalent,
|
||||
# so that the proxy can't be bypassed.
|
||||
# Examples: ["0.0.0.0", "172.128.0.16", "localhost", "[::]", "[2001:db8::fed1]"]
|
||||
# accessible by other machines. For most setups you won't need to change this.
|
||||
# However, if you are using GoToSocial in a reverse proxy setup with the proxy
|
||||
# running on the same machine, you may want to set this to "localhost" or equivalent,
|
||||
# so that the proxy definitely can't be bypassed.
|
||||
#
|
||||
# Examples: ["0.0.0.0", "172.128.0.16", "localhost", "::1", "2001:db8::fed1"]
|
||||
# Default: "0.0.0.0"
|
||||
bind-address: "0.0.0.0"
|
||||
|
||||
|
|
|
|||
|
|
@ -60,9 +60,18 @@ http-client:
|
|||
#
|
||||
# THIS SETTING SHOULD BE USED FOR TESTING ONLY! IF YOU TURN THIS
|
||||
# ON WHILE RUNNING IN PRODUCTION YOU ARE LEAVING YOUR SERVER WIDE
|
||||
# OPEN TO MAN IN THE MIDDLE ATTACKS! DO NOT CHANGE THIS SETTING
|
||||
# OPEN TO MAN IN THE MIDDLE ATTACKS! DO NOT CHANGE THIS SETTING
|
||||
# UNLESS YOU KNOW EXACTLY WHAT YOU'RE DOING AND WHY YOU'RE DOING IT.
|
||||
#
|
||||
# Default: false
|
||||
tls-insecure-skip-verify: false
|
||||
|
||||
# Bool. Sets outgoing queries to webfinger, host-meta and nodeinfo to use
|
||||
# HTTP instead of HTTPS.
|
||||
#
|
||||
# THIS SETTING SHOULD BE USED FOR TESTING ONLY! DO NOT CHANGE THIS SETTING
|
||||
# UNLESS YOU KNOW EXACTLY WHAT YOU'RE DOING AND WHY YOU'RE DOING IT.
|
||||
#
|
||||
# Default: false
|
||||
insecure-outgoing: false
|
||||
```
|
||||
|
|
|
|||
|
|
@ -76,29 +76,61 @@ instance-federation-mode: "blocklist"
|
|||
# Default: false
|
||||
instance-federation-spam-filter: false
|
||||
|
||||
# Bool. Allow unauthenticated users to make queries to /api/v1/instance/peers?filter=open in order
|
||||
# to see a list of instances that this instance 'peers' with. Even if set to 'false', then authenticated
|
||||
# users (members of the instance) will still be able to query the endpoint.
|
||||
# Bool. Allow unauthenticated users to make queries to /api/v1/instance/peers?filter=open
|
||||
# in order to see a list of domains that this instance 'peers' with.
|
||||
#
|
||||
# Even if set to 'false', then authenticated users (members of the instance)
|
||||
# will still be able to query these endpoints using an OAuth token.
|
||||
#
|
||||
# Options: [true, false]
|
||||
# Default: false
|
||||
instance-expose-peers: false
|
||||
|
||||
# Bool. Allow unauthenticated users to make queries to /api/v1/instance/peers?filter=suspended in order
|
||||
# to see a list of instances that this instance blocks/suspends. Even if set to 'false', then authenticated
|
||||
# users (members of the instance) will still be able to query the endpoint.
|
||||
# Bool. Allow unauthenticated users to make queries to the following instance API
|
||||
# endpoints in order to see a list of domains that this instance explicitly blocks,
|
||||
# including the public reason for each block:
|
||||
#
|
||||
# WARNING: Setting this variable to 'true' may result in your instance being scraped by blocklist scrapers.
|
||||
# - /api/v1/instance/peers?filter=blocklist
|
||||
# - /api/v1/instance/peers?filter=suspended (deprecated)
|
||||
# - /api/v1/instance/domain_blocks
|
||||
#
|
||||
# Even if set to 'false', then authenticated users (members of the instance)
|
||||
# will still be able to query these endpoints using an OAuth token.
|
||||
#
|
||||
# WARNING: Setting to 'true' may result in your instance being targeted by blocklist scrapers.
|
||||
# See: https://docs.gotosocial.org/en/latest/admin/domain_blocks/#block-announce-bots
|
||||
#
|
||||
# Options: [true, false]
|
||||
# Default: false
|
||||
instance-expose-suspended: false
|
||||
instance-expose-blocklist: false
|
||||
|
||||
# Bool. Allow unauthenticated users to view /about/suspended,
|
||||
# showing the HTML rendered list of instances that this instance blocks/suspends.
|
||||
# Bool. Allow unauthenticated users to view /about/domain_blocks,
|
||||
# which shows an HTML-rendered list of domains that this instance blocks,
|
||||
# including the public reason for each block.
|
||||
# Options: [true, false]
|
||||
# Default: false
|
||||
instance-expose-suspended-web: false
|
||||
instance-expose-blocklist-web: false
|
||||
|
||||
# Bool. Allow unauthenticated users to make queries to the following instance API
|
||||
# endpoints in order to see a list of domains that this instance explicitly allows
|
||||
# including the public reason for each allow:
|
||||
#
|
||||
# - /api/v1/instance/peers?filter=allowlist
|
||||
# - /api/v1/instance/domain_allows
|
||||
#
|
||||
# Even if set to 'false', then authenticated users (members of the instance)
|
||||
# will still be able to query these endpoints using an OAuth token.
|
||||
#
|
||||
# Options: [true, false]
|
||||
# Default: false
|
||||
instance-expose-allowlist: false
|
||||
|
||||
# Bool. Allow unauthenticated users to view /about/domain_allows,
|
||||
# which shows an HTML-rendered list of domains that this instance allows,
|
||||
# including the public reason for each allow.
|
||||
# Options: [true, false]
|
||||
# Default: false
|
||||
instance-expose-allowlist-web: false
|
||||
|
||||
# Bool. Allow unauthenticated users to make queries to /api/v1/timelines/public in order
|
||||
# to see a list of public posts on this server. Even if set to 'false', then authenticated
|
||||
|
|
@ -107,6 +139,21 @@ instance-expose-suspended-web: false
|
|||
# Default: false
|
||||
instance-expose-public-timeline: false
|
||||
|
||||
# Bool. Allow unauthenticated access to /api/v1/custom_emojis, which
|
||||
# exposes the list of custom emojis available to users on this server.
|
||||
#
|
||||
# Setting this to 'true' may alleviate issues with clients that do not
|
||||
# provide an access token in their requests to the emojis endpoint, but
|
||||
# it also means that anyone can look at what emojis are installed on your
|
||||
# instance, which may present privacy / safety issues.
|
||||
#
|
||||
# Even if set to 'false', authenticated users (ie., instance members)
|
||||
# will still be able to query the endpoint using an access token.
|
||||
#
|
||||
# Options: [true, false]
|
||||
# Default: false
|
||||
instance-expose-custom-emojis: false
|
||||
|
||||
# Bool. This flag tweaks whether GoToSocial will deliver ActivityPub messages
|
||||
# to the shared inbox of a recipient, if one is available, instead of delivering
|
||||
# each message to each actor who should receive a message individually.
|
||||
|
|
@ -174,9 +221,7 @@ instance-stats-mode: ""
|
|||
|
||||
# Bool. This flag controls whether local accounts may backdate statuses
|
||||
# using past dates with the scheduled_at param to /api/v1/statuses.
|
||||
# This flag does not affect scheduling posts in the future
|
||||
# (which is currently not implemented anyway),
|
||||
# nor can it prevent remote accounts from backdating their own statuses.
|
||||
# This flag can't prevent remote accounts from backdating their own statuses.
|
||||
#
|
||||
# If true, all local accounts may backdate statuses.
|
||||
# If false, status backdating will be disabled and an error will be returned if it's used.
|
||||
|
|
|
|||
|
|
@ -1,57 +0,0 @@
|
|||
# Observability
|
||||
|
||||
These settings let you tune and configure certain observability related behaviours.
|
||||
|
||||
## Metrics
|
||||
|
||||
Before enabling metrics, [read the guide](../advanced/metrics.md) and ensure you've taken the appropriate security measures for your setup.
|
||||
|
||||
## Settings
|
||||
|
||||
```yaml
|
||||
##################################
|
||||
##### OBSERVABILITY SETTINGS #####
|
||||
##################################
|
||||
|
||||
# String. Header name to use to extract a request or trace ID from. Typically set by a
|
||||
# loadbalancer or proxy.
|
||||
# Default: "X-Request-Id"
|
||||
request-id-header: "X-Request-Id"
|
||||
|
||||
# Bool. Enable OpenTelemetry based tracing support.
|
||||
# Default: false
|
||||
tracing-enabled: false
|
||||
|
||||
# String. Set the transport protocol for the tracing system. Can either be "grpc"
|
||||
# for OTLP gRPC, or "http" for OTLP HTTP.
|
||||
# Options: ["grpc", "http"]
|
||||
# Default: "grpc"
|
||||
tracing-transport: "grpc"
|
||||
|
||||
# String. Endpoint of the trace ingester. When using the gRPC or HTTP based
|
||||
# transports, provide the endpoint as a single address/port combination without a
|
||||
# protocol scheme.
|
||||
# Examples: ["localhost:4317"]
|
||||
# Default: ""
|
||||
tracing-endpoint: ""
|
||||
|
||||
# Bool. Disable TLS for the gRPC and HTTP transport protocols.
|
||||
# Default: false
|
||||
tracing-insecure-transport: false
|
||||
|
||||
# Bool. Enable OpenTelemetry based metrics support.
|
||||
# Default: false
|
||||
metrics-enabled: false
|
||||
|
||||
# Bool. Enable HTTP Basic Authentication for Prometheus metrics endpoint.
|
||||
# Default: false
|
||||
metrics-auth-enabled: false
|
||||
|
||||
# String. Username for Prometheus metrics endpoint.
|
||||
# Default: ""
|
||||
metrics-auth-username: ""
|
||||
|
||||
# String. Password for Prometheus metrics endpoint.
|
||||
# Default: ""
|
||||
metrics-auth-password: ""
|
||||
```
|
||||
63
docs/configuration/observability_and_metrics.md
Normal file
63
docs/configuration/observability_and_metrics.md
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
# Observability and Metrics
|
||||
|
||||
These settings let you tune and configure certain observability related behaviours.
|
||||
|
||||
GoToSocial uses OpenTelemetry. The metrics and trace exporters can be configured using the standard OpenTelemetry SDK environment variables. For a full reference, see [the OpenTelemetry docs](https://opentelemetry.io/docs/languages/sdk-configuration/).
|
||||
|
||||
## Metrics
|
||||
|
||||
Before enabling metrics, [read the guide](../advanced/metrics.md) and ensure you've taken the appropriate security measures for your setup.
|
||||
|
||||
If you want to expose metrics with (basic) authentication, you'll need to do this with a reverse proxy.
|
||||
|
||||
For more information and examples, see the [GtS metrics documentation](https://docs.gotosocial.org/en/latest/advanced/metrics/).
|
||||
|
||||
## Settings
|
||||
|
||||
```yaml
|
||||
##############################################
|
||||
##### OBSERVABILITY AND METRICS SETTINGS #####
|
||||
##############################################
|
||||
|
||||
# String. Header name to use to extract a request or
|
||||
# trace ID from. Typically set by a loadbalancer or proxy.
|
||||
#
|
||||
# Default: "X-Request-Id"
|
||||
request-id-header: "X-Request-Id"
|
||||
|
||||
# Bool. Enable OpenTelemetry based tracing support.
|
||||
#
|
||||
# When enabling tracing, you must also configure a traces
|
||||
# exporter using the OTEL environment variable documented here:
|
||||
#
|
||||
# https://opentelemetry.io/docs/languages/sdk-configuration/general/#otel_traces_exporter
|
||||
#
|
||||
# Default: false
|
||||
tracing-enabled: false
|
||||
|
||||
# Bool. Enable OpenTelemetry based metrics support.
|
||||
#
|
||||
# To expose Prometheus metrics, you must configure a metrics producer and
|
||||
# a metrics exporter, using the OTEL environment variables documented here:
|
||||
#
|
||||
# https://pkg.go.dev/go.opentelemetry.io/contrib/exporters/autoexport#NewMetricReader
|
||||
#
|
||||
# Typically, you will want to set the following environment variables
|
||||
# (take note of the plural "producers" and singular "exporter"):
|
||||
#
|
||||
# - OTEL_METRICS_PRODUCERS=prometheus
|
||||
# - OTEL_METRICS_EXPORTER=prometheus
|
||||
#
|
||||
# With these variables set, a Prometheus metrics endpoint will be exposed at
|
||||
# localhost:9464/metrics. This can be further configured using the variables:
|
||||
#
|
||||
# - OTEL_EXPORTER_PROMETHEUS_HOST
|
||||
# - OTEL_EXPORTER_PROMETHEUS_PORT
|
||||
#
|
||||
# For more information, see the GtS metrics documentation here:
|
||||
#
|
||||
# https://docs.gotosocial.org/en/latest/advanced/metrics/
|
||||
#
|
||||
# Default: false
|
||||
metrics-enabled: false
|
||||
```
|
||||
|
|
@ -20,6 +20,9 @@ To validate your configuration, you can use the "Administration -> Actions -> Em
|
|||
!!! info
|
||||
For safety reasons, the smtp library used by GoToSocial will refuse to send authentication credentials over an unencrypted connection, unless the mail provider is running on localhost.
|
||||
|
||||
!!! info
|
||||
If your SMTP server offers `STARTTLS` in its EHLO response GoToSocial will try to use it. The SMTP server must hence also have valid SSL certificates. If you're sending mail via localhost and don't want to set up certificates make sure that your SMTP server doesn't announce STARTTLS support. In postfix this can be done via `-o smtpd_tls_security_level=none`.
|
||||
|
||||
## Settings
|
||||
|
||||
The configuration options for smtp are as follows:
|
||||
|
|
|
|||
|
|
@ -35,4 +35,14 @@ statuses-poll-option-max-chars: 50
|
|||
# Examples: [4, 6, 10]
|
||||
# Default: 6
|
||||
statuses-media-max-files: 6
|
||||
|
||||
# Int. Maximum number of statuses a user can schedule at time.
|
||||
# Examples: [300]
|
||||
# Default: 300
|
||||
scheduled-statuses-max-total: 300
|
||||
|
||||
# Int. Maximum number of statuses a user can schedule for a single day.
|
||||
# Examples: [25]
|
||||
# Default: 25
|
||||
scheduled-statuses-max-daily: 25
|
||||
```
|
||||
|
|
|
|||
|
|
@ -101,6 +101,20 @@ storage-s3-secret-key: ""
|
|||
# Default: ""
|
||||
storage-s3-bucket: ""
|
||||
|
||||
|
||||
# String. Key prefix to use for the S3 storage.
|
||||
# This is optional.
|
||||
#
|
||||
# Prefix must end with a trailing slash.
|
||||
#
|
||||
# This is useful if you want to store multiple instances in the same bucket,
|
||||
# or if you want to store your data in a subdirectory of the bucket.
|
||||
# This has no effect if the storage backend isn't "s3".
|
||||
#
|
||||
# Examples: ["gts-instance1/", "gts-instance2/"]
|
||||
# Default: ""
|
||||
storage-s3-key-prefix: ""
|
||||
|
||||
# String. Bucket lookup type.
|
||||
#
|
||||
# If you know what kind of bucket lookup type you need you can specify it here.
|
||||
|
|
|
|||
|
|
@ -2,11 +2,14 @@
|
|||
|
||||
To correctly enforce [rate limiting](../api/ratelimiting.md), GoToSocial relies on the concept of "trusted proxies" in order to accurately determine the IP address of clients accessing your server.
|
||||
|
||||
A "trusted proxy" is an intermediate network hop that GoToSocial can be instructed to trust to provide a correct client IP address.
|
||||
A "trusted proxy" is an intermediate network hop that GoToSocial can be instructed to trust to provide a correct client IP address via an `X-Forwarded-For` or `X-Real-IP` header.
|
||||
|
||||
For example, if you are running in a reverse proxy configuration with Docker + Nginx, then the Docker network address of Nginx should be configured as a trusted proxy, since all traffic from the wider internet will come into GoToSocial via Nginx.
|
||||
|
||||
Without setting `trusted-proxies` correctly, GoToSocial will see all incoming client IP addresses as the same address, which leads to rate limiting issues, since GoToSocial uses client IP addresses to bucket rate limits.
|
||||
If `trusted-proxies` is not set correctly, GoToSocial will see all incoming client IP addresses as the same address. This will lead to rate limiting issues, since GoToSocial uses client IP addresses to bucket rate limits.
|
||||
|
||||
!!! tip
|
||||
If you reached this page via the trusted proxies warning on a GoToSocial page, and the suggested address to add is `127.0.0.1`, then before you try any of the other troubleshooting steps, ensure that your reverse proxy is configured to correctly pass either `X-Forwarded-For` or `X-Real-IP`.
|
||||
|
||||
## tl;dr: How to set `trusted-proxies` correctly
|
||||
|
||||
|
|
|
|||
|
|
@ -38,4 +38,4 @@ We introduced a sign-up flow in v0.16.0. The server you want to sign up to must
|
|||
|
||||
## Why's it still in Beta?
|
||||
|
||||
Take a look at the [list of open bugs](https://codeberg.org/superseriousbusiness/gotosocial/issues?q=is%3Aissue+is%3Aopen+label%3Abug) and the [roadmap](https://codeberg.org/superseriousbusiness/gotosocial/src/branch/main/ROADMAP.md) for a more detailed rundown.
|
||||
Take a look at the [list of open bugs](https://codeberg.org/superseriousbusiness/gotosocial/issues?labels=378161) and the [roadmap](https://codeberg.org/superseriousbusiness/gotosocial/src/branch/main/ROADMAP.md) for a more detailed rundown.
|
||||
|
|
|
|||
|
|
@ -248,6 +248,90 @@ Another difference between GoToSocial and other server implementations is that G
|
|||
|
||||
Instead, to build a view of a GoToSocial user's pinned posts, it is recommended that remote instances simply poll a GoToSocial Actor's `featured` collection every so often, and add/remove posts in their cached representation as appropriate.
|
||||
|
||||
## `hidesToPublicFromUnauthedWeb` and `hidesCcPublicFromUnauthedWeb`
|
||||
|
||||
GoToSocial uses the properties `hidesToPublicFromUnauthedWeb` and `hidesCcPublicFromUnauthedWeb` to indicate whether an actor prefers to hide posts addressed `to` or `cc` public from unauthenticated (ie., logged-out) visitors to web pages, web apps, and web APIs.
|
||||
|
||||
Some background for this: many ActivityPub server softwares allow unauthenticated visitors to the profile web page of an actor to see a list of posts that an actor has created that are addressed either `to` or `cc` public. These are often called "public" posts, and "unlisted", "unlocked", or "quiet public" posts, respectively. GoToSocial provides [a settings flag](../user_guide/settings.md#visibility-level-of-posts-to-show-on-your-profile) that allows GtS accounts to hide posts from the web view of their profile, as one layer of protection to make it more of a nuisance to scrape/stalk someone with a GtS account.
|
||||
|
||||
While this setting works for hiding posts of an actor *on their own instance*, prior to GoToSocial v0.20.0, this preference was not federated out to other instances, nor was it federated in from other instances. This leads to two problems:
|
||||
|
||||
1. Many other fedi server softwares permit logged-out visitors, via a web app, to look up profiles of *remote* accounts, and to see public and unlisted posts created by those accounts. This means that it is trivial to work around the ability of GtS users to hide their posts from the web. For example, say a GtS user at `@someone@gts.example.org` locks down their profile by setting the visibility of posts on their profile to "none"; this prevents visitors to `gts.example.org` from seeing posts, but one could visit eg. `mastodon.example.org` and, while logged out, look up `@someone@gts.example.org`, and see all the posts there that have been sent to, or dereferenced by, actors on `mastodon.example.org`. This makes the GtS user's choice to hide their posts significantly less meaningful.
|
||||
2. In an effort to support this extra layer of privacy, by default GoToSocial instances do not show posts from remote instances unless they are addressed `to` public. For example, if someone from `mastodon.example.org` were to reply to a post by `@someone@gts.example.org`, and the reply was only addressed `cc` public instead of `to` public, the GtS instance `gts.example.org` would *not* show that reply in the web view, as it could not determine the preferences of the user from `mastodon.example.org` with regard to showing the "quiet public" post to logged-out visitors to the web page. This could be frustrating for the GtS user, as they might want to show a more complete picture of the thread that they started, right there on their instance; this could also frustrate the Mastodon user, as are used to their "quiet public" posts being visible on the web even when logged out.
|
||||
|
||||
The actor properties `hidesToPublicFromUnauthedWeb` and `hidesCcPublicFromUnauthedWeb` are a move towards solving these issues, by allowing actors to signal their preferences for hiding or showing `to`- and/or `cc`-public posts to unauthenticated visitors via the web.
|
||||
|
||||
For example, the following actor representation indicates that the actor is happy to show both "unlisted" and "public" posts via unauthed web view (this represents the de-facto default for actors on Mastodon and most other server softwares):
|
||||
|
||||
```json
|
||||
{
|
||||
"@context": [
|
||||
"https://gotosocial.org/ns",
|
||||
"https://www.w3.org/ns/activitystreams"
|
||||
],
|
||||
"type": "Person",
|
||||
[... other properties here ...]
|
||||
"hidesToPublicFromUnauthedWeb": false,
|
||||
"hidesCcPublicFromUnauthedWeb": false,
|
||||
[... other properties here ...]
|
||||
}
|
||||
```
|
||||
|
||||
By contrast, the following indicates that the actor hides "unlisted" posts but is happy to show "public" posts unauthed (this is the default for actors on GtS instances):
|
||||
|
||||
```json
|
||||
{
|
||||
"@context": [
|
||||
"https://gotosocial.org/ns",
|
||||
"https://www.w3.org/ns/activitystreams"
|
||||
],
|
||||
"type": "Person",
|
||||
[... other properties here ...]
|
||||
"hidesToPublicFromUnauthedWeb": false,
|
||||
"hidesCcPublicFromUnauthedWeb": true,
|
||||
[... other properties here ...]
|
||||
}
|
||||
```
|
||||
|
||||
And the following shows that the actor wants to show no posts unauthed at all:
|
||||
|
||||
```json
|
||||
{
|
||||
"@context": [
|
||||
"https://gotosocial.org/ns",
|
||||
"https://www.w3.org/ns/activitystreams"
|
||||
],
|
||||
"type": "Person",
|
||||
[... other properties here ...]
|
||||
"hidesToPublicFromUnauthedWeb": true,
|
||||
"hidesCcPublicFromUnauthedWeb": true,
|
||||
[... other properties here ...]
|
||||
}
|
||||
```
|
||||
|
||||
Both `hidesToPublicFromUnauthedWeb` and `hidesCcPublicFromUnauthedWeb` are defined in [the GoToSocial json-ld `@context` document](https://gotosocial.org/ns).
|
||||
|
||||
In line with its emphasis on having people opt-in to greater visibility rather than opt-out, when receiving a post from a remote actor that does not set these flags, GoToSocial assumes `hidesToPublicFromUnauthedWeb` = `false`, and `hidesCcPublicFromUnauthedWeb` = `true`. That is, the pre-v0.20.x behavior of GoToSocial is still the default for remote servers that don't (yet) use these flags.
|
||||
|
||||
!!! note
|
||||
While unusual, it's possible for an actor to also specify that they want to show "unlisted" posts but hide "public" ones:
|
||||
|
||||
```json
|
||||
{
|
||||
"@context": [
|
||||
"https://gotosocial.org/ns",
|
||||
"https://www.w3.org/ns/activitystreams"
|
||||
],
|
||||
"type": "Person",
|
||||
[... other properties here ...]
|
||||
"hidesToPublicFromUnauthedWeb": true,
|
||||
"hidesCcPublicFromUnauthedWeb": false,
|
||||
[... other properties here ...]
|
||||
}
|
||||
```
|
||||
|
||||
GoToSocial respects these flags for incoming posts, but it does not let accounts set this combination of flags for outgoing posts. It may be desirable for other implementers to also prevent users from being able to set this state, as it doesn't make a lot of sense.
|
||||
|
||||
## Actor Migration / Aliasing
|
||||
|
||||
GoToSocial supports account migration from one instance/server to another through a combination of the `Move` activity, and the Actor Object properties `alsoKnownAs` and `movedTo`.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
# Interaction Policy
|
||||
|
||||
!!! warning "Feature still in development"
|
||||
Much like GoToSocial, this feature is still in pre-v1 beta development. Naming or schemas or methods of approval / rejection may change. We aim to finalize the document by GoToSocial v0.21.0, at which point it can be considered "ready" for other ActivityPub implementers to use.
|
||||
|
||||
GoToSocial uses the property `interactionPolicy` on posts, in order to indicate to remote instances what sort of interactions are (conditionally) permitted to be processed and stored by the origin server, for any given post.
|
||||
|
||||
The `@context` document for `interactionPolicy` and related objects and properties is at `https://gotosocial.org/ns`.
|
||||
|
|
@ -15,6 +18,9 @@ The `@context` document for `interactionPolicy` and related objects and properti
|
|||
|
||||
For better or worse, GoToSocial can offer only a best-effort, partial, technological solution to what is more or less an issue of social behavior and boundaries.
|
||||
|
||||
!!! info "Deprecated `always` and `approvalRequired` properties"
|
||||
Previous versions of this document used the properties `always` and `approvalRequired`. These are now deprecated in favor of `automaticApproval` and `manualApproval`. GoToSocial versions before v0.20.0 send and receive only these deprecated properties. GoToSocial v0.20.0 sends and receives both the deprecated and the new properties. GoToSocial v0.21.0 onwards uses only the new properties.
|
||||
|
||||
## Overview
|
||||
|
||||
`interactionPolicy` is an object property attached to the post-like `Object`s `Note`, `Article`, `Question`, etc, with the following format:
|
||||
|
|
@ -28,16 +34,16 @@ The `@context` document for `interactionPolicy` and related objects and properti
|
|||
[...],
|
||||
"interactionPolicy": {
|
||||
"canLike": {
|
||||
"always": [ "zero_or_more_uris_that_can_always_do_this" ],
|
||||
"approvalRequired": [ "zero_or_more_uris_that_require_approval_to_do_this" ]
|
||||
"automaticApproval": [ "zero_or_more_uris_that_can_always_do_this" ],
|
||||
"manualApproval": [ "zero_or_more_uris_that_require_approval_to_do_this" ]
|
||||
},
|
||||
"canReply": {
|
||||
"always": [ "zero_or_more_uris_that_can_always_do_this" ],
|
||||
"approvalRequired": [ "zero_or_more_uris_that_require_approval_to_do_this" ]
|
||||
"automaticApproval": [ "zero_or_more_uris_that_can_always_do_this" ],
|
||||
"manualApproval": [ "zero_or_more_uris_that_require_approval_to_do_this" ]
|
||||
},
|
||||
"canAnnounce": {
|
||||
"always": [ "zero_or_more_uris_that_can_always_do_this" ],
|
||||
"approvalRequired": [ "zero_or_more_uris_that_require_approval_to_do_this" ]
|
||||
"automaticApproval": [ "zero_or_more_uris_that_can_always_do_this" ],
|
||||
"manualApproval": [ "zero_or_more_uris_that_require_approval_to_do_this" ]
|
||||
}
|
||||
},
|
||||
[...]
|
||||
|
|
@ -52,10 +58,10 @@ In the `interactionPolicy` object:
|
|||
|
||||
And:
|
||||
|
||||
- `always` denotes ActivityPub URIs/IDs of `Actor`s or `Collection`s of `Actor`s who are permitted to create + distribute an interaction targeting a post without requiring manual approval by the post author.
|
||||
- `approvalRequired` denotes ActivityPub URIs/IDs of `Actor`s or `Collection`s of `Actor`s who are permitted to create an interaction targeting a post, but should wait for manual approval by the post author before distributing it (see [Requesting, Obtaining, and Validating Approval](#requesting-obtaining-and-validating-approval)).
|
||||
- `automaticApproval` denotes ActivityPub URIs/IDs of `Actor`s or `Collection`s of `Actor`s who will receive automated approval from the post author to create + distribute an interaction targeting a post.
|
||||
- `manualApproval` denotes ActivityPub URIs/IDs of `Actor`s or `Collection`s of `Actor`s who will receive approval from the post author at the author's own discretion, and may not receive approval at all, or may be rejected (see [Requesting, Obtaining, and Validating Approval](#requesting-obtaining-and-validating-approval)).
|
||||
|
||||
Valid URI entries in `always` and `approvalRequired` include:
|
||||
Valid URI entries in `automaticApproval` and `manualApproval` include:
|
||||
|
||||
- the magic ActivityStreams Public URI `https://www.w3.org/ns/activitystreams#Public`
|
||||
- the URIs of the post creator's `Following` and/or `Followers` collections
|
||||
|
|
@ -74,21 +80,21 @@ For example:
|
|||
```
|
||||
|
||||
!!! info
|
||||
Be aware that according to JSON-LD the values of `always` and `approvalRequired` can be either single strings or arrays of strings. That is, the following are all valid:
|
||||
Be aware that according to JSON-LD the values of `automaticApproval` and `manualApproval` can be either single strings or arrays of strings. That is, the following are all valid:
|
||||
|
||||
- Single string: `"always": "https://example.org/users/someone"`
|
||||
- Single-entry array: `"always": [ "https://example.org/users/someone" ]`
|
||||
- Multiple-entry array: `"always": [ "https://example.org/users/someone", "https://example.org/users/someone_else" ]`
|
||||
- Single string: `"automaticApproval": "https://example.org/users/someone"`
|
||||
- Single-entry array: `"automaticApproval": [ "https://example.org/users/someone" ]`
|
||||
- Multiple-entry array: `"automaticApproval": [ "https://example.org/users/someone", "https://example.org/users/someone_else" ]`
|
||||
|
||||
## Specifying Nobody
|
||||
|
||||
To specify that **nobody** can perform an interaction on a post **except** for its author (who is always permitted), implementations should set the `always` array to **just the URI of the post author**, and `approvalRequired` can be unset, `null`, or empty.
|
||||
To specify that **nobody** can perform an interaction on a post **except** for its author (who is always permitted), implementations should set the `automaticApproval` array to **just the URI of the post author**, and `manualApproval` can be unset, `null`, or empty.
|
||||
|
||||
For example, the following `canLike` value indicates that nobody can `Like` the post it is attached to except for the post author:
|
||||
|
||||
```json
|
||||
"canLike": {
|
||||
"always": "the_activitypub_uri_of_the_post_author"
|
||||
"automaticApproval": "the_activitypub_uri_of_the_post_author"
|
||||
},
|
||||
```
|
||||
|
||||
|
|
@ -103,13 +109,13 @@ Another example. The following `interactionPolicy` on a post by `https://example
|
|||
[...],
|
||||
"interactionPolicy": {
|
||||
"canLike": {
|
||||
"always": "https://www.w3.org/ns/activitystreams#Public"
|
||||
"automaticApproval": "https://www.w3.org/ns/activitystreams#Public"
|
||||
},
|
||||
"canReply": {
|
||||
"always": "https://example.org/users/someone"
|
||||
"automaticApproval": "https://example.org/users/someone"
|
||||
},
|
||||
"canAnnounce": {
|
||||
"always": "https://example.org/users/someone"
|
||||
"automaticApproval": "https://example.org/users/someone"
|
||||
}
|
||||
},
|
||||
[...]
|
||||
|
|
@ -128,28 +134,28 @@ For example:
|
|||
```json
|
||||
[...],
|
||||
"canReply": {
|
||||
"always": "https://example.org/users/someone",
|
||||
"approvalRequired": "https://www.w3.org/ns/activitystreams#Public"
|
||||
"automaticApproval": "https://example.org/users/someone",
|
||||
"manualApproval": "https://www.w3.org/ns/activitystreams#Public"
|
||||
},
|
||||
[...]
|
||||
```
|
||||
|
||||
Here, `@someone@example.org` is present in `always`, and is also implicitly present in the magic ActivityStreams Public collection in `approvalRequired`. In this case, they can always reply, as the `always` value is more explicit.
|
||||
Here, `@someone@example.org` is present in `automaticApproval`, and is also implicitly present in the magic ActivityStreams Public collection in `manualApproval`. In this case, they can always reply, as the `automaticApproval` value is more explicit.
|
||||
|
||||
Another example:
|
||||
|
||||
```json
|
||||
[...],
|
||||
"canReply": {
|
||||
"always": "https://www.w3.org/ns/activitystreams#Public",
|
||||
"approvalRequired": "https://example.org/users/someone"
|
||||
"automaticApproval": "https://www.w3.org/ns/activitystreams#Public",
|
||||
"manualApproval": "https://example.org/users/someone"
|
||||
},
|
||||
[...]
|
||||
```
|
||||
|
||||
Here, `@someone@example.org` is present in `approvalRequired`, but is also implicitly present in the magic ActivityStreams Public collection in `always`. In this case everyone can reply without approval, **except** for `@someone@example.org`, who requires approval.
|
||||
Here, `@someone@example.org` is present in `manualApproval`, but is also implicitly present in the magic ActivityStreams Public collection in `automaticApproval`. In this case everyone can reply without approval, **except** for `@someone@example.org`, who requires approval.
|
||||
|
||||
In case the **exact same** URI is present in both `always` and `approvalRequired`, the **highest level of permission** takes precedence (ie., a URI in `always` takes precedence over the same URI in `approvalRequired`).
|
||||
In case the **exact same** URI is present in both `automaticApproval` and `manualApproval`, the **highest level of permission** takes precedence (ie., a URI in `automaticApproval` takes precedence over the same URI in `manualApproval`).
|
||||
|
||||
## Default / fallback `interactionPolicy`
|
||||
|
||||
|
|
@ -164,20 +170,20 @@ When the `interactionPolicy` property is not present at all on a post, or the `i
|
|||
[...],
|
||||
"interactionPolicy": {
|
||||
"canLike": {
|
||||
"always": "https://www.w3.org/ns/activitystreams#Public"
|
||||
"automaticApproval": "https://www.w3.org/ns/activitystreams#Public"
|
||||
},
|
||||
"canReply": {
|
||||
"always": "https://www.w3.org/ns/activitystreams#Public"
|
||||
"automaticApproval": "https://www.w3.org/ns/activitystreams#Public"
|
||||
},
|
||||
"canAnnounce": {
|
||||
"always": "https://www.w3.org/ns/activitystreams#Public"
|
||||
"automaticApproval": "https://www.w3.org/ns/activitystreams#Public"
|
||||
}
|
||||
},
|
||||
[...]
|
||||
}
|
||||
```
|
||||
|
||||
As implied by the lack of any `approvalRequired` property in any of the sub-policies, the default value for `approvalRequired` is an empty array.
|
||||
As implied by the lack of any `manualApproval` property in any of the sub-policies, the default value for `manualApproval` is an empty array.
|
||||
|
||||
This default `interactionPolicy` was designed to reflect the de facto interaction policy of all posts from pre-v0.17.0 GoToSocial, and other ActivityPub server softwares, at the time of writing. That is to say, it is exactly what servers that are not interaction policy aware *already assume* about interaction permissions.
|
||||
|
||||
|
|
@ -200,7 +206,7 @@ If `canLike` is missing on an `interactionPolicy`, or the value of `canLike` is
|
|||
|
||||
```json
|
||||
"canLike": {
|
||||
"always": "https://www.w3.org/ns/activitystreams#Public"
|
||||
"automaticApproval": "https://www.w3.org/ns/activitystreams#Public"
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -212,7 +218,7 @@ If `canReply` is missing on an `interactionPolicy`, or the value of `canReply` i
|
|||
|
||||
```json
|
||||
"canReply": {
|
||||
"always": "https://www.w3.org/ns/activitystreams#Public"
|
||||
"automaticApproval": "https://www.w3.org/ns/activitystreams#Public"
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -224,7 +230,7 @@ If `canAnnounce` is missing on an `interactionPolicy`, or the value of `canAnnou
|
|||
|
||||
```json
|
||||
"canAnnounce": {
|
||||
"always": "https://www.w3.org/ns/activitystreams#Public"
|
||||
"automaticApproval": "https://www.w3.org/ns/activitystreams#Public"
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -266,7 +272,7 @@ Likewise, when enforcing received interaction policies, GoToSocial will **ALWAYS
|
|||
|
||||
As such, when sending out interaction policies, GoToSocial will **ALWAYS** add the URI of the post author to the `canLike.always`, `canReply.always`, and `canAnnounce.always` arrays, **UNLESS** they are already covered by the ActivityStreams magic public URI.
|
||||
|
||||
Likewise, when enforcing received interaction policies, GoToSocial will **ALWAYS** behave as though the URI of the post author themself is present in each `always` field, even if it wasn't.
|
||||
Likewise, when enforcing received interaction policies, GoToSocial will **ALWAYS** behave as though the URI of the post author themself is present in each `automaticApproval` field, even if it wasn't.
|
||||
|
||||
## Examples
|
||||
|
||||
|
|
@ -293,18 +299,18 @@ This can be achieved with the following `interactionPolicy`, which is attached t
|
|||
[...],
|
||||
"interactionPolicy": {
|
||||
"canLike": {
|
||||
"always": "https://www.w3.org/ns/activitystreams#Public"
|
||||
"automaticApproval": "https://www.w3.org/ns/activitystreams#Public"
|
||||
},
|
||||
"canReply": {
|
||||
"always": [
|
||||
"automaticApproval": [
|
||||
"https://example.org/users/the_mighty_zork",
|
||||
"https://example.org/users/booblover6969",
|
||||
"https://example.org/users/hodor"
|
||||
],
|
||||
"approvalRequired": "https://www.w3.org/ns/activitystreams#Public"
|
||||
"manualApproval": "https://www.w3.org/ns/activitystreams#Public"
|
||||
},
|
||||
"canAnnounce": {
|
||||
"always": [
|
||||
"automaticApproval": [
|
||||
"https://example.org/users/the_mighty_zork",
|
||||
"https://example.org/users/the_mighty_zork/followers",
|
||||
"https://example.org/users/booblover6969",
|
||||
|
|
@ -333,13 +339,13 @@ This can be achieved by setting the following `interactionPolicy` on every post
|
|||
[...],
|
||||
"interactionPolicy": {
|
||||
"canLike": {
|
||||
"always": "https://www.w3.org/ns/activitystreams#Public"
|
||||
"automaticApproval": "https://www.w3.org/ns/activitystreams#Public"
|
||||
},
|
||||
"canReply": {
|
||||
"always": "https://example.org/users/the_mighty_zork"
|
||||
"automaticApproval": "https://example.org/users/the_mighty_zork"
|
||||
},
|
||||
"canAnnounce": {
|
||||
"always": "https://www.w3.org/ns/activitystreams#Public"
|
||||
"automaticApproval": "https://www.w3.org/ns/activitystreams#Public"
|
||||
}
|
||||
},
|
||||
[...]
|
||||
|
|
@ -361,13 +367,13 @@ In this example, `@the_mighty_zork` wants to write a completely open post that c
|
|||
[...],
|
||||
"interactionPolicy": {
|
||||
"canLike": {
|
||||
"always": "https://www.w3.org/ns/activitystreams#Public"
|
||||
"automaticApproval": "https://www.w3.org/ns/activitystreams#Public"
|
||||
},
|
||||
"canReply": {
|
||||
"always": "https://www.w3.org/ns/activitystreams#Public"
|
||||
"automaticApproval": "https://www.w3.org/ns/activitystreams#Public"
|
||||
},
|
||||
"canAnnounce": {
|
||||
"always": "https://www.w3.org/ns/activitystreams#Public"
|
||||
"automaticApproval": "https://www.w3.org/ns/activitystreams#Public"
|
||||
}
|
||||
},
|
||||
[...]
|
||||
|
|
@ -416,7 +422,7 @@ This section describes the enforcement and verification of interaction policies,
|
|||
|
||||
### Requesting, Obtaining, and Validating Approval
|
||||
|
||||
When an actor's URI is in the `approvalRequired` array for a type of interaction, **or** their presence in a collection needs to be validated (see [Validating presence in a Followers / Following collection](#validating-presence-in-a-followers--following-collection)), implementations wishing to obtain approval for that actor to interact with a policied post should do the following:
|
||||
When an actor's URI is in the `manualApproval` array for a type of interaction, **or** their presence in a collection needs to be validated (see [Validating presence in a Followers / Following collection](#validating-presence-in-a-followers--following-collection)), implementations wishing to obtain approval for that actor to interact with a policied post should do the following:
|
||||
|
||||
1. Compose the interaction `Activity` (ie., `Like`, `Create` (reply), or `Announce`), as normal.
|
||||
2. Address the `Activity` `to` and `cc` the expected recipients for that `Activity`, as normal.
|
||||
|
|
@ -592,11 +598,11 @@ If the approval cannot be dereferenced, or does not pass validity checks, the in
|
|||
|
||||
### Validating presence in a Followers / Following collection
|
||||
|
||||
If an `Actor` interacting with an `object` (via `Like`, `inReplyTo`, or `Announce`) is permitted to do that interaction based on their presence in a `Followers` or `Following` collection in the `always` field of an interaction policy, then their server **should still wait** for an `Accept` to be received from the server of the target actor, before distributing the interaction more widely with the `approvedBy` property set to the URI/ID of the approval.
|
||||
If an `Actor` interacting with an `object` (via `Like`, `inReplyTo`, or `Announce`) is permitted to do that interaction based on their presence in a `Followers` or `Following` collection in the `automaticApproval` field of an interaction policy, then their server **should still wait** for an `Accept` to be received from the server of the target actor, before distributing the interaction more widely with the `approvedBy` property set to the URI/ID of the approval.
|
||||
|
||||
This is to prevent scenarios where third servers have to somehow verify the presence of the interacting `Actor` in the `Followers` or `Following` collection of the `Actor` being interacted with. It is simpler to allow the target server to do that verification, and to trust that their approval implicitly agrees that the interacting `Actor` is present in the relevant collection.
|
||||
|
||||
Likewise, when receiving an interaction from an `Actor` whose permission to interact matches with one of the `Following` or `Followers` collections in the `always` property, the server of the interacted-with `Actor` should ensure that they *always* send out an `Accept` as soon as possible, so that the interacting `Actor` server can send out the `Activity` with the proper proof of acceptance.
|
||||
Likewise, when receiving an interaction from an `Actor` whose permission to interact matches with one of the `Following` or `Followers` collections in the `automaticApproval` property, the server of the interacted-with `Actor` should ensure that they *always* send out an `Accept` as soon as possible, so that the interacting `Actor` server can send out the `Activity` with the proper proof of acceptance.
|
||||
|
||||
This process should bypass the normal "pending approval" stage whereby the server of the `Actor` being interacted with notifies them of the pending interaction, and waits for them to accept or reject, since there is no point notifying an `Actor` of a pending approval that they have already explicitly agreed to. In the GoToSocial codebase in particular, this is called "preapproval".
|
||||
|
||||
|
|
@ -606,7 +612,7 @@ This section describes optional behaviors that implementers *may* use when sendi
|
|||
|
||||
#### Always send out `Accept`s
|
||||
|
||||
Implementers may wish to *always* send out an `Accept` to remote interacters, even when the interaction is implicitly or explicitly permitted by their presence in the `always` array. When receiving such an `Accept`, implementations may still want to update their representation of the interaction to include an `approvedBy` URI pointing at an approval. This may be useful later on when handling revocations (TODO).
|
||||
Implementers may wish to *always* send out an `Accept` to remote interacters, even when the interaction is implicitly or explicitly permitted by their presence in the `automaticApproval` array. When receiving such an `Accept`, implementations may still want to update their representation of the interaction to include an `approvedBy` URI pointing at an approval. This may be useful later on when handling revocations (TODO).
|
||||
|
||||
#### Type hinting: inline `object` property on `Accept` and `Reject`
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,56 @@
|
|||
# Posts and Post Properties
|
||||
|
||||
## Attachments, Blurhash, and Focal Point
|
||||
|
||||
GoToSocial sends media attachments in the `attachment` property of posts using the following types:
|
||||
|
||||
- `Image` - any image type (webp, jpeg, gif, png, etc).
|
||||
- `Video` - any video type (mp4, mkv, webm, etc).
|
||||
- `Audio` - any audio type (mp3, flac, wma, etc).
|
||||
- `Document` - fallback for any other / unknown type.
|
||||
|
||||
Attachments sent from GoToSocial include the MIME `mediaType`, the `url` of the full-sized version of the media file, and the `summary` property, which can be interpreted by remote instance's as a short description / alt text for the attachment.
|
||||
|
||||
Types `Image` and `Video` will also include the `http://joinmastodon.org/ns#blurhash` property so that remotes can generate a colorful hash of the image. If an audio file included an embedded cover art image, then the `Audio` type will also include a blurhash. See the [Mastodon blurhash docs](https://docs.joinmastodon.org/spec/activitypub/#blurhash).
|
||||
|
||||
`Image` types may also include the `http://joinmastodon.org/ns#focalPoint` property, which will be an array of two floats between -1.0 and 1.0 indicating the x-y coordinates of the image's focal point. See the [Mastondon focalPoint docs](https://docs.joinmastodon.org/spec/activitypub/#focalPoint).
|
||||
|
||||
Here's an example of a `Note` with one attachment:
|
||||
|
||||
```json
|
||||
{
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
{
|
||||
"blurhash": "toot:blurhash",
|
||||
"focalPoint": {
|
||||
"@container": "@list",
|
||||
"@id": "toot:focalPoint"
|
||||
},
|
||||
"toot": "http://joinmastodon.org/ns#"
|
||||
}
|
||||
],
|
||||
"type": "Note",
|
||||
[...],
|
||||
"attachment": [
|
||||
{
|
||||
"blurhash": "LIIE|gRj00WB-;j[t7j[4nWBj[Rj",
|
||||
"focalPoint": [
|
||||
-0.5,
|
||||
0.5
|
||||
],
|
||||
"mediaType": "image/jpeg",
|
||||
"summary": "Black and white image of some 50's style text saying: Welcome On Board",
|
||||
"type": "Image",
|
||||
"url": "http://localhost:8080/fileserver/01F8MH17FWEB39HZJ76B6VXSKF/attachment/original/01F8MH6NEM8D7527KZAECTCR76.jpg"
|
||||
}
|
||||
],
|
||||
[...]
|
||||
}
|
||||
```
|
||||
|
||||
When receiving posts with attachments from remote instances, it will try to parse any of the four types `Image`, `Video`, `Audio`, or `Document` into media attachments. It doesn't matter which type is used. It will check for `blurhash` and `focalPoint` properties and use these if they are set. It will use the `summary` value as a short description / alt text, falling back to `name` if `summary` is not set.
|
||||
|
||||
## Hashtags
|
||||
|
||||
GoToSocial users can include hashtags in their posts, which indicate to other instances that that user wishes their post to be grouped together with other posts using the same hashtag, for discovery purposes.
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ For media storage, including [storage of remote media files in the cache](../adm
|
|||
|
||||
### Single-board Computers
|
||||
|
||||
GoToSocial's light system requirements means that it runs pretty well on decently-specced single-board computers. If running on a single-board computer, you should ensure that GoToSocial is using a USB drive (preferably an SSD) to store its database files and media, not SD card storage, since the latter tends to be too slow to run a database on.
|
||||
GoToSocial's light system requirements means that it runs pretty well on decently-specced single-board computers. If running on a single-board computer, you should ensure that GoToSocial is using a USB drive (preferably an SSD) to store its database files and media, not SD card storage, since the latter tends to be too slow to run a database on and can easily get corrupted with write-heavy workloads.
|
||||
|
||||
### VPS
|
||||
|
||||
|
|
|
|||
|
|
@ -63,8 +63,6 @@ You'll probably need to change the following settings:
|
|||
|
||||
- Set `host` to whatever hostname you're going to be running the server on (eg., `example.org`).
|
||||
- Set `port` to `443`.
|
||||
- Set `db-type` to `sqlite`.
|
||||
- Set `db-address` to `sqlite.db`.
|
||||
- Set `storage-local-base-path` to the storage directory you created above (eg., `/gotosocial/storage`).
|
||||
- Set `letsencrypt-enabled` to `true`.
|
||||
- Set `letsencrypt-cert-dir` to the certificate storage directory you created above (eg., `/gotosocial/storage/certs`).
|
||||
|
|
|
|||
|
|
@ -10,6 +10,6 @@ GoToSocial 在主域名上提供一个 `robots.txt` 文件。该文件包含试
|
|||
|
||||
AI 爬虫来自一个[社区维护的仓库][airobots]。目前是手动保持同步的。如果你知道有任何遗漏的爬虫,请给他们提交一个 PR!
|
||||
|
||||
众所周知,很多 AI 爬虫在 `robots.txt` 不允许其 User-Agent 的情况下,仍然会忽略对应规则并继续抓去内容。这意味着 `robots.txt` 文件并不是确保 AI 爬虫不抓取你的内容的万无一失的方法。除此以外,你可能还需要考虑通过[请求标头过滤](request_filtering_modes.md)来阻止对应 User-Agent,以及启用基于工作证明的[爬虫防护](../advanced/scraper_deterrence.md)。
|
||||
众所周知,很多 AI 爬虫在 `robots.txt` 不允许其 User-Agent 的情况下,仍然会忽略对应规则并继续抓去内容。这意味着 `robots.txt` 文件并不是确保 AI 爬虫不抓取你的内容的万无一失的方法。除此以外,你可能还需要考虑通过[请求标头过滤](request_filtering_modes.md)来阻止对应 User-Agent。
|
||||
|
||||
[airobots]: https://github.com/ai-robots-txt/ai.robots.txt/
|
||||
|
|
|
|||
|
|
@ -54,4 +54,4 @@ location /metrics {
|
|||
|
||||
[otel]: https://opentelemetry.io/
|
||||
[prom]: https://prometheus.io/docs/instrumenting/exposition_formats/
|
||||
[obs]: ../configuration/observability.md
|
||||
[obs]: ../configuration/observability_and_metrics.md
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
# 爬虫防护
|
||||
|
||||
GoToSocial 提供一个可选的、基于工作量证明的爬虫和自动化 HTTP 客户端防护机制,可在账户页和贴文页的网页视图上启用。
|
||||
|
||||
它的工作原理是:针对每个传入的 HTTP 请求,系统会根据客户端信息和当前时间生成一个唯一质询(一个十六进制编码的 SHA256 哈希值)。然后,它要求客户端为该质询的一部分找到一个附加值,使(附加值+质询部分)组合计算出的新 SHA256 哈希值(同样为十六进制编码)至少包含 4 个前导 '0' 字符。这个质询会通过一个极简的等待页面呈现给客户端,该页面包含一个独立的 JavaScript worker 来计算解决方案。
|
||||
|
||||
一旦客户端提供了此质询的解,并通过在查询参数中携带该方案刷新页面,GoToSocial 将验证此方案。验证成功后,服务端会返回用户期望访问的账户或贴文页面,并设置一个 Cookie。该 Cookie 允许用户在接下来最多一小时内免验证访问该实例。
|
||||
|
||||
启用此功能的目的是让自动化数据收集(例如 AI 公司、搜索引擎)对你实例的账户和贴文页面进行爬取的行为,在经济上变得不可行。唯一的缺点是,用户需要启用 JavaScript 才能访问你的账户和贴文网页视图。
|
||||
|
||||
这个功能深受优秀的 [anubis] 项目的启发,但我们最终决定自己实现,只包含我们需要的功能,使用最少的代码,并能与我们现有的授权/认证流程实现更细粒度的结合。
|
||||
|
||||
GoToSocial 实现的这个爬虫防护功能仍然是极其精简的。因此,如果你需要更多功能或对防护措施进行更精细的控制,那么完全可以禁用我们的内置功能,并在你的实例前部署像 [anubis] 这样的服务!
|
||||
|
||||
!!! warning "警告"
|
||||
这个基于工作量证明的爬虫防护机制并不保护用户账户页的 RSS feed,因为这会带来额外的复杂性。如果你需要确保 RSS feed 可被访问,那么在这种情况下,[anubis] 可能是更合适的选择!
|
||||
|
||||
[anubis]: https://github.com/TecharoHQ/anubis
|
||||
|
|
@ -16,7 +16,7 @@ tracing-insecure-transport: true
|
|||
```
|
||||
|
||||
[otel]: https://opentelemetry.io/
|
||||
[obs]: ../configuration/observability.md
|
||||
[obs]: ../configuration/observability_and_metrics.md
|
||||
[tempo]: https://grafana.com/oss/tempo/
|
||||
[grafana]: https://grafana.com/oss/grafana/
|
||||
[ext]: https://codeberg.org/superseriousbusiness/gotosocial/tree/main/example/tracing
|
||||
|
|
|
|||
|
|
@ -149,21 +149,4 @@ advanced-csp-extra-uris: []
|
|||
# 选项: ["block", "allow", ""]
|
||||
# 默认: ""
|
||||
advanced-header-filter-mode: ""
|
||||
|
||||
# 布尔值。启用基于工作量证明的爬虫威慑机制,
|
||||
# 作用于账户页和贴文页面。这将为每个 HTTP 客户端生成一个唯一确定
|
||||
# 的质询,需要由客户端在访问上述端点时完成。
|
||||
# 完成后,客户端会获得一个 Cookie,允许其在 1 小时窗口内免验证访问。
|
||||
#
|
||||
# 这样做的结果是,它理论上使得对这些端点的抓取在经济上变得不可行,
|
||||
# 同时对你自己的实例的性能影响可以忽略不计。
|
||||
#
|
||||
# 缺点是它要求客户端启用 JavaScript。
|
||||
#
|
||||
# 更多详情请查阅文档:
|
||||
# https://docs.gotosocial.org/zh-cn/latest/admin/scraper_deterrence
|
||||
#
|
||||
# 选项: [true, false]
|
||||
# 默认值: true
|
||||
advanced-scraper-deterrence: false
|
||||
```
|
||||
|
|
@ -38,4 +38,4 @@ GoToSocial 默认配置了基于 IP 的[限流规则](./api/ratelimiting.md),
|
|||
|
||||
## 为什么还在 Beta 阶段?
|
||||
|
||||
查看[当前 bug 列表](https://codeberg.org/superseriousbusiness/gotosocial/issues?q=is%3Aissue+is%3Aopen+label%3Abug)和[路线图](https://codeberg.org/superseriousbusiness/gotosocial/src/branch/main/docs/locales/zh/repo/ROADMAP.md)以获取更详细的信息。
|
||||
查看[当前 bug 列表](https://codeberg.org/superseriousbusiness/gotosocial/issues?labels=378161)和[路线图](https://codeberg.org/superseriousbusiness/gotosocial/src/branch/main/docs/locales/zh/repo/ROADMAP.md)以获取更详细的信息。
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ nav:
|
|||
- "configuration/syslog.md"
|
||||
- "configuration/httpclient.md"
|
||||
- "configuration/advanced.md"
|
||||
- "configuration/observability.md"
|
||||
- "configuration/observability_and_metrics.md"
|
||||
- "进阶":
|
||||
- "概述": "advanced/index.md"
|
||||
- "advanced/host-account-domain.md"
|
||||
|
|
@ -80,7 +80,6 @@ nav:
|
|||
- "advanced/tracing.md"
|
||||
- "advanced/metrics.md"
|
||||
- "advanced/replicating-sqlite.md"
|
||||
- "advanced/scraper_deterrence.md"
|
||||
- "advanced/sqlite-networked-storage.md"
|
||||
- "适用进阶场景的构建":
|
||||
- "advanced/builds/nowasm.md"
|
||||
|
|
|
|||
|
|
@ -272,9 +272,9 @@ GoToSocial 支持 [OpenID Connect (OIDC)](https://openid.net/connect/) 身份提
|
|||
|
||||
## 已知问题
|
||||
|
||||
由于 GoToSocial 仍处于测试阶段,存在很多错误。我们使用 [Codeberg issues](https://codeberg.org/superseriousbusiness/gotosocial/issues?q=is%3Aissue+is%3Aopen+label%3Abug) 跟踪这些问题。
|
||||
由于 GoToSocial 仍处于测试阶段,存在很多错误。我们使用 [Codeberg issues](https://codeberg.org/superseriousbusiness/gotosocial/issues?labels=378161) 跟踪这些问题。
|
||||
|
||||
由于每个 ActivityPub 服务端实现对协议的解释略有不同,有些服务端尚未与 GoToSocial 正常联合。我们在 [这个项目](https://codeberg.org/superseriousbusiness/gotosocial/projects/4) 中跟踪这些问题。最终,我们希望确保任何可以与 Mastodon 正确联合的 ActivityPub 实现也能够与 GoToSocial 联合。
|
||||
由于每个 ActivityPub 服务端实现对协议的解释略有不同,有些服务端尚未与 GoToSocial 正常联合。我们在 [这个项目](https://codeberg.org/superseriousbusiness/gotosocial/issues?labels=378188) 中跟踪这些问题。最终,我们希望确保任何可以与 Mastodon 正确联合的 ActivityPub 实现也能够与 GoToSocial 联合。
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
BIN
docs/overrides/public/theme-hacker-dark.png
Normal file
BIN
docs/overrides/public/theme-hacker-dark.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 624 KiB |
BIN
docs/overrides/public/theme-hacker-light.png
Normal file
BIN
docs/overrides/public/theme-hacker-light.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 631 KiB |
BIN
docs/overrides/public/theme-programmer-socks-dark.png
Normal file
BIN
docs/overrides/public/theme-programmer-socks-dark.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 572 KiB |
BIN
docs/overrides/public/theme-programmer-socks-light.png
Normal file
BIN
docs/overrides/public/theme-programmer-socks-light.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 630 KiB |
BIN
docs/overrides/public/user-settings-listenbrainz-fields.png
Normal file
BIN
docs/overrides/public/user-settings-listenbrainz-fields.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 34 KiB |
BIN
docs/overrides/public/user-settings-listenbrainz.png
Normal file
BIN
docs/overrides/public/user-settings-listenbrainz.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 35 KiB |
|
|
@ -41,6 +41,7 @@
|
|||
// - read:applications: grants read access to user-managed applications
|
||||
// - read:blocks: grants read access to blocks
|
||||
// - read:bookmarks: grants read access to bookmarks
|
||||
// - read:custom_emojis: grants read access to custom emojis
|
||||
// - read:favourites: grants read access to accounts
|
||||
// - read:filters: grants read access to filters
|
||||
// - read:follows: grants read access to follows
|
||||
|
|
@ -99,6 +100,7 @@
|
|||
// read:applications: grants read access to user-managed applications
|
||||
// read:blocks: grants read access to blocks
|
||||
// read:bookmarks: grants read access to bookmarks
|
||||
// read:custom_emojis: grants read access to custom emojis
|
||||
// read:favourites: grants read access to accounts
|
||||
// read:filters: grants read access to filters
|
||||
// read:follows: grants read access to follows
|
||||
|
|
|
|||
|
|
@ -12,4 +12,8 @@ When enabled, the RSS feed for your account will be available at `https://[your-
|
|||
|
||||
## Which posts are shared via RSS?
|
||||
|
||||
Only your latest 20 Public posts are shared via RSS. Replies and reblogs/boosts are not included. Unlisted posts are not included. In other words, the only posts visible via RSS will be the same ones that are visible when you open your profile in a browser.
|
||||
Only your latest 20 Public posts are shared via RSS by default. Replies and reblogs/boosts are not included. Unlisted posts are not included. In other words, the only posts visible via RSS will be the same ones that are visible when you open your profile in a browser.
|
||||
|
||||
If you want to see more posts, you can provide our standard set of timeline paging parameters ([as per our swagger documentation](https://docs.gotosocial.org/en/latest/api/swagger)) to see beyond the first page.
|
||||
|
||||
You can also access Atom and JSON feeds from this same endpoint, but providing the appropriate request content-type header. i.e. `application/atom+xml` for an Atom feed, or `application/feed+json` for a JSON feed.
|
||||
|
|
|
|||
|
|
@ -98,19 +98,32 @@ Some examples:
|
|||
- Pronouns : she/her
|
||||
- My other account : @someone@somewhere.com
|
||||
|
||||
!!! Tip "ListenBrainz integration"
|
||||
If you set the key of one of your profile fields to "ListenBrainz" and the value to the URL of your ListenBrainz profile (something like `https://listenbrainz.org/user/your_listenbrainz_username/` -- the slash at the end is important!), then the field will be replaced on the web frontend with whatever you're currently listening to!
|
||||
|
||||
This only applies to the web view of your GoToSocial profile, for visitors with Javascript enabled; the "currently listening" value doesn't federate to other servers, only your ListenBrainz URL.
|
||||
|
||||
How to set it:
|
||||
|
||||

|
||||
|
||||
How it looks on the web when you're listening to something:
|
||||
|
||||

|
||||
|
||||
### Visibility and Privacy
|
||||
|
||||
#### Visibility Level of Posts to Show on Your Profile
|
||||
|
||||
Using this dropdown, you can choose what visibility level(s) of posts should be shown on the public web views of your profile, of your statuses, and in your RSS feed (if you have enabled RSS).
|
||||
Using this dropdown, you can choose what visibility level(s) of posts should be shown on the public web views of your profile and posts, and in your RSS feed (if enabled).
|
||||
|
||||
**By default, GoToSocial shows only Public visibility posts on its web views, not Unlisted.** You can adjust this setting to also show Unlisted visibility posts, which is similar to the default for other ActivityPub softwares like Mastodon etc.
|
||||
|
||||
You can also choose to show no posts at all on GoToSocial's web views. This allows you to write posts without having to worry about scrapers, rubberneckers, and other nosy parkers visiting your web profile and looking at your posts.
|
||||
You can also choose to show no posts at all on the web view of your profile. This allows you to post without having to worry about scrapers, rubberneckers, and other nosy parkers being able to easily look through your posts by opening your profile in a browser.
|
||||
|
||||
This setting only applies to the visibility of your own posts. Other user's Unlisted posts are never shown.
|
||||
Please bear in mind that this setting only applies to the logged-out (unauthenticated) web view of your profile and threads that people visit in their web browser, and RSS feed (if enabled). It does not change the visibility of your posts over the ActivityPub protocol. So even if you choose to show no posts to logged-out visitors to your web profile, folks on instances you federate with will be able to see your posts via ActivityPub if they follow you, have your posts boosted onto their timeline by other people, use a link to search a post of yours, etc.
|
||||
|
||||
This setting does not affect visibility of your posts over the ActivityPub protocol, so even if you choose to show no posts on your public web profile, others will be able to see your posts in their client if they follow you, and/or have your posts boosted onto their timeline, use a link to search a post of yours, etc.
|
||||
Furthermore, while GoToSocial does [transmit a flag to other instances](../federation/actors.md) indicating your preference of what post visibility level to show to logged-out visitors, not all servers will respect this flag; indeed, many servers allow logged-out visitors to look up remote profiles, which can expose your public and unlisted posts. If you require stricter control over who sees your posts, consider posting things at the [followers-only](./posts.md#privatefollowers-only) visibility level, which almost all server softwares respect and properly gate behind authorization.
|
||||
|
||||
!!! warning
|
||||
Be aware that changes to this setting also apply retroactively.
|
||||
|
|
|
|||
|
|
@ -36,6 +36,19 @@ log-db-queries: false
|
|||
# Default: true
|
||||
log-client-ip: true
|
||||
|
||||
# String. Format to use for formatting log entries.
|
||||
# Supports "logfmt" and "json", with examples below:
|
||||
#
|
||||
# logfmt:
|
||||
# func=router.(*Router).Start.func1 level=INFO msg="listening on 127.0.0.1:8080"
|
||||
#
|
||||
# json:
|
||||
# {"func":"router.(*Router).Start.func1", "level":"INFO", "msg":"listening on 127.0.0.1:8080"}
|
||||
#
|
||||
# Examples: ["logfmt", "json"]
|
||||
# Default: "logfmt"
|
||||
log-format: "logfmt"
|
||||
|
||||
# String. Format to use for the timestamp in log lines.
|
||||
# If set to the empty string, the timestamp will be
|
||||
# ommitted from the logs entirely.
|
||||
|
|
@ -104,14 +117,16 @@ account-domain: ""
|
|||
# Default: "https"
|
||||
protocol: "https"
|
||||
|
||||
# String. Address to bind the GoToSocial server to.
|
||||
# This can be an IPv4 address or an IPv6 address (surrounded in square brackets), or a hostname.
|
||||
# String. Address to bind the GoToSocial HTTP server to.
|
||||
# This can be an IPv4 address, an IPv6 address, or a hostname.
|
||||
#
|
||||
# The default value will bind to all interfaces, which makes the server
|
||||
# accessible by other machines. For most setups there is no need to change this.
|
||||
# If you are using GoToSocial in a reverse proxy setup with the proxy running on
|
||||
# the same machine, you will want to set this to "localhost" or an equivalent,
|
||||
# so that the proxy can't be bypassed.
|
||||
# Examples: ["0.0.0.0", "172.128.0.16", "localhost", "[::]", "[2001:db8::fed1]"]
|
||||
# accessible by other machines. For most setups you won't need to change this.
|
||||
# However, if you are using GoToSocial in a reverse proxy setup with the proxy
|
||||
# running on the same machine, you may want to set this to "localhost" or equivalent,
|
||||
# so that the proxy definitely can't be bypassed.
|
||||
#
|
||||
# Examples: ["0.0.0.0", "172.128.0.16", "localhost", "::1", "2001:db8::fed1"]
|
||||
# Default: "0.0.0.0"
|
||||
bind-address: "0.0.0.0"
|
||||
|
||||
|
|
@ -141,8 +156,8 @@ trusted-proxies:
|
|||
|
||||
# String. Database type.
|
||||
# Options: ["postgres","sqlite"]
|
||||
# Default: "postgres"
|
||||
db-type: "postgres"
|
||||
# Default: ""
|
||||
db-type: "sqlite"
|
||||
|
||||
# String. Database address or parameters.
|
||||
#
|
||||
|
|
@ -157,29 +172,29 @@ db-type: "postgres"
|
|||
#
|
||||
# Examples: ["localhost","my.db.host","127.0.0.1","192.111.39.110",":memory:", "sqlite.db"]
|
||||
# Default: ""
|
||||
db-address: ""
|
||||
db-address: "sqlite.db"
|
||||
|
||||
# Int. Port for database connection.
|
||||
# Int. Port for postgres database connection; ignored for sqlite.
|
||||
# Examples: [5432, 1234, 6969]
|
||||
# Default: 5432
|
||||
db-port: 5432
|
||||
|
||||
# String. Username for the database connection.
|
||||
# String. Username for postgres database connection.
|
||||
# Examples: ["mydbuser","postgres","gotosocial"]
|
||||
# Default: ""
|
||||
db-user: ""
|
||||
|
||||
# String. Password to use for the database connection
|
||||
# String. Password to use for postgres database connection.
|
||||
# Examples: ["password123","verysafepassword","postgres"]
|
||||
# Default: ""
|
||||
db-password: ""
|
||||
|
||||
# String. Name of the database to use within the provided database type.
|
||||
# String. Name of the database to use for postgres database connection.
|
||||
# Examples: ["mydb","postgres","gotosocial"]
|
||||
# Default: "gotosocial"
|
||||
db-database: "gotosocial"
|
||||
|
||||
# String. Disable, enable, or require SSL/TLS connection to the database.
|
||||
# String. Disable, enable, or require SSL/TLS connection to postgres.
|
||||
# If "disable" then no TLS connection will be attempted.
|
||||
# If "enable" then TLS will be tried, but the database certificate won't be checked (for self-signed certs).
|
||||
# If "require" then TLS will be required to make a connection, and a valid certificate must be presented.
|
||||
|
|
@ -362,29 +377,61 @@ instance-federation-mode: "blocklist"
|
|||
# Default: false
|
||||
instance-federation-spam-filter: false
|
||||
|
||||
# Bool. Allow unauthenticated users to make queries to /api/v1/instance/peers?filter=open in order
|
||||
# to see a list of instances that this instance 'peers' with. Even if set to 'false', then authenticated
|
||||
# users (members of the instance) will still be able to query the endpoint.
|
||||
# Bool. Allow unauthenticated users to make queries to /api/v1/instance/peers?filter=open
|
||||
# in order to see a list of domains that this instance 'peers' with.
|
||||
#
|
||||
# Even if set to 'false', then authenticated users (members of the instance)
|
||||
# will still be able to query these endpoints using an OAuth token.
|
||||
#
|
||||
# Options: [true, false]
|
||||
# Default: false
|
||||
instance-expose-peers: false
|
||||
|
||||
# Bool. Allow unauthenticated users to make queries to /api/v1/instance/peers?filter=suspended in order
|
||||
# to see a list of instances that this instance blocks/suspends. Even if set to 'false', then authenticated
|
||||
# users (members of the instance) will still be able to query the endpoint.
|
||||
# Bool. Allow unauthenticated users to make queries to the following instance API
|
||||
# endpoints in order to see a list of domains that this instance explicitly blocks,
|
||||
# including the public reason for each block:
|
||||
#
|
||||
# WARNING: Setting this variable to 'true' may result in your instance being scraped by blocklist scrapers.
|
||||
# - /api/v1/instance/peers?filter=blocklist
|
||||
# - /api/v1/instance/peers?filter=suspended (deprecated)
|
||||
# - /api/v1/instance/domain_blocks
|
||||
#
|
||||
# Even if set to 'false', then authenticated users (members of the instance)
|
||||
# will still be able to query these endpoints using an OAuth token.
|
||||
#
|
||||
# WARNING: Setting to 'true' may result in your instance being targeted by blocklist scrapers.
|
||||
# See: https://docs.gotosocial.org/en/latest/admin/domain_blocks/#block-announce-bots
|
||||
#
|
||||
# Options: [true, false]
|
||||
# Default: false
|
||||
instance-expose-suspended: false
|
||||
instance-expose-blocklist: false
|
||||
|
||||
# Bool. Allow unauthenticated users to view /about/suspended,
|
||||
# showing the HTML rendered list of instances that this instance blocks/suspends.
|
||||
# Bool. Allow unauthenticated users to view /about/domain_blocks,
|
||||
# which shows an HTML-rendered list of domains that this instance blocks,
|
||||
# including the public reason for each block.
|
||||
# Options: [true, false]
|
||||
# Default: false
|
||||
instance-expose-suspended-web: false
|
||||
instance-expose-blocklist-web: false
|
||||
|
||||
# Bool. Allow unauthenticated users to make queries to the following instance API
|
||||
# endpoints in order to see a list of domains that this instance explicitly allows
|
||||
# including the public reason for each allow:
|
||||
#
|
||||
# - /api/v1/instance/peers?filter=allowlist
|
||||
# - /api/v1/instance/domain_allows
|
||||
#
|
||||
# Even if set to 'false', then authenticated users (members of the instance)
|
||||
# will still be able to query these endpoints using an OAuth token.
|
||||
#
|
||||
# Options: [true, false]
|
||||
# Default: false
|
||||
instance-expose-allowlist: false
|
||||
|
||||
# Bool. Allow unauthenticated users to view /about/domain_allows,
|
||||
# which shows an HTML-rendered list of domains that this instance allows,
|
||||
# including the public reason for each allow.
|
||||
# Options: [true, false]
|
||||
# Default: false
|
||||
instance-expose-allowlist-web: false
|
||||
|
||||
# Bool. Allow unauthenticated users to make queries to /api/v1/timelines/public in order
|
||||
# to see a list of public posts on this server. Even if set to 'false', then authenticated
|
||||
|
|
@ -393,6 +440,21 @@ instance-expose-suspended-web: false
|
|||
# Default: false
|
||||
instance-expose-public-timeline: false
|
||||
|
||||
# Bool. Allow unauthenticated access to /api/v1/custom_emojis, which
|
||||
# exposes the list of custom emojis available to users on this server.
|
||||
#
|
||||
# Setting this to 'true' may alleviate issues with clients that do not
|
||||
# provide an access token in their requests to the emojis endpoint, but
|
||||
# it also means that anyone can look at what emojis are installed on your
|
||||
# instance, which may present privacy / safety issues.
|
||||
#
|
||||
# Even if set to 'false', authenticated users (ie., instance members)
|
||||
# will still be able to query the endpoint using an access token.
|
||||
#
|
||||
# Options: [true, false]
|
||||
# Default: false
|
||||
instance-expose-custom-emojis: false
|
||||
|
||||
# Bool. This flag tweaks whether GoToSocial will deliver ActivityPub messages
|
||||
# to the shared inbox of a recipient, if one is available, instead of delivering
|
||||
# each message to each actor who should receive a message individually.
|
||||
|
|
@ -460,9 +522,7 @@ instance-stats-mode: ""
|
|||
|
||||
# Bool. This flag controls whether local accounts may backdate statuses
|
||||
# using past dates with the scheduled_at param to /api/v1/statuses.
|
||||
# This flag does not affect scheduling posts in the future
|
||||
# (which is currently not implemented anyway),
|
||||
# nor can it prevent remote accounts from backdating their own statuses.
|
||||
# This flag can't prevent remote accounts from backdating their own statuses.
|
||||
#
|
||||
# If true, all local accounts may backdate statuses.
|
||||
# If false, status backdating will be disabled and an error will be returned if it's used.
|
||||
|
|
@ -533,6 +593,14 @@ accounts-allow-custom-css: false
|
|||
# Default: 10000
|
||||
accounts-custom-css-length: 10000
|
||||
|
||||
# Int. The maximum number of profile fields allowed for each account.
|
||||
#
|
||||
# Note that going way higher than the default might break federation.
|
||||
#
|
||||
# Examples: [4, 6, 12]
|
||||
# Default: 6
|
||||
accounts-max-profile-fields: 6
|
||||
|
||||
########################
|
||||
##### MEDIA CONFIG #####
|
||||
########################
|
||||
|
|
@ -574,6 +642,13 @@ media-video-size-hint: 40MiB
|
|||
# Default: 40MiB (41943040 bytes)
|
||||
media-remote-max-size: 40MiB
|
||||
|
||||
# Int. Max size in pixels of any one dimension of
|
||||
# a thumbnail (as input media ratio is preserved).
|
||||
#
|
||||
# Examples: [256, 512, 1024]
|
||||
# Default: 512
|
||||
media-thumb-max-pixels: 512
|
||||
|
||||
# Int. Minimum amount of characters required as an image or video description.
|
||||
# Examples: [500, 1000, 1500]
|
||||
# Default: 0 (not required)
|
||||
|
|
@ -795,6 +870,16 @@ statuses-poll-option-max-chars: 50
|
|||
# Default: 6
|
||||
statuses-media-max-files: 6
|
||||
|
||||
# Int. Maximum number of statuses a user can schedule at time.
|
||||
# Examples: [300]
|
||||
# Default: 300
|
||||
scheduled-statuses-max-total: 300
|
||||
|
||||
# Int. Maximum number of statuses a user can schedule for a single day.
|
||||
# Examples: [25]
|
||||
# Default: 25
|
||||
scheduled-statuses-max-daily: 25
|
||||
|
||||
##############################
|
||||
##### LETSENCRYPT CONFIG #####
|
||||
##############################
|
||||
|
|
@ -993,51 +1078,53 @@ syslog-protocol: "udp"
|
|||
# Default: "localhost:514"
|
||||
syslog-address: "localhost:514"
|
||||
|
||||
##################################
|
||||
##### OBSERVABILITY SETTINGS #####
|
||||
##################################
|
||||
##############################################
|
||||
##### OBSERVABILITY AND METRICS SETTINGS #####
|
||||
##############################################
|
||||
|
||||
# String. Header name to use to extract a request or trace ID from. Typically set by a
|
||||
# loadbalancer or proxy.
|
||||
# String. Header name to use to extract a request or
|
||||
# trace ID from. Typically set by a loadbalancer or proxy.
|
||||
#
|
||||
# Default: "X-Request-Id"
|
||||
request-id-header: "X-Request-Id"
|
||||
|
||||
# Bool. Enable OpenTelemetry based tracing support.
|
||||
#
|
||||
# When enabling tracing, you must also configure a traces
|
||||
# exporter using the OTEL environment variable documented here:
|
||||
#
|
||||
# https://opentelemetry.io/docs/languages/sdk-configuration/general/#otel_traces_exporter
|
||||
#
|
||||
# Default: false
|
||||
tracing-enabled: false
|
||||
|
||||
# String. Set the transport protocol for the tracing system. Can either be "grpc"
|
||||
# for OTLP gRPC, or "http" for OTLP HTTP.
|
||||
# Options: ["grpc", "http"]
|
||||
# Default: "grpc"
|
||||
tracing-transport: "grpc"
|
||||
|
||||
# String. Endpoint of the trace ingester. When using the gRPC or HTTP based transports,
|
||||
# provide the endpoint as a single address/port combination without a protocol scheme.
|
||||
# Examples: ["localhost:4317"]
|
||||
# Default: ""
|
||||
tracing-endpoint: ""
|
||||
|
||||
# Bool. Disable TLS for the gRPC and HTTP transport protocols.
|
||||
# Default: false
|
||||
tracing-insecure-transport: false
|
||||
|
||||
# Bool. Enable OpenTelemetry based metrics support.
|
||||
#
|
||||
# To expose Prometheus metrics, you must configure a metrics producer and
|
||||
# a metrics exporter, using the OTEL environment variables documented here:
|
||||
#
|
||||
# https://pkg.go.dev/go.opentelemetry.io/contrib/exporters/autoexport#NewMetricReader
|
||||
#
|
||||
# Typically, you will want to set the following environment variables
|
||||
# (take note of the plural "producers" and singular "exporter"):
|
||||
#
|
||||
# - OTEL_METRICS_PRODUCERS=prometheus
|
||||
# - OTEL_METRICS_EXPORTER=prometheus
|
||||
#
|
||||
# With these variables set, a Prometheus metrics endpoint will be exposed at
|
||||
# localhost:9464/metrics. This can be further configured using the variables:
|
||||
#
|
||||
# - OTEL_EXPORTER_OTLP_METRICS_PROTOCOL
|
||||
# - OTEL_EXPORTER_PROMETHEUS_HOST
|
||||
# - OTEL_EXPORTER_PROMETHEUS_PORT
|
||||
#
|
||||
# For more information, see the GtS metrics documentation here:
|
||||
#
|
||||
# https://docs.gotosocial.org/en/latest/advanced/metrics/
|
||||
#
|
||||
# Default: false
|
||||
metrics-enabled: false
|
||||
|
||||
# Bool. Enable HTTP Basic Authentication for Prometheus metrics endpoint.
|
||||
# Default: false
|
||||
metrics-auth-enabled: false
|
||||
|
||||
# String. Username for Prometheus metrics endpoint.
|
||||
# Default: ""
|
||||
metrics-auth-username: ""
|
||||
|
||||
# String. Password for Prometheus metrics endpoint.
|
||||
# Default: ""
|
||||
metrics-auth-password: ""
|
||||
|
||||
################################
|
||||
##### HTTP CLIENT SETTINGS #####
|
||||
################################
|
||||
|
|
@ -1101,6 +1188,15 @@ http-client:
|
|||
# Default: false
|
||||
tls-insecure-skip-verify: false
|
||||
|
||||
# Bool. Sets outgoing queries to webfinger, host-meta and nodeinfo to use
|
||||
# HTTP instead of HTTPS.
|
||||
#
|
||||
# THIS SETTING SHOULD BE USED FOR TESTING ONLY! DO NOT CHANGE THIS SETTING
|
||||
# UNLESS YOU KNOW EXACTLY WHAT YOU'RE DOING AND WHY YOU'RE DOING IT.
|
||||
#
|
||||
# Default: false
|
||||
insecure-outgoing: false
|
||||
|
||||
#############################
|
||||
##### ADVANCED SETTINGS #####
|
||||
#############################
|
||||
|
|
|
|||
|
|
@ -22,6 +22,11 @@ Restart=on-failure
|
|||
# Whatever you do, make sure the dir exists and that the gotosocial user has permission to write + read from it.
|
||||
#Environment="GTS_WAZERO_COMPILATION_CACHE=/gotosocial/.cache"
|
||||
|
||||
# If you have set `metrics-enabled` to `true` in your GoToSocial config file, and you want
|
||||
# to expose Prometheus metrics at localhost:9464/metrics, uncomment the following two lines:
|
||||
#Environment="OTEL_METRICS_EXPORTER=prometheus"
|
||||
#Environment="OTEL_METRICS_PRODUCERS=prometheus"
|
||||
|
||||
# change if your path to the GoToSocial binary is different
|
||||
ExecStart=/gotosocial/gotosocial --config-path config.yaml server start
|
||||
WorkingDirectory=/gotosocial
|
||||
|
|
|
|||
1980
example/metrics/gotosocial_grafana_dashboard.json
Normal file
1980
example/metrics/gotosocial_grafana_dashboard.json
Normal file
File diff suppressed because it is too large
Load diff
201
go.mod
201
go.mod
|
|
@ -1,106 +1,105 @@
|
|||
module code.superseriousbusiness.org/gotosocial
|
||||
|
||||
go 1.23.0
|
||||
|
||||
toolchain go1.23.3
|
||||
go 1.24.6
|
||||
|
||||
// Replace go-swagger with our version that fixes (ours particularly) use of Go1.23
|
||||
replace github.com/go-swagger/go-swagger => codeberg.org/superseriousbusiness/go-swagger v0.31.0-gts-go1.23-fix
|
||||
replace github.com/go-swagger/go-swagger => codeberg.org/superseriousbusiness/go-swagger v0.32.3-gts-go1.23-fix
|
||||
|
||||
// Replace modernc/sqlite with our version that fixes the concurrency INTERRUPT issue
|
||||
replace modernc.org/sqlite => gitlab.com/NyaaaWhatsUpDoc/sqlite v1.37.0-concurrency-workaround
|
||||
replace modernc.org/sqlite => gitlab.com/NyaaaWhatsUpDoc/sqlite v1.39.0-concurrency-workaround
|
||||
|
||||
require (
|
||||
code.superseriousbusiness.org/activity v1.15.0
|
||||
code.superseriousbusiness.org/activity v1.17.0
|
||||
code.superseriousbusiness.org/exif-terminator v0.11.0
|
||||
code.superseriousbusiness.org/httpsig v1.4.0
|
||||
code.superseriousbusiness.org/oauth2/v4 v4.8.0
|
||||
codeberg.org/gruf/go-bytes v1.0.2
|
||||
codeberg.org/gruf/go-bytesize v1.0.3
|
||||
code.superseriousbusiness.org/oauth2/v4 v4.5.4-0.20250812115401-3961e46a7384
|
||||
codeberg.org/gruf/go-bytesize v1.0.4
|
||||
codeberg.org/gruf/go-byteutil v1.3.0
|
||||
codeberg.org/gruf/go-cache/v3 v3.6.1
|
||||
codeberg.org/gruf/go-caller v0.0.0-20250806133437-db8d0b1f71cf
|
||||
codeberg.org/gruf/go-debug v1.3.0
|
||||
codeberg.org/gruf/go-errors/v2 v2.3.2
|
||||
codeberg.org/gruf/go-fastcopy v1.1.3
|
||||
codeberg.org/gruf/go-ffmpreg v0.6.7
|
||||
codeberg.org/gruf/go-fastpath/v2 v2.0.0
|
||||
codeberg.org/gruf/go-ffmpreg v0.6.12
|
||||
codeberg.org/gruf/go-iotools v0.0.0-20240710125620-934ae9c654cf
|
||||
codeberg.org/gruf/go-kv v1.6.5
|
||||
codeberg.org/gruf/go-kv/v2 v2.0.7
|
||||
codeberg.org/gruf/go-list v0.0.0-20240425093752-494db03d641f
|
||||
codeberg.org/gruf/go-mempool v0.0.0-20240507125005-cef10d64a760
|
||||
codeberg.org/gruf/go-mutexes v1.5.2
|
||||
codeberg.org/gruf/go-mempool v0.0.0-20251003110531-b54adae66253
|
||||
codeberg.org/gruf/go-mutexes v1.5.8
|
||||
codeberg.org/gruf/go-runners v1.6.3
|
||||
codeberg.org/gruf/go-sched v1.2.4
|
||||
codeberg.org/gruf/go-storage v0.2.0
|
||||
codeberg.org/gruf/go-structr v0.9.6
|
||||
codeberg.org/gruf/go-split v1.2.0
|
||||
codeberg.org/gruf/go-storage v0.3.1
|
||||
codeberg.org/gruf/go-structr v0.9.12
|
||||
github.com/DmitriyVTitov/size v1.5.0
|
||||
github.com/KimMachineGun/automemlimit v0.7.1
|
||||
github.com/KimMachineGun/automemlimit v0.7.4
|
||||
github.com/SherClockHolmes/webpush-go v1.4.0
|
||||
github.com/buckket/go-blurhash v1.1.0
|
||||
github.com/coreos/go-oidc/v3 v3.14.1
|
||||
github.com/gin-contrib/cors v1.7.5
|
||||
github.com/coreos/go-oidc/v3 v3.15.0
|
||||
github.com/gin-contrib/cors v1.7.6
|
||||
github.com/gin-contrib/gzip v1.2.3
|
||||
github.com/gin-contrib/sessions v1.0.3
|
||||
github.com/gin-gonic/gin v1.10.0
|
||||
github.com/gin-contrib/sessions v1.0.4
|
||||
github.com/gin-gonic/gin v1.10.1
|
||||
github.com/go-playground/form/v4 v4.2.1
|
||||
github.com/go-swagger/go-swagger v0.31.0
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1
|
||||
github.com/go-swagger/go-swagger v0.32.3
|
||||
github.com/google/go-cmp v0.7.0
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/gorilla/feeds v1.2.0
|
||||
github.com/gorilla/websocket v1.5.3
|
||||
github.com/jackc/pgx/v5 v5.7.4
|
||||
github.com/jackc/pgx/v5 v5.7.6
|
||||
github.com/k3a/html2text v1.2.1
|
||||
github.com/microcosm-cc/bluemonday v1.0.27
|
||||
github.com/miekg/dns v1.1.65
|
||||
github.com/minio/minio-go/v7 v7.0.91
|
||||
github.com/miekg/dns v1.1.68
|
||||
github.com/minio/minio-go/v7 v7.0.95
|
||||
github.com/mitchellh/mapstructure v1.5.0
|
||||
github.com/ncruces/go-sqlite3 v0.25.1
|
||||
github.com/ncruces/go-sqlite3 v0.29.1
|
||||
github.com/oklog/ulid v1.3.1
|
||||
github.com/pquerna/otp v1.4.0
|
||||
github.com/prometheus/client_golang v1.22.0
|
||||
github.com/pquerna/otp v1.5.0
|
||||
github.com/rivo/uniseg v0.4.7
|
||||
github.com/spf13/cobra v1.9.1
|
||||
github.com/spf13/viper v1.20.1
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/tdewolff/minify/v2 v2.23.1
|
||||
github.com/technologize/otel-go-contrib v1.1.1
|
||||
github.com/spf13/cast v1.10.0
|
||||
github.com/spf13/cobra v1.10.1
|
||||
github.com/spf13/pflag v1.0.10
|
||||
github.com/spf13/viper v1.21.0
|
||||
github.com/stretchr/testify v1.11.1
|
||||
github.com/tdewolff/minify/v2 v2.24.3
|
||||
github.com/temoto/robotstxt v1.1.2
|
||||
github.com/tetratelabs/wazero v1.9.0
|
||||
github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80
|
||||
github.com/tomnomnom/linkheader v0.0.0-20250811210735-e5fe3b51442e
|
||||
github.com/ulule/limiter/v3 v3.11.2
|
||||
github.com/uptrace/bun v1.2.11
|
||||
github.com/uptrace/bun/dialect/pgdialect v1.2.11
|
||||
github.com/uptrace/bun/dialect/sqlitedialect v1.2.11
|
||||
github.com/uptrace/bun/extra/bunotel v1.2.11
|
||||
github.com/uptrace/bun v1.2.15
|
||||
github.com/uptrace/bun/dialect/pgdialect v1.2.15
|
||||
github.com/uptrace/bun/dialect/sqlitedialect v1.2.15
|
||||
github.com/uptrace/bun/extra/bunotel v1.2.15
|
||||
github.com/wagslane/go-password-validator v0.3.0
|
||||
github.com/yuin/goldmark v1.7.11
|
||||
go.opentelemetry.io/otel v1.35.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0
|
||||
go.opentelemetry.io/otel/exporters/prometheus v0.57.0
|
||||
go.opentelemetry.io/otel/metric v1.35.0
|
||||
go.opentelemetry.io/otel/sdk v1.35.0
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0
|
||||
go.opentelemetry.io/otel/trace v1.35.0
|
||||
github.com/yuin/goldmark v1.7.13
|
||||
go.opentelemetry.io/contrib/exporters/autoexport v0.63.0
|
||||
go.opentelemetry.io/contrib/instrumentation/runtime v0.63.0
|
||||
go.opentelemetry.io/otel v1.38.0
|
||||
go.opentelemetry.io/otel/metric v1.38.0
|
||||
go.opentelemetry.io/otel/sdk v1.38.0
|
||||
go.opentelemetry.io/otel/sdk/metric v1.38.0
|
||||
go.opentelemetry.io/otel/trace v1.38.0
|
||||
go.uber.org/automaxprocs v1.6.0
|
||||
golang.org/x/crypto v0.37.0
|
||||
golang.org/x/image v0.26.0
|
||||
golang.org/x/net v0.39.0
|
||||
golang.org/x/oauth2 v0.29.0
|
||||
golang.org/x/sys v0.32.0
|
||||
golang.org/x/text v0.24.0
|
||||
golang.org/x/crypto v0.42.0
|
||||
golang.org/x/image v0.31.0
|
||||
golang.org/x/net v0.44.0
|
||||
golang.org/x/oauth2 v0.31.0
|
||||
golang.org/x/sys v0.36.0
|
||||
golang.org/x/text v0.29.0
|
||||
gopkg.in/mcuadros/go-syslog.v2 v2.3.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
modernc.org/sqlite v1.37.0
|
||||
modernc.org/sqlite v1.39.0
|
||||
mvdan.cc/xurls/v2 v2.6.0
|
||||
)
|
||||
|
||||
require (
|
||||
code.superseriousbusiness.org/go-jpeg-image-structure/v2 v2.3.0 // indirect
|
||||
code.superseriousbusiness.org/go-png-image-structure/v2 v2.3.0 // indirect
|
||||
codeberg.org/gruf/go-fastpath/v2 v2.0.0 // indirect
|
||||
codeberg.org/gruf/go-mangler v1.4.4 // indirect
|
||||
codeberg.org/gruf/go-kv v1.6.5 // indirect
|
||||
codeberg.org/gruf/go-mangler/v2 v2.0.6 // indirect
|
||||
codeberg.org/gruf/go-maps v1.0.4 // indirect
|
||||
codeberg.org/gruf/go-xunsafe v0.0.0-20250809104800-512a9df57d73 // indirect
|
||||
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||
github.com/Masterminds/semver/v3 v3.2.1 // indirect
|
||||
github.com/Masterminds/sprig/v3 v3.2.3 // indirect
|
||||
|
|
@ -108,9 +107,9 @@ require (
|
|||
github.com/aymerick/douceur v0.2.0 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
|
||||
github.com/bytedance/sonic v1.13.2 // indirect
|
||||
github.com/bytedance/sonic v1.13.3 // indirect
|
||||
github.com/bytedance/sonic/loader v0.2.4 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/cloudwego/base64x v0.1.5 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
|
|
@ -121,13 +120,13 @@ require (
|
|||
github.com/dsoprea/go-utility/v2 v2.0.0-20200717064901-2fccff4aa15e // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/fsnotify/fsnotify v1.8.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
|
||||
github.com/gin-contrib/sse v1.0.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.9 // indirect
|
||||
github.com/gin-contrib/sse v1.1.0 // indirect
|
||||
github.com/go-errors/errors v1.1.1 // indirect
|
||||
github.com/go-ini/ini v1.67.0 // indirect
|
||||
github.com/go-jose/go-jose/v4 v4.0.5 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
github.com/go-jose/go-jose/v4 v4.1.1 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-openapi/analysis v0.23.0 // indirect
|
||||
github.com/go-openapi/errors v0.22.0 // indirect
|
||||
|
|
@ -143,17 +142,18 @@ require (
|
|||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.26.0 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
|
||||
github.com/go-xmlfmt/xmlfmt v0.0.0-20191208150333-d5b6f63a941b // indirect
|
||||
github.com/goccy/go-json v0.10.5 // indirect
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0 // indirect
|
||||
github.com/golang/geo v0.0.0-20200319012246-673a6f80352d // indirect
|
||||
github.com/gorilla/context v1.1.2 // indirect
|
||||
github.com/gorilla/css v1.0.1 // indirect
|
||||
github.com/gorilla/handlers v1.5.2 // indirect
|
||||
github.com/gorilla/securecookie v1.1.2 // indirect
|
||||
github.com/gorilla/sessions v1.4.0 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 // indirect
|
||||
github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 // indirect
|
||||
github.com/huandu/xstrings v1.4.0 // indirect
|
||||
github.com/imdario/mergo v0.3.16 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
|
|
@ -165,13 +165,13 @@ require (
|
|||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/compress v1.18.0 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.10 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.11 // indirect
|
||||
github.com/kr/pretty v0.3.1 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/minio/crc64nvme v1.0.1 // indirect
|
||||
github.com/minio/crc64nvme v1.0.2 // indirect
|
||||
github.com/minio/md5-simd v1.1.2 // indirect
|
||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||
|
|
@ -181,48 +181,63 @@ require (
|
|||
github.com/ncruces/go-strftime v0.1.9 // indirect
|
||||
github.com/ncruces/julianday v1.0.0 // indirect
|
||||
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||
github.com/philhofer/fwd v1.2.0 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/prometheus/client_model v0.6.1 // indirect
|
||||
github.com/prometheus/common v0.62.0 // indirect
|
||||
github.com/prometheus/procfs v0.15.1 // indirect
|
||||
github.com/prometheus/client_golang v1.23.0 // indirect
|
||||
github.com/prometheus/client_model v0.6.2 // indirect
|
||||
github.com/prometheus/common v0.65.0 // indirect
|
||||
github.com/prometheus/otlptranslator v0.0.2 // indirect
|
||||
github.com/prometheus/procfs v0.17.0 // indirect
|
||||
github.com/puzpuzpuz/xsync/v3 v3.5.1 // indirect
|
||||
github.com/quasoft/memstore v0.0.0-20191010062613-2bce066d2b0b // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/rogpeppe/go-internal v1.13.2-0.20241226121412-a5dc8ff20d0a // indirect
|
||||
github.com/rs/xid v1.6.0 // indirect
|
||||
github.com/sagikazarmark/locafero v0.7.0 // indirect
|
||||
github.com/sagikazarmark/locafero v0.11.0 // indirect
|
||||
github.com/shopspring/decimal v1.3.1 // indirect
|
||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||
github.com/spf13/afero v1.12.0 // indirect
|
||||
github.com/spf13/cast v1.7.1 // indirect
|
||||
github.com/spf13/pflag v1.0.6 // indirect
|
||||
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect
|
||||
github.com/spf13/afero v1.15.0 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
github.com/tdewolff/parse/v2 v2.7.23 // indirect
|
||||
github.com/tdewolff/parse/v2 v2.8.3 // indirect
|
||||
github.com/tinylib/msgp v1.3.0 // indirect
|
||||
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect
|
||||
github.com/toqueteos/webbrowser v1.2.0 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.12 // indirect
|
||||
github.com/ugorji/go/codec v1.3.0 // indirect
|
||||
github.com/uptrace/opentelemetry-go-extra/otelsql v0.3.2 // indirect
|
||||
github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
|
||||
go.mongodb.org/mongo-driver v1.17.3 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.5.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/arch v0.16.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect
|
||||
golang.org/x/mod v0.24.0 // indirect
|
||||
golang.org/x/sync v0.13.0 // indirect
|
||||
golang.org/x/tools v0.31.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a // indirect
|
||||
google.golang.org/grpc v1.71.0 // indirect
|
||||
google.golang.org/protobuf v1.36.6 // indirect
|
||||
go.opentelemetry.io/contrib/bridges/prometheus v0.63.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.14.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.14.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/prometheus v0.60.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.14.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/log v0.14.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/log v0.14.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.7.1 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/arch v0.18.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b // indirect
|
||||
golang.org/x/mod v0.27.0 // indirect
|
||||
golang.org/x/sync v0.17.0 // indirect
|
||||
golang.org/x/tools v0.36.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 // indirect
|
||||
google.golang.org/grpc v1.75.0 // indirect
|
||||
google.golang.org/protobuf v1.36.8 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
modernc.org/libc v1.62.1 // indirect
|
||||
modernc.org/libc v1.66.3 // indirect
|
||||
modernc.org/mathutil v1.7.1 // indirect
|
||||
modernc.org/memory v1.9.1 // indirect
|
||||
modernc.org/memory v1.11.0 // indirect
|
||||
)
|
||||
|
|
|
|||
451
go.sum
generated
451
go.sum
generated
|
|
@ -1,5 +1,5 @@
|
|||
code.superseriousbusiness.org/activity v1.15.0 h1:XwdO/1ssry8GCLWDFlUxSb+lNpUvdjkKG3HVHTEkvi4=
|
||||
code.superseriousbusiness.org/activity v1.15.0/go.mod h1:BTMWJIAuwDH1w+ieRP5N+T5LipbXjw35U6KZy0V/xdg=
|
||||
code.superseriousbusiness.org/activity v1.17.0 h1:01x4LyvL5fzKgtce+o3mqYbR1O+RaL6j/z7v/B6ivqo=
|
||||
code.superseriousbusiness.org/activity v1.17.0/go.mod h1:BTMWJIAuwDH1w+ieRP5N+T5LipbXjw35U6KZy0V/xdg=
|
||||
code.superseriousbusiness.org/exif-terminator v0.11.0 h1:Hof0MCcsa+1fS17gf86fTTZ8AQnMY9h9kzcc+2C6mVg=
|
||||
code.superseriousbusiness.org/exif-terminator v0.11.0/go.mod h1:9sutT1axa/kSdlPLlRFjCNKmyo/KNx8eX3XZvWBlAEY=
|
||||
code.superseriousbusiness.org/go-jpeg-image-structure/v2 v2.3.0 h1:r9uq8StaSHYKJ8DklR9Xy+E9c40G1Z8yj5TRGi8L6+4=
|
||||
|
|
@ -8,16 +8,16 @@ code.superseriousbusiness.org/go-png-image-structure/v2 v2.3.0 h1:I512jiIeXDC4//
|
|||
code.superseriousbusiness.org/go-png-image-structure/v2 v2.3.0/go.mod h1:SNHomXNW88o1pFfLHpD4KsCZLfcr4z5dm+xcX5SV10A=
|
||||
code.superseriousbusiness.org/httpsig v1.4.0 h1:g9+KQMoTG0oR0II5gYb5pVVdNjbc7CiiuqK8vcZjeQg=
|
||||
code.superseriousbusiness.org/httpsig v1.4.0/go.mod h1:i2AKpj/WbA/o/UTvia9TAREzt0jP1AH3T1Uxjyhdzlw=
|
||||
code.superseriousbusiness.org/oauth2/v4 v4.8.0 h1:4LVXoPJXKgmDfwDegzBQPNpsdleMaL6YmDgFi6UDgEE=
|
||||
code.superseriousbusiness.org/oauth2/v4 v4.8.0/go.mod h1:+RLRBXPkjP/VhIC/46dcZkx3t5IvBSJYOjVCPgeWors=
|
||||
codeberg.org/gruf/go-bytes v1.0.2 h1:malqE42Ni+h1nnYWBUAJaDDtEzF4aeN4uPN8DfMNNvo=
|
||||
codeberg.org/gruf/go-bytes v1.0.2/go.mod h1:1v/ibfaosfXSZtRdW2rWaVrDXMc9E3bsi/M9Ekx39cg=
|
||||
codeberg.org/gruf/go-bytesize v1.0.3 h1:Tz8tCxhPLeyM5VryuBNjUHgKmLj4Bx9RbPaUSA3qg6g=
|
||||
codeberg.org/gruf/go-bytesize v1.0.3/go.mod h1:n/GU8HzL9f3UNp/mUKyr1qVmTlj7+xacpp0OHfkvLPs=
|
||||
code.superseriousbusiness.org/oauth2/v4 v4.5.4-0.20250812115401-3961e46a7384 h1:eJzULGUyhHGk2DdQxX/jbH9FKZOyoIF90p3dzukCfLA=
|
||||
code.superseriousbusiness.org/oauth2/v4 v4.5.4-0.20250812115401-3961e46a7384/go.mod h1:4x61i4C725jkXOpnUPFNfmiEthF6FZc/byADbalp+F8=
|
||||
codeberg.org/gruf/go-bytesize v1.0.4 h1:LEojK46lUoE748Om7yldx6kLe6jCCuiytz5IZ8vH35g=
|
||||
codeberg.org/gruf/go-bytesize v1.0.4/go.mod h1:n/GU8HzL9f3UNp/mUKyr1qVmTlj7+xacpp0OHfkvLPs=
|
||||
codeberg.org/gruf/go-byteutil v1.3.0 h1:nRqJnCcRQ7xbfU6azw7zOzJrSMDIJHBqX6FL9vEMYmU=
|
||||
codeberg.org/gruf/go-byteutil v1.3.0/go.mod h1:chgnZz1LUcfaObaIFglxF5MRYQkJGjQf4WwVz95ccCM=
|
||||
codeberg.org/gruf/go-cache/v3 v3.6.1 h1:sY1XhYeskjZAuYeMm5R0o4Qymru5taNbzmZPSn1oXLE=
|
||||
codeberg.org/gruf/go-cache/v3 v3.6.1/go.mod h1:JUNjc4E8gRccn3t+B99akxURFrU6NTDkvFVcwiZirnw=
|
||||
codeberg.org/gruf/go-caller v0.0.0-20250806133437-db8d0b1f71cf h1:Rzu7WLpscj2w1N+ClIHlJoTYf9SuqZrZ7E4f9T7jGdw=
|
||||
codeberg.org/gruf/go-caller v0.0.0-20250806133437-db8d0b1f71cf/go.mod h1:jEyYiqCzH1TaxfclSFYthE32oI0dsMnRS6EHqy6y0uo=
|
||||
codeberg.org/gruf/go-debug v1.3.0 h1:PIRxQiWUFKtGOGZFdZ3Y0pqyfI0Xr87j224IYe2snZs=
|
||||
codeberg.org/gruf/go-debug v1.3.0/go.mod h1:N+vSy9uJBQgpQcJUqjctvqFz7tBHJf+S/PIjLILzpLg=
|
||||
codeberg.org/gruf/go-errors/v2 v2.3.2 h1:8ItWaOMfhDaqrJK1Pw8MO0Nu+o/tVcQtR5cJ58Vc4zo=
|
||||
|
|
@ -26,38 +26,44 @@ codeberg.org/gruf/go-fastcopy v1.1.3 h1:Jo9VTQjI6KYimlw25PPc7YLA3Xm+XMQhaHwKnM7x
|
|||
codeberg.org/gruf/go-fastcopy v1.1.3/go.mod h1:GDDYR0Cnb3U/AIfGM3983V/L+GN+vuwVMvrmVABo21s=
|
||||
codeberg.org/gruf/go-fastpath/v2 v2.0.0 h1:iAS9GZahFhyWEH0KLhFEJR+txx1ZhMXxYzu2q5Qo9c0=
|
||||
codeberg.org/gruf/go-fastpath/v2 v2.0.0/go.mod h1:3pPqu5nZjpbRrOqvLyAK7puS1OfEtQvjd6342Cwz56Q=
|
||||
codeberg.org/gruf/go-ffmpreg v0.6.7 h1:WBAgmHT58oqHBm6ckU1jPz7qamqFJj/Uag0yalevpPo=
|
||||
codeberg.org/gruf/go-ffmpreg v0.6.7/go.mod h1:tGqIMh/I2cizqauxxNAN+WGkICI0j5G3xwF1uBkyw1E=
|
||||
codeberg.org/gruf/go-ffmpreg v0.6.12 h1:mPdRx1TAQJQPhRkTOOHnRSY6omNCLJ7M6ajjuEMNNvE=
|
||||
codeberg.org/gruf/go-ffmpreg v0.6.12/go.mod h1:tGqIMh/I2cizqauxxNAN+WGkICI0j5G3xwF1uBkyw1E=
|
||||
codeberg.org/gruf/go-iotools v0.0.0-20240710125620-934ae9c654cf h1:84s/ii8N6lYlskZjHH+DG6jyia8w2mXMZlRwFn8Gs3A=
|
||||
codeberg.org/gruf/go-iotools v0.0.0-20240710125620-934ae9c654cf/go.mod h1:zZAICsp5rY7+hxnws2V0ePrWxE0Z2Z/KXcN3p/RQCfk=
|
||||
codeberg.org/gruf/go-kv v1.6.5 h1:ttPf0NA8F79pDqBttSudPTVCZmGncumeNIxmeM9ztz0=
|
||||
codeberg.org/gruf/go-kv v1.6.5/go.mod h1:c4PsGqw05bDScvISpK+d31SiDEpBorweCL50hsiK3dc=
|
||||
codeberg.org/gruf/go-kv/v2 v2.0.7 h1:RdTY28NX1N/lc3/ivuasnyqnMdQKwV0es3iqSM/DG44=
|
||||
codeberg.org/gruf/go-kv/v2 v2.0.7/go.mod h1:uo6rPR14/ll+SDSU3K7DfINNmWD5NJ0EiahPayOguy0=
|
||||
codeberg.org/gruf/go-list v0.0.0-20240425093752-494db03d641f h1:Ss6Z+vygy+jOGhj96d/GwsYYDd22QmIcH74zM7/nQkw=
|
||||
codeberg.org/gruf/go-list v0.0.0-20240425093752-494db03d641f/go.mod h1:F9pl4h34iuVN7kucKam9fLwsItTc+9mmaKt7pNXRd/4=
|
||||
codeberg.org/gruf/go-loosy v0.0.0-20231007123304-bb910d1ab5c4 h1:IXwfoU7f2whT6+JKIKskNl/hBlmWmnF1vZd84Eb3cyA=
|
||||
codeberg.org/gruf/go-loosy v0.0.0-20231007123304-bb910d1ab5c4/go.mod h1:fiO8HE1wjZCephcYmRRsVnNI/i0+mhy44Z5dQalS0rM=
|
||||
codeberg.org/gruf/go-mangler v1.4.4 h1:moQl7FSSLLaByS7w5UP7b3Z7r2ex/F4IpvSp+PyRWK4=
|
||||
codeberg.org/gruf/go-mangler v1.4.4/go.mod h1:mDmW8Ia352RvNFaXoP9K60TgcmCZJtX0j6wm3vjAsJE=
|
||||
codeberg.org/gruf/go-mangler/v2 v2.0.6 h1:c3cwnI6Mi17EAwGSYGNMN6+9PMzaIj2GLAKx9DKZwoI=
|
||||
codeberg.org/gruf/go-mangler/v2 v2.0.6/go.mod h1:CXIm7zAWPdNmZVAGM1NRiF/ekJTPE7YTb8kiRxiEFaQ=
|
||||
codeberg.org/gruf/go-maps v1.0.4 h1:K+Ww4vvR3TZqm5jqrKVirmguZwa3v1VUvmig2SE8uxY=
|
||||
codeberg.org/gruf/go-maps v1.0.4/go.mod h1:ASX7osM7kFwt5O8GfGflcFjrwYGD8eIuRLl/oMjhEi8=
|
||||
codeberg.org/gruf/go-mempool v0.0.0-20240507125005-cef10d64a760 h1:m2/UCRXhjDwAg4vyji6iKCpomKw6P4PmBOUi5DvAMH4=
|
||||
codeberg.org/gruf/go-mempool v0.0.0-20240507125005-cef10d64a760/go.mod h1:E3RcaCFNq4zXpvaJb8lfpPqdUAmSkP5F1VmMiEUYTEk=
|
||||
codeberg.org/gruf/go-mutexes v1.5.2 h1:rp2o774ApGUVtOHDksqtBiqIcvniVfgFWSazszDluy0=
|
||||
codeberg.org/gruf/go-mutexes v1.5.2/go.mod h1:AnhagsMzUISL/nBVwhnHwDwTZOAxMILwCOG8/wKOblg=
|
||||
codeberg.org/gruf/go-mempool v0.0.0-20251003110531-b54adae66253 h1:qPAY72xCWlySVROSNZecfLGAyeV/SiXmPmfhUU+o3Xw=
|
||||
codeberg.org/gruf/go-mempool v0.0.0-20251003110531-b54adae66253/go.mod h1:761koiXmqfgzvu5mez2Rk7YlwWilpqJ/zv5hIA6NoNI=
|
||||
codeberg.org/gruf/go-mutexes v1.5.8 h1:HRGnvT4COb3jX9xdeoSUUbjPgmk5kXPuDfld9ksUJKA=
|
||||
codeberg.org/gruf/go-mutexes v1.5.8/go.mod h1:21sy/hWH8dDQBk7ocsxqo2GNpWiIir+e82RG3hjnN20=
|
||||
codeberg.org/gruf/go-runners v1.6.3 h1:To/AX7eTrWuXrTkA3RA01YTP5zha1VZ68LQ+0D4RY7E=
|
||||
codeberg.org/gruf/go-runners v1.6.3/go.mod h1:oXAaUmG2VxoKttpCqZGv5nQBeSvZSR2BzIk7h1yTRlU=
|
||||
codeberg.org/gruf/go-sched v1.2.4 h1:ddBB9o0D/2oU8NbQ0ldN5aWxogpXPRBATWi58+p++Hw=
|
||||
codeberg.org/gruf/go-sched v1.2.4/go.mod h1:wad6l+OcYGWMA2TzNLMmLObsrbBDxdJfEy5WvTgBjNk=
|
||||
codeberg.org/gruf/go-storage v0.2.0 h1:mKj3Lx6AavEkuXXtxqPhdq+akW9YwrnP16yQBF7K5ZI=
|
||||
codeberg.org/gruf/go-storage v0.2.0/go.mod h1:o3GzMDE5QNUaRnm/daUzFqvuAaC4utlgXDXYO79sWKU=
|
||||
codeberg.org/gruf/go-structr v0.9.6 h1:FSbJ1A0ubTQB82rC0K4o6qyiqrDGH1t9ivttm8Zy64o=
|
||||
codeberg.org/gruf/go-structr v0.9.6/go.mod h1:9k5hYztZ4PsBS+m1v5hUTeFiVUBTLF5VA7d9cd1OEMs=
|
||||
codeberg.org/superseriousbusiness/go-swagger v0.31.0-gts-go1.23-fix h1:+JvBZqsQfdT+ROnk2DkvXsKQ9QBorKKKBk5fBqw62I8=
|
||||
codeberg.org/superseriousbusiness/go-swagger v0.31.0-gts-go1.23-fix/go.mod h1:WSigRRWEig8zV6t6Sm8Y+EmUjlzA/HoaZJ5edupq7po=
|
||||
codeberg.org/gruf/go-split v1.2.0 h1:PmzL23nVEVHm8VxjsJmv4m4wGQz2bGgQw52dgSSj65c=
|
||||
codeberg.org/gruf/go-split v1.2.0/go.mod h1:0rejWJpqvOoFAd7nwm5tIXYKaAqjtFGOXmTqQV+VO38=
|
||||
codeberg.org/gruf/go-storage v0.3.1 h1:g66UIM/xXnEk9ejT+W0T9s/PODBZhXa/8ajzeY/MELI=
|
||||
codeberg.org/gruf/go-storage v0.3.1/go.mod h1:r43n/zi7YGOCl2iSl7AMI27D1zcWS65Bi2+5xDzypeo=
|
||||
codeberg.org/gruf/go-structr v0.9.12 h1:yMopvexnuKgZme9WgvIhrJaAuAjfper/x38xsVuJOOo=
|
||||
codeberg.org/gruf/go-structr v0.9.12/go.mod h1:sP2ZSjM5X5XKlxuhAbTKuVQm9DWbHsrQRuTl3MUwbHw=
|
||||
codeberg.org/gruf/go-xunsafe v0.0.0-20250809104800-512a9df57d73 h1:pRaOwIOS1WSZoPCAvE0H1zpv+D4gF37OVppybffqdI8=
|
||||
codeberg.org/gruf/go-xunsafe v0.0.0-20250809104800-512a9df57d73/go.mod h1:9wkq+dmHjUhB/0ZxDUWAwsWuXwwGyx5N1dDCB9hpWs8=
|
||||
codeberg.org/superseriousbusiness/go-swagger v0.32.3-gts-go1.23-fix h1:k76/Th+bruqU/d+dB0Ru466ctTF2aVjKpisy/471ILE=
|
||||
codeberg.org/superseriousbusiness/go-swagger v0.32.3-gts-go1.23-fix/go.mod h1:lAwO1nKff3qNRJYVQeTCl1am5pcNiiA2VyDf8TqzS24=
|
||||
github.com/DmitriyVTitov/size v1.5.0 h1:/PzqxYrOyOUX1BXj6J9OuVRVGe+66VL4D9FlUaW515g=
|
||||
github.com/DmitriyVTitov/size v1.5.0/go.mod h1:le6rNI4CoLQV1b9gzp1+3d7hMAD/uu2QcJ+aYbNgiU0=
|
||||
github.com/KimMachineGun/automemlimit v0.7.1 h1:QcG/0iCOLChjfUweIMC3YL5Xy9C3VBeNmCZHrZfJMBw=
|
||||
github.com/KimMachineGun/automemlimit v0.7.1/go.mod h1:QZxpHaGOQoYvFhv/r4u3U0JTC2ZcOwbSr11UZF46UBM=
|
||||
github.com/KimMachineGun/automemlimit v0.7.4 h1:UY7QYOIfrr3wjjOAqahFmC3IaQCLWvur9nmfIn6LnWk=
|
||||
github.com/KimMachineGun/automemlimit v0.7.4/go.mod h1:QZxpHaGOQoYvFhv/r4u3U0JTC2ZcOwbSr11UZF46UBM=
|
||||
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
|
||||
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
|
||||
github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
|
||||
|
|
@ -67,10 +73,12 @@ github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj
|
|||
github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM=
|
||||
github.com/SherClockHolmes/webpush-go v1.4.0 h1:ocnzNKWN23T9nvHi6IfyrQjkIc0oJWv1B1pULsf9i3s=
|
||||
github.com/SherClockHolmes/webpush-go v1.4.0/go.mod h1:XSq8pKX11vNV8MJEMwjrlTkxhAj1zKfxmyhdV7Pd6UA=
|
||||
github.com/TylerBrock/colorjson v0.0.0-20200706003622-8a50f05110d2 h1:ZBbLwSJqkHBuFDA6DUhhse0IGJ7T5bemHyNILUjvOq4=
|
||||
github.com/TylerBrock/colorjson v0.0.0-20200706003622-8a50f05110d2/go.mod h1:VSw57q4QFiWDbRnjdX8Cb3Ow0SFncRw+bA/ofY6Q83w=
|
||||
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
|
||||
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
|
||||
github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
|
||||
github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
|
||||
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
|
||||
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
|
||||
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
|
||||
|
|
@ -81,13 +89,13 @@ github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8
|
|||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
||||
github.com/buckket/go-blurhash v1.1.0 h1:X5M6r0LIvwdvKiUtiNcRL2YlmOfMzYobI3VCKCZc9Do=
|
||||
github.com/buckket/go-blurhash v1.1.0/go.mod h1:aT2iqo5W9vu9GpyoLErKfTHwgODsZp3bQfXjXJUxNb8=
|
||||
github.com/bytedance/sonic v1.13.2 h1:8/H1FempDZqC4VqjptGo14QQlJx8VdZJegxs6wwfqpQ=
|
||||
github.com/bytedance/sonic v1.13.2/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4=
|
||||
github.com/bytedance/sonic v1.13.3 h1:MS8gmaH16Gtirygw7jV91pDCN33NyMrPbN7qiYhEsF0=
|
||||
github.com/bytedance/sonic v1.13.3/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4=
|
||||
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||
github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY=
|
||||
github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
|
||||
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/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=
|
||||
github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
|
||||
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/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4=
|
||||
|
|
@ -95,8 +103,8 @@ github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJ
|
|||
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
|
||||
github.com/cnf/structhash v0.0.0-20201127153200-e1b16c1ebc08 h1:ox2F0PSMlrAAiAdknSRMDrAr8mfxPCfSZolH+/qQnyQ=
|
||||
github.com/cnf/structhash v0.0.0-20201127153200-e1b16c1ebc08/go.mod h1:pCxVEbcm3AMg7ejXyorUXi6HQCzOIBf7zEDVPtw0/U4=
|
||||
github.com/coreos/go-oidc/v3 v3.14.1 h1:9ePWwfdwC4QKRlCXsJGou56adA/owXczOzwKdOumLqk=
|
||||
github.com/coreos/go-oidc/v3 v3.14.1/go.mod h1:HaZ3szPaZ0e4r6ebqvsLWlk2Tn+aejfmrfah6hnSYEU=
|
||||
github.com/coreos/go-oidc/v3 v3.15.0 h1:R6Oz8Z4bqWR7VFQ+sPSvZPQv4x8M+sJkDO5ojgwlyAg=
|
||||
github.com/coreos/go-oidc/v3 v3.15.0/go.mod h1:HaZ3szPaZ0e4r6ebqvsLWlk2Tn+aejfmrfah6hnSYEU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
|
|
@ -121,41 +129,43 @@ github.com/dsoprea/go-utility/v2 v2.0.0-20200717064901-2fccff4aa15e h1:IxIbA7VbC
|
|||
github.com/dsoprea/go-utility/v2 v2.0.0-20200717064901-2fccff4aa15e/go.mod h1:uAzdkPTub5Y9yQwXe8W4m2XuP0tK4a9Q/dantD0+uaU=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
|
||||
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
|
||||
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
|
||||
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
|
||||
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
||||
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||
github.com/fxamacker/cbor v1.5.1 h1:XjQWBgdmQyqimslUh5r4tUGmoqzHmBFQOImkWGi2awg=
|
||||
github.com/fxamacker/cbor v1.5.1/go.mod h1:3aPGItF174ni7dDzd6JZ206H8cmr4GDNBGpPa971zsU=
|
||||
github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
|
||||
github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
|
||||
github.com/gavv/httpexpect v2.0.0+incompatible h1:1X9kcRshkSKEjNJJxX9Y9mQ5BRfbxU5kORdjhlA1yX8=
|
||||
github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc=
|
||||
github.com/gin-contrib/cors v1.7.5 h1:cXC9SmofOrRg0w9PigwGlHG3ztswH6bqq4vJVXnvYMk=
|
||||
github.com/gin-contrib/cors v1.7.5/go.mod h1:4q3yi7xBEDDWKapjT2o1V7mScKDDr8k+jZ0fSquGoy0=
|
||||
github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY=
|
||||
github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok=
|
||||
github.com/gavv/httpexpect/v2 v2.17.0 h1:nIJqt5v5e4P7/0jODpX2gtSw+pHXUqdP28YcjqwDZmE=
|
||||
github.com/gavv/httpexpect/v2 v2.17.0/go.mod h1:E8ENFlT9MZ3Si2sfM6c6ONdwXV2noBCGkhA+lkJgkP0=
|
||||
github.com/gin-contrib/cors v1.7.6 h1:3gQ8GMzs1Ylpf70y8bMw4fVpycXIeX1ZemuSQIsnQQY=
|
||||
github.com/gin-contrib/cors v1.7.6/go.mod h1:Ulcl+xN4jel9t1Ry8vqph23a60FwH9xVLd+3ykmTjOk=
|
||||
github.com/gin-contrib/gzip v1.2.3 h1:dAhT722RuEG330ce2agAs75z7yB+NKvX/ZM1r8w0u2U=
|
||||
github.com/gin-contrib/gzip v1.2.3/go.mod h1:ad72i4Bzmaypk8M762gNXa2wkxxjbz0icRNnuLJ9a/c=
|
||||
github.com/gin-contrib/sessions v1.0.3 h1:AZ4j0AalLsGqdrKNbbrKcXx9OJZqViirvNGsJTxcQps=
|
||||
github.com/gin-contrib/sessions v1.0.3/go.mod h1:5i4XMx4KPtQihnzxEqG9u1K446lO3G19jAi2GtbfsAI=
|
||||
github.com/gin-contrib/sse v1.0.0 h1:y3bT1mUWUxDpW4JLQg/HnTqV4rozuW4tC9eFKTxYI9E=
|
||||
github.com/gin-contrib/sse v1.0.0/go.mod h1:zNuFdwarAygJBht0NTKiSi3jRf6RbqeILZ9Sp6Slhe0=
|
||||
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
|
||||
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
||||
github.com/gin-contrib/sessions v1.0.4 h1:ha6CNdpYiTOK/hTp05miJLbpTSNfOnFg5Jm2kbcqy8U=
|
||||
github.com/gin-contrib/sessions v1.0.4/go.mod h1:ccmkrb2z6iU2osiAHZG3x3J4suJK+OU27oqzlWOqQgs=
|
||||
github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
|
||||
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
|
||||
github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ=
|
||||
github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
||||
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
|
||||
github.com/go-errors/errors v1.0.2/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs=
|
||||
github.com/go-errors/errors v1.1.1 h1:ljK/pL5ltg3qoN+OtN6yCv9HWSfMwxSx90GJCZQxYNg=
|
||||
github.com/go-errors/errors v1.1.1/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs=
|
||||
github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
|
||||
github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
||||
github.com/go-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE=
|
||||
github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA=
|
||||
github.com/go-jose/go-jose/v4 v4.1.1 h1:JYhSgy4mXXzAdF3nUx3ygx347LRXJRrpgyU3adRmkAI=
|
||||
github.com/go-jose/go-jose/v4 v4.1.1/go.mod h1:BdsZGqgdO3b6tTc6LSE56wcDbMMLuPsw5d4ZD5f94kA=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-openapi/analysis v0.23.0 h1:aGday7OWupfMs+LbmLZG4k0MYXIANxcuBTYUC03zFCU=
|
||||
|
|
@ -193,17 +203,17 @@ github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc
|
|||
github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
|
||||
github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
|
||||
github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
|
||||
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/go-xmlfmt/xmlfmt v0.0.0-20191208150333-d5b6f63a941b h1:khEcpUM4yFcxg4/FHQWkvVRmgijNXRfzkIDHh23ggEo=
|
||||
github.com/go-xmlfmt/xmlfmt v0.0.0-20191208150333-d5b6f63a941b/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM=
|
||||
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
||||
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
|
||||
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
|
||||
github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
|
||||
github.com/golang/geo v0.0.0-20200319012246-673a6f80352d h1:C/hKUcHT483btRbeGkrRjJz+Zbcj8audldIi9tRJDCc=
|
||||
github.com/golang/geo v0.0.0-20200319012246-673a6f80352d/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
|
||||
|
|
@ -243,8 +253,10 @@ github.com/gorilla/sessions v1.4.0 h1:kpIYOp/oi6MG/p5PgxApU8srsSw9tuFbt46Lt7auzq
|
|||
github.com/gorilla/sessions v1.4.0/go.mod h1:FLWm50oby91+hl7p/wRxDth9bWSuk0qVL2emc7lT5ik=
|
||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 h1:e9Rjr40Z98/clHv5Yg79Is0NtosR5LXRvdr7o/6NwbA=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1/go.mod h1:tIxuGz/9mpox++sgp9fJjHO0+q1X9/UOWd798aAm22M=
|
||||
github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc h1:GN2Lv3MGO7AS6PrRoT6yV5+wkrOpcszoIsO4+4ds248=
|
||||
github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc/go.mod h1:+JKpmjMGhpgPL+rXZ5nsZieVzvarn86asRlBg4uNGnk=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnVTyacbefKhmbLhIhU=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2/go.mod h1:pkJQ2tZHJ0aFOVEEot6oZmaVEZcRme73eIFmhiVuRWs=
|
||||
github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||
github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU=
|
||||
github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||
|
|
@ -259,8 +271,8 @@ github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsI
|
|||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||
github.com/jackc/pgx/v5 v5.7.4 h1:9wKznZrhWa2QiHL+NjTSPP6yjl3451BX3imWDnokYlg=
|
||||
github.com/jackc/pgx/v5 v5.7.4/go.mod h1:ncY89UGWxg82EykZUwSpUKEfccBGGYq1xjrOpsbsfGQ=
|
||||
github.com/jackc/pgx/v5 v5.7.6 h1:rWQc5FwZSPX58r1OQmkuaNicxdmExaEz5A2DO2hUuTk=
|
||||
github.com/jackc/pgx/v5 v5.7.6/go.mod h1:aruU7o91Tc2q2cFp5h4uP3f6ztExVpyVv88Xl/8Vl8M=
|
||||
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
|
||||
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
|
|
@ -280,8 +292,8 @@ github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zt
|
|||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
|
||||
github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||
github.com/klauspost/cpuid/v2 v2.2.11 h1:0OwqZRYI2rFrjS4kvkDnqJkKHdHaRnCm68/DY4OxRzU=
|
||||
github.com/klauspost/cpuid/v2 v2.2.11/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
|
|
@ -293,21 +305,25 @@ github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
|||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=
|
||||
github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=
|
||||
github.com/miekg/dns v1.1.65 h1:0+tIPHzUW0GCge7IiK3guGP57VAw7hoPDfApjkMD1Fc=
|
||||
github.com/miekg/dns v1.1.65/go.mod h1:Dzw9769uoKVaLuODMDZz9M6ynFU6Em65csPuoi8G0ck=
|
||||
github.com/minio/crc64nvme v1.0.1 h1:DHQPrYPdqK7jQG/Ls5CTBZWeex/2FMS3G5XGkycuFrY=
|
||||
github.com/minio/crc64nvme v1.0.1/go.mod h1:eVfm2fAzLlxMdUGc0EEBGSMmPwmXD5XiNRpnu9J3bvg=
|
||||
github.com/miekg/dns v1.1.68 h1:jsSRkNozw7G/mnmXULynzMNIsgY2dHC8LO6U6Ij2JEA=
|
||||
github.com/miekg/dns v1.1.68/go.mod h1:fujopn7TB3Pu3JM69XaawiU0wqjpL9/8xGop5UrTPps=
|
||||
github.com/minio/crc64nvme v1.0.2 h1:6uO1UxGAD+kwqWWp7mBFsi5gAse66C4NXO8cmcVculg=
|
||||
github.com/minio/crc64nvme v1.0.2/go.mod h1:eVfm2fAzLlxMdUGc0EEBGSMmPwmXD5XiNRpnu9J3bvg=
|
||||
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
|
||||
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
|
||||
github.com/minio/minio-go/v7 v7.0.91 h1:tWLZnEfo3OZl5PoXQwcwTAPNNrjyWwOh6cbZitW5JQc=
|
||||
github.com/minio/minio-go/v7 v7.0.91/go.mod h1:uvMUcGrpgeSAAI6+sD3818508nUyMULw94j2Nxku/Go=
|
||||
github.com/minio/minio-go/v7 v7.0.95 h1:ywOUPg+PebTMTzn9VDsoFJy32ZuARN9zhB+K3IYEvYU=
|
||||
github.com/minio/minio-go/v7 v7.0.95/go.mod h1:wOOX3uxS334vImCNRVyIDdXX9OsXDm89ToynKgqUKlo=
|
||||
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
|
||||
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
|
||||
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
|
||||
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
|
||||
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
|
||||
github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4=
|
||||
github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
|
|
@ -320,12 +336,10 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w
|
|||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/moul/http2curl v1.0.0 h1:dRMWoAtb+ePxMlLkrCbAqh4TlPHXvoGUSQ323/9Zahs=
|
||||
github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/ncruces/go-sqlite3 v0.25.1 h1:nRK2mZ0jLNFJco8QFZ9+dCXxOGe6Re8bbG5o8gyalr8=
|
||||
github.com/ncruces/go-sqlite3 v0.25.1/go.mod h1:4BtkHRLbX5hE0PhBxJ11qETTwL7M4lk0ttru9nora1E=
|
||||
github.com/ncruces/go-sqlite3 v0.29.1 h1:NIi8AISWBToRHyoz01FXiTNvU147Tqdibgj2tFzJCqM=
|
||||
github.com/ncruces/go-sqlite3 v0.29.1/go.mod h1:PpccBNNhvjwUOwDQEn2gXQPFPTWdlromj0+fSkd5KSg=
|
||||
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
|
||||
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||
github.com/ncruces/julianday v1.0.0 h1:fH0OKwa7NWvniGQtxdJRxAgkBMolni2BjDHaWTxqt7M=
|
||||
|
|
@ -334,26 +348,30 @@ github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
|
|||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0=
|
||||
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||
github.com/philhofer/fwd v1.2.0 h1:e6DnBTl7vGY+Gz322/ASL4Gyp1FspeMvx1RNDoToZuM=
|
||||
github.com/philhofer/fwd v1.2.0/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
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/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pquerna/otp v1.4.0 h1:wZvl1TIVxKRThZIBiwOOHOGP/1+nZyWBil9Y2XNEDzg=
|
||||
github.com/pquerna/otp v1.4.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
|
||||
github.com/pquerna/otp v1.5.0 h1:NMMR+WrmaqXU4EzdGJEE1aUUI0AMRzsp96fFFWNPwxs=
|
||||
github.com/pquerna/otp v1.5.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
|
||||
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
|
||||
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
|
||||
github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
|
||||
github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
|
||||
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.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
|
||||
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
|
||||
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/prometheus/client_golang v1.23.0 h1:ust4zpdl9r4trLY/gSjlm07PuiBq2ynaXXlptpfy8Uc=
|
||||
github.com/prometheus/client_golang v1.23.0/go.mod h1:i/o0R9ByOnHX0McrTMTyhYvKE4haaf2mW08I+jGAjEE=
|
||||
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
|
||||
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||
github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE=
|
||||
github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8=
|
||||
github.com/prometheus/otlptranslator v0.0.2 h1:+1CdeLVrRQ6Psmhnobldo0kTp96Rj80DRXRd5OSnMEQ=
|
||||
github.com/prometheus/otlptranslator v0.0.2/go.mod h1:P8AwMgdD7XEr6QRUJ2QWLpiAZTgTE2UYgjlu3svompI=
|
||||
github.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7DuK0=
|
||||
github.com/prometheus/procfs v0.17.0/go.mod h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUOVhe0wYB2zw=
|
||||
github.com/puzpuzpuz/xsync/v3 v3.5.1 h1:GJYJZwO6IdxN/IKbneznS6yPkVC+c3zyY/j19c++5Fg=
|
||||
github.com/puzpuzpuz/xsync/v3 v3.5.1/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA=
|
||||
github.com/quasoft/memstore v0.0.0-20191010062613-2bce066d2b0b h1:aUNXCGgukb4gtY99imuIeoh8Vr0GSwAlYxPAhqZrpFc=
|
||||
|
|
@ -368,36 +386,38 @@ github.com/rogpeppe/go-internal v1.13.2-0.20241226121412-a5dc8ff20d0a/go.mod h1:
|
|||
github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=
|
||||
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo=
|
||||
github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k=
|
||||
github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
|
||||
github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
|
||||
github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc=
|
||||
github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik=
|
||||
github.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQo=
|
||||
github.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=
|
||||
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
|
||||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||
github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
|
||||
github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
||||
github.com/smarty/assertions v1.16.0 h1:EvHNkdRA4QHMrn75NZSoUQ/mAUXAYWfatfB01yTCzfY=
|
||||
github.com/smarty/assertions v1.16.0/go.mod h1:duaaFdCS0K9dnoM50iyek/eYINOZ64gbh1Xlf6LG7AI=
|
||||
github.com/smarty/assertions v1.15.0 h1:cR//PqUBUiQRakZWqBiFFQ9wb8emQGDb0HeGdqGByCY=
|
||||
github.com/smarty/assertions v1.15.0/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+E8C6HtKdec=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/smartystreets/goconvey v1.8.1 h1:qGjIddxOk4grTu9JPOU31tVfq3cNdBlNa5sSznIX1xY=
|
||||
github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60=
|
||||
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
|
||||
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
|
||||
github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs=
|
||||
github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4=
|
||||
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw=
|
||||
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U=
|
||||
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
|
||||
github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
|
||||
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
|
||||
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
|
||||
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
|
||||
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
|
||||
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4=
|
||||
github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4=
|
||||
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
|
||||
github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
|
||||
github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s=
|
||||
github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0=
|
||||
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
||||
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU=
|
||||
github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
|
|
@ -405,65 +425,64 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
|||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
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/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||
github.com/tdewolff/minify/v2 v2.23.1 h1:r6sKQrumHzskWZRdhiRa+pZhn7CdBMojACNP9fuKpXQ=
|
||||
github.com/tdewolff/minify/v2 v2.23.1/go.mod h1:RkUGjklq6uIsBoOdzY3ll35HKKQ2aFqLQhnanBHhDyU=
|
||||
github.com/tdewolff/parse/v2 v2.7.23 h1:sCW2PNTCM1yVldh5YK/8wrpRI9rSbloUZWjAydlN2IA=
|
||||
github.com/tdewolff/parse/v2 v2.7.23/go.mod h1:I7TXO37t3aSG9SlPUBefAhgIF8nt7yYUwVGgETIoBcA=
|
||||
github.com/tdewolff/minify/v2 v2.24.3 h1:BaKgWSFLKbKDiUskbeRgbe2n5d1Ci1x3cN/eXna8zOA=
|
||||
github.com/tdewolff/minify/v2 v2.24.3/go.mod h1:1JrCtoZXaDbqioQZfk3Jdmr0GPJKiU7c1Apmb+7tCeE=
|
||||
github.com/tdewolff/parse/v2 v2.8.3 h1:5VbvtJ83cfb289A1HzRA9sf02iT8YyUwN84ezjkdY1I=
|
||||
github.com/tdewolff/parse/v2 v2.8.3/go.mod h1:Hwlni2tiVNKyzR1o6nUs4FOF07URA+JLBLd6dlIXYqo=
|
||||
github.com/tdewolff/test v1.0.11 h1:FdLbwQVHxqG16SlkGveC0JVyrJN62COWTRyUFzfbtBE=
|
||||
github.com/tdewolff/test v1.0.11/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=
|
||||
github.com/technologize/otel-go-contrib v1.1.1 h1:wZH9aSPNWZWIkEh3vfaKfMb15AJ80jJ1aVj/4GZdqIw=
|
||||
github.com/technologize/otel-go-contrib v1.1.1/go.mod h1:dCN/wj2WyUO8aFZFdIN+6tfJHImjTML/8r2YVYAy3So=
|
||||
github.com/temoto/robotstxt v1.1.2 h1:W2pOjSJ6SWvldyEuiFXNxz3xZ8aiWX5LbfDiOFd7Fxg=
|
||||
github.com/temoto/robotstxt v1.1.2/go.mod h1:+1AmkuG3IYkh1kv0d2qEB9Le88ehNO0zwOr3ujewlOo=
|
||||
github.com/tetratelabs/wazero v1.9.0 h1:IcZ56OuxrtaEz8UYNRHBrUa9bYeX9oVY93KspZZBf/I=
|
||||
github.com/tetratelabs/wazero v1.9.0/go.mod h1:TSbcXCfFP0L2FGkRPxHphadXPjo1T6W+CseNNY7EkjM=
|
||||
github.com/tidwall/btree v1.7.0 h1:L1fkJH/AuEh5zBnnBbmTwQ5Lt+bRJ5A8EWecslvo9iI=
|
||||
github.com/tidwall/btree v1.7.0/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EUQ2cKY=
|
||||
github.com/tidwall/btree v1.4.2 h1:PpkaieETJMUxYNADsjgtNRcERX7mGc/GP2zp/r5FM3g=
|
||||
github.com/tidwall/btree v1.4.2/go.mod h1:LGm8L/DZjPLmeWGjv5kFrY8dL4uVhMmzmmLYmsObdKE=
|
||||
github.com/tidwall/buntdb v1.3.2 h1:qd+IpdEGs0pZci37G4jF51+fSKlkuUTMXuHhXL1AkKg=
|
||||
github.com/tidwall/buntdb v1.3.2/go.mod h1:lZZrZUWzlyDJKlLQ6DKAy53LnG7m5kHyrEHvvcDmBpU=
|
||||
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
|
||||
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/gjson v1.14.3 h1:9jvXn7olKEHU1S9vwoMGliaT8jq1vJ7IH/n9zD9Dnlw=
|
||||
github.com/tidwall/gjson v1.14.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/grect v0.1.4 h1:dA3oIgNgWdSspFzn1kS4S/RDpZFLrIxAZOdJKjYapOg=
|
||||
github.com/tidwall/grect v0.1.4/go.mod h1:9FBsaYRaR0Tcy4UwefBX/UDcDcDy9V5jUcxHzv2jd5Q=
|
||||
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
||||
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
|
||||
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
|
||||
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/tidwall/rtred v0.1.2 h1:exmoQtOLvDoO8ud++6LwVsAMTu0KPzLTUrMln8u1yu8=
|
||||
github.com/tidwall/rtred v0.1.2/go.mod h1:hd69WNXQ5RP9vHd7dqekAz+RIdtfBogmglkZSRxCHFQ=
|
||||
github.com/tidwall/tinyqueue v0.1.1 h1:SpNEvEggbpyN5DIReaJ2/1ndroY8iyEGxPYxoSaymYE=
|
||||
github.com/tidwall/tinyqueue v0.1.1/go.mod h1:O/QNHwrnjqr6IHItYrzoHAKYhBkLI67Q096fQP5zMYw=
|
||||
github.com/tinylib/msgp v1.3.0 h1:ULuf7GPooDaIlbyvgAxBV/FI7ynli6LZ1/nVUNu+0ww=
|
||||
github.com/tinylib/msgp v1.3.0/go.mod h1:ykjzy2wzgrlvpDCRc4LA8UXy6D8bzMSuAF3WD57Gok0=
|
||||
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc h1:9lRDQMhESg+zvGYmW5DyG0UqvY96Bu5QYsTLvCHdrgo=
|
||||
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs=
|
||||
github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80 h1:nrZ3ySNYwJbSpD6ce9duiP+QkD3JuLCcWkdaehUS/3Y=
|
||||
github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80/go.mod h1:iFyPdL66DjUD96XmzVL3ZntbzcflLnznH0fr99w5VqE=
|
||||
github.com/tomnomnom/linkheader v0.0.0-20250811210735-e5fe3b51442e h1:tD38/4xg4nuQCASJ/JxcvCHNb46w0cdAaJfkzQOO1bA=
|
||||
github.com/tomnomnom/linkheader v0.0.0-20250811210735-e5fe3b51442e/go.mod h1:krvJ5AY/MjdPkTeRgMYbIDhbbbVvnPQPzsIsDJO8xrY=
|
||||
github.com/toqueteos/webbrowser v1.2.0 h1:tVP/gpK69Fx+qMJKsLE7TD8LuGWPnEV71wBN9rrstGQ=
|
||||
github.com/toqueteos/webbrowser v1.2.0/go.mod h1:XWoZq4cyp9WeUeak7w7LXRUQf1F1ATJMir8RTqb4ayM=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
|
||||
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA=
|
||||
github.com/ugorji/go/codec v1.3.0/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
|
||||
github.com/ulule/limiter/v3 v3.11.2 h1:P4yOrxoEMJbOTfRJR2OzjL90oflzYPPmWg+dvwN2tHA=
|
||||
github.com/ulule/limiter/v3 v3.11.2/go.mod h1:QG5GnFOCV+k7lrL5Y8kgEeeflPH3+Cviqlqa8SVSQxI=
|
||||
github.com/uptrace/bun v1.2.11 h1:l9dTymsdZZAoSZ1+Qo3utms0RffgkDbIv+1UGk8N1wQ=
|
||||
github.com/uptrace/bun v1.2.11/go.mod h1:ww5G8h59UrOnCHmZ8O1I/4Djc7M/Z3E+EWFS2KLB6dQ=
|
||||
github.com/uptrace/bun/dialect/pgdialect v1.2.11 h1:n0VKWm1fL1dwJK5TRxYYLaRKRe14BOg2+AQgpvqzG/M=
|
||||
github.com/uptrace/bun/dialect/pgdialect v1.2.11/go.mod h1:NvV1S/zwtwBnW8yhJ3XEKAQEw76SkeH7yUhfrx3W1Eo=
|
||||
github.com/uptrace/bun/dialect/sqlitedialect v1.2.11 h1:t4OIcbkWnRPshRj7ZnbHVwUENa3OHhCUruyFcl3P+TY=
|
||||
github.com/uptrace/bun/dialect/sqlitedialect v1.2.11/go.mod h1:XHFFTvdlNtNFWPhpRAConN6DnVgt9EHr5G5IIarHYyg=
|
||||
github.com/uptrace/bun/extra/bunotel v1.2.11 h1:ddt96XrbvlVZu5vBddP6WmbD6bdeJTaWY9jXlfuJKZE=
|
||||
github.com/uptrace/bun/extra/bunotel v1.2.11/go.mod h1:w6Mhie5tLFeP+5ryjq4PvgZEESRJ1iL2cbvxhm+f8q4=
|
||||
github.com/uptrace/bun v1.2.15 h1:Ut68XRBLDgp9qG9QBMa9ELWaZOmzHNdczHQdrOZbEFE=
|
||||
github.com/uptrace/bun v1.2.15/go.mod h1:Eghz7NonZMiTX/Z6oKYytJ0oaMEJ/eq3kEV4vSqG038=
|
||||
github.com/uptrace/bun/dialect/pgdialect v1.2.15 h1:er+/3giAIqpfrXJw+KP9B7ujyQIi5XkPnFmgjAVL6bA=
|
||||
github.com/uptrace/bun/dialect/pgdialect v1.2.15/go.mod h1:QSiz6Qpy9wlGFsfpf7UMSL6mXAL1jDJhFwuOVacCnOQ=
|
||||
github.com/uptrace/bun/dialect/sqlitedialect v1.2.15 h1:7upGMVjFRB1oI78GQw6ruNLblYn5CR+kxqcbbeBBils=
|
||||
github.com/uptrace/bun/dialect/sqlitedialect v1.2.15/go.mod h1:c7YIDaPNS2CU2uI1p7umFuFWkuKbDcPDDvp+DLHZnkI=
|
||||
github.com/uptrace/bun/extra/bunotel v1.2.15 h1:6KAvKRpH9BC/7n3eMXVgDYLqghHf2H3FJOvxs/yjFJM=
|
||||
github.com/uptrace/bun/extra/bunotel v1.2.15/go.mod h1:qnASdcJVuoEE+13N3Gd8XHi5gwCydt2S1TccJnefH2k=
|
||||
github.com/uptrace/opentelemetry-go-extra/otelsql v0.3.2 h1:ZjUj9BLYf9PEqBn8W/OapxhPjVRdC6CsXTdULHsyk5c=
|
||||
github.com/uptrace/opentelemetry-go-extra/otelsql v0.3.2/go.mod h1:O8bHQfyinKwTXKkiKNGmLQS7vRsqRxIQTFZpYpHK3IQ=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasthttp v1.59.0 h1:Qu0qYHfXvPk1mSLNqcFtEk6DpxgA26hy6bmydotDpRI=
|
||||
github.com/valyala/fasthttp v1.59.0/go.mod h1:GTxNb9Bc6r2a9D0TWNSPwDz78UxnTGBViY3xZNEqyYU=
|
||||
github.com/valyala/fasthttp v1.47.0 h1:y7moDoxYzMooFpT5aHgNgVOQDrS3qlkfiP9mDtGGK9c=
|
||||
github.com/valyala/fasthttp v1.47.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA=
|
||||
github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=
|
||||
github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
|
||||
|
|
@ -485,42 +504,68 @@ github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FB
|
|||
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3IfnEUduWvb9is428/nNb5L3U01M=
|
||||
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yuin/goldmark v1.7.11 h1:ZCxLyDMtz0nT2HFfsYG8WZ47Trip2+JyLysKcMYE5bo=
|
||||
github.com/yuin/goldmark v1.7.11/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
|
||||
gitlab.com/NyaaaWhatsUpDoc/sqlite v1.37.0-concurrency-workaround h1:QbfrBqNKgAFSSK89fYf547vxDQuz8p6iJUzzAMrusNk=
|
||||
gitlab.com/NyaaaWhatsUpDoc/sqlite v1.37.0-concurrency-workaround/go.mod h1:5YiWv+YviqGMuGw4V+PNplcyaJ5v+vQd7TQOgkACoJM=
|
||||
github.com/yuin/goldmark v1.7.13 h1:GPddIs617DnBLFFVJFgpo1aBfe/4xcvMc3SB5t/D0pA=
|
||||
github.com/yuin/goldmark v1.7.13/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
|
||||
gitlab.com/NyaaaWhatsUpDoc/sqlite v1.39.0-concurrency-workaround h1:+e2m4Ycgsri3YaePPAuYcTawQxklz0q/3CbKEbqxhOM=
|
||||
gitlab.com/NyaaaWhatsUpDoc/sqlite v1.39.0-concurrency-workaround/go.mod h1:cPTJYSlgg3Sfg046yBShXENNtPrWrDX8bsbAQBzgQ5E=
|
||||
go.mongodb.org/mongo-driver v1.17.3 h1:TQyXhnsWfWtgAhMtOgtYHMTkZIfBTpMTsMnd9ZBeHxQ=
|
||||
go.mongodb.org/mongo-driver v1.17.3/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
|
||||
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0/go.mod h1:zjPK58DtkqQFn+YUMbx0M2XV3QgKU0gS9LeGohREyK4=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 h1:m639+BofXTvcY1q8CGs4ItwQarYtJPOWmVobfM1HpVI=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0/go.mod h1:LjReUci/F4BUyv+y4dwnq3h/26iNOeC3wAIqgvTIZVo=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0 h1:xJ2qHD0C1BeYVTLLR9sX12+Qb95kfeD/byKj6Ky1pXg=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0/go.mod h1:u5BF1xyjstDowA1R5QAO9JHzqK+ublenEW/dyqTjBVk=
|
||||
go.opentelemetry.io/otel/exporters/prometheus v0.57.0 h1:AHh/lAP1BHrY5gBwk8ncc25FXWm/gmmY3BX258z5nuk=
|
||||
go.opentelemetry.io/otel/exporters/prometheus v0.57.0/go.mod h1:QpFWz1QxqevfjwzYdbMb4Y1NnlJvqSGwyuU0B4iuc9c=
|
||||
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
|
||||
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
|
||||
go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY=
|
||||
go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
|
||||
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
|
||||
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
|
||||
go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4=
|
||||
go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4=
|
||||
go.opentelemetry.io/contrib/bridges/prometheus v0.63.0 h1:/Rij/t18Y7rUayNg7Id6rPrEnHgorxYabm2E6wUdPP4=
|
||||
go.opentelemetry.io/contrib/bridges/prometheus v0.63.0/go.mod h1:AdyDPn6pkbkt2w01n3BubRVk7xAsCRq1Yg1mpfyA/0E=
|
||||
go.opentelemetry.io/contrib/exporters/autoexport v0.63.0 h1:NLnZybb9KkfMXPwZhd5diBYJoVxiO9Qa06dacEA7ySY=
|
||||
go.opentelemetry.io/contrib/exporters/autoexport v0.63.0/go.mod h1:OvRg7gm5WRSCtxzGSsrFHbDLToYlStHNZQ+iPNIyD6g=
|
||||
go.opentelemetry.io/contrib/instrumentation/runtime v0.63.0 h1:PeBoRj6af6xMI7qCupwFvTbbnd49V7n5YpG6pg8iDYQ=
|
||||
go.opentelemetry.io/contrib/instrumentation/runtime v0.63.0/go.mod h1:ingqBCtMCe8I4vpz/UVzCW6sxoqgZB37nao91mLQ3Bw=
|
||||
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
|
||||
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.14.0 h1:OMqPldHt79PqWKOMYIAQs3CxAi7RLgPxwfFSwr4ZxtM=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.14.0/go.mod h1:1biG4qiqTxKiUCtoWDPpL3fB3KxVwCiGw81j3nKMuHE=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.14.0 h1:QQqYw3lkrzwVsoEX0w//EhH/TCnpRdEenKBOOEIMjWc=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.14.0/go.mod h1:gSVQcr17jk2ig4jqJ2DX30IdWH251JcNAecvrqTxH1s=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0 h1:vl9obrcoWVKp/lwl8tRE33853I8Xru9HFbw/skNeLs8=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0/go.mod h1:GAXRxmLJcVM3u22IjTg74zWBrRCKq8BnOqUVLodpcpw=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.38.0 h1:Oe2z/BCg5q7k4iXC3cqJxKYg0ieRiOqF0cecFYdPTwk=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.38.0/go.mod h1:ZQM5lAJpOsKnYagGg/zV2krVqTtaVdYdDkhMoX6Oalg=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 h1:GqRJVj7UmLjCVyVJ3ZFLdPRmhDUp2zFmQe3RHIOsw24=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0/go.mod h1:ri3aaHSmCTVYu2AWv44YMauwAQc0aqI9gHKIcSbI1pU=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 h1:lwI4Dc5leUqENgGuQImwLo4WnuXFPetmPpkLi2IrX54=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0/go.mod h1:Kz/oCE7z5wuyhPxsXDuaPteSWqjSBD5YaSdbxZYGbGk=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0 h1:aTL7F04bJHUlztTsNGJ2l+6he8c+y/b//eR0jjjemT4=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0/go.mod h1:kldtb7jDTeol0l3ewcmd8SDvx3EmIE7lyvqbasU3QC4=
|
||||
go.opentelemetry.io/otel/exporters/prometheus v0.60.0 h1:cGtQxGvZbnrWdC2GyjZi0PDKVSLWP/Jocix3QWfXtbo=
|
||||
go.opentelemetry.io/otel/exporters/prometheus v0.60.0/go.mod h1:hkd1EekxNo69PTV4OWFGZcKQiIqg0RfuWExcPKFvepk=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.14.0 h1:B/g+qde6Mkzxbry5ZZag0l7QrQBCtVm7lVjaLgmpje8=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.14.0/go.mod h1:mOJK8eMmgW6ocDJn6Bn11CcZ05gi3P8GylBXEkZtbgA=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.38.0 h1:wm/Q0GAAykXv83wzcKzGGqAnnfLFyFe7RslekZuv+VI=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.38.0/go.mod h1:ra3Pa40+oKjvYh+ZD3EdxFZZB0xdMfuileHAm4nNN7w=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.38.0 h1:kJxSDN4SgWWTjG/hPp3O7LCGLcHXFlvS2/FFOrwL+SE=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.38.0/go.mod h1:mgIOzS7iZeKJdeB8/NYHrJ48fdGc71Llo5bJ1J4DWUE=
|
||||
go.opentelemetry.io/otel/log v0.14.0 h1:2rzJ+pOAZ8qmZ3DDHg73NEKzSZkhkGIua9gXtxNGgrM=
|
||||
go.opentelemetry.io/otel/log v0.14.0/go.mod h1:5jRG92fEAgx0SU/vFPxmJvhIuDU9E1SUnEQrMlJpOno=
|
||||
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
|
||||
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=
|
||||
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E=
|
||||
go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg=
|
||||
go.opentelemetry.io/otel/sdk/log v0.14.0 h1:JU/U3O7N6fsAXj0+CXz21Czg532dW2V4gG1HE/e8Zrg=
|
||||
go.opentelemetry.io/otel/sdk/log v0.14.0/go.mod h1:imQvII+0ZylXfKU7/wtOND8Hn4OpT3YUoIgqJVksUkM=
|
||||
go.opentelemetry.io/otel/sdk/log/logtest v0.14.0 h1:Ijbtz+JKXl8T2MngiwqBlPaHqc4YCaP/i13Qrow6gAM=
|
||||
go.opentelemetry.io/otel/sdk/log/logtest v0.14.0/go.mod h1:dCU8aEL6q+L9cYTqcVOk8rM9Tp8WdnHOPLiBgp0SGOA=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA=
|
||||
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
|
||||
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
|
||||
go.opentelemetry.io/proto/otlp v1.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOVAtj4=
|
||||
go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE=
|
||||
go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
|
||||
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
golang.org/x/arch v0.16.0 h1:foMtLTdyOmIniqWCHjY6+JxuC54XP1fDwx4N0ASyW+U=
|
||||
golang.org/x/arch v0.16.0/go.mod h1:JmwW7aLIoRUKgaTzhkiEFxvcEiQGyOg9BMonBJUS7EE=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
golang.org/x/arch v0.18.0 h1:WN9poc33zL4AzGxqf8VtpKUnGvMi8O9lhNyBMF/85qc=
|
||||
golang.org/x/arch v0.18.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||
|
|
@ -528,19 +573,19 @@ golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliY
|
|||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
|
||||
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
|
||||
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw=
|
||||
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM=
|
||||
golang.org/x/image v0.26.0 h1:4XjIFEZWQmCZi6Wv8BoxsDhRU3RVnLX04dToTDAEPlY=
|
||||
golang.org/x/image v0.26.0/go.mod h1:lcxbMFAovzpnJxzXS3nyL83K27tmqtKzIJpctK8YO5c=
|
||||
golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI=
|
||||
golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8=
|
||||
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o=
|
||||
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8=
|
||||
golang.org/x/image v0.31.0 h1:mLChjE2MV6g1S7oqbXC0/UcKijjm5fnJLUYKIYrLESA=
|
||||
golang.org/x/image v0.31.0/go.mod h1:R9ec5Lcp96v9FTF+ajwaH3uGxPH4fKfHHAVbUILxghA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
|
||||
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||
golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ=
|
||||
golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
|
|
@ -556,10 +601,10 @@ golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
|||
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
|
||||
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
|
||||
golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98=
|
||||
golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
|
||||
golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I=
|
||||
golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
|
||||
golang.org/x/oauth2 v0.31.0 h1:8Fq0yVZLh4j4YA47vHKFTa9Ew5XIrCP8LC6UeNZnLxo=
|
||||
golang.org/x/oauth2 v0.31.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
|
|
@ -567,8 +612,8 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
|||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
|
||||
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug=
|
||||
golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
|
@ -584,8 +629,8 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
||||
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
|
||||
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
|
|
@ -596,8 +641,8 @@ golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
|||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
||||
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
|
||||
golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o=
|
||||
golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw=
|
||||
golang.org/x/term v0.35.0 h1:bZBVKBudEyhRcajGcNc3jIfWPqV4y/Kt2XcoigOWtDQ=
|
||||
golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
|
|
@ -608,8 +653,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
|||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
|
||||
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
|
||||
golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
|
||||
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
|
|
@ -617,17 +662,19 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
|
|||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
golang.org/x/tools v0.31.0 h1:0EedkvKDbh+qistFTd0Bcwe/YLh4vHwWEkiI0toFIBU=
|
||||
golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ=
|
||||
golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
|
||||
golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a h1:nwKuGPlUAt+aR+pcrkfFRrTU1BVrSmYyYMxYbUIVHr0=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a/go.mod h1:3kWAYMk1I75K4vykHtKt2ycnOgpA6974V7bREqbsenU=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a h1:51aaUVRocpvUOSQKM6Q7VuoaktNIaMCLuhZB6DKksq4=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a/go.mod h1:uRxBH1mhmO8PGhU89cMcHaXKZqO+OfakD8QQO0oYwlQ=
|
||||
google.golang.org/grpc v1.71.0 h1:kF77BGdPTQ4/JZWMlb9VpJ5pa25aqvVqogsxNHHdeBg=
|
||||
google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec=
|
||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 h1:BIRfGDEjiHRrk0QKZe3Xv2ieMhtgRGeLcZQ0mIVn4EY=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5/go.mod h1:j3QtIyytwqGr1JUDtYXwtMXWPKsEa5LtzIFN1Wn5WvE=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 h1:eaY8u2EuxbRv7c3NiGK0/NedzVsCcV6hDuU5qPX5EGE=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5/go.mod h1:M4/wBTSeyLxupu3W3tJtOgB14jILAS/XWPSSa3TAlJc=
|
||||
google.golang.org/grpc v1.75.0 h1:+TW+dqTd2Biwe6KKfhE5JpiYIBWq865PhKGSXiivqt4=
|
||||
google.golang.org/grpc v1.75.0/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ=
|
||||
google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc=
|
||||
google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/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=
|
||||
|
|
@ -641,20 +688,22 @@ 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=
|
||||
modernc.org/cc/v4 v4.25.2 h1:T2oH7sZdGvTaie0BRNFbIYsabzCxUQg8nLqCdQ2i0ic=
|
||||
modernc.org/cc/v4 v4.25.2/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
|
||||
modernc.org/ccgo/v4 v4.25.1 h1:TFSzPrAGmDsdnhT9X2UrcPMI3N/mJ9/X9ykKXwLhDsU=
|
||||
modernc.org/ccgo/v4 v4.25.1/go.mod h1:njjuAYiPflywOOrm3B7kCB444ONP5pAVr8PIEoE0uDw=
|
||||
modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE=
|
||||
modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ=
|
||||
modernc.org/cc/v4 v4.26.2 h1:991HMkLjJzYBIfha6ECZdjrIYz2/1ayr+FL8GN+CNzM=
|
||||
modernc.org/cc/v4 v4.26.2/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
|
||||
modernc.org/ccgo/v4 v4.28.0 h1:rjznn6WWehKq7dG4JtLRKxb52Ecv8OUGah8+Z/SfpNU=
|
||||
modernc.org/ccgo/v4 v4.28.0/go.mod h1:JygV3+9AV6SmPhDasu4JgquwU81XAKLd3OKTUDNOiKE=
|
||||
modernc.org/fileutil v1.3.8 h1:qtzNm7ED75pd1C7WgAGcK4edm4fvhtBsEiI/0NQ54YM=
|
||||
modernc.org/fileutil v1.3.8/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc=
|
||||
modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
|
||||
modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
|
||||
modernc.org/libc v1.62.1 h1:s0+fv5E3FymN8eJVmnk0llBe6rOxCu/DEU+XygRbS8s=
|
||||
modernc.org/libc v1.62.1/go.mod h1:iXhATfJQLjG3NWy56a6WVU73lWOcdYVxsvwCgoPljuo=
|
||||
modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks=
|
||||
modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI=
|
||||
modernc.org/libc v1.66.3 h1:cfCbjTUcdsKyyZZfEUKfoHcP3S0Wkvz3jgSzByEWVCQ=
|
||||
modernc.org/libc v1.66.3/go.mod h1:XD9zO8kt59cANKvHPXpx7yS2ELPheAey0vjIuZOhOU8=
|
||||
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
|
||||
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
|
||||
modernc.org/memory v1.9.1 h1:V/Z1solwAVmMW1yttq3nDdZPJqV1rM05Ccq6KMSZ34g=
|
||||
modernc.org/memory v1.9.1/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
|
||||
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
|
||||
modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
|
||||
modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
|
||||
modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
|
||||
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
|
||||
|
|
@ -663,6 +712,8 @@ modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
|
|||
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
|
||||
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
||||
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||
moul.io/http2curl/v2 v2.3.0 h1:9r3JfDzWPcbIklMOs2TnIFzDYvfAZvjeavG6EzP7jYs=
|
||||
moul.io/http2curl/v2 v2.3.0/go.mod h1:RW4hyBjTWSYDOxapodpNEtX0g5Eb16sxklBqmd2RHcE=
|
||||
mvdan.cc/xurls/v2 v2.6.0 h1:3NTZpeTxYVWNSokW3MKeyVkz/j7uYXYiMtXRUfmjbgI=
|
||||
mvdan.cc/xurls/v2 v2.6.0/go.mod h1:bCvEZ1XvdA6wDnxY7jPPjEmigDtvtvPXAD/Exa9IMSk=
|
||||
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ func (suite *ActionsTestSuite) SetupSuite() {
|
|||
func (suite *ActionsTestSuite) TestActionOverlap() {
|
||||
var (
|
||||
testStructs = testrig.SetupTestStructs(rMediaPath, rTemplatePath)
|
||||
ctx = context.Background()
|
||||
ctx = suite.T().Context()
|
||||
)
|
||||
defer testrig.TearDownTestStructs(testStructs)
|
||||
|
||||
|
|
@ -130,7 +130,7 @@ func (suite *ActionsTestSuite) TestActionOverlap() {
|
|||
func (suite *ActionsTestSuite) TestActionWithErrors() {
|
||||
var (
|
||||
testStructs = testrig.SetupTestStructs(rMediaPath, rTemplatePath)
|
||||
ctx = context.Background()
|
||||
ctx = suite.T().Context()
|
||||
)
|
||||
defer testrig.TearDownTestStructs(testStructs)
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ import (
|
|||
"errors"
|
||||
"time"
|
||||
|
||||
"codeberg.org/gruf/go-kv"
|
||||
"codeberg.org/gruf/go-kv/v2"
|
||||
|
||||
"code.superseriousbusiness.org/gotosocial/internal/ap"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/config"
|
||||
|
|
|
|||
|
|
@ -23,15 +23,18 @@ import (
|
|||
"code.superseriousbusiness.org/activity/pub"
|
||||
)
|
||||
|
||||
// PublicURI returns a fresh copy of the *url.URL version of the
|
||||
// magic ActivityPub URI https://www.w3.org/ns/activitystreams#Public
|
||||
func PublicURI() *url.URL {
|
||||
publicURI, err := url.Parse(pub.PublicActivityPubIRI)
|
||||
// publicIRI is a pre-parsed global public IRI instance.
|
||||
var publicIRI = func() *url.URL {
|
||||
url, err := url.Parse(pub.PublicActivityPubIRI)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return publicURI
|
||||
}
|
||||
return url
|
||||
}()
|
||||
|
||||
// PublicIRI returns a fresh copy of the *url.URL version of the
|
||||
// magic ActivityPub URI https://www.w3.org/ns/activitystreams#Public
|
||||
func PublicIRI() *url.URL { var u url.URL; u = *publicIRI; return &u }
|
||||
|
||||
// https://www.w3.org/TR/activitystreams-vocabulary
|
||||
const (
|
||||
|
|
@ -102,9 +105,12 @@ const (
|
|||
|
||||
/* GtS stuff */
|
||||
|
||||
ObjectLikeApproval = "LikeApproval"
|
||||
ObjectReplyApproval = "ReplyApproval"
|
||||
ObjectAnnounceApproval = "AnnounceApproval"
|
||||
ActivityLikeRequest = "LikeRequest"
|
||||
ActivityReplyRequest = "ReplyRequest"
|
||||
ActivityAnnounceRequest = "AnnounceRequest"
|
||||
ObjectLikeAuthorization = "LikeAuthorization"
|
||||
ObjectReplyAuthorization = "ReplyAuthorization"
|
||||
ObjectAnnounceAuthorization = "AnnounceAuthorization"
|
||||
|
||||
/* Funkwhale stuff */
|
||||
|
||||
|
|
@ -138,7 +144,10 @@ func isActivity(typeName string) bool {
|
|||
ActivityAnnounce,
|
||||
ActivityBlock,
|
||||
ActivityFlag,
|
||||
ActivityDislike:
|
||||
ActivityDislike,
|
||||
ActivityLikeRequest,
|
||||
ActivityReplyRequest,
|
||||
ActivityAnnounceRequest:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
|
|
|
|||
|
|
@ -19,9 +19,9 @@ package ap_test
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"code.superseriousbusiness.org/activity/streams"
|
||||
"code.superseriousbusiness.org/activity/streams/vocab"
|
||||
|
|
@ -110,7 +110,7 @@ func noteWithMentions1() vocab.ActivityStreamsNote {
|
|||
|
||||
// Anyone can like.
|
||||
canLikeAlwaysProp := streams.NewGoToSocialAlwaysProperty()
|
||||
canLikeAlwaysProp.AppendIRI(ap.PublicURI())
|
||||
canLikeAlwaysProp.AppendIRI(ap.PublicIRI())
|
||||
canLike.SetGoToSocialAlways(canLikeAlwaysProp)
|
||||
|
||||
// Empty approvalRequired.
|
||||
|
|
@ -127,7 +127,7 @@ func noteWithMentions1() vocab.ActivityStreamsNote {
|
|||
|
||||
// Anyone can reply.
|
||||
canReplyAlwaysProp := streams.NewGoToSocialAlwaysProperty()
|
||||
canReplyAlwaysProp.AppendIRI(ap.PublicURI())
|
||||
canReplyAlwaysProp.AppendIRI(ap.PublicIRI())
|
||||
canReply.SetGoToSocialAlways(canReplyAlwaysProp)
|
||||
|
||||
// Set empty approvalRequired.
|
||||
|
|
@ -150,7 +150,7 @@ func noteWithMentions1() vocab.ActivityStreamsNote {
|
|||
|
||||
// Public requires approval to announce.
|
||||
canAnnounceApprovalRequiredProp := streams.NewGoToSocialApprovalRequiredProperty()
|
||||
canAnnounceApprovalRequiredProp.AppendIRI(ap.PublicURI())
|
||||
canAnnounceApprovalRequiredProp.AppendIRI(ap.PublicIRI())
|
||||
canAnnounce.SetGoToSocialApprovalRequired(canAnnounceApprovalRequiredProp)
|
||||
|
||||
// Set canAnnounce on the policy.
|
||||
|
|
@ -250,7 +250,7 @@ func (suite *APTestSuite) noteWithHashtags1() ap.Statusable {
|
|||
}`)
|
||||
|
||||
statusable, err := ap.ResolveStatusable(
|
||||
context.Background(),
|
||||
suite.T().Context(),
|
||||
io.NopCloser(bytes.NewReader(noteJson)),
|
||||
)
|
||||
if err != nil {
|
||||
|
|
@ -265,7 +265,7 @@ func addressable1() ap.Addressable {
|
|||
note := streams.NewActivityStreamsNote()
|
||||
|
||||
toProp := streams.NewActivityStreamsToProperty()
|
||||
toProp.AppendIRI(ap.PublicURI())
|
||||
toProp.AppendIRI(ap.PublicIRI())
|
||||
|
||||
note.SetActivityStreamsTo(toProp)
|
||||
|
||||
|
|
@ -287,7 +287,7 @@ func addressable2() ap.Addressable {
|
|||
note.SetActivityStreamsTo(toProp)
|
||||
|
||||
ccProp := streams.NewActivityStreamsCcProperty()
|
||||
ccProp.AppendIRI(ap.PublicURI())
|
||||
ccProp.AppendIRI(ap.PublicIRI())
|
||||
|
||||
note.SetActivityStreamsCc(ccProp)
|
||||
|
||||
|
|
@ -306,7 +306,7 @@ func addressable3() ap.Addressable {
|
|||
return note
|
||||
}
|
||||
|
||||
func addressable4() vocab.ActivityStreamsAnnounce {
|
||||
func addressable4(t *testing.T) vocab.ActivityStreamsAnnounce {
|
||||
// https://codeberg.org/superseriousbusiness/gotosocial/issues/267
|
||||
announceJson := []byte(`
|
||||
{
|
||||
|
|
@ -326,12 +326,12 @@ func addressable4() vocab.ActivityStreamsAnnounce {
|
|||
panic(err)
|
||||
}
|
||||
|
||||
t, err := streams.ToType(context.Background(), jsonAsMap)
|
||||
typ, err := streams.ToType(t.Context(), jsonAsMap)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return t.(vocab.ActivityStreamsAnnounce)
|
||||
return typ.(vocab.ActivityStreamsAnnounce)
|
||||
}
|
||||
|
||||
func addressable5() ap.Addressable {
|
||||
|
|
@ -366,7 +366,7 @@ func (suite *APTestSuite) jsonToType(rawJson string) (vocab.Type, map[string]int
|
|||
panic(err)
|
||||
}
|
||||
|
||||
t, err := streams.ToType(context.Background(), raw)
|
||||
t, err := streams.ToType(suite.T().Context(), raw)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
|
@ -395,7 +395,7 @@ func (suite *APTestSuite) SetupTest() {
|
|||
suite.addressable1 = addressable1()
|
||||
suite.addressable2 = addressable2()
|
||||
suite.addressable3 = addressable3()
|
||||
suite.addressable4 = addressable4()
|
||||
suite.addressable4 = addressable4(suite.T())
|
||||
suite.addressable5 = addressable5()
|
||||
suite.testAccounts = testrig.NewTestAccounts()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ import (
|
|||
"code.superseriousbusiness.org/gotosocial/internal/util"
|
||||
)
|
||||
|
||||
// ExtractObjects will extract object vocab.Types from given implementing interface.
|
||||
// ExtractObjects will extract object TypeOrIRIs from given implementing interface.
|
||||
func ExtractObjects(with WithObject) []TypeOrIRI {
|
||||
// Extract the attached object (if any).
|
||||
objProp := with.GetActivityStreamsObject()
|
||||
|
|
@ -58,6 +58,28 @@ func ExtractObjects(with WithObject) []TypeOrIRI {
|
|||
return objs
|
||||
}
|
||||
|
||||
// ExtractInstrument will extract instrument TypeOrIRIs from given implementing interface.
|
||||
func ExtractInstruments(with WithInstrument) []TypeOrIRI {
|
||||
// Extract the attached instrument (if any).
|
||||
instrProp := with.GetActivityStreamsInstrument()
|
||||
if instrProp == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Check for invalid len.
|
||||
if instrProp.Len() == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Accumulate all of the instruments into a slice.
|
||||
instrs := make([]TypeOrIRI, instrProp.Len())
|
||||
for i := range instrProp.Len() {
|
||||
instrs[i] = instrProp.At(i)
|
||||
}
|
||||
|
||||
return instrs
|
||||
}
|
||||
|
||||
// ExtractActivityData will extract the usable data type (e.g. Note, Question, etc) and corresponding JSON, from activity.
|
||||
func ExtractActivityData(activity pub.Activity, rawJSON map[string]any) ([]TypeOrIRI, []any, bool) {
|
||||
switch typeName := activity.GetTypeName(); {
|
||||
|
|
@ -97,11 +119,12 @@ func ExtractActivityData(activity pub.Activity, rawJSON map[string]any) ([]TypeO
|
|||
func ExtractAccountables(arr []TypeOrIRI) ([]Accountable, []TypeOrIRI) {
|
||||
var accounts []Accountable
|
||||
|
||||
for i := 0; i < len(arr); i++ {
|
||||
for i := 0; i < len(arr); {
|
||||
elem := arr[i]
|
||||
|
||||
if elem.IsIRI() {
|
||||
// skip IRIs
|
||||
i++ // iter
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
@ -112,6 +135,7 @@ func ExtractAccountables(arr []TypeOrIRI) ([]Accountable, []TypeOrIRI) {
|
|||
// Try cast AS type as Accountable.
|
||||
account, ok := ToAccountable(t)
|
||||
if !ok {
|
||||
i++ // iter
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
@ -130,11 +154,12 @@ func ExtractAccountables(arr []TypeOrIRI) ([]Accountable, []TypeOrIRI) {
|
|||
func ExtractStatusables(arr []TypeOrIRI) ([]Statusable, []TypeOrIRI) {
|
||||
var statuses []Statusable
|
||||
|
||||
for i := 0; i < len(arr); i++ {
|
||||
for i := 0; i < len(arr); {
|
||||
elem := arr[i]
|
||||
|
||||
if elem.IsIRI() {
|
||||
// skip IRIs
|
||||
i++ // iter
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
@ -145,10 +170,11 @@ func ExtractStatusables(arr []TypeOrIRI) ([]Statusable, []TypeOrIRI) {
|
|||
// Try cast AS type as Statusable.
|
||||
status, ok := ToStatusable(t)
|
||||
if !ok {
|
||||
i++ // iter
|
||||
continue
|
||||
}
|
||||
|
||||
// Add casted Statusable type.
|
||||
// Append casted Statusable type.
|
||||
statuses = append(statuses, status)
|
||||
|
||||
// Drop elem from slice.
|
||||
|
|
@ -163,11 +189,12 @@ func ExtractStatusables(arr []TypeOrIRI) ([]Statusable, []TypeOrIRI) {
|
|||
func ExtractPollOptionables(arr []TypeOrIRI) ([]PollOptionable, []TypeOrIRI) {
|
||||
var options []PollOptionable
|
||||
|
||||
for i := 0; i < len(arr); i++ {
|
||||
for i := 0; i < len(arr); {
|
||||
elem := arr[i]
|
||||
|
||||
if elem.IsIRI() {
|
||||
// skip IRIs
|
||||
i++ // iter
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
@ -178,6 +205,7 @@ func ExtractPollOptionables(arr []TypeOrIRI) ([]PollOptionable, []TypeOrIRI) {
|
|||
// Try cast as PollOptionable.
|
||||
option, ok := ToPollOptionable(t)
|
||||
if !ok {
|
||||
i++ // iter
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
@ -357,15 +385,67 @@ func ExtractIconURI(i WithIcon) (*url.URL, error) {
|
|||
continue
|
||||
}
|
||||
|
||||
imageURL, err := ExtractURL(image)
|
||||
if err == nil && imageURL != nil {
|
||||
return imageURL, nil
|
||||
imageURL := GetURL(image)
|
||||
if len(imageURL) == 0 {
|
||||
// Nothing here.
|
||||
continue
|
||||
}
|
||||
|
||||
// Got a hit.
|
||||
return imageURL[0], nil
|
||||
}
|
||||
|
||||
return nil, gtserror.New("could not extract valid image URI from icon")
|
||||
}
|
||||
|
||||
// ExtractIconDescription extracts the name property from
|
||||
// the given WithIcon which links to a supported image file,
|
||||
// or returns an empty string.
|
||||
// Input will look something like this:
|
||||
//
|
||||
// "icon": {
|
||||
// "mediaType": "image/jpeg",
|
||||
// "name": "some description",
|
||||
// "type": "Image",
|
||||
// "url": "http://example.org/path/to/some/file.jpeg"
|
||||
// },
|
||||
func ExtractIconDescription(i WithIcon) string {
|
||||
iconProp := i.GetActivityStreamsIcon()
|
||||
if iconProp == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Icon can potentially contain multiple entries,
|
||||
// so we iterate through all of them here in order
|
||||
// to find the first one that meets these criteria:
|
||||
//
|
||||
// 1. Is an image.
|
||||
// 2. Has a URL that we can use to derefereince it.
|
||||
for iter := iconProp.Begin(); iter != iconProp.End(); iter = iter.Next() {
|
||||
if !iter.IsActivityStreamsImage() {
|
||||
continue
|
||||
}
|
||||
|
||||
image := iter.GetActivityStreamsImage()
|
||||
if image == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
imageURL := GetURL(image)
|
||||
if len(imageURL) == 0 {
|
||||
// Nothing here.
|
||||
continue
|
||||
}
|
||||
|
||||
imageDescription := ExtractName(image)
|
||||
|
||||
// Got a hit.
|
||||
return imageDescription
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// ExtractImageURI extracts the first URI it can find from
|
||||
// the given WithImage which links to a supported image file.
|
||||
// Input will look something like this:
|
||||
|
|
@ -399,15 +479,67 @@ func ExtractImageURI(i WithImage) (*url.URL, error) {
|
|||
continue
|
||||
}
|
||||
|
||||
imageURL, err := ExtractURL(image)
|
||||
if err == nil && imageURL != nil {
|
||||
return imageURL, nil
|
||||
imageURL := GetURL(image)
|
||||
if len(imageURL) == 0 {
|
||||
// Nothing here.
|
||||
continue
|
||||
}
|
||||
|
||||
// Got a hit.
|
||||
return imageURL[0], nil
|
||||
}
|
||||
|
||||
return nil, gtserror.New("could not extract valid image URI from image")
|
||||
}
|
||||
|
||||
// ExtractImageDescription extracts the name property from
|
||||
// the given WithImage which links to a supported image file,
|
||||
// or returns an empty string.
|
||||
// Input will look something like this:
|
||||
//
|
||||
// "image": {
|
||||
// "mediaType": "image/jpeg",
|
||||
// "name": "some description",
|
||||
// "type": "Image",
|
||||
// "url": "http://example.org/path/to/some/file.jpeg"
|
||||
// },
|
||||
func ExtractImageDescription(i WithImage) string {
|
||||
imageProp := i.GetActivityStreamsImage()
|
||||
if imageProp == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Image can potentially contain multiple entries,
|
||||
// so we iterate through all of them here in order
|
||||
// to find the first one that meets these criteria:
|
||||
//
|
||||
// 1. Is an image.
|
||||
// 2. Has a URL that we can use to derefereince it.
|
||||
for iter := imageProp.Begin(); iter != imageProp.End(); iter = iter.Next() {
|
||||
if !iter.IsActivityStreamsImage() {
|
||||
continue
|
||||
}
|
||||
|
||||
image := iter.GetActivityStreamsImage()
|
||||
if image == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
imageURL := GetURL(image)
|
||||
if len(imageURL) == 0 {
|
||||
// Nothing here.
|
||||
continue
|
||||
}
|
||||
|
||||
imageDescription := ExtractName(image)
|
||||
|
||||
// Got a hit.
|
||||
return imageDescription
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// ExtractSummary extracts the summary/content warning of
|
||||
// the given WithSummary interface. Will return an empty
|
||||
// string if no summary/content warning was present.
|
||||
|
|
@ -488,28 +620,6 @@ func ExtractFields(i WithAttachment) []*gtsmodel.Field {
|
|||
return fields
|
||||
}
|
||||
|
||||
// ExtractURL extracts the first URI it can find from the
|
||||
// given WithURL interface, or an error if no URL was set.
|
||||
// The ID of a type will not work, this function wants a URI
|
||||
// specifically.
|
||||
func ExtractURL(i WithURL) (*url.URL, error) {
|
||||
urlProp := i.GetActivityStreamsUrl()
|
||||
if urlProp == nil {
|
||||
return nil, gtserror.New("url property was nil")
|
||||
}
|
||||
|
||||
for iter := urlProp.Begin(); iter != urlProp.End(); iter = iter.Next() {
|
||||
if !iter.IsIRI() {
|
||||
continue
|
||||
}
|
||||
|
||||
// Found it.
|
||||
return iter.GetIRI(), nil
|
||||
}
|
||||
|
||||
return nil, gtserror.New("no valid URL property found")
|
||||
}
|
||||
|
||||
// ExtractPubKeyFromActor extracts the public key, public key ID, and public
|
||||
// key owner ID from an interface, or an error if something goes wrong.
|
||||
func ExtractPubKeyFromActor(i WithPublicKey) (
|
||||
|
|
@ -634,32 +744,38 @@ func ExtractContent(i WithContent) gtsmodel.Content {
|
|||
return content
|
||||
}
|
||||
|
||||
// ExtractAttachments attempts to extract barebones MediaAttachment objects from given AS interface type.
|
||||
// ExtractAttachments attempts to extract barebones
|
||||
// MediaAttachment objects from given AS interface type.
|
||||
func ExtractAttachments(i WithAttachment) ([]*gtsmodel.MediaAttachment, error) {
|
||||
attachmentProp := i.GetActivityStreamsAttachment()
|
||||
if attachmentProp == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var errs gtserror.MultiError
|
||||
var (
|
||||
attachments = make([]*gtsmodel.MediaAttachment, 0, attachmentProp.Len())
|
||||
errs gtserror.MultiError
|
||||
)
|
||||
|
||||
attachments := make([]*gtsmodel.MediaAttachment, 0, attachmentProp.Len())
|
||||
for iter := attachmentProp.Begin(); iter != attachmentProp.End(); iter = iter.Next() {
|
||||
t := iter.GetType()
|
||||
if t == nil {
|
||||
errs.Appendf("nil attachment type")
|
||||
continue
|
||||
}
|
||||
attachmentable, ok := t.(Attachmentable)
|
||||
|
||||
attachmentable, ok := ToAttachmentable(t)
|
||||
if !ok {
|
||||
errs.Appendf("incorrect attachment type: %T", t)
|
||||
errs.Appendf("could not cast %T to Attachmentable", t)
|
||||
continue
|
||||
}
|
||||
|
||||
attachment, err := ExtractAttachment(attachmentable)
|
||||
if err != nil {
|
||||
errs.Appendf("error extracting attachment: %w", err)
|
||||
continue
|
||||
}
|
||||
|
||||
attachments = append(attachments, attachment)
|
||||
}
|
||||
|
||||
|
|
@ -670,18 +786,21 @@ func ExtractAttachments(i WithAttachment) ([]*gtsmodel.MediaAttachment, error) {
|
|||
// (just remote URL, description, and blurhash) from the given
|
||||
// Attachmentable interface, or an error if no remote URL is set.
|
||||
func ExtractAttachment(i Attachmentable) (*gtsmodel.MediaAttachment, error) {
|
||||
// Get the URL for the attachment file.
|
||||
// Get the first URL for the attachment file.
|
||||
// If no URL is set, we can't do anything.
|
||||
remoteURL, err := ExtractURL(i)
|
||||
if err != nil {
|
||||
return nil, gtserror.Newf("error extracting attachment URL: %w", err)
|
||||
remoteURL := GetURL(i)
|
||||
if len(remoteURL) == 0 {
|
||||
return nil, gtserror.New("empty attachment URL")
|
||||
}
|
||||
|
||||
return >smodel.MediaAttachment{
|
||||
RemoteURL: remoteURL.String(),
|
||||
RemoteURL: remoteURL[0].String(),
|
||||
Description: ExtractDescription(i),
|
||||
Blurhash: ExtractBlurhash(i),
|
||||
Processing: gtsmodel.ProcessingStatusReceived,
|
||||
FileMeta: gtsmodel.FileMeta{
|
||||
Focus: ExtractFocus(i),
|
||||
},
|
||||
Processing: gtsmodel.ProcessingStatusReceived,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
|
@ -708,6 +827,50 @@ func ExtractBlurhash(i WithBlurhash) string {
|
|||
return blurhashProp.Get()
|
||||
}
|
||||
|
||||
// ExtractFocus parses a gtsmodel.Focus from the given Attachmentable's
|
||||
// `focalPoint` property, if Attachmentable can have `focalPoint`, and
|
||||
// `focalPoint` is set to a valid pair of floats. Otherwise, returns a
|
||||
// zero gtsmodel.Focus (ie., focus in the centre of the image).
|
||||
func ExtractFocus(attachmentable Attachmentable) gtsmodel.Focus {
|
||||
focus := gtsmodel.Focus{}
|
||||
|
||||
withFocalPoint, ok := attachmentable.(WithFocalPoint)
|
||||
if !ok {
|
||||
return focus
|
||||
}
|
||||
|
||||
focalPointProp := withFocalPoint.GetTootFocalPoint()
|
||||
if focalPointProp == nil || focalPointProp.Len() != 2 {
|
||||
return focus
|
||||
}
|
||||
|
||||
xProp := focalPointProp.At(0)
|
||||
if !xProp.IsXMLSchemaFloat() {
|
||||
return focus
|
||||
}
|
||||
|
||||
yProp := focalPointProp.At(1)
|
||||
if !yProp.IsXMLSchemaFloat() {
|
||||
return focus
|
||||
}
|
||||
|
||||
x := xProp.Get()
|
||||
if x < -1 || x > 1 {
|
||||
return focus
|
||||
}
|
||||
|
||||
y := yProp.Get()
|
||||
if y < -1 || y > 1 {
|
||||
return focus
|
||||
}
|
||||
|
||||
// Looks good.
|
||||
focus.X = float32(x)
|
||||
focus.Y = float32(y)
|
||||
|
||||
return focus
|
||||
}
|
||||
|
||||
// ExtractHashtags extracts a slice of minimal gtsmodel.Tags
|
||||
// from a WithTag. If an entry in the WithTag is not a hashtag,
|
||||
// or has a name that cannot be normalized, it will be ignored.
|
||||
|
|
@ -1074,18 +1237,21 @@ func ExtractVisibility(addressable Addressable, actorFollowersURI string) (gtsmo
|
|||
// Will be nil (default policy) for Statusables that have no policy
|
||||
// set on them, or have a null policy. In such a case, the caller
|
||||
// should assume the default policy for the status's visibility level.
|
||||
//
|
||||
// Sub-policies of the returned policy, eg., CanLike, CanReply, may
|
||||
// each be nil if they were not set on the interaction policy.
|
||||
func ExtractInteractionPolicy(
|
||||
statusable Statusable,
|
||||
owner *gtsmodel.Account,
|
||||
) *gtsmodel.InteractionPolicy {
|
||||
ipa, ok := statusable.(InteractionPolicyAware)
|
||||
wip, ok := statusable.(WithInteractionPolicy)
|
||||
if !ok {
|
||||
// Not a type with interaction
|
||||
// policy properties settable.
|
||||
return nil
|
||||
}
|
||||
|
||||
policyProp := ipa.GetGoToSocialInteractionPolicy()
|
||||
policyProp := wip.GetGoToSocialInteractionPolicy()
|
||||
if policyProp == nil || policyProp.Len() != 1 {
|
||||
return nil
|
||||
}
|
||||
|
|
@ -1100,6 +1266,8 @@ func ExtractInteractionPolicy(
|
|||
return nil
|
||||
}
|
||||
|
||||
// There's a policy key/value
|
||||
// set, extract sub-policies.
|
||||
return >smodel.InteractionPolicy{
|
||||
CanLike: extractCanLike(policy.GetGoToSocialCanLike(), owner),
|
||||
CanReply: extractCanReply(policy.GetGoToSocialCanReply(), owner),
|
||||
|
|
@ -1107,75 +1275,104 @@ func ExtractInteractionPolicy(
|
|||
}
|
||||
}
|
||||
|
||||
// Returns either a parsed CanLike sub-policy, or nil
|
||||
// if canLike is not set, ie., if this post is from an
|
||||
// instance that doesn't know / care about canLike.
|
||||
func extractCanLike(
|
||||
prop vocab.GoToSocialCanLikeProperty,
|
||||
owner *gtsmodel.Account,
|
||||
) gtsmodel.PolicyRules {
|
||||
) *gtsmodel.PolicyRules {
|
||||
if prop == nil || prop.Len() != 1 {
|
||||
return gtsmodel.PolicyRules{}
|
||||
return nil
|
||||
}
|
||||
|
||||
propIter := prop.At(0)
|
||||
if !propIter.IsGoToSocialCanLike() {
|
||||
return gtsmodel.PolicyRules{}
|
||||
return nil
|
||||
}
|
||||
|
||||
withRules := propIter.Get()
|
||||
if withRules == nil {
|
||||
return gtsmodel.PolicyRules{}
|
||||
return nil
|
||||
}
|
||||
|
||||
return gtsmodel.PolicyRules{
|
||||
Always: extractPolicyValues(withRules.GetGoToSocialAlways(), owner),
|
||||
WithApproval: extractPolicyValues(withRules.GetGoToSocialApprovalRequired(), owner),
|
||||
}
|
||||
return extractPolicyRules(withRules, owner)
|
||||
}
|
||||
|
||||
// Returns either a parsed CanReply sub-policy, or nil
|
||||
// if canReply is not set, ie., if this post is from an
|
||||
// instance that doesn't know / care about canReply.
|
||||
func extractCanReply(
|
||||
prop vocab.GoToSocialCanReplyProperty,
|
||||
owner *gtsmodel.Account,
|
||||
) gtsmodel.PolicyRules {
|
||||
) *gtsmodel.PolicyRules {
|
||||
if prop == nil || prop.Len() != 1 {
|
||||
return gtsmodel.PolicyRules{}
|
||||
return nil
|
||||
}
|
||||
|
||||
propIter := prop.At(0)
|
||||
if !propIter.IsGoToSocialCanReply() {
|
||||
return gtsmodel.PolicyRules{}
|
||||
return nil
|
||||
}
|
||||
|
||||
withRules := propIter.Get()
|
||||
if withRules == nil {
|
||||
return gtsmodel.PolicyRules{}
|
||||
return nil
|
||||
}
|
||||
|
||||
return gtsmodel.PolicyRules{
|
||||
Always: extractPolicyValues(withRules.GetGoToSocialAlways(), owner),
|
||||
WithApproval: extractPolicyValues(withRules.GetGoToSocialApprovalRequired(), owner),
|
||||
}
|
||||
return extractPolicyRules(withRules, owner)
|
||||
}
|
||||
|
||||
// Returns either a parsed CanAnnounce sub-policy, or nil
|
||||
// if canAnnounce is not set, ie., if this post is from an
|
||||
// instance that doesn't know / care about canAnnounce.
|
||||
func extractCanAnnounce(
|
||||
prop vocab.GoToSocialCanAnnounceProperty,
|
||||
owner *gtsmodel.Account,
|
||||
) gtsmodel.PolicyRules {
|
||||
) *gtsmodel.PolicyRules {
|
||||
if prop == nil || prop.Len() != 1 {
|
||||
return gtsmodel.PolicyRules{}
|
||||
return nil
|
||||
}
|
||||
|
||||
propIter := prop.At(0)
|
||||
if !propIter.IsGoToSocialCanAnnounce() {
|
||||
return gtsmodel.PolicyRules{}
|
||||
return nil
|
||||
}
|
||||
|
||||
withRules := propIter.Get()
|
||||
if withRules == nil {
|
||||
return gtsmodel.PolicyRules{}
|
||||
return nil
|
||||
}
|
||||
|
||||
return gtsmodel.PolicyRules{
|
||||
Always: extractPolicyValues(withRules.GetGoToSocialAlways(), owner),
|
||||
WithApproval: extractPolicyValues(withRules.GetGoToSocialApprovalRequired(), owner),
|
||||
return extractPolicyRules(withRules, owner)
|
||||
}
|
||||
|
||||
func extractPolicyRules(
|
||||
withRules WithPolicyRules,
|
||||
owner *gtsmodel.Account,
|
||||
) *gtsmodel.PolicyRules {
|
||||
// Check for `automaticApproval` and
|
||||
// `manualApproval` properties first.
|
||||
var (
|
||||
automaticApproval = withRules.GetGoToSocialAutomaticApproval()
|
||||
manualApproval = withRules.GetGoToSocialManualApproval()
|
||||
)
|
||||
if (automaticApproval != nil && automaticApproval.Len() != 0) ||
|
||||
(manualApproval != nil && manualApproval.Len() != 0) {
|
||||
// At least one is set, use these props.
|
||||
return >smodel.PolicyRules{
|
||||
AutomaticApproval: extractPolicyValues(automaticApproval, owner),
|
||||
ManualApproval: extractPolicyValues(manualApproval, owner),
|
||||
}
|
||||
}
|
||||
|
||||
// Fall back to deprecated `always`
|
||||
// and `withApproval` properties.
|
||||
//
|
||||
// TODO: Remove this in GtS v0.21.0.
|
||||
return >smodel.PolicyRules{
|
||||
AutomaticApproval: extractPolicyValues(withRules.GetGoToSocialAlways(), owner),
|
||||
ManualApproval: extractPolicyValues(withRules.GetGoToSocialApprovalRequired(), owner),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@
|
|||
package ap_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
|
|
@ -36,7 +35,7 @@ func (suite *ExtractAttachmentsTestSuite) TestExtractAttachmentMissingURL() {
|
|||
d1.SetActivityStreamsUrl(streams.NewActivityStreamsUrlProperty())
|
||||
|
||||
attachment, err := ap.ExtractAttachment(d1)
|
||||
suite.EqualError(err, "ExtractAttachment: error extracting attachment URL: ExtractURL: no valid URL property found")
|
||||
suite.EqualError(err, "ExtractAttachment: empty attachment URL")
|
||||
suite.Nil(attachment)
|
||||
}
|
||||
|
||||
|
|
@ -59,7 +58,7 @@ func (suite *ExtractAttachmentsTestSuite) TestExtractDescription() {
|
|||
suite.FailNow(err.Error())
|
||||
}
|
||||
|
||||
t, err := streams.ToType(context.Background(), raw)
|
||||
t, err := streams.ToType(suite.T().Context(), raw)
|
||||
if err != nil {
|
||||
suite.FailNow(err.Error())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ package ap_test
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
|
|
@ -61,7 +60,7 @@ func (suite *ExtractEmojisTestSuite) TestExtractEmojis() {
|
|||
}`
|
||||
|
||||
statusable, err := ap.ResolveStatusable(
|
||||
context.Background(),
|
||||
suite.T().Context(),
|
||||
io.NopCloser(bytes.NewBufferString(noteWithEmojis)),
|
||||
)
|
||||
if err != nil {
|
||||
|
|
@ -115,7 +114,7 @@ func (suite *ExtractEmojisTestSuite) TestExtractEmojisNoID() {
|
|||
}`
|
||||
|
||||
statusable, err := ap.ResolveStatusable(
|
||||
context.Background(),
|
||||
suite.T().Context(),
|
||||
io.NopCloser(bytes.NewBufferString(noteWithEmojis)),
|
||||
)
|
||||
if err != nil {
|
||||
|
|
@ -170,7 +169,7 @@ func (suite *ExtractEmojisTestSuite) TestExtractEmojisNullID() {
|
|||
}`
|
||||
|
||||
statusable, err := ap.ResolveStatusable(
|
||||
context.Background(),
|
||||
suite.T().Context(),
|
||||
io.NopCloser(bytes.NewBufferString(noteWithEmojis)),
|
||||
)
|
||||
if err != nil {
|
||||
|
|
@ -225,7 +224,7 @@ func (suite *ExtractEmojisTestSuite) TestExtractEmojisEmptyID() {
|
|||
}`
|
||||
|
||||
statusable, err := ap.ResolveStatusable(
|
||||
context.Background(),
|
||||
suite.T().Context(),
|
||||
io.NopCloser(bytes.NewBufferString(noteWithEmojis)),
|
||||
)
|
||||
if err != nil {
|
||||
|
|
|
|||
124
internal/ap/extractfocus_test.go
Normal file
124
internal/ap/extractfocus_test.go
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
// GoToSocial
|
||||
// Copyright (C) GoToSocial Authors admin@gotosocial.org
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it 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.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package ap_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"code.superseriousbusiness.org/activity/streams"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/ap"
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
type ExtractFocusTestSuite struct {
|
||||
APTestSuite
|
||||
}
|
||||
|
||||
func (suite *ExtractFocusTestSuite) TestExtractFocus() {
|
||||
ctx := suite.T().Context()
|
||||
|
||||
type test struct {
|
||||
data string
|
||||
expectX float32
|
||||
expectY float32
|
||||
}
|
||||
|
||||
for _, test := range []test{
|
||||
{
|
||||
// Fine.
|
||||
data: "-0.5, 0.5",
|
||||
expectX: -0.5,
|
||||
expectY: 0.5,
|
||||
},
|
||||
{
|
||||
// Also fine.
|
||||
data: "1, 1",
|
||||
expectX: 1,
|
||||
expectY: 1,
|
||||
},
|
||||
{
|
||||
// Out of range.
|
||||
data: "1.5, 1",
|
||||
expectX: 0,
|
||||
expectY: 0,
|
||||
},
|
||||
{
|
||||
// Too many points.
|
||||
data: "1, 1, 0",
|
||||
expectX: 0,
|
||||
expectY: 0,
|
||||
},
|
||||
{
|
||||
// Not enough points.
|
||||
data: "1",
|
||||
expectX: 0,
|
||||
expectY: 0,
|
||||
},
|
||||
} {
|
||||
// Wrap provided test.data
|
||||
// in a minimal Attachmentable.
|
||||
const fmts = `{
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
{
|
||||
"focalPoint": {
|
||||
"@container": "@list",
|
||||
"@id": "toot:focalPoint"
|
||||
},
|
||||
"toot": "http://joinmastodon.org/ns#"
|
||||
}
|
||||
],
|
||||
"focalPoint": [ %s ],
|
||||
"type": "Image"
|
||||
}`
|
||||
|
||||
// Unmarshal test data.
|
||||
data := fmt.Sprintf(fmts, test.data)
|
||||
m := make(map[string]any)
|
||||
if err := json.Unmarshal([]byte(data), &m); err != nil {
|
||||
suite.FailNow(err.Error())
|
||||
}
|
||||
|
||||
// Convert to type.
|
||||
t, err := streams.ToType(ctx, m)
|
||||
if err != nil {
|
||||
suite.FailNow(err.Error())
|
||||
}
|
||||
|
||||
// Convert to attachmentable.
|
||||
attachmentable, ok := t.(ap.Attachmentable)
|
||||
if !ok {
|
||||
suite.FailNow("", "%T was not Attachmentable", t)
|
||||
}
|
||||
|
||||
// Check extracted focus.
|
||||
focus := ap.ExtractFocus(attachmentable)
|
||||
if focus.X != test.expectX || focus.Y != test.expectY {
|
||||
suite.Fail("",
|
||||
"expected x=%.2f y=%.2f got x=%.2f y=%.2f",
|
||||
test.expectX, test.expectY, focus.X, focus.Y,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractFocusTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(ExtractFocusTestSuite))
|
||||
}
|
||||
|
|
@ -19,7 +19,6 @@ package ap_test
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
|
|
@ -43,6 +42,174 @@ func (suite *ExtractPolicyTestSuite) TestExtractPolicy() {
|
|||
"en": "hey @f0x and @dumpsterqueer",
|
||||
"fr": "bonjour @f0x et @dumpsterqueer"
|
||||
},
|
||||
"interactionPolicy": {
|
||||
"canLike": {
|
||||
"automaticApproval": [
|
||||
"https://www.w3.org/ns/activitystreams#Public"
|
||||
],
|
||||
"manualApproval": []
|
||||
},
|
||||
"canReply": {
|
||||
"automaticApproval": [
|
||||
"http://localhost:8080/users/the_mighty_zork",
|
||||
"http://localhost:8080/users/the_mighty_zork/followers",
|
||||
"https://gts.superseriousbusiness.org/users/dumpsterqueer",
|
||||
"https://gts.superseriousbusiness.org/users/f0x"
|
||||
],
|
||||
"manualApproval": [
|
||||
"https://www.w3.org/ns/activitystreams#Public"
|
||||
]
|
||||
},
|
||||
"canAnnounce": {
|
||||
"automaticApproval": [
|
||||
"http://localhost:8080/users/the_mighty_zork"
|
||||
],
|
||||
"manualApproval": [
|
||||
"https://www.w3.org/ns/activitystreams#Public"
|
||||
]
|
||||
}
|
||||
},
|
||||
"tag": [
|
||||
{
|
||||
"href": "https://gts.superseriousbusiness.org/users/dumpsterqueer",
|
||||
"name": "@dumpsterqueer@superseriousbusiness.org",
|
||||
"type": "Mention"
|
||||
},
|
||||
{
|
||||
"href": "https://gts.superseriousbusiness.org/users/f0x",
|
||||
"name": "@f0x@superseriousbusiness.org",
|
||||
"type": "Mention"
|
||||
}
|
||||
],
|
||||
"type": "Note"
|
||||
}`
|
||||
|
||||
statusable, err := ap.ResolveStatusable(
|
||||
suite.T().Context(),
|
||||
io.NopCloser(
|
||||
bytes.NewBufferString(rawNote),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
suite.FailNow(err.Error())
|
||||
}
|
||||
|
||||
policy := ap.ExtractInteractionPolicy(
|
||||
statusable,
|
||||
// Zork didn't actually create
|
||||
// this status but nevermind.
|
||||
suite.testAccounts["local_account_1"],
|
||||
)
|
||||
|
||||
expectedPolicy := >smodel.InteractionPolicy{
|
||||
CanLike: >smodel.PolicyRules{
|
||||
AutomaticApproval: gtsmodel.PolicyValues{
|
||||
gtsmodel.PolicyValuePublic,
|
||||
},
|
||||
ManualApproval: gtsmodel.PolicyValues{},
|
||||
},
|
||||
CanReply: >smodel.PolicyRules{
|
||||
AutomaticApproval: gtsmodel.PolicyValues{
|
||||
gtsmodel.PolicyValueAuthor,
|
||||
gtsmodel.PolicyValueFollowers,
|
||||
"https://gts.superseriousbusiness.org/users/dumpsterqueer",
|
||||
"https://gts.superseriousbusiness.org/users/f0x",
|
||||
},
|
||||
ManualApproval: gtsmodel.PolicyValues{
|
||||
gtsmodel.PolicyValuePublic,
|
||||
},
|
||||
},
|
||||
CanAnnounce: >smodel.PolicyRules{
|
||||
AutomaticApproval: gtsmodel.PolicyValues{
|
||||
gtsmodel.PolicyValueAuthor,
|
||||
},
|
||||
ManualApproval: gtsmodel.PolicyValues{
|
||||
gtsmodel.PolicyValuePublic,
|
||||
},
|
||||
},
|
||||
}
|
||||
suite.EqualValues(expectedPolicy, policy)
|
||||
}
|
||||
|
||||
func (suite *ExtractPolicyTestSuite) TestExtractPolicyUnsetProps() {
|
||||
rawNote := `{
|
||||
"@context": [
|
||||
"https://gotosocial.org/ns",
|
||||
"https://www.w3.org/ns/activitystreams"
|
||||
],
|
||||
"content": "hey @f0x and @dumpsterqueer",
|
||||
"contentMap": {
|
||||
"en": "hey @f0x and @dumpsterqueer",
|
||||
"fr": "bonjour @f0x et @dumpsterqueer"
|
||||
},
|
||||
"interactionPolicy": {
|
||||
"canAnnounce": {
|
||||
"automaticApproval": [
|
||||
"http://localhost:8080/users/the_mighty_zork"
|
||||
],
|
||||
"manualApproval": [
|
||||
"https://www.w3.org/ns/activitystreams#Public"
|
||||
]
|
||||
}
|
||||
},
|
||||
"tag": [
|
||||
{
|
||||
"href": "https://gts.superseriousbusiness.org/users/dumpsterqueer",
|
||||
"name": "@dumpsterqueer@superseriousbusiness.org",
|
||||
"type": "Mention"
|
||||
},
|
||||
{
|
||||
"href": "https://gts.superseriousbusiness.org/users/f0x",
|
||||
"name": "@f0x@superseriousbusiness.org",
|
||||
"type": "Mention"
|
||||
}
|
||||
],
|
||||
"type": "Note"
|
||||
}`
|
||||
|
||||
statusable, err := ap.ResolveStatusable(
|
||||
suite.T().Context(),
|
||||
io.NopCloser(
|
||||
bytes.NewBufferString(rawNote),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
suite.FailNow(err.Error())
|
||||
}
|
||||
|
||||
policy := ap.ExtractInteractionPolicy(
|
||||
statusable,
|
||||
// Zork didn't actually create
|
||||
// this status but nevermind.
|
||||
suite.testAccounts["local_account_1"],
|
||||
)
|
||||
|
||||
expectedPolicy := >smodel.InteractionPolicy{
|
||||
CanLike: nil,
|
||||
CanReply: nil,
|
||||
CanAnnounce: >smodel.PolicyRules{
|
||||
AutomaticApproval: gtsmodel.PolicyValues{
|
||||
gtsmodel.PolicyValueAuthor,
|
||||
},
|
||||
ManualApproval: gtsmodel.PolicyValues{
|
||||
gtsmodel.PolicyValuePublic,
|
||||
},
|
||||
},
|
||||
}
|
||||
suite.EqualValues(expectedPolicy, policy)
|
||||
}
|
||||
|
||||
func (suite *ExtractPolicyTestSuite) TestExtractPolicyDeprecated() {
|
||||
rawNote := `{
|
||||
"@context": [
|
||||
"https://gotosocial.org/ns",
|
||||
"https://www.w3.org/ns/activitystreams"
|
||||
],
|
||||
"content": "hey @f0x and @dumpsterqueer",
|
||||
"contentMap": {
|
||||
"en": "hey @f0x and @dumpsterqueer",
|
||||
"fr": "bonjour @f0x et @dumpsterqueer"
|
||||
},
|
||||
"interactionPolicy": {
|
||||
"canLike": {
|
||||
"always": [
|
||||
|
|
@ -86,7 +253,7 @@ func (suite *ExtractPolicyTestSuite) TestExtractPolicy() {
|
|||
}`
|
||||
|
||||
statusable, err := ap.ResolveStatusable(
|
||||
context.Background(),
|
||||
suite.T().Context(),
|
||||
io.NopCloser(
|
||||
bytes.NewBufferString(rawNote),
|
||||
),
|
||||
|
|
@ -103,28 +270,28 @@ func (suite *ExtractPolicyTestSuite) TestExtractPolicy() {
|
|||
)
|
||||
|
||||
expectedPolicy := >smodel.InteractionPolicy{
|
||||
CanLike: gtsmodel.PolicyRules{
|
||||
Always: gtsmodel.PolicyValues{
|
||||
CanLike: >smodel.PolicyRules{
|
||||
AutomaticApproval: gtsmodel.PolicyValues{
|
||||
gtsmodel.PolicyValuePublic,
|
||||
},
|
||||
WithApproval: gtsmodel.PolicyValues{},
|
||||
ManualApproval: gtsmodel.PolicyValues{},
|
||||
},
|
||||
CanReply: gtsmodel.PolicyRules{
|
||||
Always: gtsmodel.PolicyValues{
|
||||
CanReply: >smodel.PolicyRules{
|
||||
AutomaticApproval: gtsmodel.PolicyValues{
|
||||
gtsmodel.PolicyValueAuthor,
|
||||
gtsmodel.PolicyValueFollowers,
|
||||
"https://gts.superseriousbusiness.org/users/dumpsterqueer",
|
||||
"https://gts.superseriousbusiness.org/users/f0x",
|
||||
},
|
||||
WithApproval: gtsmodel.PolicyValues{
|
||||
ManualApproval: gtsmodel.PolicyValues{
|
||||
gtsmodel.PolicyValuePublic,
|
||||
},
|
||||
},
|
||||
CanAnnounce: gtsmodel.PolicyRules{
|
||||
Always: gtsmodel.PolicyValues{
|
||||
CanAnnounce: >smodel.PolicyRules{
|
||||
AutomaticApproval: gtsmodel.PolicyValues{
|
||||
gtsmodel.PolicyValueAuthor,
|
||||
},
|
||||
WithApproval: gtsmodel.PolicyValues{
|
||||
ManualApproval: gtsmodel.PolicyValues{
|
||||
gtsmodel.PolicyValuePublic,
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@
|
|||
package ap_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
|
|
@ -62,7 +61,7 @@ func (suite *ExtractPubKeyTestSuite) TestExtractPubKeyFromStub() {
|
|||
suite.FailNow(err.Error())
|
||||
}
|
||||
|
||||
t, err := streams.ToType(context.Background(), m)
|
||||
t, err := streams.ToType(suite.T().Context(), m)
|
||||
if err != nil {
|
||||
suite.FailNow(err.Error())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -143,13 +143,13 @@ func ToAcceptable(t vocab.Type) (Acceptable, bool) {
|
|||
return acceptable, true
|
||||
}
|
||||
|
||||
// IsApprovable returns whether AS vocab type name
|
||||
// is something that can be cast to Approvable.
|
||||
func IsApprovable(typeName string) bool {
|
||||
// IsAuthorizationable returns whether AS vocab type name
|
||||
// is something that can be cast to Authorizationable.
|
||||
func IsAuthorizationable(typeName string) bool {
|
||||
switch typeName {
|
||||
case ObjectLikeApproval,
|
||||
ObjectReplyApproval,
|
||||
ObjectAnnounceApproval:
|
||||
case ObjectLikeAuthorization,
|
||||
ObjectReplyAuthorization,
|
||||
ObjectAnnounceAuthorization:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
|
|
@ -157,12 +157,65 @@ func IsApprovable(typeName string) bool {
|
|||
}
|
||||
|
||||
// ToAcceptable safely tries to cast vocab.Type as Approvable.
|
||||
func ToApprovable(t vocab.Type) (Approvable, bool) {
|
||||
approvable, ok := t.(Approvable)
|
||||
if !ok || !IsApprovable(t.GetTypeName()) {
|
||||
func ToAuthorizationable(t vocab.Type) (Authorizationable, bool) {
|
||||
authable, ok := t.(Authorizationable)
|
||||
if !ok || !IsAuthorizationable(t.GetTypeName()) {
|
||||
return nil, false
|
||||
}
|
||||
return approvable, true
|
||||
return authable, true
|
||||
}
|
||||
|
||||
// IsAttachmentable returns whether AS vocab type name
|
||||
// is something that can be cast to Attachmentable.
|
||||
func IsAttachmentable(typeName string) bool {
|
||||
switch typeName {
|
||||
case ObjectAudio,
|
||||
ObjectDocument,
|
||||
ObjectImage,
|
||||
ObjectVideo:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// ToAttachmentable safely tries to cast vocab.Type as Attachmentable.
|
||||
func ToAttachmentable(t vocab.Type) (Attachmentable, bool) {
|
||||
attachmentable, ok := t.(Attachmentable)
|
||||
if !ok || !IsAttachmentable(t.GetTypeName()) {
|
||||
return nil, false
|
||||
}
|
||||
return attachmentable, true
|
||||
}
|
||||
|
||||
// IsAnnounceable returns whether AS vocab type name
|
||||
// is something that can be cast to vocab.ActivityStreamsAnnounce.
|
||||
func IsAnnounceable(typeName string) bool {
|
||||
return typeName == ActivityAnnounce
|
||||
}
|
||||
|
||||
// ToAnnounceable safely tries to cast vocab.Type as vocab.ActivityStreamsAnnounce.
|
||||
func ToAnnounceable(t vocab.Type) (vocab.ActivityStreamsAnnounce, bool) {
|
||||
announceable, ok := t.(vocab.ActivityStreamsAnnounce)
|
||||
if !ok || t.GetTypeName() != ActivityAnnounce {
|
||||
return nil, false
|
||||
}
|
||||
return announceable, true
|
||||
}
|
||||
|
||||
// IsLikeable returns whether AS vocab type name
|
||||
// is something that can be cast to vocab.ActivityStreamsLike.
|
||||
func IsLikeable(typeName string) bool {
|
||||
return typeName == ActivityLike
|
||||
}
|
||||
|
||||
// ToAnnouncToLikeableeable safely tries to cast vocab.Type as vocab.ActivityStreamsLike.
|
||||
func ToLikeable(t vocab.Type) (vocab.ActivityStreamsLike, bool) {
|
||||
likeable, ok := t.(vocab.ActivityStreamsLike)
|
||||
if !ok || t.GetTypeName() != ActivityLike {
|
||||
return nil, false
|
||||
}
|
||||
return likeable, true
|
||||
}
|
||||
|
||||
// Activityable represents the minimum activitypub interface for representing an 'activity'.
|
||||
|
|
@ -204,6 +257,8 @@ type Accountable interface {
|
|||
WithMovedTo
|
||||
WithAlsoKnownAs
|
||||
WithManuallyApprovesFollowers
|
||||
WithHidesToPublicFromUnauthedWeb
|
||||
WithHidesCcPublicFromUnauthedWeb
|
||||
WithEndpoints
|
||||
WithTag
|
||||
WithPublished
|
||||
|
|
@ -233,11 +288,6 @@ type Statusable interface {
|
|||
WithReplies
|
||||
}
|
||||
|
||||
type InteractionPolicyAware interface {
|
||||
WithInteractionPolicy
|
||||
WithApprovedBy
|
||||
}
|
||||
|
||||
// Pollable represents the minimum activitypub interface for representing a 'poll' (it's a subset of a status).
|
||||
// (see: IsPollable() for types implementing this, though you MUST make sure to check
|
||||
// the typeName as this bare interface may be implementable by non-Pollable types).
|
||||
|
|
@ -274,14 +324,14 @@ type Acceptable interface {
|
|||
WithResult
|
||||
}
|
||||
|
||||
// Approvable represents the minimum activitypub interface
|
||||
// for a LikeApproval, ReplyApproval, or AnnounceApproval.
|
||||
type Approvable interface {
|
||||
// Authorizationable represents the minimum interface for a
|
||||
// LikeAuthorization, ReplyAuthorization, AnnounceAuthorization.
|
||||
type Authorizationable interface {
|
||||
vocab.Type
|
||||
|
||||
WithAttributedTo
|
||||
WithObject
|
||||
WithTarget
|
||||
WithInteractingObject
|
||||
WithInteractionTarget
|
||||
}
|
||||
|
||||
// Attachmentable represents the minimum activitypub interface for representing a 'mediaAttachment'. (see: IsAttachmentable).
|
||||
|
|
@ -367,6 +417,16 @@ type ReplyToable interface {
|
|||
WithInReplyTo
|
||||
}
|
||||
|
||||
// InteractionRequestable represents the minimum interface for an interaction request
|
||||
// activity, eg., LikeRequest, ReplyRequest, AnnounceRequest, QuoteRequest, etc..
|
||||
type InteractionRequestable interface {
|
||||
vocab.Type
|
||||
|
||||
WithActor
|
||||
WithObject
|
||||
WithInstrument
|
||||
}
|
||||
|
||||
// CollectionIterator represents the minimum interface for interacting with a
|
||||
// wrapped Collection or OrderedCollection in order to access next / prev items.
|
||||
type CollectionIterator interface {
|
||||
|
|
@ -628,9 +688,11 @@ type WithBlurhash interface {
|
|||
SetTootBlurhash(vocab.TootBlurhashProperty)
|
||||
}
|
||||
|
||||
// type withFocalPoint interface {
|
||||
// // TODO
|
||||
// }
|
||||
// WithFocalPoint represents an object with TootFocalPointProperty.
|
||||
type WithFocalPoint interface {
|
||||
GetTootFocalPoint() vocab.TootFocalPointProperty
|
||||
SetTootFocalPoint(vocab.TootFocalPointProperty)
|
||||
}
|
||||
|
||||
// WithHref represents an activity with ActivityStreamsHrefProperty
|
||||
type WithHref interface {
|
||||
|
|
@ -656,6 +718,12 @@ type WithObject interface {
|
|||
SetActivityStreamsObject(vocab.ActivityStreamsObjectProperty)
|
||||
}
|
||||
|
||||
// WithInstrument represents an activity with ActivityStreamsInstrumentProperty
|
||||
type WithInstrument interface {
|
||||
GetActivityStreamsInstrument() vocab.ActivityStreamsInstrumentProperty
|
||||
SetActivityStreamsInstrument(vocab.ActivityStreamsInstrumentProperty)
|
||||
}
|
||||
|
||||
// WithTarget represents an activity with ActivityStreamsTargetProperty
|
||||
type WithTarget interface {
|
||||
GetActivityStreamsTarget() vocab.ActivityStreamsTargetProperty
|
||||
|
|
@ -686,6 +754,18 @@ type WithManuallyApprovesFollowers interface {
|
|||
SetActivityStreamsManuallyApprovesFollowers(vocab.ActivityStreamsManuallyApprovesFollowersProperty)
|
||||
}
|
||||
|
||||
// WithHidesToPublicFromUnauthedWeb represents a Person or profile with the hidesToPublicFromUnauthedWeb property.
|
||||
type WithHidesToPublicFromUnauthedWeb interface {
|
||||
GetGoToSocialHidesToPublicFromUnauthedWeb() vocab.GoToSocialHidesToPublicFromUnauthedWebProperty
|
||||
SetGoToSocialHidesToPublicFromUnauthedWeb(vocab.GoToSocialHidesToPublicFromUnauthedWebProperty)
|
||||
}
|
||||
|
||||
// WithHidesCcPublicFromUnauthedWeb represents a Person or profile with the hidesCcPublicFromUnauthedWeb property.
|
||||
type WithHidesCcPublicFromUnauthedWeb interface {
|
||||
GetGoToSocialHidesCcPublicFromUnauthedWeb() vocab.GoToSocialHidesCcPublicFromUnauthedWebProperty
|
||||
SetGoToSocialHidesCcPublicFromUnauthedWeb(vocab.GoToSocialHidesCcPublicFromUnauthedWebProperty)
|
||||
}
|
||||
|
||||
// WithEndpoints represents a Person or profile with the endpoints property
|
||||
type WithEndpoints interface {
|
||||
GetActivityStreamsEndpoints() vocab.ActivityStreamsEndpointsProperty
|
||||
|
|
@ -730,18 +810,50 @@ type WithInteractionPolicy interface {
|
|||
|
||||
// WithPolicyRules represents an activity with always and approvalRequired properties.
|
||||
type WithPolicyRules interface {
|
||||
GetGoToSocialAlways() vocab.GoToSocialAlwaysProperty
|
||||
GetGoToSocialApprovalRequired() vocab.GoToSocialApprovalRequiredProperty
|
||||
GetGoToSocialAutomaticApproval() vocab.GoToSocialAutomaticApprovalProperty
|
||||
GetGoToSocialManualApproval() vocab.GoToSocialManualApprovalProperty
|
||||
GetGoToSocialAlways() vocab.GoToSocialAlwaysProperty // Deprecated
|
||||
GetGoToSocialApprovalRequired() vocab.GoToSocialApprovalRequiredProperty // Deprecated
|
||||
}
|
||||
|
||||
// WithApprovedBy represents a Statusable with the approvedBy property.
|
||||
// WithApprovedBy represents an object with the approvedBy property.
|
||||
type WithApprovedBy interface {
|
||||
GetGoToSocialApprovedBy() vocab.GoToSocialApprovedByProperty
|
||||
SetGoToSocialApprovedBy(vocab.GoToSocialApprovedByProperty)
|
||||
}
|
||||
|
||||
// WithVotersCount represents an activity or object the result property.
|
||||
// WithLikeAuthorization represents a Likeable with the likeAuthorization property.
|
||||
type WithLikeAuthorization interface {
|
||||
GetGoToSocialLikeAuthorization() vocab.GoToSocialLikeAuthorizationProperty
|
||||
SetGoToSocialLikeAuthorization(vocab.GoToSocialLikeAuthorizationProperty)
|
||||
}
|
||||
|
||||
// WithReplyAuthorization represents a statusable with the replyAuthorization property.
|
||||
type WithReplyAuthorization interface {
|
||||
GetGoToSocialReplyAuthorization() vocab.GoToSocialReplyAuthorizationProperty
|
||||
SetGoToSocialReplyAuthorization(vocab.GoToSocialReplyAuthorizationProperty)
|
||||
}
|
||||
|
||||
// WithAnnounceAuthorization represents an Announceable with the announceAuthorization property.
|
||||
type WithAnnounceAuthorization interface {
|
||||
GetGoToSocialAnnounceAuthorization() vocab.GoToSocialAnnounceAuthorizationProperty
|
||||
SetGoToSocialAnnounceAuthorization(vocab.GoToSocialAnnounceAuthorizationProperty)
|
||||
}
|
||||
|
||||
// WithResult represents an activity or object with the result property.
|
||||
type WithResult interface {
|
||||
GetActivityStreamsResult() vocab.ActivityStreamsResultProperty
|
||||
SetActivityStreamsResult(vocab.ActivityStreamsResultProperty)
|
||||
}
|
||||
|
||||
// WithInteractingObject represents an activity or object with the InteractingObject property.
|
||||
type WithInteractingObject interface {
|
||||
GetGoToSocialInteractingObject() vocab.GoToSocialInteractingObjectProperty
|
||||
SetGoToSocialInteractingObject(vocab.GoToSocialInteractingObjectProperty)
|
||||
}
|
||||
|
||||
// WithInteractionTarget represents an activity or object with the InteractionTarget property.
|
||||
type WithInteractionTarget interface {
|
||||
GetGoToSocialInteractionTarget() vocab.GoToSocialInteractionTargetProperty
|
||||
SetGoToSocialInteractionTarget(vocab.GoToSocialInteractionTargetProperty)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -582,16 +582,16 @@ func NormalizeOutgoingContentProp(item WithContent, rawJSON map[string]interface
|
|||
//
|
||||
// "interactionPolicy": {
|
||||
// "canAnnounce": {
|
||||
// "always": "https://www.w3.org/ns/activitystreams#Public",
|
||||
// "approvalRequired": []
|
||||
// "automaticApproval": "https://www.w3.org/ns/activitystreams#Public",
|
||||
// "manualApproval": []
|
||||
// },
|
||||
// "canLike": {
|
||||
// "always": "https://www.w3.org/ns/activitystreams#Public",
|
||||
// "approvalRequired": []
|
||||
// "automaticApproval": "https://www.w3.org/ns/activitystreams#Public",
|
||||
// "manualApproval": []
|
||||
// },
|
||||
// "canReply": {
|
||||
// "always": "https://www.w3.org/ns/activitystreams#Public",
|
||||
// "approvalRequired": []
|
||||
// "automaticApproval": "https://www.w3.org/ns/activitystreams#Public",
|
||||
// "manualApproval": []
|
||||
// }
|
||||
// }
|
||||
//
|
||||
|
|
@ -599,22 +599,22 @@ func NormalizeOutgoingContentProp(item WithContent, rawJSON map[string]interface
|
|||
//
|
||||
// "interactionPolicy": {
|
||||
// "canAnnounce": {
|
||||
// "always": [
|
||||
// "automaticApproval": [
|
||||
// "https://www.w3.org/ns/activitystreams#Public"
|
||||
// ],
|
||||
// "approvalRequired": []
|
||||
// "manualApproval": []
|
||||
// },
|
||||
// "canLike": {
|
||||
// "always": [
|
||||
// "automaticApproval": [
|
||||
// "https://www.w3.org/ns/activitystreams#Public"
|
||||
// ],
|
||||
// "approvalRequired": []
|
||||
// "manualApproval": []
|
||||
// },
|
||||
// "canReply": {
|
||||
// "always": [
|
||||
// "automaticApproval": [
|
||||
// "https://www.w3.org/ns/activitystreams#Public"
|
||||
// ],
|
||||
// "approvalRequired": []
|
||||
// "manualApproval": []
|
||||
// }
|
||||
// }
|
||||
//
|
||||
|
|
@ -655,8 +655,10 @@ func NormalizeOutgoingInteractionPolicyProp(item WithInteractionPolicy, rawJSON
|
|||
}
|
||||
|
||||
for _, PolicyValuesKey := range []string{
|
||||
"always",
|
||||
"approvalRequired",
|
||||
"automaticApproval",
|
||||
"manualApproval",
|
||||
"always", // deprecated
|
||||
"approvalRequired", // deprecated
|
||||
} {
|
||||
PolicyValuesVal, ok := rulesValMap[PolicyValuesKey]
|
||||
if !ok {
|
||||
|
|
|
|||
|
|
@ -135,7 +135,8 @@ func AppendBcc(with WithBcc, bcc ...*url.URL) {
|
|||
}, bcc...)
|
||||
}
|
||||
|
||||
// GetURL returns the IRIs contained in the URL property of 'with'.
|
||||
// GetURL returns IRIs contained
|
||||
// in the URL property of 'with'.
|
||||
func GetURL(with WithURL) []*url.URL {
|
||||
urlProp := with.GetActivityStreamsUrl()
|
||||
if urlProp == nil || urlProp.Len() == 0 {
|
||||
|
|
@ -144,9 +145,31 @@ func GetURL(with WithURL) []*url.URL {
|
|||
urls := make([]*url.URL, 0, urlProp.Len())
|
||||
for i := 0; i < urlProp.Len(); i++ {
|
||||
at := urlProp.At(i)
|
||||
|
||||
// See if it's a plain URI.
|
||||
if at.IsXMLSchemaAnyURI() {
|
||||
u := at.GetXMLSchemaAnyURI()
|
||||
urls = append(urls, u)
|
||||
continue
|
||||
}
|
||||
|
||||
// See if it's a Link obj
|
||||
// with an href property.
|
||||
if at.IsActivityStreamsLink() {
|
||||
l := at.GetActivityStreamsLink()
|
||||
hr := l.GetActivityStreamsHref()
|
||||
if hr == nil {
|
||||
// No href.
|
||||
continue
|
||||
}
|
||||
|
||||
if hr.IsXMLSchemaAnyURI() {
|
||||
u := hr.Get()
|
||||
urls = append(urls, u)
|
||||
} else if hr.IsIRI() {
|
||||
u := hr.GetIRI()
|
||||
urls = append(urls, u)
|
||||
}
|
||||
}
|
||||
}
|
||||
return urls
|
||||
|
|
@ -203,6 +226,36 @@ func AppendObjectIRIs(with WithObject, object ...*url.URL) {
|
|||
}, object...)
|
||||
}
|
||||
|
||||
// AppendInstrumentIRIs appends the given IRIs to the Instrument property of 'with'.
|
||||
func AppendInstrumentIRIs(with WithInstrument, instrument ...*url.URL) {
|
||||
appendIRIs(func() Property[vocab.ActivityStreamsInstrumentPropertyIterator] {
|
||||
instrumentProp := with.GetActivityStreamsInstrument()
|
||||
if instrumentProp == nil {
|
||||
instrumentProp = streams.NewActivityStreamsInstrumentProperty()
|
||||
with.SetActivityStreamsInstrument(instrumentProp)
|
||||
}
|
||||
return instrumentProp
|
||||
}, instrument...)
|
||||
}
|
||||
|
||||
// GetResultIRIs returns the IRIs contained in the `result` property of 'with'.
|
||||
func GetResultIRIs(with WithResult) []*url.URL {
|
||||
resultProp := with.GetActivityStreamsResult()
|
||||
return extractIRIs(resultProp)
|
||||
}
|
||||
|
||||
// AppendResultIRIs appends the given IRIs to the Result property of 'with'.
|
||||
func AppendResultIRIs(with WithResult, result ...*url.URL) {
|
||||
appendIRIs(func() Property[vocab.ActivityStreamsResultPropertyIterator] {
|
||||
resultProp := with.GetActivityStreamsResult()
|
||||
if resultProp == nil {
|
||||
resultProp = streams.NewActivityStreamsResultProperty()
|
||||
with.SetActivityStreamsResult(resultProp)
|
||||
}
|
||||
return resultProp
|
||||
}, result...)
|
||||
}
|
||||
|
||||
// GetTargetIRIs returns the IRIs contained in the Target property of 'with'.
|
||||
func GetTargetIRIs(with WithTarget) []*url.URL {
|
||||
targetProp := with.GetActivityStreamsTarget()
|
||||
|
|
@ -239,6 +292,42 @@ func AppendAttributedTo(with WithAttributedTo, attribTo ...*url.URL) {
|
|||
}, attribTo...)
|
||||
}
|
||||
|
||||
// GetInteractingObject returns IRIs contained in the interactingObject property of 'with'.
|
||||
func GetInteractingObject(with WithInteractingObject) []*url.URL {
|
||||
intObjProp := with.GetGoToSocialInteractingObject()
|
||||
return getIRIs(intObjProp)
|
||||
}
|
||||
|
||||
// AppendInteractingObject appends the given IRIs to the interactingObject property of 'with'.
|
||||
func AppendInteractingObject(with WithInteractingObject, interactingObject ...*url.URL) {
|
||||
appendIRIs(func() Property[vocab.GoToSocialInteractingObjectPropertyIterator] {
|
||||
intObjProp := with.GetGoToSocialInteractingObject()
|
||||
if intObjProp == nil {
|
||||
intObjProp = streams.NewGoToSocialInteractingObjectProperty()
|
||||
with.SetGoToSocialInteractingObject(intObjProp)
|
||||
}
|
||||
return intObjProp
|
||||
}, interactingObject...)
|
||||
}
|
||||
|
||||
// GetInteractionTarget returns IRIs contained in the interactionTarget property of 'with'.
|
||||
func GetInteractionTarget(with WithInteractionTarget) []*url.URL {
|
||||
intTargetProp := with.GetGoToSocialInteractionTarget()
|
||||
return getIRIs(intTargetProp)
|
||||
}
|
||||
|
||||
// AppendInteractionTarget appends the given IRIs to the interactionTarget property of 'with'.
|
||||
func AppendInteractionTarget(with WithInteractionTarget, interactionTarget ...*url.URL) {
|
||||
appendIRIs(func() Property[vocab.GoToSocialInteractionTargetPropertyIterator] {
|
||||
intTargetProp := with.GetGoToSocialInteractionTarget()
|
||||
if intTargetProp == nil {
|
||||
intTargetProp = streams.NewGoToSocialInteractionTargetProperty()
|
||||
with.SetGoToSocialInteractionTarget(intTargetProp)
|
||||
}
|
||||
return intTargetProp
|
||||
}, interactionTarget...)
|
||||
}
|
||||
|
||||
// GetInReplyTo returns the IRIs contained in the InReplyTo property of 'with'.
|
||||
func GetInReplyTo(with WithInReplyTo) []*url.URL {
|
||||
replyProp := with.GetActivityStreamsInReplyTo()
|
||||
|
|
@ -539,14 +628,56 @@ func SetManuallyApprovesFollowers(with WithManuallyApprovesFollowers, manuallyAp
|
|||
mafProp.Set(manuallyApprovesFollowers)
|
||||
}
|
||||
|
||||
// GetHidesToPublicFromUnauthedWeb returns the boolean contained in the hidesToPublicFromUnauthedWeb property of 'with'.
|
||||
//
|
||||
// Returns default 'false' if property unusable or not set.
|
||||
func GetHidesToPublicFromUnauthedWeb(with WithHidesToPublicFromUnauthedWeb) bool {
|
||||
hidesProp := with.GetGoToSocialHidesToPublicFromUnauthedWeb()
|
||||
if hidesProp == nil || !hidesProp.IsXMLSchemaBoolean() {
|
||||
return false
|
||||
}
|
||||
return hidesProp.Get()
|
||||
}
|
||||
|
||||
// SetHidesToPublicFromUnauthedWeb sets the given boolean on the hidesToPublicFromUnauthedWeb property of 'with'.
|
||||
func SetHidesToPublicFromUnauthedWeb(with WithHidesToPublicFromUnauthedWeb, hidesToPublicFromUnauthedWeb bool) {
|
||||
hidesProp := with.GetGoToSocialHidesToPublicFromUnauthedWeb()
|
||||
if hidesProp == nil {
|
||||
hidesProp = streams.NewGoToSocialHidesToPublicFromUnauthedWebProperty()
|
||||
with.SetGoToSocialHidesToPublicFromUnauthedWeb(hidesProp)
|
||||
}
|
||||
hidesProp.Set(hidesToPublicFromUnauthedWeb)
|
||||
}
|
||||
|
||||
// GetHidesCcPublicFromUnauthedWeb returns the boolean contained in the hidesCcPublicFromUnauthedWeb property of 'with'.
|
||||
//
|
||||
// Returns default 'true' if property unusable or not set.
|
||||
func GetHidesCcPublicFromUnauthedWeb(with WithHidesCcPublicFromUnauthedWeb) bool {
|
||||
hidesProp := with.GetGoToSocialHidesCcPublicFromUnauthedWeb()
|
||||
if hidesProp == nil || !hidesProp.IsXMLSchemaBoolean() {
|
||||
return true
|
||||
}
|
||||
return hidesProp.Get()
|
||||
}
|
||||
|
||||
// SetHidesCcPublicFromUnauthedWeb sets the given boolean on the hidesCcPublicFromUnauthedWeb property of 'with'.
|
||||
func SetHidesCcPublicFromUnauthedWeb(with WithHidesCcPublicFromUnauthedWeb, hidesCcPublicFromUnauthedWeb bool) {
|
||||
hidesProp := with.GetGoToSocialHidesCcPublicFromUnauthedWeb()
|
||||
if hidesProp == nil {
|
||||
hidesProp = streams.NewGoToSocialHidesCcPublicFromUnauthedWebProperty()
|
||||
with.SetGoToSocialHidesCcPublicFromUnauthedWeb(hidesProp)
|
||||
}
|
||||
hidesProp.Set(hidesCcPublicFromUnauthedWeb)
|
||||
}
|
||||
|
||||
// GetApprovedBy returns the URL contained in
|
||||
// the ApprovedBy property of 'with', if set.
|
||||
func GetApprovedBy(with WithApprovedBy) *url.URL {
|
||||
mafProp := with.GetGoToSocialApprovedBy()
|
||||
if mafProp == nil || !mafProp.IsIRI() {
|
||||
abProp := with.GetGoToSocialApprovedBy()
|
||||
if abProp == nil || !abProp.IsIRI() {
|
||||
return nil
|
||||
}
|
||||
return mafProp.Get()
|
||||
return abProp.Get()
|
||||
}
|
||||
|
||||
// SetApprovedBy sets the given url
|
||||
|
|
@ -560,6 +691,177 @@ func SetApprovedBy(with WithApprovedBy, approvedBy *url.URL) {
|
|||
abProp.Set(approvedBy)
|
||||
}
|
||||
|
||||
// GetLikeAuthorization returns the URL contained in
|
||||
// the likeAuthorization property of 'with', if set.
|
||||
func GetLikeAuthorization(with WithLikeAuthorization) *url.URL {
|
||||
laProp := with.GetGoToSocialLikeAuthorization()
|
||||
if laProp == nil || !laProp.IsIRI() {
|
||||
return nil
|
||||
}
|
||||
return laProp.Get()
|
||||
}
|
||||
|
||||
// SetLikeAuthorization sets the given url on
|
||||
// the 'likeAuthorization' property of 'with'.
|
||||
func SetLikeAuthorization(with WithLikeAuthorization, likeAuthorization *url.URL) {
|
||||
laProp := with.GetGoToSocialLikeAuthorization()
|
||||
if laProp == nil {
|
||||
laProp = streams.NewGoToSocialLikeAuthorizationProperty()
|
||||
with.SetGoToSocialLikeAuthorization(laProp)
|
||||
}
|
||||
laProp.Set(likeAuthorization)
|
||||
}
|
||||
|
||||
// GetReplyAuthorization returns the URL contained in
|
||||
// the replyAuthorization property of 'with', if set.
|
||||
func GetReplyAuthorization(with WithReplyAuthorization) *url.URL {
|
||||
raProp := with.GetGoToSocialReplyAuthorization()
|
||||
if raProp == nil || !raProp.IsIRI() {
|
||||
return nil
|
||||
}
|
||||
return raProp.Get()
|
||||
}
|
||||
|
||||
// SetReplyAuthorization sets the given url on
|
||||
// the 'replyAuthorization' property of 'with'.
|
||||
func SetReplyAuthorization(with WithReplyAuthorization, replyAuthorization *url.URL) {
|
||||
raProp := with.GetGoToSocialReplyAuthorization()
|
||||
if raProp == nil {
|
||||
raProp = streams.NewGoToSocialReplyAuthorizationProperty()
|
||||
with.SetGoToSocialReplyAuthorization(raProp)
|
||||
}
|
||||
raProp.Set(replyAuthorization)
|
||||
}
|
||||
|
||||
// GetAnnounceAuthorization returns the URL contained in
|
||||
// the announceAuthorization property of 'with', if set.
|
||||
func GetAnnounceAuthorization(with WithAnnounceAuthorization) *url.URL {
|
||||
aaProp := with.GetGoToSocialAnnounceAuthorization()
|
||||
if aaProp == nil || !aaProp.IsIRI() {
|
||||
return nil
|
||||
}
|
||||
return aaProp.Get()
|
||||
}
|
||||
|
||||
// SetAnnounceAuthorization sets the given url on
|
||||
// the 'announceAuthorization' property of 'with'.
|
||||
func SetAnnounceAuthorization(with WithAnnounceAuthorization, announceAuthorization *url.URL) {
|
||||
aaProp := with.GetGoToSocialAnnounceAuthorization()
|
||||
if aaProp == nil {
|
||||
aaProp = streams.NewGoToSocialAnnounceAuthorizationProperty()
|
||||
with.SetGoToSocialAnnounceAuthorization(aaProp)
|
||||
}
|
||||
aaProp.Set(announceAuthorization)
|
||||
}
|
||||
|
||||
// GetMediaType returns the string contained in
|
||||
// the MediaType property of 'with', if set.
|
||||
func GetMediaType(with WithMediaType) string {
|
||||
mtProp := with.GetActivityStreamsMediaType()
|
||||
if mtProp == nil || !mtProp.IsRFCRfc2045() {
|
||||
return ""
|
||||
}
|
||||
return mtProp.Get()
|
||||
}
|
||||
|
||||
// SetMediaType sets the given string
|
||||
// on the MediaType property of 'with'.
|
||||
func SetMediaType(with WithMediaType, mediaType string) {
|
||||
mtProp := with.GetActivityStreamsMediaType()
|
||||
if mtProp == nil {
|
||||
mtProp = streams.NewActivityStreamsMediaTypeProperty()
|
||||
with.SetActivityStreamsMediaType(mtProp)
|
||||
}
|
||||
mtProp.Set(mediaType)
|
||||
}
|
||||
|
||||
// AppendName appends the given name
|
||||
// vals to the Name property of 'with'.
|
||||
func AppendName(with WithName, name ...string) {
|
||||
if len(name) == 0 {
|
||||
return
|
||||
}
|
||||
nameProp := with.GetActivityStreamsName()
|
||||
if nameProp == nil {
|
||||
nameProp = streams.NewActivityStreamsNameProperty()
|
||||
with.SetActivityStreamsName(nameProp)
|
||||
}
|
||||
for _, name := range name {
|
||||
nameProp.AppendXMLSchemaString(name)
|
||||
}
|
||||
}
|
||||
|
||||
// AppendSummary appends the given summary
|
||||
// vals to the Summary property of 'with'.
|
||||
func AppendSummary(with WithSummary, summary ...string) {
|
||||
if len(summary) == 0 {
|
||||
return
|
||||
}
|
||||
summaryProp := with.GetActivityStreamsSummary()
|
||||
if summaryProp == nil {
|
||||
summaryProp = streams.NewActivityStreamsSummaryProperty()
|
||||
with.SetActivityStreamsSummary(summaryProp)
|
||||
}
|
||||
for _, summary := range summary {
|
||||
summaryProp.AppendXMLSchemaString(summary)
|
||||
}
|
||||
}
|
||||
|
||||
// SetBlurhash sets the given string
|
||||
// on the Blurhash property of 'with'.
|
||||
func SetBlurhash(with WithBlurhash, mediaType string) {
|
||||
bProp := with.GetTootBlurhash()
|
||||
if bProp == nil {
|
||||
bProp = streams.NewTootBlurhashProperty()
|
||||
with.SetTootBlurhash(bProp)
|
||||
}
|
||||
bProp.Set(mediaType)
|
||||
}
|
||||
|
||||
// AppendSensitive appends the given sensitive
|
||||
// boolean to the `sensitive` property of 'with'.
|
||||
func AppendSensitive(with WithSensitive, sensitive bool) {
|
||||
sProp := with.GetActivityStreamsSensitive()
|
||||
if sProp == nil {
|
||||
sProp = streams.NewActivityStreamsSensitiveProperty()
|
||||
with.SetActivityStreamsSensitive(sProp)
|
||||
}
|
||||
sProp.AppendXMLSchemaBoolean(sensitive)
|
||||
}
|
||||
|
||||
// AppendContent appends the given content
|
||||
// string to the `content` property of 'with'.
|
||||
func AppendContent(with WithContent, content string) {
|
||||
cProp := with.GetActivityStreamsContent()
|
||||
if cProp == nil {
|
||||
cProp = streams.NewActivityStreamsContentProperty()
|
||||
with.SetActivityStreamsContent(cProp)
|
||||
}
|
||||
cProp.AppendXMLSchemaString(content)
|
||||
}
|
||||
|
||||
// AppendContentMap appends the given content
|
||||
// language map to the `content` property of 'with'.
|
||||
func AppendContentMap(with WithContent, contentMap map[string]string) {
|
||||
cProp := with.GetActivityStreamsContent()
|
||||
if cProp == nil {
|
||||
cProp = streams.NewActivityStreamsContentProperty()
|
||||
with.SetActivityStreamsContent(cProp)
|
||||
}
|
||||
cProp.AppendRDFLangString(contentMap)
|
||||
}
|
||||
|
||||
// SetReplies sets the given replies collection
|
||||
// to the `replies` property of 'with'.
|
||||
func SetReplies(with WithReplies, replies vocab.ActivityStreamsCollection) {
|
||||
rProp := with.GetActivityStreamsReplies()
|
||||
if rProp == nil {
|
||||
rProp = streams.NewActivityStreamsRepliesProperty()
|
||||
with.SetActivityStreamsReplies(rProp)
|
||||
}
|
||||
rProp.SetActivityStreamsCollection(replies)
|
||||
}
|
||||
|
||||
// extractIRIs extracts just the AP IRIs from an iterable
|
||||
// property that may contain types (with IRIs) or just IRIs.
|
||||
//
|
||||
|
|
|
|||
283
internal/ap/properties_test.go
Normal file
283
internal/ap/properties_test.go
Normal file
|
|
@ -0,0 +1,283 @@
|
|||
// GoToSocial
|
||||
// Copyright (C) GoToSocial Authors admin@gotosocial.org
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it 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.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package ap_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"code.superseriousbusiness.org/gotosocial/internal/ap"
|
||||
"code.superseriousbusiness.org/gotosocial/testrig"
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
type PropertiesTestSuite struct {
|
||||
suite.Suite
|
||||
}
|
||||
|
||||
func (suite *PropertiesTestSuite) TestGetStatusableURL() {
|
||||
// Pretty good representation of
|
||||
// how a peertube video is federated.
|
||||
const peertubeVideo = `{
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/activitystreams"
|
||||
],
|
||||
"to": [
|
||||
"https://www.w3.org/ns/activitystreams#Public"
|
||||
],
|
||||
"cc": [
|
||||
"https://example.org/accounts/someone/followers"
|
||||
],
|
||||
"type": "Video",
|
||||
"id": "https://example.org/videos/watch/942d51e6-9320-4f40-980b-76bba0652bc2",
|
||||
"url": [
|
||||
{
|
||||
"type": "Link",
|
||||
"mediaType": "text/html",
|
||||
"href": "https://example.org/w/jifTXYpdLJSU269svW8Jdb"
|
||||
},
|
||||
{
|
||||
"type": "Link",
|
||||
"mediaType": "text/html",
|
||||
"href": "https://example.org/videos/watch/942d51e6-9320-4f40-980b-76bba0652bc2"
|
||||
},
|
||||
{
|
||||
"type": "Link",
|
||||
"mediaType": "application/x-mpegURL",
|
||||
"href": "https://example.org/static/streaming-playlists/hls/942d51e6-9320-4f40-980b-76bba0652bc2/3d412b0f-3f2e-4509-9d0f-0142223b1752-master.m3u8",
|
||||
"tag": [
|
||||
{
|
||||
"type": "Infohash",
|
||||
"name": "4b5a702f76333963655575616e627a5261426269"
|
||||
},
|
||||
{
|
||||
"type": "Infohash",
|
||||
"name": "4f6c5552324a39324a55447036735649586b4875"
|
||||
},
|
||||
{
|
||||
"type": "Infohash",
|
||||
"name": "476d4154793667574d6d594c7276523471364732"
|
||||
},
|
||||
{
|
||||
"type": "Link",
|
||||
"name": "sha256",
|
||||
"mediaType": "application/json",
|
||||
"href": "https://example.org/static/streaming-playlists/hls/942d51e6-9320-4f40-980b-76bba0652bc2/0c607a4c-ab78-4bed-aeef-9970abd88e77-segments-sha256.json"
|
||||
},
|
||||
{
|
||||
"type": "Link",
|
||||
"mediaType": "video/mp4",
|
||||
"href": "https://example.org/static/streaming-playlists/hls/942d51e6-9320-4f40-980b-76bba0652bc2/c6b6c9fb-83da-425c-9ce6-680e00eb9ecb-480-fragmented.mp4",
|
||||
"height": 480,
|
||||
"width": 854,
|
||||
"size": 11260985,
|
||||
"fps": 30,
|
||||
"attachment": [
|
||||
{
|
||||
"type": "PropertyValue",
|
||||
"name": "ffprobe_codec_type",
|
||||
"value": "video"
|
||||
},
|
||||
{
|
||||
"type": "PropertyValue",
|
||||
"name": "peertube_format_flag",
|
||||
"value": "fragmented"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Link",
|
||||
"rel": [
|
||||
"metadata",
|
||||
"video/mp4"
|
||||
],
|
||||
"mediaType": "application/json",
|
||||
"href": "https://example.org/api/v1/videos/942d51e6-9320-4f40-980b-76bba0652bc2/metadata/236424",
|
||||
"height": 480,
|
||||
"width": 854,
|
||||
"fps": 30
|
||||
},
|
||||
{
|
||||
"type": "Link",
|
||||
"mediaType": "application/x-bittorrent",
|
||||
"href": "https://example.org/lazy-static/torrents/fac3fb9c-55a6-4e56-82f5-8de8a3f62d8f-480-hls.torrent",
|
||||
"height": 480,
|
||||
"width": 854,
|
||||
"fps": 30
|
||||
},
|
||||
{
|
||||
"type": "Link",
|
||||
"mediaType": "application/x-bittorrent;x-scheme-handler/magnet",
|
||||
"href": "magnet:?xs=https%3A%2F%2Fexample.org%2Flazy-static%2Ftorrents%2Ffac3fb9c-55a6-4e56-82f5-8de8a3f62d8f-480-hls.torrent&xt=urn:btih:b5a55918c3a05c2459156b6f34570ea64c69fd5a&dn=Na+proch%C3%A1zce+%E2%99%A5%EF%B8%8F+Walking+with+our+gang+%F0%9F%98%83&tr=https%3A%2F%2Fexample.org%2Ftracker%2Fannounce&tr=wss%3A%2F%2Fexample.org%3A443%2Ftracker%2Fsocket&ws=https%3A%2F%2Fexample.org%2Fstatic%2Fstreaming-playlists%2Fhls%2F942d51e6-9320-4f40-980b-76bba0652bc2%2Fc6b6c9fb-83da-425c-9ce6-680e00eb9ecb-480-fragmented.mp4",
|
||||
"height": 480,
|
||||
"width": 854,
|
||||
"fps": 30
|
||||
},
|
||||
{
|
||||
"type": "Link",
|
||||
"mediaType": "video/mp4",
|
||||
"href": "https://example.org/static/streaming-playlists/hls/942d51e6-9320-4f40-980b-76bba0652bc2/113ebf59-8e27-42f5-b971-d315f3fec77d-0-fragmented.mp4",
|
||||
"height": 0,
|
||||
"width": 0,
|
||||
"size": 1472647,
|
||||
"fps": 0,
|
||||
"attachment": [
|
||||
{
|
||||
"type": "PropertyValue",
|
||||
"name": "ffprobe_codec_type",
|
||||
"value": "audio"
|
||||
},
|
||||
{
|
||||
"type": "PropertyValue",
|
||||
"name": "peertube_format_flag",
|
||||
"value": "fragmented"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Link",
|
||||
"rel": [
|
||||
"metadata",
|
||||
"video/mp4"
|
||||
],
|
||||
"mediaType": "application/json",
|
||||
"href": "https://example.org/api/v1/videos/942d51e6-9320-4f40-980b-76bba0652bc2/metadata/236425",
|
||||
"height": 0,
|
||||
"width": 0,
|
||||
"fps": 0
|
||||
},
|
||||
{
|
||||
"type": "Link",
|
||||
"mediaType": "application/x-bittorrent",
|
||||
"href": "https://example.org/lazy-static/torrents/babc50e0-4643-4467-bde0-5d837d71fed5-0-hls.torrent",
|
||||
"height": 0,
|
||||
"width": 0,
|
||||
"fps": 0
|
||||
},
|
||||
{
|
||||
"type": "Link",
|
||||
"mediaType": "application/x-bittorrent;x-scheme-handler/magnet",
|
||||
"href": "magnet:?xs=https%3A%2F%2Fexample.org%2Flazy-static%2Ftorrents%2Fbabc50e0-4643-4467-bde0-5d837d71fed5-0-hls.torrent&xt=urn:btih:395086b81fae8b1b9f7d0a03375c66214acff459&dn=Na+proch%C3%A1zce+%E2%99%A5%EF%B8%8F+Walking+with+our+gang+%F0%9F%98%83&tr=https%3A%2F%2Fexample.org%2Ftracker%2Fannounce&tr=wss%3A%2F%2Fexample.org%3A443%2Ftracker%2Fsocket&ws=https%3A%2F%2Fexample.org%2Fstatic%2Fstreaming-playlists%2Fhls%2F942d51e6-9320-4f40-980b-76bba0652bc2%2F113ebf59-8e27-42f5-b971-d315f3fec77d-0-fragmented.mp4",
|
||||
"height": 0,
|
||||
"width": 0,
|
||||
"fps": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Link",
|
||||
"name": "tracker-http",
|
||||
"rel": [
|
||||
"tracker",
|
||||
"http"
|
||||
],
|
||||
"href": "https://example.org/tracker/announce"
|
||||
},
|
||||
{
|
||||
"type": "Link",
|
||||
"name": "tracker-websocket",
|
||||
"rel": [
|
||||
"tracker",
|
||||
"websocket"
|
||||
],
|
||||
"href": "wss://example.org:443/tracker/socket"
|
||||
}
|
||||
]
|
||||
}`
|
||||
|
||||
// Mix of plain IRIs and Links,
|
||||
// we should be able to parse this.
|
||||
//
|
||||
// The last one with no href should be ignored.
|
||||
const mixedPlainURIsAndLinks = `{
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/activitystreams"
|
||||
],
|
||||
"to": [
|
||||
"https://www.w3.org/ns/activitystreams#Public"
|
||||
],
|
||||
"cc": [
|
||||
"https://example.org/accounts/someone/followers"
|
||||
],
|
||||
"type": "Video",
|
||||
"id": "https://example.org/videos/watch/942d51e6-9320-4f40-980b-76bba0652bc2",
|
||||
"url": [
|
||||
"https://example.org/videos/watch/942d51e6-9320-4f40-980b-76bba0652bc2",
|
||||
{
|
||||
"type": "Link",
|
||||
"mediaType": "text/html",
|
||||
"href": "https://example.org/w/jifTXYpdLJSU269svW8Jdb"
|
||||
},
|
||||
{
|
||||
"type": "Link",
|
||||
"mediaType": "text/html",
|
||||
"href": "https://example.org/videos/watch/942d51e6-9320-4f40-980b-76bba0652bc2"
|
||||
},
|
||||
{
|
||||
"type": "Link",
|
||||
"mediaType": "text/html"
|
||||
}
|
||||
]
|
||||
}`
|
||||
|
||||
for i, test := range []struct {
|
||||
in string
|
||||
expectedURLs []*url.URL
|
||||
}{
|
||||
{
|
||||
in: peertubeVideo,
|
||||
expectedURLs: []*url.URL{
|
||||
testrig.URLMustParse("https://example.org/w/jifTXYpdLJSU269svW8Jdb"),
|
||||
testrig.URLMustParse("https://example.org/videos/watch/942d51e6-9320-4f40-980b-76bba0652bc2"),
|
||||
testrig.URLMustParse("https://example.org/static/streaming-playlists/hls/942d51e6-9320-4f40-980b-76bba0652bc2/3d412b0f-3f2e-4509-9d0f-0142223b1752-master.m3u8"),
|
||||
testrig.URLMustParse("https://example.org/tracker/announce"),
|
||||
testrig.URLMustParse("wss://example.org:443/tracker/socket"),
|
||||
},
|
||||
},
|
||||
{
|
||||
in: mixedPlainURIsAndLinks,
|
||||
expectedURLs: []*url.URL{
|
||||
testrig.URLMustParse("https://example.org/videos/watch/942d51e6-9320-4f40-980b-76bba0652bc2"),
|
||||
testrig.URLMustParse("https://example.org/w/jifTXYpdLJSU269svW8Jdb"),
|
||||
testrig.URLMustParse("https://example.org/videos/watch/942d51e6-9320-4f40-980b-76bba0652bc2"),
|
||||
},
|
||||
},
|
||||
} {
|
||||
// Parse input to statusable.
|
||||
statusable, err := ap.ResolveStatusable(
|
||||
suite.T().Context(),
|
||||
io.NopCloser(bytes.NewBufferString(test.in)),
|
||||
)
|
||||
if err != nil {
|
||||
suite.FailNow(err.Error())
|
||||
}
|
||||
|
||||
// Ensure URL fields as expected.
|
||||
suite.EqualValues(
|
||||
test.expectedURLs,
|
||||
ap.GetURL(statusable),
|
||||
"mismatch in test case %d", i,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPropertiesTestSuite(t *testing.T) {
|
||||
suite.Run(t, new(PropertiesTestSuite))
|
||||
}
|
||||
|
|
@ -19,7 +19,6 @@ package ap_test
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
|
|
@ -36,7 +35,7 @@ func (suite *ResolveTestSuite) TestResolveDocumentAsStatusable() {
|
|||
b := []byte(suite.typeToJson(suite.document1))
|
||||
|
||||
statusable, err := ap.ResolveStatusable(
|
||||
context.Background(), io.NopCloser(bytes.NewReader(b)),
|
||||
suite.T().Context(), io.NopCloser(bytes.NewReader(b)),
|
||||
)
|
||||
suite.NoError(err)
|
||||
suite.NotNil(statusable)
|
||||
|
|
@ -46,7 +45,7 @@ func (suite *ResolveTestSuite) TestResolveDocumentAsAccountable() {
|
|||
b := []byte(suite.typeToJson(suite.document1))
|
||||
|
||||
accountable, err := ap.ResolveAccountable(
|
||||
context.Background(), io.NopCloser(bytes.NewReader(b)),
|
||||
suite.T().Context(), io.NopCloser(bytes.NewReader(b)),
|
||||
)
|
||||
suite.True(gtserror.IsWrongType(err))
|
||||
suite.EqualError(err, "ResolveAccountable: cannot resolve vocab type *typedocument.ActivityStreamsDocument as accountable")
|
||||
|
|
@ -58,7 +57,7 @@ func (suite *ResolveTestSuite) TestResolveHTMLAsAccountable() {
|
|||
<title>.</title>`)
|
||||
|
||||
accountable, err := ap.ResolveAccountable(
|
||||
context.Background(), io.NopCloser(bytes.NewReader(b)),
|
||||
suite.T().Context(), io.NopCloser(bytes.NewReader(b)),
|
||||
)
|
||||
suite.True(gtserror.IsWrongType(err))
|
||||
suite.EqualError(err, "ResolveAccountable: error decoding into json: invalid character '<' looking for beginning of value")
|
||||
|
|
@ -73,7 +72,7 @@ func (suite *ResolveTestSuite) TestResolveNonAPJSONAsAccountable() {
|
|||
}`)
|
||||
|
||||
accountable, err := ap.ResolveAccountable(
|
||||
context.Background(), io.NopCloser(bytes.NewReader(b)),
|
||||
suite.T().Context(), io.NopCloser(bytes.NewReader(b)),
|
||||
)
|
||||
suite.True(gtserror.IsWrongType(err))
|
||||
suite.EqualError(err, "ResolveAccountable: error resolving json into ap vocab type: activity stream did not match any known types")
|
||||
|
|
@ -124,7 +123,7 @@ func (suite *ResolveTestSuite) TestResolveBandwagonAlbumAsStatusable() {
|
|||
}`)
|
||||
|
||||
statusable, err := ap.ResolveStatusable(
|
||||
context.Background(), io.NopCloser(bytes.NewReader(b)),
|
||||
suite.T().Context(), io.NopCloser(bytes.NewReader(b)),
|
||||
)
|
||||
suite.NoError(err)
|
||||
suite.NotNil(statusable)
|
||||
|
|
|
|||
|
|
@ -153,8 +153,8 @@ func serializeStatusable(t vocab.Type, includeContext bool) (map[string]interfac
|
|||
|
||||
NormalizeOutgoingAttachmentProp(statusable, data)
|
||||
NormalizeOutgoingContentProp(statusable, data)
|
||||
if ipa, ok := statusable.(InteractionPolicyAware); ok {
|
||||
NormalizeOutgoingInteractionPolicyProp(ipa, data)
|
||||
if wip, ok := statusable.(WithInteractionPolicy); ok {
|
||||
NormalizeOutgoingInteractionPolicyProp(wip, data)
|
||||
}
|
||||
|
||||
return data, nil
|
||||
|
|
|
|||
|
|
@ -28,7 +28,6 @@ import (
|
|||
"code.superseriousbusiness.org/gotosocial/internal/db"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/email"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/federation"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/filter/visibility"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/gtsmodel"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/media"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/middleware"
|
||||
|
|
@ -79,12 +78,6 @@ func (suite *EmojiGetTestSuite) SetupTest() {
|
|||
suite.state.Storage = suite.storage
|
||||
suite.tc = typeutils.NewConverter(&suite.state)
|
||||
|
||||
testrig.StartTimelines(
|
||||
&suite.state,
|
||||
visibility.NewFilter(&suite.state),
|
||||
suite.tc,
|
||||
)
|
||||
|
||||
suite.mediaManager = testrig.NewTestMediaManager(&suite.state)
|
||||
suite.federator = testrig.NewTestFederator(&suite.state, testrig.NewTestTransportController(&suite.state, testrig.NewMockHTTPClient(nil, "../../../../testrig/media")), suite.mediaManager)
|
||||
suite.emailSender = testrig.NewEmailSender("../../../../web/template/", nil)
|
||||
|
|
|
|||
56
internal/api/activitypub/users/authorizationget.go
Normal file
56
internal/api/activitypub/users/authorizationget.go
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
// GoToSocial
|
||||
// Copyright (C) GoToSocial Authors admin@gotosocial.org
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it 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.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package users
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
apiutil "code.superseriousbusiness.org/gotosocial/internal/api/util"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/gtserror"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// AuthorizationGETHandler serves an accepted interaction request as a
|
||||
// LikeAuthorization, ReplyAuthorization, or AnnounceAuthorization type.
|
||||
func (m *Module) AuthorizationGETHandler(c *gin.Context) {
|
||||
username, errWithCode := apiutil.ParseUsername(c.Param(apiutil.UsernameKey))
|
||||
if errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
reqID, errWithCode := apiutil.ParseID(c.Param(apiutil.IDKey))
|
||||
if errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
contentType, err := apiutil.NegotiateAccept(c, apiutil.ActivityPubHeaders...)
|
||||
if err != nil {
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
resp, errWithCode := m.processor.Fedi().AuthorizationGet(c.Request.Context(), username, reqID)
|
||||
if errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
apiutil.JSONType(c, http.StatusOK, contentType, resp)
|
||||
}
|
||||
|
|
@ -19,7 +19,6 @@ package users_test
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
|
|
@ -193,7 +192,7 @@ func (suite *InboxPostTestSuite) newDelete(actorIRI string, objectIRI string, de
|
|||
|
||||
// Set the To of the delete as public
|
||||
deleteTo := streams.NewActivityStreamsToProperty()
|
||||
deleteTo.AppendIRI(ap.PublicURI())
|
||||
deleteTo.AppendIRI(ap.PublicIRI())
|
||||
delete.SetActivityStreamsTo(deleteTo)
|
||||
|
||||
// set some random-ass ID for the activity
|
||||
|
|
@ -232,7 +231,7 @@ func (suite *InboxPostTestSuite) TestPostBlock() {
|
|||
)
|
||||
|
||||
if !testrig.WaitFor(func() bool {
|
||||
dbBlock, err = suite.db.GetBlock(context.Background(), requestingAccount.ID, targetAccount.ID)
|
||||
dbBlock, err = suite.db.GetBlock(suite.T().Context(), requestingAccount.ID, targetAccount.ID)
|
||||
return err == nil && dbBlock != nil
|
||||
}) {
|
||||
suite.FailNow("timed out waiting for block to be created")
|
||||
|
|
@ -243,7 +242,7 @@ func (suite *InboxPostTestSuite) TestPostBlock() {
|
|||
// one of our instance users should be able to undo that block.
|
||||
func (suite *InboxPostTestSuite) TestPostUnblock() {
|
||||
var (
|
||||
ctx = context.Background()
|
||||
ctx = suite.T().Context()
|
||||
requestingAccount = suite.testAccounts["remote_account_1"]
|
||||
targetAccount = suite.testAccounts["local_account_1"]
|
||||
blockID = "http://fossbros-anonymous.io/blocks/01H1462TPRTVG2RTQCTSQ7N6Q0"
|
||||
|
|
@ -315,7 +314,7 @@ func (suite *InboxPostTestSuite) TestPostUpdate() {
|
|||
requestingAccount.Emojis = []*gtsmodel.Emoji{testEmoji}
|
||||
|
||||
// Create an update from the account.
|
||||
accountable, err := suite.tc.AccountToAS(context.Background(), requestingAccount)
|
||||
accountable, err := suite.tc.AccountToAS(suite.T().Context(), requestingAccount)
|
||||
if err != nil {
|
||||
suite.FailNow(err.Error())
|
||||
}
|
||||
|
|
@ -344,7 +343,7 @@ func (suite *InboxPostTestSuite) TestPostUpdate() {
|
|||
|
||||
if !testrig.WaitFor(func() bool {
|
||||
// displayName should be updated
|
||||
dbUpdatedAccount, _ = suite.db.GetAccountByID(context.Background(), requestingAccount.ID)
|
||||
dbUpdatedAccount, _ = suite.db.GetAccountByID(suite.T().Context(), requestingAccount.ID)
|
||||
return dbUpdatedAccount.DisplayName == updatedDisplayName
|
||||
}) {
|
||||
suite.FailNow("timed out waiting for account update")
|
||||
|
|
@ -399,7 +398,7 @@ func (suite *InboxPostTestSuite) TestPostUpdate() {
|
|||
|
||||
func (suite *InboxPostTestSuite) TestPostDelete() {
|
||||
var (
|
||||
ctx = context.Background()
|
||||
ctx = suite.T().Context()
|
||||
requestingAccount = suite.testAccounts["remote_account_1"]
|
||||
targetAccount = suite.testAccounts["local_account_1"]
|
||||
activityID = requestingAccount.URI + "/some-new-activity/01FG9C441MCTW3R2W117V2PQK3"
|
||||
|
|
@ -517,7 +516,7 @@ func (suite *InboxPostTestSuite) TestPostFromBlockedAccount() {
|
|||
)
|
||||
|
||||
// Create an update from the account.
|
||||
accountable, err := suite.tc.AccountToAS(context.Background(), requestingAccount)
|
||||
accountable, err := suite.tc.AccountToAS(suite.T().Context(), requestingAccount)
|
||||
if err != nil {
|
||||
suite.FailNow(err.Error())
|
||||
}
|
||||
|
|
@ -559,7 +558,7 @@ func (suite *InboxPostTestSuite) TestPostFromBlockedAccountToOtherAccount() {
|
|||
suite.signatureCheck,
|
||||
)
|
||||
|
||||
_, err := suite.state.DB.GetStatusByURI(context.Background(), statusURI)
|
||||
_, err := suite.state.DB.GetStatusByURI(suite.T().Context(), statusURI)
|
||||
suite.ErrorIs(err, db.ErrNoEntries)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ package users_test
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
|
@ -90,7 +89,7 @@ func (suite *OutboxGetTestSuite) TestGetOutbox() {
|
|||
err = json.Unmarshal(b, &m)
|
||||
suite.NoError(err)
|
||||
|
||||
t, err := streams.ToType(context.Background(), m)
|
||||
t, err := streams.ToType(suite.T().Context(), m)
|
||||
suite.NoError(err)
|
||||
|
||||
_, ok := t.(vocab.ActivityStreamsOrderedCollection)
|
||||
|
|
@ -177,7 +176,7 @@ func (suite *OutboxGetTestSuite) TestGetOutboxFirstPage() {
|
|||
err = json.Unmarshal(b, &m)
|
||||
suite.NoError(err)
|
||||
|
||||
t, err := streams.ToType(context.Background(), m)
|
||||
t, err := streams.ToType(suite.T().Context(), m)
|
||||
suite.NoError(err)
|
||||
|
||||
_, ok := t.(vocab.ActivityStreamsOrderedCollectionPage)
|
||||
|
|
@ -240,7 +239,7 @@ func (suite *OutboxGetTestSuite) TestGetOutboxNextPage() {
|
|||
err = json.Unmarshal(b, &m)
|
||||
suite.NoError(err)
|
||||
|
||||
t, err := streams.ToType(context.Background(), m)
|
||||
t, err := streams.ToType(suite.T().Context(), m)
|
||||
suite.NoError(err)
|
||||
|
||||
_, ok := t.(vocab.ActivityStreamsOrderedCollectionPage)
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ package users_test
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
|
|
@ -101,7 +100,7 @@ func (suite *RepliesGetTestSuite) TestGetReplies() {
|
|||
err = json.Unmarshal(b, &m)
|
||||
assert.NoError(suite.T(), err)
|
||||
|
||||
t, err := streams.ToType(context.Background(), m)
|
||||
t, err := streams.ToType(suite.T().Context(), m)
|
||||
assert.NoError(suite.T(), err)
|
||||
|
||||
_, ok := t.(vocab.ActivityStreamsOrderedCollection)
|
||||
|
|
@ -172,7 +171,7 @@ func (suite *RepliesGetTestSuite) TestGetRepliesNext() {
|
|||
err = json.Unmarshal(b, &m)
|
||||
assert.NoError(suite.T(), err)
|
||||
|
||||
t, err := streams.ToType(context.Background(), m)
|
||||
t, err := streams.ToType(suite.T().Context(), m)
|
||||
assert.NoError(suite.T(), err)
|
||||
|
||||
page, ok := t.(vocab.ActivityStreamsOrderedCollectionPage)
|
||||
|
|
@ -243,7 +242,7 @@ func (suite *RepliesGetTestSuite) TestGetRepliesLast() {
|
|||
err = json.Unmarshal(b, &m)
|
||||
assert.NoError(suite.T(), err)
|
||||
|
||||
t, err := streams.ToType(context.Background(), m)
|
||||
t, err := streams.ToType(suite.T().Context(), m)
|
||||
assert.NoError(suite.T(), err)
|
||||
|
||||
page, ok := t.(vocab.ActivityStreamsOrderedCollectionPage)
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue