mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2025-12-29 22:26:15 -06:00
more updates
This commit is contained in:
parent
a3322b2bf3
commit
81ea286254
30 changed files with 290 additions and 251 deletions
|
|
@ -62,6 +62,8 @@ var models []interface{} = []interface{}{
|
||||||
>smodel.MediaAttachment{},
|
>smodel.MediaAttachment{},
|
||||||
>smodel.Mention{},
|
>smodel.Mention{},
|
||||||
>smodel.Status{},
|
>smodel.Status{},
|
||||||
|
>smodel.StatusToEmoji{},
|
||||||
|
>smodel.StatusToTag{},
|
||||||
>smodel.StatusFave{},
|
>smodel.StatusFave{},
|
||||||
>smodel.StatusBookmark{},
|
>smodel.StatusBookmark{},
|
||||||
>smodel.StatusMute{},
|
>smodel.StatusMute{},
|
||||||
|
|
|
||||||
|
|
@ -18,14 +18,13 @@
|
||||||
|
|
||||||
package db
|
package db
|
||||||
|
|
||||||
import "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
)
|
||||||
|
|
||||||
type Account interface {
|
type Account interface {
|
||||||
// GetAccountByUserID is a shortcut for the common action of fetching an account corresponding to a user ID.
|
|
||||||
// The given account pointer will be set to the result of the query, whatever it is.
|
|
||||||
// In case of no entries, a 'no entries' error will be returned
|
|
||||||
GetAccountByUserID(userID string, account *gtsmodel.Account) DBError
|
|
||||||
|
|
||||||
// GetAccountByID returns one account with the given ID, or an error if something goes wrong.
|
// GetAccountByID returns one account with the given ID, or an error if something goes wrong.
|
||||||
GetAccountByID(id string) (*gtsmodel.Account, DBError)
|
GetAccountByID(id string) (*gtsmodel.Account, DBError)
|
||||||
|
|
||||||
|
|
@ -75,10 +74,10 @@ type Account interface {
|
||||||
|
|
||||||
GetAccountBlocks(accountID string, maxID string, sinceID string, limit int) ([]*gtsmodel.Account, string, string, DBError)
|
GetAccountBlocks(accountID string, maxID string, sinceID string, limit int) ([]*gtsmodel.Account, string, string, DBError)
|
||||||
|
|
||||||
// GetAccountLastStatus simply gets the most recent status by the given account.
|
// GetAccountLastPosted simply gets the timestamp of the most recent post by the account.
|
||||||
// The given slice 'status' pointer will be set to the result of the query, whatever it is.
|
//
|
||||||
// In case of no entries, a 'no entries' error will be returned
|
// The returned time will be zero if account has never posted anything.
|
||||||
GetAccountLastStatus(accountID string, status *gtsmodel.Status) DBError
|
GetAccountLastPosted(accountID string) (time.Time, DBError)
|
||||||
|
|
||||||
// SetAccountHeaderOrAvatar sets the header or avatar for the given accountID to the given media attachment.
|
// SetAccountHeaderOrAvatar sets the header or avatar for the given accountID to the given media attachment.
|
||||||
SetAccountHeaderOrAvatar(mediaAttachment *gtsmodel.MediaAttachment, accountID string) DBError
|
SetAccountHeaderOrAvatar(mediaAttachment *gtsmodel.MediaAttachment, accountID string) DBError
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,8 @@ import "fmt"
|
||||||
type DBError error
|
type DBError error
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrNoEntries DBError = fmt.Errorf("no entries")
|
ErrNoEntries DBError = fmt.Errorf("no entries")
|
||||||
ErrAlreadyExists DBError = fmt.Errorf("already exists")
|
ErrMultipleEntries DBError = fmt.Errorf("multiple entries")
|
||||||
ErrUnknown DBError = fmt.Errorf("unknown error")
|
ErrAlreadyExists DBError = fmt.Errorf("already exists")
|
||||||
|
ErrUnknown DBError = fmt.Errorf("unknown error")
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/go-pg/pg/v10"
|
"github.com/go-pg/pg/v10"
|
||||||
"github.com/go-pg/pg/v10/orm"
|
"github.com/go-pg/pg/v10/orm"
|
||||||
|
|
@ -44,24 +45,15 @@ func (a *accountDB) newAccountQ(account *gtsmodel.Account) *orm.Query {
|
||||||
Relation("HeaderMediaAttachment")
|
Relation("HeaderMediaAttachment")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *accountDB) processResponse(account *gtsmodel.Account, err error) (*gtsmodel.Account, db.DBError) {
|
|
||||||
switch err {
|
|
||||||
case pg.ErrNoRows:
|
|
||||||
return nil, db.ErrNoEntries
|
|
||||||
case nil:
|
|
||||||
return account, nil
|
|
||||||
default:
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *accountDB) GetAccountByID(id string) (*gtsmodel.Account, db.DBError) {
|
func (a *accountDB) GetAccountByID(id string) (*gtsmodel.Account, db.DBError) {
|
||||||
account := >smodel.Account{}
|
account := >smodel.Account{}
|
||||||
|
|
||||||
q := a.newAccountQ(account).
|
q := a.newAccountQ(account).
|
||||||
Where("account.id = ?", id)
|
Where("account.id = ?", id)
|
||||||
|
|
||||||
return a.processResponse(account, q.Select())
|
err := processErrorResponse(q.Select())
|
||||||
|
|
||||||
|
return account, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *accountDB) GetAccountByURI(uri string) (*gtsmodel.Account, db.DBError) {
|
func (a *accountDB) GetAccountByURI(uri string) (*gtsmodel.Account, db.DBError) {
|
||||||
|
|
@ -70,7 +62,9 @@ func (a *accountDB) GetAccountByURI(uri string) (*gtsmodel.Account, db.DBError)
|
||||||
q := a.newAccountQ(account).
|
q := a.newAccountQ(account).
|
||||||
Where("account.uri = ?", uri)
|
Where("account.uri = ?", uri)
|
||||||
|
|
||||||
return a.processResponse(account, q.Select())
|
err := processErrorResponse(q.Select())
|
||||||
|
|
||||||
|
return account, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *accountDB) GetInstanceAccount(domain string) (*gtsmodel.Account, db.DBError) {
|
func (a *accountDB) GetInstanceAccount(domain string) (*gtsmodel.Account, db.DBError) {
|
||||||
|
|
@ -88,18 +82,23 @@ func (a *accountDB) GetInstanceAccount(domain string) (*gtsmodel.Account, db.DBE
|
||||||
Where("? IS NULL", pg.Ident("domain"))
|
Where("? IS NULL", pg.Ident("domain"))
|
||||||
}
|
}
|
||||||
|
|
||||||
return a.processResponse(account, q.Select())
|
err := processErrorResponse(q.Select())
|
||||||
|
|
||||||
|
return account, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *accountDB) GetAccountLastStatus(accountID string, status *gtsmodel.Status) db.DBError {
|
func (a *accountDB) GetAccountLastPosted(accountID string) (time.Time, db.DBError) {
|
||||||
if err := a.conn.Model(status).Order("created_at DESC").Limit(1).Where("account_id = ?", accountID).Select(); err != nil {
|
status := >smodel.Status{}
|
||||||
if err == pg.ErrNoRows {
|
|
||||||
return db.ErrNoEntries
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
|
|
||||||
|
q := a.conn.Model(status).
|
||||||
|
Order("id DESC").
|
||||||
|
Limit(1).
|
||||||
|
Where("account_id = ?", accountID).
|
||||||
|
Column("created_at")
|
||||||
|
|
||||||
|
err := processErrorResponse(q.Select())
|
||||||
|
|
||||||
|
return status.CreatedAt, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *accountDB) SetAccountHeaderOrAvatar(mediaAttachment *gtsmodel.MediaAttachment, accountID string) db.DBError {
|
func (a *accountDB) SetAccountHeaderOrAvatar(mediaAttachment *gtsmodel.MediaAttachment, accountID string) db.DBError {
|
||||||
|
|
@ -127,25 +126,6 @@ func (a *accountDB) SetAccountHeaderOrAvatar(mediaAttachment *gtsmodel.MediaAtta
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *accountDB) GetAccountByUserID(userID string, account *gtsmodel.Account) db.DBError {
|
|
||||||
user := >smodel.User{
|
|
||||||
ID: userID,
|
|
||||||
}
|
|
||||||
if err := a.conn.Model(user).Where("id = ?", userID).Select(); err != nil {
|
|
||||||
if err == pg.ErrNoRows {
|
|
||||||
return db.ErrNoEntries
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := a.conn.Model(account).Where("id = ?", user.AccountID).Select(); err != nil {
|
|
||||||
if err == pg.ErrNoRows {
|
|
||||||
return db.ErrNoEntries
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *accountDB) GetLocalAccountByUsername(username string, account *gtsmodel.Account) db.DBError {
|
func (a *accountDB) GetLocalAccountByUsername(username string, account *gtsmodel.Account) db.DBError {
|
||||||
if err := a.conn.Model(account).Where("username = ?", username).Where("? IS NULL", pg.Ident("domain")).Select(); err != nil {
|
if err := a.conn.Model(account).Where("username = ?", username).Where("? IS NULL", pg.Ident("domain")).Select(); err != nil {
|
||||||
if err == pg.ErrNoRows {
|
if err == pg.ErrNoRows {
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ import (
|
||||||
|
|
||||||
"github.com/go-pg/pg/extra/pgdebug"
|
"github.com/go-pg/pg/extra/pgdebug"
|
||||||
"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"
|
||||||
|
|
@ -38,6 +39,11 @@ import (
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/id"
|
"github.com/superseriousbusiness/gotosocial/internal/id"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var registerTables []interface{} = []interface{}{
|
||||||
|
>smodel.StatusToEmoji{},
|
||||||
|
>smodel.StatusToTag{},
|
||||||
|
}
|
||||||
|
|
||||||
// postgresService satisfies the DB interface
|
// postgresService satisfies the DB interface
|
||||||
type postgresService struct {
|
type postgresService struct {
|
||||||
db.Account
|
db.Account
|
||||||
|
|
@ -58,6 +64,11 @@ type postgresService struct {
|
||||||
// NewPostgresService returns a postgresService derived from the provided config, which implements the go-fed DB interface.
|
// NewPostgresService returns a postgresService derived from the provided config, which implements the go-fed DB interface.
|
||||||
// Under the hood, it uses https://github.com/go-pg/pg to create and maintain a database connection.
|
// Under the hood, it uses https://github.com/go-pg/pg to create and maintain a database connection.
|
||||||
func NewPostgresService(ctx context.Context, c *config.Config, log *logrus.Logger) (db.DB, error) {
|
func NewPostgresService(ctx context.Context, c *config.Config, log *logrus.Logger) (db.DB, error) {
|
||||||
|
for _, t := range registerTables {
|
||||||
|
// https://pg.uptrace.dev/orm/many-to-many-relation/
|
||||||
|
orm.RegisterTable(t)
|
||||||
|
}
|
||||||
|
|
||||||
opts, err := derivePGOptions(c)
|
opts, err := derivePGOptions(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not create postgres service: %s", err)
|
return nil, fmt.Errorf("could not create postgres service: %s", err)
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import (
|
||||||
"container/list"
|
"container/list"
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/go-pg/pg/v10"
|
"github.com/go-pg/pg/v10"
|
||||||
"github.com/go-pg/pg/v10/orm"
|
"github.com/go-pg/pg/v10/orm"
|
||||||
|
|
@ -38,7 +39,7 @@ type statusDB struct {
|
||||||
cancel context.CancelFunc
|
cancel context.CancelFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *statusDB) newStatusQ(status *gtsmodel.Status) *orm.Query {
|
func (s *statusDB) newStatusQ(status interface{}) *orm.Query {
|
||||||
return s.conn.Model(status).
|
return s.conn.Model(status).
|
||||||
Relation("Attachments").
|
Relation("Attachments").
|
||||||
Relation("Tags").
|
Relation("Tags").
|
||||||
|
|
@ -52,15 +53,11 @@ func (s *statusDB) newStatusQ(status *gtsmodel.Status) *orm.Query {
|
||||||
Relation("CreatedWithApplication")
|
Relation("CreatedWithApplication")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *statusDB) processStatusResponse(status *gtsmodel.Status, err error) (*gtsmodel.Status, db.DBError) {
|
func (s *statusDB) newFaveQ(faves interface{}) *orm.Query {
|
||||||
switch err {
|
return s.conn.Model(faves).
|
||||||
case pg.ErrNoRows:
|
Relation("Account").
|
||||||
return nil, db.ErrNoEntries
|
Relation("TargetAccount").
|
||||||
case nil:
|
Relation("Status")
|
||||||
return status, nil
|
|
||||||
default:
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *statusDB) GetStatusByID(id string) (*gtsmodel.Status, db.DBError) {
|
func (s *statusDB) GetStatusByID(id string) (*gtsmodel.Status, db.DBError) {
|
||||||
|
|
@ -69,7 +66,9 @@ 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.processStatusResponse(status, q.Select())
|
err := processErrorResponse(q.Select())
|
||||||
|
|
||||||
|
return status, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *statusDB) GetStatusByURI(uri string) (*gtsmodel.Status, db.DBError) {
|
func (s *statusDB) GetStatusByURI(uri string) (*gtsmodel.Status, db.DBError) {
|
||||||
|
|
@ -78,10 +77,52 @@ 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.processStatusResponse(status, q.Select())
|
err := processErrorResponse(q.Select())
|
||||||
|
|
||||||
|
return status, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *statusDB) StatusParents(status *gtsmodel.Status, onlyDirect bool) ([]*gtsmodel.Status, db.DBError) {
|
func (s *statusDB) PutStatus(status *gtsmodel.Status) db.DBError {
|
||||||
|
transaction := func(tx *pg.Tx) error {
|
||||||
|
// create links between this status and any emojis it uses
|
||||||
|
for _, i := range status.EmojiIDs {
|
||||||
|
if _, err := tx.Model(>smodel.StatusToEmoji{
|
||||||
|
StatusID: status.ID,
|
||||||
|
EmojiID: i,
|
||||||
|
}).Insert(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// create links between this status and any tags it uses
|
||||||
|
for _, i := range status.TagIDs {
|
||||||
|
if _, err := tx.Model(>smodel.StatusToTag{
|
||||||
|
StatusID: status.ID,
|
||||||
|
TagID: i,
|
||||||
|
}).Insert(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// change the status ID of the media attachments to the new status
|
||||||
|
for _, a := range status.Attachments {
|
||||||
|
a.StatusID = status.ID
|
||||||
|
a.UpdatedAt = time.Now()
|
||||||
|
if _, err := s.conn.Model(a).
|
||||||
|
Where("id = ?", a.ID).
|
||||||
|
Update(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := tx.Model(status).Insert()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return processErrorResponse(s.conn.RunInTransaction(context.Background(), transaction))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *statusDB) GetStatusParents(status *gtsmodel.Status, onlyDirect bool) ([]*gtsmodel.Status, db.DBError) {
|
||||||
parents := []*gtsmodel.Status{}
|
parents := []*gtsmodel.Status{}
|
||||||
s.statusParent(status, &parents, onlyDirect)
|
s.statusParent(status, &parents, onlyDirect)
|
||||||
|
|
||||||
|
|
@ -93,18 +134,19 @@ func (s *statusDB) statusParent(status *gtsmodel.Status, foundStatuses *[]*gtsmo
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
parentStatus := >smodel.Status{}
|
parentStatus, err := s.GetStatusByID(status.InReplyToID)
|
||||||
if err := s.conn.Model(parentStatus).Where("id = ?", status.InReplyToID).Select(); err == nil {
|
if err == nil {
|
||||||
*foundStatuses = append(*foundStatuses, parentStatus)
|
*foundStatuses = append(*foundStatuses, parentStatus)
|
||||||
}
|
}
|
||||||
|
|
||||||
if onlyDirect {
|
if onlyDirect {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
s.statusParent(parentStatus, foundStatuses, false)
|
s.statusParent(parentStatus, foundStatuses, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *statusDB) StatusChildren(status *gtsmodel.Status, onlyDirect bool, minID string) ([]*gtsmodel.Status, db.DBError) {
|
func (s *statusDB) GetStatusChildren(status *gtsmodel.Status, onlyDirect bool, minID string) ([]*gtsmodel.Status, db.DBError) {
|
||||||
foundStatuses := &list.List{}
|
foundStatuses := &list.List{}
|
||||||
foundStatuses.PushFront(status)
|
foundStatuses.PushFront(status)
|
||||||
s.statusChildren(status, foundStatuses, onlyDirect, minID)
|
s.statusChildren(status, foundStatuses, onlyDirect, minID)
|
||||||
|
|
@ -159,78 +201,52 @@ func (s *statusDB) statusChildren(status *gtsmodel.Status, foundStatuses *list.L
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *statusDB) GetReplyCountForStatus(status *gtsmodel.Status) (int, db.DBError) {
|
func (s *statusDB) CountStatusReplies(status *gtsmodel.Status) (int, db.DBError) {
|
||||||
return s.conn.Model(>smodel.Status{}).Where("in_reply_to_id = ?", status.ID).Count()
|
return s.conn.Model(>smodel.Status{}).Where("in_reply_to_id = ?", status.ID).Count()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *statusDB) GetReblogCountForStatus(status *gtsmodel.Status) (int, db.DBError) {
|
func (s *statusDB) CountStatusReblogs(status *gtsmodel.Status) (int, db.DBError) {
|
||||||
return s.conn.Model(>smodel.Status{}).Where("boost_of_id = ?", status.ID).Count()
|
return s.conn.Model(>smodel.Status{}).Where("boost_of_id = ?", status.ID).Count()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *statusDB) GetFaveCountForStatus(status *gtsmodel.Status) (int, db.DBError) {
|
func (s *statusDB) CountStatusFaves(status *gtsmodel.Status) (int, db.DBError) {
|
||||||
return s.conn.Model(>smodel.StatusFave{}).Where("status_id = ?", status.ID).Count()
|
return s.conn.Model(>smodel.StatusFave{}).Where("status_id = ?", status.ID).Count()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *statusDB) StatusFavedBy(status *gtsmodel.Status, accountID string) (bool, db.DBError) {
|
func (s *statusDB) IsStatusFavedBy(status *gtsmodel.Status, accountID string) (bool, db.DBError) {
|
||||||
return s.conn.Model(>smodel.StatusFave{}).Where("status_id = ?", status.ID).Where("account_id = ?", accountID).Exists()
|
return s.conn.Model(>smodel.StatusFave{}).Where("status_id = ?", status.ID).Where("account_id = ?", accountID).Exists()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *statusDB) StatusRebloggedBy(status *gtsmodel.Status, accountID string) (bool, db.DBError) {
|
func (s *statusDB) IsStatusRebloggedBy(status *gtsmodel.Status, accountID string) (bool, db.DBError) {
|
||||||
return s.conn.Model(>smodel.Status{}).Where("boost_of_id = ?", status.ID).Where("account_id = ?", accountID).Exists()
|
return s.conn.Model(>smodel.Status{}).Where("boost_of_id = ?", status.ID).Where("account_id = ?", accountID).Exists()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *statusDB) StatusMutedBy(status *gtsmodel.Status, accountID string) (bool, db.DBError) {
|
func (s *statusDB) IsStatusMutedBy(status *gtsmodel.Status, accountID string) (bool, db.DBError) {
|
||||||
return s.conn.Model(>smodel.StatusMute{}).Where("status_id = ?", status.ID).Where("account_id = ?", accountID).Exists()
|
return s.conn.Model(>smodel.StatusMute{}).Where("status_id = ?", status.ID).Where("account_id = ?", accountID).Exists()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *statusDB) StatusBookmarkedBy(status *gtsmodel.Status, accountID string) (bool, db.DBError) {
|
func (s *statusDB) IsStatusBookmarkedBy(status *gtsmodel.Status, accountID string) (bool, db.DBError) {
|
||||||
return s.conn.Model(>smodel.StatusBookmark{}).Where("status_id = ?", status.ID).Where("account_id = ?", accountID).Exists()
|
return s.conn.Model(>smodel.StatusBookmark{}).Where("status_id = ?", status.ID).Where("account_id = ?", accountID).Exists()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *statusDB) WhoFavedStatus(status *gtsmodel.Status) ([]*gtsmodel.Account, db.DBError) {
|
func (s *statusDB) GetStatusFaves(status *gtsmodel.Status) ([]*gtsmodel.StatusFave, db.DBError) {
|
||||||
accounts := []*gtsmodel.Account{}
|
|
||||||
|
|
||||||
faves := []*gtsmodel.StatusFave{}
|
faves := []*gtsmodel.StatusFave{}
|
||||||
if err := s.conn.Model(&faves).Where("status_id = ?", status.ID).Select(); err != nil {
|
|
||||||
if err == pg.ErrNoRows {
|
|
||||||
return accounts, nil // no rows just means nobody has faved this status, so that's fine
|
|
||||||
}
|
|
||||||
return nil, err // an actual error has occurred
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, f := range faves {
|
q := s.newFaveQ(&faves).
|
||||||
acc := >smodel.Account{}
|
Where("status_id = ?", status.ID)
|
||||||
if err := s.conn.Model(acc).Where("id = ?", f.AccountID).Select(); err != nil {
|
|
||||||
if err == pg.ErrNoRows {
|
err := processErrorResponse(q.Select())
|
||||||
continue // the account doesn't exist for some reason??? but this isn't the place to worry about that so just skip it
|
|
||||||
}
|
return faves, err
|
||||||
return nil, err // an actual error has occurred
|
|
||||||
}
|
|
||||||
accounts = append(accounts, acc)
|
|
||||||
}
|
|
||||||
return accounts, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *statusDB) WhoBoostedStatus(status *gtsmodel.Status) ([]*gtsmodel.Account, db.DBError) {
|
func (s *statusDB) GetStatusReblogs(status *gtsmodel.Status) ([]*gtsmodel.Status, db.DBError) {
|
||||||
accounts := []*gtsmodel.Account{}
|
reblogs := []*gtsmodel.Status{}
|
||||||
|
|
||||||
boosts := []*gtsmodel.Status{}
|
q := s.newStatusQ(&reblogs).
|
||||||
if err := s.conn.Model(&boosts).Where("boost_of_id = ?", status.ID).Select(); err != nil {
|
Where("boost_of_id = ?", status.ID)
|
||||||
if err == pg.ErrNoRows {
|
|
||||||
return accounts, nil // no rows just means nobody has boosted this status, so that's fine
|
|
||||||
}
|
|
||||||
return nil, err // an actual error has occurred
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, f := range boosts {
|
err := processErrorResponse(q.Select())
|
||||||
acc := >smodel.Account{}
|
|
||||||
if err := s.conn.Model(acc).Where("id = ?", f.AccountID).Select(); err != nil {
|
return reblogs, err
|
||||||
if err == pg.ErrNoRows {
|
|
||||||
continue // the account doesn't exist for some reason??? but this isn't the place to worry about that so just skip it
|
|
||||||
}
|
|
||||||
return nil, err // an actual error has occurred
|
|
||||||
}
|
|
||||||
accounts = append(accounts, acc)
|
|
||||||
}
|
|
||||||
return accounts, nil
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -80,6 +80,7 @@ func (suite *PGStandardTestSuite) TestGetStatusByURI() {
|
||||||
suite.Nil(status.InReplyTo)
|
suite.Nil(status.InReplyTo)
|
||||||
suite.Nil(status.InReplyToAccount)
|
suite.Nil(status.InReplyToAccount)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *PGStandardTestSuite) TestGetStatusWithExtras() {
|
func (suite *PGStandardTestSuite) TestGetStatusWithExtras() {
|
||||||
status, err := suite.db.GetStatusByID(suite.testStatuses["admin_account_status_1"].ID)
|
status, err := suite.db.GetStatusByID(suite.testStatuses["admin_account_status_1"].ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -92,6 +93,7 @@ func (suite *PGStandardTestSuite) TestGetStatusWithExtras() {
|
||||||
suite.NotEmpty(status.Attachments)
|
suite.NotEmpty(status.Attachments)
|
||||||
suite.NotEmpty(status.Emojis)
|
suite.NotEmpty(status.Emojis)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestStatusTestSuite(t *testing.T) {
|
func TestStatusTestSuite(t *testing.T) {
|
||||||
suite.Run(t, new(PGStandardTestSuite))
|
suite.Run(t, new(PGStandardTestSuite))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
25
internal/db/pg/util.go
Normal file
25
internal/db/pg/util.go
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
package pg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/go-pg/pg/v10"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
|
)
|
||||||
|
|
||||||
|
// processErrorResponse parses the given error and returns an appropriate DBError.
|
||||||
|
func processErrorResponse(err error) db.DBError {
|
||||||
|
switch err {
|
||||||
|
case nil:
|
||||||
|
return nil
|
||||||
|
case pg.ErrNoRows:
|
||||||
|
return db.ErrNoEntries
|
||||||
|
case pg.ErrMultiRows:
|
||||||
|
return db.ErrMultipleEntries
|
||||||
|
default:
|
||||||
|
if strings.Contains(err.Error(), "duplicate key value violates unique constraint") {
|
||||||
|
return db.ErrAlreadyExists
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -27,42 +27,45 @@ type Status interface {
|
||||||
// GetStatusByURI returns one status from the database, with all rel fields populated (if possible).
|
// GetStatusByURI returns one status from the database, with all rel fields populated (if possible).
|
||||||
GetStatusByURI(uri string) (*gtsmodel.Status, DBError)
|
GetStatusByURI(uri string) (*gtsmodel.Status, DBError)
|
||||||
|
|
||||||
// GetReplyCountForStatus returns the amount of replies recorded for a status, or an error if something goes wrong
|
// PutStatus stores one status in the database.
|
||||||
GetReplyCountForStatus(status *gtsmodel.Status) (int, DBError)
|
PutStatus(status *gtsmodel.Status) DBError
|
||||||
|
|
||||||
// GetReblogCountForStatus returns the amount of reblogs/boosts recorded for a status, or an error if something goes wrong
|
// CountStatusReplies returns the amount of replies recorded for a status, or an error if something goes wrong
|
||||||
GetReblogCountForStatus(status *gtsmodel.Status) (int, DBError)
|
CountStatusReplies(status *gtsmodel.Status) (int, DBError)
|
||||||
|
|
||||||
// GetFaveCountForStatus returns the amount of faves/likes recorded for a status, or an error if something goes wrong
|
// CountStatusReblogs returns the amount of reblogs/boosts recorded for a status, or an error if something goes wrong
|
||||||
GetFaveCountForStatus(status *gtsmodel.Status) (int, DBError)
|
CountStatusReblogs(status *gtsmodel.Status) (int, DBError)
|
||||||
|
|
||||||
// StatusParents get the parent statuses of a given status.
|
// CountStatusFaves returns the amount of faves/likes recorded for a status, or an error if something goes wrong
|
||||||
|
CountStatusFaves(status *gtsmodel.Status) (int, DBError)
|
||||||
|
|
||||||
|
// GetStatusParents get the parent statuses of a given status.
|
||||||
//
|
//
|
||||||
// If onlyDirect is true, only the immediate parent will be returned.
|
// If onlyDirect is true, only the immediate parent will be returned.
|
||||||
StatusParents(status *gtsmodel.Status, onlyDirect bool) ([]*gtsmodel.Status, DBError)
|
GetStatusParents(status *gtsmodel.Status, onlyDirect bool) ([]*gtsmodel.Status, DBError)
|
||||||
|
|
||||||
// StatusChildren gets the child statuses of a given status.
|
// GetStatusChildren gets the child statuses of a given status.
|
||||||
//
|
//
|
||||||
// If onlyDirect is true, only the immediate children will be returned.
|
// If onlyDirect is true, only the immediate children will be returned.
|
||||||
StatusChildren(status *gtsmodel.Status, onlyDirect bool, minID string) ([]*gtsmodel.Status, DBError)
|
GetStatusChildren(status *gtsmodel.Status, onlyDirect bool, minID string) ([]*gtsmodel.Status, DBError)
|
||||||
|
|
||||||
// StatusFavedBy checks if a given status has been faved by a given account ID
|
// IsStatusFavedBy checks if a given status has been faved by a given account ID
|
||||||
StatusFavedBy(status *gtsmodel.Status, accountID string) (bool, DBError)
|
IsStatusFavedBy(status *gtsmodel.Status, accountID string) (bool, DBError)
|
||||||
|
|
||||||
// StatusRebloggedBy checks if a given status has been reblogged/boosted by a given account ID
|
// IsStatusRebloggedBy checks if a given status has been reblogged/boosted by a given account ID
|
||||||
StatusRebloggedBy(status *gtsmodel.Status, accountID string) (bool, DBError)
|
IsStatusRebloggedBy(status *gtsmodel.Status, accountID string) (bool, DBError)
|
||||||
|
|
||||||
// StatusMutedBy checks if a given status has been muted by a given account ID
|
// IsStatusMutedBy checks if a given status has been muted by a given account ID
|
||||||
StatusMutedBy(status *gtsmodel.Status, accountID string) (bool, DBError)
|
IsStatusMutedBy(status *gtsmodel.Status, accountID string) (bool, DBError)
|
||||||
|
|
||||||
// StatusBookmarkedBy checks if a given status has been bookmarked by a given account ID
|
// IsStatusBookmarkedBy checks if a given status has been bookmarked by a given account ID
|
||||||
StatusBookmarkedBy(status *gtsmodel.Status, accountID string) (bool, DBError)
|
IsStatusBookmarkedBy(status *gtsmodel.Status, accountID string) (bool, DBError)
|
||||||
|
|
||||||
// WhoFavedStatus returns a slice of accounts who faved the given status.
|
// GetStatusFaves returns a slice of faves/likes of the given status.
|
||||||
// This slice will be unfiltered, not taking account of blocks and whatnot, so filter it before serving it back to a user.
|
// This slice will be unfiltered, not taking account of blocks and whatnot, so filter it before serving it back to a user.
|
||||||
WhoFavedStatus(status *gtsmodel.Status) ([]*gtsmodel.Account, DBError)
|
GetStatusFaves(status *gtsmodel.Status) ([]*gtsmodel.StatusFave, DBError)
|
||||||
|
|
||||||
// WhoBoostedStatus returns a slice of accounts who boosted the given status.
|
// GetStatusReblogs returns a slice of statuses that are a boost/reblog of the given status.
|
||||||
// This slice will be unfiltered, not taking account of blocks and whatnot, so filter it before serving it back to a user.
|
// This slice will be unfiltered, not taking account of blocks and whatnot, so filter it before serving it back to a user.
|
||||||
WhoBoostedStatus(status *gtsmodel.Status) ([]*gtsmodel.Account, DBError)
|
GetStatusReblogs(status *gtsmodel.Status) ([]*gtsmodel.Status, DBError)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,6 @@ import (
|
||||||
"github.com/go-fed/activity/streams/vocab"
|
"github.com/go-fed/activity/streams/vocab"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/ap"
|
"github.com/superseriousbusiness/gotosocial/internal/ap"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/id"
|
"github.com/superseriousbusiness/gotosocial/internal/id"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/transport"
|
"github.com/superseriousbusiness/gotosocial/internal/transport"
|
||||||
|
|
@ -65,8 +64,8 @@ func (d *deref) GetRemoteAccount(username string, remoteAccountID *url.URL, refr
|
||||||
new := true
|
new := true
|
||||||
|
|
||||||
// check if we already have the account in our db
|
// check if we already have the account in our db
|
||||||
maybeAccount := >smodel.Account{}
|
maybeAccount, err := d.db.GetAccountByURI(remoteAccountID.String())
|
||||||
if err := d.db.GetWhere([]db.Where{{Key: "uri", Value: remoteAccountID.String()}}, maybeAccount); err == nil {
|
if err == nil {
|
||||||
// we've seen this account before so it's not new
|
// we've seen this account before so it's not new
|
||||||
new = false
|
new = false
|
||||||
if !refresh {
|
if !refresh {
|
||||||
|
|
|
||||||
|
|
@ -109,7 +109,7 @@ func (d *deref) GetRemoteStatus(username string, remoteStatusID *url.URL, refres
|
||||||
return nil, statusable, new, fmt.Errorf("GetRemoteStatus: error populating status fields: %s", err)
|
return nil, statusable, new, fmt.Errorf("GetRemoteStatus: error populating status fields: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := d.db.Put(gtsStatus); err != nil {
|
if err := d.db.PutStatus(gtsStatus); err != nil {
|
||||||
return nil, statusable, new, fmt.Errorf("GetRemoteStatus: error putting new status: %s", err)
|
return nil, statusable, new, fmt.Errorf("GetRemoteStatus: error putting new status: %s", err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -338,7 +338,10 @@ func (d *deref) populateStatusFields(status *gtsmodel.Status, requestingUsername
|
||||||
}
|
}
|
||||||
|
|
||||||
m.StatusID = status.ID
|
m.StatusID = status.ID
|
||||||
|
m.Status = status
|
||||||
|
|
||||||
m.OriginAccountID = status.Account.ID
|
m.OriginAccountID = status.Account.ID
|
||||||
|
m.OriginAccount = status.Account
|
||||||
m.OriginAccountURI = status.Account.URI
|
m.OriginAccountURI = status.Account.URI
|
||||||
|
|
||||||
targetAccount, _, err := d.GetRemoteAccount(requestingUsername, uri, false)
|
targetAccount, _, err := d.GetRemoteAccount(requestingUsername, uri, false)
|
||||||
|
|
@ -348,6 +351,7 @@ func (d *deref) populateStatusFields(status *gtsmodel.Status, requestingUsername
|
||||||
|
|
||||||
// by this point, we know the targetAccount exists in our database with an ID :)
|
// by this point, we know the targetAccount exists in our database with an ID :)
|
||||||
m.TargetAccountID = targetAccount.ID
|
m.TargetAccountID = targetAccount.ID
|
||||||
|
m.TargetAccount = targetAccount
|
||||||
if err := d.db.Put(m); err != nil {
|
if err := d.db.Put(m); err != nil {
|
||||||
return fmt.Errorf("error creating mention: %s", err)
|
return fmt.Errorf("error creating mention: %s", err)
|
||||||
}
|
}
|
||||||
|
|
@ -357,11 +361,12 @@ func (d *deref) populateStatusFields(status *gtsmodel.Status, requestingUsername
|
||||||
|
|
||||||
// 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 == "" {
|
||||||
replyToStatus := >smodel.Status{}
|
if replyToStatus, err := d.db.GetStatusByURI(status.InReplyToURI); err == nil {
|
||||||
if err := d.db.GetWhere([]db.Where{{Key: "uri", Value: status.InReplyToURI}}, replyToStatus); err == nil {
|
|
||||||
// we have the status
|
// we have the status
|
||||||
status.InReplyToID = replyToStatus.ID
|
status.InReplyToID = replyToStatus.ID
|
||||||
|
status.InReplyTo = replyToStatus
|
||||||
status.InReplyToAccountID = replyToStatus.AccountID
|
status.InReplyToAccountID = replyToStatus.AccountID
|
||||||
|
status.InReplyToAccount = replyToStatus.Account
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -112,7 +112,7 @@ func (f *federatingDB) Create(ctx context.Context, asType vocab.Type) error {
|
||||||
}
|
}
|
||||||
status.ID = statusID
|
status.ID = statusID
|
||||||
|
|
||||||
if err := f.db.Put(status); err != nil {
|
if err := f.db.PutStatus(status); err != nil {
|
||||||
if err == db.ErrAlreadyExists {
|
if err == db.ErrAlreadyExists {
|
||||||
// the status already exists in the database, which means we've already handled everything else,
|
// the status already exists in the database, which means we've already handled everything else,
|
||||||
// so we can just return nil here and be done with it.
|
// so we can just return nil here and be done with it.
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,8 @@ func (f *federatingDB) Followers(c context.Context, actorIRI *url.URL) (follower
|
||||||
acct := >smodel.Account{}
|
acct := >smodel.Account{}
|
||||||
|
|
||||||
if util.IsUserPath(actorIRI) {
|
if util.IsUserPath(actorIRI) {
|
||||||
if err := f.db.GetWhere([]db.Where{{Key: "uri", Value: actorIRI.String()}}, acct); err != nil {
|
acct, err = f.db.GetAccountByURI(actorIRI.String())
|
||||||
|
if err != nil {
|
||||||
return nil, fmt.Errorf("FOLLOWERS: db error getting account with uri %s: %s", actorIRI.String(), err)
|
return nil, fmt.Errorf("FOLLOWERS: db error getting account with uri %s: %s", actorIRI.String(), err)
|
||||||
}
|
}
|
||||||
} else if util.IsFollowersPath(actorIRI) {
|
} else if util.IsFollowersPath(actorIRI) {
|
||||||
|
|
|
||||||
|
|
@ -43,8 +43,8 @@ func (f *federatingDB) Get(c context.Context, id *url.URL) (value vocab.Type, er
|
||||||
l.Debug("entering GET function")
|
l.Debug("entering GET function")
|
||||||
|
|
||||||
if util.IsUserPath(id) {
|
if util.IsUserPath(id) {
|
||||||
acct := >smodel.Account{}
|
acct, err := f.db.GetAccountByURI(id.String())
|
||||||
if err := f.db.GetWhere([]db.Where{{Key: "uri", Value: id.String()}}, acct); err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
l.Debug("is user path! returning account")
|
l.Debug("is user path! returning account")
|
||||||
|
|
|
||||||
|
|
@ -97,8 +97,8 @@ func (f *federatingDB) NewID(c context.Context, t vocab.Type) (idURL *url.URL, e
|
||||||
for iter := actorProp.Begin(); iter != actorProp.End(); iter = iter.Next() {
|
for iter := actorProp.Begin(); iter != actorProp.End(); iter = iter.Next() {
|
||||||
// take the IRI of the first actor we can find (there should only be one)
|
// take the IRI of the first actor we can find (there should only be one)
|
||||||
if iter.IsIRI() {
|
if iter.IsIRI() {
|
||||||
actorAccount := >smodel.Account{}
|
// if there's an error here, just use the fallback behavior -- we don't need to return an error here
|
||||||
if err := f.db.GetWhere([]db.Where{{Key: "uri", Value: iter.GetIRI().String()}}, actorAccount); err == nil { // if there's an error here, just use the fallback behavior -- we don't need to return an error here
|
if actorAccount, err := f.db.GetAccountByURI(iter.GetIRI().String()); err == nil {
|
||||||
newID, err := id.NewRandomULID()
|
newID, err := id.NewRandomULID()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
||||||
|
|
@ -200,8 +200,8 @@ func (f *federator) Blocked(ctx context.Context, actorIRIs []*url.URL) (bool, er
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
requestingAccount := >smodel.Account{}
|
requestingAccount, err := f.db.GetAccountByURI(uri.String())
|
||||||
if err := f.db.GetWhere([]db.Where{{Key: "uri", Value: uri.String()}}, requestingAccount); err != nil {
|
if err != nil {
|
||||||
if err == db.ErrNoEntries {
|
if err == db.ErrNoEntries {
|
||||||
// we don't have an entry for this account so it's not blocked
|
// we don't have an entry for this account so it's not blocked
|
||||||
// TODO: allow a different default to be set for this behavior
|
// TODO: allow a different default to be set for this behavior
|
||||||
|
|
|
||||||
|
|
@ -37,13 +37,13 @@ type Status struct {
|
||||||
Attachments []*MediaAttachment `pg:"rel:has-many"`
|
Attachments []*MediaAttachment `pg:"rel:has-many"`
|
||||||
// Database IDs of any tags used in this status
|
// Database IDs of any tags used in this status
|
||||||
TagIDs []string `pg:"tags,array"`
|
TagIDs []string `pg:"tags,array"`
|
||||||
Tags []*Tag `pg:"rel:has-many"`
|
Tags []*Tag `pg:"many2many:status_to_tags"` // https://pg.uptrace.dev/orm/many-to-many-relation/
|
||||||
// Database IDs of any mentions in this status
|
// Database IDs of any mentions in this status
|
||||||
MentionIDs []string `pg:"mentions,array"`
|
MentionIDs []string `pg:"mentions,array"`
|
||||||
Mentions []*Mention `pg:"rel:has-many"`
|
Mentions []*Mention `pg:"rel:has-many"`
|
||||||
// Database IDs of any emojis used in this status
|
// Database IDs of any emojis used in this status
|
||||||
EmojiIDs []string `pg:"emojis,array"`
|
EmojiIDs []string `pg:"emojis,array"`
|
||||||
Emojis []*Emoji `pg:"rel:many2many"`
|
Emojis []*Emoji `pg:"many2many:status_to_emojis"` // https://pg.uptrace.dev/orm/many-to-many-relation/
|
||||||
// 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?
|
||||||
|
|
@ -91,6 +91,18 @@ type Status struct {
|
||||||
Pinned bool
|
Pinned bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StatusToTag is an intermediate struct to facilitate the many2many relationship between a status and one or more tags.
|
||||||
|
type StatusToTag struct {
|
||||||
|
StatusID string `pg:"unique:statustag"`
|
||||||
|
TagID string `pg:"unique:statustag"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// StatusToEmoji is an intermediate struct to facilitate the many2many relationship between a status and one or more emojis.
|
||||||
|
type StatusToEmoji struct {
|
||||||
|
StatusID string `pg:"unique:statusemoji"`
|
||||||
|
EmojiID string `pg:"unique:statusemoji"`
|
||||||
|
}
|
||||||
|
|
||||||
// Visibility represents the visibility granularity of a status.
|
// Visibility represents the visibility granularity of a status.
|
||||||
type Visibility string
|
type Visibility string
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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:belongs-to"`
|
Account *Account `pg:"rel:has-one"`
|
||||||
// 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"`
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ type Tag struct {
|
||||||
// Href of this tag, eg https://example.org/tags/somehashtag
|
// Href of this tag, eg https://example.org/tags/somehashtag
|
||||||
URL string
|
URL string
|
||||||
// name of this tag -- the tag without the hash part
|
// name of this tag -- the tag without the hash part
|
||||||
Name string `pg:",unique,pk,notnull"`
|
Name string `pg:",unique,notnull"`
|
||||||
// Which account ID is the first one we saw using this tag?
|
// Which account ID is the first one we saw using this tag?
|
||||||
FirstSeenFromAccountID string `pg:"type:CHAR(26)"`
|
FirstSeenFromAccountID string `pg:"type:CHAR(26)"`
|
||||||
// when was this tag created
|
// when was this tag created
|
||||||
|
|
|
||||||
|
|
@ -321,7 +321,7 @@ func (p *processor) GetFediStatusReplies(ctx context.Context, requestedUsername
|
||||||
} else {
|
} else {
|
||||||
// scenario 3
|
// scenario 3
|
||||||
// get immediate children
|
// get immediate children
|
||||||
replies, err := p.db.StatusChildren(s, true, minID)
|
replies, err := p.db.GetStatusChildren(s, true, minID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, gtserror.NewErrorInternalError(err)
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -100,7 +100,7 @@ func (p *processor) processFromFederator(federatorMsg gtsmodel.FromFederator) er
|
||||||
}
|
}
|
||||||
incomingAnnounce.ID = incomingAnnounceID
|
incomingAnnounce.ID = incomingAnnounceID
|
||||||
|
|
||||||
if err := p.db.Put(incomingAnnounce); err != nil {
|
if err := p.db.PutStatus(incomingAnnounce); err != nil {
|
||||||
if err != db.ErrNoEntries {
|
if err != db.ErrNoEntries {
|
||||||
return fmt.Errorf("error adding dereferenced announce to the db: %s", err)
|
return fmt.Errorf("error adding dereferenced announce to the db: %s", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ func (p *processor) Boost(requestingAccount *gtsmodel.Account, application *gtsm
|
||||||
boostWrapperStatus.BoostOfAccount = targetStatus.Account
|
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.PutStatus(boostWrapperStatus); err != nil {
|
||||||
return nil, gtserror.NewErrorInternalError(err)
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,21 +26,20 @@ func (p *processor) BoostedBy(requestingAccount *gtsmodel.Account, targetStatusI
|
||||||
return nil, gtserror.NewErrorNotFound(errors.New("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
|
statusReblogs, err := p.db.GetStatusReblogs(targetStatus)
|
||||||
favingAccounts, err := p.db.WhoBoostedStatus(targetStatus)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, gtserror.NewErrorNotFound(fmt.Errorf("StatusBoostedBy: error seeing who boosted status: %s", err))
|
return nil, gtserror.NewErrorNotFound(fmt.Errorf("StatusBoostedBy: error seeing who boosted status: %s", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
// filter the list so the user doesn't see accounts they blocked or which blocked them
|
// 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 _, s := range statusReblogs {
|
||||||
blocked, err := p.db.Blocked(requestingAccount.ID, acc.ID, true)
|
blocked, err := p.db.Blocked(requestingAccount.ID, s.AccountID, 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))
|
||||||
}
|
}
|
||||||
if !blocked {
|
if !blocked {
|
||||||
filteredAccounts = append(filteredAccounts, acc)
|
filteredAccounts = append(filteredAccounts, s.Account)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ func (p *processor) Context(requestingAccount *gtsmodel.Account, targetStatusID
|
||||||
Descendants: []apimodel.Status{},
|
Descendants: []apimodel.Status{},
|
||||||
}
|
}
|
||||||
|
|
||||||
parents, err := p.db.StatusParents(targetStatus, false)
|
parents, err := p.db.GetStatusParents(targetStatus, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, gtserror.NewErrorInternalError(err)
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
}
|
}
|
||||||
|
|
@ -50,7 +50,7 @@ func (p *processor) Context(requestingAccount *gtsmodel.Account, targetStatusID
|
||||||
return context.Ancestors[i].ID < context.Ancestors[j].ID
|
return context.Ancestors[i].ID < context.Ancestors[j].ID
|
||||||
})
|
})
|
||||||
|
|
||||||
children, err := p.db.StatusChildren(targetStatus, false, "")
|
children, err := p.db.GetStatusChildren(targetStatus, false, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, gtserror.NewErrorInternalError(err)
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,27 +38,22 @@ func (p *processor) Create(account *gtsmodel.Account, application *gtsmodel.Appl
|
||||||
Text: form.Status,
|
Text: form.Status,
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if replyToID is ok
|
|
||||||
if err := p.ProcessReplyToID(form, account.ID, newStatus); err != nil {
|
if err := p.ProcessReplyToID(form, account.ID, newStatus); err != nil {
|
||||||
return nil, gtserror.NewErrorInternalError(err)
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if mediaIDs are ok
|
|
||||||
if err := p.ProcessMediaIDs(form, account.ID, newStatus); err != nil {
|
if err := p.ProcessMediaIDs(form, account.ID, newStatus); err != nil {
|
||||||
return nil, gtserror.NewErrorInternalError(err)
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if visibility settings are ok
|
|
||||||
if err := p.ProcessVisibility(form, account.Privacy, newStatus); err != nil {
|
if err := p.ProcessVisibility(form, account.Privacy, newStatus); err != nil {
|
||||||
return nil, gtserror.NewErrorInternalError(err)
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle language settings
|
|
||||||
if err := p.ProcessLanguage(form, account.Language, newStatus); err != nil {
|
if err := p.ProcessLanguage(form, account.Language, newStatus); err != nil {
|
||||||
return nil, gtserror.NewErrorInternalError(err)
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// handle mentions
|
|
||||||
if err := p.ProcessMentions(form, account.ID, newStatus); err != nil {
|
if err := p.ProcessMentions(form, account.ID, newStatus); err != nil {
|
||||||
return nil, gtserror.NewErrorInternalError(err)
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
}
|
}
|
||||||
|
|
@ -75,20 +70,11 @@ func (p *processor) Create(account *gtsmodel.Account, application *gtsmodel.Appl
|
||||||
return nil, gtserror.NewErrorInternalError(err)
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// put the new status in the database, generating an ID for it in the process
|
// put the new status in the database
|
||||||
if err := p.db.Put(newStatus); err != nil {
|
if err := p.db.PutStatus(newStatus); err != nil {
|
||||||
return nil, gtserror.NewErrorInternalError(err)
|
return nil, gtserror.NewErrorInternalError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// change the status ID of the media attachments to the new status
|
|
||||||
for _, a := range newStatus.Attachments {
|
|
||||||
a.StatusID = newStatus.ID
|
|
||||||
a.UpdatedAt = time.Now()
|
|
||||||
if err := p.db.UpdateByID(a.ID, a); err != nil {
|
|
||||||
return nil, gtserror.NewErrorInternalError(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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.ActivityStreamsNote,
|
APObjectType: gtsmodel.ActivityStreamsNote,
|
||||||
|
|
|
||||||
|
|
@ -26,21 +26,20 @@ func (p *processor) FavedBy(requestingAccount *gtsmodel.Account, targetStatusID
|
||||||
return nil, gtserror.NewErrorNotFound(errors.New("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
|
statusFaves, err := p.db.GetStatusFaves(targetStatus)
|
||||||
favingAccounts, err := p.db.WhoFavedStatus(targetStatus)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, gtserror.NewErrorNotFound(fmt.Errorf("error seeing who faved status: %s", err))
|
return nil, gtserror.NewErrorNotFound(fmt.Errorf("error seeing who faved status: %s", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
// filter the list so the user doesn't see accounts they blocked or which blocked them
|
// 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 _, fave := range statusFaves {
|
||||||
blocked, err := p.db.Blocked(requestingAccount.ID, acc.ID, true)
|
blocked, err := p.db.Blocked(requestingAccount.ID, fave.AccountID, 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))
|
||||||
}
|
}
|
||||||
if !blocked {
|
if !blocked {
|
||||||
filteredAccounts = append(filteredAccounts, acc)
|
filteredAccounts = append(filteredAccounts, fave.Account)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,9 +37,8 @@ func (c *converter) ASRepresentationToAccount(accountable ap.Accountable, update
|
||||||
}
|
}
|
||||||
uri := uriProp.GetIRI()
|
uri := uriProp.GetIRI()
|
||||||
|
|
||||||
acct := >smodel.Account{}
|
|
||||||
if !update {
|
if !update {
|
||||||
err := c.db.GetWhere([]db.Where{{Key: "uri", Value: uri.String()}}, acct)
|
acct, err := c.db.GetAccountByURI(uri.String())
|
||||||
if err == nil {
|
if err == nil {
|
||||||
// we already know this account so we can skip generating it
|
// we already know this account so we can skip generating it
|
||||||
return acct, nil
|
return acct, nil
|
||||||
|
|
@ -51,7 +50,7 @@ func (c *converter) ASRepresentationToAccount(accountable ap.Accountable, update
|
||||||
}
|
}
|
||||||
|
|
||||||
// we don't know the account, or we're being told to update it, so we need to generate it from the person -- at least we already have the URI!
|
// we don't know the account, or we're being told to update it, so we need to generate it from the person -- at least we already have the URI!
|
||||||
acct = >smodel.Account{}
|
acct := >smodel.Account{}
|
||||||
acct.URI = uri.String()
|
acct.URI = uri.String()
|
||||||
|
|
||||||
// Username aka preferredUsername
|
// Username aka preferredUsername
|
||||||
|
|
@ -225,8 +224,8 @@ func (c *converter) ASStatusToStatus(statusable ap.Statusable) (*gtsmodel.Status
|
||||||
}
|
}
|
||||||
status.AccountURI = attributedTo.String()
|
status.AccountURI = attributedTo.String()
|
||||||
|
|
||||||
statusOwner := >smodel.Account{}
|
statusOwner, err := c.db.GetAccountByURI(attributedTo.String())
|
||||||
if err := c.db.GetWhere([]db.Where{{Key: "uri", Value: attributedTo.String(), CaseInsensitive: true}}, statusOwner); err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("couldn't get status owner from db: %s", err)
|
return nil, fmt.Errorf("couldn't get status owner from db: %s", err)
|
||||||
}
|
}
|
||||||
status.AccountID = statusOwner.ID
|
status.AccountID = statusOwner.ID
|
||||||
|
|
@ -241,18 +240,16 @@ func (c *converter) ASStatusToStatus(statusable ap.Statusable) (*gtsmodel.Status
|
||||||
status.InReplyToURI = inReplyToURI.String()
|
status.InReplyToURI = inReplyToURI.String()
|
||||||
|
|
||||||
// now we can check if we have the replied-to status in our db already
|
// now we can check if we have the replied-to status in our db already
|
||||||
inReplyToStatus := >smodel.Status{}
|
if inReplyToStatus, err := c.db.GetStatusByURI(inReplyToURI.String()); err == nil {
|
||||||
if err := c.db.GetWhere([]db.Where{{Key: "uri", Value: inReplyToURI.String()}}, inReplyToStatus); err == nil {
|
|
||||||
// we have the status in our database already
|
// we have the status in our database already
|
||||||
// so we can set these fields here and then...
|
// so we can set these fields here and now...
|
||||||
status.InReplyToID = inReplyToStatus.ID
|
status.InReplyToID = inReplyToStatus.ID
|
||||||
status.InReplyToAccountID = inReplyToStatus.AccountID
|
status.InReplyToAccountID = inReplyToStatus.AccountID
|
||||||
status.InReplyTo = inReplyToStatus
|
status.InReplyTo = inReplyToStatus
|
||||||
|
if status.InReplyToAccount == nil {
|
||||||
// ... check if we've seen the account already
|
if inReplyToAccount, err := c.db.GetAccountByID(inReplyToStatus.AccountID); err == nil {
|
||||||
inReplyToAccount := >smodel.Account{}
|
status.InReplyToAccount = inReplyToAccount
|
||||||
if err := c.db.GetByID(inReplyToStatus.AccountID, inReplyToAccount); err == nil {
|
}
|
||||||
status.InReplyToAccount = inReplyToAccount
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -328,8 +325,8 @@ func (c *converter) ASFollowToFollowRequest(followable ap.Followable) (*gtsmodel
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.New("error extracting actor property from follow")
|
return nil, errors.New("error extracting actor property from follow")
|
||||||
}
|
}
|
||||||
originAccount := >smodel.Account{}
|
originAccount, err := c.db.GetAccountByURI(origin.String())
|
||||||
if err := c.db.GetWhere([]db.Where{{Key: "uri", Value: origin.String()}}, originAccount); err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error extracting account with uri %s from the database: %s", origin.String(), err)
|
return nil, fmt.Errorf("error extracting account with uri %s from the database: %s", origin.String(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -337,8 +334,8 @@ func (c *converter) ASFollowToFollowRequest(followable ap.Followable) (*gtsmodel
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.New("error extracting object property from follow")
|
return nil, errors.New("error extracting object property from follow")
|
||||||
}
|
}
|
||||||
targetAccount := >smodel.Account{}
|
targetAccount, err := c.db.GetAccountByURI(target.String())
|
||||||
if err := c.db.GetWhere([]db.Where{{Key: "uri", Value: target.String()}}, targetAccount); err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error extracting account with uri %s from the database: %s", origin.String(), err)
|
return nil, fmt.Errorf("error extracting account with uri %s from the database: %s", origin.String(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -362,8 +359,8 @@ func (c *converter) ASFollowToFollow(followable ap.Followable) (*gtsmodel.Follow
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.New("error extracting actor property from follow")
|
return nil, errors.New("error extracting actor property from follow")
|
||||||
}
|
}
|
||||||
originAccount := >smodel.Account{}
|
originAccount, err := c.db.GetAccountByURI(origin.String())
|
||||||
if err := c.db.GetWhere([]db.Where{{Key: "uri", Value: origin.String()}}, originAccount); err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error extracting account with uri %s from the database: %s", origin.String(), err)
|
return nil, fmt.Errorf("error extracting account with uri %s from the database: %s", origin.String(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -371,8 +368,8 @@ func (c *converter) ASFollowToFollow(followable ap.Followable) (*gtsmodel.Follow
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.New("error extracting object property from follow")
|
return nil, errors.New("error extracting object property from follow")
|
||||||
}
|
}
|
||||||
targetAccount := >smodel.Account{}
|
targetAccount, err := c.db.GetAccountByURI(target.String())
|
||||||
if err := c.db.GetWhere([]db.Where{{Key: "uri", Value: target.String()}}, targetAccount); err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error extracting account with uri %s from the database: %s", origin.String(), err)
|
return nil, fmt.Errorf("error extracting account with uri %s from the database: %s", origin.String(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -396,8 +393,8 @@ func (c *converter) ASLikeToFave(likeable ap.Likeable) (*gtsmodel.StatusFave, er
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.New("error extracting actor property from like")
|
return nil, errors.New("error extracting actor property from like")
|
||||||
}
|
}
|
||||||
originAccount := >smodel.Account{}
|
originAccount, err := c.db.GetAccountByURI(origin.String())
|
||||||
if err := c.db.GetWhere([]db.Where{{Key: "uri", Value: origin.String()}}, originAccount); err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error extracting account with uri %s from the database: %s", origin.String(), err)
|
return nil, fmt.Errorf("error extracting account with uri %s from the database: %s", origin.String(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -406,24 +403,30 @@ func (c *converter) ASLikeToFave(likeable ap.Likeable) (*gtsmodel.StatusFave, er
|
||||||
return nil, errors.New("error extracting object property from like")
|
return nil, errors.New("error extracting object property from like")
|
||||||
}
|
}
|
||||||
|
|
||||||
targetStatus := >smodel.Status{}
|
targetStatus, err := c.db.GetStatusByURI(target.String())
|
||||||
if err := c.db.GetWhere([]db.Where{{Key: "uri", Value: target.String()}}, targetStatus); err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error extracting status with uri %s from the database: %s", target.String(), err)
|
return nil, fmt.Errorf("error extracting status with uri %s from the database: %s", target.String(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
targetAccount := >smodel.Account{}
|
var targetAccount *gtsmodel.Account
|
||||||
if err := c.db.GetByID(targetStatus.AccountID, targetAccount); err != nil {
|
if targetStatus.Account != nil {
|
||||||
return nil, fmt.Errorf("error extracting account with id %s from the database: %s", targetStatus.AccountID, err)
|
targetAccount = targetStatus.Account
|
||||||
|
} else {
|
||||||
|
a, err := c.db.GetAccountByID(targetStatus.AccountID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error extracting account with id %s from the database: %s", targetStatus.AccountID, err)
|
||||||
|
}
|
||||||
|
targetAccount = a
|
||||||
}
|
}
|
||||||
|
|
||||||
return >smodel.StatusFave{
|
return >smodel.StatusFave{
|
||||||
TargetAccountID: targetAccount.ID,
|
AccountID: originAccount.ID,
|
||||||
StatusID: targetStatus.ID,
|
Account: originAccount,
|
||||||
AccountID: originAccount.ID,
|
TargetAccountID: targetAccount.ID,
|
||||||
URI: uri,
|
TargetAccount: targetAccount,
|
||||||
Status: targetStatus,
|
StatusID: targetStatus.ID,
|
||||||
TargetAccount: targetAccount,
|
Status: targetStatus,
|
||||||
Account: originAccount,
|
URI: uri,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -438,9 +441,9 @@ func (c *converter) ASBlockToBlock(blockable ap.Blockable) (*gtsmodel.Block, err
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.New("ASBlockToBlock: error extracting actor property from block")
|
return nil, errors.New("ASBlockToBlock: error extracting actor property from block")
|
||||||
}
|
}
|
||||||
originAccount := >smodel.Account{}
|
originAccount, err := c.db.GetAccountByURI(origin.String())
|
||||||
if err := c.db.GetWhere([]db.Where{{Key: "uri", Value: origin.String()}}, originAccount); err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("ASBlockToBlock: error extracting account with uri %s from the database: %s", origin.String(), err)
|
return nil, fmt.Errorf("error extracting account with uri %s from the database: %s", origin.String(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
target, err := ap.ExtractObject(blockable)
|
target, err := ap.ExtractObject(blockable)
|
||||||
|
|
@ -448,9 +451,9 @@ func (c *converter) ASBlockToBlock(blockable ap.Blockable) (*gtsmodel.Block, err
|
||||||
return nil, errors.New("ASBlockToBlock: error extracting object property from block")
|
return nil, errors.New("ASBlockToBlock: error extracting object property from block")
|
||||||
}
|
}
|
||||||
|
|
||||||
targetAccount := >smodel.Account{}
|
targetAccount, err := c.db.GetAccountByURI(target.String())
|
||||||
if err := c.db.GetWhere([]db.Where{{Key: "uri", Value: target.String(), CaseInsensitive: true}}, targetAccount); err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("ASBlockToBlock: error extracting account with uri %s from the database: %s", target.String(), err)
|
return nil, fmt.Errorf("error extracting account with uri %s from the database: %s", origin.String(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return >smodel.Block{
|
return >smodel.Block{
|
||||||
|
|
@ -473,7 +476,7 @@ func (c *converter) ASAnnounceToStatus(announceable ap.Announceable) (*gtsmodel.
|
||||||
}
|
}
|
||||||
uri := idProp.GetIRI().String()
|
uri := idProp.GetIRI().String()
|
||||||
|
|
||||||
if err := c.db.GetWhere([]db.Where{{Key: "uri", Value: uri}}, status); err == nil {
|
if status, err := c.db.GetStatusByURI(uri); err == nil {
|
||||||
// we already have it, great, just return it as-is :)
|
// we already have it, great, just return it as-is :)
|
||||||
isNew = false
|
isNew = false
|
||||||
return status, isNew, nil
|
return status, isNew, nil
|
||||||
|
|
@ -507,12 +510,13 @@ func (c *converter) ASAnnounceToStatus(announceable ap.Announceable) (*gtsmodel.
|
||||||
|
|
||||||
// get the boosting account based on the URI
|
// get the boosting account based on the URI
|
||||||
// this should have been dereferenced already before we hit this point so we can confidently error out if we don't have it
|
// this should have been dereferenced already before we hit this point so we can confidently error out if we don't have it
|
||||||
boostingAccount := >smodel.Account{}
|
boostingAccount, err := c.db.GetAccountByURI(actor.String())
|
||||||
if err := c.db.GetWhere([]db.Where{{Key: "uri", Value: actor.String()}}, boostingAccount); err != nil {
|
if err != nil {
|
||||||
return nil, isNew, fmt.Errorf("ASAnnounceToStatus: error in db fetching account with uri %s: %s", actor.String(), err)
|
return nil, isNew, fmt.Errorf("ASAnnounceToStatus: error in db fetching account with uri %s: %s", actor.String(), err)
|
||||||
}
|
}
|
||||||
status.AccountID = boostingAccount.ID
|
status.AccountID = boostingAccount.ID
|
||||||
status.AccountURI = boostingAccount.URI
|
status.AccountURI = boostingAccount.URI
|
||||||
|
status.Account = boostingAccount
|
||||||
|
|
||||||
// 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.AttachmentIDs = []string{}
|
status.AttachmentIDs = []string{}
|
||||||
|
|
@ -552,7 +556,6 @@ func (c *converter) ASAnnounceToStatus(announceable ap.Announceable) (*gtsmodel.
|
||||||
status.Visibility = visibility
|
status.Visibility = visibility
|
||||||
|
|
||||||
// the rest of the fields will be taken from the target status, but it's not our job to do the dereferencing here
|
// the rest of the fields will be taken from the target status, but it's not our job to do the dereferencing here
|
||||||
|
|
||||||
return status, isNew, nil
|
return status, isNew, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -77,23 +77,17 @@ func (c *converter) AccountToMastoPublic(a *gtsmodel.Account) (*model.Account, e
|
||||||
// count statuses
|
// count statuses
|
||||||
statusesCount, err := c.db.CountAccountStatuses(a.ID)
|
statusesCount, err := c.db.CountAccountStatuses(a.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error getting last statuses: %s", err)
|
return nil, fmt.Errorf("error counting statuses: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// check when the last status was
|
// check when the last status was
|
||||||
lastStatus := >smodel.Status{}
|
|
||||||
if err := c.db.GetAccountLastStatus(a.ID, lastStatus); err != nil {
|
|
||||||
if err != db.ErrNoEntries {
|
|
||||||
return nil, fmt.Errorf("error getting last status: %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var lastStatusAt string
|
var lastStatusAt string
|
||||||
if lastStatus != nil {
|
lastPosted, err := c.db.GetAccountLastPosted(a.ID)
|
||||||
lastStatusAt = lastStatus.CreatedAt.Format(time.RFC3339)
|
if err == nil && !lastPosted.IsZero() {
|
||||||
|
lastStatusAt = lastPosted.Format(time.RFC3339)
|
||||||
}
|
}
|
||||||
|
|
||||||
// build the avatar and header URLs
|
// build the avatar and header URLs
|
||||||
|
|
||||||
var aviURL string
|
var aviURL string
|
||||||
var aviURLStatic string
|
var aviURLStatic string
|
||||||
if a.AvatarMediaAttachment != nil {
|
if a.AvatarMediaAttachment != nil {
|
||||||
|
|
@ -285,17 +279,17 @@ func (c *converter) TagToMasto(t *gtsmodel.Tag) (model.Tag, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *converter) StatusToMasto(s *gtsmodel.Status, requestingAccount *gtsmodel.Account) (*model.Status, error) {
|
func (c *converter) StatusToMasto(s *gtsmodel.Status, requestingAccount *gtsmodel.Account) (*model.Status, error) {
|
||||||
repliesCount, err := c.db.GetReplyCountForStatus(s)
|
repliesCount, err := c.db.CountStatusReplies(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error counting replies: %s", err)
|
return nil, fmt.Errorf("error counting replies: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
reblogsCount, err := c.db.GetReblogCountForStatus(s)
|
reblogsCount, err := c.db.CountStatusReblogs(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error counting reblogs: %s", err)
|
return nil, fmt.Errorf("error counting reblogs: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
favesCount, err := c.db.GetFaveCountForStatus(s)
|
favesCount, err := c.db.CountStatusFaves(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error counting faves: %s", err)
|
return nil, fmt.Errorf("error counting faves: %s", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,25 +10,25 @@ func (c *converter) interactionsWithStatusForAccount(s *gtsmodel.Status, request
|
||||||
si := &statusInteractions{}
|
si := &statusInteractions{}
|
||||||
|
|
||||||
if requestingAccount != nil {
|
if requestingAccount != nil {
|
||||||
faved, err := c.db.StatusFavedBy(s, requestingAccount.ID)
|
faved, err := c.db.IsStatusFavedBy(s, requestingAccount.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error checking if requesting account has faved status: %s", err)
|
return nil, fmt.Errorf("error checking if requesting account has faved status: %s", err)
|
||||||
}
|
}
|
||||||
si.Faved = faved
|
si.Faved = faved
|
||||||
|
|
||||||
reblogged, err := c.db.StatusRebloggedBy(s, requestingAccount.ID)
|
reblogged, err := c.db.IsStatusRebloggedBy(s, requestingAccount.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error checking if requesting account has reblogged status: %s", err)
|
return nil, fmt.Errorf("error checking if requesting account has reblogged status: %s", err)
|
||||||
}
|
}
|
||||||
si.Reblogged = reblogged
|
si.Reblogged = reblogged
|
||||||
|
|
||||||
muted, err := c.db.StatusMutedBy(s, requestingAccount.ID)
|
muted, err := c.db.IsStatusMutedBy(s, requestingAccount.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error checking if requesting account has muted status: %s", err)
|
return nil, fmt.Errorf("error checking if requesting account has muted status: %s", err)
|
||||||
}
|
}
|
||||||
si.Muted = muted
|
si.Muted = muted
|
||||||
|
|
||||||
bookmarked, err := c.db.StatusBookmarkedBy(s, requestingAccount.ID)
|
bookmarked, err := c.db.IsStatusBookmarkedBy(s, requestingAccount.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error checking if requesting account has bookmarked status: %s", err)
|
return nil, fmt.Errorf("error checking if requesting account has bookmarked status: %s", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,8 @@ var testModels []interface{} = []interface{}{
|
||||||
>smodel.MediaAttachment{},
|
>smodel.MediaAttachment{},
|
||||||
>smodel.Mention{},
|
>smodel.Mention{},
|
||||||
>smodel.Status{},
|
>smodel.Status{},
|
||||||
|
>smodel.StatusToEmoji{},
|
||||||
|
>smodel.StatusToTag{},
|
||||||
>smodel.StatusFave{},
|
>smodel.StatusFave{},
|
||||||
>smodel.StatusBookmark{},
|
>smodel.StatusBookmark{},
|
||||||
>smodel.StatusMute{},
|
>smodel.StatusMute{},
|
||||||
|
|
@ -133,7 +135,7 @@ func StandardDBSetup(db db.DB, accounts map[string]*gtsmodel.Account) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, v := range NewTestStatuses() {
|
for _, v := range NewTestStatuses() {
|
||||||
if err := db.Put(v); err != nil {
|
if err := db.PutStatus(v); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue