forked from mirrors/gotosocial
[chore] tidy up media manager, add calling func to errors, build-script improvements (#1835)
* media manager tidy-up: de-interface and remove unused PostDataFunc Signed-off-by: kim <grufwub@gmail.com> * remove last traces of media.Manager being an interface Signed-off-by: kim <grufwub@gmail.com> * update error to provide caller, allow tuneable via build tags Signed-off-by: kim <grufwub@gmail.com> * remove kim-specific build script changes Signed-off-by: kim <grufwub@gmail.com> * fix merge conflicts Signed-off-by: kim <grufwub@gmail.com> * update build-script to support externally setting build variables Signed-off-by: kim <grufwub@gmail.com> --------- Signed-off-by: kim <grufwub@gmail.com>
This commit is contained in:
parent
1f06914007
commit
5faeb4de20
64 changed files with 444 additions and 392 deletions
|
@ -31,7 +31,7 @@ import (
|
||||||
type prune struct {
|
type prune struct {
|
||||||
dbService db.DB
|
dbService db.DB
|
||||||
storage *gtsstorage.Driver
|
storage *gtsstorage.Driver
|
||||||
manager media.Manager
|
manager *media.Manager
|
||||||
state *state.State
|
state *state.State
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,7 @@ type EmojiGetTestSuite struct {
|
||||||
suite.Suite
|
suite.Suite
|
||||||
db db.DB
|
db db.DB
|
||||||
tc typeutils.TypeConverter
|
tc typeutils.TypeConverter
|
||||||
mediaManager media.Manager
|
mediaManager *media.Manager
|
||||||
federator federation.Federator
|
federator federation.Federator
|
||||||
emailSender email.Sender
|
emailSender email.Sender
|
||||||
processor *processing.Processor
|
processor *processing.Processor
|
||||||
|
|
|
@ -40,7 +40,7 @@ type UserStandardTestSuite struct {
|
||||||
suite.Suite
|
suite.Suite
|
||||||
db db.DB
|
db db.DB
|
||||||
tc typeutils.TypeConverter
|
tc typeutils.TypeConverter
|
||||||
mediaManager media.Manager
|
mediaManager *media.Manager
|
||||||
federator federation.Federator
|
federator federation.Federator
|
||||||
emailSender email.Sender
|
emailSender email.Sender
|
||||||
processor *processing.Processor
|
processor *processing.Processor
|
||||||
|
|
|
@ -46,7 +46,7 @@ type AuthStandardTestSuite struct {
|
||||||
db db.DB
|
db db.DB
|
||||||
storage *storage.Driver
|
storage *storage.Driver
|
||||||
state state.State
|
state state.State
|
||||||
mediaManager media.Manager
|
mediaManager *media.Manager
|
||||||
federator federation.Federator
|
federator federation.Federator
|
||||||
processor *processing.Processor
|
processor *processing.Processor
|
||||||
emailSender email.Sender
|
emailSender email.Sender
|
||||||
|
|
|
@ -45,7 +45,7 @@ type AccountStandardTestSuite struct {
|
||||||
suite.Suite
|
suite.Suite
|
||||||
db db.DB
|
db db.DB
|
||||||
storage *storage.Driver
|
storage *storage.Driver
|
||||||
mediaManager media.Manager
|
mediaManager *media.Manager
|
||||||
federator federation.Federator
|
federator federation.Federator
|
||||||
processor *processing.Processor
|
processor *processing.Processor
|
||||||
emailSender email.Sender
|
emailSender email.Sender
|
||||||
|
|
|
@ -45,7 +45,7 @@ type AdminStandardTestSuite struct {
|
||||||
suite.Suite
|
suite.Suite
|
||||||
db db.DB
|
db db.DB
|
||||||
storage *storage.Driver
|
storage *storage.Driver
|
||||||
mediaManager media.Manager
|
mediaManager *media.Manager
|
||||||
federator federation.Federator
|
federator federation.Federator
|
||||||
processor *processing.Processor
|
processor *processing.Processor
|
||||||
emailSender email.Sender
|
emailSender email.Sender
|
||||||
|
|
|
@ -51,7 +51,7 @@ type BookmarkTestSuite struct {
|
||||||
suite.Suite
|
suite.Suite
|
||||||
db db.DB
|
db db.DB
|
||||||
tc typeutils.TypeConverter
|
tc typeutils.TypeConverter
|
||||||
mediaManager media.Manager
|
mediaManager *media.Manager
|
||||||
federator federation.Federator
|
federator federation.Federator
|
||||||
emailSender email.Sender
|
emailSender email.Sender
|
||||||
processor *processing.Processor
|
processor *processing.Processor
|
||||||
|
|
|
@ -38,7 +38,7 @@ type FavouritesStandardTestSuite struct {
|
||||||
suite.Suite
|
suite.Suite
|
||||||
db db.DB
|
db db.DB
|
||||||
tc typeutils.TypeConverter
|
tc typeutils.TypeConverter
|
||||||
mediaManager media.Manager
|
mediaManager *media.Manager
|
||||||
federator federation.Federator
|
federator federation.Federator
|
||||||
emailSender email.Sender
|
emailSender email.Sender
|
||||||
processor *processing.Processor
|
processor *processing.Processor
|
||||||
|
|
|
@ -43,7 +43,7 @@ type FollowRequestStandardTestSuite struct {
|
||||||
suite.Suite
|
suite.Suite
|
||||||
db db.DB
|
db db.DB
|
||||||
storage *storage.Driver
|
storage *storage.Driver
|
||||||
mediaManager media.Manager
|
mediaManager *media.Manager
|
||||||
federator federation.Federator
|
federator federation.Federator
|
||||||
processor *processing.Processor
|
processor *processing.Processor
|
||||||
emailSender email.Sender
|
emailSender email.Sender
|
||||||
|
|
|
@ -44,7 +44,7 @@ type InstanceStandardTestSuite struct {
|
||||||
suite.Suite
|
suite.Suite
|
||||||
db db.DB
|
db db.DB
|
||||||
storage *storage.Driver
|
storage *storage.Driver
|
||||||
mediaManager media.Manager
|
mediaManager *media.Manager
|
||||||
federator federation.Federator
|
federator federation.Federator
|
||||||
processor *processing.Processor
|
processor *processing.Processor
|
||||||
emailSender email.Sender
|
emailSender email.Sender
|
||||||
|
|
|
@ -53,7 +53,7 @@ type MediaCreateTestSuite struct {
|
||||||
suite.Suite
|
suite.Suite
|
||||||
db db.DB
|
db db.DB
|
||||||
storage *storage.Driver
|
storage *storage.Driver
|
||||||
mediaManager media.Manager
|
mediaManager *media.Manager
|
||||||
federator federation.Federator
|
federator federation.Federator
|
||||||
tc typeutils.TypeConverter
|
tc typeutils.TypeConverter
|
||||||
oauthServer oauth.Server
|
oauthServer oauth.Server
|
||||||
|
|
|
@ -53,7 +53,7 @@ type MediaUpdateTestSuite struct {
|
||||||
storage *storage.Driver
|
storage *storage.Driver
|
||||||
federator federation.Federator
|
federator federation.Federator
|
||||||
tc typeutils.TypeConverter
|
tc typeutils.TypeConverter
|
||||||
mediaManager media.Manager
|
mediaManager *media.Manager
|
||||||
oauthServer oauth.Server
|
oauthServer oauth.Server
|
||||||
emailSender email.Sender
|
emailSender email.Sender
|
||||||
processor *processing.Processor
|
processor *processing.Processor
|
||||||
|
|
|
@ -36,7 +36,7 @@ type ReportsStandardTestSuite struct {
|
||||||
suite.Suite
|
suite.Suite
|
||||||
db db.DB
|
db db.DB
|
||||||
storage *storage.Driver
|
storage *storage.Driver
|
||||||
mediaManager media.Manager
|
mediaManager *media.Manager
|
||||||
federator federation.Federator
|
federator federation.Federator
|
||||||
processor *processing.Processor
|
processor *processing.Processor
|
||||||
emailSender email.Sender
|
emailSender email.Sender
|
||||||
|
|
|
@ -44,7 +44,7 @@ type SearchStandardTestSuite struct {
|
||||||
suite.Suite
|
suite.Suite
|
||||||
db db.DB
|
db db.DB
|
||||||
storage *storage.Driver
|
storage *storage.Driver
|
||||||
mediaManager media.Manager
|
mediaManager *media.Manager
|
||||||
federator federation.Federator
|
federator federation.Federator
|
||||||
processor *processing.Processor
|
processor *processing.Processor
|
||||||
emailSender email.Sender
|
emailSender email.Sender
|
||||||
|
|
|
@ -38,7 +38,7 @@ type StatusStandardTestSuite struct {
|
||||||
suite.Suite
|
suite.Suite
|
||||||
db db.DB
|
db db.DB
|
||||||
tc typeutils.TypeConverter
|
tc typeutils.TypeConverter
|
||||||
mediaManager media.Manager
|
mediaManager *media.Manager
|
||||||
federator federation.Federator
|
federator federation.Federator
|
||||||
emailSender email.Sender
|
emailSender email.Sender
|
||||||
processor *processing.Processor
|
processor *processing.Processor
|
||||||
|
|
|
@ -50,7 +50,7 @@ type StreamingTestSuite struct {
|
||||||
suite.Suite
|
suite.Suite
|
||||||
db db.DB
|
db db.DB
|
||||||
tc typeutils.TypeConverter
|
tc typeutils.TypeConverter
|
||||||
mediaManager media.Manager
|
mediaManager *media.Manager
|
||||||
federator federation.Federator
|
federator federation.Federator
|
||||||
emailSender email.Sender
|
emailSender email.Sender
|
||||||
processor *processing.Processor
|
processor *processing.Processor
|
||||||
|
|
|
@ -37,7 +37,7 @@ type UserStandardTestSuite struct {
|
||||||
suite.Suite
|
suite.Suite
|
||||||
db db.DB
|
db db.DB
|
||||||
tc typeutils.TypeConverter
|
tc typeutils.TypeConverter
|
||||||
mediaManager media.Manager
|
mediaManager *media.Manager
|
||||||
federator federation.Federator
|
federator federation.Federator
|
||||||
emailSender email.Sender
|
emailSender email.Sender
|
||||||
processor *processing.Processor
|
processor *processing.Processor
|
||||||
|
|
|
@ -46,7 +46,7 @@ type FileserverTestSuite struct {
|
||||||
federator federation.Federator
|
federator federation.Federator
|
||||||
tc typeutils.TypeConverter
|
tc typeutils.TypeConverter
|
||||||
processor *processing.Processor
|
processor *processing.Processor
|
||||||
mediaManager media.Manager
|
mediaManager *media.Manager
|
||||||
oauthServer oauth.Server
|
oauthServer oauth.Server
|
||||||
emailSender email.Sender
|
emailSender email.Sender
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,7 @@ type WebfingerStandardTestSuite struct {
|
||||||
db db.DB
|
db db.DB
|
||||||
state state.State
|
state state.State
|
||||||
tc typeutils.TypeConverter
|
tc typeutils.TypeConverter
|
||||||
mediaManager media.Manager
|
mediaManager *media.Manager
|
||||||
federator federation.Federator
|
federator federation.Federator
|
||||||
emailSender email.Sender
|
emailSender email.Sender
|
||||||
processor *processing.Processor
|
processor *processing.Processor
|
||||||
|
|
|
@ -21,7 +21,6 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"net/url"
|
"net/url"
|
||||||
"time"
|
"time"
|
||||||
|
@ -31,6 +30,7 @@ import (
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/ap"
|
"github.com/superseriousbusiness/gotosocial/internal/ap"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/id"
|
"github.com/superseriousbusiness/gotosocial/internal/id"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/log"
|
"github.com/superseriousbusiness/gotosocial/internal/log"
|
||||||
|
@ -93,14 +93,14 @@ func (d *deref) getAccountByURI(ctx context.Context, requestUser string, uri *ur
|
||||||
// Search the database for existing account with ID URI.
|
// Search the database for existing account with ID URI.
|
||||||
account, err = d.state.DB.GetAccountByURI(ctx, uriStr)
|
account, err = d.state.DB.GetAccountByURI(ctx, uriStr)
|
||||||
if err != nil && !errors.Is(err, db.ErrNoEntries) {
|
if err != nil && !errors.Is(err, db.ErrNoEntries) {
|
||||||
return nil, nil, fmt.Errorf("GetAccountByURI: error checking database for account %s by uri: %w", uriStr, err)
|
return nil, nil, gtserror.Newf("error checking database for account %s by uri: %w", uriStr, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if account == nil {
|
if account == nil {
|
||||||
// Else, search the database for existing by ID URL.
|
// Else, search the database for existing by ID URL.
|
||||||
account, err = d.state.DB.GetAccountByURL(ctx, uriStr)
|
account, err = d.state.DB.GetAccountByURL(ctx, uriStr)
|
||||||
if err != nil && !errors.Is(err, db.ErrNoEntries) {
|
if err != nil && !errors.Is(err, db.ErrNoEntries) {
|
||||||
return nil, nil, fmt.Errorf("GetAccountByURI: error checking database for account %s by url: %w", uriStr, err)
|
return nil, nil, gtserror.Newf("error checking database for account %s by url: %w", uriStr, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,7 +155,7 @@ func (d *deref) GetAccountByUsernameDomain(ctx context.Context, requestUser stri
|
||||||
// Search the database for existing account with USERNAME@DOMAIN.
|
// Search the database for existing account with USERNAME@DOMAIN.
|
||||||
account, err := d.state.DB.GetAccountByUsernameDomain(ctx, username, domain)
|
account, err := d.state.DB.GetAccountByUsernameDomain(ctx, username, domain)
|
||||||
if err != nil && !errors.Is(err, db.ErrNoEntries) {
|
if err != nil && !errors.Is(err, db.ErrNoEntries) {
|
||||||
return nil, nil, fmt.Errorf("GetAccountByUsernameDomain: error checking database for account %s@%s: %w", username, domain, err)
|
return nil, nil, gtserror.Newf("error checking database for account %s@%s: %w", username, domain, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if account == nil {
|
if account == nil {
|
||||||
|
@ -209,7 +209,7 @@ func (d *deref) RefreshAccount(ctx context.Context, requestUser string, account
|
||||||
// Parse the URI from account.
|
// Parse the URI from account.
|
||||||
uri, err := url.Parse(account.URI)
|
uri, err := url.Parse(account.URI)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("RefreshAccount: invalid account uri %q: %w", account.URI, err)
|
return nil, nil, gtserror.Newf("invalid account uri %q: %w", account.URI, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to update + deref existing account model.
|
// Try to update + deref existing account model.
|
||||||
|
@ -249,7 +249,7 @@ func (d *deref) RefreshAccountAsync(ctx context.Context, requestUser string, acc
|
||||||
// Parse the URI from account.
|
// Parse the URI from account.
|
||||||
uri, err := url.Parse(account.URI)
|
uri, err := url.Parse(account.URI)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf(ctx, "RefreshAccountAsync: invalid account uri %q: %v", account.URI, err)
|
log.Errorf(ctx, "invalid account uri %q: %v", account.URI, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -273,7 +273,7 @@ func (d *deref) enrichAccount(ctx context.Context, requestUser string, uri *url.
|
||||||
// Pre-fetch a transport for requesting username, used by later deref procedures.
|
// Pre-fetch a transport for requesting username, used by later deref procedures.
|
||||||
tsport, err := d.transportController.NewTransportForUsername(ctx, requestUser)
|
tsport, err := d.transportController.NewTransportForUsername(ctx, requestUser)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("enrichAccount: couldn't create transport: %w", err)
|
return nil, nil, gtserror.Newf("couldn't create transport: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if account.Username != "" {
|
if account.Username != "" {
|
||||||
|
@ -282,7 +282,7 @@ func (d *deref) enrichAccount(ctx context.Context, requestUser string, uri *url.
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if account.URI == "" {
|
if account.URI == "" {
|
||||||
// this is a new account (to us) with username@domain but failed webfinger, nothing more we can do.
|
// this is a new account (to us) with username@domain but failed webfinger, nothing more we can do.
|
||||||
return nil, nil, &ErrNotRetrievable{fmt.Errorf("enrichAccount: error webfingering account: %w", err)}
|
return nil, nil, &ErrNotRetrievable{gtserror.Newf("error webfingering account: %w", err)}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Simply log this error and move on, we already have an account URI.
|
// Simply log this error and move on, we already have an account URI.
|
||||||
|
@ -298,7 +298,7 @@ func (d *deref) enrichAccount(ctx context.Context, requestUser string, uri *url.
|
||||||
// After webfinger, we now have correct account domain from which we can do a final DB check.
|
// After webfinger, we now have correct account domain from which we can do a final DB check.
|
||||||
alreadyAccount, err := d.state.DB.GetAccountByUsernameDomain(ctx, account.Username, accDomain)
|
alreadyAccount, err := d.state.DB.GetAccountByUsernameDomain(ctx, account.Username, accDomain)
|
||||||
if err != nil && !errors.Is(err, db.ErrNoEntries) {
|
if err != nil && !errors.Is(err, db.ErrNoEntries) {
|
||||||
return nil, nil, fmt.Errorf("enrichAccount: db err looking for account again after webfinger: %w", err)
|
return nil, nil, gtserror.Newf("db err looking for account again after webfinger: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if alreadyAccount != nil {
|
if alreadyAccount != nil {
|
||||||
|
@ -318,15 +318,15 @@ func (d *deref) enrichAccount(ctx context.Context, requestUser string, uri *url.
|
||||||
// No URI provided / found, must parse from account.
|
// No URI provided / found, must parse from account.
|
||||||
uri, err = url.Parse(account.URI)
|
uri, err = url.Parse(account.URI)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("enrichAccount: invalid uri %q: %w", account.URI, err)
|
return nil, nil, gtserror.Newf("invalid uri %q: %w", account.URI, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check whether this account URI is a blocked domain / subdomain.
|
// Check whether this account URI is a blocked domain / subdomain.
|
||||||
if blocked, err := d.state.DB.IsDomainBlocked(ctx, uri.Host); err != nil {
|
if blocked, err := d.state.DB.IsDomainBlocked(ctx, uri.Host); err != nil {
|
||||||
return nil, nil, fmt.Errorf("enrichAccount: error checking blocked domain: %w", err)
|
return nil, nil, gtserror.Newf("error checking blocked domain: %w", err)
|
||||||
} else if blocked {
|
} else if blocked {
|
||||||
return nil, nil, fmt.Errorf("enrichAccount: %s is blocked", uri.Host)
|
return nil, nil, gtserror.Newf("%s is blocked", uri.Host)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mark deref+update handshake start.
|
// Mark deref+update handshake start.
|
||||||
|
@ -341,13 +341,13 @@ func (d *deref) enrichAccount(ctx context.Context, requestUser string, uri *url.
|
||||||
// Dereference latest version of the account.
|
// Dereference latest version of the account.
|
||||||
b, err := tsport.Dereference(ctx, uri)
|
b, err := tsport.Dereference(ctx, uri)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, &ErrNotRetrievable{fmt.Errorf("enrichAccount: error deferencing %s: %w", uri, err)}
|
return nil, nil, &ErrNotRetrievable{gtserror.Newf("error deferencing %s: %w", uri, err)}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attempt to resolve ActivityPub account from data.
|
// Attempt to resolve ActivityPub account from data.
|
||||||
apubAcc, err = ap.ResolveAccountable(ctx, b)
|
apubAcc, err = ap.ResolveAccountable(ctx, b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("enrichAccount: error resolving accountable from data for account %s: %w", uri, err)
|
return nil, nil, gtserror.Newf("error resolving accountable from data for account %s: %w", uri, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert the dereferenced AP account object to our GTS model.
|
// Convert the dereferenced AP account object to our GTS model.
|
||||||
|
@ -356,7 +356,7 @@ func (d *deref) enrichAccount(ctx context.Context, requestUser string, uri *url.
|
||||||
account.Domain,
|
account.Domain,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("enrichAccount: error converting accountable to gts model for account %s: %w", uri, err)
|
return nil, nil, gtserror.Newf("error converting accountable to gts model for account %s: %w", uri, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -375,7 +375,7 @@ func (d *deref) enrichAccount(ctx context.Context, requestUser string, uri *url.
|
||||||
// Assume the host from the returned ActivityPub representation.
|
// Assume the host from the returned ActivityPub representation.
|
||||||
idProp := apubAcc.GetJSONLDId()
|
idProp := apubAcc.GetJSONLDId()
|
||||||
if idProp == nil || !idProp.IsIRI() {
|
if idProp == nil || !idProp.IsIRI() {
|
||||||
return nil, nil, errors.New("enrichAccount: no id property found on person, or id was not an iri")
|
return nil, nil, gtserror.New("no id property found on person, or id was not an iri")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get IRI host value.
|
// Get IRI host value.
|
||||||
|
@ -471,7 +471,7 @@ func (d *deref) enrichAccount(ctx context.Context, requestUser string, uri *url.
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("enrichAccount: error putting in database: %w", err)
|
return nil, nil, gtserror.Newf("error putting in database: %w", err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Set time of update from the last-fetched date.
|
// Set time of update from the last-fetched date.
|
||||||
|
@ -483,7 +483,7 @@ func (d *deref) enrichAccount(ctx context.Context, requestUser string, uri *url.
|
||||||
|
|
||||||
// This is an existing account, update the model in the database.
|
// This is an existing account, update the model in the database.
|
||||||
if err := d.state.DB.UpdateAccount(ctx, latestAcc); err != nil {
|
if err := d.state.DB.UpdateAccount(ctx, latestAcc); err != nil {
|
||||||
return nil, nil, fmt.Errorf("enrichAccount: error updating database: %w", err)
|
return nil, nil, gtserror.Newf("error updating database: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -513,7 +513,7 @@ func (d *deref) fetchRemoteAccountAvatar(ctx context.Context, tsport transport.T
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create new media processing request from the media manager instance.
|
// Create new media processing request from the media manager instance.
|
||||||
processing, err = d.mediaManager.PreProcessMedia(ctx, data, nil, accountID, &media.AdditionalMediaInfo{
|
processing, err = d.mediaManager.PreProcessMedia(ctx, data, accountID, &media.AdditionalMediaInfo{
|
||||||
Avatar: func() *bool { v := true; return &v }(),
|
Avatar: func() *bool { v := true; return &v }(),
|
||||||
RemoteURL: &avatarURL,
|
RemoteURL: &avatarURL,
|
||||||
})
|
})
|
||||||
|
@ -566,7 +566,7 @@ func (d *deref) fetchRemoteAccountHeader(ctx context.Context, tsport transport.T
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create new media processing request from the media manager instance.
|
// Create new media processing request from the media manager instance.
|
||||||
processing, err = d.mediaManager.PreProcessMedia(ctx, data, nil, accountID, &media.AdditionalMediaInfo{
|
processing, err = d.mediaManager.PreProcessMedia(ctx, data, accountID, &media.AdditionalMediaInfo{
|
||||||
Header: func() *bool { v := true; return &v }(),
|
Header: func() *bool { v := true; return &v }(),
|
||||||
RemoteURL: &headerURL,
|
RemoteURL: &headerURL,
|
||||||
})
|
})
|
||||||
|
@ -719,7 +719,7 @@ func (d *deref) dereferenceAccountFeatured(ctx context.Context, requestUser stri
|
||||||
// Pre-fetch a transport for requesting username, used by later deref procedures.
|
// Pre-fetch a transport for requesting username, used by later deref procedures.
|
||||||
tsport, err := d.transportController.NewTransportForUsername(ctx, requestUser)
|
tsport, err := d.transportController.NewTransportForUsername(ctx, requestUser)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("enrichAccount: couldn't create transport: %w", err)
|
return gtserror.Newf("couldn't create transport: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
b, err := tsport.Dereference(ctx, uri)
|
b, err := tsport.Dereference(ctx, uri)
|
||||||
|
@ -729,32 +729,32 @@ func (d *deref) dereferenceAccountFeatured(ctx context.Context, requestUser stri
|
||||||
|
|
||||||
m := make(map[string]interface{})
|
m := make(map[string]interface{})
|
||||||
if err := json.Unmarshal(b, &m); err != nil {
|
if err := json.Unmarshal(b, &m); err != nil {
|
||||||
return fmt.Errorf("error unmarshalling bytes into json: %w", err)
|
return gtserror.Newf("error unmarshalling bytes into json: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
t, err := streams.ToType(ctx, m)
|
t, err := streams.ToType(ctx, m)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error resolving json into ap vocab type: %w", err)
|
return gtserror.Newf("error resolving json into ap vocab type: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if t.GetTypeName() != ap.ObjectOrderedCollection {
|
if t.GetTypeName() != ap.ObjectOrderedCollection {
|
||||||
return fmt.Errorf("%s was not an OrderedCollection", uri)
|
return gtserror.Newf("%s was not an OrderedCollection", uri)
|
||||||
}
|
}
|
||||||
|
|
||||||
collection, ok := t.(vocab.ActivityStreamsOrderedCollection)
|
collection, ok := t.(vocab.ActivityStreamsOrderedCollection)
|
||||||
if !ok {
|
if !ok {
|
||||||
return errors.New("couldn't coerce OrderedCollection")
|
return gtserror.New("couldn't coerce OrderedCollection")
|
||||||
}
|
}
|
||||||
|
|
||||||
items := collection.GetActivityStreamsOrderedItems()
|
items := collection.GetActivityStreamsOrderedItems()
|
||||||
if items == nil {
|
if items == nil {
|
||||||
return errors.New("nil orderedItems")
|
return gtserror.New("nil orderedItems")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get previous pinned statuses (we'll need these later).
|
// Get previous pinned statuses (we'll need these later).
|
||||||
wasPinned, err := d.state.DB.GetAccountPinnedStatuses(ctx, account.ID)
|
wasPinned, err := d.state.DB.GetAccountPinnedStatuses(ctx, account.ID)
|
||||||
if err != nil && !errors.Is(err, db.ErrNoEntries) {
|
if err != nil && !errors.Is(err, db.ErrNoEntries) {
|
||||||
return fmt.Errorf("error getting account pinned statuses: %w", err)
|
return gtserror.Newf("error getting account pinned statuses: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
statusURIs := make([]*url.URL, 0, items.Len())
|
statusURIs := make([]*url.URL, 0, items.Len())
|
||||||
|
|
|
@ -81,7 +81,7 @@ type deref struct {
|
||||||
state *state.State
|
state *state.State
|
||||||
typeConverter typeutils.TypeConverter
|
typeConverter typeutils.TypeConverter
|
||||||
transportController transport.Controller
|
transportController transport.Controller
|
||||||
mediaManager media.Manager
|
mediaManager *media.Manager
|
||||||
derefAvatars map[string]*media.ProcessingMedia
|
derefAvatars map[string]*media.ProcessingMedia
|
||||||
derefAvatarsMu mutexes.Mutex
|
derefAvatarsMu mutexes.Mutex
|
||||||
derefHeaders map[string]*media.ProcessingMedia
|
derefHeaders map[string]*media.ProcessingMedia
|
||||||
|
@ -93,7 +93,7 @@ type deref struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDereferencer returns a Dereferencer initialized with the given parameters.
|
// NewDereferencer returns a Dereferencer initialized with the given parameters.
|
||||||
func NewDereferencer(state *state.State, typeConverter typeutils.TypeConverter, transportController transport.Controller, mediaManager media.Manager) Dereferencer {
|
func NewDereferencer(state *state.State, typeConverter typeutils.TypeConverter, transportController transport.Controller, mediaManager *media.Manager) Dereferencer {
|
||||||
return &deref{
|
return &deref{
|
||||||
state: state,
|
state: state,
|
||||||
typeConverter: typeConverter,
|
typeConverter: typeConverter,
|
||||||
|
|
|
@ -60,7 +60,7 @@ func (d *deref) GetRemoteEmoji(ctx context.Context, requestingUsername string, r
|
||||||
return t.DereferenceMedia(innerCtx, derefURI)
|
return t.DereferenceMedia(innerCtx, derefURI)
|
||||||
}
|
}
|
||||||
|
|
||||||
newProcessing, err := d.mediaManager.PreProcessEmoji(ctx, dataFunc, nil, shortcode, id, emojiURI, ai, refresh)
|
newProcessing, err := d.mediaManager.PreProcessEmoji(ctx, dataFunc, shortcode, id, emojiURI, ai, refresh)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("GetRemoteEmoji: error processing emoji %s: %s", shortcodeDomain, err)
|
return nil, fmt.Errorf("GetRemoteEmoji: error processing emoji %s: %s", shortcodeDomain, err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,7 +45,7 @@ func (d *deref) GetRemoteMedia(ctx context.Context, requestingUsername string, a
|
||||||
return t.DereferenceMedia(innerCtx, derefURI)
|
return t.DereferenceMedia(innerCtx, derefURI)
|
||||||
}
|
}
|
||||||
|
|
||||||
processingMedia, err := d.mediaManager.ProcessMedia(ctx, dataFunc, nil, accountID, ai)
|
processingMedia, err := d.mediaManager.ProcessMedia(ctx, dataFunc, accountID, ai)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("GetRemoteMedia: error processing attachment: %s", err)
|
return nil, fmt.Errorf("GetRemoteMedia: error processing attachment: %s", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,6 @@ package dereferencing
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"net/url"
|
"net/url"
|
||||||
"time"
|
"time"
|
||||||
|
@ -28,6 +27,7 @@ import (
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/ap"
|
"github.com/superseriousbusiness/gotosocial/internal/ap"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/id"
|
"github.com/superseriousbusiness/gotosocial/internal/id"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/log"
|
"github.com/superseriousbusiness/gotosocial/internal/log"
|
||||||
|
@ -83,14 +83,14 @@ func (d *deref) getStatusByURI(ctx context.Context, requestUser string, uri *url
|
||||||
// Search the database for existing status with ID URI.
|
// Search the database for existing status with ID URI.
|
||||||
status, err = d.state.DB.GetStatusByURI(ctx, uriStr)
|
status, err = d.state.DB.GetStatusByURI(ctx, uriStr)
|
||||||
if err != nil && !errors.Is(err, db.ErrNoEntries) {
|
if err != nil && !errors.Is(err, db.ErrNoEntries) {
|
||||||
return nil, nil, fmt.Errorf("GetStatusByURI: error checking database for status %s by uri: %w", uriStr, err)
|
return nil, nil, gtserror.Newf("error checking database for status %s by uri: %w", uriStr, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if status == nil {
|
if status == nil {
|
||||||
// Else, search the database for existing by ID URL.
|
// Else, search the database for existing by ID URL.
|
||||||
status, err = d.state.DB.GetStatusByURL(ctx, uriStr)
|
status, err = d.state.DB.GetStatusByURL(ctx, uriStr)
|
||||||
if err != nil && !errors.Is(err, db.ErrNoEntries) {
|
if err != nil && !errors.Is(err, db.ErrNoEntries) {
|
||||||
return nil, nil, fmt.Errorf("GetStatusByURI: error checking database for status %s by url: %w", uriStr, err)
|
return nil, nil, gtserror.Newf("error checking database for status %s by url: %w", uriStr, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,7 +138,7 @@ func (d *deref) RefreshStatus(ctx context.Context, requestUser string, status *g
|
||||||
// Parse the URI from status.
|
// Parse the URI from status.
|
||||||
uri, err := url.Parse(status.URI)
|
uri, err := url.Parse(status.URI)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("RefreshStatus: invalid status uri %q: %w", status.URI, err)
|
return nil, nil, gtserror.Newf("invalid status uri %q: %w", status.URI, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to update + deref existing status model.
|
// Try to update + deref existing status model.
|
||||||
|
@ -170,7 +170,7 @@ func (d *deref) RefreshStatusAsync(ctx context.Context, requestUser string, stat
|
||||||
// Parse the URI from status.
|
// Parse the URI from status.
|
||||||
uri, err := url.Parse(status.URI)
|
uri, err := url.Parse(status.URI)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf(ctx, "RefreshStatusAsync: invalid status uri %q: %v", status.URI, err)
|
log.Errorf(ctx, "invalid status uri %q: %v", status.URI, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -192,14 +192,14 @@ func (d *deref) enrichStatus(ctx context.Context, requestUser string, uri *url.U
|
||||||
// Pre-fetch a transport for requesting username, used by later dereferencing.
|
// Pre-fetch a transport for requesting username, used by later dereferencing.
|
||||||
tsport, err := d.transportController.NewTransportForUsername(ctx, requestUser)
|
tsport, err := d.transportController.NewTransportForUsername(ctx, requestUser)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("enrichStatus: couldn't create transport: %w", err)
|
return nil, nil, gtserror.Newf("couldn't create transport: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check whether this account URI is a blocked domain / subdomain.
|
// Check whether this account URI is a blocked domain / subdomain.
|
||||||
if blocked, err := d.state.DB.IsDomainBlocked(ctx, uri.Host); err != nil {
|
if blocked, err := d.state.DB.IsDomainBlocked(ctx, uri.Host); err != nil {
|
||||||
return nil, nil, fmt.Errorf("enrichStatus: error checking blocked domain: %w", err)
|
return nil, nil, gtserror.Newf("error checking blocked domain: %w", err)
|
||||||
} else if blocked {
|
} else if blocked {
|
||||||
return nil, nil, fmt.Errorf("enrichStatus: %s is blocked", uri.Host)
|
return nil, nil, gtserror.Newf("%s is blocked", uri.Host)
|
||||||
}
|
}
|
||||||
|
|
||||||
var derefd bool
|
var derefd bool
|
||||||
|
@ -208,13 +208,13 @@ func (d *deref) enrichStatus(ctx context.Context, requestUser string, uri *url.U
|
||||||
// Dereference latest version of the status.
|
// Dereference latest version of the status.
|
||||||
b, err := tsport.Dereference(ctx, uri)
|
b, err := tsport.Dereference(ctx, uri)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, &ErrNotRetrievable{fmt.Errorf("enrichStatus: error deferencing %s: %w", uri, err)}
|
return nil, nil, &ErrNotRetrievable{gtserror.Newf("error deferencing %s: %w", uri, err)}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attempt to resolve ActivityPub status from data.
|
// Attempt to resolve ActivityPub status from data.
|
||||||
apubStatus, err = ap.ResolveStatusable(ctx, b)
|
apubStatus, err = ap.ResolveStatusable(ctx, b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("enrichStatus: error resolving statusable from data for account %s: %w", uri, err)
|
return nil, nil, gtserror.Newf("error resolving statusable from data for account %s: %w", uri, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mark as deref'd.
|
// Mark as deref'd.
|
||||||
|
@ -224,14 +224,14 @@ func (d *deref) enrichStatus(ctx context.Context, requestUser string, uri *url.U
|
||||||
// Get the attributed-to status in order to fetch profile.
|
// Get the attributed-to status in order to fetch profile.
|
||||||
attributedTo, err := ap.ExtractAttributedTo(apubStatus)
|
attributedTo, err := ap.ExtractAttributedTo(apubStatus)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, errors.New("enrichStatus: attributedTo was empty")
|
return nil, nil, gtserror.New("attributedTo was empty")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure we have the author account of the status dereferenced (+ up-to-date).
|
// Ensure we have the author account of the status dereferenced (+ up-to-date).
|
||||||
if author, _, err := d.getAccountByURI(ctx, requestUser, attributedTo); err != nil {
|
if author, _, err := d.getAccountByURI(ctx, requestUser, attributedTo); err != nil {
|
||||||
if status.AccountID == "" {
|
if status.AccountID == "" {
|
||||||
// Provided status account is nil, i.e. this is a new status / author, so a deref fail is unrecoverable.
|
// Provided status account is nil, i.e. this is a new status / author, so a deref fail is unrecoverable.
|
||||||
return nil, nil, fmt.Errorf("enrichStatus: failed to dereference status author %s: %w", uri, err)
|
return nil, nil, gtserror.Newf("failed to dereference status author %s: %w", uri, err)
|
||||||
}
|
}
|
||||||
} else if status.AccountID != "" && status.AccountID != author.ID {
|
} else if status.AccountID != "" && status.AccountID != author.ID {
|
||||||
// There already existed an account for this status author, but account ID changed. This shouldn't happen!
|
// There already existed an account for this status author, but account ID changed. This shouldn't happen!
|
||||||
|
@ -247,7 +247,7 @@ func (d *deref) enrichStatus(ctx context.Context, requestUser string, uri *url.U
|
||||||
// may contain out-of-date information, convert AP model to our GTS model.
|
// may contain out-of-date information, convert AP model to our GTS model.
|
||||||
latestStatus, err = d.typeConverter.ASStatusToStatus(ctx, apubStatus)
|
latestStatus, err = d.typeConverter.ASStatusToStatus(ctx, apubStatus)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("enrichStatus: error converting statusable to gts model for status %s: %w", uri, err)
|
return nil, nil, gtserror.Newf("error converting statusable to gts model for status %s: %w", uri, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -258,7 +258,7 @@ func (d *deref) enrichStatus(ctx context.Context, requestUser string, uri *url.U
|
||||||
// Generate new status ID from the provided creation date.
|
// Generate new status ID from the provided creation date.
|
||||||
latestStatus.ID, err = id.NewULIDFromTime(latestStatus.CreatedAt)
|
latestStatus.ID, err = id.NewULIDFromTime(latestStatus.CreatedAt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("enrichStatus: invalid created at date: %w", err)
|
return nil, nil, gtserror.Newf("invalid created at date: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -268,19 +268,19 @@ func (d *deref) enrichStatus(ctx context.Context, requestUser string, uri *url.U
|
||||||
|
|
||||||
// Ensure the status' mentions are populated, and pass in existing to check for changes.
|
// Ensure the status' mentions are populated, and pass in existing to check for changes.
|
||||||
if err := d.fetchStatusMentions(ctx, requestUser, status, latestStatus); err != nil {
|
if err := d.fetchStatusMentions(ctx, requestUser, status, latestStatus); err != nil {
|
||||||
return nil, nil, fmt.Errorf("enrichStatus: error populating mentions for status %s: %w", uri, err)
|
return nil, nil, gtserror.Newf("error populating mentions for status %s: %w", uri, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: populateStatusTags()
|
// TODO: populateStatusTags()
|
||||||
|
|
||||||
// Ensure the status' media attachments are populated, passing in existing to check for changes.
|
// Ensure the status' media attachments are populated, passing in existing to check for changes.
|
||||||
if err := d.fetchStatusAttachments(ctx, tsport, status, latestStatus); err != nil {
|
if err := d.fetchStatusAttachments(ctx, tsport, status, latestStatus); err != nil {
|
||||||
return nil, nil, fmt.Errorf("enrichStatus: error populating attachments for status %s: %w", uri, err)
|
return nil, nil, gtserror.Newf("error populating attachments for status %s: %w", uri, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure the status' emoji attachments are populated, passing in existing to check for changes.
|
// Ensure the status' emoji attachments are populated, passing in existing to check for changes.
|
||||||
if err := d.fetchStatusEmojis(ctx, requestUser, status, latestStatus); err != nil {
|
if err := d.fetchStatusEmojis(ctx, requestUser, status, latestStatus); err != nil {
|
||||||
return nil, nil, fmt.Errorf("enrichStatus: error populating emojis for status %s: %w", uri, err)
|
return nil, nil, gtserror.Newf("error populating emojis for status %s: %w", uri, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if status.CreatedAt.IsZero() {
|
if status.CreatedAt.IsZero() {
|
||||||
|
@ -297,12 +297,12 @@ func (d *deref) enrichStatus(ctx context.Context, requestUser string, uri *url.U
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("enrichStatus: error putting in database: %w", err)
|
return nil, nil, gtserror.Newf("error putting in database: %w", err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// This is an existing status, update the model in the database.
|
// This is an existing status, update the model in the database.
|
||||||
if err := d.state.DB.UpdateStatus(ctx, latestStatus); err != nil {
|
if err := d.state.DB.UpdateStatus(ctx, latestStatus); err != nil {
|
||||||
return nil, nil, fmt.Errorf("enrichStatus: error updating database: %w", err)
|
return nil, nil, gtserror.Newf("error updating database: %w", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -359,7 +359,7 @@ func (d *deref) fetchStatusMentions(ctx context.Context, requestUser string, exi
|
||||||
|
|
||||||
// Place the new mention into the database.
|
// Place the new mention into the database.
|
||||||
if err := d.state.DB.PutMention(ctx, mention); err != nil {
|
if err := d.state.DB.PutMention(ctx, mention); err != nil {
|
||||||
return fmt.Errorf("error putting mention in database: %w", err)
|
return gtserror.Newf("error putting mention in database: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the *new* mention and ID.
|
// Set the *new* mention and ID.
|
||||||
|
@ -406,7 +406,7 @@ func (d *deref) fetchStatusAttachments(ctx context.Context, tsport transport.Tra
|
||||||
// Start pre-processing remote media at remote URL.
|
// Start pre-processing remote media at remote URL.
|
||||||
processing, err := d.mediaManager.PreProcessMedia(ctx, func(ctx context.Context) (io.ReadCloser, int64, error) {
|
processing, err := d.mediaManager.PreProcessMedia(ctx, func(ctx context.Context) (io.ReadCloser, int64, error) {
|
||||||
return tsport.DereferenceMedia(ctx, remoteURL)
|
return tsport.DereferenceMedia(ctx, remoteURL)
|
||||||
}, nil, status.AccountID, &media.AdditionalMediaInfo{
|
}, status.AccountID, &media.AdditionalMediaInfo{
|
||||||
StatusID: &status.ID,
|
StatusID: &status.ID,
|
||||||
RemoteURL: &placeholder.RemoteURL,
|
RemoteURL: &placeholder.RemoteURL,
|
||||||
Description: &placeholder.Description,
|
Description: &placeholder.Description,
|
||||||
|
@ -447,7 +447,7 @@ func (d *deref) fetchStatusEmojis(ctx context.Context, requestUser string, exist
|
||||||
// Fetch the full-fleshed-out emoji objects for our status.
|
// Fetch the full-fleshed-out emoji objects for our status.
|
||||||
emojis, err := d.populateEmojis(ctx, status.Emojis, requestUser)
|
emojis, err := d.populateEmojis(ctx, status.Emojis, requestUser)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to populate emojis: %w", err)
|
return gtserror.Newf("failed to populate emojis: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Iterate over and get their IDs.
|
// Iterate over and get their IDs.
|
||||||
|
|
|
@ -19,13 +19,13 @@ package dereferencing
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
"codeberg.org/gruf/go-kv"
|
"codeberg.org/gruf/go-kv"
|
||||||
"github.com/superseriousbusiness/activity/streams/vocab"
|
"github.com/superseriousbusiness/activity/streams/vocab"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/ap"
|
"github.com/superseriousbusiness/gotosocial/internal/ap"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/log"
|
"github.com/superseriousbusiness/gotosocial/internal/log"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/uris"
|
"github.com/superseriousbusiness/gotosocial/internal/uris"
|
||||||
|
@ -39,12 +39,12 @@ const maxIter = 1000
|
||||||
func (d *deref) dereferenceThread(ctx context.Context, username string, statusIRI *url.URL, status *gtsmodel.Status, statusable ap.Statusable) {
|
func (d *deref) dereferenceThread(ctx context.Context, username string, statusIRI *url.URL, status *gtsmodel.Status, statusable ap.Statusable) {
|
||||||
// Ensure that ancestors have been fully dereferenced
|
// Ensure that ancestors have been fully dereferenced
|
||||||
if err := d.dereferenceStatusAncestors(ctx, username, status); err != nil {
|
if err := d.dereferenceStatusAncestors(ctx, username, status); err != nil {
|
||||||
log.Errorf(ctx, "error dereferencing status ancestors: %v", err)
|
log.Error(ctx, err) // log entry and error will include caller prefixes
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure that descendants have been fully dereferenced
|
// Ensure that descendants have been fully dereferenced
|
||||||
if err := d.dereferenceStatusDescendants(ctx, username, statusIRI, statusable); err != nil {
|
if err := d.dereferenceStatusDescendants(ctx, username, statusIRI, statusable); err != nil {
|
||||||
log.Errorf(ctx, "error dereferencing status descendants: %v", err)
|
log.Error(ctx, err) // log entry and error will include caller prefixes
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,7 +72,7 @@ func (d *deref) dereferenceStatusAncestors(ctx context.Context, username string,
|
||||||
// Parse this status's replied IRI
|
// Parse this status's replied IRI
|
||||||
replyIRI, err := url.Parse(status.InReplyToURI)
|
replyIRI, err := url.Parse(status.InReplyToURI)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("invalid status InReplyToURI %q: %w", status.InReplyToURI, err)
|
return gtserror.Newf("invalid status InReplyToURI %q: %w", status.InReplyToURI, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if replyIRI.Host == config.GetHost() {
|
if replyIRI.Host == config.GetHost() {
|
||||||
|
@ -81,13 +81,13 @@ func (d *deref) dereferenceStatusAncestors(ctx context.Context, username string,
|
||||||
// This is our status, extract ID from path
|
// This is our status, extract ID from path
|
||||||
_, id, err := uris.ParseStatusesPath(replyIRI)
|
_, id, err := uris.ParseStatusesPath(replyIRI)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("invalid local status IRI %q: %w", status.InReplyToURI, err)
|
return gtserror.Newf("invalid local status IRI %q: %w", status.InReplyToURI, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch this status from the database
|
// Fetch this status from the database
|
||||||
localStatus, err := d.state.DB.GetStatusByID(ctx, id)
|
localStatus, err := d.state.DB.GetStatusByID(ctx, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error fetching local status %q: %w", id, err)
|
return gtserror.Newf("error fetching local status %q: %w", id, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the fetched status
|
// Set the fetched status
|
||||||
|
@ -102,7 +102,7 @@ func (d *deref) dereferenceStatusAncestors(ctx context.Context, username string,
|
||||||
replyIRI,
|
replyIRI,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error fetching remote status %q: %w", status.InReplyToURI, err)
|
return gtserror.Newf("error fetching remote status %q: %w", status.InReplyToURI, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the fetched status
|
// Set the fetched status
|
||||||
|
@ -110,7 +110,7 @@ func (d *deref) dereferenceStatusAncestors(ctx context.Context, username string,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Errorf("reached %d ancestor iterations for %q", maxIter, ogIRI)
|
return gtserror.Newf("reached %d ancestor iterations for %q", maxIter, ogIRI)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *deref) dereferenceStatusDescendants(ctx context.Context, username string, statusIRI *url.URL, parent ap.Statusable) error {
|
func (d *deref) dereferenceStatusDescendants(ctx context.Context, username string, statusIRI *url.URL, parent ap.Statusable) error {
|
||||||
|
@ -312,5 +312,5 @@ stackLoop:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Errorf("reached %d descendant iterations for %q", maxIter, ogIRI.String())
|
return gtserror.Newf("reached %d descendant iterations for %q", maxIter, ogIRI.String())
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,13 +62,13 @@ type federator struct {
|
||||||
clock pub.Clock
|
clock pub.Clock
|
||||||
typeConverter typeutils.TypeConverter
|
typeConverter typeutils.TypeConverter
|
||||||
transportController transport.Controller
|
transportController transport.Controller
|
||||||
mediaManager media.Manager
|
mediaManager *media.Manager
|
||||||
actor pub.FederatingActor
|
actor pub.FederatingActor
|
||||||
dereferencing.Dereferencer
|
dereferencing.Dereferencer
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewFederator returns a new federator
|
// NewFederator returns a new federator
|
||||||
func NewFederator(state *state.State, federatingDB federatingdb.DB, transportController transport.Controller, typeConverter typeutils.TypeConverter, mediaManager media.Manager) Federator {
|
func NewFederator(state *state.State, federatingDB federatingdb.DB, transportController transport.Controller, typeConverter typeutils.TypeConverter, mediaManager *media.Manager) Federator {
|
||||||
dereferencer := dereferencing.NewDereferencer(state, typeConverter, transportController, mediaManager)
|
dereferencer := dereferencing.NewDereferencer(state, typeConverter, transportController, mediaManager)
|
||||||
|
|
||||||
clock := &Clock{}
|
clock := &Clock{}
|
||||||
|
|
|
@ -41,7 +41,7 @@ type Server interface {
|
||||||
// NewServer returns a new gotosocial server, initialized with the given configuration.
|
// NewServer returns a new gotosocial server, initialized with the given configuration.
|
||||||
// An error will be returned the caller if something goes wrong during initialization
|
// An error will be returned the caller if something goes wrong during initialization
|
||||||
// eg., no db or storage connection, port for router already in use, etc.
|
// eg., no db or storage connection, port for router already in use, etc.
|
||||||
func NewServer(db db.DB, apiRouter router.Router, federator federation.Federator, mediaManager media.Manager) (Server, error) {
|
func NewServer(db db.DB, apiRouter router.Router, federator federation.Federator, mediaManager *media.Manager) (Server, error) {
|
||||||
return &gotosocial{
|
return &gotosocial{
|
||||||
db: db,
|
db: db,
|
||||||
apiRouter: apiRouter,
|
apiRouter: apiRouter,
|
||||||
|
@ -55,7 +55,7 @@ type gotosocial struct {
|
||||||
db db.DB
|
db db.DB
|
||||||
apiRouter router.Router
|
apiRouter router.Router
|
||||||
federator federation.Federator
|
federator federation.Federator
|
||||||
mediaManager media.Manager
|
mediaManager *media.Manager
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start starts up the gotosocial server. If something goes wrong
|
// Start starts up the gotosocial server. If something goes wrong
|
||||||
|
|
|
@ -18,48 +18,38 @@
|
||||||
package gtserror
|
package gtserror
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"codeberg.org/gruf/go-byteutil"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// New returns a new error, prepended with caller function name if gtserror.Caller is enabled.
|
||||||
|
func New(msg string) error {
|
||||||
|
return newAt(3, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Newf returns a new formatted error, prepended with caller function name if gtserror.Caller is enabled.
|
||||||
|
func Newf(msgf string, args ...any) error {
|
||||||
|
return newfAt(3, msgf, args...)
|
||||||
|
}
|
||||||
|
|
||||||
// NewResponseError crafts an error from provided HTTP response
|
// NewResponseError crafts an error from provided HTTP response
|
||||||
// including the method, status and body (if any provided). This
|
// including the method, status and body (if any provided). This
|
||||||
// will also wrap the returned error using WithStatusCode().
|
// will also wrap the returned error using WithStatusCode() and
|
||||||
func NewResponseError(rsp *http.Response) error {
|
// will include the caller function name as a prefix.
|
||||||
var buf byteutil.Buffer
|
func NewFromResponse(rsp *http.Response) error {
|
||||||
|
// Build error with message without
|
||||||
// Get URL string ahead of time.
|
|
||||||
urlStr := rsp.Request.URL.String()
|
|
||||||
|
|
||||||
// Alloc guesstimate of required buf size.
|
|
||||||
buf.Guarantee(0 +
|
|
||||||
len(rsp.Request.Method) +
|
|
||||||
12 + // request to
|
|
||||||
len(urlStr) +
|
|
||||||
17 + // failed: status="
|
|
||||||
len(rsp.Status) +
|
|
||||||
8 + // " body="
|
|
||||||
256 + // max body size
|
|
||||||
1, // "
|
|
||||||
)
|
|
||||||
|
|
||||||
// Build error message string without
|
|
||||||
// using "fmt", as chances are this will
|
// using "fmt", as chances are this will
|
||||||
// be used in a hot code path and we
|
// be used in a hot code path and we
|
||||||
// know all the incoming types involved.
|
// know all the incoming types involved.
|
||||||
_, _ = buf.WriteString(rsp.Request.Method)
|
err := newAt(3, ""+
|
||||||
_, _ = buf.WriteString(" request to ")
|
rsp.Request.Method+
|
||||||
_, _ = buf.WriteString(urlStr)
|
" request to "+
|
||||||
_, _ = buf.WriteString(" failed: status=\"")
|
rsp.Request.URL.String()+
|
||||||
_, _ = buf.WriteString(rsp.Status)
|
" failed: status=\""+
|
||||||
_, _ = buf.WriteString("\" body=\"")
|
rsp.Status+
|
||||||
_, _ = buf.WriteString(drainBody(rsp.Body, 256))
|
"\" body=\""+
|
||||||
_, _ = buf.WriteString("\"")
|
drainBody(rsp.Body, 256)+
|
||||||
|
"\"",
|
||||||
// Create new error from msg.
|
)
|
||||||
err := errors.New(buf.String())
|
|
||||||
|
|
||||||
// Wrap error to provide status code.
|
// Wrap error to provide status code.
|
||||||
return WithStatusCode(err, rsp.StatusCode)
|
return WithStatusCode(err, rsp.StatusCode)
|
||||||
|
|
92
internal/gtserror/new_caller.go
Normal file
92
internal/gtserror/new_caller.go
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
// 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 !noerrcaller
|
||||||
|
|
||||||
|
package gtserror
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Caller returns whether created errors will prepend calling function name.
|
||||||
|
const Caller = true
|
||||||
|
|
||||||
|
// cerror wraps an error with a string
|
||||||
|
// prefix of the caller function name.
|
||||||
|
type cerror struct {
|
||||||
|
c string
|
||||||
|
e error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ce *cerror) Error() string {
|
||||||
|
msg := ce.e.Error()
|
||||||
|
return ce.c + ": " + msg
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ce *cerror) Unwrap() error {
|
||||||
|
return ce.e
|
||||||
|
}
|
||||||
|
|
||||||
|
// newAt is the same as New() but allows specifying calldepth.
|
||||||
|
func newAt(calldepth int, msg string) error {
|
||||||
|
return &cerror{
|
||||||
|
c: caller(calldepth + 1),
|
||||||
|
e: errors.New(msg),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// newfAt is the same as Newf() but allows specifying calldepth.
|
||||||
|
func newfAt(calldepth int, msgf string, args ...any) error {
|
||||||
|
return &cerror{
|
||||||
|
c: caller(calldepth + 1),
|
||||||
|
e: fmt.Errorf(msgf, args...),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// caller fetches the calling function name, skipping 'depth'. Results are cached per PC.
|
||||||
|
func caller(depth int) string {
|
||||||
|
var pcs [1]uintptr
|
||||||
|
|
||||||
|
// Fetch calling function using calldepth
|
||||||
|
_ = runtime.Callers(depth, pcs[:])
|
||||||
|
fn := runtime.FuncForPC(pcs[0])
|
||||||
|
|
||||||
|
if fn == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get func name.
|
||||||
|
name := fn.Name()
|
||||||
|
|
||||||
|
// Drop everything but but function name itself
|
||||||
|
if idx := strings.LastIndexByte(name, '.'); idx >= 0 {
|
||||||
|
name = name[idx+1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
const params = `[...]`
|
||||||
|
|
||||||
|
// Drop any generic type parameter markers
|
||||||
|
if idx := strings.Index(name, params); idx >= 0 {
|
||||||
|
name = name[:idx] + name[idx+len(params):]
|
||||||
|
}
|
||||||
|
|
||||||
|
return name
|
||||||
|
}
|
38
internal/gtserror/new_nocaller.go
Normal file
38
internal/gtserror/new_nocaller.go
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
// 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 noerrcaller
|
||||||
|
|
||||||
|
package gtserror
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Caller returns whether created errors will prepend calling function name.
|
||||||
|
const Caller = false
|
||||||
|
|
||||||
|
// newAt is the same as New() but allows specifying calldepth.
|
||||||
|
func newAt(_ int, msg string) error {
|
||||||
|
return errors.New(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// newfAt is the same as Newf() but allows specifying calldepth.
|
||||||
|
func newfAt(_ int, msgf string, args ...any) error {
|
||||||
|
return fmt.Errorf(msgf, args...)
|
||||||
|
}
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestResponseError(t *testing.T) {
|
func TestResponseError(t *testing.T) {
|
||||||
|
@ -53,13 +54,19 @@ func testResponseError(t *testing.T, rsp http.Response) {
|
||||||
body = string(b[:trunc])
|
body = string(b[:trunc])
|
||||||
}
|
}
|
||||||
expect := fmt.Sprintf(
|
expect := fmt.Sprintf(
|
||||||
"%s request to %s failed: status=\"%s\" body=\"%s\"",
|
"%s%s request to %s failed: status=\"%s\" body=\"%s\"",
|
||||||
|
func() string {
|
||||||
|
if gtserror.Caller {
|
||||||
|
return strings.Split(log.Caller(3), ".")[1] + ": "
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}(),
|
||||||
rsp.Request.Method,
|
rsp.Request.Method,
|
||||||
rsp.Request.URL.String(),
|
rsp.Request.URL.String(),
|
||||||
rsp.Status,
|
rsp.Status,
|
||||||
body,
|
body,
|
||||||
)
|
)
|
||||||
err := gtserror.NewResponseError(&rsp)
|
err := gtserror.NewFromResponse(&rsp)
|
||||||
if str := err.Error(); str != expect {
|
if str := err.Error(); str != expect {
|
||||||
t.Errorf("unexpected error string: recv=%q expct=%q", str, expect)
|
t.Errorf("unexpected error string: recv=%q expct=%q", str, expect)
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,12 +34,7 @@ func Caller(depth int) string {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// return formatted name
|
// Get func name.
|
||||||
return callername(fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
// callername generates a human-readable calling function name.
|
|
||||||
func callername(fn *runtime.Func) string {
|
|
||||||
name := fn.Name()
|
name := fn.Name()
|
||||||
|
|
||||||
// Drop all but the package name and function name, no mod path
|
// Drop all but the package name and function name, no mod path
|
||||||
|
|
|
@ -21,8 +21,10 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"codeberg.org/gruf/go-iotools"
|
||||||
"codeberg.org/gruf/go-runners"
|
"codeberg.org/gruf/go-runners"
|
||||||
"codeberg.org/gruf/go-sched"
|
"codeberg.org/gruf/go-sched"
|
||||||
"codeberg.org/gruf/go-store/v2/storage"
|
"codeberg.org/gruf/go-store/v2/storage"
|
||||||
|
@ -47,112 +49,7 @@ var SupportedEmojiMIMETypes = []string{
|
||||||
mimeImagePng,
|
mimeImagePng,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Manager provides an interface for managing media: parsing, storing, and retrieving media objects like photos, videos, and gifs.
|
type Manager struct {
|
||||||
type Manager interface {
|
|
||||||
/*
|
|
||||||
PROCESSING FUNCTIONS
|
|
||||||
*/
|
|
||||||
|
|
||||||
// PreProcessMedia begins the process of decoding and storing the given data as an attachment.
|
|
||||||
// It will return a pointer to a ProcessingMedia struct upon which further actions can be performed, such as getting
|
|
||||||
// the finished media, thumbnail, attachment, etc.
|
|
||||||
//
|
|
||||||
// data should be a function that the media manager can call to return a reader containing the media data.
|
|
||||||
//
|
|
||||||
// postData will be called after data has been called; it can be used to clean up any remaining resources.
|
|
||||||
// The provided function can be nil, in which case it will not be executed.
|
|
||||||
//
|
|
||||||
// accountID should be the account that the media belongs to.
|
|
||||||
//
|
|
||||||
// ai is optional and can be nil. Any additional information about the attachment provided will be put in the database.
|
|
||||||
//
|
|
||||||
// Note: unlike ProcessMedia, this will NOT queue the media to be asynchronously processed.
|
|
||||||
PreProcessMedia(ctx context.Context, data DataFunc, postData PostDataCallbackFunc, accountID string, ai *AdditionalMediaInfo) (*ProcessingMedia, error)
|
|
||||||
|
|
||||||
// PreProcessMediaRecache refetches, reprocesses, and recaches an existing attachment that has been uncached via pruneRemote.
|
|
||||||
//
|
|
||||||
// Note: unlike ProcessMedia, this will NOT queue the media to be asychronously processed.
|
|
||||||
PreProcessMediaRecache(ctx context.Context, data DataFunc, postData PostDataCallbackFunc, attachmentID string) (*ProcessingMedia, error)
|
|
||||||
|
|
||||||
// ProcessMedia will call PreProcessMedia, followed by queuing the media to be processing in the media worker queue.
|
|
||||||
ProcessMedia(ctx context.Context, data DataFunc, postData PostDataCallbackFunc, accountID string, ai *AdditionalMediaInfo) (*ProcessingMedia, error)
|
|
||||||
|
|
||||||
// PreProcessEmoji begins the process of decoding and storing the given data as an emoji.
|
|
||||||
// It will return a pointer to a ProcessingEmoji struct upon which further actions can be performed, such as getting
|
|
||||||
// the finished media, thumbnail, attachment, etc.
|
|
||||||
//
|
|
||||||
// data should be a function that the media manager can call to return a reader containing the emoji data.
|
|
||||||
//
|
|
||||||
// postData will be called after data has been called; it can be used to clean up any remaining resources.
|
|
||||||
// The provided function can be nil, in which case it will not be executed.
|
|
||||||
//
|
|
||||||
// shortcode should be the emoji shortcode without the ':'s around it.
|
|
||||||
//
|
|
||||||
// id is the database ID that should be used to store the emoji.
|
|
||||||
//
|
|
||||||
// uri is the ActivityPub URI/ID of the emoji.
|
|
||||||
//
|
|
||||||
// ai is optional and can be nil. Any additional information about the emoji provided will be put in the database.
|
|
||||||
//
|
|
||||||
// Note: unlike ProcessEmoji, this will NOT queue the emoji to be asynchronously processed.
|
|
||||||
PreProcessEmoji(ctx context.Context, data DataFunc, postData PostDataCallbackFunc, shortcode string, id string, uri string, ai *AdditionalEmojiInfo, refresh bool) (*ProcessingEmoji, error)
|
|
||||||
|
|
||||||
// ProcessEmoji will call PreProcessEmoji, followed by queuing the emoji to be processing in the emoji worker queue.
|
|
||||||
ProcessEmoji(ctx context.Context, data DataFunc, postData PostDataCallbackFunc, shortcode string, id string, uri string, ai *AdditionalEmojiInfo, refresh bool) (*ProcessingEmoji, error)
|
|
||||||
|
|
||||||
/*
|
|
||||||
PRUNING/UNCACHING FUNCTIONS
|
|
||||||
*/
|
|
||||||
|
|
||||||
// PruneAll runs all of the below pruning/uncacheing functions, and then cleans up any resulting
|
|
||||||
// empty directories from the storage driver. It can be called as a shortcut for calling the below
|
|
||||||
// pruning functions one by one.
|
|
||||||
//
|
|
||||||
// If blocking is true, then any errors encountered during the prune will be combined + returned to
|
|
||||||
// the caller. If blocking is false, the prune is run in the background and errors are just logged
|
|
||||||
// instead.
|
|
||||||
PruneAll(ctx context.Context, mediaCacheRemoteDays int, blocking bool) error
|
|
||||||
// UncacheRemote uncaches all remote media attachments older than the given amount of days.
|
|
||||||
//
|
|
||||||
// In this context, uncacheing means deleting media files from storage and marking the attachment
|
|
||||||
// as cached=false in the database.
|
|
||||||
//
|
|
||||||
// If 'dry' is true, then only a dry run will be performed: nothing will actually be changed.
|
|
||||||
//
|
|
||||||
// The returned int is the amount of media that was/would be uncached by this function.
|
|
||||||
UncacheRemote(ctx context.Context, olderThanDays int, dry bool) (int, error)
|
|
||||||
// PruneUnusedRemote prunes unused/out of date headers and avatars cached on this instance.
|
|
||||||
//
|
|
||||||
// The returned int is the amount of media that was pruned by this function.
|
|
||||||
PruneUnusedRemote(ctx context.Context, dry bool) (int, error)
|
|
||||||
// PruneUnusedLocal prunes unused media attachments that were uploaded by
|
|
||||||
// a user on this instance, but never actually attached to a status, or attached but
|
|
||||||
// later detached.
|
|
||||||
//
|
|
||||||
// The returned int is the amount of media that was pruned by this function.
|
|
||||||
PruneUnusedLocal(ctx context.Context, dry bool) (int, error)
|
|
||||||
// PruneOrphaned prunes files that exist in storage but which do not have a corresponding
|
|
||||||
// entry in the database.
|
|
||||||
//
|
|
||||||
// If dry is true, then nothing will be changed, only the amount that *would* be removed
|
|
||||||
// is returned to the caller.
|
|
||||||
PruneOrphaned(ctx context.Context, dry bool) (int, error)
|
|
||||||
|
|
||||||
/*
|
|
||||||
REFETCHING FUNCTIONS
|
|
||||||
Useful when data loss has occurred.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// RefetchEmojis iterates through remote emojis (for the given domain, or all if domain is empty string).
|
|
||||||
//
|
|
||||||
// For each emoji, the manager will check whether both the full size and static images are present in storage.
|
|
||||||
// If not, the manager will refetch and reprocess full size and static images for the emoji.
|
|
||||||
//
|
|
||||||
// The provided DereferenceMedia function will be used when it's necessary to refetch something this way.
|
|
||||||
RefetchEmojis(ctx context.Context, domain string, dereferenceMedia DereferenceMedia) (int, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
type manager struct {
|
|
||||||
state *state.State
|
state *state.State
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -162,13 +59,24 @@ type manager struct {
|
||||||
// a limited number of media will be processed in parallel. The numbers of workers
|
// a limited number of media will be processed in parallel. The numbers of workers
|
||||||
// is determined from the $GOMAXPROCS environment variable (usually no. CPU cores).
|
// is determined from the $GOMAXPROCS environment variable (usually no. CPU cores).
|
||||||
// See internal/concurrency.NewWorkerPool() documentation for further information.
|
// See internal/concurrency.NewWorkerPool() documentation for further information.
|
||||||
func NewManager(state *state.State) Manager {
|
func NewManager(state *state.State) *Manager {
|
||||||
m := &manager{state: state}
|
m := &Manager{state: state}
|
||||||
scheduleCleanupJobs(m)
|
scheduleCleanupJobs(m)
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *manager) PreProcessMedia(ctx context.Context, data DataFunc, postData PostDataCallbackFunc, accountID string, ai *AdditionalMediaInfo) (*ProcessingMedia, error) {
|
// PreProcessMedia begins the process of decoding and storing the given data as an attachment.
|
||||||
|
// It will return a pointer to a ProcessingMedia struct upon which further actions can be performed, such as getting
|
||||||
|
// the finished media, thumbnail, attachment, etc.
|
||||||
|
//
|
||||||
|
// data should be a function that the media manager can call to return a reader containing the media data.
|
||||||
|
//
|
||||||
|
// accountID should be the account that the media belongs to.
|
||||||
|
//
|
||||||
|
// ai is optional and can be nil. Any additional information about the attachment provided will be put in the database.
|
||||||
|
//
|
||||||
|
// Note: unlike ProcessMedia, this will NOT queue the media to be asynchronously processed.
|
||||||
|
func (m *Manager) PreProcessMedia(ctx context.Context, data DataFunc, accountID string, ai *AdditionalMediaInfo) (*ProcessingMedia, error) {
|
||||||
id, err := id.NewRandomULID()
|
id, err := id.NewRandomULID()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -248,14 +156,16 @@ func (m *manager) PreProcessMedia(ctx context.Context, data DataFunc, postData P
|
||||||
processingMedia := &ProcessingMedia{
|
processingMedia := &ProcessingMedia{
|
||||||
media: attachment,
|
media: attachment,
|
||||||
dataFn: data,
|
dataFn: data,
|
||||||
postFn: postData,
|
|
||||||
mgr: m,
|
mgr: m,
|
||||||
}
|
}
|
||||||
|
|
||||||
return processingMedia, nil
|
return processingMedia, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *manager) PreProcessMediaRecache(ctx context.Context, data DataFunc, postData PostDataCallbackFunc, attachmentID string) (*ProcessingMedia, error) {
|
// PreProcessMediaRecache refetches, reprocesses, and recaches an existing attachment that has been uncached via pruneRemote.
|
||||||
|
//
|
||||||
|
// Note: unlike ProcessMedia, this will NOT queue the media to be asychronously processed.
|
||||||
|
func (m *Manager) PreProcessMediaRecache(ctx context.Context, data DataFunc, attachmentID string) (*ProcessingMedia, error) {
|
||||||
// get the existing attachment from database.
|
// get the existing attachment from database.
|
||||||
attachment, err := m.state.DB.GetAttachmentByID(ctx, attachmentID)
|
attachment, err := m.state.DB.GetAttachmentByID(ctx, attachmentID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -265,7 +175,6 @@ func (m *manager) PreProcessMediaRecache(ctx context.Context, data DataFunc, pos
|
||||||
processingMedia := &ProcessingMedia{
|
processingMedia := &ProcessingMedia{
|
||||||
media: attachment,
|
media: attachment,
|
||||||
dataFn: data,
|
dataFn: data,
|
||||||
postFn: postData,
|
|
||||||
recache: true, // indicate it's a recache
|
recache: true, // indicate it's a recache
|
||||||
mgr: m,
|
mgr: m,
|
||||||
}
|
}
|
||||||
|
@ -273,9 +182,10 @@ func (m *manager) PreProcessMediaRecache(ctx context.Context, data DataFunc, pos
|
||||||
return processingMedia, nil
|
return processingMedia, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *manager) ProcessMedia(ctx context.Context, data DataFunc, postData PostDataCallbackFunc, accountID string, ai *AdditionalMediaInfo) (*ProcessingMedia, error) {
|
// ProcessMedia will call PreProcessMedia, followed by queuing the media to be processing in the media worker queue.
|
||||||
|
func (m *Manager) ProcessMedia(ctx context.Context, data DataFunc, accountID string, ai *AdditionalMediaInfo) (*ProcessingMedia, error) {
|
||||||
// Create a new processing media object for this media request.
|
// Create a new processing media object for this media request.
|
||||||
media, err := m.PreProcessMedia(ctx, data, postData, accountID, ai)
|
media, err := m.PreProcessMedia(ctx, data, accountID, ai)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -286,7 +196,22 @@ func (m *manager) ProcessMedia(ctx context.Context, data DataFunc, postData Post
|
||||||
return media, nil
|
return media, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *manager) PreProcessEmoji(ctx context.Context, data DataFunc, postData PostDataCallbackFunc, shortcode string, emojiID string, uri string, ai *AdditionalEmojiInfo, refresh bool) (*ProcessingEmoji, error) {
|
// PreProcessEmoji begins the process of decoding and storing the given data as an emoji.
|
||||||
|
// It will return a pointer to a ProcessingEmoji struct upon which further actions can be performed, such as getting
|
||||||
|
// the finished media, thumbnail, attachment, etc.
|
||||||
|
//
|
||||||
|
// data should be a function that the media manager can call to return a reader containing the emoji data.
|
||||||
|
//
|
||||||
|
// shortcode should be the emoji shortcode without the ':'s around it.
|
||||||
|
//
|
||||||
|
// id is the database ID that should be used to store the emoji.
|
||||||
|
//
|
||||||
|
// uri is the ActivityPub URI/ID of the emoji.
|
||||||
|
//
|
||||||
|
// ai is optional and can be nil. Any additional information about the emoji provided will be put in the database.
|
||||||
|
//
|
||||||
|
// Note: unlike ProcessEmoji, this will NOT queue the emoji to be asynchronously processed.
|
||||||
|
func (m *Manager) PreProcessEmoji(ctx context.Context, data DataFunc, shortcode string, emojiID string, uri string, ai *AdditionalEmojiInfo, refresh bool) (*ProcessingEmoji, error) {
|
||||||
instanceAccount, err := m.state.DB.GetInstanceAccount(ctx, "")
|
instanceAccount, err := m.state.DB.GetInstanceAccount(ctx, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("preProcessEmoji: error fetching this instance account from the db: %s", err)
|
return nil, fmt.Errorf("preProcessEmoji: error fetching this instance account from the db: %s", err)
|
||||||
|
@ -299,36 +224,38 @@ func (m *manager) PreProcessEmoji(ctx context.Context, data DataFunc, postData P
|
||||||
)
|
)
|
||||||
|
|
||||||
if refresh {
|
if refresh {
|
||||||
|
// Look for existing emoji by given ID.
|
||||||
emoji, err = m.state.DB.GetEmojiByID(ctx, emojiID)
|
emoji, err = m.state.DB.GetEmojiByID(ctx, emojiID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("preProcessEmoji: error fetching emoji to refresh from the db: %s", err)
|
return nil, fmt.Errorf("preProcessEmoji: error fetching emoji to refresh from the db: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// if this is a refresh, we will end up with new images
|
// if this is a refresh, we will end up with new images
|
||||||
// stored for this emoji, so we can use the postData function
|
// stored for this emoji, so we can use an io.Closer callback
|
||||||
// to perform clean up of the old images from storage
|
// to perform clean up of the old images from storage
|
||||||
originalPostData := postData
|
originalData := data
|
||||||
originalImagePath := emoji.ImagePath
|
originalImagePath := emoji.ImagePath
|
||||||
originalImageStaticPath := emoji.ImageStaticPath
|
originalImageStaticPath := emoji.ImageStaticPath
|
||||||
postData = func(innerCtx context.Context) error {
|
data = func(ctx context.Context) (io.ReadCloser, int64, error) {
|
||||||
// trigger the original postData function if it was provided
|
// Call original data func.
|
||||||
if originalPostData != nil {
|
rc, sz, err := originalData(ctx)
|
||||||
if err := originalPostData(innerCtx); err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, 0, err
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
l := log.WithContext(ctx).
|
// Wrap closer to cleanup old data.
|
||||||
WithField("shortcode@domain", emoji.Shortcode+"@"+emoji.Domain)
|
c := iotools.CloserCallback(rc, func() {
|
||||||
l.Debug("postData: cleaning up old emoji files for refreshed emoji")
|
if err := m.state.Storage.Delete(ctx, originalImagePath); err != nil && !errors.Is(err, storage.ErrNotFound) {
|
||||||
if err := m.state.Storage.Delete(innerCtx, originalImagePath); err != nil && !errors.Is(err, storage.ErrNotFound) {
|
log.Errorf(ctx, "error removing old emoji %s@%s from storage: %v", emoji.Shortcode, emoji.Domain, err)
|
||||||
l.Errorf("postData: error cleaning up old emoji image at %s for refreshed emoji: %s", originalImagePath, err)
|
|
||||||
}
|
|
||||||
if err := m.state.Storage.Delete(innerCtx, originalImageStaticPath); err != nil && !errors.Is(err, storage.ErrNotFound) {
|
|
||||||
l.Errorf("postData: error cleaning up old emoji static image at %s for refreshed emoji: %s", originalImageStaticPath, err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
if err := m.state.Storage.Delete(ctx, originalImageStaticPath); err != nil && !errors.Is(err, storage.ErrNotFound) {
|
||||||
|
log.Errorf(ctx, "error removing old static emoji %s@%s from storage: %v", emoji.Shortcode, emoji.Domain, err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Return newly wrapped readcloser and size.
|
||||||
|
return iotools.ReadCloser(rc, c), sz, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
newPathID, err = id.NewRandomULID()
|
newPathID, err = id.NewRandomULID()
|
||||||
|
@ -410,16 +337,16 @@ func (m *manager) PreProcessEmoji(ctx context.Context, data DataFunc, postData P
|
||||||
refresh: refresh,
|
refresh: refresh,
|
||||||
newPathID: newPathID,
|
newPathID: newPathID,
|
||||||
dataFn: data,
|
dataFn: data,
|
||||||
postFn: postData,
|
|
||||||
mgr: m,
|
mgr: m,
|
||||||
}
|
}
|
||||||
|
|
||||||
return processingEmoji, nil
|
return processingEmoji, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *manager) ProcessEmoji(ctx context.Context, data DataFunc, postData PostDataCallbackFunc, shortcode string, id string, uri string, ai *AdditionalEmojiInfo, refresh bool) (*ProcessingEmoji, error) {
|
// ProcessEmoji will call PreProcessEmoji, followed by queuing the emoji to be processing in the emoji worker queue.
|
||||||
|
func (m *Manager) ProcessEmoji(ctx context.Context, data DataFunc, shortcode string, id string, uri string, ai *AdditionalEmojiInfo, refresh bool) (*ProcessingEmoji, error) {
|
||||||
// Create a new processing emoji object for this emoji request.
|
// Create a new processing emoji object for this emoji request.
|
||||||
emoji, err := m.PreProcessEmoji(ctx, data, postData, shortcode, id, uri, ai, refresh)
|
emoji, err := m.PreProcessEmoji(ctx, data, shortcode, id, uri, ai, refresh)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -430,7 +357,7 @@ func (m *manager) ProcessEmoji(ctx context.Context, data DataFunc, postData Post
|
||||||
return emoji, nil
|
return emoji, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func scheduleCleanupJobs(m *manager) {
|
func scheduleCleanupJobs(m *Manager) {
|
||||||
const day = time.Hour * 24
|
const day = time.Hour * 24
|
||||||
|
|
||||||
// Calculate closest midnight.
|
// Calculate closest midnight.
|
||||||
|
|
|
@ -55,7 +55,7 @@ func (suite *ManagerTestSuite) TestEmojiProcessBlocking() {
|
||||||
emojiID := "01GDQ9G782X42BAMFASKP64343"
|
emojiID := "01GDQ9G782X42BAMFASKP64343"
|
||||||
emojiURI := "http://localhost:8080/emoji/01GDQ9G782X42BAMFASKP64343"
|
emojiURI := "http://localhost:8080/emoji/01GDQ9G782X42BAMFASKP64343"
|
||||||
|
|
||||||
processingEmoji, err := suite.manager.ProcessEmoji(ctx, data, nil, "rainbow_test", emojiID, emojiURI, nil, false)
|
processingEmoji, err := suite.manager.ProcessEmoji(ctx, data, "rainbow_test", emojiID, emojiURI, nil, false)
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
|
|
||||||
// do a blocking call to fetch the emoji
|
// do a blocking call to fetch the emoji
|
||||||
|
@ -125,7 +125,7 @@ func (suite *ManagerTestSuite) TestEmojiProcessBlockingRefresh() {
|
||||||
emojiID := emojiToUpdate.ID
|
emojiID := emojiToUpdate.ID
|
||||||
emojiURI := emojiToUpdate.URI
|
emojiURI := emojiToUpdate.URI
|
||||||
|
|
||||||
processingEmoji, err := suite.manager.ProcessEmoji(ctx, data, nil, "yell", emojiID, emojiURI, &media.AdditionalEmojiInfo{
|
processingEmoji, err := suite.manager.ProcessEmoji(ctx, data, "yell", emojiID, emojiURI, &media.AdditionalEmojiInfo{
|
||||||
CreatedAt: &emojiToUpdate.CreatedAt,
|
CreatedAt: &emojiToUpdate.CreatedAt,
|
||||||
Domain: &emojiToUpdate.Domain,
|
Domain: &emojiToUpdate.Domain,
|
||||||
ImageRemoteURL: &newImageRemoteURL,
|
ImageRemoteURL: &newImageRemoteURL,
|
||||||
|
@ -209,7 +209,7 @@ func (suite *ManagerTestSuite) TestEmojiProcessBlockingTooLarge() {
|
||||||
emojiID := "01GDQ9G782X42BAMFASKP64343"
|
emojiID := "01GDQ9G782X42BAMFASKP64343"
|
||||||
emojiURI := "http://localhost:8080/emoji/01GDQ9G782X42BAMFASKP64343"
|
emojiURI := "http://localhost:8080/emoji/01GDQ9G782X42BAMFASKP64343"
|
||||||
|
|
||||||
processingEmoji, err := suite.manager.ProcessEmoji(ctx, data, nil, "big_panda", emojiID, emojiURI, nil, false)
|
processingEmoji, err := suite.manager.ProcessEmoji(ctx, data, "big_panda", emojiID, emojiURI, nil, false)
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
|
|
||||||
// do a blocking call to fetch the emoji
|
// do a blocking call to fetch the emoji
|
||||||
|
@ -233,7 +233,7 @@ func (suite *ManagerTestSuite) TestEmojiProcessBlockingTooLargeNoSizeGiven() {
|
||||||
emojiID := "01GDQ9G782X42BAMFASKP64343"
|
emojiID := "01GDQ9G782X42BAMFASKP64343"
|
||||||
emojiURI := "http://localhost:8080/emoji/01GDQ9G782X42BAMFASKP64343"
|
emojiURI := "http://localhost:8080/emoji/01GDQ9G782X42BAMFASKP64343"
|
||||||
|
|
||||||
processingEmoji, err := suite.manager.ProcessEmoji(ctx, data, nil, "big_panda", emojiID, emojiURI, nil, false)
|
processingEmoji, err := suite.manager.ProcessEmoji(ctx, data, "big_panda", emojiID, emojiURI, nil, false)
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
|
|
||||||
// do a blocking call to fetch the emoji
|
// do a blocking call to fetch the emoji
|
||||||
|
@ -258,7 +258,7 @@ func (suite *ManagerTestSuite) TestEmojiProcessBlockingNoFileSizeGiven() {
|
||||||
emojiURI := "http://localhost:8080/emoji/01GDQ9G782X42BAMFASKP64343"
|
emojiURI := "http://localhost:8080/emoji/01GDQ9G782X42BAMFASKP64343"
|
||||||
|
|
||||||
// process the media with no additional info provided
|
// process the media with no additional info provided
|
||||||
processingEmoji, err := suite.manager.ProcessEmoji(ctx, data, nil, "rainbow_test", emojiID, emojiURI, nil, false)
|
processingEmoji, err := suite.manager.ProcessEmoji(ctx, data, "rainbow_test", emojiID, emojiURI, nil, false)
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
|
|
||||||
// do a blocking call to fetch the emoji
|
// do a blocking call to fetch the emoji
|
||||||
|
@ -319,7 +319,7 @@ func (suite *ManagerTestSuite) TestSimpleJpegProcessBlocking() {
|
||||||
accountID := "01FS1X72SK9ZPW0J1QQ68BD264"
|
accountID := "01FS1X72SK9ZPW0J1QQ68BD264"
|
||||||
|
|
||||||
// process the media with no additional info provided
|
// process the media with no additional info provided
|
||||||
processingMedia, err := suite.manager.ProcessMedia(ctx, data, nil, accountID, nil)
|
processingMedia, err := suite.manager.ProcessMedia(ctx, data, accountID, nil)
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
// fetch the attachment id from the processing media
|
// fetch the attachment id from the processing media
|
||||||
attachmentID := processingMedia.AttachmentID()
|
attachmentID := processingMedia.AttachmentID()
|
||||||
|
@ -391,7 +391,7 @@ func (suite *ManagerTestSuite) TestSlothVineProcessBlocking() {
|
||||||
accountID := "01FS1X72SK9ZPW0J1QQ68BD264"
|
accountID := "01FS1X72SK9ZPW0J1QQ68BD264"
|
||||||
|
|
||||||
// process the media with no additional info provided
|
// process the media with no additional info provided
|
||||||
processingMedia, err := suite.manager.ProcessMedia(ctx, data, nil, accountID, nil)
|
processingMedia, err := suite.manager.ProcessMedia(ctx, data, accountID, nil)
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
// fetch the attachment id from the processing media
|
// fetch the attachment id from the processing media
|
||||||
attachmentID := processingMedia.AttachmentID()
|
attachmentID := processingMedia.AttachmentID()
|
||||||
|
@ -467,7 +467,7 @@ func (suite *ManagerTestSuite) TestLongerMp4ProcessBlocking() {
|
||||||
accountID := "01FS1X72SK9ZPW0J1QQ68BD264"
|
accountID := "01FS1X72SK9ZPW0J1QQ68BD264"
|
||||||
|
|
||||||
// process the media with no additional info provided
|
// process the media with no additional info provided
|
||||||
processingMedia, err := suite.manager.ProcessMedia(ctx, data, nil, accountID, nil)
|
processingMedia, err := suite.manager.ProcessMedia(ctx, data, accountID, nil)
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
// fetch the attachment id from the processing media
|
// fetch the attachment id from the processing media
|
||||||
attachmentID := processingMedia.AttachmentID()
|
attachmentID := processingMedia.AttachmentID()
|
||||||
|
@ -543,7 +543,7 @@ func (suite *ManagerTestSuite) TestBirdnestMp4ProcessBlocking() {
|
||||||
accountID := "01FS1X72SK9ZPW0J1QQ68BD264"
|
accountID := "01FS1X72SK9ZPW0J1QQ68BD264"
|
||||||
|
|
||||||
// process the media with no additional info provided
|
// process the media with no additional info provided
|
||||||
processingMedia, err := suite.manager.ProcessMedia(ctx, data, nil, accountID, nil)
|
processingMedia, err := suite.manager.ProcessMedia(ctx, data, accountID, nil)
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
// fetch the attachment id from the processing media
|
// fetch the attachment id from the processing media
|
||||||
attachmentID := processingMedia.AttachmentID()
|
attachmentID := processingMedia.AttachmentID()
|
||||||
|
@ -621,7 +621,7 @@ func (suite *ManagerTestSuite) TestNotAnMp4ProcessBlocking() {
|
||||||
accountID := "01FS1X72SK9ZPW0J1QQ68BD264"
|
accountID := "01FS1X72SK9ZPW0J1QQ68BD264"
|
||||||
|
|
||||||
// pre processing should go fine but...
|
// pre processing should go fine but...
|
||||||
processingMedia, err := suite.manager.ProcessMedia(ctx, data, nil, accountID, nil)
|
processingMedia, err := suite.manager.ProcessMedia(ctx, data, accountID, nil)
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
|
|
||||||
// we should get an error while loading
|
// we should get an error while loading
|
||||||
|
@ -646,7 +646,7 @@ func (suite *ManagerTestSuite) TestSimpleJpegProcessBlockingNoContentLengthGiven
|
||||||
accountID := "01FS1X72SK9ZPW0J1QQ68BD264"
|
accountID := "01FS1X72SK9ZPW0J1QQ68BD264"
|
||||||
|
|
||||||
// process the media with no additional info provided
|
// process the media with no additional info provided
|
||||||
processingMedia, err := suite.manager.ProcessMedia(ctx, data, nil, accountID, nil)
|
processingMedia, err := suite.manager.ProcessMedia(ctx, data, accountID, nil)
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
// fetch the attachment id from the processing media
|
// fetch the attachment id from the processing media
|
||||||
attachmentID := processingMedia.AttachmentID()
|
attachmentID := processingMedia.AttachmentID()
|
||||||
|
@ -719,7 +719,7 @@ func (suite *ManagerTestSuite) TestSimpleJpegProcessBlockingReadCloser() {
|
||||||
accountID := "01FS1X72SK9ZPW0J1QQ68BD264"
|
accountID := "01FS1X72SK9ZPW0J1QQ68BD264"
|
||||||
|
|
||||||
// process the media with no additional info provided
|
// process the media with no additional info provided
|
||||||
processingMedia, err := suite.manager.ProcessMedia(ctx, data, nil, accountID, nil)
|
processingMedia, err := suite.manager.ProcessMedia(ctx, data, accountID, nil)
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
// fetch the attachment id from the processing media
|
// fetch the attachment id from the processing media
|
||||||
attachmentID := processingMedia.AttachmentID()
|
attachmentID := processingMedia.AttachmentID()
|
||||||
|
@ -791,7 +791,7 @@ func (suite *ManagerTestSuite) TestPngNoAlphaChannelProcessBlocking() {
|
||||||
accountID := "01FS1X72SK9ZPW0J1QQ68BD264"
|
accountID := "01FS1X72SK9ZPW0J1QQ68BD264"
|
||||||
|
|
||||||
// process the media with no additional info provided
|
// process the media with no additional info provided
|
||||||
processingMedia, err := suite.manager.ProcessMedia(ctx, data, nil, accountID, nil)
|
processingMedia, err := suite.manager.ProcessMedia(ctx, data, accountID, nil)
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
// fetch the attachment id from the processing media
|
// fetch the attachment id from the processing media
|
||||||
attachmentID := processingMedia.AttachmentID()
|
attachmentID := processingMedia.AttachmentID()
|
||||||
|
@ -863,7 +863,7 @@ func (suite *ManagerTestSuite) TestPngAlphaChannelProcessBlocking() {
|
||||||
accountID := "01FS1X72SK9ZPW0J1QQ68BD264"
|
accountID := "01FS1X72SK9ZPW0J1QQ68BD264"
|
||||||
|
|
||||||
// process the media with no additional info provided
|
// process the media with no additional info provided
|
||||||
processingMedia, err := suite.manager.ProcessMedia(ctx, data, nil, accountID, nil)
|
processingMedia, err := suite.manager.ProcessMedia(ctx, data, accountID, nil)
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
// fetch the attachment id from the processing media
|
// fetch the attachment id from the processing media
|
||||||
attachmentID := processingMedia.AttachmentID()
|
attachmentID := processingMedia.AttachmentID()
|
||||||
|
@ -932,18 +932,10 @@ func (suite *ManagerTestSuite) TestSimpleJpegProcessBlockingWithCallback() {
|
||||||
return io.NopCloser(bytes.NewBuffer(b)), int64(len(b)), nil
|
return io.NopCloser(bytes.NewBuffer(b)), int64(len(b)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// test the callback function by setting a simple boolean
|
|
||||||
var calledPostData bool
|
|
||||||
postData := func(_ context.Context) error {
|
|
||||||
calledPostData = true
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
suite.False(calledPostData) // not called yet (obvs)
|
|
||||||
|
|
||||||
accountID := "01FS1X72SK9ZPW0J1QQ68BD264"
|
accountID := "01FS1X72SK9ZPW0J1QQ68BD264"
|
||||||
|
|
||||||
// process the media with no additional info provided
|
// process the media with no additional info provided
|
||||||
processingMedia, err := suite.manager.ProcessMedia(ctx, data, postData, accountID, nil)
|
processingMedia, err := suite.manager.ProcessMedia(ctx, data, accountID, nil)
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
// fetch the attachment id from the processing media
|
// fetch the attachment id from the processing media
|
||||||
attachmentID := processingMedia.AttachmentID()
|
attachmentID := processingMedia.AttachmentID()
|
||||||
|
@ -953,9 +945,6 @@ func (suite *ManagerTestSuite) TestSimpleJpegProcessBlockingWithCallback() {
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
suite.NotNil(attachment)
|
suite.NotNil(attachment)
|
||||||
|
|
||||||
// the post data callback should have been called
|
|
||||||
suite.True(calledPostData)
|
|
||||||
|
|
||||||
// make sure it's got the stuff set on it that we expect
|
// make sure it's got the stuff set on it that we expect
|
||||||
// the attachment ID and accountID we expect
|
// the attachment ID and accountID we expect
|
||||||
suite.Equal(attachmentID, attachment.ID)
|
suite.Equal(attachmentID, attachment.ID)
|
||||||
|
@ -1019,7 +1008,7 @@ func (suite *ManagerTestSuite) TestSimpleJpegProcessAsync() {
|
||||||
accountID := "01FS1X72SK9ZPW0J1QQ68BD264"
|
accountID := "01FS1X72SK9ZPW0J1QQ68BD264"
|
||||||
|
|
||||||
// process the media with no additional info provided
|
// process the media with no additional info provided
|
||||||
processingMedia, err := suite.manager.ProcessMedia(ctx, data, nil, accountID, nil)
|
processingMedia, err := suite.manager.ProcessMedia(ctx, data, accountID, nil)
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
|
|
||||||
// fetch the attachment id from the processing media
|
// fetch the attachment id from the processing media
|
||||||
|
@ -1101,7 +1090,7 @@ func (suite *ManagerTestSuite) TestSimpleJpegQueueSpamming() {
|
||||||
inProcess := []*media.ProcessingMedia{}
|
inProcess := []*media.ProcessingMedia{}
|
||||||
for i := 0; i < spam; i++ {
|
for i := 0; i < spam; i++ {
|
||||||
// process the media with no additional info provided
|
// process the media with no additional info provided
|
||||||
processingMedia, err := suite.manager.ProcessMedia(ctx, data, nil, accountID, nil)
|
processingMedia, err := suite.manager.ProcessMedia(ctx, data, accountID, nil)
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
inProcess = append(inProcess, processingMedia)
|
inProcess = append(inProcess, processingMedia)
|
||||||
}
|
}
|
||||||
|
@ -1202,7 +1191,7 @@ func (suite *ManagerTestSuite) TestSimpleJpegProcessBlockingWithDiskStorage() {
|
||||||
suite.manager = diskManager
|
suite.manager = diskManager
|
||||||
|
|
||||||
// process the media with no additional info provided
|
// process the media with no additional info provided
|
||||||
processingMedia, err := diskManager.ProcessMedia(ctx, data, nil, accountID, nil)
|
processingMedia, err := diskManager.ProcessMedia(ctx, data, accountID, nil)
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
// fetch the attachment id from the processing media
|
// fetch the attachment id from the processing media
|
||||||
attachmentID := processingMedia.AttachmentID()
|
attachmentID := processingMedia.AttachmentID()
|
||||||
|
|
|
@ -35,7 +35,7 @@ type MediaStandardTestSuite struct {
|
||||||
db db.DB
|
db db.DB
|
||||||
storage *storage.Driver
|
storage *storage.Driver
|
||||||
state state.State
|
state state.State
|
||||||
manager media.Manager
|
manager *media.Manager
|
||||||
transportController transport.Controller
|
transportController transport.Controller
|
||||||
testAttachments map[string]*gtsmodel.MediaAttachment
|
testAttachments map[string]*gtsmodel.MediaAttachment
|
||||||
testAccounts map[string]*gtsmodel.Account
|
testAccounts map[string]*gtsmodel.Account
|
||||||
|
|
|
@ -41,11 +41,10 @@ type ProcessingEmoji struct {
|
||||||
refresh bool // whether this is an existing emoji being refreshed
|
refresh bool // whether this is an existing emoji being refreshed
|
||||||
newPathID string // new emoji path ID to use if refreshed
|
newPathID string // new emoji path ID to use if refreshed
|
||||||
dataFn DataFunc // load-data function, returns media stream
|
dataFn DataFunc // load-data function, returns media stream
|
||||||
postFn PostDataCallbackFunc // post data callback function
|
|
||||||
done bool // done is set when process finishes with non ctx canceled type error
|
done bool // done is set when process finishes with non ctx canceled type error
|
||||||
proc runners.Processor // proc helps synchronize only a singular running processing instance
|
proc runners.Processor // proc helps synchronize only a singular running processing instance
|
||||||
err error // error stores permanent error value when done
|
err error // error stores permanent error value when done
|
||||||
mgr *manager // mgr instance (access to db / storage)
|
mgr *Manager // mgr instance (access to db / storage)
|
||||||
}
|
}
|
||||||
|
|
||||||
// EmojiID returns the ID of the underlying emoji without blocking processing.
|
// EmojiID returns the ID of the underlying emoji without blocking processing.
|
||||||
|
@ -158,17 +157,6 @@ func (p *ProcessingEmoji) load(ctx context.Context) (*gtsmodel.Emoji, bool, erro
|
||||||
// and updates the underlying attachment fields as necessary. It will then stream
|
// and updates the underlying attachment fields as necessary. It will then stream
|
||||||
// bytes from p's reader directly into storage so that it can be retrieved later.
|
// bytes from p's reader directly into storage so that it can be retrieved later.
|
||||||
func (p *ProcessingEmoji) store(ctx context.Context) error {
|
func (p *ProcessingEmoji) store(ctx context.Context) error {
|
||||||
defer func() {
|
|
||||||
if p.postFn == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure post callback gets called.
|
|
||||||
if err := p.postFn(ctx); err != nil {
|
|
||||||
log.Errorf(ctx, "error executing postdata function: %v", err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Load media from provided data fn.
|
// Load media from provided data fn.
|
||||||
rc, sz, err := p.dataFn(ctx)
|
rc, sz, err := p.dataFn(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -40,12 +40,11 @@ import (
|
||||||
type ProcessingMedia struct {
|
type ProcessingMedia struct {
|
||||||
media *gtsmodel.MediaAttachment // processing media attachment details
|
media *gtsmodel.MediaAttachment // processing media attachment details
|
||||||
dataFn DataFunc // load-data function, returns media stream
|
dataFn DataFunc // load-data function, returns media stream
|
||||||
postFn PostDataCallbackFunc // post data callback function
|
|
||||||
recache bool // recaching existing (uncached) media
|
recache bool // recaching existing (uncached) media
|
||||||
done bool // done is set when process finishes with non ctx canceled type error
|
done bool // done is set when process finishes with non ctx canceled type error
|
||||||
proc runners.Processor // proc helps synchronize only a singular running processing instance
|
proc runners.Processor // proc helps synchronize only a singular running processing instance
|
||||||
err error // error stores permanent error value when done
|
err error // error stores permanent error value when done
|
||||||
mgr *manager // mgr instance (access to db / storage)
|
mgr *Manager // mgr instance (access to db / storage)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AttachmentID returns the ID of the underlying media attachment without blocking processing.
|
// AttachmentID returns the ID of the underlying media attachment without blocking processing.
|
||||||
|
@ -143,17 +142,6 @@ func (p *ProcessingMedia) load(ctx context.Context) (*gtsmodel.MediaAttachment,
|
||||||
// and updates the underlying attachment fields as necessary. It will then stream
|
// and updates the underlying attachment fields as necessary. It will then stream
|
||||||
// bytes from p's reader directly into storage so that it can be retrieved later.
|
// bytes from p's reader directly into storage so that it can be retrieved later.
|
||||||
func (p *ProcessingMedia) store(ctx context.Context) error {
|
func (p *ProcessingMedia) store(ctx context.Context) error {
|
||||||
defer func() {
|
|
||||||
if p.postFn == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// ensure post callback gets called.
|
|
||||||
if err := p.postFn(ctx); err != nil {
|
|
||||||
log.Errorf(ctx, "error executing postdata function: %v", err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
// Load media from provided data fun
|
// Load media from provided data fun
|
||||||
rc, sz, err := p.dataFn(ctx)
|
rc, sz, err := p.dataFn(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -37,7 +37,14 @@ const (
|
||||||
unusedLocalAttachmentDays = 3 // Number of days to keep local media in storage if not attached to a status.
|
unusedLocalAttachmentDays = 3 // Number of days to keep local media in storage if not attached to a status.
|
||||||
)
|
)
|
||||||
|
|
||||||
func (m *manager) PruneAll(ctx context.Context, mediaCacheRemoteDays int, blocking bool) error {
|
// PruneAll runs all of the below pruning/uncacheing functions, and then cleans up any resulting
|
||||||
|
// empty directories from the storage driver. It can be called as a shortcut for calling the below
|
||||||
|
// pruning functions one by one.
|
||||||
|
//
|
||||||
|
// If blocking is true, then any errors encountered during the prune will be combined + returned to
|
||||||
|
// the caller. If blocking is false, the prune is run in the background and errors are just logged
|
||||||
|
// instead.
|
||||||
|
func (m *Manager) PruneAll(ctx context.Context, mediaCacheRemoteDays int, blocking bool) error {
|
||||||
const dry = false
|
const dry = false
|
||||||
|
|
||||||
f := func(innerCtx context.Context) error {
|
f := func(innerCtx context.Context) error {
|
||||||
|
@ -93,7 +100,10 @@ func (m *manager) PruneAll(ctx context.Context, mediaCacheRemoteDays int, blocki
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *manager) PruneUnusedRemote(ctx context.Context, dry bool) (int, error) {
|
// PruneUnusedRemote prunes unused/out of date headers and avatars cached on this instance.
|
||||||
|
//
|
||||||
|
// The returned int is the amount of media that was pruned by this function.
|
||||||
|
func (m *Manager) PruneUnusedRemote(ctx context.Context, dry bool) (int, error) {
|
||||||
var (
|
var (
|
||||||
totalPruned int
|
totalPruned int
|
||||||
maxID string
|
maxID string
|
||||||
|
@ -152,7 +162,12 @@ func (m *manager) PruneUnusedRemote(ctx context.Context, dry bool) (int, error)
|
||||||
return totalPruned, nil
|
return totalPruned, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *manager) PruneOrphaned(ctx context.Context, dry bool) (int, error) {
|
// PruneOrphaned prunes files that exist in storage but which do not have a corresponding
|
||||||
|
// entry in the database.
|
||||||
|
//
|
||||||
|
// If dry is true, then nothing will be changed, only the amount that *would* be removed
|
||||||
|
// is returned to the caller.
|
||||||
|
func (m *Manager) PruneOrphaned(ctx context.Context, dry bool) (int, error) {
|
||||||
// Emojis are stored under the instance account, so we
|
// Emojis are stored under the instance account, so we
|
||||||
// need the ID of the instance account for the next part.
|
// need the ID of the instance account for the next part.
|
||||||
instanceAccount, err := m.state.DB.GetInstanceAccount(ctx, "")
|
instanceAccount, err := m.state.DB.GetInstanceAccount(ctx, "")
|
||||||
|
@ -200,7 +215,7 @@ func (m *manager) PruneOrphaned(ctx context.Context, dry bool) (int, error) {
|
||||||
return m.removeFiles(ctx, orphanedKeys...)
|
return m.removeFiles(ctx, orphanedKeys...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *manager) orphaned(ctx context.Context, key string, instanceAccountID string) (bool, error) {
|
func (m *Manager) orphaned(ctx context.Context, key string, instanceAccountID string) (bool, error) {
|
||||||
pathParts := regexes.FilePath.FindStringSubmatch(key)
|
pathParts := regexes.FilePath.FindStringSubmatch(key)
|
||||||
if len(pathParts) != 6 {
|
if len(pathParts) != 6 {
|
||||||
// This doesn't match our expectations so
|
// This doesn't match our expectations so
|
||||||
|
@ -239,7 +254,15 @@ func (m *manager) orphaned(ctx context.Context, key string, instanceAccountID st
|
||||||
return orphaned, nil
|
return orphaned, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *manager) UncacheRemote(ctx context.Context, olderThanDays int, dry bool) (int, error) {
|
// UncacheRemote uncaches all remote media attachments older than the given amount of days.
|
||||||
|
//
|
||||||
|
// In this context, uncacheing means deleting media files from storage and marking the attachment
|
||||||
|
// as cached=false in the database.
|
||||||
|
//
|
||||||
|
// If 'dry' is true, then only a dry run will be performed: nothing will actually be changed.
|
||||||
|
//
|
||||||
|
// The returned int is the amount of media that was/would be uncached by this function.
|
||||||
|
func (m *Manager) UncacheRemote(ctx context.Context, olderThanDays int, dry bool) (int, error) {
|
||||||
if olderThanDays < 0 {
|
if olderThanDays < 0 {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
@ -276,7 +299,12 @@ func (m *manager) UncacheRemote(ctx context.Context, olderThanDays int, dry bool
|
||||||
return totalPruned, nil
|
return totalPruned, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *manager) PruneUnusedLocal(ctx context.Context, dry bool) (int, error) {
|
// PruneUnusedLocal prunes unused media attachments that were uploaded by
|
||||||
|
// a user on this instance, but never actually attached to a status, or attached but
|
||||||
|
// later detached.
|
||||||
|
//
|
||||||
|
// The returned int is the amount of media that was pruned by this function.
|
||||||
|
func (m *Manager) PruneUnusedLocal(ctx context.Context, dry bool) (int, error) {
|
||||||
olderThan := time.Now().Add(-time.Hour * 24 * time.Duration(unusedLocalAttachmentDays))
|
olderThan := time.Now().Add(-time.Hour * 24 * time.Duration(unusedLocalAttachmentDays))
|
||||||
|
|
||||||
if dry {
|
if dry {
|
||||||
|
@ -313,7 +341,7 @@ func (m *manager) PruneUnusedLocal(ctx context.Context, dry bool) (int, error) {
|
||||||
Handy little helpers
|
Handy little helpers
|
||||||
*/
|
*/
|
||||||
|
|
||||||
func (m *manager) deleteAttachment(ctx context.Context, attachment *gtsmodel.MediaAttachment) error {
|
func (m *Manager) deleteAttachment(ctx context.Context, attachment *gtsmodel.MediaAttachment) error {
|
||||||
if _, err := m.removeFiles(ctx, attachment.File.Path, attachment.Thumbnail.Path); err != nil {
|
if _, err := m.removeFiles(ctx, attachment.File.Path, attachment.Thumbnail.Path); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -322,7 +350,7 @@ func (m *manager) deleteAttachment(ctx context.Context, attachment *gtsmodel.Med
|
||||||
return m.state.DB.DeleteAttachment(ctx, attachment.ID)
|
return m.state.DB.DeleteAttachment(ctx, attachment.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *manager) uncacheAttachment(ctx context.Context, attachment *gtsmodel.MediaAttachment) error {
|
func (m *Manager) uncacheAttachment(ctx context.Context, attachment *gtsmodel.MediaAttachment) error {
|
||||||
if _, err := m.removeFiles(ctx, attachment.File.Path, attachment.Thumbnail.Path); err != nil {
|
if _, err := m.removeFiles(ctx, attachment.File.Path, attachment.Thumbnail.Path); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -332,7 +360,7 @@ func (m *manager) uncacheAttachment(ctx context.Context, attachment *gtsmodel.Me
|
||||||
return m.state.DB.UpdateAttachment(ctx, attachment, "cached")
|
return m.state.DB.UpdateAttachment(ctx, attachment, "cached")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *manager) removeFiles(ctx context.Context, keys ...string) (int, error) {
|
func (m *Manager) removeFiles(ctx context.Context, keys ...string) (int, error) {
|
||||||
errs := make(gtserror.MultiError, 0, len(keys))
|
errs := make(gtserror.MultiError, 0, len(keys))
|
||||||
|
|
||||||
for _, key := range keys {
|
for _, key := range keys {
|
||||||
|
|
|
@ -312,7 +312,7 @@ func (suite *PruneTestSuite) TestUncacheAndRecache() {
|
||||||
testStatusAttachment,
|
testStatusAttachment,
|
||||||
testHeader,
|
testHeader,
|
||||||
} {
|
} {
|
||||||
processingRecache, err := suite.manager.PreProcessMediaRecache(ctx, data, nil, original.ID)
|
processingRecache, err := suite.manager.PreProcessMediaRecache(ctx, data, original.ID)
|
||||||
suite.NoError(err)
|
suite.NoError(err)
|
||||||
|
|
||||||
// synchronously load the recached attachment
|
// synchronously load the recached attachment
|
||||||
|
|
|
@ -32,7 +32,13 @@ import (
|
||||||
|
|
||||||
type DereferenceMedia func(ctx context.Context, iri *url.URL) (io.ReadCloser, int64, error)
|
type DereferenceMedia func(ctx context.Context, iri *url.URL) (io.ReadCloser, int64, error)
|
||||||
|
|
||||||
func (m *manager) RefetchEmojis(ctx context.Context, domain string, dereferenceMedia DereferenceMedia) (int, error) {
|
// RefetchEmojis iterates through remote emojis (for the given domain, or all if domain is empty string).
|
||||||
|
//
|
||||||
|
// For each emoji, the manager will check whether both the full size and static images are present in storage.
|
||||||
|
// If not, the manager will refetch and reprocess full size and static images for the emoji.
|
||||||
|
//
|
||||||
|
// The provided DereferenceMedia function will be used when it's necessary to refetch something this way.
|
||||||
|
func (m *Manager) RefetchEmojis(ctx context.Context, domain string, dereferenceMedia DereferenceMedia) (int, error) {
|
||||||
// normalize domain
|
// normalize domain
|
||||||
if domain == "" {
|
if domain == "" {
|
||||||
domain = db.EmojiAllDomains
|
domain = db.EmojiAllDomains
|
||||||
|
@ -107,7 +113,7 @@ func (m *manager) RefetchEmojis(ctx context.Context, domain string, dereferenceM
|
||||||
return dereferenceMedia(ctx, emojiImageIRI)
|
return dereferenceMedia(ctx, emojiImageIRI)
|
||||||
}
|
}
|
||||||
|
|
||||||
processingEmoji, err := m.PreProcessEmoji(ctx, dataFunc, nil, emoji.Shortcode, emoji.ID, emoji.URI, &AdditionalEmojiInfo{
|
processingEmoji, err := m.PreProcessEmoji(ctx, dataFunc, emoji.Shortcode, emoji.ID, emoji.URI, &AdditionalEmojiInfo{
|
||||||
Domain: &emoji.Domain,
|
Domain: &emoji.Domain,
|
||||||
ImageRemoteURL: &emoji.ImageRemoteURL,
|
ImageRemoteURL: &emoji.ImageRemoteURL,
|
||||||
ImageStaticRemoteURL: &emoji.ImageStaticRemoteURL,
|
ImageStaticRemoteURL: &emoji.ImageStaticRemoteURL,
|
||||||
|
@ -131,7 +137,7 @@ func (m *manager) RefetchEmojis(ctx context.Context, domain string, dereferenceM
|
||||||
return totalRefetched, nil
|
return totalRefetched, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *manager) emojiRequiresRefetch(ctx context.Context, emoji *gtsmodel.Emoji) (bool, error) {
|
func (m *Manager) emojiRequiresRefetch(ctx context.Context, emoji *gtsmodel.Emoji) (bool, error) {
|
||||||
if has, err := m.state.Storage.Has(ctx, emoji.ImagePath); err != nil {
|
if has, err := m.state.Storage.Has(ctx, emoji.ImagePath); err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
} else if !has {
|
} else if !has {
|
||||||
|
|
|
@ -110,9 +110,3 @@ type AdditionalEmojiInfo struct {
|
||||||
|
|
||||||
// DataFunc represents a function used to retrieve the raw bytes of a piece of media.
|
// DataFunc represents a function used to retrieve the raw bytes of a piece of media.
|
||||||
type DataFunc func(ctx context.Context) (reader io.ReadCloser, fileSize int64, err error)
|
type DataFunc func(ctx context.Context) (reader io.ReadCloser, fileSize int64, err error)
|
||||||
|
|
||||||
// PostDataCallbackFunc represents a function executed after the DataFunc has been executed,
|
|
||||||
// and the returned reader has been read. It can be used to clean up any remaining resources.
|
|
||||||
//
|
|
||||||
// This can be set to nil, and will then not be executed.
|
|
||||||
type PostDataCallbackFunc func(ctx context.Context) error
|
|
||||||
|
|
|
@ -34,7 +34,7 @@ import (
|
||||||
type Processor struct {
|
type Processor struct {
|
||||||
state *state.State
|
state *state.State
|
||||||
tc typeutils.TypeConverter
|
tc typeutils.TypeConverter
|
||||||
mediaManager media.Manager
|
mediaManager *media.Manager
|
||||||
oauthServer oauth.Server
|
oauthServer oauth.Server
|
||||||
filter *visibility.Filter
|
filter *visibility.Filter
|
||||||
formatter text.Formatter
|
formatter text.Formatter
|
||||||
|
@ -46,7 +46,7 @@ type Processor struct {
|
||||||
func New(
|
func New(
|
||||||
state *state.State,
|
state *state.State,
|
||||||
tc typeutils.TypeConverter,
|
tc typeutils.TypeConverter,
|
||||||
mediaManager media.Manager,
|
mediaManager *media.Manager,
|
||||||
oauthServer oauth.Server,
|
oauthServer oauth.Server,
|
||||||
federator federation.Federator,
|
federator federation.Federator,
|
||||||
filter *visibility.Filter,
|
filter *visibility.Filter,
|
||||||
|
|
|
@ -45,7 +45,7 @@ type AccountStandardTestSuite struct {
|
||||||
tc typeutils.TypeConverter
|
tc typeutils.TypeConverter
|
||||||
storage *storage.Driver
|
storage *storage.Driver
|
||||||
state state.State
|
state state.State
|
||||||
mediaManager media.Manager
|
mediaManager *media.Manager
|
||||||
oauthServer oauth.Server
|
oauthServer oauth.Server
|
||||||
fromClientAPIChan chan messages.FromClientAPI
|
fromClientAPIChan chan messages.FromClientAPI
|
||||||
transportController transport.Controller
|
transportController transport.Controller
|
||||||
|
|
|
@ -300,7 +300,7 @@ func (p *Processor) UpdateAvatar(ctx context.Context, avatar *multipart.FileHead
|
||||||
Description: description,
|
Description: description,
|
||||||
}
|
}
|
||||||
|
|
||||||
processingMedia, err := p.mediaManager.PreProcessMedia(ctx, dataFunc, nil, accountID, ai)
|
processingMedia, err := p.mediaManager.PreProcessMedia(ctx, dataFunc, accountID, ai)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("UpdateAvatar: error processing avatar: %s", err)
|
return nil, fmt.Errorf("UpdateAvatar: error processing avatar: %s", err)
|
||||||
}
|
}
|
||||||
|
@ -327,7 +327,7 @@ func (p *Processor) UpdateHeader(ctx context.Context, header *multipart.FileHead
|
||||||
Header: &isHeader,
|
Header: &isHeader,
|
||||||
}
|
}
|
||||||
|
|
||||||
processingMedia, err := p.mediaManager.PreProcessMedia(ctx, dataFunc, nil, accountID, ai)
|
processingMedia, err := p.mediaManager.PreProcessMedia(ctx, dataFunc, accountID, ai)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("UpdateHeader: error processing header: %s", err)
|
return nil, fmt.Errorf("UpdateHeader: error processing header: %s", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,13 +28,13 @@ import (
|
||||||
type Processor struct {
|
type Processor struct {
|
||||||
state *state.State
|
state *state.State
|
||||||
tc typeutils.TypeConverter
|
tc typeutils.TypeConverter
|
||||||
mediaManager media.Manager
|
mediaManager *media.Manager
|
||||||
transportController transport.Controller
|
transportController transport.Controller
|
||||||
emailSender email.Sender
|
emailSender email.Sender
|
||||||
}
|
}
|
||||||
|
|
||||||
// New returns a new admin processor.
|
// New returns a new admin processor.
|
||||||
func New(state *state.State, tc typeutils.TypeConverter, mediaManager media.Manager, transportController transport.Controller, emailSender email.Sender) Processor {
|
func New(state *state.State, tc typeutils.TypeConverter, mediaManager *media.Manager, transportController transport.Controller, emailSender email.Sender) Processor {
|
||||||
return Processor{
|
return Processor{
|
||||||
state: state,
|
state: state,
|
||||||
tc: tc,
|
tc: tc,
|
||||||
|
|
|
@ -74,7 +74,7 @@ func (p *Processor) EmojiCreate(ctx context.Context, account *gtsmodel.Account,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
processingEmoji, err := p.mediaManager.PreProcessEmoji(ctx, data, nil, form.Shortcode, emojiID, emojiURI, ai, false)
|
processingEmoji, err := p.mediaManager.PreProcessEmoji(ctx, data, form.Shortcode, emojiID, emojiURI, ai, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error processing emoji: %s", err), "error processing emoji")
|
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error processing emoji: %s", err), "error processing emoji")
|
||||||
}
|
}
|
||||||
|
@ -355,7 +355,7 @@ func (p *Processor) emojiUpdateCopy(ctx context.Context, emoji *gtsmodel.Emoji,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
processingEmoji, err := p.mediaManager.PreProcessEmoji(ctx, data, nil, *shortcode, newEmojiID, newEmojiURI, ai, false)
|
processingEmoji, err := p.mediaManager.PreProcessEmoji(ctx, data, *shortcode, newEmojiID, newEmojiURI, ai, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("emojiUpdateCopy: error processing emoji %s: %s", emoji.ID, err)
|
err = fmt.Errorf("emojiUpdateCopy: error processing emoji %s: %s", emoji.ID, err)
|
||||||
return nil, gtserror.NewErrorInternalError(err)
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
|
@ -461,7 +461,7 @@ func (p *Processor) emojiUpdateModify(ctx context.Context, emoji *gtsmodel.Emoji
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
processingEmoji, err := p.mediaManager.PreProcessEmoji(ctx, data, nil, emoji.Shortcode, emoji.ID, emoji.URI, ai, true)
|
processingEmoji, err := p.mediaManager.PreProcessEmoji(ctx, data, emoji.Shortcode, emoji.ID, emoji.URI, ai, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("emojiUpdateModify: error processing emoji %s: %s", emoji.ID, err)
|
err = fmt.Errorf("emojiUpdateModify: error processing emoji %s: %s", emoji.ID, err)
|
||||||
return nil, gtserror.NewErrorInternalError(err)
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
|
|
|
@ -379,8 +379,8 @@ func (p *Processor) processUpdateAccountFromFederator(ctx context.Context, feder
|
||||||
return errors.New("Accountable was not parseable on update account message")
|
return errors.New("Accountable was not parseable on update account message")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call RefreshAccount to fetch up-to-date bio, avatar, header, etc.
|
// Fetch up-to-date bio, avatar, header, etc.
|
||||||
updatedAccount, _, err := p.federator.RefreshAccount(
|
_, _, err := p.federator.RefreshAccount(
|
||||||
ctx,
|
ctx,
|
||||||
federatorMsg.ReceivingAccount.Username,
|
federatorMsg.ReceivingAccount.Username,
|
||||||
incomingAccount,
|
incomingAccount,
|
||||||
|
@ -391,11 +391,6 @@ func (p *Processor) processUpdateAccountFromFederator(ctx context.Context, feder
|
||||||
return fmt.Errorf("error enriching updated account from federator: %s", err)
|
return fmt.Errorf("error enriching updated account from federator: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RefreshAccount doesn't make DB update calls, so do that here.
|
|
||||||
if err := p.state.DB.UpdateAccount(ctx, updatedAccount); err != nil {
|
|
||||||
return fmt.Errorf("error enriching updated account from federator: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,7 @@ func (p *Processor) Create(ctx context.Context, account *gtsmodel.Account, form
|
||||||
}
|
}
|
||||||
|
|
||||||
// process the media attachment and load it immediately
|
// process the media attachment and load it immediately
|
||||||
media, err := p.mediaManager.PreProcessMedia(ctx, data, nil, account.ID, &media.AdditionalMediaInfo{
|
media, err := p.mediaManager.PreProcessMedia(ctx, data, account.ID, &media.AdditionalMediaInfo{
|
||||||
Description: &form.Description,
|
Description: &form.Description,
|
||||||
FocusX: &focusX,
|
FocusX: &focusX,
|
||||||
FocusY: &focusY,
|
FocusY: &focusY,
|
||||||
|
|
|
@ -148,8 +148,7 @@ func (p *Processor) getAttachmentContent(ctx context.Context, requestingAccount
|
||||||
// [
|
// [
|
||||||
// the reason it was removed was because a slow
|
// the reason it was removed was because a slow
|
||||||
// client connection could hold open a storage
|
// client connection could hold open a storage
|
||||||
// recache operation, and so holding open a media
|
// recache operation -> holding open a media worker.
|
||||||
// worker worker.
|
|
||||||
// ]
|
// ]
|
||||||
|
|
||||||
dataFn := func(innerCtx context.Context) (io.ReadCloser, int64, error) {
|
dataFn := func(innerCtx context.Context) (io.ReadCloser, int64, error) {
|
||||||
|
@ -161,7 +160,7 @@ func (p *Processor) getAttachmentContent(ctx context.Context, requestingAccount
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start recaching this media with the prepared data function.
|
// Start recaching this media with the prepared data function.
|
||||||
processingMedia, err := p.mediaManager.PreProcessMediaRecache(ctx, dataFn, nil, wantedMediaID)
|
processingMedia, err := p.mediaManager.PreProcessMediaRecache(ctx, dataFn, wantedMediaID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, gtserror.NewErrorNotFound(fmt.Errorf("error recaching media: %s", err))
|
return nil, gtserror.NewErrorNotFound(fmt.Errorf("error recaching media: %s", err))
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,12 +27,12 @@ import (
|
||||||
type Processor struct {
|
type Processor struct {
|
||||||
state *state.State
|
state *state.State
|
||||||
tc typeutils.TypeConverter
|
tc typeutils.TypeConverter
|
||||||
mediaManager media.Manager
|
mediaManager *media.Manager
|
||||||
transportController transport.Controller
|
transportController transport.Controller
|
||||||
}
|
}
|
||||||
|
|
||||||
// New returns a new media processor.
|
// New returns a new media processor.
|
||||||
func New(state *state.State, tc typeutils.TypeConverter, mediaManager media.Manager, transportController transport.Controller) Processor {
|
func New(state *state.State, tc typeutils.TypeConverter, mediaManager *media.Manager, transportController transport.Controller) Processor {
|
||||||
return Processor{
|
return Processor{
|
||||||
state: state,
|
state: state,
|
||||||
tc: tc,
|
tc: tc,
|
||||||
|
|
|
@ -37,7 +37,7 @@ type MediaStandardTestSuite struct {
|
||||||
tc typeutils.TypeConverter
|
tc typeutils.TypeConverter
|
||||||
storage *storage.Driver
|
storage *storage.Driver
|
||||||
state state.State
|
state state.State
|
||||||
mediaManager media.Manager
|
mediaManager *media.Manager
|
||||||
transportController transport.Controller
|
transportController transport.Controller
|
||||||
|
|
||||||
// standard suite models
|
// standard suite models
|
||||||
|
|
|
@ -45,7 +45,7 @@ type Processor struct {
|
||||||
federator federation.Federator
|
federator federation.Federator
|
||||||
tc typeutils.TypeConverter
|
tc typeutils.TypeConverter
|
||||||
oauthServer oauth.Server
|
oauthServer oauth.Server
|
||||||
mediaManager mm.Manager
|
mediaManager *mm.Manager
|
||||||
state *state.State
|
state *state.State
|
||||||
emailSender email.Sender
|
emailSender email.Sender
|
||||||
filter *visibility.Filter
|
filter *visibility.Filter
|
||||||
|
@ -111,7 +111,7 @@ func NewProcessor(
|
||||||
tc typeutils.TypeConverter,
|
tc typeutils.TypeConverter,
|
||||||
federator federation.Federator,
|
federator federation.Federator,
|
||||||
oauthServer oauth.Server,
|
oauthServer oauth.Server,
|
||||||
mediaManager mm.Manager,
|
mediaManager *mm.Manager,
|
||||||
state *state.State,
|
state *state.State,
|
||||||
emailSender email.Sender,
|
emailSender email.Sender,
|
||||||
) *Processor {
|
) *Processor {
|
||||||
|
|
|
@ -43,7 +43,7 @@ type ProcessingStandardTestSuite struct {
|
||||||
db db.DB
|
db db.DB
|
||||||
storage *storage.Driver
|
storage *storage.Driver
|
||||||
state state.State
|
state state.State
|
||||||
mediaManager media.Manager
|
mediaManager *media.Manager
|
||||||
typeconverter typeutils.TypeConverter
|
typeconverter typeutils.TypeConverter
|
||||||
httpClient *testrig.MockHTTPClient
|
httpClient *testrig.MockHTTPClient
|
||||||
transportController transport.Controller
|
transportController transport.Controller
|
||||||
|
|
|
@ -40,7 +40,7 @@ type StatusStandardTestSuite struct {
|
||||||
tc transport.Controller
|
tc transport.Controller
|
||||||
storage *storage.Driver
|
storage *storage.Driver
|
||||||
state state.State
|
state state.State
|
||||||
mediaManager media.Manager
|
mediaManager *media.Manager
|
||||||
federator federation.Federator
|
federator federation.Federator
|
||||||
|
|
||||||
// standard suite models
|
// standard suite models
|
||||||
|
|
|
@ -130,7 +130,7 @@ func (t *transport) deliver(ctx context.Context, b []byte, to *url.URL) error {
|
||||||
|
|
||||||
if code := rsp.StatusCode; code != http.StatusOK &&
|
if code := rsp.StatusCode; code != http.StatusOK &&
|
||||||
code != http.StatusCreated && code != http.StatusAccepted {
|
code != http.StatusCreated && code != http.StatusAccepted {
|
||||||
return gtserror.NewResponseError(rsp)
|
return gtserror.NewFromResponse(rsp)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -65,7 +65,7 @@ func (t *transport) Dereference(ctx context.Context, iri *url.URL) ([]byte, erro
|
||||||
defer rsp.Body.Close()
|
defer rsp.Body.Close()
|
||||||
|
|
||||||
if rsp.StatusCode != http.StatusOK {
|
if rsp.StatusCode != http.StatusOK {
|
||||||
return nil, gtserror.NewResponseError(rsp)
|
return nil, gtserror.NewFromResponse(rsp)
|
||||||
}
|
}
|
||||||
|
|
||||||
return io.ReadAll(rsp.Body)
|
return io.ReadAll(rsp.Body)
|
||||||
|
|
|
@ -102,7 +102,7 @@ func dereferenceByAPIV1Instance(ctx context.Context, t *transport, iri *url.URL)
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
return nil, gtserror.NewResponseError(resp)
|
return nil, gtserror.NewFromResponse(resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
b, err := io.ReadAll(resp.Body)
|
b, err := io.ReadAll(resp.Body)
|
||||||
|
@ -252,7 +252,7 @@ func callNodeInfoWellKnown(ctx context.Context, t *transport, iri *url.URL) (*ur
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
return nil, gtserror.NewResponseError(resp)
|
return nil, gtserror.NewFromResponse(resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
b, err := io.ReadAll(resp.Body)
|
b, err := io.ReadAll(resp.Body)
|
||||||
|
@ -303,7 +303,7 @@ func callNodeInfo(ctx context.Context, t *transport, iri *url.URL) (*apimodel.No
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
return nil, gtserror.NewResponseError(resp)
|
return nil, gtserror.NewFromResponse(resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
b, err := io.ReadAll(resp.Body)
|
b, err := io.ReadAll(resp.Body)
|
||||||
|
|
|
@ -46,7 +46,7 @@ func (t *transport) DereferenceMedia(ctx context.Context, iri *url.URL) (io.Read
|
||||||
|
|
||||||
// Check for an expected status code
|
// Check for an expected status code
|
||||||
if rsp.StatusCode != http.StatusOK {
|
if rsp.StatusCode != http.StatusOK {
|
||||||
return nil, 0, gtserror.NewResponseError(rsp)
|
return nil, 0, gtserror.NewFromResponse(rsp)
|
||||||
}
|
}
|
||||||
|
|
||||||
return rsp.Body, rsp.ContentLength, nil
|
return rsp.Body, rsp.ContentLength, nil
|
||||||
|
|
|
@ -156,7 +156,7 @@ func (t *transport) Finger(ctx context.Context, targetUsername string, targetDom
|
||||||
}
|
}
|
||||||
// We've reached the end of the line here, both the original request
|
// We've reached the end of the line here, both the original request
|
||||||
// and our attempt to resolve it through the fallback have failed
|
// and our attempt to resolve it through the fallback have failed
|
||||||
return nil, gtserror.NewResponseError(rsp)
|
return nil, gtserror.NewFromResponse(rsp)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the URL in cache here, since host-meta told us this should be the
|
// Set the URL in cache here, since host-meta told us this should be the
|
||||||
|
|
|
@ -39,7 +39,7 @@ type TransportTestSuite struct {
|
||||||
suite.Suite
|
suite.Suite
|
||||||
db db.DB
|
db db.DB
|
||||||
storage *storage.Driver
|
storage *storage.Driver
|
||||||
mediaManager media.Manager
|
mediaManager *media.Manager
|
||||||
federator federation.Federator
|
federator federation.Federator
|
||||||
processor *processing.Processor
|
processor *processing.Processor
|
||||||
emailSender email.Sender
|
emailSender email.Sender
|
||||||
|
|
|
@ -1,11 +1,27 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
set -eu
|
set -e
|
||||||
|
|
||||||
# DEBUG returns whether DEBUG build is enabled.
|
# Log and execute provided args.
|
||||||
DEBUG() { [ ! -z "${DEBUG-}" ]; }
|
log_exec() { echo "$ ${*}"; "$@"; }
|
||||||
|
|
||||||
CGO_ENABLED=0 go build -trimpath \
|
# Grab environment variables and set defaults + requirements.
|
||||||
-tags "netgo osusergo static_build kvformat $(DEBUG && echo 'debugenv')" \
|
GO_BUILDTAGS="${GO_BUILDTAGS-} netgo osusergo static_build kvformat"
|
||||||
-ldflags="-s -w -extldflags '-static' -X 'main.Version=${VERSION:-$(git describe --tags --abbrev=0)}'" \
|
GO_LDFLAGS="${GO_LDFLAGS-} -s -w -extldflags '-static' -X 'main.Version=${VERSION:-$(git describe --tags --abbrev=0)}'"
|
||||||
|
GO_GCFLAGS=${GO_GCFLAGS-}
|
||||||
|
|
||||||
|
# Maintain old $DEBUG compat.
|
||||||
|
[ ! -z "$DEBUG" ] && \
|
||||||
|
GO_BUILDTAGS="${GO_BUILDTAGS} debugenv"
|
||||||
|
|
||||||
|
# Available Go build tags, with explanation, followed by benefits of enabling it:
|
||||||
|
# - kvformat: enables prettier output of log fields (slightly better performance)
|
||||||
|
# - notracing: disables compiling-in otel tracing support (reduced binary size, better performance)
|
||||||
|
# - noerrcaller: disables caller function prefix in errors (slightly better performance, at cost of err readability)
|
||||||
|
# - debug: enables /debug/pprof endpoint (adds debug, at performance cost)
|
||||||
|
# - debugenv: enables /debug/pprof endpoint if DEBUG=1 env during runtime (adds debug, at performance cost)
|
||||||
|
log_exec env CGO_ENABLED=0 go build -trimpath -v \
|
||||||
|
-tags "${GO_BUILDTAGS}" \
|
||||||
|
-ldflags="${GO_LDFLAGS}" \
|
||||||
|
-gcflags="${GO_GCFLAGS}" \
|
||||||
./cmd/gotosocial
|
./cmd/gotosocial
|
||||||
|
|
|
@ -25,6 +25,6 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewTestFederator returns a federator with the given database and (mock!!) transport controller.
|
// NewTestFederator returns a federator with the given database and (mock!!) transport controller.
|
||||||
func NewTestFederator(state *state.State, tc transport.Controller, mediaManager media.Manager) federation.Federator {
|
func NewTestFederator(state *state.State, tc transport.Controller, mediaManager *media.Manager) federation.Federator {
|
||||||
return federation.NewFederator(state, NewTestFederatingDB(state), tc, NewTestTypeConverter(state.DB), mediaManager)
|
return federation.NewFederator(state, NewTestFederatingDB(state), tc, NewTestTypeConverter(state.DB), mediaManager)
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewTestMediaManager returns a media handler with the default test config, and the given db and storage.
|
// NewTestMediaManager returns a media handler with the default test config, and the given db and storage.
|
||||||
func NewTestMediaManager(state *state.State) media.Manager {
|
func NewTestMediaManager(state *state.State) *media.Manager {
|
||||||
StartWorkers(state) // ensure started
|
StartWorkers(state) // ensure started
|
||||||
return media.NewManager(state)
|
return media.NewManager(state)
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewTestProcessor returns a Processor suitable for testing purposes
|
// NewTestProcessor returns a Processor suitable for testing purposes
|
||||||
func NewTestProcessor(state *state.State, federator federation.Federator, emailSender email.Sender, mediaManager media.Manager) *processing.Processor {
|
func NewTestProcessor(state *state.State, federator federation.Federator, emailSender email.Sender, mediaManager *media.Manager) *processing.Processor {
|
||||||
p := processing.NewProcessor(NewTestTypeConverter(state.DB), federator, NewTestOauthServer(state.DB), mediaManager, state, emailSender)
|
p := processing.NewProcessor(NewTestTypeConverter(state.DB), federator, NewTestOauthServer(state.DB), mediaManager, state, emailSender)
|
||||||
state.Workers.EnqueueClientAPI = p.EnqueueClientAPI
|
state.Workers.EnqueueClientAPI = p.EnqueueClientAPI
|
||||||
state.Workers.EnqueueFederator = p.EnqueueFederator
|
state.Workers.EnqueueFederator = p.EnqueueFederator
|
||||||
|
|
Loading…
Reference in a new issue