restructure a bunch, get unfaves working
This commit is contained in:
parent
de3b6bc6d9
commit
1f44b06c06
@ -23,7 +23,7 @@ import (
|
|||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/processing"
|
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||||
)
|
)
|
||||||
|
|
||||||
// InboxPOSTHandler deals with incoming POST requests to an actor's inbox.
|
// InboxPOSTHandler deals with incoming POST requests to an actor's inbox.
|
||||||
@ -42,7 +42,7 @@ func (m *Module) InboxPOSTHandler(c *gin.Context) {
|
|||||||
|
|
||||||
posted, err := m.processor.InboxPost(c.Request.Context(), c.Writer, c.Request)
|
posted, err := m.processor.InboxPost(c.Request.Context(), c.Writer, c.Request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if withCode, ok := err.(processing.ErrorWithCode); ok {
|
if withCode, ok := err.(gtserror.WithCode); ok {
|
||||||
l.Debug(withCode.Error())
|
l.Debug(withCode.Error())
|
||||||
c.JSON(withCode.Code(), withCode.Safe())
|
c.JSON(withCode.Code(), withCode.Safe())
|
||||||
return
|
return
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package processing
|
package gtserror
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
@ -24,12 +24,12 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrorWithCode wraps an internal error with an http code, and a 'safe' version of
|
// WithCode wraps an internal error with an http code, and a 'safe' version of
|
||||||
// the error that can be served to clients without revealing internal business logic.
|
// the error that can be served to clients without revealing internal business logic.
|
||||||
//
|
//
|
||||||
// A typical use of this error would be to first log the Original error, then return
|
// A typical use of this error would be to first log the Original error, then return
|
||||||
// the Safe error and the StatusCode to an API caller.
|
// the Safe error and the StatusCode to an API caller.
|
||||||
type ErrorWithCode interface {
|
type WithCode interface {
|
||||||
// Error returns the original internal error for debugging within the GoToSocial logs.
|
// Error returns the original internal error for debugging within the GoToSocial logs.
|
||||||
// This should *NEVER* be returned to a client as it may contain sensitive information.
|
// This should *NEVER* be returned to a client as it may contain sensitive information.
|
||||||
Error() string
|
Error() string
|
||||||
@ -40,31 +40,31 @@ type ErrorWithCode interface {
|
|||||||
Code() int
|
Code() int
|
||||||
}
|
}
|
||||||
|
|
||||||
type errorWithCode struct {
|
type withCode struct {
|
||||||
original error
|
original error
|
||||||
safe error
|
safe error
|
||||||
code int
|
code int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e errorWithCode) Error() string {
|
func (e withCode) Error() string {
|
||||||
return e.original.Error()
|
return e.original.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e errorWithCode) Safe() string {
|
func (e withCode) Safe() string {
|
||||||
return e.safe.Error()
|
return e.safe.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e errorWithCode) Code() int {
|
func (e withCode) Code() int {
|
||||||
return e.code
|
return e.code
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewErrorBadRequest returns an ErrorWithCode 400 with the given original error and optional help text.
|
// NewErrorBadRequest returns an ErrorWithCode 400 with the given original error and optional help text.
|
||||||
func NewErrorBadRequest(original error, helpText ...string) ErrorWithCode {
|
func NewErrorBadRequest(original error, helpText ...string) WithCode {
|
||||||
safe := "bad request"
|
safe := "bad request"
|
||||||
if helpText != nil {
|
if helpText != nil {
|
||||||
safe = safe + ": " + strings.Join(helpText, ": ")
|
safe = safe + ": " + strings.Join(helpText, ": ")
|
||||||
}
|
}
|
||||||
return errorWithCode{
|
return withCode{
|
||||||
original: original,
|
original: original,
|
||||||
safe: errors.New(safe),
|
safe: errors.New(safe),
|
||||||
code: http.StatusBadRequest,
|
code: http.StatusBadRequest,
|
||||||
@ -72,12 +72,12 @@ func NewErrorBadRequest(original error, helpText ...string) ErrorWithCode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewErrorNotAuthorized returns an ErrorWithCode 401 with the given original error and optional help text.
|
// NewErrorNotAuthorized returns an ErrorWithCode 401 with the given original error and optional help text.
|
||||||
func NewErrorNotAuthorized(original error, helpText ...string) ErrorWithCode {
|
func NewErrorNotAuthorized(original error, helpText ...string) WithCode {
|
||||||
safe := "not authorized"
|
safe := "not authorized"
|
||||||
if helpText != nil {
|
if helpText != nil {
|
||||||
safe = safe + ": " + strings.Join(helpText, ": ")
|
safe = safe + ": " + strings.Join(helpText, ": ")
|
||||||
}
|
}
|
||||||
return errorWithCode{
|
return withCode{
|
||||||
original: original,
|
original: original,
|
||||||
safe: errors.New(safe),
|
safe: errors.New(safe),
|
||||||
code: http.StatusUnauthorized,
|
code: http.StatusUnauthorized,
|
||||||
@ -85,12 +85,12 @@ func NewErrorNotAuthorized(original error, helpText ...string) ErrorWithCode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewErrorForbidden returns an ErrorWithCode 403 with the given original error and optional help text.
|
// NewErrorForbidden returns an ErrorWithCode 403 with the given original error and optional help text.
|
||||||
func NewErrorForbidden(original error, helpText ...string) ErrorWithCode {
|
func NewErrorForbidden(original error, helpText ...string) WithCode {
|
||||||
safe := "forbidden"
|
safe := "forbidden"
|
||||||
if helpText != nil {
|
if helpText != nil {
|
||||||
safe = safe + ": " + strings.Join(helpText, ": ")
|
safe = safe + ": " + strings.Join(helpText, ": ")
|
||||||
}
|
}
|
||||||
return errorWithCode{
|
return withCode{
|
||||||
original: original,
|
original: original,
|
||||||
safe: errors.New(safe),
|
safe: errors.New(safe),
|
||||||
code: http.StatusForbidden,
|
code: http.StatusForbidden,
|
||||||
@ -98,12 +98,12 @@ func NewErrorForbidden(original error, helpText ...string) ErrorWithCode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewErrorNotFound returns an ErrorWithCode 404 with the given original error and optional help text.
|
// NewErrorNotFound returns an ErrorWithCode 404 with the given original error and optional help text.
|
||||||
func NewErrorNotFound(original error, helpText ...string) ErrorWithCode {
|
func NewErrorNotFound(original error, helpText ...string) WithCode {
|
||||||
safe := "404 not found"
|
safe := "404 not found"
|
||||||
if helpText != nil {
|
if helpText != nil {
|
||||||
safe = safe + ": " + strings.Join(helpText, ": ")
|
safe = safe + ": " + strings.Join(helpText, ": ")
|
||||||
}
|
}
|
||||||
return errorWithCode{
|
return withCode{
|
||||||
original: original,
|
original: original,
|
||||||
safe: errors.New(safe),
|
safe: errors.New(safe),
|
||||||
code: http.StatusNotFound,
|
code: http.StatusNotFound,
|
||||||
@ -111,12 +111,12 @@ func NewErrorNotFound(original error, helpText ...string) ErrorWithCode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewErrorInternalError returns an ErrorWithCode 500 with the given original error and optional help text.
|
// NewErrorInternalError returns an ErrorWithCode 500 with the given original error and optional help text.
|
||||||
func NewErrorInternalError(original error, helpText ...string) ErrorWithCode {
|
func NewErrorInternalError(original error, helpText ...string) WithCode {
|
||||||
safe := "internal server error"
|
safe := "internal server error"
|
||||||
if helpText != nil {
|
if helpText != nil {
|
||||||
safe = safe + ": " + strings.Join(helpText, ": ")
|
safe = safe + ": " + strings.Join(helpText, ": ")
|
||||||
}
|
}
|
||||||
return errorWithCode{
|
return withCode{
|
||||||
original: original,
|
original: original,
|
||||||
safe: errors.New(safe),
|
safe: errors.New(safe),
|
||||||
code: http.StatusInternalServerError,
|
code: http.StatusInternalServerError,
|
@ -25,6 +25,7 @@ import (
|
|||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
"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/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/util"
|
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||||
@ -202,13 +203,13 @@ func (p *processor) AccountUpdate(authed *oauth.Auth, form *apimodel.UpdateCrede
|
|||||||
return acctSensitive, nil
|
return acctSensitive, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *processor) AccountStatusesGet(authed *oauth.Auth, targetAccountID string, limit int, excludeReplies bool, maxID string, pinned bool, mediaOnly bool) ([]apimodel.Status, ErrorWithCode) {
|
func (p *processor) AccountStatusesGet(authed *oauth.Auth, targetAccountID string, limit int, excludeReplies bool, maxID string, pinned bool, mediaOnly bool) ([]apimodel.Status, gtserror.WithCode) {
|
||||||
targetAccount := >smodel.Account{}
|
targetAccount := >smodel.Account{}
|
||||||
if err := p.db.GetByID(targetAccountID, targetAccount); err != nil {
|
if err := p.db.GetByID(targetAccountID, targetAccount); err != nil {
|
||||||
if _, ok := err.(db.ErrNoEntries); ok {
|
if _, ok := err.(db.ErrNoEntries); ok {
|
||||||
return nil, NewErrorNotFound(fmt.Errorf("no entry found for account id %s", targetAccountID))
|
return nil, gtserror.NewErrorNotFound(fmt.Errorf("no entry found for account id %s", targetAccountID))
|
||||||
}
|
}
|
||||||
return nil, NewErrorInternalError(err)
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
statuses := []gtsmodel.Status{}
|
statuses := []gtsmodel.Status{}
|
||||||
@ -217,18 +218,18 @@ func (p *processor) AccountStatusesGet(authed *oauth.Auth, targetAccountID strin
|
|||||||
if _, ok := err.(db.ErrNoEntries); ok {
|
if _, ok := err.(db.ErrNoEntries); ok {
|
||||||
return apiStatuses, nil
|
return apiStatuses, nil
|
||||||
}
|
}
|
||||||
return nil, NewErrorInternalError(err)
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, s := range statuses {
|
for _, s := range statuses {
|
||||||
relevantAccounts, err := p.db.PullRelevantAccountsFromStatus(&s)
|
relevantAccounts, err := p.db.PullRelevantAccountsFromStatus(&s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, NewErrorInternalError(fmt.Errorf("error getting relevant statuses: %s", err))
|
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error getting relevant statuses: %s", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
visible, err := p.db.StatusVisible(&s, targetAccount, authed.Account, relevantAccounts)
|
visible, err := p.db.StatusVisible(&s, targetAccount, authed.Account, relevantAccounts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, NewErrorInternalError(fmt.Errorf("error checking status visibility: %s", err))
|
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error checking status visibility: %s", err))
|
||||||
}
|
}
|
||||||
if !visible {
|
if !visible {
|
||||||
continue
|
continue
|
||||||
@ -238,16 +239,16 @@ func (p *processor) AccountStatusesGet(authed *oauth.Auth, targetAccountID strin
|
|||||||
if s.BoostOfID != "" {
|
if s.BoostOfID != "" {
|
||||||
bs := >smodel.Status{}
|
bs := >smodel.Status{}
|
||||||
if err := p.db.GetByID(s.BoostOfID, bs); err != nil {
|
if err := p.db.GetByID(s.BoostOfID, bs); err != nil {
|
||||||
return nil, NewErrorInternalError(fmt.Errorf("error getting boosted status: %s", err))
|
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error getting boosted status: %s", err))
|
||||||
}
|
}
|
||||||
boostedRelevantAccounts, err := p.db.PullRelevantAccountsFromStatus(bs)
|
boostedRelevantAccounts, err := p.db.PullRelevantAccountsFromStatus(bs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, NewErrorInternalError(fmt.Errorf("error getting relevant accounts from boosted status: %s", err))
|
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error getting relevant accounts from boosted status: %s", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
boostedVisible, err := p.db.StatusVisible(bs, relevantAccounts.BoostedAccount, authed.Account, boostedRelevantAccounts)
|
boostedVisible, err := p.db.StatusVisible(bs, relevantAccounts.BoostedAccount, authed.Account, boostedRelevantAccounts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, NewErrorInternalError(fmt.Errorf("error checking boosted status visibility: %s", err))
|
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error checking boosted status visibility: %s", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
if boostedVisible {
|
if boostedVisible {
|
||||||
@ -257,7 +258,7 @@ func (p *processor) AccountStatusesGet(authed *oauth.Auth, targetAccountID strin
|
|||||||
|
|
||||||
apiStatus, err := p.tc.StatusToMasto(&s, targetAccount, authed.Account, relevantAccounts.BoostedAccount, relevantAccounts.ReplyToAccount, boostedStatus)
|
apiStatus, err := p.tc.StatusToMasto(&s, targetAccount, authed.Account, relevantAccounts.BoostedAccount, relevantAccounts.ReplyToAccount, boostedStatus)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, NewErrorInternalError(fmt.Errorf("error converting status to masto: %s", err))
|
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting status to masto: %s", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
apiStatuses = append(apiStatuses, *apiStatus)
|
apiStatuses = append(apiStatuses, *apiStatus)
|
||||||
@ -266,14 +267,14 @@ func (p *processor) AccountStatusesGet(authed *oauth.Auth, targetAccountID strin
|
|||||||
return apiStatuses, nil
|
return apiStatuses, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *processor) AccountFollowersGet(authed *oauth.Auth, targetAccountID string) ([]apimodel.Account, ErrorWithCode) {
|
func (p *processor) AccountFollowersGet(authed *oauth.Auth, targetAccountID string) ([]apimodel.Account, gtserror.WithCode) {
|
||||||
blocked, err := p.db.Blocked(authed.Account.ID, targetAccountID)
|
blocked, err := p.db.Blocked(authed.Account.ID, targetAccountID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, NewErrorInternalError(err)
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if blocked {
|
if blocked {
|
||||||
return nil, NewErrorNotFound(fmt.Errorf("block exists between accounts"))
|
return nil, gtserror.NewErrorNotFound(fmt.Errorf("block exists between accounts"))
|
||||||
}
|
}
|
||||||
|
|
||||||
followers := []gtsmodel.Follow{}
|
followers := []gtsmodel.Follow{}
|
||||||
@ -282,13 +283,13 @@ func (p *processor) AccountFollowersGet(authed *oauth.Auth, targetAccountID stri
|
|||||||
if _, ok := err.(db.ErrNoEntries); ok {
|
if _, ok := err.(db.ErrNoEntries); ok {
|
||||||
return accounts, nil
|
return accounts, nil
|
||||||
}
|
}
|
||||||
return nil, NewErrorInternalError(err)
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, f := range followers {
|
for _, f := range followers {
|
||||||
blocked, err := p.db.Blocked(authed.Account.ID, f.AccountID)
|
blocked, err := p.db.Blocked(authed.Account.ID, f.AccountID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, NewErrorInternalError(err)
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
}
|
}
|
||||||
if blocked {
|
if blocked {
|
||||||
continue
|
continue
|
||||||
@ -299,7 +300,7 @@ func (p *processor) AccountFollowersGet(authed *oauth.Auth, targetAccountID stri
|
|||||||
if _, ok := err.(db.ErrNoEntries); ok {
|
if _, ok := err.(db.ErrNoEntries); ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
return nil, NewErrorInternalError(err)
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// derefence account fields in case we haven't done it already
|
// derefence account fields in case we haven't done it already
|
||||||
@ -310,21 +311,21 @@ func (p *processor) AccountFollowersGet(authed *oauth.Auth, targetAccountID stri
|
|||||||
|
|
||||||
account, err := p.tc.AccountToMastoPublic(a)
|
account, err := p.tc.AccountToMastoPublic(a)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, NewErrorInternalError(err)
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
}
|
}
|
||||||
accounts = append(accounts, *account)
|
accounts = append(accounts, *account)
|
||||||
}
|
}
|
||||||
return accounts, nil
|
return accounts, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *processor) AccountFollowingGet(authed *oauth.Auth, targetAccountID string) ([]apimodel.Account, ErrorWithCode) {
|
func (p *processor) AccountFollowingGet(authed *oauth.Auth, targetAccountID string) ([]apimodel.Account, gtserror.WithCode) {
|
||||||
blocked, err := p.db.Blocked(authed.Account.ID, targetAccountID)
|
blocked, err := p.db.Blocked(authed.Account.ID, targetAccountID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, NewErrorInternalError(err)
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if blocked {
|
if blocked {
|
||||||
return nil, NewErrorNotFound(fmt.Errorf("block exists between accounts"))
|
return nil, gtserror.NewErrorNotFound(fmt.Errorf("block exists between accounts"))
|
||||||
}
|
}
|
||||||
|
|
||||||
following := []gtsmodel.Follow{}
|
following := []gtsmodel.Follow{}
|
||||||
@ -333,13 +334,13 @@ func (p *processor) AccountFollowingGet(authed *oauth.Auth, targetAccountID stri
|
|||||||
if _, ok := err.(db.ErrNoEntries); ok {
|
if _, ok := err.(db.ErrNoEntries); ok {
|
||||||
return accounts, nil
|
return accounts, nil
|
||||||
}
|
}
|
||||||
return nil, NewErrorInternalError(err)
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, f := range following {
|
for _, f := range following {
|
||||||
blocked, err := p.db.Blocked(authed.Account.ID, f.AccountID)
|
blocked, err := p.db.Blocked(authed.Account.ID, f.AccountID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, NewErrorInternalError(err)
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
}
|
}
|
||||||
if blocked {
|
if blocked {
|
||||||
continue
|
continue
|
||||||
@ -350,7 +351,7 @@ func (p *processor) AccountFollowingGet(authed *oauth.Auth, targetAccountID stri
|
|||||||
if _, ok := err.(db.ErrNoEntries); ok {
|
if _, ok := err.(db.ErrNoEntries); ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
return nil, NewErrorInternalError(err)
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// derefence account fields in case we haven't done it already
|
// derefence account fields in case we haven't done it already
|
||||||
@ -361,53 +362,53 @@ func (p *processor) AccountFollowingGet(authed *oauth.Auth, targetAccountID stri
|
|||||||
|
|
||||||
account, err := p.tc.AccountToMastoPublic(a)
|
account, err := p.tc.AccountToMastoPublic(a)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, NewErrorInternalError(err)
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
}
|
}
|
||||||
accounts = append(accounts, *account)
|
accounts = append(accounts, *account)
|
||||||
}
|
}
|
||||||
return accounts, nil
|
return accounts, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *processor) AccountRelationshipGet(authed *oauth.Auth, targetAccountID string) (*apimodel.Relationship, ErrorWithCode) {
|
func (p *processor) AccountRelationshipGet(authed *oauth.Auth, targetAccountID string) (*apimodel.Relationship, gtserror.WithCode) {
|
||||||
if authed == nil || authed.Account == nil {
|
if authed == nil || authed.Account == nil {
|
||||||
return nil, NewErrorForbidden(errors.New("not authed"))
|
return nil, gtserror.NewErrorForbidden(errors.New("not authed"))
|
||||||
}
|
}
|
||||||
|
|
||||||
gtsR, err := p.db.GetRelationship(authed.Account.ID, targetAccountID)
|
gtsR, err := p.db.GetRelationship(authed.Account.ID, targetAccountID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, NewErrorInternalError(fmt.Errorf("error getting relationship: %s", err))
|
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error getting relationship: %s", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
r, err := p.tc.RelationshipToMasto(gtsR)
|
r, err := p.tc.RelationshipToMasto(gtsR)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, NewErrorInternalError(fmt.Errorf("error converting relationship: %s", err))
|
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting relationship: %s", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
return r, nil
|
return r, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *processor) AccountFollowCreate(authed *oauth.Auth, form *apimodel.AccountFollowRequest) (*apimodel.Relationship, ErrorWithCode) {
|
func (p *processor) AccountFollowCreate(authed *oauth.Auth, form *apimodel.AccountFollowRequest) (*apimodel.Relationship, gtserror.WithCode) {
|
||||||
// if there's a block between the accounts we shouldn't create the request ofc
|
// if there's a block between the accounts we shouldn't create the request ofc
|
||||||
blocked, err := p.db.Blocked(authed.Account.ID, form.TargetAccountID)
|
blocked, err := p.db.Blocked(authed.Account.ID, form.TargetAccountID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, NewErrorInternalError(err)
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
}
|
}
|
||||||
if blocked {
|
if blocked {
|
||||||
return nil, NewErrorNotFound(fmt.Errorf("accountfollowcreate: block exists between accounts"))
|
return nil, gtserror.NewErrorNotFound(fmt.Errorf("accountfollowcreate: block exists between accounts"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// make sure the target account actually exists in our db
|
// make sure the target account actually exists in our db
|
||||||
targetAcct := >smodel.Account{}
|
targetAcct := >smodel.Account{}
|
||||||
if err := p.db.GetByID(form.TargetAccountID, targetAcct); err != nil {
|
if err := p.db.GetByID(form.TargetAccountID, targetAcct); err != nil {
|
||||||
if _, ok := err.(db.ErrNoEntries); ok {
|
if _, ok := err.(db.ErrNoEntries); ok {
|
||||||
return nil, NewErrorNotFound(fmt.Errorf("accountfollowcreate: account %s not found in the db: %s", form.TargetAccountID, err))
|
return nil, gtserror.NewErrorNotFound(fmt.Errorf("accountfollowcreate: account %s not found in the db: %s", form.TargetAccountID, err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if a follow exists already
|
// check if a follow exists already
|
||||||
follows, err := p.db.Follows(authed.Account, targetAcct)
|
follows, err := p.db.Follows(authed.Account, targetAcct)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, NewErrorInternalError(fmt.Errorf("accountfollowcreate: error checking follow in db: %s", err))
|
return nil, gtserror.NewErrorInternalError(fmt.Errorf("accountfollowcreate: error checking follow in db: %s", err))
|
||||||
}
|
}
|
||||||
if follows {
|
if follows {
|
||||||
// already follows so just return the relationship
|
// already follows so just return the relationship
|
||||||
@ -417,7 +418,7 @@ func (p *processor) AccountFollowCreate(authed *oauth.Auth, form *apimodel.Accou
|
|||||||
// check if a follow exists already
|
// check if a follow exists already
|
||||||
followRequested, err := p.db.FollowRequested(authed.Account, targetAcct)
|
followRequested, err := p.db.FollowRequested(authed.Account, targetAcct)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, NewErrorInternalError(fmt.Errorf("accountfollowcreate: error checking follow request in db: %s", err))
|
return nil, gtserror.NewErrorInternalError(fmt.Errorf("accountfollowcreate: error checking follow request in db: %s", err))
|
||||||
}
|
}
|
||||||
if followRequested {
|
if followRequested {
|
||||||
// already follow requested so just return the relationship
|
// already follow requested so just return the relationship
|
||||||
@ -445,13 +446,13 @@ func (p *processor) AccountFollowCreate(authed *oauth.Auth, form *apimodel.Accou
|
|||||||
|
|
||||||
// whack it in the database
|
// whack it in the database
|
||||||
if err := p.db.Put(fr); err != nil {
|
if err := p.db.Put(fr); err != nil {
|
||||||
return nil, NewErrorInternalError(fmt.Errorf("accountfollowcreate: error creating follow request in db: %s", err))
|
return nil, gtserror.NewErrorInternalError(fmt.Errorf("accountfollowcreate: error creating follow request in db: %s", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
// if it's a local account that's not locked we can just straight up accept the follow request
|
// if it's a local account that's not locked we can just straight up accept the follow request
|
||||||
if !targetAcct.Locked && targetAcct.Domain == "" {
|
if !targetAcct.Locked && targetAcct.Domain == "" {
|
||||||
if _, err := p.db.AcceptFollowRequest(authed.Account.ID, form.TargetAccountID); err != nil {
|
if _, err := p.db.AcceptFollowRequest(authed.Account.ID, form.TargetAccountID); err != nil {
|
||||||
return nil, NewErrorInternalError(fmt.Errorf("accountfollowcreate: error accepting folow request for local unlocked account: %s", err))
|
return nil, gtserror.NewErrorInternalError(fmt.Errorf("accountfollowcreate: error accepting folow request for local unlocked account: %s", err))
|
||||||
}
|
}
|
||||||
// return the new relationship
|
// return the new relationship
|
||||||
return p.AccountRelationshipGet(authed, form.TargetAccountID)
|
return p.AccountRelationshipGet(authed, form.TargetAccountID)
|
||||||
@ -470,21 +471,21 @@ func (p *processor) AccountFollowCreate(authed *oauth.Auth, form *apimodel.Accou
|
|||||||
return p.AccountRelationshipGet(authed, form.TargetAccountID)
|
return p.AccountRelationshipGet(authed, form.TargetAccountID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *processor) AccountFollowRemove(authed *oauth.Auth, targetAccountID string) (*apimodel.Relationship, ErrorWithCode) {
|
func (p *processor) AccountFollowRemove(authed *oauth.Auth, targetAccountID string) (*apimodel.Relationship, gtserror.WithCode) {
|
||||||
// if there's a block between the accounts we shouldn't do anything
|
// if there's a block between the accounts we shouldn't do anything
|
||||||
blocked, err := p.db.Blocked(authed.Account.ID, targetAccountID)
|
blocked, err := p.db.Blocked(authed.Account.ID, targetAccountID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, NewErrorInternalError(err)
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
}
|
}
|
||||||
if blocked {
|
if blocked {
|
||||||
return nil, NewErrorNotFound(fmt.Errorf("AccountFollowRemove: block exists between accounts"))
|
return nil, gtserror.NewErrorNotFound(fmt.Errorf("AccountFollowRemove: block exists between accounts"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// make sure the target account actually exists in our db
|
// make sure the target account actually exists in our db
|
||||||
targetAcct := >smodel.Account{}
|
targetAcct := >smodel.Account{}
|
||||||
if err := p.db.GetByID(targetAccountID, targetAcct); err != nil {
|
if err := p.db.GetByID(targetAccountID, targetAcct); err != nil {
|
||||||
if _, ok := err.(db.ErrNoEntries); ok {
|
if _, ok := err.(db.ErrNoEntries); ok {
|
||||||
return nil, NewErrorNotFound(fmt.Errorf("AccountFollowRemove: account %s not found in the db: %s", targetAccountID, err))
|
return nil, gtserror.NewErrorNotFound(fmt.Errorf("AccountFollowRemove: account %s not found in the db: %s", targetAccountID, err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -498,7 +499,7 @@ func (p *processor) AccountFollowRemove(authed *oauth.Auth, targetAccountID stri
|
|||||||
}, fr); err == nil {
|
}, fr); err == nil {
|
||||||
frURI = fr.URI
|
frURI = fr.URI
|
||||||
if err := p.db.DeleteByID(fr.ID, fr); err != nil {
|
if err := p.db.DeleteByID(fr.ID, fr); err != nil {
|
||||||
return nil, NewErrorInternalError(fmt.Errorf("AccountFollowRemove: error removing follow request from db: %s", err))
|
return nil, gtserror.NewErrorInternalError(fmt.Errorf("AccountFollowRemove: error removing follow request from db: %s", err))
|
||||||
}
|
}
|
||||||
frChanged = true
|
frChanged = true
|
||||||
}
|
}
|
||||||
@ -513,7 +514,7 @@ func (p *processor) AccountFollowRemove(authed *oauth.Auth, targetAccountID stri
|
|||||||
}, f); err == nil {
|
}, f); err == nil {
|
||||||
fURI = f.URI
|
fURI = f.URI
|
||||||
if err := p.db.DeleteByID(f.ID, f); err != nil {
|
if err := p.db.DeleteByID(f.ID, f); err != nil {
|
||||||
return nil, NewErrorInternalError(fmt.Errorf("AccountFollowRemove: error removing follow from db: %s", err))
|
return nil, gtserror.NewErrorInternalError(fmt.Errorf("AccountFollowRemove: error removing follow from db: %s", err))
|
||||||
}
|
}
|
||||||
fChanged = true
|
fChanged = true
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,7 @@ import (
|
|||||||
"github.com/go-fed/activity/streams"
|
"github.com/go-fed/activity/streams"
|
||||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
"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/util"
|
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||||
)
|
)
|
||||||
@ -88,141 +89,141 @@ func (p *processor) authenticateAndDereferenceFediRequest(username string, r *ht
|
|||||||
return requestingAccount, nil
|
return requestingAccount, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *processor) GetFediUser(requestedUsername string, request *http.Request) (interface{}, ErrorWithCode) {
|
func (p *processor) GetFediUser(requestedUsername string, request *http.Request) (interface{}, gtserror.WithCode) {
|
||||||
// get the account the request is referring to
|
// get the account the request is referring to
|
||||||
requestedAccount := >smodel.Account{}
|
requestedAccount := >smodel.Account{}
|
||||||
if err := p.db.GetLocalAccountByUsername(requestedUsername, requestedAccount); err != nil {
|
if err := p.db.GetLocalAccountByUsername(requestedUsername, requestedAccount); err != nil {
|
||||||
return nil, NewErrorNotFound(fmt.Errorf("database error getting account with username %s: %s", requestedUsername, err))
|
return nil, gtserror.NewErrorNotFound(fmt.Errorf("database error getting account with username %s: %s", requestedUsername, err))
|
||||||
}
|
}
|
||||||
|
|
||||||
// authenticate the request
|
// authenticate the request
|
||||||
requestingAccount, err := p.authenticateAndDereferenceFediRequest(requestedUsername, request)
|
requestingAccount, err := p.authenticateAndDereferenceFediRequest(requestedUsername, request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, NewErrorNotAuthorized(err)
|
return nil, gtserror.NewErrorNotAuthorized(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
blocked, err := p.db.Blocked(requestedAccount.ID, requestingAccount.ID)
|
blocked, err := p.db.Blocked(requestedAccount.ID, requestingAccount.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, NewErrorInternalError(err)
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if blocked {
|
if blocked {
|
||||||
return nil, NewErrorNotAuthorized(fmt.Errorf("block exists between accounts %s and %s", requestedAccount.ID, requestingAccount.ID))
|
return nil, gtserror.NewErrorNotAuthorized(fmt.Errorf("block exists between accounts %s and %s", requestedAccount.ID, requestingAccount.ID))
|
||||||
}
|
}
|
||||||
|
|
||||||
requestedPerson, err := p.tc.AccountToAS(requestedAccount)
|
requestedPerson, err := p.tc.AccountToAS(requestedAccount)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, NewErrorInternalError(err)
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err := streams.Serialize(requestedPerson)
|
data, err := streams.Serialize(requestedPerson)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, NewErrorInternalError(err)
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return data, nil
|
return data, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *processor) GetFediFollowers(requestedUsername string, request *http.Request) (interface{}, ErrorWithCode) {
|
func (p *processor) GetFediFollowers(requestedUsername string, request *http.Request) (interface{}, gtserror.WithCode) {
|
||||||
// get the account the request is referring to
|
// get the account the request is referring to
|
||||||
requestedAccount := >smodel.Account{}
|
requestedAccount := >smodel.Account{}
|
||||||
if err := p.db.GetLocalAccountByUsername(requestedUsername, requestedAccount); err != nil {
|
if err := p.db.GetLocalAccountByUsername(requestedUsername, requestedAccount); err != nil {
|
||||||
return nil, NewErrorNotFound(fmt.Errorf("database error getting account with username %s: %s", requestedUsername, err))
|
return nil, gtserror.NewErrorNotFound(fmt.Errorf("database error getting account with username %s: %s", requestedUsername, err))
|
||||||
}
|
}
|
||||||
|
|
||||||
// authenticate the request
|
// authenticate the request
|
||||||
requestingAccount, err := p.authenticateAndDereferenceFediRequest(requestedUsername, request)
|
requestingAccount, err := p.authenticateAndDereferenceFediRequest(requestedUsername, request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, NewErrorNotAuthorized(err)
|
return nil, gtserror.NewErrorNotAuthorized(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
blocked, err := p.db.Blocked(requestedAccount.ID, requestingAccount.ID)
|
blocked, err := p.db.Blocked(requestedAccount.ID, requestingAccount.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, NewErrorInternalError(err)
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if blocked {
|
if blocked {
|
||||||
return nil, NewErrorNotAuthorized(fmt.Errorf("block exists between accounts %s and %s", requestedAccount.ID, requestingAccount.ID))
|
return nil, gtserror.NewErrorNotAuthorized(fmt.Errorf("block exists between accounts %s and %s", requestedAccount.ID, requestingAccount.ID))
|
||||||
}
|
}
|
||||||
|
|
||||||
requestedAccountURI, err := url.Parse(requestedAccount.URI)
|
requestedAccountURI, err := url.Parse(requestedAccount.URI)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, NewErrorInternalError(fmt.Errorf("error parsing url %s: %s", requestedAccount.URI, err))
|
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error parsing url %s: %s", requestedAccount.URI, err))
|
||||||
}
|
}
|
||||||
|
|
||||||
requestedFollowers, err := p.federator.FederatingDB().Followers(context.Background(), requestedAccountURI)
|
requestedFollowers, err := p.federator.FederatingDB().Followers(context.Background(), requestedAccountURI)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, NewErrorInternalError(fmt.Errorf("error fetching followers for uri %s: %s", requestedAccountURI.String(), err))
|
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error fetching followers for uri %s: %s", requestedAccountURI.String(), err))
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err := streams.Serialize(requestedFollowers)
|
data, err := streams.Serialize(requestedFollowers)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, NewErrorInternalError(err)
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return data, nil
|
return data, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *processor) GetFediFollowing(requestedUsername string, request *http.Request) (interface{}, ErrorWithCode) {
|
func (p *processor) GetFediFollowing(requestedUsername string, request *http.Request) (interface{}, gtserror.WithCode) {
|
||||||
// get the account the request is referring to
|
// get the account the request is referring to
|
||||||
requestedAccount := >smodel.Account{}
|
requestedAccount := >smodel.Account{}
|
||||||
if err := p.db.GetLocalAccountByUsername(requestedUsername, requestedAccount); err != nil {
|
if err := p.db.GetLocalAccountByUsername(requestedUsername, requestedAccount); err != nil {
|
||||||
return nil, NewErrorNotFound(fmt.Errorf("database error getting account with username %s: %s", requestedUsername, err))
|
return nil, gtserror.NewErrorNotFound(fmt.Errorf("database error getting account with username %s: %s", requestedUsername, err))
|
||||||
}
|
}
|
||||||
|
|
||||||
// authenticate the request
|
// authenticate the request
|
||||||
requestingAccount, err := p.authenticateAndDereferenceFediRequest(requestedUsername, request)
|
requestingAccount, err := p.authenticateAndDereferenceFediRequest(requestedUsername, request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, NewErrorNotAuthorized(err)
|
return nil, gtserror.NewErrorNotAuthorized(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
blocked, err := p.db.Blocked(requestedAccount.ID, requestingAccount.ID)
|
blocked, err := p.db.Blocked(requestedAccount.ID, requestingAccount.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, NewErrorInternalError(err)
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if blocked {
|
if blocked {
|
||||||
return nil, NewErrorNotAuthorized(fmt.Errorf("block exists between accounts %s and %s", requestedAccount.ID, requestingAccount.ID))
|
return nil, gtserror.NewErrorNotAuthorized(fmt.Errorf("block exists between accounts %s and %s", requestedAccount.ID, requestingAccount.ID))
|
||||||
}
|
}
|
||||||
|
|
||||||
requestedAccountURI, err := url.Parse(requestedAccount.URI)
|
requestedAccountURI, err := url.Parse(requestedAccount.URI)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, NewErrorInternalError(fmt.Errorf("error parsing url %s: %s", requestedAccount.URI, err))
|
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error parsing url %s: %s", requestedAccount.URI, err))
|
||||||
}
|
}
|
||||||
|
|
||||||
requestedFollowing, err := p.federator.FederatingDB().Following(context.Background(), requestedAccountURI)
|
requestedFollowing, err := p.federator.FederatingDB().Following(context.Background(), requestedAccountURI)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, NewErrorInternalError(fmt.Errorf("error fetching following for uri %s: %s", requestedAccountURI.String(), err))
|
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error fetching following for uri %s: %s", requestedAccountURI.String(), err))
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err := streams.Serialize(requestedFollowing)
|
data, err := streams.Serialize(requestedFollowing)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, NewErrorInternalError(err)
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return data, nil
|
return data, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *processor) GetFediStatus(requestedUsername string, requestedStatusID string, request *http.Request) (interface{}, ErrorWithCode) {
|
func (p *processor) GetFediStatus(requestedUsername string, requestedStatusID string, request *http.Request) (interface{}, gtserror.WithCode) {
|
||||||
// get the account the request is referring to
|
// get the account the request is referring to
|
||||||
requestedAccount := >smodel.Account{}
|
requestedAccount := >smodel.Account{}
|
||||||
if err := p.db.GetLocalAccountByUsername(requestedUsername, requestedAccount); err != nil {
|
if err := p.db.GetLocalAccountByUsername(requestedUsername, requestedAccount); err != nil {
|
||||||
return nil, NewErrorNotFound(fmt.Errorf("database error getting account with username %s: %s", requestedUsername, err))
|
return nil, gtserror.NewErrorNotFound(fmt.Errorf("database error getting account with username %s: %s", requestedUsername, err))
|
||||||
}
|
}
|
||||||
|
|
||||||
// authenticate the request
|
// authenticate the request
|
||||||
requestingAccount, err := p.authenticateAndDereferenceFediRequest(requestedUsername, request)
|
requestingAccount, err := p.authenticateAndDereferenceFediRequest(requestedUsername, request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, NewErrorNotAuthorized(err)
|
return nil, gtserror.NewErrorNotAuthorized(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
blocked, err := p.db.Blocked(requestedAccount.ID, requestingAccount.ID)
|
blocked, err := p.db.Blocked(requestedAccount.ID, requestingAccount.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, NewErrorInternalError(err)
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if blocked {
|
if blocked {
|
||||||
return nil, NewErrorNotAuthorized(fmt.Errorf("block exists between accounts %s and %s", requestedAccount.ID, requestingAccount.ID))
|
return nil, gtserror.NewErrorNotAuthorized(fmt.Errorf("block exists between accounts %s and %s", requestedAccount.ID, requestingAccount.ID))
|
||||||
}
|
}
|
||||||
|
|
||||||
s := >smodel.Status{}
|
s := >smodel.Status{}
|
||||||
@ -230,27 +231,27 @@ func (p *processor) GetFediStatus(requestedUsername string, requestedStatusID st
|
|||||||
{Key: "id", Value: requestedStatusID},
|
{Key: "id", Value: requestedStatusID},
|
||||||
{Key: "account_id", Value: requestedAccount.ID},
|
{Key: "account_id", Value: requestedAccount.ID},
|
||||||
}, s); err != nil {
|
}, s); err != nil {
|
||||||
return nil, NewErrorNotFound(fmt.Errorf("database error getting status with id %s and account id %s: %s", requestedStatusID, requestedAccount.ID, err))
|
return nil, gtserror.NewErrorNotFound(fmt.Errorf("database error getting status with id %s and account id %s: %s", requestedStatusID, requestedAccount.ID, err))
|
||||||
}
|
}
|
||||||
|
|
||||||
asStatus, err := p.tc.StatusToAS(s)
|
asStatus, err := p.tc.StatusToAS(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, NewErrorInternalError(err)
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err := streams.Serialize(asStatus)
|
data, err := streams.Serialize(asStatus)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, NewErrorInternalError(err)
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return data, nil
|
return data, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *processor) GetWebfingerAccount(requestedUsername string, request *http.Request) (*apimodel.WebfingerAccountResponse, ErrorWithCode) {
|
func (p *processor) GetWebfingerAccount(requestedUsername string, request *http.Request) (*apimodel.WebfingerAccountResponse, gtserror.WithCode) {
|
||||||
// get the account the request is referring to
|
// get the account the request is referring to
|
||||||
requestedAccount := >smodel.Account{}
|
requestedAccount := >smodel.Account{}
|
||||||
if err := p.db.GetLocalAccountByUsername(requestedUsername, requestedAccount); err != nil {
|
if err := p.db.GetLocalAccountByUsername(requestedUsername, requestedAccount); err != nil {
|
||||||
return nil, NewErrorNotFound(fmt.Errorf("database error getting account with username %s: %s", requestedUsername, err))
|
return nil, gtserror.NewErrorNotFound(fmt.Errorf("database error getting account with username %s: %s", requestedUsername, err))
|
||||||
}
|
}
|
||||||
|
|
||||||
// return the webfinger representation
|
// return the webfinger representation
|
||||||
|
@ -21,15 +21,16 @@ package processing
|
|||||||
import (
|
import (
|
||||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
"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/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (p *processor) FollowRequestsGet(auth *oauth.Auth) ([]apimodel.Account, ErrorWithCode) {
|
func (p *processor) FollowRequestsGet(auth *oauth.Auth) ([]apimodel.Account, gtserror.WithCode) {
|
||||||
frs := []gtsmodel.FollowRequest{}
|
frs := []gtsmodel.FollowRequest{}
|
||||||
if err := p.db.GetFollowRequestsForAccountID(auth.Account.ID, &frs); err != nil {
|
if err := p.db.GetFollowRequestsForAccountID(auth.Account.ID, &frs); err != nil {
|
||||||
if _, ok := err.(db.ErrNoEntries); !ok {
|
if _, ok := err.(db.ErrNoEntries); !ok {
|
||||||
return nil, NewErrorInternalError(err)
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,31 +38,31 @@ func (p *processor) FollowRequestsGet(auth *oauth.Auth) ([]apimodel.Account, Err
|
|||||||
for _, fr := range frs {
|
for _, fr := range frs {
|
||||||
acct := >smodel.Account{}
|
acct := >smodel.Account{}
|
||||||
if err := p.db.GetByID(fr.AccountID, acct); err != nil {
|
if err := p.db.GetByID(fr.AccountID, acct); err != nil {
|
||||||
return nil, NewErrorInternalError(err)
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
}
|
}
|
||||||
mastoAcct, err := p.tc.AccountToMastoPublic(acct)
|
mastoAcct, err := p.tc.AccountToMastoPublic(acct)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, NewErrorInternalError(err)
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
}
|
}
|
||||||
accts = append(accts, *mastoAcct)
|
accts = append(accts, *mastoAcct)
|
||||||
}
|
}
|
||||||
return accts, nil
|
return accts, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *processor) FollowRequestAccept(auth *oauth.Auth, accountID string) (*apimodel.Relationship, ErrorWithCode) {
|
func (p *processor) FollowRequestAccept(auth *oauth.Auth, accountID string) (*apimodel.Relationship, gtserror.WithCode) {
|
||||||
follow, err := p.db.AcceptFollowRequest(accountID, auth.Account.ID)
|
follow, err := p.db.AcceptFollowRequest(accountID, auth.Account.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, NewErrorNotFound(err)
|
return nil, gtserror.NewErrorNotFound(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
originAccount := >smodel.Account{}
|
originAccount := >smodel.Account{}
|
||||||
if err := p.db.GetByID(follow.AccountID, originAccount); err != nil {
|
if err := p.db.GetByID(follow.AccountID, originAccount); err != nil {
|
||||||
return nil, NewErrorInternalError(err)
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
targetAccount := >smodel.Account{}
|
targetAccount := >smodel.Account{}
|
||||||
if err := p.db.GetByID(follow.TargetAccountID, targetAccount); err != nil {
|
if err := p.db.GetByID(follow.TargetAccountID, targetAccount); err != nil {
|
||||||
return nil, NewErrorInternalError(err)
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
p.fromClientAPI <- gtsmodel.FromClientAPI{
|
p.fromClientAPI <- gtsmodel.FromClientAPI{
|
||||||
@ -74,17 +75,17 @@ func (p *processor) FollowRequestAccept(auth *oauth.Auth, accountID string) (*ap
|
|||||||
|
|
||||||
gtsR, err := p.db.GetRelationship(auth.Account.ID, accountID)
|
gtsR, err := p.db.GetRelationship(auth.Account.ID, accountID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, NewErrorInternalError(err)
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
r, err := p.tc.RelationshipToMasto(gtsR)
|
r, err := p.tc.RelationshipToMasto(gtsR)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, NewErrorInternalError(err)
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return r, nil
|
return r, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *processor) FollowRequestDeny(auth *oauth.Auth) ErrorWithCode {
|
func (p *processor) FollowRequestDeny(auth *oauth.Auth) gtserror.WithCode {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -127,6 +127,13 @@ func (p *processor) processFromClientAPI(clientMsg gtsmodel.FromClientAPI) error
|
|||||||
return errors.New("undo was not parseable as *gtsmodel.Follow")
|
return errors.New("undo was not parseable as *gtsmodel.Follow")
|
||||||
}
|
}
|
||||||
return p.federateUnfollow(follow, clientMsg.OriginAccount, clientMsg.TargetAccount)
|
return p.federateUnfollow(follow, clientMsg.OriginAccount, clientMsg.TargetAccount)
|
||||||
|
case gtsmodel.ActivityStreamsLike:
|
||||||
|
// UNDO LIKE/FAVE
|
||||||
|
fave, ok := clientMsg.GTSModel.(*gtsmodel.StatusFave)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("undo was not parseable as *gtsmodel.StatusFave")
|
||||||
|
}
|
||||||
|
return p.federateUnfave(fave, clientMsg.OriginAccount, clientMsg.TargetAccount)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@ -210,6 +217,45 @@ func (p *processor) federateUnfollow(follow *gtsmodel.Follow, originAccount *gts
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *processor) federateUnfave(fave *gtsmodel.StatusFave, originAccount *gtsmodel.Account, targetAccount *gtsmodel.Account) error {
|
||||||
|
// if both accounts are local there's nothing to do here
|
||||||
|
if originAccount.Domain == "" && targetAccount.Domain == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// create the AS fave
|
||||||
|
asFave, err := p.tc.FaveToAS(fave)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("federateFave: error converting fave to as format: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
targetAccountURI, err := url.Parse(targetAccount.URI)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error parsing uri %s: %s", targetAccount.URI, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// create an Undo and set the appropriate actor on it
|
||||||
|
undo := streams.NewActivityStreamsUndo()
|
||||||
|
undo.SetActivityStreamsActor(asFave.GetActivityStreamsActor())
|
||||||
|
|
||||||
|
// Set the fave as the 'object' property.
|
||||||
|
undoObject := streams.NewActivityStreamsObjectProperty()
|
||||||
|
undoObject.AppendActivityStreamsLike(asFave)
|
||||||
|
undo.SetActivityStreamsObject(undoObject)
|
||||||
|
|
||||||
|
// Set the To of the undo as the target of the fave
|
||||||
|
undoTo := streams.NewActivityStreamsToProperty()
|
||||||
|
undoTo.AppendIRI(targetAccountURI)
|
||||||
|
undo.SetActivityStreamsTo(undoTo)
|
||||||
|
|
||||||
|
outboxIRI, err := url.Parse(originAccount.OutboxURI)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("federateFave: error parsing outboxURI %s: %s", originAccount.OutboxURI, err)
|
||||||
|
}
|
||||||
|
_, err = p.federator.FederatingActor().Send(context.Background(), outboxIRI, undo)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
func (p *processor) federateAcceptFollowRequest(follow *gtsmodel.Follow, originAccount *gtsmodel.Account, targetAccount *gtsmodel.Account) error {
|
func (p *processor) federateAcceptFollowRequest(follow *gtsmodel.Follow, originAccount *gtsmodel.Account, targetAccount *gtsmodel.Account) error {
|
||||||
// if both accounts are local there's nothing to do here
|
// if both accounts are local there's nothing to do here
|
||||||
if originAccount.Domain == "" && targetAccount.Domain == "" {
|
if originAccount.Domain == "" && targetAccount.Domain == "" {
|
||||||
|
@ -300,3 +300,7 @@ func (p *processor) timelineStatusForAccount(status *gtsmodel.Status, accountID
|
|||||||
errors <- fmt.Errorf("initTimelineFor: error ingesting status %s: %s", status.ID, err)
|
errors <- fmt.Errorf("initTimelineFor: error ingesting status %s: %s", status.ID, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *processor) fullyDeleteStatus(status *gtsmodel.Status, accountID string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -23,18 +23,19 @@ import (
|
|||||||
|
|
||||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
"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"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (p *processor) InstanceGet(domain string) (*apimodel.Instance, ErrorWithCode) {
|
func (p *processor) InstanceGet(domain string) (*apimodel.Instance, gtserror.WithCode) {
|
||||||
i := >smodel.Instance{}
|
i := >smodel.Instance{}
|
||||||
if err := p.db.GetWhere([]db.Where{{Key: "domain", Value: domain}}, i); err != nil {
|
if err := p.db.GetWhere([]db.Where{{Key: "domain", Value: domain}}, i); err != nil {
|
||||||
return nil, NewErrorInternalError(fmt.Errorf("db error fetching instance %s: %s", p.config.Host, err))
|
return nil, gtserror.NewErrorInternalError(fmt.Errorf("db error fetching instance %s: %s", p.config.Host, err))
|
||||||
}
|
}
|
||||||
|
|
||||||
ai, err := p.tc.InstanceToMasto(i)
|
ai, err := p.tc.InstanceToMasto(i)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, NewErrorInternalError(fmt.Errorf("error converting instance to api representation: %s", err))
|
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting instance to api representation: %s", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
return ai, nil
|
return ai, nil
|
||||||
|
@ -28,6 +28,7 @@ import (
|
|||||||
|
|
||||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
"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/media"
|
"github.com/superseriousbusiness/gotosocial/internal/media"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
@ -92,64 +93,64 @@ func (p *processor) MediaCreate(authed *oauth.Auth, form *apimodel.AttachmentReq
|
|||||||
return &mastoAttachment, nil
|
return &mastoAttachment, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *processor) MediaGet(authed *oauth.Auth, mediaAttachmentID string) (*apimodel.Attachment, ErrorWithCode) {
|
func (p *processor) MediaGet(authed *oauth.Auth, mediaAttachmentID string) (*apimodel.Attachment, gtserror.WithCode) {
|
||||||
attachment := >smodel.MediaAttachment{}
|
attachment := >smodel.MediaAttachment{}
|
||||||
if err := p.db.GetByID(mediaAttachmentID, attachment); err != nil {
|
if err := p.db.GetByID(mediaAttachmentID, attachment); err != nil {
|
||||||
if _, ok := err.(db.ErrNoEntries); ok {
|
if _, ok := err.(db.ErrNoEntries); ok {
|
||||||
// attachment doesn't exist
|
// attachment doesn't exist
|
||||||
return nil, NewErrorNotFound(errors.New("attachment doesn't exist in the db"))
|
return nil, gtserror.NewErrorNotFound(errors.New("attachment doesn't exist in the db"))
|
||||||
}
|
}
|
||||||
return nil, NewErrorNotFound(fmt.Errorf("db error getting attachment: %s", err))
|
return nil, gtserror.NewErrorNotFound(fmt.Errorf("db error getting attachment: %s", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
if attachment.AccountID != authed.Account.ID {
|
if attachment.AccountID != authed.Account.ID {
|
||||||
return nil, NewErrorNotFound(errors.New("attachment not owned by requesting account"))
|
return nil, gtserror.NewErrorNotFound(errors.New("attachment not owned by requesting account"))
|
||||||
}
|
}
|
||||||
|
|
||||||
a, err := p.tc.AttachmentToMasto(attachment)
|
a, err := p.tc.AttachmentToMasto(attachment)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, NewErrorNotFound(fmt.Errorf("error converting attachment: %s", err))
|
return nil, gtserror.NewErrorNotFound(fmt.Errorf("error converting attachment: %s", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
return &a, nil
|
return &a, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *processor) MediaUpdate(authed *oauth.Auth, mediaAttachmentID string, form *apimodel.AttachmentUpdateRequest) (*apimodel.Attachment, ErrorWithCode) {
|
func (p *processor) MediaUpdate(authed *oauth.Auth, mediaAttachmentID string, form *apimodel.AttachmentUpdateRequest) (*apimodel.Attachment, gtserror.WithCode) {
|
||||||
attachment := >smodel.MediaAttachment{}
|
attachment := >smodel.MediaAttachment{}
|
||||||
if err := p.db.GetByID(mediaAttachmentID, attachment); err != nil {
|
if err := p.db.GetByID(mediaAttachmentID, attachment); err != nil {
|
||||||
if _, ok := err.(db.ErrNoEntries); ok {
|
if _, ok := err.(db.ErrNoEntries); ok {
|
||||||
// attachment doesn't exist
|
// attachment doesn't exist
|
||||||
return nil, NewErrorNotFound(errors.New("attachment doesn't exist in the db"))
|
return nil, gtserror.NewErrorNotFound(errors.New("attachment doesn't exist in the db"))
|
||||||
}
|
}
|
||||||
return nil, NewErrorNotFound(fmt.Errorf("db error getting attachment: %s", err))
|
return nil, gtserror.NewErrorNotFound(fmt.Errorf("db error getting attachment: %s", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
if attachment.AccountID != authed.Account.ID {
|
if attachment.AccountID != authed.Account.ID {
|
||||||
return nil, NewErrorNotFound(errors.New("attachment not owned by requesting account"))
|
return nil, gtserror.NewErrorNotFound(errors.New("attachment not owned by requesting account"))
|
||||||
}
|
}
|
||||||
|
|
||||||
if form.Description != nil {
|
if form.Description != nil {
|
||||||
attachment.Description = *form.Description
|
attachment.Description = *form.Description
|
||||||
if err := p.db.UpdateByID(mediaAttachmentID, attachment); err != nil {
|
if err := p.db.UpdateByID(mediaAttachmentID, attachment); err != nil {
|
||||||
return nil, NewErrorInternalError(fmt.Errorf("database error updating description: %s", err))
|
return nil, gtserror.NewErrorInternalError(fmt.Errorf("database error updating description: %s", err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if form.Focus != nil {
|
if form.Focus != nil {
|
||||||
focusx, focusy, err := parseFocus(*form.Focus)
|
focusx, focusy, err := parseFocus(*form.Focus)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, NewErrorBadRequest(err)
|
return nil, gtserror.NewErrorBadRequest(err)
|
||||||
}
|
}
|
||||||
attachment.FileMeta.Focus.X = focusx
|
attachment.FileMeta.Focus.X = focusx
|
||||||
attachment.FileMeta.Focus.Y = focusy
|
attachment.FileMeta.Focus.Y = focusy
|
||||||
if err := p.db.UpdateByID(mediaAttachmentID, attachment); err != nil {
|
if err := p.db.UpdateByID(mediaAttachmentID, attachment); err != nil {
|
||||||
return nil, NewErrorInternalError(fmt.Errorf("database error updating focus: %s", err))
|
return nil, gtserror.NewErrorInternalError(fmt.Errorf("database error updating focus: %s", err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
a, err := p.tc.AttachmentToMasto(attachment)
|
a, err := p.tc.AttachmentToMasto(attachment)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, NewErrorNotFound(fmt.Errorf("error converting attachment: %s", err))
|
return nil, gtserror.NewErrorNotFound(fmt.Errorf("error converting attachment: %s", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
return &a, nil
|
return &a, nil
|
||||||
@ -159,37 +160,37 @@ func (p *processor) FileGet(authed *oauth.Auth, form *apimodel.GetContentRequest
|
|||||||
// parse the form fields
|
// parse the form fields
|
||||||
mediaSize, err := media.ParseMediaSize(form.MediaSize)
|
mediaSize, err := media.ParseMediaSize(form.MediaSize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, NewErrorNotFound(fmt.Errorf("media size %s not valid", form.MediaSize))
|
return nil, gtserror.NewErrorNotFound(fmt.Errorf("media size %s not valid", form.MediaSize))
|
||||||
}
|
}
|
||||||
|
|
||||||
mediaType, err := media.ParseMediaType(form.MediaType)
|
mediaType, err := media.ParseMediaType(form.MediaType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, NewErrorNotFound(fmt.Errorf("media type %s not valid", form.MediaType))
|
return nil, gtserror.NewErrorNotFound(fmt.Errorf("media type %s not valid", form.MediaType))
|
||||||
}
|
}
|
||||||
|
|
||||||
spl := strings.Split(form.FileName, ".")
|
spl := strings.Split(form.FileName, ".")
|
||||||
if len(spl) != 2 || spl[0] == "" || spl[1] == "" {
|
if len(spl) != 2 || spl[0] == "" || spl[1] == "" {
|
||||||
return nil, NewErrorNotFound(fmt.Errorf("file name %s not parseable", form.FileName))
|
return nil, gtserror.NewErrorNotFound(fmt.Errorf("file name %s not parseable", form.FileName))
|
||||||
}
|
}
|
||||||
wantedMediaID := spl[0]
|
wantedMediaID := spl[0]
|
||||||
|
|
||||||
// get the account that owns the media and make sure it's not suspended
|
// get the account that owns the media and make sure it's not suspended
|
||||||
acct := >smodel.Account{}
|
acct := >smodel.Account{}
|
||||||
if err := p.db.GetByID(form.AccountID, acct); err != nil {
|
if err := p.db.GetByID(form.AccountID, acct); err != nil {
|
||||||
return nil, NewErrorNotFound(fmt.Errorf("account with id %s could not be selected from the db: %s", form.AccountID, err))
|
return nil, gtserror.NewErrorNotFound(fmt.Errorf("account with id %s could not be selected from the db: %s", form.AccountID, err))
|
||||||
}
|
}
|
||||||
if !acct.SuspendedAt.IsZero() {
|
if !acct.SuspendedAt.IsZero() {
|
||||||
return nil, NewErrorNotFound(fmt.Errorf("account with id %s is suspended", form.AccountID))
|
return nil, gtserror.NewErrorNotFound(fmt.Errorf("account with id %s is suspended", form.AccountID))
|
||||||
}
|
}
|
||||||
|
|
||||||
// make sure the requesting account and the media account don't block each other
|
// make sure the requesting account and the media account don't block each other
|
||||||
if authed.Account != nil {
|
if authed.Account != nil {
|
||||||
blocked, err := p.db.Blocked(authed.Account.ID, form.AccountID)
|
blocked, err := p.db.Blocked(authed.Account.ID, form.AccountID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, NewErrorNotFound(fmt.Errorf("block status could not be established between accounts %s and %s: %s", form.AccountID, authed.Account.ID, err))
|
return nil, gtserror.NewErrorNotFound(fmt.Errorf("block status could not be established between accounts %s and %s: %s", form.AccountID, authed.Account.ID, err))
|
||||||
}
|
}
|
||||||
if blocked {
|
if blocked {
|
||||||
return nil, NewErrorNotFound(fmt.Errorf("block exists between accounts %s and %s", form.AccountID, authed.Account.ID))
|
return nil, gtserror.NewErrorNotFound(fmt.Errorf("block exists between accounts %s and %s", form.AccountID, authed.Account.ID))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -201,10 +202,10 @@ func (p *processor) FileGet(authed *oauth.Auth, form *apimodel.GetContentRequest
|
|||||||
case media.Emoji:
|
case media.Emoji:
|
||||||
e := >smodel.Emoji{}
|
e := >smodel.Emoji{}
|
||||||
if err := p.db.GetByID(wantedMediaID, e); err != nil {
|
if err := p.db.GetByID(wantedMediaID, e); err != nil {
|
||||||
return nil, NewErrorNotFound(fmt.Errorf("emoji %s could not be taken from the db: %s", wantedMediaID, err))
|
return nil, gtserror.NewErrorNotFound(fmt.Errorf("emoji %s could not be taken from the db: %s", wantedMediaID, err))
|
||||||
}
|
}
|
||||||
if e.Disabled {
|
if e.Disabled {
|
||||||
return nil, NewErrorNotFound(fmt.Errorf("emoji %s has been disabled", wantedMediaID))
|
return nil, gtserror.NewErrorNotFound(fmt.Errorf("emoji %s has been disabled", wantedMediaID))
|
||||||
}
|
}
|
||||||
switch mediaSize {
|
switch mediaSize {
|
||||||
case media.Original:
|
case media.Original:
|
||||||
@ -214,15 +215,15 @@ func (p *processor) FileGet(authed *oauth.Auth, form *apimodel.GetContentRequest
|
|||||||
content.ContentType = e.ImageStaticContentType
|
content.ContentType = e.ImageStaticContentType
|
||||||
storagePath = e.ImageStaticPath
|
storagePath = e.ImageStaticPath
|
||||||
default:
|
default:
|
||||||
return nil, NewErrorNotFound(fmt.Errorf("media size %s not recognized for emoji", mediaSize))
|
return nil, gtserror.NewErrorNotFound(fmt.Errorf("media size %s not recognized for emoji", mediaSize))
|
||||||
}
|
}
|
||||||
case media.Attachment, media.Header, media.Avatar:
|
case media.Attachment, media.Header, media.Avatar:
|
||||||
a := >smodel.MediaAttachment{}
|
a := >smodel.MediaAttachment{}
|
||||||
if err := p.db.GetByID(wantedMediaID, a); err != nil {
|
if err := p.db.GetByID(wantedMediaID, a); err != nil {
|
||||||
return nil, NewErrorNotFound(fmt.Errorf("attachment %s could not be taken from the db: %s", wantedMediaID, err))
|
return nil, gtserror.NewErrorNotFound(fmt.Errorf("attachment %s could not be taken from the db: %s", wantedMediaID, err))
|
||||||
}
|
}
|
||||||
if a.AccountID != form.AccountID {
|
if a.AccountID != form.AccountID {
|
||||||
return nil, NewErrorNotFound(fmt.Errorf("attachment %s is not owned by %s", wantedMediaID, form.AccountID))
|
return nil, gtserror.NewErrorNotFound(fmt.Errorf("attachment %s is not owned by %s", wantedMediaID, form.AccountID))
|
||||||
}
|
}
|
||||||
switch mediaSize {
|
switch mediaSize {
|
||||||
case media.Original:
|
case media.Original:
|
||||||
@ -232,13 +233,13 @@ func (p *processor) FileGet(authed *oauth.Auth, form *apimodel.GetContentRequest
|
|||||||
content.ContentType = a.Thumbnail.ContentType
|
content.ContentType = a.Thumbnail.ContentType
|
||||||
storagePath = a.Thumbnail.Path
|
storagePath = a.Thumbnail.Path
|
||||||
default:
|
default:
|
||||||
return nil, NewErrorNotFound(fmt.Errorf("media size %s not recognized for attachment", mediaSize))
|
return nil, gtserror.NewErrorNotFound(fmt.Errorf("media size %s not recognized for attachment", mediaSize))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes, err := p.storage.RetrieveFileFrom(storagePath)
|
bytes, err := p.storage.RetrieveFileFrom(storagePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, NewErrorNotFound(fmt.Errorf("error retrieving from storage: %s", err))
|
return nil, gtserror.NewErrorNotFound(fmt.Errorf("error retrieving from storage: %s", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
content.ContentLength = int64(len(bytes))
|
content.ContentLength = int64(len(bytes))
|
||||||
|
@ -20,15 +20,16 @@ package processing
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (p *processor) NotificationsGet(authed *oauth.Auth, limit int, maxID string, sinceID string) ([]*apimodel.Notification, ErrorWithCode) {
|
func (p *processor) NotificationsGet(authed *oauth.Auth, limit int, maxID string, sinceID string) ([]*apimodel.Notification, gtserror.WithCode) {
|
||||||
l := p.log.WithField("func", "NotificationsGet")
|
l := p.log.WithField("func", "NotificationsGet")
|
||||||
|
|
||||||
notifs, err := p.db.GetNotificationsForAccount(authed.Account.ID, limit, maxID, sinceID)
|
notifs, err := p.db.GetNotificationsForAccount(authed.Account.ID, limit, maxID, sinceID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, NewErrorInternalError(err)
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
mastoNotifs := []*apimodel.Notification{}
|
mastoNotifs := []*apimodel.Notification{}
|
||||||
|
@ -28,9 +28,11 @@ import (
|
|||||||
"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/federation"
|
"github.com/superseriousbusiness/gotosocial/internal/federation"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/media"
|
"github.com/superseriousbusiness/gotosocial/internal/media"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/processing/status"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/processing/timeline"
|
"github.com/superseriousbusiness/gotosocial/internal/processing/timeline"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||||
)
|
)
|
||||||
@ -71,17 +73,17 @@ type Processor interface {
|
|||||||
AccountUpdate(authed *oauth.Auth, form *apimodel.UpdateCredentialsRequest) (*apimodel.Account, error)
|
AccountUpdate(authed *oauth.Auth, form *apimodel.UpdateCredentialsRequest) (*apimodel.Account, error)
|
||||||
// AccountStatusesGet fetches a number of statuses (in time descending order) from the given account, filtered by visibility for
|
// AccountStatusesGet fetches a number of statuses (in time descending order) from the given account, filtered by visibility for
|
||||||
// the account given in authed.
|
// the account given in authed.
|
||||||
AccountStatusesGet(authed *oauth.Auth, targetAccountID string, limit int, excludeReplies bool, maxID string, pinned bool, mediaOnly bool) ([]apimodel.Status, ErrorWithCode)
|
AccountStatusesGet(authed *oauth.Auth, targetAccountID string, limit int, excludeReplies bool, maxID string, pinned bool, mediaOnly bool) ([]apimodel.Status, gtserror.WithCode)
|
||||||
// AccountFollowersGet fetches a list of the target account's followers.
|
// AccountFollowersGet fetches a list of the target account's followers.
|
||||||
AccountFollowersGet(authed *oauth.Auth, targetAccountID string) ([]apimodel.Account, ErrorWithCode)
|
AccountFollowersGet(authed *oauth.Auth, targetAccountID string) ([]apimodel.Account, gtserror.WithCode)
|
||||||
// AccountFollowingGet fetches a list of the accounts that target account is following.
|
// AccountFollowingGet fetches a list of the accounts that target account is following.
|
||||||
AccountFollowingGet(authed *oauth.Auth, targetAccountID string) ([]apimodel.Account, ErrorWithCode)
|
AccountFollowingGet(authed *oauth.Auth, targetAccountID string) ([]apimodel.Account, gtserror.WithCode)
|
||||||
// AccountRelationshipGet returns a relationship model describing the relationship of the targetAccount to the Authed account.
|
// AccountRelationshipGet returns a relationship model describing the relationship of the targetAccount to the Authed account.
|
||||||
AccountRelationshipGet(authed *oauth.Auth, targetAccountID string) (*apimodel.Relationship, ErrorWithCode)
|
AccountRelationshipGet(authed *oauth.Auth, targetAccountID string) (*apimodel.Relationship, gtserror.WithCode)
|
||||||
// AccountFollowCreate handles a follow request to an account, either remote or local.
|
// AccountFollowCreate handles a follow request to an account, either remote or local.
|
||||||
AccountFollowCreate(authed *oauth.Auth, form *apimodel.AccountFollowRequest) (*apimodel.Relationship, ErrorWithCode)
|
AccountFollowCreate(authed *oauth.Auth, form *apimodel.AccountFollowRequest) (*apimodel.Relationship, gtserror.WithCode)
|
||||||
// AccountFollowRemove handles the removal of a follow/follow request to an account, either remote or local.
|
// AccountFollowRemove handles the removal of a follow/follow request to an account, either remote or local.
|
||||||
AccountFollowRemove(authed *oauth.Auth, targetAccountID string) (*apimodel.Relationship, ErrorWithCode)
|
AccountFollowRemove(authed *oauth.Auth, targetAccountID string) (*apimodel.Relationship, gtserror.WithCode)
|
||||||
|
|
||||||
// AdminEmojiCreate handles the creation of a new instance emoji by an admin, using the given form.
|
// AdminEmojiCreate handles the creation of a new instance emoji by an admin, using the given form.
|
||||||
AdminEmojiCreate(authed *oauth.Auth, form *apimodel.EmojiCreateRequest) (*apimodel.Emoji, error)
|
AdminEmojiCreate(authed *oauth.Auth, form *apimodel.EmojiCreateRequest) (*apimodel.Emoji, error)
|
||||||
@ -93,25 +95,25 @@ type Processor interface {
|
|||||||
FileGet(authed *oauth.Auth, form *apimodel.GetContentRequestForm) (*apimodel.Content, error)
|
FileGet(authed *oauth.Auth, form *apimodel.GetContentRequestForm) (*apimodel.Content, error)
|
||||||
|
|
||||||
// FollowRequestsGet handles the getting of the authed account's incoming follow requests
|
// FollowRequestsGet handles the getting of the authed account's incoming follow requests
|
||||||
FollowRequestsGet(auth *oauth.Auth) ([]apimodel.Account, ErrorWithCode)
|
FollowRequestsGet(auth *oauth.Auth) ([]apimodel.Account, gtserror.WithCode)
|
||||||
// FollowRequestAccept handles the acceptance of a follow request from the given account ID
|
// FollowRequestAccept handles the acceptance of a follow request from the given account ID
|
||||||
FollowRequestAccept(auth *oauth.Auth, accountID string) (*apimodel.Relationship, ErrorWithCode)
|
FollowRequestAccept(auth *oauth.Auth, accountID string) (*apimodel.Relationship, gtserror.WithCode)
|
||||||
|
|
||||||
// InstanceGet retrieves instance information for serving at api/v1/instance
|
// InstanceGet retrieves instance information for serving at api/v1/instance
|
||||||
InstanceGet(domain string) (*apimodel.Instance, ErrorWithCode)
|
InstanceGet(domain string) (*apimodel.Instance, gtserror.WithCode)
|
||||||
|
|
||||||
// MediaCreate handles the creation of a media attachment, using the given form.
|
// MediaCreate handles the creation of a media attachment, using the given form.
|
||||||
MediaCreate(authed *oauth.Auth, form *apimodel.AttachmentRequest) (*apimodel.Attachment, error)
|
MediaCreate(authed *oauth.Auth, form *apimodel.AttachmentRequest) (*apimodel.Attachment, error)
|
||||||
// MediaGet handles the GET of a media attachment with the given ID
|
// MediaGet handles the GET of a media attachment with the given ID
|
||||||
MediaGet(authed *oauth.Auth, attachmentID string) (*apimodel.Attachment, ErrorWithCode)
|
MediaGet(authed *oauth.Auth, attachmentID string) (*apimodel.Attachment, gtserror.WithCode)
|
||||||
// MediaUpdate handles the PUT of a media attachment with the given ID and form
|
// MediaUpdate handles the PUT of a media attachment with the given ID and form
|
||||||
MediaUpdate(authed *oauth.Auth, attachmentID string, form *apimodel.AttachmentUpdateRequest) (*apimodel.Attachment, ErrorWithCode)
|
MediaUpdate(authed *oauth.Auth, attachmentID string, form *apimodel.AttachmentUpdateRequest) (*apimodel.Attachment, gtserror.WithCode)
|
||||||
|
|
||||||
// NotificationsGet
|
// NotificationsGet
|
||||||
NotificationsGet(authed *oauth.Auth, limit int, maxID string, sinceID string) ([]*apimodel.Notification, ErrorWithCode)
|
NotificationsGet(authed *oauth.Auth, limit int, maxID string, sinceID string) ([]*apimodel.Notification, gtserror.WithCode)
|
||||||
|
|
||||||
// SearchGet performs a search with the given params, resolving/dereferencing remotely as desired
|
// SearchGet performs a search with the given params, resolving/dereferencing remotely as desired
|
||||||
SearchGet(authed *oauth.Auth, searchQuery *apimodel.SearchQuery) (*apimodel.SearchResult, ErrorWithCode)
|
SearchGet(authed *oauth.Auth, searchQuery *apimodel.SearchQuery) (*apimodel.SearchResult, gtserror.WithCode)
|
||||||
|
|
||||||
// StatusCreate processes the given form to create a new status, returning the api model representation of that status if it's OK.
|
// StatusCreate processes the given form to create a new status, returning the api model representation of that status if it's OK.
|
||||||
StatusCreate(authed *oauth.Auth, form *apimodel.AdvancedStatusCreateForm) (*apimodel.Status, error)
|
StatusCreate(authed *oauth.Auth, form *apimodel.AdvancedStatusCreateForm) (*apimodel.Status, error)
|
||||||
@ -120,9 +122,9 @@ type Processor interface {
|
|||||||
// StatusFave processes the faving of a given status, returning the updated status if the fave goes through.
|
// StatusFave processes the faving of a given status, returning the updated status if the fave goes through.
|
||||||
StatusFave(authed *oauth.Auth, targetStatusID string) (*apimodel.Status, error)
|
StatusFave(authed *oauth.Auth, targetStatusID string) (*apimodel.Status, error)
|
||||||
// StatusBoost processes the boost/reblog of a given status, returning the newly-created boost if all is well.
|
// StatusBoost processes the boost/reblog of a given status, returning the newly-created boost if all is well.
|
||||||
StatusBoost(authed *oauth.Auth, targetStatusID string) (*apimodel.Status, ErrorWithCode)
|
StatusBoost(authed *oauth.Auth, targetStatusID string) (*apimodel.Status, gtserror.WithCode)
|
||||||
// StatusBoostedBy returns a slice of accounts that have boosted the given status, filtered according to privacy settings.
|
// StatusBoostedBy returns a slice of accounts that have boosted the given status, filtered according to privacy settings.
|
||||||
StatusBoostedBy(authed *oauth.Auth, targetStatusID string) ([]*apimodel.Account, ErrorWithCode)
|
StatusBoostedBy(authed *oauth.Auth, targetStatusID string) ([]*apimodel.Account, gtserror.WithCode)
|
||||||
// StatusFavedBy returns a slice of accounts that have liked the given status, filtered according to privacy settings.
|
// StatusFavedBy returns a slice of accounts that have liked the given status, filtered according to privacy settings.
|
||||||
StatusFavedBy(authed *oauth.Auth, targetStatusID string) ([]*apimodel.Account, error)
|
StatusFavedBy(authed *oauth.Auth, targetStatusID string) ([]*apimodel.Account, error)
|
||||||
// StatusGet gets the given status, taking account of privacy settings and blocks etc.
|
// StatusGet gets the given status, taking account of privacy settings and blocks etc.
|
||||||
@ -130,12 +132,12 @@ type Processor interface {
|
|||||||
// StatusUnfave processes the unfaving of a given status, returning the updated status if the fave goes through.
|
// StatusUnfave processes the unfaving of a given status, returning the updated status if the fave goes through.
|
||||||
StatusUnfave(authed *oauth.Auth, targetStatusID string) (*apimodel.Status, error)
|
StatusUnfave(authed *oauth.Auth, targetStatusID string) (*apimodel.Status, error)
|
||||||
// StatusGetContext returns the context (previous and following posts) from the given status ID
|
// StatusGetContext returns the context (previous and following posts) from the given status ID
|
||||||
StatusGetContext(authed *oauth.Auth, targetStatusID string) (*apimodel.Context, ErrorWithCode)
|
StatusGetContext(authed *oauth.Auth, targetStatusID string) (*apimodel.Context, gtserror.WithCode)
|
||||||
|
|
||||||
// HomeTimelineGet returns statuses from the home timeline, with the given filters/parameters.
|
// HomeTimelineGet returns statuses from the home timeline, with the given filters/parameters.
|
||||||
HomeTimelineGet(authed *oauth.Auth, maxID string, sinceID string, minID string, limit int, local bool) ([]*apimodel.Status, ErrorWithCode)
|
HomeTimelineGet(authed *oauth.Auth, maxID string, sinceID string, minID string, limit int, local bool) ([]*apimodel.Status, gtserror.WithCode)
|
||||||
// PublicTimelineGet returns statuses from the public/local timeline, with the given filters/parameters.
|
// PublicTimelineGet returns statuses from the public/local timeline, with the given filters/parameters.
|
||||||
PublicTimelineGet(authed *oauth.Auth, maxID string, sinceID string, minID string, limit int, local bool) ([]*apimodel.Status, ErrorWithCode)
|
PublicTimelineGet(authed *oauth.Auth, maxID string, sinceID string, minID string, limit int, local bool) ([]*apimodel.Status, gtserror.WithCode)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
FEDERATION API-FACING PROCESSING FUNCTIONS
|
FEDERATION API-FACING PROCESSING FUNCTIONS
|
||||||
@ -147,22 +149,22 @@ type Processor interface {
|
|||||||
|
|
||||||
// GetFediUser handles the getting of a fedi/activitypub representation of a user/account, performing appropriate authentication
|
// GetFediUser handles the getting of a fedi/activitypub representation of a user/account, performing appropriate authentication
|
||||||
// before returning a JSON serializable interface to the caller.
|
// before returning a JSON serializable interface to the caller.
|
||||||
GetFediUser(requestedUsername string, request *http.Request) (interface{}, ErrorWithCode)
|
GetFediUser(requestedUsername string, request *http.Request) (interface{}, gtserror.WithCode)
|
||||||
|
|
||||||
// GetFediFollowers handles the getting of a fedi/activitypub representation of a user/account's followers, performing appropriate
|
// GetFediFollowers handles the getting of a fedi/activitypub representation of a user/account's followers, performing appropriate
|
||||||
// authentication before returning a JSON serializable interface to the caller.
|
// authentication before returning a JSON serializable interface to the caller.
|
||||||
GetFediFollowers(requestedUsername string, request *http.Request) (interface{}, ErrorWithCode)
|
GetFediFollowers(requestedUsername string, request *http.Request) (interface{}, gtserror.WithCode)
|
||||||
|
|
||||||
// GetFediFollowing handles the getting of a fedi/activitypub representation of a user/account's following, performing appropriate
|
// GetFediFollowing handles the getting of a fedi/activitypub representation of a user/account's following, performing appropriate
|
||||||
// authentication before returning a JSON serializable interface to the caller.
|
// authentication before returning a JSON serializable interface to the caller.
|
||||||
GetFediFollowing(requestedUsername string, request *http.Request) (interface{}, ErrorWithCode)
|
GetFediFollowing(requestedUsername string, request *http.Request) (interface{}, gtserror.WithCode)
|
||||||
|
|
||||||
// GetFediStatus handles the getting of a fedi/activitypub representation of a particular status, performing appropriate
|
// GetFediStatus handles the getting of a fedi/activitypub representation of a particular status, performing appropriate
|
||||||
// authentication before returning a JSON serializable interface to the caller.
|
// authentication before returning a JSON serializable interface to the caller.
|
||||||
GetFediStatus(requestedUsername string, requestedStatusID string, request *http.Request) (interface{}, ErrorWithCode)
|
GetFediStatus(requestedUsername string, requestedStatusID string, request *http.Request) (interface{}, gtserror.WithCode)
|
||||||
|
|
||||||
// GetWebfingerAccount handles the GET for a webfinger resource. Most commonly, it will be used for returning account lookups.
|
// GetWebfingerAccount handles the GET for a webfinger resource. Most commonly, it will be used for returning account lookups.
|
||||||
GetWebfingerAccount(requestedUsername string, request *http.Request) (*apimodel.WebfingerAccountResponse, ErrorWithCode)
|
GetWebfingerAccount(requestedUsername string, request *http.Request) (*apimodel.WebfingerAccountResponse, gtserror.WithCode)
|
||||||
|
|
||||||
// InboxPost handles POST requests to a user's inbox for new activitypub messages.
|
// InboxPost handles POST requests to a user's inbox for new activitypub messages.
|
||||||
//
|
//
|
||||||
@ -179,10 +181,7 @@ type Processor interface {
|
|||||||
|
|
||||||
// processor just implements the Processor interface
|
// processor just implements the Processor interface
|
||||||
type processor struct {
|
type processor struct {
|
||||||
// federator pub.FederatingActor
|
fromClientAPI chan gtsmodel.FromClientAPI
|
||||||
// toClientAPI chan gtsmodel.ToClientAPI
|
|
||||||
fromClientAPI chan gtsmodel.FromClientAPI
|
|
||||||
// toFederator chan gtsmodel.ToFederator
|
|
||||||
fromFederator chan gtsmodel.FromFederator
|
fromFederator chan gtsmodel.FromFederator
|
||||||
federator federation.Federator
|
federator federation.Federator
|
||||||
stop chan interface{}
|
stop chan interface{}
|
||||||
@ -194,15 +193,25 @@ type processor struct {
|
|||||||
storage blob.Storage
|
storage blob.Storage
|
||||||
timelineManager timeline.Manager
|
timelineManager timeline.Manager
|
||||||
db db.DB
|
db db.DB
|
||||||
|
|
||||||
|
/*
|
||||||
|
SUB-PROCESSORS
|
||||||
|
*/
|
||||||
|
|
||||||
|
statusProcessor status.Processor
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewProcessor returns a new Processor that uses the given federator and logger
|
// NewProcessor returns a new Processor that uses the given federator and logger
|
||||||
func NewProcessor(config *config.Config, tc typeutils.TypeConverter, federator federation.Federator, oauthServer oauth.Server, mediaHandler media.Handler, storage blob.Storage, timelineManager timeline.Manager, db db.DB, log *logrus.Logger) Processor {
|
func NewProcessor(config *config.Config, tc typeutils.TypeConverter, federator federation.Federator, oauthServer oauth.Server, mediaHandler media.Handler, storage blob.Storage, timelineManager timeline.Manager, db db.DB, log *logrus.Logger) Processor {
|
||||||
|
|
||||||
|
fromClientAPI := make(chan gtsmodel.FromClientAPI, 1000)
|
||||||
|
fromFederator := make(chan gtsmodel.FromFederator, 1000)
|
||||||
|
|
||||||
|
statusProcessor := status.New(db, tc, config, fromClientAPI, log)
|
||||||
|
|
||||||
return &processor{
|
return &processor{
|
||||||
// toClientAPI: make(chan gtsmodel.ToClientAPI, 100),
|
fromClientAPI: fromClientAPI,
|
||||||
fromClientAPI: make(chan gtsmodel.FromClientAPI, 100),
|
fromFederator: fromFederator,
|
||||||
// toFederator: make(chan gtsmodel.ToFederator, 100),
|
|
||||||
fromFederator: make(chan gtsmodel.FromFederator, 100),
|
|
||||||
federator: federator,
|
federator: federator,
|
||||||
stop: make(chan interface{}),
|
stop: make(chan interface{}),
|
||||||
log: log,
|
log: log,
|
||||||
@ -213,6 +222,8 @@ func NewProcessor(config *config.Config, tc typeutils.TypeConverter, federator f
|
|||||||
storage: storage,
|
storage: storage,
|
||||||
timelineManager: timelineManager,
|
timelineManager: timelineManager,
|
||||||
db: db,
|
db: db,
|
||||||
|
|
||||||
|
statusProcessor: statusProcessor,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,12 +27,13 @@ import (
|
|||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
"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/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/util"
|
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (p *processor) SearchGet(authed *oauth.Auth, searchQuery *apimodel.SearchQuery) (*apimodel.SearchResult, ErrorWithCode) {
|
func (p *processor) SearchGet(authed *oauth.Auth, searchQuery *apimodel.SearchQuery) (*apimodel.SearchResult, gtserror.WithCode) {
|
||||||
l := p.log.WithFields(logrus.Fields{
|
l := p.log.WithFields(logrus.Fields{
|
||||||
"func": "SearchGet",
|
"func": "SearchGet",
|
||||||
"query": searchQuery.Query,
|
"query": searchQuery.Query,
|
||||||
@ -164,7 +165,7 @@ func (p *processor) searchStatusByURI(authed *oauth.Auth, uri *url.URL, resolve
|
|||||||
// first turn it into a gtsmodel.Status
|
// first turn it into a gtsmodel.Status
|
||||||
status, err := p.tc.ASStatusToStatus(statusable)
|
status, err := p.tc.ASStatusToStatus(statusable)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, NewErrorInternalError(err)
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// put it in the DB so it gets a UUID
|
// put it in the DB so it gets a UUID
|
||||||
|
@ -19,531 +19,43 @@
|
|||||||
package processing
|
package processing
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/google/uuid"
|
|
||||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/util"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (p *processor) StatusCreate(auth *oauth.Auth, form *apimodel.AdvancedStatusCreateForm) (*apimodel.Status, error) {
|
func (p *processor) StatusCreate(authed *oauth.Auth, form *apimodel.AdvancedStatusCreateForm) (*apimodel.Status, error) {
|
||||||
uris := util.GenerateURIsForAccount(auth.Account.Username, p.config.Protocol, p.config.Host)
|
return p.statusProcessor.Create(authed.Account, authed.Application, form)
|
||||||
thisStatusID := uuid.NewString()
|
|
||||||
thisStatusURI := fmt.Sprintf("%s/%s", uris.StatusesURI, thisStatusID)
|
|
||||||
thisStatusURL := fmt.Sprintf("%s/%s", uris.StatusesURL, thisStatusID)
|
|
||||||
newStatus := >smodel.Status{
|
|
||||||
ID: thisStatusID,
|
|
||||||
URI: thisStatusURI,
|
|
||||||
URL: thisStatusURL,
|
|
||||||
Content: util.HTMLFormat(form.Status),
|
|
||||||
CreatedAt: time.Now(),
|
|
||||||
UpdatedAt: time.Now(),
|
|
||||||
Local: true,
|
|
||||||
AccountID: auth.Account.ID,
|
|
||||||
ContentWarning: form.SpoilerText,
|
|
||||||
ActivityStreamsType: gtsmodel.ActivityStreamsNote,
|
|
||||||
Sensitive: form.Sensitive,
|
|
||||||
Language: form.Language,
|
|
||||||
CreatedWithApplicationID: auth.Application.ID,
|
|
||||||
Text: form.Status,
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if replyToID is ok
|
|
||||||
if err := p.processReplyToID(form, auth.Account.ID, newStatus); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if mediaIDs are ok
|
|
||||||
if err := p.processMediaIDs(form, auth.Account.ID, newStatus); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if visibility settings are ok
|
|
||||||
if err := p.processVisibility(form, auth.Account.Privacy, newStatus); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// handle language settings
|
|
||||||
if err := p.processLanguage(form, auth.Account.Language, newStatus); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// handle mentions
|
|
||||||
if err := p.processMentions(form, auth.Account.ID, newStatus); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := p.processTags(form, auth.Account.ID, newStatus); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := p.processEmojis(form, auth.Account.ID, newStatus); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// put the new status in the database, generating an ID for it in the process
|
|
||||||
if err := p.db.Put(newStatus); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// change the status ID of the media attachments to the new status
|
|
||||||
for _, a := range newStatus.GTSMediaAttachments {
|
|
||||||
a.StatusID = newStatus.ID
|
|
||||||
a.UpdatedAt = time.Now()
|
|
||||||
if err := p.db.UpdateByID(a.ID, a); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// put the new status in the appropriate channel for async processing
|
|
||||||
p.fromClientAPI <- gtsmodel.FromClientAPI{
|
|
||||||
APObjectType: newStatus.ActivityStreamsType,
|
|
||||||
APActivityType: gtsmodel.ActivityStreamsCreate,
|
|
||||||
GTSModel: newStatus,
|
|
||||||
}
|
|
||||||
|
|
||||||
// return the frontend representation of the new status to the submitter
|
|
||||||
return p.tc.StatusToMasto(newStatus, auth.Account, auth.Account, nil, newStatus.GTSReplyToAccount, nil)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *processor) StatusDelete(authed *oauth.Auth, targetStatusID string) (*apimodel.Status, error) {
|
func (p *processor) StatusDelete(authed *oauth.Auth, targetStatusID string) (*apimodel.Status, error) {
|
||||||
l := p.log.WithField("func", "StatusDelete")
|
return p.statusProcessor.Delete(authed.Account, targetStatusID)
|
||||||
l.Tracef("going to search for target status %s", targetStatusID)
|
|
||||||
targetStatus := >smodel.Status{}
|
|
||||||
if err := p.db.GetByID(targetStatusID, targetStatus); err != nil {
|
|
||||||
return nil, fmt.Errorf("error fetching status %s: %s", targetStatusID, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if targetStatus.AccountID != authed.Account.ID {
|
|
||||||
return nil, errors.New("status doesn't belong to requesting account")
|
|
||||||
}
|
|
||||||
|
|
||||||
l.Trace("going to get relevant accounts")
|
|
||||||
relevantAccounts, err := p.db.PullRelevantAccountsFromStatus(targetStatus)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("error fetching related accounts for status %s: %s", targetStatusID, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var boostOfStatus *gtsmodel.Status
|
|
||||||
if targetStatus.BoostOfID != "" {
|
|
||||||
boostOfStatus = >smodel.Status{}
|
|
||||||
if err := p.db.GetByID(targetStatus.BoostOfID, boostOfStatus); err != nil {
|
|
||||||
return nil, fmt.Errorf("error fetching boosted status %s: %s", targetStatus.BoostOfID, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mastoStatus, err := p.tc.StatusToMasto(targetStatus, authed.Account, authed.Account, relevantAccounts.BoostedAccount, relevantAccounts.ReplyToAccount, boostOfStatus)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("error converting status %s to frontend representation: %s", targetStatus.ID, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := p.db.DeleteByID(targetStatus.ID, targetStatus); err != nil {
|
|
||||||
return nil, fmt.Errorf("error deleting status from the database: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return mastoStatus, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *processor) StatusFave(authed *oauth.Auth, targetStatusID string) (*apimodel.Status, error) {
|
func (p *processor) StatusFave(authed *oauth.Auth, targetStatusID string) (*apimodel.Status, error) {
|
||||||
l := p.log.WithField("func", "StatusFave")
|
return p.statusProcessor.Fave(authed.Account, targetStatusID)
|
||||||
l.Tracef("going to search for target status %s", targetStatusID)
|
|
||||||
targetStatus := >smodel.Status{}
|
|
||||||
if err := p.db.GetByID(targetStatusID, targetStatus); err != nil {
|
|
||||||
return nil, fmt.Errorf("error fetching status %s: %s", targetStatusID, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
l.Tracef("going to search for target account %s", targetStatus.AccountID)
|
|
||||||
targetAccount := >smodel.Account{}
|
|
||||||
if err := p.db.GetByID(targetStatus.AccountID, targetAccount); err != nil {
|
|
||||||
return nil, fmt.Errorf("error fetching target account %s: %s", targetStatus.AccountID, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
l.Trace("going to get relevant accounts")
|
|
||||||
relevantAccounts, err := p.db.PullRelevantAccountsFromStatus(targetStatus)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("error fetching related accounts for status %s: %s", targetStatusID, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var boostOfStatus *gtsmodel.Status
|
|
||||||
if targetStatus.BoostOfID != "" {
|
|
||||||
boostOfStatus = >smodel.Status{}
|
|
||||||
if err := p.db.GetByID(targetStatus.BoostOfID, boostOfStatus); err != nil {
|
|
||||||
return nil, fmt.Errorf("error fetching boosted status %s: %s", targetStatus.BoostOfID, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
l.Trace("going to see if status is visible")
|
|
||||||
visible, err := p.db.StatusVisible(targetStatus, targetAccount, authed.Account, relevantAccounts) // requestingAccount might well be nil here, but StatusVisible knows how to take care of that
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("error seeing if status %s is visible: %s", targetStatus.ID, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !visible {
|
|
||||||
return nil, errors.New("status is not visible")
|
|
||||||
}
|
|
||||||
|
|
||||||
// is the status faveable?
|
|
||||||
if targetStatus.VisibilityAdvanced != nil {
|
|
||||||
if !targetStatus.VisibilityAdvanced.Likeable {
|
|
||||||
return nil, errors.New("status is not faveable")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// first check if the status is already faved, if so we don't need to do anything
|
|
||||||
newFave := true
|
|
||||||
gtsFave := >smodel.Status{}
|
|
||||||
if err := p.db.GetWhere([]db.Where{{Key: "status_id", Value: targetStatus.ID}, {Key: "account_id", Value: authed.Account.ID}}, gtsFave); err == nil {
|
|
||||||
// we already have a fave for this status
|
|
||||||
newFave = false
|
|
||||||
}
|
|
||||||
|
|
||||||
if newFave {
|
|
||||||
thisFaveID := uuid.NewString()
|
|
||||||
|
|
||||||
// we need to create a new fave in the database
|
|
||||||
gtsFave := >smodel.StatusFave{
|
|
||||||
ID: thisFaveID,
|
|
||||||
AccountID: authed.Account.ID,
|
|
||||||
TargetAccountID: targetAccount.ID,
|
|
||||||
StatusID: targetStatus.ID,
|
|
||||||
URI: util.GenerateURIForLike(authed.Account.Username, p.config.Protocol, p.config.Host, thisFaveID),
|
|
||||||
GTSStatus: targetStatus,
|
|
||||||
GTSTargetAccount: targetAccount,
|
|
||||||
GTSFavingAccount: authed.Account,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := p.db.Put(gtsFave); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// send the new fave through the processor channel for federation etc
|
|
||||||
p.fromClientAPI <- gtsmodel.FromClientAPI{
|
|
||||||
APObjectType: gtsmodel.ActivityStreamsLike,
|
|
||||||
APActivityType: gtsmodel.ActivityStreamsCreate,
|
|
||||||
GTSModel: gtsFave,
|
|
||||||
OriginAccount: authed.Account,
|
|
||||||
TargetAccount: targetAccount,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// return the mastodon representation of the target status
|
|
||||||
mastoStatus, err := p.tc.StatusToMasto(targetStatus, targetAccount, authed.Account, relevantAccounts.BoostedAccount, relevantAccounts.ReplyToAccount, boostOfStatus)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("error converting status %s to frontend representation: %s", targetStatus.ID, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return mastoStatus, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *processor) StatusBoost(authed *oauth.Auth, targetStatusID string) (*apimodel.Status, ErrorWithCode) {
|
func (p *processor) StatusBoost(authed *oauth.Auth, targetStatusID string) (*apimodel.Status, gtserror.WithCode) {
|
||||||
l := p.log.WithField("func", "StatusBoost")
|
return p.statusProcessor.Boost(authed.Account, authed.Application, targetStatusID)
|
||||||
|
|
||||||
l.Tracef("going to search for target status %s", targetStatusID)
|
|
||||||
targetStatus := >smodel.Status{}
|
|
||||||
if err := p.db.GetByID(targetStatusID, targetStatus); err != nil {
|
|
||||||
return nil, NewErrorNotFound(fmt.Errorf("error fetching status %s: %s", targetStatusID, err))
|
|
||||||
}
|
|
||||||
|
|
||||||
l.Tracef("going to search for target account %s", targetStatus.AccountID)
|
|
||||||
targetAccount := >smodel.Account{}
|
|
||||||
if err := p.db.GetByID(targetStatus.AccountID, targetAccount); err != nil {
|
|
||||||
return nil, NewErrorNotFound(fmt.Errorf("error fetching target account %s: %s", targetStatus.AccountID, err))
|
|
||||||
}
|
|
||||||
|
|
||||||
l.Trace("going to get relevant accounts")
|
|
||||||
relevantAccounts, err := p.db.PullRelevantAccountsFromStatus(targetStatus)
|
|
||||||
if err != nil {
|
|
||||||
return nil, NewErrorNotFound(fmt.Errorf("error fetching related accounts for status %s: %s", targetStatusID, err))
|
|
||||||
}
|
|
||||||
|
|
||||||
l.Trace("going to see if status is visible")
|
|
||||||
visible, err := p.db.StatusVisible(targetStatus, targetAccount, authed.Account, relevantAccounts) // requestingAccount might well be nil here, but StatusVisible knows how to take care of that
|
|
||||||
if err != nil {
|
|
||||||
return nil, NewErrorNotFound(fmt.Errorf("error seeing if status %s is visible: %s", targetStatus.ID, err))
|
|
||||||
}
|
|
||||||
|
|
||||||
if !visible {
|
|
||||||
return nil, NewErrorNotFound(errors.New("status is not visible"))
|
|
||||||
}
|
|
||||||
|
|
||||||
if targetStatus.VisibilityAdvanced != nil {
|
|
||||||
if !targetStatus.VisibilityAdvanced.Boostable {
|
|
||||||
return nil, NewErrorForbidden(errors.New("status is not boostable"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// it's visible! it's boostable! so let's boost the FUCK out of it
|
|
||||||
boostWrapperStatus, err := p.tc.StatusToBoost(targetStatus, authed.Account)
|
|
||||||
if err != nil {
|
|
||||||
return nil, NewErrorInternalError(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
boostWrapperStatus.CreatedWithApplicationID = authed.Application.ID
|
|
||||||
boostWrapperStatus.GTSBoostedAccount = targetAccount
|
|
||||||
|
|
||||||
// put the boost in the database
|
|
||||||
if err := p.db.Put(boostWrapperStatus); err != nil {
|
|
||||||
return nil, NewErrorInternalError(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// send it to the processor for async processing
|
|
||||||
p.fromClientAPI <- gtsmodel.FromClientAPI{
|
|
||||||
APObjectType: gtsmodel.ActivityStreamsAnnounce,
|
|
||||||
APActivityType: gtsmodel.ActivityStreamsCreate,
|
|
||||||
GTSModel: boostWrapperStatus,
|
|
||||||
OriginAccount: authed.Account,
|
|
||||||
TargetAccount: targetAccount,
|
|
||||||
}
|
|
||||||
|
|
||||||
// return the frontend representation of the new status to the submitter
|
|
||||||
mastoStatus, err := p.tc.StatusToMasto(boostWrapperStatus, authed.Account, authed.Account, targetAccount, nil, targetStatus)
|
|
||||||
if err != nil {
|
|
||||||
return nil, NewErrorInternalError(fmt.Errorf("error converting status %s to frontend representation: %s", targetStatus.ID, err))
|
|
||||||
}
|
|
||||||
|
|
||||||
return mastoStatus, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *processor) StatusBoostedBy(authed *oauth.Auth, targetStatusID string) ([]*apimodel.Account, ErrorWithCode) {
|
func (p *processor) StatusBoostedBy(authed *oauth.Auth, targetStatusID string) ([]*apimodel.Account, gtserror.WithCode) {
|
||||||
l := p.log.WithField("func", "StatusBoostedBy")
|
return p.statusProcessor.BoostedBy(authed.Account, targetStatusID)
|
||||||
|
|
||||||
l.Tracef("going to search for target status %s", targetStatusID)
|
|
||||||
targetStatus := >smodel.Status{}
|
|
||||||
if err := p.db.GetByID(targetStatusID, targetStatus); err != nil {
|
|
||||||
return nil, NewErrorNotFound(fmt.Errorf("StatusBoostedBy: error fetching status %s: %s", targetStatusID, err))
|
|
||||||
}
|
|
||||||
|
|
||||||
l.Tracef("going to search for target account %s", targetStatus.AccountID)
|
|
||||||
targetAccount := >smodel.Account{}
|
|
||||||
if err := p.db.GetByID(targetStatus.AccountID, targetAccount); err != nil {
|
|
||||||
return nil, NewErrorNotFound(fmt.Errorf("StatusBoostedBy: error fetching target account %s: %s", targetStatus.AccountID, err))
|
|
||||||
}
|
|
||||||
|
|
||||||
l.Trace("going to get relevant accounts")
|
|
||||||
relevantAccounts, err := p.db.PullRelevantAccountsFromStatus(targetStatus)
|
|
||||||
if err != nil {
|
|
||||||
return nil, NewErrorNotFound(fmt.Errorf("StatusBoostedBy: error fetching related accounts for status %s: %s", targetStatusID, err))
|
|
||||||
}
|
|
||||||
|
|
||||||
l.Trace("going to see if status is visible")
|
|
||||||
visible, err := p.db.StatusVisible(targetStatus, targetAccount, authed.Account, relevantAccounts) // requestingAccount might well be nil here, but StatusVisible knows how to take care of that
|
|
||||||
if err != nil {
|
|
||||||
return nil, NewErrorNotFound(fmt.Errorf("StatusBoostedBy: error seeing if status %s is visible: %s", targetStatus.ID, err))
|
|
||||||
}
|
|
||||||
|
|
||||||
if !visible {
|
|
||||||
return nil, NewErrorNotFound(errors.New("StatusBoostedBy: status is not visible"))
|
|
||||||
}
|
|
||||||
|
|
||||||
// get ALL accounts that faved a status -- doesn't take account of blocks and mutes and stuff
|
|
||||||
favingAccounts, err := p.db.WhoBoostedStatus(targetStatus)
|
|
||||||
if err != nil {
|
|
||||||
return nil, NewErrorNotFound(fmt.Errorf("StatusBoostedBy: error seeing who boosted status: %s", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
// filter the list so the user doesn't see accounts they blocked or which blocked them
|
|
||||||
filteredAccounts := []*gtsmodel.Account{}
|
|
||||||
for _, acc := range favingAccounts {
|
|
||||||
blocked, err := p.db.Blocked(authed.Account.ID, acc.ID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, NewErrorNotFound(fmt.Errorf("StatusBoostedBy: error checking blocks: %s", err))
|
|
||||||
}
|
|
||||||
if !blocked {
|
|
||||||
filteredAccounts = append(filteredAccounts, acc)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: filter other things here? suspended? muted? silenced?
|
|
||||||
|
|
||||||
// now we can return the masto representation of those accounts
|
|
||||||
mastoAccounts := []*apimodel.Account{}
|
|
||||||
for _, acc := range filteredAccounts {
|
|
||||||
mastoAccount, err := p.tc.AccountToMastoPublic(acc)
|
|
||||||
if err != nil {
|
|
||||||
return nil, NewErrorNotFound(fmt.Errorf("StatusFavedBy: error converting account to api model: %s", err))
|
|
||||||
}
|
|
||||||
mastoAccounts = append(mastoAccounts, mastoAccount)
|
|
||||||
}
|
|
||||||
|
|
||||||
return mastoAccounts, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *processor) StatusFavedBy(authed *oauth.Auth, targetStatusID string) ([]*apimodel.Account, error) {
|
func (p *processor) StatusFavedBy(authed *oauth.Auth, targetStatusID string) ([]*apimodel.Account, error) {
|
||||||
l := p.log.WithField("func", "StatusFavedBy")
|
return p.statusProcessor.FavedBy(authed.Account, targetStatusID)
|
||||||
|
|
||||||
l.Tracef("going to search for target status %s", targetStatusID)
|
|
||||||
targetStatus := >smodel.Status{}
|
|
||||||
if err := p.db.GetByID(targetStatusID, targetStatus); err != nil {
|
|
||||||
return nil, fmt.Errorf("error fetching status %s: %s", targetStatusID, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
l.Tracef("going to search for target account %s", targetStatus.AccountID)
|
|
||||||
targetAccount := >smodel.Account{}
|
|
||||||
if err := p.db.GetByID(targetStatus.AccountID, targetAccount); err != nil {
|
|
||||||
return nil, fmt.Errorf("error fetching target account %s: %s", targetStatus.AccountID, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
l.Trace("going to get relevant accounts")
|
|
||||||
relevantAccounts, err := p.db.PullRelevantAccountsFromStatus(targetStatus)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("error fetching related accounts for status %s: %s", targetStatusID, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
l.Trace("going to see if status is visible")
|
|
||||||
visible, err := p.db.StatusVisible(targetStatus, targetAccount, authed.Account, relevantAccounts) // requestingAccount might well be nil here, but StatusVisible knows how to take care of that
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("error seeing if status %s is visible: %s", targetStatus.ID, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !visible {
|
|
||||||
return nil, errors.New("status is not visible")
|
|
||||||
}
|
|
||||||
|
|
||||||
// get ALL accounts that faved a status -- doesn't take account of blocks and mutes and stuff
|
|
||||||
favingAccounts, err := p.db.WhoFavedStatus(targetStatus)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("error seeing who faved status: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// filter the list so the user doesn't see accounts they blocked or which blocked them
|
|
||||||
filteredAccounts := []*gtsmodel.Account{}
|
|
||||||
for _, acc := range favingAccounts {
|
|
||||||
blocked, err := p.db.Blocked(authed.Account.ID, acc.ID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("error checking blocks: %s", err)
|
|
||||||
}
|
|
||||||
if !blocked {
|
|
||||||
filteredAccounts = append(filteredAccounts, acc)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: filter other things here? suspended? muted? silenced?
|
|
||||||
|
|
||||||
// now we can return the masto representation of those accounts
|
|
||||||
mastoAccounts := []*apimodel.Account{}
|
|
||||||
for _, acc := range filteredAccounts {
|
|
||||||
mastoAccount, err := p.tc.AccountToMastoPublic(acc)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("error converting account to api model: %s", err)
|
|
||||||
}
|
|
||||||
mastoAccounts = append(mastoAccounts, mastoAccount)
|
|
||||||
}
|
|
||||||
|
|
||||||
return mastoAccounts, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *processor) StatusGet(authed *oauth.Auth, targetStatusID string) (*apimodel.Status, error) {
|
func (p *processor) StatusGet(authed *oauth.Auth, targetStatusID string) (*apimodel.Status, error) {
|
||||||
l := p.log.WithField("func", "StatusGet")
|
return p.statusProcessor.Get(authed.Account, targetStatusID)
|
||||||
|
|
||||||
l.Tracef("going to search for target status %s", targetStatusID)
|
|
||||||
targetStatus := >smodel.Status{}
|
|
||||||
if err := p.db.GetByID(targetStatusID, targetStatus); err != nil {
|
|
||||||
return nil, fmt.Errorf("error fetching status %s: %s", targetStatusID, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
l.Tracef("going to search for target account %s", targetStatus.AccountID)
|
|
||||||
targetAccount := >smodel.Account{}
|
|
||||||
if err := p.db.GetByID(targetStatus.AccountID, targetAccount); err != nil {
|
|
||||||
return nil, fmt.Errorf("error fetching target account %s: %s", targetStatus.AccountID, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
l.Trace("going to get relevant accounts")
|
|
||||||
relevantAccounts, err := p.db.PullRelevantAccountsFromStatus(targetStatus)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("error fetching related accounts for status %s: %s", targetStatusID, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
l.Trace("going to see if status is visible")
|
|
||||||
visible, err := p.db.StatusVisible(targetStatus, targetAccount, authed.Account, relevantAccounts) // requestingAccount might well be nil here, but StatusVisible knows how to take care of that
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("error seeing if status %s is visible: %s", targetStatus.ID, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !visible {
|
|
||||||
return nil, errors.New("status is not visible")
|
|
||||||
}
|
|
||||||
|
|
||||||
var boostOfStatus *gtsmodel.Status
|
|
||||||
if targetStatus.BoostOfID != "" {
|
|
||||||
boostOfStatus = >smodel.Status{}
|
|
||||||
if err := p.db.GetByID(targetStatus.BoostOfID, boostOfStatus); err != nil {
|
|
||||||
return nil, fmt.Errorf("error fetching boosted status %s: %s", targetStatus.BoostOfID, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mastoStatus, err := p.tc.StatusToMasto(targetStatus, targetAccount, authed.Account, relevantAccounts.BoostedAccount, relevantAccounts.ReplyToAccount, boostOfStatus)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("error converting status %s to frontend representation: %s", targetStatus.ID, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return mastoStatus, nil
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *processor) StatusUnfave(authed *oauth.Auth, targetStatusID string) (*apimodel.Status, error) {
|
func (p *processor) StatusUnfave(authed *oauth.Auth, targetStatusID string) (*apimodel.Status, error) {
|
||||||
l := p.log.WithField("func", "StatusUnfave")
|
return p.statusProcessor.Unfave(authed.Account, targetStatusID)
|
||||||
l.Tracef("going to search for target status %s", targetStatusID)
|
|
||||||
targetStatus := >smodel.Status{}
|
|
||||||
if err := p.db.GetByID(targetStatusID, targetStatus); err != nil {
|
|
||||||
return nil, fmt.Errorf("error fetching status %s: %s", targetStatusID, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
l.Tracef("going to search for target account %s", targetStatus.AccountID)
|
|
||||||
targetAccount := >smodel.Account{}
|
|
||||||
if err := p.db.GetByID(targetStatus.AccountID, targetAccount); err != nil {
|
|
||||||
return nil, fmt.Errorf("error fetching target account %s: %s", targetStatus.AccountID, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
l.Trace("going to get relevant accounts")
|
|
||||||
relevantAccounts, err := p.db.PullRelevantAccountsFromStatus(targetStatus)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("error fetching related accounts for status %s: %s", targetStatusID, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
l.Trace("going to see if status is visible")
|
|
||||||
visible, err := p.db.StatusVisible(targetStatus, targetAccount, authed.Account, relevantAccounts) // requestingAccount might well be nil here, but StatusVisible knows how to take care of that
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("error seeing if status %s is visible: %s", targetStatus.ID, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !visible {
|
|
||||||
return nil, errors.New("status is not visible")
|
|
||||||
}
|
|
||||||
|
|
||||||
// is the status faveable?
|
|
||||||
if targetStatus.VisibilityAdvanced != nil {
|
|
||||||
if !targetStatus.VisibilityAdvanced.Likeable {
|
|
||||||
return nil, errors.New("status is not faveable")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// it's visible! it's faveable! so let's unfave the FUCK out of it
|
|
||||||
_, err = p.db.UnfaveStatus(targetStatus, authed.Account.ID)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("error unfaveing status: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var boostOfStatus *gtsmodel.Status
|
|
||||||
if targetStatus.BoostOfID != "" {
|
|
||||||
boostOfStatus = >smodel.Status{}
|
|
||||||
if err := p.db.GetByID(targetStatus.BoostOfID, boostOfStatus); err != nil {
|
|
||||||
return nil, fmt.Errorf("error fetching boosted status %s: %s", targetStatus.BoostOfID, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mastoStatus, err := p.tc.StatusToMasto(targetStatus, targetAccount, authed.Account, relevantAccounts.BoostedAccount, relevantAccounts.ReplyToAccount, boostOfStatus)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("error converting status %s to frontend representation: %s", targetStatus.ID, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return mastoStatus, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *processor) StatusGetContext(authed *oauth.Auth, targetStatusID string) (*apimodel.Context, ErrorWithCode) {
|
func (p *processor) StatusGetContext(authed *oauth.Auth, targetStatusID string) (*apimodel.Context, gtserror.WithCode) {
|
||||||
return &apimodel.Context{}, nil
|
return p.statusProcessor.Context(authed.Account, targetStatusID)
|
||||||
}
|
}
|
||||||
|
79
internal/processing/status/boost.go
Normal file
79
internal/processing/status/boost.go
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
package status
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (p *processor) Boost(account *gtsmodel.Account, application *gtsmodel.Application, targetStatusID string) (*apimodel.Status, gtserror.WithCode) {
|
||||||
|
l := p.log.WithField("func", "StatusBoost")
|
||||||
|
|
||||||
|
l.Tracef("going to search for target status %s", targetStatusID)
|
||||||
|
targetStatus := >smodel.Status{}
|
||||||
|
if err := p.db.GetByID(targetStatusID, targetStatus); err != nil {
|
||||||
|
return nil, gtserror.NewErrorNotFound(fmt.Errorf("error fetching status %s: %s", targetStatusID, err))
|
||||||
|
}
|
||||||
|
|
||||||
|
l.Tracef("going to search for target account %s", targetStatus.AccountID)
|
||||||
|
targetAccount := >smodel.Account{}
|
||||||
|
if err := p.db.GetByID(targetStatus.AccountID, targetAccount); err != nil {
|
||||||
|
return nil, gtserror.NewErrorNotFound(fmt.Errorf("error fetching target account %s: %s", targetStatus.AccountID, err))
|
||||||
|
}
|
||||||
|
|
||||||
|
l.Trace("going to get relevant accounts")
|
||||||
|
relevantAccounts, err := p.db.PullRelevantAccountsFromStatus(targetStatus)
|
||||||
|
if err != nil {
|
||||||
|
return nil, gtserror.NewErrorNotFound(fmt.Errorf("error fetching related accounts for status %s: %s", targetStatusID, err))
|
||||||
|
}
|
||||||
|
|
||||||
|
l.Trace("going to see if status is visible")
|
||||||
|
visible, err := p.db.StatusVisible(targetStatus, targetAccount, account, relevantAccounts) // requestingAccount might well be nil here, but StatusVisible knows how to take care of that
|
||||||
|
if err != nil {
|
||||||
|
return nil, gtserror.NewErrorNotFound(fmt.Errorf("error seeing if status %s is visible: %s", targetStatus.ID, err))
|
||||||
|
}
|
||||||
|
|
||||||
|
if !visible {
|
||||||
|
return nil, gtserror.NewErrorNotFound(errors.New("status is not visible"))
|
||||||
|
}
|
||||||
|
|
||||||
|
if targetStatus.VisibilityAdvanced != nil {
|
||||||
|
if !targetStatus.VisibilityAdvanced.Boostable {
|
||||||
|
return nil, gtserror.NewErrorForbidden(errors.New("status is not boostable"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// it's visible! it's boostable! so let's boost the FUCK out of it
|
||||||
|
boostWrapperStatus, err := p.tc.StatusToBoost(targetStatus, account)
|
||||||
|
if err != nil {
|
||||||
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
boostWrapperStatus.CreatedWithApplicationID = application.ID
|
||||||
|
boostWrapperStatus.GTSBoostedAccount = targetAccount
|
||||||
|
|
||||||
|
// put the boost in the database
|
||||||
|
if err := p.db.Put(boostWrapperStatus); err != nil {
|
||||||
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// send it to the processor for async processing
|
||||||
|
p.fromClientAPI <- gtsmodel.FromClientAPI{
|
||||||
|
APObjectType: gtsmodel.ActivityStreamsAnnounce,
|
||||||
|
APActivityType: gtsmodel.ActivityStreamsCreate,
|
||||||
|
GTSModel: boostWrapperStatus,
|
||||||
|
OriginAccount: account,
|
||||||
|
TargetAccount: targetAccount,
|
||||||
|
}
|
||||||
|
|
||||||
|
// return the frontend representation of the new status to the submitter
|
||||||
|
mastoStatus, err := p.tc.StatusToMasto(boostWrapperStatus, account, account, targetAccount, nil, targetStatus)
|
||||||
|
if err != nil {
|
||||||
|
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting status %s to frontend representation: %s", targetStatus.ID, err))
|
||||||
|
}
|
||||||
|
|
||||||
|
return mastoStatus, nil
|
||||||
|
}
|
74
internal/processing/status/boostedby.go
Normal file
74
internal/processing/status/boostedby.go
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
package status
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (p *processor) BoostedBy(account *gtsmodel.Account, targetStatusID string) ([]*apimodel.Account, gtserror.WithCode) {
|
||||||
|
l := p.log.WithField("func", "StatusBoostedBy")
|
||||||
|
|
||||||
|
l.Tracef("going to search for target status %s", targetStatusID)
|
||||||
|
targetStatus := >smodel.Status{}
|
||||||
|
if err := p.db.GetByID(targetStatusID, targetStatus); err != nil {
|
||||||
|
return nil, gtserror.NewErrorNotFound(fmt.Errorf("StatusBoostedBy: error fetching status %s: %s", targetStatusID, err))
|
||||||
|
}
|
||||||
|
|
||||||
|
l.Tracef("going to search for target account %s", targetStatus.AccountID)
|
||||||
|
targetAccount := >smodel.Account{}
|
||||||
|
if err := p.db.GetByID(targetStatus.AccountID, targetAccount); err != nil {
|
||||||
|
return nil, gtserror.NewErrorNotFound(fmt.Errorf("StatusBoostedBy: error fetching target account %s: %s", targetStatus.AccountID, err))
|
||||||
|
}
|
||||||
|
|
||||||
|
l.Trace("going to get relevant accounts")
|
||||||
|
relevantAccounts, err := p.db.PullRelevantAccountsFromStatus(targetStatus)
|
||||||
|
if err != nil {
|
||||||
|
return nil, gtserror.NewErrorNotFound(fmt.Errorf("StatusBoostedBy: error fetching related accounts for status %s: %s", targetStatusID, err))
|
||||||
|
}
|
||||||
|
|
||||||
|
l.Trace("going to see if status is visible")
|
||||||
|
visible, err := p.db.StatusVisible(targetStatus, targetAccount, account, relevantAccounts) // requestingAccount might well be nil here, but StatusVisible knows how to take care of that
|
||||||
|
if err != nil {
|
||||||
|
return nil, gtserror.NewErrorNotFound(fmt.Errorf("StatusBoostedBy: error seeing if status %s is visible: %s", targetStatus.ID, err))
|
||||||
|
}
|
||||||
|
|
||||||
|
if !visible {
|
||||||
|
return nil, gtserror.NewErrorNotFound(errors.New("StatusBoostedBy: status is not visible"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// get ALL accounts that faved a status -- doesn't take account of blocks and mutes and stuff
|
||||||
|
favingAccounts, err := p.db.WhoBoostedStatus(targetStatus)
|
||||||
|
if err != nil {
|
||||||
|
return nil, gtserror.NewErrorNotFound(fmt.Errorf("StatusBoostedBy: error seeing who boosted status: %s", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
// filter the list so the user doesn't see accounts they blocked or which blocked them
|
||||||
|
filteredAccounts := []*gtsmodel.Account{}
|
||||||
|
for _, acc := range favingAccounts {
|
||||||
|
blocked, err := p.db.Blocked(account.ID, acc.ID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, gtserror.NewErrorNotFound(fmt.Errorf("StatusBoostedBy: error checking blocks: %s", err))
|
||||||
|
}
|
||||||
|
if !blocked {
|
||||||
|
filteredAccounts = append(filteredAccounts, acc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: filter other things here? suspended? muted? silenced?
|
||||||
|
|
||||||
|
// now we can return the masto representation of those accounts
|
||||||
|
mastoAccounts := []*apimodel.Account{}
|
||||||
|
for _, acc := range filteredAccounts {
|
||||||
|
mastoAccount, err := p.tc.AccountToMastoPublic(acc)
|
||||||
|
if err != nil {
|
||||||
|
return nil, gtserror.NewErrorNotFound(fmt.Errorf("StatusFavedBy: error converting account to api model: %s", err))
|
||||||
|
}
|
||||||
|
mastoAccounts = append(mastoAccounts, mastoAccount)
|
||||||
|
}
|
||||||
|
|
||||||
|
return mastoAccounts, nil
|
||||||
|
}
|
11
internal/processing/status/context.go
Normal file
11
internal/processing/status/context.go
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package status
|
||||||
|
|
||||||
|
import (
|
||||||
|
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (p *processor) Context(account *gtsmodel.Account, targetStatusID string) (*apimodel.Context, gtserror.WithCode) {
|
||||||
|
return &apimodel.Context{}, nil
|
||||||
|
}
|
98
internal/processing/status/create.go
Normal file
98
internal/processing/status/create.go
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
package status
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (p *processor) Create(account *gtsmodel.Account, application *gtsmodel.Application, form *apimodel.AdvancedStatusCreateForm) (*apimodel.Status, gtserror.WithCode) {
|
||||||
|
uris := util.GenerateURIsForAccount(account.Username, p.config.Protocol, p.config.Host)
|
||||||
|
thisStatusID := uuid.NewString()
|
||||||
|
thisStatusURI := fmt.Sprintf("%s/%s", uris.StatusesURI, thisStatusID)
|
||||||
|
thisStatusURL := fmt.Sprintf("%s/%s", uris.StatusesURL, thisStatusID)
|
||||||
|
newStatus := >smodel.Status{
|
||||||
|
ID: thisStatusID,
|
||||||
|
URI: thisStatusURI,
|
||||||
|
URL: thisStatusURL,
|
||||||
|
Content: util.HTMLFormat(form.Status),
|
||||||
|
CreatedAt: time.Now(),
|
||||||
|
UpdatedAt: time.Now(),
|
||||||
|
Local: true,
|
||||||
|
AccountID: account.ID,
|
||||||
|
ContentWarning: form.SpoilerText,
|
||||||
|
ActivityStreamsType: gtsmodel.ActivityStreamsNote,
|
||||||
|
Sensitive: form.Sensitive,
|
||||||
|
Language: form.Language,
|
||||||
|
CreatedWithApplicationID: application.ID,
|
||||||
|
Text: form.Status,
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if replyToID is ok
|
||||||
|
if err := p.processReplyToID(form, account.ID, newStatus); err != nil {
|
||||||
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if mediaIDs are ok
|
||||||
|
if err := p.processMediaIDs(form, account.ID, newStatus); err != nil {
|
||||||
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if visibility settings are ok
|
||||||
|
if err := p.processVisibility(form, account.Privacy, newStatus); err != nil {
|
||||||
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle language settings
|
||||||
|
if err := p.processLanguage(form, account.Language, newStatus); err != nil {
|
||||||
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle mentions
|
||||||
|
if err := p.processMentions(form, account.ID, newStatus); err != nil {
|
||||||
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := p.processTags(form, account.ID, newStatus); err != nil {
|
||||||
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := p.processEmojis(form, account.ID, newStatus); err != nil {
|
||||||
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// put the new status in the database, generating an ID for it in the process
|
||||||
|
if err := p.db.Put(newStatus); err != nil {
|
||||||
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// change the status ID of the media attachments to the new status
|
||||||
|
for _, a := range newStatus.GTSMediaAttachments {
|
||||||
|
a.StatusID = newStatus.ID
|
||||||
|
a.UpdatedAt = time.Now()
|
||||||
|
if err := p.db.UpdateByID(a.ID, a); err != nil {
|
||||||
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// put the new status in the appropriate channel for async processing
|
||||||
|
p.fromClientAPI <- gtsmodel.FromClientAPI{
|
||||||
|
APObjectType: gtsmodel.ActivityStreamsNote,
|
||||||
|
APActivityType: gtsmodel.ActivityStreamsCreate,
|
||||||
|
GTSModel: newStatus,
|
||||||
|
OriginAccount: account,
|
||||||
|
}
|
||||||
|
|
||||||
|
// return the frontend representation of the new status to the submitter
|
||||||
|
mastoStatus, err := p.tc.StatusToMasto(newStatus, account, account, nil, newStatus.GTSReplyToAccount, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting status %s to frontend representation: %s", newStatus.ID, err))
|
||||||
|
}
|
||||||
|
|
||||||
|
return mastoStatus, nil
|
||||||
|
}
|
55
internal/processing/status/delete.go
Normal file
55
internal/processing/status/delete.go
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
package status
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (p *processor) Delete(account *gtsmodel.Account, targetStatusID string) (*apimodel.Status, gtserror.WithCode) {
|
||||||
|
l := p.log.WithField("func", "StatusDelete")
|
||||||
|
l.Tracef("going to search for target status %s", targetStatusID)
|
||||||
|
targetStatus := >smodel.Status{}
|
||||||
|
if err := p.db.GetByID(targetStatusID, targetStatus); err != nil {
|
||||||
|
return nil, gtserror.NewErrorNotFound(fmt.Errorf("error fetching status %s: %s", targetStatusID, err))
|
||||||
|
}
|
||||||
|
|
||||||
|
if targetStatus.AccountID != account.ID {
|
||||||
|
return nil, gtserror.NewErrorForbidden(errors.New("status doesn't belong to requesting account"))
|
||||||
|
}
|
||||||
|
|
||||||
|
l.Trace("going to get relevant accounts")
|
||||||
|
relevantAccounts, err := p.db.PullRelevantAccountsFromStatus(targetStatus)
|
||||||
|
if err != nil {
|
||||||
|
return nil, gtserror.NewErrorNotFound(fmt.Errorf("error fetching related accounts for status %s: %s", targetStatusID, err))
|
||||||
|
}
|
||||||
|
|
||||||
|
var boostOfStatus *gtsmodel.Status
|
||||||
|
if targetStatus.BoostOfID != "" {
|
||||||
|
boostOfStatus = >smodel.Status{}
|
||||||
|
if err := p.db.GetByID(targetStatus.BoostOfID, boostOfStatus); err != nil {
|
||||||
|
return nil, gtserror.NewErrorNotFound(fmt.Errorf("error fetching boosted status %s: %s", targetStatus.BoostOfID, err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mastoStatus, err := p.tc.StatusToMasto(targetStatus, account, account, relevantAccounts.BoostedAccount, relevantAccounts.ReplyToAccount, boostOfStatus)
|
||||||
|
if err != nil {
|
||||||
|
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting status %s to frontend representation: %s", targetStatus.ID, err))
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := p.db.DeleteByID(targetStatus.ID, targetStatus); err != nil {
|
||||||
|
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error deleting status from the database: %s", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
p.fromClientAPI <- gtsmodel.FromClientAPI{
|
||||||
|
APObjectType: gtsmodel.ActivityStreamsNote,
|
||||||
|
APActivityType: gtsmodel.ActivityStreamsDelete,
|
||||||
|
GTSModel: targetStatus,
|
||||||
|
OriginAccount: account,
|
||||||
|
}
|
||||||
|
|
||||||
|
return mastoStatus, nil
|
||||||
|
}
|
104
internal/processing/status/fave.go
Normal file
104
internal/processing/status/fave.go
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
package status
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
|
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (p *processor) Fave(account *gtsmodel.Account, targetStatusID string) (*apimodel.Status, gtserror.WithCode) {
|
||||||
|
l := p.log.WithField("func", "StatusFave")
|
||||||
|
l.Tracef("going to search for target status %s", targetStatusID)
|
||||||
|
targetStatus := >smodel.Status{}
|
||||||
|
if err := p.db.GetByID(targetStatusID, targetStatus); err != nil {
|
||||||
|
return nil, gtserror.NewErrorNotFound(fmt.Errorf("error fetching status %s: %s", targetStatusID, err))
|
||||||
|
}
|
||||||
|
|
||||||
|
l.Tracef("going to search for target account %s", targetStatus.AccountID)
|
||||||
|
targetAccount := >smodel.Account{}
|
||||||
|
if err := p.db.GetByID(targetStatus.AccountID, targetAccount); err != nil {
|
||||||
|
return nil, gtserror.NewErrorNotFound(fmt.Errorf("error fetching target account %s: %s", targetStatus.AccountID, err))
|
||||||
|
}
|
||||||
|
|
||||||
|
l.Trace("going to get relevant accounts")
|
||||||
|
relevantAccounts, err := p.db.PullRelevantAccountsFromStatus(targetStatus)
|
||||||
|
if err != nil {
|
||||||
|
return nil, gtserror.NewErrorNotFound(fmt.Errorf("error fetching related accounts for status %s: %s", targetStatusID, err))
|
||||||
|
}
|
||||||
|
|
||||||
|
var boostOfStatus *gtsmodel.Status
|
||||||
|
if targetStatus.BoostOfID != "" {
|
||||||
|
boostOfStatus = >smodel.Status{}
|
||||||
|
if err := p.db.GetByID(targetStatus.BoostOfID, boostOfStatus); err != nil {
|
||||||
|
return nil, gtserror.NewErrorNotFound(fmt.Errorf("error fetching boosted status %s: %s", targetStatus.BoostOfID, err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
l.Trace("going to see if status is visible")
|
||||||
|
visible, err := p.db.StatusVisible(targetStatus, targetAccount, account, relevantAccounts) // requestingAccount might well be nil here, but StatusVisible knows how to take care of that
|
||||||
|
if err != nil {
|
||||||
|
return nil, gtserror.NewErrorNotFound(fmt.Errorf("error seeing if status %s is visible: %s", targetStatus.ID, err))
|
||||||
|
}
|
||||||
|
|
||||||
|
if !visible {
|
||||||
|
return nil, gtserror.NewErrorNotFound(errors.New("status is not visible"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// is the status faveable?
|
||||||
|
if targetStatus.VisibilityAdvanced != nil {
|
||||||
|
if !targetStatus.VisibilityAdvanced.Likeable {
|
||||||
|
return nil, gtserror.NewErrorForbidden(errors.New("status is not faveable"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// first check if the status is already faved, if so we don't need to do anything
|
||||||
|
newFave := true
|
||||||
|
gtsFave := >smodel.StatusFave{}
|
||||||
|
if err := p.db.GetWhere([]db.Where{{Key: "status_id", Value: targetStatus.ID}, {Key: "account_id", Value: account.ID}}, gtsFave); err == nil {
|
||||||
|
// we already have a fave for this status
|
||||||
|
newFave = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if newFave {
|
||||||
|
thisFaveID := uuid.NewString()
|
||||||
|
|
||||||
|
// we need to create a new fave in the database
|
||||||
|
gtsFave := >smodel.StatusFave{
|
||||||
|
ID: thisFaveID,
|
||||||
|
AccountID: account.ID,
|
||||||
|
TargetAccountID: targetAccount.ID,
|
||||||
|
StatusID: targetStatus.ID,
|
||||||
|
URI: util.GenerateURIForLike(account.Username, p.config.Protocol, p.config.Host, thisFaveID),
|
||||||
|
GTSStatus: targetStatus,
|
||||||
|
GTSTargetAccount: targetAccount,
|
||||||
|
GTSFavingAccount: account,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := p.db.Put(gtsFave); err != nil {
|
||||||
|
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error putting fave in database: %s", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
// send the new fave through the processor channel for federation etc
|
||||||
|
p.fromClientAPI <- gtsmodel.FromClientAPI{
|
||||||
|
APObjectType: gtsmodel.ActivityStreamsLike,
|
||||||
|
APActivityType: gtsmodel.ActivityStreamsCreate,
|
||||||
|
GTSModel: gtsFave,
|
||||||
|
OriginAccount: account,
|
||||||
|
TargetAccount: targetAccount,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// return the mastodon representation of the target status
|
||||||
|
mastoStatus, err := p.tc.StatusToMasto(targetStatus, targetAccount, account, relevantAccounts.BoostedAccount, relevantAccounts.ReplyToAccount, boostOfStatus)
|
||||||
|
if err != nil {
|
||||||
|
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting status %s to frontend representation: %s", targetStatus.ID, err))
|
||||||
|
}
|
||||||
|
|
||||||
|
return mastoStatus, nil
|
||||||
|
}
|
74
internal/processing/status/favedby.go
Normal file
74
internal/processing/status/favedby.go
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
package status
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (p *processor) FavedBy(account *gtsmodel.Account, targetStatusID string) ([]*apimodel.Account, gtserror.WithCode) {
|
||||||
|
l := p.log.WithField("func", "StatusFavedBy")
|
||||||
|
|
||||||
|
l.Tracef("going to search for target status %s", targetStatusID)
|
||||||
|
targetStatus := >smodel.Status{}
|
||||||
|
if err := p.db.GetByID(targetStatusID, targetStatus); err != nil {
|
||||||
|
return nil, gtserror.NewErrorNotFound(fmt.Errorf("error fetching status %s: %s", targetStatusID, err))
|
||||||
|
}
|
||||||
|
|
||||||
|
l.Tracef("going to search for target account %s", targetStatus.AccountID)
|
||||||
|
targetAccount := >smodel.Account{}
|
||||||
|
if err := p.db.GetByID(targetStatus.AccountID, targetAccount); err != nil {
|
||||||
|
return nil, gtserror.NewErrorNotFound(fmt.Errorf("error fetching target account %s: %s", targetStatus.AccountID, err))
|
||||||
|
}
|
||||||
|
|
||||||
|
l.Trace("going to get relevant accounts")
|
||||||
|
relevantAccounts, err := p.db.PullRelevantAccountsFromStatus(targetStatus)
|
||||||
|
if err != nil {
|
||||||
|
return nil, gtserror.NewErrorNotFound(fmt.Errorf("error fetching related accounts for status %s: %s", targetStatusID, err))
|
||||||
|
}
|
||||||
|
|
||||||
|
l.Trace("going to see if status is visible")
|
||||||
|
visible, err := p.db.StatusVisible(targetStatus, targetAccount, account, relevantAccounts) // requestingAccount might well be nil here, but StatusVisible knows how to take care of that
|
||||||
|
if err != nil {
|
||||||
|
return nil, gtserror.NewErrorNotFound(fmt.Errorf("error seeing if status %s is visible: %s", targetStatus.ID, err))
|
||||||
|
}
|
||||||
|
|
||||||
|
if !visible {
|
||||||
|
return nil, gtserror.NewErrorNotFound(errors.New("status is not visible"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// get ALL accounts that faved a status -- doesn't take account of blocks and mutes and stuff
|
||||||
|
favingAccounts, err := p.db.WhoFavedStatus(targetStatus)
|
||||||
|
if err != nil {
|
||||||
|
return nil, gtserror.NewErrorNotFound(fmt.Errorf("error seeing who faved status: %s", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
// filter the list so the user doesn't see accounts they blocked or which blocked them
|
||||||
|
filteredAccounts := []*gtsmodel.Account{}
|
||||||
|
for _, acc := range favingAccounts {
|
||||||
|
blocked, err := p.db.Blocked(account.ID, acc.ID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error checking blocks: %s", err))
|
||||||
|
}
|
||||||
|
if !blocked {
|
||||||
|
filteredAccounts = append(filteredAccounts, acc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: filter other things here? suspended? muted? silenced?
|
||||||
|
|
||||||
|
// now we can return the masto representation of those accounts
|
||||||
|
mastoAccounts := []*apimodel.Account{}
|
||||||
|
for _, acc := range filteredAccounts {
|
||||||
|
mastoAccount, err := p.tc.AccountToMastoPublic(acc)
|
||||||
|
if err != nil {
|
||||||
|
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting status %s to frontend representation: %s", targetStatus.ID, err))
|
||||||
|
}
|
||||||
|
mastoAccounts = append(mastoAccounts, mastoAccount)
|
||||||
|
}
|
||||||
|
|
||||||
|
return mastoAccounts, nil
|
||||||
|
}
|
58
internal/processing/status/get.go
Normal file
58
internal/processing/status/get.go
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
package status
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (p *processor) Get(account *gtsmodel.Account, targetStatusID string) (*apimodel.Status, gtserror.WithCode) {
|
||||||
|
l := p.log.WithField("func", "StatusGet")
|
||||||
|
|
||||||
|
l.Tracef("going to search for target status %s", targetStatusID)
|
||||||
|
targetStatus := >smodel.Status{}
|
||||||
|
if err := p.db.GetByID(targetStatusID, targetStatus); err != nil {
|
||||||
|
return nil, gtserror.NewErrorNotFound(fmt.Errorf("error fetching status %s: %s", targetStatusID, err))
|
||||||
|
}
|
||||||
|
|
||||||
|
l.Tracef("going to search for target account %s", targetStatus.AccountID)
|
||||||
|
targetAccount := >smodel.Account{}
|
||||||
|
if err := p.db.GetByID(targetStatus.AccountID, targetAccount); err != nil {
|
||||||
|
return nil, gtserror.NewErrorNotFound(fmt.Errorf("error fetching target account %s: %s", targetStatus.AccountID, err))
|
||||||
|
}
|
||||||
|
|
||||||
|
l.Trace("going to get relevant accounts")
|
||||||
|
relevantAccounts, err := p.db.PullRelevantAccountsFromStatus(targetStatus)
|
||||||
|
if err != nil {
|
||||||
|
return nil, gtserror.NewErrorNotFound(fmt.Errorf("error fetching related accounts for status %s: %s", targetStatusID, err))
|
||||||
|
}
|
||||||
|
|
||||||
|
l.Trace("going to see if status is visible")
|
||||||
|
visible, err := p.db.StatusVisible(targetStatus, targetAccount, account, relevantAccounts) // requestingAccount might well be nil here, but StatusVisible knows how to take care of that
|
||||||
|
if err != nil {
|
||||||
|
return nil, gtserror.NewErrorNotFound(fmt.Errorf("error seeing if status %s is visible: %s", targetStatus.ID, err))
|
||||||
|
}
|
||||||
|
|
||||||
|
if !visible {
|
||||||
|
return nil, gtserror.NewErrorNotFound(errors.New("status is not visible"))
|
||||||
|
}
|
||||||
|
|
||||||
|
var boostOfStatus *gtsmodel.Status
|
||||||
|
if targetStatus.BoostOfID != "" {
|
||||||
|
boostOfStatus = >smodel.Status{}
|
||||||
|
if err := p.db.GetByID(targetStatus.BoostOfID, boostOfStatus); err != nil {
|
||||||
|
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error fetching boosted status %s: %s", targetStatus.BoostOfID, err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mastoStatus, err := p.tc.StatusToMasto(targetStatus, targetAccount, account, relevantAccounts.BoostedAccount, relevantAccounts.ReplyToAccount, boostOfStatus)
|
||||||
|
if err != nil {
|
||||||
|
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting status %s to frontend representation: %s", targetStatus.ID, err))
|
||||||
|
}
|
||||||
|
|
||||||
|
return mastoStatus, nil
|
||||||
|
|
||||||
|
}
|
52
internal/processing/status/status.go
Normal file
52
internal/processing/status/status.go
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
package status
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Processor wraps a bunch of functions for processing statuses.
|
||||||
|
type Processor interface {
|
||||||
|
// Create processes the given form to create a new status, returning the api model representation of that status if it's OK.
|
||||||
|
Create(account *gtsmodel.Account, application *gtsmodel.Application, form *apimodel.AdvancedStatusCreateForm) (*apimodel.Status, gtserror.WithCode)
|
||||||
|
// Delete processes the delete of a given status, returning the deleted status if the delete goes through.
|
||||||
|
Delete(account *gtsmodel.Account, targetStatusID string) (*apimodel.Status, gtserror.WithCode)
|
||||||
|
// Fave processes the faving of a given status, returning the updated status if the fave goes through.
|
||||||
|
Fave(account *gtsmodel.Account, targetStatusID string) (*apimodel.Status, gtserror.WithCode)
|
||||||
|
// Boost processes the boost/reblog of a given status, returning the newly-created boost if all is well.
|
||||||
|
Boost(account *gtsmodel.Account, application *gtsmodel.Application, targetStatusID string) (*apimodel.Status, gtserror.WithCode)
|
||||||
|
// BoostedBy returns a slice of accounts that have boosted the given status, filtered according to privacy settings.
|
||||||
|
BoostedBy(account *gtsmodel.Account, targetStatusID string) ([]*apimodel.Account, gtserror.WithCode)
|
||||||
|
// FavedBy returns a slice of accounts that have liked the given status, filtered according to privacy settings.
|
||||||
|
FavedBy(account *gtsmodel.Account, targetStatusID string) ([]*apimodel.Account, gtserror.WithCode)
|
||||||
|
// Get gets the given status, taking account of privacy settings and blocks etc.
|
||||||
|
Get(account *gtsmodel.Account, targetStatusID string) (*apimodel.Status, gtserror.WithCode)
|
||||||
|
// Unfave processes the unfaving of a given status, returning the updated status if the fave goes through.
|
||||||
|
Unfave(account *gtsmodel.Account, targetStatusID string) (*apimodel.Status, gtserror.WithCode)
|
||||||
|
// Context returns the context (previous and following posts) from the given status ID
|
||||||
|
Context(account *gtsmodel.Account, targetStatusID string) (*apimodel.Context, gtserror.WithCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
type processor struct {
|
||||||
|
tc typeutils.TypeConverter
|
||||||
|
config *config.Config
|
||||||
|
db db.DB
|
||||||
|
fromClientAPI chan gtsmodel.FromClientAPI
|
||||||
|
log *logrus.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns a new status processor.
|
||||||
|
func New(db db.DB, tc typeutils.TypeConverter, config *config.Config, fromClientAPI chan gtsmodel.FromClientAPI, log *logrus.Logger) Processor {
|
||||||
|
return &processor{
|
||||||
|
tc: tc,
|
||||||
|
config: config,
|
||||||
|
db: db,
|
||||||
|
fromClientAPI: fromClientAPI,
|
||||||
|
log: log,
|
||||||
|
}
|
||||||
|
}
|
93
internal/processing/status/unfave.go
Normal file
93
internal/processing/status/unfave.go
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
package status
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (p *processor) Unfave(account *gtsmodel.Account, targetStatusID string) (*apimodel.Status, gtserror.WithCode) {
|
||||||
|
l := p.log.WithField("func", "StatusUnfave")
|
||||||
|
l.Tracef("going to search for target status %s", targetStatusID)
|
||||||
|
targetStatus := >smodel.Status{}
|
||||||
|
if err := p.db.GetByID(targetStatusID, targetStatus); err != nil {
|
||||||
|
return nil, gtserror.NewErrorNotFound(fmt.Errorf("error fetching status %s: %s", targetStatusID, err))
|
||||||
|
}
|
||||||
|
|
||||||
|
l.Tracef("going to search for target account %s", targetStatus.AccountID)
|
||||||
|
targetAccount := >smodel.Account{}
|
||||||
|
if err := p.db.GetByID(targetStatus.AccountID, targetAccount); err != nil {
|
||||||
|
return nil, gtserror.NewErrorNotFound(fmt.Errorf("error fetching target account %s: %s", targetStatus.AccountID, err))
|
||||||
|
}
|
||||||
|
|
||||||
|
l.Trace("going to get relevant accounts")
|
||||||
|
relevantAccounts, err := p.db.PullRelevantAccountsFromStatus(targetStatus)
|
||||||
|
if err != nil {
|
||||||
|
return nil, gtserror.NewErrorNotFound(fmt.Errorf("error fetching related accounts for status %s: %s", targetStatusID, err))
|
||||||
|
}
|
||||||
|
|
||||||
|
l.Trace("going to see if status is visible")
|
||||||
|
visible, err := p.db.StatusVisible(targetStatus, targetAccount, account, relevantAccounts) // requestingAccount might well be nil here, but StatusVisible knows how to take care of that
|
||||||
|
if err != nil {
|
||||||
|
return nil, gtserror.NewErrorNotFound(fmt.Errorf("error seeing if status %s is visible: %s", targetStatus.ID, err))
|
||||||
|
}
|
||||||
|
|
||||||
|
if !visible {
|
||||||
|
return nil, gtserror.NewErrorNotFound(errors.New("status is not visible"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if we actually have a fave for this status
|
||||||
|
var toUnfave bool
|
||||||
|
|
||||||
|
gtsFave := >smodel.StatusFave{}
|
||||||
|
err = p.db.GetWhere([]db.Where{{Key: "status_id", Value: targetStatus.ID}, {Key: "account_id", Value: account.ID}}, gtsFave)
|
||||||
|
if err == nil {
|
||||||
|
// we have a fave
|
||||||
|
toUnfave = true
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
// something went wrong in the db finding the fave
|
||||||
|
if _, ok := err.(db.ErrNoEntries); !ok {
|
||||||
|
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error fetching existing fave from database: %s", err))
|
||||||
|
}
|
||||||
|
// we just don't have a fave
|
||||||
|
toUnfave = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if toUnfave {
|
||||||
|
// we had a fave, so take some action to get rid of it
|
||||||
|
_, err = p.db.UnfaveStatus(targetStatus, account.ID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error unfaveing status: %s", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
// send the unfave through the processor channel for federation etc
|
||||||
|
p.fromClientAPI <- gtsmodel.FromClientAPI{
|
||||||
|
APObjectType: gtsmodel.ActivityStreamsLike,
|
||||||
|
APActivityType: gtsmodel.ActivityStreamsUndo,
|
||||||
|
GTSModel: gtsFave,
|
||||||
|
OriginAccount: account,
|
||||||
|
TargetAccount: targetAccount,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// return the status (whatever its state) back to the caller
|
||||||
|
var boostOfStatus *gtsmodel.Status
|
||||||
|
if targetStatus.BoostOfID != "" {
|
||||||
|
boostOfStatus = >smodel.Status{}
|
||||||
|
if err := p.db.GetByID(targetStatus.BoostOfID, boostOfStatus); err != nil {
|
||||||
|
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error fetching boosted status %s: %s", targetStatus.BoostOfID, err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mastoStatus, err := p.tc.StatusToMasto(targetStatus, targetAccount, account, relevantAccounts.BoostedAccount, relevantAccounts.ReplyToAccount, boostOfStatus)
|
||||||
|
if err != nil {
|
||||||
|
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting status %s to frontend representation: %s", targetStatus.ID, err))
|
||||||
|
}
|
||||||
|
|
||||||
|
return mastoStatus, nil
|
||||||
|
}
|
230
internal/processing/status/util.go
Normal file
230
internal/processing/status/util.go
Normal file
@ -0,0 +1,230 @@
|
|||||||
|
package status
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (p *processor) processVisibility(form *apimodel.AdvancedStatusCreateForm, accountDefaultVis gtsmodel.Visibility, status *gtsmodel.Status) error {
|
||||||
|
// by default all flags are set to true
|
||||||
|
gtsAdvancedVis := >smodel.VisibilityAdvanced{
|
||||||
|
Federated: true,
|
||||||
|
Boostable: true,
|
||||||
|
Replyable: true,
|
||||||
|
Likeable: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
var gtsBasicVis gtsmodel.Visibility
|
||||||
|
// Advanced takes priority if it's set.
|
||||||
|
// If it's not set, take whatever masto visibility is set.
|
||||||
|
// If *that's* not set either, then just take the account default.
|
||||||
|
// If that's also not set, take the default for the whole instance.
|
||||||
|
if form.VisibilityAdvanced != nil {
|
||||||
|
gtsBasicVis = gtsmodel.Visibility(*form.VisibilityAdvanced)
|
||||||
|
} else if form.Visibility != "" {
|
||||||
|
gtsBasicVis = p.tc.MastoVisToVis(form.Visibility)
|
||||||
|
} else if accountDefaultVis != "" {
|
||||||
|
gtsBasicVis = accountDefaultVis
|
||||||
|
} else {
|
||||||
|
gtsBasicVis = gtsmodel.VisibilityDefault
|
||||||
|
}
|
||||||
|
|
||||||
|
switch gtsBasicVis {
|
||||||
|
case gtsmodel.VisibilityPublic:
|
||||||
|
// for public, there's no need to change any of the advanced flags from true regardless of what the user filled out
|
||||||
|
break
|
||||||
|
case gtsmodel.VisibilityUnlocked:
|
||||||
|
// for unlocked the user can set any combination of flags they like so look at them all to see if they're set and then apply them
|
||||||
|
if form.Federated != nil {
|
||||||
|
gtsAdvancedVis.Federated = *form.Federated
|
||||||
|
}
|
||||||
|
|
||||||
|
if form.Boostable != nil {
|
||||||
|
gtsAdvancedVis.Boostable = *form.Boostable
|
||||||
|
}
|
||||||
|
|
||||||
|
if form.Replyable != nil {
|
||||||
|
gtsAdvancedVis.Replyable = *form.Replyable
|
||||||
|
}
|
||||||
|
|
||||||
|
if form.Likeable != nil {
|
||||||
|
gtsAdvancedVis.Likeable = *form.Likeable
|
||||||
|
}
|
||||||
|
|
||||||
|
case gtsmodel.VisibilityFollowersOnly, gtsmodel.VisibilityMutualsOnly:
|
||||||
|
// for followers or mutuals only, boostable will *always* be false, but the other fields can be set so check and apply them
|
||||||
|
gtsAdvancedVis.Boostable = false
|
||||||
|
|
||||||
|
if form.Federated != nil {
|
||||||
|
gtsAdvancedVis.Federated = *form.Federated
|
||||||
|
}
|
||||||
|
|
||||||
|
if form.Replyable != nil {
|
||||||
|
gtsAdvancedVis.Replyable = *form.Replyable
|
||||||
|
}
|
||||||
|
|
||||||
|
if form.Likeable != nil {
|
||||||
|
gtsAdvancedVis.Likeable = *form.Likeable
|
||||||
|
}
|
||||||
|
|
||||||
|
case gtsmodel.VisibilityDirect:
|
||||||
|
// direct is pretty easy: there's only one possible setting so return it
|
||||||
|
gtsAdvancedVis.Federated = true
|
||||||
|
gtsAdvancedVis.Boostable = false
|
||||||
|
gtsAdvancedVis.Federated = true
|
||||||
|
gtsAdvancedVis.Likeable = true
|
||||||
|
}
|
||||||
|
|
||||||
|
status.Visibility = gtsBasicVis
|
||||||
|
status.VisibilityAdvanced = gtsAdvancedVis
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *processor) processReplyToID(form *apimodel.AdvancedStatusCreateForm, thisAccountID string, status *gtsmodel.Status) error {
|
||||||
|
if form.InReplyToID == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this status is a reply to another status, we need to do a bit of work to establish whether or not this status can be posted:
|
||||||
|
//
|
||||||
|
// 1. Does the replied status exist in the database?
|
||||||
|
// 2. Is the replied status marked as replyable?
|
||||||
|
// 3. Does a block exist between either the current account or the account that posted the status it's replying to?
|
||||||
|
//
|
||||||
|
// If this is all OK, then we fetch the repliedStatus and the repliedAccount for later processing.
|
||||||
|
repliedStatus := >smodel.Status{}
|
||||||
|
repliedAccount := >smodel.Account{}
|
||||||
|
// check replied status exists + is replyable
|
||||||
|
if err := p.db.GetByID(form.InReplyToID, repliedStatus); err != nil {
|
||||||
|
if _, ok := err.(db.ErrNoEntries); ok {
|
||||||
|
return fmt.Errorf("status with id %s not replyable because it doesn't exist", form.InReplyToID)
|
||||||
|
}
|
||||||
|
return fmt.Errorf("status with id %s not replyable: %s", form.InReplyToID, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if repliedStatus.VisibilityAdvanced != nil {
|
||||||
|
if !repliedStatus.VisibilityAdvanced.Replyable {
|
||||||
|
return fmt.Errorf("status with id %s is marked as not replyable", form.InReplyToID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check replied account is known to us
|
||||||
|
if err := p.db.GetByID(repliedStatus.AccountID, repliedAccount); err != nil {
|
||||||
|
if _, ok := err.(db.ErrNoEntries); ok {
|
||||||
|
return fmt.Errorf("status with id %s not replyable because account id %s is not known", form.InReplyToID, repliedStatus.AccountID)
|
||||||
|
}
|
||||||
|
return fmt.Errorf("status with id %s not replyable: %s", form.InReplyToID, err)
|
||||||
|
}
|
||||||
|
// check if a block exists
|
||||||
|
if blocked, err := p.db.Blocked(thisAccountID, repliedAccount.ID); err != nil {
|
||||||
|
if _, ok := err.(db.ErrNoEntries); !ok {
|
||||||
|
return fmt.Errorf("status with id %s not replyable: %s", form.InReplyToID, err)
|
||||||
|
}
|
||||||
|
} else if blocked {
|
||||||
|
return fmt.Errorf("status with id %s not replyable", form.InReplyToID)
|
||||||
|
}
|
||||||
|
status.InReplyToID = repliedStatus.ID
|
||||||
|
status.InReplyToAccountID = repliedAccount.ID
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *processor) processMediaIDs(form *apimodel.AdvancedStatusCreateForm, thisAccountID string, status *gtsmodel.Status) error {
|
||||||
|
if form.MediaIDs == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
gtsMediaAttachments := []*gtsmodel.MediaAttachment{}
|
||||||
|
attachments := []string{}
|
||||||
|
for _, mediaID := range form.MediaIDs {
|
||||||
|
// check these attachments exist
|
||||||
|
a := >smodel.MediaAttachment{}
|
||||||
|
if err := p.db.GetByID(mediaID, a); err != nil {
|
||||||
|
return fmt.Errorf("invalid media type or media not found for media id %s", mediaID)
|
||||||
|
}
|
||||||
|
// check they belong to the requesting account id
|
||||||
|
if a.AccountID != thisAccountID {
|
||||||
|
return fmt.Errorf("media with id %s does not belong to account %s", mediaID, thisAccountID)
|
||||||
|
}
|
||||||
|
// check they're not already used in a status
|
||||||
|
if a.StatusID != "" || a.ScheduledStatusID != "" {
|
||||||
|
return fmt.Errorf("media with id %s is already attached to a status", mediaID)
|
||||||
|
}
|
||||||
|
gtsMediaAttachments = append(gtsMediaAttachments, a)
|
||||||
|
attachments = append(attachments, a.ID)
|
||||||
|
}
|
||||||
|
status.GTSMediaAttachments = gtsMediaAttachments
|
||||||
|
status.Attachments = attachments
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *processor) processLanguage(form *apimodel.AdvancedStatusCreateForm, accountDefaultLanguage string, status *gtsmodel.Status) error {
|
||||||
|
if form.Language != "" {
|
||||||
|
status.Language = form.Language
|
||||||
|
} else {
|
||||||
|
status.Language = accountDefaultLanguage
|
||||||
|
}
|
||||||
|
if status.Language == "" {
|
||||||
|
return errors.New("no language given either in status create form or account default")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *processor) processMentions(form *apimodel.AdvancedStatusCreateForm, accountID string, status *gtsmodel.Status) error {
|
||||||
|
menchies := []string{}
|
||||||
|
gtsMenchies, err := p.db.MentionStringsToMentions(util.DeriveMentionsFromStatus(form.Status), accountID, status.ID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error generating mentions from status: %s", err)
|
||||||
|
}
|
||||||
|
for _, menchie := range gtsMenchies {
|
||||||
|
if err := p.db.Put(menchie); err != nil {
|
||||||
|
return fmt.Errorf("error putting mentions in db: %s", err)
|
||||||
|
}
|
||||||
|
menchies = append(menchies, menchie.ID)
|
||||||
|
}
|
||||||
|
// add full populated gts menchies to the status for passing them around conveniently
|
||||||
|
status.GTSMentions = gtsMenchies
|
||||||
|
// add just the ids of the mentioned accounts to the status for putting in the db
|
||||||
|
status.Mentions = menchies
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *processor) processTags(form *apimodel.AdvancedStatusCreateForm, accountID string, status *gtsmodel.Status) error {
|
||||||
|
tags := []string{}
|
||||||
|
gtsTags, err := p.db.TagStringsToTags(util.DeriveHashtagsFromStatus(form.Status), accountID, status.ID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error generating hashtags from status: %s", err)
|
||||||
|
}
|
||||||
|
for _, tag := range gtsTags {
|
||||||
|
if err := p.db.Upsert(tag, "name"); err != nil {
|
||||||
|
return fmt.Errorf("error putting tags in db: %s", err)
|
||||||
|
}
|
||||||
|
tags = append(tags, tag.ID)
|
||||||
|
}
|
||||||
|
// add full populated gts tags to the status for passing them around conveniently
|
||||||
|
status.GTSTags = gtsTags
|
||||||
|
// add just the ids of the used tags to the status for putting in the db
|
||||||
|
status.Tags = tags
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *processor) processEmojis(form *apimodel.AdvancedStatusCreateForm, accountID string, status *gtsmodel.Status) error {
|
||||||
|
emojis := []string{}
|
||||||
|
gtsEmojis, err := p.db.EmojiStringsToEmojis(util.DeriveEmojisFromStatus(form.Status), accountID, status.ID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error generating emojis from status: %s", err)
|
||||||
|
}
|
||||||
|
for _, e := range gtsEmojis {
|
||||||
|
emojis = append(emojis, e.ID)
|
||||||
|
}
|
||||||
|
// add full populated gts emojis to the status for passing them around conveniently
|
||||||
|
status.GTSEmojis = gtsEmojis
|
||||||
|
// add just the ids of the used emojis to the status for putting in the db
|
||||||
|
status.Emojis = emojis
|
||||||
|
return nil
|
||||||
|
}
|
@ -25,29 +25,30 @@ import (
|
|||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
"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/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (p *processor) HomeTimelineGet(authed *oauth.Auth, maxID string, sinceID string, minID string, limit int, local bool) ([]*apimodel.Status, ErrorWithCode) {
|
func (p *processor) HomeTimelineGet(authed *oauth.Auth, maxID string, sinceID string, minID string, limit int, local bool) ([]*apimodel.Status, gtserror.WithCode) {
|
||||||
|
|
||||||
statuses, err := p.timelineManager.HomeTimeline(authed.Account.ID, maxID, sinceID, minID, limit, local)
|
statuses, err := p.timelineManager.HomeTimeline(authed.Account.ID, maxID, sinceID, minID, limit, local)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, NewErrorInternalError(err)
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return statuses, nil
|
return statuses, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *processor) PublicTimelineGet(authed *oauth.Auth, maxID string, sinceID string, minID string, limit int, local bool) ([]*apimodel.Status, ErrorWithCode) {
|
func (p *processor) PublicTimelineGet(authed *oauth.Auth, maxID string, sinceID string, minID string, limit int, local bool) ([]*apimodel.Status, gtserror.WithCode) {
|
||||||
statuses, err := p.db.GetPublicTimelineForAccount(authed.Account.ID, maxID, sinceID, minID, limit, local)
|
statuses, err := p.db.GetPublicTimelineForAccount(authed.Account.ID, maxID, sinceID, minID, limit, local)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, NewErrorInternalError(err)
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
s, err := p.filterStatuses(authed, statuses)
|
s, err := p.filterStatuses(authed, statuses)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, NewErrorInternalError(err)
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return s, nil
|
return s, nil
|
||||||
@ -64,7 +65,7 @@ func (p *processor) filterStatuses(authed *oauth.Auth, statuses []*gtsmodel.Stat
|
|||||||
l.Debugf("skipping status %s because account %s can't be found in the db", s.ID, s.AccountID)
|
l.Debugf("skipping status %s because account %s can't be found in the db", s.ID, s.AccountID)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
return nil, NewErrorInternalError(fmt.Errorf("HomeTimelineGet: error getting status author: %s", err))
|
return nil, gtserror.NewErrorInternalError(fmt.Errorf("HomeTimelineGet: error getting status author: %s", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
relevantAccounts, err := p.db.PullRelevantAccountsFromStatus(s)
|
relevantAccounts, err := p.db.PullRelevantAccountsFromStatus(s)
|
||||||
@ -75,7 +76,7 @@ func (p *processor) filterStatuses(authed *oauth.Auth, statuses []*gtsmodel.Stat
|
|||||||
|
|
||||||
visible, err := p.db.StatusVisible(s, targetAccount, authed.Account, relevantAccounts)
|
visible, err := p.db.StatusVisible(s, targetAccount, authed.Account, relevantAccounts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, NewErrorInternalError(fmt.Errorf("HomeTimelineGet: error checking status visibility: %s", err))
|
return nil, gtserror.NewErrorInternalError(fmt.Errorf("HomeTimelineGet: error checking status visibility: %s", err))
|
||||||
}
|
}
|
||||||
if !visible {
|
if !visible {
|
||||||
continue
|
continue
|
||||||
@ -89,7 +90,7 @@ func (p *processor) filterStatuses(authed *oauth.Auth, statuses []*gtsmodel.Stat
|
|||||||
l.Debugf("skipping status %s because status %s can't be found in the db", s.ID, s.BoostOfID)
|
l.Debugf("skipping status %s because status %s can't be found in the db", s.ID, s.BoostOfID)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
return nil, NewErrorInternalError(fmt.Errorf("HomeTimelineGet: error getting boosted status: %s", err))
|
return nil, gtserror.NewErrorInternalError(fmt.Errorf("HomeTimelineGet: error getting boosted status: %s", err))
|
||||||
}
|
}
|
||||||
boostedRelevantAccounts, err := p.db.PullRelevantAccountsFromStatus(bs)
|
boostedRelevantAccounts, err := p.db.PullRelevantAccountsFromStatus(bs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -99,7 +100,7 @@ func (p *processor) filterStatuses(authed *oauth.Auth, statuses []*gtsmodel.Stat
|
|||||||
|
|
||||||
boostedVisible, err := p.db.StatusVisible(bs, relevantAccounts.BoostedAccount, authed.Account, boostedRelevantAccounts)
|
boostedVisible, err := p.db.StatusVisible(bs, relevantAccounts.BoostedAccount, authed.Account, boostedRelevantAccounts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, NewErrorInternalError(fmt.Errorf("HomeTimelineGet: error checking boosted status visibility: %s", err))
|
return nil, gtserror.NewErrorInternalError(fmt.Errorf("HomeTimelineGet: error checking boosted status visibility: %s", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
if boostedVisible {
|
if boostedVisible {
|
||||||
|
Loading…
Reference in New Issue
Block a user