more fiddling

This commit is contained in:
tsmethurst 2021-08-17 19:03:41 +02:00
commit a3322b2bf3
65 changed files with 712 additions and 508 deletions

View file

@ -581,7 +581,7 @@ func ExtractMention(i Mentionable) (*gtsmodel.Mention, error) {
if hrefProp == nil || !hrefProp.IsIRI() { if hrefProp == nil || !hrefProp.IsIRI() {
return nil, errors.New("no href prop") return nil, errors.New("no href prop")
} }
mention.MentionedAccountURI = hrefProp.GetIRI().String() mention.TargetAccountURI = hrefProp.GetIRI().String()
return mention, nil return mention, nil
} }

View file

@ -1,3 +1,21 @@
/*
GoToSocial
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package db package db
import "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" import "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"

View file

@ -1,3 +1,21 @@
/*
GoToSocial
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package db package db
import ( import (

View file

@ -1,3 +1,21 @@
/*
GoToSocial
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package db package db
import "context" import "context"
@ -11,6 +29,10 @@ type Basic interface {
// For implementations that don't use tables, this can just return nil. // For implementations that don't use tables, this can just return nil.
DropTable(i interface{}) DBError DropTable(i interface{}) DBError
// RegisterTable registers a table for use in many2many relations.
// For implementations that don't use tables, or many2many relations, this can just return nil.
RegisterTable(i interface{}) DBError
// Stop should stop and close the database connection cleanly, returning an error if this is not possible. // Stop should stop and close the database connection cleanly, returning an error if this is not possible.
// If the database implementation doesn't need to be stopped, this can just return nil. // If the database implementation doesn't need to be stopped, this can just return nil.
Stop(ctx context.Context) DBError Stop(ctx context.Context) DBError

View file

@ -35,6 +35,7 @@ type DB interface {
Admin Admin
Basic Basic
Instance Instance
Mention
Notification Notification
Relationship Relationship
Status Status

View file

@ -1,3 +1,21 @@
/*
GoToSocial
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package db package db
import "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" import "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"

29
internal/db/mention.go Normal file
View file

@ -0,0 +1,29 @@
/*
GoToSocial
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package db
import "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
type Mention interface {
// GetMention gets a single mention by ID
GetMention(id string) (*gtsmodel.Mention, DBError)
// GetMentions gets multiple mentions.
GetMentions(ids []string) ([]*gtsmodel.Mention, DBError)
}

View file

@ -1,3 +1,21 @@
/*
GoToSocial
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package db package db
import "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" import "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"

View file

@ -185,6 +185,11 @@ func (b *basicDB) DropTable(i interface{}) db.DBError {
}) })
} }
func (b *basicDB) RegisterTable(i interface{}) db.DBError {
orm.RegisterTable(i)
return nil
}
func (b *basicDB) IsHealthy(ctx context.Context) db.DBError { func (b *basicDB) IsHealthy(ctx context.Context) db.DBError {
return b.conn.Ping(ctx) return b.conn.Ping(ctx)
} }

77
internal/db/pg/mention.go Normal file
View file

@ -0,0 +1,77 @@
/*
GoToSocial
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package pg
import (
"context"
"github.com/go-pg/pg/v10"
"github.com/go-pg/pg/v10/orm"
"github.com/sirupsen/logrus"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
)
type mentionDB struct {
config *config.Config
conn *pg.DB
log *logrus.Logger
cancel context.CancelFunc
}
func (m *mentionDB) newMentionQ(i interface{}) *orm.Query {
return m.conn.Model(i).
Relation("Status").
Relation("OriginAccount").
Relation("TargetAccount")
}
func (m *mentionDB) processResponse(mention *gtsmodel.Mention, err error) (*gtsmodel.Mention, db.DBError) {
switch err {
case pg.ErrNoRows:
return nil, db.ErrNoEntries
case nil:
return mention, nil
default:
return nil, err
}
}
func (m *mentionDB) GetMention(id string) (*gtsmodel.Mention, db.DBError) {
mention := &gtsmodel.Mention{}
q := m.newMentionQ(mention).
Where("mention.id = ?", id)
return m.processResponse(mention, q.Select())
}
func (m *mentionDB) GetMentions(ids []string) ([]*gtsmodel.Mention, db.DBError) {
mentions := []*gtsmodel.Mention{}
q := m.newMentionQ(mentions).
Where("mention.id in (?)", pg.In(ids))
if err := q.Select(); err != nil {
return nil, err
}
return mentions, nil
}

View file

@ -44,6 +44,7 @@ type postgresService struct {
db.Admin db.Admin
db.Basic db.Basic
db.Instance db.Instance
db.Mention
db.Notification db.Notification
db.Relationship db.Relationship
db.Status db.Status
@ -116,6 +117,12 @@ func NewPostgresService(ctx context.Context, c *config.Config, log *logrus.Logge
log: log, log: log,
cancel: cancel, cancel: cancel,
}, },
Mention: &mentionDB{
config: c,
conn: conn,
log: log,
cancel: cancel,
},
Relationship: &relationshipDB{ Relationship: &relationshipDB{
config: c, config: c,
conn: conn, conn: conn,
@ -313,14 +320,14 @@ func (ps *postgresService) MentionStringsToMentions(targetAccounts []string, ori
// id, createdAt and updatedAt will be populated by the db, so we have everything we need! // id, createdAt and updatedAt will be populated by the db, so we have everything we need!
menchies = append(menchies, &gtsmodel.Mention{ menchies = append(menchies, &gtsmodel.Mention{
StatusID: statusID, StatusID: statusID,
OriginAccountID: ogAccount.ID, OriginAccountID: ogAccount.ID,
OriginAccountURI: ogAccount.URI, OriginAccountURI: ogAccount.URI,
TargetAccountID: mentionedAccount.ID, TargetAccountID: mentionedAccount.ID,
NameString: a, NameString: a,
MentionedAccountURI: mentionedAccount.URI, TargetAccountURI: mentionedAccount.URI,
MentionedAccountURL: mentionedAccount.URL, TargetAccountURL: mentionedAccount.URL,
GTSAccount: mentionedAccount, OriginAccount: mentionedAccount,
}) })
} }
return menchies, nil return menchies, nil

View file

@ -23,6 +23,7 @@ import (
"fmt" "fmt"
"github.com/go-pg/pg/v10" "github.com/go-pg/pg/v10"
"github.com/go-pg/pg/v10/orm"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/superseriousbusiness/gotosocial/internal/config" "github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/db"
@ -36,21 +37,41 @@ type relationshipDB struct {
cancel context.CancelFunc cancel context.CancelFunc
} }
func (r *relationshipDB) Blocked(account1 string, account2 string) (bool, db.DBError) { func (r *relationshipDB) newBlockQ(block *gtsmodel.Block) *orm.Query {
// TODO: check domain blocks as well return r.conn.Model(block).
var blocked bool Relation("Account").
if err := r.conn.Model(&gtsmodel.Block{}). Relation("TargetAccount")
Where("account_id = ?", account1).Where("target_account_id = ?", account2). }
WhereOr("target_account_id = ?", account1).Where("account_id = ?", account2).
Select(); err != nil { func (r *relationshipDB) processResponse(block *gtsmodel.Block, err error) (*gtsmodel.Block, db.DBError) {
if err == pg.ErrNoRows { switch err {
blocked = false case pg.ErrNoRows:
return blocked, nil return nil, db.ErrNoEntries
} case nil:
return blocked, err return block, nil
default:
return nil, err
} }
blocked = true }
return blocked, nil
func (r *relationshipDB) Blocked(account1 string, account2 string, eitherDirection bool) (bool, db.DBError) {
q := r.conn.Model(&gtsmodel.Block{}).Where("account_id = ?", account1).Where("target_account_id = ?", account2)
if eitherDirection {
q = q.WhereOr("target_account_id = ?", account1).Where("account_id = ?", account2)
}
return q.Exists()
}
func (r *relationshipDB) GetBlock(account1 string, account2 string) (*gtsmodel.Block, db.DBError) {
block := &gtsmodel.Block{}
q := r.newBlockQ(block).
Where("block.account_id = ?", account1).
Where("block.target_account_id = ?", account2)
return r.processResponse(block, q.Select())
} }
func (r *relationshipDB) GetRelationship(requestingAccount string, targetAccount string) (*gtsmodel.Relationship, db.DBError) { func (r *relationshipDB) GetRelationship(requestingAccount string, targetAccount string) (*gtsmodel.Relationship, db.DBError) {

View file

@ -40,6 +40,10 @@ type statusDB struct {
func (s *statusDB) newStatusQ(status *gtsmodel.Status) *orm.Query { func (s *statusDB) newStatusQ(status *gtsmodel.Status) *orm.Query {
return s.conn.Model(status). return s.conn.Model(status).
Relation("Attachments").
Relation("Tags").
Relation("Mentions").
Relation("Emojis").
Relation("Account"). Relation("Account").
Relation("InReplyTo"). Relation("InReplyTo").
Relation("InReplyToAccount"). Relation("InReplyToAccount").
@ -48,7 +52,7 @@ func (s *statusDB) newStatusQ(status *gtsmodel.Status) *orm.Query {
Relation("CreatedWithApplication") Relation("CreatedWithApplication")
} }
func (s *statusDB) processResponse(status *gtsmodel.Status, err error) (*gtsmodel.Status, db.DBError) { func (s *statusDB) processStatusResponse(status *gtsmodel.Status, err error) (*gtsmodel.Status, db.DBError) {
switch err { switch err {
case pg.ErrNoRows: case pg.ErrNoRows:
return nil, db.ErrNoEntries return nil, db.ErrNoEntries
@ -65,7 +69,7 @@ func (s *statusDB) GetStatusByID(id string) (*gtsmodel.Status, db.DBError) {
q := s.newStatusQ(status). q := s.newStatusQ(status).
Where("status.id = ?", id) Where("status.id = ?", id)
return s.processResponse(status, q.Select()) return s.processStatusResponse(status, q.Select())
} }
func (s *statusDB) GetStatusByURI(uri string) (*gtsmodel.Status, db.DBError) { func (s *statusDB) GetStatusByURI(uri string) (*gtsmodel.Status, db.DBError) {
@ -74,7 +78,7 @@ func (s *statusDB) GetStatusByURI(uri string) (*gtsmodel.Status, db.DBError) {
q := s.newStatusQ(status). q := s.newStatusQ(status).
Where("LOWER(status.uri) = LOWER(?)", uri) Where("LOWER(status.uri) = LOWER(?)", uri)
return s.processResponse(status, q.Select()) return s.processStatusResponse(status, q.Select())
} }
func (s *statusDB) StatusParents(status *gtsmodel.Status, onlyDirect bool) ([]*gtsmodel.Status, db.DBError) { func (s *statusDB) StatusParents(status *gtsmodel.Status, onlyDirect bool) ([]*gtsmodel.Status, db.DBError) {

View file

@ -80,7 +80,18 @@ func (suite *PGStandardTestSuite) TestGetStatusByURI() {
suite.Nil(status.InReplyTo) suite.Nil(status.InReplyTo)
suite.Nil(status.InReplyToAccount) suite.Nil(status.InReplyToAccount)
} }
func (suite *PGStandardTestSuite) TestGetStatusWithExtras() {
status, err := suite.db.GetStatusByID(suite.testStatuses["admin_account_status_1"].ID)
if err != nil {
suite.FailNow(err.Error())
}
suite.NotNil(status)
suite.NotNil(status.Account)
suite.NotNil(status.CreatedWithApplication)
suite.NotEmpty(status.Tags)
suite.NotEmpty(status.Attachments)
suite.NotEmpty(status.Emojis)
}
func TestStatusTestSuite(t *testing.T) { func TestStatusTestSuite(t *testing.T) {
suite.Run(t, new(PGStandardTestSuite)) suite.Run(t, new(PGStandardTestSuite))
} }

View file

@ -1,11 +1,35 @@
/*
GoToSocial
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package db package db
import "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" import "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
type Relationship interface { type Relationship interface {
// Blocked checks whether a block exists in eiher direction between two accounts. // Blocked checks whether account 1 has a block in place against block2.
// That is, it returns true if account1 blocks account2, OR if account2 blocks account1. // If eitherDirection is true, then the function returns true if account1 blocks account2, OR if account2 blocks account1.
Blocked(account1 string, account2 string) (bool, DBError) Blocked(account1 string, account2 string, eitherDirection bool) (bool, DBError)
// GetBlock returns the block from account1 targeting account2, if it exists, or an error if it doesn't.
//
// Because this is slower than Blocked, only use it if you need the actual Block struct for some reason,
// not if you're just checking for the existence of a block.
GetBlock(account1 string, account2 string) (*gtsmodel.Block, DBError)
// GetRelationship retrieves the relationship of the targetAccount to the requestingAccount. // GetRelationship retrieves the relationship of the targetAccount to the requestingAccount.
GetRelationship(requestingAccount string, targetAccount string) (*gtsmodel.Relationship, DBError) GetRelationship(requestingAccount string, targetAccount string) (*gtsmodel.Relationship, DBError)

View file

@ -1,3 +1,21 @@
/*
GoToSocial
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package db package db
import "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" import "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"

View file

@ -1,3 +1,21 @@
/*
GoToSocial
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package db package db
import "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" import "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"

View file

@ -276,7 +276,7 @@ func (d *deref) populateStatusFields(status *gtsmodel.Status, requestingUsername
// * the remote URL (a.RemoteURL) // * the remote URL (a.RemoteURL)
// This should be enough to pass along to the media processor. // This should be enough to pass along to the media processor.
attachmentIDs := []string{} attachmentIDs := []string{}
for _, a := range status.GTSMediaAttachments { for _, a := range status.Attachments {
l.Tracef("dereferencing attachment: %+v", a) l.Tracef("dereferencing attachment: %+v", a)
// it might have been processed elsewhere so check first if it's already in the database or not // it might have been processed elsewhere so check first if it's already in the database or not
@ -307,7 +307,7 @@ func (d *deref) populateStatusFields(status *gtsmodel.Status, requestingUsername
} }
attachmentIDs = append(attachmentIDs, deferencedAttachment.ID) attachmentIDs = append(attachmentIDs, deferencedAttachment.ID)
} }
status.Attachments = attachmentIDs status.AttachmentIDs = attachmentIDs
// 2. Hashtags // 2. Hashtags
@ -318,7 +318,7 @@ func (d *deref) populateStatusFields(status *gtsmodel.Status, requestingUsername
// //
// We should dereference any accounts mentioned here which we don't have in our db yet, by their URI. // We should dereference any accounts mentioned here which we don't have in our db yet, by their URI.
mentions := []string{} mentions := []string{}
for _, m := range status.GTSMentions { for _, m := range status.Mentions {
if m.ID != "" { if m.ID != "" {
continue continue
@ -331,9 +331,9 @@ func (d *deref) populateStatusFields(status *gtsmodel.Status, requestingUsername
} }
m.ID = mID m.ID = mID
uri, err := url.Parse(m.MentionedAccountURI) uri, err := url.Parse(m.TargetAccountURI)
if err != nil { if err != nil {
l.Debugf("error parsing mentioned account uri %s: %s", m.MentionedAccountURI, err) l.Debugf("error parsing mentioned account uri %s: %s", m.TargetAccountURI, err)
continue continue
} }
@ -353,7 +353,7 @@ func (d *deref) populateStatusFields(status *gtsmodel.Status, requestingUsername
} }
mentions = append(mentions, m.ID) mentions = append(mentions, m.ID)
} }
status.Mentions = mentions status.MentionIDs = mentions
// status has replyToURI but we don't have an ID yet for the status it replies to // status has replyToURI but we don't have an ID yet for the status it replies to
if status.InReplyToURI != "" && status.InReplyToID == "" { if status.InReplyToURI != "" && status.InReplyToID == "" {

View file

@ -32,6 +32,7 @@ type DomainBlock struct {
UpdatedAt time.Time `pg:"type:timestamp,notnull,default:now()"` UpdatedAt time.Time `pg:"type:timestamp,notnull,default:now()"`
// Account ID of the creator of this block // Account ID of the creator of this block
CreatedByAccountID string `pg:"type:CHAR(26),notnull"` CreatedByAccountID string `pg:"type:CHAR(26),notnull"`
CreatedByAccount *Account `pg:"rel:belongs-to"`
// Private comment on this block, viewable to admins // Private comment on this block, viewable to admins
PrivateComment string PrivateComment string
// Public comment on this block, viewable (optionally) by everyone // Public comment on this block, viewable (optionally) by everyone

View file

@ -32,4 +32,5 @@ type EmailDomainBlock struct {
UpdatedAt time.Time `pg:"type:timestamp,notnull,default:now()"` UpdatedAt time.Time `pg:"type:timestamp,notnull,default:now()"`
// Account ID of the creator of this block // Account ID of the creator of this block
CreatedByAccountID string `pg:"type:CHAR(26),notnull"` CreatedByAccountID string `pg:"type:CHAR(26),notnull"`
CreatedByAccount *Account `pg:"rel:belongs-to"`
} }

View file

@ -73,5 +73,6 @@ type Emoji struct {
// Is this emoji visible in the admin emoji picker? // Is this emoji visible in the admin emoji picker?
VisibleInPicker bool `pg:",notnull,default:true"` VisibleInPicker bool `pg:",notnull,default:true"`
// In which emoji category is this emoji visible? // In which emoji category is this emoji visible?
CategoryID string `pg:"type:CHAR(26)"` CategoryID string `pg:"type:CHAR(26)"`
Status *Status `pg:"rel:belongs-to"`
} }

View file

@ -30,8 +30,10 @@ type Follow struct {
UpdatedAt time.Time `pg:"type:timestamp,notnull,default:now()"` UpdatedAt time.Time `pg:"type:timestamp,notnull,default:now()"`
// Who does this follow belong to? // Who does this follow belong to?
AccountID string `pg:"type:CHAR(26),unique:srctarget,notnull"` AccountID string `pg:"type:CHAR(26),unique:srctarget,notnull"`
Account *Account `pg:"rel:belongs-to"`
// Who does AccountID follow? // Who does AccountID follow?
TargetAccountID string `pg:"type:CHAR(26),unique:srctarget,notnull"` TargetAccountID string `pg:"type:CHAR(26),unique:srctarget,notnull"`
TargetAccount *Account `pg:"rel:has-one"`
// Does this follow also want to see reblogs and not just posts? // Does this follow also want to see reblogs and not just posts?
ShowReblogs bool `pg:"default:true"` ShowReblogs bool `pg:"default:true"`
// What is the activitypub URI of this follow? // What is the activitypub URI of this follow?

View file

@ -29,9 +29,11 @@ type FollowRequest struct {
// When was this follow request last updated? // When was this follow request last updated?
UpdatedAt time.Time `pg:"type:timestamp,notnull,default:now()"` UpdatedAt time.Time `pg:"type:timestamp,notnull,default:now()"`
// Who does this follow request originate from? // Who does this follow request originate from?
AccountID string `pg:"type:CHAR(26),unique:srctarget,notnull"` AccountID string `pg:"type:CHAR(26),unique:srctarget,notnull"`
Account Account `pg:"rel:has-one"`
// Who is the target of this follow request? // Who is the target of this follow request?
TargetAccountID string `pg:"type:CHAR(26),unique:srctarget,notnull"` TargetAccountID string `pg:"type:CHAR(26),unique:srctarget,notnull"`
TargetAccount Account `pg:"rel:has-one"`
// Does this follow also want to see reblogs and not just posts? // Does this follow also want to see reblogs and not just posts?
ShowReblogs bool `pg:"default:true"` ShowReblogs bool `pg:"default:true"`
// What is the activitypub URI of this follow request? // What is the activitypub URI of this follow request?

View file

@ -43,6 +43,7 @@ type MediaAttachment struct {
FileMeta FileMeta FileMeta FileMeta
// To which account does this attachment belong // To which account does this attachment belong
AccountID string `pg:"type:CHAR(26),notnull"` AccountID string `pg:"type:CHAR(26),notnull"`
Account *Account `pg:"rel:belongs-to"`
// Description of the attachment (for screenreaders) // Description of the attachment (for screenreaders)
Description string Description string
// To which scheduled status does this attachment belong // To which scheduled status does this attachment belong

View file

@ -26,16 +26,19 @@ type Mention struct {
ID string `pg:"type:CHAR(26),pk,notnull,unique"` ID string `pg:"type:CHAR(26),pk,notnull,unique"`
// ID of the status this mention originates from // ID of the status this mention originates from
StatusID string `pg:"type:CHAR(26),notnull"` StatusID string `pg:"type:CHAR(26),notnull"`
Status *Status `pg:"rel:belongs-to"`
// When was this mention created? // When was this mention created?
CreatedAt time.Time `pg:"type:timestamp,notnull,default:now()"` CreatedAt time.Time `pg:"type:timestamp,notnull,default:now()"`
// When was this mention last updated? // When was this mention last updated?
UpdatedAt time.Time `pg:"type:timestamp,notnull,default:now()"` UpdatedAt time.Time `pg:"type:timestamp,notnull,default:now()"`
// What's the internal account ID of the originator of the mention? // What's the internal account ID of the originator of the mention?
OriginAccountID string `pg:"type:CHAR(26),notnull"` OriginAccountID string `pg:"type:CHAR(26),notnull"`
OriginAccount *Account `pg:"rel:has-one"`
// What's the AP URI of the originator of the mention? // What's the AP URI of the originator of the mention?
OriginAccountURI string `pg:",notnull"` OriginAccountURI string `pg:",notnull"`
// What's the internal account ID of the mention target? // What's the internal account ID of the mention target?
TargetAccountID string `pg:"type:CHAR(26),notnull"` TargetAccountID string `pg:"type:CHAR(26),notnull"`
TargetAccount *Account `pg:"rel:has-one"`
// Prevent this mention from generating a notification? // Prevent this mention from generating a notification?
Silent bool Silent bool
@ -52,14 +55,14 @@ type Mention struct {
// //
// This will not be put in the database, it's just for convenience. // This will not be put in the database, it's just for convenience.
NameString string `pg:"-"` NameString string `pg:"-"`
// MentionedAccountURI is the AP ID (uri) of the user mentioned. // TargetAccountURI is the AP ID (uri) of the user mentioned.
// //
// This will not be put in the database, it's just for convenience. // This will not be put in the database, it's just for convenience.
MentionedAccountURI string `pg:"-"` TargetAccountURI string `pg:"-"`
// MentionedAccountURL is the web url of the user mentioned. // TargetAccountURL is the web url of the user mentioned.
// //
// This will not be put in the database, it's just for convenience. // This will not be put in the database, it's just for convenience.
MentionedAccountURL string `pg:"-"` TargetAccountURL string `pg:"-"`
// A pointer to the gtsmodel account of the mentioned account. // A pointer to the gtsmodel account of the mentioned account.
GTSAccount *Account `pg:"-"`
} }

View file

@ -29,24 +29,16 @@ type Notification struct {
// Creation time of this notification // Creation time of this notification
CreatedAt time.Time `pg:"type:timestamp,notnull,default:now()"` CreatedAt time.Time `pg:"type:timestamp,notnull,default:now()"`
// Which account does this notification target (ie., who will receive the notification?) // Which account does this notification target (ie., who will receive the notification?)
TargetAccountID string `pg:"type:CHAR(26),notnull"` TargetAccountID string `pg:"type:CHAR(26),notnull"`
TargetAccount *Account `pg:"rel:has-one"`
// Which account performed the action that created this notification? // Which account performed the action that created this notification?
OriginAccountID string `pg:"type:CHAR(26),notnull"` OriginAccountID string `pg:"type:CHAR(26),notnull"`
OriginAccount *Account `pg:"rel:has-one"`
// If the notification pertains to a status, what is the database ID of that status? // If the notification pertains to a status, what is the database ID of that status?
StatusID string `pg:"type:CHAR(26)"` StatusID string `pg:"type:CHAR(26)"`
Status *Status `pg:"rel:has-one"`
// Has this notification been read already? // Has this notification been read already?
Read bool Read bool
/*
NON-DATABASE fields
*/
// gts model of the target account, won't be put in the database, it's just for convenience when passing the notification around.
GTSTargetAccount *Account `pg:"-"`
// gts model of the origin account, won't be put in the database, it's just for convenience when passing the notification around.
GTSOriginAccount *Account `pg:"-"`
// gts model of the relevant status, won't be put in the database, it's just for convenience when passing the notification around.
GTSStatus *Status `pg:"-"`
} }
// NotificationType describes the reason/type of this notification. // NotificationType describes the reason/type of this notification.

View file

@ -33,13 +33,17 @@ type Status struct {
// the html-formatted content of this status // the html-formatted content of this status
Content string Content string
// Database IDs of any media attachments associated with this status // Database IDs of any media attachments associated with this status
Attachments []string `pg:",array"` AttachmentIDs []string `pg:"attachments,array"`
Attachments []*MediaAttachment `pg:"rel:has-many"`
// Database IDs of any tags used in this status // Database IDs of any tags used in this status
Tags []string `pg:",array"` TagIDs []string `pg:"tags,array"`
Tags []*Tag `pg:"rel:has-many"`
// Database IDs of any mentions in this status // Database IDs of any mentions in this status
Mentions []string `pg:",array"` MentionIDs []string `pg:"mentions,array"`
Mentions []*Mention `pg:"rel:has-many"`
// Database IDs of any emojis used in this status // Database IDs of any emojis used in this status
Emojis []string `pg:",array"` EmojiIDs []string `pg:"emojis,array"`
Emojis []*Emoji `pg:"rel:many2many"`
// when was this status created? // when was this status created?
CreatedAt time.Time `pg:"type:timestamp,notnull,default:now()"` CreatedAt time.Time `pg:"type:timestamp,notnull,default:now()"`
// when was this status updated? // when was this status updated?
@ -85,25 +89,6 @@ type Status struct {
Text string Text string
// Has this status been pinned by its owner? // Has this status been pinned by its owner?
Pinned bool Pinned bool
/*
INTERNAL MODEL NON-DATABASE FIELDS
These are for convenience while passing the status around internally,
but these fields should *never* be put in the db.
*/
// Account that created this status
// Mentions created in this status
GTSMentions []*Mention `pg:"-"`
// Hashtags used in this status
GTSTags []*Tag `pg:"-"`
// Emojis used in this status
GTSEmojis []*Emoji `pg:"-"`
// MediaAttachments used in this status
GTSMediaAttachments []*MediaAttachment `pg:"-"`
} }
// Visibility represents the visibility granularity of a status. // Visibility represents the visibility granularity of a status.

View file

@ -28,8 +28,10 @@ type StatusBookmark struct {
CreatedAt time.Time `pg:"type:timestamp,notnull,default:now()"` CreatedAt time.Time `pg:"type:timestamp,notnull,default:now()"`
// id of the account that created ('did') the bookmarking // id of the account that created ('did') the bookmarking
AccountID string `pg:"type:CHAR(26),notnull"` AccountID string `pg:"type:CHAR(26),notnull"`
Account *Account `pg:"rel:belongs-to"`
// id the account owning the bookmarked status // id the account owning the bookmarked status
TargetAccountID string `pg:"type:CHAR(26),notnull"` TargetAccountID string `pg:"type:CHAR(26),notnull"`
TargetAccount *Account `pg:"rel:has-one"`
// database id of the status that has been bookmarked // database id of the status that has been bookmarked
StatusID string `pg:"type:CHAR(26),notnull"` StatusID string `pg:"type:CHAR(26),notnull"`
} }

View file

@ -28,7 +28,7 @@ type StatusFave struct {
CreatedAt time.Time `pg:"type:timestamp,notnull,default:now()"` CreatedAt time.Time `pg:"type:timestamp,notnull,default:now()"`
// id of the account that created ('did') the fave // id of the account that created ('did') the fave
AccountID string `pg:"type:CHAR(26),notnull"` AccountID string `pg:"type:CHAR(26),notnull"`
Account *Account `pg:"rel:has-one"` Account *Account `pg:"rel:belongs-to"`
// id the account owning the faved status // id the account owning the faved status
TargetAccountID string `pg:"type:CHAR(26),notnull"` TargetAccountID string `pg:"type:CHAR(26),notnull"`
TargetAccount *Account `pg:"rel:has-one"` TargetAccount *Account `pg:"rel:has-one"`

View file

@ -28,8 +28,11 @@ type StatusMute struct {
CreatedAt time.Time `pg:"type:timestamp,notnull,default:now()"` CreatedAt time.Time `pg:"type:timestamp,notnull,default:now()"`
// id of the account that created ('did') the mute // id of the account that created ('did') the mute
AccountID string `pg:"type:CHAR(26),notnull"` AccountID string `pg:"type:CHAR(26),notnull"`
Account *Account `pg:"rel:belongs-to"`
// id the account owning the muted status (can be the same as accountID) // id the account owning the muted status (can be the same as accountID)
TargetAccountID string `pg:"type:CHAR(26),notnull"` TargetAccountID string `pg:"type:CHAR(26),notnull"`
TargetAccount *Account `pg:"rel:has-one"`
// database id of the status that has been muted // database id of the status that has been muted
StatusID string `pg:"type:CHAR(26),notnull"` StatusID string `pg:"type:CHAR(26),notnull"`
Status *Status `pg:"rel:has-one"`
} }

View file

@ -35,7 +35,8 @@ type User struct {
// confirmed email address for this user, this should be unique -- only one email address registered per instance, multiple users per email are not supported // confirmed email address for this user, this should be unique -- only one email address registered per instance, multiple users per email are not supported
Email string `pg:"default:null,unique"` Email string `pg:"default:null,unique"`
// The id of the local gtsmodel.Account entry for this user, if it exists (unconfirmed users don't have an account yet) // The id of the local gtsmodel.Account entry for this user, if it exists (unconfirmed users don't have an account yet)
AccountID string `pg:"type:CHAR(26),unique"` AccountID string `pg:"type:CHAR(26),unique"`
Account *Account `pg:"rel:has-one"`
// The encrypted password of this user, generated using https://pkg.go.dev/golang.org/x/crypto/bcrypt#GenerateFromPassword. A salt is included so we're safe against 🌈 tables // The encrypted password of this user, generated using https://pkg.go.dev/golang.org/x/crypto/bcrypt#GenerateFromPassword. A salt is included so we're safe against 🌈 tables
EncryptedPassword string `pg:",notnull"` EncryptedPassword string `pg:",notnull"`
@ -68,7 +69,8 @@ type User struct {
// In what timezone/locale is this user located? // In what timezone/locale is this user located?
Locale string Locale string
// Which application id created this user? See gtsmodel.Application // Which application id created this user? See gtsmodel.Application
CreatedByApplicationID string `pg:"type:CHAR(26)"` CreatedByApplicationID string `pg:"type:CHAR(26)"`
CreatedByApplication *Application `pg:"rel:has-one"`
// When did we last contact this user // When did we last contact this user
LastEmailedAt time.Time `pg:"type:timestamp"` LastEmailedAt time.Time `pg:"type:timestamp"`

View file

@ -31,24 +31,20 @@ import (
func (p *processor) BlockCreate(requestingAccount *gtsmodel.Account, targetAccountID string) (*apimodel.Relationship, gtserror.WithCode) { func (p *processor) BlockCreate(requestingAccount *gtsmodel.Account, targetAccountID string) (*apimodel.Relationship, gtserror.WithCode) {
// make sure the target account actually exists in our db // make sure the target account actually exists in our db
targetAcct := &gtsmodel.Account{} targetAccount, err := p.db.GetAccountByID(targetAccountID)
if err := p.db.GetByID(targetAccountID, targetAcct); err != nil { if err != nil {
if err == db.ErrNoEntries { return nil, gtserror.NewErrorNotFound(fmt.Errorf("BlockCreate: error getting account %s from the db: %s", targetAccountID, err))
return nil, gtserror.NewErrorNotFound(fmt.Errorf("BlockCreate: account %s not found in the db: %s", targetAccountID, err))
}
} }
// if requestingAccount already blocks target account, we don't need to do anything // if requestingAccount already blocks target account, we don't need to do anything
block := &gtsmodel.Block{} if blocked, err := p.db.Blocked(requestingAccount.ID, targetAccountID, false); err != nil {
if err := p.db.GetWhere([]db.Where{ return nil, gtserror.NewErrorInternalError(fmt.Errorf("BlockCreate: error checking existence of block: %s", err))
{Key: "account_id", Value: requestingAccount.ID}, } else if blocked {
{Key: "target_account_id", Value: targetAccountID},
}, block); err == nil {
// block already exists, just return relationship
return p.RelationshipGet(requestingAccount, targetAccountID) return p.RelationshipGet(requestingAccount, targetAccountID)
} }
// make the block // make the block
block := &gtsmodel.Block{}
newBlockID, err := id.NewULID() newBlockID, err := id.NewULID()
if err != nil { if err != nil {
return nil, gtserror.NewErrorInternalError(err) return nil, gtserror.NewErrorInternalError(err)
@ -57,7 +53,7 @@ func (p *processor) BlockCreate(requestingAccount *gtsmodel.Account, targetAccou
block.AccountID = requestingAccount.ID block.AccountID = requestingAccount.ID
block.Account = requestingAccount block.Account = requestingAccount
block.TargetAccountID = targetAccountID block.TargetAccountID = targetAccountID
block.TargetAccount = targetAcct block.TargetAccount = targetAccount
block.URI = util.GenerateURIForBlock(requestingAccount.Username, p.config.Protocol, p.config.Host, newBlockID) block.URI = util.GenerateURIForBlock(requestingAccount.Username, p.config.Protocol, p.config.Host, newBlockID)
// whack it in the database // whack it in the database
@ -123,7 +119,7 @@ func (p *processor) BlockCreate(requestingAccount *gtsmodel.Account, targetAccou
URI: frURI, URI: frURI,
}, },
OriginAccount: requestingAccount, OriginAccount: requestingAccount,
TargetAccount: targetAcct, TargetAccount: targetAccount,
} }
} }
@ -138,7 +134,7 @@ func (p *processor) BlockCreate(requestingAccount *gtsmodel.Account, targetAccou
URI: fURI, URI: fURI,
}, },
OriginAccount: requestingAccount, OriginAccount: requestingAccount,
TargetAccount: targetAcct, TargetAccount: targetAccount,
} }
} }
@ -148,7 +144,7 @@ func (p *processor) BlockCreate(requestingAccount *gtsmodel.Account, targetAccou
APActivityType: gtsmodel.ActivityStreamsCreate, APActivityType: gtsmodel.ActivityStreamsCreate,
GTSModel: block, GTSModel: block,
OriginAccount: requestingAccount, OriginAccount: requestingAccount,
TargetAccount: targetAcct, TargetAccount: targetAccount,
} }
return p.RelationshipGet(requestingAccount, targetAccountID) return p.RelationshipGet(requestingAccount, targetAccountID)

View file

@ -31,38 +31,33 @@ import (
func (p *processor) FollowCreate(requestingAccount *gtsmodel.Account, form *apimodel.AccountFollowRequest) (*apimodel.Relationship, gtserror.WithCode) { func (p *processor) FollowCreate(requestingAccount *gtsmodel.Account, 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(requestingAccount.ID, form.ID) if blocked, err := p.db.Blocked(requestingAccount.ID, form.ID, true); err != nil {
if err != nil {
return nil, gtserror.NewErrorInternalError(err) return nil, gtserror.NewErrorInternalError(err)
} } else if blocked {
if blocked { return nil, gtserror.NewErrorNotFound(fmt.Errorf("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 := &gtsmodel.Account{} targetAcct, err := p.db.GetAccountByID(form.ID)
if err := p.db.GetByID(form.ID, targetAcct); err != nil { if err != nil {
if err == db.ErrNoEntries { if err == db.ErrNoEntries {
return nil, gtserror.NewErrorNotFound(fmt.Errorf("accountfollowcreate: account %s not found in the db: %s", form.ID, err)) return nil, gtserror.NewErrorNotFound(fmt.Errorf("accountfollowcreate: account %s not found in the db: %s", form.ID, err))
} }
return nil, gtserror.NewErrorInternalError(err)
} }
// check if a follow exists already // check if a follow exists already
follows, err := p.db.Follows(requestingAccount, targetAcct) if follows, err := p.db.Follows(requestingAccount, targetAcct); err != nil {
if err != nil {
return nil, gtserror.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))
} } else if follows {
if follows {
// already follows so just return the relationship // already follows so just return the relationship
return p.RelationshipGet(requestingAccount, form.ID) return p.RelationshipGet(requestingAccount, form.ID)
} }
// check if a follow exists already // check if a follow request exists already
followRequested, err := p.db.FollowRequested(requestingAccount, targetAcct) if followRequested, err := p.db.FollowRequested(requestingAccount, targetAcct); err != nil {
if err != nil {
return nil, gtserror.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))
} } else if followRequested {
if followRequested {
// already follow requested so just return the relationship // already follow requested so just return the relationship
return p.RelationshipGet(requestingAccount, form.ID) return p.RelationshipGet(requestingAccount, form.ID)
} }

View file

@ -39,7 +39,7 @@ func (p *processor) Get(requestingAccount *gtsmodel.Account, targetAccountID str
var blocked bool var blocked bool
var err error var err error
if requestingAccount != nil { if requestingAccount != nil {
blocked, err = p.db.Blocked(requestingAccount.ID, targetAccountID) blocked, err = p.db.Blocked(requestingAccount.ID, targetAccountID, true)
if err != nil { if err != nil {
return nil, fmt.Errorf("error checking account block: %s", err) return nil, fmt.Errorf("error checking account block: %s", err)
} }

View file

@ -28,12 +28,9 @@ import (
) )
func (p *processor) FollowersGet(requestingAccount *gtsmodel.Account, targetAccountID string) ([]apimodel.Account, gtserror.WithCode) { func (p *processor) FollowersGet(requestingAccount *gtsmodel.Account, targetAccountID string) ([]apimodel.Account, gtserror.WithCode) {
blocked, err := p.db.Blocked(requestingAccount.ID, targetAccountID) if blocked, err := p.db.Blocked(requestingAccount.ID, targetAccountID, true); err != nil {
if err != nil {
return nil, gtserror.NewErrorInternalError(err) return nil, gtserror.NewErrorInternalError(err)
} } else if blocked {
if blocked {
return nil, gtserror.NewErrorNotFound(fmt.Errorf("block exists between accounts")) return nil, gtserror.NewErrorNotFound(fmt.Errorf("block exists between accounts"))
} }
@ -47,7 +44,7 @@ func (p *processor) FollowersGet(requestingAccount *gtsmodel.Account, targetAcco
} }
for _, f := range followers { for _, f := range followers {
blocked, err := p.db.Blocked(requestingAccount.ID, f.AccountID) blocked, err := p.db.Blocked(requestingAccount.ID, f.AccountID, true)
if err != nil { if err != nil {
return nil, gtserror.NewErrorInternalError(err) return nil, gtserror.NewErrorInternalError(err)
} }

View file

@ -28,12 +28,9 @@ import (
) )
func (p *processor) FollowingGet(requestingAccount *gtsmodel.Account, targetAccountID string) ([]apimodel.Account, gtserror.WithCode) { func (p *processor) FollowingGet(requestingAccount *gtsmodel.Account, targetAccountID string) ([]apimodel.Account, gtserror.WithCode) {
blocked, err := p.db.Blocked(requestingAccount.ID, targetAccountID) if blocked, err := p.db.Blocked(requestingAccount.ID, targetAccountID, true); err != nil {
if err != nil {
return nil, gtserror.NewErrorInternalError(err) return nil, gtserror.NewErrorInternalError(err)
} } else if blocked {
if blocked {
return nil, gtserror.NewErrorNotFound(fmt.Errorf("block exists between accounts")) return nil, gtserror.NewErrorNotFound(fmt.Errorf("block exists between accounts"))
} }
@ -47,7 +44,7 @@ func (p *processor) FollowingGet(requestingAccount *gtsmodel.Account, targetAcco
} }
for _, f := range following { for _, f := range following {
blocked, err := p.db.Blocked(requestingAccount.ID, f.AccountID) blocked, err := p.db.Blocked(requestingAccount.ID, f.AccountID, true)
if err != nil { if err != nil {
return nil, gtserror.NewErrorInternalError(err) return nil, gtserror.NewErrorInternalError(err)
} }

View file

@ -28,15 +28,14 @@ import (
) )
func (p *processor) StatusesGet(requestingAccount *gtsmodel.Account, targetAccountID string, limit int, excludeReplies bool, maxID string, pinnedOnly bool, mediaOnly bool) ([]apimodel.Status, gtserror.WithCode) { func (p *processor) StatusesGet(requestingAccount *gtsmodel.Account, targetAccountID string, limit int, excludeReplies bool, maxID string, pinnedOnly bool, mediaOnly bool) ([]apimodel.Status, gtserror.WithCode) {
targetAccount := &gtsmodel.Account{} if blocked, err := p.db.Blocked(requestingAccount.ID, targetAccountID, true); err != nil {
if err := p.db.GetByID(targetAccountID, targetAccount); err != nil {
if err == db.ErrNoEntries {
return nil, gtserror.NewErrorNotFound(fmt.Errorf("no entry found for account id %s", targetAccountID))
}
return nil, gtserror.NewErrorInternalError(err) return nil, gtserror.NewErrorInternalError(err)
} else if blocked {
return nil, gtserror.NewErrorNotFound(fmt.Errorf("block exists between accounts"))
} }
apiStatuses := []apimodel.Status{} apiStatuses := []apimodel.Status{}
statuses, err := p.db.GetAccountStatuses(targetAccountID, limit, excludeReplies, maxID, pinnedOnly, mediaOnly) statuses, err := p.db.GetAccountStatuses(targetAccountID, limit, excludeReplies, maxID, pinnedOnly, mediaOnly)
if err != nil { if err != nil {
if err == db.ErrNoEntries { if err == db.ErrNoEntries {

View file

@ -29,11 +29,9 @@ import (
func (p *processor) BlockRemove(requestingAccount *gtsmodel.Account, targetAccountID string) (*apimodel.Relationship, gtserror.WithCode) { func (p *processor) BlockRemove(requestingAccount *gtsmodel.Account, targetAccountID string) (*apimodel.Relationship, gtserror.WithCode) {
// make sure the target account actually exists in our db // make sure the target account actually exists in our db
targetAcct := &gtsmodel.Account{} targetAccount, err := p.db.GetAccountByID(targetAccountID)
if err := p.db.GetByID(targetAccountID, targetAcct); err != nil { if err != nil {
if err == db.ErrNoEntries { return nil, gtserror.NewErrorNotFound(fmt.Errorf("BlockCreate: error getting account %s from the db: %s", targetAccountID, err))
return nil, gtserror.NewErrorNotFound(fmt.Errorf("BlockRemove: account %s not found in the db: %s", targetAccountID, err))
}
} }
// check if a block exists, and remove it if it does (storing the URI for later) // check if a block exists, and remove it if it does (storing the URI for later)
@ -44,7 +42,7 @@ func (p *processor) BlockRemove(requestingAccount *gtsmodel.Account, targetAccou
{Key: "target_account_id", Value: targetAccountID}, {Key: "target_account_id", Value: targetAccountID},
}, block); err == nil { }, block); err == nil {
block.Account = requestingAccount block.Account = requestingAccount
block.TargetAccount = targetAcct block.TargetAccount = targetAccount
if err := p.db.DeleteByID(block.ID, &gtsmodel.Block{}); err != nil { if err := p.db.DeleteByID(block.ID, &gtsmodel.Block{}); err != nil {
return nil, gtserror.NewErrorInternalError(fmt.Errorf("BlockRemove: error removing block from db: %s", err)) return nil, gtserror.NewErrorInternalError(fmt.Errorf("BlockRemove: error removing block from db: %s", err))
} }
@ -58,7 +56,7 @@ func (p *processor) BlockRemove(requestingAccount *gtsmodel.Account, targetAccou
APActivityType: gtsmodel.ActivityStreamsUndo, APActivityType: gtsmodel.ActivityStreamsUndo,
GTSModel: block, GTSModel: block,
OriginAccount: requestingAccount, OriginAccount: requestingAccount,
TargetAccount: targetAcct, TargetAccount: targetAccount,
} }
} }

View file

@ -29,7 +29,7 @@ import (
func (p *processor) FollowRemove(requestingAccount *gtsmodel.Account, targetAccountID string) (*apimodel.Relationship, gtserror.WithCode) { func (p *processor) FollowRemove(requestingAccount *gtsmodel.Account, 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(requestingAccount.ID, targetAccountID) blocked, err := p.db.Blocked(requestingAccount.ID, targetAccountID, true)
if err != nil { if err != nil {
return nil, gtserror.NewErrorInternalError(err) return nil, gtserror.NewErrorInternalError(err)
} }

View file

@ -63,7 +63,7 @@ func (p *processor) GetFediUser(ctx context.Context, requestedUsername string, r
return nil, gtserror.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, true)
if err != nil { if err != nil {
return nil, gtserror.NewErrorInternalError(err) return nil, gtserror.NewErrorInternalError(err)
} }
@ -107,7 +107,7 @@ func (p *processor) GetFediFollowers(ctx context.Context, requestedUsername stri
return nil, gtserror.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, true)
if err != nil { if err != nil {
return nil, gtserror.NewErrorInternalError(err) return nil, gtserror.NewErrorInternalError(err)
} }
@ -152,7 +152,7 @@ func (p *processor) GetFediFollowing(ctx context.Context, requestedUsername stri
return nil, gtserror.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, true)
if err != nil { if err != nil {
return nil, gtserror.NewErrorInternalError(err) return nil, gtserror.NewErrorInternalError(err)
} }
@ -199,7 +199,7 @@ func (p *processor) GetFediStatus(ctx context.Context, requestedUsername string,
// authorize the request: // authorize the request:
// 1. check if a block exists between the requester and the requestee // 1. check if a block exists between the requester and the requestee
blocked, err := p.db.Blocked(requestedAccount.ID, requestingAccount.ID) blocked, err := p.db.Blocked(requestedAccount.ID, requestingAccount.ID, true)
if err != nil { if err != nil {
return nil, gtserror.NewErrorInternalError(err) return nil, gtserror.NewErrorInternalError(err)
} }
@ -259,7 +259,7 @@ func (p *processor) GetFediStatusReplies(ctx context.Context, requestedUsername
// authorize the request: // authorize the request:
// 1. check if a block exists between the requester and the requestee // 1. check if a block exists between the requester and the requestee
blocked, err := p.db.Blocked(requestedAccount.ID, requestingAccount.ID) blocked, err := p.db.Blocked(requestedAccount.ID, requestingAccount.ID, true)
if err != nil { if err != nil {
return nil, gtserror.NewErrorInternalError(err) return nil, gtserror.NewErrorInternalError(err)
} }

View file

@ -192,14 +192,14 @@ func (p *processor) processFromClientAPI(clientMsg gtsmodel.FromClientAPI) error
} }
// delete all attachments for this status // delete all attachments for this status
for _, a := range statusToDelete.Attachments { for _, a := range statusToDelete.AttachmentIDs {
if err := p.mediaProcessor.Delete(a); err != nil { if err := p.mediaProcessor.Delete(a); err != nil {
return err return err
} }
} }
// delete all mentions for this status // delete all mentions for this status
for _, m := range statusToDelete.Mentions { for _, m := range statusToDelete.MentionIDs {
if err := p.db.DeleteByID(m, &gtsmodel.Mention{}); err != nil { if err := p.db.DeleteByID(m, &gtsmodel.Mention{}); err != nil {
return err return err
} }

View file

@ -30,35 +30,35 @@ import (
func (p *processor) notifyStatus(status *gtsmodel.Status) error { func (p *processor) notifyStatus(status *gtsmodel.Status) error {
// if there are no mentions in this status then just bail // if there are no mentions in this status then just bail
if len(status.Mentions) == 0 { if len(status.MentionIDs) == 0 {
return nil return nil
} }
if status.GTSMentions == nil { if status.Mentions == nil {
// there are mentions but they're not fully populated on the status yet so do this // there are mentions but they're not fully populated on the status yet so do this
menchies := []*gtsmodel.Mention{} menchies := []*gtsmodel.Mention{}
for _, m := range status.Mentions { for _, m := range status.MentionIDs {
gtsm := &gtsmodel.Mention{} gtsm := &gtsmodel.Mention{}
if err := p.db.GetByID(m, gtsm); err != nil { if err := p.db.GetByID(m, gtsm); err != nil {
return fmt.Errorf("notifyStatus: error getting mention with id %s from the db: %s", m, err) return fmt.Errorf("notifyStatus: error getting mention with id %s from the db: %s", m, err)
} }
menchies = append(menchies, gtsm) menchies = append(menchies, gtsm)
} }
status.GTSMentions = menchies status.Mentions = menchies
} }
// now we have mentions as full gtsmodel.Mention structs on the status we can continue // now we have mentions as full gtsmodel.Mention structs on the status we can continue
for _, m := range status.GTSMentions { for _, m := range status.Mentions {
// make sure this is a local account, otherwise we don't need to create a notification for it // make sure this is a local account, otherwise we don't need to create a notification for it
if m.GTSAccount == nil { if m.OriginAccount == nil {
a := &gtsmodel.Account{} a := &gtsmodel.Account{}
if err := p.db.GetByID(m.TargetAccountID, a); err != nil { if err := p.db.GetByID(m.TargetAccountID, a); err != nil {
// we don't have the account or there's been an error // we don't have the account or there's been an error
return fmt.Errorf("notifyStatus: error getting account with id %s from the db: %s", m.TargetAccountID, err) return fmt.Errorf("notifyStatus: error getting account with id %s from the db: %s", m.TargetAccountID, err)
} }
m.GTSAccount = a m.OriginAccount = a
} }
if m.GTSAccount.Domain != "" { if m.OriginAccount.Domain != "" {
// not a local account so skip it // not a local account so skip it
continue continue
} }
@ -103,7 +103,7 @@ func (p *processor) notifyStatus(status *gtsmodel.Status) error {
return fmt.Errorf("notifyStatus: error converting notification to masto representation: %s", err) return fmt.Errorf("notifyStatus: error converting notification to masto representation: %s", err)
} }
if err := p.streamingProcessor.StreamNotificationToAccount(mastoNotif, m.GTSAccount); err != nil { if err := p.streamingProcessor.StreamNotificationToAccount(mastoNotif, m.OriginAccount); err != nil {
return fmt.Errorf("notifyStatus: error streaming notification to account: %s", err) return fmt.Errorf("notifyStatus: error streaming notification to account: %s", err)
} }
} }

View file

@ -164,14 +164,14 @@ func (p *processor) processFromFederator(federatorMsg gtsmodel.FromFederator) er
} }
// delete all attachments for this status // delete all attachments for this status
for _, a := range statusToDelete.Attachments { for _, a := range statusToDelete.AttachmentIDs {
if err := p.mediaProcessor.Delete(a); err != nil { if err := p.mediaProcessor.Delete(a); err != nil {
return err return err
} }
} }
// delete all mentions for this status // delete all mentions for this status
for _, m := range statusToDelete.Mentions { for _, m := range statusToDelete.MentionIDs {
if err := p.db.DeleteByID(m, &gtsmodel.Mention{}); err != nil { if err := p.db.DeleteByID(m, &gtsmodel.Mention{}); err != nil {
return err return err
} }

View file

@ -57,7 +57,7 @@ func (p *processor) GetFile(account *gtsmodel.Account, form *apimodel.GetContent
// 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 account != nil { if account != nil {
blocked, err := p.db.Blocked(account.ID, form.AccountID) blocked, err := p.db.Blocked(account.ID, form.AccountID, true)
if err != nil { if err != nil {
return nil, gtserror.NewErrorNotFound(fmt.Errorf("block status could not be established between accounts %s and %s: %s", form.AccountID, account.ID, err)) return nil, gtserror.NewErrorNotFound(fmt.Errorf("block status could not be established between accounts %s and %s: %s", form.AccountID, account.ID, err))
} }

View file

@ -90,7 +90,7 @@ func (p *processor) SearchGet(authed *oauth.Auth, searchQuery *apimodel.SearchQu
*/ */
for _, foundAccount := range foundAccounts { for _, foundAccount := range foundAccounts {
// make sure there's no block in either direction between the account and the requester // make sure there's no block in either direction between the account and the requester
if blocked, err := p.db.Blocked(authed.Account.ID, foundAccount.ID); err == nil && !blocked { if blocked, err := p.db.Blocked(authed.Account.ID, foundAccount.ID, true); err == nil && !blocked {
// all good, convert it and add it to the results // all good, convert it and add it to the results
if acctMasto, err := p.tc.AccountToMastoPublic(foundAccount); err == nil && acctMasto != nil { if acctMasto, err := p.tc.AccountToMastoPublic(foundAccount); err == nil && acctMasto != nil {
results.Accounts = append(results.Accounts, *acctMasto) results.Accounts = append(results.Accounts, *acctMasto)

View file

@ -9,31 +9,22 @@ import (
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
) )
func (p *processor) Boost(account *gtsmodel.Account, application *gtsmodel.Application, targetStatusID string) (*apimodel.Status, gtserror.WithCode) { func (p *processor) Boost(requestingAccount *gtsmodel.Account, application *gtsmodel.Application, targetStatusID string) (*apimodel.Status, gtserror.WithCode) {
l := p.log.WithField("func", "StatusBoost") targetStatus, err := p.db.GetStatusByID(targetStatusID)
if err != nil {
l.Tracef("going to search for target status %s", targetStatusID)
targetStatus := &gtsmodel.Status{}
if err := p.db.GetByID(targetStatusID, targetStatus); err != nil {
return nil, gtserror.NewErrorNotFound(fmt.Errorf("error fetching status %s: %s", targetStatusID, err)) return nil, gtserror.NewErrorNotFound(fmt.Errorf("error fetching status %s: %s", targetStatusID, err))
} }
if targetStatus.Account == nil {
l.Tracef("going to search for target account %s", targetStatus.AccountID) return nil, gtserror.NewErrorNotFound(fmt.Errorf("no status owner for status %s", targetStatusID))
targetAccount := &gtsmodel.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 see if status is visible") visible, err := p.filter.StatusVisible(targetStatus, requestingAccount)
visible, err := p.filter.StatusVisible(targetStatus, account)
if err != nil { if err != nil {
return nil, gtserror.NewErrorNotFound(fmt.Errorf("error seeing if status %s is visible: %s", targetStatus.ID, err)) return nil, gtserror.NewErrorNotFound(fmt.Errorf("error seeing if status %s is visible: %s", targetStatus.ID, err))
} }
if !visible { if !visible {
return nil, gtserror.NewErrorNotFound(errors.New("status is not visible")) return nil, gtserror.NewErrorNotFound(errors.New("status is not visible"))
} }
if targetStatus.VisibilityAdvanced != nil { if targetStatus.VisibilityAdvanced != nil {
if !targetStatus.VisibilityAdvanced.Boostable { if !targetStatus.VisibilityAdvanced.Boostable {
return nil, gtserror.NewErrorForbidden(errors.New("status is not boostable")) return nil, gtserror.NewErrorForbidden(errors.New("status is not boostable"))
@ -41,13 +32,13 @@ func (p *processor) Boost(account *gtsmodel.Account, application *gtsmodel.Appli
} }
// it's visible! it's boostable! so let's boost the FUCK out of it // it's visible! it's boostable! so let's boost the FUCK out of it
boostWrapperStatus, err := p.tc.StatusToBoost(targetStatus, account) boostWrapperStatus, err := p.tc.StatusToBoost(targetStatus, requestingAccount)
if err != nil { if err != nil {
return nil, gtserror.NewErrorInternalError(err) return nil, gtserror.NewErrorInternalError(err)
} }
boostWrapperStatus.CreatedWithApplicationID = application.ID boostWrapperStatus.CreatedWithApplicationID = application.ID
boostWrapperStatus.BoostOfAccount = targetAccount boostWrapperStatus.BoostOfAccount = targetStatus.Account
// put the boost in the database // put the boost in the database
if err := p.db.Put(boostWrapperStatus); err != nil { if err := p.db.Put(boostWrapperStatus); err != nil {
@ -59,12 +50,12 @@ func (p *processor) Boost(account *gtsmodel.Account, application *gtsmodel.Appli
APObjectType: gtsmodel.ActivityStreamsAnnounce, APObjectType: gtsmodel.ActivityStreamsAnnounce,
APActivityType: gtsmodel.ActivityStreamsCreate, APActivityType: gtsmodel.ActivityStreamsCreate,
GTSModel: boostWrapperStatus, GTSModel: boostWrapperStatus,
OriginAccount: account, OriginAccount: requestingAccount,
TargetAccount: targetAccount, TargetAccount: targetStatus.Account,
} }
// return the frontend representation of the new status to the submitter // return the frontend representation of the new status to the submitter
mastoStatus, err := p.tc.StatusToMasto(boostWrapperStatus, account) mastoStatus, err := p.tc.StatusToMasto(boostWrapperStatus, requestingAccount)
if err != nil { if err != nil {
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting status %s to frontend representation: %s", targetStatus.ID, err)) return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting status %s to frontend representation: %s", targetStatus.ID, err))
} }

View file

@ -9,29 +9,21 @@ import (
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
) )
func (p *processor) BoostedBy(account *gtsmodel.Account, targetStatusID string) ([]*apimodel.Account, gtserror.WithCode) { func (p *processor) BoostedBy(requestingAccount *gtsmodel.Account, targetStatusID string) ([]*apimodel.Account, gtserror.WithCode) {
l := p.log.WithField("func", "StatusBoostedBy") targetStatus, err := p.db.GetStatusByID(targetStatusID)
l.Tracef("going to search for target status %s", targetStatusID)
targetStatus := &gtsmodel.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 := &gtsmodel.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 see if status is visible")
visible, err := p.filter.StatusVisible(targetStatus, account)
if err != nil { if err != nil {
return nil, gtserror.NewErrorNotFound(fmt.Errorf("StatusBoostedBy: error seeing if status %s is visible: %s", targetStatus.ID, err)) return nil, gtserror.NewErrorNotFound(fmt.Errorf("error fetching status %s: %s", targetStatusID, err))
}
if targetStatus.Account == nil {
return nil, gtserror.NewErrorNotFound(fmt.Errorf("no status owner for status %s", targetStatusID))
} }
visible, err := p.filter.StatusVisible(targetStatus, requestingAccount)
if err != nil {
return nil, gtserror.NewErrorNotFound(fmt.Errorf("error seeing if status %s is visible: %s", targetStatus.ID, err))
}
if !visible { if !visible {
return nil, gtserror.NewErrorNotFound(errors.New("StatusBoostedBy: status is not 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 // get ALL accounts that faved a status -- doesn't take account of blocks and mutes and stuff
@ -43,7 +35,7 @@ func (p *processor) BoostedBy(account *gtsmodel.Account, targetStatusID string)
// filter the list so the user doesn't see accounts they blocked or which blocked them // filter the list so the user doesn't see accounts they blocked or which blocked them
filteredAccounts := []*gtsmodel.Account{} filteredAccounts := []*gtsmodel.Account{}
for _, acc := range favingAccounts { for _, acc := range favingAccounts {
blocked, err := p.db.Blocked(account.ID, acc.ID) blocked, err := p.db.Blocked(requestingAccount.ID, acc.ID, true)
if err != nil { if err != nil {
return nil, gtserror.NewErrorNotFound(fmt.Errorf("StatusBoostedBy: error checking blocks: %s", err)) return nil, gtserror.NewErrorNotFound(fmt.Errorf("StatusBoostedBy: error checking blocks: %s", err))
} }

View file

@ -1,46 +1,45 @@
package status package status
import ( import (
"errors"
"fmt" "fmt"
"sort" "sort"
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/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
) )
func (p *processor) Context(account *gtsmodel.Account, targetStatusID string) (*apimodel.Context, gtserror.WithCode) { func (p *processor) Context(requestingAccount *gtsmodel.Account, targetStatusID string) (*apimodel.Context, gtserror.WithCode) {
targetStatus, err := p.db.GetStatusByID(targetStatusID)
if err != nil {
return nil, gtserror.NewErrorNotFound(fmt.Errorf("error fetching status %s: %s", targetStatusID, err))
}
if targetStatus.Account == nil {
return nil, gtserror.NewErrorNotFound(fmt.Errorf("no status owner for status %s", targetStatusID))
}
visible, err := p.filter.StatusVisible(targetStatus, requestingAccount)
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"))
}
context := &apimodel.Context{ context := &apimodel.Context{
Ancestors: []apimodel.Status{}, Ancestors: []apimodel.Status{},
Descendants: []apimodel.Status{}, Descendants: []apimodel.Status{},
} }
targetStatus := &gtsmodel.Status{}
if err := p.db.GetByID(targetStatusID, targetStatus); err != nil {
if err == db.ErrNoEntries {
return nil, gtserror.NewErrorNotFound(err)
}
return nil, gtserror.NewErrorInternalError(err)
}
visible, err := p.filter.StatusVisible(targetStatus, account)
if err != nil {
return nil, gtserror.NewErrorNotFound(err)
}
if !visible {
return nil, gtserror.NewErrorForbidden(fmt.Errorf("account with id %s does not have permission to view status %s", account.ID, targetStatusID))
}
parents, err := p.db.StatusParents(targetStatus, false) parents, err := p.db.StatusParents(targetStatus, false)
if err != nil { if err != nil {
return nil, gtserror.NewErrorInternalError(err) return nil, gtserror.NewErrorInternalError(err)
} }
for _, status := range parents { for _, status := range parents {
if v, err := p.filter.StatusVisible(status, account); err == nil && v { if v, err := p.filter.StatusVisible(status, requestingAccount); err == nil && v {
mastoStatus, err := p.tc.StatusToMasto(status, account) mastoStatus, err := p.tc.StatusToMasto(status, requestingAccount)
if err == nil { if err == nil {
context.Ancestors = append(context.Ancestors, *mastoStatus) context.Ancestors = append(context.Ancestors, *mastoStatus)
} }
@ -57,8 +56,8 @@ func (p *processor) Context(account *gtsmodel.Account, targetStatusID string) (*
} }
for _, status := range children { for _, status := range children {
if v, err := p.filter.StatusVisible(status, account); err == nil && v { if v, err := p.filter.StatusVisible(status, requestingAccount); err == nil && v {
mastoStatus, err := p.tc.StatusToMasto(status, account) mastoStatus, err := p.tc.StatusToMasto(status, requestingAccount)
if err == nil { if err == nil {
context.Descendants = append(context.Descendants, *mastoStatus) context.Descendants = append(context.Descendants, *mastoStatus)
} }

View file

@ -81,7 +81,7 @@ func (p *processor) Create(account *gtsmodel.Account, application *gtsmodel.Appl
} }
// change the status ID of the media attachments to the new status // change the status ID of the media attachments to the new status
for _, a := range newStatus.GTSMediaAttachments { for _, a := range newStatus.Attachments {
a.StatusID = newStatus.ID a.StatusID = newStatus.ID
a.UpdatedAt = time.Now() a.UpdatedAt = time.Now()
if err := p.db.UpdateByID(a.ID, a); err != nil { if err := p.db.UpdateByID(a.ID, a); err != nil {

View file

@ -5,36 +5,24 @@ import (
"fmt" "fmt"
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/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
) )
func (p *processor) Delete(account *gtsmodel.Account, targetStatusID string) (*apimodel.Status, gtserror.WithCode) { func (p *processor) Delete(requestingAccount *gtsmodel.Account, targetStatusID string) (*apimodel.Status, gtserror.WithCode) {
l := p.log.WithField("func", "StatusDelete") targetStatus, err := p.db.GetStatusByID(targetStatusID)
l.Tracef("going to search for target status %s", targetStatusID) if err != nil {
targetStatus := &gtsmodel.Status{} return nil, gtserror.NewErrorNotFound(fmt.Errorf("error fetching status %s: %s", targetStatusID, err))
if err := p.db.GetByID(targetStatusID, targetStatus); err != nil { }
if err != db.ErrNoEntries { if targetStatus.Account == nil {
return nil, gtserror.NewErrorNotFound(fmt.Errorf("error fetching status %s: %s", targetStatusID, err)) return nil, gtserror.NewErrorNotFound(fmt.Errorf("no status owner for status %s", targetStatusID))
}
// status is already gone
return nil, nil
} }
if targetStatus.AccountID != account.ID { if targetStatus.AccountID != requestingAccount.ID {
return nil, gtserror.NewErrorForbidden(errors.New("status doesn't belong to requesting account")) return nil, gtserror.NewErrorForbidden(errors.New("status doesn't belong to requesting account"))
} }
var boostOfStatus *gtsmodel.Status mastoStatus, err := p.tc.StatusToMasto(targetStatus, requestingAccount)
if targetStatus.BoostOfID != "" {
boostOfStatus = &gtsmodel.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)
if err != nil { if err != nil {
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting status %s to frontend representation: %s", targetStatus.ID, err)) return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting status %s to frontend representation: %s", targetStatus.ID, err))
} }
@ -48,8 +36,8 @@ func (p *processor) Delete(account *gtsmodel.Account, targetStatusID string) (*a
APObjectType: gtsmodel.ActivityStreamsNote, APObjectType: gtsmodel.ActivityStreamsNote,
APActivityType: gtsmodel.ActivityStreamsDelete, APActivityType: gtsmodel.ActivityStreamsDelete,
GTSModel: targetStatus, GTSModel: targetStatus,
OriginAccount: account, OriginAccount: requestingAccount,
TargetAccount: account, TargetAccount: requestingAccount,
} }
return mastoStatus, nil return mastoStatus, nil

View file

@ -12,39 +12,22 @@ import (
"github.com/superseriousbusiness/gotosocial/internal/util" "github.com/superseriousbusiness/gotosocial/internal/util"
) )
func (p *processor) Fave(account *gtsmodel.Account, targetStatusID string) (*apimodel.Status, gtserror.WithCode) { func (p *processor) Fave(requestingAccount *gtsmodel.Account, targetStatusID string) (*apimodel.Status, gtserror.WithCode) {
l := p.log.WithField("func", "StatusFave") targetStatus, err := p.db.GetStatusByID(targetStatusID)
l.Tracef("going to search for target status %s", targetStatusID) if err != nil {
targetStatus := &gtsmodel.Status{}
if err := p.db.GetByID(targetStatusID, targetStatus); err != nil {
return nil, gtserror.NewErrorNotFound(fmt.Errorf("error fetching status %s: %s", targetStatusID, err)) return nil, gtserror.NewErrorNotFound(fmt.Errorf("error fetching status %s: %s", targetStatusID, err))
} }
if targetStatus.Account == nil {
l.Tracef("going to search for target account %s", targetStatus.AccountID) return nil, gtserror.NewErrorNotFound(fmt.Errorf("no status owner for status %s", targetStatusID))
targetAccount := &gtsmodel.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))
} }
var boostOfStatus *gtsmodel.Status visible, err := p.filter.StatusVisible(targetStatus, requestingAccount)
if targetStatus.BoostOfID != "" {
boostOfStatus = &gtsmodel.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.filter.StatusVisible(targetStatus, account) // requestingAccount might well be nil here, but StatusVisible knows how to take care of that
if err != nil { if err != nil {
return nil, gtserror.NewErrorNotFound(fmt.Errorf("error seeing if status %s is visible: %s", targetStatus.ID, err)) return nil, gtserror.NewErrorNotFound(fmt.Errorf("error seeing if status %s is visible: %s", targetStatus.ID, err))
} }
if !visible { if !visible {
return nil, gtserror.NewErrorNotFound(errors.New("status is not visible")) return nil, gtserror.NewErrorNotFound(errors.New("status is not visible"))
} }
// is the status faveable?
if targetStatus.VisibilityAdvanced != nil { if targetStatus.VisibilityAdvanced != nil {
if !targetStatus.VisibilityAdvanced.Likeable { if !targetStatus.VisibilityAdvanced.Likeable {
return nil, gtserror.NewErrorForbidden(errors.New("status is not faveable")) return nil, gtserror.NewErrorForbidden(errors.New("status is not faveable"))
@ -54,7 +37,7 @@ func (p *processor) Fave(account *gtsmodel.Account, targetStatusID string) (*api
// first check if the status is already faved, if so we don't need to do anything // first check if the status is already faved, if so we don't need to do anything
newFave := true newFave := true
gtsFave := &gtsmodel.StatusFave{} gtsFave := &gtsmodel.StatusFave{}
if err := p.db.GetWhere([]db.Where{{Key: "status_id", Value: targetStatus.ID}, {Key: "account_id", Value: account.ID}}, gtsFave); err == nil { if err := p.db.GetWhere([]db.Where{{Key: "status_id", Value: targetStatus.ID}, {Key: "account_id", Value: requestingAccount.ID}}, gtsFave); err == nil {
// we already have a fave for this status // we already have a fave for this status
newFave = false newFave = false
} }
@ -67,14 +50,14 @@ func (p *processor) Fave(account *gtsmodel.Account, targetStatusID string) (*api
// we need to create a new fave in the database // we need to create a new fave in the database
gtsFave := &gtsmodel.StatusFave{ gtsFave := &gtsmodel.StatusFave{
ID: thisFaveID, ID: thisFaveID,
AccountID: account.ID, AccountID: requestingAccount.ID,
TargetAccountID: targetAccount.ID, Account: requestingAccount,
StatusID: targetStatus.ID, TargetAccountID: targetStatus.AccountID,
URI: util.GenerateURIForLike(account.Username, p.config.Protocol, p.config.Host, thisFaveID), TargetAccount: targetStatus.Account,
Status: targetStatus, StatusID: targetStatus.ID,
TargetAccount: targetAccount, Status: targetStatus,
Account: account, URI: util.GenerateURIForLike(requestingAccount.Username, p.config.Protocol, p.config.Host, thisFaveID),
} }
if err := p.db.Put(gtsFave); err != nil { if err := p.db.Put(gtsFave); err != nil {
@ -86,13 +69,13 @@ func (p *processor) Fave(account *gtsmodel.Account, targetStatusID string) (*api
APObjectType: gtsmodel.ActivityStreamsLike, APObjectType: gtsmodel.ActivityStreamsLike,
APActivityType: gtsmodel.ActivityStreamsCreate, APActivityType: gtsmodel.ActivityStreamsCreate,
GTSModel: gtsFave, GTSModel: gtsFave,
OriginAccount: account, OriginAccount: requestingAccount,
TargetAccount: targetAccount, TargetAccount: targetStatus.Account,
} }
} }
// return the mastodon representation of the target status // return the mastodon representation of the target status
mastoStatus, err := p.tc.StatusToMasto(targetStatus, account) mastoStatus, err := p.tc.StatusToMasto(targetStatus, requestingAccount)
if err != nil { if err != nil {
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting status %s to frontend representation: %s", targetStatus.ID, err)) return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting status %s to frontend representation: %s", targetStatus.ID, err))
} }

View file

@ -9,27 +9,19 @@ import (
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
) )
func (p *processor) FavedBy(account *gtsmodel.Account, targetStatusID string) ([]*apimodel.Account, gtserror.WithCode) { func (p *processor) FavedBy(requestingAccount *gtsmodel.Account, targetStatusID string) ([]*apimodel.Account, gtserror.WithCode) {
l := p.log.WithField("func", "StatusFavedBy") targetStatus, err := p.db.GetStatusByID(targetStatusID)
if err != nil {
l.Tracef("going to search for target status %s", targetStatusID)
targetStatus := &gtsmodel.Status{}
if err := p.db.GetByID(targetStatusID, targetStatus); err != nil {
return nil, gtserror.NewErrorNotFound(fmt.Errorf("error fetching status %s: %s", targetStatusID, err)) return nil, gtserror.NewErrorNotFound(fmt.Errorf("error fetching status %s: %s", targetStatusID, err))
} }
if targetStatus.Account == nil {
l.Tracef("going to search for target account %s", targetStatus.AccountID) return nil, gtserror.NewErrorNotFound(fmt.Errorf("no status owner for status %s", targetStatusID))
targetAccount := &gtsmodel.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 see if status is visible") visible, err := p.filter.StatusVisible(targetStatus, requestingAccount)
visible, err := p.filter.StatusVisible(targetStatus, account)
if err != nil { if err != nil {
return nil, gtserror.NewErrorNotFound(fmt.Errorf("error seeing if status %s is visible: %s", targetStatus.ID, err)) return nil, gtserror.NewErrorNotFound(fmt.Errorf("error seeing if status %s is visible: %s", targetStatus.ID, err))
} }
if !visible { if !visible {
return nil, gtserror.NewErrorNotFound(errors.New("status is not visible")) return nil, gtserror.NewErrorNotFound(errors.New("status is not visible"))
} }
@ -43,7 +35,7 @@ func (p *processor) FavedBy(account *gtsmodel.Account, targetStatusID string) ([
// filter the list so the user doesn't see accounts they blocked or which blocked them // filter the list so the user doesn't see accounts they blocked or which blocked them
filteredAccounts := []*gtsmodel.Account{} filteredAccounts := []*gtsmodel.Account{}
for _, acc := range favingAccounts { for _, acc := range favingAccounts {
blocked, err := p.db.Blocked(account.ID, acc.ID) blocked, err := p.db.Blocked(requestingAccount.ID, acc.ID, true)
if err != nil { if err != nil {
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error checking blocks: %s", err)) return nil, gtserror.NewErrorInternalError(fmt.Errorf("error checking blocks: %s", err))
} }
@ -52,8 +44,6 @@ func (p *processor) FavedBy(account *gtsmodel.Account, targetStatusID string) ([
} }
} }
// TODO: filter other things here? suspended? muted? silenced?
// now we can return the masto representation of those accounts // now we can return the masto representation of those accounts
mastoAccounts := []*apimodel.Account{} mastoAccounts := []*apimodel.Account{}
for _, acc := range filteredAccounts { for _, acc := range filteredAccounts {

View file

@ -9,44 +9,27 @@ import (
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
) )
func (p *processor) Get(account *gtsmodel.Account, targetStatusID string) (*apimodel.Status, gtserror.WithCode) { func (p *processor) Get(requestingAccount *gtsmodel.Account, targetStatusID string) (*apimodel.Status, gtserror.WithCode) {
l := p.log.WithField("func", "StatusGet") targetStatus, err := p.db.GetStatusByID(targetStatusID)
if err != nil {
l.Tracef("going to search for target status %s", targetStatusID)
targetStatus := &gtsmodel.Status{}
if err := p.db.GetByID(targetStatusID, targetStatus); err != nil {
return nil, gtserror.NewErrorNotFound(fmt.Errorf("error fetching status %s: %s", targetStatusID, err)) return nil, gtserror.NewErrorNotFound(fmt.Errorf("error fetching status %s: %s", targetStatusID, err))
} }
if targetStatus.Account == nil {
l.Tracef("going to search for target account %s", targetStatus.AccountID) return nil, gtserror.NewErrorNotFound(fmt.Errorf("no status owner for status %s", targetStatusID))
targetAccount := &gtsmodel.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 see if status is visible") visible, err := p.filter.StatusVisible(targetStatus, requestingAccount)
visible, err := p.filter.StatusVisible(targetStatus, account) // requestingAccount might well be nil here, but StatusVisible knows how to take care of that
if err != nil { if err != nil {
return nil, gtserror.NewErrorNotFound(fmt.Errorf("error seeing if status %s is visible: %s", targetStatus.ID, err)) return nil, gtserror.NewErrorNotFound(fmt.Errorf("error seeing if status %s is visible: %s", targetStatus.ID, err))
} }
if !visible { if !visible {
return nil, gtserror.NewErrorNotFound(errors.New("status is not visible")) return nil, gtserror.NewErrorNotFound(errors.New("status is not visible"))
} }
var boostOfStatus *gtsmodel.Status mastoStatus, err := p.tc.StatusToMasto(targetStatus, requestingAccount)
if targetStatus.BoostOfID != "" {
boostOfStatus = &gtsmodel.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, account)
if err != nil { if err != nil {
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting status %s to frontend representation: %s", targetStatus.ID, err)) return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting status %s to frontend representation: %s", targetStatus.ID, err))
} }
return mastoStatus, nil return mastoStatus, nil
} }

View file

@ -10,27 +10,19 @@ import (
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
) )
func (p *processor) Unboost(account *gtsmodel.Account, application *gtsmodel.Application, targetStatusID string) (*apimodel.Status, gtserror.WithCode) { func (p *processor) Unboost(requestingAccount *gtsmodel.Account, application *gtsmodel.Application, targetStatusID string) (*apimodel.Status, gtserror.WithCode) {
l := p.log.WithField("func", "Unboost") targetStatus, err := p.db.GetStatusByID(targetStatusID)
if err != nil {
l.Tracef("going to search for target status %s", targetStatusID)
targetStatus := &gtsmodel.Status{}
if err := p.db.GetByID(targetStatusID, targetStatus); err != nil {
return nil, gtserror.NewErrorNotFound(fmt.Errorf("error fetching status %s: %s", targetStatusID, err)) return nil, gtserror.NewErrorNotFound(fmt.Errorf("error fetching status %s: %s", targetStatusID, err))
} }
if targetStatus.Account == nil {
l.Tracef("going to search for target account %s", targetStatus.AccountID) return nil, gtserror.NewErrorNotFound(fmt.Errorf("no status owner for status %s", targetStatusID))
targetAccount := &gtsmodel.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 see if status is visible") visible, err := p.filter.StatusVisible(targetStatus, requestingAccount)
visible, err := p.filter.StatusVisible(targetStatus, account)
if err != nil { if err != nil {
return nil, gtserror.NewErrorNotFound(fmt.Errorf("error seeing if status %s is visible: %s", targetStatus.ID, err)) return nil, gtserror.NewErrorNotFound(fmt.Errorf("error seeing if status %s is visible: %s", targetStatus.ID, err))
} }
if !visible { if !visible {
return nil, gtserror.NewErrorNotFound(errors.New("status is not visible")) return nil, gtserror.NewErrorNotFound(errors.New("status is not visible"))
} }
@ -46,7 +38,7 @@ func (p *processor) Unboost(account *gtsmodel.Account, application *gtsmodel.App
}, },
{ {
Key: "account_id", Key: "account_id",
Value: account.ID, Value: requestingAccount.ID,
}, },
} }
err = p.db.GetWhere(where, gtsBoost) err = p.db.GetWhere(where, gtsBoost)
@ -71,22 +63,23 @@ func (p *processor) Unboost(account *gtsmodel.Account, application *gtsmodel.App
} }
// pin some stuff onto the boost while we have it out of the db // pin some stuff onto the boost while we have it out of the db
gtsBoost.Account = requestingAccount
gtsBoost.BoostOf = targetStatus gtsBoost.BoostOf = targetStatus
gtsBoost.BoostOf.Account = targetAccount gtsBoost.BoostOfAccount = targetStatus.Account
gtsBoost.BoostOfAccount = targetAccount gtsBoost.BoostOf.Account = targetStatus.Account
gtsBoost.Account = account
// send it back to the processor for async processing // send it back to the processor for async processing
p.fromClientAPI <- gtsmodel.FromClientAPI{ p.fromClientAPI <- gtsmodel.FromClientAPI{
APObjectType: gtsmodel.ActivityStreamsAnnounce, APObjectType: gtsmodel.ActivityStreamsAnnounce,
APActivityType: gtsmodel.ActivityStreamsUndo, APActivityType: gtsmodel.ActivityStreamsUndo,
GTSModel: gtsBoost, GTSModel: gtsBoost,
OriginAccount: account, OriginAccount: requestingAccount,
TargetAccount: targetAccount, TargetAccount: targetStatus.Account,
} }
} }
mastoStatus, err := p.tc.StatusToMasto(targetStatus, account) mastoStatus, err := p.tc.StatusToMasto(targetStatus, requestingAccount)
if err != nil { if err != nil {
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting status %s to frontend representation: %s", targetStatus.ID, err)) return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting status %s to frontend representation: %s", targetStatus.ID, err))
} }

View file

@ -10,26 +10,19 @@ import (
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
) )
func (p *processor) Unfave(account *gtsmodel.Account, targetStatusID string) (*apimodel.Status, gtserror.WithCode) { func (p *processor) Unfave(requestingAccount *gtsmodel.Account, targetStatusID string) (*apimodel.Status, gtserror.WithCode) {
l := p.log.WithField("func", "StatusUnfave") targetStatus, err := p.db.GetStatusByID(targetStatusID)
l.Tracef("going to search for target status %s", targetStatusID) if err != nil {
targetStatus := &gtsmodel.Status{}
if err := p.db.GetByID(targetStatusID, targetStatus); err != nil {
return nil, gtserror.NewErrorNotFound(fmt.Errorf("error fetching status %s: %s", targetStatusID, err)) return nil, gtserror.NewErrorNotFound(fmt.Errorf("error fetching status %s: %s", targetStatusID, err))
} }
if targetStatus.Account == nil {
l.Tracef("going to search for target account %s", targetStatus.AccountID) return nil, gtserror.NewErrorNotFound(fmt.Errorf("no status owner for status %s", targetStatusID))
targetAccount := &gtsmodel.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 see if status is visible") visible, err := p.filter.StatusVisible(targetStatus, requestingAccount)
visible, err := p.filter.StatusVisible(targetStatus, account)
if err != nil { if err != nil {
return nil, gtserror.NewErrorNotFound(fmt.Errorf("error seeing if status %s is visible: %s", targetStatus.ID, err)) return nil, gtserror.NewErrorNotFound(fmt.Errorf("error seeing if status %s is visible: %s", targetStatus.ID, err))
} }
if !visible { if !visible {
return nil, gtserror.NewErrorNotFound(errors.New("status is not visible")) return nil, gtserror.NewErrorNotFound(errors.New("status is not visible"))
} }
@ -38,7 +31,7 @@ func (p *processor) Unfave(account *gtsmodel.Account, targetStatusID string) (*a
var toUnfave bool var toUnfave bool
gtsFave := &gtsmodel.StatusFave{} gtsFave := &gtsmodel.StatusFave{}
err = p.db.GetWhere([]db.Where{{Key: "status_id", Value: targetStatus.ID}, {Key: "account_id", Value: account.ID}}, gtsFave) err = p.db.GetWhere([]db.Where{{Key: "status_id", Value: targetStatus.ID}, {Key: "account_id", Value: requestingAccount.ID}}, gtsFave)
if err == nil { if err == nil {
// we have a fave // we have a fave
toUnfave = true toUnfave = true
@ -54,7 +47,7 @@ func (p *processor) Unfave(account *gtsmodel.Account, targetStatusID string) (*a
if toUnfave { if toUnfave {
// we had a fave, so take some action to get rid of it // we had a fave, so take some action to get rid of it
if err := p.db.DeleteWhere([]db.Where{{Key: "status_id", Value: targetStatus.ID}, {Key: "account_id", Value: account.ID}}, gtsFave); err != nil { if err := p.db.DeleteWhere([]db.Where{{Key: "status_id", Value: targetStatus.ID}, {Key: "account_id", Value: requestingAccount.ID}}, gtsFave); err != nil {
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error unfaveing status: %s", err)) return nil, gtserror.NewErrorInternalError(fmt.Errorf("error unfaveing status: %s", err))
} }
@ -63,12 +56,12 @@ func (p *processor) Unfave(account *gtsmodel.Account, targetStatusID string) (*a
APObjectType: gtsmodel.ActivityStreamsLike, APObjectType: gtsmodel.ActivityStreamsLike,
APActivityType: gtsmodel.ActivityStreamsUndo, APActivityType: gtsmodel.ActivityStreamsUndo,
GTSModel: gtsFave, GTSModel: gtsFave,
OriginAccount: account, OriginAccount: requestingAccount,
TargetAccount: targetAccount, TargetAccount: targetStatus.Account,
} }
} }
mastoStatus, err := p.tc.StatusToMasto(targetStatus, account) mastoStatus, err := p.tc.StatusToMasto(targetStatus, requestingAccount)
if err != nil { if err != nil {
return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting status %s to frontend representation: %s", targetStatus.ID, err)) return nil, gtserror.NewErrorInternalError(fmt.Errorf("error converting status %s to frontend representation: %s", targetStatus.ID, err))
} }

View file

@ -119,7 +119,7 @@ func (p *processor) ProcessReplyToID(form *apimodel.AdvancedStatusCreateForm, th
return fmt.Errorf("status with id %s not replyable: %s", form.InReplyToID, err) return fmt.Errorf("status with id %s not replyable: %s", form.InReplyToID, err)
} }
// check if a block exists // check if a block exists
if blocked, err := p.db.Blocked(thisAccountID, repliedAccount.ID); err != nil { if blocked, err := p.db.Blocked(thisAccountID, repliedAccount.ID, true); err != nil {
if err != db.ErrNoEntries { if err != db.ErrNoEntries {
return fmt.Errorf("status with id %s not replyable: %s", form.InReplyToID, err) return fmt.Errorf("status with id %s not replyable: %s", form.InReplyToID, err)
} }
@ -156,8 +156,8 @@ func (p *processor) ProcessMediaIDs(form *apimodel.AdvancedStatusCreateForm, thi
gtsMediaAttachments = append(gtsMediaAttachments, a) gtsMediaAttachments = append(gtsMediaAttachments, a)
attachments = append(attachments, a.ID) attachments = append(attachments, a.ID)
} }
status.GTSMediaAttachments = gtsMediaAttachments status.Attachments = gtsMediaAttachments
status.Attachments = attachments status.AttachmentIDs = attachments
return nil return nil
} }
@ -192,9 +192,9 @@ func (p *processor) ProcessMentions(form *apimodel.AdvancedStatusCreateForm, acc
menchies = append(menchies, menchie.ID) menchies = append(menchies, menchie.ID)
} }
// add full populated gts menchies to the status for passing them around conveniently // add full populated gts menchies to the status for passing them around conveniently
status.GTSMentions = gtsMenchies status.Mentions = gtsMenchies
// add just the ids of the mentioned accounts to the status for putting in the db // add just the ids of the mentioned accounts to the status for putting in the db
status.Mentions = menchies status.MentionIDs = menchies
return nil return nil
} }
@ -211,9 +211,9 @@ func (p *processor) ProcessTags(form *apimodel.AdvancedStatusCreateForm, account
tags = append(tags, tag.ID) tags = append(tags, tag.ID)
} }
// add full populated gts tags to the status for passing them around conveniently // add full populated gts tags to the status for passing them around conveniently
status.GTSTags = gtsTags status.Tags = gtsTags
// add just the ids of the used tags to the status for putting in the db // add just the ids of the used tags to the status for putting in the db
status.Tags = tags status.TagIDs = tags
return nil return nil
} }
@ -227,9 +227,9 @@ func (p *processor) ProcessEmojis(form *apimodel.AdvancedStatusCreateForm, accou
emojis = append(emojis, e.ID) emojis = append(emojis, e.ID)
} }
// add full populated gts emojis to the status for passing them around conveniently // add full populated gts emojis to the status for passing them around conveniently
status.GTSEmojis = gtsEmojis status.Emojis = gtsEmojis
// add just the ids of the used emojis to the status for putting in the db // add just the ids of the used emojis to the status for putting in the db
status.Emojis = emojis status.EmojiIDs = emojis
return nil return nil
} }
@ -252,9 +252,9 @@ func (p *processor) ProcessContent(form *apimodel.AdvancedStatusCreateForm, acco
var formatted string var formatted string
switch form.Format { switch form.Format {
case apimodel.StatusFormatPlain: case apimodel.StatusFormatPlain:
formatted = p.formatter.FromPlain(content, status.GTSMentions, status.GTSTags) formatted = p.formatter.FromPlain(content, status.Mentions, status.Tags)
case apimodel.StatusFormatMarkdown: case apimodel.StatusFormatMarkdown:
formatted = p.formatter.FromMarkdown(content, status.GTSMentions, status.GTSTags) formatted = p.formatter.FromMarkdown(content, status.Mentions, status.Tags)
default: default:
return fmt.Errorf("format %s not recognised as a valid status format", form.Format) return fmt.Errorf("format %s not recognised as a valid status format", form.Format)
} }

View file

@ -91,19 +91,19 @@ func (suite *UtilTestSuite) TestProcessMentions1() {
err := suite.status.ProcessMentions(form, creatingAccount.ID, status) err := suite.status.ProcessMentions(form, creatingAccount.ID, status)
assert.NoError(suite.T(), err) assert.NoError(suite.T(), err)
assert.Len(suite.T(), status.GTSMentions, 1) assert.Len(suite.T(), status.Mentions, 1)
newMention := status.GTSMentions[0] newMention := status.Mentions[0]
assert.Equal(suite.T(), mentionedAccount.ID, newMention.TargetAccountID) assert.Equal(suite.T(), mentionedAccount.ID, newMention.TargetAccountID)
assert.Equal(suite.T(), creatingAccount.ID, newMention.OriginAccountID) assert.Equal(suite.T(), creatingAccount.ID, newMention.OriginAccountID)
assert.Equal(suite.T(), creatingAccount.URI, newMention.OriginAccountURI) assert.Equal(suite.T(), creatingAccount.URI, newMention.OriginAccountURI)
assert.Equal(suite.T(), status.ID, newMention.StatusID) assert.Equal(suite.T(), status.ID, newMention.StatusID)
assert.Equal(suite.T(), fmt.Sprintf("@%s@%s", mentionedAccount.Username, mentionedAccount.Domain), newMention.NameString) assert.Equal(suite.T(), fmt.Sprintf("@%s@%s", mentionedAccount.Username, mentionedAccount.Domain), newMention.NameString)
assert.Equal(suite.T(), mentionedAccount.URI, newMention.MentionedAccountURI) assert.Equal(suite.T(), mentionedAccount.URI, newMention.TargetAccountURI)
assert.Equal(suite.T(), mentionedAccount.URL, newMention.MentionedAccountURL) assert.Equal(suite.T(), mentionedAccount.URL, newMention.TargetAccountURL)
assert.NotNil(suite.T(), newMention.GTSAccount) assert.NotNil(suite.T(), newMention.OriginAccount)
assert.Len(suite.T(), status.Mentions, 1) assert.Len(suite.T(), status.MentionIDs, 1)
assert.Equal(suite.T(), newMention.ID, status.Mentions[0]) assert.Equal(suite.T(), newMention.ID, status.MentionIDs[0])
} }
func (suite *UtilTestSuite) TestProcessContentFull1() { func (suite *UtilTestSuite) TestProcessContentFull1() {
@ -232,19 +232,19 @@ func (suite *UtilTestSuite) TestProcessMentions2() {
err := suite.status.ProcessMentions(form, creatingAccount.ID, status) err := suite.status.ProcessMentions(form, creatingAccount.ID, status)
assert.NoError(suite.T(), err) assert.NoError(suite.T(), err)
assert.Len(suite.T(), status.GTSMentions, 1) assert.Len(suite.T(), status.Mentions, 1)
newMention := status.GTSMentions[0] newMention := status.Mentions[0]
assert.Equal(suite.T(), mentionedAccount.ID, newMention.TargetAccountID) assert.Equal(suite.T(), mentionedAccount.ID, newMention.TargetAccountID)
assert.Equal(suite.T(), creatingAccount.ID, newMention.OriginAccountID) assert.Equal(suite.T(), creatingAccount.ID, newMention.OriginAccountID)
assert.Equal(suite.T(), creatingAccount.URI, newMention.OriginAccountURI) assert.Equal(suite.T(), creatingAccount.URI, newMention.OriginAccountURI)
assert.Equal(suite.T(), status.ID, newMention.StatusID) assert.Equal(suite.T(), status.ID, newMention.StatusID)
assert.Equal(suite.T(), fmt.Sprintf("@%s@%s", mentionedAccount.Username, mentionedAccount.Domain), newMention.NameString) assert.Equal(suite.T(), fmt.Sprintf("@%s@%s", mentionedAccount.Username, mentionedAccount.Domain), newMention.NameString)
assert.Equal(suite.T(), mentionedAccount.URI, newMention.MentionedAccountURI) assert.Equal(suite.T(), mentionedAccount.URI, newMention.TargetAccountURI)
assert.Equal(suite.T(), mentionedAccount.URL, newMention.MentionedAccountURL) assert.Equal(suite.T(), mentionedAccount.URL, newMention.TargetAccountURL)
assert.NotNil(suite.T(), newMention.GTSAccount) assert.NotNil(suite.T(), newMention.OriginAccount)
assert.Len(suite.T(), status.Mentions, 1) assert.Len(suite.T(), status.MentionIDs, 1)
assert.Equal(suite.T(), newMention.ID, status.Mentions[0]) assert.Equal(suite.T(), newMention.ID, status.MentionIDs[0])
} }
func (suite *UtilTestSuite) TestProcessContentFull2() { func (suite *UtilTestSuite) TestProcessContentFull2() {

View file

@ -93,9 +93,9 @@ func (f *formatter) ReplaceMentions(in string, mentions []*gtsmodel.Mention) str
// make sure we have a target account, either by getting one pinned on the mention, // make sure we have a target account, either by getting one pinned on the mention,
// or by pulling it from the database // or by pulling it from the database
var targetAccount *gtsmodel.Account var targetAccount *gtsmodel.Account
if menchie.GTSAccount != nil { if menchie.OriginAccount != nil {
// got it from the mention // got it from the mention
targetAccount = menchie.GTSAccount targetAccount = menchie.OriginAccount
} else { } else {
a := &gtsmodel.Account{} a := &gtsmodel.Account{}
if err := f.db.GetByID(menchie.TargetAccountID, a); err == nil { if err := f.db.GetByID(menchie.TargetAccountID, a); err == nil {

View file

@ -188,22 +188,22 @@ func (c *converter) ASStatusToStatus(statusable ap.Statusable) (*gtsmodel.Status
// attachments to dereference and fetch later on (we don't do that here) // attachments to dereference and fetch later on (we don't do that here)
if attachments, err := ap.ExtractAttachments(statusable); err == nil { if attachments, err := ap.ExtractAttachments(statusable); err == nil {
status.GTSMediaAttachments = attachments status.Attachments = attachments
} }
// hashtags to dereference later on // hashtags to dereference later on
if hashtags, err := ap.ExtractHashtags(statusable); err == nil { if hashtags, err := ap.ExtractHashtags(statusable); err == nil {
status.GTSTags = hashtags status.Tags = hashtags
} }
// emojis to dereference and fetch later on // emojis to dereference and fetch later on
if emojis, err := ap.ExtractEmojis(statusable); err == nil { if emojis, err := ap.ExtractEmojis(statusable); err == nil {
status.GTSEmojis = emojis status.Emojis = emojis
} }
// mentions to dereference later on // mentions to dereference later on
if mentions, err := ap.ExtractMentions(statusable); err == nil { if mentions, err := ap.ExtractMentions(statusable); err == nil {
status.GTSMentions = mentions status.Mentions = mentions
} }
// cw string for this status // cw string for this status
@ -515,10 +515,10 @@ func (c *converter) ASAnnounceToStatus(announceable ap.Announceable) (*gtsmodel.
status.AccountURI = boostingAccount.URI status.AccountURI = boostingAccount.URI
// these will all be wrapped in the boosted status so set them empty here // these will all be wrapped in the boosted status so set them empty here
status.Attachments = []string{} status.AttachmentIDs = []string{}
status.Tags = []string{} status.TagIDs = []string{}
status.Mentions = []string{} status.MentionIDs = []string{}
status.Emojis = []string{} status.EmojiIDs = []string{}
// parse the visibility from the To and CC entries // parse the visibility from the To and CC entries
var visibility gtsmodel.Visibility var visibility gtsmodel.Visibility

View file

@ -54,10 +54,10 @@ func (c *converter) StatusToBoost(s *gtsmodel.Status, boostingAccount *gtsmodel.
InReplyToAccountID: "", InReplyToAccountID: "",
// these will all be wrapped in the boosted status so set them empty here // these will all be wrapped in the boosted status so set them empty here
Attachments: []string{}, AttachmentIDs: []string{},
Tags: []string{}, TagIDs: []string{},
Mentions: []string{}, MentionIDs: []string{},
Emojis: []string{}, EmojiIDs: []string{},
// the below fields will be taken from the target status // the below fields will be taken from the target status
Content: s.Content, Content: s.Content,

View file

@ -408,7 +408,7 @@ func (c *converter) StatusToAS(s *gtsmodel.Status) (vocab.ActivityStreamsNote, e
tagProp := streams.NewActivityStreamsTagProperty() tagProp := streams.NewActivityStreamsTagProperty()
// tag -- mentions // tag -- mentions
for _, m := range s.GTSMentions { for _, m := range s.Mentions {
asMention, err := c.MentionToAS(m) asMention, err := c.MentionToAS(m)
if err != nil { if err != nil {
return nil, fmt.Errorf("StatusToAS: error converting mention to AS mention: %s", err) return nil, fmt.Errorf("StatusToAS: error converting mention to AS mention: %s", err)
@ -441,10 +441,10 @@ func (c *converter) StatusToAS(s *gtsmodel.Status) (vocab.ActivityStreamsNote, e
switch s.Visibility { switch s.Visibility {
case gtsmodel.VisibilityDirect: case gtsmodel.VisibilityDirect:
// if DIRECT, then only mentioned users should be added to TO, and nothing to CC // if DIRECT, then only mentioned users should be added to TO, and nothing to CC
for _, m := range s.GTSMentions { for _, m := range s.Mentions {
iri, err := url.Parse(m.GTSAccount.URI) iri, err := url.Parse(m.OriginAccount.URI)
if err != nil { if err != nil {
return nil, fmt.Errorf("StatusToAS: error parsing uri %s: %s", m.GTSAccount.URI, err) return nil, fmt.Errorf("StatusToAS: error parsing uri %s: %s", m.OriginAccount.URI, err)
} }
toProp.AppendIRI(iri) toProp.AppendIRI(iri)
} }
@ -453,10 +453,10 @@ func (c *converter) StatusToAS(s *gtsmodel.Status) (vocab.ActivityStreamsNote, e
case gtsmodel.VisibilityFollowersOnly: case gtsmodel.VisibilityFollowersOnly:
// if FOLLOWERS ONLY then we want to add followers to TO, and mentions to CC // if FOLLOWERS ONLY then we want to add followers to TO, and mentions to CC
toProp.AppendIRI(authorFollowersURI) toProp.AppendIRI(authorFollowersURI)
for _, m := range s.GTSMentions { for _, m := range s.Mentions {
iri, err := url.Parse(m.GTSAccount.URI) iri, err := url.Parse(m.OriginAccount.URI)
if err != nil { if err != nil {
return nil, fmt.Errorf("StatusToAS: error parsing uri %s: %s", m.GTSAccount.URI, err) return nil, fmt.Errorf("StatusToAS: error parsing uri %s: %s", m.OriginAccount.URI, err)
} }
ccProp.AppendIRI(iri) ccProp.AppendIRI(iri)
} }
@ -464,10 +464,10 @@ func (c *converter) StatusToAS(s *gtsmodel.Status) (vocab.ActivityStreamsNote, e
// if UNLOCKED, we want to add followers to TO, and public and mentions to CC // if UNLOCKED, we want to add followers to TO, and public and mentions to CC
toProp.AppendIRI(authorFollowersURI) toProp.AppendIRI(authorFollowersURI)
ccProp.AppendIRI(publicURI) ccProp.AppendIRI(publicURI)
for _, m := range s.GTSMentions { for _, m := range s.Mentions {
iri, err := url.Parse(m.GTSAccount.URI) iri, err := url.Parse(m.OriginAccount.URI)
if err != nil { if err != nil {
return nil, fmt.Errorf("StatusToAS: error parsing uri %s: %s", m.GTSAccount.URI, err) return nil, fmt.Errorf("StatusToAS: error parsing uri %s: %s", m.OriginAccount.URI, err)
} }
ccProp.AppendIRI(iri) ccProp.AppendIRI(iri)
} }
@ -475,10 +475,10 @@ func (c *converter) StatusToAS(s *gtsmodel.Status) (vocab.ActivityStreamsNote, e
// if PUBLIC, we want to add public to TO, and followers and mentions to CC // if PUBLIC, we want to add public to TO, and followers and mentions to CC
toProp.AppendIRI(publicURI) toProp.AppendIRI(publicURI)
ccProp.AppendIRI(authorFollowersURI) ccProp.AppendIRI(authorFollowersURI)
for _, m := range s.GTSMentions { for _, m := range s.Mentions {
iri, err := url.Parse(m.GTSAccount.URI) iri, err := url.Parse(m.OriginAccount.URI)
if err != nil { if err != nil {
return nil, fmt.Errorf("StatusToAS: error parsing uri %s: %s", m.GTSAccount.URI, err) return nil, fmt.Errorf("StatusToAS: error parsing uri %s: %s", m.OriginAccount.URI, err)
} }
ccProp.AppendIRI(iri) ccProp.AppendIRI(iri)
} }
@ -496,7 +496,7 @@ func (c *converter) StatusToAS(s *gtsmodel.Status) (vocab.ActivityStreamsNote, e
// attachment // attachment
attachmentProp := streams.NewActivityStreamsAttachmentProperty() attachmentProp := streams.NewActivityStreamsAttachmentProperty()
for _, a := range s.GTSMediaAttachments { for _, a := range s.Attachments {
doc, err := c.AttachmentToAS(a) doc, err := c.AttachmentToAS(a)
if err != nil { if err != nil {
return nil, fmt.Errorf("StatusToAS: error converting attachment: %s", err) return nil, fmt.Errorf("StatusToAS: error converting attachment: %s", err)
@ -565,12 +565,12 @@ func (c *converter) FollowToAS(f *gtsmodel.Follow, originAccount *gtsmodel.Accou
} }
func (c *converter) MentionToAS(m *gtsmodel.Mention) (vocab.ActivityStreamsMention, error) { func (c *converter) MentionToAS(m *gtsmodel.Mention) (vocab.ActivityStreamsMention, error) {
if m.GTSAccount == nil { if m.OriginAccount == nil {
a := &gtsmodel.Account{} a := &gtsmodel.Account{}
if err := c.db.GetWhere([]db.Where{{Key: "target_account_id", Value: m.TargetAccountID}}, a); err != nil { if err := c.db.GetWhere([]db.Where{{Key: "target_account_id", Value: m.TargetAccountID}}, a); err != nil {
return nil, fmt.Errorf("MentionToAS: error getting target account from db: %s", err) return nil, fmt.Errorf("MentionToAS: error getting target account from db: %s", err)
} }
m.GTSAccount = a m.OriginAccount = a
} }
// create the mention // create the mention
@ -578,21 +578,21 @@ func (c *converter) MentionToAS(m *gtsmodel.Mention) (vocab.ActivityStreamsMenti
// href -- this should be the URI of the mentioned user // href -- this should be the URI of the mentioned user
hrefProp := streams.NewActivityStreamsHrefProperty() hrefProp := streams.NewActivityStreamsHrefProperty()
hrefURI, err := url.Parse(m.GTSAccount.URI) hrefURI, err := url.Parse(m.OriginAccount.URI)
if err != nil { if err != nil {
return nil, fmt.Errorf("MentionToAS: error parsing uri %s: %s", m.GTSAccount.URI, err) return nil, fmt.Errorf("MentionToAS: error parsing uri %s: %s", m.OriginAccount.URI, err)
} }
hrefProp.SetIRI(hrefURI) hrefProp.SetIRI(hrefURI)
mention.SetActivityStreamsHref(hrefProp) mention.SetActivityStreamsHref(hrefProp)
// name -- this should be the namestring of the mentioned user, something like @whatever@example.org // name -- this should be the namestring of the mentioned user, something like @whatever@example.org
var domain string var domain string
if m.GTSAccount.Domain == "" { if m.OriginAccount.Domain == "" {
domain = c.config.AccountDomain domain = c.config.AccountDomain
} else { } else {
domain = m.GTSAccount.Domain domain = m.OriginAccount.Domain
} }
username := m.GTSAccount.Username username := m.OriginAccount.Username
nameString := fmt.Sprintf("@%s@%s", username, domain) nameString := fmt.Sprintf("@%s@%s", username, domain)
nameProp := streams.NewActivityStreamsNameProperty() nameProp := streams.NewActivityStreamsNameProperty()
nameProp.AppendXMLSchemaString(nameString) nameProp.AppendXMLSchemaString(nameString)

View file

@ -357,8 +357,8 @@ func (c *converter) StatusToMasto(s *gtsmodel.Status, requestingAccount *gtsmode
mastoAttachments := []model.Attachment{} mastoAttachments := []model.Attachment{}
// the status might already have some gts attachments on it if it's not been pulled directly from the database // the status might already have some gts attachments on it if it's not been pulled directly from the database
// if so, we can directly convert the gts attachments into masto ones // if so, we can directly convert the gts attachments into masto ones
if s.GTSMediaAttachments != nil { if s.Attachments != nil {
for _, gtsAttachment := range s.GTSMediaAttachments { for _, gtsAttachment := range s.Attachments {
mastoAttachment, err := c.AttachmentToMasto(gtsAttachment) mastoAttachment, err := c.AttachmentToMasto(gtsAttachment)
if err != nil { if err != nil {
return nil, fmt.Errorf("error converting attachment with id %s: %s", gtsAttachment.ID, err) return nil, fmt.Errorf("error converting attachment with id %s: %s", gtsAttachment.ID, err)
@ -368,7 +368,7 @@ func (c *converter) StatusToMasto(s *gtsmodel.Status, requestingAccount *gtsmode
// the status doesn't have gts attachments on it, but it does have attachment IDs // the status doesn't have gts attachments on it, but it does have attachment IDs
// in this case, we need to pull the gts attachments from the db to convert them into masto ones // in this case, we need to pull the gts attachments from the db to convert them into masto ones
} else { } else {
for _, a := range s.Attachments { for _, a := range s.AttachmentIDs {
gtsAttachment := &gtsmodel.MediaAttachment{} gtsAttachment := &gtsmodel.MediaAttachment{}
if err := c.db.GetByID(a, gtsAttachment); err != nil { if err := c.db.GetByID(a, gtsAttachment); err != nil {
return nil, fmt.Errorf("error getting attachment with id %s: %s", a, err) return nil, fmt.Errorf("error getting attachment with id %s: %s", a, err)
@ -384,8 +384,8 @@ func (c *converter) StatusToMasto(s *gtsmodel.Status, requestingAccount *gtsmode
mastoMentions := []model.Mention{} mastoMentions := []model.Mention{}
// the status might already have some gts mentions on it if it's not been pulled directly from the database // the status might already have some gts mentions on it if it's not been pulled directly from the database
// if so, we can directly convert the gts mentions into masto ones // if so, we can directly convert the gts mentions into masto ones
if s.GTSMentions != nil { if s.Mentions != nil {
for _, gtsMention := range s.GTSMentions { for _, gtsMention := range s.Mentions {
mastoMention, err := c.MentionToMasto(gtsMention) mastoMention, err := c.MentionToMasto(gtsMention)
if err != nil { if err != nil {
return nil, fmt.Errorf("error converting mention with id %s: %s", gtsMention.ID, err) return nil, fmt.Errorf("error converting mention with id %s: %s", gtsMention.ID, err)
@ -395,7 +395,7 @@ func (c *converter) StatusToMasto(s *gtsmodel.Status, requestingAccount *gtsmode
// the status doesn't have gts mentions on it, but it does have mention IDs // the status doesn't have gts mentions on it, but it does have mention IDs
// in this case, we need to pull the gts mentions from the db to convert them into masto ones // in this case, we need to pull the gts mentions from the db to convert them into masto ones
} else { } else {
for _, m := range s.Mentions { for _, m := range s.MentionIDs {
gtsMention := &gtsmodel.Mention{} gtsMention := &gtsmodel.Mention{}
if err := c.db.GetByID(m, gtsMention); err != nil { if err := c.db.GetByID(m, gtsMention); err != nil {
return nil, fmt.Errorf("error getting mention with id %s: %s", m, err) return nil, fmt.Errorf("error getting mention with id %s: %s", m, err)
@ -411,8 +411,8 @@ func (c *converter) StatusToMasto(s *gtsmodel.Status, requestingAccount *gtsmode
mastoTags := []model.Tag{} mastoTags := []model.Tag{}
// the status might already have some gts tags on it if it's not been pulled directly from the database // the status might already have some gts tags on it if it's not been pulled directly from the database
// if so, we can directly convert the gts tags into masto ones // if so, we can directly convert the gts tags into masto ones
if s.GTSTags != nil { if s.Tags != nil {
for _, gtsTag := range s.GTSTags { for _, gtsTag := range s.Tags {
mastoTag, err := c.TagToMasto(gtsTag) mastoTag, err := c.TagToMasto(gtsTag)
if err != nil { if err != nil {
return nil, fmt.Errorf("error converting tag with id %s: %s", gtsTag.ID, err) return nil, fmt.Errorf("error converting tag with id %s: %s", gtsTag.ID, err)
@ -422,7 +422,7 @@ func (c *converter) StatusToMasto(s *gtsmodel.Status, requestingAccount *gtsmode
// the status doesn't have gts tags on it, but it does have tag IDs // the status doesn't have gts tags on it, but it does have tag IDs
// in this case, we need to pull the gts tags from the db to convert them into masto ones // in this case, we need to pull the gts tags from the db to convert them into masto ones
} else { } else {
for _, t := range s.Tags { for _, t := range s.TagIDs {
gtsTag := &gtsmodel.Tag{} gtsTag := &gtsmodel.Tag{}
if err := c.db.GetByID(t, gtsTag); err != nil { if err := c.db.GetByID(t, gtsTag); err != nil {
return nil, fmt.Errorf("error getting tag with id %s: %s", t, err) return nil, fmt.Errorf("error getting tag with id %s: %s", t, err)
@ -438,8 +438,8 @@ func (c *converter) StatusToMasto(s *gtsmodel.Status, requestingAccount *gtsmode
mastoEmojis := []model.Emoji{} mastoEmojis := []model.Emoji{}
// the status might already have some gts emojis on it if it's not been pulled directly from the database // the status might already have some gts emojis on it if it's not been pulled directly from the database
// if so, we can directly convert the gts emojis into masto ones // if so, we can directly convert the gts emojis into masto ones
if s.GTSEmojis != nil { if s.Emojis != nil {
for _, gtsEmoji := range s.GTSEmojis { for _, gtsEmoji := range s.Emojis {
mastoEmoji, err := c.EmojiToMasto(gtsEmoji) mastoEmoji, err := c.EmojiToMasto(gtsEmoji)
if err != nil { if err != nil {
return nil, fmt.Errorf("error converting emoji with id %s: %s", gtsEmoji.ID, err) return nil, fmt.Errorf("error converting emoji with id %s: %s", gtsEmoji.ID, err)
@ -449,7 +449,7 @@ func (c *converter) StatusToMasto(s *gtsmodel.Status, requestingAccount *gtsmode
// the status doesn't have gts emojis on it, but it does have emoji IDs // the status doesn't have gts emojis on it, but it does have emoji IDs
// in this case, we need to pull the gts emojis from the db to convert them into masto ones // in this case, we need to pull the gts emojis from the db to convert them into masto ones
} else { } else {
for _, e := range s.Emojis { for _, e := range s.EmojiIDs {
gtsEmoji := &gtsmodel.Emoji{} gtsEmoji := &gtsmodel.Emoji{}
if err := c.db.GetByID(e, gtsEmoji); err != nil { if err := c.db.GetByID(e, gtsEmoji); err != nil {
return nil, fmt.Errorf("error getting emoji with id %s: %s", e, err) return nil, fmt.Errorf("error getting emoji with id %s: %s", e, err)
@ -609,46 +609,46 @@ func (c *converter) RelationshipToMasto(r *gtsmodel.Relationship) (*model.Relati
func (c *converter) NotificationToMasto(n *gtsmodel.Notification) (*model.Notification, error) { func (c *converter) NotificationToMasto(n *gtsmodel.Notification) (*model.Notification, error) {
if n.GTSTargetAccount == nil { if n.TargetAccount == nil {
tAccount := &gtsmodel.Account{} tAccount := &gtsmodel.Account{}
if err := c.db.GetByID(n.TargetAccountID, tAccount); err != nil { if err := c.db.GetByID(n.TargetAccountID, tAccount); err != nil {
return nil, fmt.Errorf("NotificationToMasto: error getting target account with id %s from the db: %s", n.TargetAccountID, err) return nil, fmt.Errorf("NotificationToMasto: error getting target account with id %s from the db: %s", n.TargetAccountID, err)
} }
n.GTSTargetAccount = tAccount n.TargetAccount = tAccount
} }
if n.GTSOriginAccount == nil { if n.OriginAccount == nil {
ogAccount := &gtsmodel.Account{} ogAccount := &gtsmodel.Account{}
if err := c.db.GetByID(n.OriginAccountID, ogAccount); err != nil { if err := c.db.GetByID(n.OriginAccountID, ogAccount); err != nil {
return nil, fmt.Errorf("NotificationToMasto: error getting origin account with id %s from the db: %s", n.OriginAccountID, err) return nil, fmt.Errorf("NotificationToMasto: error getting origin account with id %s from the db: %s", n.OriginAccountID, err)
} }
n.GTSOriginAccount = ogAccount n.OriginAccount = ogAccount
} }
mastoAccount, err := c.AccountToMastoPublic(n.GTSOriginAccount) mastoAccount, err := c.AccountToMastoPublic(n.OriginAccount)
if err != nil { if err != nil {
return nil, fmt.Errorf("NotificationToMasto: error converting account to masto: %s", err) return nil, fmt.Errorf("NotificationToMasto: error converting account to masto: %s", err)
} }
var mastoStatus *model.Status var mastoStatus *model.Status
if n.StatusID != "" { if n.StatusID != "" {
if n.GTSStatus == nil { if n.Status == nil {
status := &gtsmodel.Status{} status := &gtsmodel.Status{}
if err := c.db.GetByID(n.StatusID, status); err != nil { if err := c.db.GetByID(n.StatusID, status); err != nil {
return nil, fmt.Errorf("NotificationToMasto: error getting status with id %s from the db: %s", n.StatusID, err) return nil, fmt.Errorf("NotificationToMasto: error getting status with id %s from the db: %s", n.StatusID, err)
} }
n.GTSStatus = status n.Status = status
} }
if n.GTSStatus.Account == nil { if n.Status.Account == nil {
if n.GTSStatus.AccountID == n.GTSTargetAccount.ID { if n.Status.AccountID == n.TargetAccount.ID {
n.GTSStatus.Account = n.GTSTargetAccount n.Status.Account = n.TargetAccount
} else if n.GTSStatus.AccountID == n.GTSOriginAccount.ID { } else if n.Status.AccountID == n.OriginAccount.ID {
n.GTSStatus.Account = n.GTSOriginAccount n.Status.Account = n.OriginAccount
} }
} }
var err error var err error
mastoStatus, err = c.StatusToMasto(n.GTSStatus, nil) mastoStatus, err = c.StatusToMasto(n.Status, nil)
if err != nil { if err != nil {
return nil, fmt.Errorf("NotificationToMasto: error converting status to masto: %s", err) return nil, fmt.Errorf("NotificationToMasto: error converting status to masto: %s", err)
} }

View file

@ -102,7 +102,7 @@ func (f *filter) StatusVisible(targetStatus *gtsmodel.Status, requestingAccount
// At this point we have a populated targetAccount, targetStatus, and requestingAccount, so we can check for blocks and whathaveyou // At this point we have a populated targetAccount, targetStatus, and requestingAccount, so we can check for blocks and whathaveyou
// First check if a block exists directly between the target account (which authored the status) and the requesting account. // First check if a block exists directly between the target account (which authored the status) and the requesting account.
if blocked, err := f.db.Blocked(targetAccount.ID, requestingAccount.ID); err != nil { if blocked, err := f.db.Blocked(targetAccount.ID, requestingAccount.ID, true); err != nil {
l.Debugf("something went wrong figuring out if the accounts have a block: %s", err) l.Debugf("something went wrong figuring out if the accounts have a block: %s", err)
return false, err return false, err
} else if blocked { } else if blocked {
@ -113,7 +113,7 @@ func (f *filter) StatusVisible(targetStatus *gtsmodel.Status, requestingAccount
// status replies to account id // status replies to account id
if relevantAccounts.ReplyToAccount != nil && relevantAccounts.ReplyToAccount.ID != requestingAccount.ID { if relevantAccounts.ReplyToAccount != nil && relevantAccounts.ReplyToAccount.ID != requestingAccount.ID {
if blocked, err := f.db.Blocked(relevantAccounts.ReplyToAccount.ID, requestingAccount.ID); err != nil { if blocked, err := f.db.Blocked(relevantAccounts.ReplyToAccount.ID, requestingAccount.ID, true); err != nil {
return false, err return false, err
} else if blocked { } else if blocked {
l.Trace("a block exists between requesting account and reply to account") l.Trace("a block exists between requesting account and reply to account")
@ -135,7 +135,7 @@ func (f *filter) StatusVisible(targetStatus *gtsmodel.Status, requestingAccount
// status boosts accounts id // status boosts accounts id
if relevantAccounts.BoostedStatusAuthor != nil { if relevantAccounts.BoostedStatusAuthor != nil {
if blocked, err := f.db.Blocked(relevantAccounts.BoostedStatusAuthor.ID, requestingAccount.ID); err != nil { if blocked, err := f.db.Blocked(relevantAccounts.BoostedStatusAuthor.ID, requestingAccount.ID, true); err != nil {
return false, err return false, err
} else if blocked { } else if blocked {
l.Trace("a block exists between requesting account and boosted account") l.Trace("a block exists between requesting account and boosted account")
@ -145,7 +145,7 @@ func (f *filter) StatusVisible(targetStatus *gtsmodel.Status, requestingAccount
// status boosts a reply to account id // status boosts a reply to account id
if relevantAccounts.BoostedReplyToAccount != nil { if relevantAccounts.BoostedReplyToAccount != nil {
if blocked, err := f.db.Blocked(relevantAccounts.BoostedReplyToAccount.ID, requestingAccount.ID); err != nil { if blocked, err := f.db.Blocked(relevantAccounts.BoostedReplyToAccount.ID, requestingAccount.ID, true); err != nil {
return false, err return false, err
} else if blocked { } else if blocked {
l.Trace("a block exists between requesting account and boosted reply to account") l.Trace("a block exists between requesting account and boosted reply to account")
@ -155,7 +155,7 @@ func (f *filter) StatusVisible(targetStatus *gtsmodel.Status, requestingAccount
// status mentions accounts // status mentions accounts
for _, a := range relevantAccounts.MentionedAccounts { for _, a := range relevantAccounts.MentionedAccounts {
if blocked, err := f.db.Blocked(a.ID, requestingAccount.ID); err != nil { if blocked, err := f.db.Blocked(a.ID, requestingAccount.ID, true); err != nil {
return false, err return false, err
} else if blocked { } else if blocked {
l.Trace("a block exists between requesting account and a mentioned account") l.Trace("a block exists between requesting account and a mentioned account")
@ -165,7 +165,7 @@ func (f *filter) StatusVisible(targetStatus *gtsmodel.Status, requestingAccount
// boost mentions accounts // boost mentions accounts
for _, a := range relevantAccounts.BoostedMentionedAccounts { for _, a := range relevantAccounts.BoostedMentionedAccounts {
if blocked, err := f.db.Blocked(a.ID, requestingAccount.ID); err != nil { if blocked, err := f.db.Blocked(a.ID, requestingAccount.ID, true); err != nil {
return false, err return false, err
} else if blocked { } else if blocked {
l.Trace("a block exists between requesting account and a boosted mentioned account") l.Trace("a block exists between requesting account and a boosted mentioned account")

View file

@ -13,28 +13,71 @@ func (f *filter) pullRelevantAccountsFromStatus(targetStatus *gtsmodel.Status) (
BoostedMentionedAccounts: []*gtsmodel.Account{}, BoostedMentionedAccounts: []*gtsmodel.Account{},
} }
// get the author account // get the author account if it's not set on the status already
if targetStatus.Account == nil { if targetStatus.Account == nil {
statusAuthor := &gtsmodel.Account{} statusAuthor, err := f.db.GetAccountByID(targetStatus.AccountID)
if err := f.db.GetByID(targetStatus.AccountID, statusAuthor); err != nil { if err != nil {
return accounts, fmt.Errorf("PullRelevantAccountsFromStatus: error getting statusAuthor with id %s: %s", targetStatus.AccountID, err) return accounts, fmt.Errorf("PullRelevantAccountsFromStatus: error getting statusAuthor with id %s: %s", targetStatus.AccountID, err)
} }
targetStatus.Account = statusAuthor targetStatus.Account = statusAuthor
} }
accounts.StatusAuthor = targetStatus.Account accounts.StatusAuthor = targetStatus.Account
// get the replied to account from the status and add it to the pile // get the replied to account if it's not set on the status already
if targetStatus.InReplyToAccountID != "" { if targetStatus.InReplyToAccountID != "" && targetStatus.InReplyToAccount == nil {
repliedToAccount := &gtsmodel.Account{} repliedToAccount, err := f.db.GetAccountByID(targetStatus.InReplyToAccountID)
if err := f.db.GetByID(targetStatus.InReplyToAccountID, repliedToAccount); err != nil { if err != nil {
return accounts, fmt.Errorf("PullRelevantAccountsFromStatus: error getting repliedToAcount with id %s: %s", targetStatus.InReplyToAccountID, err) return accounts, fmt.Errorf("PullRelevantAccountsFromStatus: error getting repliedToAcount with id %s: %s", targetStatus.InReplyToAccountID, err)
} }
accounts.ReplyToAccount = repliedToAccount targetStatus.InReplyToAccount = repliedToAccount
}
accounts.ReplyToAccount = targetStatus.InReplyToAccount
// get the boosted status if it's not set on the status already
if targetStatus.BoostOfID != "" && targetStatus.BoostOf == nil {
boostedStatus, err := f.db.GetStatusByID(targetStatus.BoostOfID)
if err != nil {
return accounts, fmt.Errorf("PullRelevantAccountsFromStatus: error getting boostedStatus with id %s: %s", targetStatus.BoostOfID, err)
}
targetStatus.BoostOf = boostedStatus
}
// get the boosted account if it's not set on the status already
if targetStatus.BoostOfAccountID != "" && targetStatus.BoostOfAccount == nil {
if targetStatus.BoostOf != nil && targetStatus.BoostOf.Account != nil {
targetStatus.BoostOfAccount = targetStatus.BoostOf.Account
} else {
boostedAccount, err := f.db.GetAccountByID(targetStatus.BoostOfAccountID)
if err != nil {
return accounts, fmt.Errorf("PullRelevantAccountsFromStatus: error getting boostOfAccount with id %s: %s", targetStatus.BoostOfAccountID, err)
}
targetStatus.BoostOfAccount = boostedAccount
}
}
accounts.BoostedStatusAuthor = targetStatus.BoostOfAccount
if targetStatus.BoostOf != nil {
// the boosted status might be a reply to another account so we should get that too
if targetStatus.BoostOf.InReplyToAccountID != "" && targetStatus.BoostOf.InReplyToAccount == nil {
boostOfInReplyToAccount, err := f.db.GetAccountByID(targetStatus.BoostOf.InReplyToAccountID)
if err != nil {
return accounts, fmt.Errorf("PullRelevantAccountsFromStatus: error getting boostOfInReplyToAccount with id %s: %s", targetStatus.BoostOf.InReplyToAccountID, err)
}
targetStatus.BoostOf.InReplyToAccount = boostOfInReplyToAccount
}
// now get all accounts with IDs that are mentioned in the status
if targetStatus.BoostOf.MentionIDs != nil && targetStatus.BoostOf.Mentions == nil {
mentions, err := f.db.GetMentions(targetStatus.BoostOf.MentionIDs)
if err != nil {
return accounts, fmt.Errorf("PullRelevantAccountsFromStatus: error getting mentions from boostOf status: %s", err)
}
targetStatus.BoostOf.Mentions = mentions
}
} }
// now get all accounts with IDs that are mentioned in the status // now get all accounts with IDs that are mentioned in the status
for _, mentionID := range targetStatus.Mentions { for _, mentionID := range targetStatus.MentionIDs {
mention := &gtsmodel.Mention{} mention := &gtsmodel.Mention{}
if err := f.db.GetByID(mentionID, mention); err != nil { if err := f.db.GetByID(mentionID, mention); err != nil {
return accounts, fmt.Errorf("PullRelevantAccountsFromStatus: error getting mention with id %s: %s", mentionID, err) return accounts, fmt.Errorf("PullRelevantAccountsFromStatus: error getting mention with id %s: %s", mentionID, err)
@ -47,43 +90,6 @@ func (f *filter) pullRelevantAccountsFromStatus(targetStatus *gtsmodel.Status) (
accounts.MentionedAccounts = append(accounts.MentionedAccounts, mentionedAccount) accounts.MentionedAccounts = append(accounts.MentionedAccounts, mentionedAccount)
} }
// get the boosted account from the status and add it to the pile
if targetStatus.BoostOfID != "" {
// retrieve the boosted status first
boostedStatus := &gtsmodel.Status{}
if err := f.db.GetByID(targetStatus.BoostOfID, boostedStatus); err != nil {
return accounts, fmt.Errorf("PullRelevantAccountsFromStatus: error getting boostedStatus with id %s: %s", targetStatus.BoostOfID, err)
}
boostedAccount := &gtsmodel.Account{}
if err := f.db.GetByID(boostedStatus.AccountID, boostedAccount); err != nil {
return accounts, fmt.Errorf("PullRelevantAccountsFromStatus: error getting boostedAccount with id %s: %s", boostedStatus.AccountID, err)
}
accounts.BoostedStatusAuthor = boostedAccount
// the boosted status might be a reply to another account so we should get that too
if boostedStatus.InReplyToAccountID != "" {
boostedStatusRepliedToAccount := &gtsmodel.Account{}
if err := f.db.GetByID(boostedStatus.InReplyToAccountID, boostedStatusRepliedToAccount); err != nil {
return accounts, fmt.Errorf("PullRelevantAccountsFromStatus: error getting boostedStatusRepliedToAccount with id %s: %s", boostedStatus.InReplyToAccountID, err)
}
accounts.BoostedReplyToAccount = boostedStatusRepliedToAccount
}
// now get all accounts with IDs that are mentioned in the status
for _, mentionID := range boostedStatus.Mentions {
mention := &gtsmodel.Mention{}
if err := f.db.GetByID(mentionID, mention); err != nil {
return accounts, fmt.Errorf("PullRelevantAccountsFromStatus: error getting boosted mention with id %s: %s", mentionID, err)
}
mentionedAccount := &gtsmodel.Account{}
if err := f.db.GetByID(mention.TargetAccountID, mentionedAccount); err != nil {
return accounts, fmt.Errorf("PullRelevantAccountsFromStatus: error getting boosted mentioned account: %s", err)
}
accounts.BoostedMentionedAccounts = append(accounts.BoostedMentionedAccounts, mentionedAccount)
}
}
return accounts, nil return accounts, nil
} }

View file

@ -793,10 +793,10 @@ func NewTestStatuses() map[string]*gtsmodel.Status {
URI: "http://localhost:8080/users/admin/statuses/01F8MH75CBF9JFX4ZAD54N0W0R", URI: "http://localhost:8080/users/admin/statuses/01F8MH75CBF9JFX4ZAD54N0W0R",
URL: "http://localhost:8080/@admin/statuses/01F8MH75CBF9JFX4ZAD54N0W0R", URL: "http://localhost:8080/@admin/statuses/01F8MH75CBF9JFX4ZAD54N0W0R",
Content: "hello world! #welcome ! first post on the instance :rainbow: !", Content: "hello world! #welcome ! first post on the instance :rainbow: !",
Attachments: []string{"01F8MH6NEM8D7527KZAECTCR76"}, AttachmentIDs: []string{"01F8MH6NEM8D7527KZAECTCR76"},
Tags: []string{"01F8MHA1A2NF9MJ3WCCQ3K8BSZ"}, TagIDs: []string{"01F8MHA1A2NF9MJ3WCCQ3K8BSZ"},
Mentions: []string{}, MentionIDs: []string{},
Emojis: []string{"01F8MH9H8E4VG3KDYJR9EGPXCQ"}, EmojiIDs: []string{"01F8MH9H8E4VG3KDYJR9EGPXCQ"},
CreatedAt: time.Now().Add(-71 * time.Hour), CreatedAt: time.Now().Add(-71 * time.Hour),
UpdatedAt: time.Now().Add(-71 * time.Hour), UpdatedAt: time.Now().Add(-71 * time.Hour),
Local: true, Local: true,
@ -917,7 +917,7 @@ func NewTestStatuses() map[string]*gtsmodel.Status {
URI: "http://localhost:8080/users/the_mighty_zork/statuses/01F8MH82FYRXD2RC6108DAJ5HB", URI: "http://localhost:8080/users/the_mighty_zork/statuses/01F8MH82FYRXD2RC6108DAJ5HB",
URL: "http://localhost:8080/@the_mighty_zork/statuses/01F8MH82FYRXD2RC6108DAJ5HB", URL: "http://localhost:8080/@the_mighty_zork/statuses/01F8MH82FYRXD2RC6108DAJ5HB",
Content: "here's a little gif of trent", Content: "here's a little gif of trent",
Attachments: []string{"01F8MH7TDVANYKWVE8VVKFPJTJ"}, AttachmentIDs: []string{"01F8MH7TDVANYKWVE8VVKFPJTJ"},
CreatedAt: time.Now().Add(-1 * time.Hour), CreatedAt: time.Now().Add(-1 * time.Hour),
UpdatedAt: time.Now().Add(-1 * time.Hour), UpdatedAt: time.Now().Add(-1 * time.Hour),
Local: true, Local: true,
@ -942,7 +942,7 @@ func NewTestStatuses() map[string]*gtsmodel.Status {
URI: "http://localhost:8080/users/the_mighty_zork/statuses/01FCTA44PW9H1TB328S9AQXKDS", URI: "http://localhost:8080/users/the_mighty_zork/statuses/01FCTA44PW9H1TB328S9AQXKDS",
URL: "http://localhost:8080/@the_mighty_zork/statuses/01FCTA44PW9H1TB328S9AQXKDS", URL: "http://localhost:8080/@the_mighty_zork/statuses/01FCTA44PW9H1TB328S9AQXKDS",
Content: "hi!", Content: "hi!",
Attachments: []string{}, AttachmentIDs: []string{},
CreatedAt: time.Now().Add(-1 * time.Minute), CreatedAt: time.Now().Add(-1 * time.Minute),
UpdatedAt: time.Now().Add(-1 * time.Minute), UpdatedAt: time.Now().Add(-1 * time.Minute),
Local: true, Local: true,
@ -1127,8 +1127,8 @@ func NewTestMentions() map[string]*gtsmodel.Mention {
OriginAccountURI: "http://localhost:8080/users/the_mighty_zork", OriginAccountURI: "http://localhost:8080/users/the_mighty_zork",
TargetAccountID: "01F8MH5ZK5VRH73AKHQM6Y9VNX", TargetAccountID: "01F8MH5ZK5VRH73AKHQM6Y9VNX",
NameString: "@foss_satan@fossbros-anonymous.io", NameString: "@foss_satan@fossbros-anonymous.io",
MentionedAccountURI: "http://fossbros-anonymous.io/users/foss_satan", TargetAccountURI: "http://fossbros-anonymous.io/users/foss_satan",
MentionedAccountURL: "http://fossbros-anonymous.io/@foss_satan", TargetAccountURL: "http://fossbros-anonymous.io/@foss_satan",
}, },
} }
} }