accounts.go (3572B)
1 // GoToSocial 2 // Copyright (C) GoToSocial Authors admin@gotosocial.org 3 // SPDX-License-Identifier: AGPL-3.0-or-later 4 // 5 // This program is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Affero General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // This program is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Affero General Public License for more details. 14 // 15 // You should have received a copy of the GNU Affero General Public License 16 // along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 package search 19 20 import ( 21 "context" 22 "errors" 23 "strings" 24 25 "codeberg.org/gruf/go-kv" 26 apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" 27 "github.com/superseriousbusiness/gotosocial/internal/db" 28 "github.com/superseriousbusiness/gotosocial/internal/gtserror" 29 "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" 30 "github.com/superseriousbusiness/gotosocial/internal/id" 31 "github.com/superseriousbusiness/gotosocial/internal/log" 32 ) 33 34 // Accounts does a partial search for accounts that 35 // match the given query. It expects input that looks 36 // like a namestring, and will normalize plaintext to look 37 // more like a namestring. For queries that include domain, 38 // it will only return one match at most. For namestrings 39 // that exclude domain, multiple matches may be returned. 40 // 41 // This behavior aligns more or less with Mastodon's API. 42 // See https://docs.joinmastodon.org/methods/accounts/#search. 43 func (p *Processor) Accounts( 44 ctx context.Context, 45 requestingAccount *gtsmodel.Account, 46 query string, 47 limit int, 48 offset int, 49 resolve bool, 50 following bool, 51 ) ([]*apimodel.Account, gtserror.WithCode) { 52 var ( 53 foundAccounts = make([]*gtsmodel.Account, 0, limit) 54 appendAccount = func(foundAccount *gtsmodel.Account) { foundAccounts = append(foundAccounts, foundAccount) } 55 ) 56 57 // Validate query. 58 query = strings.TrimSpace(query) 59 if query == "" { 60 err := gtserror.New("search query was empty string after trimming space") 61 return nil, gtserror.NewErrorBadRequest(err, err.Error()) 62 } 63 64 // Be nice and normalize query by prepending '@'. 65 // This will make it easier for accountsByNamestring 66 // to pick this up as a valid namestring. 67 if query[0] != '@' { 68 query = "@" + query 69 } 70 71 log. 72 WithContext(ctx). 73 WithFields(kv.Fields{ 74 {"limit", limit}, 75 {"offset", offset}, 76 {"query", query}, 77 {"resolve", resolve}, 78 {"following", following}, 79 }...). 80 Debugf("beginning search") 81 82 // todo: Currently we don't support offset for paging; 83 // if caller supplied an offset greater than 0, return 84 // nothing as though there were no additional results. 85 if offset > 0 { 86 return p.packageAccounts(ctx, requestingAccount, foundAccounts) 87 } 88 89 // Return all accounts we can find that match the 90 // provided query. If it's not a namestring, this 91 // won't return an error, it'll just return 0 results. 92 if _, err := p.accountsByNamestring( 93 ctx, 94 requestingAccount, 95 id.Highest, 96 id.Lowest, 97 limit, 98 offset, 99 query, 100 resolve, 101 following, 102 appendAccount, 103 ); err != nil && !errors.Is(err, db.ErrNoEntries) { 104 err = gtserror.Newf("error searching by namestring: %w", err) 105 return nil, gtserror.NewErrorInternalError(err) 106 } 107 108 // Return whatever we got (if anything). 109 return p.packageAccounts(ctx, requestingAccount, foundAccounts) 110 }