Compare commits

...

192 commits

Author SHA1 Message Date
tobi db803617db
[bugfix] avoid v. long notification clear query (#3007) 2024-06-14 10:14:55 +00:00
Vyr Cossont b789fe2bc7
[feature] filter API v2: Restore keywords_attributes and statuses_attributes (#2995)
These filter API v2 features were cut late in development because the form encoding version is hard to implement correctly and because I thought no clients actually used `keywords_attributes`. Unfortunately, Phanpy does use `keywords_attributes`.
2024-06-14 10:11:41 +02:00
tobi ee6e9b2795
[docs] Rework README a bit, import into docs (#3006) 2024-06-13 23:21:41 +02:00
Daenney 3c86bd890c
[chore] Silence memlimit package (#3002)
The memlimit package started to log any error returned by automemlimit.
This updates our implementation to call SetGoMemLimitWithOpts() instead
which uses the same defaults as automemlimit except for being
initialised with a noop logger.

We check the returned error for a particular substring, as when cgroups
isn't available even when running on a Linux system that's not a
problem. If it's anything but that error, we log it at the warning
level so that admins can still diagnose other cgroup related issues.

Fixes #2983

Co-authored-by: tobi <31960611+tsmethurst@users.noreply.github.com>
2024-06-13 19:02:48 +02:00
Martijn de Boer 38cd889f7b
[docs] Add optional instructions for replicating SQLite for disaster recovery (#2981)
* Add optional instructions for replicating SQLite for disaster recovery

* Remove references to MacOS from replicating-sqlite.md

Resolve comment on review

* Add mention for replicating SQLite in database_maintenance.md

Adds a reference to the replicating sqlite page under database maintenance
2024-06-13 15:09:51 +02:00
tobi 9d9013db4c
[chore] Update move test (#3005) 2024-06-13 15:08:43 +02:00
tobi fbdfce3190
[chore] Don't render sign-up form when registrations closed (#3001) 2024-06-12 15:00:21 +01:00
dependabot[bot] fef9b6a621
Bump braces from 3.0.2 to 3.0.3 in /web/source (#2999)
Bumps [braces](https://github.com/micromatch/braces) from 3.0.2 to 3.0.3.
- [Changelog](https://github.com/micromatch/braces/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/braces/compare/3.0.2...3.0.3)

---
updated-dependencies:
- dependency-name: braces
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-12 15:00:12 +01:00
tobi 20877c1474
[bugfix] Ensure side effects handled for local unlocked follows (#2998) 2024-06-12 13:23:10 +01:00
Daenney 978b4176f1
[chore] Upgrade wasm-sqlite to v0.16.2 (#2997) 2024-06-12 13:21:34 +01:00
Vyr Cossont dcf825bd87
[bugfix] Correct Swagger path for poll voting (#2996)
Updates the Swagger spec to match the router.

Fixes #2994
2024-06-12 11:16:46 +02:00
tobi 611f9de39b
[bugfix] Deref stats async, serve stub collections if handshaking (#2990)
* [bugfix] Deref stats async, allow peek if handshaking

* don't return totalItems when handshaking or hiding collections

* use GetLimit()

* use StubAccountStats
2024-06-11 11:54:59 +02:00
kim fd6637df4a
[bugfix] boost and account recursion (#2982)
* fix possible infinite recursion if moved accounts are self-referential

* adds a defensive check for a boost being a boost of a boost wrapper

* add checks on input for a boost of a boost

* remove unnecessary check

* add protections on account move to prevent move recursion loops

* separate status conversion without boost logic into separate function to remove risk of recursion

* move boost check to boost function itself

* formatting

* use error 422 instead of 500

* use gtserror not standard errors package for error creation
2024-06-10 19:42:41 +01:00
tobi ebdcb00d0a
[chore] Roll back use of (created) pseudo-header pending #2991 (#2992) 2024-06-10 19:42:26 +01:00
dependabot[bot] 69aba377bc
[chore]: Bump golang.org/x/image from 0.16.0 to 0.17.0 (#2985)
Bumps [golang.org/x/image](https://github.com/golang/image) from 0.16.0 to 0.17.0.
- [Commits](https://github.com/golang/image/compare/v0.16.0...v0.17.0)

---
updated-dependencies:
- dependency-name: golang.org/x/image
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-10 08:10:25 +00:00
dependabot[bot] a687f0634c
[chore]: Bump github.com/gorilla/websocket from 1.5.1 to 1.5.2 (#2987)
Bumps [github.com/gorilla/websocket](https://github.com/gorilla/websocket) from 1.5.1 to 1.5.2.
- [Release notes](https://github.com/gorilla/websocket/releases)
- [Commits](https://github.com/gorilla/websocket/compare/v1.5.1...v1.5.2)

---
updated-dependencies:
- dependency-name: github.com/gorilla/websocket
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-10 07:43:38 +00:00
dependabot[bot] 12c406d968
[chore]: Bump github.com/tdewolff/minify/v2 from 2.20.32 to 2.20.33 (#2984)
Bumps [github.com/tdewolff/minify/v2](https://github.com/tdewolff/minify) from 2.20.32 to 2.20.33.
- [Release notes](https://github.com/tdewolff/minify/releases)
- [Commits](https://github.com/tdewolff/minify/compare/v2.20.32...v2.20.33)

---
updated-dependencies:
- dependency-name: github.com/tdewolff/minify/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-10 07:41:15 +00:00
dependabot[bot] 3babb6c0d2
[chore]: Bump golang.org/x/net from 0.25.0 to 0.26.0 (#2986)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.25.0 to 0.26.0.
- [Commits](https://github.com/golang/net/compare/v0.25.0...v0.26.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-10 07:40:53 +00:00
dependabot[bot] 83ee766e34
[chore]: Bump github.com/minio/minio-go/v7 from 7.0.70 to 7.0.71 (#2988)
Bumps [github.com/minio/minio-go/v7](https://github.com/minio/minio-go) from 7.0.70 to 7.0.71.
- [Release notes](https://github.com/minio/minio-go/releases)
- [Commits](https://github.com/minio/minio-go/compare/v7.0.70...v7.0.71)

---
updated-dependencies:
- dependency-name: github.com/minio/minio-go/v7
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-10 07:40:16 +00:00
tobi 048339a6aa
[bugfix] Use re2 syntax for regex validation (#2978) 2024-06-08 12:32:28 +00:00
tobi ce3b8aacf7
[chore] Warn about email/password change when using OIDC (#2975)
* [chore] Warn about email/password change when using OIDC

* go fmt
2024-06-07 15:21:57 +01:00
Daenney cc4f773b0e
[chore] Update WASM go-sqlite3 to v0.16.1 (#2976)
This includes support for journal mode set to WAL on the BSDs.

Relates to: #1753, #2962
2024-06-07 15:06:43 +02:00
Vyr Cossont afcfa48a7d
[feature] Implement filters_changed stream event (#2972) 2024-06-07 10:51:13 +02:00
Vyr Cossont e605788b4b
[bugfix] Filter fixes (#2971)
* Add Filter.Expired convenience method, consistent with mutes

* Add missing Swagger for filter keyword/status delete and for 403s on moved accounts
2024-06-06 20:16:20 +02:00
Vyr Cossont 5e2d4fdb19
[feature] User muting (#2960)
* User muting

* Address review feedback

* Rename uniqueness constraint on user_mutes to match convention

* Remove unused account_id from where clause

* Add UserMute to NewTestDB

* Update test/envparsing.sh with new and fixed cache stuff

* Address tobi's review comments

* Make compiledUserMuteListEntry.expired consistent with UserMute.Expired

* Make sure mute_expires_at is serialized as an explicit null for indefinite mutes

---------

Co-authored-by: tobi <tobi.smethurst@protonmail.com>
2024-06-06 16:38:02 +00:00
kim b371c2db47
[bugfix] update media if more than just url changes (#2970)
* refactor status media handling into separate functions, handle case of changed metadata

* update fetchRemoteAccount{Avatar,Header} to use new refactored {load,update}Attachment() functions

* whoops, nearly marked avatars as headers :')

* reformatting to improve legibility
2024-06-06 15:35:50 +01:00
tobi bcda048eab
[feature] Self-serve email change for users (#2957)
* [feature] Email change

* frontend stuff for changing email

* docs

* tests etc

* differentiate more clearly between local user+account and account

* populate user
2024-06-06 14:43:25 +01:00
tobi 131020faeb
drop date (#2969) 2024-06-06 12:50:56 +01:00
kim 5dcc954072
[feature] do not uncache status / emoji media if attached status is bookmarked (#2956)
* do not uncache status / emoji media if attached status is bookmarked

* add status bookmark and bookmark IDs caches

* update status bookmark tests

* move IsStatusBookmarkedBy() to StatusBookmark{} interface, rely on cache

* fix envparsing.sh test
2024-06-06 11:44:43 +01:00
tobi 6f26b32ec3
[bugfix] Don't nil emojis + fields on blocked accounts (#2968)
* [bugfix] Don't nil emojis + fields on blocked accounts

* comment

* swagger
2024-06-06 11:22:16 +01:00
kim 3b7faac604
[bugfix] concurrent map writes in dereferencer media processing maps (#2964)
* removes the avatar / header deref maps as we now have per-uri status / account locks, adds retries on data-races, adds separate emoji map mutex

* work with a copy of account / status for each retry loop

* revert to old data race behaviour, it gets too complicated otherwise

---------

Co-authored-by: tobi <tobi.smethurst@protonmail.com>
2024-06-06 10:50:14 +02:00
tobi 9caf29bed2
[chore] Fiddle with CI tests; use wasmsqlite3 for CI tests (#2966)
* [chore] Update CI test stuff

* don't bother with ldflags

* use wasmsqlite3
2024-06-05 21:40:08 +02:00
Vyr Cossont 45fe295caa
Compile filter keyword regexps when touched through PutFilter or UpdateFilter (#2951)
Followup to #2903
2024-06-04 10:33:39 +01:00
kim f17dd62ff5
[feature] log worker startup counts (#2958)
* log number of each worker kinds started, and log when stopped

* remove worker debug logging

* whoops, fix the count of media workers
2024-06-03 22:55:50 +01:00
kim 6ed6824d5d
bump go-structr to v0.8.5 to improve memory usage (#2955) 2024-06-03 12:03:35 +01:00
dependabot[bot] addaba05c0
[chore]: Bump github.com/spf13/viper from 1.18.2 to 1.19.0 (#2954)
Bumps [github.com/spf13/viper](https://github.com/spf13/viper) from 1.18.2 to 1.19.0.
- [Release notes](https://github.com/spf13/viper/releases)
- [Commits](https://github.com/spf13/viper/compare/v1.18.2...v1.19.0)

---
updated-dependencies:
- dependency-name: github.com/spf13/viper
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-03 10:52:54 +01:00
tobi d79c2f2688
[feature/frontend] Add debug sections to settings panel (#2950)
* [feature/frontend] Add debug sections to settings panel

* max-width

* swagger
2024-06-03 11:20:53 +02:00
Vyr Cossont 04bcde08a1
[feature] Add from: search operator and account_id query param (#2943)
* Add from: search operator

* Fix whitespace in Swagger YAML comment

* Move query parsing into its own method

* Document search

* Clarify post search scope
2024-05-31 12:57:42 +02:00
Vyr Cossont 61a8d36255
[feature] Implement Filter API v2 (#2936)
* Use correct entity name

* We support server-side filters now

* Document filter v1 methods that can throw a 409

* Validate v1 filter phrase as filter title

* Always check v1 filter API status codes in tests

* Document keyword minimum requirement on filter API v1

* Make it possible to specify filter keyword update columns per filter keyword

* Implement v2 filter API

* Fix lint and tests

* Update Swagger spec

* Fix filter update test

* Update Swagger spec *correctly*

* Update actual files Swagger spec was generated from

* Remove keywords_attributes and statuses_attributes

* Add test for serialization of empty filter

* More helpful messages when object is owned by wrong account
2024-05-31 12:55:56 +02:00
tobi 4db596b8b9
[chore] little startup tweaks (#2941)
* [chore] little startup tweaks

* go fmt
2024-05-30 11:55:57 +02:00
Daenney 2fd69ec58b
[chore] Make worker run messages debug output (#2944)
On startup and shutdown of a worker, we log a message of the worker
being started together with a textual representation of a memory
address. Though this can be handy for developers to debug
startup/shutdown sequencing issues of the workers, it's typically not
very useful or informative for an admin. We can also output a lot of
these (on my system I get 265 lines of these during startup).

This changes the messages from Info to Debug, to not print them under
normal circumstances.
2024-05-30 11:00:47 +02:00
Daenney b67937c213
[docs]: Document build tag for WASM SQLite (#2942)
Follow-up for #2863.
2024-05-30 11:00:25 +02:00
浮心物语 59fedfc4f9
[docs] Fix link in domain part (#2946) 2024-05-30 11:00:01 +02:00
kim 32e570abfd
[chore] improved startup / shutdown (#2925)
* improved server shutdown with more precise shutdown of modules + deferring of ALL of it

* make the same changes to the testrig server

* use testrig specific func

* update variable name to fix nilptr

* fix removal of setting db on state
2024-05-29 13:21:04 +02:00
Vyr Cossont 975e92b7f1
[feature] Implement profile API (#2926)
* Implement profile API

This Mastodon 4.2 extension provides capabilities missing from the existing Mastodon account update API: deleting an account's avatar or header.

See: https://docs.joinmastodon.org/methods/profile/

* Move profile media methods to media processor

* Remove check for moved account
2024-05-29 12:57:44 +02:00
tobi f9a4a6120d
[feature] Debug admin endpoint to clear caches (#2940)
* [feature] Debug admin endpoint to clear caches

* go fmt
2024-05-29 12:56:17 +02:00
tobi fa9a3075a5
[chore/bugfix] Don't cache MovedTo account (#2939) 2024-05-28 15:39:45 +02:00
tobi 4dc30f8687
[chore] make wasm sqlite3 available to goreleaser via env var (#2938) 2024-05-28 15:20:40 +02:00
tobi a276b1ca06
[feature/frontend] Let admins send test email to validate SMTP config (#2934)
* [feature/frontend] Let admins send test email to validate SMTP config

* wee
2024-05-27 17:03:54 +00:00
kim 1e7b32490d
[experiment] add alternative wasm sqlite3 implementation available via build-tag (#2863)
This allows for building GoToSocial with [SQLite transpiled to WASM](https://github.com/ncruces/go-sqlite3) and accessed through [Wazero](https://wazero.io/).
2024-05-27 17:46:15 +02:00
tobi cce21c11cb
[chore] Small styling + link issues (#2933) 2024-05-27 12:37:14 +02:00
tobi 5bee30d60c
[chore] Fix report email link (#2932) 2024-05-27 12:27:49 +02:00
dependabot[bot] d96cca60a1
[chore]: Bump github.com/tdewolff/minify/v2 from 2.20.25 to 2.20.32 (#2927) 2024-05-27 09:36:09 +00:00
dependabot[bot] 0a18c0d802
[chore]: Bump github.com/jackc/pgx/v5 from 5.5.5 to 5.6.0 (#2929) 2024-05-27 09:35:41 +00:00
kim 3d3e99ae52
[performance] update storage backend and make use of seek syscall when available (#2924)
* update to use go-storage/ instead of go-store/v2/storage/

* pull in latest version from codeberg

* remove test output 😇

* add code comments

* set the exclusive bit when creating new files in disk config

* bump to actual release version

* bump to v0.1.1 (tis a simple no-logic change)

* update readme

* only use a temporary read seeker when decoding video if required (should only be S3 now)

* use fastcopy library to use memory pooled buffers when calling TempFileSeeker()

* update to use seek call in serveFileRange()
2024-05-22 11:46:24 +02:00
dependabot[bot] 06b1e0173b
--- (#2923)
updated-dependencies:
- dependency-name: github.com/tdewolff/minify/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-21 14:37:47 +01:00
kim b092da6d28
[performance] cache v2 filter keyword regular expressions (#2903)
* add caching of filterkeyword regular expressions

* formatting

* fix WholeWord nil check
2024-05-21 14:20:19 +01:00
kim 6c0d93c6cb
[chore] dependabot updates (#2922)
* [chore]: Bump github.com/prometheus/client_golang from 1.18.0 to 1.19.1

Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 1.18.0 to 1.19.1.
- [Release notes](https://github.com/prometheus/client_golang/releases)
- [Changelog](https://github.com/prometheus/client_golang/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prometheus/client_golang/compare/v1.18.0...v1.19.1)

---
updated-dependencies:
- dependency-name: github.com/prometheus/client_golang
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* [chore]: Bump github.com/KimMachineGun/automemlimit from 0.6.0 to 0.6.1

Bumps [github.com/KimMachineGun/automemlimit](https://github.com/KimMachineGun/automemlimit) from 0.6.0 to 0.6.1.
- [Release notes](https://github.com/KimMachineGun/automemlimit/releases)
- [Commits](https://github.com/KimMachineGun/automemlimit/compare/v0.6.0...v0.6.1)

---
updated-dependencies:
- dependency-name: github.com/KimMachineGun/automemlimit
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* [chore]: Bump github.com/tdewolff/minify/v2 from 2.20.20 to 2.20.24

Bumps [github.com/tdewolff/minify/v2](https://github.com/tdewolff/minify) from 2.20.20 to 2.20.24.
- [Release notes](https://github.com/tdewolff/minify/releases)
- [Commits](https://github.com/tdewolff/minify/compare/v2.20.20...v2.20.24)

---
updated-dependencies:
- dependency-name: github.com/tdewolff/minify/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* [chore]: Bump github.com/go-swagger/go-swagger

Bumps [github.com/go-swagger/go-swagger](https://github.com/go-swagger/go-swagger) from 0.30.6-0.20240418033037-c46c303aaa02 to 0.31.0.
- [Release notes](https://github.com/go-swagger/go-swagger/releases)
- [Changelog](https://github.com/go-swagger/go-swagger/blob/master/.goreleaser.yml)
- [Commits](https://github.com/go-swagger/go-swagger/commits/v0.31.0)

---
updated-dependencies:
- dependency-name: github.com/go-swagger/go-swagger
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* [chore]: Bump github.com/gin-gonic/gin from 1.9.1 to 1.10.0

Bumps [github.com/gin-gonic/gin](https://github.com/gin-gonic/gin) from 1.9.1 to 1.10.0.
- [Release notes](https://github.com/gin-gonic/gin/releases)
- [Changelog](https://github.com/gin-gonic/gin/blob/master/CHANGELOG.md)
- [Commits](https://github.com/gin-gonic/gin/compare/v1.9.1...v1.10.0)

---
updated-dependencies:
- dependency-name: github.com/gin-gonic/gin
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-21 14:17:22 +01:00
dependabot[bot] 16c1832793
[chore]: Bump github.com/gin-contrib/cors from 1.7.1 to 1.7.2 (#2912)
Bumps [github.com/gin-contrib/cors](https://github.com/gin-contrib/cors) from 1.7.1 to 1.7.2.
- [Release notes](https://github.com/gin-contrib/cors/releases)
- [Changelog](https://github.com/gin-contrib/cors/blob/master/.goreleaser.yaml)
- [Commits](https://github.com/gin-contrib/cors/compare/v1.7.1...v1.7.2)

---
updated-dependencies:
- dependency-name: github.com/gin-contrib/cors
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: kim <89579420+NyaaaWhatsUpDoc@users.noreply.github.com>
2024-05-13 08:29:54 +00:00
dependabot[bot] f817f96596
[chore]: Bump github.com/gin-contrib/sessions from 1.0.0 to 1.0.1 (#2916)
Bumps [github.com/gin-contrib/sessions](https://github.com/gin-contrib/sessions) from 1.0.0 to 1.0.1.
- [Release notes](https://github.com/gin-contrib/sessions/releases)
- [Changelog](https://github.com/gin-contrib/sessions/blob/master/.goreleaser.yaml)
- [Commits](https://github.com/gin-contrib/sessions/compare/v1.0.0...v1.0.1)

---
updated-dependencies:
- dependency-name: github.com/gin-contrib/sessions
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-13 08:07:17 +00:00
dependabot[bot] 1ba9601472
[chore]: Bump golang.org/x/crypto from 0.22.0 to 0.23.0 (#2915)
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.22.0 to 0.23.0.
- [Commits](https://github.com/golang/crypto/compare/v0.22.0...v0.23.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-13 08:06:51 +00:00
dependabot[bot] 96eea416f3
[chore]: Bump golang.org/x/net from 0.24.0 to 0.25.0 (#2914)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.24.0 to 0.25.0.
- [Commits](https://github.com/golang/net/compare/v0.24.0...v0.25.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-13 08:06:30 +00:00
kim c06e6fb656
[performance] update go-structr and go-mutexes with memory usage improvements (#2909)
* update go-structr and go-mutexes with memory usage improvements

* bump to go-structr v0.8.4
2024-05-13 08:05:46 +00:00
tobi 578a4e0cf5
[bugfix] Reset emoji fields on upload error (#2905) 2024-05-07 19:48:12 +02:00
kim f24ce34c3a
bump modernc.org/sqlite v1.29.8 -> v1.29.9 (concurrency workaround) (#2906) 2024-05-07 14:52:37 +01:00
kim f456bd3401
update the total ratios calculation to include ALL caches (previously was missing a few!) (#2907) 2024-05-06 22:29:31 +01:00
kim 3554991444
update go-structr -> v0.8.2 which includes some minor memory usage improvements (#2904) 2024-05-06 19:44:22 +00:00
Vyr Cossont 45f4afe60e
feature: filters v2 server-side warning/hiding (#2793)
* Remove dead code

* Filter statuses when converting to frontend representation

* status.filtered is an array

* Make matching case-insensitive

* Remove TODOs that don't need to be done now

* Add missing filter check for notification

* lint: rename ErrHideStatus

* APIFilterActionToFilterAction not used yet

* swaggerino docseroni

* Address review comments

* Add apimodel.FilterActionNone

---------

Co-authored-by: tobi <31960611+tsmethurst@users.noreply.github.com>
Co-authored-by: tobi <tobi.smethurst@protonmail.com>
2024-05-06 12:49:08 +01:00
dependabot[bot] a0d066844f
[chore]: Bump golang.org/x/oauth2 from 0.19.0 to 0.20.0 (#2900) 2024-05-06 11:14:04 +00:00
dependabot[bot] 8237e8d09e
[chore]: Bump codeberg.org/gruf/go-structr from 0.7.0 to 0.8.0 (#2902)
Bumps codeberg.org/gruf/go-structr from 0.7.0 to 0.8.0.

---
updated-dependencies:
- dependency-name: codeberg.org/gruf/go-structr
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-06 08:51:06 +00:00
dependabot[bot] a5f28fe0c9
[chore]: Bump github.com/gin-contrib/gzip from 1.0.0 to 1.0.1 (#2899)
Bumps [github.com/gin-contrib/gzip](https://github.com/gin-contrib/gzip) from 1.0.0 to 1.0.1.
- [Release notes](https://github.com/gin-contrib/gzip/releases)
- [Changelog](https://github.com/gin-contrib/gzip/blob/master/.goreleaser.yaml)
- [Commits](https://github.com/gin-contrib/gzip/compare/v1.0.0...v1.0.1)

---
updated-dependencies:
- dependency-name: github.com/gin-contrib/gzip
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-06 08:50:47 +00:00
dependabot[bot] c98ec6f89d
[chore]: Bump golang.org/x/image from 0.15.0 to 0.16.0 (#2898)
Bumps [golang.org/x/image](https://github.com/golang/image) from 0.15.0 to 0.16.0.
- [Commits](https://github.com/golang/image/compare/v0.15.0...v0.16.0)

---
updated-dependencies:
- dependency-name: golang.org/x/image
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-06 08:50:03 +00:00
kim d3f6960ba0
close + drain body if response body is too large (#2897) 2024-05-05 16:43:38 +01:00
tobi 6171dcbe51
[feature] Add HTTP header permission section to frontend (#2893)
* [feature] Add HTTP header filter section to frontend

* tweak naming a bit
2024-05-05 11:47:22 +00:00
tobi 35b1c54bde
[frontend] Do optimistic update when approving/rejecting/suspending account (#2892) 2024-05-02 17:57:53 +02:00
kim a840f4d49d
add missing caches to the main cache sweep command (#2891) 2024-05-02 14:09:59 +01:00
tobi ebec95a522
[bugfix] Lock when checking/creating notifs to avoid race (#2890)
* [bugfix] Lock when checking/creating notifs to avoid race

* test notif spam
2024-05-02 13:43:00 +01:00
tobi 725a21b027
[feature] Page through accounts as moderator (#2881)
* [feature] Page through accounts as moderator

* aaaaa

* use COLLATE "C" for Postgres to ensure same ordering as SQLite

* fix typo, test paging up

* don't show moderation / info for our instance acct
2024-05-01 14:11:22 +01:00
tobi 1edcb06afe
[bugfix] Tidy up remaining references to workers in cmd (#2889) 2024-05-01 12:55:00 +01:00
kim 2300d5e73b
[bugfix] function queue memory pools limitlessly grow (#2882)
* updates the simple queue memory pool to actually self-clean + limit growth

* update memory pool cleaning frequency
2024-05-01 13:30:43 +02:00
kim eb61c783ed
[bugfix] flaky paging test (#2888) 2024-05-01 13:29:42 +02:00
kim a8254a40e7
[bugfix] further paging mishaps (#2884)
* FURTHER paging shenanigans 🥲

* remove cursor logic from ToLinkURL()

* fix up paging tests

---------

Co-authored-by: tobi <tobi.smethurst@protonmail.com>
2024-04-30 16:22:23 +02:00
kim ec7c983e46
[bugfix] retry on http 500 errors *inclusive* (#2886) 2024-04-30 16:18:32 +02:00
kim ec334ece20
[chore] include attemptno in httpclient logs (#2887)
* include request attempt number in httpclient logs

* slightly nicer attempt number formatting
2024-04-30 16:15:50 +02:00
Daenney 39b3a27c82
[docs] Remove last references to RPi (#2885)
This updates the documentation to remove the last stray references to
the copaganda Pi. It now uses the the term single-board computer. GtS
can run fine on all kinds of SBCs and isn't limited to that one
particular fruit version.
2024-04-30 14:27:44 +01:00
kim 4f87ef246c
[bugfix] paging rel links (#2883)
* fix paging so it uses correct cursor query parameter name

* improved code comment

* whoops, flip the cursoring 🤦

* fix the broken test
2024-04-30 12:19:33 +02:00
dependabot[bot] bfc21e4850
[chore]: Bump github.com/tdewolff/minify/v2 from 2.20.19 to 2.20.20 (#2875) 2024-04-29 10:45:17 +00:00
dependabot[bot] d3bac8bbec
[chore]: Bump github.com/minio/minio-go/v7 from 7.0.69 to 7.0.70 (#2877) 2024-04-29 10:44:11 +00:00
tobi 40ece19055
[chore] Fix conflict in workers tests (#2880)
* [chore] Fix conflict in workers tests

* commenty-wenty
2024-04-29 11:43:18 +02:00
dependabot[bot] 1375a86919
[chore]: Bump go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc (#2878) 2024-04-29 09:18:16 +00:00
dependabot[bot] b2eecd9dc7
[chore]: Bump go.opentelemetry.io/otel/sdk from 1.25.0 to 1.26.0 (#2879) 2024-04-29 09:02:12 +00:00
kim 48b91ca239
[bugfix] Fix error string typo (#2873) 2024-04-26 15:20:56 +02:00
kim c9c0773f2c
[performance] update remaining worker pools to use queues (#2865)
* start replacing client + federator + media workers with new worker + queue types

* refactor federatingDB.Delete(), drop queued messages when deleting account / status

* move all queue purging to the processor workers

* undo toolchain updates

* code comments, ensure dereferencer worker pool gets started

* update gruf libraries in readme

* start the job scheduler separately to the worker pools

* reshuffle ordering or server.go + remove duplicate worker start / stop

* update go-list version

* fix vendoring

* move queue invalidation to before wipeing / deletion, to ensure queued work not dropped

* add logging to worker processing functions in testrig, don't start workers in unexpected places

* update go-structr to add (+then rely on) QueueCtx{} type

* ensure more worker pools get started properly in tests

* fix remaining broken tests relying on worker queue logic

* fix account test suite queue popping logic, ensure noop workers do not pull from queue

* move back accidentally shuffled account deletion order

* ensure error (non nil!!) gets passed in refactored federatingDB{}.Delete()

* silently drop deletes from accounts not permitted to

* don't warn log on forwarded deletes

* make if else clauses easier to parse

* use getFederatorMsg()

* improved code comment

* improved code comment re: requesting account delete checks

* remove boolean result from worker start / stop since false = already running or already stopped

* remove optional passed-in http.client

* remove worker starting from the admin CLI commands (we don't need to handle side-effects)

* update prune cli to start scheduler but not all of the workers

* fix rebase issues

* remove redundant return statements

* i'm sorry sir linter
2024-04-26 13:50:46 +01:00
tobi ba4f51ce2f
[chore] update Docker container to use new go swagger hash (#2872) 2024-04-26 12:12:24 +02:00
Daenney 83d24658a8
[chore] Update the flags passed to goreleaser (#2869)
* [chore] Update the flags passed to goreleaser

--rm-dist has been deprecated, the new flag is called --clean.

Co-authored-by: tobi <tobi.smethurst@protonmail.com>
2024-04-26 11:42:33 +02:00
Daenney 6c9bc26a6d
[chore] Update setting testrig loglevel (#2870)
cmp.Or was introduced in Go 1.22 and picks the first value that's not
the zero value for the type. For a string, the zero value is the empty
string, which is what os.Getenv will return if the environment variable
is not set. That then results in "error" being returned instead.

This allows loading an environment variable with a default without
having to do the check and write out the conditional.
2024-04-26 11:31:26 +02:00
tobi fd8a724e77
[chore] Bump go swagger (#2871)
* bump go swagger version

* bump swagger version
2024-04-26 11:31:10 +02:00
Daenney 3a369d834a
[chore] Upgrade our Go version to 1.22 (#2862)
* [chore] Upgrade our Go version to 1.22

With Go 1.22 having been released at the start of February, it's now
been a few months. No major issues have shown up, and the two point
release since then have primarily been security fixes plus some general
bug fixing.

This sets the required Go version to 1.22, as there's nothing in 1.22.1
or 1.22.2 that we would explicitly require. It sets the toolchain to the
latest point release, to ensure we pick up any fixes from there when
building releases etc.

* [chore] Update CI to Go 1.22

* [chore] Update golangci-lint to 1.25.7

Newer version should know about Go 1.22 and run fine.

* [chore] Update Docker container to Go 1.22

* [chore] Update Dockerfile to newer Alpine version

* sign drone.yml

* add missing license header

---------

Co-authored-by: tobi <tobi.smethurst@protonmail.com>
2024-04-26 10:40:29 +02:00
tobi aecf74951c
[chore] Settings refactor 2: the re-refactoring-ing (#2866)
* [chore] Bit more refactoring of settings panel

* fix up some remaining things

* groovy baby yeah!

* remove unused Suspense
2024-04-25 18:24:24 +02:00
tobi 7a1e639483
[chore] Refactor settings panel routing (and other fixes) (#2864) 2024-04-24 11:12:47 +01:00
dependabot[bot] 62788aa116
[chore]: Bump codeberg.org/gruf/go-mutexes from 1.4.0 to 1.4.1 (#2860)
Bumps codeberg.org/gruf/go-mutexes from 1.4.0 to 1.4.1.

---
updated-dependencies:
- dependency-name: codeberg.org/gruf/go-mutexes
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-22 13:43:45 +01:00
dependabot[bot] a57dd15a8e
[chore]: Bump github.com/miekg/dns from 1.1.58 to 1.1.59 (#2861) 2024-04-22 09:11:53 +00:00
kim 12a7eba01f
[chore] bump modernc.org/sqlite to v1.29.8 (with our concurrency workaround) (#2855) 2024-04-22 11:02:11 +02:00
Daenney dcab555a6b
[chore] Update robots.txt (#2856)
This updates the robots.txt based on the list of the ai.robots.txt
repository. We can look at automating that at some point.

It's worth pointing out that some robots, namely the ones by Bytedance,
are known to ignore robots.txt entirely.
2024-04-22 11:01:37 +02:00
dependabot[bot] 0db9e34b69
[chore]: Bump github.com/KimMachineGun/automemlimit from 0.5.0 to 0.6.0 (#2859) 2024-04-22 08:59:30 +00:00
tobi b7c629a18a
[bugfix] Fix incorrect field name for status source, add helpful message (#2854)
* [bugfix] Fix incorrect field name for status source, add helpful message

* swagger

* yyammm
2024-04-18 13:22:55 +02:00
tobi 431505b3e4
[feature] Stub conversations endpoint (#2853) 2024-04-18 12:36:02 +02:00
tobi 34d0159ad5
[feature] Stub account mutes endpoint (#2852)
* [feature] Stub account mutes endpoint

* swagger? i barely know 'er!
2024-04-18 09:59:47 +01:00
kim b3f2d44143
bump to modernc.org/sqlite v1.29.7 (#2850) 2024-04-17 17:10:51 +01:00
kim c67bbe5ba0
update to set requesting account when deleting status (#2849) 2024-04-17 16:54:40 +01:00
tobi cef9924d9a
[feature] Status source endpoint (#2848)
* [feature] statusSource endpoint

* finish up
2024-04-17 13:49:20 +01:00
tobi ef16919d4a
[feature] Stub status history endpoint (#2847) 2024-04-17 13:06:49 +01:00
tobi 8cf685fbe9
[bugfix] Fix minor API issue w/ boosted statuses (#2846) 2024-04-17 12:41:40 +02:00
tobi 6de5717d7f
[bugfix] fix get all tokens (#2841) 2024-04-16 13:25:47 +02:00
tobi 3cceed11b2
[feature/performance] Store account stats in separate table (#2831)
* [feature/performance] Store account stats in separate table, get stats from remote

* test account stats

* add some missing increment / decrement calls

* change stats function signatures

* rejig logging a bit

* use lock when updating stats
2024-04-16 13:10:13 +02:00
kim f79d50b9b2
[performance] cached oauth database types (#2838)
* update token + client code to use struct caches

* add code comments

* slight tweak to default mem ratios

* fix envparsing

* add appropriate invalidate hooks

* update the tokenstore sweeping function to rely on caches

* update to use PutClient()

* add ClientID to list of token struct indices
2024-04-15 14:22:21 +01:00
tobi 8b30709791
[chore] Turn accounts-registration-open false by default (#2839) 2024-04-15 14:41:15 +02:00
kim 1018cde107
[chore] bump bun library versions (#2837) 2024-04-15 12:01:20 +02:00
dependabot[bot] 6bb43f3f9b
[chore]: Bump golang.org/x/net from 0.23.0 to 0.24.0 (#2834) 2024-04-15 08:44:29 +00:00
dependabot[bot] 66e4510bf1
[chore]: Bump golang.org/x/crypto from 0.21.0 to 0.22.0 (#2835) 2024-04-15 08:34:49 +00:00
Bishochiparaa fdd23cb9ed
[chore] Delete the unnecessary #, because this # lead to the wrong URL (#2830) 2024-04-14 09:31:45 +02:00
tobi 89e0cfd874
[feature] Admin accounts endpoints; approve/reject sign-ups (#2826)
* update settings panels, add pending overview + approve/deny functions

* add admin accounts get, approve, reject

* send approved/rejected emails

* use signup URL

* docs!

* email

* swagger

* web linting

* fix email tests

* wee lil fixerinos

* use new paging logic for GetAccounts() series of admin endpoints, small changes to query building

* shuffle useAccountIDIn check *before* adding to query

* fix parse from toot react error

* use `netip.Addr`

* put valid slices in globals

* optimistic updates for account state

---------

Co-authored-by: kim <grufwub@gmail.com>
2024-04-13 13:25:10 +02:00
kim 1439042104
[performance] update GetAccountsByIDs() to use the new multi cache loader endpoint (#2828)
* update GetAccountsByIDs() to use the new multi cache loader endpoint

* fix broken import
2024-04-13 11:36:10 +02:00
tobi a5c6dc9716
[bugfix] Include MIME email headers to avoid mangling non-ascii text (#2827) 2024-04-12 15:18:14 +01:00
dependabot[bot] c097745c38
[chore]: Bump go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc (#2818)
Bumps [go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc](https://github.com/open-telemetry/opentelemetry-go) from 1.24.0 to 1.25.0.
- [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.24.0...v1.25.0)

---
updated-dependencies:
- dependency-name: go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-11 11:46:18 +02:00
kim db2dcc3455
[chore] update go-structr => v0.6.2 (fixes nested field ptr following) (#2822)
* update go-structr => v0.6.1 (fixes nested field ptr following)

* bump to v0.6.2
2024-04-11 11:46:08 +02:00
tobi 9fb8a78f91
[feature] New user sign-up via web page (#2796)
* [feature] User sign-up form and admin notifs

* add chosen + filtered languages to migration

* remove stray comment

* chosen languages schmosen schmanguages

* proper error on local account missing
2024-04-11 11:45:53 +02:00
kim a483bd9e38
[performance] massively improved ActivityPub delivery worker efficiency (#2812)
* add delivery worker type that pulls from queue to httpclient package

* finish up some code commenting, bodge a vendored activity library change, integrate the deliverypool changes into transportcontroller

* hook up queue deletion logic

* support deleting queued http requests by target ID

* don't index APRequest by hostname in the queue

* use gorun

* use the original context's values when wrapping msg type as delivery{}

* actually log in the AP delivery worker ...

* add uncommitted changes

* use errors.AsV2()

* use errorsv2.AsV2()

* finish adding some code comments, add bad host handling to delivery workers

* slightly tweak deliveryworkerpool API, use advanced sender multiplier

* remove PopCtx() method, let others instead rely on Wait()

* shuffle things around to move delivery stuff into transport/ subpkg

* remove dead code

* formatting

* validate request before queueing for delivery

* finish adding code comments, fix up backoff code

* finish adding more code comments

* clamp minimum no. senders to 1

* add start/stop logging to delivery worker, some slight changes

* remove double logging

* use worker ptrs

* expose the embedded log fields in httpclient.Request{}

* ensure request context values are preserved when updating ctx

* add delivery worker tests

* fix linter issues

* ensure delivery worker gets inited in testrig

* fix tests to delivering messages to check worker delivery queue

* update error type to use ptr instead of value receiver

* fix test calling Workers{}.Start() instead of testrig.StartWorkers()

* update docs for advanced-sender-multiplier

* update to the latest activity library version

* add comment about not using httptest.Server{}
2024-04-11 11:45:35 +02:00
dependabot[bot] 15733cddb2
[chore]: Bump github.com/yuin/goldmark from 1.7.0 to 1.7.1 (#2819)
Bumps [github.com/yuin/goldmark](https://github.com/yuin/goldmark) from 1.7.0 to 1.7.1.
- [Release notes](https://github.com/yuin/goldmark/releases)
- [Commits](https://github.com/yuin/goldmark/compare/v1.7.0...v1.7.1)

---
updated-dependencies:
- dependency-name: github.com/yuin/goldmark
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-10 12:04:27 +01:00
dependabot[bot] 4d16c3adaf
[chore]: Bump golang.org/x/oauth2 from 0.18.0 to 0.19.0 (#2816)
Bumps [golang.org/x/oauth2](https://github.com/golang/oauth2) from 0.18.0 to 0.19.0.
- [Commits](https://github.com/golang/oauth2/compare/v0.18.0...v0.19.0)

---
updated-dependencies:
- dependency-name: golang.org/x/oauth2
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-10 12:04:11 +01:00
tobi e20c7c21e1
[docs] update deployment considerations docs with latest findings (#2821)
* [docs] update deployment considerations docs with latest findings

* simplify single-board computer section
2024-04-10 12:03:43 +02:00
tobi 6db7d014dc
[bugfix] Ensure side effects for local -> local follows get processed (#2820) 2024-04-08 11:51:51 +02:00
kim 85bc140b58
[bugfix] temporarily replace modernc.org/sqlite-v1.29.5 with gitlab.com/NyaaaWhatsUpDoc/sqlite-v1.29.5-concurrency-workaround (#2811) 2024-04-05 11:58:28 +02:00
kim 83e7847cdf
fix possible nil panic (#2809) 2024-04-04 16:45:59 +01:00
Daenney 236604bd6b
[chore] Log less output on failed test (#2804)
* [chore] Log less output on failed test

This changes the testrig log level to be error by default instead of
info. This makes test failures a lot easier to read, as we don't have
the parade of info logs for each failure to scroll through.

It speeds up the test suite by a couple of seconds since we need
to buffer and flush a lot less messages. On a clean run, so no test
failures, it's about a 3s difference on my machine. Depending on the
amount of test failures, total time saved can vary.

This also introduces a GTS_TESTRIG_LOG_LEVEL environment variable that
we explicitly check for, making it easy to override the log level should
we have a need for it. This would be primarily for running locally, and
not so much as part of go test.

Lastly, it updates the syslog tests to use log.Error because if the log
level is set to error but we call log.Info no message is emitted and we
hang indefinitely on the channel read.

* [chore] Rename the testrig log level env var
2024-04-04 16:42:42 +01:00
Daenney 8ed1b8142c
[bugfix] Sort follows chronologically (#2801)
The id on the follows table is not a ULID, but a random ID. Sorting on
them results in a completely random order. Instead, sort on created_at,
which sould result in a stable and intended sort order.

Fixes: #2769

Co-authored-by: kim <89579420+NyaaaWhatsUpDoc@users.noreply.github.com>
2024-04-03 14:06:39 +01:00
kim 15ede4c1ea
[bugfix] improved authenticate post inbox error handling (#2803)
* improved PostInboxScheme() error handling / logging in case of failed auth

* dumbass kim. returning err instead of errWithCode...

* add checks for the slightly changed error handling in tests, add notes in codebase about the odd way of working
2024-04-03 13:57:07 +01:00
tobi 65b5366031
[bugfix] Set domain for empty-domain Friendica accounts (#2800) 2024-04-02 13:40:54 +01:00
kim e664d0918b
[bugfix] Set the Host header within the signing transport (#2799) 2024-04-02 14:28:36 +02:00
kim d61d5c8a6a
[bugfix] httpclient not signing subsequent redirect requests (#2798)
* move http request signing to transport

* actually hook up the http roundtripper ...

* add code comments for the new gtscontext functions
2024-04-02 13:12:26 +02:00
tobi 4bbdef02f1
[chore] Try to parse public key as both Actor + bare key (#2710)
* [chore] Try to parse public key as both Actor + bare key

* fix weird test fail
2024-04-02 11:30:10 +01:00
kim adf345f1ec
[chore] bump go structr cache version -> v0.6.0 (#2773)
* update go-structr library -> v0.6.0, add necessary wrapping types + code changes to support these changes

* update readme with go-structr package changes

* improved wrapping of the SliceCache type

* add code comments for the cache wrapper types

* remove test.out 😇

---------

Co-authored-by: tobi <31960611+tsmethurst@users.noreply.github.com>
2024-04-02 12:03:40 +02:00
tobi f05874be30
[feature] Option to hide followers/following (#2788) 2024-04-02 10:42:24 +01:00
tobi 29972e2c93
[feature] Add enable command to mirror existing disable command; update docs (#2792)
* [feature] add `enable` CLI command to mirror existing `disable` command

* update docs
2024-04-02 10:29:46 +01:00
tobi be259b13a7
[chore] Vendor mkdocs fonts; update docs dependencies (#2789)
* [chore] Update docs dependencies

* update rtd deps

* aaaa

* why can't you just be normal!

* vendor social plugin fonts
2024-03-29 10:52:46 +01:00
dependabot[bot] 72b38c2f12
[chore] Bump express from 4.18.2 to 4.19.2 in /web/source (#2790)
Bumps [express](https://github.com/expressjs/express) from 4.18.2 to 4.19.2.
- [Release notes](https://github.com/expressjs/express/releases)
- [Changelog](https://github.com/expressjs/express/blob/master/History.md)
- [Commits](https://github.com/expressjs/express/compare/4.18.2...4.19.2)

---
updated-dependencies:
- dependency-name: express
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-29 10:48:44 +01:00
tobi 4f66dba139
[bugfix] Serve correct URI for AP following collection (#2787) 2024-03-28 12:10:16 +01:00
tobi ac3f195b56
[chore/frontend] Adjust contrast, mute blurple a bit (#2785) 2024-03-26 11:02:09 +00:00
tobi 8953f57d88
[feature] User-selectable preset CSS themes for accounts (#2777)
* [feature] User-selectable preset themes

* docs, more theme stuff

* lint, tests

* fix css name

* correct some little issues

* add another theme

* fix poll background

* okay last theme i swear

* make retrieval of apimodel themes more conventional

* preallocate stylesheet slices
2024-03-25 17:32:24 +00:00
tobi b7b42e832a
[feature] Add healthcheck endpoints /livez and /readyz (#2783)
* [feature] Add healthcheck endpoints `/livez` and `/readyz`

* use select that returns no data
2024-03-25 17:05:14 +00:00
tobi 36f79e650c
[bugfix] Avoid empty public/local timeline queries (#2784) 2024-03-25 16:20:45 +00:00
dependabot[bot] 29031d1e27
[chore]: Bump github.com/gin-contrib/sessions from 0.0.5 to 1.0.0 (#2782) 2024-03-25 11:00:36 +00:00
dependabot[bot] a24936040c
[chore]: Bump github.com/coreos/go-oidc/v3 from 3.9.0 to 3.10.0 (#2779) 2024-03-25 10:58:34 +00:00
dependabot[bot] 5f43419a87
[chore]: Bump github.com/gin-contrib/cors from 1.7.0 to 1.7.1 (#2778) 2024-03-25 10:43:08 +00:00
dependabot[bot] 40ee470536
[chore]: Bump github.com/gin-contrib/gzip from 0.0.6 to 1.0.0 (#2781) 2024-03-25 10:31:19 +00:00
tobi 7f4a0a1aeb
[chore] Move local account settings to separate db table (#2770)
* [chore] Move local account settings to separate database model

* don't use separate settings_id
2024-03-22 14:03:46 +01:00
kim 0767647056
[bugfix] add all possible busy result codes to the sqlite errBusy catching check (#2775) 2024-03-22 13:26:02 +01:00
dependabot[bot] 967558947a
[chore]: Bump modernc.org/sqlite from 1.29.4 to 1.29.5 (#2768)
Bumps [modernc.org/sqlite](https://gitlab.com/cznic/sqlite) from 1.29.4 to 1.29.5.
- [Commits](https://gitlab.com/cznic/sqlite/commits/v1.29.5)

---
updated-dependencies:
- dependency-name: modernc.org/sqlite
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-21 11:14:24 +00:00
dependabot[bot] 51e8879779
[chore]: Bump github.com/tdewolff/minify/v2 from 2.20.18 to 2.20.19 (#2767) 2024-03-18 10:05:26 +00:00
dependabot[bot] 003d62465f
Bump follow-redirects from 1.15.4 to 1.15.6 in /web/source (#2763) 2024-03-18 09:57:09 +00:00
dependabot[bot] 8570793dfb
[chore]: Bump golang.org/x/oauth2 from 0.17.0 to 0.18.0 (#2766) 2024-03-18 09:56:42 +00:00
dependabot[bot] fae7627a1b
[chore]: Bump github.com/technologize/otel-go-contrib (#2765) 2024-03-18 09:54:32 +00:00
tobi 0362d49da0
[bugfix] Parse links that contain non-ascii characters (#2762) 2024-03-15 17:26:53 +00:00
Daenney e6e696ae22
[docs] Add IPv6 example for rate limit exceptions (#2761) 2024-03-15 13:13:57 +01:00
Vyr Cossont 4a56f31047
[chore] Swagger: add missing paging params to bookmarks list (#2759)
No code changes.
2024-03-15 11:43:24 +01:00
Blake Smith 15578835a8
[chore] Different error message for net/mail parsing on go 1.21.8 and above (#2760)
go 1.21.8 fixed some minor issues in net/mail that causes the test suite to fail
for some mail validation cases. Although we're not on go 1.21.8 yet, make the test
forward and backwards compatible.

See: 263c059b09
2024-03-15 11:36:43 +01:00
Vyr Cossont efbc8151db
[chore] Swagger: correct names and formats for several array params (#2758)
No code changes.
2024-03-15 11:31:58 +01:00
tobi 9b94b1faa2
[docs] Add database maintenance section; update info message on ANALYZE run (sqlite) (#2756)
* [chore] Limit size of ANALYZE run after migration (sqlite)

* add basic db maintenance tips

* update docs, analyze

* amend info message a wee bit

* update docs/admin/database_maintenance.md wording

Co-authored-by: Daenney <daenney@users.noreply.github.com>

---------

Co-authored-by: kim <89579420+NyaaaWhatsUpDoc@users.noreply.github.com>
Co-authored-by: Daenney <daenney@users.noreply.github.com>
2024-03-14 17:40:36 +00:00
tobi 1fe4cdaf46
[bugfix] Don't return 500 when searching for unpermitted status (#2753) 2024-03-14 17:57:54 +01:00
kim 459e75a9db
[bugfix] only check replyTo visibility during permission checks IF status is local (#2757) 2024-03-14 17:55:35 +01:00
tobi 92bf1f779b
[chore] Expose move endpoint again, small settings panel fixes (#2752) 2024-03-13 14:45:50 +01:00
tobi ab2d063fcb
[feature] Process outgoing Move from clientAPI (#2750)
* prevent moved accounts from taking create-type actions

* update move logic

* federate move out

* indicate on web profile when an account has moved

* [docs] Add migration docs section

* lock while checking + setting move state

* use redirectFollowers func for clientAPI as well

* comment typo

* linter? i barely know 'er!

* Update internal/uris/uri.go

Co-authored-by: Daenney <daenney@users.noreply.github.com>

* add a couple tests for move

* fix little mistake exposed by tests (thanks tests)

* ensure Move marked as successful

* attach shared util funcs to struct

* lock whole account when doing move

* move moving check to after error check

* replace repeated text with error func

* linterrrrrr!!!!

* catch self follow case

---------

Co-authored-by: Daenney <daenney@users.noreply.github.com>
2024-03-13 13:53:29 +01:00
Vyr Cossont 13b9fd5f92
[bugfix]: Add missing Link headers in Swagger spec (#2751)
* Adds Link headers to all endpoints that use it for paging
* Fixes the return type for listing what accounts someone follows
2024-03-13 12:23:26 +01:00
kim c27049ad15
[chore] bump modernc.org/sqlite v1.28.0 -> v1.29.4 (#2749) 2024-03-12 18:21:59 +01:00
tobi 1bcdf1da3b
[feature] Process incoming Move activity (#2724)
* [feature] Process incoming account Move activity

* fix targetAcct typo

* put move origin account on fMsg

* shift more move functionality back to the worker fn

* simplify error logic
2024-03-12 14:34:08 +00:00
Daenney 5e871e81a8
[chore] Update usage of OTEL libraries (#2725)
* otel to 1.24
* prometheus exporter to 0.46
* bunotel to 1.1.17

Also:
* Use schemaless URL for metrics
* Add software version to tracing schema
2024-03-11 15:34:34 +01:00
dependabot[bot] 8e88ee8d9c
[chore]: Bump github.com/minio/minio-go/v7 from 7.0.67 to 7.0.69 (#2748) 2024-03-11 10:51:13 +00:00
dependabot[bot] 5a56f4f8fb
[chore]: Bump github.com/go-jose/go-jose/v3 from 3.0.1 to 3.0.3 (#2733) 2024-03-11 10:36:13 +00:00
dependabot[bot] d115f9ebc4
[chore]: Bump github.com/jackc/pgx/v5 from 5.5.3 to 5.5.5 (#2747) 2024-03-11 10:13:33 +00:00
dependabot[bot] e24efcac8b
[chore]: Bump github.com/gin-contrib/cors from 1.5.0 to 1.7.0 (#2745) 2024-03-11 10:12:06 +00:00
tobi 4c155aa847
[bugfix] Fix whitespace move_id issue (#2742) 2024-03-10 13:27:31 +01:00
tobi de3c15a425
[bugfix] Don't error when populating MovedTo if account not found (#2741)
* [bugfix] Don't error when populating MovedTo if account not found

* test the thing
2024-03-10 11:59:55 +01:00
CDN 56c7c5dee2
[chore] seperate snapshot from release in goreleaser (#2740) 2024-03-10 11:10:30 +01:00
tobi ebdee5aed8
[chore] Downgrade sqlite v1.29.2 -> v1.28.0 (#2736)
* [chore] Downgrade sqlite v1.29.2 -> v1.29.0

* go down to v1.28.0
2024-03-08 11:45:15 +01:00
tobi 7050df2572
[docs] Add note about privileged ports (#2735) 2024-03-08 10:09:06 +01:00
kim 016923b4dc
[bugfix] add workaround for Xsqlite_interrupt() permanently breaking connection (#2731) 2024-03-07 14:56:33 +01:00
Vyr Cossont 74e84cf8fa
[docs/chore] Swagger fixes for filters (#2730)
* Swagger: filtersV1Get should return an array

* Swagger: context must use multi format
2024-03-07 11:28:57 +01:00
Vyr Cossont 5159664a51
[chore] Fix a Swagger warning that only manifests during Go client code generation (#2729) 2024-03-06 18:44:58 +01:00
Vyr Cossont fc3741365c
[bugfix] Fix Swagger spec and add test script (#2698)
* Add Swagger spec test script

* Fix Swagger spec errors not related to statuses with polls

* Add API tests that post a status with a poll

* Fix creating a status with a poll from form params

* Fix Swagger spec errors related to statuses with polls (this is the last error)

* Fix Swagger spec warnings not related to unused definitions

* Suppress a duplicate list update params definition that was somehow causing wrong param names

* Add Swagger test to CI

- updates Drone config
- vendorizes go-swagger
- fixes a file extension issue that caused the test script to generate JSON instead of YAML with the vendorized version

* Put `Sample: ` on its own line everywhere

* Remove unused id param from emojiCategoriesGet

* Add 5 more pairs of profile fields to account update API Swagger

* Remove Swagger prefix from dummy fields

It makes the generated code look weird

* Manually annotate params for statusCreate operation

* Fix all remaining Swagger spec warnings

- Change some models into operation parameters
- Ignore models that already correspond to manually documented operation parameters but can't be trivially changed (those with file fields)

* Documented that creating a status with scheduled_at isn't implemented yet

* sign drone.yml

* Fix filter API Swagger errors

* fixup! Fix filter API Swagger errors

---------

Co-authored-by: tobi <tobi.smethurst@protonmail.com>
2024-03-06 18:05:45 +01:00
tobi 68c8fe67cc
[chore/bugfix] Little DB fixes (#2726) 2024-03-06 15:40:37 +01:00
tobi b22e213e15
[feature/chore] Add Move database functions + cache (#2647)
* [feature/chore] Add Move database functions + cache

* add move mem ratio to envparsing.sh

* update comment
2024-03-06 11:18:57 +01:00
Vyr Cossont 61a2b91f45
[feature] Filters v1 (#2594)
* Implement client-side v1 filters

* Exclude linter false positives

* Update test/envparsing.sh

* Fix minor Swagger, style, and Bun usage issues

* Regenerate Swagger

* De-generify filter keywords

* Remove updating filter statuses

This is an operation that the Mastodon v2 filter API doesn't actually have, because filter statuses, unlike keywords, don't have options: the only info they contain is the status ID to be filtered.

* Add a test for filter statuses specifically

* De-generify filter statuses

* Inline FilterEntry

* Use vertical style for Bun operations consistently

* Add comment on Filter DB interface

* Remove GoLand linter control comments

Our existing linters should catch these, or they don't matter very much

* Reduce memory ratio for filters
2024-03-06 11:15:58 +01:00
3264 changed files with 1114756 additions and 184945 deletions

View file

@ -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
View file

@ -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

View file

@ -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'

View file

@ -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

View file

@ -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
View file

@ -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"
}
}

View file

@ -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

View file

@ -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
View file

@ -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-->

View file

@ -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.

View file

@ -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",

View file

@ -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
} }

View file

@ -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()

View file

@ -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
} }

View file

@ -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
} }

View file

@ -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",

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View 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

View 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.

View file

@ -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
View 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/

View file

@ -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
View 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
View 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.

View 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)

View file

@ -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)

View 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
```

File diff suppressed because it is too large Load diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

BIN
docs/assets/signup-form.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 137 KiB

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

View file

@ -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]

View file

@ -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: ""
``` ```

View file

@ -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"
``` ```

View file

@ -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: ""

View file

@ -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:

View file

@ -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

View file

@ -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.

View file

@ -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`.

View file

@ -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.

View file

@ -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.

View file

@ -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 \

View file

@ -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.

View file

@ -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
View 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.

View file

@ -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).

View file

@ -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
View file

@ -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
View file

@ -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=

View file

@ -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,

View file

@ -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.

View file

@ -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

View 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{})
}

View file

@ -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.

View file

@ -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'.

View file

@ -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

View file

@ -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

View file

@ -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
} }

View file

@ -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.
) )
} }

View file

@ -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

View file

@ -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())

View file

@ -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,
}) })

View file

@ -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 {

View file

@ -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
} }

View file

@ -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),

View file

@ -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())
}

View file

@ -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
} }

View file

@ -31,8 +31,6 @@ import (
// //
// Move your account to another account. // Move your account to another account.
// //
// NOT IMPLEMENTED YET!
//
// --- // ---
// tags: // tags:
// - accounts // - accounts

View file

@ -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)
} }

View file

@ -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")
} }

View file

@ -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() {

View file

@ -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)
} }

View file

@ -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

View file

@ -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.

View file

@ -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':

View file

@ -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':

View file

@ -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

View file

@ -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

View 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
}

View 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, &notifications, &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))
}

View file

@ -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

View 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)
}

View 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))
}

View file

@ -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:

View file

@ -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

View file

@ -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 != "" {

Some files were not shown because too many files have changed in this diff Show more