summaryrefslogtreecommitdiff
path: root/internal/db/bundb/account.go
diff options
context:
space:
mode:
authorLibravatar tobi <31960611+tsmethurst@users.noreply.github.com>2024-04-13 13:25:10 +0200
committerLibravatar GitHub <noreply@github.com>2024-04-13 13:25:10 +0200
commit89e0cfd8741b6763ca04e90558bccf4c3c380cfa (patch)
tree5858ada73473816fa1982f12717b66996d163f9d /internal/db/bundb/account.go
parent[performance] update GetAccountsByIDs() to use the new multi cache loader end... (diff)
downloadgotosocial-89e0cfd8741b6763ca04e90558bccf4c3c380cfa.tar.xz
[feature] Admin accounts endpoints; approve/reject sign-ups (#2826)
* update settings panels, add pending overview + approve/deny functions * add admin accounts get, approve, reject * send approved/rejected emails * use signup URL * docs! * email * swagger * web linting * fix email tests * wee lil fixerinos * use new paging logic for GetAccounts() series of admin endpoints, small changes to query building * shuffle useAccountIDIn check *before* adding to query * fix parse from toot react error * use `netip.Addr` * put valid slices in globals * optimistic updates for account state --------- Co-authored-by: kim <grufwub@gmail.com>
Diffstat (limited to 'internal/db/bundb/account.go')
-rw-r--r--internal/db/bundb/account.go254
1 files changed, 254 insertions, 0 deletions
diff --git a/internal/db/bundb/account.go b/internal/db/bundb/account.go
index 1ecf28e42..45e67c10b 100644
--- a/internal/db/bundb/account.go
+++ b/internal/db/bundb/account.go
@@ -20,6 +20,8 @@ package bundb
import (
"context"
"errors"
+ "fmt"
+ "net/netip"
"slices"
"strings"
"time"
@@ -31,6 +33,7 @@ import (
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/id"
"github.com/superseriousbusiness/gotosocial/internal/log"
+ "github.com/superseriousbusiness/gotosocial/internal/paging"
"github.com/superseriousbusiness/gotosocial/internal/state"
"github.com/superseriousbusiness/gotosocial/internal/util"
"github.com/uptrace/bun"
@@ -249,6 +252,257 @@ func (a *accountDB) GetInstanceAccount(ctx context.Context, domain string) (*gts
return a.GetAccountByUsernameDomain(ctx, username, domain)
}
+func (a *accountDB) GetAccounts(
+ ctx context.Context,
+ origin string,
+ status string,
+ mods bool,
+ invitedBy string,
+ username string,
+ displayName string,
+ domain string,
+ email string,
+ ip netip.Addr,
+ page *paging.Page,
+) (
+ []*gtsmodel.Account,
+ error,
+) {
+ var (
+ // local users lists,
+ // required for some
+ // limiting parameters.
+ users []*gtsmodel.User
+
+ // lazyLoadUsers only loads the users
+ // slice if it's required by params.
+ lazyLoadUsers = func() (err error) {
+ if users == nil {
+ users, err = a.state.DB.GetAllUsers(gtscontext.SetBarebones(ctx))
+ if err != nil {
+ return fmt.Errorf("error getting users: %w", err)
+ }
+ }
+ return nil
+ }
+
+ // Get paging params.
+ //
+ // Note this may be min_id OR since_id
+ // from the API, this gets handled below
+ // when checking order to reverse slice.
+ minID = page.GetMin()
+ maxID = page.GetMax()
+ limit = page.GetLimit()
+ order = page.GetOrder()
+
+ // Make educated guess for slice size
+ accountIDs = make([]string, 0, limit)
+ accountIDIn []string
+
+ useAccountIDIn bool
+ )
+
+ q := a.db.
+ NewSelect().
+ TableExpr("? AS ?", bun.Ident("accounts"), bun.Ident("account")).
+ // Select only IDs from table
+ Column("account.id")
+
+ // Return only accounts OLDER
+ // than account with maxID.
+ if maxID != "" {
+ maxIDAcct, err := a.GetAccountByID(
+ gtscontext.SetBarebones(ctx),
+ maxID,
+ )
+ if err != nil {
+ return nil, fmt.Errorf("error getting maxID account %s: %w", maxID, err)
+ }
+
+ q = q.Where("? < ?", bun.Ident("account.created_at"), maxIDAcct.CreatedAt)
+ }
+
+ // Return only accounts NEWER
+ // than account with minID.
+ if minID != "" {
+ minIDAcct, err := a.GetAccountByID(
+ gtscontext.SetBarebones(ctx),
+ minID,
+ )
+ if err != nil {
+ return nil, fmt.Errorf("error getting minID account %s: %w", minID, err)
+ }
+
+ q = q.Where("? > ?", bun.Ident("account.created_at"), minIDAcct.CreatedAt)
+ }
+
+ switch status {
+
+ case "active":
+ // Get only enabled accounts.
+ if err := lazyLoadUsers(); err != nil {
+ return nil, err
+ }
+ for _, user := range users {
+ if !*user.Disabled {
+ accountIDIn = append(accountIDIn, user.AccountID)
+ }
+ }
+ useAccountIDIn = true
+
+ case "pending":
+ // Get only unapproved accounts.
+ if err := lazyLoadUsers(); err != nil {
+ return nil, err
+ }
+ for _, user := range users {
+ if !*user.Approved {
+ accountIDIn = append(accountIDIn, user.AccountID)
+ }
+ }
+ useAccountIDIn = true
+
+ case "disabled":
+ // Get only disabled accounts.
+ if err := lazyLoadUsers(); err != nil {
+ return nil, err
+ }
+ for _, user := range users {
+ if *user.Disabled {
+ accountIDIn = append(accountIDIn, user.AccountID)
+ }
+ }
+ useAccountIDIn = true
+
+ case "silenced":
+ // Get only silenced accounts.
+ q = q.Where("? IS NOT NULL", bun.Ident("account.silenced_at"))
+
+ case "suspended":
+ // Get only suspended accounts.
+ q = q.Where("? IS NOT NULL", bun.Ident("account.suspended_at"))
+ }
+
+ if mods {
+ // Get only mod accounts.
+ if err := lazyLoadUsers(); err != nil {
+ return nil, err
+ }
+ for _, user := range users {
+ if *user.Moderator || *user.Admin {
+ accountIDIn = append(accountIDIn, user.AccountID)
+ }
+ }
+ useAccountIDIn = true
+ }
+
+ // TODO: invitedBy
+
+ if username != "" {
+ q = q.Where("? = ?", bun.Ident("account.username"), username)
+ }
+
+ if displayName != "" {
+ q = q.Where("? = ?", bun.Ident("account.display_name"), displayName)
+ }
+
+ if domain != "" {
+ q = q.Where("? = ?", bun.Ident("account.domain"), domain)
+ }
+
+ if email != "" {
+ if err := lazyLoadUsers(); err != nil {
+ return nil, err
+ }
+ for _, user := range users {
+ if user.Email == email || user.UnconfirmedEmail == email {
+ accountIDIn = append(accountIDIn, user.AccountID)
+ }
+ }
+ useAccountIDIn = true
+ }
+
+ // Use ip if not zero value.
+ if ip.IsValid() {
+ if err := lazyLoadUsers(); err != nil {
+ return nil, err
+ }
+ for _, user := range users {
+ if user.SignUpIP.String() == ip.String() {
+ accountIDIn = append(accountIDIn, user.AccountID)
+ }
+ }
+ useAccountIDIn = true
+ }
+
+ if origin == "local" && !useAccountIDIn {
+ // In the case we're not already limiting
+ // by specific subset of account IDs, just
+ // use existing list of user.AccountIDs
+ // instead of adding WHERE to the query.
+ if err := lazyLoadUsers(); err != nil {
+ return nil, err
+ }
+ for _, user := range users {
+ accountIDIn = append(accountIDIn, user.AccountID)
+ }
+ useAccountIDIn = true
+
+ } else if origin == "remote" {
+ if useAccountIDIn {
+ // useAccountIDIn specifically indicates
+ // a parameter that limits querying to
+ // local accounts, there will be none.
+ return nil, nil
+ }
+
+ // Get only remote accounts.
+ q = q.Where("? IS NOT NULL", bun.Ident("account.domain"))
+ }
+
+ if useAccountIDIn {
+ if len(accountIDIn) == 0 {
+ // There will be no
+ // possible answer.
+ return nil, nil
+ }
+
+ q = q.Where("? IN (?)", bun.Ident("account.id"), bun.In(accountIDIn))
+ }
+
+ if limit > 0 {
+ // Limit amount of
+ // accounts returned.
+ q = q.Limit(limit)
+ }
+
+ if order == paging.OrderAscending {
+ // Page up.
+ q = q.Order("account.created_at ASC")
+ } else {
+ // Page down.
+ q = q.Order("account.created_at DESC")
+ }
+
+ if err := q.Scan(ctx, &accountIDs); err != nil {
+ return nil, err
+ }
+
+ if len(accountIDs) == 0 {
+ return nil, nil
+ }
+
+ // If we're paging up, we still want accounts
+ // to be sorted by createdAt desc, so reverse ids slice.
+ if order == paging.OrderAscending {
+ slices.Reverse(accountIDs)
+ }
+
+ // Return account IDs loaded from cache + db.
+ return a.state.DB.GetAccountsByIDs(ctx, accountIDs)
+}
+
func (a *accountDB) getAccount(ctx context.Context, lookup string, dbQuery func(*gtsmodel.Account) error, keyParts ...any) (*gtsmodel.Account, error) {
// Fetch account from database cache with loader callback
account, err := a.state.Caches.GTS.Account.LoadOne(lookup, func() (*gtsmodel.Account, error) {