| 
									
										
										
										
											2023-03-12 16:00:57 +01:00
										 |  |  | // GoToSocial | 
					
						
							|  |  |  | // Copyright (C) GoToSocial Authors admin@gotosocial.org | 
					
						
							|  |  |  | // SPDX-License-Identifier: AGPL-3.0-or-later | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // 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-07-05 13:23:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | package account | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2022-03-15 16:12:35 +01:00
										 |  |  | 	"errors" | 
					
						
							| 
									
										
										
										
											2023-03-31 15:01:29 +02:00
										 |  |  | 	"net" | 
					
						
							| 
									
										
										
										
											2021-07-05 13:23:03 +02:00
										 |  |  | 	"time" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-19 09:47:55 +01:00
										 |  |  | 	"codeberg.org/gruf/go-kv" | 
					
						
							| 
									
										
										
										
											2023-03-31 15:01:29 +02:00
										 |  |  | 	"github.com/google/uuid" | 
					
						
							| 
									
										
										
										
											2021-08-31 15:59:12 +02:00
										 |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/ap" | 
					
						
							| 
									
										
										
										
											2021-07-05 13:23:03 +02:00
										 |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/db" | 
					
						
							| 
									
										
										
										
											2023-11-14 15:57:25 +01:00
										 |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/gtscontext" | 
					
						
							| 
									
										
										
										
											2022-03-15 16:12:35 +01:00
										 |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/gtserror" | 
					
						
							| 
									
										
										
										
											2021-07-05 13:23:03 +02:00
										 |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" | 
					
						
							| 
									
										
										
										
											2022-07-19 09:47:55 +01:00
										 |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/log" | 
					
						
							| 
									
										
										
										
											2021-08-31 15:59:12 +02:00
										 |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/messages" | 
					
						
							| 
									
										
										
										
											2023-08-07 19:38:11 +02:00
										 |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/util" | 
					
						
							| 
									
										
										
										
											2023-03-31 15:01:29 +02:00
										 |  |  | 	"golang.org/x/crypto/bcrypt" | 
					
						
							| 
									
										
										
										
											2021-07-05 13:23:03 +02:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-20 19:10:08 +01:00
										 |  |  | const deleteSelectLimit = 50 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-22 16:05:26 +01:00
										 |  |  | // Delete deletes an account, and all of that account's statuses, media, follows, notifications, etc etc etc. | 
					
						
							|  |  |  | // The origin passed here should be either the ID of the account doing the delete (can be itself), or the ID of a domain block. | 
					
						
							| 
									
										
										
										
											2023-11-14 15:57:25 +01:00
										 |  |  | func (p *Processor) Delete( | 
					
						
							|  |  |  | 	ctx context.Context, | 
					
						
							|  |  |  | 	account *gtsmodel.Account, | 
					
						
							|  |  |  | 	origin string, | 
					
						
							|  |  |  | ) gtserror.WithCode { | 
					
						
							| 
									
										
										
										
											2023-03-20 19:10:08 +01:00
										 |  |  | 	l := log.WithContext(ctx).WithFields(kv.Fields{ | 
					
						
							|  |  |  | 		{"username", account.Username}, | 
					
						
							|  |  |  | 		{"domain", account.Domain}, | 
					
						
							|  |  |  | 	}...) | 
					
						
							|  |  |  | 	l.Trace("beginning account delete process") | 
					
						
							| 
									
										
										
										
											2022-11-20 14:57:19 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-14 15:57:25 +01:00
										 |  |  | 	// Delete statuses *before* follows to ensure correct addressing | 
					
						
							|  |  |  | 	// of any outgoing fedi messages generated by deleting statuses. | 
					
						
							|  |  |  | 	if err := p.deleteAccountStatuses(ctx, account); err != nil { | 
					
						
							|  |  |  | 		l.Errorf("continuing after error during account delete: %v", err) | 
					
						
							| 
									
										
										
										
											2023-03-20 19:10:08 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-07-05 13:23:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-14 15:57:25 +01:00
										 |  |  | 	if err := p.deleteAccountFollows(ctx, account); err != nil { | 
					
						
							|  |  |  | 		l.Errorf("continuing after error during account delete: %v", err) | 
					
						
							| 
									
										
										
										
											2023-03-20 19:10:08 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-14 15:57:25 +01:00
										 |  |  | 	if err := p.deleteAccountBlocks(ctx, account); err != nil { | 
					
						
							|  |  |  | 		l.Errorf("continuing after error during account delete: %v", err) | 
					
						
							| 
									
										
										
										
											2023-03-20 19:10:08 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if err := p.deleteAccountNotifications(ctx, account); err != nil { | 
					
						
							| 
									
										
										
										
											2023-11-14 15:57:25 +01:00
										 |  |  | 		l.Errorf("continuing after error during account delete: %v", err) | 
					
						
							| 
									
										
										
										
											2023-03-20 19:10:08 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if err := p.deleteAccountPeripheral(ctx, account); err != nil { | 
					
						
							| 
									
										
										
										
											2023-11-14 15:57:25 +01:00
										 |  |  | 		l.Errorf("continuing after error during account delete: %v", err) | 
					
						
							| 
									
										
										
										
											2023-03-20 19:10:08 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-10 15:08:41 +01:00
										 |  |  | 	if account.IsLocal() { | 
					
						
							| 
									
										
										
										
											2023-11-14 15:57:25 +01:00
										 |  |  | 		// We delete tokens, applications and clients for | 
					
						
							|  |  |  | 		// account as one of the last stages during deletion, | 
					
						
							|  |  |  | 		// as other database models rely on these. | 
					
						
							| 
									
										
										
										
											2023-08-10 15:08:41 +01:00
										 |  |  | 		if err := p.deleteUserAndTokensForAccount(ctx, account); err != nil { | 
					
						
							| 
									
										
										
										
											2023-11-14 15:57:25 +01:00
										 |  |  | 			l.Errorf("continuing after error during account delete: %v", err) | 
					
						
							| 
									
										
										
										
											2023-08-10 15:08:41 +01:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-20 19:10:08 +01:00
										 |  |  | 	// To prevent the account being created again, | 
					
						
							|  |  |  | 	// stubbify it and update it in the db. | 
					
						
							|  |  |  | 	// The account will not be deleted, but it | 
					
						
							|  |  |  | 	// will become completely unusable. | 
					
						
							|  |  |  | 	columns := stubbifyAccount(account, origin) | 
					
						
							|  |  |  | 	if err := p.state.DB.UpdateAccount(ctx, account, columns...); err != nil { | 
					
						
							|  |  |  | 		return gtserror.NewErrorInternalError(err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-14 15:57:25 +01:00
										 |  |  | 	l.Info("account delete process complete") | 
					
						
							| 
									
										
										
										
											2023-03-20 19:10:08 +01:00
										 |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // DeleteSelf is like Delete, but specifically for local accounts deleting themselves. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // Calling DeleteSelf results in a delete message being enqueued in the processor, | 
					
						
							|  |  |  | // which causes side effects to occur: delete will be federated out to other instances, | 
					
						
							|  |  |  | // and the above Delete function will be called afterwards from the processor, to clear | 
					
						
							|  |  |  | // out the account's bits and bobs, and stubbify it. | 
					
						
							|  |  |  | func (p *Processor) DeleteSelf(ctx context.Context, account *gtsmodel.Account) gtserror.WithCode { | 
					
						
							|  |  |  | 	fromClientAPIMessage := messages.FromClientAPI{ | 
					
						
							|  |  |  | 		APObjectType:   ap.ActorPerson, | 
					
						
							|  |  |  | 		APActivityType: ap.ActivityDelete, | 
					
						
							|  |  |  | 		OriginAccount:  account, | 
					
						
							|  |  |  | 		TargetAccount:  account, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Process the delete side effects asynchronously. | 
					
						
							|  |  |  | 	p.state.Workers.EnqueueClientAPI(ctx, fromClientAPIMessage) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // deleteUserAndTokensForAccount deletes the gtsmodel.User and | 
					
						
							|  |  |  | // any OAuth tokens and applications for the given account. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // Callers to this function should already have checked that | 
					
						
							|  |  |  | // this is a local account, or else it won't have a user associated | 
					
						
							|  |  |  | // with it, and this will fail. | 
					
						
							|  |  |  | func (p *Processor) deleteUserAndTokensForAccount(ctx context.Context, account *gtsmodel.Account) error { | 
					
						
							|  |  |  | 	user, err := p.state.DB.GetUserByAccountID(ctx, account.ID) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2023-07-31 11:25:29 +01:00
										 |  |  | 		return gtserror.Newf("db error getting user: %w", err) | 
					
						
							| 
									
										
										
										
											2023-03-20 19:10:08 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	tokens := []*gtsmodel.Token{} | 
					
						
							|  |  |  | 	if err := p.state.DB.GetWhere(ctx, []db.Where{{Key: "user_id", Value: user.ID}}, &tokens); err != nil { | 
					
						
							| 
									
										
										
										
											2023-07-31 11:25:29 +01:00
										 |  |  | 		return gtserror.Newf("db error getting tokens: %w", err) | 
					
						
							| 
									
										
										
										
											2023-03-20 19:10:08 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, t := range tokens { | 
					
						
							|  |  |  | 		// Delete any OAuth clients associated with this token. | 
					
						
							|  |  |  | 		if err := p.state.DB.DeleteByID(ctx, t.ClientID, &[]*gtsmodel.Client{}); err != nil { | 
					
						
							| 
									
										
										
										
											2023-07-31 11:25:29 +01:00
										 |  |  | 			return gtserror.Newf("db error deleting client: %w", err) | 
					
						
							| 
									
										
										
										
											2023-03-20 19:10:08 +01:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Delete any OAuth applications associated with this token. | 
					
						
							| 
									
										
										
										
											2023-08-10 15:08:41 +01:00
										 |  |  | 		if err := p.state.DB.DeleteApplicationByClientID(ctx, t.ClientID); err != nil { | 
					
						
							| 
									
										
										
										
											2023-07-31 11:25:29 +01:00
										 |  |  | 			return gtserror.Newf("db error deleting application: %w", err) | 
					
						
							| 
									
										
										
										
											2023-03-20 19:10:08 +01:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Delete the token itself. | 
					
						
							|  |  |  | 		if err := p.state.DB.DeleteByID(ctx, t.ID, t); err != nil { | 
					
						
							| 
									
										
										
										
											2023-07-31 11:25:29 +01:00
										 |  |  | 			return gtserror.Newf("db error deleting token: %w", err) | 
					
						
							| 
									
										
										
										
											2021-07-05 13:23:03 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-31 15:01:29 +02:00
										 |  |  | 	columns, err := stubbifyUser(user) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2023-07-31 11:25:29 +01:00
										 |  |  | 		return gtserror.Newf("error stubbifying user: %w", err) | 
					
						
							| 
									
										
										
										
											2023-03-31 15:01:29 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if err := p.state.DB.UpdateUser(ctx, user, columns...); err != nil { | 
					
						
							| 
									
										
										
										
											2023-07-31 11:25:29 +01:00
										 |  |  | 		return gtserror.Newf("db error updating user: %w", err) | 
					
						
							| 
									
										
										
										
											2021-07-05 13:23:03 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-20 19:10:08 +01:00
										 |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // deleteAccountFollows deletes: | 
					
						
							|  |  |  | //   - Follows targeting account. | 
					
						
							|  |  |  | //   - Follow requests targeting account. | 
					
						
							|  |  |  | //   - Follows created by account. | 
					
						
							|  |  |  | //   - Follow requests created by account. | 
					
						
							|  |  |  | func (p *Processor) deleteAccountFollows(ctx context.Context, account *gtsmodel.Account) error { | 
					
						
							|  |  |  | 	// Delete follows targeting this account. | 
					
						
							| 
									
										
										
										
											2023-09-12 14:00:35 +01:00
										 |  |  | 	followedBy, err := p.state.DB.GetAccountFollowers(ctx, account.ID, nil) | 
					
						
							| 
									
										
										
										
											2023-03-20 19:10:08 +01:00
										 |  |  | 	if err != nil && !errors.Is(err, db.ErrNoEntries) { | 
					
						
							| 
									
										
										
										
											2023-07-31 11:25:29 +01:00
										 |  |  | 		return gtserror.Newf("db error getting follows targeting account %s: %w", account.ID, err) | 
					
						
							| 
									
										
										
										
											2023-03-20 19:10:08 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, follow := range followedBy { | 
					
						
							| 
									
										
											  
											
												[performance] refactoring + add fave / follow / request / visibility caching (#1607)
* refactor visibility checking, add caching for visibility
* invalidate visibility cache items on account / status deletes
* fix requester ID passed to visibility cache nil ptr
* de-interface caches, fix home / public timeline caching + visibility
* finish adding code comments for visibility filter
* fix angry goconst linter warnings
* actually finish adding filter visibility code comments for timeline functions
* move home timeline status author check to after visibility
* remove now-unused code
* add more code comments
* add TODO code comment, update printed cache start names
* update printed cache names on stop
* start adding separate follow(request) delete db functions, add specific visibility cache tests
* add relationship type caching
* fix getting local account follows / followed-bys, other small codebase improvements
* simplify invalidation using cache hooks, add more GetAccountBy___() functions
* fix boosting to return 404 if not boostable but no error (to not leak status ID)
* remove dead code
* improved placement of cache invalidation
* update license headers
* add example follow, follow-request config entries
* add example visibility cache configuration to config file
* use specific PutFollowRequest() instead of just Put()
* add tests for all GetAccountBy()
* add GetBlockBy() tests
* update block to check primitive fields
* update and finish adding Get{Account,Block,Follow,FollowRequest}By() tests
* fix copy-pasted code
* update envparsing test
* whitespace
* fix bun struct tag
* add license header to gtscontext
* fix old license header
* improved error creation to not use fmt.Errorf() when not needed
* fix various rebase conflicts, fix account test
* remove commented-out code, fix-up mention caching
* fix mention select bun statement
* ensure mention target account populated, pass in context to customrenderer logging
* remove more uncommented code, fix typeutil test
* add statusfave database model caching
* add status fave cache configuration
* add status fave cache example config
* woops, catch missed error. nice catch linter!
* add back testrig panic on nil db
* update example configuration to match defaults, slight tweak to cache configuration defaults
* update envparsing test with new defaults
* fetch followingget to use the follow target account
* use accounnt.IsLocal() instead of empty domain check
* use constants for the cache visibility type check
* use bun.In() for notification type restriction in db query
* include replies when fetching PublicTimeline() (to account for single-author threads in Visibility{}.StatusPublicTimelineable())
* use bun query building for nested select statements to ensure working with postgres
* update public timeline future status checks to match visibility filter
* same as previous, for home timeline
* update public timeline tests to dynamically check for appropriate statuses
* migrate accounts to allow unique constraint on public_key
* provide minimal account with publicKey
---------
Signed-off-by: kim <grufwub@gmail.com>
Co-authored-by: tsmethurst <tobi.smethurst@protonmail.com>
											
										 
											2023-03-28 14:03:14 +01:00
										 |  |  | 		if err := p.state.DB.DeleteFollowByID(ctx, follow.ID); err != nil { | 
					
						
							| 
									
										
										
										
											2023-07-31 11:25:29 +01:00
										 |  |  | 			return gtserror.Newf("db error unfollowing account followedBy: %w", err) | 
					
						
							| 
									
										
										
										
											2023-03-20 19:10:08 +01:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-07-05 13:23:03 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-20 19:10:08 +01:00
										 |  |  | 	// Delete follow requests targeting this account. | 
					
						
							| 
									
										
										
										
											2023-09-12 14:00:35 +01:00
										 |  |  | 	followRequestedBy, err := p.state.DB.GetAccountFollowRequests(ctx, account.ID, nil) | 
					
						
							| 
									
										
										
										
											2023-03-20 19:10:08 +01:00
										 |  |  | 	if err != nil && !errors.Is(err, db.ErrNoEntries) { | 
					
						
							| 
									
										
										
										
											2023-07-31 11:25:29 +01:00
										 |  |  | 		return gtserror.Newf("db error getting follow requests targeting account %s: %w", account.ID, err) | 
					
						
							| 
									
										
										
										
											2023-03-20 19:10:08 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-07-05 13:23:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-20 19:10:08 +01:00
										 |  |  | 	for _, followRequest := range followRequestedBy { | 
					
						
							| 
									
										
											  
											
												[performance] refactoring + add fave / follow / request / visibility caching (#1607)
* refactor visibility checking, add caching for visibility
* invalidate visibility cache items on account / status deletes
* fix requester ID passed to visibility cache nil ptr
* de-interface caches, fix home / public timeline caching + visibility
* finish adding code comments for visibility filter
* fix angry goconst linter warnings
* actually finish adding filter visibility code comments for timeline functions
* move home timeline status author check to after visibility
* remove now-unused code
* add more code comments
* add TODO code comment, update printed cache start names
* update printed cache names on stop
* start adding separate follow(request) delete db functions, add specific visibility cache tests
* add relationship type caching
* fix getting local account follows / followed-bys, other small codebase improvements
* simplify invalidation using cache hooks, add more GetAccountBy___() functions
* fix boosting to return 404 if not boostable but no error (to not leak status ID)
* remove dead code
* improved placement of cache invalidation
* update license headers
* add example follow, follow-request config entries
* add example visibility cache configuration to config file
* use specific PutFollowRequest() instead of just Put()
* add tests for all GetAccountBy()
* add GetBlockBy() tests
* update block to check primitive fields
* update and finish adding Get{Account,Block,Follow,FollowRequest}By() tests
* fix copy-pasted code
* update envparsing test
* whitespace
* fix bun struct tag
* add license header to gtscontext
* fix old license header
* improved error creation to not use fmt.Errorf() when not needed
* fix various rebase conflicts, fix account test
* remove commented-out code, fix-up mention caching
* fix mention select bun statement
* ensure mention target account populated, pass in context to customrenderer logging
* remove more uncommented code, fix typeutil test
* add statusfave database model caching
* add status fave cache configuration
* add status fave cache example config
* woops, catch missed error. nice catch linter!
* add back testrig panic on nil db
* update example configuration to match defaults, slight tweak to cache configuration defaults
* update envparsing test with new defaults
* fetch followingget to use the follow target account
* use accounnt.IsLocal() instead of empty domain check
* use constants for the cache visibility type check
* use bun.In() for notification type restriction in db query
* include replies when fetching PublicTimeline() (to account for single-author threads in Visibility{}.StatusPublicTimelineable())
* use bun query building for nested select statements to ensure working with postgres
* update public timeline future status checks to match visibility filter
* same as previous, for home timeline
* update public timeline tests to dynamically check for appropriate statuses
* migrate accounts to allow unique constraint on public_key
* provide minimal account with publicKey
---------
Signed-off-by: kim <grufwub@gmail.com>
Co-authored-by: tsmethurst <tobi.smethurst@protonmail.com>
											
										 
											2023-03-28 14:03:14 +01:00
										 |  |  | 		if err := p.state.DB.DeleteFollowRequestByID(ctx, followRequest.ID); err != nil { | 
					
						
							| 
									
										
										
										
											2023-07-31 11:25:29 +01:00
										 |  |  | 			return gtserror.Newf("db error unfollowing account followRequestedBy: %w", err) | 
					
						
							| 
									
										
										
										
											2023-03-20 19:10:08 +01:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-07-05 13:23:03 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-20 19:10:08 +01:00
										 |  |  | 	var ( | 
					
						
							|  |  |  | 		// Use this slice to batch unfollow messages. | 
					
						
							|  |  |  | 		msgs = []messages.FromClientAPI{} | 
					
						
							|  |  |  | 		// To avoid checking if account is local over + over | 
					
						
							|  |  |  | 		// inside the subsequent loops, just generate static | 
					
						
							|  |  |  | 		// side effects function once now. | 
					
						
							| 
									
										
										
										
											2023-11-14 15:57:25 +01:00
										 |  |  | 		unfollowSideEffects = p.unfollowSideEffectsFunc(account.IsLocal()) | 
					
						
							| 
									
										
										
										
											2023-03-20 19:10:08 +01:00
										 |  |  | 	) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Delete follows originating from this account. | 
					
						
							| 
									
										
										
										
											2023-09-12 14:00:35 +01:00
										 |  |  | 	following, err := p.state.DB.GetAccountFollows(ctx, account.ID, nil) | 
					
						
							| 
									
										
										
										
											2023-03-20 19:10:08 +01:00
										 |  |  | 	if err != nil && !errors.Is(err, db.ErrNoEntries) { | 
					
						
							| 
									
										
										
										
											2023-07-31 11:25:29 +01:00
										 |  |  | 		return gtserror.Newf("db error getting follows owned by account %s: %w", account.ID, err) | 
					
						
							| 
									
										
										
										
											2021-07-05 13:23:03 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-20 19:10:08 +01:00
										 |  |  | 	// For each follow owned by this account, unfollow | 
					
						
							|  |  |  | 	// and process side effects (noop if remote account). | 
					
						
							|  |  |  | 	for _, follow := range following { | 
					
						
							| 
									
										
											  
											
												[performance] refactoring + add fave / follow / request / visibility caching (#1607)
* refactor visibility checking, add caching for visibility
* invalidate visibility cache items on account / status deletes
* fix requester ID passed to visibility cache nil ptr
* de-interface caches, fix home / public timeline caching + visibility
* finish adding code comments for visibility filter
* fix angry goconst linter warnings
* actually finish adding filter visibility code comments for timeline functions
* move home timeline status author check to after visibility
* remove now-unused code
* add more code comments
* add TODO code comment, update printed cache start names
* update printed cache names on stop
* start adding separate follow(request) delete db functions, add specific visibility cache tests
* add relationship type caching
* fix getting local account follows / followed-bys, other small codebase improvements
* simplify invalidation using cache hooks, add more GetAccountBy___() functions
* fix boosting to return 404 if not boostable but no error (to not leak status ID)
* remove dead code
* improved placement of cache invalidation
* update license headers
* add example follow, follow-request config entries
* add example visibility cache configuration to config file
* use specific PutFollowRequest() instead of just Put()
* add tests for all GetAccountBy()
* add GetBlockBy() tests
* update block to check primitive fields
* update and finish adding Get{Account,Block,Follow,FollowRequest}By() tests
* fix copy-pasted code
* update envparsing test
* whitespace
* fix bun struct tag
* add license header to gtscontext
* fix old license header
* improved error creation to not use fmt.Errorf() when not needed
* fix various rebase conflicts, fix account test
* remove commented-out code, fix-up mention caching
* fix mention select bun statement
* ensure mention target account populated, pass in context to customrenderer logging
* remove more uncommented code, fix typeutil test
* add statusfave database model caching
* add status fave cache configuration
* add status fave cache example config
* woops, catch missed error. nice catch linter!
* add back testrig panic on nil db
* update example configuration to match defaults, slight tweak to cache configuration defaults
* update envparsing test with new defaults
* fetch followingget to use the follow target account
* use accounnt.IsLocal() instead of empty domain check
* use constants for the cache visibility type check
* use bun.In() for notification type restriction in db query
* include replies when fetching PublicTimeline() (to account for single-author threads in Visibility{}.StatusPublicTimelineable())
* use bun query building for nested select statements to ensure working with postgres
* update public timeline future status checks to match visibility filter
* same as previous, for home timeline
* update public timeline tests to dynamically check for appropriate statuses
* migrate accounts to allow unique constraint on public_key
* provide minimal account with publicKey
---------
Signed-off-by: kim <grufwub@gmail.com>
Co-authored-by: tsmethurst <tobi.smethurst@protonmail.com>
											
										 
											2023-03-28 14:03:14 +01:00
										 |  |  | 		if err := p.state.DB.DeleteFollowByID(ctx, follow.ID); err != nil { | 
					
						
							| 
									
										
										
										
											2023-07-31 11:25:29 +01:00
										 |  |  | 			return gtserror.Newf("db error unfollowing account: %w", err) | 
					
						
							| 
									
										
										
										
											2023-03-20 19:10:08 +01:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		if msg := unfollowSideEffects(ctx, account, follow); msg != nil { | 
					
						
							|  |  |  | 			// There was a side effect to process. | 
					
						
							|  |  |  | 			msgs = append(msgs, *msg) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-07-05 13:23:03 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-20 19:10:08 +01:00
										 |  |  | 	// Delete follow requests originating from this account. | 
					
						
							| 
									
										
										
										
											2023-09-12 14:00:35 +01:00
										 |  |  | 	followRequesting, err := p.state.DB.GetAccountFollowRequesting(ctx, account.ID, nil) | 
					
						
							| 
									
										
										
										
											2023-03-20 19:10:08 +01:00
										 |  |  | 	if err != nil && !errors.Is(err, db.ErrNoEntries) { | 
					
						
							| 
									
										
										
										
											2023-07-31 11:25:29 +01:00
										 |  |  | 		return gtserror.Newf("db error getting follow requests owned by account %s: %w", account.ID, err) | 
					
						
							| 
									
										
										
										
											2021-07-05 13:23:03 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-20 19:10:08 +01:00
										 |  |  | 	// For each follow owned by this account, unfollow | 
					
						
							|  |  |  | 	// and process side effects (noop if remote account). | 
					
						
							|  |  |  | 	for _, followRequest := range followRequesting { | 
					
						
							| 
									
										
											  
											
												[performance] refactoring + add fave / follow / request / visibility caching (#1607)
* refactor visibility checking, add caching for visibility
* invalidate visibility cache items on account / status deletes
* fix requester ID passed to visibility cache nil ptr
* de-interface caches, fix home / public timeline caching + visibility
* finish adding code comments for visibility filter
* fix angry goconst linter warnings
* actually finish adding filter visibility code comments for timeline functions
* move home timeline status author check to after visibility
* remove now-unused code
* add more code comments
* add TODO code comment, update printed cache start names
* update printed cache names on stop
* start adding separate follow(request) delete db functions, add specific visibility cache tests
* add relationship type caching
* fix getting local account follows / followed-bys, other small codebase improvements
* simplify invalidation using cache hooks, add more GetAccountBy___() functions
* fix boosting to return 404 if not boostable but no error (to not leak status ID)
* remove dead code
* improved placement of cache invalidation
* update license headers
* add example follow, follow-request config entries
* add example visibility cache configuration to config file
* use specific PutFollowRequest() instead of just Put()
* add tests for all GetAccountBy()
* add GetBlockBy() tests
* update block to check primitive fields
* update and finish adding Get{Account,Block,Follow,FollowRequest}By() tests
* fix copy-pasted code
* update envparsing test
* whitespace
* fix bun struct tag
* add license header to gtscontext
* fix old license header
* improved error creation to not use fmt.Errorf() when not needed
* fix various rebase conflicts, fix account test
* remove commented-out code, fix-up mention caching
* fix mention select bun statement
* ensure mention target account populated, pass in context to customrenderer logging
* remove more uncommented code, fix typeutil test
* add statusfave database model caching
* add status fave cache configuration
* add status fave cache example config
* woops, catch missed error. nice catch linter!
* add back testrig panic on nil db
* update example configuration to match defaults, slight tweak to cache configuration defaults
* update envparsing test with new defaults
* fetch followingget to use the follow target account
* use accounnt.IsLocal() instead of empty domain check
* use constants for the cache visibility type check
* use bun.In() for notification type restriction in db query
* include replies when fetching PublicTimeline() (to account for single-author threads in Visibility{}.StatusPublicTimelineable())
* use bun query building for nested select statements to ensure working with postgres
* update public timeline future status checks to match visibility filter
* same as previous, for home timeline
* update public timeline tests to dynamically check for appropriate statuses
* migrate accounts to allow unique constraint on public_key
* provide minimal account with publicKey
---------
Signed-off-by: kim <grufwub@gmail.com>
Co-authored-by: tsmethurst <tobi.smethurst@protonmail.com>
											
										 
											2023-03-28 14:03:14 +01:00
										 |  |  | 		if err := p.state.DB.DeleteFollowRequestByID(ctx, followRequest.ID); err != nil { | 
					
						
							| 
									
										
										
										
											2023-07-31 11:25:29 +01:00
										 |  |  | 			return gtserror.Newf("db error unfollowingRequesting account: %w", err) | 
					
						
							| 
									
										
										
										
											2023-03-20 19:10:08 +01:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2022-11-20 14:57:19 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-20 19:10:08 +01:00
										 |  |  | 		// Dummy out a follow so our side effects func | 
					
						
							|  |  |  | 		// has something to work with. This follow will | 
					
						
							|  |  |  | 		// never enter the db, it's just for convenience. | 
					
						
							|  |  |  | 		follow := >smodel.Follow{ | 
					
						
							| 
									
										
											  
											
												[performance] refactoring + add fave / follow / request / visibility caching (#1607)
* refactor visibility checking, add caching for visibility
* invalidate visibility cache items on account / status deletes
* fix requester ID passed to visibility cache nil ptr
* de-interface caches, fix home / public timeline caching + visibility
* finish adding code comments for visibility filter
* fix angry goconst linter warnings
* actually finish adding filter visibility code comments for timeline functions
* move home timeline status author check to after visibility
* remove now-unused code
* add more code comments
* add TODO code comment, update printed cache start names
* update printed cache names on stop
* start adding separate follow(request) delete db functions, add specific visibility cache tests
* add relationship type caching
* fix getting local account follows / followed-bys, other small codebase improvements
* simplify invalidation using cache hooks, add more GetAccountBy___() functions
* fix boosting to return 404 if not boostable but no error (to not leak status ID)
* remove dead code
* improved placement of cache invalidation
* update license headers
* add example follow, follow-request config entries
* add example visibility cache configuration to config file
* use specific PutFollowRequest() instead of just Put()
* add tests for all GetAccountBy()
* add GetBlockBy() tests
* update block to check primitive fields
* update and finish adding Get{Account,Block,Follow,FollowRequest}By() tests
* fix copy-pasted code
* update envparsing test
* whitespace
* fix bun struct tag
* add license header to gtscontext
* fix old license header
* improved error creation to not use fmt.Errorf() when not needed
* fix various rebase conflicts, fix account test
* remove commented-out code, fix-up mention caching
* fix mention select bun statement
* ensure mention target account populated, pass in context to customrenderer logging
* remove more uncommented code, fix typeutil test
* add statusfave database model caching
* add status fave cache configuration
* add status fave cache example config
* woops, catch missed error. nice catch linter!
* add back testrig panic on nil db
* update example configuration to match defaults, slight tweak to cache configuration defaults
* update envparsing test with new defaults
* fetch followingget to use the follow target account
* use accounnt.IsLocal() instead of empty domain check
* use constants for the cache visibility type check
* use bun.In() for notification type restriction in db query
* include replies when fetching PublicTimeline() (to account for single-author threads in Visibility{}.StatusPublicTimelineable())
* use bun query building for nested select statements to ensure working with postgres
* update public timeline future status checks to match visibility filter
* same as previous, for home timeline
* update public timeline tests to dynamically check for appropriate statuses
* migrate accounts to allow unique constraint on public_key
* provide minimal account with publicKey
---------
Signed-off-by: kim <grufwub@gmail.com>
Co-authored-by: tsmethurst <tobi.smethurst@protonmail.com>
											
										 
											2023-03-28 14:03:14 +01:00
										 |  |  | 			URI:             followRequest.URI, | 
					
						
							| 
									
										
										
										
											2023-03-20 19:10:08 +01:00
										 |  |  | 			AccountID:       followRequest.AccountID, | 
					
						
							|  |  |  | 			Account:         followRequest.Account, | 
					
						
							|  |  |  | 			TargetAccountID: followRequest.TargetAccountID, | 
					
						
							|  |  |  | 			TargetAccount:   followRequest.TargetAccount, | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if msg := unfollowSideEffects(ctx, account, follow); msg != nil { | 
					
						
							|  |  |  | 			// There was a side effect to process. | 
					
						
							|  |  |  | 			msgs = append(msgs, *msg) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-14 15:57:25 +01:00
										 |  |  | 	// Process accreted messages in serial. | 
					
						
							|  |  |  | 	for _, msg := range msgs { | 
					
						
							|  |  |  | 		if err := p.state.Workers.ProcessFromClientAPI(ctx, msg); err != nil { | 
					
						
							|  |  |  | 			log.Errorf( | 
					
						
							|  |  |  | 				ctx, | 
					
						
							|  |  |  | 				"error processing %s of %s during Delete of account %s: %v", | 
					
						
							|  |  |  | 				msg.APActivityType, msg.APObjectType, account.ID, err, | 
					
						
							|  |  |  | 			) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-11-20 14:57:19 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-20 19:10:08 +01:00
										 |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-14 15:57:25 +01:00
										 |  |  | func (p *Processor) unfollowSideEffectsFunc(local bool) func( | 
					
						
							|  |  |  | 	ctx context.Context, | 
					
						
							|  |  |  | 	account *gtsmodel.Account, | 
					
						
							|  |  |  | 	follow *gtsmodel.Follow, | 
					
						
							|  |  |  | ) *messages.FromClientAPI { | 
					
						
							|  |  |  | 	if !local { | 
					
						
							| 
									
										
										
										
											2023-03-20 19:10:08 +01:00
										 |  |  | 		// Don't try to process side effects | 
					
						
							|  |  |  | 		// for accounts that aren't local. | 
					
						
							| 
									
										
										
										
											2023-11-14 15:57:25 +01:00
										 |  |  | 		return func( | 
					
						
							|  |  |  | 			_ context.Context, | 
					
						
							|  |  |  | 			_ *gtsmodel.Account, | 
					
						
							|  |  |  | 			_ *gtsmodel.Follow, | 
					
						
							|  |  |  | 		) *messages.FromClientAPI { | 
					
						
							|  |  |  | 			// noop | 
					
						
							|  |  |  | 			return nil | 
					
						
							| 
									
										
										
										
											2023-03-20 19:10:08 +01:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-14 15:57:25 +01:00
										 |  |  | 	return func( | 
					
						
							|  |  |  | 		ctx context.Context, | 
					
						
							|  |  |  | 		account *gtsmodel.Account, | 
					
						
							|  |  |  | 		follow *gtsmodel.Follow, | 
					
						
							|  |  |  | 	) *messages.FromClientAPI { | 
					
						
							| 
									
										
										
										
											2023-03-20 19:10:08 +01:00
										 |  |  | 		if follow.TargetAccount == nil { | 
					
						
							|  |  |  | 			// TargetAccount seems to have gone; | 
					
						
							|  |  |  | 			// race condition? db corruption? | 
					
						
							| 
									
										
										
										
											2023-11-14 15:57:25 +01:00
										 |  |  | 			log. | 
					
						
							|  |  |  | 				WithContext(ctx). | 
					
						
							|  |  |  | 				WithField("follow", follow). | 
					
						
							|  |  |  | 				Warn("follow had no TargetAccount, likely race condition") | 
					
						
							| 
									
										
										
										
											2023-03-20 19:10:08 +01:00
										 |  |  | 			return nil | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if follow.TargetAccount.IsLocal() { | 
					
						
							| 
									
										
										
										
											2023-11-14 15:57:25 +01:00
										 |  |  | 			// No side effects | 
					
						
							|  |  |  | 			// for local unfollows. | 
					
						
							| 
									
										
										
										
											2023-03-20 19:10:08 +01:00
										 |  |  | 			return nil | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// There was a follow, process side effects. | 
					
						
							|  |  |  | 		return &messages.FromClientAPI{ | 
					
						
							|  |  |  | 			APObjectType:   ap.ActivityFollow, | 
					
						
							|  |  |  | 			APActivityType: ap.ActivityUndo, | 
					
						
							|  |  |  | 			GTSModel:       follow, | 
					
						
							|  |  |  | 			OriginAccount:  account, | 
					
						
							|  |  |  | 			TargetAccount:  follow.TargetAccount, | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (p *Processor) deleteAccountBlocks(ctx context.Context, account *gtsmodel.Account) error { | 
					
						
							| 
									
										
											  
											
												[performance] refactoring + add fave / follow / request / visibility caching (#1607)
* refactor visibility checking, add caching for visibility
* invalidate visibility cache items on account / status deletes
* fix requester ID passed to visibility cache nil ptr
* de-interface caches, fix home / public timeline caching + visibility
* finish adding code comments for visibility filter
* fix angry goconst linter warnings
* actually finish adding filter visibility code comments for timeline functions
* move home timeline status author check to after visibility
* remove now-unused code
* add more code comments
* add TODO code comment, update printed cache start names
* update printed cache names on stop
* start adding separate follow(request) delete db functions, add specific visibility cache tests
* add relationship type caching
* fix getting local account follows / followed-bys, other small codebase improvements
* simplify invalidation using cache hooks, add more GetAccountBy___() functions
* fix boosting to return 404 if not boostable but no error (to not leak status ID)
* remove dead code
* improved placement of cache invalidation
* update license headers
* add example follow, follow-request config entries
* add example visibility cache configuration to config file
* use specific PutFollowRequest() instead of just Put()
* add tests for all GetAccountBy()
* add GetBlockBy() tests
* update block to check primitive fields
* update and finish adding Get{Account,Block,Follow,FollowRequest}By() tests
* fix copy-pasted code
* update envparsing test
* whitespace
* fix bun struct tag
* add license header to gtscontext
* fix old license header
* improved error creation to not use fmt.Errorf() when not needed
* fix various rebase conflicts, fix account test
* remove commented-out code, fix-up mention caching
* fix mention select bun statement
* ensure mention target account populated, pass in context to customrenderer logging
* remove more uncommented code, fix typeutil test
* add statusfave database model caching
* add status fave cache configuration
* add status fave cache example config
* woops, catch missed error. nice catch linter!
* add back testrig panic on nil db
* update example configuration to match defaults, slight tweak to cache configuration defaults
* update envparsing test with new defaults
* fetch followingget to use the follow target account
* use accounnt.IsLocal() instead of empty domain check
* use constants for the cache visibility type check
* use bun.In() for notification type restriction in db query
* include replies when fetching PublicTimeline() (to account for single-author threads in Visibility{}.StatusPublicTimelineable())
* use bun query building for nested select statements to ensure working with postgres
* update public timeline future status checks to match visibility filter
* same as previous, for home timeline
* update public timeline tests to dynamically check for appropriate statuses
* migrate accounts to allow unique constraint on public_key
* provide minimal account with publicKey
---------
Signed-off-by: kim <grufwub@gmail.com>
Co-authored-by: tsmethurst <tobi.smethurst@protonmail.com>
											
										 
											2023-03-28 14:03:14 +01:00
										 |  |  | 	if err := p.state.DB.DeleteAccountBlocks(ctx, account.ID); err != nil { | 
					
						
							| 
									
										
										
										
											2023-07-31 11:25:29 +01:00
										 |  |  | 		return gtserror.Newf("db error deleting account blocks for %s: %w", account.ID, err) | 
					
						
							| 
									
										
										
										
											2023-03-20 19:10:08 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // deleteAccountStatuses iterates through all statuses owned by | 
					
						
							|  |  |  | // the given account, passing each discovered status (and boosts | 
					
						
							| 
									
										
										
										
											2023-11-14 15:57:25 +01:00
										 |  |  | // thereof) to the processor workers for further processing. | 
					
						
							|  |  |  | func (p *Processor) deleteAccountStatuses( | 
					
						
							|  |  |  | 	ctx context.Context, | 
					
						
							|  |  |  | 	account *gtsmodel.Account, | 
					
						
							|  |  |  | ) error { | 
					
						
							| 
									
										
										
										
											2023-03-20 19:10:08 +01:00
										 |  |  | 	// We'll select statuses 50 at a time so we don't wreck the db, | 
					
						
							|  |  |  | 	// and pass them through to the client api worker to handle. | 
					
						
							|  |  |  | 	// | 
					
						
							|  |  |  | 	// Deleting the statuses in this way also handles deleting the | 
					
						
							|  |  |  | 	// account's media attachments, mentions, and polls, since these | 
					
						
							|  |  |  | 	// are all attached to statuses. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var ( | 
					
						
							|  |  |  | 		statuses []*gtsmodel.Status | 
					
						
							|  |  |  | 		err      error | 
					
						
							|  |  |  | 		maxID    string | 
					
						
							|  |  |  | 		msgs     = []messages.FromClientAPI{} | 
					
						
							|  |  |  | 	) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | statusLoop: | 
					
						
							| 
									
										
										
										
											2021-07-05 13:23:03 +02:00
										 |  |  | 	for { | 
					
						
							| 
									
										
										
										
											2023-03-20 19:10:08 +01:00
										 |  |  | 		// Page through account's statuses. | 
					
						
							| 
									
										
										
										
											2023-08-10 15:08:41 +01:00
										 |  |  | 		statuses, err = p.state.DB.GetAccountStatuses( | 
					
						
							|  |  |  | 			ctx, | 
					
						
							|  |  |  | 			account.ID, | 
					
						
							|  |  |  | 			deleteSelectLimit, | 
					
						
							|  |  |  | 			false, | 
					
						
							|  |  |  | 			false, | 
					
						
							|  |  |  | 			maxID, | 
					
						
							|  |  |  | 			"", | 
					
						
							|  |  |  | 			false, | 
					
						
							|  |  |  | 			false, | 
					
						
							|  |  |  | 		) | 
					
						
							| 
									
										
										
										
											2023-03-20 19:10:08 +01:00
										 |  |  | 		if err != nil && !errors.Is(err, db.ErrNoEntries) { | 
					
						
							|  |  |  | 			// Make sure we don't have a real error. | 
					
						
							|  |  |  | 			return err | 
					
						
							| 
									
										
										
										
											2021-07-05 13:23:03 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-08 10:33:03 +00:00
										 |  |  | 		if len(statuses) == 0 { | 
					
						
							| 
									
										
										
										
											2023-03-20 19:10:08 +01:00
										 |  |  | 			break statusLoop | 
					
						
							| 
									
										
										
										
											2023-01-08 10:33:03 +00:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-20 19:10:08 +01:00
										 |  |  | 		// Update next maxID from last status. | 
					
						
							|  |  |  | 		maxID = statuses[len(statuses)-1].ID | 
					
						
							| 
									
										
										
										
											2022-07-10 16:18:21 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-20 19:10:08 +01:00
										 |  |  | 		for _, status := range statuses { | 
					
						
							| 
									
										
										
										
											2023-11-14 15:57:25 +01:00
										 |  |  | 			// Ensure account is set. | 
					
						
							|  |  |  | 			status.Account = account | 
					
						
							| 
									
										
										
										
											2021-07-05 13:23:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-20 19:10:08 +01:00
										 |  |  | 			// Look for any boosts of this status in DB. | 
					
						
							| 
									
										
										
										
											2023-11-14 15:57:25 +01:00
										 |  |  | 			// | 
					
						
							|  |  |  | 			// We put these in the msgs slice first so | 
					
						
							|  |  |  | 			// that they're handled first, before the | 
					
						
							|  |  |  | 			// parent status that's being boosted. | 
					
						
							|  |  |  | 			// | 
					
						
							|  |  |  | 			// Use a barebones context and just select the | 
					
						
							|  |  |  | 			// origin account separately. The rest will be | 
					
						
							|  |  |  | 			// populated later anyway, and we don't want to | 
					
						
							|  |  |  | 			// stop now because we couldn't get something. | 
					
						
							|  |  |  | 			boosts, err := p.state.DB.GetStatusBoosts( | 
					
						
							|  |  |  | 				gtscontext.SetBarebones(ctx), | 
					
						
							|  |  |  | 				status.ID, | 
					
						
							|  |  |  | 			) | 
					
						
							| 
									
										
										
										
											2022-11-20 14:57:19 +00:00
										 |  |  | 			if err != nil && !errors.Is(err, db.ErrNoEntries) { | 
					
						
							| 
									
										
										
										
											2023-11-14 15:57:25 +01:00
										 |  |  | 				return gtserror.Newf("error fetching status boosts for %s: %w", status.ID, err) | 
					
						
							| 
									
										
										
										
											2022-11-20 14:57:19 +00:00
										 |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-14 15:57:25 +01:00
										 |  |  | 			// Prepare to Undo each boost. | 
					
						
							| 
									
										
										
										
											2022-11-20 14:57:19 +00:00
										 |  |  | 			for _, boost := range boosts { | 
					
						
							| 
									
										
										
										
											2023-11-14 15:57:25 +01:00
										 |  |  | 				boost.Account, err = p.state.DB.GetAccountByID( | 
					
						
							|  |  |  | 					gtscontext.SetBarebones(ctx), | 
					
						
							|  |  |  | 					boost.AccountID, | 
					
						
							|  |  |  | 				) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				if err != nil { | 
					
						
							|  |  |  | 					log.Warnf( | 
					
						
							|  |  |  | 						ctx, | 
					
						
							|  |  |  | 						"db error getting owner %s of status boost %s: %v", | 
					
						
							|  |  |  | 						boost.AccountID, boost.ID, err, | 
					
						
							|  |  |  | 					) | 
					
						
							|  |  |  | 					continue | 
					
						
							| 
									
										
										
										
											2021-07-05 13:23:03 +02:00
										 |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-20 19:10:08 +01:00
										 |  |  | 				msgs = append(msgs, messages.FromClientAPI{ | 
					
						
							| 
									
										
										
										
											2022-11-20 14:57:19 +00:00
										 |  |  | 					APObjectType:   ap.ActivityAnnounce, | 
					
						
							|  |  |  | 					APActivityType: ap.ActivityUndo, | 
					
						
							|  |  |  | 					GTSModel:       status, | 
					
						
							|  |  |  | 					OriginAccount:  boost.Account, | 
					
						
							|  |  |  | 					TargetAccount:  account, | 
					
						
							|  |  |  | 				}) | 
					
						
							| 
									
										
										
										
											2021-07-05 13:23:03 +02:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2023-11-14 15:57:25 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			// Now prepare to Delete status. | 
					
						
							|  |  |  | 			msgs = append(msgs, messages.FromClientAPI{ | 
					
						
							|  |  |  | 				APObjectType:   ap.ObjectNote, | 
					
						
							|  |  |  | 				APActivityType: ap.ActivityDelete, | 
					
						
							|  |  |  | 				GTSModel:       status, | 
					
						
							|  |  |  | 				OriginAccount:  account, | 
					
						
							|  |  |  | 				TargetAccount:  account, | 
					
						
							|  |  |  | 			}) | 
					
						
							| 
									
										
										
										
											2021-07-05 13:23:03 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-14 15:57:25 +01:00
										 |  |  | 	// Process accreted messages in serial. | 
					
						
							|  |  |  | 	for _, msg := range msgs { | 
					
						
							|  |  |  | 		if err := p.state.Workers.ProcessFromClientAPI(ctx, msg); err != nil { | 
					
						
							|  |  |  | 			log.Errorf( | 
					
						
							|  |  |  | 				ctx, | 
					
						
							|  |  |  | 				"error processing %s of %s during Delete of account %s: %v", | 
					
						
							|  |  |  | 				msg.APActivityType, msg.APObjectType, account.ID, err, | 
					
						
							|  |  |  | 			) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-07-05 13:23:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-20 19:10:08 +01:00
										 |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2021-09-30 10:56:02 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-20 19:10:08 +01:00
										 |  |  | func (p *Processor) deleteAccountNotifications(ctx context.Context, account *gtsmodel.Account) error { | 
					
						
							| 
									
										
											  
											
												[performance] refactoring + add fave / follow / request / visibility caching (#1607)
* refactor visibility checking, add caching for visibility
* invalidate visibility cache items on account / status deletes
* fix requester ID passed to visibility cache nil ptr
* de-interface caches, fix home / public timeline caching + visibility
* finish adding code comments for visibility filter
* fix angry goconst linter warnings
* actually finish adding filter visibility code comments for timeline functions
* move home timeline status author check to after visibility
* remove now-unused code
* add more code comments
* add TODO code comment, update printed cache start names
* update printed cache names on stop
* start adding separate follow(request) delete db functions, add specific visibility cache tests
* add relationship type caching
* fix getting local account follows / followed-bys, other small codebase improvements
* simplify invalidation using cache hooks, add more GetAccountBy___() functions
* fix boosting to return 404 if not boostable but no error (to not leak status ID)
* remove dead code
* improved placement of cache invalidation
* update license headers
* add example follow, follow-request config entries
* add example visibility cache configuration to config file
* use specific PutFollowRequest() instead of just Put()
* add tests for all GetAccountBy()
* add GetBlockBy() tests
* update block to check primitive fields
* update and finish adding Get{Account,Block,Follow,FollowRequest}By() tests
* fix copy-pasted code
* update envparsing test
* whitespace
* fix bun struct tag
* add license header to gtscontext
* fix old license header
* improved error creation to not use fmt.Errorf() when not needed
* fix various rebase conflicts, fix account test
* remove commented-out code, fix-up mention caching
* fix mention select bun statement
* ensure mention target account populated, pass in context to customrenderer logging
* remove more uncommented code, fix typeutil test
* add statusfave database model caching
* add status fave cache configuration
* add status fave cache example config
* woops, catch missed error. nice catch linter!
* add back testrig panic on nil db
* update example configuration to match defaults, slight tweak to cache configuration defaults
* update envparsing test with new defaults
* fetch followingget to use the follow target account
* use accounnt.IsLocal() instead of empty domain check
* use constants for the cache visibility type check
* use bun.In() for notification type restriction in db query
* include replies when fetching PublicTimeline() (to account for single-author threads in Visibility{}.StatusPublicTimelineable())
* use bun query building for nested select statements to ensure working with postgres
* update public timeline future status checks to match visibility filter
* same as previous, for home timeline
* update public timeline tests to dynamically check for appropriate statuses
* migrate accounts to allow unique constraint on public_key
* provide minimal account with publicKey
---------
Signed-off-by: kim <grufwub@gmail.com>
Co-authored-by: tsmethurst <tobi.smethurst@protonmail.com>
											
										 
											2023-03-28 14:03:14 +01:00
										 |  |  | 	// Delete all notifications of all types targeting given account. | 
					
						
							|  |  |  | 	if err := p.state.DB.DeleteNotifications(ctx, nil, account.ID, ""); err != nil && !errors.Is(err, db.ErrNoEntries) { | 
					
						
							| 
									
										
										
										
											2023-11-08 14:32:17 +00:00
										 |  |  | 		return gtserror.Newf("error deleting notifications targeting account: %w", err) | 
					
						
							| 
									
										
										
										
											2021-07-05 13:23:03 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
											  
											
												[performance] refactoring + add fave / follow / request / visibility caching (#1607)
* refactor visibility checking, add caching for visibility
* invalidate visibility cache items on account / status deletes
* fix requester ID passed to visibility cache nil ptr
* de-interface caches, fix home / public timeline caching + visibility
* finish adding code comments for visibility filter
* fix angry goconst linter warnings
* actually finish adding filter visibility code comments for timeline functions
* move home timeline status author check to after visibility
* remove now-unused code
* add more code comments
* add TODO code comment, update printed cache start names
* update printed cache names on stop
* start adding separate follow(request) delete db functions, add specific visibility cache tests
* add relationship type caching
* fix getting local account follows / followed-bys, other small codebase improvements
* simplify invalidation using cache hooks, add more GetAccountBy___() functions
* fix boosting to return 404 if not boostable but no error (to not leak status ID)
* remove dead code
* improved placement of cache invalidation
* update license headers
* add example follow, follow-request config entries
* add example visibility cache configuration to config file
* use specific PutFollowRequest() instead of just Put()
* add tests for all GetAccountBy()
* add GetBlockBy() tests
* update block to check primitive fields
* update and finish adding Get{Account,Block,Follow,FollowRequest}By() tests
* fix copy-pasted code
* update envparsing test
* whitespace
* fix bun struct tag
* add license header to gtscontext
* fix old license header
* improved error creation to not use fmt.Errorf() when not needed
* fix various rebase conflicts, fix account test
* remove commented-out code, fix-up mention caching
* fix mention select bun statement
* ensure mention target account populated, pass in context to customrenderer logging
* remove more uncommented code, fix typeutil test
* add statusfave database model caching
* add status fave cache configuration
* add status fave cache example config
* woops, catch missed error. nice catch linter!
* add back testrig panic on nil db
* update example configuration to match defaults, slight tweak to cache configuration defaults
* update envparsing test with new defaults
* fetch followingget to use the follow target account
* use accounnt.IsLocal() instead of empty domain check
* use constants for the cache visibility type check
* use bun.In() for notification type restriction in db query
* include replies when fetching PublicTimeline() (to account for single-author threads in Visibility{}.StatusPublicTimelineable())
* use bun query building for nested select statements to ensure working with postgres
* update public timeline future status checks to match visibility filter
* same as previous, for home timeline
* update public timeline tests to dynamically check for appropriate statuses
* migrate accounts to allow unique constraint on public_key
* provide minimal account with publicKey
---------
Signed-off-by: kim <grufwub@gmail.com>
Co-authored-by: tsmethurst <tobi.smethurst@protonmail.com>
											
										 
											2023-03-28 14:03:14 +01:00
										 |  |  | 	// Delete all notifications of all types originating from given account. | 
					
						
							|  |  |  | 	if err := p.state.DB.DeleteNotifications(ctx, nil, "", account.ID); err != nil && !errors.Is(err, db.ErrNoEntries) { | 
					
						
							| 
									
										
										
										
											2023-11-08 14:32:17 +00:00
										 |  |  | 		return gtserror.Newf("error deleting notifications by account: %w", err) | 
					
						
							| 
									
										
										
										
											2021-07-05 13:23:03 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-20 19:10:08 +01:00
										 |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (p *Processor) deleteAccountPeripheral(ctx context.Context, account *gtsmodel.Account) error { | 
					
						
							|  |  |  | 	// Delete all bookmarks owned by given account. | 
					
						
							|  |  |  | 	if err := p.state.DB.DeleteStatusBookmarks(ctx, account.ID, ""); // nocollapse | 
					
						
							|  |  |  | 	err != nil && !errors.Is(err, db.ErrNoEntries) { | 
					
						
							| 
									
										
										
										
											2023-11-08 14:32:17 +00:00
										 |  |  | 		return gtserror.Newf("error deleting bookmarks by account: %w", err) | 
					
						
							| 
									
										
										
										
											2021-07-05 13:23:03 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-20 19:10:08 +01:00
										 |  |  | 	// Delete all bookmarks targeting given account. | 
					
						
							|  |  |  | 	if err := p.state.DB.DeleteStatusBookmarks(ctx, "", account.ID); // nocollapse | 
					
						
							|  |  |  | 	err != nil && !errors.Is(err, db.ErrNoEntries) { | 
					
						
							| 
									
										
										
										
											2023-11-08 14:32:17 +00:00
										 |  |  | 		return gtserror.Newf("error deleting bookmarks targeting account: %w", err) | 
					
						
							| 
									
										
										
										
											2023-03-20 19:10:08 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-07-05 13:23:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-20 19:10:08 +01:00
										 |  |  | 	// Delete all faves owned by given account. | 
					
						
							|  |  |  | 	if err := p.state.DB.DeleteStatusFaves(ctx, account.ID, ""); // nocollapse | 
					
						
							|  |  |  | 	err != nil && !errors.Is(err, db.ErrNoEntries) { | 
					
						
							| 
									
										
										
										
											2023-11-08 14:32:17 +00:00
										 |  |  | 		return gtserror.Newf("error deleting faves by account: %w", err) | 
					
						
							| 
									
										
										
										
											2023-03-20 19:10:08 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-07-05 13:23:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-20 19:10:08 +01:00
										 |  |  | 	// Delete all faves targeting given account. | 
					
						
							|  |  |  | 	if err := p.state.DB.DeleteStatusFaves(ctx, "", account.ID); // nocollapse | 
					
						
							|  |  |  | 	err != nil && !errors.Is(err, db.ErrNoEntries) { | 
					
						
							| 
									
										
										
										
											2023-11-08 14:32:17 +00:00
										 |  |  | 		return gtserror.Newf("error deleting faves targeting account: %w", err) | 
					
						
							| 
									
										
										
										
											2021-07-05 13:23:03 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-20 19:10:08 +01:00
										 |  |  | 	// TODO: add status mutes here when they're implemented. | 
					
						
							| 
									
										
										
										
											2021-07-05 13:23:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-08 14:32:17 +00:00
										 |  |  | 	// Delete all poll votes owned by given account. | 
					
						
							|  |  |  | 	if err := p.state.DB.DeletePollVotesByAccountID(ctx, account.ID); // nocollapse | 
					
						
							|  |  |  | 	err != nil && !errors.Is(err, db.ErrNoEntries) { | 
					
						
							|  |  |  | 		return gtserror.Newf("error deleting poll votes by account: %w", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-20 19:10:08 +01:00
										 |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // stubbifyAccount renders the given account as a stub, | 
					
						
							|  |  |  | // removing most information from it and marking it as | 
					
						
							|  |  |  | // suspended. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // The origin parameter refers to the origin of the | 
					
						
							|  |  |  | // suspension action; should be an account ID or domain | 
					
						
							|  |  |  | // block ID. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // For caller's convenience, this function returns the db | 
					
						
							|  |  |  | // names of all columns that are updated by it. | 
					
						
							|  |  |  | func stubbifyAccount(account *gtsmodel.Account, origin string) []string { | 
					
						
							|  |  |  | 	var ( | 
					
						
							| 
									
										
										
										
											2023-08-07 19:38:11 +02:00
										 |  |  | 		now   = time.Now() | 
					
						
							|  |  |  | 		never = time.Time{} | 
					
						
							| 
									
										
										
										
											2023-03-20 19:10:08 +01:00
										 |  |  | 	) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	account.FetchedAt = never | 
					
						
							| 
									
										
										
										
											2021-07-05 13:23:03 +02:00
										 |  |  | 	account.AvatarMediaAttachmentID = "" | 
					
						
							|  |  |  | 	account.AvatarRemoteURL = "" | 
					
						
							|  |  |  | 	account.HeaderMediaAttachmentID = "" | 
					
						
							|  |  |  | 	account.HeaderRemoteURL = "" | 
					
						
							| 
									
										
										
										
											2023-03-20 19:10:08 +01:00
										 |  |  | 	account.DisplayName = "" | 
					
						
							|  |  |  | 	account.EmojiIDs = nil | 
					
						
							|  |  |  | 	account.Emojis = nil | 
					
						
							|  |  |  | 	account.Fields = nil | 
					
						
							|  |  |  | 	account.Note = "" | 
					
						
							|  |  |  | 	account.NoteRaw = "" | 
					
						
							| 
									
										
										
										
											2023-08-07 19:38:11 +02:00
										 |  |  | 	account.Memorial = util.Ptr(false) | 
					
						
							| 
									
										
										
										
											2024-01-16 17:22:44 +01:00
										 |  |  | 	account.AlsoKnownAsURIs = nil | 
					
						
							|  |  |  | 	account.MovedToURI = "" | 
					
						
							| 
									
										
										
										
											2023-08-07 19:38:11 +02:00
										 |  |  | 	account.Discoverable = util.Ptr(false) | 
					
						
							| 
									
										
										
										
											2023-03-20 19:10:08 +01:00
										 |  |  | 	account.SuspendedAt = now | 
					
						
							| 
									
										
										
										
											2021-07-06 13:29:11 +02:00
										 |  |  | 	account.SuspensionOrigin = origin | 
					
						
							| 
									
										
										
										
											2023-03-20 19:10:08 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return []string{ | 
					
						
							|  |  |  | 		"fetched_at", | 
					
						
							|  |  |  | 		"avatar_media_attachment_id", | 
					
						
							|  |  |  | 		"avatar_remote_url", | 
					
						
							|  |  |  | 		"header_media_attachment_id", | 
					
						
							|  |  |  | 		"header_remote_url", | 
					
						
							|  |  |  | 		"display_name", | 
					
						
							|  |  |  | 		"emojis", | 
					
						
							|  |  |  | 		"fields", | 
					
						
							|  |  |  | 		"note", | 
					
						
							|  |  |  | 		"note_raw", | 
					
						
							|  |  |  | 		"memorial", | 
					
						
							| 
									
										
										
										
											2024-01-16 17:22:44 +01:00
										 |  |  | 		"also_known_as_uris", | 
					
						
							|  |  |  | 		"moved_to_uri", | 
					
						
							| 
									
										
										
										
											2023-03-20 19:10:08 +01:00
										 |  |  | 		"discoverable", | 
					
						
							|  |  |  | 		"suspended_at", | 
					
						
							|  |  |  | 		"suspension_origin", | 
					
						
							| 
									
										
										
										
											2022-03-15 16:12:35 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2023-03-31 15:01:29 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | // stubbifyUser renders the given user as a stub, | 
					
						
							|  |  |  | // removing sensitive information like IP addresses | 
					
						
							|  |  |  | // and sign-in times, but keeping email addresses to | 
					
						
							|  |  |  | // prevent the same email address from creating another | 
					
						
							|  |  |  | // account on this instance. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // `encrypted_password` is set to the bcrypt hash of a | 
					
						
							|  |  |  | // random uuid, so if the action is reversed, the user | 
					
						
							|  |  |  | // will have to reset their password via email. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // For caller's convenience, this function returns the db | 
					
						
							|  |  |  | // names of all columns that are updated by it. | 
					
						
							|  |  |  | func stubbifyUser(user *gtsmodel.User) ([]string, error) { | 
					
						
							|  |  |  | 	uuid, err := uuid.New().MarshalBinary() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	dummyPassword, err := bcrypt.GenerateFromPassword(uuid, bcrypt.DefaultCost) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-31 11:25:29 +01:00
										 |  |  | 	never := time.Time{} | 
					
						
							| 
									
										
										
										
											2023-03-31 15:01:29 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	user.EncryptedPassword = string(dummyPassword) | 
					
						
							|  |  |  | 	user.SignUpIP = net.IPv4zero | 
					
						
							|  |  |  | 	user.Locale = "" | 
					
						
							|  |  |  | 	user.CreatedByApplicationID = "" | 
					
						
							|  |  |  | 	user.LastEmailedAt = never | 
					
						
							|  |  |  | 	user.ConfirmationToken = "" | 
					
						
							|  |  |  | 	user.ConfirmationSentAt = never | 
					
						
							|  |  |  | 	user.ResetPasswordToken = "" | 
					
						
							|  |  |  | 	user.ResetPasswordSentAt = never | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return []string{ | 
					
						
							|  |  |  | 		"encrypted_password", | 
					
						
							|  |  |  | 		"sign_up_ip", | 
					
						
							|  |  |  | 		"locale", | 
					
						
							|  |  |  | 		"created_by_application_id", | 
					
						
							|  |  |  | 		"last_emailed_at", | 
					
						
							|  |  |  | 		"confirmation_token", | 
					
						
							|  |  |  | 		"confirmation_sent_at", | 
					
						
							|  |  |  | 		"reset_password_token", | 
					
						
							|  |  |  | 		"reset_password_sent_at", | 
					
						
							|  |  |  | 	}, nil | 
					
						
							|  |  |  | } |