Compare commits
192 commits
Author | SHA1 | Date | |
---|---|---|---|
|
db803617db | ||
|
b789fe2bc7 | ||
|
ee6e9b2795 | ||
|
3c86bd890c | ||
|
38cd889f7b | ||
|
9d9013db4c | ||
|
fbdfce3190 | ||
|
fef9b6a621 | ||
|
20877c1474 | ||
|
978b4176f1 | ||
|
dcf825bd87 | ||
|
611f9de39b | ||
|
fd6637df4a | ||
|
ebdcb00d0a | ||
|
69aba377bc | ||
|
a687f0634c | ||
|
12c406d968 | ||
|
3babb6c0d2 | ||
|
83ee766e34 | ||
|
048339a6aa | ||
|
ce3b8aacf7 | ||
|
cc4f773b0e | ||
|
afcfa48a7d | ||
|
e605788b4b | ||
|
5e2d4fdb19 | ||
|
b371c2db47 | ||
|
bcda048eab | ||
|
131020faeb | ||
|
5dcc954072 | ||
|
6f26b32ec3 | ||
|
3b7faac604 | ||
|
9caf29bed2 | ||
|
45fe295caa | ||
|
f17dd62ff5 | ||
|
6ed6824d5d | ||
|
addaba05c0 | ||
|
d79c2f2688 | ||
|
04bcde08a1 | ||
|
61a8d36255 | ||
|
4db596b8b9 | ||
|
2fd69ec58b | ||
|
b67937c213 | ||
|
59fedfc4f9 | ||
|
32e570abfd | ||
|
975e92b7f1 | ||
|
f9a4a6120d | ||
|
fa9a3075a5 | ||
|
4dc30f8687 | ||
|
a276b1ca06 | ||
|
1e7b32490d | ||
|
cce21c11cb | ||
|
5bee30d60c | ||
|
d96cca60a1 | ||
|
0a18c0d802 | ||
|
3d3e99ae52 | ||
|
06b1e0173b | ||
|
b092da6d28 | ||
|
6c0d93c6cb | ||
|
16c1832793 | ||
|
f817f96596 | ||
|
1ba9601472 | ||
|
96eea416f3 | ||
|
c06e6fb656 | ||
|
578a4e0cf5 | ||
|
f24ce34c3a | ||
|
f456bd3401 | ||
|
3554991444 | ||
|
45f4afe60e | ||
|
a0d066844f | ||
|
8237e8d09e | ||
|
a5f28fe0c9 | ||
|
c98ec6f89d | ||
|
d3f6960ba0 | ||
|
6171dcbe51 | ||
|
35b1c54bde | ||
|
a840f4d49d | ||
|
ebec95a522 | ||
|
725a21b027 | ||
|
1edcb06afe | ||
|
2300d5e73b | ||
|
eb61c783ed | ||
|
a8254a40e7 | ||
|
ec7c983e46 | ||
|
ec334ece20 | ||
|
39b3a27c82 | ||
|
4f87ef246c | ||
|
bfc21e4850 | ||
|
d3bac8bbec | ||
|
40ece19055 | ||
|
1375a86919 | ||
|
b2eecd9dc7 | ||
|
48b91ca239 | ||
|
c9c0773f2c | ||
|
ba4f51ce2f | ||
|
83d24658a8 | ||
|
6c9bc26a6d | ||
|
fd8a724e77 | ||
|
3a369d834a | ||
|
aecf74951c | ||
|
7a1e639483 | ||
|
62788aa116 | ||
|
a57dd15a8e | ||
|
12a7eba01f | ||
|
dcab555a6b | ||
|
0db9e34b69 | ||
|
b7c629a18a | ||
|
431505b3e4 | ||
|
34d0159ad5 | ||
|
b3f2d44143 | ||
|
c67bbe5ba0 | ||
|
cef9924d9a | ||
|
ef16919d4a | ||
|
8cf685fbe9 | ||
|
6de5717d7f | ||
|
3cceed11b2 | ||
|
f79d50b9b2 | ||
|
8b30709791 | ||
|
1018cde107 | ||
|
6bb43f3f9b | ||
|
66e4510bf1 | ||
|
fdd23cb9ed | ||
|
89e0cfd874 | ||
|
1439042104 | ||
|
a5c6dc9716 | ||
|
c097745c38 | ||
|
db2dcc3455 | ||
|
9fb8a78f91 | ||
|
a483bd9e38 | ||
|
15733cddb2 | ||
|
4d16c3adaf | ||
|
e20c7c21e1 | ||
|
6db7d014dc | ||
|
85bc140b58 | ||
|
83e7847cdf | ||
|
236604bd6b | ||
|
8ed1b8142c | ||
|
15ede4c1ea | ||
|
65b5366031 | ||
|
e664d0918b | ||
|
d61d5c8a6a | ||
|
4bbdef02f1 | ||
|
adf345f1ec | ||
|
f05874be30 | ||
|
29972e2c93 | ||
|
be259b13a7 | ||
|
72b38c2f12 | ||
|
4f66dba139 | ||
|
ac3f195b56 | ||
|
8953f57d88 | ||
|
b7b42e832a | ||
|
36f79e650c | ||
|
29031d1e27 | ||
|
a24936040c | ||
|
5f43419a87 | ||
|
40ee470536 | ||
|
7f4a0a1aeb | ||
|
0767647056 | ||
|
967558947a | ||
|
51e8879779 | ||
|
003d62465f | ||
|
8570793dfb | ||
|
fae7627a1b | ||
|
0362d49da0 | ||
|
e6e696ae22 | ||
|
4a56f31047 | ||
|
15578835a8 | ||
|
efbc8151db | ||
|
9b94b1faa2 | ||
|
1fe4cdaf46 | ||
|
459e75a9db | ||
|
92bf1f779b | ||
|
ab2d063fcb | ||
|
13b9fd5f92 | ||
|
c27049ad15 | ||
|
1bcdf1da3b | ||
|
5e871e81a8 | ||
|
8e88ee8d9c | ||
|
5a56f4f8fb | ||
|
d115f9ebc4 | ||
|
e24efcac8b | ||
|
4c155aa847 | ||
|
de3c15a425 | ||
|
56c7c5dee2 | ||
|
ebdee5aed8 | ||
|
7050df2572 | ||
|
016923b4dc | ||
|
74e84cf8fa | ||
|
5159664a51 | ||
|
fc3741365c | ||
|
68c8fe67cc | ||
|
b22e213e15 | ||
|
61a2b91f45 |
56
.drone.yml
|
@ -12,7 +12,7 @@ steps:
|
||||||
# We use golangci-lint for linting.
|
# We use golangci-lint for linting.
|
||||||
# See: https://golangci-lint.run/
|
# See: https://golangci-lint.run/
|
||||||
- name: lint
|
- name: lint
|
||||||
image: golangci/golangci-lint:v1.55.0
|
image: golangci/golangci-lint:v1.57.2
|
||||||
volumes:
|
volumes:
|
||||||
- name: go-build-cache
|
- name: go-build-cache
|
||||||
path: /root/.cache/go-build
|
path: /root/.cache/go-build
|
||||||
|
@ -28,16 +28,26 @@ steps:
|
||||||
- pull_request
|
- pull_request
|
||||||
|
|
||||||
- name: test
|
- name: test
|
||||||
image: golang:1.21-alpine
|
image: golang:1.22-alpine
|
||||||
volumes:
|
volumes:
|
||||||
- name: go-build-cache
|
- name: go-build-cache
|
||||||
path: /root/.cache/go-build
|
path: /root/.cache/go-build
|
||||||
- name: go-src
|
- name: go-src
|
||||||
path: /go
|
path: /go
|
||||||
|
environment:
|
||||||
|
CGO_ENABLED: "0"
|
||||||
commands:
|
commands:
|
||||||
- apk update --no-cache && apk add git
|
- apk update --no-cache && apk add git
|
||||||
- CGO_ENABLED=0 GTS_DB_TYPE="sqlite" GTS_DB_ADDRESS=":memory:" go test ./...
|
- >-
|
||||||
- CGO_ENABLED=0 ./test/envparsing.sh
|
go test
|
||||||
|
-failfast
|
||||||
|
-timeout=20m
|
||||||
|
-tags "wasmsqlite3 netgo osusergo static_build kvformat timetzdata"
|
||||||
|
./...
|
||||||
|
- ./test/envparsing.sh
|
||||||
|
- ./test/swagger.sh
|
||||||
|
depends_on:
|
||||||
|
- lint
|
||||||
when:
|
when:
|
||||||
event:
|
event:
|
||||||
include:
|
include:
|
||||||
|
@ -45,41 +55,43 @@ steps:
|
||||||
|
|
||||||
- name: web-setup
|
- name: web-setup
|
||||||
image: node:18-alpine
|
image: node:18-alpine
|
||||||
when:
|
|
||||||
event:
|
|
||||||
include:
|
|
||||||
- pull_request
|
|
||||||
volumes:
|
volumes:
|
||||||
- name: yarn_cache
|
- name: yarn_cache
|
||||||
path: /tmp/cache
|
path: /tmp/cache
|
||||||
commands:
|
commands:
|
||||||
- yarn --cwd ./web/source install --frozen-lockfile --cache-folder /tmp/cache
|
- yarn --cwd ./web/source install --frozen-lockfile --cache-folder /tmp/cache
|
||||||
- yarn --cwd ./web/source ts-patch install # https://typia.io/docs/setup/#manual-setup
|
- yarn --cwd ./web/source ts-patch install # https://typia.io/docs/setup/#manual-setup
|
||||||
|
depends_on:
|
||||||
|
- test
|
||||||
|
when:
|
||||||
|
event:
|
||||||
|
include:
|
||||||
|
- pull_request
|
||||||
|
|
||||||
- name: web-lint
|
- name: web-lint
|
||||||
image: node:18-alpine
|
image: node:18-alpine
|
||||||
|
commands:
|
||||||
|
- yarn --cwd ./web/source lint
|
||||||
|
depends_on:
|
||||||
|
- web-setup
|
||||||
when:
|
when:
|
||||||
event:
|
event:
|
||||||
include:
|
include:
|
||||||
- pull_request
|
- pull_request
|
||||||
depends_on:
|
|
||||||
- web-setup
|
|
||||||
commands:
|
|
||||||
- yarn --cwd ./web/source lint
|
|
||||||
|
|
||||||
- name: web-build
|
- name: web-build
|
||||||
image: node:18-alpine
|
image: node:18-alpine
|
||||||
|
commands:
|
||||||
|
- yarn --cwd ./web/source build
|
||||||
|
depends_on:
|
||||||
|
- web-setup
|
||||||
when:
|
when:
|
||||||
event:
|
event:
|
||||||
include:
|
include:
|
||||||
- pull_request
|
- pull_request
|
||||||
depends_on:
|
|
||||||
- web-setup
|
|
||||||
commands:
|
|
||||||
- yarn --cwd ./web/source build
|
|
||||||
|
|
||||||
- name: snapshot
|
- name: snapshot
|
||||||
image: superseriousbusiness/gotosocial-drone-build:0.4.0 # https://github.com/superseriousbusiness/gotosocial-drone-build
|
image: superseriousbusiness/gotosocial-drone-build:0.6.0 # https://github.com/superseriousbusiness/gotosocial-drone-build
|
||||||
volumes:
|
volumes:
|
||||||
- name: go-build-cache
|
- name: go-build-cache
|
||||||
path: /root/.cache/go-build
|
path: /root/.cache/go-build
|
||||||
|
@ -98,7 +110,7 @@ steps:
|
||||||
commands:
|
commands:
|
||||||
# Create a snapshot build with GoReleaser.
|
# Create a snapshot build with GoReleaser.
|
||||||
- git fetch --tags
|
- git fetch --tags
|
||||||
- goreleaser release --rm-dist --snapshot
|
- goreleaser release --clean --snapshot
|
||||||
|
|
||||||
# Login to Docker, push Docker image snapshots + manifests.
|
# Login to Docker, push Docker image snapshots + manifests.
|
||||||
- /go/dockerlogin.sh
|
- /go/dockerlogin.sh
|
||||||
|
@ -120,7 +132,7 @@ steps:
|
||||||
- main
|
- main
|
||||||
|
|
||||||
- name: release
|
- name: release
|
||||||
image: superseriousbusiness/gotosocial-drone-build:0.4.0 # https://github.com/superseriousbusiness/gotosocial-drone-build
|
image: superseriousbusiness/gotosocial-drone-build:0.6.0 # https://github.com/superseriousbusiness/gotosocial-drone-build
|
||||||
volumes:
|
volumes:
|
||||||
- name: go-build-cache
|
- name: go-build-cache
|
||||||
path: /root/.cache/go-build
|
path: /root/.cache/go-build
|
||||||
|
@ -135,7 +147,7 @@ steps:
|
||||||
commands:
|
commands:
|
||||||
- git fetch --tags
|
- git fetch --tags
|
||||||
- /go/dockerlogin.sh
|
- /go/dockerlogin.sh
|
||||||
- goreleaser release --rm-dist
|
- goreleaser release --clean
|
||||||
when:
|
when:
|
||||||
event:
|
event:
|
||||||
include:
|
include:
|
||||||
|
@ -179,7 +191,7 @@ clone:
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: mirror
|
- name: mirror
|
||||||
image: superseriousbusiness/gotosocial-drone-build:0.4.0
|
image: superseriousbusiness/gotosocial-drone-build:0.6.0
|
||||||
environment:
|
environment:
|
||||||
ORIGIN_REPO: https://github.com/superseriousbusiness/gotosocial
|
ORIGIN_REPO: https://github.com/superseriousbusiness/gotosocial
|
||||||
TARGET_REPO: https://codeberg.org/superseriousbusiness/gotosocial
|
TARGET_REPO: https://codeberg.org/superseriousbusiness/gotosocial
|
||||||
|
@ -192,6 +204,6 @@ steps:
|
||||||
|
|
||||||
---
|
---
|
||||||
kind: signature
|
kind: signature
|
||||||
hmac: 00f69df57e8852d610f8d570c504aae22d315c2a0ff4808ef8f191554745c5ae
|
hmac: 2e74313f4192b3e6daf6d1d00a7c3796019d93da7ce7e0a77208ccc3c37089b0
|
||||||
|
|
||||||
...
|
...
|
||||||
|
|
5
.gitignore
vendored
|
@ -37,5 +37,6 @@ shell.nix
|
||||||
/.idea/
|
/.idea/
|
||||||
/.fleet/
|
/.fleet/
|
||||||
|
|
||||||
# ignore cache dir from mkdocs serve
|
# ignore cached pngs from mkdocs serve,
|
||||||
/.cache
|
# while preserving cached fonts.
|
||||||
|
/docs/.cache/plugin/social/*.png
|
||||||
|
|
|
@ -83,3 +83,12 @@ linters-settings:
|
||||||
# Enable all checks, but disable SA1012: nil context passing.
|
# Enable all checks, but disable SA1012: nil context passing.
|
||||||
# See: https://staticcheck.io/docs/configuration/options/#checks
|
# See: https://staticcheck.io/docs/configuration/options/#checks
|
||||||
checks: ["all", "-SA1012"]
|
checks: ["all", "-SA1012"]
|
||||||
|
|
||||||
|
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'
|
||||||
|
|
|
@ -29,6 +29,8 @@ builds:
|
||||||
- timetzdata
|
- timetzdata
|
||||||
- >-
|
- >-
|
||||||
{{ if and (index .Env "DEBUG") (.Env.DEBUG) }}debugenv{{ end }}
|
{{ if and (index .Env "DEBUG") (.Env.DEBUG) }}debugenv{{ end }}
|
||||||
|
- >-
|
||||||
|
{{ if and (index .Env "WASMSQLITE3") (.Env.WASMSQLITE3) }}wasmsqlite3{{ end }}
|
||||||
env:
|
env:
|
||||||
- CGO_ENABLED=0
|
- CGO_ENABLED=0
|
||||||
goos:
|
goos:
|
||||||
|
@ -60,7 +62,7 @@ dockers:
|
||||||
image_templates:
|
image_templates:
|
||||||
- "superseriousbusiness/{{ .ProjectName }}:{{ .Version }}-amd64"
|
- "superseriousbusiness/{{ .ProjectName }}:{{ .Version }}-amd64"
|
||||||
- "superseriousbusiness/{{ .ProjectName }}:latest-amd64"
|
- "superseriousbusiness/{{ .ProjectName }}:latest-amd64"
|
||||||
- "superseriousbusiness/{{ .ProjectName }}:snapshot-amd64"
|
- "{{ if .IsSnapshot }}superseriousbusiness/{{ .ProjectName }}:snapshot-amd64{{ end }}"
|
||||||
build_flag_templates:
|
build_flag_templates:
|
||||||
- "--platform=linux/amd64"
|
- "--platform=linux/amd64"
|
||||||
- "--label=org.opencontainers.image.created={{.Date}}"
|
- "--label=org.opencontainers.image.created={{.Date}}"
|
||||||
|
@ -80,7 +82,7 @@ dockers:
|
||||||
image_templates:
|
image_templates:
|
||||||
- "superseriousbusiness/{{ .ProjectName }}:{{ .Version }}-arm64v8"
|
- "superseriousbusiness/{{ .ProjectName }}:{{ .Version }}-arm64v8"
|
||||||
- "superseriousbusiness/{{ .ProjectName }}:latest-arm64v8"
|
- "superseriousbusiness/{{ .ProjectName }}:latest-arm64v8"
|
||||||
- "superseriousbusiness/{{ .ProjectName }}:snapshot-arm64v8"
|
- "{{ if .IsSnapshot }}superseriousbusiness/{{ .ProjectName }}:snapshot-arm64v8{{ end }}"
|
||||||
build_flag_templates:
|
build_flag_templates:
|
||||||
- "--platform=linux/arm64/v8"
|
- "--platform=linux/arm64/v8"
|
||||||
- "--label=org.opencontainers.image.created={{.Date}}"
|
- "--label=org.opencontainers.image.created={{.Date}}"
|
||||||
|
@ -101,7 +103,7 @@ dockers:
|
||||||
image_templates:
|
image_templates:
|
||||||
- "superseriousbusiness/{{ .ProjectName }}:{{ .Version }}-armv6"
|
- "superseriousbusiness/{{ .ProjectName }}:{{ .Version }}-armv6"
|
||||||
- "superseriousbusiness/{{ .ProjectName }}:latest-armv6"
|
- "superseriousbusiness/{{ .ProjectName }}:latest-armv6"
|
||||||
- "superseriousbusiness/{{ .ProjectName }}:snapshot-armv6"
|
- "{{ if .IsSnapshot }}superseriousbusiness/{{ .ProjectName }}:snapshot-armv6{{ end }}"
|
||||||
build_flag_templates:
|
build_flag_templates:
|
||||||
- "--platform=linux/arm/v6"
|
- "--platform=linux/arm/v6"
|
||||||
- "--label=org.opencontainers.image.created={{.Date}}"
|
- "--label=org.opencontainers.image.created={{.Date}}"
|
||||||
|
@ -122,7 +124,7 @@ dockers:
|
||||||
image_templates:
|
image_templates:
|
||||||
- "superseriousbusiness/{{ .ProjectName }}:{{ .Version }}-armv7"
|
- "superseriousbusiness/{{ .ProjectName }}:{{ .Version }}-armv7"
|
||||||
- "superseriousbusiness/{{ .ProjectName }}:latest-armv7"
|
- "superseriousbusiness/{{ .ProjectName }}:latest-armv7"
|
||||||
- "superseriousbusiness/{{ .ProjectName }}:snapshot-armv7"
|
- "{{ if .IsSnapshot }}superseriousbusiness/{{ .ProjectName }}:snapshot-armv7{{ end }}"
|
||||||
build_flag_templates:
|
build_flag_templates:
|
||||||
- "--platform=linux/arm/v7"
|
- "--platform=linux/arm/v7"
|
||||||
- "--label=org.opencontainers.image.created={{.Date}}"
|
- "--label=org.opencontainers.image.created={{.Date}}"
|
||||||
|
@ -148,7 +150,7 @@ docker_manifests:
|
||||||
- superseriousbusiness/{{ .ProjectName }}:latest-arm64v8
|
- superseriousbusiness/{{ .ProjectName }}:latest-arm64v8
|
||||||
- superseriousbusiness/{{ .ProjectName }}:latest-armv6
|
- superseriousbusiness/{{ .ProjectName }}:latest-armv6
|
||||||
- superseriousbusiness/{{ .ProjectName }}:latest-armv7
|
- superseriousbusiness/{{ .ProjectName }}:latest-armv7
|
||||||
- name_template: superseriousbusiness/{{ .ProjectName }}:snapshot
|
- name_template: "{{ if .IsSnapshot }}superseriousbusiness/{{ .ProjectName }}:snapshot{{ end }}"
|
||||||
image_templates:
|
image_templates:
|
||||||
- superseriousbusiness/{{ .ProjectName }}:snapshot-amd64
|
- superseriousbusiness/{{ .ProjectName }}:snapshot-amd64
|
||||||
- superseriousbusiness/{{ .ProjectName }}:snapshot-arm64v8
|
- superseriousbusiness/{{ .ProjectName }}:snapshot-arm64v8
|
||||||
|
|
|
@ -6,9 +6,9 @@
|
||||||
version: 2
|
version: 2
|
||||||
|
|
||||||
build:
|
build:
|
||||||
os: "ubuntu-20.04"
|
os: ubuntu-22.04
|
||||||
tools:
|
tools:
|
||||||
python: "mambaforge-4.10" # https://docs.readthedocs.io/en/stable/guides/conda.html#making-builds-faster-with-mamba
|
python: "mambaforge-22.9" # https://docs.readthedocs.io/en/stable/guides/conda.html#making-builds-faster-with-mamba
|
||||||
|
|
||||||
mkdocs:
|
mkdocs:
|
||||||
configuration: "mkdocs.yml"
|
configuration: "mkdocs.yml"
|
||||||
|
|
13
.vscode/settings.json
vendored
|
@ -10,5 +10,14 @@
|
||||||
},
|
},
|
||||||
"eslint.workingDirectories": ["web/source"],
|
"eslint.workingDirectories": ["web/source"],
|
||||||
"eslint.lintTask.enable": true,
|
"eslint.lintTask.enable": true,
|
||||||
"eslint.lintTask.options": "${workspaceFolder}/web/source"
|
"eslint.lintTask.options": "${workspaceFolder}/web/source",
|
||||||
}
|
"eslint.validate": [
|
||||||
|
"javascript",
|
||||||
|
"javascriptreact",
|
||||||
|
"typescript",
|
||||||
|
"typescriptreact"
|
||||||
|
],
|
||||||
|
"editor.codeActionsOnSave": {
|
||||||
|
"source.fixAll.eslint": "explicit"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -110,7 +110,13 @@ When adding a new page, you need to include it in the [`mkdocs.yml`](mkdocs.yml)
|
||||||
|
|
||||||
If you don't use Conda, you can read the `docs/environment.yml` to see which dependencies are required and `pip install` them manually. It's advisable to do this in a virtual environment, which you can create with something like `python3 -m venv /path-to/store-the-venv`. You can then call `/path-to/store-the-venv/bin/pip`, `/path-to/store-the-venv/bin/mkdocs` etc.
|
If you don't use Conda, you can read the `docs/environment.yml` to see which dependencies are required and `pip install` them manually. It's advisable to do this in a virtual environment, which you can create with something like `python3 -m venv /path-to/store-the-venv`. You can then call `/path-to/store-the-venv/bin/pip`, `/path-to/store-the-venv/bin/mkdocs` etc.
|
||||||
|
|
||||||
In order to upgrade dependencies, use `conda update --update-all` in the activated environment. You can then update the `environment.yml` with `conda env export --from-history -f ./docs/environment.yml`, though you'll need to fix the `channels`. Beware that `conda env export` will also drop the `pip` dependencies, so make sure to add those back.
|
In order to upgrade dependencies, use `conda update --update-all` in the activated environment. You can then update the `environment.yml` with:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
conda env export -n gotosocial-docs --from-history --override-channels -c conda-forge -c nodefaults -f ./docs/environment.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
Beware that `conda env export` will add a `prefix` entry to the environment.yml file, and drop the `pip` dependencies, so make sure to remove the prefix and add the `pip` dependencies back in.
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
|
|
|
@ -2,11 +2,11 @@
|
||||||
# Dockerfile reference: https://docs.docker.com/engine/reference/builder/
|
# Dockerfile reference: https://docs.docker.com/engine/reference/builder/
|
||||||
|
|
||||||
# stage 1: generate up-to-date swagger.yaml to put in the final container
|
# stage 1: generate up-to-date swagger.yaml to put in the final container
|
||||||
FROM --platform=${BUILDPLATFORM} golang:1.21-alpine AS swagger
|
FROM --platform=${BUILDPLATFORM} golang:1.22-alpine AS swagger
|
||||||
|
|
||||||
RUN \
|
RUN \
|
||||||
### Installs goswagger for building swagger definitions inside this container
|
### Installs goswagger for building swagger definitions inside this container
|
||||||
go install "github.com/go-swagger/go-swagger/cmd/swagger@v0.30.5" && \
|
go install "github.com/go-swagger/go-swagger/cmd/swagger@c46c303aaa02" && \
|
||||||
# Makes swagger executable
|
# Makes swagger executable
|
||||||
chmod +x /go/bin/swagger
|
chmod +x /go/bin/swagger
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ RUN yarn --cwd ./web/source install && \
|
||||||
rm -rf ./web/source
|
rm -rf ./web/source
|
||||||
|
|
||||||
# stage 3: build the executor container
|
# stage 3: build the executor container
|
||||||
FROM --platform=${TARGETPLATFORM} alpine:3.17.2 as executor
|
FROM --platform=${TARGETPLATFORM} alpine:3.19.1 as executor
|
||||||
|
|
||||||
# switch to non-root user:group for GtS
|
# switch to non-root user:group for GtS
|
||||||
USER 1000:1000
|
USER 1000:1000
|
||||||
|
|
172
README.md
|
@ -1,3 +1,4 @@
|
||||||
|
<!--overview-start-->
|
||||||
# GoToSocial <!-- omit in toc -->
|
# GoToSocial <!-- omit in toc -->
|
||||||
|
|
||||||
GoToSocial is an [ActivityPub](https://activitypub.rocks/) social network server, written in Golang.
|
GoToSocial is an [ActivityPub](https://activitypub.rocks/) social network server, written in Golang.
|
||||||
|
@ -5,16 +6,19 @@ GoToSocial is an [ActivityPub](https://activitypub.rocks/) social network server
|
||||||
With GoToSocial, you can keep in touch with your friends, post, read, and share images and articles. All without being tracked or advertised to!
|
With GoToSocial, you can keep in touch with your friends, post, read, and share images and articles. All without being tracked or advertised to!
|
||||||
|
|
||||||
<p align="middle">
|
<p align="middle">
|
||||||
<img src="./docs/assets/sloth.png" width="300"/>
|
<img src="https://raw.githubusercontent.com/superseriousbusiness/gotosocial/main/docs/assets/sloth.png" width="300"/>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
**GoToSocial is still [ALPHA SOFTWARE](https://en.wikipedia.org/wiki/Software_release_life_cycle#Alpha)**. It is already deployable and useable, and it federates cleanly with many other Fediverse servers (not yet all). However, many things are not yet implemented, and there are plenty of bugs! We foresee entering beta around the beginning of 2024.
|
**GoToSocial is still [ALPHA SOFTWARE](https://en.wikipedia.org/wiki/Software_release_life_cycle#Alpha)**. It is already deployable and useable, and it federates cleanly with many other Fediverse servers (not yet all). However, many things are not yet implemented, and there are plenty of bugs! We foresee entering beta around the beginning of 2024.
|
||||||
|
|
||||||
Documentation is at [docs.gotosocial.org](https://docs.gotosocial.org). You can skip straight to the API documentation [here](https://docs.gotosocial.org/en/latest/api/swagger/). To build from source, check the [CONTRIBUTING.md](./CONTRIBUTING.md) file.
|
Documentation is at [docs.gotosocial.org](https://docs.gotosocial.org). You can skip straight to the API documentation [here](https://docs.gotosocial.org/en/latest/api/swagger/).
|
||||||
|
|
||||||
|
To build from source, check the [CONTRIBUTING.md](https://github.com/superseriousbusiness/gotosocial/blob/main/CONTRIBUTING.md) file.
|
||||||
|
|
||||||
Here's a screenshot of the instance landing page!
|
Here's a screenshot of the instance landing page!
|
||||||
|
|
||||||
![Screenshot of the landing page for the GoToSocial instance goblin.technology. It shows basic information about the instance; number of users and posts etc.](./docs/assets/instancesplash.png)
|
![Screenshot of the landing page for the GoToSocial instance goblin.technology. It shows basic information about the instance; number of users and posts etc.](https://raw.githubusercontent.com/superseriousbusiness/gotosocial/main/docs/assets/instancesplash.png)
|
||||||
|
<!--overview-end-->
|
||||||
|
|
||||||
## Table of Contents <!-- omit in toc -->
|
## Table of Contents <!-- omit in toc -->
|
||||||
|
|
||||||
|
@ -24,27 +28,21 @@ Here's a screenshot of the instance landing page!
|
||||||
- [Features](#features)
|
- [Features](#features)
|
||||||
- [Mastodon API compatibility](#mastodon-api-compatibility)
|
- [Mastodon API compatibility](#mastodon-api-compatibility)
|
||||||
- [Granular post settings](#granular-post-settings)
|
- [Granular post settings](#granular-post-settings)
|
||||||
- [Customizability for admins](#customizability-for-admins)
|
- [Customizability](#customizability)
|
||||||
- [Easy to run](#easy-to-run)
|
- [Easy to run](#easy-to-run)
|
||||||
- [Safety + security features](#safety--security-features)
|
- [Safety + security features](#safety--security-features)
|
||||||
- [Various federation modes](#various-federation-modes)
|
- [Various federation modes](#various-federation-modes)
|
||||||
- [OIDC integration](#oidc-integration)
|
- [OIDC integration](#oidc-integration)
|
||||||
- [Backend-first design](#backend-first-design)
|
- [Backend-first design](#backend-first-design)
|
||||||
- [Wishlist](#wishlist)
|
|
||||||
- [Getting Started](#getting-started)
|
|
||||||
- [Third-Party Packaging](#third-party-packaging)
|
|
||||||
- [Distribution packaging](#distribution-packaging)
|
|
||||||
- [Self-hosting](#self-hosting)
|
|
||||||
- [Known Issues](#known-issues)
|
- [Known Issues](#known-issues)
|
||||||
- [Client App Issues](#client-app-issues)
|
- [Getting Started](#getting-started)
|
||||||
- [Federation Issues](#federation-issues)
|
- [Stable Releases](#stable-releases)
|
||||||
- [Contributing](#contributing)
|
- [Snapshot Releases](#snapshot-releases)
|
||||||
- [Building](#building)
|
|
||||||
- [Releases](#releases)
|
|
||||||
- [Stable](#stable)
|
|
||||||
- [Snapshots](#snapshots)
|
|
||||||
- [Docker](#docker)
|
- [Docker](#docker)
|
||||||
- [Binary release .tar.gz](#binary-release-targz)
|
- [Binary release .tar.gz](#binary-release-targz)
|
||||||
|
- [From Source](#from-source)
|
||||||
|
- [Third-party Packaging](#third-party-packaging)
|
||||||
|
- [Contributing](#contributing)
|
||||||
- [Contact](#contact)
|
- [Contact](#contact)
|
||||||
- [Credits](#credits)
|
- [Credits](#credits)
|
||||||
- [Libraries](#libraries)
|
- [Libraries](#libraries)
|
||||||
|
@ -56,13 +54,14 @@ Here's a screenshot of the instance landing page!
|
||||||
- [NLnet](#nlnet)
|
- [NLnet](#nlnet)
|
||||||
- [License](#license)
|
- [License](#license)
|
||||||
|
|
||||||
|
<!--body-1-start-->
|
||||||
## What is GoToSocial?
|
## What is GoToSocial?
|
||||||
|
|
||||||
GoToSocial provides a lightweight, customizable, and safety-focused entryway into the [Fediverse](https://en.wikipedia.org/wiki/Fediverse), and is comparable to (but distinct from) existing projects such as [Mastodon](https://joinmastodon.org/), [Pleroma](https://pleroma.social/), [Friendica](https://friendi.ca), and [PixelFed](https://pixelfed.org/).
|
GoToSocial provides a lightweight, customizable, and safety-focused entryway into the [Fediverse](https://en.wikipedia.org/wiki/Fediverse), and is comparable to (but distinct from) existing projects such as [Mastodon](https://joinmastodon.org/), [Pleroma](https://pleroma.social/), [Friendica](https://friendi.ca), and [PixelFed](https://pixelfed.org/).
|
||||||
|
|
||||||
If you've ever used something like Twitter or Tumblr (or even Myspace!) GoToSocial will probably feel familiar to you: You can follow people and have followers, you make posts which people can favourite and reply to and share, and you scroll through posts from people you follow using a timeline. You can write long posts or short posts, or just post images, it's up to you. You can also, of course, block people or otherwise limit interactions that you don't want by posting just to your friends.
|
If you've ever used something like Twitter or Tumblr (or even Myspace!) GoToSocial will probably feel familiar to you: You can follow people and have followers, you make posts which people can favourite and reply to and share, and you scroll through posts from people you follow using a timeline. You can write long posts or short posts, or just post images, it's up to you. You can also, of course, block people or otherwise limit interactions that you don't want by posting just to your friends.
|
||||||
|
|
||||||
![Screenshot of the web view of a profile in GoToSocial, showing header and avatar, bio, and numbers of followers/following.](./docs/assets/profile1.png)
|
![Screenshot of the web view of a profile in GoToSocial, showing header and avatar, bio, and numbers of followers/following.](https://raw.githubusercontent.com/superseriousbusiness/gotosocial/main/docs/assets/profile1.png)
|
||||||
|
|
||||||
**GoToSocial does NOT use recommendation algorithms or collect data about you to suggest content or 'improve your experience'**. The timeline is chronological: whatever you see at the top of your timeline is there because it's *just been posted*, not because it's been selected as interesting (or controversial) based on your personal profile.
|
**GoToSocial does NOT use recommendation algorithms or collect data about you to suggest content or 'improve your experience'**. The timeline is chronological: whatever you see at the top of your timeline is there because it's *just been posted*, not because it's been selected as interesting (or controversial) based on your personal profile.
|
||||||
|
|
||||||
|
@ -74,7 +73,7 @@ GoToSocial doesn't claim to be *better* than any other application, but it offer
|
||||||
|
|
||||||
Because GoToSocial uses [ActivityPub](https://activitypub.rocks/), you can hang out not just with people on your home server, but with people all over the [Fediverse](https://en.wikipedia.org/wiki/Fediverse), seamlessly.
|
Because GoToSocial uses [ActivityPub](https://activitypub.rocks/), you can hang out not just with people on your home server, but with people all over the [Fediverse](https://en.wikipedia.org/wiki/Fediverse), seamlessly.
|
||||||
|
|
||||||
![the activitypub logo](docs/assets/ap_logo.svg)
|
![the activitypub logo](https://raw.githubusercontent.com/superseriousbusiness/gotosocial/main/docs/assets/ap_logo.svg)
|
||||||
|
|
||||||
Federation means that your home server is part of a network of servers all over the world that all communicate using the same protocol. Your data is no longer centralized on one company's servers, but resides on your own server and is shared — as you see fit — across a resilient web of servers run by other people.
|
Federation means that your home server is part of a network of servers all over the world that all communicate using the same protocol. Your data is no longer centralized on one company's servers, but resides on your own server and is shared — as you see fit — across a resilient web of servers run by other people.
|
||||||
|
|
||||||
|
@ -88,7 +87,9 @@ This project sprang up in February/March 2021 out of a dissatisfaction with the
|
||||||
|
|
||||||
It began as a solo project, and then picked up steam as more developers became interested and jumped on.
|
It began as a solo project, and then picked up steam as more developers became interested and jumped on.
|
||||||
|
|
||||||
For a detailed view on what's implemented and what's not, and progress made towards [beta release](https://en.wikipedia.org/wiki/Software_release_life_cycle#Beta), please see [the roadmap document](./ROADMAP.md). The [FAQ](docs/faq.md) contains a higher-level overview.
|
For a detailed view on what's implemented and what's not, and progress made towards [beta release](https://en.wikipedia.org/wiki/Software_release_life_cycle#Beta), please see [the roadmap document](https://github.com/superseriousbusiness/gotosocial/blob/main/ROADMAP.md).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
|
@ -117,18 +118,17 @@ It also allows you to customize how people interact with your posts:
|
||||||
- 'Likeable' toggle.
|
- 'Likeable' toggle.
|
||||||
- 'Replyable' toggle.
|
- 'Replyable' toggle.
|
||||||
|
|
||||||
### Customizability for admins
|
### Customizability
|
||||||
|
|
||||||
Plenty of [config options](./example/config.yaml) for admins to play around with, including:
|
Users can [choose from a variety of fun themes](https://docs.gotosocial.org/en/latest/user_guide/settings/#select-theme) for their profile, or even write their own [custom CSS](https://docs.gotosocial.org/en/latest/user_guide/settings/#custom-css).
|
||||||
|
|
||||||
- Easily adjustable post length.
|
Plenty of [config options](https://github.com/superseriousbusiness/gotosocial/blob/main/example/config.yaml) for admins to play around with too.
|
||||||
- Media upload size settings.
|
|
||||||
|
|
||||||
### Easy to run
|
### Easy to run
|
||||||
|
|
||||||
No external dependencies apart from a database (or just use SQLite!). Simply download the binary + assets (or Docker container), and run.
|
No external dependencies apart from a database (or just use SQLite!). Simply download the binary + assets (or Docker container), and run.
|
||||||
|
|
||||||
GoToSocial plays nice with lower-powered machines like Raspberry Pi, old laptops and tiny $5/month VPSes.
|
GoToSocial uses only about 150-250MiB of RAM, so it plays nice with single-board computers, old laptops and tiny $5/month VPSes.
|
||||||
|
|
||||||
### Safety + security features
|
### Safety + security features
|
||||||
|
|
||||||
|
@ -159,73 +159,27 @@ Instead, like Matrix.org's [Synapse](https://github.com/matrix-org/synapse) proj
|
||||||
|
|
||||||
On top of this API, web developers are encouraged to build any front-end implementation or mobile application that they wish, whether Tumblr-like, Facebook-like, Twitter-like, or something else entirely.
|
On top of this API, web developers are encouraged to build any front-end implementation or mobile application that they wish, whether Tumblr-like, Facebook-like, Twitter-like, or something else entirely.
|
||||||
|
|
||||||
## Wishlist
|
---
|
||||||
|
|
||||||
These cool things will be implemented if time allows (because we really want them):
|
|
||||||
|
|
||||||
- **Groups** and group posting!
|
|
||||||
- Reputation-based 'slow' federation.
|
|
||||||
- Community decision-making for federation and moderation actions.
|
|
||||||
- User-selectable custom templates for rendering public posts:
|
|
||||||
- Twitter-style
|
|
||||||
- Blogpost
|
|
||||||
- Gallery
|
|
||||||
- Etc.
|
|
||||||
|
|
||||||
## Getting Started
|
|
||||||
|
|
||||||
All docs for installation and configuration are hosted at [docs.gotosocial.org](https://docs.gotosocial.org).
|
|
||||||
|
|
||||||
## Third-Party Packaging
|
|
||||||
|
|
||||||
Thank you so much to the cool people who have put time and energy into packaging GoToSocial!
|
|
||||||
|
|
||||||
### Distribution packaging
|
|
||||||
|
|
||||||
These packages are not maintained by GoToSocial, so please direct questions and issues to the repository maintainers (and donate to them!).
|
|
||||||
|
|
||||||
[![Packaging status](https://repology.org/badge/vertical-allrepos/gotosocial.svg)](https://repology.org/project/gotosocial/versions)
|
|
||||||
|
|
||||||
### Self-hosting
|
|
||||||
|
|
||||||
You can deploy your own instance of GoToSocial with the help of:
|
|
||||||
|
|
||||||
- [YunoHost GoToSocial Packaging](https://github.com/YunoHost-Apps/gotosocial_ynh) by [OniriCorpe](https://github.com/OniriCorpe).
|
|
||||||
- [Ansible Playbook (MASH)](https://github.com/mother-of-all-self-hosting/mash-playbook): The playbook supports a many services, including GoToSocial. [Documentation](https://github.com/mother-of-all-self-hosting/mash-playbook/blob/main/docs/services/gotosocial.md)
|
|
||||||
- GoToSocial Helm Charts:
|
|
||||||
- [GoToSocial Helm Chart](https://github.com/fSocietySocial/charts/tree/main/charts/gotosocial) by [0hlov3](https://github.com/0hlov3).
|
|
||||||
|
|
||||||
## Known Issues
|
## Known Issues
|
||||||
|
|
||||||
Since GoToSocial is still in alpha, there are plenty of bugs. We use [GitHub issues](https://github.com/superseriousbusiness/gotosocial/issues?q=is%3Aissue+is%3Aopen+label%3Abug) to track these. The [FAQ](docs/faq.md) also describes some of the features that haven't been implemented yet.
|
Since GoToSocial is still in alpha, there are plenty of bugs. We use [GitHub issues](https://github.com/superseriousbusiness/gotosocial/issues?q=is%3Aissue+is%3Aopen+label%3Abug) to track these.
|
||||||
|
|
||||||
### Client App Issues
|
|
||||||
|
|
||||||
GoToSocial works great with Tusky and Semaphore, but some other client applications still need work or have issues connecting to GoToSocial. We're tracking them [right here](https://github.com/superseriousbusiness/gotosocial/projects/5). It's our goal to make any app that's compatible with the Mastodon API work seamlessly with GoToSocial.
|
|
||||||
|
|
||||||
### Federation Issues
|
|
||||||
|
|
||||||
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://github.com/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 [in this project](https://github.com/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.
|
||||||
|
|
||||||
## Contributing
|
---
|
||||||
|
|
||||||
You would like to contribute to GtS? Great! ❤️❤️❤️ Check out the issues page to see if there's anything you intend to jump in on, and read the [CONTRIBUTING.md](./CONTRIBUTING.md) file for guidelines and setting up your dev environment.
|
## Getting Started
|
||||||
|
|
||||||
## Building
|
|
||||||
|
|
||||||
Instructions for building GoToSocial from source are in the [CONTRIBUTING.md](./CONTRIBUTING.md) file.
|
|
||||||
|
|
||||||
## Releases
|
|
||||||
|
|
||||||
### Stable
|
|
||||||
|
|
||||||
We package our stable releases for both binary builds and Docker containers, so that you don't have to build from source yourself.
|
|
||||||
|
|
||||||
Check our [releases page](https://github.com/superseriousbusiness/gotosocial/releases) and our [getting started](https://docs.gotosocial.org/en/latest/getting_started/) documentation.
|
Check our [releases page](https://github.com/superseriousbusiness/gotosocial/releases) and our [getting started](https://docs.gotosocial.org/en/latest/getting_started/) documentation.
|
||||||
|
|
||||||
|
### Stable Releases
|
||||||
|
|
||||||
|
We package our stable releases for both binary builds and Docker containers, so that you don't have to build from source yourself.
|
||||||
|
|
||||||
The Docker image `superseriousbusiness/gotosocial:latest` will always correspond to the latest stable release. Since this tag is overwritten frequently, you may want to use Docker CLI flag `--pull always` to ensure that you always have the most up-to-date image every time you run using this tag. Alternatively, run `docker pull superseriousbusiness/gotosocial:latest` manually just before use.
|
The Docker image `superseriousbusiness/gotosocial:latest` will always correspond to the latest stable release. Since this tag is overwritten frequently, you may want to use Docker CLI flag `--pull always` to ensure that you always have the most up-to-date image every time you run using this tag. Alternatively, run `docker pull superseriousbusiness/gotosocial:latest` manually just before use.
|
||||||
|
|
||||||
### Snapshots
|
### Snapshot Releases
|
||||||
|
|
||||||
We also make snapshot builds every time something is merged into the main branch, so you can run from whatever code is on main if you wish.
|
We also make snapshot builds every time something is merged into the main branch, so you can run from whatever code is on main if you wish.
|
||||||
|
|
||||||
|
@ -241,13 +195,42 @@ To run from main using a binary release, download the appropriate .tar.gz file f
|
||||||
|
|
||||||
Snapshot binary releases in the S3 bucket are keyed by Github commit hash. To get the latest one, sort by Last Modified, or check out the list of commits [here](https://github.com/superseriousbusiness/gotosocial/commits/main), copy the SHA of the latest one, and paste it in the Minio console filter. Snapshot binary releases are expired after 28 days, to keep our hosting costs down.
|
Snapshot binary releases in the S3 bucket are keyed by Github commit hash. To get the latest one, sort by Last Modified, or check out the list of commits [here](https://github.com/superseriousbusiness/gotosocial/commits/main), copy the SHA of the latest one, and paste it in the Minio console filter. Snapshot binary releases are expired after 28 days, to keep our hosting costs down.
|
||||||
|
|
||||||
|
### From Source
|
||||||
|
|
||||||
|
Instructions for building GoToSocial from source are in the [CONTRIBUTING.md](https://github.com/superseriousbusiness/gotosocial/blob/main/CONTRIBUTING.md) file.
|
||||||
|
|
||||||
|
### Third-party Packaging
|
||||||
|
|
||||||
|
Thank you so much to the cool people who have put time and energy into packaging GoToSocial!
|
||||||
|
|
||||||
|
These packages are not maintained by GoToSocial, so please direct questions and issues to the repository maintainers (and donate to them!).
|
||||||
|
|
||||||
|
[![Packaging status](https://repology.org/badge/vertical-allrepos/gotosocial.svg)](https://repology.org/project/gotosocial/versions)
|
||||||
|
|
||||||
|
You can also deploy your own instance of GoToSocial with the help of:
|
||||||
|
|
||||||
|
- [YunoHost GoToSocial Packaging](https://github.com/YunoHost-Apps/gotosocial_ynh) by [OniriCorpe](https://github.com/OniriCorpe).
|
||||||
|
- [Ansible Playbook (MASH)](https://github.com/mother-of-all-self-hosting/mash-playbook): The playbook supports a many services, including GoToSocial. [Documentation](https://github.com/mother-of-all-self-hosting/mash-playbook/blob/main/docs/services/gotosocial.md)
|
||||||
|
- [GoToSocial Helm Chart](https://github.com/fSocietySocial/charts/tree/main/charts/gotosocial) by [0hlov3](https://github.com/0hlov3).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
You would like to contribute to GtS? Great! ❤️❤️❤️ Check out the issues page to see if there's anything you intend to jump in on, and read the [CONTRIBUTING.md](https://github.com/superseriousbusiness/gotosocial/blob/main/CONTRIBUTING.md) file for guidelines and setting up your dev environment.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Contact
|
## Contact
|
||||||
|
|
||||||
For questions and comments, you can [join our Matrix space](https://matrix.to/#/#gotosocial-space:superseriousbusiness.org) at `#gotosocial-space:superseriousbusiness.org`. This is the quickest way to reach the devs. You can also mail [admin@gotosocial.org](mailto:admin@gotosocial.org).
|
For questions and comments, you can [join our Matrix space](https://matrix.to/#/#gotosocial-space:superseriousbusiness.org) at `#gotosocial-space:superseriousbusiness.org`. This is the quickest way to reach the devs. You can also mail [admin@gotosocial.org](mailto:admin@gotosocial.org).
|
||||||
|
|
||||||
For bugs and feature requests, please check to see if there's [already an issue](https://github.com/superseriousbusiness/gotosocial/issues), and if not, open one or use one of the above channels to make a request (if you don't have a Github account).
|
For bugs and feature requests, please check to see if there's [already an issue](https://github.com/superseriousbusiness/gotosocial/issues), and if not, open one or use one of the above channels to make a request (if you don't have a Github account).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Credits
|
## Credits
|
||||||
|
<!--body-1-end-->
|
||||||
|
|
||||||
### Libraries
|
### Libraries
|
||||||
|
|
||||||
|
@ -277,13 +260,14 @@ The following open source libraries, frameworks, and tools are used by GoToSocia
|
||||||
- [gruf/go-cache](https://codeberg.org/gruf/go-cache); LRU and TTL caches. [MIT License](https://spdx.org/licenses/MIT.html).
|
- [gruf/go-cache](https://codeberg.org/gruf/go-cache); LRU and TTL caches. [MIT License](https://spdx.org/licenses/MIT.html).
|
||||||
- [gruf/go-debug](https://codeberg.org/gruf/go-debug); debug build tag. [MIT License](https://spdx.org/licenses/MIT.html).
|
- [gruf/go-debug](https://codeberg.org/gruf/go-debug); debug build tag. [MIT License](https://spdx.org/licenses/MIT.html).
|
||||||
- [gruf/go-errors](https://codeberg.org/gruf/go-errors); context-like error w/ value wrapping [MIT License](https://spdx.org/licenses/MIT.html).
|
- [gruf/go-errors](https://codeberg.org/gruf/go-errors); context-like error w/ value wrapping [MIT License](https://spdx.org/licenses/MIT.html).
|
||||||
- [gruf/go-fastcopy](https://codeberg.org/gruf/go-fastcopy); performant pooled I/O copying [MIT License](https://spdx.org/licenses/MIT.html).
|
- [gruf/go-fastcopy](https://codeberg.org/gruf/go-fastcopy); performant (buffer pooled) I/O copying [MIT License](https://spdx.org/licenses/MIT.html).
|
||||||
- [gruf/go-kv](https://codeberg.org/gruf/go-kv); log field formatting. [MIT License](https://spdx.org/licenses/MIT.html).
|
- [gruf/go-kv](https://codeberg.org/gruf/go-kv); log field formatting. [MIT License](https://spdx.org/licenses/MIT.html).
|
||||||
|
- [gruf/go-list](https://codeberg.org/gruf/go-list); generic doubly linked list. [MIT License](https://spdx.org/licenses/MIT.html).
|
||||||
- [gruf/go-mutexes](https://codeberg.org/gruf/go-mutexes); safemutex & mutex map. [MIT License](https://spdx.org/licenses/MIT.html).
|
- [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); workerpools and synchronization. [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-sched](https://codeberg.org/gruf/go-sched); task scheduler. [MIT License](https://spdx.org/licenses/MIT.html).
|
||||||
- [gruf/go-store](https://codeberg.org/gruf/go-store); file storage backend (local & s3). [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 w/ automated multiple indexing. [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).
|
||||||
- [h2non/filetype](https://github.com/h2non/filetype); filetype checking. [MIT License](https://spdx.org/licenses/MIT.html).
|
- [h2non/filetype](https://github.com/h2non/filetype); filetype checking. [MIT License](https://spdx.org/licenses/MIT.html).
|
||||||
- jackc:
|
- jackc:
|
||||||
- [jackc/pgx](https://github.com/jackc/pgconn); Postgres driver. [MIT License](https://spdx.org/licenses/MIT.html).
|
- [jackc/pgx](https://github.com/jackc/pgconn); Postgres driver. [MIT License](https://spdx.org/licenses/MIT.html).
|
||||||
|
@ -314,6 +298,7 @@ The following open source libraries, frameworks, and tools are used by GoToSocia
|
||||||
- [wagslane/go-password-validator](https://github.com/wagslane/go-password-validator); password strength validation. [MIT License](https://spdx.org/licenses/MIT.html).
|
- [wagslane/go-password-validator](https://github.com/wagslane/go-password-validator); password strength validation. [MIT License](https://spdx.org/licenses/MIT.html).
|
||||||
- [yuin/goldmark](https://github.com/yuin/goldmark); markdown parser. [MIT License](https://spdx.org/licenses/MIT.html).
|
- [yuin/goldmark](https://github.com/yuin/goldmark); markdown parser. [MIT License](https://spdx.org/licenses/MIT.html).
|
||||||
|
|
||||||
|
<!--body-2-start-->
|
||||||
### Image Attribution and Licensing
|
### Image Attribution and Licensing
|
||||||
|
|
||||||
Sloth logo by [Anna Abramek](https://abramek.art/).
|
Sloth logo by [Anna Abramek](https://abramek.art/).
|
||||||
|
@ -322,9 +307,9 @@ Sloth logo by [Anna Abramek](https://abramek.art/).
|
||||||
|
|
||||||
The Creative Commons Attribution-ShareAlike 4.0 International License license applies specifically to the following files and subdirectories of this repository:
|
The Creative Commons Attribution-ShareAlike 4.0 International License license applies specifically to the following files and subdirectories of this repository:
|
||||||
|
|
||||||
- [sloth logo png](./web/assets/logo.png)
|
- [sloth logo png](https://github.com/superseriousbusiness/gotosocial/blob/main/web/assets/logo.png)
|
||||||
- [sloth logo svg](./web/assets/logo.svg)
|
- [sloth logo svg](https://github.com/superseriousbusiness/gotosocial/blob/main/web/assets/logo.svg)
|
||||||
- [all default avatars](./web/assets/default_avatars)
|
- [all default avatars](https://github.com/superseriousbusiness/gotosocial/blob/main/web/assets/default_avatars)
|
||||||
|
|
||||||
Under the terms of the license, you are free to:
|
Under the terms of the license, you are free to:
|
||||||
|
|
||||||
|
@ -339,7 +324,7 @@ In alphabetical order (... and order of smell):
|
||||||
- f0x \[[donate with liberapay](https://liberapay.com/f0x)\]
|
- f0x \[[donate with liberapay](https://liberapay.com/f0x)\]
|
||||||
- kim \[check out my code @ [codeberg](https://codeberg.org/gruf), or find me @ [@kim](https://k.iim.gay/@kim)\]
|
- kim \[check out my code @ [codeberg](https://codeberg.org/gruf), or find me @ [@kim](https://k.iim.gay/@kim)\]
|
||||||
- tobi \[[donate with liberapay](https://liberapay.com/GoToSocial/)\]
|
- tobi \[[donate with liberapay](https://liberapay.com/GoToSocial/)\]
|
||||||
- maloki \[[@maloki@goblin.technology](https://goblin.technology/@maloki)\]
|
- vyr
|
||||||
|
|
||||||
### Special Thanks
|
### Special Thanks
|
||||||
|
|
||||||
|
@ -347,6 +332,8 @@ A huge thank you to CJ from [go-fed](https://github.com/go-fed/activity): withou
|
||||||
|
|
||||||
Thanks to everyone who has used GtS, opened an issue, suggested something, given funding, and otherwise encouraged or supported the project!
|
Thanks to everyone who has used GtS, opened an issue, suggested something, given funding, and otherwise encouraged or supported the project!
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Sponsorship + Funding
|
## Sponsorship + Funding
|
||||||
|
|
||||||
**Please note: GoToSocial has NO CORPORATE SPONSORS and does not desire corporate sponsorship. In addition, we do not take donations from any of the following: adult websites, affiliate and review websites, casinos and gambling, insurance and financial products (credit), pharmacy products, SEO services and social media buying, VPN and proxy services, and essay writing services. Donations from such sources will be automatically rejected.**
|
**Please note: GoToSocial has NO CORPORATE SPONSORS and does not desire corporate sponsorship. In addition, we do not take donations from any of the following: adult websites, affiliate and review websites, casinos and gambling, insurance and financial products (credit), pharmacy products, SEO services and social media buying, VPN and proxy services, and essay writing services. Donations from such sources will be automatically rejected.**
|
||||||
|
@ -369,13 +356,17 @@ Crowdfunded donations to our OpenCollective and Liberapay accounts go towards pa
|
||||||
|
|
||||||
<img src="https://nlnet.nl/logo/NGI/NGIZero-green.hex.svg" width="75" alt="NGIZero logo"/>
|
<img src="https://nlnet.nl/logo/NGI/NGIZero-green.hex.svg" width="75" alt="NGIZero logo"/>
|
||||||
|
|
||||||
Combined with the above crowdfunding sources, 2023 Alpha development of GoToSocial is also funded by a 50,000 EUR grant from the [NGI0 Entrust Fund](https://nlnet.nl/entrust/), via [NLnet](https://nlnet.nl/). See [here](https://nlnet.nl/project/GoToSocial/#ack) for more details. The successful grant application is archived [here](https://github.com/superseriousbusiness/gotosocial/blob/main/archive/nlnet/2022-next-generation-internet-zero.md).
|
Combined with the above crowdfunding sources, 2023 Alpha development of GoToSocial was funded by a 50,000 EUR grant from the [NGI0 Entrust Fund](https://nlnet.nl/entrust/), via [NLnet](https://nlnet.nl/). See [here](https://nlnet.nl/project/GoToSocial/#ack) for more details. The successful grant application is archived [here](https://github.com/superseriousbusiness/gotosocial/blob/main/archive/nlnet/2022-next-generation-internet-zero.md).
|
||||||
|
|
||||||
|
2024 Beta development of GoToSocial is being funded by an additional 50,000 EUR grant from the [NGI0 Entrust Fund](https://nlnet.nl/entrust/), via [NLnet](https://nlnet.nl/).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
![the gnu AGPL logo](https://www.gnu.org/graphics/agplv3-155x51.png)
|
![the gnu AGPL logo](https://www.gnu.org/graphics/agplv3-155x51.png)
|
||||||
|
|
||||||
GoToSocial is free software, licensed under the [GNU AGPL v3 LICENSE](LICENSE). We encourage forking and changing the code, hacking around with it, and experimenting.
|
GoToSocial is free software, licensed under the [GNU AGPL v3 LICENSE](https://github.com/superseriousbusiness/gotosocial/blob/main/LICENSE). We encourage forking and changing the code, hacking around with it, and experimenting.
|
||||||
|
|
||||||
See [here](https://www.gnu.org/licenses/why-affero-gpl.html) for the differences between AGPL versus GPL licensing, and [here](https://www.gnu.org/licenses/gpl-faq.html) for FAQ's about GPL licenses, including the AGPL.
|
See [here](https://www.gnu.org/licenses/why-affero-gpl.html) for the differences between AGPL versus GPL licensing, and [here](https://www.gnu.org/licenses/gpl-faq.html) for FAQ's about GPL licenses, including the AGPL.
|
||||||
|
|
||||||
|
@ -385,4 +376,5 @@ If you modify the GoToSocial source code, and run that modified code in a way th
|
||||||
|
|
||||||
Copyright (C) GoToSocial Authors
|
Copyright (C) GoToSocial Authors
|
||||||
|
|
||||||
(I'm adding this here to take the crown of having the 1000th commit ~ kim)
|
<!--I'm adding this here to take the crown of having the 1000th commit ~ kim-->
|
||||||
|
<!--body-2-end-->
|
||||||
|
|
21
ROADMAP.md
|
@ -14,10 +14,10 @@ Big thank you to all of our [Open Collective](https://opencollective.com/gotosoc
|
||||||
- [Timeline](#timeline)
|
- [Timeline](#timeline)
|
||||||
- [Mid 2023](#mid-2023)
|
- [Mid 2023](#mid-2023)
|
||||||
- [Mid/late 2023](#midlate-2023)
|
- [Mid/late 2023](#midlate-2023)
|
||||||
- [Late 2023](#late-2023)
|
|
||||||
- [Early 2024](#early-2024)
|
- [Early 2024](#early-2024)
|
||||||
- [BETA Milestone](#beta-milestone)
|
- [BETA milestone](#beta-milestone)
|
||||||
- [Remainder 2024 - early 2025](#remainder-2024---early-2025)
|
- [Remainder 2024 - early 2025](#remainder-2024---early-2025)
|
||||||
|
- [Wishlist](#wishlist)
|
||||||
|
|
||||||
## Beta Aims
|
## Beta Aims
|
||||||
|
|
||||||
|
@ -56,8 +56,8 @@ What follows is a rough timeline of features that will be implemented on the roa
|
||||||
|
|
||||||
### Early 2024
|
### Early 2024
|
||||||
|
|
||||||
- [ ] **Move activity** -- use the ActivityPub `Move` activity to support migration of a user's profile across servers.
|
- [x] **Move activity** -- use the ActivityPub `Move` activity to support migration of a user's profile across servers.
|
||||||
- [ ] **Sign-up flow** -- allow users to submit a sign-up request to an instance; allow admins to moderate sign-up requests.
|
- [x] **Sign-up flow** -- allow users to submit a sign-up request to an instance; allow admins to moderate sign-up requests.
|
||||||
|
|
||||||
### BETA milestone
|
### BETA milestone
|
||||||
|
|
||||||
|
@ -77,3 +77,16 @@ These are provided in no specific order.
|
||||||
- [ ] **Moderation: Append content warning / mark-as-sensitive all content from an instance/account**.
|
- [ ] **Moderation: Append content warning / mark-as-sensitive all content from an instance/account**.
|
||||||
|
|
||||||
More tbd!
|
More tbd!
|
||||||
|
|
||||||
|
## Wishlist
|
||||||
|
|
||||||
|
These cool things will be implemented if time allows (because we really want them):
|
||||||
|
|
||||||
|
- **Groups** and group posting!
|
||||||
|
- Reputation-based 'slow' federation.
|
||||||
|
- Community decision-making for federation and moderation actions.
|
||||||
|
- User-selectable custom templates for rendering public posts:
|
||||||
|
- Twitter-style
|
||||||
|
- Blogpost
|
||||||
|
- Gallery
|
||||||
|
- Etc.
|
||||||
|
|
|
@ -30,6 +30,7 @@ import (
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/log"
|
"github.com/superseriousbusiness/gotosocial/internal/log"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/state"
|
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/validate"
|
"github.com/superseriousbusiness/gotosocial/internal/validate"
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
)
|
)
|
||||||
|
@ -38,7 +39,6 @@ func initState(ctx context.Context) (*state.State, error) {
|
||||||
var state state.State
|
var state state.State
|
||||||
state.Caches.Init()
|
state.Caches.Init()
|
||||||
state.Caches.Start()
|
state.Caches.Start()
|
||||||
state.Workers.Start()
|
|
||||||
|
|
||||||
// Set the state DB connection
|
// Set the state DB connection
|
||||||
dbConn, err := bundb.NewBunDBService(ctx, &state)
|
dbConn, err := bundb.NewBunDBService(ctx, &state)
|
||||||
|
@ -52,7 +52,6 @@ func initState(ctx context.Context) (*state.State, error) {
|
||||||
|
|
||||||
func stopState(state *state.State) error {
|
func stopState(state *state.State) error {
|
||||||
err := state.DB.Close()
|
err := state.DB.Close()
|
||||||
state.Workers.Stop()
|
|
||||||
state.Caches.Stop()
|
state.Caches.Stop()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -185,9 +184,13 @@ var Confirm action.GTSAction = func(ctx context.Context) error {
|
||||||
user.Approved = func() *bool { a := true; return &a }()
|
user.Approved = func() *bool { a := true; return &a }()
|
||||||
user.Email = user.UnconfirmedEmail
|
user.Email = user.UnconfirmedEmail
|
||||||
user.ConfirmedAt = time.Now()
|
user.ConfirmedAt = time.Now()
|
||||||
|
user.SignUpIP = nil
|
||||||
return state.DB.UpdateUser(
|
return state.DB.UpdateUser(
|
||||||
ctx, user,
|
ctx, user,
|
||||||
"approved", "email", "confirmed_at",
|
"approved",
|
||||||
|
"email",
|
||||||
|
"confirmed_at",
|
||||||
|
"sign_up_ip",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -294,7 +297,43 @@ var Disable action.GTSAction = func(ctx context.Context) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
user.Disabled = func() *bool { d := true; return &d }()
|
user.Disabled = util.Ptr(true)
|
||||||
|
return state.DB.UpdateUser(
|
||||||
|
ctx, user,
|
||||||
|
"disabled",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable sets Disabled to false on a user.
|
||||||
|
var Enable action.GTSAction = func(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
|
||||||
|
}
|
||||||
|
|
||||||
|
user.Disabled = util.Ptr(false)
|
||||||
return state.DB.UpdateUser(
|
return state.DB.UpdateUser(
|
||||||
ctx, user,
|
ctx, user,
|
||||||
"disabled",
|
"disabled",
|
||||||
|
|
|
@ -127,8 +127,6 @@ func setupList(ctx context.Context) (*list, error) {
|
||||||
state.Caches.Init()
|
state.Caches.Init()
|
||||||
state.Caches.Start()
|
state.Caches.Start()
|
||||||
|
|
||||||
state.Workers.Start()
|
|
||||||
|
|
||||||
dbService, err := bundb.NewBunDBService(ctx, &state)
|
dbService, err := bundb.NewBunDBService(ctx, &state)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error creating dbservice: %w", err)
|
return nil, fmt.Errorf("error creating dbservice: %w", err)
|
||||||
|
@ -148,7 +146,6 @@ func setupList(ctx context.Context) (*list, error) {
|
||||||
func (l *list) shutdown() error {
|
func (l *list) shutdown() error {
|
||||||
l.out.Flush()
|
l.out.Flush()
|
||||||
err := l.dbService.Close()
|
err := l.dbService.Close()
|
||||||
l.state.Workers.Stop()
|
|
||||||
l.state.Caches.Stop()
|
l.state.Caches.Stop()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,7 +44,10 @@ func setupPrune(ctx context.Context) (*prune, error) {
|
||||||
state.Caches.Init()
|
state.Caches.Init()
|
||||||
state.Caches.Start()
|
state.Caches.Start()
|
||||||
|
|
||||||
state.Workers.Start()
|
// Scheduler is required for the
|
||||||
|
// claner, but no other workers
|
||||||
|
// are needed for this CLI action.
|
||||||
|
state.Workers.StartScheduler()
|
||||||
|
|
||||||
dbService, err := bundb.NewBunDBService(ctx, &state)
|
dbService, err := bundb.NewBunDBService(ctx, &state)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -77,15 +80,11 @@ func setupPrune(ctx context.Context) (*prune, error) {
|
||||||
func (p *prune) shutdown() error {
|
func (p *prune) shutdown() error {
|
||||||
errs := gtserror.NewMultiError(2)
|
errs := gtserror.NewMultiError(2)
|
||||||
|
|
||||||
if err := p.storage.Close(); err != nil {
|
|
||||||
errs.Appendf("error closing storage backend: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := p.dbService.Close(); err != nil {
|
if err := p.dbService.Close(); err != nil {
|
||||||
errs.Appendf("error stopping database: %w", err)
|
errs.Appendf("error stopping database: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
p.state.Workers.Stop()
|
p.state.Workers.Scheduler.Stop()
|
||||||
p.state.Caches.Stop()
|
p.state.Caches.Stop()
|
||||||
|
|
||||||
return errs.Combine()
|
return errs.Combine()
|
||||||
|
|
|
@ -24,9 +24,11 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/KimMachineGun/automemlimit/memlimit"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/superseriousbusiness/gotosocial/cmd/gotosocial/action"
|
"github.com/superseriousbusiness/gotosocial/cmd/gotosocial/action"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||||
|
@ -35,6 +37,7 @@ import (
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/filter/spam"
|
"github.com/superseriousbusiness/gotosocial/internal/filter/spam"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/filter/visibility"
|
"github.com/superseriousbusiness/gotosocial/internal/filter/visibility"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/messages"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/metrics"
|
"github.com/superseriousbusiness/gotosocial/internal/metrics"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/middleware"
|
"github.com/superseriousbusiness/gotosocial/internal/middleware"
|
||||||
tlprocessor "github.com/superseriousbusiness/gotosocial/internal/processing/timeline"
|
tlprocessor "github.com/superseriousbusiness/gotosocial/internal/processing/timeline"
|
||||||
|
@ -47,7 +50,6 @@ import (
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/email"
|
"github.com/superseriousbusiness/gotosocial/internal/email"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/federation"
|
"github.com/superseriousbusiness/gotosocial/internal/federation"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/federation/federatingdb"
|
"github.com/superseriousbusiness/gotosocial/internal/federation/federatingdb"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gotosocial"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/httpclient"
|
"github.com/superseriousbusiness/gotosocial/internal/httpclient"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/log"
|
"github.com/superseriousbusiness/gotosocial/internal/log"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/media"
|
"github.com/superseriousbusiness/gotosocial/internal/media"
|
||||||
|
@ -60,63 +62,118 @@ import (
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/transport"
|
"github.com/superseriousbusiness/gotosocial/internal/transport"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/web"
|
"github.com/superseriousbusiness/gotosocial/internal/web"
|
||||||
|
|
||||||
// Inherit memory limit if set from cgroup
|
|
||||||
_ "github.com/KimMachineGun/automemlimit"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Start creates and starts a gotosocial server
|
// Start creates and starts a gotosocial server
|
||||||
var Start action.GTSAction = func(ctx context.Context) error {
|
var Start action.GTSAction = func(ctx context.Context) error {
|
||||||
if _, err := maxprocs.Set(maxprocs.Logger(nil)); err != nil {
|
if _, err := maxprocs.Set(maxprocs.Logger(nil)); err != nil {
|
||||||
log.Infof(ctx, "could not set CPU limits from cgroup: %s", err)
|
log.Warnf(ctx, "could not set CPU limits from cgroup: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var state state.State
|
if _, err := memlimit.SetGoMemLimitWithOpts(); err != nil {
|
||||||
|
if !strings.Contains(err.Error(), "cgroup mountpoint does not exist") {
|
||||||
|
log.Warnf(ctx, "could not set Memory limits from cgroup: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize caches
|
var (
|
||||||
state.Caches.Init()
|
// Define necessary core variables
|
||||||
state.Caches.Start()
|
// before anything so we can prepare
|
||||||
defer state.Caches.Stop()
|
// defer function for safe shutdown
|
||||||
|
// depending on what services were
|
||||||
|
// managed to be started.
|
||||||
|
|
||||||
// Initialize Tracing
|
state = new(state.State)
|
||||||
|
route *router.Router
|
||||||
|
)
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
// Stop caches with
|
||||||
|
// background tasks.
|
||||||
|
state.Caches.Stop()
|
||||||
|
|
||||||
|
if route != nil {
|
||||||
|
// We reached a point where the API router
|
||||||
|
// was created + setup. Ensure it gets stopped
|
||||||
|
// first to stop processing new information.
|
||||||
|
if err := route.Stop(); err != nil {
|
||||||
|
log.Errorf(ctx, "error stopping router: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop any currently running
|
||||||
|
// worker processes / scheduled
|
||||||
|
// tasks from being executed.
|
||||||
|
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 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 tracing (noop if not enabled).
|
||||||
if err := tracing.Initialize(); err != nil {
|
if err := tracing.Initialize(); err != nil {
|
||||||
return fmt.Errorf("error initializing tracing: %w", err)
|
return fmt.Errorf("error initializing tracing: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open connection to the database
|
// Initialize caches
|
||||||
dbService, err := bundb.NewBunDBService(ctx, &state)
|
state.Caches.Init()
|
||||||
|
state.Caches.Start()
|
||||||
|
|
||||||
|
// Open connection to the database now caches started.
|
||||||
|
dbService, err := bundb.NewBunDBService(ctx, state)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error creating dbservice: %s", err)
|
return fmt.Errorf("error creating dbservice: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the state DB connection
|
// Set DB on state.
|
||||||
state.DB = dbService
|
state.DB = dbService
|
||||||
|
|
||||||
|
// Ensure necessary database instance prerequisites exist.
|
||||||
if err := dbService.CreateInstanceAccount(ctx); err != nil {
|
if err := dbService.CreateInstanceAccount(ctx); err != nil {
|
||||||
return fmt.Errorf("error creating instance account: %s", err)
|
return fmt.Errorf("error creating instance account: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := dbService.CreateInstanceInstance(ctx); err != nil {
|
if err := dbService.CreateInstanceInstance(ctx); err != nil {
|
||||||
return fmt.Errorf("error creating instance instance: %s", err)
|
return fmt.Errorf("error creating instance instance: %s", err)
|
||||||
}
|
}
|
||||||
|
if err := dbService.CreateInstanceApplication(ctx); err != nil {
|
||||||
|
return fmt.Errorf("error creating instance application: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
// Get the instance account
|
// Get the instance account (we'll need this later).
|
||||||
// (we'll need this later).
|
|
||||||
instanceAccount, err := dbService.GetInstanceAccount(ctx, "")
|
instanceAccount, err := dbService.GetInstanceAccount(ctx, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error retrieving instance account: %w", err)
|
return fmt.Errorf("error retrieving instance account: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open the storage backend
|
// Open the storage backend according to config.
|
||||||
storage, err := gtsstorage.AutoConfig()
|
state.Storage, err = gtsstorage.AutoConfig()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error creating storage backend: %w", err)
|
return fmt.Errorf("error opening storage backend: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the state storage driver
|
// Prepare wrapped httpclient with config.
|
||||||
state.Storage = storage
|
|
||||||
|
|
||||||
// Build HTTP client
|
|
||||||
client := httpclient.New(httpclient.Config{
|
client := httpclient.New(httpclient.Config{
|
||||||
AllowRanges: config.MustParseIPPrefixes(config.GetHTTPClientAllowIPs()),
|
AllowRanges: config.MustParseIPPrefixes(config.GetHTTPClientAllowIPs()),
|
||||||
BlockRanges: config.MustParseIPPrefixes(config.GetHTTPClientBlockIPs()),
|
BlockRanges: config.MustParseIPPrefixes(config.GetHTTPClientBlockIPs()),
|
||||||
|
@ -124,31 +181,15 @@ var Start action.GTSAction = func(ctx context.Context) error {
|
||||||
TLSInsecureSkipVerify: config.GetHTTPClientTLSInsecureSkipVerify(),
|
TLSInsecureSkipVerify: config.GetHTTPClientTLSInsecureSkipVerify(),
|
||||||
})
|
})
|
||||||
|
|
||||||
// Initialize workers.
|
|
||||||
state.Workers.Start()
|
|
||||||
defer state.Workers.Stop()
|
|
||||||
|
|
||||||
// Add a task to the scheduler to sweep caches.
|
|
||||||
// Frequency = 1 * minute
|
|
||||||
// Threshold = 80% capacity
|
|
||||||
_ = state.Workers.Scheduler.AddRecurring(
|
|
||||||
"@cachesweep", // id
|
|
||||||
time.Time{}, // start
|
|
||||||
time.Minute, // freq
|
|
||||||
func(context.Context, time.Time) {
|
|
||||||
state.Caches.Sweep(60)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
// Build handlers used in later initializations.
|
// Build handlers used in later initializations.
|
||||||
mediaManager := media.NewManager(&state)
|
mediaManager := media.NewManager(state)
|
||||||
oauthServer := oauth.New(ctx, dbService)
|
oauthServer := oauth.New(ctx, dbService)
|
||||||
typeConverter := typeutils.NewConverter(&state)
|
typeConverter := typeutils.NewConverter(state)
|
||||||
visFilter := visibility.NewFilter(&state)
|
visFilter := visibility.NewFilter(state)
|
||||||
spamFilter := spam.NewFilter(&state)
|
spamFilter := spam.NewFilter(state)
|
||||||
federatingDB := federatingdb.New(&state, typeConverter, visFilter, spamFilter)
|
federatingDB := federatingdb.New(state, typeConverter, visFilter, spamFilter)
|
||||||
transportController := transport.NewController(&state, federatingDB, &federation.Clock{}, client)
|
transportController := transport.NewController(state, federatingDB, &federation.Clock{}, client)
|
||||||
federator := federation.NewFederator(&state, federatingDB, transportController, typeConverter, visFilter, mediaManager)
|
federator := federation.NewFederator(state, federatingDB, transportController, typeConverter, visFilter, mediaManager)
|
||||||
|
|
||||||
// Decide whether to create a noop email
|
// Decide whether to create a noop email
|
||||||
// sender (won't send emails) or a real one.
|
// sender (won't send emails) or a real one.
|
||||||
|
@ -167,50 +208,75 @@ var Start action.GTSAction = func(ctx context.Context) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize timelines.
|
// Initialize both home / list timelines.
|
||||||
state.Timelines.Home = timeline.NewManager(
|
state.Timelines.Home = timeline.NewManager(
|
||||||
tlprocessor.HomeTimelineGrab(&state),
|
tlprocessor.HomeTimelineGrab(state),
|
||||||
tlprocessor.HomeTimelineFilter(&state, visFilter),
|
tlprocessor.HomeTimelineFilter(state, visFilter),
|
||||||
tlprocessor.HomeTimelineStatusPrepare(&state, typeConverter),
|
tlprocessor.HomeTimelineStatusPrepare(state, typeConverter),
|
||||||
tlprocessor.SkipInsert(),
|
tlprocessor.SkipInsert(),
|
||||||
)
|
)
|
||||||
if err := state.Timelines.Home.Start(); err != nil {
|
if err := state.Timelines.Home.Start(); err != nil {
|
||||||
return fmt.Errorf("error starting home timeline: %s", err)
|
return fmt.Errorf("error starting home timeline: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
state.Timelines.List = timeline.NewManager(
|
state.Timelines.List = timeline.NewManager(
|
||||||
tlprocessor.ListTimelineGrab(&state),
|
tlprocessor.ListTimelineGrab(state),
|
||||||
tlprocessor.ListTimelineFilter(&state, visFilter),
|
tlprocessor.ListTimelineFilter(state, visFilter),
|
||||||
tlprocessor.ListTimelineStatusPrepare(&state, typeConverter),
|
tlprocessor.ListTimelineStatusPrepare(state, typeConverter),
|
||||||
tlprocessor.SkipInsert(),
|
tlprocessor.SkipInsert(),
|
||||||
)
|
)
|
||||||
if err := state.Timelines.List.Start(); err != nil {
|
if err := state.Timelines.List.Start(); err != nil {
|
||||||
return fmt.Errorf("error starting list timeline: %s", err)
|
return fmt.Errorf("error starting list timeline: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a media cleaner using the given state.
|
// Start the job scheduler
|
||||||
cleaner := cleaner.New(&state)
|
// (this is required for cleaner).
|
||||||
|
state.Workers.StartScheduler()
|
||||||
|
|
||||||
// Create the processor using all the other services we've created so far.
|
// Add a task to the scheduler to sweep caches.
|
||||||
|
// Frequency = 1 * minute
|
||||||
|
// Threshold = 60% capacity
|
||||||
|
if !state.Workers.Scheduler.AddRecurring(
|
||||||
|
"@cachesweep", // id
|
||||||
|
time.Time{}, // start
|
||||||
|
time.Minute, // freq
|
||||||
|
func(context.Context, time.Time) {
|
||||||
|
state.Caches.Sweep(60)
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
return fmt.Errorf("error scheduling cache sweep: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create background cleaner.
|
||||||
|
cleaner := cleaner.New(state)
|
||||||
|
|
||||||
|
// Now schedule background cleaning tasks.
|
||||||
|
if err := cleaner.ScheduleJobs(); err != nil {
|
||||||
|
return fmt.Errorf("error scheduling cleaner jobs: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the processor using all the
|
||||||
|
// other services we've created so far.
|
||||||
processor := processing.NewProcessor(
|
processor := processing.NewProcessor(
|
||||||
cleaner,
|
cleaner,
|
||||||
typeConverter,
|
typeConverter,
|
||||||
federator,
|
federator,
|
||||||
oauthServer,
|
oauthServer,
|
||||||
mediaManager,
|
mediaManager,
|
||||||
&state,
|
state,
|
||||||
emailSender,
|
emailSender,
|
||||||
)
|
)
|
||||||
|
|
||||||
// Set state client / federator asynchronous worker enqueue functions
|
// Initialize the specialized workers pools.
|
||||||
state.Workers.EnqueueClientAPI = processor.Workers().EnqueueClientAPI
|
state.Workers.Client.Init(messages.ClientMsgIndices())
|
||||||
state.Workers.EnqueueFediAPI = processor.Workers().EnqueueFediAPI
|
state.Workers.Federator.Init(messages.FederatorMsgIndices())
|
||||||
|
state.Workers.Delivery.Init(client)
|
||||||
|
state.Workers.Client.Process = processor.Workers().ProcessFromClientAPI
|
||||||
|
state.Workers.Federator.Process = processor.Workers().ProcessFromFediAPI
|
||||||
|
|
||||||
// Set state client / federator synchronous processing functions.
|
// Now start workers!
|
||||||
state.Workers.ProcessFromClientAPI = processor.Workers().ProcessFromClientAPI
|
state.Workers.Start()
|
||||||
state.Workers.ProcessFromFediAPI = processor.Workers().ProcessFromFediAPI
|
|
||||||
|
|
||||||
// Schedule tasks for all existing poll expiries.
|
// Schedule notif tasks for all existing poll expiries.
|
||||||
if err := processor.Polls().ScheduleAll(ctx); err != nil {
|
if err := processor.Polls().ScheduleAll(ctx); err != nil {
|
||||||
return fmt.Errorf("error scheduling poll expiries: %w", err)
|
return fmt.Errorf("error scheduling poll expiries: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -224,7 +290,7 @@ var Start action.GTSAction = func(ctx context.Context) error {
|
||||||
HTTP router initialization
|
HTTP router initialization
|
||||||
*/
|
*/
|
||||||
|
|
||||||
router, err := router.New(ctx)
|
route, err = router.New(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error creating router: %s", err)
|
return fmt.Errorf("error creating router: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -249,7 +315,7 @@ var Start action.GTSAction = func(ctx context.Context) error {
|
||||||
// note: hooks adding ctx fields must be ABOVE
|
// note: hooks adding ctx fields must be ABOVE
|
||||||
// the logger, otherwise won't be accessible.
|
// the logger, otherwise won't be accessible.
|
||||||
middleware.Logger(config.GetLogClientIP()),
|
middleware.Logger(config.GetLogClientIP()),
|
||||||
middleware.HeaderFilter(&state),
|
middleware.HeaderFilter(state),
|
||||||
middleware.UserAgent(),
|
middleware.UserAgent(),
|
||||||
middleware.CORS(),
|
middleware.CORS(),
|
||||||
middleware.ExtraHeaders(),
|
middleware.ExtraHeaders(),
|
||||||
|
@ -279,10 +345,10 @@ var Start action.GTSAction = func(ctx context.Context) error {
|
||||||
middlewares = append(middlewares, middleware.ContentSecurityPolicy(cspExtraURIs...))
|
middlewares = append(middlewares, middleware.ContentSecurityPolicy(cspExtraURIs...))
|
||||||
|
|
||||||
// attach global middlewares which are used for every request
|
// attach global middlewares which are used for every request
|
||||||
router.AttachGlobalMiddleware(middlewares...)
|
route.AttachGlobalMiddleware(middlewares...)
|
||||||
|
|
||||||
// attach global no route / 404 handler to the router
|
// attach global no route / 404 handler to the router
|
||||||
router.AttachNoRouteHandler(func(c *gin.Context) {
|
route.AttachNoRouteHandler(func(c *gin.Context) {
|
||||||
apiutil.ErrorHandler(c, gtserror.NewErrorNotFound(errors.New(http.StatusText(http.StatusNotFound))), processor.InstanceGetV1)
|
apiutil.ErrorHandler(c, gtserror.NewErrorNotFound(errors.New(http.StatusText(http.StatusNotFound))), processor.InstanceGetV1)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -307,8 +373,9 @@ var Start action.GTSAction = func(ctx context.Context) error {
|
||||||
|
|
||||||
var (
|
var (
|
||||||
authModule = api.NewAuth(dbService, processor, idp, routerSession, sessionName) // auth/oauth paths
|
authModule = api.NewAuth(dbService, processor, idp, routerSession, sessionName) // auth/oauth paths
|
||||||
clientModule = api.NewClient(dbService, processor) // api client endpoints
|
clientModule = api.NewClient(state, processor) // api client endpoints
|
||||||
metricsModule = api.NewMetrics() // Metrics endpoints
|
metricsModule = api.NewMetrics() // Metrics endpoints
|
||||||
|
healthModule = api.NewHealth(dbService.Ready) // Health check endpoints
|
||||||
fileserverModule = api.NewFileserver(processor) // fileserver endpoints
|
fileserverModule = api.NewFileserver(processor) // fileserver endpoints
|
||||||
wellKnownModule = api.NewWellKnown(processor) // .well-known endpoints
|
wellKnownModule = api.NewWellKnown(processor) // .well-known endpoints
|
||||||
nodeInfoModule = api.NewNodeInfo(processor) // nodeinfo endpoint
|
nodeInfoModule = api.NewNodeInfo(processor) // nodeinfo endpoint
|
||||||
|
@ -337,21 +404,21 @@ var Start action.GTSAction = func(ctx context.Context) error {
|
||||||
|
|
||||||
// these should be routed in order;
|
// these should be routed in order;
|
||||||
// apply throttling *after* rate limiting
|
// apply throttling *after* rate limiting
|
||||||
authModule.Route(router, clLimit, clThrottle, gzip)
|
authModule.Route(route, clLimit, clThrottle, gzip)
|
||||||
clientModule.Route(router, clLimit, clThrottle, gzip)
|
clientModule.Route(route, clLimit, clThrottle, gzip)
|
||||||
metricsModule.Route(router, clLimit, clThrottle, gzip)
|
metricsModule.Route(route, clLimit, clThrottle, gzip)
|
||||||
fileserverModule.Route(router, fsMainLimit, fsThrottle)
|
healthModule.Route(route, clLimit, clThrottle)
|
||||||
fileserverModule.RouteEmojis(router, instanceAccount.ID, fsEmojiLimit, fsThrottle)
|
fileserverModule.Route(route, fsMainLimit, fsThrottle)
|
||||||
wellKnownModule.Route(router, gzip, s2sLimit, s2sThrottle)
|
fileserverModule.RouteEmojis(route, instanceAccount.ID, fsEmojiLimit, fsThrottle)
|
||||||
nodeInfoModule.Route(router, s2sLimit, s2sThrottle, gzip)
|
wellKnownModule.Route(route, gzip, s2sLimit, s2sThrottle)
|
||||||
activityPubModule.Route(router, s2sLimit, s2sThrottle, gzip)
|
nodeInfoModule.Route(route, s2sLimit, s2sThrottle, gzip)
|
||||||
activityPubModule.RoutePublicKey(router, s2sLimit, pkThrottle, gzip)
|
activityPubModule.Route(route, s2sLimit, s2sThrottle, gzip)
|
||||||
webModule.Route(router, fsMainLimit, fsThrottle, gzip)
|
activityPubModule.RoutePublicKey(route, s2sLimit, pkThrottle, gzip)
|
||||||
|
webModule.Route(route, fsMainLimit, fsThrottle, gzip)
|
||||||
|
|
||||||
// Start the GoToSocial server.
|
// Finally start the main http server!
|
||||||
server := gotosocial.NewServer(dbService, router, cleaner)
|
if err := route.Start(); err != nil {
|
||||||
if err := server.Start(ctx); err != nil {
|
return fmt.Errorf("error starting router: %w", err)
|
||||||
return fmt.Errorf("error starting gotosocial service: %s", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// catch shutdown signals from the operating system
|
// catch shutdown signals from the operating system
|
||||||
|
@ -360,11 +427,5 @@ var Start action.GTSAction = func(ctx context.Context) error {
|
||||||
sig := <-sigs // block until signal received
|
sig := <-sigs // block until signal received
|
||||||
log.Infof(ctx, "received signal %s, shutting down", sig)
|
log.Infof(ctx, "received signal %s, shutting down", sig)
|
||||||
|
|
||||||
// close down all running services in order
|
|
||||||
if err := server.Stop(ctx); err != nil {
|
|
||||||
return fmt.Errorf("error closing gotosocial service: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Info(ctx, "done! exiting...")
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,6 @@ import (
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/cleaner"
|
"github.com/superseriousbusiness/gotosocial/internal/cleaner"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/filter/visibility"
|
"github.com/superseriousbusiness/gotosocial/internal/filter/visibility"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gotosocial"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/language"
|
"github.com/superseriousbusiness/gotosocial/internal/language"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/log"
|
"github.com/superseriousbusiness/gotosocial/internal/log"
|
||||||
|
@ -43,6 +42,7 @@ import (
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/middleware"
|
"github.com/superseriousbusiness/gotosocial/internal/middleware"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oidc"
|
"github.com/superseriousbusiness/gotosocial/internal/oidc"
|
||||||
tlprocessor "github.com/superseriousbusiness/gotosocial/internal/processing/timeline"
|
tlprocessor "github.com/superseriousbusiness/gotosocial/internal/processing/timeline"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/router"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/state"
|
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/storage"
|
"github.com/superseriousbusiness/gotosocial/internal/storage"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/timeline"
|
"github.com/superseriousbusiness/gotosocial/internal/timeline"
|
||||||
|
@ -54,11 +54,71 @@ import (
|
||||||
|
|
||||||
// Start creates and starts a gotosocial testrig server
|
// Start creates and starts a gotosocial testrig server
|
||||||
var Start action.GTSAction = func(ctx context.Context) error {
|
var Start action.GTSAction = func(ctx context.Context) error {
|
||||||
var state state.State
|
|
||||||
|
|
||||||
testrig.InitTestConfig()
|
testrig.InitTestConfig()
|
||||||
testrig.InitTestLog()
|
testrig.InitTestLog()
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Define necessary core variables
|
||||||
|
// before anything so we can prepare
|
||||||
|
// defer function for safe shutdown
|
||||||
|
// depending on what services were
|
||||||
|
// managed to be started.
|
||||||
|
|
||||||
|
state = new(state.State)
|
||||||
|
route *router.Router
|
||||||
|
)
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
// Stop caches with
|
||||||
|
// background tasks.
|
||||||
|
state.Caches.Stop()
|
||||||
|
|
||||||
|
if route != nil {
|
||||||
|
// We reached a point where the API router
|
||||||
|
// was created + setup. Ensure it gets stopped
|
||||||
|
// first to stop processing new information.
|
||||||
|
if err := route.Stop(); err != nil {
|
||||||
|
log.Errorf(ctx, "error stopping router: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop any currently running
|
||||||
|
// worker processes / scheduled
|
||||||
|
// 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 {
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally reached end of shutdown.
|
||||||
|
log.Info(ctx, "done! exiting...")
|
||||||
|
}()
|
||||||
|
|
||||||
parsedLangs, err := language.InitLangs(config.GetInstanceLanguages().TagStrs())
|
parsedLangs, err := language.InitLangs(config.GetInstanceLanguages().TagStrs())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error initializing languages: %w", err)
|
return fmt.Errorf("error initializing languages: %w", err)
|
||||||
|
@ -70,17 +130,15 @@ var Start action.GTSAction = func(ctx context.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize caches and database
|
// Initialize caches and database
|
||||||
state.DB = testrig.NewTestDB(&state)
|
state.DB = testrig.NewTestDB(state)
|
||||||
|
|
||||||
// New test db inits caches so we don't need to do
|
// New test db inits caches so we don't need to do
|
||||||
// that twice, we can just start the initialized caches.
|
// that twice, we can just start the initialized caches.
|
||||||
state.Caches.Start()
|
state.Caches.Start()
|
||||||
defer state.Caches.Stop()
|
|
||||||
|
|
||||||
testrig.StandardDBSetup(state.DB, nil)
|
testrig.StandardDBSetup(state.DB, nil)
|
||||||
|
|
||||||
// Get the instance account
|
// Get the instance account (we'll need this later).
|
||||||
// (we'll need this later).
|
|
||||||
instanceAccount, err := state.DB.GetInstanceAccount(ctx, "")
|
instanceAccount, err := state.DB.GetInstanceAccount(ctx, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error retrieving instance account: %w", err)
|
return fmt.Errorf("error retrieving instance account: %w", err)
|
||||||
|
@ -98,11 +156,11 @@ var Start action.GTSAction = func(ctx context.Context) error {
|
||||||
testrig.StandardStorageSetup(state.Storage, "./testrig/media")
|
testrig.StandardStorageSetup(state.Storage, "./testrig/media")
|
||||||
|
|
||||||
// Initialize workers.
|
// Initialize workers.
|
||||||
state.Workers.Start()
|
testrig.StartNoopWorkers(state)
|
||||||
defer state.Workers.Stop()
|
defer testrig.StopWorkers(state)
|
||||||
|
|
||||||
// build backend handlers
|
// build backend handlers
|
||||||
transportController := testrig.NewTestTransportController(&state, testrig.NewMockHTTPClient(func(req *http.Request) (*http.Response, error) {
|
transportController := testrig.NewTestTransportController(state, testrig.NewMockHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||||
r := io.NopCloser(bytes.NewReader([]byte{}))
|
r := io.NopCloser(bytes.NewReader([]byte{}))
|
||||||
return &http.Response{
|
return &http.Response{
|
||||||
StatusCode: 200,
|
StatusCode: 200,
|
||||||
|
@ -112,35 +170,34 @@ var Start action.GTSAction = func(ctx context.Context) error {
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
}, ""))
|
}, ""))
|
||||||
mediaManager := testrig.NewTestMediaManager(&state)
|
mediaManager := testrig.NewTestMediaManager(state)
|
||||||
federator := testrig.NewTestFederator(&state, transportController, mediaManager)
|
federator := testrig.NewTestFederator(state, transportController, mediaManager)
|
||||||
|
|
||||||
emailSender := testrig.NewEmailSender("./web/template/", nil)
|
emailSender := testrig.NewEmailSender("./web/template/", nil)
|
||||||
typeConverter := typeutils.NewConverter(&state)
|
typeConverter := typeutils.NewConverter(state)
|
||||||
filter := visibility.NewFilter(&state)
|
filter := visibility.NewFilter(state)
|
||||||
|
|
||||||
// Initialize timelines.
|
// Initialize both home / list timelines.
|
||||||
state.Timelines.Home = timeline.NewManager(
|
state.Timelines.Home = timeline.NewManager(
|
||||||
tlprocessor.HomeTimelineGrab(&state),
|
tlprocessor.HomeTimelineGrab(state),
|
||||||
tlprocessor.HomeTimelineFilter(&state, filter),
|
tlprocessor.HomeTimelineFilter(state, filter),
|
||||||
tlprocessor.HomeTimelineStatusPrepare(&state, typeConverter),
|
tlprocessor.HomeTimelineStatusPrepare(state, typeConverter),
|
||||||
tlprocessor.SkipInsert(),
|
tlprocessor.SkipInsert(),
|
||||||
)
|
)
|
||||||
if err := state.Timelines.Home.Start(); err != nil {
|
if err := state.Timelines.Home.Start(); err != nil {
|
||||||
return fmt.Errorf("error starting home timeline: %s", err)
|
return fmt.Errorf("error starting home timeline: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
state.Timelines.List = timeline.NewManager(
|
state.Timelines.List = timeline.NewManager(
|
||||||
tlprocessor.ListTimelineGrab(&state),
|
tlprocessor.ListTimelineGrab(state),
|
||||||
tlprocessor.ListTimelineFilter(&state, filter),
|
tlprocessor.ListTimelineFilter(state, filter),
|
||||||
tlprocessor.ListTimelineStatusPrepare(&state, typeConverter),
|
tlprocessor.ListTimelineStatusPrepare(state, typeConverter),
|
||||||
tlprocessor.SkipInsert(),
|
tlprocessor.SkipInsert(),
|
||||||
)
|
)
|
||||||
if err := state.Timelines.List.Start(); err != nil {
|
if err := state.Timelines.List.Start(); err != nil {
|
||||||
return fmt.Errorf("error starting list timeline: %s", err)
|
return fmt.Errorf("error starting list timeline: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
processor := testrig.NewTestProcessor(&state, federator, emailSender, mediaManager)
|
processor := testrig.NewTestProcessor(state, federator, emailSender, mediaManager)
|
||||||
|
|
||||||
// Initialize metrics.
|
// Initialize metrics.
|
||||||
if err := metrics.Initialize(state.DB); err != nil {
|
if err := metrics.Initialize(state.DB); err != nil {
|
||||||
|
@ -151,7 +208,7 @@ var Start action.GTSAction = func(ctx context.Context) error {
|
||||||
HTTP router initialization
|
HTTP router initialization
|
||||||
*/
|
*/
|
||||||
|
|
||||||
router := testrig.NewTestRouter(state.DB)
|
route = testrig.NewTestRouter(state.DB)
|
||||||
middlewares := []gin.HandlerFunc{
|
middlewares := []gin.HandlerFunc{
|
||||||
middleware.AddRequestID(config.GetRequestIDHeader()), // requestID middleware must run before tracing
|
middleware.AddRequestID(config.GetRequestIDHeader()), // requestID middleware must run before tracing
|
||||||
}
|
}
|
||||||
|
@ -165,6 +222,7 @@ var Start action.GTSAction = func(ctx context.Context) error {
|
||||||
|
|
||||||
middlewares = append(middlewares, []gin.HandlerFunc{
|
middlewares = append(middlewares, []gin.HandlerFunc{
|
||||||
middleware.Logger(config.GetLogClientIP()),
|
middleware.Logger(config.GetLogClientIP()),
|
||||||
|
middleware.HeaderFilter(state),
|
||||||
middleware.UserAgent(),
|
middleware.UserAgent(),
|
||||||
middleware.CORS(),
|
middleware.CORS(),
|
||||||
middleware.ExtraHeaders(),
|
middleware.ExtraHeaders(),
|
||||||
|
@ -194,10 +252,10 @@ var Start action.GTSAction = func(ctx context.Context) error {
|
||||||
middlewares = append(middlewares, middleware.ContentSecurityPolicy(cspExtraURIs...))
|
middlewares = append(middlewares, middleware.ContentSecurityPolicy(cspExtraURIs...))
|
||||||
|
|
||||||
// attach global middlewares which are used for every request
|
// attach global middlewares which are used for every request
|
||||||
router.AttachGlobalMiddleware(middlewares...)
|
route.AttachGlobalMiddleware(middlewares...)
|
||||||
|
|
||||||
// attach global no route / 404 handler to the router
|
// attach global no route / 404 handler to the router
|
||||||
router.AttachNoRouteHandler(func(c *gin.Context) {
|
route.AttachNoRouteHandler(func(c *gin.Context) {
|
||||||
apiutil.ErrorHandler(c, gtserror.NewErrorNotFound(errors.New(http.StatusText(http.StatusNotFound))), processor.InstanceGetV1)
|
apiutil.ErrorHandler(c, gtserror.NewErrorNotFound(errors.New(http.StatusText(http.StatusNotFound))), processor.InstanceGetV1)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -222,8 +280,9 @@ var Start action.GTSAction = func(ctx context.Context) error {
|
||||||
|
|
||||||
var (
|
var (
|
||||||
authModule = api.NewAuth(state.DB, processor, idp, routerSession, sessionName) // auth/oauth paths
|
authModule = api.NewAuth(state.DB, processor, idp, routerSession, sessionName) // auth/oauth paths
|
||||||
clientModule = api.NewClient(state.DB, processor) // api client endpoints
|
clientModule = api.NewClient(state, processor) // api client endpoints
|
||||||
metricsModule = api.NewMetrics() // Metrics endpoints
|
metricsModule = api.NewMetrics() // Metrics endpoints
|
||||||
|
healthModule = api.NewHealth(state.DB.Ready) // Health check endpoints
|
||||||
fileserverModule = api.NewFileserver(processor) // fileserver endpoints
|
fileserverModule = api.NewFileserver(processor) // fileserver endpoints
|
||||||
wellKnownModule = api.NewWellKnown(processor) // .well-known endpoints
|
wellKnownModule = api.NewWellKnown(processor) // .well-known endpoints
|
||||||
nodeInfoModule = api.NewNodeInfo(processor) // nodeinfo endpoint
|
nodeInfoModule = api.NewNodeInfo(processor) // nodeinfo endpoint
|
||||||
|
@ -232,22 +291,29 @@ var Start action.GTSAction = func(ctx context.Context) error {
|
||||||
)
|
)
|
||||||
|
|
||||||
// these should be routed in order
|
// these should be routed in order
|
||||||
authModule.Route(router)
|
authModule.Route(route)
|
||||||
clientModule.Route(router)
|
clientModule.Route(route)
|
||||||
metricsModule.Route(router)
|
metricsModule.Route(route)
|
||||||
fileserverModule.Route(router)
|
healthModule.Route(route)
|
||||||
fileserverModule.RouteEmojis(router, instanceAccount.ID)
|
fileserverModule.Route(route)
|
||||||
wellKnownModule.Route(router)
|
fileserverModule.RouteEmojis(route, instanceAccount.ID)
|
||||||
nodeInfoModule.Route(router)
|
wellKnownModule.Route(route)
|
||||||
activityPubModule.Route(router)
|
nodeInfoModule.Route(route)
|
||||||
activityPubModule.RoutePublicKey(router)
|
activityPubModule.Route(route)
|
||||||
webModule.Route(router)
|
activityPubModule.RoutePublicKey(route)
|
||||||
|
webModule.Route(route)
|
||||||
|
|
||||||
cleaner := cleaner.New(&state)
|
// Create background cleaner.
|
||||||
|
cleaner := cleaner.New(state)
|
||||||
|
|
||||||
gts := gotosocial.NewServer(state.DB, router, cleaner)
|
// Now schedule background cleaning tasks.
|
||||||
if err := gts.Start(ctx); err != nil {
|
if err := cleaner.ScheduleJobs(); err != nil {
|
||||||
return fmt.Errorf("error starting gotosocial service: %s", err)
|
return fmt.Errorf("error scheduling cleaner jobs: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally start the main http server!
|
||||||
|
if err := route.Start(); err != nil {
|
||||||
|
return fmt.Errorf("error starting router: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// catch shutdown signals from the operating system
|
// catch shutdown signals from the operating system
|
||||||
|
@ -256,14 +322,5 @@ var Start action.GTSAction = func(ctx context.Context) error {
|
||||||
sig := <-sigs
|
sig := <-sigs
|
||||||
log.Infof(ctx, "received signal %s, shutting down", sig)
|
log.Infof(ctx, "received signal %s, shutting down", sig)
|
||||||
|
|
||||||
testrig.StandardDBTeardown(state.DB)
|
|
||||||
testrig.StandardStorageTeardown(state.Storage)
|
|
||||||
|
|
||||||
// close down all running services in order
|
|
||||||
if err := gts.Stop(ctx); err != nil {
|
|
||||||
return fmt.Errorf("error closing gotosocial service: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Info(ctx, "done! exiting...")
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -108,7 +108,7 @@ func adminCommands() *cobra.Command {
|
||||||
|
|
||||||
adminAccountDisableCmd := &cobra.Command{
|
adminAccountDisableCmd := &cobra.Command{
|
||||||
Use: "disable",
|
Use: "disable",
|
||||||
Short: "prevent a local account from signing in or posting etc, but don't delete anything",
|
Short: "set 'disabled' to true on a local account to prevent it from signing in or posting etc, but don't delete anything",
|
||||||
PreRunE: func(cmd *cobra.Command, args []string) error {
|
PreRunE: func(cmd *cobra.Command, args []string) error {
|
||||||
return preRun(preRunArgs{cmd: cmd})
|
return preRun(preRunArgs{cmd: cmd})
|
||||||
},
|
},
|
||||||
|
@ -119,6 +119,19 @@ func adminCommands() *cobra.Command {
|
||||||
config.AddAdminAccount(adminAccountDisableCmd)
|
config.AddAdminAccount(adminAccountDisableCmd)
|
||||||
adminAccountCmd.AddCommand(adminAccountDisableCmd)
|
adminAccountCmd.AddCommand(adminAccountDisableCmd)
|
||||||
|
|
||||||
|
adminAccountEnableCmd := &cobra.Command{
|
||||||
|
Use: "enable",
|
||||||
|
Short: "undo a previous disable command by setting 'disabled' to false on a 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.Enable)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
config.AddAdminAccount(adminAccountEnableCmd)
|
||||||
|
adminAccountCmd.AddCommand(adminAccountEnableCmd)
|
||||||
|
|
||||||
adminAccountPasswordCmd := &cobra.Command{
|
adminAccountPasswordCmd := &cobra.Command{
|
||||||
Use: "password",
|
Use: "password",
|
||||||
Short: "set a new password for the given local account",
|
Short: "set a new password for the given local account",
|
||||||
|
|
BIN
docs/.cache/plugin/social/Roboto-Black.ttf
Normal file
BIN
docs/.cache/plugin/social/Roboto-BlackItalic.ttf
Normal file
BIN
docs/.cache/plugin/social/Roboto-Bold.ttf
Normal file
BIN
docs/.cache/plugin/social/Roboto-BoldItalic.ttf
Normal file
BIN
docs/.cache/plugin/social/Roboto-Italic.ttf
Normal file
BIN
docs/.cache/plugin/social/Roboto-Light.ttf
Normal file
BIN
docs/.cache/plugin/social/Roboto-LightItalic.ttf
Normal file
BIN
docs/.cache/plugin/social/Roboto-Medium.ttf
Normal file
BIN
docs/.cache/plugin/social/Roboto-MediumItalic.ttf
Normal file
BIN
docs/.cache/plugin/social/Roboto-Regular.ttf
Normal file
BIN
docs/.cache/plugin/social/Roboto-Thin.ttf
Normal file
BIN
docs/.cache/plugin/social/Roboto-ThinItalic.ttf
Normal file
|
@ -20,18 +20,34 @@ Usage:
|
||||||
|
|
||||||
Available Commands:
|
Available Commands:
|
||||||
admin gotosocial admin-related tasks
|
admin gotosocial admin-related tasks
|
||||||
completion generate the autocompletion script for the specified shell
|
|
||||||
debug gotosocial debug-related tasks
|
debug gotosocial debug-related tasks
|
||||||
help Help about any command
|
help Help about any command
|
||||||
server gotosocial server-related tasks
|
server gotosocial server-related tasks
|
||||||
testrig gotosocial testrig-related tasks
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Under `Available Commands`, you can see the standard `server` command. But there are also commands doing admin and testing etc, which will be explained in this document.
|
Under `Available Commands`, you can see the standard `server` command. But there are also commands doing admin and debugging etc, which will be explained in this document.
|
||||||
|
|
||||||
**Please note -- for all of these commands, you will still need to set the global options correctly so that the CLI tool knows how eg., how to connect to your database, which database to use, which host and account domain to use etc.**
|
!!! Info "Passing global config to the CLI"
|
||||||
|
|
||||||
|
For all of these commands, you will still need to set the global options correctly so that the CLI tool knows how to connect to your database, which database to use, which host and account domain to use, etc.
|
||||||
|
|
||||||
|
You can set these options using environment variables, passing them as CLI flags (eg., `gotosocial [commands] --host example.org`), or by just pointing the CLI tool towards your config file (eg., `gotosocial --config-path ./config.yaml [commands]`).
|
||||||
|
|
||||||
You can set these options using environment variables, passing them as CLI flags (eg., `gotosocial [commands] --host example.org`), or by just pointing the CLI tool towards your config file (eg., `gotosocial --config-path ./config.yaml [commands]`).
|
!!! Info
|
||||||
|
|
||||||
|
When running CLI commands, you'll get a bit of output like the following:
|
||||||
|
|
||||||
|
```text
|
||||||
|
time=XXXX level=info msg=connected to SQLITE database
|
||||||
|
time=XXXX level=info msg=there are no new migrations to run func=doMigration
|
||||||
|
time=XXXX level=info msg=closing db connection
|
||||||
|
```
|
||||||
|
|
||||||
|
This is normal and indicates that the commands ran as expected.
|
||||||
|
|
||||||
|
!!! Warning "Restarting GtS after running admin commands"
|
||||||
|
|
||||||
|
Because of the way internal caching works in GoToSocial, you may need to restart GoToSocial after running some of these commands in order for the effect of the command to "take". We are still looking for a way to make this unnecessary. In the meantime, commands that require a restart after running the command are highlighted below.
|
||||||
|
|
||||||
## gotosocial admin
|
## gotosocial admin
|
||||||
|
|
||||||
|
@ -68,7 +84,11 @@ gotosocial admin account create \
|
||||||
|
|
||||||
### gotosocial admin account confirm
|
### gotosocial admin account confirm
|
||||||
|
|
||||||
This command can be used to confirm a user+account on your instance, allowing them to log in and use the account. Note that if the account was created using `admin account create` this is not necessary.
|
This command can be used to confirm a user+account on your instance, allowing them to log in and use the account.
|
||||||
|
|
||||||
|
!!! Info
|
||||||
|
|
||||||
|
If the account was created using `admin account create` it is not necessary to run `confirm` on the account, it will be confirmed already.
|
||||||
|
|
||||||
`gotosocial admin account confirm --help`:
|
`gotosocial admin account confirm --help`:
|
||||||
|
|
||||||
|
@ -93,6 +113,10 @@ gotosocial admin account confirm --username some_username --config-path config.y
|
||||||
|
|
||||||
This command can be used to promote a user to admin.
|
This command can be used to promote a user to admin.
|
||||||
|
|
||||||
|
!!! Warning "Server restart required"
|
||||||
|
|
||||||
|
In order for the change to "take", this command requires a restart of GoToSocial after running the command.
|
||||||
|
|
||||||
`gotosocial admin account promote --help`:
|
`gotosocial admin account promote --help`:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
|
@ -116,6 +140,10 @@ gotosocial admin account promote --username some_username --config-path config.y
|
||||||
|
|
||||||
This command can be used to demote a user from admin to normal user.
|
This command can be used to demote a user from admin to normal user.
|
||||||
|
|
||||||
|
!!! Warning "Server restart required"
|
||||||
|
|
||||||
|
In order for the change to "take", this command requires a restart of GoToSocial after running the command.
|
||||||
|
|
||||||
`gotosocial admin account demote --help`:
|
`gotosocial admin account demote --help`:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
|
@ -139,10 +167,14 @@ gotosocial admin account demote --username some_username --config-path config.ya
|
||||||
|
|
||||||
This command can be used to disable an account on your instance: prevent it from signing in or doing anything, without deleting data.
|
This command can be used to disable an account on your instance: prevent it from signing in or doing anything, without deleting data.
|
||||||
|
|
||||||
|
!!! Warning "Server restart required"
|
||||||
|
|
||||||
|
In order for the change to "take", this command requires a restart of GoToSocial after running the command.
|
||||||
|
|
||||||
`gotosocial admin account disable --help`:
|
`gotosocial admin account disable --help`:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
prevent a local account from signing in or posting etc, but don't delete anything
|
set 'disabled' to true on a local account to prevent it from signing in or posting etc, but don't delete anything
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
gotosocial admin account disable [flags]
|
gotosocial admin account disable [flags]
|
||||||
|
@ -158,10 +190,41 @@ Example:
|
||||||
gotosocial admin account disable --username some_username --config-path config.yaml
|
gotosocial admin account disable --username some_username --config-path config.yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### gotosocial admin account enable
|
||||||
|
|
||||||
|
This command can be used to reenable an account on your instance, undoing a previous `disable` command.
|
||||||
|
|
||||||
|
!!! Warning "Server restart required"
|
||||||
|
|
||||||
|
In order for the change to "take", this command requires a restart of GoToSocial after running the command.
|
||||||
|
|
||||||
|
`gotosocial admin account enable --help`:
|
||||||
|
|
||||||
|
```text
|
||||||
|
undo a previous disable command by setting 'disabled' to false on a local account
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
gotosocial admin account enable [flags]
|
||||||
|
|
||||||
|
Flags:
|
||||||
|
-h, --help help for enable
|
||||||
|
--username string the username to create/delete/etc
|
||||||
|
```
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
gotosocial admin account enable --username some_username --config-path config.yaml
|
||||||
|
```
|
||||||
|
|
||||||
### gotosocial admin account password
|
### gotosocial admin account password
|
||||||
|
|
||||||
This command can be used to set a new password on the given local account.
|
This command can be used to set a new password on the given local account.
|
||||||
|
|
||||||
|
!!! Warning "Server restart required"
|
||||||
|
|
||||||
|
In order for the change to "take", this command requires a restart of GoToSocial after running the command.
|
||||||
|
|
||||||
`gotosocial admin account password --help`:
|
`gotosocial admin account password --help`:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
|
@ -329,7 +392,11 @@ This command can be used to prune orphaned media from your GoToSocial.
|
||||||
|
|
||||||
Orphaned media is defined as media that is in storage under a key that matches the format used by GoToSocial, but which does not have a corresponding database entry. This is useful for excising files that may be remaining from a previous installation, or files that were placed in storage mistakenly.
|
Orphaned media is defined as media that is in storage under a key that matches the format used by GoToSocial, but which does not have a corresponding database entry. This is useful for excising files that may be remaining from a previous installation, or files that were placed in storage mistakenly.
|
||||||
|
|
||||||
**This command only works when GoToSocial is not running, since it acquires an exclusive lock on storage. Stop GoToSocial first before running this command!**
|
!!! Warning "Requires a stopped server"
|
||||||
|
|
||||||
|
This command only works when GoToSocial is not running, since it acquires an exclusive lock on storage.
|
||||||
|
|
||||||
|
Stop GoToSocial first before running this command!
|
||||||
|
|
||||||
```text
|
```text
|
||||||
prune orphaned media from storage
|
prune orphaned media from storage
|
||||||
|
@ -366,7 +433,11 @@ These items will be refetched later on demand, if necessary.
|
||||||
|
|
||||||
Unused media means avatars/headers/status attachments which are not currently in use by an account or status.
|
Unused media means avatars/headers/status attachments which are not currently in use by an account or status.
|
||||||
|
|
||||||
**This command only works when GoToSocial is not running, since it acquires an exclusive lock on storage. Stop GoToSocial first before running this command!**
|
!!! Warning "Requires a stopped server"
|
||||||
|
|
||||||
|
This command only works when GoToSocial is not running, since it acquires an exclusive lock on storage.
|
||||||
|
|
||||||
|
Stop GoToSocial first before running this command!
|
||||||
|
|
||||||
```text
|
```text
|
||||||
prune unused/stale remote media from storage, older than given number of days
|
prune unused/stale remote media from storage, older than given number of days
|
||||||
|
|
55
docs/admin/database_maintenance.md
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
# Database Maintenance
|
||||||
|
|
||||||
|
Regardless of whether you choose to run GoToSocial with SQLite or Postgres, you may need to occasionally take maintenance steps to keep your database running well.
|
||||||
|
|
||||||
|
!!! tip
|
||||||
|
|
||||||
|
Though the maintenance tips provided here are intended to be non-destructive, you should backup your database before manually performing maintenance. That way if you mistype something or accidentally run a bad command, you can restore your backup and try again.
|
||||||
|
|
||||||
|
!!! danger
|
||||||
|
|
||||||
|
Manually creating, deleting, or updating entries in your GoToSocial database is **heavily discouraged**, and such commands are not provided here. Even if you think you know what you are doing, running `DELETE` statements etc. may introduce issues that are very difficult to debug. The maintenance tips below are designed to help with the smooth running of your instance; they will not save your ass if you have manually gone into your database and hacked at entries, tables, and indexes.
|
||||||
|
|
||||||
|
## SQLite
|
||||||
|
|
||||||
|
To do manual SQLite maintenance, you should first install the SQLite command line tool `sqlite3` on the same machine that your GoToSocial sqlite.db file is stored on. See [here](https://sqlite.org/cli.html) for details about `sqlite3`.
|
||||||
|
|
||||||
|
### Analyze / Optimize
|
||||||
|
|
||||||
|
Following [SQLite best practice](https://sqlite.org/lang_analyze.html#recommended_usage_pattern), GoToSocial runs the `optimize` SQLite pragma with `analysis_limit=1000` on closing database connections to keep index information up to date.
|
||||||
|
|
||||||
|
After each set of database migrations (eg., when starting a newer version of GoToSocial), GoToSocial will run `ANALYZE` to ensure that any indexes added or removed by migrations are taken into account correctly by the query planner.
|
||||||
|
|
||||||
|
The `ANALYZE` command may take ~10 minutes depending on your hardware and the size of your database file.
|
||||||
|
|
||||||
|
Because of the above automated steps, in normal circumstances you should not need to run manual `ANALYZE` commands against your SQLite database file.
|
||||||
|
|
||||||
|
However, if you interrupted a previous `ANALYZE` command, and you notice that queries are running remarkably slowly, it could be the case that the index metadata stored in SQLite's internal tables has been removed or undesirably altered.
|
||||||
|
|
||||||
|
If this is the case, you can try manually running a full `ANALYZE` command, by doing the following:
|
||||||
|
|
||||||
|
1. Stop GoToSocial.
|
||||||
|
2. While connected to your GoToSocial database file in the `sqlite3` shell, run `PRAGMA analysis_limit=0; ANALYZE;` (this may take quite a few minutes).
|
||||||
|
3. Start GoToSocial.
|
||||||
|
|
||||||
|
[See here](https://sqlite.org/lang_analyze.html#approximate_analyze_for_large_databases) for more info.
|
||||||
|
|
||||||
|
### Vacuum
|
||||||
|
|
||||||
|
GoToSocial does not currently enable auto-vacuum for SQLite. To repack the database file to an optimal size you may want to run a `VACUUM` command on your SQLite database periodically (eg., every few months).
|
||||||
|
|
||||||
|
You can see lots of information about the `VACUUM` command [here](https://sqlite.org/lang_vacuum.html).
|
||||||
|
|
||||||
|
The basic steps are:
|
||||||
|
|
||||||
|
1. Stop GoToSocial.
|
||||||
|
2. While connected to your GoToSocial database file in the `sqlite3` shell, run `VACUUM;` (this may take quite a few minutes).
|
||||||
|
3. Start GoToSocial.
|
||||||
|
|
||||||
|
### Replication
|
||||||
|
|
||||||
|
It's a common practice to set up safeguards for your database like replication. SQLite can be replicated using external software. The basic steps are described on the [Replicating SQLite](../advanced/replicating-sqlite.md) page.
|
||||||
|
|
||||||
|
## Postgres
|
||||||
|
|
||||||
|
TODO: Maintenance recommendations for Postgres.
|
|
@ -7,8 +7,6 @@ GoToSocial currently offers 'block', 'allow' and disabled HTTP request header fi
|
||||||
|
|
||||||
HTTP request header filtering is also still considered "experimental". It should do what it says on the box, but it may cause bugs or edge cases to appear elsewhere, we're not sure yet!
|
HTTP request header filtering is also still considered "experimental". It should do what it says on the box, but it may cause bugs or edge cases to appear elsewhere, we're not sure yet!
|
||||||
|
|
||||||
Management via settings panel is TBA. Until then you will need to manage these directly via API endpoints.
|
|
||||||
|
|
||||||
## Disabled header filtering mode (default)
|
## Disabled header filtering mode (default)
|
||||||
|
|
||||||
When `advanced-header-filter-mode` is set to `""`, i.e. an empty string, all request header filtering will be disabled.
|
When `advanced-header-filter-mode` is set to `""`, i.e. an empty string, all request header filtering will be disabled.
|
||||||
|
@ -30,4 +28,4 @@ In allow mode, a block header filter can be used to override an existing allow f
|
||||||
A request in allow mode will only be accepted if it is EXPLICITLY ALLOWED AND NOT EXPLICITLY BLOCKED.
|
A request in allow mode will only be accepted if it is EXPLICITLY ALLOWED AND NOT EXPLICITLY BLOCKED.
|
||||||
|
|
||||||
!!! danger
|
!!! danger
|
||||||
Allow filtering mode is an extremely restrictive mode that will almost certainly prevent many (legitimate) clients from being able to access your instance, including yourself. You should only enable this mode if you know exactly what you're trying to achieve.
|
Allow filtering mode is an extremely restrictive mode that will almost certainly prevent many (legitimate) clients from being able to access your instance, including yourself. You should only enable this mode if you know exactly what you're trying to achieve.
|
||||||
|
|
13
docs/admin/robots.md
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
# Robots.txt
|
||||||
|
|
||||||
|
GoToSocial serves a `robots.txt` file on the host domain. This file contains rules that attempt to block known AI scrapers, as well as some other indexers. It also includes some rules to ensure things like API endpoints aren't indexed by search engines since there really isn't any point to them.
|
||||||
|
|
||||||
|
## AI scrapers
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
[airobots]: https://github.com/ai-robots-txt/ai.robots.txt/
|
|
@ -66,6 +66,10 @@ Instance administration settings.
|
||||||
|
|
||||||
Run one-off administrative actions.
|
Run one-off administrative actions.
|
||||||
|
|
||||||
|
#### Email
|
||||||
|
|
||||||
|
You can use this section to send a test email to the given email address, with an optional test message.
|
||||||
|
|
||||||
#### Media
|
#### Media
|
||||||
|
|
||||||
You can use this section run a media action to clean up the remote media cache using the specified number of days. Media older than the given number of days will be removed from storage (s3 or local). Media removed in this way will be refetched again later if the media is required again. This action is functionally identical to the media cleanup that runs automatically.
|
You can use this section run a media action to clean up the remote media cache using the specified number of days. Media older than the given number of days will be removed from storage (s3 or local). Media removed in this way will be refetched again later if the media is required again. This action is functionally identical to the media cleanup that runs automatically.
|
||||||
|
|
59
docs/admin/signups.md
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
# New Account Sign-Ups
|
||||||
|
|
||||||
|
If you want to allow more people than just you to have an account on your instance, you can open your instance to new account sign-ups / registrations.
|
||||||
|
|
||||||
|
Be wary that as instance admin, like it or not, you are responsible for what people post on your instance. If users on your instance harass or annoy other people on the fediverse, you may find your instance gets a bad reputation and becomes blocked by others. Moderating a space properly takes work. As such, you should carefully consider whether or not you are willing and able to do moderation, and consider accepting sign-ups on your instance only from friends and people that you really trust.
|
||||||
|
|
||||||
|
!!! warning
|
||||||
|
For the sign-up flow to work as intended, your instance [should be configured to send emails](../configuration/smtp.md).
|
||||||
|
|
||||||
|
As mentioned below, several emails are sent during the sign-up flow, both to you (as admin/moderator) and to the applicant, including an email asking them to confirm their email address.
|
||||||
|
|
||||||
|
If they cannot receive this email (because your instance is not configured to send emails), you will have to manually confirm the account by [using the CLI tool](../admin/cli.md#gotosocial-admin-account-confirm).
|
||||||
|
|
||||||
|
## Opening Sign-Ups
|
||||||
|
|
||||||
|
You can open new account sign-ups for your instance by changing the variable `accounts-registration-open` to `true` in your [configuration](../configuration/accounts.md), and restarting your GoToSocial instance.
|
||||||
|
|
||||||
|
A sign-up form for your instance will be available at the `/signup` endpoint. For example, `https://your-instance.example.org/signup`.
|
||||||
|
|
||||||
|
![Sign-up form, showing email, password, username, and reason fields.](../assets/signup-form.png)
|
||||||
|
|
||||||
|
Also, your instance homepage and "about" pages will be updated to reflect that registrations are open.
|
||||||
|
|
||||||
|
When someone submits a new sign-up, they'll receive an email at the provided email address, giving them a link to confirm that the address really belongs to them.
|
||||||
|
|
||||||
|
In the meantime, admins and moderators on your instance will receive an email and a notification that a new sign-up has been submitted.
|
||||||
|
|
||||||
|
## Handling Sign-Ups
|
||||||
|
|
||||||
|
Instance admins and moderators can handle a new sign-up by either approving or rejecting it via the "accounts" -> "pending" section in the admin panel.
|
||||||
|
|
||||||
|
![Admin settings panel open to "accounts" -> "pending", showing one account in a list.](../assets/signup-pending.png)
|
||||||
|
|
||||||
|
If you have no sign-ups, the list pictured above will be empty. If you have a pending account sign-up, however, you can click on it to open that account in the account details screen:
|
||||||
|
|
||||||
|
![Details of a new pending account, giving options to approve or reject the sign-up.](../assets/signup-account.png)
|
||||||
|
|
||||||
|
At the bottom, you will find actions that let you approve or reject the sign-up.
|
||||||
|
|
||||||
|
If you **approve** the sign-up, the account will be marked as "approved", and an email will be sent to the applicant informing them their sign-up has been approved, and reminding them to confirm their email address if they haven't already done so. If they have already confirmed their email address, they will be able to log in and start using their account.
|
||||||
|
|
||||||
|
If you **reject** the sign-up, you may wish to inform the applicant that their sign-up has been rejected, which you can do by ticking the "send email" checkbox. This will send a short email to the applicant informing them of the rejection. If you wish, you can add a custom message, which will be added at the bottom of the email. You can also add a private note that will be visible to other admins only.
|
||||||
|
|
||||||
|
!!! warning
|
||||||
|
You may want to hold off on approving a sign-up until they have confirmed their email address, in case the applicant made a typo when submitting, or the email address they provided does not actually belong to them. If they cannot confirm their email address, they will not be able to log in and use their account.
|
||||||
|
|
||||||
|
## Sign-Up Limits
|
||||||
|
|
||||||
|
To avoid sign-up backlogs overwhelming admins and moderators, GoToSocial limits the sign-up pending backlog to 20 accounts. Once there are 20 accounts pending in the backlog waiting to be handled by an admin or moderator, new sign-ups will not be accepted via the form.
|
||||||
|
|
||||||
|
New sign-ups will also not be accepted via the form if 10 or more new account sign-ups have been approved in the last 24 hours, to avoid instances rapidly expanding beyond the capabilities of moderators.
|
||||||
|
|
||||||
|
In both cases, applicants will be shown an error message explaining why they could not submit the form, and inviting them to try again later.
|
||||||
|
|
||||||
|
To combat spam accounts, GoToSocial account sign-ups **always** require manual approval by an administrator, and applicants must **always** confirm their email address before they are able to log in and post.
|
||||||
|
|
||||||
|
## Sign-Up Via Invite
|
||||||
|
|
||||||
|
NOT IMPLEMENTED YET: in a future update, admins and moderators will be able to create and send invites that allow accounts to be created even when public sign-up is closed, and to pre-approve accounts created via invitation, and/or allow them to override the sign-up limits described above.
|
30
docs/admin/themes.md
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
# Themes
|
||||||
|
|
||||||
|
Users on your instance can select a theme for their profile from any css files present in the `web/assets/themes` directory.
|
||||||
|
|
||||||
|
GoToSocial comes with some theme files already, but you can add more yourself by doing the following:
|
||||||
|
|
||||||
|
1. Create a file in `web/assets/themes` called (for example) `new-theme.css`.
|
||||||
|
2. (Optional) Include the following comment at the top of your theme file to title and describe your theme:
|
||||||
|
```css
|
||||||
|
/*
|
||||||
|
theme-title: My New Theme
|
||||||
|
theme-description: This is an example theme
|
||||||
|
*/
|
||||||
|
```
|
||||||
|
You can use any text you like for these fields, but bear in mind whatever you write here will appear in the settings panel to help users when selecting a theme, so keep it short and sweet.
|
||||||
|
3. Fill out your custom CSS in the rest of the file. You can use one of the existing CSS files to guide you. Also see [this page](../user_guide/custom_css.md) for some rough guidelines about how to write accessible CSS.
|
||||||
|
4. Restart your instance so that the new CSS file is picked up.
|
||||||
|
|
||||||
|
!!! info
|
||||||
|
If you're using Docker for your deployment, you can mount theme files from the host machine into your GoToSocial `web/assets/themes` directory instead, by including entries for them in the `volumes` section of your Docker configuration.
|
||||||
|
|
||||||
|
For example, say you've created a theme on your host machine at `~/gotosocial/my-themes/new-theme.css`, you could mount that theme into the GoToSocial Docker container in the following way:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
volumes:
|
||||||
|
[.... some other volume entries ...]
|
||||||
|
- "~/gotosocial/my-themes/new-theme.css:/gotosocial/web/assets/themes/new-theme.css"
|
||||||
|
```
|
||||||
|
|
||||||
|
Bear in mind if you mount an entire directory to `/gotosocial/web/assets/themes` instead of mounting individual theme files, you'll override the default themes.
|
48
docs/advanced/healthchecks.md
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
# Health Checks
|
||||||
|
|
||||||
|
GoToSocial exposes two health check HTTP endpoints: `/readyz` and `/livez`.
|
||||||
|
|
||||||
|
These can be used to check whether GoToSocial is reachable and able to make simple database queries.
|
||||||
|
|
||||||
|
`/livez` will always return a 200 OK response with no body, in response to both GET and HEAD requests. This is useful to check if the GoToSocial service is alive.
|
||||||
|
|
||||||
|
`/readyz` will return a 200 OK response with no body, in response to both GET and HEAD requests, if GoToSocial is able to run a very simple SELECT query against the configured database backend. If an error occurs while running the SELECT, the error will be logged, and 500 Internal Server Error will be returned, with no body.
|
||||||
|
|
||||||
|
You can use the above endpoints to implement health checks in container runtimes / orchestration systems.
|
||||||
|
|
||||||
|
For example, in a Docker setup, you could add the following to your docker-compose.yaml:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
healthcheck:
|
||||||
|
test: wget --no-verbose --tries=1 --spider http://localhost:8080/readyz || exit 1
|
||||||
|
interval: 120s
|
||||||
|
retries: 5
|
||||||
|
start_period: 30s
|
||||||
|
timeout: 10s
|
||||||
|
```
|
||||||
|
|
||||||
|
The above health check will start after 30 seconds, and check every two minutes whether the service is available by doing a HEAD request to `/readyz`. If the check fails five times in a row, the service will be reported as unhealthy. You can use this in whatever orchestration system you are using to force the container to restart.
|
||||||
|
|
||||||
|
!!! warning
|
||||||
|
When doing database migrations on slow hardware, migration might take longer than the 10 minutes afforded by the above health check.
|
||||||
|
|
||||||
|
On such a system, you may want to increase the interval or number of retries of the health check to ensure that you don't stop GoToSocial in the middle of a migration (which is a very bad thing to do!).
|
||||||
|
|
||||||
|
!!! tip
|
||||||
|
Though the health check endpoints don't reveal any sensitive info, and run only very simple queries, you may want to avoid exposing them to the outside world. You could do this in nginx, for example, by adding the following snippet to your `server` stanza:
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
location /livez {
|
||||||
|
return 404;
|
||||||
|
}
|
||||||
|
location /readyz {
|
||||||
|
return 404;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This will cause nginx to intercept these requests *before* they are passed to GoToSocial, and just return 404 Not Found.
|
||||||
|
|
||||||
|
References:
|
||||||
|
|
||||||
|
- [Dockerfile reference](https://docs.docker.com/reference/dockerfile/#healthcheck)
|
||||||
|
- [Compose file reference](https://docs.docker.com/compose/compose-file/compose-file-v3/#healthcheck)
|
|
@ -15,3 +15,4 @@ We consider these topics advanced because applying them incorrectly does have th
|
||||||
* [Firewall configuration](security/firewall.md)
|
* [Firewall configuration](security/firewall.md)
|
||||||
* [Tracing](tracing.md)
|
* [Tracing](tracing.md)
|
||||||
* [Metrics](metrics.md)
|
* [Metrics](metrics.md)
|
||||||
|
* [Replicating SQLite](replicating-sqlite.md)
|
||||||
|
|
104
docs/advanced/replicating-sqlite.md
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
# Replicating SQLite
|
||||||
|
|
||||||
|
Next to your regular [backup methods](../admin/backup_and_restore.md), you might want to set up replication for disaster recovery to another path or external host.
|
||||||
|
|
||||||
|
For this to work properly, SQLite needs the journal mode to be configured in `WAL` mode, with synchronous mode set to `NORMAL`. This is the default configuration for GoToSocial.
|
||||||
|
|
||||||
|
You can check your settings in the configuration file. The journal mode is set in `db-sqlite-journal-mode` and the synchronous mode in `db-sqlite-synchronous`.
|
||||||
|
|
||||||
|
## Litestream on Linux
|
||||||
|
|
||||||
|
A relatively light, and fast way to set up replication with SQLite is by using [Litestream](https://litestream.io). It can be configured very easily and supports different backends like file based replication, S3 compatible storage and many other setups.
|
||||||
|
|
||||||
|
You can then install the prebuilt package by either the deb file on Linux, or building it from source on other distributions.
|
||||||
|
|
||||||
|
Using a .deb package on Linux:
|
||||||
|
|
||||||
|
Navigate to the [releases page](https://github.com/benbjohnson/litestream/releases/latest), and download the latest release (make sure to select the appropiate platform for the wget command below).
|
||||||
|
|
||||||
|
|
||||||
|
```bash
|
||||||
|
wget https://github.com/benbjohnson/litestream/releases/download/v0.3.13/litestream-v0.3.13-linux-amd64.deb
|
||||||
|
sudo dpkg -i litestream-*.deb
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuring Litestream
|
||||||
|
|
||||||
|
Configuration is done by editing the configuration file. It's located in /etc/litestream.yml.
|
||||||
|
|
||||||
|
### Configuring file based replication
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
dbs:
|
||||||
|
- path: /gotosocial/sqlite.db
|
||||||
|
- path: /backup/sqlite.db
|
||||||
|
```
|
||||||
|
|
||||||
|
### Configuring S3 based replication
|
||||||
|
|
||||||
|
Set up a bucket for replication, and make sure to set it to be private.
|
||||||
|
Make sure to replace the example `access-key-id` and `secret-access-key` with the proper values from your dashboard.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
access-key-id: AKIAJSIE27KKMHXI3BJQ
|
||||||
|
secret-access-key: 5bEYu26084qjSFyclM/f2pz4gviSfoOg+mFwBH39
|
||||||
|
|
||||||
|
dbs:
|
||||||
|
- path: /gotosocial/sqlite.db
|
||||||
|
- url: s3://my.bucket.com/db
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
When using a S3 compatible storage provider you will need to set an endpoint.
|
||||||
|
For example for minio this can be done with the following configuration.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
access-key-id: miniouser
|
||||||
|
secret-access-key: miniopassword
|
||||||
|
|
||||||
|
dbs:
|
||||||
|
- path: /gotosocial/sqlite.db
|
||||||
|
- type: s3
|
||||||
|
bucket: mybucket
|
||||||
|
path: sqlite.db
|
||||||
|
endpoint: minio:9000
|
||||||
|
```
|
||||||
|
|
||||||
|
## Enabling replication
|
||||||
|
|
||||||
|
You can enable replication on Linux by enabling the Litestream service.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo systemctl enable litestream
|
||||||
|
sudo systemctl start litestream
|
||||||
|
```
|
||||||
|
|
||||||
|
Check if it's running properly using `sudo journalctl -u litestream -f`.
|
||||||
|
|
||||||
|
If you need to change the configuration file, restart Litestream:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo systemctl restart litestream
|
||||||
|
```
|
||||||
|
|
||||||
|
### Recovering from the configured backend
|
||||||
|
|
||||||
|
You can pull down a recovery file from the stored backend with the following simple command.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo litestream restore
|
||||||
|
```
|
||||||
|
|
||||||
|
If you have configured multiple files to be backupped, or have multiple replicas, specify what you want to do.
|
||||||
|
|
||||||
|
For filebased replication:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo litestream restore -o /gotosocial/sqlite.db /backup/sqlite.db
|
||||||
|
```
|
||||||
|
|
||||||
|
For s3 based replication:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo litestream restore -o /gotosocial/sqlite.db s3://bucketname/db
|
||||||
|
```
|
BIN
docs/assets/signup-account.png
Normal file
After Width: | Height: | Size: 76 KiB |
BIN
docs/assets/signup-form.png
Normal file
After Width: | Height: | Size: 110 KiB |
BIN
docs/assets/signup-pending.png
Normal file
After Width: | Height: | Size: 72 KiB |
Before Width: | Height: | Size: 104 KiB |
Before Width: | Height: | Size: 137 KiB After Width: | Height: | Size: 102 KiB |
BIN
docs/assets/user-settings-settings.png
Normal file
After Width: | Height: | Size: 108 KiB |
|
@ -9,15 +9,11 @@
|
||||||
|
|
||||||
# Config pertaining to creation and maintenance of accounts on the server, as well as defaults for new accounts.
|
# Config pertaining to creation and maintenance of accounts on the server, as well as defaults for new accounts.
|
||||||
|
|
||||||
# Bool. Do we want people to be able to just submit sign up requests, or do we want invite only?
|
# Bool. Allow people to submit new sign-up / registration requests via the form at /signup.
|
||||||
|
#
|
||||||
# Options: [true, false]
|
# Options: [true, false]
|
||||||
# Default: true
|
# Default: false
|
||||||
accounts-registration-open: true
|
accounts-registration-open: false
|
||||||
|
|
||||||
# Bool. Do sign up requests require approval from an admin/moderator before an account can sign in/use the server?
|
|
||||||
# Options: [true, false]
|
|
||||||
# Default: true
|
|
||||||
accounts-approval-required: true
|
|
||||||
|
|
||||||
# Bool. Are sign up requests required to submit a reason for the request (eg., an explanation of why they want to join the instance)?
|
# Bool. Are sign up requests required to submit a reason for the request (eg., an explanation of why they want to join the instance)?
|
||||||
# Options: [true, false]
|
# Options: [true, false]
|
||||||
|
|
|
@ -57,6 +57,10 @@ advanced-rate-limit-requests: 300
|
||||||
# applied on their requests, and rate limit headers will not be
|
# applied on their requests, and rate limit headers will not be
|
||||||
# set for those requests.
|
# set for those requests.
|
||||||
#
|
#
|
||||||
|
# For IPv6, we only take subnets up to a /64 into account. If you
|
||||||
|
# want to open up a larger prefix, you'll need to list multiple
|
||||||
|
# prefixes instead.
|
||||||
|
#
|
||||||
# This can be useful in the following example cases (and probably
|
# This can be useful in the following example cases (and probably
|
||||||
# a bunch of others as well):
|
# a bunch of others as well):
|
||||||
#
|
#
|
||||||
|
@ -76,7 +80,7 @@ advanced-rate-limit-requests: 300
|
||||||
# wide a range. If in doubt, be too restrictive rather than too
|
# wide a range. If in doubt, be too restrictive rather than too
|
||||||
# lenient, and adjust as you go.
|
# lenient, and adjust as you go.
|
||||||
#
|
#
|
||||||
# Example: ["192.168.0.0/16"]
|
# Example: ["192.168.0.0/16", "2001:DB8:FACE:CAFE::/64"]
|
||||||
# Default: []
|
# Default: []
|
||||||
advanced-rate-limit-exceptions: []
|
advanced-rate-limit-exceptions: []
|
||||||
|
|
||||||
|
@ -115,15 +119,10 @@ advanced-throttling-multiplier: 8
|
||||||
# Default: "30s"
|
# Default: "30s"
|
||||||
advanced-throttling-retry-after: "30s"
|
advanced-throttling-retry-after: "30s"
|
||||||
|
|
||||||
# Int. CPU multiplier for the amount of goroutines to spawn in order to send messages via ActivityPub.
|
# Int. CPU multiplier for the fixed number of goroutines to spawn in order to send messages via ActivityPub.
|
||||||
# Messages will be batched so that at most multiplier * CPU count messages will be sent out at once.
|
# Messages will be batched and pushed to a singular queue, from which multiplier * CPU count goroutines will
|
||||||
# This can be tuned to limit concurrent POSTing to remote inboxes, preventing your instance CPU
|
# pull and attempt deliveries. This can be tuned to limit concurrent posting to remote inboxes, preventing
|
||||||
# usage from skyrocketing when an account with many followers posts a new status.
|
# your instance CPU usage skyrocketing when accounts with many followers post statuses.
|
||||||
#
|
|
||||||
# Messages are split among available senders, and each sender processes its assigned messages in serial.
|
|
||||||
# For example, say a user with 1000 followers is on an instance with 2 CPUs. With the default multiplier
|
|
||||||
# of 2, this means 4 senders would be in process at once on this instance. When the user creates a new post,
|
|
||||||
# each sender would end up iterating through about 250 Create messages + delivering them to remote instances.
|
|
||||||
#
|
#
|
||||||
# If you set this to 0 or less, only 1 sender will be used regardless of CPU count. This may be
|
# If you set this to 0 or less, only 1 sender will be used regardless of CPU count. This may be
|
||||||
# useful in cases where you are working with very tight network or CPU constraints.
|
# useful in cases where you are working with very tight network or CPU constraints.
|
||||||
|
@ -164,4 +163,23 @@ advanced-sender-multiplier: 2
|
||||||
# Example: ["s3.example.org", "some-bucket-name.s3.example.org"]
|
# Example: ["s3.example.org", "some-bucket-name.s3.example.org"]
|
||||||
# Default: []
|
# Default: []
|
||||||
advanced-csp-extra-uris: []
|
advanced-csp-extra-uris: []
|
||||||
|
|
||||||
|
# String. HTTP request header filtering mode to use for this instance.
|
||||||
|
#
|
||||||
|
# "block" -- only requests that are explicitly blocked by header filters
|
||||||
|
# will be denied (unless they are also explicitly allowed).
|
||||||
|
#
|
||||||
|
# "allow" -- only requests that are explicitly allowed by header filters
|
||||||
|
# will be accepted (unless they are also explicitly blocked).
|
||||||
|
# This mode is considered experimental and will almost certainly
|
||||||
|
# break access to your instance unless you are very careful.
|
||||||
|
#
|
||||||
|
# "" -- request header filtering disabled.
|
||||||
|
#
|
||||||
|
# For more details on block and allow modes, check the documentation at:
|
||||||
|
# https://docs.gotosocial.org/en/latest/admin/request_filtering_modes
|
||||||
|
#
|
||||||
|
# Options: ["block", "allow", ""]
|
||||||
|
# Default: ""
|
||||||
|
advanced-header-filter-mode: ""
|
||||||
```
|
```
|
||||||
|
|
|
@ -6,7 +6,7 @@ By default, GoToSocial will use Postgres, but this is easy to change.
|
||||||
|
|
||||||
## 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 lower-powered machines like Raspberry Pi, where a dedicated database would be overkill.
|
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 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.
|
||||||
|
|
||||||
|
@ -176,4 +176,13 @@ db-sqlite-cache-size: "8MiB"
|
||||||
# Examples: ["0s", "1s", "30s", "1m", "5m"]
|
# Examples: ["0s", "1s", "30s", "1m", "5m"]
|
||||||
# Default: "30m"
|
# Default: "30m"
|
||||||
db-sqlite-busy-timeout: "30m"
|
db-sqlite-busy-timeout: "30m"
|
||||||
|
|
||||||
|
cache:
|
||||||
|
# cache.memory-target sets a target limit that
|
||||||
|
# the application will try to keep it's caches
|
||||||
|
# within. This is based on estimated sizes of
|
||||||
|
# in-memory objects, and so NOT AT ALL EXACT.
|
||||||
|
# Examples: ["100MiB", "200MiB", "500MiB", "1GiB"]
|
||||||
|
# Default: "100MiB"
|
||||||
|
memory-target: "100MiB"
|
||||||
```
|
```
|
||||||
|
|
|
@ -74,7 +74,7 @@ host: "localhost"
|
||||||
# DO NOT change this after your server has already run once, or you will break things!
|
# DO NOT change this after your server has already run once, or you will break things!
|
||||||
#
|
#
|
||||||
# Please read the appropriate section of the installation guide before you go messing around with this setting:
|
# Please read the appropriate section of the installation guide before you go messing around with this setting:
|
||||||
# https://docs.gotosocial.org/installation_guide/advanced/#can-i-host-my-instance-at-fediexampleorg-but-have-just-exampleorg-in-my-username
|
# https://docs.gotosocial.org/en/latest/advanced/host-account-domain/
|
||||||
#
|
#
|
||||||
# Examples: ["example.org","server.com"]
|
# Examples: ["example.org","server.com"]
|
||||||
# Default: ""
|
# Default: ""
|
||||||
|
|
|
@ -6,6 +6,8 @@ Configuring GoToSocial to send emails is **not required** in order to have a pro
|
||||||
|
|
||||||
In order to make GoToSocial email sending work, you need an smtp-compatible mail service running somewhere, either as a server on the same machine that GoToSocial is running on, or via an external service like [Mailgun](https://mailgun.com). It may also be possible to use a free personal email address for sending emails, if your email provider supports smtp (check with them--most do), but you might run into trouble sending lots of emails.
|
In order to make GoToSocial email sending work, you need an smtp-compatible mail service running somewhere, either as a server on the same machine that GoToSocial is running on, or via an external service like [Mailgun](https://mailgun.com). It may also be possible to use a free personal email address for sending emails, if your email provider supports smtp (check with them--most do), but you might run into trouble sending lots of emails.
|
||||||
|
|
||||||
|
To validate your configuration, you can use the "Administration -> Actions -> Email" section of the settings panel to send a test email.
|
||||||
|
|
||||||
## Settings
|
## Settings
|
||||||
|
|
||||||
The configuration options for smtp are as follows:
|
The configuration options for smtp are as follows:
|
||||||
|
|
|
@ -5,10 +5,11 @@ channels:
|
||||||
dependencies:
|
dependencies:
|
||||||
- cairosvg==2.7.1
|
- cairosvg==2.7.1
|
||||||
- mkdocs-material-extensions==1.3.1
|
- mkdocs-material-extensions==1.3.1
|
||||||
- mkdocs-material==9.5.8
|
- mkdocs-material==9.5.15
|
||||||
- mkdocs==1.5.3
|
- mkdocs==1.5.3
|
||||||
- pillow==10.0.0
|
- pillow==10.2.0
|
||||||
- pip==23.3.1
|
- pip==24
|
||||||
- python==3.11.3=h2755cc3_0_cpython
|
- python==3.12
|
||||||
|
- mkdocs-include-markdown-plugin=6.2.0
|
||||||
- pip:
|
- pip:
|
||||||
- mkdocs-swagger-ui-tag==0.6.8
|
- mkdocs-swagger-ui-tag==0.6.9
|
||||||
|
|
14
docs/faq.md
|
@ -34,18 +34,8 @@ To see posts, you have to start following people! Once you've followed a few peo
|
||||||
|
|
||||||
## How can I sign up for a server?
|
## How can I sign up for a server?
|
||||||
|
|
||||||
Right now the only way to create an account is by the server's admin to run a command directly on the server. A web-based signup flow is in the roadmap but not implemented yet.
|
We introduced a sign-up flow in v0.16.0. The server you want to sign up to must have enabled registrations/sign-ups, as detailed [right here](./admin/signups.md).
|
||||||
|
|
||||||
## Why's it still in alpha?
|
## Why's it still in alpha?
|
||||||
|
|
||||||
Take a look at the [list of open bugs](https://github.com/superseriousbusiness/gotosocial/issues?q=is%3Aissue+is%3Aopen+label%3Abug) and the [roadmap](https://github.com/superseriousbusiness/gotosocial/blob/main/ROADMAP.md) for a more detailed rundown, but the main missing features at the time of this writing are:
|
Take a look at the [list of open bugs](https://github.com/superseriousbusiness/gotosocial/issues?q=is%3Aissue+is%3Aopen+label%3Abug) and the [roadmap](https://github.com/superseriousbusiness/gotosocial/blob/main/ROADMAP.md) for a more detailed rundown.
|
||||||
|
|
||||||
- backfill of posts
|
|
||||||
- web-based signup
|
|
||||||
- scheduling posts
|
|
||||||
- account migration
|
|
||||||
- shared block lists across servers
|
|
||||||
|
|
||||||
## Will you support tables in Markdown?
|
|
||||||
|
|
||||||
Not at the moment, as most clients handle them terribly.
|
|
||||||
|
|
|
@ -42,7 +42,9 @@ ED25519
|
||||||
|
|
||||||
GoToSocial request signing is implemented in [internal/transport](https://github.com/superseriousbusiness/gotosocial/blob/main/internal/transport/signing.go).
|
GoToSocial request signing is implemented in [internal/transport](https://github.com/superseriousbusiness/gotosocial/blob/main/internal/transport/signing.go).
|
||||||
|
|
||||||
When assembling signatures:
|
Once https://github.com/superseriousbusiness/gotosocial/issues/2991 is resolved, GoToSocial will use the `(created)` pseudo-header instead of `date`.
|
||||||
|
|
||||||
|
For now however, when assembling signatures:
|
||||||
|
|
||||||
- outgoing `GET` requests use `(request-target) host date`
|
- outgoing `GET` requests use `(request-target) host date`
|
||||||
- outgoing `POST` requests use `(request-target) host date digest`
|
- outgoing `POST` requests use `(request-target) host date digest`
|
||||||
|
@ -106,7 +108,9 @@ This ensures that remote servers cannot flood a GoToSocial instance with spuriou
|
||||||
|
|
||||||
For more details on request throttling and rate limiting behavior, please see the [throttling](../api/throttling.md) and [rate limiting](../api/ratelimiting.md) documents.
|
For more details on request throttling and rate limiting behavior, please see the [throttling](../api/throttling.md) and [rate limiting](../api/ratelimiting.md) documents.
|
||||||
|
|
||||||
## Inbox
|
## Actors and Actor Properties
|
||||||
|
|
||||||
|
### Inbox
|
||||||
|
|
||||||
GoToSocial implements Inboxes for Actors following the ActivityPub specification [here](https://www.w3.org/TR/activitypub/#inbox).
|
GoToSocial implements Inboxes for Actors following the ActivityPub specification [here](https://www.w3.org/TR/activitypub/#inbox).
|
||||||
|
|
||||||
|
@ -132,7 +136,7 @@ Invalidly-formed Inbox POST requests will receive a [400 - Bad Request](https://
|
||||||
|
|
||||||
Even if GoToSocial returns a `202` status code, it may not continue processing the Activity delivered, depending on the originator(s), target(s) and type of the Activity. ActivityPub is an extensive protocol, and GoToSocial does not cover every combination of Activity and Object.
|
Even if GoToSocial returns a `202` status code, it may not continue processing the Activity delivered, depending on the originator(s), target(s) and type of the Activity. ActivityPub is an extensive protocol, and GoToSocial does not cover every combination of Activity and Object.
|
||||||
|
|
||||||
## Outbox
|
### Outbox
|
||||||
|
|
||||||
GoToSocial implements Outboxes for Actors (ie., instance accounts) following the ActivityPub specification [here](https://www.w3.org/TR/activitypub/#outbox).
|
GoToSocial implements Outboxes for Actors (ie., instance accounts) following the ActivityPub specification [here](https://www.w3.org/TR/activitypub/#outbox).
|
||||||
|
|
||||||
|
@ -142,10 +146,10 @@ The server will return an OrderedCollection of the following structure:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"@context": "https://www.w3.org/ns/activitystreams",
|
"@context": "https://www.w3.org/ns/activitystreams",
|
||||||
"id": "https://example.org/users/whatever/outbox",
|
"id": "https://example.org/users/whatever/outbox",
|
||||||
"type": "OrderedCollection",
|
"type": "OrderedCollection",
|
||||||
"first": "https://example.org/users/whatever/outbox?page=true"
|
"first": "https://example.org/users/whatever/outbox?page=true"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -153,26 +157,26 @@ Note that the `OrderedCollection` itself contains no items. Callers must derefer
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"id": "https://example.org/users/whatever/outbox?page=true",
|
"id": "https://example.org/users/whatever/outbox?page=true",
|
||||||
"type": "OrderedCollectionPage",
|
"type": "OrderedCollectionPage",
|
||||||
"next": "https://example.org/users/whatever/outbox?max_id=01FJC1Q0E3SSQR59TD2M1KP4V8&page=true",
|
"next": "https://example.org/users/whatever/outbox?max_id=01FJC1Q0E3SSQR59TD2M1KP4V8&page=true",
|
||||||
"prev": "https://example.org/users/whatever/outbox?min_id=01FJC1Q0E3SSQR59TD2M1KP4V8&page=true",
|
"prev": "https://example.org/users/whatever/outbox?min_id=01FJC1Q0E3SSQR59TD2M1KP4V8&page=true",
|
||||||
"partOf": "https://example.org/users/whatever/outbox",
|
"partOf": "https://example.org/users/whatever/outbox",
|
||||||
"orderedItems": [
|
"orderedItems": [
|
||||||
{
|
{
|
||||||
"id": "https://example.org/users/whatever/statuses/01FJC1MKPVX2VMWP2ST93Q90K7/activity",
|
"id": "https://example.org/users/whatever/statuses/01FJC1MKPVX2VMWP2ST93Q90K7/activity",
|
||||||
"type": "Create",
|
"type": "Create",
|
||||||
"actor": "https://example.org/users/whatever",
|
"actor": "https://example.org/users/whatever",
|
||||||
"published": "2021-10-18T20:06:18Z",
|
"published": "2021-10-18T20:06:18Z",
|
||||||
"to": [
|
"to": [
|
||||||
"https://www.w3.org/ns/activitystreams#Public"
|
"https://www.w3.org/ns/activitystreams#Public"
|
||||||
],
|
],
|
||||||
"cc": [
|
"cc": [
|
||||||
"https://example.org/users/whatever/followers"
|
"https://example.org/users/whatever/followers"
|
||||||
],
|
],
|
||||||
"object": "https://example.org/users/whatever/statuses/01FJC1MKPVX2VMWP2ST93Q90K7"
|
"object": "https://example.org/users/whatever/statuses/01FJC1MKPVX2VMWP2ST93Q90K7"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -180,6 +184,58 @@ The `orderedItems` array will contain up to 30 entries. To get more entries beyo
|
||||||
|
|
||||||
Note that in the returned `orderedItems`, all activity types will be `Create`. On each activity, the `object` field will be the AP URI of an original public status created by the Actor who owns the Outbox (ie., a `Note` with `https://www.w3.org/ns/activitystreams#Public` in the `to` field, which is not a reply to another status). Callers can use the returned AP URIs to dereference the content of the notes.
|
Note that in the returned `orderedItems`, all activity types will be `Create`. On each activity, the `object` field will be the AP URI of an original public status created by the Actor who owns the Outbox (ie., a `Note` with `https://www.w3.org/ns/activitystreams#Public` in the `to` field, which is not a reply to another status). Callers can use the returned AP URIs to dereference the content of the notes.
|
||||||
|
|
||||||
|
### Followers / Following Collections
|
||||||
|
|
||||||
|
GoToSocial implements followers and following collections as `OrderedCollection`s. A properly-signed `GET` request to an Actor's Following collection, for example, will return something like:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"@context": "https://www.w3.org/ns/activitystreams",
|
||||||
|
"first": "https://example.org/users/someone/following?limit=40",
|
||||||
|
"id": "https://example.org/users/someone/following",
|
||||||
|
"totalItems": 397,
|
||||||
|
"type": "OrderedCollection"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
From there, you can use the `first` page to start getting items. For example, a `GET` request to `https://example.org/users/someone/following?limit=40` will produce something like:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"@context": "https://www.w3.org/ns/activitystreams",
|
||||||
|
"id": "https://example.org/users/someone/following?limit=40",
|
||||||
|
"next": "https://example.org/users/someone/following?limit=40&max_id=01V1AY4ZJT4JK1NT271SH2WMGH",
|
||||||
|
"orderedItems": [
|
||||||
|
"https://example.org/users/someone_else",
|
||||||
|
"https://somewhere.else.example.org/users/another_account",
|
||||||
|
[... 38 more entries here ...]
|
||||||
|
],
|
||||||
|
"partOf": "https://example.org/users/someone/following",
|
||||||
|
"prev": "https://example.org/users/someone/following?limit=40&since_id=021HKBY346X7BPFYANPPJN493P",
|
||||||
|
"totalItems": 397,
|
||||||
|
"type": "OrderedCollectionPage"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
You can then use the `next` and `prev` endpoints to page down and up through the OrderedCollection.
|
||||||
|
|
||||||
|
!!! Info "Hidden Followers / Following Collections"
|
||||||
|
|
||||||
|
GoToSocial allows users to hide their followers/following collections if they wish.
|
||||||
|
|
||||||
|
If a user has chosen to hide their collections, then only a stub collection with `totalItems` will be returned, and you will not be able to page through the Actor's followers/following collections.
|
||||||
|
|
||||||
|
A `GET` to the following collection of an Actor with hidden collections will look like:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"@context": "https://www.w3.org/ns/activitystreams",
|
||||||
|
"id": "https://example.org/users/someone/following",
|
||||||
|
"type": "OrderedCollection",
|
||||||
|
"totalItems": 397
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Conversation Threads
|
## Conversation Threads
|
||||||
|
|
||||||
Due to the nature of decentralization and federation, it is practically impossible for any one server on the fediverse to be aware of every post in a given conversation thread.
|
Due to the nature of decentralization and federation, it is practically impossible for any one server on the fediverse to be aware of every post in a given conversation thread.
|
||||||
|
@ -846,4 +902,44 @@ GoToSocial will only set `movedTo` on outgoing Actors when an account `Move` has
|
||||||
|
|
||||||
### `Move` Activity
|
### `Move` Activity
|
||||||
|
|
||||||
TODO: document how `Move` works!
|
To actually trigger account migrations, GoToSocial uses the `Move` Activity with Actor URI as Object and Target, for example:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"@context": "https://www.w3.org/ns/activitystreams",
|
||||||
|
"id": "https://example.org/users/1happyturtle/moves/01HR9FDFCAGM7JYPMWNTFRDQE9",
|
||||||
|
"actor": "https://example.org/users/1happyturtle",
|
||||||
|
"type": "Move",
|
||||||
|
"object": "https://example.org/users/1happyturtle",
|
||||||
|
"target": "https://another-server.com/users/my_new_account_hurray",
|
||||||
|
"to": "https://example.org/users/1happyturtle/followers"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
In the above `Move`, Actor `https://example.org/users/1happyturtle` indicates that their account is moving to the URI `https://another-server.com/users/my_new_account_hurray`.
|
||||||
|
|
||||||
|
#### Incoming
|
||||||
|
|
||||||
|
On receiving a `Move` activity in an Actor's Inbox, GoToSocial will first validate the `Move` by making the following checks:
|
||||||
|
|
||||||
|
1. Request was signed by `actor`.
|
||||||
|
2. `actor` and `object` fields are the same (you can't `Move` someone else's account).
|
||||||
|
3. `actor` has not already moved somewhere else.
|
||||||
|
4. `target` is a valid Actor URI: retrievable, not suspended, not already moved, and on a domain that's not defederated by the GoToSocial instance that received the `Move`.
|
||||||
|
5. `target` has `alsoKnownAs` set to the `actor` that sent the `Move`. In this example, `https://another-server.com/users/my_new_account_hurray` must have an `alsoKnownAs` value that includes `https://example.org/users/1happyturtle`.
|
||||||
|
|
||||||
|
If checks pass, then GoToSocial will process the `Move` by redirecting followers to the new account:
|
||||||
|
|
||||||
|
1. Select all followers on this GtS instance of the `actor` doing the `Move`.
|
||||||
|
2. For each local follower selected in this way, send a follow request from that follower to the `target` of the `Move`.
|
||||||
|
3. Remove all follows targeting the "old" `actor`.
|
||||||
|
|
||||||
|
The end result of this is that all followers of `https://example.org/users/1happyturtle` on the receiving instance will now be following `https://another-server.com/users/my_new_account_hurray` instead.
|
||||||
|
|
||||||
|
GoToSocial will also remove all follow and pending follow requests owned by the `actor` doing the `Move`; it's up to the `target` account to send follow requests out again.
|
||||||
|
|
||||||
|
To prevent potential DoS vectors, GoToSocial enforces a 7-day cooldown on `Move`s. Once an account has successfully moved, GoToSocial will not process further moves from the new account until 7 days after the previous move.
|
||||||
|
|
||||||
|
#### Outgoing
|
||||||
|
|
||||||
|
Outgoing account migrations use the `Move` Activity in much the same way. When an Actor on a GoToSocial instance wants to `Move`, GtS will first check and validate the `Move` target, and ensure it has an `alsoKnownAs` entry equal to the Actor doing the `Move`. On successful validation, a `Move` message will be sent out to all of the moving Actor's followers, indicating the `target` of the Move. GoToSocial expects remote instances to transfer the `actor`'s followers to the `target`.
|
||||||
|
|
|
@ -14,8 +14,8 @@ GoToSocial supports both SQLite and Postgres and you can start using either. We
|
||||||
|
|
||||||
For databases to perform properly, they should be run on fast storage that operates with low and stable latency. It is possible to run databases on network attached storage, but this adds variable latency and network congestion to the mix, as well as potential I/O contention on the origin storage.
|
For databases to perform properly, they should be run on fast storage that operates with low and stable latency. It is possible to run databases on network attached storage, but this adds variable latency and network congestion to the mix, as well as potential I/O contention on the origin storage.
|
||||||
|
|
||||||
!!! danger
|
!!! danger "Cloud Storage Volumes"
|
||||||
The performance of Hetzner Cloud Volumes is not guaranteed and seems to have very volatile latency. You're going to have a bad time running your database on those with extremely poor query performance for even the most basic operations. Before filing performance issues against GoToSocial, make sure the problems reproduce with local storage.
|
Not all cloud VPS storage offerings are equal, and just because something claims to be backed by an SSD doesn't mean that it will necessarily be suitable to run a GoToSocial instance on. Please see the [Server/VPS section](#vps) section below.
|
||||||
|
|
||||||
SQLite is great for a single-user instance. If you're planning on hosting multiple people it's advisable to use Postgres instead. You can always use Postgres regardless of the instance size.
|
SQLite is great for a single-user instance. If you're planning on hosting multiple people it's advisable to use Postgres instead. You can always use Postgres regardless of the instance size.
|
||||||
|
|
||||||
|
@ -30,21 +30,10 @@ You'll commonly see usernames existing at the apex of the domain, for example `@
|
||||||
|
|
||||||
It is possible to have usernames like `@me@example.org` but have GoToSocial running on `social.example.org` instead. This is done by distinguishing between the API domain, called the "host", and the domain used for usernames, called the "account domain".
|
It is possible to have usernames like `@me@example.org` but have GoToSocial running on `social.example.org` instead. This is done by distinguishing between the API domain, called the "host", and the domain used for usernames, called the "account domain".
|
||||||
|
|
||||||
|
If you intend to deploy your GoToSocial instance in this way, please read the [Split-domain deployments](../advanced/host-account-domain.md) document for details on how to do this.
|
||||||
|
|
||||||
!!! danger
|
!!! danger
|
||||||
It's not possible to safely change whether the host and account domain are different after the fact. It requires regenerating the database and will cause confusion for any server you have already federated with.
|
It's not possible to safely change whether the host and account domain are different after the fact. It requires regenerating the database and will cause confusion for any server you have already federated with. Once your instance host and account domain are set, they're set.
|
||||||
|
|
||||||
When using a single domain, you only need to configure the "host" in the GoToSocial configuration:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
host: "example.org"
|
|
||||||
```
|
|
||||||
|
|
||||||
When using a split domain approach, you need to configure both the "host" and the "account-domain":
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
host: "social.example.org"
|
|
||||||
account-domain: "example.org"
|
|
||||||
```
|
|
||||||
|
|
||||||
## TLS
|
## TLS
|
||||||
|
|
||||||
|
@ -55,20 +44,46 @@ GoToSocial comes with built-in support for provisioning certificates through Let
|
||||||
!!! tip
|
!!! tip
|
||||||
Make sure you configure the use of modern versions of TLS, TLSv1.2 and higher, in order to keep communications between servers and clients safe. When GoToSocial handles TLS termination this is done automatically for you. If you have a reverse-proxy in use, use the [Mozilla SSL Configuration Generator](https://ssl-config.mozilla.org/).
|
Make sure you configure the use of modern versions of TLS, TLSv1.2 and higher, in order to keep communications between servers and clients safe. When GoToSocial handles TLS termination this is done automatically for you. If you have a reverse-proxy in use, use the [Mozilla SSL Configuration Generator](https://ssl-config.mozilla.org/).
|
||||||
|
|
||||||
## Server / VPS
|
## Server / VPS System Requirements
|
||||||
|
|
||||||
!!! bug "Clustering / multi-node deployments"
|
!!! warning "Clustering / multi-node deployments"
|
||||||
GoToSocial does not support [clustering or any form of multi-node deployment](https://github.com/superseriousbusiness/gotosocial/issues/1749). Though multiple GtS instances can use the same Postgres database and either shared local storage or the same object bucket, GtS relies on a lot of internal caching to keep things fast. There is no mechanism for synchronising these caches between instances. Without it, you'll get all kinds of odd and inconsistent behaviour.
|
GoToSocial does not support [clustering or any form of multi-node deployment](https://github.com/superseriousbusiness/gotosocial/issues/1749). Though multiple GtS instances can use the same Postgres database and either shared local storage or the same object bucket, GtS relies on a lot of internal caching to keep things fast. There is no mechanism for synchronising these caches between instances. Without it, you'll get all kinds of odd and inconsistent behaviour.
|
||||||
|
|
||||||
GoToSocial aims to fit in small spaces so we try and ensure that the system requirements are fairly minimal: for a single-user instance with about 100 followers/followees, it uses somewhere between 50 to 100MB of RAM. CPU usage is only intensive when handling media (encoding blurhashes, mostly) and/or doing a lot of federation requests at the same time.
|
GoToSocial aims to fit in small spaces so we try and ensure that the system requirements are fairly minimal.
|
||||||
|
|
||||||
These light requirements mean GtS runs pretty well on something like a Raspberry Pi (a €40 single-board computer). It's been tested on a Raspberry Pi Zero W as well (a €9 computer smaller than a credit card), but it's not quite able to run on that. It should run on a Raspberry Pi Zero W 2 (which costs €14!), but we haven't tested that yet. You can also repurpose an old laptop or desktop to run GoToSocial for you.
|
### Memory
|
||||||
|
|
||||||
If you decide to use a VPS instead, you can spin yourself up something cheap with Linux running on it. Most of the VPS offerings in the €2-€5 range will perform admirably for a personal GoToSocial instance.
|
For a single-user instance with about 100-300 followers/followees, GoToSocial will likely hover consistently between 100MB to 250MB of RAM usage once the internal caches are hydrated.
|
||||||
|
|
||||||
|
RAM usage may temporarily spike higher during periods of load (for example, when a status gets boosted by someone with many followers), so you should account for some overhead.
|
||||||
|
|
||||||
|
512MB to 1GB of total RAM should be enough.
|
||||||
|
|
||||||
|
In memory constrained environments, you can try setting `cache.memory-target` to a value lower than the default 100MB (see the database configuration options [here](../configuration/database.md#settings)).
|
||||||
|
|
||||||
|
### CPU
|
||||||
|
|
||||||
|
CPU usage is only intensive when handling media (encoding blurhashes, mostly) and/or handling a lot of federation requests at the same time. 1 decent CPU core should be fine.
|
||||||
|
|
||||||
|
### 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.
|
||||||
|
|
||||||
|
### VPS
|
||||||
|
|
||||||
|
If you decide to use a VPS instead, you can spin yourself up something cheap with Linux running on it. Most of the VPS offerings in the €2-€5 per month range will perform admirably for a personal GoToSocial instance.
|
||||||
|
|
||||||
[Hostwinds](https://www.hostwinds.com/) is a good option here: it's cheap and they throw in a static IP address for free.
|
[Hostwinds](https://www.hostwinds.com/) is a good option here: it's cheap and they throw in a static IP address for free.
|
||||||
|
|
||||||
[Greenhost](https://greenhost.net) is also great: it has zero CO2 emissions, but is a bit more costly.
|
[Greenhost](https://greenhost.net) is also great: it has zero CO2 emissions, but is a bit more costly. Their 1GB, 1-cpu VPS works great for a single-user or small instance.
|
||||||
|
|
||||||
|
!!! danger "Oracle Free Tier"
|
||||||
|
[Oracle Cloud Free Tier](https://www.oracle.com/cloud/free/) servers are not suitable for a GoToSocial deployment if you intend to federate with more than a handful of other instances and users.
|
||||||
|
|
||||||
|
GoToSocial admins running on Oracle Cloud Free Tier have reported that their instances become extremely slow or unresponsive during periods of moderate load. This is most likely due to memory or storage latency, which causes even simple database queries to take a long time to run.
|
||||||
|
|
||||||
|
!!! danger "Hetzner Cloud Volume"
|
||||||
|
The [performance of Hetzner Cloud Volumes](https://github.com/superseriousbusiness/gotosocial/issues/2471#issuecomment-1891098323) is not guaranteed and seems to have very volatile latency. You're going to have a bad time running your database on those, with extremely poor query performance for even the most basic operations. Before filing performance issues against GoToSocial, make sure the problems reproduce with local storage.
|
||||||
|
|
||||||
### Distribution system requirements
|
### Distribution system requirements
|
||||||
|
|
||||||
|
@ -90,6 +105,22 @@ The BSD family of distributions don't document memory requirements as much, but
|
||||||
[rhelreq]: https://access.redhat.com/articles/rhel-limits#minimum-required-memory-3
|
[rhelreq]: https://access.redhat.com/articles/rhel-limits#minimum-required-memory-3
|
||||||
[fedorareq]: https://docs.fedoraproject.org/en-US/fedora/latest/release-notes/welcome/Hardware_Overview/#hardware_overview-specs
|
[fedorareq]: https://docs.fedoraproject.org/en-US/fedora/latest/release-notes/welcome/Hardware_Overview/#hardware_overview-specs
|
||||||
|
|
||||||
|
## Ports
|
||||||
|
|
||||||
|
GoToSocial needs ports `80` and `443` open.
|
||||||
|
|
||||||
|
* `80` is used for Lets Encrypt. As such, you don't need it if you don't use the built-in Lets Encrypt provisioning.
|
||||||
|
* `443` is used to serve the API on with TLS and is what any instance you're federating with will try to connect to.
|
||||||
|
|
||||||
|
If you can't leave `443` and `80` open on the machine, don't worry! You can configure these ports in GoToSocial, but you'll have to also configure port forwarding to properly forward traffic on `443` and `80` to whatever ports you choose.
|
||||||
|
|
||||||
|
!!! tip
|
||||||
|
You should configure a firewall on your machine, as well as some protection against brute-force SSH login attempts and the like. Take a look at our [firewall documentation](../advanced/security/firewall.md) for pointers on what to configure and tools that can help you out.
|
||||||
|
|
||||||
|
## Tuning
|
||||||
|
|
||||||
|
Aside from the many instance tuning options present in the [example config file](https://github.com/superseriousbusiness/gotosocial/blob/main/example/config.yaml) you can do additional tuning on the machine your GoToSocial instance is running on.
|
||||||
|
|
||||||
### Swap
|
### Swap
|
||||||
|
|
||||||
It is possible to run a system without swap. In order to safely do so and ensure consistent performance and service availability, you need to tune the kernel, system and your workloads accordingly. This requires a good understanding of your kernel's memory management system as well as the memory usage patterns of the workloads you're running.
|
It is possible to run a system without swap. In order to safely do so and ensure consistent performance and service availability, you need to tune the kernel, system and your workloads accordingly. This requires a good understanding of your kernel's memory management system as well as the memory usage patterns of the workloads you're running.
|
||||||
|
@ -102,9 +133,10 @@ Unless you're experienced in doing this kind of tuning and troubleshooting the i
|
||||||
* less than 2GB of RAM: swap = RAM × 2
|
* less than 2GB of RAM: swap = RAM × 2
|
||||||
* more than 2GB of RAM: swap = RAM, up to 8G
|
* more than 2GB of RAM: swap = RAM, up to 8G
|
||||||
|
|
||||||
Linux swaps pretty early. This tends to not be necessary on servers and in the case of databases can cause unnecessary latency. Though it's good to let your system swap if it needs to, it can help to tell it to be a little more conservative about how early it swaps. Configuring this on Linux is done by changing the `vm.swappiness` [sysctl][sysctl] value.
|
!!! tip "Configuring Swappiness"
|
||||||
|
Linux swaps pretty early. This tends to not be necessary on servers and in the case of databases can cause unnecessary latency. Though it's good to let your system swap if it needs to, it can help to tell it to be a little more conservative about how early it swaps. Configuring this on Linux is done by changing the `vm.swappiness` [sysctl][sysctl] value.
|
||||||
By default it's `60`. You can lower that to `10` for starters and keep an eye out. It's possible to run with even lower values, but it's likely unnecessary. To make the value persistent, you'll need to drop a configuration file in `/etc/sysctl.d/`.
|
|
||||||
|
By default it's `60`. You can lower that to `10` for starters and keep an eye out. It's possible to run with even lower values, but it's likely unnecessary. To make the value persistent, you'll need to drop a configuration file in `/etc/sysctl.d/`.
|
||||||
|
|
||||||
[sysctl]: https://man7.org/linux/man-pages/man8/sysctl.8.html
|
[sysctl]: https://man7.org/linux/man-pages/man8/sysctl.8.html
|
||||||
|
|
||||||
|
@ -119,15 +151,3 @@ You can configure limits for a process using [systemd resource control settings]
|
||||||
[openrccgv2]: https://wiki.gentoo.org/wiki/OpenRC/CGroups
|
[openrccgv2]: https://wiki.gentoo.org/wiki/OpenRC/CGroups
|
||||||
[libcg]: https://github.com/libcgroup/libcgroup/
|
[libcg]: https://github.com/libcgroup/libcgroup/
|
||||||
[cgv2mem]: https://docs.kernel.org/admin-guide/cgroup-v2.html#memory-interface-files
|
[cgv2mem]: https://docs.kernel.org/admin-guide/cgroup-v2.html#memory-interface-files
|
||||||
|
|
||||||
## Ports
|
|
||||||
|
|
||||||
GoToSocial needs ports `80` and `443` open.
|
|
||||||
|
|
||||||
* `80` is used for Lets Encrypt. As such, you don't need it if you don't use the built-in Lets Encrypt provisioning.
|
|
||||||
* `443` is used to serve the API on with TLS and is what any instance you're federating with will try to connect to.
|
|
||||||
|
|
||||||
If you can't leave `443` and `80` open on the machine, don't worry! You can configure these ports in GoToSocial, but you'll have to also configure port forwarding to properly forward traffic on `443` and `80` to whatever ports you choose.
|
|
||||||
|
|
||||||
!!! tip
|
|
||||||
You should configure a firewall on your machine, as well as some protection against brute-force SSH login attempts and the like. Take a look at our [firewall documentation](../advanced/security/firewall.md) for pointers on what to configure and tools that can help you out.
|
|
||||||
|
|
|
@ -131,16 +131,34 @@ Copy it to `/etc/systemd/system/gotosocial.service`:
|
||||||
sudo cp /gotosocial/example/gotosocial.service /etc/systemd/system/
|
sudo cp /gotosocial/example/gotosocial.service /etc/systemd/system/
|
||||||
```
|
```
|
||||||
|
|
||||||
Then use `sudoedit /etc/systemd/system/gotosocial.service` to change the `ExecStart=` and `WorkingDirectory=` lines according to your installation.
|
Then use `sudoedit /etc/systemd/system/gotosocial.service` to open the file in an editor. If you installed GoToSocial in a directory different from the `/gotosocial` path used in this guide, change the `ExecStart=` and `WorkingDirectory=` lines according to your installation.
|
||||||
|
|
||||||
If you have been following this guide word for word the defaults should be fine.
|
!!! info "Running on ports 80 and 443"
|
||||||
|
|
||||||
|
If you've been following this guide word for word, your GoToSocial instance will be configured to bind to ports 443 and 80, which are known as privileged ports. To allow the GoToSocial user to bind to these, you need to uncomment the line about `CAP_NET_BIND_SERVICE` in the service file by removing the leading `#`.
|
||||||
|
|
||||||
|
Before:
|
||||||
|
|
||||||
|
```
|
||||||
|
#AmbientCapabilities=CAP_NET_BIND_SERVICE
|
||||||
|
```
|
||||||
|
|
||||||
|
After:
|
||||||
|
|
||||||
|
```
|
||||||
|
AmbientCapabilities=CAP_NET_BIND_SERVICE
|
||||||
|
```
|
||||||
|
|
||||||
|
If you later decide to run GoToSocial using a reverse proxy (see below) you may want to re-comment this line to remove the privileges, since the reverse proxy will bind to the privileged ports instead.
|
||||||
|
|
||||||
After you're done enable the service:
|
After you're done editing, save and close the file, and run the following command to enable the service:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo systemctl enable --now gotosocial.service
|
sudo systemctl enable --now gotosocial.service
|
||||||
```
|
```
|
||||||
|
|
||||||
|
GoToSocial should now be up and running.
|
||||||
|
|
||||||
## (Optional) Reverse proxy
|
## (Optional) Reverse proxy
|
||||||
|
|
||||||
If you want to run other webservers on port 443 or want to add an additional layer of security you might want to use a [reverse proxy](../reverse_proxy/index.md). We have guides available for a couple of popular open source options and will gladly take pull requests to add more.
|
If you want to run other webservers on port 443 or want to add an additional layer of security you might want to use a [reverse proxy](../reverse_proxy/index.md). We have guides available for a couple of popular open source options and will gladly take pull requests to add more.
|
||||||
|
|
|
@ -2,10 +2,10 @@
|
||||||
|
|
||||||
Regardless of the installation method, you'll need to create some users. GoToSocial currently doesn't have a way for users to be created through the web UI, or for people to sign-up through the web UI.
|
Regardless of the installation method, you'll need to create some users. GoToSocial currently doesn't have a way for users to be created through the web UI, or for people to sign-up through the web UI.
|
||||||
|
|
||||||
Using the CLI, you can create a user:
|
In the meantime, you can create a user using the CLI:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
$ gotosocial --config-path /path/to/config.yaml \
|
./gotosocial --config-path /path/to/config.yaml \
|
||||||
admin account create \
|
admin account create \
|
||||||
--username some_username \
|
--username some_username \
|
||||||
--email some_email@whatever.org \
|
--email some_email@whatever.org \
|
||||||
|
@ -17,29 +17,28 @@ In the above command, replace `some_username` with your desired username, `some_
|
||||||
If you want your user to have admin rights, you can promote them using a similar command:
|
If you want your user to have admin rights, you can promote them using a similar command:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
$ gotosocial --config-path /path/to/config.yaml \
|
./gotosocial --config-path /path/to/config.yaml \
|
||||||
admin account promote --username some_username
|
admin account promote --username some_username
|
||||||
```
|
```
|
||||||
|
|
||||||
Replace `some_username` with the username of the account you just created.
|
Replace `some_username` with the username of the account you just created.
|
||||||
|
|
||||||
!!! info
|
!!! warning "Promotion requires server restart"
|
||||||
When running these commands, you'll get a bit of output like the following:
|
|
||||||
|
Due to the way caching works in GoToSocial, some admin CLI commands require a server restart after running the command in order for the changes to "take".
|
||||||
|
|
||||||
|
For example, after promoting a user to admin, you will need to restart your GoToSocial server so that the new values can be loaded from the database.
|
||||||
|
|
||||||
```text
|
!!! tip
|
||||||
time=XXXX level=info msg=connected to SQLITE database
|
|
||||||
time=XXXX level=info msg=there are no new migrations to run func=doMigration
|
Take a look at the other available CLI commands [here](../admin/cli.md).
|
||||||
time=XXXX level=info msg=closing db connection
|
|
||||||
```
|
|
||||||
|
|
||||||
This is normal and indicates that the commands ran as expected.
|
|
||||||
|
|
||||||
## Containers
|
## Containers
|
||||||
|
|
||||||
When running GoToSocial from a container, you'll need to execute the above command in the conatiner instead. How to do this varies based on your container runtime, but for Docker it should look like:
|
When running GoToSocial from a container, you'll need to execute the above command in the container instead. How to do this varies based on your container runtime, but for Docker it should look like:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
$ docker exec -it CONTAINER_NAME_OR_ID \
|
docker exec -it CONTAINER_NAME_OR_ID \
|
||||||
/gotosocial/gotosocial \
|
/gotosocial/gotosocial \
|
||||||
admin account create \
|
admin account create \
|
||||||
--username some_username \
|
--username some_username \
|
||||||
|
|
133
docs/index.md
|
@ -1,120 +1,17 @@
|
||||||
# What is GoToSocial?
|
{%
|
||||||
|
include "../README.md"
|
||||||
|
start='<!--overview-start-->'
|
||||||
|
end='<!--overview-end-->'
|
||||||
|
%}
|
||||||
|
|
||||||
GoToSocial is an [ActivityPub](https://activitypub.rocks/) social network server, written in Golang.
|
{%
|
||||||
|
include "../README.md"
|
||||||
|
start='<!--body-1-start-->'
|
||||||
|
end='<!--body-1-end-->'
|
||||||
|
%}
|
||||||
|
|
||||||
With GoToSocial, you can keep in touch with your friends, post, read, and share images and articles. All without being tracked or advertised to!
|
{%
|
||||||
|
include "../README.md"
|
||||||
<p align="middle">
|
start='<!--body-2-start-->'
|
||||||
<img src="./assets/sloth.png" width="300"/>
|
end='<!--body-2-end-->'
|
||||||
</p>
|
%}
|
||||||
|
|
||||||
GoToSocial provides a lightweight, customizable, and safety-focused entryway into the [Fediverse](https://en.wikipedia.org/wiki/Fediverse), and is comparable to (but distinct from) existing projects such as [Mastodon](https://joinmastodon.org/), [Pleroma](https://pleroma.social/), [Friendica](https://friendica.net), and [PixelFed](https://pixelfed.org/).
|
|
||||||
|
|
||||||
**GoToSocial is still [ALPHA SOFTWARE](https://en.wikipedia.org/wiki/Software_release_life_cycle#Alpha)**. It is already deployable and useable, and it federates cleanly with many other Fediverse servers (not yet all). However, many things are not yet implemented, and there are plenty of bugs! We foresee entering beta around the beginning of 2024.
|
|
||||||
|
|
||||||
Here's a screenshot of the instance landing page!
|
|
||||||
|
|
||||||
![Screenshot of the landing page for the GoToSocial instance goblin.technology. It shows basic information about the instance; number of users and posts etc.](./assets/instancesplash.png)
|
|
||||||
|
|
||||||
If you've ever used something like Twitter or Tumblr (or even Myspace!) GoToSocial will probably feel familiar to you: You can follow people and have followers, you make posts which people can favourite and reply to and share, and you scroll through posts from people you follow using a timeline. You can write long posts or short posts, or just post images, it's up to you. You can also, of course, block people or otherwise limit interactions that you don't want by posting just to your friends.
|
|
||||||
|
|
||||||
**GoToSocial does NOT use recommendation algorithms or collect data about you to suggest content or 'improve your experience'**. The timeline is chronological: whatever you see at the top of your timeline is there because it's *just been posted*, not because it's been selected as interesting (or controversial) based on your personal profile.
|
|
||||||
|
|
||||||
GoToSocial is not designed for 'must-follow' influencers with tens of thousands of followers, and it's not designed to be addictive. Your timeline and your experience is shaped by who you follow and how you interact with people, not by metrics of engagement!
|
|
||||||
|
|
||||||
GoToSocial doesn't claim to be *better* than any other application, but it offers something that might better *for you* in particular.
|
|
||||||
|
|
||||||
![Screenshot of the web view of a profile in GoToSocial, showing header and avatar, bio, and numbers of followers/following.](./assets/profile1.png)
|
|
||||||
|
|
||||||
## History and Status
|
|
||||||
|
|
||||||
This project sprang up in 2021 out of a dissatisfaction with the safety + privacy features of other Federated microblogging/social media applications, and a desire to implement something a little different.
|
|
||||||
|
|
||||||
It began as a solo project, and then picked up steam as more developers became interested and jumped on.
|
|
||||||
|
|
||||||
## Known Issues
|
|
||||||
|
|
||||||
Since GoToSocial is still in alpha, there are plenty of bugs. We use [GitHub issues](https://github.com/superseriousbusiness/gotosocial/issues?q=is%3Aissue+is%3Aopen+label%3Abug) to track these. The [FAQ](./faq.md) also describes some of the features that haven't been implemented yet.
|
|
||||||
|
|
||||||
### Client App Issues
|
|
||||||
|
|
||||||
GoToSocial works great with Tusky, Semaphore and Feditext, but some other client applications still need work or have issues connecting to GoToSocial. We're tracking them [right here](https://github.com/superseriousbusiness/gotosocial/projects/5). It's our goal to make any app that's compatible with the Mastodon API work seamlessly with GoToSocial.
|
|
||||||
|
|
||||||
### Federation Issues
|
|
||||||
|
|
||||||
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://github.com/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.
|
|
||||||
|
|
||||||
## Contributing
|
|
||||||
|
|
||||||
You wanna contribute to GtS? Great! ❤️❤️❤️ Check out the issues page to see if there's anything you wanna jump in on, and read the CONTRIBUTING.md file on the repository for guidelines and setting up your dev environment.
|
|
||||||
|
|
||||||
## Building
|
|
||||||
|
|
||||||
Instructions for building GoToSocial from source are also in the CONTRIBUTING.md file.
|
|
||||||
|
|
||||||
## Contact
|
|
||||||
|
|
||||||
For questions and comments, you can [join our Matrix channel](https://matrix.to/#/#gotosocial:superseriousbusiness.org) at `#gotosocial:superseriousbusiness.org`. This is the quickest way to reach the devs. You can also mail [admin@gotosocial.org](mailto:admin@gotosocial.org).
|
|
||||||
|
|
||||||
For bugs and feature requests, please check to see if there's [already an issue](https://github.com/superseriousbusiness/gotosocial/issues), and if not, open one or use one of the above channels to make a request (if you don't have a Github account).
|
|
||||||
|
|
||||||
## Credits
|
|
||||||
|
|
||||||
### Image Attribution and Licensing
|
|
||||||
|
|
||||||
Sloth logo by [Anna Abramek](https://abramek.art/).
|
|
||||||
|
|
||||||
<a rel="license" href="http://creativecommons.org/licenses/by-sa/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by-sa/4.0/88x31.png" /></a><br />The GoToSocial sloth mascot is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by-sa/4.0/">Creative Commons Attribution-ShareAlike 4.0 International License</a>.
|
|
||||||
|
|
||||||
For more information on GoToSocial image licensing, see [here](https://github.com/superseriousbusiness/gotosocial#image-attribution-and-licensing).
|
|
||||||
|
|
||||||
### Developers
|
|
||||||
|
|
||||||
In alphabetical order:
|
|
||||||
|
|
||||||
- daenney
|
|
||||||
- f0x \[[donate with liberapay](https://liberapay.com/f0x)\]
|
|
||||||
- kim
|
|
||||||
- tobi \[[donate with liberapay](https://liberapay.com/GoToSocial/)\]
|
|
||||||
|
|
||||||
### Special Thanks
|
|
||||||
|
|
||||||
A huge thank you to CJ from [go-fed](https://github.com/go-fed/activity): without your work GoToSocial would not have been possible.
|
|
||||||
|
|
||||||
Thanks to everyone who has used GtS, opened an issue, suggested something, given funding, and otherwise encouraged or supported the project!
|
|
||||||
|
|
||||||
## Sponsorship + Funding
|
|
||||||
|
|
||||||
**Please note: GoToSocial has NO CORPORATE SPONSORS and does not desire corporate sponsorship. In addition, we do not take donations from any of the following: adult websites, affiliate and review websites, casinos and gambling, insurance and financial products (credit), pharmacy products, SEO services and social media buying, VPN and proxy services, and essay writing services. Donations from such sources will be automatically rejected.**
|
|
||||||
|
|
||||||
### Crowdfunding
|
|
||||||
|
|
||||||
![open collective Standard Sloth badge](https://opencollective.com/gotosocial/tiers/standard-sloth/badge.svg?label=Standard%20Sloth&color=brightgreen) ![open collective Stable Sloth badge](https://opencollective.com/gotosocial/tiers/stable-sloth/badge.svg?label=Stable%20Sloth&color=green) ![open collective Special Sloth badge](https://opencollective.com/gotosocial/tiers/special-sloth/badge.svg?label=Special%20Sloth&color=yellowgreen) ![open collective Sugar Sloth badge](https://opencollective.com/gotosocial/tiers/sugar-sloth/badge.svg?label=Sugar%20Sloth&color=blue)
|
|
||||||
|
|
||||||
If you would like to donate to GoToSocial to keep the lights on during development, [you can do so via our OpenCollective page](https://opencollective.com/gotosocial#support)!
|
|
||||||
|
|
||||||
![LiberaPay patrons](https://img.shields.io/liberapay/patrons/GoToSocial.svg?logo=liberapay) ![receives via LiberaPay](https://img.shields.io/liberapay/receives/GoToSocial.svg?logo=liberapay)
|
|
||||||
|
|
||||||
If you prefer, we also have an account on LiberaPay! You can find that [right here](https://liberapay.com/GoToSocial/).
|
|
||||||
|
|
||||||
Crowdfunded donations to our OpenCollective and Liberapay accounts go towards paying the core team, paying server costs, and paying for GtS art, design, and other bits and bobs.
|
|
||||||
|
|
||||||
💕 🦥 💕 Thank you!
|
|
||||||
|
|
||||||
### NLnet
|
|
||||||
|
|
||||||
<img src="https://nlnet.nl/logo/NGI/NGIZero-green.hex.svg" width="75" alt="NGIZero logo"/>
|
|
||||||
|
|
||||||
Combined with the above crowdfunding sources, 2023 Alpha development of GoToSocial is also funded by a 50,000 EUR grant from the [NGI0 Entrust Fund](https://nlnet.nl/entrust/), via [NLnet](https://nlnet.nl/). See [here](https://nlnet.nl/project/GoToSocial/#ack) for more details. The successful grant application is archived [here](https://github.com/superseriousbusiness/gotosocial/blob/main/archive/nlnet/2022-next-generation-internet-zero.md).
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
![the gnu AGPL logo](https://www.gnu.org/graphics/agplv3-155x51.png)
|
|
||||||
|
|
||||||
GoToSocial is free software, licensed under the GNU AGPL v3 LICENSE. We encourage forking and changing the code, hacking around with it, and experimenting.
|
|
||||||
|
|
||||||
See [here](https://www.gnu.org/licenses/why-affero-gpl.html) for the differences between AGPL versus GPL licensing, and [here](https://www.gnu.org/licenses/gpl-faq.html) for FAQ's about GPL licenses, including the AGPL.
|
|
||||||
|
|
||||||
If you modify the GoToSocial source code, and run that modified code in a way that's accessible over a network, you *must* make your modifications to the source code available following the guidelines of the license:
|
|
||||||
|
|
||||||
> \[I\]f you modify the Program, your modified version must prominently offer all users interacting with it remotely through a computer network (if your version supports such interaction) an opportunity to receive the Corresponding Source of your version by providing access to the Corresponding Source from a network server at no charge, through some standard or customary means of facilitating copying of software.
|
|
||||||
|
|
|
@ -36,6 +36,7 @@
|
||||||
// read:blocks: grant read access to blocks
|
// read:blocks: grant read access to blocks
|
||||||
// read:custom_emojis: grant read access to custom_emojis
|
// read:custom_emojis: grant read access to custom_emojis
|
||||||
// read:favourites: grant read access to favourites
|
// read:favourites: grant read access to favourites
|
||||||
|
// read:filters: grant read access to filters
|
||||||
// read:follows: grant read access to follows
|
// read:follows: grant read access to follows
|
||||||
// read:lists: grant read access to lists
|
// read:lists: grant read access to lists
|
||||||
// read:media: grant read access to media
|
// read:media: grant read access to media
|
||||||
|
@ -48,6 +49,7 @@
|
||||||
// write: grants write access to everything
|
// write: grants write access to everything
|
||||||
// write:accounts: grants write access to accounts
|
// write:accounts: grants write access to accounts
|
||||||
// write:blocks: grants write access to blocks
|
// write:blocks: grants write access to blocks
|
||||||
|
// write:filters: grants write access to filters
|
||||||
// write:follows: grants write access to follows
|
// write:follows: grants write access to follows
|
||||||
// write:lists: grants write access to lists
|
// write:lists: grants write access to lists
|
||||||
// write:media: grants write access to media
|
// write:media: grants write access to media
|
||||||
|
|
20
docs/user_guide/search.md
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
# Search
|
||||||
|
|
||||||
|
## Query formats
|
||||||
|
|
||||||
|
GotoSocial accepts several kinds of search query:
|
||||||
|
|
||||||
|
- `@username`: search for an account with the given username on any domain. Can return multiple results.
|
||||||
|
- `@username@domain`: search for a remote account with exact username and domain. Will only ever return 1 result at most.
|
||||||
|
- `https://example.org/some/arbitrary/url`: search for an account or post with the given URL. If the account or post hasn't already federated to GotoSocial, it will try to retrieve it. Will only ever return 1 result at most.
|
||||||
|
- `#hashtag_name`: search for a hashtag with the given hashtag name, or starting with the given hashtag name. Case insensitive. Can return multiple results.
|
||||||
|
- `any arbitrary text`: search for posts containing the text, hashtags containing the text, and accounts with usernames, display names, or bios containing the text, exactly as written. Both posts you've written as well as posts replying to you will be searched. Account bios will only be searched for accounts that you follow. Can return multiple results.
|
||||||
|
|
||||||
|
## Search operators
|
||||||
|
|
||||||
|
Arbitrary text queries may include the following search operators:
|
||||||
|
|
||||||
|
- `from:username`: restrict results to statuses created by the specified *local* account.
|
||||||
|
- `from:username@domain`: restrict results to statuses created by the specified remote account.
|
||||||
|
|
||||||
|
For example, you can search for `sloth from:yourusername` to find your own posts about sloths.
|
|
@ -22,6 +22,15 @@ A preview of the image as it will appear on your profile will be shown. If you'r
|
||||||
|
|
||||||
If you navigate to your profile and refresh the page, your new avatar / header will be shown. It might take a bit longer for the update to federate out to remote instances.
|
If you navigate to your profile and refresh the page, your new avatar / header will be shown. It might take a bit longer for the update to federate out to remote instances.
|
||||||
|
|
||||||
|
### Select Theme
|
||||||
|
|
||||||
|
GoToSocial provides themes for you to choose from for the web view of your profile, to change your profile's appearance and vibe.
|
||||||
|
|
||||||
|
To choose a theme, just select it from the profile settings page, and click/tap "Save profile info" at the bottom of the page. When you look at your profile in the web view (you may need to refresh the page), you'll see the new theme applied, and so will anyone else visiting your profile.
|
||||||
|
|
||||||
|
!!! tip "Adding more themes"
|
||||||
|
Instance admins can add more themes by dropping css files into the `web/assets/themes` folder. See the [themes](../admin/themes.md) part of the admin docs for more information.
|
||||||
|
|
||||||
### Basic Information
|
### Basic Information
|
||||||
|
|
||||||
#### Display Name
|
#### Display Name
|
||||||
|
@ -79,15 +88,6 @@ This option is often referred to on the fediverse as "locking" your account.
|
||||||
|
|
||||||
After ticking or unticking the checkbox, be sure to click on the `Save profile info` button at the bottom to save your new settings.
|
After ticking or unticking the checkbox, be sure to click on the `Save profile info` button at the bottom to save your new settings.
|
||||||
|
|
||||||
#### Enable RSS Feed of Public Posts
|
|
||||||
|
|
||||||
RSS feeds for users are disabled by default, but can be opted into with this checkbox. For more information see [RSS](./rss.md).
|
|
||||||
|
|
||||||
This feed only includes posts set as 'Public' (see [Privacy Settings](./posts.md#privacy-settings)).
|
|
||||||
|
|
||||||
!!! warning
|
|
||||||
Exposing your RSS feed allows *anyone* to subscribe to updates on your Public posts anonymously, bypassing follows and follow requests.
|
|
||||||
|
|
||||||
#### Mark Account as Discoverable by Search Engines and Directories
|
#### Mark Account as Discoverable by Search Engines and Directories
|
||||||
|
|
||||||
This setting updates the 'discoverable' flag on your account.
|
This setting updates the 'discoverable' flag on your account.
|
||||||
|
@ -105,19 +105,41 @@ Turning on the discoverable flag may take a week or more to propagate; your acco
|
||||||
!!! info
|
!!! info
|
||||||
The discoverable setting is about **discoverability of your account**, not searchability of your posts. It has nothing to do with indexing of your posts for search by Mastodon instances, or other federated instances that use full text search!
|
The discoverable setting is about **discoverability of your account**, not searchability of your posts. It has nothing to do with indexing of your posts for search by Mastodon instances, or other federated instances that use full text search!
|
||||||
|
|
||||||
|
#### Enable RSS Feed of Public Posts
|
||||||
|
|
||||||
|
RSS feeds for users are disabled by default, but can be opted into with this checkbox. For more information see [RSS](./rss.md).
|
||||||
|
|
||||||
|
This feed only includes posts set as 'Public' (see [Privacy Settings](./posts.md#privacy-settings)).
|
||||||
|
|
||||||
|
!!! warning
|
||||||
|
Exposing your RSS feed allows *anyone* to subscribe to updates on your Public posts anonymously, bypassing follows and follow requests.
|
||||||
|
|
||||||
|
#### Hide Who You Follow / Are Followed By
|
||||||
|
|
||||||
|
By default, GoToSocial shows your following/followers counts on your public web profile, and allows others to see who you follow and are followed by. This can be useful for account discovery purposes. However, for privacy + safety reasons you may wish to hide these counts, and to hide your following/followers lists from other accounts. You can do this by checking this box.
|
||||||
|
|
||||||
|
With the box checked, your following/followers counts will be hidden from your public web profile, and others will not be able to page through your following/followers lists.
|
||||||
|
|
||||||
### Advanced
|
### Advanced
|
||||||
|
|
||||||
#### Custom CSS
|
#### Custom CSS
|
||||||
|
|
||||||
If enabled on your instance by the instance administrator, [Custom CSS](./custom_css.md) allows you to theme the way your profile looks when visited through a browser.
|
If enabled on your instance by the instance administrator, custom CSS allows you to further customize the way your profile looks when visited through a browser.
|
||||||
|
|
||||||
When this setting is not enabled by the instance administrator, the text input box is read-only.
|
When this setting is not enabled by the instance administrator, the text input box is read-only and custom CSS will not be applied.
|
||||||
|
|
||||||
## Post Settings
|
See the [Custom CSS](./custom_css.md) page for some tips on writing custom CSS for your profile.
|
||||||
|
|
||||||
![Screenshot of the user settings section, providing drop-down menu's to select default post settings, and form fields to change your password](../assets/user-settings-post-settings.png)
|
!!! tip
|
||||||
|
Any custom CSS you add in this box will be applied *after* your selected theme, so you can pick a preset theme that you like and then make your own tweaks!
|
||||||
|
|
||||||
In the 'Settings' section, you can set various defaults for new posts.
|
## Settings
|
||||||
|
|
||||||
|
![Screenshot of the settings section](../assets/user-settings-settings.png)
|
||||||
|
|
||||||
|
In the 'Settings' section, you can set various defaults for new posts, and change your password / email address.
|
||||||
|
|
||||||
|
### Post Settings
|
||||||
|
|
||||||
The default post language setting allows you to indicate to other fediverse users which language your posts are usually written in. This is helpful for fediverse users who speak (for example) Korean, and would prefer to filter out posts written in other languages.
|
The default post language setting allows you to indicate to other fediverse users which language your posts are usually written in. This is helpful for fediverse users who speak (for example) Korean, and would prefer to filter out posts written in other languages.
|
||||||
|
|
||||||
|
@ -131,12 +153,67 @@ The markdown setting indicates that your posts should be parsed as Markdown, whi
|
||||||
|
|
||||||
When you are finished updating your post settings, remember to click the `Save post settings` button at the bottom of the section to save your changes.
|
When you are finished updating your post settings, remember to click the `Save post settings` button at the bottom of the section to save your changes.
|
||||||
|
|
||||||
## Password Change
|
### Password Change
|
||||||
|
|
||||||
You can use the Password Change section of the User Settings Panel to set a new password for your account.
|
You can use the Password Change section of the panel to set a new password for your account. For security reasons, you must provide your current password to validate the change.
|
||||||
|
|
||||||
|
!!! info
|
||||||
|
If your instance is using OIDC as its authorization/identity provider, you will not be able to change your password via the GoToSocial settings panel, and you should contact your OIDC provider instead.
|
||||||
|
|
||||||
For more information on the way GoToSocial manages passwords, please see the [Password management document](./password_management.md).
|
For more information on the way GoToSocial manages passwords, please see the [Password management document](./password_management.md).
|
||||||
|
|
||||||
|
### Email Change
|
||||||
|
|
||||||
|
You can use the Email Change section of the panel to change the email address for your account. For security reasons, you must provide your current password to validate the change.
|
||||||
|
|
||||||
|
Once a new email address has been entered, and you have clicked "Change email address", you must open the inbox of the new email address and confirm your address via the link provided. Once you've done that, your email address change will be confirmed.
|
||||||
|
|
||||||
|
!!! info
|
||||||
|
If your instance is using OIDC as its authorization/identity provider, you will be able to change your email address via the settings panel, but it will only affect the email address GoToSocial uses to contact you, it will not change the email address you need to use to log in to your account. To change that, you should contact your OIDC provider.
|
||||||
|
|
||||||
|
## Migration
|
||||||
|
|
||||||
|
In the migration section you can manage settings related to aliasing and/or migrating your account to another account.
|
||||||
|
|
||||||
|
!!! tip
|
||||||
|
Depending on the software that a target account is hosted on, target account URIs for both aliasing and moves should look something like `https://mastodon.example.org/users/account_you_are_moving_to`. If you are unsure what format to use, check with the admin of the instance you are moving or aliasing to.
|
||||||
|
|
||||||
|
### Alias Account
|
||||||
|
|
||||||
|
You can use this section to create an alias from your GoToSocial account to other accounts elsewhere, indicating that you are also known as those accounts.
|
||||||
|
|
||||||
|
**Not implemented yet**: Alias information for accounts you enter here will be shown on the web view of your profile, but only if the target accounts are also aliased back to your account first. This is to prevent accounts from claiming to be aliased to other accounts that they don't actually control.
|
||||||
|
|
||||||
|
### Move Account
|
||||||
|
|
||||||
|
Using the move account settings, you can trigger the migration of your current account to the given target account URI.
|
||||||
|
|
||||||
|
In order for the move to be successful, the target account (the account you are moving to) must be aliased back to your current account (the account you are moving from). The target account must also be reachable from your current account, ie., not blocked by you, not suspended by your current instance, and not on a domain that is blocked by your current instance. The target account does not have to be on a GoToSocial instance.
|
||||||
|
|
||||||
|
GoToSocial uses an account move cooldown of 7 days. If either your current account or the target account have recently been involved in a move, you will not be able to trigger a move to the target account until seven days have passed.
|
||||||
|
|
||||||
|
Moving your account will send a message out from your current account, to your current followers, indicating that they should follow the target account instead. Depending on the server software used by your followers, they may then automatically send a follow (request) to the target account, and unfollow your current account.
|
||||||
|
|
||||||
|
Currently, **only your followers will be carried over to the new account**. Other things like your following list, statuses, media, bookmarks, faves, blocks, etc, will not be carried over.
|
||||||
|
|
||||||
|
Once your account has moved, the web view of your current (now old) account will show a notice that you have moved, and to where.
|
||||||
|
|
||||||
|
Your old statuses and media will still be visible on the web view of the account you've moved from, unless you delete them manually. If you prefer, you can ask the admin of the instance you've moved from to suspend/delete your account after the move has gone through.
|
||||||
|
|
||||||
|
If necessary, you can retry an account move using the same target account URI. This will send the move message out again.
|
||||||
|
|
||||||
|
!!! danger "Moving your account is an irreversible, permanent action!"
|
||||||
|
|
||||||
|
From the moment you trigger an account move, you will have only basic read- and delete-level permissions on the account you've moved from.
|
||||||
|
|
||||||
|
You will still be able to log in to your old account and see your own posts, faves, bookmarks, blocks, and lists.
|
||||||
|
|
||||||
|
You will also be able to edit your profile, delete and/or unpin your own posts, and unboost, unfave, and unbookmark posts.
|
||||||
|
|
||||||
|
However, you will not be able to take any action that involves creating something, such as writing, boosting, bookmarking, or faving a post, following someone, uploading media, creating a list, etc.
|
||||||
|
|
||||||
|
Additionally, you will not be able to view any timelines (home, tag, public, list), or use the search functionality.
|
||||||
|
|
||||||
## Admins
|
## Admins
|
||||||
|
|
||||||
If your account has been promoted to admin, this interface will also show sections related to admin actions, see [Admin Settings](../admin/settings.md).
|
If your account has been promoted to admin, this interface will also show sections related to admin actions, see [Admin Settings](../admin/settings.md).
|
||||||
|
|
|
@ -406,15 +406,11 @@ instance-inject-mastodon-version: false
|
||||||
|
|
||||||
# Config pertaining to creation and maintenance of accounts on the server, as well as defaults for new accounts.
|
# Config pertaining to creation and maintenance of accounts on the server, as well as defaults for new accounts.
|
||||||
|
|
||||||
# Bool. Do we want people to be able to just submit sign up requests, or do we want invite only?
|
# Bool. Allow people to submit new sign-up / registration requests via the form at /signup.
|
||||||
|
#
|
||||||
# Options: [true, false]
|
# Options: [true, false]
|
||||||
# Default: true
|
# Default: false
|
||||||
accounts-registration-open: true
|
accounts-registration-open: false
|
||||||
|
|
||||||
# Bool. Do sign up requests require approval from an admin/moderator before an account can sign in/use the server?
|
|
||||||
# Options: [true, false]
|
|
||||||
# Default: true
|
|
||||||
accounts-approval-required: true
|
|
||||||
|
|
||||||
# Bool. Are sign up requests required to submit a reason for the request (eg., an explanation of why they want to join the instance)?
|
# Bool. Are sign up requests required to submit a reason for the request (eg., an explanation of why they want to join the instance)?
|
||||||
# Options: [true, false]
|
# Options: [true, false]
|
||||||
|
@ -980,6 +976,10 @@ advanced-rate-limit-requests: 300
|
||||||
# applied on their requests, and rate limit headers will not be
|
# applied on their requests, and rate limit headers will not be
|
||||||
# set for those requests.
|
# set for those requests.
|
||||||
#
|
#
|
||||||
|
# For IPv6, we only take subnets up to a /64 into account. If you
|
||||||
|
# want to open up a larger prefix, you'll need to list multiple
|
||||||
|
# prefixes instead.
|
||||||
|
#
|
||||||
# This can be useful in the following example cases (and probably
|
# This can be useful in the following example cases (and probably
|
||||||
# a bunch of others as well):
|
# a bunch of others as well):
|
||||||
#
|
#
|
||||||
|
@ -999,7 +999,7 @@ advanced-rate-limit-requests: 300
|
||||||
# wide a range. If in doubt, be too restrictive rather than too
|
# wide a range. If in doubt, be too restrictive rather than too
|
||||||
# lenient, and adjust as you go.
|
# lenient, and adjust as you go.
|
||||||
#
|
#
|
||||||
# Example: ["192.168.0.0/16"]
|
# Example: ["192.168.0.0/16", "2001:DB8:FACE:CAFE::/64"]
|
||||||
# Default: []
|
# Default: []
|
||||||
advanced-rate-limit-exceptions: []
|
advanced-rate-limit-exceptions: []
|
||||||
|
|
||||||
|
@ -1038,15 +1038,10 @@ advanced-throttling-multiplier: 8
|
||||||
# Default: "30s"
|
# Default: "30s"
|
||||||
advanced-throttling-retry-after: "30s"
|
advanced-throttling-retry-after: "30s"
|
||||||
|
|
||||||
# Int. CPU multiplier for the amount of goroutines to spawn in order to send messages via ActivityPub.
|
# Int. CPU multiplier for the fixed number of goroutines to spawn in order to send messages via ActivityPub.
|
||||||
# Messages will be batched so that at most multiplier * CPU count messages will be sent out at once.
|
# Messages will be batched and pushed to a singular queue, from which multiplier * CPU count goroutines will
|
||||||
# This can be tuned to limit concurrent POSTing to remote inboxes, preventing your instance CPU
|
# pull and attempt deliveries. This can be tuned to limit concurrent posting to remote inboxes, preventing
|
||||||
# usage from skyrocketing when an account with many followers posts a new status.
|
# your instance CPU usage skyrocketing when accounts with many followers post statuses.
|
||||||
#
|
|
||||||
# Messages are split among available senders, and each sender processes its assigned messages in serial.
|
|
||||||
# For example, say a user with 1000 followers is on an instance with 2 CPUs. With the default multiplier
|
|
||||||
# of 2, this means 4 senders would be in process at once on this instance. When the user creates a new post,
|
|
||||||
# each sender would end up iterating through about 250 Create messages + delivering them to remote instances.
|
|
||||||
#
|
#
|
||||||
# If you set this to 0 or less, only 1 sender will be used regardless of CPU count. This may be
|
# If you set this to 0 or less, only 1 sender will be used regardless of CPU count. This may be
|
||||||
# useful in cases where you are working with very tight network or CPU constraints.
|
# useful in cases where you are working with very tight network or CPU constraints.
|
||||||
|
@ -1095,6 +1090,8 @@ advanced-csp-extra-uris: []
|
||||||
#
|
#
|
||||||
# "allow" -- only requests that are explicitly allowed by header filters
|
# "allow" -- only requests that are explicitly allowed by header filters
|
||||||
# will be accepted (unless they are also explicitly blocked).
|
# will be accepted (unless they are also explicitly blocked).
|
||||||
|
# This mode is considered experimental and will almost certainly
|
||||||
|
# break access to your instance unless you are very careful.
|
||||||
#
|
#
|
||||||
# "" -- request header filtering disabled.
|
# "" -- request header filtering disabled.
|
||||||
#
|
#
|
||||||
|
|
194
go.mod
|
@ -1,8 +1,8 @@
|
||||||
module github.com/superseriousbusiness/gotosocial
|
module github.com/superseriousbusiness/gotosocial
|
||||||
|
|
||||||
go 1.21
|
go 1.22.2
|
||||||
|
|
||||||
toolchain go1.21.3
|
replace modernc.org/sqlite => gitlab.com/NyaaaWhatsUpDoc/sqlite v1.29.9-concurrency-workaround
|
||||||
|
|
||||||
require (
|
require (
|
||||||
codeberg.org/gruf/go-bytes v1.0.2
|
codeberg.org/gruf/go-bytes v1.0.2
|
||||||
|
@ -14,68 +14,72 @@ require (
|
||||||
codeberg.org/gruf/go-fastcopy v1.1.2
|
codeberg.org/gruf/go-fastcopy v1.1.2
|
||||||
codeberg.org/gruf/go-iotools v0.0.0-20230811115124-5d4223615a7f
|
codeberg.org/gruf/go-iotools v0.0.0-20230811115124-5d4223615a7f
|
||||||
codeberg.org/gruf/go-kv v1.6.4
|
codeberg.org/gruf/go-kv v1.6.4
|
||||||
|
codeberg.org/gruf/go-list v0.0.0-20240425093752-494db03d641f
|
||||||
codeberg.org/gruf/go-logger/v2 v2.2.1
|
codeberg.org/gruf/go-logger/v2 v2.2.1
|
||||||
codeberg.org/gruf/go-mutexes v1.4.0
|
codeberg.org/gruf/go-mempool v0.0.0-20240507125005-cef10d64a760
|
||||||
|
codeberg.org/gruf/go-mutexes v1.5.0
|
||||||
codeberg.org/gruf/go-runners v1.6.2
|
codeberg.org/gruf/go-runners v1.6.2
|
||||||
codeberg.org/gruf/go-sched v1.2.3
|
codeberg.org/gruf/go-sched v1.2.3
|
||||||
codeberg.org/gruf/go-store/v2 v2.2.4
|
codeberg.org/gruf/go-storage v0.1.1
|
||||||
codeberg.org/gruf/go-structr v0.3.0
|
codeberg.org/gruf/go-structr v0.8.5
|
||||||
codeberg.org/superseriousbusiness/exif-terminator v0.7.0
|
codeberg.org/superseriousbusiness/exif-terminator v0.7.0
|
||||||
github.com/DmitriyVTitov/size v1.5.0
|
github.com/DmitriyVTitov/size v1.5.0
|
||||||
github.com/KimMachineGun/automemlimit v0.5.0
|
github.com/KimMachineGun/automemlimit v0.6.1
|
||||||
github.com/abema/go-mp4 v1.2.0
|
github.com/abema/go-mp4 v1.2.0
|
||||||
github.com/buckket/go-blurhash v1.1.0
|
github.com/buckket/go-blurhash v1.1.0
|
||||||
github.com/coreos/go-oidc/v3 v3.9.0
|
github.com/coreos/go-oidc/v3 v3.10.0
|
||||||
github.com/disintegration/imaging v1.6.2
|
github.com/disintegration/imaging v1.6.2
|
||||||
github.com/gin-contrib/cors v1.5.0
|
github.com/gin-contrib/cors v1.7.2
|
||||||
github.com/gin-contrib/gzip v0.0.6
|
github.com/gin-contrib/gzip v1.0.1
|
||||||
github.com/gin-contrib/sessions v0.0.5
|
github.com/gin-contrib/sessions v1.0.1
|
||||||
github.com/gin-gonic/gin v1.9.1
|
github.com/gin-gonic/gin v1.10.0
|
||||||
github.com/go-playground/form/v4 v4.2.1
|
github.com/go-playground/form/v4 v4.2.1
|
||||||
|
github.com/go-swagger/go-swagger v0.31.0
|
||||||
github.com/google/uuid v1.6.0
|
github.com/google/uuid v1.6.0
|
||||||
github.com/gorilla/feeds v1.1.2
|
github.com/gorilla/feeds v1.1.2
|
||||||
github.com/gorilla/websocket v1.5.1
|
github.com/gorilla/websocket v1.5.2
|
||||||
github.com/h2non/filetype v1.1.3
|
github.com/h2non/filetype v1.1.3
|
||||||
github.com/jackc/pgx/v5 v5.5.3
|
github.com/jackc/pgx/v5 v5.6.0
|
||||||
github.com/microcosm-cc/bluemonday v1.0.26
|
github.com/microcosm-cc/bluemonday v1.0.26
|
||||||
github.com/miekg/dns v1.1.58
|
github.com/miekg/dns v1.1.59
|
||||||
github.com/minio/minio-go/v7 v7.0.67
|
github.com/minio/minio-go/v7 v7.0.71
|
||||||
github.com/mitchellh/mapstructure v1.5.0
|
github.com/mitchellh/mapstructure v1.5.0
|
||||||
|
github.com/ncruces/go-sqlite3 v0.16.2
|
||||||
github.com/oklog/ulid v1.3.1
|
github.com/oklog/ulid v1.3.1
|
||||||
github.com/prometheus/client_golang v1.18.0
|
github.com/prometheus/client_golang v1.19.1
|
||||||
github.com/spf13/cobra v1.8.0
|
github.com/spf13/cobra v1.8.0
|
||||||
github.com/spf13/viper v1.18.2
|
github.com/spf13/viper v1.19.0
|
||||||
github.com/stretchr/testify v1.9.0
|
github.com/stretchr/testify v1.9.0
|
||||||
github.com/superseriousbusiness/activity v1.6.0-gts.0.20240221151241-5d56c04088d4
|
github.com/superseriousbusiness/activity v1.6.0-gts.0.20240408131430-247f7f7110f0
|
||||||
github.com/superseriousbusiness/httpsig v1.2.0-SSB
|
github.com/superseriousbusiness/httpsig v1.2.0-SSB
|
||||||
github.com/superseriousbusiness/oauth2/v4 v4.3.2-SSB.0.20230227143000-f4900831d6c8
|
github.com/superseriousbusiness/oauth2/v4 v4.3.2-SSB.0.20230227143000-f4900831d6c8
|
||||||
github.com/tdewolff/minify/v2 v2.20.18
|
github.com/tdewolff/minify/v2 v2.20.33
|
||||||
github.com/technologize/otel-go-contrib v1.1.0
|
github.com/technologize/otel-go-contrib v1.1.1
|
||||||
github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80
|
github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80
|
||||||
github.com/ulule/limiter/v3 v3.11.2
|
github.com/ulule/limiter/v3 v3.11.2
|
||||||
github.com/uptrace/bun v1.1.17
|
github.com/uptrace/bun v1.2.1
|
||||||
github.com/uptrace/bun/dialect/pgdialect v1.1.17
|
github.com/uptrace/bun/dialect/pgdialect v1.2.1
|
||||||
github.com/uptrace/bun/dialect/sqlitedialect v1.1.17
|
github.com/uptrace/bun/dialect/sqlitedialect v1.2.1
|
||||||
github.com/uptrace/bun/extra/bunotel v1.1.16
|
github.com/uptrace/bun/extra/bunotel v1.2.1
|
||||||
github.com/wagslane/go-password-validator v0.3.0
|
github.com/wagslane/go-password-validator v0.3.0
|
||||||
github.com/yuin/goldmark v1.7.0
|
github.com/yuin/goldmark v1.7.1
|
||||||
go.opentelemetry.io/otel v1.20.0
|
go.opentelemetry.io/otel v1.26.0
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.20.0
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.26.0
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.20.0
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0
|
||||||
go.opentelemetry.io/otel/exporters/prometheus v0.43.0
|
go.opentelemetry.io/otel/exporters/prometheus v0.46.0
|
||||||
go.opentelemetry.io/otel/metric v1.20.0
|
go.opentelemetry.io/otel/metric v1.26.0
|
||||||
go.opentelemetry.io/otel/sdk v1.20.0
|
go.opentelemetry.io/otel/sdk v1.26.0
|
||||||
go.opentelemetry.io/otel/sdk/metric v1.20.0
|
go.opentelemetry.io/otel/sdk/metric v1.24.0
|
||||||
go.opentelemetry.io/otel/trace v1.20.0
|
go.opentelemetry.io/otel/trace v1.26.0
|
||||||
go.uber.org/automaxprocs v1.5.3
|
go.uber.org/automaxprocs v1.5.3
|
||||||
golang.org/x/crypto v0.20.0
|
golang.org/x/crypto v0.24.0
|
||||||
golang.org/x/image v0.15.0
|
golang.org/x/image v0.17.0
|
||||||
golang.org/x/net v0.21.0
|
golang.org/x/net v0.26.0
|
||||||
golang.org/x/oauth2 v0.17.0
|
golang.org/x/oauth2 v0.20.0
|
||||||
golang.org/x/text v0.14.0
|
golang.org/x/text v0.16.0
|
||||||
gopkg.in/mcuadros/go-syslog.v2 v2.3.0
|
gopkg.in/mcuadros/go-syslog.v2 v2.3.0
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
modernc.org/sqlite v1.29.2
|
modernc.org/sqlite v0.0.0-00010101000000-000000000000
|
||||||
mvdan.cc/xurls/v2 v2.5.0
|
mvdan.cc/xurls/v2 v2.5.0
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -83,80 +87,109 @@ require (
|
||||||
codeberg.org/gruf/go-atomics v1.1.0 // indirect
|
codeberg.org/gruf/go-atomics v1.1.0 // indirect
|
||||||
codeberg.org/gruf/go-bitutil v1.1.0 // indirect
|
codeberg.org/gruf/go-bitutil v1.1.0 // indirect
|
||||||
codeberg.org/gruf/go-fastpath/v2 v2.0.0 // indirect
|
codeberg.org/gruf/go-fastpath/v2 v2.0.0 // indirect
|
||||||
|
codeberg.org/gruf/go-mangler v1.3.0 // indirect
|
||||||
codeberg.org/gruf/go-maps v1.0.3 // indirect
|
codeberg.org/gruf/go-maps v1.0.3 // 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
|
||||||
|
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
|
||||||
github.com/aymerick/douceur v0.2.0 // indirect
|
github.com/aymerick/douceur v0.2.0 // indirect
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/bytedance/sonic v1.10.1 // indirect
|
github.com/bytedance/sonic v1.11.6 // indirect
|
||||||
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
|
github.com/bytedance/sonic/loader v0.1.1 // indirect
|
||||||
|
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||||
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
|
|
||||||
github.com/chenzhuoyu/iasm v0.9.0 // indirect
|
|
||||||
github.com/cilium/ebpf v0.9.1 // indirect
|
github.com/cilium/ebpf v0.9.1 // indirect
|
||||||
|
github.com/cloudwego/base64x v0.1.4 // indirect
|
||||||
|
github.com/cloudwego/iasm v0.2.0 // indirect
|
||||||
github.com/containerd/cgroups/v3 v3.0.1 // indirect
|
github.com/containerd/cgroups/v3 v3.0.1 // indirect
|
||||||
github.com/coreos/go-systemd/v22 v22.3.2 // indirect
|
github.com/coreos/go-systemd/v22 v22.3.2 // indirect
|
||||||
github.com/cornelk/hashmap v1.0.8 // indirect
|
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||||
github.com/docker/go-units v0.4.0 // indirect
|
github.com/docker/go-units v0.5.0 // indirect
|
||||||
|
github.com/dolthub/maphash v0.1.0 // indirect
|
||||||
|
github.com/dolthub/swiss v0.2.1 // indirect
|
||||||
github.com/dsoprea/go-exif/v3 v3.0.0-20210625224831-a6301f85c82b // indirect
|
github.com/dsoprea/go-exif/v3 v3.0.0-20210625224831-a6301f85c82b // indirect
|
||||||
github.com/dsoprea/go-iptc v0.0.0-20200610044640-bc9ca208b413 // indirect
|
github.com/dsoprea/go-iptc v0.0.0-20200610044640-bc9ca208b413 // indirect
|
||||||
github.com/dsoprea/go-logging v0.0.0-20200710184922-b02d349568dd // indirect
|
github.com/dsoprea/go-logging v0.0.0-20200710184922-b02d349568dd // indirect
|
||||||
github.com/dsoprea/go-photoshop-info-format v0.0.0-20200610045659-121dd752914d // indirect
|
github.com/dsoprea/go-photoshop-info-format v0.0.0-20200610045659-121dd752914d // indirect
|
||||||
github.com/dsoprea/go-utility/v2 v2.0.0-20200717064901-2fccff4aa15e // indirect
|
github.com/dsoprea/go-utility/v2 v2.0.0-20200717064901-2fccff4aa15e // indirect
|
||||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||||
|
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||||
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
||||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||||
github.com/go-errors/errors v1.4.1 // indirect
|
github.com/go-errors/errors v1.4.1 // indirect
|
||||||
github.com/go-fed/httpsig v1.1.0 // indirect
|
github.com/go-fed/httpsig v1.1.0 // indirect
|
||||||
github.com/go-jose/go-jose/v3 v3.0.1 // indirect
|
github.com/go-jose/go-jose/v4 v4.0.1 // indirect
|
||||||
github.com/go-logr/logr v1.4.1 // indirect
|
github.com/go-logr/logr v1.4.1 // indirect
|
||||||
github.com/go-logr/stdr v1.2.2 // 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
|
||||||
|
github.com/go-openapi/inflect v0.21.0 // indirect
|
||||||
|
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
||||||
|
github.com/go-openapi/jsonreference v0.21.0 // indirect
|
||||||
|
github.com/go-openapi/loads v0.22.0 // indirect
|
||||||
|
github.com/go-openapi/runtime v0.28.0 // indirect
|
||||||
|
github.com/go-openapi/spec v0.21.0 // indirect
|
||||||
|
github.com/go-openapi/strfmt v0.23.0 // indirect
|
||||||
|
github.com/go-openapi/swag v0.23.0 // indirect
|
||||||
|
github.com/go-openapi/validate v0.24.0 // indirect
|
||||||
github.com/go-playground/locales v0.14.1 // indirect
|
github.com/go-playground/locales v0.14.1 // indirect
|
||||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
github.com/go-playground/validator/v10 v10.15.5 // indirect
|
github.com/go-playground/validator/v10 v10.20.0 // indirect
|
||||||
github.com/go-xmlfmt/xmlfmt v0.0.0-20211206191508-7fd73a941850 // indirect
|
github.com/go-xmlfmt/xmlfmt v0.0.0-20211206191508-7fd73a941850 // indirect
|
||||||
github.com/goccy/go-json v0.10.2 // indirect
|
github.com/goccy/go-json v0.10.2 // indirect
|
||||||
github.com/godbus/dbus/v5 v5.0.4 // indirect
|
github.com/godbus/dbus/v5 v5.0.4 // indirect
|
||||||
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
|
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
|
||||||
github.com/golang/geo v0.0.0-20210211234256-740aa86cb551 // indirect
|
github.com/golang/geo v0.0.0-20210211234256-740aa86cb551 // indirect
|
||||||
github.com/golang/protobuf v1.5.3 // indirect
|
github.com/gorilla/context v1.1.2 // indirect
|
||||||
github.com/gorilla/context v1.1.1 // indirect
|
|
||||||
github.com/gorilla/css v1.0.0 // indirect
|
github.com/gorilla/css v1.0.0 // indirect
|
||||||
github.com/gorilla/securecookie v1.1.1 // indirect
|
github.com/gorilla/handlers v1.5.2 // indirect
|
||||||
github.com/gorilla/sessions v1.2.1 // indirect
|
github.com/gorilla/securecookie v1.1.2 // indirect
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect
|
github.com/gorilla/sessions v1.2.2 // indirect
|
||||||
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1 // indirect
|
||||||
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
||||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
github.com/hashicorp/hcl v1.0.0 // 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
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
||||||
github.com/jackc/puddle/v2 v2.2.1 // indirect
|
github.com/jackc/puddle/v2 v2.2.1 // indirect
|
||||||
|
github.com/jessevdk/go-flags v1.5.0 // indirect
|
||||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||||
|
github.com/josharian/intern v1.0.0 // indirect
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
github.com/klauspost/compress v1.17.4 // indirect
|
github.com/klauspost/compress v1.17.8 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.2.6 // indirect
|
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
|
||||||
github.com/leodido/go-urn v1.2.4 // 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/magiconair/properties v1.8.7 // indirect
|
github.com/magiconair/properties v1.8.7 // indirect
|
||||||
|
github.com/mailru/easyjson v0.7.7 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect
|
|
||||||
github.com/minio/md5-simd v1.1.2 // indirect
|
github.com/minio/md5-simd v1.1.2 // indirect
|
||||||
github.com/minio/sha256-simd v1.0.1 // indirect
|
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||||
|
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/ncruces/go-strftime v0.1.9 // indirect
|
github.com/ncruces/go-strftime v0.1.9 // indirect
|
||||||
|
github.com/ncruces/julianday v1.0.0 // indirect
|
||||||
github.com/opencontainers/runtime-spec v1.0.2 // indirect
|
github.com/opencontainers/runtime-spec v1.0.2 // indirect
|
||||||
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect
|
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.1.1 // indirect
|
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||||
github.com/prometheus/client_model v0.5.0 // indirect
|
github.com/prometheus/client_model v0.6.0 // indirect
|
||||||
github.com/prometheus/common v0.45.0 // indirect
|
github.com/prometheus/common v0.48.0 // indirect
|
||||||
github.com/prometheus/procfs v0.12.0 // indirect
|
github.com/prometheus/procfs v0.12.0 // indirect
|
||||||
github.com/quasoft/memstore v0.0.0-20191010062613-2bce066d2b0b // indirect
|
github.com/quasoft/memstore v0.0.0-20191010062613-2bce066d2b0b // indirect
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||||
|
github.com/rogpeppe/go-internal v1.12.0 // indirect
|
||||||
github.com/rs/xid v1.5.0 // indirect
|
github.com/rs/xid v1.5.0 // indirect
|
||||||
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
||||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||||
|
github.com/shopspring/decimal v1.3.1 // indirect
|
||||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||||
github.com/spf13/afero v1.11.0 // indirect
|
github.com/spf13/afero v1.11.0 // indirect
|
||||||
|
@ -165,34 +198,35 @@ require (
|
||||||
github.com/subosito/gotenv v1.6.0 // indirect
|
github.com/subosito/gotenv v1.6.0 // indirect
|
||||||
github.com/superseriousbusiness/go-jpeg-image-structure/v2 v2.0.0-20220321154430-d89a106fdabe // indirect
|
github.com/superseriousbusiness/go-jpeg-image-structure/v2 v2.0.0-20220321154430-d89a106fdabe // indirect
|
||||||
github.com/superseriousbusiness/go-png-image-structure/v2 v2.0.1-SSB // indirect
|
github.com/superseriousbusiness/go-png-image-structure/v2 v2.0.1-SSB // indirect
|
||||||
github.com/tdewolff/parse/v2 v2.7.12 // indirect
|
github.com/tdewolff/parse/v2 v2.7.14 // indirect
|
||||||
|
github.com/tetratelabs/wazero v1.7.3 // indirect
|
||||||
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // 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/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||||
github.com/ugorji/go/codec v1.2.11 // indirect
|
github.com/ugorji/go/codec v1.2.12 // indirect
|
||||||
github.com/uptrace/opentelemetry-go-extra/otelsql v0.2.3 // indirect
|
github.com/uptrace/opentelemetry-go-extra/otelsql v0.2.4 // indirect
|
||||||
github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
|
github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
|
||||||
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
|
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
|
||||||
github.com/zeebo/xxh3 v1.0.2 // indirect
|
go.mongodb.org/mongo-driver v1.14.0 // indirect
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.20.0 // indirect
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.26.0 // indirect
|
||||||
go.opentelemetry.io/proto/otlp v1.0.0 // indirect
|
go.opentelemetry.io/proto/otlp v1.2.0 // indirect
|
||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
golang.org/x/arch v0.5.0 // indirect
|
golang.org/x/arch v0.8.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 // indirect
|
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 // indirect
|
||||||
golang.org/x/mod v0.14.0 // indirect
|
golang.org/x/mod v0.17.0 // indirect
|
||||||
golang.org/x/sync v0.6.0 // indirect
|
golang.org/x/sync v0.7.0 // indirect
|
||||||
golang.org/x/sys v0.17.0 // indirect
|
golang.org/x/sys v0.21.0 // indirect
|
||||||
golang.org/x/tools v0.17.0 // indirect
|
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
|
||||||
google.golang.org/appengine v1.6.8 // indirect
|
google.golang.org/genproto/googleapis/api v0.0.0-20240311132316-a219d84964c2 // indirect
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f // indirect
|
google.golang.org/grpc v1.63.2 // indirect
|
||||||
google.golang.org/grpc v1.59.0 // indirect
|
google.golang.org/protobuf v1.34.1 // indirect
|
||||||
google.golang.org/protobuf v1.31.0 // indirect
|
|
||||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect
|
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect
|
||||||
modernc.org/libc v1.41.0 // indirect
|
modernc.org/libc v1.49.3 // indirect
|
||||||
modernc.org/mathutil v1.6.0 // indirect
|
modernc.org/mathutil v1.6.0 // indirect
|
||||||
modernc.org/memory v1.7.2 // indirect
|
modernc.org/memory v1.8.0 // indirect
|
||||||
modernc.org/strutil v1.2.0 // indirect
|
modernc.org/strutil v1.2.0 // indirect
|
||||||
modernc.org/token v1.1.0 // indirect
|
modernc.org/token v1.1.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
467
go.sum
|
@ -56,20 +56,28 @@ codeberg.org/gruf/go-iotools v0.0.0-20230811115124-5d4223615a7f h1:Kazm/PInN2m1S
|
||||||
codeberg.org/gruf/go-iotools v0.0.0-20230811115124-5d4223615a7f/go.mod h1:B8uq4yHtIcKXhBZT9C/SYisz25lldLHMVpwZPz4ADLQ=
|
codeberg.org/gruf/go-iotools v0.0.0-20230811115124-5d4223615a7f/go.mod h1:B8uq4yHtIcKXhBZT9C/SYisz25lldLHMVpwZPz4ADLQ=
|
||||||
codeberg.org/gruf/go-kv v1.6.4 h1:3NZiW8HVdBM3kpOiLb7XfRiihnzZWMAixdCznguhILk=
|
codeberg.org/gruf/go-kv v1.6.4 h1:3NZiW8HVdBM3kpOiLb7XfRiihnzZWMAixdCznguhILk=
|
||||||
codeberg.org/gruf/go-kv v1.6.4/go.mod h1:O/YkSvKiS9XsRolM3rqCd9YJmND7dAXu9z+PrlYO4bc=
|
codeberg.org/gruf/go-kv v1.6.4/go.mod h1:O/YkSvKiS9XsRolM3rqCd9YJmND7dAXu9z+PrlYO4bc=
|
||||||
|
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-logger/v2 v2.2.1 h1:RP2u059EQKTBFV3cN8X6xDxNk2RkzqdgXGKflKqB7Oc=
|
codeberg.org/gruf/go-logger/v2 v2.2.1 h1:RP2u059EQKTBFV3cN8X6xDxNk2RkzqdgXGKflKqB7Oc=
|
||||||
codeberg.org/gruf/go-logger/v2 v2.2.1/go.mod h1:m/vBfG5jNUmYXI8Hg9aVSk7Pn8YgEBITQB/B/CzdRss=
|
codeberg.org/gruf/go-logger/v2 v2.2.1/go.mod h1:m/vBfG5jNUmYXI8Hg9aVSk7Pn8YgEBITQB/B/CzdRss=
|
||||||
|
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.3.0 h1:cf0vuuLJuEhoIukPHj+MUBIQSWxZcfEYt2Eo/r7Rstk=
|
||||||
|
codeberg.org/gruf/go-mangler v1.3.0/go.mod h1:jnOA76AQoaO2kTHi0DlTTVaFYfRM+9fzs8f4XO6MsOk=
|
||||||
codeberg.org/gruf/go-maps v1.0.3 h1:VDwhnnaVNUIy5O93CvkcE2IZXnMB1+IJjzfop9V12es=
|
codeberg.org/gruf/go-maps v1.0.3 h1:VDwhnnaVNUIy5O93CvkcE2IZXnMB1+IJjzfop9V12es=
|
||||||
codeberg.org/gruf/go-maps v1.0.3/go.mod h1:D5LNDxlC9rsDuVQVM6JObaVGAdHB6g2dTdOdkh1aXWA=
|
codeberg.org/gruf/go-maps v1.0.3/go.mod h1:D5LNDxlC9rsDuVQVM6JObaVGAdHB6g2dTdOdkh1aXWA=
|
||||||
codeberg.org/gruf/go-mutexes v1.4.0 h1:53H6bFDRcG6rjk3iOTuGaStT/VTFdU5Uw8Dszy88a8g=
|
codeberg.org/gruf/go-mempool v0.0.0-20240507125005-cef10d64a760 h1:m2/UCRXhjDwAg4vyji6iKCpomKw6P4PmBOUi5DvAMH4=
|
||||||
codeberg.org/gruf/go-mutexes v1.4.0/go.mod h1:1j/6/MBeBQUedAtAtysLLnBKogfOZAxdym0E3wlaBD8=
|
codeberg.org/gruf/go-mempool v0.0.0-20240507125005-cef10d64a760/go.mod h1:E3RcaCFNq4zXpvaJb8lfpPqdUAmSkP5F1VmMiEUYTEk=
|
||||||
|
codeberg.org/gruf/go-mutexes v1.5.0 h1:kDegqA/FYQhcn294zUJ3U3VBegfvhtcI7ObcAku1zkw=
|
||||||
|
codeberg.org/gruf/go-mutexes v1.5.0/go.mod h1:rPEqQ/y6CmGITaZ3GPTMQVsoZAOzbsAHyIaLsJcOqVE=
|
||||||
codeberg.org/gruf/go-runners v1.6.2 h1:oQef9niahfHu/wch14xNxlRMP8i+ABXH1Cb9PzZ4oYo=
|
codeberg.org/gruf/go-runners v1.6.2 h1:oQef9niahfHu/wch14xNxlRMP8i+ABXH1Cb9PzZ4oYo=
|
||||||
codeberg.org/gruf/go-runners v1.6.2/go.mod h1:Tq5PrZ/m/rBXbLZz0u5if+yP3nG5Sf6S8O/GnyEePeQ=
|
codeberg.org/gruf/go-runners v1.6.2/go.mod h1:Tq5PrZ/m/rBXbLZz0u5if+yP3nG5Sf6S8O/GnyEePeQ=
|
||||||
codeberg.org/gruf/go-sched v1.2.3 h1:H5ViDxxzOBR3uIyGBCf0eH8b1L8wMybOXcdtUUTXZHk=
|
codeberg.org/gruf/go-sched v1.2.3 h1:H5ViDxxzOBR3uIyGBCf0eH8b1L8wMybOXcdtUUTXZHk=
|
||||||
codeberg.org/gruf/go-sched v1.2.3/go.mod h1:vT9uB6KWFIIwnG9vcPY2a0alYNoqdL1mSzRM8I+PK7A=
|
codeberg.org/gruf/go-sched v1.2.3/go.mod h1:vT9uB6KWFIIwnG9vcPY2a0alYNoqdL1mSzRM8I+PK7A=
|
||||||
codeberg.org/gruf/go-store/v2 v2.2.4 h1:8HO1Jh2gg7boQKA3hsDAIXd9zwieu5uXwDXEcTOD9js=
|
codeberg.org/gruf/go-storage v0.1.1 h1:CSX1PMMg/7vqqK8aCFtq94xCrOB3xhj7eWIvzILdLpY=
|
||||||
codeberg.org/gruf/go-store/v2 v2.2.4/go.mod h1:zI4VWe5CpXAktYMtaBMrgA5QmO0sQH53LBRvfn1huys=
|
codeberg.org/gruf/go-storage v0.1.1/go.mod h1:145IWMUOc6YpIiZIiCIEwkkNZZPiSbwMnZxRjSc5q6c=
|
||||||
codeberg.org/gruf/go-structr v0.3.0 h1:qaQz40LVm6dWDDp0pGsHbsbO0+XbqsXZ9N5YgqMmG78=
|
codeberg.org/gruf/go-structr v0.8.5 h1:WQuvLSQFyFwMjdU7dCWvgcjuhk07oWdSl9guShekzGQ=
|
||||||
codeberg.org/gruf/go-structr v0.3.0/go.mod h1:v9TsGsCBNNSVm/qeOuiblAeIS72YyxEIUoRpW8j4xm8=
|
codeberg.org/gruf/go-structr v0.8.5/go.mod h1:c5UvVDSA3lZ1kv05V+7pXkO8u8Jea+VRWFDRFBCOxSA=
|
||||||
codeberg.org/superseriousbusiness/exif-terminator v0.7.0 h1:Y6VApSXhKqExG0H2hZ2JelRK4xmWdjDQjn13CpEfzko=
|
codeberg.org/superseriousbusiness/exif-terminator v0.7.0 h1:Y6VApSXhKqExG0H2hZ2JelRK4xmWdjDQjn13CpEfzko=
|
||||||
codeberg.org/superseriousbusiness/exif-terminator v0.7.0/go.mod h1:gCWKduudUWFzsnixoMzu0FYVdxHWG+AbXnZ50DqxsUE=
|
codeberg.org/superseriousbusiness/exif-terminator v0.7.0/go.mod h1:gCWKduudUWFzsnixoMzu0FYVdxHWG+AbXnZ50DqxsUE=
|
||||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||||
|
@ -77,8 +85,15 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
|
||||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||||
github.com/DmitriyVTitov/size v1.5.0 h1:/PzqxYrOyOUX1BXj6J9OuVRVGe+66VL4D9FlUaW515g=
|
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/DmitriyVTitov/size v1.5.0/go.mod h1:le6rNI4CoLQV1b9gzp1+3d7hMAD/uu2QcJ+aYbNgiU0=
|
||||||
github.com/KimMachineGun/automemlimit v0.5.0 h1:BeOe+BbJc8L5chL3OwzVYjVzyvPALdd5wxVVOWuUZmQ=
|
github.com/KimMachineGun/automemlimit v0.6.1 h1:ILa9j1onAAMadBsyyUJv5cack8Y1WT26yLj/V+ulKp8=
|
||||||
github.com/KimMachineGun/automemlimit v0.5.0/go.mod h1:di3GCKiu9Y+1fs92erCbUvKzPkNyViN3mA0vti/ykEQ=
|
github.com/KimMachineGun/automemlimit v0.6.1/go.mod h1:T7xYht7B8r6AG/AqFcUdc7fzd2bIdBKmepfP2S1svPY=
|
||||||
|
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=
|
||||||
|
github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
|
||||||
|
github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
|
||||||
|
github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA=
|
||||||
|
github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM=
|
||||||
github.com/abema/go-mp4 v1.2.0 h1:gi4X8xg/m179N/J15Fn5ugywN9vtI6PLk6iLldHGLAk=
|
github.com/abema/go-mp4 v1.2.0 h1:gi4X8xg/m179N/J15Fn5ugywN9vtI6PLk6iLldHGLAk=
|
||||||
github.com/abema/go-mp4 v1.2.0/go.mod h1:vPl9t5ZK7K0x68jh12/+ECWBCXoWuIDtNgPtU2f04ws=
|
github.com/abema/go-mp4 v1.2.0/go.mod h1:vPl9t5ZK7K0x68jh12/+ECWBCXoWuIDtNgPtU2f04ws=
|
||||||
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
|
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
|
||||||
|
@ -86,42 +101,42 @@ github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY
|
||||||
github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
|
github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
|
||||||
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
|
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
|
||||||
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
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=
|
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
|
||||||
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
|
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
|
||||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
github.com/buckket/go-blurhash v1.1.0 h1:X5M6r0LIvwdvKiUtiNcRL2YlmOfMzYobI3VCKCZc9Do=
|
github.com/buckket/go-blurhash v1.1.0 h1:X5M6r0LIvwdvKiUtiNcRL2YlmOfMzYobI3VCKCZc9Do=
|
||||||
github.com/buckket/go-blurhash v1.1.0/go.mod h1:aT2iqo5W9vu9GpyoLErKfTHwgODsZp3bQfXjXJUxNb8=
|
github.com/buckket/go-blurhash v1.1.0/go.mod h1:aT2iqo5W9vu9GpyoLErKfTHwgODsZp3bQfXjXJUxNb8=
|
||||||
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
|
github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
|
||||||
github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM=
|
github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
|
||||||
github.com/bytedance/sonic v1.10.1 h1:7a1wuFXL1cMy7a3f7/VFcEtriuXQnUBhtoVfOZiaysc=
|
github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
|
||||||
github.com/bytedance/sonic v1.10.1/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4=
|
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||||
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
|
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
|
||||||
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
|
|
||||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
|
|
||||||
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0=
|
|
||||||
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA=
|
|
||||||
github.com/chenzhuoyu/iasm v0.9.0 h1:9fhXjVzq5hUy2gkhhgHl95zG2cEAhw9OSGs8toWWAwo=
|
|
||||||
github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog=
|
|
||||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||||
github.com/cilium/ebpf v0.9.1 h1:64sn2K3UKw8NbP/blsixRpF3nXuyhz/VjRlRzvlBRu4=
|
github.com/cilium/ebpf v0.9.1 h1:64sn2K3UKw8NbP/blsixRpF3nXuyhz/VjRlRzvlBRu4=
|
||||||
github.com/cilium/ebpf v0.9.1/go.mod h1:+OhNOIXx/Fnu1IE8bJz2dzOA+VSfyTfdNUVdlQnxUFY=
|
github.com/cilium/ebpf v0.9.1/go.mod h1:+OhNOIXx/Fnu1IE8bJz2dzOA+VSfyTfdNUVdlQnxUFY=
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
|
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
|
||||||
|
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
|
||||||
|
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
|
||||||
|
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
|
||||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||||
|
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/containerd/cgroups/v3 v3.0.1 h1:4hfGvu8rfGIwVIDd+nLzn/B9ZXx4BcCjzt5ToenJRaE=
|
github.com/containerd/cgroups/v3 v3.0.1 h1:4hfGvu8rfGIwVIDd+nLzn/B9ZXx4BcCjzt5ToenJRaE=
|
||||||
github.com/containerd/cgroups/v3 v3.0.1/go.mod h1:/vtwk1VXrtoa5AaZLkypuOJgA/6DyPMZHJPGQNtlHnw=
|
github.com/containerd/cgroups/v3 v3.0.1/go.mod h1:/vtwk1VXrtoa5AaZLkypuOJgA/6DyPMZHJPGQNtlHnw=
|
||||||
github.com/coreos/go-oidc/v3 v3.9.0 h1:0J/ogVOd4y8P0f0xUh8l9t07xRP/d8tccvjHl2dcsSo=
|
github.com/coreos/go-oidc/v3 v3.10.0 h1:tDnXHnLyiTVyT/2zLDGj09pFPkhND8Gl8lnTRhoEaJU=
|
||||||
github.com/coreos/go-oidc/v3 v3.9.0/go.mod h1:rTKz2PYwftcrtoCzV5g5kvfJoWcm0Mk8AF8y1iAQro4=
|
github.com/coreos/go-oidc/v3 v3.10.0/go.mod h1:5j11xcw0D3+SGxn6Z/WFADsgcWVMyNAlSQupk0KK3ac=
|
||||||
github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI=
|
github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI=
|
||||||
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||||
github.com/cornelk/hashmap v1.0.8 h1:nv0AWgw02n+iDcawr5It4CjQIAcdMMKRrs10HOJYlrc=
|
|
||||||
github.com/cornelk/hashmap v1.0.8/go.mod h1:RfZb7JO3RviW/rT6emczVuC/oxpdz4UsSB2LJSclR1k=
|
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
|
@ -131,8 +146,12 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
|
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
|
||||||
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
|
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
|
||||||
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
|
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
||||||
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||||
|
github.com/dolthub/maphash v0.1.0 h1:bsQ7JsF4FkkWyrP3oCnFJgrCUAFbFf3kOl4L/QxPDyQ=
|
||||||
|
github.com/dolthub/maphash v0.1.0/go.mod h1:gkg4Ch4CdCDu5h6PMriVLawB7koZ+5ijb9puGMV50a4=
|
||||||
|
github.com/dolthub/swiss v0.2.1 h1:gs2osYs5SJkAaH5/ggVJqXQxRXtWshF6uE0lgR/Y3Gw=
|
||||||
|
github.com/dolthub/swiss v0.2.1/go.mod h1:8AhKZZ1HK7g18j7v7k6c5cYIGEZJcPn0ARsai8cUrh0=
|
||||||
github.com/dsoprea/go-exif/v2 v2.0.0-20200321225314-640175a69fe4/go.mod h1:Lm2lMM2zx8p4a34ZemkaUV95AnMl4ZvLbCUbwOvLC2E=
|
github.com/dsoprea/go-exif/v2 v2.0.0-20200321225314-640175a69fe4/go.mod h1:Lm2lMM2zx8p4a34ZemkaUV95AnMl4ZvLbCUbwOvLC2E=
|
||||||
github.com/dsoprea/go-exif/v3 v3.0.0-20200717053412-08f1b6708903/go.mod h1:0nsO1ce0mh5czxGeLo4+OCZ/C6Eo6ZlMWsz7rH/Gxv8=
|
github.com/dsoprea/go-exif/v3 v3.0.0-20200717053412-08f1b6708903/go.mod h1:0nsO1ce0mh5czxGeLo4+OCZ/C6Eo6ZlMWsz7rH/Gxv8=
|
||||||
github.com/dsoprea/go-exif/v3 v3.0.0-20210428042052-dca55bf8ca15/go.mod h1:cg5SNYKHMmzxsr9X6ZeLh/nfBRHHp5PngtEPcujONtk=
|
github.com/dsoprea/go-exif/v3 v3.0.0-20210428042052-dca55bf8ca15/go.mod h1:cg5SNYKHMmzxsr9X6ZeLh/nfBRHHp5PngtEPcujONtk=
|
||||||
|
@ -158,27 +177,30 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7
|
||||||
github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8=
|
github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8=
|
||||||
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
|
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
|
||||||
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
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 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||||
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
|
github.com/fxamacker/cbor v1.5.1 h1:XjQWBgdmQyqimslUh5r4tUGmoqzHmBFQOImkWGi2awg=
|
||||||
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
|
github.com/fxamacker/cbor v1.5.1/go.mod h1:3aPGItF174ni7dDzd6JZ206H8cmr4GDNBGpPa971zsU=
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
|
||||||
|
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
|
||||||
github.com/gavv/httpexpect v2.0.0+incompatible h1:1X9kcRshkSKEjNJJxX9Y9mQ5BRfbxU5kORdjhlA1yX8=
|
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/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc=
|
||||||
github.com/gin-contrib/cors v1.5.0 h1:DgGKV7DDoOn36DFkNtbHrjoRiT5ExCe+PC9/xp7aKvk=
|
github.com/gin-contrib/cors v1.7.2 h1:oLDHxdg8W/XDoN/8zamqk/Drgt4oVZDvaV0YmvVICQw=
|
||||||
github.com/gin-contrib/cors v1.5.0/go.mod h1:TvU7MAZ3EwrPLI2ztzTt3tqgvBCq+wn8WpZmfADjupI=
|
github.com/gin-contrib/cors v1.7.2/go.mod h1:SUJVARKgQ40dmrzgXEVxj2m7Ig1v1qIboQkPDTQ9t2E=
|
||||||
github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4=
|
github.com/gin-contrib/gzip v1.0.1 h1:HQ8ENHODeLY7a4g1Au/46Z92bdGFl74OhxcZble9WJE=
|
||||||
github.com/gin-contrib/gzip v0.0.6/go.mod h1:QOJlmV2xmayAjkNS2Y8NQsMneuRShOU/kjovCXNuzzk=
|
github.com/gin-contrib/gzip v1.0.1/go.mod h1:njt428fdUNRvjuJf16tZMYZ2Yl+WQB53X5wmhDwXvC4=
|
||||||
github.com/gin-contrib/sessions v0.0.5 h1:CATtfHmLMQrMNpJRgzjWXD7worTh7g7ritsQfmF+0jE=
|
github.com/gin-contrib/sessions v1.0.1 h1:3hsJyNs7v7N8OtelFmYXFrulAf6zSR7nW/putcPEHxI=
|
||||||
github.com/gin-contrib/sessions v0.0.5/go.mod h1:vYAuaUPqie3WUSsft6HUlCjlwwoJQs97miaG2+7neKY=
|
github.com/gin-contrib/sessions v1.0.1/go.mod h1:ouxSFM24/OgIud5MJYQJLpy6AwxQ5EYO9yLhbtObGkM=
|
||||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||||
github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk=
|
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
|
||||||
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
|
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
||||||
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
|
|
||||||
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
|
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.0.2/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs=
|
||||||
github.com/go-errors/errors v1.1.1/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs=
|
github.com/go-errors/errors v1.1.1/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs=
|
||||||
|
@ -189,33 +211,53 @@ github.com/go-fed/httpsig v1.1.0/go.mod h1:RCMrTZvN1bJYtofsG4rd5NaO5obxQ5xBkdiS7
|
||||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
github.com/go-jose/go-jose/v3 v3.0.1 h1:pWmKFVtt+Jl0vBZTIpz/eAKwsm6LkIxDVVbFHKkchhA=
|
github.com/go-jose/go-jose/v4 v4.0.1 h1:QVEPDE3OluqXBQZDcnNvQrInro2h0e4eqNbnZSWqS6U=
|
||||||
github.com/go-jose/go-jose/v3 v3.0.1/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8=
|
github.com/go-jose/go-jose/v4 v4.0.1/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY=
|
||||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||||
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
|
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
|
||||||
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
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-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||||
|
github.com/go-openapi/analysis v0.23.0 h1:aGday7OWupfMs+LbmLZG4k0MYXIANxcuBTYUC03zFCU=
|
||||||
|
github.com/go-openapi/analysis v0.23.0/go.mod h1:9mz9ZWaSlV8TvjQHLl2mUW2PbZtemkE8yA5v22ohupo=
|
||||||
|
github.com/go-openapi/errors v0.22.0 h1:c4xY/OLxUBSTiepAg3j/MHuAv5mJhnf53LLMWFB+u/w=
|
||||||
|
github.com/go-openapi/errors v0.22.0/go.mod h1:J3DmZScxCDufmIMsdOuDHxJbdOGC0xtUynjIx092vXE=
|
||||||
|
github.com/go-openapi/inflect v0.21.0 h1:FoBjBTQEcbg2cJUWX6uwL9OyIW8eqc9k4KhN4lfbeYk=
|
||||||
|
github.com/go-openapi/inflect v0.21.0/go.mod h1:INezMuUu7SJQc2AyR3WO0DqqYUJSj8Kb4hBd7WtjlAw=
|
||||||
|
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
|
||||||
|
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
|
||||||
|
github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
|
||||||
|
github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
|
||||||
|
github.com/go-openapi/loads v0.22.0 h1:ECPGd4jX1U6NApCGG1We+uEozOAvXvJSF4nnwHZ8Aco=
|
||||||
|
github.com/go-openapi/loads v0.22.0/go.mod h1:yLsaTCS92mnSAZX5WWoxszLj0u+Ojl+Zs5Stn1oF+rs=
|
||||||
|
github.com/go-openapi/runtime v0.28.0 h1:gpPPmWSNGo214l6n8hzdXYhPuJcGtziTOgUpvsFWGIQ=
|
||||||
|
github.com/go-openapi/runtime v0.28.0/go.mod h1:QN7OzcS+XuYmkQLw05akXk0jRH/eZ3kb18+1KwW9gyc=
|
||||||
|
github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9ZY=
|
||||||
|
github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk=
|
||||||
|
github.com/go-openapi/strfmt v0.23.0 h1:nlUS6BCqcnAk0pyhi9Y+kdDVZdZMHfEKQiS4HaMgO/c=
|
||||||
|
github.com/go-openapi/strfmt v0.23.0/go.mod h1:NrtIpfKtWIygRkKVsxh7XQMDQW5HKQl6S5ik2elW+K4=
|
||||||
|
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
|
||||||
|
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
|
||||||
|
github.com/go-openapi/validate v0.24.0 h1:LdfDKwNbpB6Vn40xhTdNZAnfLECL81w+VX3BumrGD58=
|
||||||
|
github.com/go-openapi/validate v0.24.0/go.mod h1:iyeX1sEufmv3nPbBdX3ieNviWnOZaJ1+zquzJEf2BAQ=
|
||||||
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||||
github.com/go-playground/form/v4 v4.2.1 h1:HjdRDKO0fftVMU5epjPW2SOREcZ6/wLUzEobqUGJuPw=
|
github.com/go-playground/form/v4 v4.2.1 h1:HjdRDKO0fftVMU5epjPW2SOREcZ6/wLUzEobqUGJuPw=
|
||||||
github.com/go-playground/form/v4 v4.2.1/go.mod h1:q1a2BY+AQUUzhl6xA/6hBetay6dEIhMHjgvJiGo6K7U=
|
github.com/go-playground/form/v4 v4.2.1/go.mod h1:q1a2BY+AQUUzhl6xA/6hBetay6dEIhMHjgvJiGo6K7U=
|
||||||
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
|
|
||||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||||
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
|
|
||||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||||
github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos=
|
github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8=
|
||||||
github.com/go-playground/validator/v10 v10.15.5 h1:LEBecTWb/1j5TNY1YYG2RcOUN3R7NLylN+x8TTueE24=
|
github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||||
github.com/go-playground/validator/v10 v10.15.5/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
|
|
||||||
github.com/go-session/session v3.1.2+incompatible/go.mod h1:8B3iivBQjrz/JtC68Np2T1yBBLxTan3mn/3OM0CyRt0=
|
github.com/go-session/session v3.1.2+incompatible/go.mod h1:8B3iivBQjrz/JtC68Np2T1yBBLxTan3mn/3OM0CyRt0=
|
||||||
|
github.com/go-swagger/go-swagger v0.31.0 h1:H8eOYQnY2u7vNKWDNykv2xJP3pBhRG/R+SOCAmKrLlc=
|
||||||
|
github.com/go-swagger/go-swagger v0.31.0/go.mod h1:WSigRRWEig8zV6t6Sm8Y+EmUjlzA/HoaZJ5edupq7po=
|
||||||
github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
|
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-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
|
||||||
github.com/go-xmlfmt/xmlfmt v0.0.0-20211206191508-7fd73a941850 h1:PSPmmucxGiFBtbQcttHTUc4LQ3P09AW+ldO2qspyKdY=
|
github.com/go-xmlfmt/xmlfmt v0.0.0-20211206191508-7fd73a941850 h1:PSPmmucxGiFBtbQcttHTUc4LQ3P09AW+ldO2qspyKdY=
|
||||||
github.com/go-xmlfmt/xmlfmt v0.0.0-20211206191508-7fd73a941850/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM=
|
github.com/go-xmlfmt/xmlfmt v0.0.0-20211206191508-7fd73a941850/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM=
|
||||||
github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
|
||||||
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||||
github.com/godbus/dbus/v5 v5.0.4 h1:9349emZab16e7zQvpmsbtjc18ykshndd8y2PG3sgJbA=
|
github.com/godbus/dbus/v5 v5.0.4 h1:9349emZab16e7zQvpmsbtjc18ykshndd8y2PG3sgJbA=
|
||||||
|
@ -228,8 +270,6 @@ github.com/golang/geo v0.0.0-20200319012246-673a6f80352d/go.mod h1:QZ0nwyI2jOfgR
|
||||||
github.com/golang/geo v0.0.0-20210211234256-740aa86cb551 h1:gtexQ/VGyN+VVFRXSFiguSNcXmS6rkKT+X7FdIrTtfo=
|
github.com/golang/geo v0.0.0-20210211234256-740aa86cb551 h1:gtexQ/VGyN+VVFRXSFiguSNcXmS6rkKT+X7FdIrTtfo=
|
||||||
github.com/golang/geo v0.0.0-20210211234256-740aa86cb551/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
|
github.com/golang/geo v0.0.0-20210211234256-740aa86cb551/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo=
|
|
||||||
github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ=
|
|
||||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
|
@ -257,10 +297,6 @@ github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:W
|
||||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
|
||||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
|
||||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
|
||||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
|
||||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
|
@ -270,12 +306,13 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||||
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
|
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
|
||||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
|
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||||
|
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||||
|
@ -285,8 +322,8 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf
|
||||||
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||||
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
|
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo=
|
||||||
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
|
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
|
||||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
@ -296,21 +333,23 @@ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+
|
||||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8=
|
github.com/gorilla/context v1.1.2 h1:WRkNAv2uoa03QNIc1A6u4O7DAGMUVoopZhkiXWA2V1o=
|
||||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
github.com/gorilla/context v1.1.2/go.mod h1:KDPwT9i/MeWHiLl90fuTgrt4/wPcv75vFAZLaOOcbxM=
|
||||||
github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
|
github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
|
||||||
github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
|
github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
|
||||||
github.com/gorilla/feeds v1.1.2 h1:pxzZ5PD3RJdhFH2FsJJ4x6PqMqbgFk1+Vez4XWBW8Iw=
|
github.com/gorilla/feeds v1.1.2 h1:pxzZ5PD3RJdhFH2FsJJ4x6PqMqbgFk1+Vez4XWBW8Iw=
|
||||||
github.com/gorilla/feeds v1.1.2/go.mod h1:WMib8uJP3BbY+X8Szd1rA5Pzhdfh+HCCAYT2z7Fza6Y=
|
github.com/gorilla/feeds v1.1.2/go.mod h1:WMib8uJP3BbY+X8Szd1rA5Pzhdfh+HCCAYT2z7Fza6Y=
|
||||||
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
|
github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyEE=
|
||||||
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
|
github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w=
|
||||||
github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI=
|
github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
|
||||||
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
|
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
|
||||||
|
github.com/gorilla/sessions v1.2.2 h1:lqzMYz6bOfvn2WriPUjNByzeXIlVzURcPmgMczkmTjY=
|
||||||
|
github.com/gorilla/sessions v1.2.2/go.mod h1:ePLdVu+jbEgHH+KWw8I1z2wqd0BAdAQh/8LRvBeoNcQ=
|
||||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
|
github.com/gorilla/websocket v1.5.2 h1:qoW6V1GT3aZxybsbC6oLnailWnB+qTMVwMreOso9XUw=
|
||||||
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
|
github.com/gorilla/websocket v1.5.2/go.mod h1:0n9H61RBAcf5/38py2MCYbxzPIY9rOkpvvMT24Rqs30=
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms=
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1 h1:/c3QmbOGMGTOumP2iT/rCwB7b0QDGLKzqOmktBjT+Is=
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg=
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1/go.mod h1:5SN9VR2LTsRFsrEC6FHgRbTWrTHu6tqPeKxEQv15giM=
|
||||||
github.com/h2non/filetype v1.1.3 h1:FKkx9QbD7HR/zjK1Ia5XiBsq9zdLi5Kf3zGyFTAFkGg=
|
github.com/h2non/filetype v1.1.3 h1:FKkx9QbD7HR/zjK1Ia5XiBsq9zdLi5Kf3zGyFTAFkGg=
|
||||||
github.com/h2non/filetype v1.1.3/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY=
|
github.com/h2non/filetype v1.1.3/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY=
|
||||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
|
@ -320,7 +359,13 @@ github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyf
|
||||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
|
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=
|
||||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||||
|
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
||||||
|
github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
|
||||||
|
github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
|
||||||
github.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk=
|
github.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk=
|
||||||
github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=
|
github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||||
|
@ -329,13 +374,17 @@ 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/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
|
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
|
||||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||||
github.com/jackc/pgx/v5 v5.5.3 h1:Ces6/M3wbDXYpM8JyyPD57ivTtJACFZJd885pdIaV2s=
|
github.com/jackc/pgx/v5 v5.6.0 h1:SWJzexBzPL5jb0GEsrPMLIsi/3jOo7RHlzTjcAeDrPY=
|
||||||
github.com/jackc/pgx/v5 v5.5.3/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A=
|
github.com/jackc/pgx/v5 v5.6.0/go.mod h1:DNZ/vlrUnhWCoFGxHAG8U2ljioxukquj7utPDgtQdTw=
|
||||||
github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=
|
github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=
|
||||||
github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||||
|
github.com/jessevdk/go-flags v1.5.0 h1:1jKYvbxEjfUl0fmqTCOfonvskHHXMjBySTLW4y9LFvc=
|
||||||
|
github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
|
||||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||||
|
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||||
|
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||||
|
@ -346,16 +395,14 @@ github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q
|
||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
github.com/klauspost/compress v1.10.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
github.com/klauspost/compress v1.10.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||||
github.com/klauspost/compress v1.10.10/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
github.com/klauspost/compress v1.10.10/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||||
github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4=
|
github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU=
|
||||||
github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
|
github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
||||||
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
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.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc=
|
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||||
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
|
||||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
|
||||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
@ -363,32 +410,34 @@ github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
|
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||||
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
|
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||||
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
|
|
||||||
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
||||||
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||||
|
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.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
|
||||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U=
|
|
||||||
github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
|
||||||
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg=
|
|
||||||
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k=
|
|
||||||
github.com/microcosm-cc/bluemonday v1.0.26 h1:xbqSvqzQMeEHCqMi64VAs4d8uy6Mequs3rQ0k/Khz58=
|
github.com/microcosm-cc/bluemonday v1.0.26 h1:xbqSvqzQMeEHCqMi64VAs4d8uy6Mequs3rQ0k/Khz58=
|
||||||
github.com/microcosm-cc/bluemonday v1.0.26/go.mod h1:JyzOCs9gkyQyjs+6h10UEVSe02CGwkhd72Xdqh78TWs=
|
github.com/microcosm-cc/bluemonday v1.0.26/go.mod h1:JyzOCs9gkyQyjs+6h10UEVSe02CGwkhd72Xdqh78TWs=
|
||||||
github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4=
|
github.com/miekg/dns v1.1.59 h1:C9EXc/UToRwKLhK5wKU/I4QVsBUc8kE6MkHBkeypWZs=
|
||||||
github.com/miekg/dns v1.1.58/go.mod h1:Ypv+3b/KadlvW9vJfXOTf300O4UqaHFzFCuHz+rPkBY=
|
github.com/miekg/dns v1.1.59/go.mod h1:nZpewl5p6IvctfgrckopVx2OlSEHPRO/U4SYkRklrEk=
|
||||||
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
|
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/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
|
||||||
github.com/minio/minio-go/v7 v7.0.67 h1:BeBvZWAS+kRJm1vGTMJYVjKUNoo0FoEt/wUWdUtfmh8=
|
github.com/minio/minio-go/v7 v7.0.71 h1:No9XfOKTYi6i0GnBj+WZwD8WP5GZfL7n7GOjRqCdAjA=
|
||||||
github.com/minio/minio-go/v7 v7.0.67/go.mod h1:+UXocnUeZ3wHvVh5s95gcrA4YjMIbccT6ubB+1m054A=
|
github.com/minio/minio-go/v7 v7.0.71/go.mod h1:4yBA8v80xGA30cfM3fz0DKYMXunWl/AV/6tWEs9ryzo=
|
||||||
github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM=
|
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
|
||||||
github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8=
|
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/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=
|
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
|
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||||
|
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
|
||||||
|
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
@ -396,8 +445,12 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
|
||||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
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 h1:dRMWoAtb+ePxMlLkrCbAqh4TlPHXvoGUSQ323/9Zahs=
|
||||||
github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ=
|
github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ=
|
||||||
|
github.com/ncruces/go-sqlite3 v0.16.2 h1:HesVRr0BC6QSGSEQfEXOntFWS9wd4Z8ms4nJzfUv4Rg=
|
||||||
|
github.com/ncruces/go-sqlite3 v0.16.2/go.mod h1:wkUIvOrAjFQnefVlivJfcowKUcfMHs4mvLfhVanzHHI=
|
||||||
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
|
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
|
||||||
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||||
|
github.com/ncruces/julianday v1.0.0 h1:fH0OKwa7NWvniGQtxdJRxAgkBMolni2BjDHaWTxqt7M=
|
||||||
|
github.com/ncruces/julianday v1.0.0/go.mod h1:Dusn2KvZrrovOMJuOt0TNXL6tB7U2E8kvza5fFc9G7g=
|
||||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||||
github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
|
github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
|
||||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||||
|
@ -412,9 +465,8 @@ github.com/orcaman/writerseeker v0.0.0-20200621085525-1d3f536ff85e h1:s2RNOM/IGd
|
||||||
github.com/orcaman/writerseeker v0.0.0-20200621085525-1d3f536ff85e/go.mod h1:nBdnFKj15wFbf94Rwfq4m30eAcyY9V/IyKAGQFtqkW0=
|
github.com/orcaman/writerseeker v0.0.0-20200621085525-1d3f536ff85e/go.mod h1:nBdnFKj15wFbf94Rwfq4m30eAcyY9V/IyKAGQFtqkW0=
|
||||||
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0=
|
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/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y=
|
||||||
github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo=
|
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
|
||||||
github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI=
|
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
||||||
github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
|
|
||||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
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 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
|
@ -423,13 +475,13 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
|
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
|
||||||
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
|
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
|
||||||
github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk=
|
github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE=
|
||||||
github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA=
|
github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=
|
||||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
|
github.com/prometheus/client_model v0.6.0 h1:k1v3CzpSRUTrKMppY35TLwPvxHqBu0bYgxZzqGIgaos=
|
||||||
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
|
github.com/prometheus/client_model v0.6.0/go.mod h1:NTQHnmxFpouOD0DpvP4XujX3CdOAGQPoaGhyTchlyt8=
|
||||||
github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM=
|
github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE=
|
||||||
github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY=
|
github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
|
||||||
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
|
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
|
||||||
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
|
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
|
||||||
github.com/quasoft/memstore v0.0.0-20191010062613-2bce066d2b0b h1:aUNXCGgukb4gtY99imuIeoh8Vr0GSwAlYxPAhqZrpFc=
|
github.com/quasoft/memstore v0.0.0-20191010062613-2bce066d2b0b h1:aUNXCGgukb4gtY99imuIeoh8Vr0GSwAlYxPAhqZrpFc=
|
||||||
|
@ -437,10 +489,9 @@ github.com/quasoft/memstore v0.0.0-20191010062613-2bce066d2b0b/go.mod h1:wTPjTep
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||||
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||||
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||||
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
|
|
||||||
github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc=
|
github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc=
|
||||||
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
|
@ -451,6 +502,9 @@ github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWR
|
||||||
github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw=
|
github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw=
|
||||||
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
|
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/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/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
|
||||||
|
@ -461,33 +515,36 @@ github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9yS
|
||||||
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
|
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
|
||||||
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
|
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
|
||||||
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
|
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
|
||||||
|
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||||
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
|
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
|
||||||
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||||
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
|
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
|
||||||
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
|
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
|
||||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ=
|
github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
|
||||||
github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk=
|
github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
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.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.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.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
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.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.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
|
||||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||||
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||||
github.com/sunfish-shogi/bufseekio v0.0.0-20210207115823-a4185644b365/go.mod h1:dEzdXgvImkQ3WLI+0KQpmEx8T/C/ma9KeS3AfmU899I=
|
github.com/sunfish-shogi/bufseekio v0.0.0-20210207115823-a4185644b365/go.mod h1:dEzdXgvImkQ3WLI+0KQpmEx8T/C/ma9KeS3AfmU899I=
|
||||||
github.com/superseriousbusiness/activity v1.6.0-gts.0.20240221151241-5d56c04088d4 h1:kPjQR/hVZtROTzkxptp/EIR7Wm58O8jppwpCFrZ7sVU=
|
github.com/superseriousbusiness/activity v1.6.0-gts.0.20240408131430-247f7f7110f0 h1:zPdbgwbjPxrJqme2sFTMQoML5ukNWRhChOnilR47rss=
|
||||||
github.com/superseriousbusiness/activity v1.6.0-gts.0.20240221151241-5d56c04088d4/go.mod h1:AZw0Xb4Oju8rmaJCZ21gc5CPg47MmNgyac+Hx5jo8VM=
|
github.com/superseriousbusiness/activity v1.6.0-gts.0.20240408131430-247f7f7110f0/go.mod h1:AZw0Xb4Oju8rmaJCZ21gc5CPg47MmNgyac+Hx5jo8VM=
|
||||||
github.com/superseriousbusiness/go-jpeg-image-structure/v2 v2.0.0-20220321154430-d89a106fdabe h1:ksl2oCx/Qo8sNDc3Grb8WGKBM9nkvhCm25uvlT86azE=
|
github.com/superseriousbusiness/go-jpeg-image-structure/v2 v2.0.0-20220321154430-d89a106fdabe h1:ksl2oCx/Qo8sNDc3Grb8WGKBM9nkvhCm25uvlT86azE=
|
||||||
github.com/superseriousbusiness/go-jpeg-image-structure/v2 v2.0.0-20220321154430-d89a106fdabe/go.mod h1:gH4P6gN1V+wmIw5o97KGaa1RgXB/tVpC2UNzijhg3E4=
|
github.com/superseriousbusiness/go-jpeg-image-structure/v2 v2.0.0-20220321154430-d89a106fdabe/go.mod h1:gH4P6gN1V+wmIw5o97KGaa1RgXB/tVpC2UNzijhg3E4=
|
||||||
github.com/superseriousbusiness/go-png-image-structure/v2 v2.0.1-SSB h1:8psprYSK1KdOSH7yQ4PbJq0YYaGQY+gzdW/B0ExDb/8=
|
github.com/superseriousbusiness/go-png-image-structure/v2 v2.0.1-SSB h1:8psprYSK1KdOSH7yQ4PbJq0YYaGQY+gzdW/B0ExDb/8=
|
||||||
|
@ -496,15 +553,17 @@ github.com/superseriousbusiness/httpsig v1.2.0-SSB h1:BinBGKbf2LSuVT5+MuH0XynHN9
|
||||||
github.com/superseriousbusiness/httpsig v1.2.0-SSB/go.mod h1:+rxfATjFaDoDIVaJOTSP0gj6UrbicaYPEptvCLC9F28=
|
github.com/superseriousbusiness/httpsig v1.2.0-SSB/go.mod h1:+rxfATjFaDoDIVaJOTSP0gj6UrbicaYPEptvCLC9F28=
|
||||||
github.com/superseriousbusiness/oauth2/v4 v4.3.2-SSB.0.20230227143000-f4900831d6c8 h1:nTIhuP157oOFcscuoK1kCme1xTeGIzztSw70lX9NrDQ=
|
github.com/superseriousbusiness/oauth2/v4 v4.3.2-SSB.0.20230227143000-f4900831d6c8 h1:nTIhuP157oOFcscuoK1kCme1xTeGIzztSw70lX9NrDQ=
|
||||||
github.com/superseriousbusiness/oauth2/v4 v4.3.2-SSB.0.20230227143000-f4900831d6c8/go.mod h1:uYC/W92oVRJ49Vh1GcvTqpeFqHi+Ovrl2sMllQWRAEo=
|
github.com/superseriousbusiness/oauth2/v4 v4.3.2-SSB.0.20230227143000-f4900831d6c8/go.mod h1:uYC/W92oVRJ49Vh1GcvTqpeFqHi+Ovrl2sMllQWRAEo=
|
||||||
github.com/tdewolff/minify/v2 v2.20.18 h1:y+s6OzlZwFqApgNXWNtaMuEMEPbHT72zrCyb9Az35Xo=
|
github.com/tdewolff/minify/v2 v2.20.33 h1:lZFesDQagd+zGxyC3fEO/X2jZWB8CrahKi77lNrgAAQ=
|
||||||
github.com/tdewolff/minify/v2 v2.20.18/go.mod h1:ulkFoeAVWMLEyjuDz1ZIWOA31g5aWOawCFRp9R/MudM=
|
github.com/tdewolff/minify/v2 v2.20.33/go.mod h1:1TJni7+mATKu24cBQQpgwakrYRD27uC1/rdJOgdv8ns=
|
||||||
github.com/tdewolff/parse/v2 v2.7.12 h1:tgavkHc2ZDEQVKy1oWxwIyh5bP4F5fEh/JmBwPP/3LQ=
|
github.com/tdewolff/parse/v2 v2.7.14 h1:100KJ+QAO3PpMb3uUjzEU/NpmCdbBYz6KPmCIAfWpR8=
|
||||||
github.com/tdewolff/parse/v2 v2.7.12/go.mod h1:3FbJWZp3XT9OWVN3Hmfp0p/a08v4h8J9W1aghka0soA=
|
github.com/tdewolff/parse/v2 v2.7.14/go.mod h1:3FbJWZp3XT9OWVN3Hmfp0p/a08v4h8J9W1aghka0soA=
|
||||||
github.com/tdewolff/test v1.0.11-0.20231101010635-f1265d231d52/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
|
github.com/tdewolff/test v1.0.11-0.20231101010635-f1265d231d52/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
|
||||||
github.com/tdewolff/test v1.0.11-0.20240106005702-7de5f7df4739 h1:IkjBCtQOOjIn03u/dMQK9g+Iw9ewps4mCl1nB8Sscbo=
|
github.com/tdewolff/test v1.0.11-0.20240106005702-7de5f7df4739 h1:IkjBCtQOOjIn03u/dMQK9g+Iw9ewps4mCl1nB8Sscbo=
|
||||||
github.com/tdewolff/test v1.0.11-0.20240106005702-7de5f7df4739/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=
|
github.com/tdewolff/test v1.0.11-0.20240106005702-7de5f7df4739/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=
|
||||||
github.com/technologize/otel-go-contrib v1.1.0 h1:gl9bxxJAgXFnKJzoprJOfbvNRE1k3Ky9O7ppVJDb9gg=
|
github.com/technologize/otel-go-contrib v1.1.1 h1:wZH9aSPNWZWIkEh3vfaKfMb15AJ80jJ1aVj/4GZdqIw=
|
||||||
github.com/technologize/otel-go-contrib v1.1.0/go.mod h1:dCN/wj2WyUO8aFZFdIN+6tfJHImjTML/8r2YVYAy3So=
|
github.com/technologize/otel-go-contrib v1.1.1/go.mod h1:dCN/wj2WyUO8aFZFdIN+6tfJHImjTML/8r2YVYAy3So=
|
||||||
|
github.com/tetratelabs/wazero v1.7.3 h1:PBH5KVahrt3S2AHgEjKu4u+LlDbbk+nsGE3KLucy6Rw=
|
||||||
|
github.com/tetratelabs/wazero v1.7.3/go.mod h1:ytl6Zuh20R/eROuyDaGPkp82O9C/DJfXAwJfQ3X6/7Y=
|
||||||
github.com/tidwall/btree v0.0.0-20191029221954-400434d76274 h1:G6Z6HvJuPjG6XfNGi/feOATzeJrfgTNJY+rGrHbA04E=
|
github.com/tidwall/btree v0.0.0-20191029221954-400434d76274 h1:G6Z6HvJuPjG6XfNGi/feOATzeJrfgTNJY+rGrHbA04E=
|
||||||
github.com/tidwall/btree v0.0.0-20191029221954-400434d76274/go.mod h1:huei1BkDWJ3/sLXmO+bsCNELL+Bp2Kks9OLyQFkzvA8=
|
github.com/tidwall/btree v0.0.0-20191029221954-400434d76274/go.mod h1:huei1BkDWJ3/sLXmO+bsCNELL+Bp2Kks9OLyQFkzvA8=
|
||||||
github.com/tidwall/buntdb v1.1.2 h1:noCrqQXL9EKMtcdwJcmuVKSEjqu1ua99RHHgbLTEHRo=
|
github.com/tidwall/buntdb v1.1.2 h1:noCrqQXL9EKMtcdwJcmuVKSEjqu1ua99RHHgbLTEHRo=
|
||||||
|
@ -527,24 +586,24 @@ github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc h1:9lRDQMhESg+zvGYm
|
||||||
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs=
|
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 h1:nrZ3ySNYwJbSpD6ce9duiP+QkD3JuLCcWkdaehUS/3Y=
|
||||||
github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80/go.mod h1:iFyPdL66DjUD96XmzVL3ZntbzcflLnznH0fr99w5VqE=
|
github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80/go.mod h1:iFyPdL66DjUD96XmzVL3ZntbzcflLnznH0fr99w5VqE=
|
||||||
|
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 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||||
github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
|
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
|
||||||
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
|
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||||
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
|
|
||||||
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
|
||||||
github.com/ulule/limiter/v3 v3.11.2 h1:P4yOrxoEMJbOTfRJR2OzjL90oflzYPPmWg+dvwN2tHA=
|
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/ulule/limiter/v3 v3.11.2/go.mod h1:QG5GnFOCV+k7lrL5Y8kgEeeflPH3+Cviqlqa8SVSQxI=
|
||||||
github.com/uptrace/bun v1.1.17 h1:qxBaEIo0hC/8O3O6GrMDKxqyT+mw5/s0Pn/n6xjyGIk=
|
github.com/uptrace/bun v1.2.1 h1:2ENAcfeCfaY5+2e7z5pXrzFKy3vS8VXvkCag6N2Yzfk=
|
||||||
github.com/uptrace/bun v1.1.17/go.mod h1:hATAzivtTIRsSJR4B8AXR+uABqnQxr3myKDKEf5iQ9U=
|
github.com/uptrace/bun v1.2.1/go.mod h1:cNg+pWBUMmJ8rHnETgf65CEvn3aIKErrwOD6IA8e+Ec=
|
||||||
github.com/uptrace/bun/dialect/pgdialect v1.1.17 h1:NsvFVHAx1Az6ytlAD/B6ty3cVE6j9Yp82bjqd9R9hOs=
|
github.com/uptrace/bun/dialect/pgdialect v1.2.1 h1:ceP99r03u+s8ylaDE/RzgcajwGiC76Jz3nS2ZgyPQ4M=
|
||||||
github.com/uptrace/bun/dialect/pgdialect v1.1.17/go.mod h1:fLBDclNc7nKsZLzNjFL6BqSdgJzbj2HdnyOnLoDvAME=
|
github.com/uptrace/bun/dialect/pgdialect v1.2.1/go.mod h1:mv6B12cisvSc6bwKm9q9wcrr26awkZK8QXM+nso9n2U=
|
||||||
github.com/uptrace/bun/dialect/sqlitedialect v1.1.17 h1:i8NFU9r8YuavNFaYlNqi4ppn+MgoHtqLgpWQDrVTjm0=
|
github.com/uptrace/bun/dialect/sqlitedialect v1.2.1 h1:IprvkIKUjEjvt4VKpcmLpbMIucjrsmUPJOSlg19+a0Q=
|
||||||
github.com/uptrace/bun/dialect/sqlitedialect v1.1.17/go.mod h1:YF0FO4VVnY9GHNH6rM4r3STlVEBxkOc6L88Bm5X5mzA=
|
github.com/uptrace/bun/dialect/sqlitedialect v1.2.1/go.mod h1:mMQf4NUpgY8bnOanxGmxNiHCdALOggS4cZ3v63a9D/o=
|
||||||
github.com/uptrace/bun/extra/bunotel v1.1.16 h1:qkLTaTZK3FZk3b2P/stO/krS7KX9Fq5wSOj7Hlb2HG8=
|
github.com/uptrace/bun/extra/bunotel v1.2.1 h1:5oTy3Jh7Q1bhCd5vnPszBmJgYouw+PuuZ8iSCm+uNCQ=
|
||||||
github.com/uptrace/bun/extra/bunotel v1.1.16/go.mod h1:JwEH0kdXFnzYuK8D6eXUrf9HKsYy5wmB+lqQ/+dvH4E=
|
github.com/uptrace/bun/extra/bunotel v1.2.1/go.mod h1:SWW3HyjiXPYM36q0QSpdtTP8v21nWHnTCxu4lYkpO90=
|
||||||
github.com/uptrace/opentelemetry-go-extra/otelsql v0.2.3 h1:LNi0Qa7869/loPjz2kmMvp/jwZZnMZ9scMJKhDJ1DIo=
|
github.com/uptrace/opentelemetry-go-extra/otelsql v0.2.4 h1:x3omFAG2XkvWFg1hvXRinY2ExAL1Aacl7W9ZlYjo6gc=
|
||||||
github.com/uptrace/opentelemetry-go-extra/otelsql v0.2.3/go.mod h1:jyigonKik3C5V895QNiAGpKYKEvFuqjw9qAEZks1mUg=
|
github.com/uptrace/opentelemetry-go-extra/otelsql v0.2.4/go.mod h1:qMKJr5fTnY0p7hqCQMNrAk62bCARWR5rAbTrGUFRuh4=
|
||||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
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/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||||
github.com/valyala/fasthttp v1.14.0/go.mod h1:ol1PCaL0dX20wC0htZ7sYCsvCYmrouYra0zHzaclZhE=
|
github.com/valyala/fasthttp v1.14.0/go.mod h1:ol1PCaL0dX20wC0htZ7sYCsvCYmrouYra0zHzaclZhE=
|
||||||
|
@ -557,6 +616,8 @@ github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAh
|
||||||
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
|
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
|
||||||
github.com/wagslane/go-password-validator v0.3.0 h1:vfxOPzGHkz5S146HDpavl0cw1DSVP061Ry2PX0/ON6I=
|
github.com/wagslane/go-password-validator v0.3.0 h1:vfxOPzGHkz5S146HDpavl0cw1DSVP061Ry2PX0/ON6I=
|
||||||
github.com/wagslane/go-password-validator v0.3.0/go.mod h1:TI1XJ6T5fRdRnHqHt14pvy1tNVnrwe7m3/f1f2fDphQ=
|
github.com/wagslane/go-password-validator v0.3.0/go.mod h1:TI1XJ6T5fRdRnHqHt14pvy1tNVnrwe7m3/f1f2fDphQ=
|
||||||
|
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||||
|
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
|
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
|
||||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
|
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
|
||||||
|
@ -574,37 +635,37 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
|
||||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||||
github.com/yuin/goldmark v1.7.0 h1:EfOIvIMZIzHdB/R/zVrikYLPPwJlfMcNczJFMs1m6sA=
|
github.com/yuin/goldmark v1.7.1 h1:3bajkSilaCbjdKVsKdZjZCLBNPL9pYzrCakKaf4U49U=
|
||||||
github.com/yuin/goldmark v1.7.0/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
|
github.com/yuin/goldmark v1.7.1/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
|
||||||
github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ=
|
gitlab.com/NyaaaWhatsUpDoc/sqlite v1.29.9-concurrency-workaround h1:gFAlklid3jyXIuZBy5Vy0dhG+F6YBgosRy4syT5CDsg=
|
||||||
github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
|
gitlab.com/NyaaaWhatsUpDoc/sqlite v1.29.9-concurrency-workaround/go.mod h1:ItX2a1OVGgNsFh6Dv60JQvGfJfTPHPVpV6DF59akYOA=
|
||||||
github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0=
|
go.mongodb.org/mongo-driver v1.14.0 h1:P98w8egYRjYe3XDjxhYJagTokP/H6HzlsnojRgZRd80=
|
||||||
github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA=
|
go.mongodb.org/mongo-driver v1.14.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c=
|
||||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
go.opentelemetry.io/otel v1.20.0 h1:vsb/ggIY+hUjD/zCAQHpzTmndPqv/ml2ArbsbfBYTAc=
|
go.opentelemetry.io/otel v1.26.0 h1:LQwgL5s/1W7YiiRwxf03QGnWLb2HW4pLiAhaA5cZXBs=
|
||||||
go.opentelemetry.io/otel v1.20.0/go.mod h1:oUIGj3D77RwJdM6PPZImDpSZGDvkD9fhesHny69JFrs=
|
go.opentelemetry.io/otel v1.26.0/go.mod h1:UmLkJHUAidDval2EICqBMbnAd0/m2vmpf/dAM+fvFs4=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.20.0 h1:DeFD0VgTZ+Cj6hxravYYZE2W4GlneVH81iAOPjZkzk8=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.26.0 h1:1u/AyyOqAWzy+SkPxDpahCNZParHV8Vid1RnI2clyDE=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.20.0/go.mod h1:GijYcYmNpX1KazD5JmWGsi4P7dDTTTnfv1UbGn84MnU=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.26.0/go.mod h1:z46paqbJ9l7c9fIPCXTqTGwhQZ5XoTIsfeFYWboizjs=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.20.0 h1:gvmNvqrPYovvyRmCSygkUDyL8lC5Tl845MLEwqpxhEU=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.26.0 h1:Waw9Wfpo/IXzOI8bCB7DIk+0JZcqqsyn1JFnAc+iam8=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.20.0/go.mod h1:vNUq47TGFioo+ffTSnKNdob241vePmtNZnAODKapKd0=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.26.0/go.mod h1:wnJIG4fOqyynOnnQF/eQb4/16VlX2EJAHhHgqIqWfAo=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.20.0 h1:CsBiKCiQPdSjS+MlRiqeTI9JDDpSuk0Hb6QTRfwer8k=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0 h1:Xw8U6u2f8DK2XAkGRFV7BBLENgnTGX9i4rQRxJf+/vs=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.20.0/go.mod h1:CMJYNAfooOwSZSAmAeMUV1M+TXld3BiK++z9fqIm2xk=
|
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0/go.mod h1:6KW1Fm6R/s6Z3PGXwSJN2K4eT6wQB3vXX6CVnYX9NmM=
|
||||||
go.opentelemetry.io/otel/exporters/prometheus v0.43.0 h1:Skkl6akzvdWweXX6LLAY29tyFSO6hWZ26uDbVGTDXe8=
|
go.opentelemetry.io/otel/exporters/prometheus v0.46.0 h1:I8WIFXR351FoLJYuloU4EgXbtNX2URfU/85pUPheIEQ=
|
||||||
go.opentelemetry.io/otel/exporters/prometheus v0.43.0/go.mod h1:nZStMoc1H/YJpRjSx9IEX4abBMekORTLQcTUT1CgLkg=
|
go.opentelemetry.io/otel/exporters/prometheus v0.46.0/go.mod h1:ztwVUHe5DTR/1v7PeuGRnU5Bbd4QKYwApWmuutKsJSs=
|
||||||
go.opentelemetry.io/otel/metric v1.20.0 h1:ZlrO8Hu9+GAhnepmRGhSU7/VkpjrNowxRN9GyKR4wzA=
|
go.opentelemetry.io/otel/metric v1.26.0 h1:7S39CLuY5Jgg9CrnA9HHiEjGMF/X2VHvoXGgSllRz30=
|
||||||
go.opentelemetry.io/otel/metric v1.20.0/go.mod h1:90DRw3nfK4D7Sm/75yQ00gTJxtkBxX+wu6YaNymbpVM=
|
go.opentelemetry.io/otel/metric v1.26.0/go.mod h1:SY+rHOI4cEawI9a7N1A4nIg/nTQXe1ccCNWYOJUrpX4=
|
||||||
go.opentelemetry.io/otel/sdk v1.20.0 h1:5Jf6imeFZlZtKv9Qbo6qt2ZkmWtdWx/wzcCbNUlAWGM=
|
go.opentelemetry.io/otel/sdk v1.26.0 h1:Y7bumHf5tAiDlRYFmGqetNcLaVUZmh4iYfmGxtmz7F8=
|
||||||
go.opentelemetry.io/otel/sdk v1.20.0/go.mod h1:rmkSx1cZCm/tn16iWDn1GQbLtsW/LvsdEEFzCSRM6V0=
|
go.opentelemetry.io/otel/sdk v1.26.0/go.mod h1:0p8MXpqLeJ0pzcszQQN4F0S5FVjBLgypeGSngLsmirs=
|
||||||
go.opentelemetry.io/otel/sdk/metric v1.20.0 h1:5eD40l/H2CqdKmbSV7iht2KMK0faAIL2pVYzJOWobGk=
|
go.opentelemetry.io/otel/sdk/metric v1.24.0 h1:yyMQrPzF+k88/DbH7o4FMAs80puqd+9osbiBrJrz/w8=
|
||||||
go.opentelemetry.io/otel/sdk/metric v1.20.0/go.mod h1:AGvpC+YF/jblITiafMTYgvRBUiwi9hZf0EYE2E5XlS8=
|
go.opentelemetry.io/otel/sdk/metric v1.24.0/go.mod h1:I6Y5FjH6rvEnTTAYQz3Mmv2kl6Ek5IIrmwTLqMrrOE0=
|
||||||
go.opentelemetry.io/otel/trace v1.20.0 h1:+yxVAPZPbQhbC3OfAkeIVTky6iTFpcr4SiY9om7mXSQ=
|
go.opentelemetry.io/otel/trace v1.26.0 h1:1ieeAUb4y0TE26jUFrCIXKpTuVK7uJGN9/Z/2LP5sQA=
|
||||||
go.opentelemetry.io/otel/trace v1.20.0/go.mod h1:HJSK7F/hA5RlzpZ0zKDCHCDHm556LCDtKaAo6JmBFUU=
|
go.opentelemetry.io/otel/trace v1.26.0/go.mod h1:4iDxvGDQuUkHve82hJJ8UqrwswHYsZuWCBllGV2U2y0=
|
||||||
go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I=
|
go.opentelemetry.io/proto/otlp v1.2.0 h1:pVeZGk7nXDC9O2hncA6nHldxEjm6LByfA2aN8IOkz94=
|
||||||
go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM=
|
go.opentelemetry.io/proto/otlp v1.2.0/go.mod h1:gGpR8txAl5M03pDhMC79G6SdqNV26naRm/KDsgaHD8A=
|
||||||
go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8=
|
go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8=
|
||||||
go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0=
|
go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0=
|
||||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||||
|
@ -612,18 +673,17 @@ 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 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||||
golang.org/x/arch v0.5.0 h1:jpGode6huXQxcskEIpOCvrU+tzo81b6+oFLUYXWtH/Y=
|
golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
|
||||||
golang.org/x/arch v0.5.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.20.0 h1:jmAMJJZXr5KiCw05dfYK9QnqaqKLYXijU23lsEdcQqg=
|
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||||
golang.org/x/crypto v0.20.0/go.mod h1:Xwo95rrVNIoSMx9wa1JroENMToLWn3RNVrTBpLHgZPQ=
|
golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
|
||||||
|
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||||
|
@ -634,13 +694,13 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
|
||||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||||
golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 h1:hNQpMuAJe5CtcUqCXaWga3FHu+kQvCqcsoVaQgSV60o=
|
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 h1:LfspQV/FYTatPTr/3HzIcmiUFH7PGP+OQ6mgDYo3yuQ=
|
||||||
golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08=
|
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc=
|
||||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
golang.org/x/image v0.15.0 h1:kOELfmgrmJlw4Cdb7g/QGuB3CvDrXbqEIww/pNtNBm8=
|
golang.org/x/image v0.17.0 h1:nTRVVdajgB8zCMZVsViyzhnMKPwYeroEERRC64JuLco=
|
||||||
golang.org/x/image v0.15.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE=
|
golang.org/x/image v0.17.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E=
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
|
@ -660,8 +720,8 @@ golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzB
|
||||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
|
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
|
||||||
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
@ -693,16 +753,17 @@ golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81R
|
||||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
|
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
|
||||||
|
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||||
golang.org/x/oauth2 v0.17.0 h1:6m3ZPmLEFdVxKKWnKq4VqZ60gutO35zm+zrAHVmHyDQ=
|
golang.org/x/oauth2 v0.20.0 h1:4mQdhULixXKP1rwYBW0vAijoXnkTG0BLCDRzfe1idMo=
|
||||||
golang.org/x/oauth2 v0.17.0/go.mod h1:OzPDGQiuQMguemayvdylqddI7qcD9lnSDb+1FiwQ5HA=
|
golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
@ -712,8 +773,8 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ
|
||||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/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.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||||
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/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
@ -746,30 +807,30 @@ golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||||
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
|
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
|
||||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U=
|
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
||||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA=
|
||||||
|
golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
|
||||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
|
@ -815,8 +876,8 @@ golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc
|
||||||
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||||
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc=
|
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
|
||||||
golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps=
|
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
@ -843,8 +904,6 @@ google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
|
||||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||||
google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
|
|
||||||
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
|
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
@ -874,12 +933,10 @@ google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7Fc
|
||||||
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||||
google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 h1:wpZ8pe2x1Q3f2KyT5f8oP/fa9rHAKgFPr/HZdNuS+PQ=
|
google.golang.org/genproto/googleapis/api v0.0.0-20240311132316-a219d84964c2 h1:rIo7ocm2roD9DcFIX67Ym8icoGCKSARAiPljFhh5suQ=
|
||||||
google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:J7XzRzVy1+IPwWHZUzoD0IccYZIrXILAQpc+Qy9CMhY=
|
google.golang.org/genproto/googleapis/api v0.0.0-20240311132316-a219d84964c2/go.mod h1:O1cOfN1Cy6QEYr7VxtjOyP5AdAuR0aJ/MYZaaof623Y=
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 h1:JpwMPBpFN3uKhdaekDpiNlImDdkUAyiJ6ez/uxGaUSo=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda h1:LI5DOvAxUPMv/50agcLLoo+AdWc1irS9Rzz4vPuD1V4=
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:0xJLfVdJqpAPl8tDg1ujOCGzx6LFLttXT5NhllGOXY4=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f h1:ultW7fxlIvee4HYrtnaRPon9HpEgFk5zYpmfMgtKB5I=
|
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f/go.mod h1:L9KNLi232K1/xB6f7AlSX692koaRnKaWSR0stBki0Yc=
|
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||||
|
@ -892,8 +949,8 @@ google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKa
|
||||||
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
||||||
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||||
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||||
google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk=
|
google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM=
|
||||||
google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98=
|
google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA=
|
||||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||||
|
@ -904,11 +961,8 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
|
||||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
|
||||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||||
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
|
||||||
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
|
|
||||||
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
@ -932,7 +986,6 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
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.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
@ -942,16 +995,26 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
|
||||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||||
|
modernc.org/cc/v4 v4.20.0 h1:45Or8mQfbUqJOG9WaxvlFYOAQO0lQ5RvqBcFCXngjxk=
|
||||||
|
modernc.org/cc/v4 v4.20.0/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ=
|
||||||
|
modernc.org/ccgo/v4 v4.16.0 h1:ofwORa6vx2FMm0916/CkZjpFPSR70VwTjUCe2Eg5BnA=
|
||||||
|
modernc.org/ccgo/v4 v4.16.0/go.mod h1:dkNyWIjFrVIZ68DTo36vHK+6/ShBn4ysU61So6PIqCI=
|
||||||
|
modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE=
|
||||||
|
modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ=
|
||||||
|
modernc.org/gc/v2 v2.4.1 h1:9cNzOqPyMJBvrUipmynX0ZohMhcxPtMccYgGOJdOiBw=
|
||||||
|
modernc.org/gc/v2 v2.4.1/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU=
|
||||||
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 h1:5D53IMaUuA5InSeMu9eJtlQXS2NxAhyWQvkKEgXZhHI=
|
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 h1:5D53IMaUuA5InSeMu9eJtlQXS2NxAhyWQvkKEgXZhHI=
|
||||||
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4=
|
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4=
|
||||||
modernc.org/libc v1.41.0 h1:g9YAc6BkKlgORsUWj+JwqoB1wU3o4DE3bM3yvA3k+Gk=
|
modernc.org/libc v1.49.3 h1:j2MRCRdwJI2ls/sGbeSk0t2bypOG/uvPZUsGQFDulqg=
|
||||||
modernc.org/libc v1.41.0/go.mod h1:w0eszPsiXoOnoMJgrXjglgLuDy/bt5RR4y3QzUUeodY=
|
modernc.org/libc v1.49.3/go.mod h1:yMZuGkn7pXbKfoT/M35gFJOAEdSKdxL0q64sF7KqCDo=
|
||||||
modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
|
modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
|
||||||
modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
|
modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
|
||||||
modernc.org/memory v1.7.2 h1:Klh90S215mmH8c9gO98QxQFsY+W451E8AnzjoE2ee1E=
|
modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E=
|
||||||
modernc.org/memory v1.7.2/go.mod h1:NO4NVCQy0N7ln+T9ngWqOQfi7ley4vpwvARR+Hjw95E=
|
modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU=
|
||||||
modernc.org/sqlite v1.29.2 h1:xgBSyA3gemwgP31PWFfFjtBorQNYpeypGdoSDjXhrgI=
|
modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
|
||||||
modernc.org/sqlite v1.29.2/go.mod h1:hG41jCYxOAOoO6BRK66AdRlmOcDzXf7qnwlwjUIOqa0=
|
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
|
||||||
|
modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc=
|
||||||
|
modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss=
|
||||||
modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA=
|
modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA=
|
||||||
modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0=
|
modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0=
|
||||||
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
||||||
|
|
|
@ -25,6 +25,7 @@ import (
|
||||||
"github.com/superseriousbusiness/activity/streams/vocab"
|
"github.com/superseriousbusiness/activity/streams/vocab"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/ap"
|
"github.com/superseriousbusiness/gotosocial/internal/ap"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/paging"
|
"github.com/superseriousbusiness/gotosocial/internal/paging"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestASCollection(t *testing.T) {
|
func TestASCollection(t *testing.T) {
|
||||||
|
@ -49,8 +50,40 @@ func TestASCollection(t *testing.T) {
|
||||||
// Create new collection using builder function.
|
// Create new collection using builder function.
|
||||||
c := ap.NewASCollection(ap.CollectionParams{
|
c := ap.NewASCollection(ap.CollectionParams{
|
||||||
ID: parseURI(idURI),
|
ID: parseURI(idURI),
|
||||||
|
First: new(paging.Page),
|
||||||
Query: url.Values{"limit": []string{"40"}},
|
Query: url.Values{"limit": []string{"40"}},
|
||||||
Total: total,
|
Total: util.Ptr(total),
|
||||||
|
})
|
||||||
|
|
||||||
|
// Serialize collection.
|
||||||
|
s := toJSON(c)
|
||||||
|
|
||||||
|
// Ensure outputs are equal.
|
||||||
|
assert.Equal(t, expect, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestASCollectionTotalOnly(t *testing.T) {
|
||||||
|
const (
|
||||||
|
proto = "https"
|
||||||
|
host = "zorg.flabormagorg.xyz"
|
||||||
|
path = "/users/itsa_me_mario"
|
||||||
|
|
||||||
|
idURI = proto + "://" + host + path
|
||||||
|
total = 10
|
||||||
|
)
|
||||||
|
|
||||||
|
// Create JSON string of expected output.
|
||||||
|
expect := toJSON(map[string]any{
|
||||||
|
"@context": "https://www.w3.org/ns/activitystreams",
|
||||||
|
"type": "Collection",
|
||||||
|
"id": idURI,
|
||||||
|
"totalItems": total,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Create new collection using builder function.
|
||||||
|
c := ap.NewASCollection(ap.CollectionParams{
|
||||||
|
ID: parseURI(idURI),
|
||||||
|
Total: util.Ptr(total),
|
||||||
})
|
})
|
||||||
|
|
||||||
// Serialize collection.
|
// Serialize collection.
|
||||||
|
@ -96,7 +129,7 @@ func TestASCollectionPage(t *testing.T) {
|
||||||
p := ap.NewASCollectionPage(ap.CollectionPageParams{
|
p := ap.NewASCollectionPage(ap.CollectionPageParams{
|
||||||
CollectionParams: ap.CollectionParams{
|
CollectionParams: ap.CollectionParams{
|
||||||
ID: parseURI(idURI),
|
ID: parseURI(idURI),
|
||||||
Total: total,
|
Total: util.Ptr(total),
|
||||||
},
|
},
|
||||||
|
|
||||||
Current: currPg,
|
Current: currPg,
|
||||||
|
@ -132,8 +165,60 @@ func TestASOrderedCollection(t *testing.T) {
|
||||||
// Create new collection using builder function.
|
// Create new collection using builder function.
|
||||||
c := ap.NewASOrderedCollection(ap.CollectionParams{
|
c := ap.NewASOrderedCollection(ap.CollectionParams{
|
||||||
ID: parseURI(idURI),
|
ID: parseURI(idURI),
|
||||||
|
First: new(paging.Page),
|
||||||
Query: url.Values{"limit": []string{"40"}},
|
Query: url.Values{"limit": []string{"40"}},
|
||||||
Total: total,
|
Total: util.Ptr(total),
|
||||||
|
})
|
||||||
|
|
||||||
|
// Serialize collection.
|
||||||
|
s := toJSON(c)
|
||||||
|
|
||||||
|
// Ensure outputs are equal.
|
||||||
|
assert.Equal(t, expect, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestASOrderedCollectionTotalOnly(t *testing.T) {
|
||||||
|
const (
|
||||||
|
idURI = "https://zorg.flabormagorg.xyz/users/itsa_me_mario"
|
||||||
|
total = 10
|
||||||
|
)
|
||||||
|
|
||||||
|
// Create JSON string of expected output.
|
||||||
|
expect := toJSON(map[string]any{
|
||||||
|
"@context": "https://www.w3.org/ns/activitystreams",
|
||||||
|
"type": "OrderedCollection",
|
||||||
|
"id": idURI,
|
||||||
|
"totalItems": total,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Create new collection using builder function.
|
||||||
|
c := ap.NewASOrderedCollection(ap.CollectionParams{
|
||||||
|
ID: parseURI(idURI),
|
||||||
|
Total: util.Ptr(total),
|
||||||
|
})
|
||||||
|
|
||||||
|
// Serialize collection.
|
||||||
|
s := toJSON(c)
|
||||||
|
|
||||||
|
// Ensure outputs are equal.
|
||||||
|
assert.Equal(t, expect, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestASOrderedCollectionNoTotal(t *testing.T) {
|
||||||
|
const (
|
||||||
|
idURI = "https://zorg.flabormagorg.xyz/users/itsa_me_mario"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Create JSON string of expected output.
|
||||||
|
expect := toJSON(map[string]any{
|
||||||
|
"@context": "https://www.w3.org/ns/activitystreams",
|
||||||
|
"type": "OrderedCollection",
|
||||||
|
"id": idURI,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Create new collection using builder function.
|
||||||
|
c := ap.NewASOrderedCollection(ap.CollectionParams{
|
||||||
|
ID: parseURI(idURI),
|
||||||
})
|
})
|
||||||
|
|
||||||
// Serialize collection.
|
// Serialize collection.
|
||||||
|
@ -179,7 +264,7 @@ func TestASOrderedCollectionPage(t *testing.T) {
|
||||||
p := ap.NewASOrderedCollectionPage(ap.CollectionPageParams{
|
p := ap.NewASOrderedCollectionPage(ap.CollectionPageParams{
|
||||||
CollectionParams: ap.CollectionParams{
|
CollectionParams: ap.CollectionParams{
|
||||||
ID: parseURI(idURI),
|
ID: parseURI(idURI),
|
||||||
Total: total,
|
Total: util.Ptr(total),
|
||||||
},
|
},
|
||||||
|
|
||||||
Current: currPg,
|
Current: currPg,
|
||||||
|
|
|
@ -105,6 +105,15 @@ func (iter *regularCollectionIterator) PrevItem() TypeOrIRI {
|
||||||
return cur
|
return cur
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (iter *regularCollectionIterator) TotalItems() int {
|
||||||
|
totalItems := iter.GetActivityStreamsTotalItems()
|
||||||
|
if totalItems == nil || !totalItems.IsXMLSchemaNonNegativeInteger() {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
return totalItems.Get()
|
||||||
|
}
|
||||||
|
|
||||||
func (iter *regularCollectionIterator) initItems() bool {
|
func (iter *regularCollectionIterator) initItems() bool {
|
||||||
if iter.once {
|
if iter.once {
|
||||||
return (iter.items != nil)
|
return (iter.items != nil)
|
||||||
|
@ -147,6 +156,15 @@ func (iter *orderedCollectionIterator) PrevItem() TypeOrIRI {
|
||||||
return cur
|
return cur
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (iter *orderedCollectionIterator) TotalItems() int {
|
||||||
|
totalItems := iter.GetActivityStreamsTotalItems()
|
||||||
|
if totalItems == nil || !totalItems.IsXMLSchemaNonNegativeInteger() {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
return totalItems.Get()
|
||||||
|
}
|
||||||
|
|
||||||
func (iter *orderedCollectionIterator) initItems() bool {
|
func (iter *orderedCollectionIterator) initItems() bool {
|
||||||
if iter.once {
|
if iter.once {
|
||||||
return (iter.items != nil)
|
return (iter.items != nil)
|
||||||
|
@ -203,6 +221,15 @@ func (iter *regularCollectionPageIterator) PrevItem() TypeOrIRI {
|
||||||
return cur
|
return cur
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (iter *regularCollectionPageIterator) TotalItems() int {
|
||||||
|
totalItems := iter.GetActivityStreamsTotalItems()
|
||||||
|
if totalItems == nil || !totalItems.IsXMLSchemaNonNegativeInteger() {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
return totalItems.Get()
|
||||||
|
}
|
||||||
|
|
||||||
func (iter *regularCollectionPageIterator) initItems() bool {
|
func (iter *regularCollectionPageIterator) initItems() bool {
|
||||||
if iter.once {
|
if iter.once {
|
||||||
return (iter.items != nil)
|
return (iter.items != nil)
|
||||||
|
@ -259,6 +286,15 @@ func (iter *orderedCollectionPageIterator) PrevItem() TypeOrIRI {
|
||||||
return cur
|
return cur
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (iter *orderedCollectionPageIterator) TotalItems() int {
|
||||||
|
totalItems := iter.GetActivityStreamsTotalItems()
|
||||||
|
if totalItems == nil || !totalItems.IsXMLSchemaNonNegativeInteger() {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
return totalItems.Get()
|
||||||
|
}
|
||||||
|
|
||||||
func (iter *orderedCollectionPageIterator) initItems() bool {
|
func (iter *orderedCollectionPageIterator) initItems() bool {
|
||||||
if iter.once {
|
if iter.once {
|
||||||
return (iter.items != nil)
|
return (iter.items != nil)
|
||||||
|
@ -281,11 +317,12 @@ type CollectionParams struct {
|
||||||
ID *url.URL
|
ID *url.URL
|
||||||
|
|
||||||
// First page details.
|
// First page details.
|
||||||
First paging.Page
|
First *paging.Page
|
||||||
Query url.Values
|
Query url.Values
|
||||||
|
|
||||||
// Total no. items.
|
// Total no. items.
|
||||||
Total int
|
// Omitted if nil.
|
||||||
|
Total *int
|
||||||
}
|
}
|
||||||
|
|
||||||
type CollectionPageParams struct {
|
type CollectionPageParams struct {
|
||||||
|
@ -331,6 +368,7 @@ type CollectionPageBuilder interface {
|
||||||
// vocab.ActivityStreamsOrderedItemsProperty
|
// vocab.ActivityStreamsOrderedItemsProperty
|
||||||
type ItemsPropertyBuilder interface {
|
type ItemsPropertyBuilder interface {
|
||||||
AppendIRI(*url.URL)
|
AppendIRI(*url.URL)
|
||||||
|
AppendActivityStreamsCreate(vocab.ActivityStreamsCreate)
|
||||||
|
|
||||||
// NOTE: add more of the items-property-like interface
|
// NOTE: add more of the items-property-like interface
|
||||||
// functions here as you require them for building pages.
|
// functions here as you require them for building pages.
|
||||||
|
@ -373,9 +411,16 @@ func buildCollection[C CollectionBuilder](collection C, params CollectionParams)
|
||||||
collection.SetJSONLDId(idProp)
|
collection.SetJSONLDId(idProp)
|
||||||
|
|
||||||
// Add the collection totalItems count property.
|
// Add the collection totalItems count property.
|
||||||
totalItems := streams.NewActivityStreamsTotalItemsProperty()
|
if params.Total != nil {
|
||||||
totalItems.Set(params.Total)
|
totalItems := streams.NewActivityStreamsTotalItemsProperty()
|
||||||
collection.SetActivityStreamsTotalItems(totalItems)
|
totalItems.Set(*params.Total)
|
||||||
|
collection.SetActivityStreamsTotalItems(totalItems)
|
||||||
|
}
|
||||||
|
|
||||||
|
// No First page means we're done.
|
||||||
|
if params.First == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Append paging query params
|
// Append paging query params
|
||||||
// to those already in ID prop.
|
// to those already in ID prop.
|
||||||
|
@ -456,9 +501,11 @@ func buildCollectionPage[C CollectionPageBuilder, I ItemsPropertyBuilder](collec
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the collection totalItems count property.
|
// Add the collection totalItems count property.
|
||||||
totalItems := streams.NewActivityStreamsTotalItemsProperty()
|
if params.Total != nil {
|
||||||
totalItems.Set(params.Total)
|
totalItems := streams.NewActivityStreamsTotalItemsProperty()
|
||||||
collectionPage.SetActivityStreamsTotalItems(totalItems)
|
totalItems.Set(*params.Total)
|
||||||
|
collectionPage.SetActivityStreamsTotalItems(totalItems)
|
||||||
|
}
|
||||||
|
|
||||||
if params.Append == nil {
|
if params.Append == nil {
|
||||||
// nil check outside the for loop.
|
// nil check outside the for loop.
|
||||||
|
|
|
@ -515,9 +515,9 @@ func ExtractURL(i WithURL) (*url.URL, error) {
|
||||||
return nil, gtserror.New("no valid URL property found")
|
return nil, gtserror.New("no valid URL property found")
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExtractPublicKey extracts the public key, public key ID, and public
|
// ExtractPubKeyFromActor extracts the public key, public key ID, and public
|
||||||
// key owner ID from an interface, or an error if something goes wrong.
|
// key owner ID from an interface, or an error if something goes wrong.
|
||||||
func ExtractPublicKey(i WithPublicKey) (
|
func ExtractPubKeyFromActor(i WithPublicKey) (
|
||||||
*rsa.PublicKey, // pubkey
|
*rsa.PublicKey, // pubkey
|
||||||
*url.URL, // pubkey ID
|
*url.URL, // pubkey ID
|
||||||
*url.URL, // pubkey owner
|
*url.URL, // pubkey owner
|
||||||
|
@ -528,6 +528,7 @@ func ExtractPublicKey(i WithPublicKey) (
|
||||||
return nil, nil, nil, gtserror.New("public key property was nil")
|
return nil, nil, nil, gtserror.New("public key property was nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Take the first public key we can find.
|
||||||
for iter := pubKeyProp.Begin(); iter != pubKeyProp.End(); iter = iter.Next() {
|
for iter := pubKeyProp.Begin(); iter != pubKeyProp.End(); iter = iter.Next() {
|
||||||
if !iter.IsW3IDSecurityV1PublicKey() {
|
if !iter.IsW3IDSecurityV1PublicKey() {
|
||||||
continue
|
continue
|
||||||
|
@ -538,63 +539,74 @@ func ExtractPublicKey(i WithPublicKey) (
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
pubKeyID, err := pub.GetId(pkey)
|
return ExtractPubKeyFromKey(pkey)
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
pubKeyOwnerProp := pkey.GetW3IDSecurityV1Owner()
|
|
||||||
if pubKeyOwnerProp == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
pubKeyOwner := pubKeyOwnerProp.GetIRI()
|
|
||||||
if pubKeyOwner == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
pubKeyPemProp := pkey.GetW3IDSecurityV1PublicKeyPem()
|
|
||||||
if pubKeyPemProp == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
pkeyPem := pubKeyPemProp.Get()
|
|
||||||
if pkeyPem == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
block, _ := pem.Decode([]byte(pkeyPem))
|
|
||||||
if block == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
var p crypto.PublicKey
|
|
||||||
switch block.Type {
|
|
||||||
case "PUBLIC KEY":
|
|
||||||
p, err = x509.ParsePKIXPublicKey(block.Bytes)
|
|
||||||
case "RSA PUBLIC KEY":
|
|
||||||
p, err = x509.ParsePKCS1PublicKey(block.Bytes)
|
|
||||||
default:
|
|
||||||
err = fmt.Errorf("unknown block type: %q", block.Type)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
err = gtserror.Newf("could not parse public key from block bytes: %w", err)
|
|
||||||
return nil, nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if p == nil {
|
|
||||||
return nil, nil, nil, gtserror.New("returned public key was empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
pubKey, ok := p.(*rsa.PublicKey)
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
return pubKey, pubKeyID, pubKeyOwner, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, nil, nil, gtserror.New("couldn't find public key")
|
return nil, nil, nil, gtserror.New("couldn't find valid public key")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExtractPubKeyFromActor extracts the public key, public key ID, and public
|
||||||
|
// key owner ID from an interface, or an error if something goes wrong.
|
||||||
|
func ExtractPubKeyFromKey(pkey vocab.W3IDSecurityV1PublicKey) (
|
||||||
|
*rsa.PublicKey, // pubkey
|
||||||
|
*url.URL, // pubkey ID
|
||||||
|
*url.URL, // pubkey owner
|
||||||
|
error,
|
||||||
|
) {
|
||||||
|
pubKeyID, err := pub.GetId(pkey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, errors.New("no id set on public key")
|
||||||
|
}
|
||||||
|
|
||||||
|
pubKeyOwnerProp := pkey.GetW3IDSecurityV1Owner()
|
||||||
|
if pubKeyOwnerProp == nil {
|
||||||
|
return nil, nil, nil, errors.New("nil pubKeyOwnerProp")
|
||||||
|
}
|
||||||
|
|
||||||
|
pubKeyOwner := pubKeyOwnerProp.GetIRI()
|
||||||
|
if pubKeyOwner == nil {
|
||||||
|
return nil, nil, nil, errors.New("nil iri on pubKeyOwnerProp")
|
||||||
|
}
|
||||||
|
|
||||||
|
pubKeyPemProp := pkey.GetW3IDSecurityV1PublicKeyPem()
|
||||||
|
if pubKeyPemProp == nil {
|
||||||
|
return nil, nil, nil, errors.New("nil pubKeyPemProp")
|
||||||
|
}
|
||||||
|
|
||||||
|
pkeyPem := pubKeyPemProp.Get()
|
||||||
|
if pkeyPem == "" {
|
||||||
|
return nil, nil, nil, errors.New("empty pubKeyPemProp")
|
||||||
|
}
|
||||||
|
|
||||||
|
block, _ := pem.Decode([]byte(pkeyPem))
|
||||||
|
if block == nil {
|
||||||
|
return nil, nil, nil, errors.New("nil pubKeyPem")
|
||||||
|
}
|
||||||
|
|
||||||
|
var p crypto.PublicKey
|
||||||
|
switch block.Type {
|
||||||
|
case "PUBLIC KEY":
|
||||||
|
p, err = x509.ParsePKIXPublicKey(block.Bytes)
|
||||||
|
case "RSA PUBLIC KEY":
|
||||||
|
p, err = x509.ParsePKCS1PublicKey(block.Bytes)
|
||||||
|
default:
|
||||||
|
err = fmt.Errorf("unknown block type: %q", block.Type)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
err = fmt.Errorf("could not parse public key from block bytes: %w", err)
|
||||||
|
return nil, nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if p == nil {
|
||||||
|
return nil, nil, nil, fmt.Errorf("returned public key was empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
pubKey, ok := p.(*rsa.PublicKey)
|
||||||
|
if !ok {
|
||||||
|
return nil, nil, nil, fmt.Errorf("could not type pubKey to *rsa.PublicKey")
|
||||||
|
}
|
||||||
|
|
||||||
|
return pubKey, pubKeyID, pubKeyOwner, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExtractContent returns an intermediary representation of
|
// ExtractContent returns an intermediary representation of
|
||||||
|
|
108
internal/ap/extractpubkey_test.go
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
// 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 (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
|
"github.com/superseriousbusiness/activity/streams"
|
||||||
|
typepublickey "github.com/superseriousbusiness/activity/streams/impl/w3idsecurityv1/type_publickey"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/ap"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
stubActor = `{
|
||||||
|
"@context": [
|
||||||
|
"https://www.w3.org/ns/activitystreams",
|
||||||
|
"https://w3id.org/security/v1"
|
||||||
|
],
|
||||||
|
"id": "https://gts.superseriousbusiness.org/users/dumpsterqueer",
|
||||||
|
"preferredUsername": "dumpsterqueer",
|
||||||
|
"publicKey": {
|
||||||
|
"id": "https://gts.superseriousbusiness.org/users/dumpsterqueer/main-key",
|
||||||
|
"owner": "https://gts.superseriousbusiness.org/users/dumpsterqueer",
|
||||||
|
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAt7cDz2XfTJXbmmmVXZ3o\nQGB1zu1yP+2/QZZFbLCeM0bMm5cfjJ/olli6kpdcGLh1lFpSgyLE0PlAVNYdSke9\nzcxDao6N16wavFx/bOYhh8HJPPXzlFpNeQQ+EBQ1ivzuLQyzIFTMV4TyZzOREoG9\nizuXuuKDaH/ENDE6qlIDuqtICIjnURjpxnBLldPUxfUvuSO3zY+jTidsxhjUjqkK\nC7RtEVi/D6/CzktVevz5bE/gcAYgKmK0dmkJ9HH6LzOlvkM4Wrq5h/hrM+H1z5e5\nPpdJsl3KlRT4wusuM1Z5xqLQ0oIP4mX/Kd3ypCe150i+jaoCsqBk8OPtl/zKMw1a\nYQIDAQAB\n-----END PUBLIC KEY-----\n"
|
||||||
|
},
|
||||||
|
"type": "Person"
|
||||||
|
}`
|
||||||
|
|
||||||
|
key = `{
|
||||||
|
"@context": "https://w3id.org/security/v1",
|
||||||
|
"id": "https://gts.superseriousbusiness.org/users/dumpsterqueer/main-key",
|
||||||
|
"owner": "https://gts.superseriousbusiness.org/users/dumpsterqueer",
|
||||||
|
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAt7cDz2XfTJXbmmmVXZ3o\nQGB1zu1yP+2/QZZFbLCeM0bMm5cfjJ/olli6kpdcGLh1lFpSgyLE0PlAVNYdSke9\nzcxDao6N16wavFx/bOYhh8HJPPXzlFpNeQQ+EBQ1ivzuLQyzIFTMV4TyZzOREoG9\nizuXuuKDaH/ENDE6qlIDuqtICIjnURjpxnBLldPUxfUvuSO3zY+jTidsxhjUjqkK\nC7RtEVi/D6/CzktVevz5bE/gcAYgKmK0dmkJ9HH6LzOlvkM4Wrq5h/hrM+H1z5e5\nPpdJsl3KlRT4wusuM1Z5xqLQ0oIP4mX/Kd3ypCe150i+jaoCsqBk8OPtl/zKMw1a\nYQIDAQAB\n-----END PUBLIC KEY-----\n"
|
||||||
|
}`
|
||||||
|
)
|
||||||
|
|
||||||
|
type ExtractPubKeyTestSuite struct {
|
||||||
|
APTestSuite
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *ExtractPubKeyTestSuite) TestExtractPubKeyFromStub() {
|
||||||
|
m := make(map[string]interface{})
|
||||||
|
if err := json.Unmarshal([]byte(stubActor), &m); err != nil {
|
||||||
|
suite.FailNow(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
t, err := streams.ToType(context.Background(), m)
|
||||||
|
if err != nil {
|
||||||
|
suite.FailNow(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
wpk, ok := t.(ap.WithPublicKey)
|
||||||
|
if !ok {
|
||||||
|
suite.FailNow("", "could not parse %T as WithPublicKey", t)
|
||||||
|
}
|
||||||
|
|
||||||
|
pubKey, pubKeyID, ownerURI, err := ap.ExtractPubKeyFromActor(wpk)
|
||||||
|
if err != nil {
|
||||||
|
suite.FailNow(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
suite.NotNil(pubKey)
|
||||||
|
suite.Equal("https://gts.superseriousbusiness.org/users/dumpsterqueer/main-key", pubKeyID.String())
|
||||||
|
suite.Equal("https://gts.superseriousbusiness.org/users/dumpsterqueer", ownerURI.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *ExtractPubKeyTestSuite) TestExtractPubKeyFromKey() {
|
||||||
|
m := make(map[string]interface{})
|
||||||
|
if err := json.Unmarshal([]byte(key), &m); err != nil {
|
||||||
|
suite.FailNow(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
pk, err := typepublickey.DeserializePublicKey(m, nil)
|
||||||
|
if err != nil {
|
||||||
|
suite.FailNow(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
pubKey, pubKeyID, ownerURI, err := ap.ExtractPubKeyFromKey(pk)
|
||||||
|
if err != nil {
|
||||||
|
suite.FailNow(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
suite.NotNil(pubKey)
|
||||||
|
suite.Equal("https://gts.superseriousbusiness.org/users/dumpsterqueer/main-key", pubKeyID.String())
|
||||||
|
suite.Equal("https://gts.superseriousbusiness.org/users/dumpsterqueer", ownerURI.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExtractPubKeyTestSuite(t *testing.T) {
|
||||||
|
suite.Run(t, &ExtractPubKeyTestSuite{})
|
||||||
|
}
|
|
@ -307,6 +307,12 @@ type CollectionIterator interface {
|
||||||
|
|
||||||
NextItem() TypeOrIRI
|
NextItem() TypeOrIRI
|
||||||
PrevItem() TypeOrIRI
|
PrevItem() TypeOrIRI
|
||||||
|
|
||||||
|
// TotalItems returns the total items
|
||||||
|
// present in the collection, derived
|
||||||
|
// from the totalItems property, or -1
|
||||||
|
// if totalItems not present / readable.
|
||||||
|
TotalItems() int
|
||||||
}
|
}
|
||||||
|
|
||||||
// CollectionPageIterator represents the minimum interface for interacting with a wrapped
|
// CollectionPageIterator represents the minimum interface for interacting with a wrapped
|
||||||
|
@ -319,6 +325,12 @@ type CollectionPageIterator interface {
|
||||||
|
|
||||||
NextItem() TypeOrIRI
|
NextItem() TypeOrIRI
|
||||||
PrevItem() TypeOrIRI
|
PrevItem() TypeOrIRI
|
||||||
|
|
||||||
|
// TotalItems returns the total items
|
||||||
|
// present in the collection, derived
|
||||||
|
// from the totalItems property, or -1
|
||||||
|
// if totalItems not present / readable.
|
||||||
|
TotalItems() int
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flaggable represents the minimum interface for an activitystreams 'Flag' activity.
|
// Flaggable represents the minimum interface for an activitystreams 'Flag' activity.
|
||||||
|
|
|
@ -192,7 +192,7 @@ func GetObjectIRIs(with WithObject) []*url.URL {
|
||||||
}
|
}
|
||||||
|
|
||||||
// AppendObjectIRIs appends the given IRIs to the Object property of 'with'.
|
// AppendObjectIRIs appends the given IRIs to the Object property of 'with'.
|
||||||
func AppendObjectIRIs(with WithObject) {
|
func AppendObjectIRIs(with WithObject, object ...*url.URL) {
|
||||||
appendIRIs(func() Property[vocab.ActivityStreamsObjectPropertyIterator] {
|
appendIRIs(func() Property[vocab.ActivityStreamsObjectPropertyIterator] {
|
||||||
objectProp := with.GetActivityStreamsObject()
|
objectProp := with.GetActivityStreamsObject()
|
||||||
if objectProp == nil {
|
if objectProp == nil {
|
||||||
|
@ -200,7 +200,7 @@ func AppendObjectIRIs(with WithObject) {
|
||||||
with.SetActivityStreamsObject(objectProp)
|
with.SetActivityStreamsObject(objectProp)
|
||||||
}
|
}
|
||||||
return objectProp
|
return objectProp
|
||||||
})
|
}, object...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetTargetIRIs returns the IRIs contained in the Target property of 'with'.
|
// GetTargetIRIs returns the IRIs contained in the Target property of 'with'.
|
||||||
|
@ -210,7 +210,7 @@ func GetTargetIRIs(with WithTarget) []*url.URL {
|
||||||
}
|
}
|
||||||
|
|
||||||
// AppendTargetIRIs appends the given IRIs to the Target property of 'with'.
|
// AppendTargetIRIs appends the given IRIs to the Target property of 'with'.
|
||||||
func AppendTargetIRIs(with WithTarget) {
|
func AppendTargetIRIs(with WithTarget, target ...*url.URL) {
|
||||||
appendIRIs(func() Property[vocab.ActivityStreamsTargetPropertyIterator] {
|
appendIRIs(func() Property[vocab.ActivityStreamsTargetPropertyIterator] {
|
||||||
targetProp := with.GetActivityStreamsTarget()
|
targetProp := with.GetActivityStreamsTarget()
|
||||||
if targetProp == nil {
|
if targetProp == nil {
|
||||||
|
@ -218,7 +218,7 @@ func AppendTargetIRIs(with WithTarget) {
|
||||||
with.SetActivityStreamsTarget(targetProp)
|
with.SetActivityStreamsTarget(targetProp)
|
||||||
}
|
}
|
||||||
return targetProp
|
return targetProp
|
||||||
})
|
}, target...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAttributedTo returns the IRIs contained in the AttributedTo property of 'with'.
|
// GetAttributedTo returns the IRIs contained in the AttributedTo property of 'with'.
|
||||||
|
|
|
@ -72,7 +72,7 @@ type SwaggerFeaturedCollection struct {
|
||||||
// example: OrderedCollection
|
// example: OrderedCollection
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
// List of status URIs.
|
// List of status URIs.
|
||||||
// example: ['https://example.org/users/some_user/statuses/01GSZ0F7Q8SJKNRF777GJD271R', 'https://example.org/users/some_user/statuses/01GSZ0G012CBQ7TEKX689S3QRE']
|
// example: ["https://example.org/users/some_user/statuses/01GSZ0F7Q8SJKNRF777GJD271R", "https://example.org/users/some_user/statuses/01GSZ0G012CBQ7TEKX689S3QRE"]
|
||||||
Items []string `json:"items"`
|
Items []string `json:"items"`
|
||||||
// Number of items in this collection.
|
// Number of items in this collection.
|
||||||
// example: 2
|
// example: 2
|
||||||
|
|
|
@ -44,6 +44,14 @@ import (
|
||||||
// produces:
|
// produces:
|
||||||
// - application/activity+json
|
// - application/activity+json
|
||||||
//
|
//
|
||||||
|
// parameters:
|
||||||
|
// -
|
||||||
|
// name: username
|
||||||
|
// type: string
|
||||||
|
// description: Account name of the user
|
||||||
|
// in: path
|
||||||
|
// required: true
|
||||||
|
//
|
||||||
// responses:
|
// responses:
|
||||||
// '200':
|
// '200':
|
||||||
// in: body
|
// in: body
|
||||||
|
|
|
@ -18,13 +18,14 @@
|
||||||
package users
|
package users
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/log"
|
"github.com/superseriousbusiness/gotosocial/internal/log"
|
||||||
|
|
||||||
|
errorsv2 "codeberg.org/gruf/go-errors/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// InboxPOSTHandler deals with incoming POST requests to an actor's inbox.
|
// InboxPOSTHandler deals with incoming POST requests to an actor's inbox.
|
||||||
|
@ -32,18 +33,18 @@ import (
|
||||||
func (m *Module) InboxPOSTHandler(c *gin.Context) {
|
func (m *Module) InboxPOSTHandler(c *gin.Context) {
|
||||||
_, err := m.processor.Fedi().InboxPost(c.Request.Context(), c.Writer, c.Request)
|
_, err := m.processor.Fedi().InboxPost(c.Request.Context(), c.Writer, c.Request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errWithCode := new(gtserror.WithCode)
|
errWithCode := errorsv2.AsV2[gtserror.WithCode](err)
|
||||||
|
|
||||||
if !errors.As(err, errWithCode) {
|
if errWithCode == nil {
|
||||||
// Something else went wrong, and someone forgot to return
|
// Something else went wrong, and someone forgot to return
|
||||||
// an errWithCode! It's chill though. Log the error but don't
|
// an errWithCode! It's chill though. Log the error but don't
|
||||||
// return it as-is to the caller, to avoid leaking internals.
|
// return it as-is to the caller, to avoid leaking internals.
|
||||||
log.Errorf(c.Request.Context(), "returning Bad Request to caller, err was: %q", err)
|
log.Errorf(c.Request.Context(), "returning Bad Request to caller, err was: %q", err)
|
||||||
*errWithCode = gtserror.NewErrorBadRequest(err)
|
errWithCode = gtserror.NewErrorBadRequest(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pass along confirmed error with code to the main error handler
|
// Pass along confirmed error with code to the main error handler
|
||||||
apiutil.ErrorHandler(c, *errWithCode, m.processor.InstanceGetV1)
|
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -395,12 +395,8 @@ func (suite *InboxPostTestSuite) TestPostUpdate() {
|
||||||
suite.EqualValues(requestingAccount.AlsoKnownAsURIs, dbUpdatedAccount.AlsoKnownAsURIs)
|
suite.EqualValues(requestingAccount.AlsoKnownAsURIs, dbUpdatedAccount.AlsoKnownAsURIs)
|
||||||
suite.EqualValues(requestingAccount.MovedToURI, dbUpdatedAccount.MovedToURI)
|
suite.EqualValues(requestingAccount.MovedToURI, dbUpdatedAccount.MovedToURI)
|
||||||
suite.EqualValues(requestingAccount.Bot, dbUpdatedAccount.Bot)
|
suite.EqualValues(requestingAccount.Bot, dbUpdatedAccount.Bot)
|
||||||
suite.EqualValues(requestingAccount.Reason, dbUpdatedAccount.Reason)
|
|
||||||
suite.EqualValues(requestingAccount.Locked, dbUpdatedAccount.Locked)
|
suite.EqualValues(requestingAccount.Locked, dbUpdatedAccount.Locked)
|
||||||
suite.EqualValues(requestingAccount.Discoverable, dbUpdatedAccount.Discoverable)
|
suite.EqualValues(requestingAccount.Discoverable, dbUpdatedAccount.Discoverable)
|
||||||
suite.EqualValues(requestingAccount.Privacy, dbUpdatedAccount.Privacy)
|
|
||||||
suite.EqualValues(requestingAccount.Sensitive, dbUpdatedAccount.Sensitive)
|
|
||||||
suite.EqualValues(requestingAccount.Language, dbUpdatedAccount.Language)
|
|
||||||
suite.EqualValues(requestingAccount.URI, dbUpdatedAccount.URI)
|
suite.EqualValues(requestingAccount.URI, dbUpdatedAccount.URI)
|
||||||
suite.EqualValues(requestingAccount.URL, dbUpdatedAccount.URL)
|
suite.EqualValues(requestingAccount.URL, dbUpdatedAccount.URL)
|
||||||
suite.EqualValues(requestingAccount.InboxURI, dbUpdatedAccount.InboxURI)
|
suite.EqualValues(requestingAccount.InboxURI, dbUpdatedAccount.InboxURI)
|
||||||
|
@ -414,7 +410,6 @@ func (suite *InboxPostTestSuite) TestPostUpdate() {
|
||||||
suite.EqualValues(requestingAccount.SensitizedAt, dbUpdatedAccount.SensitizedAt)
|
suite.EqualValues(requestingAccount.SensitizedAt, dbUpdatedAccount.SensitizedAt)
|
||||||
suite.EqualValues(requestingAccount.SilencedAt, dbUpdatedAccount.SilencedAt)
|
suite.EqualValues(requestingAccount.SilencedAt, dbUpdatedAccount.SilencedAt)
|
||||||
suite.EqualValues(requestingAccount.SuspendedAt, dbUpdatedAccount.SuspendedAt)
|
suite.EqualValues(requestingAccount.SuspendedAt, dbUpdatedAccount.SuspendedAt)
|
||||||
suite.EqualValues(requestingAccount.HideCollections, dbUpdatedAccount.HideCollections)
|
|
||||||
suite.EqualValues(requestingAccount.SuspensionOrigin, dbUpdatedAccount.SuspensionOrigin)
|
suite.EqualValues(requestingAccount.SuspensionOrigin, dbUpdatedAccount.SuspensionOrigin)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -464,9 +459,7 @@ func (suite *InboxPostTestSuite) TestPostDelete() {
|
||||||
suite.Empty(dbAccount.AvatarRemoteURL)
|
suite.Empty(dbAccount.AvatarRemoteURL)
|
||||||
suite.Empty(dbAccount.HeaderMediaAttachmentID)
|
suite.Empty(dbAccount.HeaderMediaAttachmentID)
|
||||||
suite.Empty(dbAccount.HeaderRemoteURL)
|
suite.Empty(dbAccount.HeaderRemoteURL)
|
||||||
suite.Empty(dbAccount.Reason)
|
|
||||||
suite.Empty(dbAccount.Fields)
|
suite.Empty(dbAccount.Fields)
|
||||||
suite.True(*dbAccount.HideCollections)
|
|
||||||
suite.False(*dbAccount.Discoverable)
|
suite.False(*dbAccount.Discoverable)
|
||||||
suite.WithinDuration(time.Now(), dbAccount.SuspendedAt, 30*time.Second)
|
suite.WithinDuration(time.Now(), dbAccount.SuspendedAt, 30*time.Second)
|
||||||
suite.Equal(dbAccount.ID, dbAccount.SuspensionOrigin)
|
suite.Equal(dbAccount.ID, dbAccount.SuspensionOrigin)
|
||||||
|
@ -597,7 +590,7 @@ func (suite *InboxPostTestSuite) TestPostUnauthorized() {
|
||||||
requestingAccount,
|
requestingAccount,
|
||||||
targetAccount,
|
targetAccount,
|
||||||
http.StatusUnauthorized,
|
http.StatusUnauthorized,
|
||||||
`{"error":"Unauthorized: not authenticated"}`,
|
`{"error":"Unauthorized: http request wasn't signed or http signature was invalid: (verifier)"}`,
|
||||||
// Omit signature check middleware.
|
// Omit signature check middleware.
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,14 +19,13 @@ package users
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/paging"
|
||||||
)
|
)
|
||||||
|
|
||||||
// OutboxGETHandler swagger:operation GET /users/{username}/outbox s2sOutboxGet
|
// OutboxGETHandler swagger:operation GET /users/{username}/outbox s2sOutboxGet
|
||||||
|
@ -105,30 +104,17 @@ func (m *Module) OutboxGETHandler(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var page bool
|
page, errWithCode := paging.ParseIDPage(c,
|
||||||
if pageString := c.Query(PageKey); pageString != "" {
|
1, // min limit
|
||||||
i, err := strconv.ParseBool(pageString)
|
80, // max limit
|
||||||
if err != nil {
|
0, // default = disabled
|
||||||
err := fmt.Errorf("error parsing %s: %s", PageKey, err)
|
)
|
||||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGetV1)
|
if errWithCode != nil {
|
||||||
return
|
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||||
}
|
return
|
||||||
page = i
|
|
||||||
}
|
}
|
||||||
|
|
||||||
minID := ""
|
resp, errWithCode := m.processor.Fedi().OutboxGet(c.Request.Context(), requestedUsername, page)
|
||||||
minIDString := c.Query(MinIDKey)
|
|
||||||
if minIDString != "" {
|
|
||||||
minID = minIDString
|
|
||||||
}
|
|
||||||
|
|
||||||
maxID := ""
|
|
||||||
maxIDString := c.Query(MaxIDKey)
|
|
||||||
if maxIDString != "" {
|
|
||||||
maxID = maxIDString
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, errWithCode := m.processor.Fedi().OutboxGet(c.Request.Context(), requestedUsername, page, maxID, minID)
|
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||||
return
|
return
|
||||||
|
|
|
@ -80,8 +80,9 @@ func (suite *OutboxGetTestSuite) TestGetOutbox() {
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
suite.Equal(`{
|
suite.Equal(`{
|
||||||
"@context": "https://www.w3.org/ns/activitystreams",
|
"@context": "https://www.w3.org/ns/activitystreams",
|
||||||
"first": "http://localhost:8080/users/the_mighty_zork/outbox?page=true",
|
"first": "http://localhost:8080/users/the_mighty_zork/outbox?limit=40",
|
||||||
"id": "http://localhost:8080/users/the_mighty_zork/outbox",
|
"id": "http://localhost:8080/users/the_mighty_zork/outbox",
|
||||||
|
"totalItems": 7,
|
||||||
"type": "OrderedCollection"
|
"type": "OrderedCollection"
|
||||||
}`, dst.String())
|
}`, dst.String())
|
||||||
|
|
||||||
|
@ -105,7 +106,7 @@ func (suite *OutboxGetTestSuite) TestGetOutboxFirstPage() {
|
||||||
// setup request
|
// setup request
|
||||||
recorder := httptest.NewRecorder()
|
recorder := httptest.NewRecorder()
|
||||||
ctx, _ := testrig.CreateGinTestContext(recorder, nil)
|
ctx, _ := testrig.CreateGinTestContext(recorder, nil)
|
||||||
ctx.Request = httptest.NewRequest(http.MethodGet, targetAccount.OutboxURI+"?page=true", nil) // the endpoint we're hitting
|
ctx.Request = httptest.NewRequest(http.MethodGet, targetAccount.OutboxURI+"?limit=40", nil) // the endpoint we're hitting
|
||||||
ctx.Request.Header.Set("accept", "application/activity+json")
|
ctx.Request.Header.Set("accept", "application/activity+json")
|
||||||
ctx.Request.Header.Set("Signature", signedRequest.SignatureHeader)
|
ctx.Request.Header.Set("Signature", signedRequest.SignatureHeader)
|
||||||
ctx.Request.Header.Set("Date", signedRequest.DateHeader)
|
ctx.Request.Header.Set("Date", signedRequest.DateHeader)
|
||||||
|
@ -138,8 +139,8 @@ func (suite *OutboxGetTestSuite) TestGetOutboxFirstPage() {
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
suite.Equal(`{
|
suite.Equal(`{
|
||||||
"@context": "https://www.w3.org/ns/activitystreams",
|
"@context": "https://www.w3.org/ns/activitystreams",
|
||||||
"id": "http://localhost:8080/users/the_mighty_zork/outbox?page=true",
|
"id": "http://localhost:8080/users/the_mighty_zork/outbox?limit=40",
|
||||||
"next": "http://localhost:8080/users/the_mighty_zork/outbox?page=true\u0026max_id=01F8MHAMCHF6Y650WCRSCP4WMY",
|
"next": "http://localhost:8080/users/the_mighty_zork/outbox?limit=40\u0026max_id=01F8MHAMCHF6Y650WCRSCP4WMY",
|
||||||
"orderedItems": [
|
"orderedItems": [
|
||||||
{
|
{
|
||||||
"actor": "http://localhost:8080/users/the_mighty_zork",
|
"actor": "http://localhost:8080/users/the_mighty_zork",
|
||||||
|
@ -159,7 +160,8 @@ func (suite *OutboxGetTestSuite) TestGetOutboxFirstPage() {
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"partOf": "http://localhost:8080/users/the_mighty_zork/outbox",
|
"partOf": "http://localhost:8080/users/the_mighty_zork/outbox",
|
||||||
"prev": "http://localhost:8080/users/the_mighty_zork/outbox?page=true\u0026min_id=01HH9KYNQPA416TNJ53NSATP40",
|
"prev": "http://localhost:8080/users/the_mighty_zork/outbox?limit=40\u0026min_id=01HH9KYNQPA416TNJ53NSATP40",
|
||||||
|
"totalItems": 7,
|
||||||
"type": "OrderedCollectionPage"
|
"type": "OrderedCollectionPage"
|
||||||
}`, dst.String())
|
}`, dst.String())
|
||||||
|
|
||||||
|
@ -183,7 +185,7 @@ func (suite *OutboxGetTestSuite) TestGetOutboxNextPage() {
|
||||||
// setup request
|
// setup request
|
||||||
recorder := httptest.NewRecorder()
|
recorder := httptest.NewRecorder()
|
||||||
ctx, _ := testrig.CreateGinTestContext(recorder, nil)
|
ctx, _ := testrig.CreateGinTestContext(recorder, nil)
|
||||||
ctx.Request = httptest.NewRequest(http.MethodGet, targetAccount.OutboxURI+"?page=true&max_id=01F8MHAMCHF6Y650WCRSCP4WMY", nil) // the endpoint we're hitting
|
ctx.Request = httptest.NewRequest(http.MethodGet, targetAccount.OutboxURI+"?limit=40&max_id=01F8MHAMCHF6Y650WCRSCP4WMY", nil) // the endpoint we're hitting
|
||||||
ctx.Request.Header.Set("accept", "application/activity+json")
|
ctx.Request.Header.Set("accept", "application/activity+json")
|
||||||
ctx.Request.Header.Set("Signature", signedRequest.SignatureHeader)
|
ctx.Request.Header.Set("Signature", signedRequest.SignatureHeader)
|
||||||
ctx.Request.Header.Set("Date", signedRequest.DateHeader)
|
ctx.Request.Header.Set("Date", signedRequest.DateHeader)
|
||||||
|
@ -219,9 +221,10 @@ func (suite *OutboxGetTestSuite) TestGetOutboxNextPage() {
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
suite.Equal(`{
|
suite.Equal(`{
|
||||||
"@context": "https://www.w3.org/ns/activitystreams",
|
"@context": "https://www.w3.org/ns/activitystreams",
|
||||||
"id": "http://localhost:8080/users/the_mighty_zork/outbox?page=true&maxID=01F8MHAMCHF6Y650WCRSCP4WMY",
|
"id": "http://localhost:8080/users/the_mighty_zork/outbox?limit=40&max_id=01F8MHAMCHF6Y650WCRSCP4WMY",
|
||||||
"orderedItems": [],
|
"orderedItems": [],
|
||||||
"partOf": "http://localhost:8080/users/the_mighty_zork/outbox",
|
"partOf": "http://localhost:8080/users/the_mighty_zork/outbox",
|
||||||
|
"totalItems": 7,
|
||||||
"type": "OrderedCollectionPage"
|
"type": "OrderedCollectionPage"
|
||||||
}`, dst.String())
|
}`, dst.String())
|
||||||
|
|
||||||
|
|
|
@ -161,8 +161,8 @@ func (suite *RepliesGetTestSuite) TestGetRepliesNext() {
|
||||||
"type": "OrderedCollectionPage",
|
"type": "OrderedCollectionPage",
|
||||||
"id": targetStatus.URI + "/replies?limit=20&only_other_accounts=false",
|
"id": targetStatus.URI + "/replies?limit=20&only_other_accounts=false",
|
||||||
"partOf": targetStatus.URI + "/replies?only_other_accounts=false",
|
"partOf": targetStatus.URI + "/replies?only_other_accounts=false",
|
||||||
"next": "http://localhost:8080/users/the_mighty_zork/statuses/01F8MHAMCHF6Y650WCRSCP4WMY/replies?limit=20&min_id=01FF25D5Q0DH7CHD57CTRS6WK0&only_other_accounts=false",
|
"next": "http://localhost:8080/users/the_mighty_zork/statuses/01F8MHAMCHF6Y650WCRSCP4WMY/replies?limit=20&max_id=01FF25D5Q0DH7CHD57CTRS6WK0&only_other_accounts=false",
|
||||||
"prev": "http://localhost:8080/users/the_mighty_zork/statuses/01F8MHAMCHF6Y650WCRSCP4WMY/replies?limit=20&max_id=01FF25D5Q0DH7CHD57CTRS6WK0&only_other_accounts=false",
|
"prev": "http://localhost:8080/users/the_mighty_zork/statuses/01F8MHAMCHF6Y650WCRSCP4WMY/replies?limit=20&min_id=01FF25D5Q0DH7CHD57CTRS6WK0&only_other_accounts=false",
|
||||||
"orderedItems": []string{"http://localhost:8080/users/admin/statuses/01FF25D5Q0DH7CHD57CTRS6WK0"},
|
"orderedItems": []string{"http://localhost:8080/users/admin/statuses/01FF25D5Q0DH7CHD57CTRS6WK0"},
|
||||||
"totalItems": 1,
|
"totalItems": 1,
|
||||||
})
|
})
|
||||||
|
|
|
@ -97,7 +97,7 @@ func (suite *UserGetTestSuite) TestGetUserPublicKeyDeleted() {
|
||||||
userModule := users.New(suite.processor)
|
userModule := users.New(suite.processor)
|
||||||
targetAccount := suite.testAccounts["local_account_1"]
|
targetAccount := suite.testAccounts["local_account_1"]
|
||||||
|
|
||||||
suite.processor.Account().DeleteSelf(context.Background(), suite.testAccounts["local_account_1"])
|
suite.processor.User().DeleteSelf(context.Background(), suite.testAccounts["local_account_1"])
|
||||||
|
|
||||||
// wait for the account delete to be processed
|
// wait for the account delete to be processed
|
||||||
if !testrig.WaitFor(func() bool {
|
if !testrig.WaitFor(func() bool {
|
||||||
|
|
|
@ -49,7 +49,7 @@ func (m *Module) TokenPOSTHandler(c *gin.Context) {
|
||||||
|
|
||||||
form := &tokenRequestForm{}
|
form := &tokenRequestForm{}
|
||||||
if err := c.ShouldBind(form); err != nil {
|
if err := c.ShouldBind(form); err != nil {
|
||||||
apiutil.OAuthErrorHandler(c, gtserror.NewErrorBadRequest(oauth.InvalidRequest(), err.Error()))
|
apiutil.OAuthErrorHandler(c, gtserror.NewErrorBadRequest(oauth.ErrInvalidRequest, err.Error()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,7 +98,7 @@ func (m *Module) TokenPOSTHandler(c *gin.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(help) != 0 {
|
if len(help) != 0 {
|
||||||
apiutil.OAuthErrorHandler(c, gtserror.NewErrorBadRequest(oauth.InvalidRequest(), help...))
|
apiutil.OAuthErrorHandler(c, gtserror.NewErrorBadRequest(oauth.ErrInvalidRequest, help...))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,15 +26,18 @@ import (
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/apps"
|
"github.com/superseriousbusiness/gotosocial/internal/api/client/apps"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/blocks"
|
"github.com/superseriousbusiness/gotosocial/internal/api/client/blocks"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/bookmarks"
|
"github.com/superseriousbusiness/gotosocial/internal/api/client/bookmarks"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/api/client/conversations"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/customemojis"
|
"github.com/superseriousbusiness/gotosocial/internal/api/client/customemojis"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/favourites"
|
"github.com/superseriousbusiness/gotosocial/internal/api/client/favourites"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/featuredtags"
|
"github.com/superseriousbusiness/gotosocial/internal/api/client/featuredtags"
|
||||||
filter "github.com/superseriousbusiness/gotosocial/internal/api/client/filters"
|
filtersV1 "github.com/superseriousbusiness/gotosocial/internal/api/client/filters/v1"
|
||||||
|
filtersV2 "github.com/superseriousbusiness/gotosocial/internal/api/client/filters/v2"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/followrequests"
|
"github.com/superseriousbusiness/gotosocial/internal/api/client/followrequests"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/instance"
|
"github.com/superseriousbusiness/gotosocial/internal/api/client/instance"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/lists"
|
"github.com/superseriousbusiness/gotosocial/internal/api/client/lists"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/markers"
|
"github.com/superseriousbusiness/gotosocial/internal/api/client/markers"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/media"
|
"github.com/superseriousbusiness/gotosocial/internal/api/client/media"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/api/client/mutes"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/notifications"
|
"github.com/superseriousbusiness/gotosocial/internal/api/client/notifications"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/polls"
|
"github.com/superseriousbusiness/gotosocial/internal/api/client/polls"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/preferences"
|
"github.com/superseriousbusiness/gotosocial/internal/api/client/preferences"
|
||||||
|
@ -48,6 +51,7 @@ import (
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/middleware"
|
"github.com/superseriousbusiness/gotosocial/internal/middleware"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/processing"
|
"github.com/superseriousbusiness/gotosocial/internal/processing"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/router"
|
"github.com/superseriousbusiness/gotosocial/internal/router"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Client struct {
|
type Client struct {
|
||||||
|
@ -59,15 +63,18 @@ type Client struct {
|
||||||
apps *apps.Module // api/v1/apps
|
apps *apps.Module // api/v1/apps
|
||||||
blocks *blocks.Module // api/v1/blocks
|
blocks *blocks.Module // api/v1/blocks
|
||||||
bookmarks *bookmarks.Module // api/v1/bookmarks
|
bookmarks *bookmarks.Module // api/v1/bookmarks
|
||||||
|
conversations *conversations.Module // api/v1/conversations
|
||||||
customEmojis *customemojis.Module // api/v1/custom_emojis
|
customEmojis *customemojis.Module // api/v1/custom_emojis
|
||||||
favourites *favourites.Module // api/v1/favourites
|
favourites *favourites.Module // api/v1/favourites
|
||||||
featuredTags *featuredtags.Module // api/v1/featured_tags
|
featuredTags *featuredtags.Module // api/v1/featured_tags
|
||||||
filters *filter.Module // api/v1/filters
|
filtersV1 *filtersV1.Module // api/v1/filters
|
||||||
|
filtersV2 *filtersV2.Module // api/v2/filters
|
||||||
followRequests *followrequests.Module // api/v1/follow_requests
|
followRequests *followrequests.Module // api/v1/follow_requests
|
||||||
instance *instance.Module // api/v1/instance
|
instance *instance.Module // api/v1/instance
|
||||||
lists *lists.Module // api/v1/lists
|
lists *lists.Module // api/v1/lists
|
||||||
markers *markers.Module // api/v1/markers
|
markers *markers.Module // api/v1/markers
|
||||||
media *media.Module // api/v1/media, api/v2/media
|
media *media.Module // api/v1/media, api/v2/media
|
||||||
|
mutes *mutes.Module // api/v1/mutes
|
||||||
notifications *notifications.Module // api/v1/notifications
|
notifications *notifications.Module // api/v1/notifications
|
||||||
polls *polls.Module // api/v1/polls
|
polls *polls.Module // api/v1/polls
|
||||||
preferences *preferences.Module // api/v1/preferences
|
preferences *preferences.Module // api/v1/preferences
|
||||||
|
@ -101,15 +108,18 @@ func (c *Client) Route(r *router.Router, m ...gin.HandlerFunc) {
|
||||||
c.apps.Route(h)
|
c.apps.Route(h)
|
||||||
c.blocks.Route(h)
|
c.blocks.Route(h)
|
||||||
c.bookmarks.Route(h)
|
c.bookmarks.Route(h)
|
||||||
|
c.conversations.Route(h)
|
||||||
c.customEmojis.Route(h)
|
c.customEmojis.Route(h)
|
||||||
c.favourites.Route(h)
|
c.favourites.Route(h)
|
||||||
c.featuredTags.Route(h)
|
c.featuredTags.Route(h)
|
||||||
c.filters.Route(h)
|
c.filtersV1.Route(h)
|
||||||
|
c.filtersV2.Route(h)
|
||||||
c.followRequests.Route(h)
|
c.followRequests.Route(h)
|
||||||
c.instance.Route(h)
|
c.instance.Route(h)
|
||||||
c.lists.Route(h)
|
c.lists.Route(h)
|
||||||
c.markers.Route(h)
|
c.markers.Route(h)
|
||||||
c.media.Route(h)
|
c.media.Route(h)
|
||||||
|
c.mutes.Route(h)
|
||||||
c.notifications.Route(h)
|
c.notifications.Route(h)
|
||||||
c.polls.Route(h)
|
c.polls.Route(h)
|
||||||
c.preferences.Route(h)
|
c.preferences.Route(h)
|
||||||
|
@ -121,25 +131,28 @@ func (c *Client) Route(r *router.Router, m ...gin.HandlerFunc) {
|
||||||
c.user.Route(h)
|
c.user.Route(h)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewClient(db db.DB, p *processing.Processor) *Client {
|
func NewClient(state *state.State, p *processing.Processor) *Client {
|
||||||
return &Client{
|
return &Client{
|
||||||
processor: p,
|
processor: p,
|
||||||
db: db,
|
db: state.DB,
|
||||||
|
|
||||||
accounts: accounts.New(p),
|
accounts: accounts.New(p),
|
||||||
admin: admin.New(p),
|
admin: admin.New(state, p),
|
||||||
apps: apps.New(p),
|
apps: apps.New(p),
|
||||||
blocks: blocks.New(p),
|
blocks: blocks.New(p),
|
||||||
bookmarks: bookmarks.New(p),
|
bookmarks: bookmarks.New(p),
|
||||||
|
conversations: conversations.New(p),
|
||||||
customEmojis: customemojis.New(p),
|
customEmojis: customemojis.New(p),
|
||||||
favourites: favourites.New(p),
|
favourites: favourites.New(p),
|
||||||
featuredTags: featuredtags.New(p),
|
featuredTags: featuredtags.New(p),
|
||||||
filters: filter.New(p),
|
filtersV1: filtersV1.New(p),
|
||||||
|
filtersV2: filtersV2.New(p),
|
||||||
followRequests: followrequests.New(p),
|
followRequests: followrequests.New(p),
|
||||||
instance: instance.New(p),
|
instance: instance.New(p),
|
||||||
lists: lists.New(p),
|
lists: lists.New(p),
|
||||||
markers: markers.New(p),
|
markers: markers.New(p),
|
||||||
media: media.New(p),
|
media: media.New(p),
|
||||||
|
mutes: mutes.New(p),
|
||||||
notifications: notifications.New(p),
|
notifications: notifications.New(p),
|
||||||
polls: polls.New(p),
|
polls: polls.New(p),
|
||||||
preferences: preferences.New(p),
|
preferences: preferences.New(p),
|
||||||
|
|
|
@ -25,7 +25,6 @@ import (
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/validate"
|
"github.com/superseriousbusiness/gotosocial/internal/validate"
|
||||||
|
@ -67,6 +66,11 @@ import (
|
||||||
// description: not found
|
// description: not found
|
||||||
// '406':
|
// '406':
|
||||||
// description: not acceptable
|
// description: not acceptable
|
||||||
|
// '422':
|
||||||
|
// description: >-
|
||||||
|
// Unprocessable. Your account creation request cannot be processed
|
||||||
|
// because either too many accounts have been created on this instance
|
||||||
|
// in the last 24h, or the pending account backlog is full.
|
||||||
// '500':
|
// '500':
|
||||||
// description: internal server error
|
// description: internal server error
|
||||||
func (m *Module) AccountCreatePOSTHandler(c *gin.Context) {
|
func (m *Module) AccountCreatePOSTHandler(c *gin.Context) {
|
||||||
|
@ -87,7 +91,7 @@ func (m *Module) AccountCreatePOSTHandler(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := validateNormalizeCreateAccount(form); err != nil {
|
if err := validate.CreateAccount(form); err != nil {
|
||||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGetV1)
|
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGetV1)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -101,7 +105,25 @@ func (m *Module) AccountCreatePOSTHandler(c *gin.Context) {
|
||||||
}
|
}
|
||||||
form.IP = signUpIP
|
form.IP = signUpIP
|
||||||
|
|
||||||
ti, errWithCode := m.processor.Account().Create(c.Request.Context(), authed.Token, authed.Application, form)
|
// Create the new user+account.
|
||||||
|
ctx := c.Request.Context()
|
||||||
|
user, errWithCode := m.processor.User().Create(
|
||||||
|
ctx,
|
||||||
|
authed.Application,
|
||||||
|
form,
|
||||||
|
)
|
||||||
|
if errWithCode != nil {
|
||||||
|
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a token for the new user.
|
||||||
|
ti, errWithCode := m.processor.User().TokenForNewUser(
|
||||||
|
ctx,
|
||||||
|
authed.Token,
|
||||||
|
authed.Application,
|
||||||
|
user,
|
||||||
|
)
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||||
return
|
return
|
||||||
|
@ -109,40 +131,3 @@ func (m *Module) AccountCreatePOSTHandler(c *gin.Context) {
|
||||||
|
|
||||||
apiutil.JSON(c, http.StatusOK, ti)
|
apiutil.JSON(c, http.StatusOK, ti)
|
||||||
}
|
}
|
||||||
|
|
||||||
// validateNormalizeCreateAccount checks through all the necessary prerequisites for creating a new account,
|
|
||||||
// according to the provided account create request. If the account isn't eligible, an error will be returned.
|
|
||||||
// Side effect: normalizes the provided language tag for the user's locale.
|
|
||||||
func validateNormalizeCreateAccount(form *apimodel.AccountCreateRequest) error {
|
|
||||||
if form == nil {
|
|
||||||
return errors.New("form was nil")
|
|
||||||
}
|
|
||||||
|
|
||||||
if !config.GetAccountsRegistrationOpen() {
|
|
||||||
return errors.New("registration is not open for this server")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := validate.Username(form.Username); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := validate.Email(form.Email); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := validate.Password(form.Password); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if !form.Agreement {
|
|
||||||
return errors.New("agreement to terms and conditions not given")
|
|
||||||
}
|
|
||||||
|
|
||||||
locale, err := validate.Language(form.Locale)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
form.Locale = locale
|
|
||||||
|
|
||||||
return validate.SignUpReason(form.Reason, config.GetAccountsReasonRequired())
|
|
||||||
}
|
|
||||||
|
|
|
@ -91,7 +91,7 @@ func (m *Module) AccountDeletePOSTHandler(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if errWithCode := m.processor.Account().DeleteSelf(c.Request.Context(), authed.Account); errWithCode != nil {
|
if errWithCode := m.processor.User().DeleteSelf(c.Request.Context(), authed.Account); errWithCode != nil {
|
||||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,8 +31,6 @@ import (
|
||||||
//
|
//
|
||||||
// Move your account to another account.
|
// Move your account to another account.
|
||||||
//
|
//
|
||||||
// NOT IMPLEMENTED YET!
|
|
||||||
//
|
|
||||||
// ---
|
// ---
|
||||||
// tags:
|
// tags:
|
||||||
// - accounts
|
// - accounts
|
||||||
|
|
|
@ -45,16 +45,24 @@ const (
|
||||||
FollowPath = BasePathWithID + "/follow"
|
FollowPath = BasePathWithID + "/follow"
|
||||||
ListsPath = BasePathWithID + "/lists"
|
ListsPath = BasePathWithID + "/lists"
|
||||||
LookupPath = BasePath + "/lookup"
|
LookupPath = BasePath + "/lookup"
|
||||||
|
MutePath = BasePathWithID + "/mute"
|
||||||
NotePath = BasePathWithID + "/note"
|
NotePath = BasePathWithID + "/note"
|
||||||
RelationshipsPath = BasePath + "/relationships"
|
RelationshipsPath = BasePath + "/relationships"
|
||||||
SearchPath = BasePath + "/search"
|
SearchPath = BasePath + "/search"
|
||||||
StatusesPath = BasePathWithID + "/statuses"
|
StatusesPath = BasePathWithID + "/statuses"
|
||||||
UnblockPath = BasePathWithID + "/unblock"
|
UnblockPath = BasePathWithID + "/unblock"
|
||||||
UnfollowPath = BasePathWithID + "/unfollow"
|
UnfollowPath = BasePathWithID + "/unfollow"
|
||||||
|
UnmutePath = BasePathWithID + "/unmute"
|
||||||
UpdatePath = BasePath + "/update_credentials"
|
UpdatePath = BasePath + "/update_credentials"
|
||||||
VerifyPath = BasePath + "/verify_credentials"
|
VerifyPath = BasePath + "/verify_credentials"
|
||||||
MovePath = BasePath + "/move"
|
MovePath = BasePath + "/move"
|
||||||
AliasPath = BasePath + "/alias"
|
AliasPath = BasePath + "/alias"
|
||||||
|
ThemesPath = BasePath + "/themes"
|
||||||
|
|
||||||
|
// ProfileBasePath for the profile API, an extension of the account update API with a different path.
|
||||||
|
ProfileBasePath = "/v1/profile"
|
||||||
|
AvatarPath = ProfileBasePath + "/avatar"
|
||||||
|
HeaderPath = ProfileBasePath + "/header"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Module struct {
|
type Module struct {
|
||||||
|
@ -83,6 +91,10 @@ func (m *Module) Route(attachHandler func(method string, path string, f ...gin.H
|
||||||
// modify account
|
// modify account
|
||||||
attachHandler(http.MethodPatch, UpdatePath, m.AccountUpdateCredentialsPATCHHandler)
|
attachHandler(http.MethodPatch, UpdatePath, m.AccountUpdateCredentialsPATCHHandler)
|
||||||
|
|
||||||
|
// modify account profile media
|
||||||
|
attachHandler(http.MethodDelete, AvatarPath, m.AccountAvatarDELETEHandler)
|
||||||
|
attachHandler(http.MethodDelete, HeaderPath, m.AccountHeaderDELETEHandler)
|
||||||
|
|
||||||
// get account's statuses
|
// get account's statuses
|
||||||
attachHandler(http.MethodGet, StatusesPath, m.AccountStatusesGETHandler)
|
attachHandler(http.MethodGet, StatusesPath, m.AccountStatusesGETHandler)
|
||||||
|
|
||||||
|
@ -107,11 +119,18 @@ func (m *Module) Route(attachHandler func(method string, path string, f ...gin.H
|
||||||
// account note
|
// account note
|
||||||
attachHandler(http.MethodPost, NotePath, m.AccountNotePOSTHandler)
|
attachHandler(http.MethodPost, NotePath, m.AccountNotePOSTHandler)
|
||||||
|
|
||||||
|
// mute or unmute account
|
||||||
|
attachHandler(http.MethodPost, MutePath, m.AccountMutePOSTHandler)
|
||||||
|
attachHandler(http.MethodPost, UnmutePath, m.AccountUnmutePOSTHandler)
|
||||||
|
|
||||||
// search for accounts
|
// search for accounts
|
||||||
attachHandler(http.MethodGet, SearchPath, m.AccountSearchGETHandler)
|
attachHandler(http.MethodGet, SearchPath, m.AccountSearchGETHandler)
|
||||||
attachHandler(http.MethodGet, LookupPath, m.AccountLookupGETHandler)
|
attachHandler(http.MethodGet, LookupPath, m.AccountLookupGETHandler)
|
||||||
|
|
||||||
// migration handlers
|
// migration handlers
|
||||||
attachHandler(http.MethodPost, AliasPath, m.AccountAliasPOSTHandler)
|
attachHandler(http.MethodPost, AliasPath, m.AccountAliasPOSTHandler)
|
||||||
// attachHandler(http.MethodPost, MovePath, m.AccountMovePOSTHandler) // todo: enable this only when Move is completed
|
attachHandler(http.MethodPost, MovePath, m.AccountMovePOSTHandler)
|
||||||
|
|
||||||
|
// account themes
|
||||||
|
attachHandler(http.MethodGet, ThemesPath, m.AccountThemesGETHandler)
|
||||||
}
|
}
|
||||||
|
|
|
@ -108,6 +108,14 @@ import (
|
||||||
// description: Default content type to use for authored statuses (text/plain or text/markdown).
|
// description: Default content type to use for authored statuses (text/plain or text/markdown).
|
||||||
// type: string
|
// type: string
|
||||||
// -
|
// -
|
||||||
|
// name: theme
|
||||||
|
// in: formData
|
||||||
|
// description: >-
|
||||||
|
// FileName of the theme to use when rendering this account's profile or statuses.
|
||||||
|
// The theme must exist on this server, as indicated by /api/v1/accounts/themes.
|
||||||
|
// Empty string unsets theme and returns to the default GoToSocial theme.
|
||||||
|
// type: string
|
||||||
|
// -
|
||||||
// name: custom_css
|
// name: custom_css
|
||||||
// in: formData
|
// in: formData
|
||||||
// description: >-
|
// description: >-
|
||||||
|
@ -120,12 +128,72 @@ import (
|
||||||
// description: Enable RSS feed for this account's Public posts at `/[username]/feed.rss`
|
// description: Enable RSS feed for this account's Public posts at `/[username]/feed.rss`
|
||||||
// type: boolean
|
// type: boolean
|
||||||
// -
|
// -
|
||||||
// name: fields_attributes
|
// name: hide_collections
|
||||||
// in: formData
|
// in: formData
|
||||||
// description: Profile fields to be added to this account's profile
|
// description: Hide the account's following/followers collections.
|
||||||
// type: array
|
// type: boolean
|
||||||
// items:
|
// -
|
||||||
// type: object
|
// name: fields_attributes[0][name]
|
||||||
|
// in: formData
|
||||||
|
// description: Name of 1st profile field to be added to this account's profile.
|
||||||
|
// (The index may be any string; add more indexes to send more fields.)
|
||||||
|
// type: string
|
||||||
|
// -
|
||||||
|
// name: fields_attributes[0][value]
|
||||||
|
// in: formData
|
||||||
|
// description: Value of 1st profile field to be added to this account's profile.
|
||||||
|
// (The index may be any string; add more indexes to send more fields.)
|
||||||
|
// type: string
|
||||||
|
// -
|
||||||
|
// name: fields_attributes[1][name]
|
||||||
|
// in: formData
|
||||||
|
// description: Name of 2nd profile field to be added to this account's profile.
|
||||||
|
// type: string
|
||||||
|
// -
|
||||||
|
// name: fields_attributes[1][value]
|
||||||
|
// in: formData
|
||||||
|
// description: Value of 2nd profile field to be added to this account's profile.
|
||||||
|
// type: string
|
||||||
|
// -
|
||||||
|
// name: fields_attributes[2][name]
|
||||||
|
// in: formData
|
||||||
|
// description: Name of 3rd profile field to be added to this account's profile.
|
||||||
|
// type: string
|
||||||
|
// -
|
||||||
|
// name: fields_attributes[2][value]
|
||||||
|
// in: formData
|
||||||
|
// description: Value of 3rd profile field to be added to this account's profile.
|
||||||
|
// type: string
|
||||||
|
// -
|
||||||
|
// name: fields_attributes[3][name]
|
||||||
|
// in: formData
|
||||||
|
// description: Name of 4th profile field to be added to this account's profile.
|
||||||
|
// type: string
|
||||||
|
// -
|
||||||
|
// name: fields_attributes[3][value]
|
||||||
|
// in: formData
|
||||||
|
// description: Value of 4th profile field to be added to this account's profile.
|
||||||
|
// type: string
|
||||||
|
// -
|
||||||
|
// name: fields_attributes[4][name]
|
||||||
|
// in: formData
|
||||||
|
// description: Name of 5th profile field to be added to this account's profile.
|
||||||
|
// type: string
|
||||||
|
// -
|
||||||
|
// name: fields_attributes[4][value]
|
||||||
|
// in: formData
|
||||||
|
// description: Value of 5th profile field to be added to this account's profile.
|
||||||
|
// type: string
|
||||||
|
// -
|
||||||
|
// name: fields_attributes[5][name]
|
||||||
|
// in: formData
|
||||||
|
// description: Name of 6th profile field to be added to this account's profile.
|
||||||
|
// type: string
|
||||||
|
// -
|
||||||
|
// name: fields_attributes[5][value]
|
||||||
|
// in: formData
|
||||||
|
// description: Value of 6th profile field to be added to this account's profile.
|
||||||
|
// type: string
|
||||||
//
|
//
|
||||||
// security:
|
// security:
|
||||||
// - OAuth2 Bearer:
|
// - OAuth2 Bearer:
|
||||||
|
@ -254,8 +322,10 @@ func parseUpdateAccountForm(c *gin.Context) (*apimodel.UpdateCredentialsRequest,
|
||||||
form.Source.Language == nil &&
|
form.Source.Language == nil &&
|
||||||
form.Source.StatusContentType == nil &&
|
form.Source.StatusContentType == nil &&
|
||||||
form.FieldsAttributes == nil &&
|
form.FieldsAttributes == nil &&
|
||||||
|
form.Theme == nil &&
|
||||||
form.CustomCSS == nil &&
|
form.CustomCSS == nil &&
|
||||||
form.EnableRSS == nil) {
|
form.EnableRSS == nil &&
|
||||||
|
form.HideCollections == nil) {
|
||||||
return nil, errors.New("empty form submitted")
|
return nil, errors.New("empty form submitted")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -481,7 +481,7 @@ func (suite *AccountUpdateTestSuite) TestUpdateAccountSourceBadContentTypeFormDa
|
||||||
if err != nil {
|
if err != nil {
|
||||||
suite.FailNow(err.Error())
|
suite.FailNow(err.Error())
|
||||||
}
|
}
|
||||||
suite.Equal(data["source[status_content_type]"][0], dbAccount.StatusContentType)
|
suite.Equal(data["source[status_content_type]"][0], dbAccount.Settings.StatusContentType)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *AccountUpdateTestSuite) TestAccountUpdateCredentialsPATCHHandlerUpdateStatusContentTypeBad() {
|
func (suite *AccountUpdateTestSuite) TestAccountUpdateCredentialsPATCHHandlerUpdateStatusContentTypeBad() {
|
||||||
|
|
|
@ -81,7 +81,7 @@ func (suite *AccountVerifyTestSuite) TestAccountVerifyGet() {
|
||||||
suite.Equal(2, apimodelAccount.FollowingCount)
|
suite.Equal(2, apimodelAccount.FollowingCount)
|
||||||
suite.Equal(7, apimodelAccount.StatusesCount)
|
suite.Equal(7, apimodelAccount.StatusesCount)
|
||||||
suite.EqualValues(gtsmodel.VisibilityPublic, apimodelAccount.Source.Privacy)
|
suite.EqualValues(gtsmodel.VisibilityPublic, apimodelAccount.Source.Privacy)
|
||||||
suite.Equal(testAccount.Language, apimodelAccount.Source.Language)
|
suite.Equal(testAccount.Settings.Language, apimodelAccount.Source.Language)
|
||||||
suite.Equal(testAccount.NoteRaw, apimodelAccount.Source.Note)
|
suite.Equal(testAccount.NoteRaw, apimodelAccount.Source.Note)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -97,6 +97,11 @@ func (m *Module) AccountFollowPOSTHandler(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if authed.Account.IsMoving() {
|
||||||
|
apiutil.ForbiddenAfterMove(c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
|
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
|
||||||
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGetV1)
|
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGetV1)
|
||||||
return
|
return
|
||||||
|
|
|
@ -21,8 +21,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io"
|
||||||
"math/rand"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
@ -42,9 +41,6 @@ import (
|
||||||
"github.com/tomnomnom/linkheader"
|
"github.com/tomnomnom/linkheader"
|
||||||
)
|
)
|
||||||
|
|
||||||
// random reader according to current-time source seed.
|
|
||||||
var randRd = rand.New(rand.NewSource(time.Now().Unix()))
|
|
||||||
|
|
||||||
type FollowTestSuite struct {
|
type FollowTestSuite struct {
|
||||||
AccountStandardTestSuite
|
AccountStandardTestSuite
|
||||||
}
|
}
|
||||||
|
@ -76,33 +72,33 @@ func (suite *FollowTestSuite) TestFollowSelf() {
|
||||||
defer result.Body.Close()
|
defer result.Body.Close()
|
||||||
|
|
||||||
// check the response
|
// check the response
|
||||||
b, err := ioutil.ReadAll(result.Body)
|
b, err := io.ReadAll(result.Body)
|
||||||
_ = b
|
_ = b
|
||||||
assert.NoError(suite.T(), err)
|
assert.NoError(suite.T(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *FollowTestSuite) TestGetFollowersPageBackwardLimit2() {
|
func (suite *FollowTestSuite) TestGetFollowersPageNewestToOldestLimit2() {
|
||||||
suite.testGetFollowersPage(2, "backward")
|
suite.testGetFollowersPage(2, "newestToOldest")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *FollowTestSuite) TestGetFollowersPageBackwardLimit4() {
|
func (suite *FollowTestSuite) TestGetFollowersPageNewestToOldestLimit4() {
|
||||||
suite.testGetFollowersPage(4, "backward")
|
suite.testGetFollowersPage(4, "newestToOldest")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *FollowTestSuite) TestGetFollowersPageBackwardLimit6() {
|
func (suite *FollowTestSuite) TestGetFollowersPageNewestToOldestLimit6() {
|
||||||
suite.testGetFollowersPage(6, "backward")
|
suite.testGetFollowersPage(6, "newestToOldest")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *FollowTestSuite) TestGetFollowersPageForwardLimit2() {
|
func (suite *FollowTestSuite) TestGetFollowersPageOldestToNewestLimit2() {
|
||||||
suite.testGetFollowersPage(2, "forward")
|
suite.testGetFollowersPage(2, "oldestToNewest")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *FollowTestSuite) TestGetFollowersPageForwardLimit4() {
|
func (suite *FollowTestSuite) TestGetFollowersPageOldestToNewestLimit4() {
|
||||||
suite.testGetFollowersPage(4, "forward")
|
suite.testGetFollowersPage(4, "oldestToNewest")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *FollowTestSuite) TestGetFollowersPageForwardLimit6() {
|
func (suite *FollowTestSuite) TestGetFollowersPageOldestToNewestLimit6() {
|
||||||
suite.testGetFollowersPage(6, "forward")
|
suite.testGetFollowersPage(6, "oldestToNewest")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *FollowTestSuite) testGetFollowersPage(limit int, direction string) {
|
func (suite *FollowTestSuite) testGetFollowersPage(limit int, direction string) {
|
||||||
|
@ -117,8 +113,11 @@ func (suite *FollowTestSuite) testGetFollowersPage(limit int, direction string)
|
||||||
|
|
||||||
var i int
|
var i int
|
||||||
|
|
||||||
for _, targetAccount := range suite.testAccounts {
|
// Have each account in the testrig follow the account
|
||||||
if targetAccount.ID == requestingAccount.ID {
|
// that is requesting their followers from the API.
|
||||||
|
for _, account := range suite.testAccounts {
|
||||||
|
targetAccount := requestingAccount
|
||||||
|
if account.ID == targetAccount.ID {
|
||||||
// we cannot be our own target...
|
// we cannot be our own target...
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -132,9 +131,9 @@ func (suite *FollowTestSuite) testGetFollowersPage(limit int, direction string)
|
||||||
ID: id,
|
ID: id,
|
||||||
CreatedAt: now,
|
CreatedAt: now,
|
||||||
UpdatedAt: now,
|
UpdatedAt: now,
|
||||||
URI: fmt.Sprintf("%s/follow/%s", targetAccount.URI, id),
|
URI: fmt.Sprintf("%s/follow/%s", account.URI, id),
|
||||||
AccountID: targetAccount.ID,
|
AccountID: account.ID,
|
||||||
TargetAccountID: requestingAccount.ID,
|
TargetAccountID: targetAccount.ID,
|
||||||
})
|
})
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
|
|
||||||
|
@ -152,15 +151,17 @@ func (suite *FollowTestSuite) testGetFollowersPage(limit int, direction string)
|
||||||
var query string
|
var query string
|
||||||
|
|
||||||
switch direction {
|
switch direction {
|
||||||
case "backward":
|
case "newestToOldest":
|
||||||
// Set the starting query to page backward from newest.
|
// Set the starting query to page from
|
||||||
|
// newest (ie., first entry in slice).
|
||||||
acc := expectAccounts[0].(*model.Account)
|
acc := expectAccounts[0].(*model.Account)
|
||||||
newest, _ := suite.db.GetFollow(ctx, acc.ID, requestingAccount.ID)
|
newest, _ := suite.db.GetFollow(ctx, acc.ID, requestingAccount.ID)
|
||||||
expectAccounts = expectAccounts[1:]
|
expectAccounts = expectAccounts[1:]
|
||||||
query = fmt.Sprintf("limit=%d&max_id=%s", limit, newest.ID)
|
query = fmt.Sprintf("limit=%d&max_id=%s", limit, newest.ID)
|
||||||
|
|
||||||
case "forward":
|
case "oldestToNewest":
|
||||||
// Set the starting query to page forward from the oldest.
|
// Set the starting query to page from
|
||||||
|
// oldest (ie., last entry in slice).
|
||||||
acc := expectAccounts[len(expectAccounts)-1].(*model.Account)
|
acc := expectAccounts[len(expectAccounts)-1].(*model.Account)
|
||||||
oldest, _ := suite.db.GetFollow(ctx, acc.ID, requestingAccount.ID)
|
oldest, _ := suite.db.GetFollow(ctx, acc.ID, requestingAccount.ID)
|
||||||
expectAccounts = expectAccounts[:len(expectAccounts)-1]
|
expectAccounts = expectAccounts[:len(expectAccounts)-1]
|
||||||
|
@ -208,9 +209,9 @@ func (suite *FollowTestSuite) testGetFollowersPage(limit int, direction string)
|
||||||
)
|
)
|
||||||
|
|
||||||
switch direction {
|
switch direction {
|
||||||
case "backward":
|
case "newestToOldest":
|
||||||
// When paging backwards (DESC) we:
|
// When paging newest to oldest (ie., first page to last page):
|
||||||
// - iter from end of received accounts
|
// - iter from start of received accounts
|
||||||
// - iterate backward through received accounts
|
// - iterate backward through received accounts
|
||||||
// - stop when we reach last index of received accounts
|
// - stop when we reach last index of received accounts
|
||||||
// - compare each received with the first index of expected accounts
|
// - compare each received with the first index of expected accounts
|
||||||
|
@ -221,8 +222,8 @@ func (suite *FollowTestSuite) testGetFollowersPage(limit int, direction string)
|
||||||
expect = func(i []interface{}) interface{} { return i[0] }
|
expect = func(i []interface{}) interface{} { return i[0] }
|
||||||
trunc = func(i []interface{}) []interface{} { return i[1:] }
|
trunc = func(i []interface{}) []interface{} { return i[1:] }
|
||||||
|
|
||||||
case "forward":
|
case "oldestToNewest":
|
||||||
// When paging forwards (ASC) we:
|
// When paging oldest to newest (ie., last page to first page):
|
||||||
// - iter from end of received accounts
|
// - iter from end of received accounts
|
||||||
// - iterate backward through received accounts
|
// - iterate backward through received accounts
|
||||||
// - stop when we reach first index of received accounts
|
// - stop when we reach first index of received accounts
|
||||||
|
@ -230,7 +231,7 @@ func (suite *FollowTestSuite) testGetFollowersPage(limit int, direction string)
|
||||||
// - after each compare, drop the last index of expected accounts
|
// - after each compare, drop the last index of expected accounts
|
||||||
start = func(i []*model.Account) int { return len(i) - 1 }
|
start = func(i []*model.Account) int { return len(i) - 1 }
|
||||||
iter = func(i int) int { return i - 1 }
|
iter = func(i int) int { return i - 1 }
|
||||||
check = func(idx int, i []*model.Account) bool { return idx >= 0 }
|
check = func(idx int, _ []*model.Account) bool { return idx >= 0 }
|
||||||
expect = func(i []interface{}) interface{} { return i[len(i)-1] }
|
expect = func(i []interface{}) interface{} { return i[len(i)-1] }
|
||||||
trunc = func(i []interface{}) []interface{} { return i[:len(i)-1] }
|
trunc = func(i []interface{}) []interface{} { return i[:len(i)-1] }
|
||||||
}
|
}
|
||||||
|
@ -256,7 +257,14 @@ func (suite *FollowTestSuite) testGetFollowersPage(limit int, direction string)
|
||||||
// Parse response link header values.
|
// Parse response link header values.
|
||||||
values := result.Header.Values("Link")
|
values := result.Header.Values("Link")
|
||||||
links := linkheader.ParseMultiple(values)
|
links := linkheader.ParseMultiple(values)
|
||||||
filteredLinks := links.FilterByRel("next")
|
|
||||||
|
var filteredLinks linkheader.Links
|
||||||
|
if direction == "newestToOldest" {
|
||||||
|
filteredLinks = links.FilterByRel("next")
|
||||||
|
} else {
|
||||||
|
filteredLinks = links.FilterByRel("prev")
|
||||||
|
}
|
||||||
|
|
||||||
suite.NotEmpty(filteredLinks, "no next link provided with more remaining accounts on page=%d", p)
|
suite.NotEmpty(filteredLinks, "no next link provided with more remaining accounts on page=%d", p)
|
||||||
|
|
||||||
// A ref link header was set.
|
// A ref link header was set.
|
||||||
|
@ -271,28 +279,28 @@ func (suite *FollowTestSuite) testGetFollowersPage(limit int, direction string)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *FollowTestSuite) TestGetFollowingPageBackwardLimit2() {
|
func (suite *FollowTestSuite) TestGetFollowingPageNewestToOldestLimit2() {
|
||||||
suite.testGetFollowingPage(2, "backward")
|
suite.testGetFollowingPage(2, "newestToOldest")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *FollowTestSuite) TestGetFollowingPageBackwardLimit4() {
|
func (suite *FollowTestSuite) TestGetFollowingPageNewestToOldestLimit4() {
|
||||||
suite.testGetFollowingPage(4, "backward")
|
suite.testGetFollowingPage(4, "newestToOldest")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *FollowTestSuite) TestGetFollowingPageBackwardLimit6() {
|
func (suite *FollowTestSuite) TestGetFollowingPageNewestToOldestLimit6() {
|
||||||
suite.testGetFollowingPage(6, "backward")
|
suite.testGetFollowingPage(6, "newestToOldest")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *FollowTestSuite) TestGetFollowingPageForwardLimit2() {
|
func (suite *FollowTestSuite) TestGetFollowingPageOldestToNewestLimit2() {
|
||||||
suite.testGetFollowingPage(2, "forward")
|
suite.testGetFollowingPage(2, "oldestToNewest")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *FollowTestSuite) TestGetFollowingPageForwardLimit4() {
|
func (suite *FollowTestSuite) TestGetFollowingPageOldestToNewestLimit4() {
|
||||||
suite.testGetFollowingPage(4, "forward")
|
suite.testGetFollowingPage(4, "oldestToNewest")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *FollowTestSuite) TestGetFollowingPageForwardLimit6() {
|
func (suite *FollowTestSuite) TestGetFollowingPageOldestToNewestLimit6() {
|
||||||
suite.testGetFollowingPage(6, "forward")
|
suite.testGetFollowingPage(6, "oldestToNewest")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *FollowTestSuite) testGetFollowingPage(limit int, direction string) {
|
func (suite *FollowTestSuite) testGetFollowingPage(limit int, direction string) {
|
||||||
|
@ -307,8 +315,11 @@ func (suite *FollowTestSuite) testGetFollowingPage(limit int, direction string)
|
||||||
|
|
||||||
var i int
|
var i int
|
||||||
|
|
||||||
|
// Have the account that is requesting their following
|
||||||
|
// list from the API follow each account in the testrig.
|
||||||
for _, targetAccount := range suite.testAccounts {
|
for _, targetAccount := range suite.testAccounts {
|
||||||
if targetAccount.ID == requestingAccount.ID {
|
account := requestingAccount
|
||||||
|
if targetAccount.ID == account.ID {
|
||||||
// we cannot be our own target...
|
// we cannot be our own target...
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -322,8 +333,8 @@ func (suite *FollowTestSuite) testGetFollowingPage(limit int, direction string)
|
||||||
ID: id,
|
ID: id,
|
||||||
CreatedAt: now,
|
CreatedAt: now,
|
||||||
UpdatedAt: now,
|
UpdatedAt: now,
|
||||||
URI: fmt.Sprintf("%s/follow/%s", requestingAccount.URI, id),
|
URI: fmt.Sprintf("%s/follow/%s", account.URI, id),
|
||||||
AccountID: requestingAccount.ID,
|
AccountID: account.ID,
|
||||||
TargetAccountID: targetAccount.ID,
|
TargetAccountID: targetAccount.ID,
|
||||||
})
|
})
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
|
@ -342,15 +353,17 @@ func (suite *FollowTestSuite) testGetFollowingPage(limit int, direction string)
|
||||||
var query string
|
var query string
|
||||||
|
|
||||||
switch direction {
|
switch direction {
|
||||||
case "backward":
|
case "newestToOldest":
|
||||||
// Set the starting query to page backward from newest.
|
// Set the starting query to page from
|
||||||
|
// newest (ie., first entry in slice).
|
||||||
acc := expectAccounts[0].(*model.Account)
|
acc := expectAccounts[0].(*model.Account)
|
||||||
newest, _ := suite.db.GetFollow(ctx, requestingAccount.ID, acc.ID)
|
newest, _ := suite.db.GetFollow(ctx, requestingAccount.ID, acc.ID)
|
||||||
expectAccounts = expectAccounts[1:]
|
expectAccounts = expectAccounts[1:]
|
||||||
query = fmt.Sprintf("limit=%d&max_id=%s", limit, newest.ID)
|
query = fmt.Sprintf("limit=%d&max_id=%s", limit, newest.ID)
|
||||||
|
|
||||||
case "forward":
|
case "oldestToNewest":
|
||||||
// Set the starting query to page forward from the oldest.
|
// Set the starting query to page from
|
||||||
|
// oldest (ie., last entry in slice).
|
||||||
acc := expectAccounts[len(expectAccounts)-1].(*model.Account)
|
acc := expectAccounts[len(expectAccounts)-1].(*model.Account)
|
||||||
oldest, _ := suite.db.GetFollow(ctx, requestingAccount.ID, acc.ID)
|
oldest, _ := suite.db.GetFollow(ctx, requestingAccount.ID, acc.ID)
|
||||||
expectAccounts = expectAccounts[:len(expectAccounts)-1]
|
expectAccounts = expectAccounts[:len(expectAccounts)-1]
|
||||||
|
@ -397,9 +410,9 @@ func (suite *FollowTestSuite) testGetFollowingPage(limit int, direction string)
|
||||||
)
|
)
|
||||||
|
|
||||||
switch direction {
|
switch direction {
|
||||||
case "backward":
|
case "newestToOldest":
|
||||||
// When paging backwards (DESC) we:
|
// When paging newest to oldest (ie., first page to last page):
|
||||||
// - iter from end of received accounts
|
// - iter from start of received accounts
|
||||||
// - iterate backward through received accounts
|
// - iterate backward through received accounts
|
||||||
// - stop when we reach last index of received accounts
|
// - stop when we reach last index of received accounts
|
||||||
// - compare each received with the first index of expected accounts
|
// - compare each received with the first index of expected accounts
|
||||||
|
@ -410,8 +423,8 @@ func (suite *FollowTestSuite) testGetFollowingPage(limit int, direction string)
|
||||||
expect = func(i []interface{}) interface{} { return i[0] }
|
expect = func(i []interface{}) interface{} { return i[0] }
|
||||||
trunc = func(i []interface{}) []interface{} { return i[1:] }
|
trunc = func(i []interface{}) []interface{} { return i[1:] }
|
||||||
|
|
||||||
case "forward":
|
case "oldestToNewest":
|
||||||
// When paging forwards (ASC) we:
|
// When paging oldest to newest (ie., last page to first page):
|
||||||
// - iter from end of received accounts
|
// - iter from end of received accounts
|
||||||
// - iterate backward through received accounts
|
// - iterate backward through received accounts
|
||||||
// - stop when we reach first index of received accounts
|
// - stop when we reach first index of received accounts
|
||||||
|
@ -419,7 +432,7 @@ func (suite *FollowTestSuite) testGetFollowingPage(limit int, direction string)
|
||||||
// - after each compare, drop the last index of expected accounts
|
// - after each compare, drop the last index of expected accounts
|
||||||
start = func(i []*model.Account) int { return len(i) - 1 }
|
start = func(i []*model.Account) int { return len(i) - 1 }
|
||||||
iter = func(i int) int { return i - 1 }
|
iter = func(i int) int { return i - 1 }
|
||||||
check = func(idx int, i []*model.Account) bool { return idx >= 0 }
|
check = func(idx int, _ []*model.Account) bool { return idx >= 0 }
|
||||||
expect = func(i []interface{}) interface{} { return i[len(i)-1] }
|
expect = func(i []interface{}) interface{} { return i[len(i)-1] }
|
||||||
trunc = func(i []interface{}) []interface{} { return i[:len(i)-1] }
|
trunc = func(i []interface{}) []interface{} { return i[:len(i)-1] }
|
||||||
}
|
}
|
||||||
|
@ -445,7 +458,14 @@ func (suite *FollowTestSuite) testGetFollowingPage(limit int, direction string)
|
||||||
// Parse response link header values.
|
// Parse response link header values.
|
||||||
values := result.Header.Values("Link")
|
values := result.Header.Values("Link")
|
||||||
links := linkheader.ParseMultiple(values)
|
links := linkheader.ParseMultiple(values)
|
||||||
filteredLinks := links.FilterByRel("next")
|
|
||||||
|
var filteredLinks linkheader.Links
|
||||||
|
if direction == "newestToOldest" {
|
||||||
|
filteredLinks = links.FilterByRel("next")
|
||||||
|
} else {
|
||||||
|
filteredLinks = links.FilterByRel("prev")
|
||||||
|
}
|
||||||
|
|
||||||
suite.NotEmpty(filteredLinks, "no next link provided with more remaining accounts on page=%d", p)
|
suite.NotEmpty(filteredLinks, "no next link provided with more remaining accounts on page=%d", p)
|
||||||
|
|
||||||
// A ref link header was set.
|
// A ref link header was set.
|
||||||
|
|
|
@ -39,6 +39,8 @@ import (
|
||||||
// <https://example.org/api/v1/accounts/0657WMDEC3KQDTD6NZ4XJZBK4M/followers?limit=80&max_id=01FC0SKA48HNSVR6YKZCQGS2V8>; rel="next", <https://example.org/api/v1/accounts/0657WMDEC3KQDTD6NZ4XJZBK4M/followers?limit=80&min_id=01FC0SKW5JK2Q4EVAV2B462YY0>; rel="prev"
|
// <https://example.org/api/v1/accounts/0657WMDEC3KQDTD6NZ4XJZBK4M/followers?limit=80&max_id=01FC0SKA48HNSVR6YKZCQGS2V8>; rel="next", <https://example.org/api/v1/accounts/0657WMDEC3KQDTD6NZ4XJZBK4M/followers?limit=80&min_id=01FC0SKW5JK2Q4EVAV2B462YY0>; rel="prev"
|
||||||
// ````
|
// ````
|
||||||
//
|
//
|
||||||
|
// If account `hide_collections` is true, and requesting account != target account, no results will be returned.
|
||||||
|
//
|
||||||
// ---
|
// ---
|
||||||
// tags:
|
// tags:
|
||||||
// - accounts
|
// - accounts
|
||||||
|
@ -102,6 +104,10 @@ import (
|
||||||
// type: array
|
// type: array
|
||||||
// items:
|
// items:
|
||||||
// "$ref": "#/definitions/account"
|
// "$ref": "#/definitions/account"
|
||||||
|
// headers:
|
||||||
|
// Link:
|
||||||
|
// type: string
|
||||||
|
// description: Links to the next and previous queries.
|
||||||
// '400':
|
// '400':
|
||||||
// description: bad request
|
// description: bad request
|
||||||
// '401':
|
// '401':
|
||||||
|
|
|
@ -39,6 +39,8 @@ import (
|
||||||
// <https://example.org/api/v1/accounts/0657WMDEC3KQDTD6NZ4XJZBK4M/following?limit=80&max_id=01FC0SKA48HNSVR6YKZCQGS2V8>; rel="next", <https://example.org/api/v1/accounts/0657WMDEC3KQDTD6NZ4XJZBK4M/following?limit=80&min_id=01FC0SKW5JK2Q4EVAV2B462YY0>; rel="prev"
|
// <https://example.org/api/v1/accounts/0657WMDEC3KQDTD6NZ4XJZBK4M/following?limit=80&max_id=01FC0SKA48HNSVR6YKZCQGS2V8>; rel="next", <https://example.org/api/v1/accounts/0657WMDEC3KQDTD6NZ4XJZBK4M/following?limit=80&min_id=01FC0SKW5JK2Q4EVAV2B462YY0>; rel="prev"
|
||||||
// ````
|
// ````
|
||||||
//
|
//
|
||||||
|
// If account `hide_collections` is true, and requesting account != target account, no results will be returned.
|
||||||
|
//
|
||||||
// ---
|
// ---
|
||||||
// tags:
|
// tags:
|
||||||
// - accounts
|
// - accounts
|
||||||
|
@ -99,9 +101,13 @@ import (
|
||||||
// name: accounts
|
// name: accounts
|
||||||
// description: Array of accounts that are followed by this account.
|
// description: Array of accounts that are followed by this account.
|
||||||
// schema:
|
// schema:
|
||||||
// type: array
|
// type: array
|
||||||
// items:
|
// items:
|
||||||
// "$ref": "#/definitions/account"
|
// "$ref": "#/definitions/account"
|
||||||
|
// headers:
|
||||||
|
// Link:
|
||||||
|
// type: string
|
||||||
|
// description: Links to the next and previous queries.
|
||||||
// '400':
|
// '400':
|
||||||
// description: bad request
|
// description: bad request
|
||||||
// '401':
|
// '401':
|
||||||
|
|
|
@ -69,7 +69,7 @@ import (
|
||||||
// '500':
|
// '500':
|
||||||
// description: internal server error
|
// description: internal server error
|
||||||
func (m *Module) AccountListsGETHandler(c *gin.Context) {
|
func (m *Module) AccountListsGETHandler(c *gin.Context) {
|
||||||
authed, err := oauth.Authed(c, false, false, false, false)
|
authed, err := oauth.Authed(c, true, true, true, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGetV1)
|
apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGetV1)
|
||||||
return
|
return
|
||||||
|
|
|
@ -72,6 +72,13 @@ func (m *Module) AccountLookupGETHandler(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if authed.Account.IsMoving() {
|
||||||
|
// For moving/moved accounts, just return
|
||||||
|
// empty to avoid breaking client apps.
|
||||||
|
apiutil.NotFoundAfterMove(c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
|
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
|
||||||
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGetV1)
|
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGetV1)
|
||||||
return
|
return
|
||||||
|
|
170
internal/api/client/accounts/mute.go
Normal file
|
@ -0,0 +1,170 @@
|
||||||
|
// 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 accounts
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
|
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AccountMutePOSTHandler swagger:operation POST /api/v1/accounts/{id}/mute accountMute
|
||||||
|
//
|
||||||
|
// Mute account by ID.
|
||||||
|
//
|
||||||
|
// If account was already muted, succeeds anyway. This can be used to update the details of a mute.
|
||||||
|
//
|
||||||
|
// ---
|
||||||
|
// tags:
|
||||||
|
// - accounts
|
||||||
|
//
|
||||||
|
// produces:
|
||||||
|
// - application/json
|
||||||
|
//
|
||||||
|
// parameters:
|
||||||
|
// -
|
||||||
|
// name: id
|
||||||
|
// type: string
|
||||||
|
// description: The ID of the account to block.
|
||||||
|
// in: path
|
||||||
|
// required: true
|
||||||
|
// -
|
||||||
|
// name: notifications
|
||||||
|
// type: boolean
|
||||||
|
// description: Mute notifications as well as posts.
|
||||||
|
// in: formData
|
||||||
|
// required: false
|
||||||
|
// default: false
|
||||||
|
// -
|
||||||
|
// name: duration
|
||||||
|
// type: number
|
||||||
|
// description: How long the mute should last, in seconds. If 0 or not provided, mute lasts indefinitely.
|
||||||
|
// in: formData
|
||||||
|
// required: false
|
||||||
|
// default: 0
|
||||||
|
//
|
||||||
|
// security:
|
||||||
|
// - OAuth2 Bearer:
|
||||||
|
// - write:mutes
|
||||||
|
//
|
||||||
|
// responses:
|
||||||
|
// '200':
|
||||||
|
// description: Your relationship to the account.
|
||||||
|
// schema:
|
||||||
|
// "$ref": "#/definitions/accountRelationship"
|
||||||
|
// '400':
|
||||||
|
// description: bad request
|
||||||
|
// '401':
|
||||||
|
// description: unauthorized
|
||||||
|
// '403':
|
||||||
|
// description: forbidden to moved accounts
|
||||||
|
// '404':
|
||||||
|
// description: not found
|
||||||
|
// '406':
|
||||||
|
// description: not acceptable
|
||||||
|
// '500':
|
||||||
|
// description: internal server error
|
||||||
|
func (m *Module) AccountMutePOSTHandler(c *gin.Context) {
|
||||||
|
authed, err := oauth.Authed(c, true, true, true, true)
|
||||||
|
if err != nil {
|
||||||
|
apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGetV1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if authed.Account.IsMoving() {
|
||||||
|
apiutil.ForbiddenAfterMove(c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
|
||||||
|
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGetV1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
targetAcctID := c.Param(IDKey)
|
||||||
|
if targetAcctID == "" {
|
||||||
|
err := errors.New("no account id specified")
|
||||||
|
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGetV1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
form := &apimodel.UserMuteCreateUpdateRequest{}
|
||||||
|
if err := c.ShouldBind(form); err != nil {
|
||||||
|
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGetV1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := normalizeCreateUpdateMute(form); err != nil {
|
||||||
|
apiutil.ErrorHandler(c, gtserror.NewErrorUnprocessableEntity(err, err.Error()), m.processor.InstanceGetV1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
relationship, errWithCode := m.processor.Account().MuteCreate(
|
||||||
|
c.Request.Context(),
|
||||||
|
authed.Account,
|
||||||
|
targetAcctID,
|
||||||
|
form,
|
||||||
|
)
|
||||||
|
if errWithCode != nil {
|
||||||
|
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
apiutil.JSON(c, http.StatusOK, relationship)
|
||||||
|
}
|
||||||
|
|
||||||
|
func normalizeCreateUpdateMute(form *apimodel.UserMuteCreateUpdateRequest) error {
|
||||||
|
// Apply defaults for missing fields.
|
||||||
|
form.Notifications = util.Ptr(util.PtrValueOr(form.Notifications, false))
|
||||||
|
|
||||||
|
// Normalize mute duration if necessary.
|
||||||
|
// If we parsed this as JSON, expires_in
|
||||||
|
// may be either a float64 or a string.
|
||||||
|
if ei := form.DurationI; ei != nil {
|
||||||
|
switch e := ei.(type) {
|
||||||
|
case float64:
|
||||||
|
form.Duration = util.Ptr(int(e))
|
||||||
|
|
||||||
|
case string:
|
||||||
|
duration, err := strconv.Atoi(e)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not parse duration value %s as integer: %w", e, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
form.Duration = &duration
|
||||||
|
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("could not parse expires_in type %T as integer", ei)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interpret zero as indefinite duration.
|
||||||
|
if form.Duration != nil && *form.Duration == 0 {
|
||||||
|
form.Duration = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
173
internal/api/client/accounts/mute_test.go
Normal file
|
@ -0,0 +1,173 @@
|
||||||
|
// 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 accounts_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"net/url"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/api/client/accounts"
|
||||||
|
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MuteTestSuite struct {
|
||||||
|
AccountStandardTestSuite
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *MuteTestSuite) postMute(
|
||||||
|
accountID string,
|
||||||
|
notifications *bool,
|
||||||
|
duration *int,
|
||||||
|
requestJson *string,
|
||||||
|
expectedHTTPStatus int,
|
||||||
|
expectedBody string,
|
||||||
|
) (*apimodel.Relationship, error) {
|
||||||
|
// instantiate recorder + test context
|
||||||
|
recorder := httptest.NewRecorder()
|
||||||
|
ctx, _ := testrig.CreateGinTestContext(recorder, nil)
|
||||||
|
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"])
|
||||||
|
ctx.Set(oauth.SessionAuthorizedToken, oauth.DBTokenToToken(suite.testTokens["local_account_1"]))
|
||||||
|
ctx.Set(oauth.SessionAuthorizedApplication, suite.testApplications["application_1"])
|
||||||
|
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"])
|
||||||
|
|
||||||
|
// create the request
|
||||||
|
ctx.Request = httptest.NewRequest(http.MethodPut, config.GetProtocol()+"://"+config.GetHost()+"/api/"+accounts.BasePath+"/"+accountID+"/mute", nil)
|
||||||
|
ctx.Request.Header.Set("accept", "application/json")
|
||||||
|
if requestJson != nil {
|
||||||
|
ctx.Request.Header.Set("content-type", "application/json")
|
||||||
|
ctx.Request.Body = io.NopCloser(strings.NewReader(*requestJson))
|
||||||
|
} else {
|
||||||
|
ctx.Request.Form = make(url.Values)
|
||||||
|
if notifications != nil {
|
||||||
|
ctx.Request.Form["notifications"] = []string{strconv.FormatBool(*notifications)}
|
||||||
|
}
|
||||||
|
if duration != nil {
|
||||||
|
ctx.Request.Form["duration"] = []string{strconv.Itoa(*duration)}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.AddParam("id", accountID)
|
||||||
|
|
||||||
|
// trigger the handler
|
||||||
|
suite.accountsModule.AccountMutePOSTHandler(ctx)
|
||||||
|
|
||||||
|
// read the response
|
||||||
|
result := recorder.Result()
|
||||||
|
defer result.Body.Close()
|
||||||
|
|
||||||
|
b, err := io.ReadAll(result.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
errs := gtserror.NewMultiError(2)
|
||||||
|
|
||||||
|
// check code + body
|
||||||
|
if resultCode := recorder.Code; expectedHTTPStatus != resultCode {
|
||||||
|
errs.Appendf("expected %d got %d", expectedHTTPStatus, resultCode)
|
||||||
|
if expectedBody == "" {
|
||||||
|
return nil, errs.Combine()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we got an expected body, return early
|
||||||
|
if expectedBody != "" {
|
||||||
|
if string(b) != expectedBody {
|
||||||
|
errs.Appendf("expected %s got %s", expectedBody, string(b))
|
||||||
|
}
|
||||||
|
return nil, errs.Combine()
|
||||||
|
}
|
||||||
|
|
||||||
|
resp := &apimodel.Relationship{}
|
||||||
|
if err := json.Unmarshal(b, resp); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *MuteTestSuite) TestPostMuteFull() {
|
||||||
|
accountID := suite.testAccounts["remote_account_1"].ID
|
||||||
|
notifications := true
|
||||||
|
duration := 86400
|
||||||
|
relationship, err := suite.postMute(accountID, ¬ifications, &duration, nil, http.StatusOK, "")
|
||||||
|
if err != nil {
|
||||||
|
suite.FailNow(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
suite.True(relationship.Muting)
|
||||||
|
suite.Equal(notifications, relationship.MutingNotifications)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *MuteTestSuite) TestPostMuteFullJSON() {
|
||||||
|
accountID := suite.testAccounts["remote_account_2"].ID
|
||||||
|
// Use a numeric literal with a fractional part to test the JSON-specific handling for non-integer "duration".
|
||||||
|
requestJson := `{
|
||||||
|
"notifications": true,
|
||||||
|
"duration": 86400.1
|
||||||
|
}`
|
||||||
|
relationship, err := suite.postMute(accountID, nil, nil, &requestJson, http.StatusOK, "")
|
||||||
|
if err != nil {
|
||||||
|
suite.FailNow(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
suite.True(relationship.Muting)
|
||||||
|
suite.True(relationship.MutingNotifications)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *MuteTestSuite) TestPostMuteMinimal() {
|
||||||
|
accountID := suite.testAccounts["remote_account_3"].ID
|
||||||
|
relationship, err := suite.postMute(accountID, nil, nil, nil, http.StatusOK, "")
|
||||||
|
if err != nil {
|
||||||
|
suite.FailNow(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
suite.True(relationship.Muting)
|
||||||
|
suite.False(relationship.MutingNotifications)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *MuteTestSuite) TestPostMuteSelf() {
|
||||||
|
accountID := suite.testAccounts["local_account_1"].ID
|
||||||
|
_, err := suite.postMute(accountID, nil, nil, nil, http.StatusNotAcceptable, `{"error":"Not Acceptable: getMuteTarget: account 01F8MH1H7YV1Z7D2C8K2730QBF cannot mute or unmute itself"}`)
|
||||||
|
if err != nil {
|
||||||
|
suite.FailNow(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *MuteTestSuite) TestPostMuteNonexistentAccount() {
|
||||||
|
accountID := "not_even_a_real_ULID"
|
||||||
|
_, err := suite.postMute(accountID, nil, nil, nil, http.StatusNotFound, `{"error":"Not Found: getMuteTarget: target account not_even_a_real_ULID not found in the db"}`)
|
||||||
|
if err != nil {
|
||||||
|
suite.FailNow(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMuteTestSuite(t *testing.T) {
|
||||||
|
suite.Run(t, new(MuteTestSuite))
|
||||||
|
}
|
|
@ -81,6 +81,11 @@ func (m *Module) AccountNotePOSTHandler(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if authed.Account.IsMoving() {
|
||||||
|
apiutil.ForbiddenAfterMove(c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
|
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
|
||||||
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGetV1)
|
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGetV1)
|
||||||
return
|
return
|
||||||
|
|
123
internal/api/client/accounts/profile.go
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
// 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 accounts
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
|
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AccountAvatarDELETEHandler swagger:operation DELETE /api/v1/profile/avatar accountAvatarDelete
|
||||||
|
//
|
||||||
|
// Delete the authenticated account's avatar.
|
||||||
|
// If the account doesn't have an avatar, the call succeeds anyway.
|
||||||
|
//
|
||||||
|
// ---
|
||||||
|
// tags:
|
||||||
|
// - accounts
|
||||||
|
//
|
||||||
|
// produces:
|
||||||
|
// - application/json
|
||||||
|
//
|
||||||
|
// security:
|
||||||
|
// - OAuth2 Bearer:
|
||||||
|
// - admin
|
||||||
|
//
|
||||||
|
// responses:
|
||||||
|
// '200':
|
||||||
|
// description: The updated account, including profile source information.
|
||||||
|
// schema:
|
||||||
|
// "$ref": "#/definitions/account"
|
||||||
|
// '400':
|
||||||
|
// description: bad request
|
||||||
|
// '401':
|
||||||
|
// description: unauthorized
|
||||||
|
// '403':
|
||||||
|
// description: forbidden
|
||||||
|
// '406':
|
||||||
|
// description: not acceptable
|
||||||
|
// '500':
|
||||||
|
// description: internal server error
|
||||||
|
func (m *Module) AccountAvatarDELETEHandler(c *gin.Context) {
|
||||||
|
m.accountDeleteProfileAttachment(c, m.processor.Media().DeleteAvatar)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AccountHeaderDELETEHandler swagger:operation DELETE /api/v1/profile/header accountHeaderDelete
|
||||||
|
//
|
||||||
|
// Delete the authenticated account's header.
|
||||||
|
// If the account doesn't have a header, the call succeeds anyway.
|
||||||
|
//
|
||||||
|
// ---
|
||||||
|
// tags:
|
||||||
|
// - accounts
|
||||||
|
//
|
||||||
|
// produces:
|
||||||
|
// - application/json
|
||||||
|
//
|
||||||
|
// security:
|
||||||
|
// - OAuth2 Bearer:
|
||||||
|
// - admin
|
||||||
|
//
|
||||||
|
// responses:
|
||||||
|
// '200':
|
||||||
|
// description: The updated account, including profile source information.
|
||||||
|
// schema:
|
||||||
|
// "$ref": "#/definitions/account"
|
||||||
|
// '400':
|
||||||
|
// description: bad request
|
||||||
|
// '401':
|
||||||
|
// description: unauthorized
|
||||||
|
// '403':
|
||||||
|
// description: forbidden
|
||||||
|
// '406':
|
||||||
|
// description: not acceptable
|
||||||
|
// '500':
|
||||||
|
// description: internal server error
|
||||||
|
func (m *Module) AccountHeaderDELETEHandler(c *gin.Context) {
|
||||||
|
m.accountDeleteProfileAttachment(c, m.processor.Media().DeleteHeader)
|
||||||
|
}
|
||||||
|
|
||||||
|
// accountDeleteProfileAttachment checks that an authenticated account is present and allowed to alter itself,
|
||||||
|
// runs an attachment deletion processor method, and returns the updated account.
|
||||||
|
func (m *Module) accountDeleteProfileAttachment(c *gin.Context, processDelete func(context.Context, *gtsmodel.Account) (*apimodel.Account, gtserror.WithCode)) {
|
||||||
|
authed, err := oauth.Authed(c, true, true, true, true)
|
||||||
|
if err != nil {
|
||||||
|
apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGetV1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
|
||||||
|
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGetV1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
acctSensitive, errWithCode := processDelete(c, authed.Account)
|
||||||
|
if errWithCode != nil {
|
||||||
|
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
apiutil.JSON(c, http.StatusOK, acctSensitive)
|
||||||
|
}
|
141
internal/api/client/accounts/profile_test.go
Normal file
|
@ -0,0 +1,141 @@
|
||||||
|
// 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 accounts_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/api/client/accounts"
|
||||||
|
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AccountProfileTestSuite struct {
|
||||||
|
AccountStandardTestSuite
|
||||||
|
}
|
||||||
|
|
||||||
|
func (suite *AccountProfileTestSuite) deleteProfileAttachment(
|
||||||
|
testAccountFixtureName string,
|
||||||
|
profileSubpath string,
|
||||||
|
handler func(*gin.Context),
|
||||||
|
expectedHTTPStatus int,
|
||||||
|
) (*apimodel.Account, error) {
|
||||||
|
// instantiate recorder + test context
|
||||||
|
recorder := httptest.NewRecorder()
|
||||||
|
ctx, _ := testrig.CreateGinTestContext(recorder, nil)
|
||||||
|
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts[testAccountFixtureName])
|
||||||
|
ctx.Set(oauth.SessionAuthorizedToken, oauth.DBTokenToToken(suite.testTokens[testAccountFixtureName]))
|
||||||
|
ctx.Set(oauth.SessionAuthorizedApplication, suite.testApplications["application_1"])
|
||||||
|
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers[testAccountFixtureName])
|
||||||
|
|
||||||
|
// create the request
|
||||||
|
ctx.Request = httptest.NewRequest(http.MethodDelete, config.GetProtocol()+"://"+config.GetHost()+"/api"+accounts.ProfileBasePath+"/"+profileSubpath, nil)
|
||||||
|
ctx.Request.Header.Set("accept", "application/json")
|
||||||
|
|
||||||
|
// trigger the handler
|
||||||
|
handler(ctx)
|
||||||
|
|
||||||
|
// read the response
|
||||||
|
result := recorder.Result()
|
||||||
|
defer result.Body.Close()
|
||||||
|
|
||||||
|
b, err := io.ReadAll(result.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// check code
|
||||||
|
if resultCode := recorder.Code; expectedHTTPStatus != resultCode {
|
||||||
|
return nil, fmt.Errorf("expected %d got %d", expectedHTTPStatus, resultCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp := &apimodel.Account{}
|
||||||
|
if err := json.Unmarshal(b, resp); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete the avatar of a user that has an avatar. Should succeed.
|
||||||
|
func (suite *AccountProfileTestSuite) TestDeleteAvatar() {
|
||||||
|
account, err := suite.deleteProfileAttachment(
|
||||||
|
"local_account_1",
|
||||||
|
"avatar",
|
||||||
|
suite.accountsModule.AccountAvatarDELETEHandler,
|
||||||
|
http.StatusOK,
|
||||||
|
)
|
||||||
|
if suite.NoError(err) {
|
||||||
|
// An empty URL is legal *only* in the test environment, which may have no default avatars.
|
||||||
|
suite.True(account.Avatar == "" || strings.HasPrefix(account.Avatar, "http://localhost:8080/assets/default_avatars/"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete the avatar of a user that doesn't have an avatar. Should succeed.
|
||||||
|
func (suite *AccountProfileTestSuite) TestDeleteNonexistentAvatar() {
|
||||||
|
account, err := suite.deleteProfileAttachment(
|
||||||
|
"admin_account",
|
||||||
|
"avatar",
|
||||||
|
suite.accountsModule.AccountAvatarDELETEHandler,
|
||||||
|
http.StatusOK,
|
||||||
|
)
|
||||||
|
if suite.NoError(err) {
|
||||||
|
// An empty URL is legal *only* in the test environment, which may have no default avatars.
|
||||||
|
suite.True(account.Avatar == "" || strings.HasPrefix(account.Avatar, "http://localhost:8080/assets/default_avatars/"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete the header of a user that has a header. Should succeed.
|
||||||
|
func (suite *AccountProfileTestSuite) TestDeleteHeader() {
|
||||||
|
account, err := suite.deleteProfileAttachment(
|
||||||
|
"local_account_2",
|
||||||
|
"header",
|
||||||
|
suite.accountsModule.AccountHeaderDELETEHandler,
|
||||||
|
http.StatusOK,
|
||||||
|
)
|
||||||
|
if suite.NoError(err) {
|
||||||
|
suite.Equal("http://localhost:8080/assets/default_header.png", account.Header)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete the header of a user that doesn't have a header. Should succeed.
|
||||||
|
func (suite *AccountProfileTestSuite) TestDeleteNonexistentHeader() {
|
||||||
|
account, err := suite.deleteProfileAttachment(
|
||||||
|
"admin_account",
|
||||||
|
"header",
|
||||||
|
suite.accountsModule.AccountHeaderDELETEHandler,
|
||||||
|
http.StatusOK,
|
||||||
|
)
|
||||||
|
if suite.NoError(err) {
|
||||||
|
suite.Equal("http://localhost:8080/assets/default_header.png", account.Header)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAccountProfileTestSuite(t *testing.T) {
|
||||||
|
suite.Run(t, new(AccountProfileTestSuite))
|
||||||
|
}
|
|
@ -41,12 +41,13 @@ import (
|
||||||
//
|
//
|
||||||
// parameters:
|
// parameters:
|
||||||
// -
|
// -
|
||||||
// name: id
|
// name: id[]
|
||||||
// type: array
|
// type: array
|
||||||
// items:
|
// items:
|
||||||
// type: string
|
// type: string
|
||||||
// description: Account IDs.
|
// description: Account IDs.
|
||||||
// in: query
|
// in: query
|
||||||
|
// collectionFormat: multi
|
||||||
// required: true
|
// required: true
|
||||||
//
|
//
|
||||||
// security:
|
// security:
|
||||||
|
|
|
@ -113,6 +113,13 @@ func (m *Module) AccountSearchGETHandler(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if authed.Account.IsMoving() {
|
||||||
|
// For moving/moved accounts, just return
|
||||||
|
// empty to avoid breaking client apps.
|
||||||
|
apiutil.Data(c, http.StatusOK, apiutil.AppJSON, apiutil.EmptyJSONArray)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
|
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
|
||||||
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGetV1)
|
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGetV1)
|
||||||
return
|
return
|
||||||
|
|
|
@ -119,6 +119,10 @@ import (
|
||||||
// type: array
|
// type: array
|
||||||
// items:
|
// items:
|
||||||
// "$ref": "#/definitions/status"
|
// "$ref": "#/definitions/status"
|
||||||
|
// headers:
|
||||||
|
// Link:
|
||||||
|
// type: string
|
||||||
|
// description: Links to the next and previous queries.
|
||||||
// '400':
|
// '400':
|
||||||
// description: bad request
|
// description: bad request
|
||||||
// '401':
|
// '401':
|
||||||
|
@ -130,7 +134,7 @@ import (
|
||||||
// '500':
|
// '500':
|
||||||
// description: internal server error
|
// description: internal server error
|
||||||
func (m *Module) AccountStatusesGETHandler(c *gin.Context) {
|
func (m *Module) AccountStatusesGETHandler(c *gin.Context) {
|
||||||
authed, err := oauth.Authed(c, false, false, false, false)
|
authed, err := oauth.Authed(c, true, true, true, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGetV1)
|
apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGetV1)
|
||||||
return
|
return
|
||||||
|
@ -148,6 +152,13 @@ func (m *Module) AccountStatusesGETHandler(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if authed.Account.IsMoving() && targetAcctID != authed.Account.ID {
|
||||||
|
// For moving/moved accounts, allow the
|
||||||
|
// account to view its own statuses only.
|
||||||
|
apiutil.Data(c, http.StatusOK, apiutil.AppJSON, apiutil.EmptyJSONArray)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
limit := 30
|
limit := 30
|
||||||
limitString := c.Query(LimitKey)
|
limitString := c.Query(LimitKey)
|
||||||
if limitString != "" {
|
if limitString != "" {
|
||||||
|
|