This commit is contained in:
commit
a9363705e8
8
.drone.yml
Normal file
8
.drone.yml
Normal file
@ -0,0 +1,8 @@
|
||||
---
|
||||
kind: pipeline
|
||||
name: blurp
|
||||
steps:
|
||||
- name: build
|
||||
image: golang:1.21
|
||||
commands:
|
||||
- go build -v ./...
|
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
archive
|
15
LICENSE
Normal file
15
LICENSE
Normal file
@ -0,0 +1,15 @@
|
||||
blurp: A GoToSocial status deletion tool.
|
||||
Copyright (C) 2024 decentral1se
|
||||
|
||||
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 <https://www.gnu.org/licenses/>.
|
37
Makefile
Normal file
37
Makefile
Normal file
@ -0,0 +1,37 @@
|
||||
PROG := blurp
|
||||
COMMIT := $(shell git rev-list -1 HEAD)
|
||||
GCFLAGS := -gcflags="all=-l -B"
|
||||
LDFLAGS := -ldflags="-X 'main.Commit=$(COMMIT)' -s -w"
|
||||
ALLFLAGS := -v $(GCFLAGS) $(LDFLAGS)
|
||||
|
||||
default: build
|
||||
|
||||
check:
|
||||
@test -z $$(gofmt -l .) || \
|
||||
(echo "gofmt: formatting issue - run 'make format' to resolve" && exit 1)
|
||||
|
||||
format:
|
||||
@gofmt -s -w .
|
||||
|
||||
build: check
|
||||
@go build $(ALLFLAGS) .
|
||||
|
||||
install:
|
||||
@go install $(ALLFLAGS) .
|
||||
|
||||
test:
|
||||
@go test ./... -cover -v
|
||||
|
||||
clean:
|
||||
@rm ./$(PROG)
|
||||
|
||||
upx:
|
||||
@upx --lzma ./$(PROG)
|
||||
|
||||
release:
|
||||
@goreleaser release --snapshot --clean
|
||||
|
||||
deps:
|
||||
@go get -t -u ./...
|
||||
|
||||
.PHONY: check format build install test clean upx release deps
|
60
README.md
Normal file
60
README.md
Normal file
@ -0,0 +1,60 @@
|
||||
# blurp
|
||||
|
||||
[](https://build.coopcloud.tech/decentral1se/blurp)
|
||||
|
||||
> 🚧 Status: **ALPHA SOFTWARE** 🚧
|
||||
|
||||
A [GoToSocial](https://docs.gotosocial.org) status deletion tool.
|
||||
|
||||
## Install
|
||||
|
||||
It's ~ 3 MB in size. Only `linux/amd64` is supported atm.
|
||||
|
||||
```
|
||||
curl https://git.coopcloud.tech/decentral1se/blurp/raw/branch/main/blurp -o blurp
|
||||
chmod +x blurp
|
||||
```
|
||||
|
||||
## Preparation
|
||||
|
||||
* `blurp delete` will get you rate limited by your own instance. I temporarily solved this by [turning off rate limiting](https://docs.gotosocial.org/en/latest/api/ratelimiting/#can-i-configure-the-rate-limit-can-i-just-turn-it-off), running the command and then turning rate limiting back on again. I could probably implement some backoff in the code but this is just easier.
|
||||
|
||||
* `blurp delete` will remove statuses but not media attachments *immediately* for **local media**. Remote media is removed immediately. Unattached local media will be remove the next time the media cleaning job runs.
|
||||
|
||||
## Delete stuff 🔥
|
||||
|
||||
You'll nee to log in first:
|
||||
|
||||
```
|
||||
blurp login -u foo@bar.zone
|
||||
```
|
||||
|
||||
You might want to archive stuff offline before wiping it:
|
||||
|
||||
```
|
||||
blurp archive
|
||||
```
|
||||
|
||||
See `./archive` for every status (favourited, pinned, boosted, bookmarked etc.)
|
||||
connected with your account. All media attachments are downloaded alongside the
|
||||
status. Status content is dumped as a JSON file.
|
||||
|
||||
> 🔴 **DANGER ZONE** 🔴
|
||||
|
||||
To delete all statuses older than *2 weeks*:
|
||||
|
||||
```
|
||||
blurp delete
|
||||
```
|
||||
|
||||
> 🔴 **DANGER ZONE** 🔴
|
||||
|
||||
## ACK
|
||||
|
||||
Made possible by the good work of [slurp](https://github.com/VyrCossont/slurp).
|
||||
|
||||
# License
|
||||
|
||||
<a href="https://git.coopcloud.tech/decentral1se/blurp/src/branch/main/LICENSE">
|
||||
<img src="https://www.gnu.org/graphics/agplv3-with-text-162x68.png" />
|
||||
</a>
|
298
blurp.go
Normal file
298
blurp.go
Normal file
@ -0,0 +1,298 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
auth "git.coopcloud.tech/decentral1se/gtslib-auth-keyring"
|
||||
"git.coopcloud.tech/decentral1se/gtslib/client/accounts"
|
||||
"git.coopcloud.tech/decentral1se/gtslib/client/statuses"
|
||||
"git.coopcloud.tech/decentral1se/gtslib/models"
|
||||
"github.com/peterhellberg/link"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// getAccount returns the currently authenticated account.
|
||||
func getAccount(authClient *auth.Client) (*models.Account, error) {
|
||||
err := authClient.Wait()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := authClient.Client.Accounts.AccountVerify(nil, authClient.Auth)
|
||||
if err != nil {
|
||||
return nil, errors.WithStack(err)
|
||||
}
|
||||
|
||||
return resp.GetPayload(), nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
slog.Error("woops, something went wrong", "error", err)
|
||||
}
|
||||
}
|
||||
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "blurp",
|
||||
Short: "A GoToSocial status deletion tool",
|
||||
}
|
||||
|
||||
var authLoginCmd = &cobra.Command{
|
||||
Use: "login",
|
||||
Short: "Log in",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return auth.Login(user)
|
||||
},
|
||||
}
|
||||
|
||||
var archiveCmd = &cobra.Command{
|
||||
Use: "archive",
|
||||
Short: "Archive statuses",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
authClient, err := auth.NewAuthClient(user)
|
||||
if err != nil {
|
||||
slog.Error("unable to create auth client", "error", err)
|
||||
}
|
||||
|
||||
acc, err := getAccount(authClient)
|
||||
if err != nil {
|
||||
slog.Error("unable to retrieve account", "error", err)
|
||||
}
|
||||
|
||||
pagedRequester := &statusPagedRequester{accID: acc.ID}
|
||||
statuses, err := ReadAllPaged(authClient, pagedRequester)
|
||||
if err != nil {
|
||||
slog.Error("unable to download paged response", "error", err)
|
||||
}
|
||||
|
||||
basePath := filepath.Join(".", "archive")
|
||||
if err := os.MkdirAll(basePath, 0755); err != nil {
|
||||
slog.Error("unable to create status directory", "error", err)
|
||||
}
|
||||
|
||||
for _, status := range statuses {
|
||||
basePath = filepath.Join(".", "archive")
|
||||
|
||||
if len(status.MediaAttachments) > 0 {
|
||||
basePath = filepath.Join(basePath, status.ID)
|
||||
|
||||
if err := os.MkdirAll(basePath, 0755); err != nil {
|
||||
slog.Error("unable to create status directory", "error", err)
|
||||
}
|
||||
|
||||
for _, media := range status.MediaAttachments {
|
||||
parsed, err := url.Parse(media.URL)
|
||||
if err != nil {
|
||||
slog.Error("unable to parse media URL", "error", err)
|
||||
}
|
||||
|
||||
imagePath := filepath.Join(basePath, filepath.Base(parsed.Path))
|
||||
if _, err := os.Stat(imagePath); errors.Is(err, os.ErrNotExist) {
|
||||
if err := httpGetFile(imagePath, media.URL); err != nil {
|
||||
slog.Error("unable to download file", "error", err)
|
||||
}
|
||||
slog.Info(fmt.Sprintf("archived %s", imagePath))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
payload, err := json.MarshalIndent(status, "", " ")
|
||||
if err != nil {
|
||||
slog.Error("unable to marshal", "error", err)
|
||||
}
|
||||
|
||||
jsonPath := filepath.Join(basePath, fmt.Sprintf("%s.json", status.ID))
|
||||
if _, err := os.Stat(jsonPath); errors.Is(err, os.ErrNotExist) {
|
||||
if err = ioutil.WriteFile(jsonPath, payload, 0644); err != nil {
|
||||
slog.Error("unable to write JSON file", "error", err)
|
||||
}
|
||||
slog.Info(fmt.Sprintf("archived %s", jsonPath))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
// httpGetFile downloads a file from the internet.
|
||||
func httpGetFile(filepath, url string) error {
|
||||
out, err := os.Create(filepath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("httpGetFile: unable to create '%s': %s", filepath, err)
|
||||
}
|
||||
defer out.Close()
|
||||
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
return fmt.Errorf("httpGetFile: unable to HTTP GET '%s'", url)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("httpGetFile: HTTP GET response code %v for '%s'", resp.StatusCode, url)
|
||||
}
|
||||
|
||||
_, err = io.Copy(out, resp.Body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("httpGetFile: unable to copy HTTP GET response to disk: %s", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type PagedRequester[Response PagedResponse[Element], Element any] interface {
|
||||
Request(authClient *auth.Client, maxID *string) (Response, error)
|
||||
}
|
||||
|
||||
type PagedResponse[Element any] interface {
|
||||
Link() string
|
||||
Elements() []Element
|
||||
}
|
||||
|
||||
// ParseLinkMaxID extracts the `max_id` from the `next` link for paging to older items.
|
||||
func ParseLinkMaxID(linkHeader string) (*string, error) {
|
||||
next := link.Parse(linkHeader)["next"]
|
||||
if next == nil {
|
||||
// No link header in that direction means end of results.
|
||||
return nil, nil
|
||||
}
|
||||
nextUrl, err := url.Parse(next.URI)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "couldn't parse next page URL")
|
||||
}
|
||||
nextMaxID := nextUrl.Query().Get("max_id")
|
||||
if nextMaxID == "" {
|
||||
return nil, errors.New("couldn't find next page max ID")
|
||||
}
|
||||
return &nextMaxID, err
|
||||
}
|
||||
|
||||
func ReadAllPaged[
|
||||
Requester PagedRequester[Response, Element],
|
||||
Response PagedResponse[Element], Element any](
|
||||
authClient *auth.Client, pagedRequester Requester) ([]Element, error) {
|
||||
var all []Element
|
||||
var maxID *string
|
||||
|
||||
for {
|
||||
err := authClient.Wait()
|
||||
if err != nil {
|
||||
return all, errors.WithStack(err)
|
||||
}
|
||||
|
||||
pagedResponse, err := pagedRequester.Request(authClient, maxID)
|
||||
if err != nil {
|
||||
slog.Error("error fetching page", "error", err)
|
||||
return all, errors.WithStack(err)
|
||||
}
|
||||
|
||||
maxID, err = ParseLinkMaxID(pagedResponse.Link())
|
||||
if err != nil {
|
||||
slog.Error("error parsing Link header", "error", err)
|
||||
return all, errors.WithStack(err)
|
||||
}
|
||||
if maxID == nil {
|
||||
// End of pages.
|
||||
break
|
||||
}
|
||||
|
||||
all = append(all, pagedResponse.Elements()...)
|
||||
}
|
||||
|
||||
return all, nil
|
||||
}
|
||||
|
||||
type statusPagedRequester struct {
|
||||
accID string
|
||||
}
|
||||
|
||||
type statusPagedResponse struct {
|
||||
resp *accounts.AccountStatusesOK
|
||||
}
|
||||
|
||||
func (pagedResponse *statusPagedResponse) Link() string {
|
||||
return pagedResponse.resp.Link
|
||||
}
|
||||
|
||||
func (pagedResponse *statusPagedResponse) Elements() []*models.Status {
|
||||
return pagedResponse.resp.GetPayload()
|
||||
}
|
||||
|
||||
func (pagedRequester *statusPagedRequester) Request(
|
||||
authClient *auth.Client, maxID *string) (*statusPagedResponse, error) {
|
||||
resp, err := authClient.Client.Accounts.AccountStatuses(&accounts.AccountStatusesParams{
|
||||
ID: pagedRequester.accID,
|
||||
MaxID: maxID,
|
||||
}, authClient.Auth)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &statusPagedResponse{resp}, nil
|
||||
}
|
||||
|
||||
var deleteCmd = &cobra.Command{
|
||||
Use: "delete",
|
||||
Short: "Delete statuses like tears in the rain",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
authClient, err := auth.NewAuthClient(user)
|
||||
if err != nil {
|
||||
slog.Error("unable to create auth client", err)
|
||||
}
|
||||
|
||||
acc, err := getAccount(authClient)
|
||||
if err != nil {
|
||||
slog.Error("unable to retrieve account", err)
|
||||
}
|
||||
|
||||
pagedRequester := &statusPagedRequester{accID: acc.ID}
|
||||
allStatuses, err := ReadAllPaged(authClient, pagedRequester)
|
||||
if err != nil {
|
||||
slog.Error("unable to download paged response", err)
|
||||
}
|
||||
|
||||
ISO8601 := "2006-01-02T15:04:05.000Z"
|
||||
for _, status := range allStatuses {
|
||||
t, err := time.Parse(ISO8601, status.CreatedAt)
|
||||
if err != nil {
|
||||
slog.Error("unable to parse status 'CreatedAt' value", err)
|
||||
}
|
||||
|
||||
// NOTE(d1): 336 hours = 2 weeks
|
||||
if t.Before(time.Now().Add(-time.Hour * 336)) {
|
||||
_, err := authClient.Client.Statuses.StatusDelete(&statuses.StatusDeleteParams{
|
||||
ID: status.ID,
|
||||
}, authClient.Auth)
|
||||
if err != nil {
|
||||
slog.Error("unable to delete status", err)
|
||||
}
|
||||
slog.Info(fmt.Sprintf("deleted %s (created: %s)", status.ID, t.Format(time.DateOnly)))
|
||||
} else {
|
||||
slog.Info(fmt.Sprintf("keeping %s (created: %s)", status.ID, t.Format(time.DateOnly)))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var user string
|
||||
|
||||
func init() {
|
||||
rootCmd.PersistentFlags().StringVarP(
|
||||
&user, "user", "u", "", "username@domain of account",
|
||||
)
|
||||
|
||||
rootCmd.AddCommand(authLoginCmd)
|
||||
rootCmd.AddCommand(archiveCmd)
|
||||
rootCmd.AddCommand(deleteCmd)
|
||||
}
|
50
go.mod
Normal file
50
go.mod
Normal file
@ -0,0 +1,50 @@
|
||||
module git.coopcloud.tech/decentral1se/blurp
|
||||
|
||||
go 1.21.1
|
||||
|
||||
require (
|
||||
git.coopcloud.tech/decentral1se/gtslib v0.0.0-20240731163334-5a7b6157993c
|
||||
git.coopcloud.tech/decentral1se/gtslib-auth-keyring v0.0.0-20240731165536-91d7360746a5
|
||||
github.com/peterhellberg/link v1.2.0
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/spf13/cobra v1.8.1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/adrg/xdg v0.5.0 // indirect
|
||||
github.com/alessio/shellescape v1.4.2 // indirect
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
|
||||
github.com/danieljoos/wincred v1.2.2 // indirect
|
||||
github.com/go-logr/logr v1.4.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/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/godbus/dbus/v5 v5.1.0 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/oklog/ulid v1.3.1 // indirect
|
||||
github.com/opentracing/opentracing-go v1.2.0 // indirect
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/zalando/go-keyring v0.2.5 // indirect
|
||||
go.mongodb.org/mongo-driver v1.16.0 // indirect
|
||||
go.opentelemetry.io/otel v1.28.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.28.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.28.0 // indirect
|
||||
golang.org/x/sync v0.7.0 // indirect
|
||||
golang.org/x/sys v0.22.0 // indirect
|
||||
golang.org/x/time v0.5.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
webfinger.net/go/webfinger v0.1.0 // indirect
|
||||
)
|
111
go.sum
Normal file
111
go.sum
Normal file
@ -0,0 +1,111 @@
|
||||
git.coopcloud.tech/decentral1se/gtslib v0.0.0-20240731163334-5a7b6157993c h1:hi7jaEF5J5zEFurV2N5P1JTIPMtDgMTeQVis8b1J6Uk=
|
||||
git.coopcloud.tech/decentral1se/gtslib v0.0.0-20240731163334-5a7b6157993c/go.mod h1:isFkvbxjltfWUlzdVETbupmKLZvDf5XviBkgd2/+A5M=
|
||||
git.coopcloud.tech/decentral1se/gtslib-auth-keyring v0.0.0-20240731165536-91d7360746a5 h1:/enn25rJ8hfI15YHd/ATJpOGTsb62uVnQWK//KYFz6c=
|
||||
git.coopcloud.tech/decentral1se/gtslib-auth-keyring v0.0.0-20240731165536-91d7360746a5/go.mod h1:fWMf/Wlc6Qjosg4xoaJg0UOb9GHoP7GrlIXz38wZAws=
|
||||
github.com/adrg/xdg v0.5.0 h1:dDaZvhMXatArP1NPHhnfaQUqWBLBsmx1h1HXQdMoFCY=
|
||||
github.com/adrg/xdg v0.5.0/go.mod h1:dDdY4M4DF9Rjy4kHPeNL+ilVF+p2lK8IdM9/rTSGcI4=
|
||||
github.com/alessio/shellescape v1.4.2 h1:MHPfaU+ddJ0/bYWpgIeUnQUqKrlJ1S7BfEYPM4uEoM0=
|
||||
github.com/alessio/shellescape v1.4.2/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30=
|
||||
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/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/danieljoos/wincred v1.2.2 h1:774zMFJrqaeYCK2W57BgAem/MLi6mtSE47MB6BOJ0i0=
|
||||
github.com/danieljoos/wincred v1.2.2/go.mod h1:w7w4Utbrz8lqeMbDAK0lkNJUv5sAOkFi7nd/ogr0Uh8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-openapi/analysis v0.23.0 h1:aGday7OWupfMs+LbmLZG4k0MYXIANxcuBTYUC03zFCU=
|
||||
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/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/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
|
||||
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
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/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
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/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
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/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
|
||||
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
|
||||
github.com/peterhellberg/link v1.2.0 h1:UA5pg3Gp/E0F2WdX7GERiNrPQrM1K6CVJUUWfHa4t6c=
|
||||
github.com/peterhellberg/link v1.2.0/go.mod h1:gYfAh+oJgQu2SrZHg5hROVRQe1ICoK0/HHJTcE0edxc=
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
|
||||
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
|
||||
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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
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/zalando/go-keyring v0.2.5 h1:Bc2HHpjALryKD62ppdEzaFG6VxL6Bc+5v0LYpN8Lba8=
|
||||
github.com/zalando/go-keyring v0.2.5/go.mod h1:HL4k+OXQfJUWaMnqyuSOc0drfGPX2b51Du6K+MRgZMk=
|
||||
go.mongodb.org/mongo-driver v1.16.0 h1:tpRsfBJMROVHKpdGyc1BBEzzjDUWjItxbVSZ8Ls4BQ4=
|
||||
go.mongodb.org/mongo-driver v1.16.0/go.mod h1:oB6AhJQvFQL4LEHyXi6aJzQJtBiTQHiAd83l0GdFaiw=
|
||||
go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo=
|
||||
go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4=
|
||||
go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q=
|
||||
go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s=
|
||||
go.opentelemetry.io/otel/sdk v1.24.0 h1:YMPPDNymmQN3ZgczicBY3B6sf9n62Dlj9pWD3ucgoDw=
|
||||
go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg=
|
||||
go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g=
|
||||
go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI=
|
||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
|
||||
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
webfinger.net/go/webfinger v0.1.0 h1:e/J18UgjFE8+ZbKxzKm4+gv4ehidNnF6hcbHwS3K63U=
|
||||
webfinger.net/go/webfinger v0.1.0/go.mod h1:+najbdnIKfnKo68tU2TF+AXm8/MOqLYXqx22j8Xw7FM=
|
Loading…
x
Reference in New Issue
Block a user