merge upstream
This commit is contained in:
@@ -332,17 +332,17 @@ Notes on 64-bit CPU feature requirements:
|
||||
|
||||
- ARM64 requires no specific features, ARMv8 CPUs (and later) have all required features.
|
||||
|
||||
If any of the above features are missing, performance of media processing (and possibly, SQLite) will suffer. In these situations, you may have some success building a binary yourself with the totally **unsupported, experimental** [nowasm](https://docs.gotosocial.org/en/latest/advanced/builds/nowasm/) tag.
|
||||
If any of the above features are missing, performance of media processing will suffer. In these situations, you may have some success building a binary yourself with the totally **unsupported, experimental** [nowasm](https://docs.gotosocial.org/en/latest/advanced/builds/nowasm/) tag.
|
||||
|
||||
#### BSDs
|
||||
|
||||
Mostly works, just [a few things to keep in mind](https://github.com/ncruces/go-sqlite3/wiki/Support-matrix) with WASM SQLite; check release notes carefully when installing on NetBSD or FreeBSD. If running with Postgres you should have no issues.
|
||||
Mostly works, just [a few things to keep in mind](https://github.com/ncruces/go-sqlite3/wiki/Support-matrix) with SQLite; check release notes carefully when installing on NetBSD or FreeBSD. If running with Postgres you should have no issues, or alternatively you can build with the `moderncsqlite3` build-tag to build with https://gitlab.com/cznic/sqlite instead.
|
||||
|
||||
#### 32-bit
|
||||
|
||||
GtS doesn't work well on 32-bit systems like i386, or Armv6/v7, mainly due to performance of media decoding.
|
||||
|
||||
We don't recommend running GtS on 32-bit, but you may have some success either turning off remote media processing altogether, or building a binary yourself with the totally **unsupported, experimental** [nowasm](https://docs.gotosocial.org/en/latest/advanced/builds/nowasm/) tag.
|
||||
We don't recommend running GtS on 32-bit, but you may have some success either turning off remote media processing altogether, or building a binary yourself with the totally **unsupported, experimental** [nowasm](https://docs.gotosocial.org/en/latest/advanced/builds/nowasm/) tag. There are still [a few things to keep in mind](https://github.com/ncruces/go-sqlite3/wiki/Support-matrix) with SQLite on non-Linux platforms, or alternatively you can build with the `moderncsqlite3` build-tag to build with https://gitlab.com/cznic/sqlite instead.
|
||||
|
||||
For more guidance, check release notes when trying to install on 32-bit.
|
||||
|
||||
@@ -350,7 +350,7 @@ For more guidance, check release notes when trying to install on 32-bit.
|
||||
|
||||
Marked as unsupported due to performance issues (no WASM compiler, high memory usage when idle, crashes while processing media).
|
||||
|
||||
While we don't support running GtS on OpenBSD, you may have some success building a binary yourself with the totally **unsupported, experimental** [nowasm](https://docs.gotosocial.org/en/latest/advanced/builds/nowasm/) tag.
|
||||
While we don't support running GtS on OpenBSD, you may have some success building a binary yourself with the totally **unsupported, experimental** [nowasm](https://docs.gotosocial.org/en/latest/advanced/builds/nowasm/) tag. There are still [a few things to keep in mind](https://github.com/ncruces/go-sqlite3/wiki/Support-matrix) with SQLite, or alternatively you can build with the `moderncsqlite3` build-tag to build with https://gitlab.com/cznic/sqlite instead.
|
||||
|
||||
### Stable Releases
|
||||
|
||||
|
||||
@@ -2,21 +2,22 @@
|
||||
|
||||
things noted down by the maintainers that could do with being done!
|
||||
|
||||
## database chores
|
||||
- tidy up db.GetAccountStatuses() (separate functions perhaps?)
|
||||
- update remaining database queries / API endpoints to use paging.Page{}
|
||||
- add migrations to convert edits, accounts (, etc) flags to bitsets
|
||||
- drop unnecessary 'updated_at' columns
|
||||
- replace 'created_at' columns with time parsed from ULIDs (where possible)
|
||||
- replace ULID columns with binary representation
|
||||
- replace "upsert" queries with more performant alternatives (these do a lot of runtime logic which could be determined ahead of time)
|
||||
## database
|
||||
- [chore] tidy up db.GetAccountStatuses() (separate functions perhaps?)
|
||||
- [chore] update remaining database queries / API endpoints to use paging.Page{}
|
||||
- [space] add migrations to convert edits, accounts (, etc) flags to bitsets
|
||||
- [space] drop unnecessary 'updated_at' columns
|
||||
- [space] replace 'created_at' columns with time parsed from ULIDs (where possible)
|
||||
- [performance] replace ULID columns with binary representation
|
||||
- [performance] replace "upsert" queries with more performant alternatives (these do a lot of runtime logic which could be determined ahead of time)
|
||||
- [performance] add multi-delete queries for mentions, statuses (+boosts), etc
|
||||
|
||||
## miscellaneous chores
|
||||
- update to Go 1.25 (we're now being held back from dependency updates)
|
||||
- finish code commenting where missing (search for '// (\w+\b)?...')
|
||||
- move away from using Gin, they're all-in on "AI", blegh
|
||||
|
||||
## performance
|
||||
- kim: update ffmpreg to use ncruces/wasm2go (?)
|
||||
- kim: write alternative bundb serializing backend (?)
|
||||
- kim: fork bundb to support not always going through "database/sql" (?)
|
||||
## miscellaneous
|
||||
- [chore] move dereferencer hooks to Ingester{} type that handles calling appropriate hooks for any ingested model
|
||||
- [chore] finish code commenting where missing (search for '// (\w+\b)?...')
|
||||
- [chore] move away from using Gin, they're all-in on "AI", blegh
|
||||
- [chore] deinterface the database somehow (where possible given dependency cycling 😭), have a single DB type so all bundb/*.go can access all other internal funcs
|
||||
- kim: [chore/docs] update support matrix given go-sqlite3 dropped wazero usage, (potentially) remove modernc.org/sqlite dependency
|
||||
- kim: [supported platforms] update ffmpreg to use ncruces/wasm2go (?)
|
||||
- kim: [performance] write alternative bundb serializing backend (?)
|
||||
- kim: [performance] fork bundb to support not always going through "database/sql" (?)
|
||||
|
||||
+13
-7
@@ -15,10 +15,11 @@
|
||||
// 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 prune
|
||||
package media
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"code.superseriousbusiness.org/gopkg/log"
|
||||
"code.superseriousbusiness.org/gotosocial/cmd/gotosocial/action"
|
||||
@@ -27,10 +28,11 @@ import (
|
||||
)
|
||||
|
||||
// check function conformance.
|
||||
var _ action.GTSAction = All
|
||||
var _ action.GTSAction = PruneAll
|
||||
|
||||
// PruneAll performs all media clean actions
|
||||
func PruneAll(ctx context.Context) error {
|
||||
|
||||
// All performs all media clean actions
|
||||
func All(ctx context.Context) error {
|
||||
// Setup pruning utilities.
|
||||
prune, err := setupPrune(ctx)
|
||||
if err != nil {
|
||||
@@ -49,11 +51,15 @@ func All(ctx context.Context) error {
|
||||
ctx = gtscontext.SetDryRun(ctx)
|
||||
}
|
||||
|
||||
days := config.GetMediaRemoteCacheDays()
|
||||
// Current time.
|
||||
now := time.Now()
|
||||
|
||||
// Get media maximum remote cache duration.
|
||||
dur := config.GetMediaRemoteCacheDuration()
|
||||
|
||||
// Perform the actual pruning with logging.
|
||||
prune.cleaner.Media().AllAndFix(ctx, days)
|
||||
prune.cleaner.Emoji().AllAndFix(ctx, days)
|
||||
prune.cleaner.Media().AllAndFix(ctx, now, dur)
|
||||
prune.cleaner.Emoji().AllAndFix(ctx, now, dur)
|
||||
|
||||
return nil
|
||||
}
|
||||
+22
-29
@@ -15,56 +15,51 @@
|
||||
// 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 prune
|
||||
package media
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"code.superseriousbusiness.org/gotosocial/internal/cleaner"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/db"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/db/bundb"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/gtserror"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/media"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/state"
|
||||
gtsstorage "code.superseriousbusiness.org/gotosocial/internal/storage"
|
||||
)
|
||||
|
||||
type prune struct {
|
||||
dbService db.DB
|
||||
storage *gtsstorage.Driver
|
||||
manager *media.Manager
|
||||
cleaner *cleaner.Cleaner
|
||||
state *state.State
|
||||
manager *media.Manager
|
||||
cleaner *cleaner.Cleaner
|
||||
state *state.State
|
||||
}
|
||||
|
||||
func setupPrune(ctx context.Context) (*prune, error) {
|
||||
var state state.State
|
||||
|
||||
state.Caches.Init()
|
||||
if err := state.Caches.Start(); err != nil {
|
||||
|
||||
err := state.Caches.Start()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error starting caches: %w", err)
|
||||
}
|
||||
|
||||
// Set state DB connection.
|
||||
// Don't need Actions for this.
|
||||
state.DB, err = bundb.NewBunDBService(ctx, &state)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error starting database: %w", err)
|
||||
}
|
||||
|
||||
// Scheduler is required for the
|
||||
// cleaner, but no other workers
|
||||
// are needed for this CLI action.
|
||||
state.Workers.StartScheduler()
|
||||
|
||||
// Set state DB connection.
|
||||
// Don't need Actions for this.
|
||||
dbService, err := bundb.NewBunDBService(ctx, &state)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating dbservice: %w", err)
|
||||
}
|
||||
state.DB = dbService
|
||||
|
||||
//nolint:contextcheck
|
||||
storage, err := gtsstorage.AutoConfig()
|
||||
state.Storage, err = gtsstorage.AutoConfig()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating storage backend: %w", err)
|
||||
}
|
||||
state.Storage = storage
|
||||
|
||||
//nolint:contextcheck
|
||||
manager := media.NewManager(&state)
|
||||
@@ -73,23 +68,21 @@ func setupPrune(ctx context.Context) (*prune, error) {
|
||||
cleaner := cleaner.New(&state)
|
||||
|
||||
return &prune{
|
||||
dbService: dbService,
|
||||
storage: storage,
|
||||
manager: manager,
|
||||
cleaner: cleaner,
|
||||
state: &state,
|
||||
manager: manager,
|
||||
cleaner: cleaner,
|
||||
state: &state,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *prune) shutdown() error {
|
||||
errs := gtserror.NewMultiError(2)
|
||||
var err error
|
||||
|
||||
if err := p.dbService.Close(); err != nil {
|
||||
errs.Appendf("error stopping database: %w", err)
|
||||
if err = p.state.DB.Close(); err != nil {
|
||||
err = fmt.Errorf("error stopping database: %w", err)
|
||||
}
|
||||
|
||||
p.state.Workers.Scheduler.Stop()
|
||||
p.state.Caches.Stop()
|
||||
|
||||
return errs.Combine()
|
||||
return err
|
||||
}
|
||||
+6
-5
@@ -15,7 +15,7 @@
|
||||
// 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 prune
|
||||
package media
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -27,10 +27,11 @@ import (
|
||||
)
|
||||
|
||||
// check function conformance.
|
||||
var _ action.GTSAction = Orphaned
|
||||
var _ action.GTSAction = PruneOrphaned
|
||||
|
||||
// PruneOrphaned prunes orphaned media from storage.
|
||||
func PruneOrphaned(ctx context.Context) error {
|
||||
|
||||
// Orphaned prunes orphaned media from storage.
|
||||
func Orphaned(ctx context.Context) error {
|
||||
// Setup pruning utilities.
|
||||
prune, err := setupPrune(ctx)
|
||||
if err != nil {
|
||||
@@ -53,7 +54,7 @@ func Orphaned(ctx context.Context) error {
|
||||
prune.cleaner.Media().LogPruneOrphaned(ctx)
|
||||
|
||||
// Perform a cleanup of storage (for removed local dirs).
|
||||
if err := prune.storage.Storage.Clean(ctx); err != nil {
|
||||
if err := prune.state.Storage.Storage.Clean(ctx); err != nil {
|
||||
log.Error(ctx, "error cleaning storage: %v", err)
|
||||
}
|
||||
|
||||
+9
-7
@@ -15,7 +15,7 @@
|
||||
// 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 prune
|
||||
package media
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -28,10 +28,11 @@ import (
|
||||
)
|
||||
|
||||
// check function conformance.
|
||||
var _ action.GTSAction = Remote
|
||||
var _ action.GTSAction = PruneRemote
|
||||
|
||||
// PruneRemote prunes old and/or unused remote media.
|
||||
func PruneRemote(ctx context.Context) error {
|
||||
|
||||
// Remote prunes old and/or unused remote media.
|
||||
func Remote(ctx context.Context) error {
|
||||
// Setup pruning utilities.
|
||||
prune, err := setupPrune(ctx)
|
||||
if err != nil {
|
||||
@@ -50,14 +51,15 @@ func Remote(ctx context.Context) error {
|
||||
ctx = gtscontext.SetDryRun(ctx)
|
||||
}
|
||||
|
||||
t := time.Now().Add(-24 * time.Hour * time.Duration(config.GetMediaRemoteCacheDays()))
|
||||
// Get media remote cache duration as an "olderThan" time.
|
||||
olderThan := config.GetMediaRemoteCacheOlderThanTime(time.Now())
|
||||
|
||||
// Perform the actual pruning with logging.
|
||||
prune.cleaner.Media().LogPruneUnused(ctx)
|
||||
prune.cleaner.Media().LogUncacheRemote(ctx, t)
|
||||
prune.cleaner.Media().LogUncacheRemote(ctx, olderThan)
|
||||
|
||||
// Perform a cleanup of storage (for removed local dirs).
|
||||
if err := prune.storage.Storage.Clean(ctx); err != nil {
|
||||
if err := prune.state.Storage.Storage.Clean(ctx); err != nil {
|
||||
log.Error(ctx, "error cleaning storage: %v", err)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
// 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 statuses
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"code.superseriousbusiness.org/gopkg/log"
|
||||
"code.superseriousbusiness.org/gotosocial/cmd/gotosocial/action"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/cleaner"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/config"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/db/bundb"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/state"
|
||||
)
|
||||
|
||||
// check function conformance.
|
||||
var _ action.GTSAction = PruneLeafStubs
|
||||
var _ action.GTSAction = PruneOldRemote
|
||||
|
||||
func PruneLeafStubs(ctx context.Context) error {
|
||||
return do(ctx, func(p *pruner) error {
|
||||
olderThan := time.Now().Add(-7 * 24 * time.Hour)
|
||||
p.cleaner.Status().LogPruneLeafStubs(ctx, olderThan, 0)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func PruneOldRemote(ctx context.Context) error {
|
||||
return do(ctx, func(p *pruner) error {
|
||||
_, dur := config.GetStatusesCleanupRemoteOlderThan().Duration()
|
||||
if dur == 0 {
|
||||
return fmt.Errorf("%s = 0, no statuses to cleanup", config.StatusesCleanupRemoteOlderThanFlag)
|
||||
}
|
||||
olderThan := time.Now().Add(-dur)
|
||||
p.cleaner.Status().LogPruneOldRemote(ctx, olderThan, 0)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
type pruner struct {
|
||||
state *state.State
|
||||
cleaner *cleaner.Cleaner
|
||||
}
|
||||
|
||||
func do(ctx context.Context, do func(*pruner) error) error {
|
||||
var state state.State
|
||||
|
||||
defer func() {
|
||||
if state.DB != nil {
|
||||
// Lastly, if database service was started,
|
||||
// ensure it gets closed now all else stopped.
|
||||
if err := state.DB.Close(); err != nil {
|
||||
log.Errorf(ctx, "error stopping database: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Finally reached end of shutdown.
|
||||
log.Info(ctx, "done! exiting...")
|
||||
}()
|
||||
|
||||
// Initialize caches
|
||||
state.Caches.Init()
|
||||
err := state.Caches.Start()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error starting caches: %w", err)
|
||||
}
|
||||
|
||||
log.Info(ctx, "starting db service...")
|
||||
|
||||
// Open conn to database now caches are started.
|
||||
state.DB, err = bundb.NewBunDBService(ctx, &state)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating dbservice: %w", err)
|
||||
}
|
||||
|
||||
// Create common receiver type.
|
||||
cleaner := cleaner.New(&state)
|
||||
pruner := &pruner{&state, cleaner}
|
||||
|
||||
// Perform provided CLI function.
|
||||
if err := do(pruner); err != nil {
|
||||
return fmt.Errorf("error performing action: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
// 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 database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"code.superseriousbusiness.org/gopkg/log"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/db/bundb"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/state"
|
||||
"github.com/uptrace/bun"
|
||||
)
|
||||
|
||||
func Ping(ctx context.Context) error {
|
||||
return do(ctx, func(db *bun.DB) error {
|
||||
log.Info(ctx, "ping")
|
||||
return db.PingContext(ctx)
|
||||
})
|
||||
}
|
||||
|
||||
func do(ctx context.Context, do func(db *bun.DB) error) error {
|
||||
var state state.State
|
||||
|
||||
defer func() {
|
||||
if state.DB != nil {
|
||||
// Lastly, if database service was started,
|
||||
// ensure it gets closed now all else stopped.
|
||||
if err := state.DB.Close(); err != nil {
|
||||
log.Errorf(ctx, "error stopping database: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Finally reached end of shutdown.
|
||||
log.Info(ctx, "done! exiting...")
|
||||
}()
|
||||
|
||||
// Initialize caches
|
||||
state.Caches.Init()
|
||||
if err := state.Caches.Start(); err != nil {
|
||||
return fmt.Errorf("error starting caches: %w", err)
|
||||
}
|
||||
|
||||
log.Info(ctx, "starting db service...")
|
||||
|
||||
// Open conn to database now caches started.
|
||||
db, err := bundb.NewBunDBService(ctx, &state)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating dbservice: %w", err)
|
||||
}
|
||||
|
||||
// Add hook to log executed queries.
|
||||
bundb := db.(*bundb.DBService).DB()
|
||||
bundb = bundb.WithQueryHook(queryHook{})
|
||||
|
||||
// Perform the provided db function.
|
||||
if err := do(bundb); err != nil {
|
||||
return fmt.Errorf("error executing query: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type queryHook struct{}
|
||||
|
||||
func (h queryHook) BeforeQuery(ctx context.Context, ev *bun.QueryEvent) context.Context {
|
||||
log.InfoKV(ctx, "query", ev.Query)
|
||||
return ctx
|
||||
}
|
||||
|
||||
func (h queryHook) AfterQuery(_ context.Context, _ *bun.QueryEvent) {}
|
||||
@@ -0,0 +1,54 @@
|
||||
// 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/>.
|
||||
|
||||
//go:build !nosqlite
|
||||
|
||||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"code.superseriousbusiness.org/gotosocial/cmd/gotosocial/action"
|
||||
"github.com/uptrace/bun"
|
||||
"github.com/uptrace/bun/dialect"
|
||||
)
|
||||
|
||||
// check function conformance.
|
||||
var _ action.GTSAction = SQLiteAnalyze
|
||||
|
||||
// var _ action.GTSAction = SQLiteVacuum
|
||||
|
||||
func SQLiteAnalyze(ctx context.Context) error {
|
||||
return do(ctx, func(db *bun.DB) error {
|
||||
if db.Dialect().Name() != dialect.SQLite {
|
||||
return errors.New("cannot perform sqlite analyze on non-sqlite db")
|
||||
}
|
||||
_, err := db.ExecContext(ctx, "analyze;")
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
// func SQLiteVacuum(ctx context.Context) error {
|
||||
// return do(ctx, func(db *bun.DB) error {
|
||||
// if db.Dialect().Name() != dialect.SQLite {
|
||||
// return errors.New("cannot perform sqlite vacuum on non-sqlite db")
|
||||
// }
|
||||
// _, err := db.ExecContext(ctx, "vacuum;")
|
||||
// return err
|
||||
// })
|
||||
// }
|
||||
@@ -58,7 +58,7 @@ func Run(ctx context.Context) error {
|
||||
// Open connection to the database now caches started.
|
||||
dbService, err := bundb.NewBunDBService(ctx, &state)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating dbservice: %s", err)
|
||||
return fmt.Errorf("error creating dbservice: %w", err)
|
||||
}
|
||||
|
||||
// Set DB on state.
|
||||
|
||||
@@ -43,6 +43,7 @@ import (
|
||||
"code.superseriousbusiness.org/gotosocial/internal/federation/federatingdb"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/filter/interaction"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/filter/mutes"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/filter/relay"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/filter/spam"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/filter/status"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/filter/visibility"
|
||||
@@ -77,7 +78,7 @@ var _ action.GTSAction = Start
|
||||
// Maintenance starts and creates a GoToSocial server
|
||||
// in maintenance mode (returns 503 for most requests).
|
||||
func Maintenance(ctx context.Context) error {
|
||||
route, err := router.New(ctx)
|
||||
route, err := router.New(ctx, router.Config{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating maintenance router: %w", err)
|
||||
}
|
||||
@@ -174,8 +175,8 @@ func Start(ctx context.Context) error {
|
||||
|
||||
var err error
|
||||
|
||||
// Create maintenance router.
|
||||
route, err = router.New(ctx)
|
||||
// Create maintenance router without config.
|
||||
route, err = router.New(ctx, router.Config{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating maintenance router: %w", err)
|
||||
}
|
||||
@@ -248,8 +249,17 @@ func Start(ctx context.Context) error {
|
||||
client := httpclient.New(httpclient.Config{
|
||||
AllowRanges: ranges.allow,
|
||||
BlockRanges: ranges.block,
|
||||
Timeout: config.GetHTTPClientTimeout(),
|
||||
TLSInsecureSkipVerify: config.GetHTTPClientTLSInsecureSkipVerify(),
|
||||
DisableKeepAlives: config.GetHTTPClientDisableKeepAlives(),
|
||||
MaxIdleConns: config.GetHTTPClientMaxIdleConns(),
|
||||
MaxIdleConnsPerHost: config.GetHTTPClientMaxIdleConnsPerHost(),
|
||||
MaxConnsPerHost: config.GetHTTPClientMaxConnsPerHost(),
|
||||
IdleConnTimeout: config.GetHTTPClientIdleConnTimeout(),
|
||||
TLSHandshakeTimeout: config.GetHTTPClientTLSHandshakeTimeout(),
|
||||
ResponseHeaderTimeout: config.GetHTTPClientResponseHeaderTimeout(),
|
||||
ReadBufferSize: int(config.GetHTTPClientReadBufferSize()), // nolint:gosec
|
||||
WriteBufferSize: int(config.GetHTTPClientWriteBufferSize()), // nolint:gosec
|
||||
Timeout: config.GetHTTPClientTimeout(),
|
||||
})
|
||||
|
||||
// Compile WASM modules ahead of first use
|
||||
@@ -279,6 +289,7 @@ func Start(ctx context.Context) error {
|
||||
intFilter := interaction.NewFilter(state)
|
||||
statusFilter := status.NewFilter(state)
|
||||
spamFilter := spam.NewFilter(state)
|
||||
relayFilter := relay.NewFilter(state)
|
||||
federatingDB := federatingdb.New(state, typeConverter, visFilter, intFilter, spamFilter)
|
||||
transportController := transport.NewController(state, federatingDB, client)
|
||||
federator := federation.NewFederator(
|
||||
@@ -288,6 +299,7 @@ func Start(ctx context.Context) error {
|
||||
typeConverter,
|
||||
visFilter,
|
||||
intFilter,
|
||||
relayFilter,
|
||||
mediaManager,
|
||||
)
|
||||
|
||||
@@ -411,8 +423,25 @@ func Start(ctx context.Context) error {
|
||||
return fmt.Errorf("error stopping maintenance router: %w", err)
|
||||
}
|
||||
|
||||
// Instantiate the main router.
|
||||
route, err = router.New(ctx)
|
||||
// Instantiate the main router with config.
|
||||
route, err = router.New(ctx, router.Config{
|
||||
MaxMultipartMemory: int64(config.GetHTTPServerMaxMultipartMemory()), // nolint:gosec
|
||||
UseH2C: config.GetHTTPServerUseH2C(),
|
||||
ReadTimeout: config.GetHTTPServerReadTimeout(),
|
||||
ReadHeaderTimeout: config.GetHTTPServerReadHeaderTimeout(),
|
||||
WriteTimeout: config.GetHTTPServerWriteTimeout(),
|
||||
IdleTimeout: config.GetHTTPServerIdleTimeout(),
|
||||
MaxHeaderBytes: int(config.GetHTTPServerMaxHeaderBytes()), // nolint:gosec
|
||||
MaxConcurrentStreams: config.GetHTTPServerMaxConcurrentStreams(),
|
||||
MaxDecoderHeaderTableSize: int(config.GetHTTPServerMaxDecoderHeaderTableSize()), // nolint:gosec
|
||||
MaxEncoderHeaderTableSize: int(config.GetHTTPServerMaxEncoderHeaderTableSize()), // nolint:gosec
|
||||
MaxReadFrameSize: int(config.GetHTTPServerMaxReadFrameSize()), // nolint:gosec
|
||||
MaxReceiveBufferPerConnection: int(config.GetHTTPServerMaxReceiveBufferPerConnection()), // nolint:gosec
|
||||
MaxReceiveBufferPerStream: int(config.GetHTTPServerMaxReceiveBufferPerStream()), // nolint:gosec
|
||||
SendPingTimeout: config.GetHTTPServerSendPingTimeout(),
|
||||
PingTimeout: config.GetHTTPServerPingTimeout(),
|
||||
WriteByteTimeout: config.GetHTTPServerWriteByteTimeout(),
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating main router: %s", err)
|
||||
}
|
||||
@@ -576,12 +605,10 @@ func Start(ctx context.Context) error {
|
||||
return fmt.Errorf("error filling worker queues: %w", err)
|
||||
}
|
||||
|
||||
// catch shutdown signals from the operating system
|
||||
sigs := make(chan os.Signal, 1)
|
||||
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
|
||||
sig := <-sigs // block until signal received
|
||||
log.Infof(ctx, "received signal %s, shutting down", sig)
|
||||
|
||||
// Block until
|
||||
// signal given.
|
||||
<-ctx.Done()
|
||||
log.Info(ctx, "received signal, shutting down")
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
+101
-80
@@ -20,7 +20,7 @@ package main
|
||||
import (
|
||||
"code.superseriousbusiness.org/gotosocial/cmd/gotosocial/action/admin/account"
|
||||
"code.superseriousbusiness.org/gotosocial/cmd/gotosocial/action/admin/media"
|
||||
"code.superseriousbusiness.org/gotosocial/cmd/gotosocial/action/admin/media/prune"
|
||||
"code.superseriousbusiness.org/gotosocial/cmd/gotosocial/action/admin/statuses"
|
||||
"code.superseriousbusiness.org/gotosocial/cmd/gotosocial/action/admin/trans"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/config"
|
||||
"github.com/spf13/cobra"
|
||||
@@ -36,13 +36,12 @@ func adminCommands() *cobra.Command {
|
||||
ADMIN ACCOUNT COMMANDS
|
||||
*/
|
||||
|
||||
adminAccountCmd := &cobra.Command{
|
||||
adminAccountCmd := add(adminCmd, &cobra.Command{
|
||||
Use: "account",
|
||||
Short: "admin commands related to local (this instance) accounts",
|
||||
}
|
||||
config.AddAdminAccount(adminAccountCmd)
|
||||
})
|
||||
|
||||
adminAccountCreateCmd := &cobra.Command{
|
||||
_ = add(adminAccountCmd, &cobra.Command{
|
||||
Use: "create",
|
||||
Short: "create a new local account",
|
||||
PreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
@@ -51,11 +50,11 @@ func adminCommands() *cobra.Command {
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return run(cmd.Context(), account.Create)
|
||||
},
|
||||
}
|
||||
config.AddAdminAccountCreate(adminAccountCreateCmd)
|
||||
adminAccountCmd.AddCommand(adminAccountCreateCmd)
|
||||
},
|
||||
config.AddAdminAccountCreate,
|
||||
)
|
||||
|
||||
adminAccountListCmd := &cobra.Command{
|
||||
_ = add(adminAccountCmd, &cobra.Command{
|
||||
Use: "list",
|
||||
Short: "list all existing local accounts",
|
||||
PreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
@@ -64,10 +63,9 @@ func adminCommands() *cobra.Command {
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return run(cmd.Context(), account.List)
|
||||
},
|
||||
}
|
||||
adminAccountCmd.AddCommand(adminAccountListCmd)
|
||||
})
|
||||
|
||||
adminAccountConfirmCmd := &cobra.Command{
|
||||
_ = add(adminAccountCmd, &cobra.Command{
|
||||
Use: "confirm",
|
||||
Short: "confirm an existing local account manually, thereby skipping email confirmation",
|
||||
PreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
@@ -76,11 +74,11 @@ func adminCommands() *cobra.Command {
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return run(cmd.Context(), account.Confirm)
|
||||
},
|
||||
}
|
||||
config.AddAdminAccount(adminAccountConfirmCmd)
|
||||
adminAccountCmd.AddCommand(adminAccountConfirmCmd)
|
||||
},
|
||||
config.AddAdminAccount,
|
||||
)
|
||||
|
||||
adminAccountPromoteCmd := &cobra.Command{
|
||||
_ = add(adminAccountCmd, &cobra.Command{
|
||||
Use: "promote",
|
||||
Short: "promote a local account to admin",
|
||||
PreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
@@ -89,11 +87,11 @@ func adminCommands() *cobra.Command {
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return run(cmd.Context(), account.Promote)
|
||||
},
|
||||
}
|
||||
config.AddAdminAccount(adminAccountPromoteCmd)
|
||||
adminAccountCmd.AddCommand(adminAccountPromoteCmd)
|
||||
},
|
||||
config.AddAdminAccount,
|
||||
)
|
||||
|
||||
adminAccountDemoteCmd := &cobra.Command{
|
||||
_ = add(adminAccountCmd, &cobra.Command{
|
||||
Use: "demote",
|
||||
Short: "demote a local account from admin to normal user",
|
||||
PreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
@@ -102,11 +100,11 @@ func adminCommands() *cobra.Command {
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return run(cmd.Context(), account.Demote)
|
||||
},
|
||||
}
|
||||
config.AddAdminAccount(adminAccountDemoteCmd)
|
||||
adminAccountCmd.AddCommand(adminAccountDemoteCmd)
|
||||
},
|
||||
config.AddAdminAccount,
|
||||
)
|
||||
|
||||
adminAccountDisableCmd := &cobra.Command{
|
||||
_ = add(adminAccountCmd, &cobra.Command{
|
||||
Use: "disable",
|
||||
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 {
|
||||
@@ -115,11 +113,11 @@ func adminCommands() *cobra.Command {
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return run(cmd.Context(), account.Disable)
|
||||
},
|
||||
}
|
||||
config.AddAdminAccount(adminAccountDisableCmd)
|
||||
adminAccountCmd.AddCommand(adminAccountDisableCmd)
|
||||
},
|
||||
config.AddAdminAccount,
|
||||
)
|
||||
|
||||
adminAccountEnableCmd := &cobra.Command{
|
||||
_ = add(adminAccountCmd, &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 {
|
||||
@@ -128,11 +126,11 @@ func adminCommands() *cobra.Command {
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return run(cmd.Context(), account.Enable)
|
||||
},
|
||||
}
|
||||
config.AddAdminAccount(adminAccountEnableCmd)
|
||||
adminAccountCmd.AddCommand(adminAccountEnableCmd)
|
||||
},
|
||||
config.AddAdminAccount,
|
||||
)
|
||||
|
||||
adminAccountPasswordCmd := &cobra.Command{
|
||||
_ = add(adminAccountCmd, &cobra.Command{
|
||||
Use: "password",
|
||||
Short: "set a new password for the given local account",
|
||||
PreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
@@ -141,12 +139,12 @@ func adminCommands() *cobra.Command {
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return run(cmd.Context(), account.Password)
|
||||
},
|
||||
}
|
||||
config.AddAdminAccount(adminAccountPasswordCmd)
|
||||
config.AddAdminAccountPassword(adminAccountPasswordCmd)
|
||||
adminAccountCmd.AddCommand(adminAccountPasswordCmd)
|
||||
},
|
||||
config.AddAdminAccount,
|
||||
config.AddAdminAccountPassword,
|
||||
)
|
||||
|
||||
adminAccountDisable2FACmd := &cobra.Command{
|
||||
_ = add(adminAccountCmd, &cobra.Command{
|
||||
Use: "disable-2fa",
|
||||
Short: "disable 2fa for the given local account",
|
||||
PreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
@@ -155,17 +153,15 @@ func adminCommands() *cobra.Command {
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return run(cmd.Context(), account.Disable2FA)
|
||||
},
|
||||
}
|
||||
config.AddAdminAccount(adminAccountDisable2FACmd)
|
||||
adminAccountCmd.AddCommand(adminAccountDisable2FACmd)
|
||||
|
||||
adminCmd.AddCommand(adminAccountCmd)
|
||||
},
|
||||
config.AddAdminAccount,
|
||||
)
|
||||
|
||||
/*
|
||||
ADMIN IMPORT/EXPORT COMMANDS
|
||||
*/
|
||||
|
||||
adminExportCmd := &cobra.Command{
|
||||
_ = add(adminCmd, &cobra.Command{
|
||||
Use: "export",
|
||||
Short: "export data from the database to file at the given path",
|
||||
PreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
@@ -174,11 +170,11 @@ func adminCommands() *cobra.Command {
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return run(cmd.Context(), trans.Export)
|
||||
},
|
||||
}
|
||||
config.AddAdminTrans(adminExportCmd)
|
||||
adminCmd.AddCommand(adminExportCmd)
|
||||
},
|
||||
config.AddAdminTrans,
|
||||
)
|
||||
|
||||
adminImportCmd := &cobra.Command{
|
||||
_ = add(adminCmd, &cobra.Command{
|
||||
Use: "import",
|
||||
Short: "import data from a file into the database",
|
||||
PreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
@@ -187,24 +183,24 @@ func adminCommands() *cobra.Command {
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return run(cmd.Context(), trans.Import)
|
||||
},
|
||||
}
|
||||
config.AddAdminTrans(adminImportCmd)
|
||||
adminCmd.AddCommand(adminImportCmd)
|
||||
},
|
||||
config.AddAdminTrans,
|
||||
)
|
||||
|
||||
/*
|
||||
ADMIN MEDIA COMMANDS
|
||||
*/
|
||||
|
||||
adminMediaCmd := &cobra.Command{
|
||||
adminMediaCmd := add(adminCmd, &cobra.Command{
|
||||
Use: "media",
|
||||
Short: "admin commands related to stored media / emojis",
|
||||
}
|
||||
})
|
||||
|
||||
/*
|
||||
ADMIN MEDIA LIST COMMANDS
|
||||
*/
|
||||
|
||||
adminMediaListAttachmentsCmd := &cobra.Command{
|
||||
_ = add(adminMediaCmd, &cobra.Command{
|
||||
Use: "list-attachments",
|
||||
Short: "list local, remote, or all attachments",
|
||||
PreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
@@ -213,11 +209,11 @@ func adminCommands() *cobra.Command {
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return run(cmd.Context(), media.ListAttachments)
|
||||
},
|
||||
}
|
||||
config.AddAdminMediaList(adminMediaListAttachmentsCmd)
|
||||
adminMediaCmd.AddCommand(adminMediaListAttachmentsCmd)
|
||||
},
|
||||
config.AddAdminMediaList,
|
||||
)
|
||||
|
||||
adminMediaListEmojisLocalCmd := &cobra.Command{
|
||||
_ = add(adminMediaCmd, &cobra.Command{
|
||||
Use: "list-emojis",
|
||||
Short: "list local, remote, or all emojis",
|
||||
PreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
@@ -226,60 +222,85 @@ func adminCommands() *cobra.Command {
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return run(cmd.Context(), media.ListEmojis)
|
||||
},
|
||||
}
|
||||
config.AddAdminMediaList(adminMediaListEmojisLocalCmd)
|
||||
adminMediaCmd.AddCommand(adminMediaListEmojisLocalCmd)
|
||||
},
|
||||
config.AddAdminMediaList,
|
||||
)
|
||||
|
||||
/*
|
||||
ADMIN MEDIA PRUNE COMMANDS
|
||||
*/
|
||||
adminMediaPruneCmd := &cobra.Command{
|
||||
adminMediaPruneCmd := add(adminMediaCmd, &cobra.Command{
|
||||
Use: "prune",
|
||||
Short: "admin commands for pruning media from storage",
|
||||
}
|
||||
})
|
||||
|
||||
adminMediaPruneOrphanedCmd := &cobra.Command{
|
||||
_ = add(adminMediaPruneCmd, &cobra.Command{
|
||||
Use: "orphaned",
|
||||
Short: "prune orphaned media from storage",
|
||||
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(), prune.Orphaned)
|
||||
return run(cmd.Context(), media.PruneOrphaned)
|
||||
},
|
||||
}
|
||||
config.AddAdminMediaPrune(adminMediaPruneOrphanedCmd)
|
||||
adminMediaPruneCmd.AddCommand(adminMediaPruneOrphanedCmd)
|
||||
},
|
||||
config.AddAdminMediaPrune,
|
||||
)
|
||||
|
||||
adminMediaPruneRemoteCmd := &cobra.Command{
|
||||
_ = add(adminMediaPruneCmd, &cobra.Command{
|
||||
Use: "remote",
|
||||
Short: "prune unused / stale media from storage, older than given number of days",
|
||||
Short: "prune unused / stale media from storage, older than given duration",
|
||||
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(), prune.Remote)
|
||||
return run(cmd.Context(), media.PruneRemote)
|
||||
},
|
||||
}
|
||||
config.AddAdminMediaPrune(adminMediaPruneRemoteCmd)
|
||||
adminMediaPruneCmd.AddCommand(adminMediaPruneRemoteCmd)
|
||||
},
|
||||
config.AddAdminMediaPrune,
|
||||
)
|
||||
|
||||
adminMediaPruneAllCmd := &cobra.Command{
|
||||
_ = add(adminMediaPruneCmd, &cobra.Command{
|
||||
Use: "all",
|
||||
Short: "perform all media and emoji prune / cleaning commands",
|
||||
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(), prune.All)
|
||||
return run(cmd.Context(), media.PruneAll)
|
||||
},
|
||||
}
|
||||
config.AddAdminMediaPrune(adminMediaPruneAllCmd)
|
||||
adminMediaPruneCmd.AddCommand(adminMediaPruneAllCmd)
|
||||
},
|
||||
config.AddAdminMediaPrune,
|
||||
)
|
||||
|
||||
adminMediaCmd.AddCommand(adminMediaPruneCmd)
|
||||
/*
|
||||
ADMIN STATUS COMMANDS
|
||||
*/
|
||||
|
||||
adminCmd.AddCommand(adminMediaCmd)
|
||||
adminStatusesCmd := add(adminCmd, &cobra.Command{
|
||||
Use: "statuses",
|
||||
Short: "admin commands related to stored statuses",
|
||||
})
|
||||
|
||||
/*
|
||||
ADMIN STATUS PRUNE COMMANDS
|
||||
*/
|
||||
|
||||
adminStatusesPruneCmd := add(adminStatusesCmd, &cobra.Command{
|
||||
Use: "prune",
|
||||
Short: "admin commands for pruning statuses from the database",
|
||||
})
|
||||
|
||||
_ = add(adminStatusesPruneCmd, &cobra.Command{
|
||||
Use: "remote",
|
||||
Short: "prune old, locally-not-interacted-with remote status threads from the database",
|
||||
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(), statuses.PruneOldRemote)
|
||||
},
|
||||
})
|
||||
|
||||
return adminCmd
|
||||
}
|
||||
|
||||
@@ -27,6 +27,15 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// add will add 'next' to 'base' command, returning 'next' after applying any 'flagFns'.
|
||||
func add(base, next *cobra.Command, flagFns ...func(*cobra.Command)) *cobra.Command {
|
||||
base.AddCommand(next)
|
||||
for _, flagFn := range flagFns {
|
||||
flagFn(next)
|
||||
}
|
||||
return next
|
||||
}
|
||||
|
||||
type preRunArgs struct {
|
||||
cmd *cobra.Command
|
||||
skipValidation bool
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
// 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 main
|
||||
|
||||
import (
|
||||
"code.superseriousbusiness.org/gotosocial/cmd/gotosocial/action/database"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// databaseCommands returns the 'database' subcommand.
|
||||
func databaseCommands() *cobra.Command {
|
||||
databaseCmd := &cobra.Command{
|
||||
Use: "database",
|
||||
Short: "gotosocial database-releated tasks",
|
||||
}
|
||||
|
||||
// SQLite commands only set if !nosqlite tag.
|
||||
if sqlite := sqliteCommands(); sqlite != nil {
|
||||
databaseCmd.AddCommand(sqlite)
|
||||
}
|
||||
|
||||
databaseCmd.AddCommand(&cobra.Command{
|
||||
Use: "ping",
|
||||
Short: "perform a database \"ping\"",
|
||||
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(), database.Ping)
|
||||
},
|
||||
})
|
||||
|
||||
return databaseCmd
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
// 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/>.
|
||||
|
||||
//go:build nosqlite
|
||||
|
||||
package main
|
||||
|
||||
import "github.com/spf13/cobra"
|
||||
|
||||
func sqliteCommands() *cobra.Command { return nil }
|
||||
@@ -0,0 +1,58 @@
|
||||
// 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/>.
|
||||
|
||||
//go:build !nosqlite
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"code.superseriousbusiness.org/gotosocial/cmd/gotosocial/action/database"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// sqliteCommands returns the 'sqlite' subcommand
|
||||
func sqliteCommands() *cobra.Command {
|
||||
sqliteCmd := &cobra.Command{
|
||||
Use: "sqlite",
|
||||
Short: "sqlite-specific tasks",
|
||||
}
|
||||
|
||||
// sqliteCmd.AddCommand(&cobra.Command{
|
||||
// Use: "vacuum",
|
||||
// Short: "performs an sqlite \"vacuum\" operation; " +
|
||||
// "please note this requires AT LEAST db size free disk-space",
|
||||
// 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(), database.SQLiteVacuum)
|
||||
// },
|
||||
// })
|
||||
|
||||
sqliteCmd.AddCommand(&cobra.Command{
|
||||
Use: "analyze",
|
||||
Short: "performs an sqlite \"analyze\" operation",
|
||||
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(), database.SQLiteAnalyze)
|
||||
},
|
||||
})
|
||||
|
||||
return sqliteCmd
|
||||
}
|
||||
+24
-5
@@ -18,10 +18,13 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
godebug "runtime/debug"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
_ "code.superseriousbusiness.org/gotosocial/docs"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/config"
|
||||
@@ -56,23 +59,39 @@ func main() {
|
||||
rootCmd.AddCommand(debugCommands())
|
||||
rootCmd.AddCommand(adminCommands())
|
||||
rootCmd.AddCommand(migrationCommands())
|
||||
rootCmd.AddCommand(databaseCommands())
|
||||
|
||||
// Testrigcmd will only be set when debug is enabled.
|
||||
if testrigCmd := testrigCommands(); testrigCmd != nil {
|
||||
rootCmd.AddCommand(testrigCmd)
|
||||
} else if len(os.Args) > 1 && os.Args[1] == "testrig" {
|
||||
log.Fatal("gotosocial must be built and run with the DEBUG enviroment variable set to enable and access testrig")
|
||||
panic("gotosocial must be built and run with the DEBUG enviroment variable set to enable and access testrig")
|
||||
}
|
||||
|
||||
// Run the prepared root command.
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
log.Fatalf("error executing command: %s", err)
|
||||
// Start with background ctx.
|
||||
ctx := context.Background()
|
||||
|
||||
// Setup context to cancel on signal.
|
||||
ctx, cncl := signal.NotifyContext(ctx,
|
||||
syscall.SIGTERM,
|
||||
syscall.SIGKILL,
|
||||
syscall.SIGINT,
|
||||
)
|
||||
|
||||
// Cancel ctx
|
||||
// on return.
|
||||
defer cncl()
|
||||
|
||||
// Run the prepared root command with notify context.
|
||||
if err := rootCmd.ExecuteContext(ctx); err != nil {
|
||||
panic(fmt.Sprintf("error executing command: %v", err))
|
||||
}
|
||||
}
|
||||
|
||||
// version will build a version string from binary's stored build information.
|
||||
// It is SemVer-compatible so long as Version is SemVer-compatible.
|
||||
func version() string {
|
||||
|
||||
// Read build information from binary
|
||||
build, ok := godebug.ReadBuildInfo()
|
||||
if !ok {
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
<!--what-is-a-relay-start-->
|
||||
## What is a relay?
|
||||
|
||||
An ActivityPub relay is a software that exposes one or more ActivityPub-compatible actors, which accept posts in one or more inboxes, and then forward (ie., "Announce") those posts to actors who are subscribed to (ie., "Follow") the relay actor(s).
|
||||
<!--what-is-a-relay-end-->
|
||||
|
||||
<!--relay-actor-uri-start-->
|
||||
### Relay actor URI
|
||||
|
||||
A relay actor URI usually looks something like `https://relay.example.org/actor`, but for the precise URI you should check the documentation or homepage of the relay you would like to connect to.
|
||||
<!--relay-actor-uri-end-->
|
||||
|
||||
<!--relay-matchers-start-->
|
||||
## Relay matchers
|
||||
|
||||
Using relay matchers, you can either include or exclude posts from relaying by matching keywords (whole words or partial) in the post.
|
||||
|
||||
To create a relay matcher, open the detailed view of a relay connection. Below the relay update form, you will see a "Matchers" section. Here, you can view existing matchers, create a new matcher, and/or delete an existing matcher.
|
||||
|
||||
The matcher keyword is the phrase to be matched. For example, "sloth". Matcher keywords are case insensitive, so "Sloth", "sloth", and "SLOTH" etc are all equivalent.
|
||||
|
||||
If you want to match the keyword as a whole word, tick the "Match whole word" checkbox when creating a matcher. With this checkbox ticked, the matcher "sloth" will match a post with content "I saw a cool sloth today", but will *not* match with the post "I saw some cool sloths today". By contrast, with the box unticked, any post containing a word with the fragment "sloth" in it will match. So "I saw some cool sloths today" will match.
|
||||
|
||||
Keyword matches are made against a post's content *and* its content warning (if present).
|
||||
|
||||
!!! tip "Matching hashtags"
|
||||
You can also match hashtags by prefacing a keyword with a hash symbol, so you could match all posts that use the hashtag "GoToSocial" by creating a whole-word matcher with keyword `#GoToSocial`.
|
||||
|
||||
To create an exclude matcher, tick the checkbox "Exclude posts matched by this matcher" when creating a new matcher. When a post matches an exclude matcher, it is *never* relayed, even if other matchers would include it, or the relay connection matches all posts by default.
|
||||
<!--relay-matchers-end-->
|
||||
|
||||
<!--relay-not-private-start-->
|
||||
!!! warning "Relay connections are not always private"
|
||||
When you create a relay connection, the home page for the relay may show that your instance subscribes to that relay. Before creating a relay connection, think carefully if you want your instance to have that kind of exposure on a public page.
|
||||
<!--relay-not-private-end-->
|
||||
@@ -12,44 +12,106 @@ Regardless of whether you choose to run GoToSocial with SQLite or Postgres, you
|
||||
|
||||
## 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`.
|
||||
### SQLite Vacuum
|
||||
|
||||
### 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).
|
||||
To minimize fragmentation, GoToSocial does not currently enable auto-vacuum for SQLite. To defragment the database file and repack it 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:
|
||||
To vacuum your GoToSocial database file, you must meet the following requirements:
|
||||
|
||||
- The SQLite command line tool `sqlite3` must be installed on the same machine that your GoToSocial `sqlite.db` file is stored on. For more details, see the [`sqlite3` cli docs](https://sqlite.org/cli.html).
|
||||
- You must have free disk space roughly equivalent to 2x the size of your `sqlite.db` file. Eg., if you have a 10GB database file, you should make sure you have ~20GB of free space. This space is used during the vacuum to create a temporary copy of your database file, and to populate a wal file.
|
||||
|
||||
Once you've met these requirements, do the following:
|
||||
|
||||
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.
|
||||
2. In the same directory as your GoToSocial `sqlite.db` file, run the command: `sqlite3 sqlite.db "VACUUM;"`
|
||||
This may take quite a few minutes depending on the size of your database. DO NOT INTERRUPT IT.
|
||||
3. When the command has finished running, start GoToSocial again.
|
||||
|
||||
### Replication
|
||||
### SQLite Analyze / Optimize
|
||||
|
||||
GoToSocial runs a [full analyze command](https://sqlite.org/lang_analyze.html) after each set of database migrations (eg., when starting an updated version of GoToSocial), to ensure that any indexes added or removed by migrations are taken into account correctly by the query planner.
|
||||
|
||||
Following SQLite best practice, GoToSocial also runs the [`optimize` SQLite pragma](https://sqlite.org/pragma.html#pragma_optimize) when closing database connections, to help keep index information up to date. The `optimize` pragma runs `analyze` only if it's deemed to be beneficial, given the queries that the closed database connection handled.
|
||||
|
||||
Because of the above automated steps, in normal circumstances you should not need to run a manual `analyze` against your SQLite database file.
|
||||
|
||||
However, if you notice that queries are running very slowly, it could be the case that the index metadata stored in SQLite's internal tables has become out of date, or has been removed or otherwise undesirably altered, leading the query planner to make poor choices.
|
||||
|
||||
This is particularly prone to happening if a large cleanup operation has just occured, eg., you've just [cleaned up a lot of old statuses](./post_caching.md).
|
||||
|
||||
If you notice lots of timeouts, for example when trying to view your timelines or profile page, you can use the GoToSocial binary to manually run a full `analyze`.
|
||||
|
||||
!!! important "Use the GoToSocial binary to run `analyze`!"
|
||||
It's very important that when doing an analyze, you use the GoToSocial binary, and **not** the `sqlite3` command line tool.
|
||||
|
||||
This is because the version of SQLite used in the GoToSocial binary has the compile-time flag `SQLITE_ENABLE_STAT4` enabled, which leads to much more thorough analysis and better query plans.
|
||||
|
||||
If you run `analyze` using the `sqlite3` command line tool, you risk severely degrading performance for certain timeline and profile page queries!
|
||||
|
||||
For more info on `SQLITE_ENABLE_STAT4`, see [the SQLite compilation flag docs](https://sqlite.org/compile.html#enable_stat4).
|
||||
|
||||
For a **binary** install, run the following command to do an `analyze` using the GoToSocial binary, replacing `/path/to/config.yaml` with the actual path to your `config.yaml` file:
|
||||
|
||||
```sh
|
||||
./gotosocial --config-path /path/to/config.yaml \
|
||||
database sqlite analyze
|
||||
```
|
||||
|
||||
When running GoToSocial from a **container**, you'll need to execute the above command inside the container instead. How to do this varies based on your container runtime, but for Docker it should look like:
|
||||
|
||||
```sh
|
||||
docker exec -it CONTAINER_NAME_OR_ID \
|
||||
/gotosocial/gotosocial \
|
||||
database sqlite analyze
|
||||
```
|
||||
|
||||
The command may take up to 15 minutes to finish running, depending on the size of your database file, and the specs of your machine. **DO NOT INTERRUPT IT**.
|
||||
|
||||
You will likely notice degraded performance of GoToSocial while the `analyze` is running, this is normal. If you prefer, you can stop GoToSocial before running the command, and start it again after running the command.
|
||||
|
||||
### SQLite 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.
|
||||
The commands in this section rely on having the Postgres [`psql` command line tool](https://www.postgresql.org/docs/current/app-psql.html) installed either on the same machine that is running Postgres, or inside the Postgres docker container.
|
||||
|
||||
### Postgres Vacuum
|
||||
|
||||
By default, [autovacuum is enabled for Postgres](https://www.postgresql.org/docs/current/runtime-config-vacuum.html#GUC-AUTOVACUUM). This means that unless you've configured your Postgres deployment differently from the default, you should not need to run a manual vacuum operation on your Postgres GoToSocial database.
|
||||
|
||||
However, if you have autovacuum disabled, or have recently [removed a lot of entries from the database](./post_caching.md), you may wish to run a vacuum manually.
|
||||
|
||||
1. Ensure you have some spare disk space, about 1.5x the size of the GoToSocial database.
|
||||
2. Stop GoToSocial.
|
||||
3. With `psql` connected to the GoToSocial database, run the command `VACUUM FULL;`.
|
||||
This may take quite a few minutes depending on the size of your database. DO NOT INTERRUPT IT. If you want more feedback you can run `VACUUM FULL VERBOSE;` instead.
|
||||
4. When the command has finished running, start GoToSocial again.
|
||||
|
||||
For more info, see the [Postgres docs for the `VACUUM` command](https://www.postgresql.org/docs/current/sql-vacuum.html).
|
||||
|
||||
### Postgres Analyze
|
||||
|
||||
If you notice degradation in the performance of GoToSocial on your Postgres database, it is possible that you need to use `psql` to force Postgres to rebuild statistics that the query planner uses to make decisions on which indexes to use.
|
||||
|
||||
To do this:
|
||||
|
||||
1. Ensure you have some spare disk space, about 1.5x the size of the GoToSocial database.
|
||||
2. Stop GoToSocial.
|
||||
3. With `psql` connected to the GoToSocial database, run the command `VACUUM FULL ANALYZE;`.
|
||||
This may take quite a few minutes depending on the size of your database. DO NOT INTERRUPT IT.
|
||||
4. DO NOT SKIP THIS STEP: When the previous command has finished running, run the command `VACUUM (DISABLE_PAGE_SKIPPING ON);` to force Postgres to rebuild its [visibility map](https://www.postgresql.org/docs/current/routine-vacuuming.html#VACUUM-FOR-VISIBILITY-MAP).
|
||||
This may take quite a few minutes depending on the size of your database. DO NOT INTERRUPT IT.
|
||||
5. When the second command has finished running, start GoToSocial again.
|
||||
|
||||
For more info on this process, see [this issue about Postgres performance](https://codeberg.org/superseriousbusiness/gotosocial/issues/4757#issuecomment-17348090).
|
||||
|
||||
!!! tip "Verbose"
|
||||
If you want more feedback when running either of the above commands, you can use [the `VERBOSE` option](https://www.postgresql.org/docs/current/sql-vacuum.html).
|
||||
|
||||
!!! warning "Postgres in Docker"
|
||||
If you're running Postgres in Docker and, while running the `VACUUM` command, you get a warning like `could not resize shared memory segment "/PostgreSQL.3206998826" to 67128672 bytes: No space left on device`, then you will need to increase the Docker parameter `shm-size` to a larger amount than the default. Try something like `--shm-size=128M` for example.
|
||||
|
||||
+25
-19
@@ -1,4 +1,4 @@
|
||||
# Media Caching
|
||||
# Media Caching and Pruning
|
||||
|
||||
GoToSocial uses the configured [storage backend](https://docs.gotosocial.org/en/latest/configuration/storage/) in order to store media (images, videos, etc) uploaded to the instance by local users, as well as to cache media attached to posts and profiles federated in from remote instances.
|
||||
|
||||
@@ -21,37 +21,43 @@ Remote media, on the other hand, is cached only temporarily. After a certain amo
|
||||
|
||||
Cleanup of the remote media cache occurs as a scheduled background process, and no manual intervention is required by admins. Cleanup takes somewhere between 5-30 minutes depending on the speed of the server, the speed of the configured storage, and the amount of media to work through.
|
||||
|
||||
GoToSocial exposes three variables that let you, the admin, tune when and how this work is performed: `media-remote-cache-days`, `media-cleanup-from` and `media-cleanup-every`.
|
||||
GoToSocial exposes two variables that let you, the admin, tune when and how this work is performed: `media-cleanup-cron` (accepts a cron expression), and `media-remote-cache-duration` (accepts a human-language duration string).
|
||||
|
||||
!!! info "Cron expressions"
|
||||
A ["cron expression"](https://en.wikipedia.org/wiki/Cron#Cron_expression) is a string that allows a user or computer administrator to specify when a background task should be run. Cron expressions are typically used in programming and system maintenance to schedule jobs using the program ["cron"](https://en.wikipedia.org/wiki/Cron), which is a time-based job scheduler. Because of their ubiquity, however, cron expressions are also accepted by some other programs -- such as GoToSocial! -- to allow users to customize the scheduling of background tasks.
|
||||
|
||||
For more information on cron expressions and for help writing them, see the following resources:
|
||||
|
||||
- [wikipedia page for cron expressions](https://en.wikipedia.org/wiki/Cron#Cron_expression)
|
||||
- [cron expression helper website](https://crontab.guru)
|
||||
|
||||
By default, these variables are set to the following values:
|
||||
|
||||
| Variable name | Default | Meaning |
|
||||
|---------------------------|--------------|----------|
|
||||
| `media-remote-cache-days` | `7` | 7 days |
|
||||
| `media-cleanup-from` | `"00:00"` | midnight |
|
||||
| `media-cleanup-every` | `"24h"` | daily |
|
||||
| Variable name | Default | Meaning |
|
||||
|-------------------------------|--------------|------------------------------------------------|
|
||||
| `media-cleanup-cron` | `0 0 * * *` | Cron expression meaning every night @ midnight |
|
||||
| `media-remote-cache-duration` | `7 days` | 7 days |
|
||||
|
||||
In other words, the default settings mean that every night at midnight, remote media older than a week will be uncached and removed from storage.
|
||||
In other words, the default settings mean that every night at midnight, remote media older than seven days will be uncached and removed from storage.
|
||||
|
||||
You can achieve different results by tuning these variables. For example, say you wanted to prune at 4.30am instead of midnight, you could change `media-cleanup-from` to `"04:30"`.
|
||||
You can achieve different results by tuning these variables. For example, say you wanted to prune at 4.30am instead of midnight, you could change `media-cleanup-cron` to `30 4 * * *`.
|
||||
|
||||
If you only want to prune every couple of days instead of every night, you could set `media-cleanup-every` to a higher value, like `"48h"` or `"72h"`.
|
||||
If you only want to prune every two days instead of every night, you could set `media-cleanup-cron` to something like `0 0 */2 * *`
|
||||
|
||||
If you wanted to adopt a more aggressive cleanup strategy to minimize storage usage, you could set the following values:
|
||||
If you wanted to adopt an aggressive cleanup strategy to minimize storage usage, you could set the following values:
|
||||
|
||||
| Variable name | Setting | Meaning |
|
||||
|---------------------------|--------------|-------------|
|
||||
| `media-remote-cache-days` | `1` | 1 day |
|
||||
| `media-cleanup-from` | `"00:00"` | midnight |
|
||||
| `media-cleanup-every` | `"8h"` | every 8 hrs |
|
||||
| Variable name | Setting | Meaning |
|
||||
|-------------------------------|---------------|-------------|
|
||||
| `media-cleanup-cron` | `0 */8 * * *` | every 8 hrs |
|
||||
| `media-remote-cache-duration` | `1` | 1 day |
|
||||
|
||||
The above settings would mean that every 8 hours starting from midnight, GoToSocial would prune any media older than 1 day (24hrs). The prune jobs would run at 00:00, 08:00, and 16:00, ie., midnight, 8am, and 4pm. With this configuration, the longest amount of time you could possibly keep remote media in your storage would be about 32 hours.
|
||||
The above settings would mean that every 8 hours, GoToSocial would prune any media older than 1 day (24hrs). With this configuration, the longest amount of time you could possibly keep remote media in your storage would be about 32 hours.
|
||||
|
||||
!!! tip
|
||||
Setting `media-remote-cache-days` to 0 or less means that remote media will never be uncached. However, cleanup jobs for orphaned local media and other consistency checks will still be run using the schedule defined by the other variables.
|
||||
Setting `media-remote-cache-duration` to 0 or less means that remote media will never be uncached. However, cleanup jobs for orphaned local media and other consistency checks will still be run using the schedule defined by the other variables.
|
||||
|
||||
!!! tip
|
||||
You can also run cleanup manually as a one-off action through the admin panel, if you so wish ([see docs](./settings.md#media)).
|
||||
|
||||
!!! warning
|
||||
Setting `media-cleanup-every` to a very small value like `"30m"` or less will probably cause your instance to just constantly iterate through attachments, causing high database use for very little benefit. We don't recommend setting this value to less than about `"8h"` and even that is probably overkill.
|
||||
Setting `media-cleanup-cron` to a very small value like every hour or less will probably cause your instance to just constantly iterate through attachments, causing high database use for very little benefit. We don't recommend setting this value to less than about every eight hours, and even that is probably overkill.
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
# Post Caching and Pruning
|
||||
|
||||
GoToSocial stores posts (both local and remote) in whatever [database backend](../configuration/database.md) the instance is configured to use.
|
||||
|
||||
Typically, the `statuses` table that GoToSocial uses to store posts is by far the largest table in the database, and will continue to grow over time the longer you use your GoToSocial instance for, as your instance receives new posts from people you follow, and dereferences boosted posts and replied-to posts, etc.
|
||||
|
||||
To avoid the issue of ever-increasing database sizes, GoToSocial provides a mechanism whereby posts can be regularly pruned from the database by a background job, freeing up space.
|
||||
|
||||
## Which posts will be pruned
|
||||
|
||||
When selecting posts to clean up, GoToSocial considers posts in the context of the thread that they're part of (if applicable). For the purposes of cleanup, single posts are considered to be in a thread of length 1.
|
||||
|
||||
To be eligible for pruning, a thread must meet the following criteria:
|
||||
|
||||
1. **All posts in the thread are remote**, ie., created by a remote account. If a local account created or has participated in a thread, that thread will never be removed using this pruning method.
|
||||
2. **No posts in the thread have been interacted with by a local account**. If a local user has faved, boosted, or bookmarked any post in a thread, that thread will not be pruned.
|
||||
3. **All posts in the thread have been created or fetched less recently than the duration `statuses-cleanup-remote-older-than`**. Ie., only threads that haven't been dereferenced or added to since `statuses-cleanup-remote-older-than` will be pruned. (See below for more info on this config setting.)
|
||||
|
||||
If a thread meets the above criteria, then the cleanup process will mark that thread for removal, and all posts in the thread (and any attached media) will be removed from the database.
|
||||
|
||||
!!! info "Posts will be refetched on demand"
|
||||
Much like with [media caching + cleanup](./media_caching.md), if a post is removed from post pruning, it can always be fetched again by the instance later, for example if a local user looks up one of the posts in the thread by its URI/URL, or if the instance sees a post in that thread due to someone replying to it, etc.
|
||||
|
||||
## How to enable remote post pruning
|
||||
|
||||
By default, the remote post pruning task is not enabled. To enable it, update your config to set `statuses-cleanup-remote-older-than` to a valid duration string, for example "1 year", and restart your instance. Posts last created/fetched/updated before that duration will then become [eligible for cleanup](#which-posts-will-be-pruned).
|
||||
|
||||
## When will post pruning occur
|
||||
|
||||
The scheduling of the post cleanup operation is controlled by the config variable `statuses-cleanup-cron`, which accepts a cron expression as a value. By default, this is `"0 1 * * 0`, which means every Sunday at 1am (ie., weekly).
|
||||
|
||||
!!! info "Cron expressions"
|
||||
A ["cron expression"](https://en.wikipedia.org/wiki/Cron#Cron_expression) is a string that allows a user or computer administrator to specify when a background task should be run. Cron expressions are typically used in programming and system maintenance to schedule jobs using the program ["cron"](https://en.wikipedia.org/wiki/Cron), which is a time-based job scheduler. Because of their ubiquity, however, cron expressions are also accepted by some other programs -- such as GoToSocial! -- to allow users to customize the scheduling of background tasks.
|
||||
|
||||
For more information on cron expressions and for help writing them, see the following resources:
|
||||
|
||||
- [wikipedia page for cron expressions](https://en.wikipedia.org/wiki/Cron#Cron_expression)
|
||||
- [cron expression helper website](https://crontab.guru)
|
||||
|
||||
If you wish to this cleanup operation to run more or less frequently, depending on your needs and resources, you can adjust the config value to your liking. For example, to run the job once per month instead of once per week, you could change `statuses-cleanup-cron` to `0 1 1 * *`, which will cause the job to run at 1am on the first day of every month.
|
||||
|
||||
!!! tip "First cleanup can take a long time"
|
||||
If you've enabled remote post pruning for the first time on a large database (eg., larger than a few GB), be aware that the first cleanup operation can take quite a long time, as there will be a significant number of threads that must be iterated through, depending on how you've set `statuses-cleanup-remote-older-than`.
|
||||
|
||||
For example, on a 17GB SQLite database with `statuses-cleanup-remote-older-than` set to "1 year", on a 1CPU, 1GB ram VPS, the first remote post prune operation was observed to take about 48+ hours in total.
|
||||
|
||||
While the cleanup is running, depending on the specs of the machine that GoToSocial is running on, you may notice high CPU usage and some degradation of performance (eg., home timeline queries taking longer to respond, that sort of thing). This is normal and does not indicate a problem with the operation.
|
||||
|
||||
After you've run a cleanup for the first time, subsequent cleanups should be much faster, as there will be fewer threads remaining that must be iterated through.
|
||||
|
||||
## Considerations for after cleanup
|
||||
|
||||
### SQLite
|
||||
|
||||
With SQLite, when you clean up posts from the database the database file will not shrink in size automatically, as GoToSocial does not enable [autovacuum](https://sqlite.org/pragma.html#pragma_auto_vacuum) in SQLite. Instead, freed space will be held by the database file and written into when new posts arrive.
|
||||
|
||||
If you want to actually shrink the size of your database file after cleanup, you should [run a `vacuum` command against it using the `sqlite3` CLI tool](./database_maintenance.md#sqlite-vacuum). To ensure indexes remain performant after removal of a large amount of statuses, you may also wish to [run `analyze` on your database file using the GoToSocial binary](./database_maintenance.md#sqlite-analyze--optimize)
|
||||
|
||||
### Postgres
|
||||
|
||||
By default, [autovacuum is enabled for Postgres](https://www.postgresql.org/docs/current/runtime-config-vacuum.html#GUC-AUTOVACUUM). This means that any space freed up by the removal of remote posts should be made available to the operating system again once the autovacuum daemon has done its thing, and you should not need to manually intervene in order to shrink your database volume.
|
||||
|
||||
However, it may be that you wish to run vacuum manually, and/or reanalyze the database after the removal of a large amount of posts, to ensure that indexes remain performant. For steps on how to do this, check the [Postgres maintenance docs](./database_maintenance.md#postgres).
|
||||
@@ -0,0 +1,71 @@
|
||||
# Relay Subscriptions
|
||||
|
||||

|
||||
|
||||
GoToSocial allows you (as admin) to create subscriptions to ActivityPub relays in order to populate your instance with posts that you may not otherwise have seen by just following other accounts across the fediverse.
|
||||
|
||||
!!! warning
|
||||
Relay subscriptions are useful for populating your instance with posts, but bear in mind that more posts stored on your instance means more storage space used. If you subscribe your instance to a busy relay, expect your storage requirements to increase significantly!
|
||||
|
||||
{%
|
||||
include "../.fragments/relay.md"
|
||||
start='<!--what-is-a-relay-start-->'
|
||||
end='<!--what-is-a-relay-end-->'
|
||||
%}
|
||||
|
||||
## What happens when you subscribe to a relay?
|
||||
|
||||
When you create a relay subscription (see below), the service actor for your instance will send a follow request to the relay actor. When the follow request is accepted, either automatically, or pending a manual action by the administrator of the relay, your instance service actor will begin to receive posts from the relay in its inbox. Posts received in this way will be ingested into your instance and shown in the federated timeline (if applicable).
|
||||
|
||||
{%
|
||||
include "../.fragments/relay.md"
|
||||
start='<!--relay-not-private-start-->'
|
||||
end='<!--relay-not-private-end-->'
|
||||
%}
|
||||
|
||||
## Create a relay subscription
|
||||
|
||||
You can subscribe to a relay by using the [admin settings panel](./settings.md#relay-subscriptions) to input the relay actor URI, and select flags that you would like to apply to the relay connection.
|
||||
|
||||
!!! tip "Relay connection must be approved by relay owner"
|
||||
After a relay subscription is created, you must wait for approval from the relay owner before the subscription will become active. This approval may be instantaneous + automatic, or may never happen at all! Some relay admins require that you message or email them *before* sending a subscription request, so make sure you take account of this.
|
||||
|
||||
{%
|
||||
include "../.fragments/relay.md"
|
||||
start='<!--relay-actor-uri-start-->'
|
||||
end='<!--relay-actor-uri-end-->'
|
||||
%}
|
||||
|
||||
### Flags
|
||||
|
||||
The flag checkboxes allow you to customize which posts should be ingested by the relay subscription.
|
||||
|
||||
<dl>
|
||||
|
||||
<dt><strong>Ingest public visibility posts:</strong></dt>
|
||||
<dd>By checking this flag, you instruct GoToSocial to ingest Public posts via this relay subscription. If the box is not checked, then posts with Public visibility will never be ingested by the relay subscription.</dd>
|
||||
|
||||
<dt><strong>Ingest unlisted visibility posts:</strong></dt>
|
||||
<dd>By checking this flag, you instruct GoToSocial to ingest Unlisted (aka Unlocked, aka Quiet Public) posts via this relay subscription. If the box is not checked, then posts with Unlisted visibility will never be ingested by the relay subscription.</dd>
|
||||
|
||||
<dt><strong>Never ingest posts marked as sensitive:</strong></dt>
|
||||
<dd>With this flag checked, this relay subscription will <em>never</em> ingest a post that was marked as sensitive by the author, even if such a post would normally be matched and ingested.</dd>
|
||||
|
||||
<dt><strong>Never ingest posts with media:</strong></dt>
|
||||
<dd>With this flag checked, this relay subscription will <em>never</em> ingest a post that has media attachments, even if such a post would normally be matched and ingested. This can be useful to avoid ballooning an instance's storage size when it subscribes to a relay that has lots of media posts.</dd>
|
||||
|
||||
<dt><strong>Never ingest replies/comments:</strong></dt>
|
||||
<dd>With this flag checked, this relay subscription will <em>never</em> ingest replies or comments that are forwarded to it by the relay. In this context, a reply or comment is a post that replies to the post of another author. Note that even if you check this flag, GoToSocial will still try to dereference comments on top-level posts or self-reply thread posts that are sent to it by the relay.</dd>
|
||||
|
||||
<dt><strong>Match posts by default:</strong></dt>
|
||||
<dd>With this flag checked, you tell GoToSocial that all posts sent to it via this relay subscription should be matched by default. In other words, any posts (of appropriate visibilities) that are not ignored because of other flags will be ingested, unless their content is matched by an exclude matcher. With the flag unchecked, posts will only be ingested if their content is matched by one or more matchers.</dd>
|
||||
|
||||
</dl>
|
||||
|
||||
Any posts that should not be ingested by the relay subscription, according to the above flags, will be dropped.
|
||||
|
||||
{%
|
||||
include "../.fragments/relay.md"
|
||||
start='<!--relay-matchers-start-->'
|
||||
end='<!--relay-matchers-end-->'
|
||||
%}
|
||||
+51
-43
@@ -157,46 +157,6 @@ If your instance is able to fetch and parse permissions at the subscription URI,
|
||||
|
||||
Instance administration settings.
|
||||
|
||||
### 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
|
||||
|
||||
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). This action is functionally identical to the media cleanup that runs automatically.
|
||||
|
||||
You can also use this section to **purge** all media belonging to a specific domain from your media storage. This is useful in combination with [domain limits](./domain_limits.md) to prevent your instance from storing potentially problematic media from a domain you don't fully trust.
|
||||
|
||||
Media removed via a cleanup or purge operation will be fetched again from remote domains later if needed, unless a domain limit with a media policy of "reject" is in place to prevent this for certain domains.
|
||||
|
||||
#### Keys
|
||||
|
||||
You can use this section to expire/invalidate public keys from the selected remote instance. The next time your instance receives a signed request using an expired key, it will attempt to fetch and store the public key again.
|
||||
|
||||
### Custom Emoji
|
||||
|
||||
Custom Emoji will be automatically fetched when included in remote toots, but to use them in your own posts they have to be enabled on your instance.
|
||||
|
||||
#### Local
|
||||
|
||||

|
||||
|
||||
This section shows an overview of all the custom emoji enabled on your instance, sorted by their category. Clicking an emoji shows it's details, and provides options to change the category or image, or delete it completely. The shortcode cannot be updated here, you would have to upload it with the new shortcode yourself (and optionally delete the old one).
|
||||
|
||||
Below the overview you can upload your own custom emoji, after previewing how they look in a toot. PNG and (animated) GIF's are supported.
|
||||
|
||||
#### Remote
|
||||
|
||||

|
||||
|
||||
Through the 'remote' section, you can look up a link to any remote toots (provided the instance isn't suspended). If they use any custom emoji they will be listed, providing an easy way to copy them to the local emoji (for use in your own toots), or disable them ( hiding them from toots).
|
||||
|
||||
**Note:** as the testrig server does not federate, this feature can't be used in development (500: Internal Server Error).
|
||||
|
||||
### Instance Settings
|
||||
|
||||

|
||||
@@ -255,7 +215,7 @@ You can also mention accounts using the standard `@user[@domain]` format.
|
||||
|
||||
Have a look at the [markdown cheat sheet](https://markdownguide.offshoot.io/cheat-sheet/) to see what else you can do.
|
||||
|
||||
### Instance Contact Info
|
||||
#### Instance Contact Info
|
||||
|
||||
In this section, you can provide visitors to your instance with a convenient way of reaching your instance admin.
|
||||
|
||||
@@ -265,10 +225,58 @@ The selected **contact user** must be an active (not suspended) admin and/or mod
|
||||
|
||||
If you're on a single-user instance and you give admin privileges to your main account, you can just fill in your own username here; you don't need to make a separate admin account just for this.
|
||||
|
||||
### Instance Custom CSS
|
||||
#### Instance Custom CSS
|
||||
|
||||
custom CSS allows you to further customize the way your instance looks when visited through a browser.
|
||||
Custom CSS allows you to further customize the way your instance looks when visited through a browser.
|
||||
|
||||
This custom CSS will be applied to all pages of your instance. Users themes and CSS still take precedence over this customization.
|
||||
|
||||
See the [Custom CSS](../user_guide/custom_css.md) page for some tips on writing custom CSS for your instance.
|
||||
|
||||
### Custom Emoji
|
||||
|
||||
Custom Emoji will be automatically fetched when included in remote toots, but to use them in your own posts they have to be enabled on your instance.
|
||||
|
||||
#### Local
|
||||
|
||||

|
||||
|
||||
This section shows an overview of all the custom emoji enabled on your instance, sorted by their category. Clicking an emoji shows it's details, and provides options to change the category or image, or delete it completely. The shortcode cannot be updated here, you would have to upload it with the new shortcode yourself (and optionally delete the old one).
|
||||
|
||||
Below the overview you can upload your own custom emoji, after previewing how they look in a toot. PNG and (animated) GIF's are supported.
|
||||
|
||||
#### Remote
|
||||
|
||||

|
||||
|
||||
Through the 'remote' section, you can look up a link to any remote toots (provided the instance isn't suspended). If they use any custom emoji they will be listed, providing an easy way to copy them to the local emoji (for use in your own toots), or disable them ( hiding them from toots).
|
||||
|
||||
**Note:** as the testrig server does not federate, this feature can't be used in development (500: Internal Server Error).
|
||||
|
||||
### Relay Subscriptions
|
||||
|
||||

|
||||
|
||||
In this section you can view, create, update, and delete relay subscriptions on your instance.
|
||||
|
||||
For more details about what relay subscriptions are, and what they do, please see the separate [relay subscriptions](./relay_subscriptions.md) document.
|
||||
|
||||
### 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
|
||||
|
||||
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). This action is functionally identical to the media cleanup that runs automatically.
|
||||
|
||||
You can also use this section to **purge** all media belonging to a specific domain from your media storage. This is useful in combination with [domain limits](./domain_limits.md) to prevent your instance from storing potentially problematic media from a domain you don't fully trust.
|
||||
|
||||
Media removed via a cleanup or purge operation will be fetched again from remote domains later if needed, unless a domain limit with a media policy of "reject" is in place to prevent this for certain domains.
|
||||
|
||||
#### Keys
|
||||
|
||||
You can use this section to expire/invalidate public keys from the selected remote instance. The next time your instance receives a signed request using an expired key, it will attempt to fetch and store the public key again.
|
||||
|
||||
@@ -3,21 +3,19 @@
|
||||
!!! Danger "This is unsupported"
|
||||
We do not offer any kind of support for deployments of GoToSocial built with the `nowasm` tag described in this section. Such builds should be considered strictly experimental, and any issues that come when running them are none of our business! Please don't open issues on the repo looking for help debugging deployments of `nowasm` builds.
|
||||
|
||||
On [supported platforms](../../getting_started/releases.md#supported-platforms), GoToSocial uses the WebAssembly runtime [Wazero](https://wazero.io/) to sandbox `ffmpeg`, `ffprobe`, and `sqlite3` WebAssembly binaries, allowing these applications to be packaged and run inside the GoToSocial binary, without requiring admins to install + manage any external dependencies.
|
||||
On [supported platforms](../../getting_started/releases.md#supported-platforms), GoToSocial uses the WebAssembly runtime [Wazero](https://wazero.io/) to sandbox `ffmpeg` and `ffprobe` WebAssembly binaries, allowing these applications to be packaged and run inside the GoToSocial binary, without requiring admins to install + manage any external dependencies.
|
||||
|
||||
This has the advantage of making it easier for admins to maintain their GoToSocial instance, as their GtS binary is completely isolated from any changes to their system-installed `ffmpeg`, `ffprobe`, and `sqlite`. It's also a bit safer to run `ffmpeg` in this way, as GoToSocial wraps the `ffmpeg` binary in a very constrained file system that doesn't permit the `ffmpeg` binary to access any files other than the ones it's decoding + reencoding. In other words, GoToSocial on supported platforms offers most of the functionality of `ffmpeg` and so on, without some of the headaches.
|
||||
This has the advantage of making it easier for admins to maintain their GoToSocial instance, as their GtS binary is completely isolated from any changes to their system-installed `ffmpeg` and `ffprobe`. It's also a bit safer to run `ffmpeg` in this way, as GoToSocial wraps the `ffmpeg` binary in a very constrained file system that doesn't permit the `ffmpeg` binary to access any files other than the ones it's decoding + reencoding. In other words, GoToSocial on supported platforms offers most of the functionality of `ffmpeg` and so on, without some of the headaches.
|
||||
|
||||
However, not all platforms are capable of running Wazero in the much-faster "compiler" mode, and have to fall back to the very slow (and resource-heavy) "interpreter" mode. See [this table](https://github.com/tetratelabs/wazero?tab=readme-ov-file#conformance) from Wazero for conformance.
|
||||
|
||||
"Interpreter" mode runs so poorly for GoToSocial's use case that it's simply not feasible to run a GoToSocial instance in a stable manner on platforms that aren't 64-bit Linux or 64-bit FreeBSD, as all the memory and CPU get gobbled up by media processing.
|
||||
|
||||
However! To enable folks to run **experimental, unsupported deployments of GoToSocial**, we expose the `nowasm` build tag, which can be used to compile a build of GoToSocial that does not use Wazero or WASM at all.
|
||||
|
||||
A GoToSocial binary built with `nowasm` will use the [modernc version of SQLite](https://pkg.go.dev/modernc.org/sqlite) instead of the WASM one, and will use on-system `ffmpeg` and `ffprobe` binaries for media processing.
|
||||
However! To enable folks to run **experimental, unsupported deployments of GoToSocial**, we expose the `nowasm` build tag, which can be used to compile a build of GoToSocial that does not use Wazero or WASM at all. A GoToSocial binary built with `nowasm` will use system `ffmpeg` and `ffprobe` binaries for media processing.
|
||||
|
||||
!!! tip
|
||||
To test if your system is compatible with the standard builds, you can use this command:
|
||||
`if grep -qE '^flags.* (sse4|LSE)' /proc/cpuinfo; then echo "Your system is supporting GTS!"; else echo "Your system is not supporting GTS, you'll have to use the 'nowasm' builds :("; fi`
|
||||
`if grep -qE '^flags.* (sse4|LSE)' /proc/cpuinfo; then echo "Your system supports GTS!"; else echo "Your system does not support GTS, you'll have to use the 'nowasm' builds :("; fi`
|
||||
|
||||
To build GoToSocial with the `nowasm` tag, you can pass the tag into our convenience `build.sh` script like so:
|
||||
|
||||
@@ -28,4 +26,4 @@ GO_BUILDTAGS=nowasm ./scripts/build.sh
|
||||
In order to run a version of GoToSocial built in this way, you must ensure that `ffmpeg` and `ffprobe` are installed on the host. This is usually as simple as running a command like `doas -u root pkg_add ffmpeg` (OpenBSD), or `sudo apt install ffmpeg` (Debian etc.).
|
||||
|
||||
!!! Danger "No really though, it's unsupported"
|
||||
Again, if running builds of GoToSocial with `nowasm` works for your OS/Arch combination, that's great, but we do not support such builds and we won't be able to help debugging why something doesn't work.
|
||||
Again, if running builds of GoToSocial with `nowasm` works for your OS/Arch combination, that's great, but we do not support such builds and we won't be able to help debugging why media processing isn't working.
|
||||
|
||||
+1214
-18
File diff suppressed because it is too large
Load Diff
@@ -9,15 +9,14 @@
|
||||
|
||||
# Settings for OUTGOING http client connections used by GoToSocial to make
|
||||
# requests to remote resources (status GETs, media GETs, inbox POSTs, etc).
|
||||
|
||||
http-client:
|
||||
|
||||
# Duration. Timeout to use for outgoing HTTP requests. If the timeout
|
||||
# is exceeded, the connection to the remote server will be dropped.
|
||||
# A value of 0s indicates no timeout: this is not advised!
|
||||
# Examples: ["5s", "10s", "0s"]
|
||||
# Default: "10s"
|
||||
timeout: "10s"
|
||||
# Examples: ["5s", "30s", "0s"]
|
||||
# Default: "1m"
|
||||
timeout: "1m"
|
||||
|
||||
########################################
|
||||
#### RESERVED IP RANGE EXCEPTIONS ######
|
||||
@@ -74,4 +73,71 @@ http-client:
|
||||
#
|
||||
# Default: false
|
||||
insecure-outgoing: false
|
||||
|
||||
##########################################################################
|
||||
##### Options passed directly to the HTTP client's http.Transport{}: #####
|
||||
##########################################################################
|
||||
|
||||
# NOTE: all of the below are internal HTTP client configuration flags,
|
||||
# you should not uncomment + tweak these unless you know what you're doing!
|
||||
#
|
||||
# The commented-out values are set to the sensible values GtS uses by default.
|
||||
|
||||
# DisableKeepAlives, if true, disables HTTP keep-alives and will only use the
|
||||
# connection to the server for a single HTTP request.
|
||||
#
|
||||
# This is unrelated to the similarly named TCP keep-alives.
|
||||
#
|
||||
# Default: false
|
||||
#disable-keep-alives: false
|
||||
|
||||
# MaxConnsPerHost optionally limits the total number of connections per host,
|
||||
# including connections in the dialing, active, and idle states. On limit
|
||||
# violation, dials will block.
|
||||
#
|
||||
# Default: 6
|
||||
#max-conns-per-host: 6
|
||||
|
||||
# MaxIdleConns controls the maximum number of idle (keep-alive) connections across
|
||||
# all hosts. Zero means no limit.
|
||||
#
|
||||
# Default: 0 (no limit)
|
||||
#max-idle-conns: 0
|
||||
|
||||
# MaxIdleConnsPerHost, if non-zero, controls the maximum idle (keep-alive)
|
||||
# connections to keep per-host. If zero, DefaultMaxIdleConnsPerHost (2) is used.
|
||||
#
|
||||
# Default: 0 (use built-in default 2)
|
||||
#max-idle-conns-per-host: 0
|
||||
|
||||
# IdleConnTimeout is the maximum amount of time an idle (keep-alive) connection
|
||||
# will remain idle before closing itself. Zero means no limit.
|
||||
#
|
||||
# Default: 5 minutes
|
||||
#idle-conn-timeout: "5 minutes"
|
||||
|
||||
# TLSHandshakeTimeout specifies the maximum amount of time to wait for a TLS
|
||||
# handshake. Zero means no timeout.
|
||||
#
|
||||
# Default: 0 (no limit)
|
||||
#tls-handshake-timeout: "0"
|
||||
|
||||
# ResponseHeaderTimeout, if non-zero, specifies the amount of time to wait for a
|
||||
# server's response headers after fully writing the request (including its body, if any).
|
||||
# This time does not include the time to read the response body.
|
||||
#
|
||||
# Default: 0 (no limit)
|
||||
#response-header-timeout: "0"
|
||||
|
||||
# ReadBufferSize specifies the size of the read buffer used when reading from the transport.
|
||||
# If zero, a default (currently 4KB) is used.
|
||||
#
|
||||
# Default: 0 (use built-in default 4KB)
|
||||
#read-buffer-size: "0"
|
||||
|
||||
# WriteBufferSize specifies the size of the write buffer used when writing to the transport.
|
||||
# If zero, a default (currently 4KB) is used.
|
||||
#
|
||||
# Default: 0 (use built-in default 4KB)
|
||||
#write-buffer-size: "0"
|
||||
```
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
# HTTP Server
|
||||
|
||||
## Settings
|
||||
|
||||
```yaml
|
||||
################################
|
||||
##### HTTP SERVER SETTINGS #####
|
||||
################################
|
||||
|
||||
# Settings for INCOMING http server connections received by GoToSocial.
|
||||
#
|
||||
# WARNING: all of the below are internal HTTP server configuration flags,
|
||||
# you should not uncomment + tweak these unless you know what you're doing!
|
||||
# They are set to sensible defaults and you shouldn't need to change them.
|
||||
http-server:
|
||||
|
||||
#######################################################################
|
||||
##### Options passed directly to our HTTP router's gin.Engine{}: #####
|
||||
#######################################################################
|
||||
|
||||
# MaxMultipartMemory value of 'maxMemory' param that is given to
|
||||
# http.Request's ParseMultipartForm method call.
|
||||
#
|
||||
# Default: 40MiB
|
||||
#max-multipart-memory: "40MiB"
|
||||
|
||||
# Enable h2c (http2 without TLS).
|
||||
#
|
||||
# May be useful if your reverse proxy supports connecting to GoToSocial via HTTP2.
|
||||
#
|
||||
# Default: false
|
||||
#use-h2c: false
|
||||
|
||||
#######################################################################
|
||||
##### Options passed directly to our base http.Server{} instance: #####
|
||||
#######################################################################
|
||||
|
||||
# ReadTimeout is the maximum duration for reading the entire request,
|
||||
# including the body. A zero or negative value means there will be no timeout.
|
||||
#
|
||||
# Because ReadTimeout does not let Handlers make per-request decisions on
|
||||
# each request body's acceptable deadline or upload rate, most users will prefer
|
||||
# to use ReadHeaderTimeout. It is valid to use them both.
|
||||
#
|
||||
# Default: 60s
|
||||
#read-timeout: "60s"
|
||||
|
||||
# ReadHeaderTimeout is the amount of time allowed to read request headers.
|
||||
# The connection's read deadline is reset after reading the headers and the
|
||||
# Handler can decide what is considered too slow for the body. If zero, the
|
||||
# value of ReadTimeout is used. If negative, or if zero and ReadTimeout is
|
||||
# zero or negative, there is no timeout.
|
||||
#
|
||||
# Default: 0 (use read-timeout duration).
|
||||
#read-header-timeout: "0"
|
||||
|
||||
# WriteTimeout is the maximum duration before timing out writes of the response.
|
||||
# It is reset whenever a new request's header is read. Like ReadTimeout, it does
|
||||
# not let Handlers make decisions on a per-request basis. A zero or negative value
|
||||
# means there will be no timeout.
|
||||
#
|
||||
# Default: 30s
|
||||
#write-timeout: "30s"
|
||||
|
||||
# IdleTimeout is the maximum amount of time to wait for the next request when
|
||||
# keep-alives are enabled. If zero, the value of ReadTimeout is used. If negative,
|
||||
# or if zero and ReadTimeout is zero or negative, there is no timeout.
|
||||
#
|
||||
# Default: 30s
|
||||
#idle-timeout: "30s"
|
||||
|
||||
# MaxHeaderBytes controls the maximum number of bytes the server will read parsing
|
||||
# the request header's keys and values, including the request line. It does not limit
|
||||
# the size of the request body. If zero, DefaultMaxHeaderBytes is used.
|
||||
#
|
||||
# Default: 0 (use built-in default 1048576, ie., 1 mebibyte)
|
||||
#max-header-bytes: "0"
|
||||
|
||||
############################################################################
|
||||
##### Options passed directly to our HTTP server's http.HTTP2Config{}: #####
|
||||
############################################################################
|
||||
|
||||
# MaxConcurrentStreams optionally specifies the number of concurrent streams that a
|
||||
# peer may have open at a time. If zero, MaxConcurrentStreams defaults to at least 100.
|
||||
#
|
||||
# Default: 0 (at least 100)
|
||||
#max-concurrent-streams: "0"
|
||||
|
||||
# MaxDecoderHeaderTableSize optionally specifies an upper limit for the size of the header
|
||||
# compression table used for decoding headers sent by the peer. A valid value is less than
|
||||
# 4MiB. If zero or invalid, a default value is used.
|
||||
#
|
||||
# Default: 0 (use built-in default 4096, ie., 4 kibibytes)
|
||||
#max-decoder-header-table-size: "0"
|
||||
|
||||
# MaxEncoderHeaderTableSize optionally specifies an upper limit for the header compression
|
||||
# table used for sending headers to the peer. A valid value is less than 4MiB. If zero or
|
||||
# invalid, a default value is used.
|
||||
#
|
||||
# Default: 0 (use built-in default 4096, ie., 4 kibibytes)
|
||||
#max-encoder-header-table-size: "0"
|
||||
|
||||
# MaxReadFrameSize optionally specifies the largest frame this endpoint is willing to read.
|
||||
# A valid value is between 16KiB and 16MiB, inclusive. If zero or invalid, a default value is used.
|
||||
#
|
||||
# Default: 0 (use built-in default 1048576, ie., 1 mebibyte)
|
||||
#max-read-frame-size: "0"
|
||||
|
||||
# MaxReceiveBufferPerConnection is the maximum size of the flow control window for data received
|
||||
# on a connection. A valid value is at least 64KiB and less than 4MiB. If invalid, a default value
|
||||
# is used.
|
||||
#
|
||||
# Default: 0 (use built-in default 1048576, ie., 1 mebibyte)
|
||||
#max-receive-buffer-per-connection: "0"
|
||||
|
||||
# MaxReceiveBufferPerStream is the maximum size of the flow control window for data received on a
|
||||
# stream (request). A valid value is less than 4MiB. If zero or invalid, a default value is used.
|
||||
#
|
||||
# Default: 0 (use built-in default 1048576, ie., 1 mebibyte)
|
||||
#max-receive-buffer-per-stream: "0"
|
||||
|
||||
# SendPingTimeout is the timeout after which a health check using a ping frame will be carried out
|
||||
# if no frame is received on a connection. If zero, no health check is performed.
|
||||
#
|
||||
# Default: 0
|
||||
#send-ping-timeout: "0"
|
||||
|
||||
# PingTimeout is the timeout after which a connection will be closed if a response to a ping is not
|
||||
# received. If zero, a default of 15 seconds is used.
|
||||
#
|
||||
# Default: 0 (use built-in default 15s)
|
||||
#ping-timeout: "0"
|
||||
|
||||
# WriteByteTimeout is the timeout after which a connection will be closed if no data can be written to it.
|
||||
# The timeout begins when data is available to write, and is extended whenever any bytes are written.
|
||||
#
|
||||
# Default: 0
|
||||
#write-byte-timeout: "0"
|
||||
```
|
||||
@@ -176,15 +176,11 @@ instance-deliver-to-shared-inboxes: true
|
||||
# Default: false
|
||||
instance-inject-mastodon-version: false
|
||||
|
||||
# String. 24hr time of day formatted as hh:mm.
|
||||
# Examples: ["14:30", "00:00", "04:00"]
|
||||
# Default: "23:00" (11pm).
|
||||
instance-subscriptions-process-from: "23:00"
|
||||
|
||||
# Duration. Period between subscription updates.
|
||||
# Examples: ["24h", "72h", "12h"]
|
||||
# Default: "24h" (once per day).
|
||||
instance-subscriptions-process-every: "24h"
|
||||
# Cron expression (see https://crontab.guru/ for help).
|
||||
#
|
||||
# Examples: ["0 0 * * *", "30 0 * * *", "0 0 * * 0"]
|
||||
# Default: "0 23 * * *" (at 23:00pm, every day)
|
||||
instance-subscriptions-process-cron: "0 23 * * *"
|
||||
|
||||
# String. Allows you to customize if and how stats are served to
|
||||
# crawlers at the /api/v1|v2/instance and /nodeinfo endpoints.
|
||||
|
||||
+10
-23
@@ -98,28 +98,15 @@ media-ffmpeg-pool-size: 1
|
||||
# what these settings do, with some customization examples, see the docs:
|
||||
# https://docs.gotosocial.org/en/latest/admin/media_caching#cleanup
|
||||
|
||||
# Int. Number of days to cache media from remote instances before
|
||||
# they are removed from the cache. When remote media is removed from
|
||||
# the cache, it is deleted from storage but the database entries for
|
||||
# the media are kept so that it can be fetched again if requested by a user.
|
||||
#
|
||||
# If this is set to 0, then media from remote instances will be cached indefinitely.
|
||||
#
|
||||
# Examples: [30, 60, 7, 0]
|
||||
# Default: 7
|
||||
media-remote-cache-days: 7
|
||||
# Integer duration.
|
||||
#
|
||||
# Examples: ["7 days", "1 week", "1 month"]
|
||||
# Default: "7 days"
|
||||
media-remote-cache-duration: "7 days"
|
||||
|
||||
# String. 24hr time of day formatted as hh:mm.
|
||||
# Examples: ["14:30", "00:00", "04:00"]
|
||||
# Default: "00:00" (midnight).
|
||||
media-cleanup-from: "00:00"
|
||||
|
||||
# Duration. Period between media cleanup runs.
|
||||
# More than once per 24h is not recommended
|
||||
# is likely overkill. Setting this to something
|
||||
# very low like once every 10 minutes will probably
|
||||
# cause lag and possibly other issues.
|
||||
# Examples: ["24h", "72h", "12h"]
|
||||
# Default: "24h" (once per day).
|
||||
media-cleanup-every: "24h"
|
||||
# Cron expression (see https://crontab.guru/ for help).
|
||||
#
|
||||
# Examples: ["0 0 * * *", "30 0 * * *", "0 0 * * 0"]
|
||||
# Default: "0 0 * * *" (at 00:00am, every day)
|
||||
media-cleanup-cron: "0 0 * * *"
|
||||
```
|
||||
|
||||
@@ -36,6 +36,18 @@ statuses-poll-option-max-chars: 50
|
||||
# Default: 6
|
||||
statuses-media-max-files: 6
|
||||
|
||||
# Cron expression (see https://crontab.guru/ for help).
|
||||
#
|
||||
# Examples: ["0 0 * * *", "30 0 * * *", "0 0 * * 0"]
|
||||
# Default: "0 1 * * 0" (at 01:00am, every Sunday)
|
||||
statuses-cleanup-cron: "0 1 * * 0"
|
||||
|
||||
# Integer duration.
|
||||
#
|
||||
# Examples: ["6 months", "1 year", "2 years"]
|
||||
# Default: "0s" (i.e. disabled)
|
||||
statuses-cleanup-remote-older-than: "0s"
|
||||
|
||||
# Int. Maximum number of statuses a user can schedule at time.
|
||||
# Examples: [300]
|
||||
# Default: 300
|
||||
|
||||
@@ -127,6 +127,17 @@ storage-s3-key-prefix: ""
|
||||
# Default: "auto"
|
||||
storage-s3-bucket-lookup: "auto"
|
||||
|
||||
# String. Region containing the bucket.
|
||||
#
|
||||
# If this is left blank, the region for the bucket will be automatically detected
|
||||
# using GetBucketLocation during startup.
|
||||
#
|
||||
# This can safely be left blank unless your provider does not support the GetBucketLocation Action.
|
||||
#
|
||||
# Examples: ["", "us-east-2"]
|
||||
# Default: ""
|
||||
storage-s3-region: ""
|
||||
|
||||
cache:
|
||||
# cache.s3-object-info (if set) enables caching
|
||||
# of S3 object information in the storage driver.
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 278 KiB |
@@ -31,6 +31,7 @@
|
||||
// - admin:read:domain_blocks: grants admin read access to domain blocks
|
||||
// - admin:read:domain_limits: grants admin read access to domain limits
|
||||
// - admin:read:instances: grants admin read access to instances
|
||||
// - admin:read:relays: grants admin read access to relays
|
||||
// - admin:read:reports: grants admin read access to reports
|
||||
// - admin:write: grants admin write access to everything
|
||||
// - admin:write:accounts: grants write read access to accounts
|
||||
@@ -38,6 +39,8 @@
|
||||
// - admin:write:domain_allows: grants admin write access to domain allows
|
||||
// - admin:write:domain_blocks: grants admin write access to domain blocks
|
||||
// - admin:write:domain_limits: grants admin write access to domain limits
|
||||
// - admin:write:instances: grants admin write access to instances
|
||||
// - admin:write:relays: grants admin write access to relays
|
||||
// - admin:write:reports: grants admin write access to reports
|
||||
// - profile: grants read access to verify_credentials
|
||||
// - push: grants read/write access to push
|
||||
@@ -53,6 +56,7 @@
|
||||
// - read:lists: grants read access to lists
|
||||
// - read:mutes: grants read access to mutes
|
||||
// - read:notifications: grants read access to notifications
|
||||
// - read:relays: grants read access to relays
|
||||
// - read:search: grants read access to search
|
||||
// - read:statuses: grants read access to statuses
|
||||
// - write: grants write access to everything
|
||||
@@ -68,6 +72,7 @@
|
||||
// - write:media: grants write access to media
|
||||
// - write:mutes: grants write access to mutes
|
||||
// - write:notifications: grants write access to notifications
|
||||
// - write:relays: grants write access to relays
|
||||
// - write:reports: grants write access to reports
|
||||
// - write:statuses: grants write access to statuses
|
||||
//
|
||||
@@ -95,6 +100,7 @@
|
||||
// admin:read:domain_blocks: grants admin read access to domain blocks
|
||||
// admin:read:domain_limits: grants admin read access to domain limits
|
||||
// admin:read:instances: grants admin read access to instances
|
||||
// admin:read:relays: grants admin read access to relays
|
||||
// admin:read:reports: grants admin read access to reports
|
||||
// admin:write: grants admin write access to everything
|
||||
// admin:write:accounts: grants write read access to accounts
|
||||
@@ -102,6 +108,8 @@
|
||||
// admin:write:domain_allows: grants admin write access to domain allows
|
||||
// admin:write:domain_blocks: grants admin write access to domain blocks
|
||||
// admin:write:domain_limits: grants admin write access to domain limits
|
||||
// admin:write:instances: grants admin write access to instances
|
||||
// admin:write:relays: grants admin write access to relays
|
||||
// admin:write:reports: grants admin write access to reports
|
||||
// profile: grants read access to verify_credentials
|
||||
// push: grants read/write access to push
|
||||
@@ -117,6 +125,7 @@
|
||||
// read:lists: grants read access to lists
|
||||
// read:mutes: grants read access to mutes
|
||||
// read:notifications: grants read access to notifications
|
||||
// read:relays: grants read access to relays
|
||||
// read:search: grants read access to search
|
||||
// read:statuses: grants read access to statuses
|
||||
// write: grants write access to everything
|
||||
@@ -132,6 +141,7 @@
|
||||
// write:media: grants write access to media
|
||||
// write:mutes: grants write access to mutes
|
||||
// write:notifications: grants write access to notifications
|
||||
// write:relays: grants write access to relays
|
||||
// write:reports: grants write access to reports
|
||||
// write:statuses: grants write access to statuses
|
||||
// OAuth2 Application:
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
# Relay Pushes
|
||||
|
||||
GoToSocial allows you to create relay push connections, in order to forward your posts to relays which will then broadcast them to all instances that are connected (subscribed) to the relay. By opting in to sending your posts to one or more relays, you make it more likely that your posts will be seen by ActivityPub users who don't follow you directly and might not have encountered you otherwise.
|
||||
|
||||
{%
|
||||
include "../.fragments/relay.md"
|
||||
start='<!--what-is-a-relay-start-->'
|
||||
end='<!--what-is-a-relay-end-->'
|
||||
%}
|
||||
|
||||
## What happens when you create a relay push connection?
|
||||
|
||||
When you create a relay push connection (see below), the service actor for your instance will send a follow request to the relay actor to indicate that you wish to take part in the relay. When the follow request is accepted, either automatically, or pending a manual action by the administrator of the relay, your instance's service actor will begin to send your posts to the inbox of the relay. The relay actor will then distribute your posts by forwarding them to relay subscribers.
|
||||
|
||||
{%
|
||||
include "../.fragments/relay.md"
|
||||
start='<!--relay-not-private-start-->'
|
||||
end='<!--relay-not-private-end-->'
|
||||
%}
|
||||
|
||||
## Create a relay push connection
|
||||
|
||||
You can configure pushing your posts to a relay by using the [user settings panel](./settings.md) to input a relay actor URI, and then select flags that you would like to apply to the relay connection.
|
||||
|
||||
!!! tip "Relay connection must be approved by relay owner"
|
||||
After a relay push connection is created, you must wait for approval from the relay owner before the connection will become active. This approval may be instantaneous + automatic, or may never happen at all! Some relay admins require that you message or email them *before* creating a connection, so make sure you take account of this.
|
||||
|
||||
{%
|
||||
include "../.fragments/relay.md"
|
||||
start='<!--relay-actor-uri-start-->'
|
||||
end='<!--relay-actor-uri-end-->'
|
||||
%}
|
||||
|
||||
### Flags
|
||||
|
||||
The flag checkboxes allow you to customize which of your posts should be pushed to a relay.
|
||||
|
||||
<dl>
|
||||
|
||||
<dt><strong>Send public visibility posts:</strong></dt>
|
||||
<dd>By checking this flag, you instruct GoToSocial to send your Public posts to the relay. If the box is not checked, then posts you create with Public visibility will never be pushed to the relay.</dd>
|
||||
|
||||
<dt><strong>Send unlisted visibility posts:</strong></dt>
|
||||
<dd>By checking this flag, you instruct GoToSocial to send your Unlisted (aka Unlocked, aka Quiet Public) posts to the relay. If the box is not checked, then posts with Unlisted visibility will never be pushed to the relay.</dd>
|
||||
|
||||
<dt><strong>Never send posts marked as sensitive:</strong></dt>
|
||||
<dd>With this flag checked, GoToSocial will never push a post of yours to the relay connection if that post is marked "sensitive" by a content warning or inclusion of sensitive media attachments.</dd>
|
||||
|
||||
<dt><strong>Never send posts with media:</strong></dt>
|
||||
<dd>With this flag checked, GoToSocial will never push a post of yours to the relay connection if that post include media attachments.</dd>
|
||||
|
||||
<dt><strong>Never send replies/comments:</strong></dt>
|
||||
<dd>With this flag checked, GoToSocial will never send your replies to other accounts' posts to the relay, it will only send top-level posts and self-replies within a thread.</dd>
|
||||
|
||||
<dt><strong>Match posts by default:</strong></dt>
|
||||
<dd>With this flag checked, you tell GoToSocial that all of your posts should be matched by default. In other words, all of your posts (of appropriate visibilities) that are not ignored because of other flags will be sent to the relay, unless their content is matched by an exclude matcher. With the flag unchecked, posts of yours will only be pushed to the relay if their content is matched by one or more matchers.</dd>
|
||||
|
||||
</dl>
|
||||
|
||||
{%
|
||||
include "../.fragments/relay.md"
|
||||
start='<!--relay-matchers-start-->'
|
||||
end='<!--relay-matchers-end-->'
|
||||
%}
|
||||
@@ -443,3 +443,7 @@ On the details page for an application, you can view the application's client ID
|
||||
If you included the settings panel callback URL in your redirect URIs list, you can also use this page to request an access token for your account. This will redirect you to the sign in page for your instance, where you must provide your credentials in order to authorize your application. You will then be redirected again to the settings panel callback URL, where you can receive your access token.
|
||||
|
||||
You can also use this page to delete your application. When a managed application is deleted, all tokens that were created via that application will also be deleted, so ensure you only do this when your application is not being used.
|
||||
|
||||
## Relay Pushes
|
||||
|
||||
Using this section, you can view, create, and remove relay push connections that send your posts to relays. For more information on relay push connections, please see the dedicated [relay pushes](./relay-pushes.md) document.
|
||||
|
||||
+241
-34
@@ -524,15 +524,11 @@ instance-deliver-to-shared-inboxes: true
|
||||
# Default: false
|
||||
instance-inject-mastodon-version: false
|
||||
|
||||
# String. 24hr time of day formatted as hh:mm.
|
||||
# Examples: ["14:30", "00:00", "04:00"]
|
||||
# Default: "23:00" (11pm).
|
||||
instance-subscriptions-process-from: "23:00"
|
||||
|
||||
# Duration. Period between subscription updates.
|
||||
# Examples: ["24h", "72h", "12h"]
|
||||
# Default: "24h" (once per day).
|
||||
instance-subscriptions-process-every: "24h"
|
||||
# Cron expression (see https://crontab.guru/ for help).
|
||||
#
|
||||
# Examples: ["0 0 * * *", "30 0 * * *", "0 0 * * 0"]
|
||||
# Default: "0 23 * * *" (at 23:00pm, every day)
|
||||
instance-subscriptions-process-cron: "0 23 * * *"
|
||||
|
||||
# String. Allows you to customize if and how stats are served to
|
||||
# crawlers at the /api/v1|v2/instance and /nodeinfo endpoints.
|
||||
@@ -782,30 +778,17 @@ media-ffmpeg-pool-size: 1
|
||||
# what these settings do, with some customization examples, see the docs:
|
||||
# https://docs.gotosocial.org/en/latest/admin/media_caching#cleanup
|
||||
|
||||
# Int. Number of days to cache media from remote instances before
|
||||
# they are removed from the cache. When remote media is removed from
|
||||
# the cache, it is deleted from storage but the database entries for
|
||||
# the media are kept so that it can be fetched again if requested by a user.
|
||||
#
|
||||
# If this is set to 0, then media from remote instances will be cached indefinitely.
|
||||
#
|
||||
# Examples: [30, 60, 7, 0]
|
||||
# Default: 7
|
||||
media-remote-cache-days: 7
|
||||
# Integer duration.
|
||||
#
|
||||
# Examples: ["7 days", "1 week", "1 month"]
|
||||
# Default: "7 days"
|
||||
media-remote-cache-duration: "7 days"
|
||||
|
||||
# String. 24hr time of day formatted as hh:mm.
|
||||
# Examples: ["14:30", "00:00", "04:00"]
|
||||
# Default: "00:00" (midnight).
|
||||
media-cleanup-from: "00:00"
|
||||
|
||||
# Duration. Period between media cleanup runs.
|
||||
# More than once per 24h is not recommended
|
||||
# is likely overkill. Setting this to something
|
||||
# very low like once every 10 minutes will probably
|
||||
# cause lag and possibly other issues.
|
||||
# Examples: ["24h", "72h", "12h"]
|
||||
# Default: "24h" (once per day).
|
||||
media-cleanup-every: "24h"
|
||||
# Cron expression (see https://crontab.guru/ for help).
|
||||
#
|
||||
# Examples: ["0 0 * * *", "30 0 * * *", "0 0 * * 0"]
|
||||
# Default: "0 0 * * *" (at 00:00am, every day)
|
||||
media-cleanup-cron: "0 0 * * *"
|
||||
|
||||
##########################
|
||||
##### STORAGE CONFIG #####
|
||||
@@ -916,6 +899,17 @@ storage-s3-bucket: ""
|
||||
# Default: "auto"
|
||||
storage-s3-bucket-lookup: "auto"
|
||||
|
||||
# String. Region containing the bucket.
|
||||
#
|
||||
# If this is left blank, the region for the bucket will be automatically detected
|
||||
# using GetBucketLocation during startup.
|
||||
#
|
||||
# This can safely be left blank unless your provider does not support the GetBucketLocation Action.
|
||||
#
|
||||
# Examples: ["", "us-east-2"]
|
||||
# Default: ""
|
||||
storage-s3-region: ""
|
||||
|
||||
###########################
|
||||
##### STATUSES CONFIG #####
|
||||
###########################
|
||||
@@ -949,6 +943,18 @@ statuses-poll-option-max-chars: 50
|
||||
# Default: 6
|
||||
statuses-media-max-files: 6
|
||||
|
||||
# Cron expression (see https://crontab.guru/ for help).
|
||||
#
|
||||
# Examples: ["0 0 * * *", "30 0 * * *", "0 0 * * 0"]
|
||||
# Default: "0 1 * * 0" (at 01:00am, every Sunday)
|
||||
statuses-cleanup-cron: "0 1 * * 0"
|
||||
|
||||
# Integer duration.
|
||||
#
|
||||
# Examples: ["6 months", "1 year", "2 years"]
|
||||
# Default: "0s" (i.e. disabled)
|
||||
statuses-cleanup-remote-older-than: "0s"
|
||||
|
||||
# Int. Maximum number of statuses a user can schedule at time.
|
||||
# Examples: [300]
|
||||
# Default: 300
|
||||
@@ -1220,8 +1226,8 @@ http-client:
|
||||
# is exceeded, the connection to the remote server will be dropped.
|
||||
# A value of 0s indicates no timeout: this is not advised!
|
||||
# Examples: ["5s", "30s", "0s"]
|
||||
# Default: "30s"
|
||||
timeout: "30s"
|
||||
# Default: "1m"
|
||||
timeout: "1m"
|
||||
|
||||
########################################
|
||||
#### RESERVED IP RANGE EXCEPTIONS ######
|
||||
@@ -1278,6 +1284,207 @@ http-client:
|
||||
#
|
||||
# Default: false
|
||||
insecure-outgoing: false
|
||||
|
||||
##########################################################################
|
||||
##### Options passed directly to the HTTP client's http.Transport{}: #####
|
||||
##########################################################################
|
||||
|
||||
# NOTE: all of the below are internal HTTP client configuration flags,
|
||||
# you should not uncomment + tweak these unless you know what you're doing!
|
||||
#
|
||||
# The commented-out values are set to the sensible values GtS uses by default.
|
||||
|
||||
# DisableKeepAlives, if true, disables HTTP keep-alives and will only use the
|
||||
# connection to the server for a single HTTP request.
|
||||
#
|
||||
# This is unrelated to the similarly named TCP keep-alives.
|
||||
#
|
||||
# Default: false
|
||||
#disable-keep-alives: false
|
||||
|
||||
# MaxConnsPerHost optionally limits the total number of connections per host,
|
||||
# including connections in the dialing, active, and idle states. On limit
|
||||
# violation, dials will block.
|
||||
#
|
||||
# Default: 6
|
||||
#max-conns-per-host: 6
|
||||
|
||||
# MaxIdleConns controls the maximum number of idle (keep-alive) connections across
|
||||
# all hosts. Zero means no limit.
|
||||
#
|
||||
# Default: 0 (no limit)
|
||||
#max-idle-conns: 0
|
||||
|
||||
# MaxIdleConnsPerHost, if non-zero, controls the maximum idle (keep-alive)
|
||||
# connections to keep per-host. If zero, DefaultMaxIdleConnsPerHost (2) is used.
|
||||
#
|
||||
# Default: 0 (use built-in default 2)
|
||||
#max-idle-conns-per-host: 0
|
||||
|
||||
# IdleConnTimeout is the maximum amount of time an idle (keep-alive) connection
|
||||
# will remain idle before closing itself. Zero means no limit.
|
||||
#
|
||||
# Default: 5 minutes
|
||||
#idle-conn-timeout: "5 minutes"
|
||||
|
||||
# TLSHandshakeTimeout specifies the maximum amount of time to wait for a TLS
|
||||
# handshake. Zero means no timeout.
|
||||
#
|
||||
# Default: 0 (no limit)
|
||||
#tls-handshake-timeout: "0"
|
||||
|
||||
# ResponseHeaderTimeout, if non-zero, specifies the amount of time to wait for a
|
||||
# server's response headers after fully writing the request (including its body, if any).
|
||||
# This time does not include the time to read the response body.
|
||||
#
|
||||
# Default: 0 (no limit)
|
||||
#response-header-timeout: "0"
|
||||
|
||||
# ReadBufferSize specifies the size of the read buffer used when reading from the transport.
|
||||
# If zero, a default (currently 4KB) is used.
|
||||
#
|
||||
# Default: 0 (use built-in default 4KB)
|
||||
#read-buffer-size: "0"
|
||||
|
||||
# WriteBufferSize specifies the size of the write buffer used when writing to the transport.
|
||||
# If zero, a default (currently 4KB) is used.
|
||||
#
|
||||
# Default: 0 (use built-in default 4KB)
|
||||
#write-buffer-size: "0"
|
||||
|
||||
################################
|
||||
##### HTTP SERVER SETTINGS #####
|
||||
################################
|
||||
|
||||
# Settings for INCOMING http server connections received by GoToSocial.
|
||||
#
|
||||
# WARNING: all of the below are internal HTTP server configuration flags,
|
||||
# you should not uncomment + tweak these unless you know what you're doing!
|
||||
# They are set to sensible defaults and you shouldn't need to change them.
|
||||
http-server:
|
||||
|
||||
#######################################################################
|
||||
##### Options passed directly to our HTTP router's gin.Engine{}: #####
|
||||
#######################################################################
|
||||
|
||||
# MaxMultipartMemory value of 'maxMemory' param that is given to
|
||||
# http.Request's ParseMultipartForm method call.
|
||||
#
|
||||
# Default: 40MiB
|
||||
#max-multipart-memory: "40MiB"
|
||||
|
||||
# Enable h2c (http2 without TLS).
|
||||
#
|
||||
# May be useful if your reverse proxy supports connecting to GoToSocial via HTTP2.
|
||||
#
|
||||
# Default: false
|
||||
#use-h2c: false
|
||||
|
||||
#######################################################################
|
||||
##### Options passed directly to our base http.Server{} instance: #####
|
||||
#######################################################################
|
||||
|
||||
# ReadTimeout is the maximum duration for reading the entire request,
|
||||
# including the body. A zero or negative value means there will be no timeout.
|
||||
#
|
||||
# Because ReadTimeout does not let Handlers make per-request decisions on
|
||||
# each request body's acceptable deadline or upload rate, most users will prefer
|
||||
# to use ReadHeaderTimeout. It is valid to use them both.
|
||||
#
|
||||
# Default: 60s
|
||||
#read-timeout: "60s"
|
||||
|
||||
# ReadHeaderTimeout is the amount of time allowed to read request headers.
|
||||
# The connection's read deadline is reset after reading the headers and the
|
||||
# Handler can decide what is considered too slow for the body. If zero, the
|
||||
# value of ReadTimeout is used. If negative, or if zero and ReadTimeout is
|
||||
# zero or negative, there is no timeout.
|
||||
#
|
||||
# Default: 0 (use read-timeout duration).
|
||||
#read-header-timeout: "0"
|
||||
|
||||
# WriteTimeout is the maximum duration before timing out writes of the response.
|
||||
# It is reset whenever a new request's header is read. Like ReadTimeout, it does
|
||||
# not let Handlers make decisions on a per-request basis. A zero or negative value
|
||||
# means there will be no timeout.
|
||||
#
|
||||
# Default: 30s
|
||||
#write-timeout: "30s"
|
||||
|
||||
# IdleTimeout is the maximum amount of time to wait for the next request when
|
||||
# keep-alives are enabled. If zero, the value of ReadTimeout is used. If negative,
|
||||
# or if zero and ReadTimeout is zero or negative, there is no timeout.
|
||||
#
|
||||
# Default: 30s
|
||||
#idle-timeout: "30s"
|
||||
|
||||
# MaxHeaderBytes controls the maximum number of bytes the server will read parsing
|
||||
# the request header's keys and values, including the request line. It does not limit
|
||||
# the size of the request body. If zero, DefaultMaxHeaderBytes is used.
|
||||
#
|
||||
# Default: 0 (use built-in default 1048576, ie., 1 mebibyte)
|
||||
#max-header-bytes: "0"
|
||||
|
||||
############################################################################
|
||||
##### Options passed directly to our HTTP server's http.HTTP2Config{}: #####
|
||||
############################################################################
|
||||
|
||||
# MaxConcurrentStreams optionally specifies the number of concurrent streams that a
|
||||
# peer may have open at a time. If zero, MaxConcurrentStreams defaults to at least 100.
|
||||
#
|
||||
# Default: 0 (at least 100)
|
||||
#max-concurrent-streams: "0"
|
||||
|
||||
# MaxDecoderHeaderTableSize optionally specifies an upper limit for the size of the header
|
||||
# compression table used for decoding headers sent by the peer. A valid value is less than
|
||||
# 4MiB. If zero or invalid, a default value is used.
|
||||
#
|
||||
# Default: 0 (use built-in default 4096, ie., 4 kibibytes)
|
||||
#max-decoder-header-table-size: "0"
|
||||
|
||||
# MaxEncoderHeaderTableSize optionally specifies an upper limit for the header compression
|
||||
# table used for sending headers to the peer. A valid value is less than 4MiB. If zero or
|
||||
# invalid, a default value is used.
|
||||
#
|
||||
# Default: 0 (use built-in default 4096, ie., 4 kibibytes)
|
||||
#max-encoder-header-table-size: "0"
|
||||
|
||||
# MaxReadFrameSize optionally specifies the largest frame this endpoint is willing to read.
|
||||
# A valid value is between 16KiB and 16MiB, inclusive. If zero or invalid, a default value is used.
|
||||
#
|
||||
# Default: 0 (use built-in default 1048576, ie., 1 mebibyte)
|
||||
#max-read-frame-size: "0"
|
||||
|
||||
# MaxReceiveBufferPerConnection is the maximum size of the flow control window for data received
|
||||
# on a connection. A valid value is at least 64KiB and less than 4MiB. If invalid, a default value
|
||||
# is used.
|
||||
#
|
||||
# Default: 0 (use built-in default 1048576, ie., 1 mebibyte)
|
||||
#max-receive-buffer-per-connection: "0"
|
||||
|
||||
# MaxReceiveBufferPerStream is the maximum size of the flow control window for data received on a
|
||||
# stream (request). A valid value is less than 4MiB. If zero or invalid, a default value is used.
|
||||
#
|
||||
# Default: 0 (use built-in default 1048576, ie., 1 mebibyte)
|
||||
#max-receive-buffer-per-stream: "0"
|
||||
|
||||
# SendPingTimeout is the timeout after which a health check using a ping frame will be carried out
|
||||
# if no frame is received on a connection. If zero, no health check is performed.
|
||||
#
|
||||
# Default: 0
|
||||
#send-ping-timeout: "0"
|
||||
|
||||
# PingTimeout is the timeout after which a connection will be closed if a response to a ping is not
|
||||
# received. If zero, a default of 15 seconds is used.
|
||||
#
|
||||
# Default: 0 (use built-in default 15s)
|
||||
#ping-timeout: "0"
|
||||
|
||||
# WriteByteTimeout is the timeout after which a connection will be closed if no data can be written to it.
|
||||
# The timeout begins when data is available to write, and is extended whenever any bytes are written.
|
||||
#
|
||||
# Default: 0
|
||||
#write-byte-timeout: "0"
|
||||
|
||||
#############################
|
||||
##### ADVANCED SETTINGS #####
|
||||
|
||||
@@ -3,34 +3,35 @@ module code.superseriousbusiness.org/gotosocial
|
||||
go 1.25.0
|
||||
|
||||
// Replace modernc/sqlite with our version that fixes the concurrency INTERRUPT issue
|
||||
replace modernc.org/sqlite => gitlab.com/NyaaaWhatsUpDoc/sqlite v1.49.1-concurrency-workaround
|
||||
replace modernc.org/sqlite => gitlab.com/NyaaaWhatsUpDoc/sqlite v1.53.0-concurrency-workaround
|
||||
|
||||
// Replace gin with our own fork of v1.11.0 with yet-to-be-released array binding fix
|
||||
replace github.com/gin-gonic/gin => codeberg.org/superseriousbusiness/gin v1.11.0-array-binding-fix-2
|
||||
|
||||
require (
|
||||
code.superseriousbusiness.org/activity v1.19.0
|
||||
code.superseriousbusiness.org/exif-terminator v0.11.2
|
||||
code.superseriousbusiness.org/gopkg v0.0.0-20260314203815-181df26aa86e
|
||||
code.superseriousbusiness.org/exif-terminator v0.11.4
|
||||
code.superseriousbusiness.org/gopkg v0.0.0-20260623123713-4ead213a2d22
|
||||
code.superseriousbusiness.org/httpsig v1.5.0
|
||||
code.superseriousbusiness.org/oauth2/v4 v4.5.4-0.20250812115401-3961e46a7384
|
||||
codeberg.org/gruf/go-bytesize v1.0.4
|
||||
codeberg.org/gruf/go-byteutil v1.3.0
|
||||
codeberg.org/gruf/go-cache/v3 v3.6.2
|
||||
codeberg.org/gruf/go-caller v0.0.0-20250806133437-db8d0b1f71cf
|
||||
codeberg.org/gruf/go-caller v0.0.0-20260615202027-bc5a6ff3cb31
|
||||
codeberg.org/gruf/go-debug v1.3.0
|
||||
codeberg.org/gruf/go-errors/v2 v2.3.4
|
||||
codeberg.org/gruf/go-fastcopy v1.1.3
|
||||
codeberg.org/gruf/go-fastcopy v1.2.1
|
||||
codeberg.org/gruf/go-fastpath/v2 v2.0.1
|
||||
codeberg.org/gruf/go-ffmpreg v0.6.19
|
||||
codeberg.org/gruf/go-ffmpreg v0.6.20
|
||||
codeberg.org/gruf/go-iotools v0.0.0-20240710125620-934ae9c654cf
|
||||
codeberg.org/gruf/go-kv/v2 v2.0.10
|
||||
codeberg.org/gruf/go-list v0.0.0-20240425093752-494db03d641f
|
||||
codeberg.org/gruf/go-longdur v0.1.3
|
||||
codeberg.org/gruf/go-mempool v0.0.0-20251205182607-a05549c9a993
|
||||
codeberg.org/gruf/go-mmap v0.0.0-20251111184116-345a42dab154
|
||||
codeberg.org/gruf/go-mutexes v1.5.9
|
||||
codeberg.org/gruf/go-runners v1.7.0
|
||||
codeberg.org/gruf/go-sched v1.3.0
|
||||
codeberg.org/gruf/go-sched v1.3.1
|
||||
codeberg.org/gruf/go-split v1.2.0
|
||||
codeberg.org/gruf/go-storage v0.6.0
|
||||
codeberg.org/gruf/go-structr v0.9.16
|
||||
@@ -38,7 +39,7 @@ require (
|
||||
github.com/KimMachineGun/automemlimit v0.7.5
|
||||
github.com/SherClockHolmes/webpush-go v1.4.0
|
||||
github.com/buckket/go-blurhash v1.1.0
|
||||
github.com/coreos/go-oidc/v3 v3.18.0
|
||||
github.com/coreos/go-oidc/v3 v3.19.0
|
||||
github.com/gin-contrib/cors v1.7.7
|
||||
github.com/gin-contrib/gzip v1.2.6
|
||||
github.com/gin-contrib/sessions v1.1.0
|
||||
@@ -49,13 +50,15 @@ require (
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/gorilla/feeds v1.2.0
|
||||
github.com/gorilla/websocket v1.5.3
|
||||
github.com/jackc/pgx/v5 v5.9.2
|
||||
github.com/hashicorp/cronexpr v1.1.3
|
||||
github.com/jackc/pgx/v5 v5.10.0
|
||||
github.com/k3a/html2text v1.4.0
|
||||
github.com/klauspost/compress v1.18.6
|
||||
github.com/microcosm-cc/bluemonday v1.0.27
|
||||
github.com/miekg/dns v1.1.72
|
||||
github.com/minio/minio-go/v7 v7.0.100
|
||||
github.com/minio/minio-go/v7 v7.2.0
|
||||
github.com/mitchellh/mapstructure v1.5.0
|
||||
github.com/ncruces/go-sqlite3 v0.34.0
|
||||
github.com/ncruces/go-sqlite3 v0.35.1
|
||||
github.com/oklog/ulid/v2 v2.1.1
|
||||
github.com/pquerna/otp v1.5.0
|
||||
github.com/prometheus/client_golang v1.23.2
|
||||
@@ -65,9 +68,9 @@ require (
|
||||
github.com/spf13/pflag v1.0.10
|
||||
github.com/spf13/viper v1.21.0
|
||||
github.com/stretchr/testify v1.11.1
|
||||
github.com/tdewolff/minify/v2 v2.24.12
|
||||
github.com/tdewolff/minify/v2 v2.24.13
|
||||
github.com/temoto/robotstxt v1.1.2
|
||||
github.com/tetratelabs/wazero v1.11.0
|
||||
github.com/tetratelabs/wazero v1.12.0
|
||||
github.com/tomnomnom/linkheader v0.0.0-20250811210735-e5fe3b51442e
|
||||
github.com/ulule/limiter/v3 v3.11.2
|
||||
github.com/uptrace/bun v1.2.18
|
||||
@@ -76,24 +79,24 @@ require (
|
||||
github.com/uptrace/bun/extra/bunotel v1.2.18
|
||||
github.com/wagslane/go-password-validator v0.3.0
|
||||
github.com/yuin/goldmark v1.8.2
|
||||
go.opentelemetry.io/contrib/exporters/autoexport v0.68.0
|
||||
go.opentelemetry.io/contrib/instrumentation/runtime v0.68.0
|
||||
go.opentelemetry.io/otel v1.43.0
|
||||
go.opentelemetry.io/otel/exporters/prometheus v0.65.0
|
||||
go.opentelemetry.io/otel/metric v1.43.0
|
||||
go.opentelemetry.io/otel/sdk v1.43.0
|
||||
go.opentelemetry.io/otel/sdk/metric v1.43.0
|
||||
go.opentelemetry.io/otel/trace v1.43.0
|
||||
go.opentelemetry.io/contrib/exporters/autoexport v0.69.0
|
||||
go.opentelemetry.io/contrib/instrumentation/runtime v0.69.0
|
||||
go.opentelemetry.io/otel v1.44.0
|
||||
go.opentelemetry.io/otel/exporters/prometheus v0.66.0
|
||||
go.opentelemetry.io/otel/metric v1.44.0
|
||||
go.opentelemetry.io/otel/sdk v1.44.0
|
||||
go.opentelemetry.io/otel/sdk/metric v1.44.0
|
||||
go.opentelemetry.io/otel/trace v1.44.0
|
||||
go.uber.org/automaxprocs v1.6.0
|
||||
golang.org/x/crypto v0.50.0
|
||||
golang.org/x/image v0.39.0
|
||||
golang.org/x/net v0.53.0
|
||||
golang.org/x/crypto v0.53.0
|
||||
golang.org/x/image v0.43.0
|
||||
golang.org/x/net v0.56.0
|
||||
golang.org/x/oauth2 v0.36.0
|
||||
golang.org/x/sys v0.43.0
|
||||
golang.org/x/text v0.36.0
|
||||
golang.org/x/sys v0.46.0
|
||||
golang.org/x/text v0.38.0
|
||||
gopkg.in/mcuadros/go-syslog.v2 v2.3.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
modernc.org/sqlite v1.49.1
|
||||
modernc.org/sqlite v1.53.0
|
||||
mvdan.cc/xurls/v2 v2.6.0
|
||||
)
|
||||
|
||||
@@ -106,7 +109,7 @@ require (
|
||||
codeberg.org/gruf/go-xunsafe v0.0.0-20260211121553-1c99e9364fee // indirect
|
||||
dario.cat/mergo v1.0.2 // indirect
|
||||
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||
github.com/Masterminds/semver/v3 v3.4.0 // indirect
|
||||
github.com/Masterminds/semver/v3 v3.5.0 // indirect
|
||||
github.com/Masterminds/sprig/v3 v3.3.0 // indirect
|
||||
github.com/aymerick/douceur v0.2.0 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
@@ -125,41 +128,41 @@ require (
|
||||
github.com/dsoprea/go-utility/v2 v2.0.0-20221003172846-a3e1774ef349 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.10.1 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.12 // indirect
|
||||
github.com/gin-contrib/sse v1.1.0 // indirect
|
||||
github.com/go-errors/errors v1.4.2 // indirect
|
||||
github.com/go-ini/ini v1.67.0 // indirect
|
||||
github.com/go-jose/go-jose/v4 v4.1.4 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-openapi/analysis v0.24.3 // indirect
|
||||
github.com/go-openapi/analysis v0.25.0 // indirect
|
||||
github.com/go-openapi/errors v0.22.7 // indirect
|
||||
github.com/go-openapi/inflect v0.21.5 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.22.5 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.23.1 // indirect
|
||||
github.com/go-openapi/jsonreference v0.21.5 // indirect
|
||||
github.com/go-openapi/loads v0.23.3 // indirect
|
||||
github.com/go-openapi/runtime v0.29.3 // indirect
|
||||
github.com/go-openapi/runtime v0.32.2 // indirect
|
||||
github.com/go-openapi/runtime/server-middleware v0.32.1 // indirect
|
||||
github.com/go-openapi/spec v0.22.4 // indirect
|
||||
github.com/go-openapi/strfmt v0.26.1 // indirect
|
||||
github.com/go-openapi/swag v0.25.5 // indirect
|
||||
github.com/go-openapi/swag/cmdutils v0.25.5 // indirect
|
||||
github.com/go-openapi/swag/conv v0.25.5 // indirect
|
||||
github.com/go-openapi/swag/fileutils v0.25.5 // indirect
|
||||
github.com/go-openapi/swag/jsonname v0.25.5 // indirect
|
||||
github.com/go-openapi/swag/jsonutils v0.25.5 // indirect
|
||||
github.com/go-openapi/swag/loading v0.25.5 // indirect
|
||||
github.com/go-openapi/swag/mangling v0.25.5 // indirect
|
||||
github.com/go-openapi/swag/netutils v0.25.5 // indirect
|
||||
github.com/go-openapi/swag/stringutils v0.25.5 // indirect
|
||||
github.com/go-openapi/swag/typeutils v0.25.5 // indirect
|
||||
github.com/go-openapi/swag/yamlutils v0.25.5 // indirect
|
||||
github.com/go-openapi/strfmt v0.26.2 // indirect
|
||||
github.com/go-openapi/swag v0.26.0 // indirect
|
||||
github.com/go-openapi/swag/cmdutils v0.26.0 // indirect
|
||||
github.com/go-openapi/swag/conv v0.26.0 // indirect
|
||||
github.com/go-openapi/swag/fileutils v0.26.0 // indirect
|
||||
github.com/go-openapi/swag/jsonname v0.26.0 // indirect
|
||||
github.com/go-openapi/swag/jsonutils v0.26.0 // indirect
|
||||
github.com/go-openapi/swag/loading v0.26.0 // indirect
|
||||
github.com/go-openapi/swag/mangling v0.26.0 // indirect
|
||||
github.com/go-openapi/swag/netutils v0.26.0 // indirect
|
||||
github.com/go-openapi/swag/stringutils v0.26.0 // indirect
|
||||
github.com/go-openapi/swag/typeutils v0.26.0 // indirect
|
||||
github.com/go-openapi/swag/yamlutils v0.26.0 // indirect
|
||||
github.com/go-openapi/validate v0.25.2 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.30.1 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.5.0 // indirect
|
||||
github.com/go-xmlfmt/xmlfmt v0.0.0-20191208150333-d5b6f63a941b // indirect
|
||||
github.com/go-xmlfmt/xmlfmt v1.1.3 // indirect
|
||||
github.com/goccy/go-json v0.10.5 // indirect
|
||||
github.com/goccy/go-yaml v1.19.2 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.3.1 // indirect
|
||||
@@ -169,7 +172,7 @@ require (
|
||||
github.com/gorilla/handlers v1.5.2 // indirect
|
||||
github.com/gorilla/securecookie v1.1.2 // indirect
|
||||
github.com/gorilla/sessions v1.4.0 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 // indirect
|
||||
github.com/huandu/xstrings v1.5.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
@@ -178,7 +181,6 @@ require (
|
||||
github.com/jessevdk/go-flags v1.6.1 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/compress v1.18.2 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
|
||||
github.com/klauspost/crc32 v1.3.0 // indirect
|
||||
github.com/kr/pretty v0.3.1 // indirect
|
||||
@@ -192,11 +194,11 @@ require (
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/ncruces/go-sqlite3-wasm/v2 v2.1.35300 // indirect
|
||||
github.com/ncruces/go-sqlite3-wasm/v3 v3.1.35302 // indirect
|
||||
github.com/ncruces/go-strftime v1.0.0 // indirect
|
||||
github.com/ncruces/julianday v1.0.0 // indirect
|
||||
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.3.1 // indirect
|
||||
github.com/philhofer/fwd v1.2.0 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
@@ -211,12 +213,11 @@ require (
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/rogpeppe/go-internal v1.14.1 // indirect
|
||||
github.com/rs/xid v1.6.0 // indirect
|
||||
github.com/sagikazarmark/locafero v0.11.0 // indirect
|
||||
github.com/sagikazarmark/locafero v0.12.0 // indirect
|
||||
github.com/shopspring/decimal v1.4.0 // indirect
|
||||
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect
|
||||
github.com/spf13/afero v1.15.0 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
github.com/tdewolff/parse/v2 v2.8.11 // indirect
|
||||
github.com/tdewolff/parse/v2 v2.8.12 // indirect
|
||||
github.com/tinylib/msgp v1.6.1 // indirect
|
||||
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect
|
||||
github.com/toqueteos/webbrowser v1.2.1 // indirect
|
||||
@@ -225,33 +226,35 @@ require (
|
||||
github.com/uptrace/opentelemetry-go-extra/otelsql v0.3.2 // indirect
|
||||
github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
|
||||
github.com/zeebo/xxh3 v1.1.0 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/contrib/bridges/prometheus v0.68.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.19.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.19.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.19.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel/log v0.19.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/log v0.19.0 // indirect
|
||||
go.opentelemetry.io/contrib/bridges/prometheus v0.69.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.20.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.20.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.44.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.44.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.44.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.44.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.44.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.20.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.44.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.44.0 // indirect
|
||||
go.opentelemetry.io/otel/log v0.20.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/log v0.20.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.10.0 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.4 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/arch v0.23.0 // indirect
|
||||
golang.org/x/mod v0.34.0 // indirect
|
||||
golang.org/x/sync v0.20.0 // indirect
|
||||
golang.org/x/tools v0.43.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260406210006-6f92a3bedf2d // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260406210006-6f92a3bedf2d // indirect
|
||||
google.golang.org/grpc v1.80.0 // indirect
|
||||
golang.org/x/mod v0.36.0 // indirect
|
||||
golang.org/x/sync v0.21.0 // indirect
|
||||
golang.org/x/tools v0.45.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa // indirect
|
||||
google.golang.org/grpc v1.81.1 // indirect
|
||||
google.golang.org/protobuf v1.36.11 // indirect
|
||||
gopkg.in/ini.v1 v1.67.2 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
modernc.org/libc v1.72.0 // indirect
|
||||
modernc.org/libc v1.73.4 // indirect
|
||||
modernc.org/mathutil v1.7.1 // indirect
|
||||
modernc.org/memory v1.11.0 // indirect
|
||||
)
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
code.superseriousbusiness.org/activity v1.19.0 h1:3WeE2+x5ZN4PQ3hF0YfxbnfJcD2uG8XAGIw3dc66CM0=
|
||||
code.superseriousbusiness.org/activity v1.19.0/go.mod h1:BTMWJIAuwDH1w+ieRP5N+T5LipbXjw35U6KZy0V/xdg=
|
||||
code.superseriousbusiness.org/exif-terminator v0.11.2 h1:nkTdaghZb6I0oGYFhTLSALhA2ShgkPnYAJryE5IE9+0=
|
||||
code.superseriousbusiness.org/exif-terminator v0.11.2/go.mod h1:/Z+3DHSrefCzzN5ePkGjVYKFErRimoeUf694Gz8Pn/Y=
|
||||
code.superseriousbusiness.org/exif-terminator v0.11.4 h1:rqgt5N2POhuJl1L1wsa90MQnKzyiZjFfjVYclTb2dus=
|
||||
code.superseriousbusiness.org/exif-terminator v0.11.4/go.mod h1:/Z+3DHSrefCzzN5ePkGjVYKFErRimoeUf694Gz8Pn/Y=
|
||||
code.superseriousbusiness.org/go-jpeg-image-structure/v2 v2.3.0 h1:r9uq8StaSHYKJ8DklR9Xy+E9c40G1Z8yj5TRGi8L6+4=
|
||||
code.superseriousbusiness.org/go-jpeg-image-structure/v2 v2.3.0/go.mod h1:IK1OlR6APjVB3E9tuYGvf0qXMrwP+TrzcHS5rf4wffQ=
|
||||
code.superseriousbusiness.org/go-png-image-structure/v2 v2.3.0 h1:I512jiIeXDC4//2BeSPrRM2ZS4wpBKUaPeTPxakMNGA=
|
||||
code.superseriousbusiness.org/go-png-image-structure/v2 v2.3.0/go.mod h1:SNHomXNW88o1pFfLHpD4KsCZLfcr4z5dm+xcX5SV10A=
|
||||
code.superseriousbusiness.org/gopkg v0.0.0-20260314203815-181df26aa86e h1:57ICwitbFxGPMJZG34srw20oou4RtqCGIrggy5/vazI=
|
||||
code.superseriousbusiness.org/gopkg v0.0.0-20260314203815-181df26aa86e/go.mod h1:PNtiv80R4b+z7HvnZbX69dAPmgRSDGts3SNcSC6Ti30=
|
||||
code.superseriousbusiness.org/gopkg v0.0.0-20260623123713-4ead213a2d22 h1:Pz5KlaHuNUpuJzIcnqmvsnJ68MrB35gffvFs2NGJfZo=
|
||||
code.superseriousbusiness.org/gopkg v0.0.0-20260623123713-4ead213a2d22/go.mod h1:f2KFg2HOM6YHEXR4wjnab9xPY2PmphjZaz7S6/+Y1Xc=
|
||||
code.superseriousbusiness.org/httpsig v1.5.0 h1:jw/qc//yYWSoOYytTZXHvW7yh8kceCipNIBfUeXQghA=
|
||||
code.superseriousbusiness.org/httpsig v1.5.0/go.mod h1:i2AKpj/WbA/o/UTvia9TAREzt0jP1AH3T1Uxjyhdzlw=
|
||||
code.superseriousbusiness.org/oauth2/v4 v4.5.4-0.20250812115401-3961e46a7384 h1:eJzULGUyhHGk2DdQxX/jbH9FKZOyoIF90p3dzukCfLA=
|
||||
@@ -18,18 +18,18 @@ codeberg.org/gruf/go-byteutil v1.3.0 h1:nRqJnCcRQ7xbfU6azw7zOzJrSMDIJHBqX6FL9vEM
|
||||
codeberg.org/gruf/go-byteutil v1.3.0/go.mod h1:chgnZz1LUcfaObaIFglxF5MRYQkJGjQf4WwVz95ccCM=
|
||||
codeberg.org/gruf/go-cache/v3 v3.6.2 h1:fQn7Dkj5gQpSNjlnoFeotXHKwbAh1PTH4qrD5BMm5ZA=
|
||||
codeberg.org/gruf/go-cache/v3 v3.6.2/go.mod h1:yjmrOyda2K8B5sAfCpPPAAqI3oewf4mNQmvxygmP+g8=
|
||||
codeberg.org/gruf/go-caller v0.0.0-20250806133437-db8d0b1f71cf h1:Rzu7WLpscj2w1N+ClIHlJoTYf9SuqZrZ7E4f9T7jGdw=
|
||||
codeberg.org/gruf/go-caller v0.0.0-20250806133437-db8d0b1f71cf/go.mod h1:jEyYiqCzH1TaxfclSFYthE32oI0dsMnRS6EHqy6y0uo=
|
||||
codeberg.org/gruf/go-caller v0.0.0-20260615202027-bc5a6ff3cb31 h1:yGDC2A1/WD2NuQ+dT9/7JNgPnTI/WAyEYX4y6ipjsA0=
|
||||
codeberg.org/gruf/go-caller v0.0.0-20260615202027-bc5a6ff3cb31/go.mod h1:jEyYiqCzH1TaxfclSFYthE32oI0dsMnRS6EHqy6y0uo=
|
||||
codeberg.org/gruf/go-debug v1.3.0 h1:PIRxQiWUFKtGOGZFdZ3Y0pqyfI0Xr87j224IYe2snZs=
|
||||
codeberg.org/gruf/go-debug v1.3.0/go.mod h1:N+vSy9uJBQgpQcJUqjctvqFz7tBHJf+S/PIjLILzpLg=
|
||||
codeberg.org/gruf/go-errors/v2 v2.3.4 h1:eC0UYpEx9y+aQgxv26JxjtYyN48OMil9JQ9cbHmw0ys=
|
||||
codeberg.org/gruf/go-errors/v2 v2.3.4/go.mod h1:SpaIhBvi+hd4ohaS3UQJxA1OVmzApHBy4IufOn7OkD0=
|
||||
codeberg.org/gruf/go-fastcopy v1.1.3 h1:Jo9VTQjI6KYimlw25PPc7YLA3Xm+XMQhaHwKnM7xD1g=
|
||||
codeberg.org/gruf/go-fastcopy v1.1.3/go.mod h1:GDDYR0Cnb3U/AIfGM3983V/L+GN+vuwVMvrmVABo21s=
|
||||
codeberg.org/gruf/go-fastcopy v1.2.1 h1:RUpiim4H7CJKo+4aj0RIaS9EOYJ/hb1S5DGdH9wwsyg=
|
||||
codeberg.org/gruf/go-fastcopy v1.2.1/go.mod h1:GDDYR0Cnb3U/AIfGM3983V/L+GN+vuwVMvrmVABo21s=
|
||||
codeberg.org/gruf/go-fastpath/v2 v2.0.1 h1:smCq3mpYiPibH4Na3SEN2rtr5oPmrY+cVRQ8nFCznJ4=
|
||||
codeberg.org/gruf/go-fastpath/v2 v2.0.1/go.mod h1:ABH9P8jc/bUub/RW9yTI7i+Gm/Oqe6ZHcIGEumvAOKk=
|
||||
codeberg.org/gruf/go-ffmpreg v0.6.19 h1:2dCx2fFDRSdOrfe2OI00pbpYPKaghqjuz7n8XRsuqjY=
|
||||
codeberg.org/gruf/go-ffmpreg v0.6.19/go.mod h1:hE0Dmx3cjI3ZgCUVFNi3H+0JgIz6Us27arY1ML0AxqU=
|
||||
codeberg.org/gruf/go-ffmpreg v0.6.20 h1:4Rc9yQLH1GTdMeiGPlMR1JUGNMJSjw6mRIxd+rxqDCo=
|
||||
codeberg.org/gruf/go-ffmpreg v0.6.20/go.mod h1:XM6eW8CSgAuZRKRynm5nDiSNedm0rav30IychqygOHE=
|
||||
codeberg.org/gruf/go-iotools v0.0.0-20240710125620-934ae9c654cf h1:84s/ii8N6lYlskZjHH+DG6jyia8w2mXMZlRwFn8Gs3A=
|
||||
codeberg.org/gruf/go-iotools v0.0.0-20240710125620-934ae9c654cf/go.mod h1:zZAICsp5rY7+hxnws2V0ePrWxE0Z2Z/KXcN3p/RQCfk=
|
||||
codeberg.org/gruf/go-kv v1.6.5 h1:ttPf0NA8F79pDqBttSudPTVCZmGncumeNIxmeM9ztz0=
|
||||
@@ -38,6 +38,8 @@ codeberg.org/gruf/go-kv/v2 v2.0.10 h1:aNIg4UzZhSorcGpSPAF2kSPlOzW4wWloNarTIoK9GE
|
||||
codeberg.org/gruf/go-kv/v2 v2.0.10/go.mod h1:diLoh5ZMJyCy5cRQuOMeYKMCxb9n/0V/6ec4z6uqtBc=
|
||||
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-longdur v0.1.3 h1:DLKFdNKeZKitPMu05QDGN0o/AGUx6pKpaN5imifaVro=
|
||||
codeberg.org/gruf/go-longdur v0.1.3/go.mod h1:YeKA3ci+KlRB7keMqQ26jO1XLiVMqzAGi31kx+WMOOM=
|
||||
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/v2 v2.0.9 h1:Zb4YCVQxM48bhV3bPBd4DWk3/7VwWnO79EXyMHSw3eI=
|
||||
@@ -52,8 +54,8 @@ codeberg.org/gruf/go-mutexes v1.5.9 h1:sQWz7VwvkAADMY5xHn25ak2hnw0+UhiLR+i/tqVKk
|
||||
codeberg.org/gruf/go-mutexes v1.5.9/go.mod h1:21sy/hWH8dDQBk7ocsxqo2GNpWiIir+e82RG3hjnN20=
|
||||
codeberg.org/gruf/go-runners v1.7.0 h1:Z+8Qne4H9nAdZZbA4cij0PWhhJxtigUGA4Mp7griYes=
|
||||
codeberg.org/gruf/go-runners v1.7.0/go.mod h1:1xBodiyuPfosJga+NYTfeepQYUrlBGCAa4NuQTbtiBw=
|
||||
codeberg.org/gruf/go-sched v1.3.0 h1:3Y+Vb6p+rt05iUC6Oj3TDFc9GQZCDImDfTKSUKUI9WA=
|
||||
codeberg.org/gruf/go-sched v1.3.0/go.mod h1:qL9MdPdBFaNXzSfumpQ18TStBAwnRddCTD+wlrZUEgI=
|
||||
codeberg.org/gruf/go-sched v1.3.1 h1:9PKL9i9tI+EbIagOZB/2ar54+yEJo5ZPCCIs1+Ylu+E=
|
||||
codeberg.org/gruf/go-sched v1.3.1/go.mod h1:qL9MdPdBFaNXzSfumpQ18TStBAwnRddCTD+wlrZUEgI=
|
||||
codeberg.org/gruf/go-split v1.2.0 h1:PmzL23nVEVHm8VxjsJmv4m4wGQz2bGgQw52dgSSj65c=
|
||||
codeberg.org/gruf/go-split v1.2.0/go.mod h1:0rejWJpqvOoFAd7nwm5tIXYKaAqjtFGOXmTqQV+VO38=
|
||||
codeberg.org/gruf/go-storage v0.6.0 h1:xL5lProzy3y/bewWkEmoHnTwifdkjabzgL7TyUJ5sPs=
|
||||
@@ -72,8 +74,8 @@ github.com/KimMachineGun/automemlimit v0.7.5 h1:RkbaC0MwhjL1ZuBKunGDjE/ggwAX43Dw
|
||||
github.com/KimMachineGun/automemlimit v0.7.5/go.mod h1:QZxpHaGOQoYvFhv/r4u3U0JTC2ZcOwbSr11UZF46UBM=
|
||||
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
|
||||
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
|
||||
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
|
||||
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
|
||||
github.com/Masterminds/semver/v3 v3.5.0 h1:kQceYJfbupGfZOKZQg0kou0DgAKhzDg2NZPAwZ/2OOE=
|
||||
github.com/Masterminds/semver/v3 v3.5.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
|
||||
github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs=
|
||||
github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0=
|
||||
github.com/SherClockHolmes/webpush-go v1.4.0 h1:ocnzNKWN23T9nvHi6IfyrQjkIc0oJWv1B1pULsf9i3s=
|
||||
@@ -106,8 +108,8 @@ github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI
|
||||
github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU=
|
||||
github.com/cnf/structhash v0.0.0-20201127153200-e1b16c1ebc08 h1:ox2F0PSMlrAAiAdknSRMDrAr8mfxPCfSZolH+/qQnyQ=
|
||||
github.com/cnf/structhash v0.0.0-20201127153200-e1b16c1ebc08/go.mod h1:pCxVEbcm3AMg7ejXyorUXi6HQCzOIBf7zEDVPtw0/U4=
|
||||
github.com/coreos/go-oidc/v3 v3.18.0 h1:V9orjXynvu5wiC9SemFTWnG4F45v403aIcjWo0d41+A=
|
||||
github.com/coreos/go-oidc/v3 v3.18.0/go.mod h1:DYCf24+ncYi+XkIH97GY1+dqoRlbaSI26KVTCI9SrY4=
|
||||
github.com/coreos/go-oidc/v3 v3.19.0 h1:F/xyOi3x1UnG1U27YVnM1N6bHiL1K2upi6U/0qr8r+I=
|
||||
github.com/coreos/go-oidc/v3 v3.19.0/go.mod h1:DYCf24+ncYi+XkIH97GY1+dqoRlbaSI26KVTCI9SrY4=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@@ -138,16 +140,16 @@ github.com/dsoprea/go-utility/v2 v2.0.0-20221003172846-a3e1774ef349 h1:DilThiXje
|
||||
github.com/dsoprea/go-utility/v2 v2.0.0-20221003172846-a3e1774ef349/go.mod h1:4GC5sXji84i/p+irqghpPFZBF8tRN/Q7+700G0/DLe8=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
|
||||
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
|
||||
github.com/fatih/color v1.19.0 h1:Zp3PiM21/9Ld6FzSKyL5c/BULoe/ONr9KlbYVOfG8+w=
|
||||
github.com/fatih/color v1.19.0/go.mod h1:zNk67I0ZUT1bEGsSGyCZYZNrHuTkJJB+r6Q9VuMi0LE=
|
||||
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
|
||||
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
||||
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||
github.com/fsnotify/fsnotify v1.10.1 h1:b0/UzAf9yR5rhf3RPm9gf3ehBPpf0oZKIjtpKrx59Ho=
|
||||
github.com/fsnotify/fsnotify v1.10.1/go.mod h1:TLheqan6HD6GBK6PrDWyDPBaEV8LspOxvPSjC+bVfgo=
|
||||
github.com/fxamacker/cbor v1.5.1 h1:XjQWBgdmQyqimslUh5r4tUGmoqzHmBFQOImkWGi2awg=
|
||||
github.com/fxamacker/cbor v1.5.1/go.mod h1:3aPGItF174ni7dDzd6JZ206H8cmr4GDNBGpPa971zsU=
|
||||
github.com/gabriel-vasile/mimetype v1.4.12 h1:e9hWvmLYvtp846tLHam2o++qitpguFiYCKbn0w9jyqw=
|
||||
@@ -167,8 +169,6 @@ github.com/go-errors/errors v1.0.2/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWE
|
||||
github.com/go-errors/errors v1.1.1/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs=
|
||||
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
|
||||
github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
|
||||
github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
|
||||
github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
|
||||
github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA=
|
||||
github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
@@ -176,54 +176,56 @@ github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-openapi/analysis v0.24.3 h1:a1hrvMr8X0Xt69KP5uVTu5jH62DscmDifrLzNglAayk=
|
||||
github.com/go-openapi/analysis v0.24.3/go.mod h1:Nc+dWJ/FxZbhSow5Yh3ozg5CLJioB+XXT6MdLvJUsUw=
|
||||
github.com/go-openapi/analysis v0.25.0 h1:EnjAq1yO8wEO9HbPmY8vLPEIkdZuuFhCAKBPvCB7bCs=
|
||||
github.com/go-openapi/analysis v0.25.0/go.mod h1:5WFTRE43WLkPG9r9OtlMfqkkvUTYLVVCIxLlEpyF8kE=
|
||||
github.com/go-openapi/errors v0.22.7 h1:JLFBGC0Apwdzw3484MmBqspjPbwa2SHvpDm0u5aGhUA=
|
||||
github.com/go-openapi/errors v0.22.7/go.mod h1://QW6SD9OsWtH6gHllUCddOXDL0tk0ZGNYHwsw4sW3w=
|
||||
github.com/go-openapi/inflect v0.21.5 h1:M2RCq6PPS3YbIaL7CXosGL3BbzAcmfBAT0nC3YfesZA=
|
||||
github.com/go-openapi/inflect v0.21.5/go.mod h1:GypUyi6bU880NYurWaEH2CmH84zFDNd+EhhmzroHmB4=
|
||||
github.com/go-openapi/jsonpointer v0.22.5 h1:8on/0Yp4uTb9f4XvTrM2+1CPrV05QPZXu+rvu2o9jcA=
|
||||
github.com/go-openapi/jsonpointer v0.22.5/go.mod h1:gyUR3sCvGSWchA2sUBJGluYMbe1zazrYWIkWPjjMUY0=
|
||||
github.com/go-openapi/jsonpointer v0.23.1 h1:1HBACs7XIwR2RcmItfdSFlALhGbe6S92p0ry4d1GWg4=
|
||||
github.com/go-openapi/jsonpointer v0.23.1/go.mod h1:iWRmZTrGn7XwYhtPt/fvdSFj1OfNBngqRT2UG3BxSqY=
|
||||
github.com/go-openapi/jsonreference v0.21.5 h1:6uCGVXU/aNF13AQNggxfysJ+5ZcU4nEAe+pJyVWRdiE=
|
||||
github.com/go-openapi/jsonreference v0.21.5/go.mod h1:u25Bw85sX4E2jzFodh1FOKMTZLcfifd1Q+iKKOUxExw=
|
||||
github.com/go-openapi/loads v0.23.3 h1:g5Xap1JfwKkUnZdn+S0L3SzBDpcTIYzZ5Qaag0YDkKQ=
|
||||
github.com/go-openapi/loads v0.23.3/go.mod h1:NOH07zLajXo8y55hom0omlHWDVVvCwBM/S+csCK8LqA=
|
||||
github.com/go-openapi/runtime v0.29.3 h1:h5twGaEqxtQg40ePiYm9vFFH1q06Czd7Ot6ufdK0w/Y=
|
||||
github.com/go-openapi/runtime v0.29.3/go.mod h1:8A1W0/L5eyNJvKciqZtvIVQvYO66NlB7INMSZ9bw/oI=
|
||||
github.com/go-openapi/runtime v0.32.2 h1:X9mZz716lFwYZ6bFV1BBnthNdHTy46zKM5Em4D1UISI=
|
||||
github.com/go-openapi/runtime v0.32.2/go.mod h1:IfM3cpgencPuwBp5Uo16i2IQaE74odL7Q4DCGovIQac=
|
||||
github.com/go-openapi/runtime/server-middleware v0.32.1 h1:HCtsghA5YuJEJwne3wQj8agMkEgg1iWbJjZw7Qdvu8g=
|
||||
github.com/go-openapi/runtime/server-middleware v0.32.1/go.mod h1:fYPep4GdTwg/XqZUjR40uIM/8C12Ba5M+MrGCiwpTHo=
|
||||
github.com/go-openapi/spec v0.22.4 h1:4pxGjipMKu0FzFiu/DPwN3CTBRlVM2yLf/YTWorYfDQ=
|
||||
github.com/go-openapi/spec v0.22.4/go.mod h1:WQ6Ai0VPWMZgMT4XySjlRIE6GP1bGQOtEThn3gcWLtQ=
|
||||
github.com/go-openapi/strfmt v0.26.1 h1:7zGCHji7zSYDC2tCXIusoxYQz/48jAf2q+sF6wXTG+c=
|
||||
github.com/go-openapi/strfmt v0.26.1/go.mod h1:Zslk5VZPOISLwmWTMBIS7oiVFem1o1EI6zULY8Uer7Y=
|
||||
github.com/go-openapi/swag v0.25.5 h1:pNkwbUEeGwMtcgxDr+2GBPAk4kT+kJ+AaB+TMKAg+TU=
|
||||
github.com/go-openapi/swag v0.25.5/go.mod h1:B3RT6l8q7X803JRxa2e59tHOiZlX1t8viplOcs9CwTA=
|
||||
github.com/go-openapi/swag/cmdutils v0.25.5 h1:yh5hHrpgsw4NwM9KAEtaDTXILYzdXh/I8Whhx9hKj7c=
|
||||
github.com/go-openapi/swag/cmdutils v0.25.5/go.mod h1:pdae/AFo6WxLl5L0rq87eRzVPm/XRHM3MoYgRMvG4A0=
|
||||
github.com/go-openapi/swag/conv v0.25.5 h1:wAXBYEXJjoKwE5+vc9YHhpQOFj2JYBMF2DUi+tGu97g=
|
||||
github.com/go-openapi/swag/conv v0.25.5/go.mod h1:CuJ1eWvh1c4ORKx7unQnFGyvBbNlRKbnRyAvDvzWA4k=
|
||||
github.com/go-openapi/swag/fileutils v0.25.5 h1:B6JTdOcs2c0dBIs9HnkyTW+5gC+8NIhVBUwERkFhMWk=
|
||||
github.com/go-openapi/swag/fileutils v0.25.5/go.mod h1:V3cT9UdMQIaH4WiTrUc9EPtVA4txS0TOmRURmhGF4kc=
|
||||
github.com/go-openapi/swag/jsonname v0.25.5 h1:8p150i44rv/Drip4vWI3kGi9+4W9TdI3US3uUYSFhSo=
|
||||
github.com/go-openapi/swag/jsonname v0.25.5/go.mod h1:jNqqikyiAK56uS7n8sLkdaNY/uq6+D2m2LANat09pKU=
|
||||
github.com/go-openapi/swag/jsonutils v0.25.5 h1:XUZF8awQr75MXeC+/iaw5usY/iM7nXPDwdG3Jbl9vYo=
|
||||
github.com/go-openapi/swag/jsonutils v0.25.5/go.mod h1:48FXUaz8YsDAA9s5AnaUvAmry1UcLcNVWUjY42XkrN4=
|
||||
github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.5 h1:SX6sE4FrGb4sEnnxbFL/25yZBb5Hcg1inLeErd86Y1U=
|
||||
github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.5/go.mod h1:/2KvOTrKWjVA5Xli3DZWdMCZDzz3uV/T7bXwrKWPquo=
|
||||
github.com/go-openapi/swag/loading v0.25.5 h1:odQ/umlIZ1ZVRteI6ckSrvP6e2w9UTF5qgNdemJHjuU=
|
||||
github.com/go-openapi/swag/loading v0.25.5/go.mod h1:I8A8RaaQ4DApxhPSWLNYWh9NvmX2YKMoB9nwvv6oW6g=
|
||||
github.com/go-openapi/swag/mangling v0.25.5 h1:hyrnvbQRS7vKePQPHHDso+k6CGn5ZBs5232UqWZmJZw=
|
||||
github.com/go-openapi/swag/mangling v0.25.5/go.mod h1:6hadXM/o312N/h98RwByLg088U61TPGiltQn71Iw0NY=
|
||||
github.com/go-openapi/swag/netutils v0.25.5 h1:LZq2Xc2QI8+7838elRAaPCeqJnHODfSyOa7ZGfxDKlU=
|
||||
github.com/go-openapi/swag/netutils v0.25.5/go.mod h1:lHbtmj4m57APG/8H7ZcMMSWzNqIQcu0RFiXrPUara14=
|
||||
github.com/go-openapi/swag/stringutils v0.25.5 h1:NVkoDOA8YBgtAR/zvCx5rhJKtZF3IzXcDdwOsYzrB6M=
|
||||
github.com/go-openapi/swag/stringutils v0.25.5/go.mod h1:PKK8EZdu4QJq8iezt17HM8RXnLAzY7gW0O1KKarrZII=
|
||||
github.com/go-openapi/swag/typeutils v0.25.5 h1:EFJ+PCga2HfHGdo8s8VJXEVbeXRCYwzzr9u4rJk7L7E=
|
||||
github.com/go-openapi/swag/typeutils v0.25.5/go.mod h1:itmFmScAYE1bSD8C4rS0W+0InZUBrB2xSPbWt6DLGuc=
|
||||
github.com/go-openapi/swag/yamlutils v0.25.5 h1:kASCIS+oIeoc55j28T4o8KwlV2S4ZLPT6G0iq2SSbVQ=
|
||||
github.com/go-openapi/swag/yamlutils v0.25.5/go.mod h1:Gek1/SjjfbYvM+Iq4QGwa/2lEXde9n2j4a3wI3pNuOQ=
|
||||
github.com/go-openapi/testify/enable/yaml/v2 v2.4.1 h1:NZOrZmIb6PTv5LTFxr5/mKV/FjbUzGE7E6gLz7vFoOQ=
|
||||
github.com/go-openapi/testify/enable/yaml/v2 v2.4.1/go.mod h1:r7dwsujEHawapMsxA69i+XMGZrQ5tRauhLAjV/sxg3Q=
|
||||
github.com/go-openapi/testify/v2 v2.4.1 h1:zB34HDKj4tHwyUQHrUkpV0Q0iXQ6dUCOQtIqn8hE6Iw=
|
||||
github.com/go-openapi/testify/v2 v2.4.1/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54=
|
||||
github.com/go-openapi/strfmt v0.26.2 h1:ysjheCh4i1rmFEo2LanhELDNucNzfWTZhUDKgWWPaFM=
|
||||
github.com/go-openapi/strfmt v0.26.2/go.mod h1:fXh1e449cyUn2NYuz+wb3wARBUdMl7qPEZwX00nqivY=
|
||||
github.com/go-openapi/swag v0.26.0 h1:GVDXCmfvhfu1BxiHo8/FA+BbKmhecHnG3varjON5/RI=
|
||||
github.com/go-openapi/swag v0.26.0/go.mod h1:82g3193sZJRbocs7bNCqGfIgq8pkuwVwCfhKIRlEQF0=
|
||||
github.com/go-openapi/swag/cmdutils v0.26.0 h1:iowihOcvq7y4egO8cOq0dmfohz6wfeQ63U1EnuhO2TU=
|
||||
github.com/go-openapi/swag/cmdutils v0.26.0/go.mod h1:Sm1MVFMkF6guJJ+pQqHnQA3N0j9qALV3NxzDSv6bETM=
|
||||
github.com/go-openapi/swag/conv v0.26.0 h1:5yGGsPYI1ZCva93U0AoKi/iZrNhaJEjr324YVsiD89I=
|
||||
github.com/go-openapi/swag/conv v0.26.0/go.mod h1:tpAmIL7X58VPnHHiSO4uE3jBeRamGsFsfdDeDtb5ECE=
|
||||
github.com/go-openapi/swag/fileutils v0.26.0 h1:WJoPRvsA7QRiiWluowkLJa9jaYR7FCuxmDvnCgaRRxU=
|
||||
github.com/go-openapi/swag/fileutils v0.26.0/go.mod h1:0WDJ7lp67eNjPMO50wAWYlKvhOb6CQ37rzR7wrgI8Tc=
|
||||
github.com/go-openapi/swag/jsonname v0.26.0 h1:gV1NFX9M8avo0YSpmWogqfQISigCmpaiNci8cGECU5w=
|
||||
github.com/go-openapi/swag/jsonname v0.26.0/go.mod h1:urBBR8bZNoDYGr653ynhIx+gTeIz0ARZxHkAPktJK2M=
|
||||
github.com/go-openapi/swag/jsonutils v0.26.0 h1:FawFML2iAXsPqmERscuMPIHmFsoP1tOqWkxBaKNMsnA=
|
||||
github.com/go-openapi/swag/jsonutils v0.26.0/go.mod h1:2VmA0CJlyFqgawOaPI9psnjFDqzyivIqLYN34t9p91E=
|
||||
github.com/go-openapi/swag/jsonutils/fixtures_test v0.26.0 h1:apqeINu/ICHouqiRZbyFvuDge5jCmmLTqGQ9V95EaOM=
|
||||
github.com/go-openapi/swag/jsonutils/fixtures_test v0.26.0/go.mod h1:AyM6QT8uz5IdKxk5akv0y6u4QvcL9GWERt0Jx/F/R8Y=
|
||||
github.com/go-openapi/swag/loading v0.26.0 h1:Apg6zaKhCJurpJer0DCxq99qwmhFddBhaMX7kilDcko=
|
||||
github.com/go-openapi/swag/loading v0.26.0/go.mod h1:dBxQ/6V2uBaAQdevN18VELE6xSpJWZxLX4txe12JwDg=
|
||||
github.com/go-openapi/swag/mangling v0.26.0 h1:Du2YC4YLA/Y5m/YKQd7AnY5qq0wRKSFZTTt8ktFaXcQ=
|
||||
github.com/go-openapi/swag/mangling v0.26.0/go.mod h1:jifS7W9vbg+pw63bT+GI53otluMQL3CeemuyCHKwVx0=
|
||||
github.com/go-openapi/swag/netutils v0.26.0 h1:CmZp+ZT7HrmFwrC3GdGsXBq2+42T1bjKBapcqVpIs3c=
|
||||
github.com/go-openapi/swag/netutils v0.26.0/go.mod h1:5iK+Ok3ZohWWex1C50BFTPexi03UaPwjW4Oj8kgrpwo=
|
||||
github.com/go-openapi/swag/stringutils v0.26.0 h1:qZQngLxs5s7SLijc3N2ZO+fUq2o8LjuWAASSrJuh+xg=
|
||||
github.com/go-openapi/swag/stringutils v0.26.0/go.mod h1:sWn5uY+QIIspwPhvgnqJsH8xqFT2ZbYcvbcFanRyhFE=
|
||||
github.com/go-openapi/swag/typeutils v0.26.0 h1:2kdEwdiNWy+JJdOvu5MA2IIg2SylWAFuuyQIKYybfq4=
|
||||
github.com/go-openapi/swag/typeutils v0.26.0/go.mod h1:oovDuIUvTrEHVMqWilQzKzV4YlSKgyZmFh7AlfABNVE=
|
||||
github.com/go-openapi/swag/yamlutils v0.26.0 h1:H7O8l/8NJJQ/oiReEN+oMpnGMyt8G0hl460nRZxhLMQ=
|
||||
github.com/go-openapi/swag/yamlutils v0.26.0/go.mod h1:1evKEGAtP37Pkwcc7EWMF0hedX0/x3Rkvei2wtG/TbU=
|
||||
github.com/go-openapi/testify/enable/yaml/v2 v2.5.1 h1:q9NtHwK4qHF7yZziBPvZyv7zWAIk8ok88Gh2mR6Jpc8=
|
||||
github.com/go-openapi/testify/enable/yaml/v2 v2.5.1/go.mod h1:JW0MXIotCYps/XsgJnG3a8Q7rE5xAiBwoOD5OfaIQBk=
|
||||
github.com/go-openapi/testify/v2 v2.5.1 h1:TMdhCaw8fUNraVSf3Omoob1dO/AzBfhtFAPW0an6sBo=
|
||||
github.com/go-openapi/testify/v2 v2.5.1/go.mod h1:SgsVHtfooshd0tublTtJ50FPKhujf47YRqauXXOUxfw=
|
||||
github.com/go-openapi/validate v0.25.2 h1:12NsfLAwGegqbGWr2CnvT65X/Q2USJipmJ9b7xDJZz0=
|
||||
github.com/go-openapi/validate v0.25.2/go.mod h1:Pgl1LpPPGFnZ+ys4/hTlDiRYQdI1ocKypgE+8Q8BLfY=
|
||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
@@ -242,8 +244,8 @@ github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
|
||||
github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
|
||||
github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro=
|
||||
github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/go-xmlfmt/xmlfmt v0.0.0-20191208150333-d5b6f63a941b h1:khEcpUM4yFcxg4/FHQWkvVRmgijNXRfzkIDHh23ggEo=
|
||||
github.com/go-xmlfmt/xmlfmt v0.0.0-20191208150333-d5b6f63a941b/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM=
|
||||
github.com/go-xmlfmt/xmlfmt v1.1.3 h1:t8Ey3Uy7jDSEisW2K3somuMKIpzktkWptA0iFCnRUWY=
|
||||
github.com/go-xmlfmt/xmlfmt v1.1.3/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM=
|
||||
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
||||
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
|
||||
@@ -292,8 +294,10 @@ github.com/gorilla/sessions v1.4.0 h1:kpIYOp/oi6MG/p5PgxApU8srsSw9tuFbt46Lt7auzq
|
||||
github.com/gorilla/sessions v1.4.0/go.mod h1:FLWm50oby91+hl7p/wRxDth9bWSuk0qVL2emc7lT5ik=
|
||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 h1:HWRh5R2+9EifMyIHV7ZV+MIZqgz+PMpZ14Jynv3O2Zs=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0/go.mod h1:JfhWUomR1baixubs02l85lZYYOm7LV6om4ceouMv45c=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 h1:5VipnvEpbqr2gA2VbM+nYVbkIF28c5ZQfqCBQ5g2xfk=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0/go.mod h1:Hyl3n6Twe1hvtd9XUXDec4pTvgMSEixRuQKPTMH2bNs=
|
||||
github.com/hashicorp/cronexpr v1.1.3 h1:rl5IkxXN2m681EfivTlccqIryzYJSXRGRNa0xeG7NA4=
|
||||
github.com/hashicorp/cronexpr v1.1.3/go.mod h1:P4wA0KBl9C5q2hABiMO7cp6jcIg96CDh1Efb3g1PWA4=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
||||
github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
|
||||
@@ -306,8 +310,8 @@ github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsI
|
||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
|
||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||
github.com/jackc/pgx/v5 v5.9.2 h1:3ZhOzMWnR4yJ+RW1XImIPsD1aNSz4T4fyP7zlQb56hw=
|
||||
github.com/jackc/pgx/v5 v5.9.2/go.mod h1:mal1tBGAFfLHvZzaYh77YS/eC6IX9OWbRV1QIIM0Jn4=
|
||||
github.com/jackc/pgx/v5 v5.10.0 h1:VhSvgU2jSli8o3AqIEOTJr7rZwAEUVo4E4XhR94Zfr0=
|
||||
github.com/jackc/pgx/v5 v5.10.0/go.mod h1:mal1tBGAFfLHvZzaYh77YS/eC6IX9OWbRV1QIIM0Jn4=
|
||||
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
|
||||
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
@@ -322,8 +326,8 @@ github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/k3a/html2text v1.4.0 h1:e4xarrVgZST+h+5C/fbA6AI49VFDSlEWMmIcDWcxsd0=
|
||||
github.com/k3a/html2text v1.4.0/go.mod h1:ieEXykM67iT8lTvEWBh6fhpH4B23kB9OMKPdIBmgUqA=
|
||||
github.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=
|
||||
github.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
|
||||
github.com/klauspost/compress v1.18.6 h1:2jupLlAwFm95+YDR+NwD2MEfFO9d4z4Prjl1XXDjuao=
|
||||
github.com/klauspost/compress v1.18.6/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
|
||||
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||
@@ -349,8 +353,8 @@ github.com/minio/crc64nvme v1.1.1 h1:8dwx/Pz49suywbO+auHCBpCtlW1OfpcLN7wYgVR6wAI
|
||||
github.com/minio/crc64nvme v1.1.1/go.mod h1:eVfm2fAzLlxMdUGc0EEBGSMmPwmXD5XiNRpnu9J3bvg=
|
||||
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
|
||||
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
|
||||
github.com/minio/minio-go/v7 v7.0.100 h1:ShkWi8Tyj9RtU57OQB2HIXKz4bFgtVib0bbT1sbtLI8=
|
||||
github.com/minio/minio-go/v7 v7.0.100/go.mod h1:EtGNKtlX20iL2yaYnxEigaIvj0G0GwSDnifnG8ClIdw=
|
||||
github.com/minio/minio-go/v7 v7.2.0 h1:RCJM0R1XOsRs+A3x3UCaf3ZYbByDaLjFeAi+YCQEPhs=
|
||||
github.com/minio/minio-go/v7 v7.2.0/go.mod h1:EU9hENAStx/xXduNdrGO5e4X5vk19NtgB+RIPjZO8o0=
|
||||
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
|
||||
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
|
||||
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
|
||||
@@ -368,10 +372,10 @@ 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/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/ncruces/go-sqlite3 v0.34.0 h1:q2I6wHTLWIoz6ehYkKdG5dGQc66eJv7ZGnekhvuMfK8=
|
||||
github.com/ncruces/go-sqlite3 v0.34.0/go.mod h1:qpBxsSdGPnO9K5OExuv5GEsrGQ7Rk6JsJFH6wn2DwwU=
|
||||
github.com/ncruces/go-sqlite3-wasm/v2 v2.1.35300 h1:cRdxCt3BDfMu0vfSdoqaAPD+dzIXPkGREjqyZMLN2Ak=
|
||||
github.com/ncruces/go-sqlite3-wasm/v2 v2.1.35300/go.mod h1:R2kJLPoSA/GBX/b8x7zwOq/KLAw6rLMY1l3Hi76SQIo=
|
||||
github.com/ncruces/go-sqlite3 v0.35.1 h1:h/LaVyQwIvBBT0+2JmVe2tbYyWjUQ093/pYhpBqdxJo=
|
||||
github.com/ncruces/go-sqlite3 v0.35.1/go.mod h1:fXOSIkWwN5NXgbJk+7Zls8QIW4xOflmgh11OFvcY+J0=
|
||||
github.com/ncruces/go-sqlite3-wasm/v3 v3.1.35302 h1:Cew7/eNAMd1zhpXYBjofBua/63pFvbvB2h4PM/p6gKU=
|
||||
github.com/ncruces/go-sqlite3-wasm/v3 v3.1.35302/go.mod h1:xe0CfafDUxfh+fSVKjHHMiAxoG9KALt5nFtbGNb/jRs=
|
||||
github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w=
|
||||
github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||
github.com/ncruces/julianday v1.0.0 h1:fH0OKwa7NWvniGQtxdJRxAgkBMolni2BjDHaWTxqt7M=
|
||||
@@ -381,8 +385,8 @@ github.com/oklog/ulid/v2 v2.1.1/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNs
|
||||
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/pborman/getopt v0.0.0-20170112200414-7148bc3a4c30/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||
github.com/pelletier/go-toml/v2 v2.3.1 h1:MYEvvGnQjeNkRF1qUuGolNtNExTDwct51yp7olPtrEc=
|
||||
github.com/pelletier/go-toml/v2 v2.3.1/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||
github.com/philhofer/fwd v1.2.0 h1:e6DnBTl7vGY+Gz322/ASL4Gyp1FspeMvx1RNDoToZuM=
|
||||
github.com/philhofer/fwd v1.2.0/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
@@ -423,8 +427,8 @@ github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7
|
||||
github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=
|
||||
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc=
|
||||
github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik=
|
||||
github.com/sagikazarmark/locafero v0.12.0 h1:/NQhBAkUb4+fH1jivKHWusDYFjMOOKU88eegjfxfHb4=
|
||||
github.com/sagikazarmark/locafero v0.12.0/go.mod h1:sZh36u/YSZ918v0Io+U9ogLYQJ9tLLBmM4eneO6WwsI=
|
||||
github.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQo=
|
||||
github.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=
|
||||
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
|
||||
@@ -437,8 +441,6 @@ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/smartystreets/goconvey v1.8.1 h1:qGjIddxOk4grTu9JPOU31tVfq3cNdBlNa5sSznIX1xY=
|
||||
github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60=
|
||||
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw=
|
||||
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U=
|
||||
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
|
||||
github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
|
||||
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
|
||||
@@ -464,16 +466,17 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||
github.com/tdewolff/minify/v2 v2.24.12 h1:YXJxVJmz7vxgnEv1v8J/EI4x+Uw4MMohcRFK7TFOjmk=
|
||||
github.com/tdewolff/minify/v2 v2.24.12/go.mod h1:exq1pjdrh9uAICdfVKQwqz6MsJmWmQahZuTC6pTO6ro=
|
||||
github.com/tdewolff/parse/v2 v2.8.11 h1:SGyjEy3xEqd+W9WVzTlTQ5GkP/en4a1AZNZVJ1cvgm0=
|
||||
github.com/tdewolff/parse/v2 v2.8.11/go.mod h1:Hwlni2tiVNKyzR1o6nUs4FOF07URA+JLBLd6dlIXYqo=
|
||||
github.com/tdewolff/test v1.0.11 h1:FdLbwQVHxqG16SlkGveC0JVyrJN62COWTRyUFzfbtBE=
|
||||
github.com/tdewolff/minify/v2 v2.24.13 h1:xrcF7gKDnUszseEY9WX9mUlZII2v2Go/QAcAwRASw58=
|
||||
github.com/tdewolff/minify/v2 v2.24.13/go.mod h1:emvwoYeIl8bfAKqRU5ww95LX9Gpggpqv/naal9a8Yq0=
|
||||
github.com/tdewolff/parse/v2 v2.8.12 h1:5BBjfaCv482v3nltlS0u6wH1xJaxjR6ofDrWttNvROg=
|
||||
github.com/tdewolff/parse/v2 v2.8.12/go.mod h1:Hwlni2tiVNKyzR1o6nUs4FOF07URA+JLBLd6dlIXYqo=
|
||||
github.com/tdewolff/test v1.0.11/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=
|
||||
github.com/tdewolff/test v1.0.12 h1:7F21DqIajswxuche0geHdrUZRCWE4oko4b7bcmkkrxk=
|
||||
github.com/tdewolff/test v1.0.12/go.mod h1:XPuWBzvdUzhCuxWO1ojpXsyzsA5bFoS3tO/Q3kFuTG8=
|
||||
github.com/temoto/robotstxt v1.1.2 h1:W2pOjSJ6SWvldyEuiFXNxz3xZ8aiWX5LbfDiOFd7Fxg=
|
||||
github.com/temoto/robotstxt v1.1.2/go.mod h1:+1AmkuG3IYkh1kv0d2qEB9Le88ehNO0zwOr3ujewlOo=
|
||||
github.com/tetratelabs/wazero v1.11.0 h1:+gKemEuKCTevU4d7ZTzlsvgd1uaToIDtlQlmNbwqYhA=
|
||||
github.com/tetratelabs/wazero v1.11.0/go.mod h1:eV28rsN8Q+xwjogd7f4/Pp4xFxO7uOGbLcD/LzB1wiU=
|
||||
github.com/tetratelabs/wazero v1.12.0 h1:DuWcpNu/FzgEXgGBDp8J1Spc+CWOvvtvVyjKlaZopYU=
|
||||
github.com/tetratelabs/wazero v1.12.0/go.mod h1:LvKtzl2RqO4gyF27BiXU+nKAjcV8f38U+kP/q2vgxh0=
|
||||
github.com/tidwall/btree v1.4.2 h1:PpkaieETJMUxYNADsjgtNRcERX7mGc/GP2zp/r5FM3g=
|
||||
github.com/tidwall/btree v1.4.2/go.mod h1:LGm8L/DZjPLmeWGjv5kFrY8dL4uVhMmzmmLYmsObdKE=
|
||||
github.com/tidwall/buntdb v1.3.2 h1:qd+IpdEGs0pZci37G4jF51+fSKlkuUTMXuHhXL1AkKg=
|
||||
@@ -541,54 +544,60 @@ github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDf
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yuin/goldmark v1.8.2 h1:kEGpgqJXdgbkhcOgBxkC0X0PmoPG1ZyoZ117rDVp4zE=
|
||||
github.com/yuin/goldmark v1.8.2/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
|
||||
gitlab.com/NyaaaWhatsUpDoc/sqlite v1.49.1-concurrency-workaround h1:LazUCiMPqKvSc88I66DmZe4plPBhi7QBhRFJ/qIkUdA=
|
||||
gitlab.com/NyaaaWhatsUpDoc/sqlite v1.49.1-concurrency-workaround/go.mod h1:m0w8xhwYUVY3H6pSDwc3gkJ/irZT/0YEXwBlhaxQEew=
|
||||
github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ=
|
||||
github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
|
||||
github.com/zeebo/xxh3 v1.1.0 h1:s7DLGDK45Dyfg7++yxI0khrfwq9661w9EN78eP/UZVs=
|
||||
github.com/zeebo/xxh3 v1.1.0/go.mod h1:IisAie1LELR4xhVinxWS5+zf1lA4p0MW4T+w+W07F5s=
|
||||
gitlab.com/NyaaaWhatsUpDoc/sqlite v1.53.0-concurrency-workaround h1:b9HnKjDR4JDIASehKIURkG5Uy4SHe0TXc0Q2OTXNUKo=
|
||||
gitlab.com/NyaaaWhatsUpDoc/sqlite v1.53.0-concurrency-workaround/go.mod h1:xoEpOIpGrgT48H5iiyt/YXPCZPEzlfmfFwtk8Lklw8s=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
go.opentelemetry.io/contrib/bridges/prometheus v0.68.0 h1:w3zlHYETbDwXyWHZlyyR58ZC39XGi8rAhkBgUgJ9d5w=
|
||||
go.opentelemetry.io/contrib/bridges/prometheus v0.68.0/go.mod h1:GR/mClR2nn7vE8RLwxKjoBNg+QtgdDhRzxVa93koy5o=
|
||||
go.opentelemetry.io/contrib/exporters/autoexport v0.68.0 h1:0D3GFvELGIwQGfC6agLsbrEYSGWZTRTxIXxcQUqrOuk=
|
||||
go.opentelemetry.io/contrib/exporters/autoexport v0.68.0/go.mod h1:DM2NV7Zb8CcGeVPt6glouY0FAiwZQ/iqgcWExhgWeN8=
|
||||
go.opentelemetry.io/contrib/instrumentation/runtime v0.68.0 h1:jhVIQEprwUTV+KfzzliLidclhoTOoHTgdz96kAyR8mU=
|
||||
go.opentelemetry.io/contrib/instrumentation/runtime v0.68.0/go.mod h1:4HsdbLUbernaTnA8CNaNE+1g026SciXb3juRYe3l8EY=
|
||||
go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I=
|
||||
go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.19.0 h1:Dn8rkudDzY6KV9dr/D/bTUuWgqDf9xe0rr4G2elrn0Y=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.19.0/go.mod h1:gMk9F0xDgyN9M/3Ed5Y1wKcx/9mlU91NXY2SNq7RQuU=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.19.0 h1:HIBTQ3VO5aupLKjC90JgMqpezVXwFuq6Ryjn0/izoag=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.19.0/go.mod h1:ji9vId85hMxqfvICA0Jt8JqEdrXaAkcpkI9HPXya0ro=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.43.0 h1:8UQVDcZxOJLtX6gxtDt3vY2WTgvZqMQRzjsqiIHQdkc=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.43.0/go.mod h1:2lmweYCiHYpEjQ/lSJBYhj9jP1zvCvQW4BqL9dnT7FQ=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.43.0 h1:w1K+pCJoPpQifuVpsKamUdn9U0zM3xUziVOqsGksUrY=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.43.0/go.mod h1:HBy4BjzgVE8139ieRI75oXm3EcDN+6GhD88JT1Kjvxg=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 h1:88Y4s2C8oTui1LGM6bTWkw0ICGcOLCAI5l6zsD1j20k=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0/go.mod h1:Vl1/iaggsuRlrHf/hfPJPvVag77kKyvrLeD10kpMl+A=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0 h1:RAE+JPfvEmvy+0LzyUA25/SGawPwIUbZ6u0Wug54sLc=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.43.0/go.mod h1:AGmbycVGEsRx9mXMZ75CsOyhSP6MFIcj/6dnG+vhVjk=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0 h1:3iZJKlCZufyRzPzlQhUIWVmfltrXuGyfjREgGP3UUjc=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0/go.mod h1:/G+nUPfhq2e+qiXMGxMwumDrP5jtzU+mWN7/sjT2rak=
|
||||
go.opentelemetry.io/otel/exporters/prometheus v0.65.0 h1:jOveH/b4lU9HT7y+Gfamf18BqlOuz2PWEvs8yM7Q6XE=
|
||||
go.opentelemetry.io/otel/exporters/prometheus v0.65.0/go.mod h1:i1P8pcumauPtUI4YNopea1dhzEMuEqWP1xoUZDylLHo=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.19.0 h1:GJkybS+crDMdExT/BUNCEgfrmfboztcS6PhvSo88HKM=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.19.0/go.mod h1:NuAyxRYIG2lKX3YQkB+83StTxM7s52PUUkRRiC0wnYI=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.43.0 h1:TC+BewnDpeiAmcscXbGMfxkO+mwYUwE/VySwvw88PfA=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.43.0/go.mod h1:J/ZyF4vfPwsSr9xJSPyQ4LqtcTPULFR64KwTikGLe+A=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.43.0 h1:mS47AX77OtFfKG4vtp+84kuGSFZHTyxtXIN269vChY0=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.43.0/go.mod h1:PJnsC41lAGncJlPUniSwM81gc80GkgWJWr3cu2nKEtU=
|
||||
go.opentelemetry.io/otel/log v0.19.0 h1:KUZs/GOsw79TBBMfDWsXS+KZ4g2Ckzksd1ymzsIEbo4=
|
||||
go.opentelemetry.io/otel/log v0.19.0/go.mod h1:5DQYeGmxVIr4n0/BcJvF4upsraHjg6vudJJpnkL6Ipk=
|
||||
go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM=
|
||||
go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY=
|
||||
go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg=
|
||||
go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg=
|
||||
go.opentelemetry.io/otel/sdk/log v0.19.0 h1:scYVLqT22D2gqXItnWiocLUKGH9yvkkeql5dBDiXyko=
|
||||
go.opentelemetry.io/otel/sdk/log v0.19.0/go.mod h1:vFBowwXGLlW9AvpuF7bMgnNI95LiW10szrOdvzBHlAg=
|
||||
go.opentelemetry.io/otel/sdk/log/logtest v0.19.0 h1:BEbF7ZBB6qQloV/Ub1+3NQoOUnVtcGkU3XX4Ws3GQfk=
|
||||
go.opentelemetry.io/otel/sdk/log/logtest v0.19.0/go.mod h1:Lua81/3yM0wOmoHTokLj9y9ADeA02v1naRrVrkAZuKk=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A=
|
||||
go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A=
|
||||
go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0=
|
||||
go.opentelemetry.io/contrib/bridges/prometheus v0.69.0 h1:saQoWg5845Q8TojpqeVStS7zGwVZ6bc5W2PJavTPiBM=
|
||||
go.opentelemetry.io/contrib/bridges/prometheus v0.69.0/go.mod h1:AAaS6xs5AyqMdR3Ir0nSWK+QudL2XM8Vbw5INzUxNc8=
|
||||
go.opentelemetry.io/contrib/exporters/autoexport v0.69.0 h1:R3jsCoTIzv0BiYNhW0axyswn/6SMJ8xL1OuGxvni1Kw=
|
||||
go.opentelemetry.io/contrib/exporters/autoexport v0.69.0/go.mod h1:m07gqyr2QhQxKOKb5vqKCCBtLH3uqlNYR7PU/FISXVU=
|
||||
go.opentelemetry.io/contrib/instrumentation/runtime v0.69.0 h1:MtkMsuRo3zEXTTMALfyrszwCDZTkB6wolyPjbwFAdq0=
|
||||
go.opentelemetry.io/contrib/instrumentation/runtime v0.69.0/go.mod h1:FYTxnpsm+UPD0erZNq20GvnM8T2YQHiHtT2vokdpoac=
|
||||
go.opentelemetry.io/otel v1.44.0 h1:JjwHmHpA4iZ3wBxluu2fbbE7j4kqlE8jXyAyPXH7HqU=
|
||||
go.opentelemetry.io/otel v1.44.0/go.mod h1:BMgjTHL9WPRlRjL2oZCBTL4whCGtXch2H4BhOPIAyYc=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.20.0 h1:rydZ9sxbcFdm/oWrVyfLTjHIygMgv0bEeMd+3B/BvoM=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.20.0/go.mod h1:earQ25dooT0Hhspq59DZ8YCC50jWfOlFEeWoxy/P444=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.20.0 h1:owlhcJ3QO3X0YTDTCcDZ4V+6aVDkWbNmBoQ5NUp7Oww=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.20.0/go.mod h1:MP4eemTiI9zC8fgg+DYynhYDYf3ba72S376TvP+Ye0Q=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.44.0 h1:SUplec5dp06reu1zaXmOXdvqH398taqrDXqUl99jxSc=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.44.0/go.mod h1:ho2g4N+ane+swq5I/VBkKWnRDY4kUINH3FuqyZqX/Ug=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.44.0 h1:RuynHbfU8JUEw7DyONgkVYg2SVtsoF28y0LGIr69jgA=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.44.0/go.mod h1:qZF+/lBs71APw8mlnEZcqZHMzqrYrsFiJOv83lX1OGo=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.44.0 h1:4YsVu3B8+3qtWYYrsUYgn0OG78pN0rnNPRGX4SbokQI=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.44.0/go.mod h1:+wnlSn0mD1ADVMe3v9Z/WIaiz6q6gL2J/ejaAmdmv80=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.44.0 h1:qazEJlUOQzhCpzQpFETGby7EdqjI1wsd0W+6Gg1SCTU=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.44.0/go.mod h1:fOD2Yefuxixkx3ahVNf0O/PERb6r4OlbxfATVnYvzCo=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.44.0 h1:lgh3PiVrRUWMLOVSkQicxzZll5NjF1r+AtsX1XRIHw0=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.44.0/go.mod h1:5Cnhth3m/AgOeTgE3ex12pPmiu/gGtZit03kSzx9X7s=
|
||||
go.opentelemetry.io/otel/exporters/prometheus v0.66.0 h1:vkrK8PAznv2NKt2r+kdu252ccGzkEqLc2aSXbQIALYQ=
|
||||
go.opentelemetry.io/otel/exporters/prometheus v0.66.0/go.mod h1:V/UB6D3vMF/UBOL5igAsAYnk1nG/bzYYTzvsB16cy7o=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.20.0 h1:aZfdmtI6QU/DAPD4b7YZ5zuJgewxO1EW9miOZklqleU=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.20.0/go.mod h1:isNl10/Om5CBWu9jj8WOb2+tJLbCVXDgqwzCaJMnJ6w=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.44.0 h1:hqxVTu/GtBF+vJ8d1fzW7fRxZFvgoDjWcxwwCaFDYpU=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.44.0/go.mod h1:z5fVEF4X5v0ESvlJqBrrFlBVoj5EQuefZpzsu7R+x5Q=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.44.0 h1:bl2S7Ubua0Nms+D/gAmznQTd4dxxMA93aKbcpKqiTCs=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.44.0/go.mod h1:L0hRV50XdVIODHUfWEqGRCXQvj2rV82STVo12FMFBU0=
|
||||
go.opentelemetry.io/otel/log v0.20.0 h1:/5i0vuHxCLWUfChWG41K9wkM0jafruPw9NU1/RCJirs=
|
||||
go.opentelemetry.io/otel/log v0.20.0/go.mod h1:wOcMcjsZpG8x7Bak7IhSi/lg8wscV2C1VdrKCLPlt0E=
|
||||
go.opentelemetry.io/otel/metric v1.44.0 h1:1w0gILTcHdr3YI+ixLyjemwrVnsMURbTZFrSYCdDdmc=
|
||||
go.opentelemetry.io/otel/metric v1.44.0/go.mod h1:8O7hanEPBNgEMmybD3s2VBKcgWOCsA6tzHBPODAiquo=
|
||||
go.opentelemetry.io/otel/metric/x v0.66.0 h1:YkCrx1zLOChi9ZcZ6euupOcsgzbVlec7D/xoEU1+cTA=
|
||||
go.opentelemetry.io/otel/metric/x v0.66.0/go.mod h1:d1+BDj9t96do0/1LoU1ayfCv79ZgNE41qbhBvnMOBZk=
|
||||
go.opentelemetry.io/otel/sdk v1.44.0 h1:nHYwb9lK+fJPU/dnT6s7W7Z8itMWyqrnVfbheVYrZ58=
|
||||
go.opentelemetry.io/otel/sdk v1.44.0/go.mod h1:Osuydd3Se74nqjAKxid74N5eC+jfEqfTegHRnq58oK0=
|
||||
go.opentelemetry.io/otel/sdk/log v0.20.0 h1:vM3xI7TQgKPiSghe6urZtAkyFY7SodrSpC83CffDFuY=
|
||||
go.opentelemetry.io/otel/sdk/log v0.20.0/go.mod h1:Knej2nmsTUzN79T2eeXdRsjjPcoxoq2pUyUHz9TFyyU=
|
||||
go.opentelemetry.io/otel/sdk/log/logtest v0.20.0 h1:OqdRZ1guyzamK3M6LlRsmGqRrjkHWw6WZOKKli5ELpg=
|
||||
go.opentelemetry.io/otel/sdk/log/logtest v0.20.0/go.mod h1:PuMIlm7zAt7c3z8zfOI5ox4iT1Z87We+PF6YoINux/M=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.44.0 h1:3LlKgI+VjbVsjNRFZJZAJ30WjXC5VkNRks6si09iEfI=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.44.0/go.mod h1:5B5pMARnXxKhltooO4xUuCBorl65a4EpnTalObqOigA=
|
||||
go.opentelemetry.io/otel/trace v1.44.0 h1:jxF5CsGYCe74MCRx2X4g7WsY/VBKRqqpNvXlX/6gtIk=
|
||||
go.opentelemetry.io/otel/trace v1.44.0/go.mod h1:oLl1jrMQAVo6v3GAggN+1VH9VIz9iUSvW53sW1Q8PIE=
|
||||
go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g=
|
||||
go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk=
|
||||
go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
|
||||
@@ -609,17 +618,17 @@ golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliY
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||
golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI=
|
||||
golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q=
|
||||
golang.org/x/image v0.39.0 h1:skVYidAEVKgn8lZ602XO75asgXBgLj9G/FE3RbuPFww=
|
||||
golang.org/x/image v0.39.0/go.mod h1:sIbmppfU+xFLPIG0FoVUTvyBMmgng1/XAMhQ2ft0hpA=
|
||||
golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto=
|
||||
golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio=
|
||||
golang.org/x/image v0.43.0 h1:FLxcP4ec2350nTfOC8ysKtqYSIFbk/QGjw1ZHNP4tsY=
|
||||
golang.org/x/image v0.43.0/go.mod h1:rrpelvGFt+kLPAjPM4HeWPgrl0FtafueU//e5N0qk/Q=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI=
|
||||
golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY=
|
||||
golang.org/x/mod v0.36.0 h1:JJjpVx6myfUsUdAzZuOSTTmRE0PfZeNWzzvKrP7amb4=
|
||||
golang.org/x/mod v0.36.0/go.mod h1:moc6ELqsWcOw5Ef3xVprK5ul/MvtVvkIXLziUOICjUQ=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
@@ -635,8 +644,8 @@ golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA=
|
||||
golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs=
|
||||
golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o=
|
||||
golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec=
|
||||
golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=
|
||||
golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -646,8 +655,8 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM=
|
||||
golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -664,8 +673,8 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
|
||||
golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
|
||||
golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
@@ -675,8 +684,8 @@ golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
||||
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
|
||||
golang.org/x/term v0.42.0 h1:UiKe+zDFmJobeJ5ggPwOshJIVt6/Ft0rcfrXZDLWAWY=
|
||||
golang.org/x/term v0.42.0/go.mod h1:Dq/D+snpsbazcBG5+F9Q1n2rXV8Ma+71xEjTRufARgY=
|
||||
golang.org/x/term v0.44.0 h1:0rLvDRCtNj0gZkyIXhCyOb2OAzEhLVqc4B+hrsBhrmc=
|
||||
golang.org/x/term v0.44.0/go.mod h1:7ze4MdzUzLXpSAoFP1H0bOI9aXDqveSvatT5vKcFh2Y=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
@@ -686,8 +695,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg=
|
||||
golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164=
|
||||
golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE=
|
||||
golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
@@ -695,22 +704,24 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
golang.org/x/tools v0.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s=
|
||||
golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0=
|
||||
golang.org/x/tools v0.45.0 h1:18qN3FAooORvApf5XjCXgsuayZOEtXf6JK18I3+ONa8=
|
||||
golang.org/x/tools v0.45.0/go.mod h1:LuUGqqaXcXMEFEruIVJVm5mgDD8vww/z/SR1gQ4uE/0=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
|
||||
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260406210006-6f92a3bedf2d h1:/aDRtSZJjyLQzm75d+a1wOJaqyKBMvIAfeQmoa3ORiI=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260406210006-6f92a3bedf2d/go.mod h1:etfGUgejTiadZAUaEP14NP97xi1RGeawqkjDARA/UOs=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260406210006-6f92a3bedf2d h1:wT2n40TBqFY6wiwazVK9/iTWbsQrgk5ZfCSVFLO9LQA=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260406210006-6f92a3bedf2d/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||
google.golang.org/grpc v1.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM=
|
||||
google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa h1:Kjn0N0tCrDgiAFW+lGO4JZ3ck44CehvJQMAwj9QF0G8=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:q4lMZS6kskjT5HvCPrnnypcDPVJqT/f4nfxmkE7gryY=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa h1:mZHHdPZl0dbGHCflZgAq/Q468DWVFcU2whhB2KAo8fk=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||
google.golang.org/grpc v1.81.1 h1:VnnIIZ88UzOOKLukQi+ImGz8O1Wdp8nAGGnvOfEIWQQ=
|
||||
google.golang.org/grpc v1.81.1/go.mod h1:xGH9GfzOyMTGIOXBJmXt+BX/V0kcdQbdcuwQ/zNw42I=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
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/ini.v1 v1.67.2 h1:JtOSMb9OuaCZKr7h5D/h6iii14sK0hLbplTc6frx4Ss=
|
||||
gopkg.in/ini.v1 v1.67.2/go.mod h1:x/cyOwCgZqOkJoDIJ3c1KNHMo10+nLGAhh+kn3Zizss=
|
||||
gopkg.in/mcuadros/go-syslog.v2 v2.3.0 h1:kcsiS+WsTKyIEPABJBJtoG0KkOS6yzvJ+/eZlhD79kk=
|
||||
gopkg.in/mcuadros/go-syslog.v2 v2.3.0/go.mod h1:l5LPIyOOyIdQquNg+oU6Z3524YwrcqEm0aKH+5zpt2U=
|
||||
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
@@ -720,26 +731,26 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
modernc.org/cc/v4 v4.27.3 h1:uNCgn37E5U09mTv1XgskEVUJ8ADKpmFMPxzGJ0TSo+U=
|
||||
modernc.org/cc/v4 v4.27.3/go.mod h1:3YjcbCqhoTTHPycJDRl2WZKKFj0nwcOIPBfEZK0Hdk8=
|
||||
modernc.org/ccgo/v4 v4.32.4 h1:L5OB8rpEX4ZsXEQwGozRfJyJSFHbbNVOoQ59DU9/KuU=
|
||||
modernc.org/ccgo/v4 v4.32.4/go.mod h1:lY7f+fiTDHfcv6YlRgSkxYfhs+UvOEEzj49jAn2TOx0=
|
||||
modernc.org/cc/v4 v4.28.4 h1:Hd/4Es+MBj+/7hSdZaisNyu6bv3V0Dp2MdllyfqaH+c=
|
||||
modernc.org/cc/v4 v4.28.4/go.mod h1:OnovgIhbbMXMu1aISnJ0wvVD1KnW+cAUJkIrAWh+kVI=
|
||||
modernc.org/ccgo/v4 v4.34.4 h1:OVnSOWQjVKOYkFxoHYB+qQmSHK5gqMqARM+K9DpR/Ws=
|
||||
modernc.org/ccgo/v4 v4.34.4/go.mod h1:qdKqE8FNIYyysougB1RX9MxCzp5oJOcQXSobANJ4TuE=
|
||||
modernc.org/fileutil v1.4.0 h1:j6ZzNTftVS054gi281TyLjHPp6CPHr2KCxEXjEbD6SM=
|
||||
modernc.org/fileutil v1.4.0/go.mod h1:EqdKFDxiByqxLk8ozOxObDSfcVOv/54xDs/DUHdvCUU=
|
||||
modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
|
||||
modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
|
||||
modernc.org/gc/v3 v3.1.2 h1:ZtDCnhonXSZexk/AYsegNRV1lJGgaNZJuKjJSWKyEqo=
|
||||
modernc.org/gc/v3 v3.1.2/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY=
|
||||
modernc.org/gc/v3 v3.1.3 h1:6QAplYyVO+KdPW3pGnqmJDUxtkec8ooEWvks/hhU3lc=
|
||||
modernc.org/gc/v3 v3.1.3/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY=
|
||||
modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks=
|
||||
modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI=
|
||||
modernc.org/libc v1.72.0 h1:IEu559v9a0XWjw0DPoVKtXpO2qt5NVLAnFaBbjq+n8c=
|
||||
modernc.org/libc v1.72.0/go.mod h1:tTU8DL8A+XLVkEY3x5E/tO7s2Q/q42EtnNWda/L5QhQ=
|
||||
modernc.org/libc v1.73.4 h1:+ra4Ui8ngyt8HDcO1FTDPWlkAh6yOdaO2yAoh8MddQA=
|
||||
modernc.org/libc v1.73.4/go.mod h1:DXZ3eO8qMCNn2SnmTNCiC71nJ9Rcq3PsnpU6Vc4rWK8=
|
||||
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
|
||||
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
|
||||
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
|
||||
modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
|
||||
modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
|
||||
modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
|
||||
modernc.org/opt v0.2.0 h1:tGyef5ApycA7FSEOMraay9SaTk5zmbx7Tu+cJs4QKZg=
|
||||
modernc.org/opt v0.2.0/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
|
||||
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
|
||||
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
|
||||
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
|
||||
|
||||
+26
-143
@@ -91,9 +91,11 @@ func ExtractActivityObjectsAndInstruments(
|
||||
rawJSON map[string]any,
|
||||
) ([]TypeOrIRI, []any) {
|
||||
switch typeName := activity.GetTypeName(); {
|
||||
|
||||
// Activity: has "object"
|
||||
// and/or "instrument".
|
||||
case isActivity(typeName):
|
||||
|
||||
// Gather object types and raw json.
|
||||
objTypes, objJSON := extractObjectTypesAndJSON(activity, rawJSON)
|
||||
|
||||
@@ -312,26 +314,6 @@ func ExtractName(i WithName) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// ExtractInReplyToURI extracts the first inReplyTo URI
|
||||
// property it can find from an interface. Will return
|
||||
// nil if no valid URI can be found.
|
||||
func ExtractInReplyToURI(i WithInReplyTo) *url.URL {
|
||||
inReplyToProp := i.GetActivityStreamsInReplyTo()
|
||||
if inReplyToProp == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
for iter := inReplyToProp.Begin(); iter != inReplyToProp.End(); iter = iter.Next() {
|
||||
iri, err := pub.ToId(iter)
|
||||
if err == nil && iri != nil {
|
||||
// Found one we can use.
|
||||
return iri
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ExtractItemsURIs extracts each URI it can
|
||||
// find for an item from the provided WithItems.
|
||||
func ExtractItemsURIs(i WithItems) []*url.URL {
|
||||
@@ -352,64 +334,6 @@ func ExtractItemsURIs(i WithItems) []*url.URL {
|
||||
return uris
|
||||
}
|
||||
|
||||
// ExtractToURIs returns a slice of URIs
|
||||
// that the given WithTo addresses as To.
|
||||
func ExtractToURIs(i WithTo) []*url.URL {
|
||||
toProp := i.GetActivityStreamsTo()
|
||||
if toProp == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
uris := make([]*url.URL, 0, toProp.Len())
|
||||
for iter := toProp.Begin(); iter != toProp.End(); iter = iter.Next() {
|
||||
uri, err := pub.ToId(iter)
|
||||
if err == nil {
|
||||
// Found one we can use.
|
||||
uris = append(uris, uri)
|
||||
}
|
||||
}
|
||||
|
||||
return uris
|
||||
}
|
||||
|
||||
// ExtractCcURIs returns a slice of URIs
|
||||
// that the given WithCC addresses as Cc.
|
||||
func ExtractCcURIs(i WithCc) []*url.URL {
|
||||
ccProp := i.GetActivityStreamsCc()
|
||||
if ccProp == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
urls := make([]*url.URL, 0, ccProp.Len())
|
||||
for iter := ccProp.Begin(); iter != ccProp.End(); iter = iter.Next() {
|
||||
uri, err := pub.ToId(iter)
|
||||
if err == nil {
|
||||
// Found one we can use.
|
||||
urls = append(urls, uri)
|
||||
}
|
||||
}
|
||||
|
||||
return urls
|
||||
}
|
||||
|
||||
// ExtractAttributedToURI returns the first URI it can find in the
|
||||
// given WithAttributedTo, or an error if no URI can be found.
|
||||
func ExtractAttributedToURI(i WithAttributedTo) (*url.URL, error) {
|
||||
attributedToProp := i.GetActivityStreamsAttributedTo()
|
||||
if attributedToProp == nil {
|
||||
return nil, gtserror.New("attributedToProp was nil")
|
||||
}
|
||||
|
||||
for iter := attributedToProp.Begin(); iter != attributedToProp.End(); iter = iter.Next() {
|
||||
id, err := pub.ToId(iter)
|
||||
if err == nil {
|
||||
return id, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, gtserror.New("couldn't find iri for attributed to")
|
||||
}
|
||||
|
||||
// ExtractIconURI extracts the first URI it can find from
|
||||
// the given WithIcon which links to a supported image file.
|
||||
// Input will look something like this:
|
||||
@@ -691,21 +615,20 @@ func ExtractPubKeyFromActor(i WithPublicKey) (
|
||||
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() {
|
||||
if !iter.IsW3IDSecurityV1PublicKey() {
|
||||
continue
|
||||
}
|
||||
|
||||
pkey := iter.Get()
|
||||
if pkey == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
return ExtractPubKeyFromKey(pkey)
|
||||
if l := pubKeyProp.Len(); l != 1 {
|
||||
return nil, nil, nil, gtserror.Newf("unexpected public key property length: %d", l)
|
||||
}
|
||||
|
||||
return nil, nil, nil, gtserror.New("couldn't find valid public key")
|
||||
// Start property iterator.
|
||||
iter := pubKeyProp.Begin()
|
||||
|
||||
// Ensure this is a expected valid key.
|
||||
if !iter.IsW3IDSecurityV1PublicKey() {
|
||||
return nil, nil, nil, gtserror.New("couldn't find valid public key")
|
||||
}
|
||||
|
||||
// Extract RSA details from key type.
|
||||
return ExtractPubKeyFromKey(iter.Get())
|
||||
}
|
||||
|
||||
// ExtractPubKeyFromActor extracts the public key, public key ID, and public
|
||||
@@ -718,32 +641,32 @@ func ExtractPubKeyFromKey(pkey vocab.W3IDSecurityV1PublicKey) (
|
||||
) {
|
||||
pubKeyID, err := pub.GetId(pkey)
|
||||
if err != nil {
|
||||
return nil, nil, nil, errors.New("no id set on public key")
|
||||
return nil, nil, nil, gtserror.New("no id set on public key")
|
||||
}
|
||||
|
||||
pubKeyOwnerProp := pkey.GetW3IDSecurityV1Owner()
|
||||
if pubKeyOwnerProp == nil {
|
||||
return nil, nil, nil, errors.New("nil pubKeyOwnerProp")
|
||||
return nil, nil, nil, gtserror.New("nil pubKeyOwnerProp")
|
||||
}
|
||||
|
||||
pubKeyOwner := pubKeyOwnerProp.GetIRI()
|
||||
if pubKeyOwner == nil {
|
||||
return nil, nil, nil, errors.New("nil iri on pubKeyOwnerProp")
|
||||
return nil, nil, nil, gtserror.New("nil iri on pubKeyOwnerProp")
|
||||
}
|
||||
|
||||
pubKeyPemProp := pkey.GetW3IDSecurityV1PublicKeyPem()
|
||||
if pubKeyPemProp == nil {
|
||||
return nil, nil, nil, errors.New("nil pubKeyPemProp")
|
||||
return nil, nil, nil, gtserror.New("nil pubKeyPemProp")
|
||||
}
|
||||
|
||||
pkeyPem := pubKeyPemProp.Get()
|
||||
if pkeyPem == "" {
|
||||
return nil, nil, nil, errors.New("empty pubKeyPemProp")
|
||||
return nil, nil, nil, gtserror.New("empty pubKeyPemProp")
|
||||
}
|
||||
|
||||
block, _ := pem.Decode([]byte(pkeyPem))
|
||||
if block == nil {
|
||||
return nil, nil, nil, errors.New("nil pubKeyPem")
|
||||
return nil, nil, nil, gtserror.New("nil pubKeyPem")
|
||||
}
|
||||
|
||||
var p crypto.PublicKey
|
||||
@@ -753,20 +676,19 @@ func ExtractPubKeyFromKey(pkey vocab.W3IDSecurityV1PublicKey) (
|
||||
case "RSA PUBLIC KEY":
|
||||
p, err = x509.ParsePKCS1PublicKey(block.Bytes)
|
||||
default:
|
||||
err = fmt.Errorf("unknown block type: %q", block.Type)
|
||||
err = gtserror.Newf("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
|
||||
return nil, nil, nil, gtserror.Newf("could not parse public key from block bytes: %w", err)
|
||||
}
|
||||
|
||||
if p == nil {
|
||||
return nil, nil, nil, fmt.Errorf("returned public key was empty")
|
||||
return nil, nil, nil, gtserror.New("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 nil, nil, nil, gtserror.New("could not type pubKey to *rsa.PublicKey")
|
||||
}
|
||||
|
||||
return pubKey, pubKeyID, pubKeyOwner, nil
|
||||
@@ -1209,45 +1131,6 @@ func ExtractActorURI(withActor WithActor) (*url.URL, error) {
|
||||
return nil, gtserror.New("no iri found for actor prop")
|
||||
}
|
||||
|
||||
// ExtractObjectURI extracts the first Object URI
|
||||
// it can find from a WithObject interface.
|
||||
func ExtractObjectURI(withObject WithObject) (*url.URL, error) {
|
||||
objectProp := withObject.GetActivityStreamsObject()
|
||||
if objectProp == nil {
|
||||
return nil, gtserror.New("object property was nil")
|
||||
}
|
||||
|
||||
for iter := objectProp.Begin(); iter != objectProp.End(); iter = iter.Next() {
|
||||
id, err := pub.ToId(iter)
|
||||
if err == nil {
|
||||
// Found one we can use.
|
||||
return id, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, gtserror.New("no iri found for object prop")
|
||||
}
|
||||
|
||||
// ExtractObjectURIs extracts the URLs of each Object
|
||||
// it can find from a WithObject interface.
|
||||
func ExtractObjectURIs(withObject WithObject) ([]*url.URL, error) {
|
||||
objectProp := withObject.GetActivityStreamsObject()
|
||||
if objectProp == nil {
|
||||
return nil, gtserror.New("object property was nil")
|
||||
}
|
||||
|
||||
urls := make([]*url.URL, 0, objectProp.Len())
|
||||
for iter := objectProp.Begin(); iter != objectProp.End(); iter = iter.Next() {
|
||||
id, err := pub.ToId(iter)
|
||||
if err == nil {
|
||||
// Found one we can use.
|
||||
urls = append(urls, id)
|
||||
}
|
||||
}
|
||||
|
||||
return urls, nil
|
||||
}
|
||||
|
||||
// ExtractVisibility extracts the gtsmodel.Visibility
|
||||
// of a given addressable with a To and CC property.
|
||||
//
|
||||
@@ -1258,8 +1141,8 @@ func ExtractObjectURIs(withObject WithObject) ([]*url.URL, error) {
|
||||
// eg., `https://example.org/users/whoever/followers`.
|
||||
func ExtractVisibility(addressable Addressable, actorFollowersURI string) (gtsmodel.Visibility, error) {
|
||||
var (
|
||||
to = ExtractToURIs(addressable)
|
||||
cc = ExtractCcURIs(addressable)
|
||||
to = GetTo(addressable)
|
||||
cc = GetCc(addressable)
|
||||
)
|
||||
|
||||
if len(to) == 0 && len(cc) == 0 {
|
||||
|
||||
+96
-13
@@ -24,6 +24,7 @@ import (
|
||||
|
||||
"code.superseriousbusiness.org/activity/streams"
|
||||
"code.superseriousbusiness.org/activity/streams/vocab"
|
||||
"code.superseriousbusiness.org/gopkg/log"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/gtserror"
|
||||
)
|
||||
|
||||
@@ -84,7 +85,7 @@ func SetJSONLDIdStr(with WithJSONLDId, id string) error {
|
||||
// GetTo returns the IRIs contained in the To property of 'with'. Panics on entries with missing ID.
|
||||
func GetTo(with WithTo) []*url.URL {
|
||||
toProp := with.GetActivityStreamsTo()
|
||||
return getIRIs[vocab.ActivityStreamsToPropertyIterator](toProp)
|
||||
return getIRIs(toProp)
|
||||
}
|
||||
|
||||
// AppendTo appends the given IRIs to the To property of 'with'.
|
||||
@@ -102,7 +103,7 @@ func AppendTo(with WithTo, to ...*url.URL) {
|
||||
// GetCc returns the IRIs contained in the Cc property of 'with'. Panics on entries with missing ID.
|
||||
func GetCc(with WithCc) []*url.URL {
|
||||
ccProp := with.GetActivityStreamsCc()
|
||||
return extractIRIs[vocab.ActivityStreamsCcPropertyIterator](ccProp)
|
||||
return extractIRIs(ccProp)
|
||||
}
|
||||
|
||||
// AppendCc appends the given IRIs to the Cc property of 'with'.
|
||||
@@ -120,7 +121,7 @@ func AppendCc(with WithCc, cc ...*url.URL) {
|
||||
// GetBcc returns the IRIs contained in the Bcc property of 'with'. Panics on entries with missing ID.
|
||||
func GetBcc(with WithBcc) []*url.URL {
|
||||
bccProp := with.GetActivityStreamsBcc()
|
||||
return extractIRIs[vocab.ActivityStreamsBccPropertyIterator](bccProp)
|
||||
return extractIRIs(bccProp)
|
||||
}
|
||||
|
||||
// AppendBcc appends the given IRIs to the Bcc property of 'with'.
|
||||
@@ -190,10 +191,24 @@ func AppendURL(with WithURL, url ...*url.URL) {
|
||||
}
|
||||
}
|
||||
|
||||
// GetOneActorIRI extracts a single `actor` property IRI from 'with', else returns error.
|
||||
// Note: it is an error for the passed type to return any count other than 1 actor value.
|
||||
func GetOneActorIRI(with WithActor) (*url.URL, error) {
|
||||
actors := GetActorIRIs(with)
|
||||
if len(actors) != 1 {
|
||||
return nil, gtserror.NewfAt(3, "expected single actor, received: %v", log.Formatted(actors))
|
||||
}
|
||||
return actors[0], nil
|
||||
}
|
||||
|
||||
// If > 1 is accepted, but only first wanted, use:
|
||||
//
|
||||
// func GetFirstActorIRI(...) (*url.URL, error) {}
|
||||
|
||||
// GetActorIRIs returns the IRIs contained in the Actor property of 'with'.
|
||||
func GetActorIRIs(with WithActor) []*url.URL {
|
||||
actorProp := with.GetActivityStreamsActor()
|
||||
return extractIRIs[vocab.ActivityStreamsActorPropertyIterator](actorProp)
|
||||
return extractIRIs(actorProp)
|
||||
}
|
||||
|
||||
// AppendActorIRIs appends the given IRIs to the Actor property of 'with'.
|
||||
@@ -208,10 +223,24 @@ func AppendActorIRIs(with WithActor, actor ...*url.URL) {
|
||||
}, actor...)
|
||||
}
|
||||
|
||||
// GetOneObjectIRI extracts a single `object` property IRI from 'with', else returns error.
|
||||
// Note: it is an error for the passed type to return any count other than 1 object value.
|
||||
func GetOneObjectIRI(with WithObject) (*url.URL, error) {
|
||||
objects := GetObjectIRIs(with)
|
||||
if len(objects) != 1 {
|
||||
return nil, gtserror.NewfAt(3, "expected single object, received: %v", log.Formatted(objects))
|
||||
}
|
||||
return objects[0], nil
|
||||
}
|
||||
|
||||
// If > 1 is accepted, but only first wanted, use:
|
||||
//
|
||||
// func GetFirstObjectIRI(...) (*url.URL, error) {}
|
||||
|
||||
// GetObjectIRIs returns the IRIs contained in the Object property of 'with'.
|
||||
func GetObjectIRIs(with WithObject) []*url.URL {
|
||||
objectProp := with.GetActivityStreamsObject()
|
||||
return extractIRIs[vocab.ActivityStreamsObjectPropertyIterator](objectProp)
|
||||
return extractIRIs(objectProp)
|
||||
}
|
||||
|
||||
// AppendObjectIRIs appends the given IRIs to the Object property of 'with'.
|
||||
@@ -238,6 +267,16 @@ func AppendInstrumentIRIs(with WithInstrument, instrument ...*url.URL) {
|
||||
}, instrument...)
|
||||
}
|
||||
|
||||
// GetOneResultIRI extracts a single `result` property IRI from 'with', else returns error.
|
||||
// Note: it is an error for the passed type to return any count other than 1 result value.
|
||||
func GetOneResultIRI(with WithResult) (*url.URL, error) {
|
||||
results := GetResultIRIs(with)
|
||||
if len(results) != 1 {
|
||||
return nil, gtserror.NewfAt(3, "expected single result, received: %v", log.Formatted(results))
|
||||
}
|
||||
return results[0], nil
|
||||
}
|
||||
|
||||
// GetResultIRIs returns the IRIs contained in the `result` property of 'with'.
|
||||
func GetResultIRIs(with WithResult) []*url.URL {
|
||||
resultProp := with.GetActivityStreamsResult()
|
||||
@@ -256,13 +295,23 @@ func AppendResultIRIs(with WithResult, result ...*url.URL) {
|
||||
}, result...)
|
||||
}
|
||||
|
||||
// GetTargetIRIs returns the IRIs contained in the Target property of 'with'.
|
||||
func GetTargetIRIs(with WithTarget) []*url.URL {
|
||||
targetProp := with.GetActivityStreamsTarget()
|
||||
return extractIRIs[vocab.ActivityStreamsTargetPropertyIterator](targetProp)
|
||||
// GetOneTargetIRI extracts a single `target` IRI property from 'with', else returns error.
|
||||
// Note: it is an error for the passed type to return any count other than 1 target value.
|
||||
func GetOneTargetIRI(with WithTarget) (*url.URL, error) {
|
||||
targets := GetTargetIRIs(with)
|
||||
if len(targets) != 1 {
|
||||
return nil, gtserror.NewfAt(3, "expected single target, received: %v", log.Formatted(targets))
|
||||
}
|
||||
return targets[0], nil
|
||||
}
|
||||
|
||||
// AppendTargetIRIs appends the given IRIs to the Target property of 'with'.
|
||||
// GetTargetIRIs returns the IRIs contained in the `target` property of 'with'.
|
||||
func GetTargetIRIs(with WithTarget) []*url.URL {
|
||||
targetProp := with.GetActivityStreamsTarget()
|
||||
return extractIRIs(targetProp)
|
||||
}
|
||||
|
||||
// AppendTargetIRIs appends the given IRIs to the `target` property of 'with'.
|
||||
func AppendTargetIRIs(with WithTarget, target ...*url.URL) {
|
||||
appendIRIs(func() Property[vocab.ActivityStreamsTargetPropertyIterator] {
|
||||
targetProp := with.GetActivityStreamsTarget()
|
||||
@@ -274,10 +323,24 @@ func AppendTargetIRIs(with WithTarget, target ...*url.URL) {
|
||||
}, target...)
|
||||
}
|
||||
|
||||
// GetOneAttributedTo extracts a single attributedTo property from 'with', else returns error.
|
||||
// Note: it is an error for the passed type to return any count other than 1 attributedTo value.
|
||||
func GetOneAttributedTo(with WithAttributedTo) (*url.URL, error) {
|
||||
attribTo := GetAttributedTo(with)
|
||||
if len(attribTo) != 1 {
|
||||
return nil, gtserror.NewfAt(3, "expected single attributedTo, received: %v", log.Formatted(attribTo))
|
||||
}
|
||||
return attribTo[0], nil
|
||||
}
|
||||
|
||||
// If > 1 is accepted, but only first wanted, use:
|
||||
//
|
||||
// func GetFirstAttributedTo(...) (*url.URL, error) {}
|
||||
|
||||
// GetAttributedTo returns the IRIs contained in the AttributedTo property of 'with'.
|
||||
func GetAttributedTo(with WithAttributedTo) []*url.URL {
|
||||
attribProp := with.GetActivityStreamsAttributedTo()
|
||||
return extractIRIs[vocab.ActivityStreamsAttributedToPropertyIterator](attribProp)
|
||||
return extractIRIs(attribProp)
|
||||
}
|
||||
|
||||
// AppendAttributedTo appends the given IRIs to the AttributedTo property of 'with'.
|
||||
@@ -292,6 +355,16 @@ func AppendAttributedTo(with WithAttributedTo, attribTo ...*url.URL) {
|
||||
}, attribTo...)
|
||||
}
|
||||
|
||||
// GetOneInteractingObject extracts a single interactingObject property from 'with', else returns error.
|
||||
// Note: it is an error for the passed type to return any count other than 1 interactingObject value.
|
||||
func GetOneInteractingObject(with WithInteractingObject) (*url.URL, error) {
|
||||
object := GetInteractingObject(with)
|
||||
if len(object) != 1 {
|
||||
return nil, gtserror.NewfAt(3, "expected single interactingObject, received: %v", log.Formatted(object))
|
||||
}
|
||||
return object[0], nil
|
||||
}
|
||||
|
||||
// GetInteractingObject returns IRIs contained in the interactingObject property of 'with'.
|
||||
func GetInteractingObject(with WithInteractingObject) []*url.URL {
|
||||
intObjProp := with.GetGoToSocialInteractingObject()
|
||||
@@ -310,6 +383,16 @@ func AppendInteractingObject(with WithInteractingObject, interactingObject ...*u
|
||||
}, interactingObject...)
|
||||
}
|
||||
|
||||
// GetOneInteractionTarget extracts a single interactingTarget property from 'with', else returns error.
|
||||
// Note: it is an error for the passed type to return any count other than 1 interactionTarget value.
|
||||
func GetOneInteractionTarget(with WithInteractionTarget) (*url.URL, error) {
|
||||
target := GetInteractionTarget(with)
|
||||
if len(target) != 1 {
|
||||
return nil, gtserror.NewfAt(3, "expected single interactionTarget, received: %v", log.Formatted(target))
|
||||
}
|
||||
return target[0], nil
|
||||
}
|
||||
|
||||
// GetInteractionTarget returns IRIs contained in the interactionTarget property of 'with'.
|
||||
func GetInteractionTarget(with WithInteractionTarget) []*url.URL {
|
||||
intTargetProp := with.GetGoToSocialInteractionTarget()
|
||||
@@ -331,7 +414,7 @@ func AppendInteractionTarget(with WithInteractionTarget, interactionTarget ...*u
|
||||
// GetInReplyTo returns the IRIs contained in the InReplyTo property of 'with'.
|
||||
func GetInReplyTo(with WithInReplyTo) []*url.URL {
|
||||
replyProp := with.GetActivityStreamsInReplyTo()
|
||||
return extractIRIs[vocab.ActivityStreamsInReplyToPropertyIterator](replyProp)
|
||||
return extractIRIs(replyProp)
|
||||
}
|
||||
|
||||
// AppendInReplyTo appends the given IRIs to the InReplyTo property of 'with'.
|
||||
@@ -463,7 +546,7 @@ func SetMovedTo(with WithMovedTo, movedTo *url.URL) {
|
||||
// GetAlsoKnownAs returns the IRI contained in the alsoKnownAs property of 'with'.
|
||||
func GetAlsoKnownAs(with WithAlsoKnownAs) []*url.URL {
|
||||
alsoKnownAsProp := with.GetActivityStreamsAlsoKnownAs()
|
||||
return getIRIs[vocab.ActivityStreamsAlsoKnownAsPropertyIterator](alsoKnownAsProp)
|
||||
return getIRIs(alsoKnownAsProp)
|
||||
}
|
||||
|
||||
// SetAlsoKnownAs sets the given IRIs on the alsoKnownAs property of 'with'.
|
||||
|
||||
@@ -537,7 +537,7 @@ func (suite *InboxPostTestSuite) TestPostFromBlockedAccount() {
|
||||
requestingAccount,
|
||||
targetAccount,
|
||||
http.StatusForbidden,
|
||||
`{"error":"Forbidden: blocked"}`,
|
||||
`{"error":"Forbidden"}`,
|
||||
suite.signatureCheck,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -49,6 +49,7 @@ import (
|
||||
"code.superseriousbusiness.org/gotosocial/internal/api/client/polls"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/api/client/preferences"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/api/client/push"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/api/client/relaypushes"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/api/client/reports"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/api/client/scheduledstatuses"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/api/client/search"
|
||||
@@ -101,6 +102,7 @@ type Client struct {
|
||||
polls *polls.Module // api/v1/polls
|
||||
preferences *preferences.Module // api/v1/preferences
|
||||
push *push.Module // api/v1/push
|
||||
relayPushes *relaypushes.Module // api/v1/relay_pushes
|
||||
reports *reports.Module // api/v1/reports
|
||||
scheduledStatuses *scheduledstatuses.Module // api/v1/scheduled_statuses
|
||||
search *search.Module // api/v1/search, api/v2/search
|
||||
@@ -160,6 +162,7 @@ func (c *Client) Route(r *router.Router, m ...gin.HandlerFunc) {
|
||||
c.polls.Route(h)
|
||||
c.preferences.Route(h)
|
||||
c.push.Route(h)
|
||||
c.relayPushes.Route(h)
|
||||
c.reports.Route(h)
|
||||
c.scheduledStatuses.Route(h)
|
||||
c.search.Route(h)
|
||||
@@ -207,6 +210,7 @@ func NewClient(state *state.State, p *processing.Processor) *Client {
|
||||
polls: polls.New(p),
|
||||
preferences: preferences.New(p),
|
||||
push: push.New(p),
|
||||
relayPushes: relaypushes.New(p),
|
||||
reports: reports.New(p),
|
||||
scheduledStatuses: scheduledstatuses.New(p),
|
||||
search: search.New(p),
|
||||
|
||||
@@ -27,50 +27,56 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
BasePath = "/v1/admin"
|
||||
EmojiPath = BasePath + "/custom_emojis"
|
||||
EmojiPathWithID = EmojiPath + "/:" + apiutil.IDKey
|
||||
EmojiCategoriesPath = EmojiPath + "/categories"
|
||||
DomainBlocksPath = BasePath + "/domain_blocks"
|
||||
DomainBlocksPathWithID = DomainBlocksPath + "/:" + apiutil.IDKey
|
||||
DomainAllowsPath = BasePath + "/domain_allows"
|
||||
DomainAllowsPathWithID = DomainAllowsPath + "/:" + apiutil.IDKey
|
||||
DomainLimitsPath = BasePath + "/domain_limits"
|
||||
DomainLimitsPathWithID = DomainLimitsPath + "/:" + apiutil.IDKey
|
||||
DomainPermissionDraftsPath = BasePath + "/domain_permission_drafts"
|
||||
DomainPermissionDraftsPathWithID = DomainPermissionDraftsPath + "/:" + apiutil.IDKey
|
||||
DomainPermissionDraftAcceptPath = DomainPermissionDraftsPathWithID + "/accept"
|
||||
DomainPermissionDraftRemovePath = DomainPermissionDraftsPathWithID + "/remove"
|
||||
DomainPermissionExcludesPath = BasePath + "/domain_permission_excludes"
|
||||
DomainPermissionExcludesPathWithID = DomainPermissionExcludesPath + "/:" + apiutil.IDKey
|
||||
DomainPermissionSubscriptionsPath = BasePath + "/domain_permission_subscriptions"
|
||||
DomainPermissionSubscriptionsPathWithID = DomainPermissionSubscriptionsPath + "/:" + apiutil.IDKey
|
||||
DomainPermissionSubscriptionsPreviewPath = DomainPermissionSubscriptionsPath + "/preview"
|
||||
DomainPermissionSubscriptionRemovePath = DomainPermissionSubscriptionsPathWithID + "/remove"
|
||||
DomainPermissionSubscriptionTestPath = DomainPermissionSubscriptionsPathWithID + "/test"
|
||||
DomainKeysExpirePath = BasePath + "/domain_keys_expire"
|
||||
HeaderAllowsPath = BasePath + "/header_allows"
|
||||
HeaderAllowsPathWithID = HeaderAllowsPath + "/:" + apiutil.IDKey
|
||||
HeaderBlocksPath = BasePath + "/header_blocks"
|
||||
HeaderBlocksPathWithID = HeaderBlocksPath + "/:" + apiutil.IDKey
|
||||
AccountsV1Path = BasePath + "/accounts"
|
||||
AccountsV2Path = "/v2/admin/accounts"
|
||||
AccountsPathWithID = AccountsV1Path + "/:" + apiutil.IDKey
|
||||
AccountsActionPath = AccountsPathWithID + "/action"
|
||||
AccountsApprovePath = AccountsPathWithID + "/approve"
|
||||
AccountsRejectPath = AccountsPathWithID + "/reject"
|
||||
MediaCleanupPath = BasePath + "/media_cleanup"
|
||||
MediaPurgePath = BasePath + "/media_purge"
|
||||
MediaRefetchPath = BasePath + "/media_refetch"
|
||||
ReportsPath = BasePath + "/reports"
|
||||
ReportsPathWithID = ReportsPath + "/:" + apiutil.IDKey
|
||||
ReportsResolvePath = ReportsPathWithID + "/resolve"
|
||||
EmailPath = BasePath + "/email"
|
||||
EmailTestPath = EmailPath + "/test"
|
||||
InstanceRulesPath = BasePath + "/instance/rules"
|
||||
InstanceRulesPathWithID = InstanceRulesPath + "/:" + apiutil.IDKey
|
||||
InstancesPath = BasePath + "/instances"
|
||||
InstancesPathWithID = InstancesPath + "/:" + apiutil.IDKey
|
||||
BasePath = "/v1/admin"
|
||||
WithID = "/:" + apiutil.IDKey
|
||||
EmojiPath = BasePath + "/custom_emojis"
|
||||
EmojiPathWithID = EmojiPath + WithID
|
||||
EmojiCategoriesPath = EmojiPath + "/categories"
|
||||
DomainBlocksPath = BasePath + "/domain_blocks"
|
||||
DomainBlocksPathWithID = DomainBlocksPath + WithID
|
||||
DomainAllowsPath = BasePath + "/domain_allows"
|
||||
DomainAllowsPathWithID = DomainAllowsPath + WithID
|
||||
DomainLimitsPath = BasePath + "/domain_limits"
|
||||
DomainLimitsPathWithID = DomainLimitsPath + WithID
|
||||
DomainPermissionDraftsPath = BasePath + "/domain_permission_drafts"
|
||||
DomainPermissionDraftsPathWithID = DomainPermissionDraftsPath + WithID
|
||||
DomainPermissionDraftAcceptPath = DomainPermissionDraftsPathWithID + "/accept"
|
||||
DomainPermissionDraftRemovePath = DomainPermissionDraftsPathWithID + "/remove"
|
||||
DomainPermissionExcludesPath = BasePath + "/domain_permission_excludes"
|
||||
DomainPermissionExcludesPathWithID = DomainPermissionExcludesPath + WithID
|
||||
DomainPermissionSubscriptionsPath = BasePath + "/domain_permission_subscriptions"
|
||||
DomainPermissionSubscriptionsPathWithID = DomainPermissionSubscriptionsPath + WithID
|
||||
DomainPermissionSubscriptionsPreviewPath = DomainPermissionSubscriptionsPath + "/preview"
|
||||
DomainPermissionSubscriptionRemovePath = DomainPermissionSubscriptionsPathWithID + "/remove"
|
||||
DomainPermissionSubscriptionTestPath = DomainPermissionSubscriptionsPathWithID + "/test"
|
||||
DomainKeysExpirePath = BasePath + "/domain_keys_expire"
|
||||
HeaderAllowsPath = BasePath + "/header_allows"
|
||||
HeaderAllowsPathWithID = HeaderAllowsPath + WithID
|
||||
HeaderBlocksPath = BasePath + "/header_blocks"
|
||||
HeaderBlocksPathWithID = HeaderBlocksPath + WithID
|
||||
AccountsV1Path = BasePath + "/accounts"
|
||||
AccountsV2Path = "/v2/admin/accounts"
|
||||
AccountsPathWithID = AccountsV1Path + WithID
|
||||
AccountsActionPath = AccountsPathWithID + "/action"
|
||||
AccountsApprovePath = AccountsPathWithID + "/approve"
|
||||
AccountsRejectPath = AccountsPathWithID + "/reject"
|
||||
MediaCleanupPath = BasePath + "/media_cleanup"
|
||||
MediaPurgePath = BasePath + "/media_purge"
|
||||
MediaRefetchPath = BasePath + "/media_refetch"
|
||||
ReportsPath = BasePath + "/reports"
|
||||
ReportsPathWithID = ReportsPath + WithID
|
||||
ReportsResolvePath = ReportsPathWithID + "/resolve"
|
||||
EmailPath = BasePath + "/email"
|
||||
EmailTestPath = EmailPath + "/test"
|
||||
InstanceRulesPath = BasePath + "/instance/rules"
|
||||
InstanceRulesPathWithID = InstanceRulesPath + WithID
|
||||
InstancesPath = BasePath + "/instances"
|
||||
InstancesPathWithID = InstancesPath + WithID
|
||||
InstanceClearDeliveryErrorsPath = InstancesPathWithID + "/clear_delivery_errors"
|
||||
RelaySubscriptionsPath = BasePath + "/relay_subscriptions"
|
||||
RelaySubscriptionsPathWithID = RelaySubscriptionsPath + WithID
|
||||
RelaySubscriptionMatchersPath = RelaySubscriptionsPathWithID + "/matchers"
|
||||
RelaySubscriptionMatchersPathWithMatcherID = RelaySubscriptionMatchersPath + "/:" + apiutil.RelayMatcherIDKey
|
||||
|
||||
FilterQueryKey = "filter"
|
||||
MaxShortcodeDomainKey = "max_shortcode_domain"
|
||||
@@ -185,4 +191,15 @@ func (m *Module) Route(attachHandler func(method string, path string, f ...gin.H
|
||||
// instances stuff
|
||||
attachHandler(http.MethodGet, InstancesPath, m.InstancesGETHandler)
|
||||
attachHandler(http.MethodGet, InstancesPathWithID, m.InstanceGETHandler)
|
||||
attachHandler(http.MethodPost, InstanceClearDeliveryErrorsPath, m.InstanceClearDeliveryErrorsPOSTHandler)
|
||||
|
||||
// relays stuff
|
||||
attachHandler(http.MethodGet, RelaySubscriptionsPath, m.RelaySubscriptionsGETHandler)
|
||||
attachHandler(http.MethodGet, RelaySubscriptionsPathWithID, m.RelaySubscriptionGETHandler)
|
||||
attachHandler(http.MethodPost, RelaySubscriptionsPath, m.RelaySubscriptionPOSTHandler)
|
||||
attachHandler(http.MethodPut, RelaySubscriptionsPathWithID, m.RelaySubscriptionPUTHandler)
|
||||
attachHandler(http.MethodDelete, RelaySubscriptionsPathWithID, m.RelaySubscriptionDELETEHandler)
|
||||
attachHandler(http.MethodPost, RelaySubscriptionMatchersPath, m.RelaySubscriptionMatcherPOSTHandler)
|
||||
attachHandler(http.MethodDelete, RelaySubscriptionMatchersPathWithMatcherID, m.RelaySubscriptionMatcherDELETEHandler)
|
||||
attachHandler(http.MethodPut, RelaySubscriptionMatchersPathWithMatcherID, m.RelaySubscriptionMatcherPUTHandler)
|
||||
}
|
||||
|
||||
@@ -122,6 +122,14 @@ func (m *Module) createDomainPermissions(
|
||||
}
|
||||
|
||||
if !importing {
|
||||
// Punify domain already to ensure it's valid
|
||||
// so we can pass bad request if it's not.
|
||||
form.Domain, err = util.PunifySafely(form.Domain)
|
||||
if err != nil {
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
// Single domain permission creation.
|
||||
perm, _, errWithCode := single(
|
||||
c.Request.Context(),
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
// 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 admin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
apiutil "code.superseriousbusiness.org/gotosocial/internal/api/util"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/gtserror"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// InstanceClearDeliveryErrorsPOSTHandler swagger:operation POST /api/v1/admin/instances/{id}/clear_delivery_errors adminInstanceClearDeliveryErrors
|
||||
//
|
||||
// Clear delivery errors for instance with given ID.
|
||||
//
|
||||
// ---
|
||||
// tags:
|
||||
// - admin
|
||||
//
|
||||
// produces:
|
||||
// - application/json
|
||||
//
|
||||
// parameters:
|
||||
// -
|
||||
// name: id
|
||||
// type: string
|
||||
// description: The id of the instance.
|
||||
// in: path
|
||||
// required: true
|
||||
//
|
||||
// security:
|
||||
// - OAuth2 Bearer:
|
||||
// - admin:write:instances
|
||||
//
|
||||
// responses:
|
||||
// '200':
|
||||
// name: instances
|
||||
// description: Admin model instance.
|
||||
// schema:
|
||||
// "$ref": "#/definitions/adminInstance"
|
||||
// '400':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: bad request
|
||||
// '401':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: unauthorized
|
||||
// '404':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: not found
|
||||
// '406':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: not acceptable
|
||||
// '500':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: internal server error
|
||||
func (m *Module) InstanceClearDeliveryErrorsPOSTHandler(c *gin.Context) {
|
||||
authed, errWithCode := apiutil.TokenAuth(c,
|
||||
true, true, true, true,
|
||||
apiutil.ScopeAdminWriteInstances,
|
||||
)
|
||||
if errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
if !*authed.User.Admin {
|
||||
err := fmt.Errorf("user %s not an admin", authed.User.ID)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
if authed.Account.IsMoving() {
|
||||
apiutil.ForbiddenAfterMove(c)
|
||||
return
|
||||
}
|
||||
|
||||
if _, errWithCode := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
id, errWithCode := apiutil.ParseID(c.Param(apiutil.IDKey))
|
||||
if errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
resp, errWithCode := m.processor.Admin().InstanceDeliveryErrorsClear(
|
||||
c.Request.Context(),
|
||||
id,
|
||||
)
|
||||
if errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
apiutil.JSON(c, http.StatusOK, resp)
|
||||
}
|
||||
@@ -60,7 +60,7 @@ import (
|
||||
// in: query
|
||||
// type: string
|
||||
// description: Order by default "first_seen" (newest -> oldest) or "alphabetical" (a -> z).
|
||||
// default: latest
|
||||
// default: first_seen
|
||||
// -
|
||||
// name: with_errors_only
|
||||
// in: query
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
@@ -107,23 +106,17 @@ func (m *Module) MediaCleanupPOSTHandler(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// Normalize remoteCacheDays.
|
||||
var remoteCacheDays int
|
||||
if form.RemoteCacheDays == nil {
|
||||
remoteCacheDays = config.GetMediaRemoteCacheDays()
|
||||
} else if remoteCacheDays = *form.RemoteCacheDays; remoteCacheDays < 0 {
|
||||
text := fmt.Sprintf("invalid value for remote_cache_days; value was %d, cannot be less than 0", remoteCacheDays)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(errors.New(text), text), m.processor.InstanceGetV1)
|
||||
return
|
||||
if form.RemoteCacheDays.Duration == 0 {
|
||||
form.RemoteCacheDays.Duration = config.GetMediaRemoteCacheDuration()
|
||||
}
|
||||
|
||||
if errWithCode := m.processor.Admin().MediaPrune(
|
||||
c.Request.Context(),
|
||||
remoteCacheDays,
|
||||
form.RemoteCacheDays.Duration,
|
||||
); errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
apiutil.JSON(c, http.StatusOK, remoteCacheDays)
|
||||
apiutil.JSON(c, http.StatusOK, form.RemoteCacheDays)
|
||||
}
|
||||
|
||||
@@ -132,7 +132,7 @@ func (suite *MediaCleanupTestSuite) TestMediaCleanupNegative() {
|
||||
if err != nil {
|
||||
suite.FailNow(err.Error())
|
||||
}
|
||||
suite.Equal(`{"error":"Bad Request: invalid value for remote_cache_days; value was -10, cannot be less than 0"}`, string(b))
|
||||
suite.Equal(`{"error":"Bad Request: invalid unit"}`, string(b))
|
||||
}
|
||||
|
||||
func TestMediaCleanupTestSuite(t *testing.T) {
|
||||
|
||||
@@ -0,0 +1,603 @@
|
||||
// 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 admin
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"code.superseriousbusiness.org/gotosocial/internal/gtserror"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/util"
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
apimodel "code.superseriousbusiness.org/gotosocial/internal/api/model"
|
||||
apiutil "code.superseriousbusiness.org/gotosocial/internal/api/util"
|
||||
)
|
||||
|
||||
// RelaySubscriptionsGETHandler swagger:operation GET /api/v1/admin/relay_subscriptions adminRelaySubscriptions
|
||||
//
|
||||
// View relay subscriptions.
|
||||
//
|
||||
// The subscriptions will be returned in descending chronological order (newest first), with sequential IDs (bigger = newer).
|
||||
//
|
||||
// ---
|
||||
// tags:
|
||||
// - admin
|
||||
//
|
||||
// produces:
|
||||
// - application/json
|
||||
//
|
||||
// security:
|
||||
// - OAuth2 Bearer:
|
||||
// - admin:read:relays
|
||||
//
|
||||
// responses:
|
||||
// '200':
|
||||
// name: relay subscriptions
|
||||
// description: Array of relay subscriptions.
|
||||
// schema:
|
||||
// type: array
|
||||
// items:
|
||||
// "$ref": "#/definitions/relayConnection"
|
||||
// '400':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: bad request
|
||||
// '401':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: unauthorized
|
||||
// '404':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: not found
|
||||
// '406':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: not acceptable
|
||||
// '500':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: internal server error
|
||||
func (m *Module) RelaySubscriptionsGETHandler(c *gin.Context) {
|
||||
authed, errWithCode := apiutil.TokenAuth(c,
|
||||
true, true, true, true,
|
||||
apiutil.ScopeAdminReadRelays,
|
||||
)
|
||||
if errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
if !*authed.User.Admin {
|
||||
err := fmt.Errorf("user %s not an admin", authed.User.ID)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
if _, errWithCode := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
resp, errWithCode := m.processor.Admin().RelaySubscriptionsGet(c.Request.Context())
|
||||
if errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
if resp.LinkHeader != "" {
|
||||
c.Header("Link", resp.LinkHeader)
|
||||
}
|
||||
|
||||
apiutil.JSON(c, http.StatusOK, resp.Items)
|
||||
}
|
||||
|
||||
// RelaySubscriptionGETHandler swagger:operation GET /api/v1/admin/relay_subscriptions/{id} adminRelaySubscriptionGet
|
||||
//
|
||||
// View relay subscription with the given ID.
|
||||
//
|
||||
// ---
|
||||
// tags:
|
||||
// - admin
|
||||
//
|
||||
// produces:
|
||||
// - application/json
|
||||
//
|
||||
// parameters:
|
||||
// -
|
||||
// name: id
|
||||
// type: string
|
||||
// description: The id of the relay subscription.
|
||||
// in: path
|
||||
// required: true
|
||||
//
|
||||
// security:
|
||||
// - OAuth2 Bearer:
|
||||
// - admin:read:relays
|
||||
//
|
||||
// responses:
|
||||
// '200':
|
||||
// name: relay subscription
|
||||
// description: The requested relay subscription.
|
||||
// schema:
|
||||
// "$ref": "#/definitions/relayConnection"
|
||||
// '400':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: bad request
|
||||
// '401':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: unauthorized
|
||||
// '404':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: not found
|
||||
// '406':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: not acceptable
|
||||
// '500':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: internal server error
|
||||
func (m *Module) RelaySubscriptionGETHandler(c *gin.Context) {
|
||||
authed, errWithCode := apiutil.TokenAuth(c,
|
||||
true, true, true, true,
|
||||
apiutil.ScopeAdminReadRelays,
|
||||
)
|
||||
if errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
if !*authed.User.Admin {
|
||||
err := fmt.Errorf("user %s not an admin", authed.User.ID)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
if _, errWithCode := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
id, errWithCode := apiutil.ParseID(c.Param(apiutil.IDKey))
|
||||
if errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
resp, errWithCode := m.processor.Admin().RelaySubscriptionGet(c.Request.Context(), id)
|
||||
if errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
apiutil.JSON(c, http.StatusOK, resp)
|
||||
}
|
||||
|
||||
// RelaySubscriptionPOSTHandler swagger:operation POST /api/v1/admin/relay_subscriptions relaySubscriptionCreate
|
||||
//
|
||||
// Create a new relay subscription targeting a remote relay actor URI.
|
||||
//
|
||||
// The parameters can also be given in the body of the request, as JSON, if the content-type is set to 'application/json'.
|
||||
// The parameters can also be given in the body of the request, as XML, if the content-type is set to 'application/xml'.
|
||||
//
|
||||
// ---
|
||||
// tags:
|
||||
// - admin
|
||||
//
|
||||
// consumes:
|
||||
// - application/json
|
||||
// - application/xml
|
||||
// - application/x-www-form-urlencoded
|
||||
//
|
||||
// produces:
|
||||
// - application/json
|
||||
//
|
||||
// parameters:
|
||||
// -
|
||||
// name: relay_actor_uri
|
||||
// in: formData
|
||||
// description: The ActivityPub URI of the remote relay actor.
|
||||
// type: string
|
||||
// required: true
|
||||
// -
|
||||
// name: public
|
||||
// in: formData
|
||||
// description: Ingest public posts. If false, never ingest public posts via this subscription.
|
||||
// type: boolean
|
||||
// default: true
|
||||
// -
|
||||
// name: unlisted
|
||||
// in: formData
|
||||
// description: Ingest unlisted posts. If false, never ingest unlisted posts via this subscription.
|
||||
// type: boolean
|
||||
// -
|
||||
// name: match_by_default
|
||||
// in: formData
|
||||
// description: >-
|
||||
// Controls whether the relay subscription should ingest all non-ignored posts by default.
|
||||
// If set true, and no "exclude"-type matchers are set on the subscription, then all included, non-ignored posts will be ingested.
|
||||
// type: boolean
|
||||
// -
|
||||
// name: ignore_sensitive
|
||||
// in: formData
|
||||
// description: Never ingest sensitive posts via this subscription.
|
||||
// type: boolean
|
||||
// -
|
||||
// name: ignore_media
|
||||
// in: formData
|
||||
// description: Never ingest posts with media attachments via this subscription.
|
||||
// type: boolean
|
||||
// -
|
||||
// name: ignore_replies
|
||||
// in: formData
|
||||
// description: Never ingest non-self-replies (ie., comments) via this subscription.
|
||||
// type: boolean
|
||||
//
|
||||
// security:
|
||||
// - OAuth2 Bearer:
|
||||
// - admin:write:relays
|
||||
//
|
||||
// responses:
|
||||
// '200':
|
||||
// description: The newly-created relay subscription.
|
||||
// schema:
|
||||
// "$ref": "#/definitions/relayConnection"
|
||||
// '400':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: bad request
|
||||
// '401':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: unauthorized
|
||||
// '403':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: forbidden
|
||||
// '404':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: not found
|
||||
// '406':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: not acceptable
|
||||
// '422':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: unprocessable -- remote actor URI could not be dereferenced, or remote actor host is blocked
|
||||
// '500':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: internal server error
|
||||
func (m *Module) RelaySubscriptionPOSTHandler(c *gin.Context) {
|
||||
authed, errWithCode := apiutil.TokenAuth(c,
|
||||
true, true, true, true,
|
||||
apiutil.ScopeAdminWriteRelays,
|
||||
)
|
||||
if errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
if !*authed.User.Admin {
|
||||
err := fmt.Errorf("user %s not an admin", authed.User.ID)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
if authed.Account.IsMoving() {
|
||||
apiutil.ForbiddenAfterMove(c)
|
||||
return
|
||||
}
|
||||
|
||||
if _, errWithCode := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
form := new(apimodel.RelayConnectionCreateRequest)
|
||||
if err := c.ShouldBind(form); err != nil {
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
// Ensure relayActorURI is parseable.
|
||||
relayActorURI, err := url.Parse(form.RelayActorURI)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("relay_actor_uri not parseable: %w", err)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
resp, errWithCode := m.processor.Admin().RelaySubscriptionCreate(
|
||||
c.Request.Context(),
|
||||
authed,
|
||||
relayActorURI,
|
||||
util.PtrOrValue(form.Public, true), // default true
|
||||
util.PtrOrZero(form.Unlisted), // default false
|
||||
util.PtrOrZero(form.MatchByDefault), // default false
|
||||
util.PtrOrZero(form.IgnoreSensitive), // default false
|
||||
util.PtrOrZero(form.IgnoreMedia), // default false
|
||||
util.PtrOrZero(form.IgnoreReplies), // default false
|
||||
)
|
||||
if errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
apiutil.JSON(c, http.StatusOK, resp)
|
||||
}
|
||||
|
||||
// RelaySubscriptionPUTHandler swagger:operation PUT /api/v1/admin/relay_subscriptions/{id} relaySubscriptionUpdate
|
||||
//
|
||||
// Update a relay subscription.
|
||||
//
|
||||
// The parameters can also be given in the body of the request, as JSON, if the content-type is set to 'application/json'.
|
||||
// The parameters can also be given in the body of the request, as XML, if the content-type is set to 'application/xml'.
|
||||
//
|
||||
// ---
|
||||
// tags:
|
||||
// - admin
|
||||
//
|
||||
// consumes:
|
||||
// - application/json
|
||||
// - application/xml
|
||||
// - application/x-www-form-urlencoded
|
||||
//
|
||||
// produces:
|
||||
// - application/json
|
||||
//
|
||||
// parameters:
|
||||
// -
|
||||
// name: id
|
||||
// type: string
|
||||
// description: The id of the relay subscription.
|
||||
// in: path
|
||||
// required: true
|
||||
// -
|
||||
// name: public
|
||||
// in: formData
|
||||
// description: Ingest public posts. If false, never ingest public posts via this subscription.
|
||||
// type: boolean
|
||||
// -
|
||||
// name: unlisted
|
||||
// in: formData
|
||||
// description: Ingest unlisted posts. If false, never ingest unlisted posts via this subscription.
|
||||
// type: boolean
|
||||
// -
|
||||
// name: match_by_default
|
||||
// in: formData
|
||||
// description: >-
|
||||
// Controls whether the relay subscription should ingest all non-ignored posts by default.
|
||||
// If set true, and no "exclude"-type matchers are set on the subscription, then all included, non-ignored posts will be ingested.
|
||||
// type: boolean
|
||||
// -
|
||||
// name: ignore_sensitive
|
||||
// in: formData
|
||||
// description: Never ingest sensitive posts via this subscription.
|
||||
// type: boolean
|
||||
// -
|
||||
// name: ignore_media
|
||||
// in: formData
|
||||
// description: Never ingest posts with media attachments via this subscription.
|
||||
// type: boolean
|
||||
// -
|
||||
// name: ignore_replies
|
||||
// in: formData
|
||||
// description: Never ingest non-self-replies (ie., comments) via this subscription.
|
||||
// type: boolean
|
||||
//
|
||||
// security:
|
||||
// - OAuth2 Bearer:
|
||||
// - admin:write:relays
|
||||
//
|
||||
// responses:
|
||||
// '200':
|
||||
// description: The newly-created relay subscription.
|
||||
// schema:
|
||||
// "$ref": "#/definitions/relayConnection"
|
||||
// '400':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: bad request
|
||||
// '401':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: unauthorized
|
||||
// '403':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: forbidden
|
||||
// '404':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: not found
|
||||
// '406':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: not acceptable
|
||||
// '422':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: unprocessable -- remote actor URI could not be dereferenced, or remote actor host is blocked
|
||||
// '500':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: internal server error
|
||||
func (m *Module) RelaySubscriptionPUTHandler(c *gin.Context) {
|
||||
authed, errWithCode := apiutil.TokenAuth(c,
|
||||
true, true, true, true,
|
||||
apiutil.ScopeAdminWriteRelays,
|
||||
)
|
||||
if errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
if !*authed.User.Admin {
|
||||
err := fmt.Errorf("user %s not an admin", authed.User.ID)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
if authed.Account.IsMoving() {
|
||||
apiutil.ForbiddenAfterMove(c)
|
||||
return
|
||||
}
|
||||
|
||||
if _, errWithCode := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
id, errWithCode := apiutil.ParseID(c.Param(apiutil.IDKey))
|
||||
if errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
// Parse form.
|
||||
form := new(apimodel.RelayConnectionUpdateRequest)
|
||||
if err := c.ShouldBind(form); err != nil {
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
// Ensure something is being updated.
|
||||
if form.Public == nil &&
|
||||
form.Unlisted == nil &&
|
||||
form.MatchByDefault == nil &&
|
||||
form.IgnoreSensitive == nil &&
|
||||
form.IgnoreMedia == nil &&
|
||||
form.IgnoreReplies == nil {
|
||||
const errText = "no update fields provided"
|
||||
errWithCode := gtserror.NewErrorBadRequest(errors.New(errText), errText)
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
resp, errWithCode := m.processor.Admin().RelaySubscriptionUpdate(
|
||||
c.Request.Context(),
|
||||
id,
|
||||
form.Public,
|
||||
form.Unlisted,
|
||||
form.MatchByDefault,
|
||||
form.IgnoreSensitive,
|
||||
form.IgnoreMedia,
|
||||
form.IgnoreReplies,
|
||||
)
|
||||
if errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
apiutil.JSON(c, http.StatusOK, resp)
|
||||
}
|
||||
|
||||
// RelaySubscriptionDELETEHandler swagger:operation DELETE /api/v1/admin/relay_subscriptions/{id} adminRelaySubscriptionDelete
|
||||
//
|
||||
// Delete relay subscription with the given ID.
|
||||
//
|
||||
// ---
|
||||
// tags:
|
||||
// - admin
|
||||
//
|
||||
// produces:
|
||||
// - application/json
|
||||
//
|
||||
// parameters:
|
||||
// -
|
||||
// name: id
|
||||
// type: string
|
||||
// description: The id of the relay subscription.
|
||||
// in: path
|
||||
// required: true
|
||||
//
|
||||
// security:
|
||||
// - OAuth2 Bearer:
|
||||
// - admin:write:relays
|
||||
//
|
||||
// responses:
|
||||
// '200':
|
||||
// name: relay subscription
|
||||
// description: The deleted relay subscription.
|
||||
// schema:
|
||||
// "$ref": "#/definitions/relayConnection"
|
||||
// '400':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: bad request
|
||||
// '401':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: unauthorized
|
||||
// '404':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: not found
|
||||
// '406':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: not acceptable
|
||||
// '500':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: internal server error
|
||||
func (m *Module) RelaySubscriptionDELETEHandler(c *gin.Context) {
|
||||
authed, errWithCode := apiutil.TokenAuth(c,
|
||||
true, true, true, true,
|
||||
apiutil.ScopeAdminWriteRelays,
|
||||
)
|
||||
if errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
if !*authed.User.Admin {
|
||||
err := fmt.Errorf("user %s not an admin", authed.User.ID)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
if _, errWithCode := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
id, errWithCode := apiutil.ParseID(c.Param(apiutil.IDKey))
|
||||
if errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
resp, errWithCode := m.processor.Admin().RelaySubscriptionDelete(c.Request.Context(), id)
|
||||
if errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
apiutil.JSON(c, http.StatusOK, resp)
|
||||
}
|
||||
@@ -0,0 +1,430 @@
|
||||
// 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 admin
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
apimodel "code.superseriousbusiness.org/gotosocial/internal/api/model"
|
||||
apiutil "code.superseriousbusiness.org/gotosocial/internal/api/util"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/gtserror"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/util"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// RelaySubscriptionMatcherPOSTHandler swagger:operation POST /api/v1/admin/relay_subscriptions/{id}/matchers relaySubscriptionMatcherPost
|
||||
//
|
||||
// Add a relay matcher to a relay subscription.
|
||||
//
|
||||
// Returns the relay subscription with the given matcher added to it.
|
||||
//
|
||||
// ---
|
||||
// tags:
|
||||
// - admin
|
||||
//
|
||||
// consumes:
|
||||
// - application/json
|
||||
// - application/xml
|
||||
// - application/x-www-form-urlencoded
|
||||
//
|
||||
// produces:
|
||||
// - application/json
|
||||
//
|
||||
// parameters:
|
||||
// -
|
||||
// name: id
|
||||
// in: path
|
||||
// type: string
|
||||
// required: true
|
||||
// description: ID of the relay subscription.
|
||||
// -
|
||||
// name: keyword
|
||||
// in: formData
|
||||
// required: true
|
||||
// description: The text to be matched.
|
||||
// type: string
|
||||
// -
|
||||
// name: whole_word
|
||||
// in: formData
|
||||
// description: Matcher should consider word boundaries.
|
||||
// type: boolean
|
||||
// default: false
|
||||
// -
|
||||
// name: exclude
|
||||
// in: formData
|
||||
// description: Matcher should cause matched posts to be excluded from relaying rather than included.
|
||||
// type: boolean
|
||||
// default: false
|
||||
//
|
||||
// security:
|
||||
// - OAuth2 Bearer:
|
||||
// - admin:write:relays
|
||||
//
|
||||
// responses:
|
||||
// '200':
|
||||
// name: relay subscription
|
||||
// description: Relay subscription the newly-created matcher belongs to.
|
||||
// schema:
|
||||
// "$ref": "#/definitions/relayConnection"
|
||||
// '400':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: bad request
|
||||
// '401':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: unauthorized
|
||||
// '403':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: forbidden to moved accounts
|
||||
// '404':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: not found
|
||||
// '406':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: not acceptable
|
||||
// '422':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: unprocessable content
|
||||
// '500':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: internal server error
|
||||
func (m *Module) RelaySubscriptionMatcherPOSTHandler(c *gin.Context) {
|
||||
authed, errWithCode := apiutil.TokenAuth(c,
|
||||
true, true, true, true,
|
||||
apiutil.ScopeAdminWriteRelays,
|
||||
)
|
||||
if errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
if !*authed.User.Admin {
|
||||
err := fmt.Errorf("user %s not an admin", authed.User.ID)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
if authed.Account.IsMoving() {
|
||||
apiutil.ForbiddenAfterMove(c)
|
||||
return
|
||||
}
|
||||
|
||||
if _, errWithCode := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
relaySubscriptionID, errWithCode := apiutil.ParseID(c.Param(apiutil.IDKey))
|
||||
if errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
form := new(apimodel.RelayMatcherCreateUpdateRequest)
|
||||
if err := c.ShouldBind(form); err != nil {
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
// Ensure keyword is set.
|
||||
if form.Keyword == nil || *form.Keyword == "" {
|
||||
const errText = "keyword not provided"
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(errors.New(errText), errText), m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
resp, errWithCode := m.processor.Admin().RelaySubscriptionMatcherCreate(
|
||||
c.Request.Context(),
|
||||
relaySubscriptionID,
|
||||
*form.Keyword,
|
||||
util.PtrOrZero(form.WholeWord),
|
||||
util.PtrOrZero(form.Exclude),
|
||||
)
|
||||
if errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
apiutil.JSON(c, http.StatusOK, resp)
|
||||
}
|
||||
|
||||
// RelaySubscriptionMatcherDELETEHandler swagger:operation DELETE /api/v1/admin/relay_subscriptions/{id}/matchers/{matcher_id} relaySubscriptionMatcherDelete
|
||||
//
|
||||
// Remove a relay matcher from a relay subscription.
|
||||
//
|
||||
// Returns the relay subscription with the given matcher removed from it.
|
||||
//
|
||||
// ---
|
||||
// tags:
|
||||
// - admin
|
||||
//
|
||||
// produces:
|
||||
// - application/json
|
||||
//
|
||||
// parameters:
|
||||
// -
|
||||
// name: id
|
||||
// in: path
|
||||
// type: string
|
||||
// required: true
|
||||
// description: ID of the relay subscription.
|
||||
// -
|
||||
// name: matcher_id
|
||||
// type: string
|
||||
// description: ID of the relay matcher.
|
||||
// in: path
|
||||
// required: true
|
||||
//
|
||||
// security:
|
||||
// - OAuth2 Bearer:
|
||||
// - admin:write:relays
|
||||
//
|
||||
// responses:
|
||||
// '200':
|
||||
// name: relay subscription
|
||||
// description: Relay subscription the now-deleted matcher belonged to.
|
||||
// schema:
|
||||
// "$ref": "#/definitions/relayConnection"
|
||||
// '400':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: bad request
|
||||
// '401':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: unauthorized
|
||||
// '404':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: not found
|
||||
// '406':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: not acceptable
|
||||
// '500':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: internal server error
|
||||
func (m *Module) RelaySubscriptionMatcherDELETEHandler(c *gin.Context) {
|
||||
authed, errWithCode := apiutil.TokenAuth(c,
|
||||
true, true, true, true,
|
||||
apiutil.ScopeWriteRelays,
|
||||
)
|
||||
if errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
if !*authed.User.Admin {
|
||||
err := fmt.Errorf("user %s not an admin", authed.User.ID)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
if authed.Account.IsMoving() {
|
||||
apiutil.ForbiddenAfterMove(c)
|
||||
return
|
||||
}
|
||||
|
||||
if _, errWithCode := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
relayID, errWithCode := apiutil.ParseID(c.Param(apiutil.IDKey))
|
||||
if errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
matcherID, errWithCode := apiutil.ParseRelayMatcherID(c.Param(apiutil.RelayMatcherIDKey))
|
||||
if errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
resp, errWithCode := m.processor.Admin().RelaySubscriptionMatcherDelete(
|
||||
c.Request.Context(),
|
||||
relayID,
|
||||
matcherID,
|
||||
)
|
||||
if errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
apiutil.JSON(c, http.StatusOK, resp)
|
||||
}
|
||||
|
||||
// RelaySubscriptionMatcherPUTHandler swagger:operation PUT /api/v1/admin/relay_subscriptions/{id}/matchers/{matcher_id} relaySubscriptionMatcherPut
|
||||
//
|
||||
// Update a relay matcher on a relay subscription.
|
||||
//
|
||||
// ---
|
||||
// tags:
|
||||
// - admin
|
||||
//
|
||||
// consumes:
|
||||
// - application/json
|
||||
// - application/xml
|
||||
// - application/x-www-form-urlencoded
|
||||
//
|
||||
// produces:
|
||||
// - application/json
|
||||
//
|
||||
// parameters:
|
||||
// -
|
||||
// name: id
|
||||
// in: path
|
||||
// type: string
|
||||
// required: true
|
||||
// description: ID of the relay subscription.
|
||||
// -
|
||||
// name: matcher_id
|
||||
// type: string
|
||||
// description: ID of the relay matcher.
|
||||
// in: path
|
||||
// required: true
|
||||
// -
|
||||
// name: keyword
|
||||
// in: formData
|
||||
// required: true
|
||||
// description: The text to be matched.
|
||||
// type: string
|
||||
// -
|
||||
// name: whole_word
|
||||
// in: formData
|
||||
// description: Matcher should consider word boundaries.
|
||||
// type: boolean
|
||||
// default: false
|
||||
// -
|
||||
// name: exclude
|
||||
// in: formData
|
||||
// description: Matcher should cause matched posts to be excluded from relaying rather than included.
|
||||
// type: boolean
|
||||
// default: false
|
||||
//
|
||||
// security:
|
||||
// - OAuth2 Bearer:
|
||||
// - admin:write:relays
|
||||
//
|
||||
// responses:
|
||||
// '200':
|
||||
// name: relay subscription
|
||||
// description: Relay subscription the now-updated matcher belongs to.
|
||||
// schema:
|
||||
// "$ref": "#/definitions/relayConnection"
|
||||
// '400':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: bad request
|
||||
// '401':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: unauthorized
|
||||
// '403':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: forbidden to moved accounts
|
||||
// '404':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: not found
|
||||
// '406':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: not acceptable
|
||||
// '409':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: conflict (duplicate keyword)
|
||||
// '422':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: unprocessable content
|
||||
// '500':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: internal server error
|
||||
func (m *Module) RelaySubscriptionMatcherPUTHandler(c *gin.Context) {
|
||||
authed, errWithCode := apiutil.TokenAuth(c,
|
||||
true, true, true, true,
|
||||
apiutil.ScopeAdminWriteRelays,
|
||||
)
|
||||
if errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
if !*authed.User.Admin {
|
||||
err := fmt.Errorf("user %s not an admin", authed.User.ID)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
if authed.Account.IsMoving() {
|
||||
apiutil.ForbiddenAfterMove(c)
|
||||
return
|
||||
}
|
||||
|
||||
if _, errWithCode := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
relayID, errWithCode := apiutil.ParseID(c.Param(apiutil.IDKey))
|
||||
if errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
matcherID, errWithCode := apiutil.ParseRelayMatcherID(c.Param(apiutil.RelayMatcherIDKey))
|
||||
if errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
form := new(apimodel.RelayMatcherCreateUpdateRequest)
|
||||
if err := c.ShouldBind(form); err != nil {
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
resp, errWithCode := m.processor.Admin().RelaySubscriptionMatcherUpdate(
|
||||
c.Request.Context(),
|
||||
relayID,
|
||||
matcherID,
|
||||
form.Keyword,
|
||||
form.WholeWord,
|
||||
form.Exclude,
|
||||
)
|
||||
if errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
apiutil.JSON(c, http.StatusOK, resp)
|
||||
}
|
||||
@@ -0,0 +1,414 @@
|
||||
// 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 relaypushes
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
apimodel "code.superseriousbusiness.org/gotosocial/internal/api/model"
|
||||
apiutil "code.superseriousbusiness.org/gotosocial/internal/api/util"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/gtserror"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/util"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// RelayPushMatcherPOSTHandler swagger:operation POST /api/v1/relay_pushes/{id}/matchers relayPushMatcherPost
|
||||
//
|
||||
// Add a relay matcher to a relay push.
|
||||
//
|
||||
// Returns the relay push with the given matcher added to it.
|
||||
//
|
||||
// ---
|
||||
// tags:
|
||||
// - relay_pushes
|
||||
//
|
||||
// consumes:
|
||||
// - application/json
|
||||
// - application/xml
|
||||
// - application/x-www-form-urlencoded
|
||||
//
|
||||
// produces:
|
||||
// - application/json
|
||||
//
|
||||
// parameters:
|
||||
// -
|
||||
// name: id
|
||||
// in: path
|
||||
// type: string
|
||||
// required: true
|
||||
// description: ID of the relay push.
|
||||
// -
|
||||
// name: keyword
|
||||
// in: formData
|
||||
// required: true
|
||||
// description: The text to be matched.
|
||||
// type: string
|
||||
// -
|
||||
// name: whole_word
|
||||
// in: formData
|
||||
// description: Matcher should consider word boundaries.
|
||||
// type: boolean
|
||||
// default: false
|
||||
// -
|
||||
// name: exclude
|
||||
// in: formData
|
||||
// description: Matcher should cause matched posts to be excluded from relaying rather than included.
|
||||
// type: boolean
|
||||
// default: false
|
||||
//
|
||||
// security:
|
||||
// - OAuth2 Bearer:
|
||||
// - write:relays
|
||||
//
|
||||
// responses:
|
||||
// '200':
|
||||
// name: relay push
|
||||
// description: Relay push the newly-created matcher belongs to.
|
||||
// schema:
|
||||
// "$ref": "#/definitions/relayConnection"
|
||||
// '400':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: bad request
|
||||
// '401':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: unauthorized
|
||||
// '403':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: forbidden to moved accounts
|
||||
// '404':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: not found
|
||||
// '406':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: not acceptable
|
||||
// '422':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: unprocessable content
|
||||
// '500':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: internal server error
|
||||
func (m *Module) RelayPushMatcherPOSTHandler(c *gin.Context) {
|
||||
authed, errWithCode := apiutil.TokenAuth(c,
|
||||
true, true, true, true,
|
||||
apiutil.ScopeWriteRelays,
|
||||
)
|
||||
if errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
if authed.Account.IsMoving() {
|
||||
apiutil.ForbiddenAfterMove(c)
|
||||
return
|
||||
}
|
||||
|
||||
if _, errWithCode := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
relayPushID, errWithCode := apiutil.ParseID(c.Param(apiutil.IDKey))
|
||||
if errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
form := new(apimodel.RelayMatcherCreateUpdateRequest)
|
||||
if err := c.ShouldBind(form); err != nil {
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
// Ensure keyword is set.
|
||||
if form.Keyword == nil || *form.Keyword == "" {
|
||||
const errText = "keyword not provided"
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(errors.New(errText), errText), m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
resp, errWithCode := m.processor.RelayPush().MatcherCreate(
|
||||
c.Request.Context(),
|
||||
authed,
|
||||
relayPushID,
|
||||
*form.Keyword,
|
||||
util.PtrOrZero(form.WholeWord),
|
||||
util.PtrOrZero(form.Exclude),
|
||||
)
|
||||
if errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
apiutil.JSON(c, http.StatusOK, resp)
|
||||
}
|
||||
|
||||
// RelayPushMatcherDELETEHandler swagger:operation DELETE /api/v1/relay_pushes/{id}/matchers/{matcher_id} relayPushMatcherDelete
|
||||
//
|
||||
// Remove a relay matcher from a relay push.
|
||||
//
|
||||
// Returns the relay push with the given matcher removed from it.
|
||||
//
|
||||
// ---
|
||||
// tags:
|
||||
// - relay_pushes
|
||||
//
|
||||
// produces:
|
||||
// - application/json
|
||||
//
|
||||
// parameters:
|
||||
// -
|
||||
// name: id
|
||||
// in: path
|
||||
// type: string
|
||||
// required: true
|
||||
// description: ID of the relay push.
|
||||
// -
|
||||
// name: matcher_id
|
||||
// type: string
|
||||
// description: ID of the relay matcher.
|
||||
// in: path
|
||||
// required: true
|
||||
//
|
||||
// security:
|
||||
// - OAuth2 Bearer:
|
||||
// - write:relays
|
||||
//
|
||||
// responses:
|
||||
// '200':
|
||||
// name: relay push
|
||||
// description: Relay push the now-deleted matcher belonged to.
|
||||
// schema:
|
||||
// "$ref": "#/definitions/relayConnection"
|
||||
// '400':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: bad request
|
||||
// '401':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: unauthorized
|
||||
// '404':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: not found
|
||||
// '406':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: not acceptable
|
||||
// '500':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: internal server error
|
||||
func (m *Module) RelayPushMatcherDELETEHandler(c *gin.Context) {
|
||||
authed, errWithCode := apiutil.TokenAuth(c,
|
||||
true, true, true, true,
|
||||
apiutil.ScopeWriteRelays,
|
||||
)
|
||||
if errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
if authed.Account.IsMoving() {
|
||||
apiutil.ForbiddenAfterMove(c)
|
||||
return
|
||||
}
|
||||
|
||||
if _, errWithCode := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
relayID, errWithCode := apiutil.ParseID(c.Param(apiutil.IDKey))
|
||||
if errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
matcherID, errWithCode := apiutil.ParseRelayMatcherID(c.Param(apiutil.RelayMatcherIDKey))
|
||||
if errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
resp, errWithCode := m.processor.RelayPush().MatcherDelete(
|
||||
c.Request.Context(),
|
||||
authed,
|
||||
relayID,
|
||||
matcherID,
|
||||
)
|
||||
if errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
apiutil.JSON(c, http.StatusOK, resp)
|
||||
}
|
||||
|
||||
// RelayPushMatcherPUTHandler swagger:operation PUT /api/v1/relay_pushes/{id}/matchers/{matcher_id} relayPushMatcherPut
|
||||
//
|
||||
// Update a relay matcher on a relay push.
|
||||
//
|
||||
// ---
|
||||
// tags:
|
||||
// - relay_pushes
|
||||
//
|
||||
// consumes:
|
||||
// - application/json
|
||||
// - application/xml
|
||||
// - application/x-www-form-urlencoded
|
||||
//
|
||||
// produces:
|
||||
// - application/json
|
||||
//
|
||||
// parameters:
|
||||
// -
|
||||
// name: id
|
||||
// in: path
|
||||
// type: string
|
||||
// required: true
|
||||
// description: ID of the relay push.
|
||||
// -
|
||||
// name: matcher_id
|
||||
// type: string
|
||||
// description: ID of the relay matcher.
|
||||
// in: path
|
||||
// required: true
|
||||
// -
|
||||
// name: keyword
|
||||
// in: formData
|
||||
// required: true
|
||||
// description: The text to be matched.
|
||||
// type: string
|
||||
// -
|
||||
// name: whole_word
|
||||
// in: formData
|
||||
// description: Matcher should consider word boundaries.
|
||||
// type: boolean
|
||||
// default: false
|
||||
// -
|
||||
// name: exclude
|
||||
// in: formData
|
||||
// description: Matcher should cause matched posts to be excluded from relaying rather than included.
|
||||
// type: boolean
|
||||
// default: false
|
||||
//
|
||||
// security:
|
||||
// - OAuth2 Bearer:
|
||||
// - write:relays
|
||||
//
|
||||
// responses:
|
||||
// '200':
|
||||
// name: relay push
|
||||
// description: Relay push the now-updated matcher belongs to.
|
||||
// schema:
|
||||
// "$ref": "#/definitions/relayConnection"
|
||||
// '400':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: bad request
|
||||
// '401':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: unauthorized
|
||||
// '403':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: forbidden to moved accounts
|
||||
// '404':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: not found
|
||||
// '406':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: not acceptable
|
||||
// '409':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: conflict (duplicate keyword)
|
||||
// '422':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: unprocessable content
|
||||
// '500':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: internal server error
|
||||
func (m *Module) RelayPushMatcherPUTHandler(c *gin.Context) {
|
||||
authed, errWithCode := apiutil.TokenAuth(c,
|
||||
true, true, true, true,
|
||||
apiutil.ScopeWriteRelays,
|
||||
)
|
||||
if errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
if authed.Account.IsMoving() {
|
||||
apiutil.ForbiddenAfterMove(c)
|
||||
return
|
||||
}
|
||||
|
||||
if _, errWithCode := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
relayID, errWithCode := apiutil.ParseID(c.Param(apiutil.IDKey))
|
||||
if errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
matcherID, errWithCode := apiutil.ParseRelayMatcherID(c.Param(apiutil.RelayMatcherIDKey))
|
||||
if errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
form := new(apimodel.RelayMatcherCreateUpdateRequest)
|
||||
if err := c.ShouldBind(form); err != nil {
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
resp, errWithCode := m.processor.RelayPush().MatcherUpdate(
|
||||
c.Request.Context(),
|
||||
authed,
|
||||
relayID,
|
||||
matcherID,
|
||||
form.Keyword,
|
||||
form.WholeWord,
|
||||
form.Exclude,
|
||||
)
|
||||
if errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
apiutil.JSON(c, http.StatusOK, resp)
|
||||
}
|
||||
@@ -0,0 +1,607 @@
|
||||
// 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 relaypushes
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
apimodel "code.superseriousbusiness.org/gotosocial/internal/api/model"
|
||||
apiutil "code.superseriousbusiness.org/gotosocial/internal/api/util"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/gtserror"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/processing"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/util"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
const (
|
||||
BasePath = "/v1/relay_pushes"
|
||||
WithID = "/:" + apiutil.IDKey
|
||||
BasePathWithID = BasePath + WithID
|
||||
RelayPushMatchersPath = BasePathWithID + "/matchers"
|
||||
RelayPushMatchersPathWithID = RelayPushMatchersPath + "/:" + apiutil.RelayMatcherIDKey
|
||||
)
|
||||
|
||||
type Module struct {
|
||||
processor *processing.Processor
|
||||
}
|
||||
|
||||
func New(processor *processing.Processor) *Module {
|
||||
return &Module{
|
||||
processor: processor,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Module) Route(attachHandler func(method string, path string, f ...gin.HandlerFunc) gin.IRoutes) {
|
||||
attachHandler(http.MethodGet, BasePath, m.RelayPushesGETHandler)
|
||||
attachHandler(http.MethodGet, BasePathWithID, m.RelayPushGETHandler)
|
||||
attachHandler(http.MethodPost, BasePath, m.RelayPushPOSTHandler)
|
||||
attachHandler(http.MethodPut, BasePathWithID, m.RelayPushPUTHandler)
|
||||
attachHandler(http.MethodDelete, BasePathWithID, m.RelayPushDELETEHandler)
|
||||
attachHandler(http.MethodPost, RelayPushMatchersPath, m.RelayPushMatcherPOSTHandler)
|
||||
attachHandler(http.MethodDelete, RelayPushMatchersPathWithID, m.RelayPushMatcherDELETEHandler)
|
||||
attachHandler(http.MethodPut, RelayPushMatchersPathWithID, m.RelayPushMatcherPUTHandler)
|
||||
}
|
||||
|
||||
// RelayPushesGETHandler swagger:operation GET /api/v1/relay_pushes relayPushes
|
||||
//
|
||||
// View relay pushes.
|
||||
//
|
||||
// The pushes will be returned in descending chronological order (newest first), with sequential IDs (bigger = newer).
|
||||
//
|
||||
// ---
|
||||
// tags:
|
||||
// - relay_pushes
|
||||
//
|
||||
// produces:
|
||||
// - application/json
|
||||
//
|
||||
// security:
|
||||
// - OAuth2 Bearer:
|
||||
// - read:relays
|
||||
//
|
||||
// responses:
|
||||
// '200':
|
||||
// name: relay pushes
|
||||
// description: Array of relay pushes.
|
||||
// schema:
|
||||
// type: array
|
||||
// items:
|
||||
// "$ref": "#/definitions/relayConnection"
|
||||
// '400':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: bad request
|
||||
// '401':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: unauthorized
|
||||
// '404':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: not found
|
||||
// '406':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: not acceptable
|
||||
// '500':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: internal server error
|
||||
func (m *Module) RelayPushesGETHandler(c *gin.Context) {
|
||||
authed, errWithCode := apiutil.TokenAuth(c,
|
||||
true, true, true, true,
|
||||
apiutil.ScopeReadRelays,
|
||||
)
|
||||
if errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
if _, errWithCode := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
resp, errWithCode := m.processor.RelayPush().RelayPushesGet(c.Request.Context(), authed.Account.ID)
|
||||
if errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
if resp.LinkHeader != "" {
|
||||
c.Header("Link", resp.LinkHeader)
|
||||
}
|
||||
|
||||
apiutil.JSON(c, http.StatusOK, resp.Items)
|
||||
}
|
||||
|
||||
// RelayPushGETHandler swagger:operation GET /api/v1/relay_pushes/{id} relayPushGet
|
||||
//
|
||||
// View relay push with the given ID.
|
||||
//
|
||||
// ---
|
||||
// tags:
|
||||
// - relay_pushes
|
||||
//
|
||||
// produces:
|
||||
// - application/json
|
||||
//
|
||||
// parameters:
|
||||
// -
|
||||
// name: id
|
||||
// type: string
|
||||
// description: The id of the relay push.
|
||||
// in: path
|
||||
// required: true
|
||||
//
|
||||
// security:
|
||||
// - OAuth2 Bearer:
|
||||
// - read:relays
|
||||
//
|
||||
// responses:
|
||||
// '200':
|
||||
// name: relay push
|
||||
// description: The requested relay push.
|
||||
// schema:
|
||||
// "$ref": "#/definitions/relayConnection"
|
||||
// '400':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: bad request
|
||||
// '401':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: unauthorized
|
||||
// '404':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: not found
|
||||
// '406':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: not acceptable
|
||||
// '500':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: internal server error
|
||||
func (m *Module) RelayPushGETHandler(c *gin.Context) {
|
||||
authed, errWithCode := apiutil.TokenAuth(c,
|
||||
true, true, true, true,
|
||||
apiutil.ScopeReadRelays,
|
||||
)
|
||||
if errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
if _, errWithCode := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
id, errWithCode := apiutil.ParseID(c.Param(apiutil.IDKey))
|
||||
if errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
resp, errWithCode := m.processor.RelayPush().RelayPushGet(
|
||||
c.Request.Context(),
|
||||
authed,
|
||||
id,
|
||||
)
|
||||
if errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
apiutil.JSON(c, http.StatusOK, resp)
|
||||
}
|
||||
|
||||
// RelayPushPOSTHandler swagger:operation POST /api/v1/relay_pushes relayPushCreate
|
||||
//
|
||||
// Create a new relay push targeting a remote relay actor URI.
|
||||
//
|
||||
// The parameters can also be given in the body of the request, as JSON, if the content-type is set to 'application/json'.
|
||||
// The parameters can also be given in the body of the request, as XML, if the content-type is set to 'application/xml'.
|
||||
//
|
||||
// ---
|
||||
// tags:
|
||||
// - relay_pushes
|
||||
//
|
||||
// consumes:
|
||||
// - application/json
|
||||
// - application/xml
|
||||
// - application/x-www-form-urlencoded
|
||||
//
|
||||
// produces:
|
||||
// - application/json
|
||||
//
|
||||
// parameters:
|
||||
// -
|
||||
// name: relay_actor_uri
|
||||
// in: formData
|
||||
// description: The ActivityPub URI of the remote relay actor.
|
||||
// type: string
|
||||
// required: true
|
||||
// -
|
||||
// name: public
|
||||
// in: formData
|
||||
// description: Push public posts. If false, never send public posts to this relay.
|
||||
// type: boolean
|
||||
// default: true
|
||||
// -
|
||||
// name: unlisted
|
||||
// in: formData
|
||||
// description: Push unlisted posts. If false, never send unlisted posts to this relay.
|
||||
// type: boolean
|
||||
// -
|
||||
// name: match_by_default
|
||||
// in: formData
|
||||
// description: >-
|
||||
// Controls whether the relay push should send all non-ignored posts by default.
|
||||
// If set true, and no "exclude"-type matchers are set on the push, then all included, non-ignored posts will be sent.
|
||||
// type: boolean
|
||||
// -
|
||||
// name: ignore_sensitive
|
||||
// in: formData
|
||||
// description: Never send sensitive posts to this relay.
|
||||
// type: boolean
|
||||
// -
|
||||
// name: ignore_media
|
||||
// in: formData
|
||||
// description: Never send posts with media attachments to this relay.
|
||||
// type: boolean
|
||||
// -
|
||||
// name: ignore_replies
|
||||
// in: formData
|
||||
// description: Never send non-self-replies (ie., comments) to this relay.
|
||||
// type: boolean
|
||||
//
|
||||
// security:
|
||||
// - OAuth2 Bearer:
|
||||
// - write:relays
|
||||
//
|
||||
// responses:
|
||||
// '200':
|
||||
// description: The newly-created relay push.
|
||||
// schema:
|
||||
// "$ref": "#/definitions/relayConnection"
|
||||
// '400':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: bad request
|
||||
// '401':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: unauthorized
|
||||
// '403':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: forbidden
|
||||
// '404':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: not found
|
||||
// '406':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: not acceptable
|
||||
// '422':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: unprocessable -- remote actor URI could not be dereferenced, or remote actor host is blocked
|
||||
// '500':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/error"
|
||||
// description: internal server error
|
||||
func (m *Module) RelayPushPOSTHandler(c *gin.Context) {
|
||||
authed, errWithCode := apiutil.TokenAuth(c,
|
||||
true, true, true, true,
|
||||
apiutil.ScopeWriteRelays,
|
||||
)
|
||||
if errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
if authed.Account.IsMoving() {
|
||||
apiutil.ForbiddenAfterMove(c)
|
||||
return
|
||||
}
|
||||
|
||||
if _, errWithCode := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
form := new(apimodel.RelayConnectionCreateRequest)
|
||||
if err := c.ShouldBind(form); err != nil {
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
// Ensure relayActorURI is parseable.
|
||||
relayActorURI, err := url.Parse(form.RelayActorURI)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("relay_actor_uri not parseable: %w", err)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGetV1)
|
||||
return
|
||||
}
|
||||
|
||||
resp, errWithCode := m.processor.RelayPush().RelayPushCreate(
|
||||
c.Request.Context(),
|
||||
authed,
|
||||
relayActorURI,
|
||||