| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | /* | 
					
						
							|  |  |  |    GoToSocial | 
					
						
							| 
									
										
										
										
											2021-12-20 18:42:19 +01:00
										 |  |  |    Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |    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/>. | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | package bundb | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"context" | 
					
						
							|  |  |  | 	"errors" | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2022-05-02 12:53:46 +02:00
										 |  |  | 	"strings" | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 	"time" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-10 10:56:49 +02:00
										 |  |  | 	"github.com/sirupsen/logrus" | 
					
						
							| 
									
										
										
										
											2021-09-01 10:08:21 +01:00
										 |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/cache" | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/config" | 
					
						
							|  |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/db" | 
					
						
							|  |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 	"github.com/uptrace/bun" | 
					
						
							| 
									
										
										
										
											2022-06-10 10:56:49 +02:00
										 |  |  | 	"github.com/uptrace/bun/dialect" | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type accountDB struct { | 
					
						
							| 
									
										
										
										
											2022-07-10 16:18:21 +01:00
										 |  |  | 	conn   *DBConn | 
					
						
							|  |  |  | 	cache  *cache.AccountCache | 
					
						
							|  |  |  | 	status *statusDB | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | func (a *accountDB) newAccountQ(account *gtsmodel.Account) *bun.SelectQuery { | 
					
						
							|  |  |  | 	return a.conn. | 
					
						
							|  |  |  | 		NewSelect(). | 
					
						
							|  |  |  | 		Model(account). | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 		Relation("AvatarMediaAttachment"). | 
					
						
							|  |  |  | 		Relation("HeaderMediaAttachment") | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | func (a *accountDB) GetAccountByID(ctx context.Context, id string) (*gtsmodel.Account, db.Error) { | 
					
						
							| 
									
										
										
										
											2021-09-01 10:08:21 +01:00
										 |  |  | 	return a.getAccount( | 
					
						
							|  |  |  | 		ctx, | 
					
						
							|  |  |  | 		func() (*gtsmodel.Account, bool) { | 
					
						
							|  |  |  | 			return a.cache.GetByID(id) | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		func(account *gtsmodel.Account) error { | 
					
						
							|  |  |  | 			return a.newAccountQ(account).Where("account.id = ?", id).Scan(ctx) | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	) | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | func (a *accountDB) GetAccountByURI(ctx context.Context, uri string) (*gtsmodel.Account, db.Error) { | 
					
						
							| 
									
										
										
										
											2021-09-01 10:08:21 +01:00
										 |  |  | 	return a.getAccount( | 
					
						
							|  |  |  | 		ctx, | 
					
						
							|  |  |  | 		func() (*gtsmodel.Account, bool) { | 
					
						
							|  |  |  | 			return a.cache.GetByURI(uri) | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		func(account *gtsmodel.Account) error { | 
					
						
							|  |  |  | 			return a.newAccountQ(account).Where("account.uri = ?", uri).Scan(ctx) | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	) | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-01 10:08:21 +01:00
										 |  |  | func (a *accountDB) GetAccountByURL(ctx context.Context, url string) (*gtsmodel.Account, db.Error) { | 
					
						
							|  |  |  | 	return a.getAccount( | 
					
						
							|  |  |  | 		ctx, | 
					
						
							|  |  |  | 		func() (*gtsmodel.Account, bool) { | 
					
						
							|  |  |  | 			return a.cache.GetByURL(url) | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		func(account *gtsmodel.Account) error { | 
					
						
							|  |  |  | 			return a.newAccountQ(account).Where("account.url = ?", url).Scan(ctx) | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	) | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-01 10:08:21 +01:00
										 |  |  | func (a *accountDB) getAccount(ctx context.Context, cacheGet func() (*gtsmodel.Account, bool), dbQuery func(*gtsmodel.Account) error) (*gtsmodel.Account, db.Error) { | 
					
						
							|  |  |  | 	// Attempt to fetch cached account | 
					
						
							|  |  |  | 	account, cached := cacheGet() | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-01 10:08:21 +01:00
										 |  |  | 	if !cached { | 
					
						
							|  |  |  | 		account = >smodel.Account{} | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-01 10:08:21 +01:00
										 |  |  | 		// Not cached! Perform database query | 
					
						
							|  |  |  | 		err := dbQuery(account) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, a.conn.ProcessError(err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Place in the cache | 
					
						
							|  |  |  | 		a.cache.Put(account) | 
					
						
							| 
									
										
										
										
											2021-08-29 15:41:41 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-09-01 10:08:21 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-29 15:41:41 +01:00
										 |  |  | 	return account, nil | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | func (a *accountDB) UpdateAccount(ctx context.Context, account *gtsmodel.Account) (*gtsmodel.Account, db.Error) { | 
					
						
							| 
									
										
										
										
											2021-09-11 13:19:06 +02:00
										 |  |  | 	// Update the account's last-updated | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 	account.UpdatedAt = time.Now() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-01 10:08:21 +01:00
										 |  |  | 	// Update the account model in the DB | 
					
						
							| 
									
										
										
										
											2021-09-11 13:19:06 +02:00
										 |  |  | 	_, err := a.conn. | 
					
						
							|  |  |  | 		NewUpdate(). | 
					
						
							|  |  |  | 		Model(account). | 
					
						
							|  |  |  | 		WherePK(). | 
					
						
							|  |  |  | 		Exec(ctx) | 
					
						
							| 
									
										
										
										
											2021-08-29 15:41:41 +01:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, a.conn.ProcessError(err) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-09-01 10:08:21 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Place updated account in cache | 
					
						
							|  |  |  | 	// (this will replace existing, i.e. invalidating) | 
					
						
							|  |  |  | 	a.cache.Put(account) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-29 15:41:41 +01:00
										 |  |  | 	return account, nil | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (a *accountDB) GetInstanceAccount(ctx context.Context, domain string) (*gtsmodel.Account, db.Error) { | 
					
						
							|  |  |  | 	account := new(gtsmodel.Account) | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	q := a.newAccountQ(account) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-17 14:40:39 +01:00
										 |  |  | 	if domain != "" { | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 		q = q. | 
					
						
							|  |  |  | 			Where("account.username = ?", domain). | 
					
						
							|  |  |  | 			Where("account.domain = ?", domain) | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		q = q. | 
					
						
							| 
									
										
										
										
											2022-05-30 13:41:24 +01:00
										 |  |  | 			Where("account.username = ?", config.GetHost()). | 
					
						
							| 
									
										
										
										
											2021-08-26 11:28:16 +02:00
										 |  |  | 			WhereGroup(" AND ", whereEmptyOrNull("domain")) | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-22 08:46:19 +01:00
										 |  |  | 	if err := q.Scan(ctx); err != nil { | 
					
						
							| 
									
										
										
										
											2021-08-29 15:41:41 +01:00
										 |  |  | 		return nil, a.conn.ProcessError(err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return account, nil | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | func (a *accountDB) GetAccountLastPosted(ctx context.Context, accountID string) (time.Time, db.Error) { | 
					
						
							|  |  |  | 	status := new(gtsmodel.Status) | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 	q := a.conn. | 
					
						
							|  |  |  | 		NewSelect(). | 
					
						
							|  |  |  | 		Model(status). | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 		Order("id DESC"). | 
					
						
							|  |  |  | 		Limit(1). | 
					
						
							|  |  |  | 		Where("account_id = ?", accountID). | 
					
						
							|  |  |  | 		Column("created_at") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-22 08:46:19 +01:00
										 |  |  | 	if err := q.Scan(ctx); err != nil { | 
					
						
							| 
									
										
										
										
											2021-08-29 15:41:41 +01:00
										 |  |  | 		return time.Time{}, a.conn.ProcessError(err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return status.CreatedAt, nil | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | func (a *accountDB) SetAccountHeaderOrAvatar(ctx context.Context, mediaAttachment *gtsmodel.MediaAttachment, accountID string) db.Error { | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 	if mediaAttachment.Avatar && mediaAttachment.Header { | 
					
						
							|  |  |  | 		return errors.New("one media attachment cannot be both header and avatar") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var headerOrAVI string | 
					
						
							| 
									
										
										
										
											2021-11-22 08:46:19 +01:00
										 |  |  | 	switch { | 
					
						
							|  |  |  | 	case mediaAttachment.Avatar: | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 		headerOrAVI = "avatar" | 
					
						
							| 
									
										
										
										
											2021-11-22 08:46:19 +01:00
										 |  |  | 	case mediaAttachment.Header: | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 		headerOrAVI = "header" | 
					
						
							| 
									
										
										
										
											2021-11-22 08:46:19 +01:00
										 |  |  | 	default: | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 		return errors.New("given media attachment was neither a header nor an avatar") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// TODO: there are probably more side effects here that need to be handled | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 	if _, err := a.conn. | 
					
						
							|  |  |  | 		NewInsert(). | 
					
						
							|  |  |  | 		Model(mediaAttachment). | 
					
						
							|  |  |  | 		Exec(ctx); err != nil { | 
					
						
							| 
									
										
										
										
											2021-08-29 15:41:41 +01:00
										 |  |  | 		return a.conn.ProcessError(err) | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 	if _, err := a.conn. | 
					
						
							|  |  |  | 		NewUpdate(). | 
					
						
							|  |  |  | 		Model(>smodel.Account{}). | 
					
						
							|  |  |  | 		Set(fmt.Sprintf("%s_media_attachment_id = ?", headerOrAVI), mediaAttachment.ID). | 
					
						
							|  |  |  | 		Where("id = ?", accountID). | 
					
						
							|  |  |  | 		Exec(ctx); err != nil { | 
					
						
							| 
									
										
										
										
											2021-08-29 15:41:41 +01:00
										 |  |  | 		return a.conn.ProcessError(err) | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-08-29 15:41:41 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | func (a *accountDB) GetLocalAccountByUsername(ctx context.Context, username string) (*gtsmodel.Account, db.Error) { | 
					
						
							|  |  |  | 	account := new(gtsmodel.Account) | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	q := a.newAccountQ(account). | 
					
						
							| 
									
										
										
										
											2022-05-02 12:53:46 +02:00
										 |  |  | 		Where("username = ?", strings.ToLower(username)). // usernames on our instance will always be lowercase | 
					
						
							| 
									
										
										
										
											2021-08-26 11:28:16 +02:00
										 |  |  | 		WhereGroup(" AND ", whereEmptyOrNull("domain")) | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-22 08:46:19 +01:00
										 |  |  | 	if err := q.Scan(ctx); err != nil { | 
					
						
							| 
									
										
										
										
											2021-08-29 15:41:41 +01:00
										 |  |  | 		return nil, a.conn.ProcessError(err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return account, nil | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | func (a *accountDB) GetAccountFaves(ctx context.Context, accountID string) ([]*gtsmodel.StatusFave, db.Error) { | 
					
						
							|  |  |  | 	faves := new([]*gtsmodel.StatusFave) | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 	if err := a.conn. | 
					
						
							|  |  |  | 		NewSelect(). | 
					
						
							|  |  |  | 		Model(faves). | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 		Where("account_id = ?", accountID). | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 		Scan(ctx); err != nil { | 
					
						
							| 
									
										
										
										
											2021-08-29 15:41:41 +01:00
										 |  |  | 		return nil, a.conn.ProcessError(err) | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-08-29 15:41:41 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 	return *faves, nil | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | func (a *accountDB) CountAccountStatuses(ctx context.Context, accountID string) (int, db.Error) { | 
					
						
							|  |  |  | 	return a.conn. | 
					
						
							|  |  |  | 		NewSelect(). | 
					
						
							|  |  |  | 		Model(>smodel.Status{}). | 
					
						
							|  |  |  | 		Where("account_id = ?", accountID). | 
					
						
							|  |  |  | 		Count(ctx) | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-15 14:33:01 +02:00
										 |  |  | func (a *accountDB) GetAccountStatuses(ctx context.Context, accountID string, limit int, excludeReplies bool, excludeReblogs bool, maxID string, minID string, pinnedOnly bool, mediaOnly bool, publicOnly bool) ([]*gtsmodel.Status, db.Error) { | 
					
						
							| 
									
										
										
										
											2022-07-10 16:18:21 +01:00
										 |  |  | 	statusIDs := []string{} | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 	q := a.conn. | 
					
						
							|  |  |  | 		NewSelect(). | 
					
						
							| 
									
										
										
										
											2022-07-10 16:18:21 +01:00
										 |  |  | 		Table("statuses"). | 
					
						
							|  |  |  | 		Column("id"). | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 		Order("id DESC") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 	if accountID != "" { | 
					
						
							|  |  |  | 		q = q.Where("account_id = ?", accountID) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if limit != 0 { | 
					
						
							|  |  |  | 		q = q.Limit(limit) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-24 11:57:39 +02:00
										 |  |  | 	if excludeReplies { | 
					
						
							|  |  |  | 		q = q.WhereGroup(" AND ", whereEmptyOrNull("in_reply_to_id")) | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-15 14:33:01 +02:00
										 |  |  | 	if excludeReblogs { | 
					
						
							|  |  |  | 		q = q.WhereGroup(" AND ", whereEmptyOrNull("boost_of_id")) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 	if maxID != "" { | 
					
						
							|  |  |  | 		q = q.Where("id < ?", maxID) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-24 11:57:39 +02:00
										 |  |  | 	if minID != "" { | 
					
						
							|  |  |  | 		q = q.Where("id > ?", minID) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if pinnedOnly { | 
					
						
							|  |  |  | 		q = q.Where("pinned = ?", true) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 	if mediaOnly { | 
					
						
							| 
									
										
										
										
											2022-04-10 15:11:12 +02:00
										 |  |  | 		// attachments are stored as a json object; | 
					
						
							|  |  |  | 		// this implementation differs between sqlite and postgres, | 
					
						
							| 
									
										
										
										
											2022-06-10 10:56:49 +02:00
										 |  |  | 		// so we have to be thorough to cover all eventualities | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 		q = q.WhereGroup(" AND ", func(q *bun.SelectQuery) *bun.SelectQuery { | 
					
						
							| 
									
										
										
										
											2022-06-10 10:56:49 +02:00
										 |  |  | 			switch a.conn.Dialect().Name() { | 
					
						
							|  |  |  | 			case dialect.PG: | 
					
						
							|  |  |  | 				return q. | 
					
						
							|  |  |  | 					Where("? IS NOT NULL", bun.Ident("attachments")). | 
					
						
							|  |  |  | 					Where("? != '{}'", bun.Ident("attachments")) | 
					
						
							|  |  |  | 			case dialect.SQLite: | 
					
						
							|  |  |  | 				return q. | 
					
						
							|  |  |  | 					Where("? IS NOT NULL", bun.Ident("attachments")). | 
					
						
							|  |  |  | 					Where("? != ''", bun.Ident("attachments")). | 
					
						
							|  |  |  | 					Where("? != 'null'", bun.Ident("attachments")). | 
					
						
							|  |  |  | 					Where("? != '{}'", bun.Ident("attachments")). | 
					
						
							|  |  |  | 					Where("? != '[]'", bun.Ident("attachments")) | 
					
						
							|  |  |  | 			default: | 
					
						
							|  |  |  | 				logrus.Panic("db dialect was neither pg nor sqlite") | 
					
						
							|  |  |  | 				return q | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 		}) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-24 11:57:39 +02:00
										 |  |  | 	if publicOnly { | 
					
						
							|  |  |  | 		q = q.Where("visibility = ?", gtsmodel.VisibilityPublic) | 
					
						
							| 
									
										
										
										
											2021-08-26 22:06:34 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-10 16:18:21 +01:00
										 |  |  | 	if err := q.Scan(ctx, &statusIDs); err != nil { | 
					
						
							| 
									
										
										
										
											2021-08-29 15:41:41 +01:00
										 |  |  | 		return nil, a.conn.ProcessError(err) | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-13 09:57:47 +02:00
										 |  |  | 	return a.statusesFromIDs(ctx, statusIDs) | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-13 09:57:47 +02:00
										 |  |  | func (a *accountDB) GetAccountWebStatuses(ctx context.Context, accountID string, limit int, maxID string) ([]*gtsmodel.Status, db.Error) { | 
					
						
							|  |  |  | 	statusIDs := []string{} | 
					
						
							| 
									
										
										
										
											2022-07-10 16:18:21 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-13 09:57:47 +02:00
										 |  |  | 	q := a.conn. | 
					
						
							|  |  |  | 		NewSelect(). | 
					
						
							|  |  |  | 		Table("statuses"). | 
					
						
							|  |  |  | 		Column("id"). | 
					
						
							|  |  |  | 		Where("account_id = ?", accountID). | 
					
						
							|  |  |  | 		WhereGroup(" AND ", whereEmptyOrNull("in_reply_to_id")). | 
					
						
							|  |  |  | 		WhereGroup(" AND ", whereEmptyOrNull("boost_of_id")). | 
					
						
							|  |  |  | 		Where("visibility = ?", gtsmodel.VisibilityPublic). | 
					
						
							|  |  |  | 		Where("federated = ?", true) | 
					
						
							| 
									
										
										
										
											2022-07-10 16:18:21 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-13 09:57:47 +02:00
										 |  |  | 	if maxID != "" { | 
					
						
							|  |  |  | 		q = q.Where("id < ?", maxID) | 
					
						
							| 
									
										
										
										
											2022-07-10 16:18:21 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-13 09:57:47 +02:00
										 |  |  | 	q = q.Limit(limit).Order("id DESC") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if err := q.Scan(ctx, &statusIDs); err != nil { | 
					
						
							|  |  |  | 		return nil, a.conn.ProcessError(err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return a.statusesFromIDs(ctx, statusIDs) | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | func (a *accountDB) GetAccountBlocks(ctx context.Context, accountID string, maxID string, sinceID string, limit int) ([]*gtsmodel.Account, string, string, db.Error) { | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 	blocks := []*gtsmodel.Block{} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 	fq := a.conn. | 
					
						
							|  |  |  | 		NewSelect(). | 
					
						
							|  |  |  | 		Model(&blocks). | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 		Where("block.account_id = ?", accountID). | 
					
						
							|  |  |  | 		Relation("TargetAccount"). | 
					
						
							|  |  |  | 		Order("block.id DESC") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if maxID != "" { | 
					
						
							|  |  |  | 		fq = fq.Where("block.id < ?", maxID) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if sinceID != "" { | 
					
						
							|  |  |  | 		fq = fq.Where("block.id > ?", sinceID) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if limit > 0 { | 
					
						
							|  |  |  | 		fq = fq.Limit(limit) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-22 08:46:19 +01:00
										 |  |  | 	if err := fq.Scan(ctx); err != nil { | 
					
						
							| 
									
										
										
										
											2021-08-29 15:41:41 +01:00
										 |  |  | 		return nil, "", "", a.conn.ProcessError(err) | 
					
						
							| 
									
										
										
										
											2021-08-20 12:26:56 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if len(blocks) == 0 { | 
					
						
							|  |  |  | 		return nil, "", "", db.ErrNoEntries | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	accounts := []*gtsmodel.Account{} | 
					
						
							|  |  |  | 	for _, b := range blocks { | 
					
						
							|  |  |  | 		accounts = append(accounts, b.TargetAccount) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	nextMaxID := blocks[len(blocks)-1].ID | 
					
						
							|  |  |  | 	prevMinID := blocks[0].ID | 
					
						
							|  |  |  | 	return accounts, nextMaxID, prevMinID, nil | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2022-07-13 09:57:47 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | func (a *accountDB) statusesFromIDs(ctx context.Context, statusIDs []string) ([]*gtsmodel.Status, db.Error) { | 
					
						
							|  |  |  | 	// Catch case of no statuses early | 
					
						
							|  |  |  | 	if len(statusIDs) == 0 { | 
					
						
							|  |  |  | 		return nil, db.ErrNoEntries | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Allocate return slice (will be at most len statusIDS) | 
					
						
							|  |  |  | 	statuses := make([]*gtsmodel.Status, 0, len(statusIDs)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, id := range statusIDs { | 
					
						
							|  |  |  | 		// Fetch from status from database by ID | 
					
						
							|  |  |  | 		status, err := a.status.GetStatusByID(ctx, id) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			logrus.Errorf("statusesFromIDs: error getting status %q: %v", id, err) | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Append to return slice | 
					
						
							|  |  |  | 		statuses = append(statuses, status) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return statuses, nil | 
					
						
							|  |  |  | } |