mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-10-31 14:02:25 -05:00 
			
		
		
		
	[chore] Move local account settings to separate db table (#2770)
* [chore] Move local account settings to separate database model * don't use separate settings_id
This commit is contained in:
		
					parent
					
						
							
								0767647056
							
						
					
				
			
			
				commit
				
					
						7f4a0a1aeb
					
				
			
		
					 36 changed files with 525 additions and 191 deletions
				
			
		|  | @ -395,12 +395,8 @@ func (suite *InboxPostTestSuite) TestPostUpdate() { | ||||||
| 	suite.EqualValues(requestingAccount.AlsoKnownAsURIs, dbUpdatedAccount.AlsoKnownAsURIs) | 	suite.EqualValues(requestingAccount.AlsoKnownAsURIs, dbUpdatedAccount.AlsoKnownAsURIs) | ||||||
| 	suite.EqualValues(requestingAccount.MovedToURI, dbUpdatedAccount.MovedToURI) | 	suite.EqualValues(requestingAccount.MovedToURI, dbUpdatedAccount.MovedToURI) | ||||||
| 	suite.EqualValues(requestingAccount.Bot, dbUpdatedAccount.Bot) | 	suite.EqualValues(requestingAccount.Bot, dbUpdatedAccount.Bot) | ||||||
| 	suite.EqualValues(requestingAccount.Reason, dbUpdatedAccount.Reason) |  | ||||||
| 	suite.EqualValues(requestingAccount.Locked, dbUpdatedAccount.Locked) | 	suite.EqualValues(requestingAccount.Locked, dbUpdatedAccount.Locked) | ||||||
| 	suite.EqualValues(requestingAccount.Discoverable, dbUpdatedAccount.Discoverable) | 	suite.EqualValues(requestingAccount.Discoverable, dbUpdatedAccount.Discoverable) | ||||||
| 	suite.EqualValues(requestingAccount.Privacy, dbUpdatedAccount.Privacy) |  | ||||||
| 	suite.EqualValues(requestingAccount.Sensitive, dbUpdatedAccount.Sensitive) |  | ||||||
| 	suite.EqualValues(requestingAccount.Language, dbUpdatedAccount.Language) |  | ||||||
| 	suite.EqualValues(requestingAccount.URI, dbUpdatedAccount.URI) | 	suite.EqualValues(requestingAccount.URI, dbUpdatedAccount.URI) | ||||||
| 	suite.EqualValues(requestingAccount.URL, dbUpdatedAccount.URL) | 	suite.EqualValues(requestingAccount.URL, dbUpdatedAccount.URL) | ||||||
| 	suite.EqualValues(requestingAccount.InboxURI, dbUpdatedAccount.InboxURI) | 	suite.EqualValues(requestingAccount.InboxURI, dbUpdatedAccount.InboxURI) | ||||||
|  | @ -414,7 +410,6 @@ func (suite *InboxPostTestSuite) TestPostUpdate() { | ||||||
| 	suite.EqualValues(requestingAccount.SensitizedAt, dbUpdatedAccount.SensitizedAt) | 	suite.EqualValues(requestingAccount.SensitizedAt, dbUpdatedAccount.SensitizedAt) | ||||||
| 	suite.EqualValues(requestingAccount.SilencedAt, dbUpdatedAccount.SilencedAt) | 	suite.EqualValues(requestingAccount.SilencedAt, dbUpdatedAccount.SilencedAt) | ||||||
| 	suite.EqualValues(requestingAccount.SuspendedAt, dbUpdatedAccount.SuspendedAt) | 	suite.EqualValues(requestingAccount.SuspendedAt, dbUpdatedAccount.SuspendedAt) | ||||||
| 	suite.EqualValues(requestingAccount.HideCollections, dbUpdatedAccount.HideCollections) |  | ||||||
| 	suite.EqualValues(requestingAccount.SuspensionOrigin, dbUpdatedAccount.SuspensionOrigin) | 	suite.EqualValues(requestingAccount.SuspensionOrigin, dbUpdatedAccount.SuspensionOrigin) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -464,9 +459,7 @@ func (suite *InboxPostTestSuite) TestPostDelete() { | ||||||
| 	suite.Empty(dbAccount.AvatarRemoteURL) | 	suite.Empty(dbAccount.AvatarRemoteURL) | ||||||
| 	suite.Empty(dbAccount.HeaderMediaAttachmentID) | 	suite.Empty(dbAccount.HeaderMediaAttachmentID) | ||||||
| 	suite.Empty(dbAccount.HeaderRemoteURL) | 	suite.Empty(dbAccount.HeaderRemoteURL) | ||||||
| 	suite.Empty(dbAccount.Reason) |  | ||||||
| 	suite.Empty(dbAccount.Fields) | 	suite.Empty(dbAccount.Fields) | ||||||
| 	suite.True(*dbAccount.HideCollections) |  | ||||||
| 	suite.False(*dbAccount.Discoverable) | 	suite.False(*dbAccount.Discoverable) | ||||||
| 	suite.WithinDuration(time.Now(), dbAccount.SuspendedAt, 30*time.Second) | 	suite.WithinDuration(time.Now(), dbAccount.SuspendedAt, 30*time.Second) | ||||||
| 	suite.Equal(dbAccount.ID, dbAccount.SuspensionOrigin) | 	suite.Equal(dbAccount.ID, dbAccount.SuspensionOrigin) | ||||||
|  |  | ||||||
|  | @ -481,7 +481,7 @@ func (suite *AccountUpdateTestSuite) TestUpdateAccountSourceBadContentTypeFormDa | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		suite.FailNow(err.Error()) | 		suite.FailNow(err.Error()) | ||||||
| 	} | 	} | ||||||
| 	suite.Equal(data["source[status_content_type]"][0], dbAccount.StatusContentType) | 	suite.Equal(data["source[status_content_type]"][0], dbAccount.Settings.StatusContentType) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (suite *AccountUpdateTestSuite) TestAccountUpdateCredentialsPATCHHandlerUpdateStatusContentTypeBad() { | func (suite *AccountUpdateTestSuite) TestAccountUpdateCredentialsPATCHHandlerUpdateStatusContentTypeBad() { | ||||||
|  |  | ||||||
|  | @ -81,7 +81,7 @@ func (suite *AccountVerifyTestSuite) TestAccountVerifyGet() { | ||||||
| 	suite.Equal(2, apimodelAccount.FollowingCount) | 	suite.Equal(2, apimodelAccount.FollowingCount) | ||||||
| 	suite.Equal(7, apimodelAccount.StatusesCount) | 	suite.Equal(7, apimodelAccount.StatusesCount) | ||||||
| 	suite.EqualValues(gtsmodel.VisibilityPublic, apimodelAccount.Source.Privacy) | 	suite.EqualValues(gtsmodel.VisibilityPublic, apimodelAccount.Source.Privacy) | ||||||
| 	suite.Equal(testAccount.Language, apimodelAccount.Source.Language) | 	suite.Equal(testAccount.Settings.Language, apimodelAccount.Source.Language) | ||||||
| 	suite.Equal(testAccount.NoteRaw, apimodelAccount.Source.Note) | 	suite.Equal(testAccount.NoteRaw, apimodelAccount.Source.Note) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -103,16 +103,22 @@ func (suite *StatusCreateTestSuite) TestPostNewStatus() { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (suite *StatusCreateTestSuite) TestPostNewStatusMarkdown() { | func (suite *StatusCreateTestSuite) TestPostNewStatusMarkdown() { | ||||||
| 	// set default post language of account 1 to markdown | 	// Copy zork. | ||||||
| 	testAccount := suite.testAccounts["local_account_1"] | 	testAccount := >smodel.Account{} | ||||||
| 	testAccount.StatusContentType = "text/markdown" | 	*testAccount = *suite.testAccounts["local_account_1"] | ||||||
| 	a := testAccount |  | ||||||
| 
 | 
 | ||||||
| 	err := suite.db.UpdateAccount(context.Background(), a) | 	// Copy zork's settings. | ||||||
|  | 	settings := >smodel.AccountSettings{} | ||||||
|  | 	*settings = *suite.testAccounts["local_account_1"].Settings | ||||||
|  | 	testAccount.Settings = settings | ||||||
|  | 
 | ||||||
|  | 	// set default post language of zork to markdown | ||||||
|  | 	testAccount.Settings.StatusContentType = "text/markdown" | ||||||
|  | 	err := suite.db.UpdateAccountSettings(context.Background(), testAccount.Settings) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		suite.FailNow(err.Error()) | 		suite.FailNow(err.Error()) | ||||||
| 	} | 	} | ||||||
| 	suite.Equal(a.StatusContentType, "text/markdown") | 	suite.Equal(testAccount.Settings.StatusContentType, "text/markdown") | ||||||
| 
 | 
 | ||||||
| 	t := suite.testTokens["local_account_1"] | 	t := suite.testTokens["local_account_1"] | ||||||
| 	oauthToken := oauth.DBTokenToToken(t) | 	oauthToken := oauth.DBTokenToToken(t) | ||||||
|  | @ -122,7 +128,7 @@ func (suite *StatusCreateTestSuite) TestPostNewStatusMarkdown() { | ||||||
| 	ctx.Set(oauth.SessionAuthorizedApplication, suite.testApplications["application_1"]) | 	ctx.Set(oauth.SessionAuthorizedApplication, suite.testApplications["application_1"]) | ||||||
| 	ctx.Set(oauth.SessionAuthorizedToken, oauthToken) | 	ctx.Set(oauth.SessionAuthorizedToken, oauthToken) | ||||||
| 	ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"]) | 	ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"]) | ||||||
| 	ctx.Set(oauth.SessionAuthorizedAccount, a) | 	ctx.Set(oauth.SessionAuthorizedAccount, testAccount) | ||||||
| 
 | 
 | ||||||
| 	ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080/%s", statuses.BasePath), nil) | 	ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080/%s", statuses.BasePath), nil) | ||||||
| 	ctx.Request.Header.Set("accept", "application/json") | 	ctx.Request.Header.Set("accept", "application/json") | ||||||
|  |  | ||||||
|  | @ -100,7 +100,6 @@ func (suite *WebfingerGetTestSuite) funkifyAccountDomain(host string, accountDom | ||||||
| 	targetAccount := >smodel.Account{ | 	targetAccount := >smodel.Account{ | ||||||
| 		ID:                    "01FG1K8EA7SYHEC7V6XKVNC4ZA", | 		ID:                    "01FG1K8EA7SYHEC7V6XKVNC4ZA", | ||||||
| 		Username:              "new_account_domain_user", | 		Username:              "new_account_domain_user", | ||||||
| 		Privacy:               gtsmodel.VisibilityDefault, |  | ||||||
| 		URI:                   "http://" + host + "/users/new_account_domain_user", | 		URI:                   "http://" + host + "/users/new_account_domain_user", | ||||||
| 		URL:                   "http://" + host + "/@new_account_domain_user", | 		URL:                   "http://" + host + "/@new_account_domain_user", | ||||||
| 		InboxURI:              "http://" + host + "/users/new_account_domain_user/inbox", | 		InboxURI:              "http://" + host + "/users/new_account_domain_user/inbox", | ||||||
|  | @ -118,6 +117,10 @@ func (suite *WebfingerGetTestSuite) funkifyAccountDomain(host string, accountDom | ||||||
| 		suite.FailNow(err.Error()) | 		suite.FailNow(err.Error()) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	if err := suite.db.PutAccountSettings(context.Background(), >smodel.AccountSettings{AccountID: targetAccount.ID}); err != nil { | ||||||
|  | 		suite.FailNow(err.Error()) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	return targetAccount | 	return targetAccount | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										2
									
								
								internal/cache/cache.go
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								internal/cache/cache.go
									
										
									
									
										vendored
									
									
								
							|  | @ -53,6 +53,7 @@ func (c *Caches) Init() { | ||||||
| 	c.initAccount() | 	c.initAccount() | ||||||
| 	c.initAccountCounts() | 	c.initAccountCounts() | ||||||
| 	c.initAccountNote() | 	c.initAccountNote() | ||||||
|  | 	c.initAccountSettings() | ||||||
| 	c.initApplication() | 	c.initApplication() | ||||||
| 	c.initBlock() | 	c.initBlock() | ||||||
| 	c.initBlockIDs() | 	c.initBlockIDs() | ||||||
|  | @ -119,6 +120,7 @@ func (c *Caches) Stop() { | ||||||
| func (c *Caches) Sweep(threshold float64) { | func (c *Caches) Sweep(threshold float64) { | ||||||
| 	c.GTS.Account.Trim(threshold) | 	c.GTS.Account.Trim(threshold) | ||||||
| 	c.GTS.AccountNote.Trim(threshold) | 	c.GTS.AccountNote.Trim(threshold) | ||||||
|  | 	c.GTS.AccountSettings.Trim(threshold) | ||||||
| 	c.GTS.Block.Trim(threshold) | 	c.GTS.Block.Trim(threshold) | ||||||
| 	c.GTS.BlockIDs.Trim(threshold) | 	c.GTS.BlockIDs.Trim(threshold) | ||||||
| 	c.GTS.Emoji.Trim(threshold) | 	c.GTS.Emoji.Trim(threshold) | ||||||
|  |  | ||||||
							
								
								
									
										27
									
								
								internal/cache/db.go
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										27
									
								
								internal/cache/db.go
									
										
									
									
										vendored
									
									
								
							|  | @ -43,6 +43,9 @@ type GTSCaches struct { | ||||||
| 		Pinned   int | 		Pinned   int | ||||||
| 	}] | 	}] | ||||||
| 
 | 
 | ||||||
|  | 	// AccountSettings provides access to the gtsmodel AccountSettings database cache. | ||||||
|  | 	AccountSettings structr.Cache[*gtsmodel.AccountSettings] | ||||||
|  | 
 | ||||||
| 	// Application provides access to the gtsmodel Application database cache. | 	// Application provides access to the gtsmodel Application database cache. | ||||||
| 	Application structr.Cache[*gtsmodel.Application] | 	Application structr.Cache[*gtsmodel.Application] | ||||||
| 
 | 
 | ||||||
|  | @ -190,6 +193,7 @@ func (c *Caches) initAccount() { | ||||||
| 		a2.Emojis = nil | 		a2.Emojis = nil | ||||||
| 		a2.AlsoKnownAs = nil | 		a2.AlsoKnownAs = nil | ||||||
| 		a2.Move = nil | 		a2.Move = nil | ||||||
|  | 		a2.Settings = nil | ||||||
| 
 | 
 | ||||||
| 		return a2 | 		return a2 | ||||||
| 	} | 	} | ||||||
|  | @ -262,6 +266,29 @@ func (c *Caches) initAccountNote() { | ||||||
| 	}) | 	}) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func (c *Caches) initAccountSettings() { | ||||||
|  | 	// Calculate maximum cache size. | ||||||
|  | 	cap := calculateResultCacheMax( | ||||||
|  | 		sizeofAccountSettings(), // model in-mem size. | ||||||
|  | 		config.GetCacheAccountSettingsMemRatio(), | ||||||
|  | 	) | ||||||
|  | 
 | ||||||
|  | 	log.Infof(nil, "cache size = %d", cap) | ||||||
|  | 
 | ||||||
|  | 	c.GTS.AccountSettings.Init(structr.Config[*gtsmodel.AccountSettings]{ | ||||||
|  | 		Indices: []structr.IndexConfig{ | ||||||
|  | 			{Fields: "AccountID"}, | ||||||
|  | 		}, | ||||||
|  | 		MaxSize:   cap, | ||||||
|  | 		IgnoreErr: ignoreErrors, | ||||||
|  | 		CopyValue: func(s1 *gtsmodel.AccountSettings) *gtsmodel.AccountSettings { | ||||||
|  | 			s2 := new(gtsmodel.AccountSettings) | ||||||
|  | 			*s2 = *s1 | ||||||
|  | 			return s2 | ||||||
|  | 		}, | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func (c *Caches) initApplication() { | func (c *Caches) initApplication() { | ||||||
| 	// Calculate maximum cache size. | 	// Calculate maximum cache size. | ||||||
| 	cap := calculateResultCacheMax( | 	cap := calculateResultCacheMax( | ||||||
|  |  | ||||||
							
								
								
									
										22
									
								
								internal/cache/size.go
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										22
									
								
								internal/cache/size.go
									
										
									
									
										vendored
									
									
								
							|  | @ -28,6 +28,7 @@ import ( | ||||||
| 	"github.com/superseriousbusiness/gotosocial/internal/config" | 	"github.com/superseriousbusiness/gotosocial/internal/config" | ||||||
| 	"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" | 	"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" | ||||||
| 	"github.com/superseriousbusiness/gotosocial/internal/id" | 	"github.com/superseriousbusiness/gotosocial/internal/id" | ||||||
|  | 	"github.com/superseriousbusiness/gotosocial/internal/util" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| const ( | const ( | ||||||
|  | @ -219,9 +220,6 @@ func sizeofAccount() uintptr { | ||||||
| 		Bot:                     func() *bool { ok := true; return &ok }(), | 		Bot:                     func() *bool { ok := true; return &ok }(), | ||||||
| 		Locked:                  func() *bool { ok := true; return &ok }(), | 		Locked:                  func() *bool { ok := true; return &ok }(), | ||||||
| 		Discoverable:            func() *bool { ok := false; return &ok }(), | 		Discoverable:            func() *bool { ok := false; return &ok }(), | ||||||
| 		Privacy:                 gtsmodel.VisibilityFollowersOnly, |  | ||||||
| 		Sensitive:               func() *bool { ok := true; return &ok }(), |  | ||||||
| 		Language:                "fr", |  | ||||||
| 		URI:                     exampleURI, | 		URI:                     exampleURI, | ||||||
| 		URL:                     exampleURI, | 		URL:                     exampleURI, | ||||||
| 		InboxURI:                exampleURI, | 		InboxURI:                exampleURI, | ||||||
|  | @ -236,9 +234,7 @@ func sizeofAccount() uintptr { | ||||||
| 		SensitizedAt:            exampleTime, | 		SensitizedAt:            exampleTime, | ||||||
| 		SilencedAt:              exampleTime, | 		SilencedAt:              exampleTime, | ||||||
| 		SuspendedAt:             exampleTime, | 		SuspendedAt:             exampleTime, | ||||||
| 		HideCollections:         func() *bool { ok := true; return &ok }(), |  | ||||||
| 		SuspensionOrigin:        exampleID, | 		SuspensionOrigin:        exampleID, | ||||||
| 		EnableRSS:               func() *bool { ok := true; return &ok }(), |  | ||||||
| 	})) | 	})) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -251,6 +247,22 @@ func sizeofAccountNote() uintptr { | ||||||
| 	})) | 	})) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func sizeofAccountSettings() uintptr { | ||||||
|  | 	return uintptr(size.Of(>smodel.AccountSettings{ | ||||||
|  | 		AccountID:         exampleID, | ||||||
|  | 		CreatedAt:         exampleTime, | ||||||
|  | 		UpdatedAt:         exampleTime, | ||||||
|  | 		Reason:            exampleText, | ||||||
|  | 		Privacy:           gtsmodel.VisibilityFollowersOnly, | ||||||
|  | 		Sensitive:         util.Ptr(true), | ||||||
|  | 		Language:          "fr", | ||||||
|  | 		StatusContentType: "text/plain", | ||||||
|  | 		CustomCSS:         exampleText, | ||||||
|  | 		EnableRSS:         util.Ptr(true), | ||||||
|  | 		HideCollections:   util.Ptr(false), | ||||||
|  | 	})) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func sizeofApplication() uintptr { | func sizeofApplication() uintptr { | ||||||
| 	return uintptr(size.Of(>smodel.Application{ | 	return uintptr(size.Of(>smodel.Application{ | ||||||
| 		ID:           exampleID, | 		ID:           exampleID, | ||||||
|  |  | ||||||
|  | @ -195,6 +195,7 @@ type CacheConfiguration struct { | ||||||
| 	MemoryTarget             bytesize.Size `name:"memory-target"` | 	MemoryTarget             bytesize.Size `name:"memory-target"` | ||||||
| 	AccountMemRatio          float64       `name:"account-mem-ratio"` | 	AccountMemRatio          float64       `name:"account-mem-ratio"` | ||||||
| 	AccountNoteMemRatio      float64       `name:"account-note-mem-ratio"` | 	AccountNoteMemRatio      float64       `name:"account-note-mem-ratio"` | ||||||
|  | 	AccountSettingsMemRatio  float64       `name:"account-settings-mem-ratio"` | ||||||
| 	ApplicationMemRatio      float64       `name:"application-mem-ratio"` | 	ApplicationMemRatio      float64       `name:"application-mem-ratio"` | ||||||
| 	BlockMemRatio            float64       `name:"block-mem-ratio"` | 	BlockMemRatio            float64       `name:"block-mem-ratio"` | ||||||
| 	BlockIDsMemRatio         float64       `name:"block-mem-ratio"` | 	BlockIDsMemRatio         float64       `name:"block-mem-ratio"` | ||||||
|  |  | ||||||
|  | @ -159,6 +159,7 @@ var Defaults = Configuration{ | ||||||
| 		// be able to make some more sense :D | 		// be able to make some more sense :D | ||||||
| 		AccountMemRatio:          5, | 		AccountMemRatio:          5, | ||||||
| 		AccountNoteMemRatio:      1, | 		AccountNoteMemRatio:      1, | ||||||
|  | 		AccountSettingsMemRatio:  0.1, | ||||||
| 		ApplicationMemRatio:      0.1, | 		ApplicationMemRatio:      0.1, | ||||||
| 		BlockMemRatio:            2, | 		BlockMemRatio:            2, | ||||||
| 		BlockIDsMemRatio:         3, | 		BlockIDsMemRatio:         3, | ||||||
|  |  | ||||||
|  | @ -2825,6 +2825,31 @@ func GetCacheAccountNoteMemRatio() float64 { return global.GetCacheAccountNoteMe | ||||||
| // SetCacheAccountNoteMemRatio safely sets the value for global configuration 'Cache.AccountNoteMemRatio' field | // SetCacheAccountNoteMemRatio safely sets the value for global configuration 'Cache.AccountNoteMemRatio' field | ||||||
| func SetCacheAccountNoteMemRatio(v float64) { global.SetCacheAccountNoteMemRatio(v) } | func SetCacheAccountNoteMemRatio(v float64) { global.SetCacheAccountNoteMemRatio(v) } | ||||||
| 
 | 
 | ||||||
|  | // GetCacheAccountSettingsMemRatio safely fetches the Configuration value for state's 'Cache.AccountSettingsMemRatio' field | ||||||
|  | func (st *ConfigState) GetCacheAccountSettingsMemRatio() (v float64) { | ||||||
|  | 	st.mutex.RLock() | ||||||
|  | 	v = st.config.Cache.AccountSettingsMemRatio | ||||||
|  | 	st.mutex.RUnlock() | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // SetCacheAccountSettingsMemRatio safely sets the Configuration value for state's 'Cache.AccountSettingsMemRatio' field | ||||||
|  | func (st *ConfigState) SetCacheAccountSettingsMemRatio(v float64) { | ||||||
|  | 	st.mutex.Lock() | ||||||
|  | 	defer st.mutex.Unlock() | ||||||
|  | 	st.config.Cache.AccountSettingsMemRatio = v | ||||||
|  | 	st.reloadToViper() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // CacheAccountSettingsMemRatioFlag returns the flag name for the 'Cache.AccountSettingsMemRatio' field | ||||||
|  | func CacheAccountSettingsMemRatioFlag() string { return "cache-account-settings-mem-ratio" } | ||||||
|  | 
 | ||||||
|  | // GetCacheAccountSettingsMemRatio safely fetches the value for global configuration 'Cache.AccountSettingsMemRatio' field | ||||||
|  | func GetCacheAccountSettingsMemRatio() float64 { return global.GetCacheAccountSettingsMemRatio() } | ||||||
|  | 
 | ||||||
|  | // SetCacheAccountSettingsMemRatio safely sets the value for global configuration 'Cache.AccountSettingsMemRatio' field | ||||||
|  | func SetCacheAccountSettingsMemRatio(v float64) { global.SetCacheAccountSettingsMemRatio(v) } | ||||||
|  | 
 | ||||||
| // GetCacheApplicationMemRatio safely fetches the Configuration value for state's 'Cache.ApplicationMemRatio' field | // GetCacheApplicationMemRatio safely fetches the Configuration value for state's 'Cache.ApplicationMemRatio' field | ||||||
| func (st *ConfigState) GetCacheApplicationMemRatio() (v float64) { | func (st *ConfigState) GetCacheApplicationMemRatio() (v float64) { | ||||||
| 	st.mutex.RLock() | 	st.mutex.RLock() | ||||||
|  |  | ||||||
|  | @ -117,4 +117,13 @@ type Account interface { | ||||||
| 	// GetInstanceAccount returns the instance account for the given domain. | 	// GetInstanceAccount returns the instance account for the given domain. | ||||||
| 	// If domain is empty, this instance account will be returned. | 	// If domain is empty, this instance account will be returned. | ||||||
| 	GetInstanceAccount(ctx context.Context, domain string) (*gtsmodel.Account, error) | 	GetInstanceAccount(ctx context.Context, domain string) (*gtsmodel.Account, error) | ||||||
|  | 
 | ||||||
|  | 	// Get local account settings with the given ID. | ||||||
|  | 	GetAccountSettings(ctx context.Context, id string) (*gtsmodel.AccountSettings, error) | ||||||
|  | 
 | ||||||
|  | 	// Store local account settings. | ||||||
|  | 	PutAccountSettings(ctx context.Context, settings *gtsmodel.AccountSettings) error | ||||||
|  | 
 | ||||||
|  | 	// Update local account settings. | ||||||
|  | 	UpdateAccountSettings(ctx context.Context, settings *gtsmodel.AccountSettings, columns ...string) error | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -338,6 +338,17 @@ func (a *accountDB) PopulateAccount(ctx context.Context, account *gtsmodel.Accou | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	if account.IsLocal() && account.Settings == nil && !account.IsInstance() { | ||||||
|  | 		// Account settings not set, fetch from db. | ||||||
|  | 		account.Settings, err = a.state.DB.GetAccountSettings( | ||||||
|  | 			ctx, // these are already barebones | ||||||
|  | 			account.ID, | ||||||
|  | 		) | ||||||
|  | 		if err != nil { | ||||||
|  | 			errs.Appendf("error populating account settings: %w", err) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	return errs.Combine() | 	return errs.Combine() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -504,12 +515,22 @@ func (a *accountDB) SetAccountHeaderOrAvatar(ctx context.Context, mediaAttachmen | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a *accountDB) GetAccountCustomCSSByUsername(ctx context.Context, username string) (string, error) { | func (a *accountDB) GetAccountCustomCSSByUsername(ctx context.Context, username string) (string, error) { | ||||||
|  | 	// Get local account. | ||||||
| 	account, err := a.GetAccountByUsernameDomain(ctx, username, "") | 	account, err := a.GetAccountByUsernameDomain(ctx, username, "") | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return "", err | 		return "", err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return account.CustomCSS, nil | 	// Ensure settings populated, in case | ||||||
|  | 	// barebones context was passed. | ||||||
|  | 	if account.Settings == nil { | ||||||
|  | 		account.Settings, err = a.GetAccountSettings(ctx, account.ID) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return "", err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return account.Settings.CustomCSS, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (a *accountDB) GetAccountsUsingEmoji(ctx context.Context, emojiID string) ([]*gtsmodel.Account, error) { | func (a *accountDB) GetAccountsUsingEmoji(ctx context.Context, emojiID string) ([]*gtsmodel.Account, error) { | ||||||
|  | @ -780,3 +801,68 @@ func (a *accountDB) GetAccountWebStatuses(ctx context.Context, accountID string, | ||||||
| 
 | 
 | ||||||
| 	return a.state.DB.GetStatusesByIDs(ctx, statusIDs) | 	return a.state.DB.GetStatusesByIDs(ctx, statusIDs) | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | func (a *accountDB) GetAccountSettings( | ||||||
|  | 	ctx context.Context, | ||||||
|  | 	accountID string, | ||||||
|  | ) (*gtsmodel.AccountSettings, error) { | ||||||
|  | 	// Fetch settings from db cache with loader callback. | ||||||
|  | 	return a.state.Caches.GTS.AccountSettings.LoadOne( | ||||||
|  | 		"AccountID", | ||||||
|  | 		func() (*gtsmodel.AccountSettings, error) { | ||||||
|  | 			// Not cached! Perform database query. | ||||||
|  | 			var settings gtsmodel.AccountSettings | ||||||
|  | 			if err := a.db. | ||||||
|  | 				NewSelect(). | ||||||
|  | 				Model(&settings). | ||||||
|  | 				Where("? = ?", bun.Ident("account_settings.account_id"), accountID). | ||||||
|  | 				Scan(ctx); err != nil { | ||||||
|  | 				return nil, err | ||||||
|  | 			} | ||||||
|  | 			return &settings, nil | ||||||
|  | 		}, | ||||||
|  | 		accountID, | ||||||
|  | 	) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (a *accountDB) PutAccountSettings( | ||||||
|  | 	ctx context.Context, | ||||||
|  | 	settings *gtsmodel.AccountSettings, | ||||||
|  | ) error { | ||||||
|  | 	return a.state.Caches.GTS.AccountSettings.Store(settings, func() error { | ||||||
|  | 		if _, err := a.db. | ||||||
|  | 			NewInsert(). | ||||||
|  | 			Model(settings). | ||||||
|  | 			Exec(ctx); err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		return nil | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (a *accountDB) UpdateAccountSettings( | ||||||
|  | 	ctx context.Context, | ||||||
|  | 	settings *gtsmodel.AccountSettings, | ||||||
|  | 	columns ...string, | ||||||
|  | ) error { | ||||||
|  | 	return a.state.Caches.GTS.AccountSettings.Store(settings, func() error { | ||||||
|  | 		settings.UpdatedAt = time.Now() | ||||||
|  | 		if len(columns) > 0 { | ||||||
|  | 			// If we're updating by column, | ||||||
|  | 			// ensure "updated_at" is included. | ||||||
|  | 			columns = append(columns, "updated_at") | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if _, err := a.db. | ||||||
|  | 			NewUpdate(). | ||||||
|  | 			Model(settings). | ||||||
|  | 			Column(columns...). | ||||||
|  | 			Where("? = ?", bun.Ident("account_settings.account_id"), settings.AccountID). | ||||||
|  | 			Exec(ctx); err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		return nil | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -216,6 +216,8 @@ func (suite *AccountTestSuite) TestGetAccountBy() { | ||||||
| 		a2.AvatarMediaAttachment = nil | 		a2.AvatarMediaAttachment = nil | ||||||
| 		a1.Emojis = nil | 		a1.Emojis = nil | ||||||
| 		a2.Emojis = nil | 		a2.Emojis = nil | ||||||
|  | 		a1.Settings = nil | ||||||
|  | 		a2.Settings = nil | ||||||
| 
 | 
 | ||||||
| 		// Clear database-set fields. | 		// Clear database-set fields. | ||||||
| 		a1.CreatedAt = time.Time{} | 		a1.CreatedAt = time.Time{} | ||||||
|  | @ -439,15 +441,11 @@ func (suite *AccountTestSuite) TestInsertAccountWithDefaults() { | ||||||
| 	err = suite.db.Put(context.Background(), newAccount) | 	err = suite.db.Put(context.Background(), newAccount) | ||||||
| 	suite.NoError(err) | 	suite.NoError(err) | ||||||
| 
 | 
 | ||||||
| 	suite.Equal("en", newAccount.Language) |  | ||||||
| 	suite.WithinDuration(time.Now(), newAccount.CreatedAt, 30*time.Second) | 	suite.WithinDuration(time.Now(), newAccount.CreatedAt, 30*time.Second) | ||||||
| 	suite.WithinDuration(time.Now(), newAccount.UpdatedAt, 30*time.Second) | 	suite.WithinDuration(time.Now(), newAccount.UpdatedAt, 30*time.Second) | ||||||
| 	suite.True(*newAccount.Locked) | 	suite.True(*newAccount.Locked) | ||||||
| 	suite.False(*newAccount.Memorial) |  | ||||||
| 	suite.False(*newAccount.Bot) | 	suite.False(*newAccount.Bot) | ||||||
| 	suite.False(*newAccount.Discoverable) | 	suite.False(*newAccount.Discoverable) | ||||||
| 	suite.False(*newAccount.Sensitive) |  | ||||||
| 	suite.False(*newAccount.HideCollections) |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (suite *AccountTestSuite) TestGetAccountPinnedStatusesSomeResults() { | func (suite *AccountTestSuite) TestGetAccountPinnedStatusesSomeResults() { | ||||||
|  |  | ||||||
|  | @ -119,12 +119,21 @@ func (a *adminDB) NewSignup(ctx context.Context, newSignup gtsmodel.NewSignup) ( | ||||||
| 			return nil, err | 			return nil, err | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | 		settings := >smodel.AccountSettings{ | ||||||
|  | 			AccountID: accountID, | ||||||
|  | 			Reason:    newSignup.Reason, | ||||||
|  | 			Privacy:   gtsmodel.VisibilityDefault, | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		// Insert the settings! | ||||||
|  | 		if err := a.state.DB.PutAccountSettings(ctx, settings); err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		account = >smodel.Account{ | 		account = >smodel.Account{ | ||||||
| 			ID:                    accountID, | 			ID:                    accountID, | ||||||
| 			Username:              newSignup.Username, | 			Username:              newSignup.Username, | ||||||
| 			DisplayName:           newSignup.Username, | 			DisplayName:           newSignup.Username, | ||||||
| 			Reason:                newSignup.Reason, |  | ||||||
| 			Privacy:               gtsmodel.VisibilityDefault, |  | ||||||
| 			URI:                   uris.UserURI, | 			URI:                   uris.UserURI, | ||||||
| 			URL:                   uris.UserURL, | 			URL:                   uris.UserURL, | ||||||
| 			InboxURI:              uris.InboxURI, | 			InboxURI:              uris.InboxURI, | ||||||
|  | @ -136,6 +145,7 @@ func (a *adminDB) NewSignup(ctx context.Context, newSignup gtsmodel.NewSignup) ( | ||||||
| 			PrivateKey:            privKey, | 			PrivateKey:            privKey, | ||||||
| 			PublicKey:             &privKey.PublicKey, | 			PublicKey:             &privKey.PublicKey, | ||||||
| 			PublicKeyURI:          uris.PublicKeyURI, | 			PublicKeyURI:          uris.PublicKeyURI, | ||||||
|  | 			Settings:              settings, | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		// Insert the new account! | 		// Insert the new account! | ||||||
|  |  | ||||||
|  | @ -85,19 +85,13 @@ func (suite *BasicTestSuite) TestPutAccountWithBunDefaultFields() { | ||||||
| 	suite.Nil(a.Fields) | 	suite.Nil(a.Fields) | ||||||
| 	suite.Empty(a.Note) | 	suite.Empty(a.Note) | ||||||
| 	suite.Empty(a.NoteRaw) | 	suite.Empty(a.NoteRaw) | ||||||
| 	suite.False(*a.Memorial) |  | ||||||
| 	suite.Empty(a.AlsoKnownAsURIs) | 	suite.Empty(a.AlsoKnownAsURIs) | ||||||
| 	suite.Empty(a.MovedToURI) | 	suite.Empty(a.MovedToURI) | ||||||
| 	suite.False(*a.Bot) | 	suite.False(*a.Bot) | ||||||
| 	suite.Empty(a.Reason) |  | ||||||
| 	// Locked is especially important, since it's a bool that defaults | 	// Locked is especially important, since it's a bool that defaults | ||||||
| 	// to true, which is why we use pointers for bools in the first place | 	// to true, which is why we use pointers for bools in the first place | ||||||
| 	suite.True(*a.Locked) | 	suite.True(*a.Locked) | ||||||
| 	suite.False(*a.Discoverable) | 	suite.False(*a.Discoverable) | ||||||
| 	suite.Empty(a.Privacy) |  | ||||||
| 	suite.False(*a.Sensitive) |  | ||||||
| 	suite.Equal("en", a.Language) |  | ||||||
| 	suite.Empty(a.StatusContentType) |  | ||||||
| 	suite.Equal(testAccount.URI, a.URI) | 	suite.Equal(testAccount.URI, a.URI) | ||||||
| 	suite.Equal(testAccount.URL, a.URL) | 	suite.Equal(testAccount.URL, a.URL) | ||||||
| 	suite.Zero(testAccount.FetchedAt) | 	suite.Zero(testAccount.FetchedAt) | ||||||
|  | @ -113,7 +107,6 @@ func (suite *BasicTestSuite) TestPutAccountWithBunDefaultFields() { | ||||||
| 	suite.Zero(a.SensitizedAt) | 	suite.Zero(a.SensitizedAt) | ||||||
| 	suite.Zero(a.SilencedAt) | 	suite.Zero(a.SilencedAt) | ||||||
| 	suite.Zero(a.SuspendedAt) | 	suite.Zero(a.SuspendedAt) | ||||||
| 	suite.False(*a.HideCollections) |  | ||||||
| 	suite.Empty(a.SuspensionOrigin) | 	suite.Empty(a.SuspensionOrigin) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										122
									
								
								internal/db/bundb/migrations/20240318115336_account_settings.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										122
									
								
								internal/db/bundb/migrations/20240318115336_account_settings.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,122 @@ | ||||||
|  | // 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/>. | ||||||
|  | 
 | ||||||
|  | package migrations | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  | 
 | ||||||
|  | 	oldgtsmodel "github.com/superseriousbusiness/gotosocial/internal/db/bundb/migrations/20230328203024_migration_fix" | ||||||
|  | 	newgtsmodel "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" | ||||||
|  | 	"github.com/superseriousbusiness/gotosocial/internal/log" | ||||||
|  | 	"github.com/superseriousbusiness/gotosocial/internal/util" | ||||||
|  | 
 | ||||||
|  | 	"github.com/uptrace/bun" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func init() { | ||||||
|  | 	up := func(ctx context.Context, db *bun.DB) error { | ||||||
|  | 		log.Info(ctx, "migrating account settings to new table, please wait...") | ||||||
|  | 		return db.RunInTx(ctx, nil, func(ctx context.Context, tx bun.Tx) error { | ||||||
|  | 			// Columns we'll be moving | ||||||
|  | 			// to AccountSettings. | ||||||
|  | 			var columns = []string{ | ||||||
|  | 				"reason", | ||||||
|  | 				"privacy", | ||||||
|  | 				"sensitive", | ||||||
|  | 				"language", | ||||||
|  | 				"status_content_type", | ||||||
|  | 				"custom_css", | ||||||
|  | 				"enable_rss", | ||||||
|  | 				"hide_collections", | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			// Create the new account settings table. | ||||||
|  | 			if _, err := tx. | ||||||
|  | 				NewCreateTable(). | ||||||
|  | 				Model(&newgtsmodel.AccountSettings{}). | ||||||
|  | 				IfNotExists(). | ||||||
|  | 				Exec(ctx); err != nil { | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			// Select each local account. | ||||||
|  | 			accounts := []*oldgtsmodel.Account{} | ||||||
|  | 			if err := tx. | ||||||
|  | 				NewSelect(). | ||||||
|  | 				TableExpr("? AS ?", bun.Ident("accounts"), bun.Ident("account")). | ||||||
|  | 				Column("account.id"). | ||||||
|  | 				Column(columns...). | ||||||
|  | 				Join( | ||||||
|  | 					"JOIN ? AS ? ON ? = ?", | ||||||
|  | 					bun.Ident("users"), bun.Ident("user"), | ||||||
|  | 					bun.Ident("user.account_id"), bun.Ident("account.id"), | ||||||
|  | 				). | ||||||
|  | 				Scan(ctx, &accounts); err != nil { | ||||||
|  | 				return err | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			// Create a settings entry for each existing account, taking | ||||||
|  | 			// values from the old account model (with sensible defaults). | ||||||
|  | 			for _, account := range accounts { | ||||||
|  | 				settings := &newgtsmodel.AccountSettings{ | ||||||
|  | 					AccountID:         account.ID, | ||||||
|  | 					CreatedAt:         account.CreatedAt, | ||||||
|  | 					Reason:            account.Reason, | ||||||
|  | 					Privacy:           newgtsmodel.Visibility(account.Privacy), | ||||||
|  | 					Sensitive:         util.Ptr(util.PtrValueOr(account.Sensitive, false)), | ||||||
|  | 					Language:          account.Language, | ||||||
|  | 					StatusContentType: account.StatusContentType, | ||||||
|  | 					CustomCSS:         account.CustomCSS, | ||||||
|  | 					EnableRSS:         util.Ptr(util.PtrValueOr(account.EnableRSS, false)), | ||||||
|  | 					HideCollections:   util.Ptr(util.PtrValueOr(account.HideCollections, false)), | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				// Insert the settings model. | ||||||
|  | 				if _, err := tx. | ||||||
|  | 					NewInsert(). | ||||||
|  | 					Model(settings). | ||||||
|  | 					Exec(ctx); err != nil { | ||||||
|  | 					return err | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			// Drop now unused columns from accounts table. | ||||||
|  | 			for _, column := range columns { | ||||||
|  | 				if _, err := tx. | ||||||
|  | 					NewDropColumn(). | ||||||
|  | 					Table("accounts"). | ||||||
|  | 					Column(column). | ||||||
|  | 					Exec(ctx); err != nil { | ||||||
|  | 					return err | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			return nil | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	down := func(ctx context.Context, db *bun.DB) error { | ||||||
|  | 		return db.RunInTx(ctx, nil, func(ctx context.Context, tx bun.Tx) error { | ||||||
|  | 			return nil | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if err := Migrations.Register(up, down); err != nil { | ||||||
|  | 		panic(err) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | @ -743,9 +743,6 @@ func (d *Dereferencer) enrichAccount( | ||||||
| 		// Set time of update from the last-fetched date. | 		// Set time of update from the last-fetched date. | ||||||
| 		latestAcc.UpdatedAt = latestAcc.FetchedAt | 		latestAcc.UpdatedAt = latestAcc.FetchedAt | ||||||
| 
 | 
 | ||||||
| 		// Carry over existing account language. |  | ||||||
| 		latestAcc.Language = account.Language |  | ||||||
| 
 |  | ||||||
| 		// This is an existing account, update the model in the database. | 		// This is an existing account, update the model in the database. | ||||||
| 		if err := d.state.DB.UpdateAccount(ctx, latestAcc); err != nil { | 		if err := d.state.DB.UpdateAccount(ctx, latestAcc); err != nil { | ||||||
| 			return nil, nil, gtserror.Newf("error updating database: %w", err) | 			return nil, nil, gtserror.Newf("error updating database: %w", err) | ||||||
|  |  | ||||||
|  | @ -48,8 +48,8 @@ type Account struct { | ||||||
| 	DisplayName             string           `bun:""`                                                            // DisplayName for this account. Can be empty, then just the Username will be used for display purposes. | 	DisplayName             string           `bun:""`                                                            // DisplayName for this account. Can be empty, then just the Username will be used for display purposes. | ||||||
| 	EmojiIDs                []string         `bun:"emojis,array"`                                                // Database IDs of any emojis used in this account's bio, display name, etc | 	EmojiIDs                []string         `bun:"emojis,array"`                                                // Database IDs of any emojis used in this account's bio, display name, etc | ||||||
| 	Emojis                  []*Emoji         `bun:"attached_emojis,m2m:account_to_emojis"`                       // Emojis corresponding to emojiIDs. https://bun.uptrace.dev/guide/relations.html#many-to-many-relation | 	Emojis                  []*Emoji         `bun:"attached_emojis,m2m:account_to_emojis"`                       // Emojis corresponding to emojiIDs. https://bun.uptrace.dev/guide/relations.html#many-to-many-relation | ||||||
| 	Fields                  []*Field         // A slice of of fields that this account has added to their profile. | 	Fields                  []*Field         `bun:""`                                                            // A slice of of fields that this account has added to their profile. | ||||||
| 	FieldsRaw               []*Field         // The raw (unparsed) content of fields that this account has added to their profile, without conversion to HTML, only available when requester = target | 	FieldsRaw               []*Field         `bun:""`                                                            // The raw (unparsed) content of fields that this account has added to their profile, without conversion to HTML, only available when requester = target | ||||||
| 	Note                    string           `bun:""`                                                            // A note that this account has on their profile (ie., the account's bio/description of themselves) | 	Note                    string           `bun:""`                                                            // A note that this account has on their profile (ie., the account's bio/description of themselves) | ||||||
| 	NoteRaw                 string           `bun:""`                                                            // The raw contents of .Note without conversion to HTML, only available when requester = target | 	NoteRaw                 string           `bun:""`                                                            // The raw contents of .Note without conversion to HTML, only available when requester = target | ||||||
| 	Memorial                *bool            `bun:",default:false"`                                              // Is this a memorial account, ie., has the user passed away? | 	Memorial                *bool            `bun:",default:false"`                                              // Is this a memorial account, ie., has the user passed away? | ||||||
|  | @ -60,14 +60,8 @@ type Account struct { | ||||||
| 	MoveID                  string           `bun:"type:CHAR(26),nullzero"`                                      // ID of a Move in the database for this account. Only set if we received or created a Move activity for which this account URI was the origin. | 	MoveID                  string           `bun:"type:CHAR(26),nullzero"`                                      // ID of a Move in the database for this account. Only set if we received or created a Move activity for which this account URI was the origin. | ||||||
| 	Move                    *Move            `bun:"-"`                                                           // Move corresponding to MoveID, if set. | 	Move                    *Move            `bun:"-"`                                                           // Move corresponding to MoveID, if set. | ||||||
| 	Bot                     *bool            `bun:",default:false"`                                              // Does this account identify itself as a bot? | 	Bot                     *bool            `bun:",default:false"`                                              // Does this account identify itself as a bot? | ||||||
| 	Reason                  string           `bun:""`                               // What reason was given for signing up when this account was created? |  | ||||||
| 	Locked                  *bool            `bun:",default:true"`                                               // Does this account need an approval for new followers? | 	Locked                  *bool            `bun:",default:true"`                                               // Does this account need an approval for new followers? | ||||||
| 	Discoverable            *bool            `bun:",default:false"`                                              // Should this account be shown in the instance's profile directory? | 	Discoverable            *bool            `bun:",default:false"`                                              // Should this account be shown in the instance's profile directory? | ||||||
| 	Privacy                 Visibility       `bun:",nullzero"`                      // Default post privacy for this account |  | ||||||
| 	Sensitive               *bool            `bun:",default:false"`                 // Set posts from this account to sensitive by default? |  | ||||||
| 	Language                string           `bun:",nullzero,notnull,default:'en'"` // What language does this account post in? |  | ||||||
| 	StatusContentType       string           `bun:",nullzero"`                      // What is the default format for statuses posted by this account (only for local accounts). |  | ||||||
| 	CustomCSS               string           `bun:",nullzero"`                      // Custom CSS that should be displayed for this Account's profile and statuses. |  | ||||||
| 	URI                     string           `bun:",nullzero,notnull,unique"`                                    // ActivityPub URI for this account. | 	URI                     string           `bun:",nullzero,notnull,unique"`                                    // ActivityPub URI for this account. | ||||||
| 	URL                     string           `bun:",nullzero,unique"`                                            // Web URL for this account's profile | 	URL                     string           `bun:",nullzero,unique"`                                            // Web URL for this account's profile | ||||||
| 	InboxURI                string           `bun:",nullzero,unique"`                                            // Address of this account's ActivityPub inbox, for sending activity to | 	InboxURI                string           `bun:",nullzero,unique"`                                            // Address of this account's ActivityPub inbox, for sending activity to | ||||||
|  | @ -84,9 +78,8 @@ type Account struct { | ||||||
| 	SensitizedAt            time.Time        `bun:"type:timestamptz,nullzero"`                                   // When was this account set to have all its media shown as sensitive? | 	SensitizedAt            time.Time        `bun:"type:timestamptz,nullzero"`                                   // When was this account set to have all its media shown as sensitive? | ||||||
| 	SilencedAt              time.Time        `bun:"type:timestamptz,nullzero"`                                   // When was this account silenced (eg., statuses only visible to followers, not public)? | 	SilencedAt              time.Time        `bun:"type:timestamptz,nullzero"`                                   // When was this account silenced (eg., statuses only visible to followers, not public)? | ||||||
| 	SuspendedAt             time.Time        `bun:"type:timestamptz,nullzero"`                                   // When was this account suspended (eg., don't allow it to log in/post, don't accept media/posts from this account) | 	SuspendedAt             time.Time        `bun:"type:timestamptz,nullzero"`                                   // When was this account suspended (eg., don't allow it to log in/post, don't accept media/posts from this account) | ||||||
| 	HideCollections         *bool            `bun:",default:false"`                 // Hide this account's collections |  | ||||||
| 	SuspensionOrigin        string           `bun:"type:CHAR(26),nullzero"`                                      // id of the database entry that caused this account to become suspended -- can be an account ID or a domain block ID | 	SuspensionOrigin        string           `bun:"type:CHAR(26),nullzero"`                                      // id of the database entry that caused this account to become suspended -- can be an account ID or a domain block ID | ||||||
| 	EnableRSS               *bool            `bun:",default:false"`                 // enable RSS feed subscription for this account's public posts at [URL]/feed | 	Settings                *AccountSettings `bun:"-"`                                                           // gtsmodel.AccountSettings for this account. | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // IsLocal returns whether account is a local user account. | // IsLocal returns whether account is a local user account. | ||||||
|  |  | ||||||
							
								
								
									
										35
									
								
								internal/gtsmodel/accountsettings.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								internal/gtsmodel/accountsettings.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,35 @@ | ||||||
|  | // 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/>. | ||||||
|  | 
 | ||||||
|  | package gtsmodel | ||||||
|  | 
 | ||||||
|  | import "time" | ||||||
|  | 
 | ||||||
|  | // AccountSettings models settings / preferences for a local, non-instance account. | ||||||
|  | type AccountSettings struct { | ||||||
|  | 	AccountID         string     `bun:"type:CHAR(26),pk,nullzero,notnull,unique"`                    // AccountID that owns this settings. | ||||||
|  | 	CreatedAt         time.Time  `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item created. | ||||||
|  | 	UpdatedAt         time.Time  `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // when was item was last updated. | ||||||
|  | 	Reason            string     `bun:",nullzero"`                                                   // What reason was given for signing up when this account was created? | ||||||
|  | 	Privacy           Visibility `bun:",nullzero"`                                                   // Default post privacy for this account | ||||||
|  | 	Sensitive         *bool      `bun:",nullzero,notnull,default:false"`                             // Set posts from this account to sensitive by default? | ||||||
|  | 	Language          string     `bun:",nullzero,notnull,default:'en'"`                              // What language does this account post in? | ||||||
|  | 	StatusContentType string     `bun:",nullzero"`                                                   // What is the default format for statuses posted by this account (only for local accounts). | ||||||
|  | 	CustomCSS         string     `bun:",nullzero"`                                                   // Custom CSS that should be displayed for this Account's profile and statuses. | ||||||
|  | 	EnableRSS         *bool      `bun:",nullzero,notnull,default:false"`                             // enable RSS feed subscription for this account's public posts at [URL]/feed | ||||||
|  | 	HideCollections   *bool      `bun:",nullzero,notnull,default:false"`                             // Hide this account's followers/following collections. | ||||||
|  | } | ||||||
|  | @ -518,14 +518,9 @@ func stubbifyAccount(account *gtsmodel.Account, origin string) []string { | ||||||
| 	account.Memorial = util.Ptr(false) | 	account.Memorial = util.Ptr(false) | ||||||
| 	account.AlsoKnownAsURIs = nil | 	account.AlsoKnownAsURIs = nil | ||||||
| 	account.MovedToURI = "" | 	account.MovedToURI = "" | ||||||
| 	account.Reason = "" |  | ||||||
| 	account.Discoverable = util.Ptr(false) | 	account.Discoverable = util.Ptr(false) | ||||||
| 	account.StatusContentType = "" |  | ||||||
| 	account.CustomCSS = "" |  | ||||||
| 	account.SuspendedAt = now | 	account.SuspendedAt = now | ||||||
| 	account.SuspensionOrigin = origin | 	account.SuspensionOrigin = origin | ||||||
| 	account.HideCollections = util.Ptr(true) |  | ||||||
| 	account.EnableRSS = util.Ptr(false) |  | ||||||
| 
 | 
 | ||||||
| 	return []string{ | 	return []string{ | ||||||
| 		"fetched_at", | 		"fetched_at", | ||||||
|  | @ -541,14 +536,9 @@ func stubbifyAccount(account *gtsmodel.Account, origin string) []string { | ||||||
| 		"memorial", | 		"memorial", | ||||||
| 		"also_known_as_uris", | 		"also_known_as_uris", | ||||||
| 		"moved_to_uri", | 		"moved_to_uri", | ||||||
| 		"reason", |  | ||||||
| 		"discoverable", | 		"discoverable", | ||||||
| 		"status_content_type", |  | ||||||
| 		"custom_css", |  | ||||||
| 		"suspended_at", | 		"suspended_at", | ||||||
| 		"suspension_origin", | 		"suspension_origin", | ||||||
| 		"hide_collections", |  | ||||||
| 		"enable_rss", |  | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -66,14 +66,9 @@ func (suite *AccountDeleteTestSuite) TestAccountDeleteLocal() { | ||||||
| 	suite.Zero(updatedAccount.NoteRaw) | 	suite.Zero(updatedAccount.NoteRaw) | ||||||
| 	suite.False(*updatedAccount.Memorial) | 	suite.False(*updatedAccount.Memorial) | ||||||
| 	suite.Empty(updatedAccount.AlsoKnownAsURIs) | 	suite.Empty(updatedAccount.AlsoKnownAsURIs) | ||||||
| 	suite.Zero(updatedAccount.Reason) |  | ||||||
| 	suite.False(*updatedAccount.Discoverable) | 	suite.False(*updatedAccount.Discoverable) | ||||||
| 	suite.Zero(updatedAccount.StatusContentType) |  | ||||||
| 	suite.Zero(updatedAccount.CustomCSS) |  | ||||||
| 	suite.WithinDuration(time.Now(), updatedAccount.SuspendedAt, 1*time.Minute) | 	suite.WithinDuration(time.Now(), updatedAccount.SuspendedAt, 1*time.Minute) | ||||||
| 	suite.Equal(suspensionOrigin, updatedAccount.SuspensionOrigin) | 	suite.Equal(suspensionOrigin, updatedAccount.SuspensionOrigin) | ||||||
| 	suite.True(*updatedAccount.HideCollections) |  | ||||||
| 	suite.False(*updatedAccount.EnableRSS) |  | ||||||
| 
 | 
 | ||||||
| 	updatedUser, err := suite.db.GetUserByAccountID(ctx, testAccount.ID) | 	updatedUser, err := suite.db.GetUserByAccountID(ctx, testAccount.ID) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
|  |  | ||||||
|  | @ -64,7 +64,7 @@ func (p *Processor) GetRSSFeedForUsername(ctx context.Context, username string) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// Ensure account has rss feed enabled. | 	// Ensure account has rss feed enabled. | ||||||
| 	if !*account.EnableRSS { | 	if !*account.Settings.EnableRSS { | ||||||
| 		err = gtserror.New("account RSS feed not enabled") | 		err = gtserror.New("account RSS feed not enabled") | ||||||
| 		return nil, never, gtserror.NewErrorNotFound(err) | 		return nil, never, gtserror.NewErrorNotFound(err) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -47,6 +47,11 @@ func (p *Processor) selectNoteFormatter(contentType string) text.FormatFunc { | ||||||
| 
 | 
 | ||||||
| // Update processes the update of an account with the given form. | // Update processes the update of an account with the given form. | ||||||
| func (p *Processor) Update(ctx context.Context, account *gtsmodel.Account, form *apimodel.UpdateCredentialsRequest) (*apimodel.Account, gtserror.WithCode) { | func (p *Processor) Update(ctx context.Context, account *gtsmodel.Account, form *apimodel.UpdateCredentialsRequest) (*apimodel.Account, gtserror.WithCode) { | ||||||
|  | 	// Ensure account populated; we'll need settings. | ||||||
|  | 	if err := p.state.DB.PopulateAccount(ctx, account); err != nil { | ||||||
|  | 		log.Errorf(ctx, "error(s) populating account, will continue: %s", err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	if form.Discoverable != nil { | 	if form.Discoverable != nil { | ||||||
| 		account.Discoverable = form.Discoverable | 		account.Discoverable = form.Discoverable | ||||||
| 	} | 	} | ||||||
|  | @ -146,7 +151,7 @@ func (p *Processor) Update(ctx context.Context, account *gtsmodel.Account, form | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		// Format + set note according to user prefs. | 		// Format + set note according to user prefs. | ||||||
| 		f := p.selectNoteFormatter(account.StatusContentType) | 		f := p.selectNoteFormatter(account.Settings.StatusContentType) | ||||||
| 		formatNoteResult := f(ctx, p.parseMention, account.ID, "", account.NoteRaw) | 		formatNoteResult := f(ctx, p.parseMention, account.ID, "", account.NoteRaw) | ||||||
| 		account.Note = formatNoteResult.HTML | 		account.Note = formatNoteResult.HTML | ||||||
| 
 | 
 | ||||||
|  | @ -227,11 +232,11 @@ func (p *Processor) Update(ctx context.Context, account *gtsmodel.Account, form | ||||||
| 			if err != nil { | 			if err != nil { | ||||||
| 				return nil, gtserror.NewErrorBadRequest(err) | 				return nil, gtserror.NewErrorBadRequest(err) | ||||||
| 			} | 			} | ||||||
| 			account.Language = language | 			account.Settings.Language = language | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if form.Source.Sensitive != nil { | 		if form.Source.Sensitive != nil { | ||||||
| 			account.Sensitive = form.Source.Sensitive | 			account.Settings.Sensitive = form.Source.Sensitive | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if form.Source.Privacy != nil { | 		if form.Source.Privacy != nil { | ||||||
|  | @ -239,7 +244,7 @@ func (p *Processor) Update(ctx context.Context, account *gtsmodel.Account, form | ||||||
| 				return nil, gtserror.NewErrorBadRequest(err) | 				return nil, gtserror.NewErrorBadRequest(err) | ||||||
| 			} | 			} | ||||||
| 			privacy := typeutils.APIVisToVis(apimodel.Visibility(*form.Source.Privacy)) | 			privacy := typeutils.APIVisToVis(apimodel.Visibility(*form.Source.Privacy)) | ||||||
| 			account.Privacy = privacy | 			account.Settings.Privacy = privacy | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if form.Source.StatusContentType != nil { | 		if form.Source.StatusContentType != nil { | ||||||
|  | @ -247,7 +252,7 @@ func (p *Processor) Update(ctx context.Context, account *gtsmodel.Account, form | ||||||
| 				return nil, gtserror.NewErrorBadRequest(err, err.Error()) | 				return nil, gtserror.NewErrorBadRequest(err, err.Error()) | ||||||
| 			} | 			} | ||||||
| 
 | 
 | ||||||
| 			account.StatusContentType = *form.Source.StatusContentType | 			account.Settings.StatusContentType = *form.Source.StatusContentType | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -256,18 +261,21 @@ func (p *Processor) Update(ctx context.Context, account *gtsmodel.Account, form | ||||||
| 		if err := validate.CustomCSS(customCSS); err != nil { | 		if err := validate.CustomCSS(customCSS); err != nil { | ||||||
| 			return nil, gtserror.NewErrorBadRequest(err, err.Error()) | 			return nil, gtserror.NewErrorBadRequest(err, err.Error()) | ||||||
| 		} | 		} | ||||||
| 		account.CustomCSS = text.SanitizeToPlaintext(customCSS) | 		account.Settings.CustomCSS = text.SanitizeToPlaintext(customCSS) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if form.EnableRSS != nil { | 	if form.EnableRSS != nil { | ||||||
| 		account.EnableRSS = form.EnableRSS | 		account.Settings.EnableRSS = form.EnableRSS | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	err := p.state.DB.UpdateAccount(ctx, account) | 	if err := p.state.DB.UpdateAccount(ctx, account); err != nil { | ||||||
| 	if err != nil { |  | ||||||
| 		return nil, gtserror.NewErrorInternalError(fmt.Errorf("could not update account %s: %s", account.ID, err)) | 		return nil, gtserror.NewErrorInternalError(fmt.Errorf("could not update account %s: %s", account.ID, err)) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	if err := p.state.DB.UpdateAccountSettings(ctx, account.Settings); err != nil { | ||||||
|  | 		return nil, gtserror.NewErrorInternalError(fmt.Errorf("could not update account settings %s: %s", account.ID, err)) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	p.state.Workers.EnqueueClientAPI(ctx, messages.FromClientAPI{ | 	p.state.Workers.EnqueueClientAPI(ctx, messages.FromClientAPI{ | ||||||
| 		APObjectType:   ap.ObjectProfile, | 		APObjectType:   ap.ObjectProfile, | ||||||
| 		APActivityType: ap.ActivityUpdate, | 		APActivityType: ap.ActivityUpdate, | ||||||
|  |  | ||||||
|  | @ -126,9 +126,15 @@ func (suite *AccountUpdateTestSuite) TestAccountUpdateWithMention() { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (suite *AccountUpdateTestSuite) TestAccountUpdateWithMarkdownNote() { | func (suite *AccountUpdateTestSuite) TestAccountUpdateWithMarkdownNote() { | ||||||
|  | 	// Copy zork. | ||||||
| 	testAccount := >smodel.Account{} | 	testAccount := >smodel.Account{} | ||||||
| 	*testAccount = *suite.testAccounts["local_account_1"] | 	*testAccount = *suite.testAccounts["local_account_1"] | ||||||
| 
 | 
 | ||||||
|  | 	// Copy zork's settings. | ||||||
|  | 	settings := >smodel.AccountSettings{} | ||||||
|  | 	*settings = *suite.testAccounts["local_account_1"].Settings | ||||||
|  | 	testAccount.Settings = settings | ||||||
|  | 
 | ||||||
| 	var ( | 	var ( | ||||||
| 		ctx          = context.Background() | 		ctx          = context.Background() | ||||||
| 		note         = "*hello* ~~here~~ i am!" | 		note         = "*hello* ~~here~~ i am!" | ||||||
|  | @ -136,8 +142,8 @@ func (suite *AccountUpdateTestSuite) TestAccountUpdateWithMarkdownNote() { | ||||||
| 	) | 	) | ||||||
| 
 | 
 | ||||||
| 	// Set status content type of account 1 to markdown for this test. | 	// Set status content type of account 1 to markdown for this test. | ||||||
| 	testAccount.StatusContentType = "text/markdown" | 	testAccount.Settings.StatusContentType = "text/markdown" | ||||||
| 	if err := suite.db.UpdateAccount(ctx, testAccount, "status_content_type"); err != nil { | 	if err := suite.db.UpdateAccountSettings(ctx, testAccount.Settings, "status_content_type"); err != nil { | ||||||
| 		suite.FailNow(err.Error()) | 		suite.FailNow(err.Error()) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -32,9 +32,9 @@ func (p *Processor) PreferencesGet(ctx context.Context, accountID string) (*apim | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return &apimodel.Preferences{ | 	return &apimodel.Preferences{ | ||||||
| 		PostingDefaultVisibility: mastoPrefVisibility(act.Privacy), | 		PostingDefaultVisibility: mastoPrefVisibility(act.Settings.Privacy), | ||||||
| 		PostingDefaultSensitive:  *act.Sensitive, | 		PostingDefaultSensitive:  *act.Settings.Sensitive, | ||||||
| 		PostingDefaultLanguage:   act.Language, | 		PostingDefaultLanguage:   act.Settings.Language, | ||||||
| 		// The Reading* preferences don't appear to actually be settable by the | 		// The Reading* preferences don't appear to actually be settable by the | ||||||
| 		// client, so forcing some sensible defaults here | 		// client, so forcing some sensible defaults here | ||||||
| 		ReadingExpandMedia:    "default", | 		ReadingExpandMedia:    "default", | ||||||
|  |  | ||||||
|  | @ -50,6 +50,11 @@ func (p *Processor) Create( | ||||||
| 	*apimodel.Status, | 	*apimodel.Status, | ||||||
| 	gtserror.WithCode, | 	gtserror.WithCode, | ||||||
| ) { | ) { | ||||||
|  | 	// Ensure account populated; we'll need settings. | ||||||
|  | 	if err := p.state.DB.PopulateAccount(ctx, requester); err != nil { | ||||||
|  | 		log.Errorf(ctx, "error(s) populating account, will continue: %s", err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	// Generate new ID for status. | 	// Generate new ID for status. | ||||||
| 	statusID := id.NewULID() | 	statusID := id.NewULID() | ||||||
| 
 | 
 | ||||||
|  | @ -112,11 +117,11 @@ func (p *Processor) Create( | ||||||
| 		return nil, errWithCode | 		return nil, errWithCode | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if err := processVisibility(form, requester.Privacy, status); err != nil { | 	if err := processVisibility(form, requester.Settings.Privacy, status); err != nil { | ||||||
| 		return nil, gtserror.NewErrorInternalError(err) | 		return nil, gtserror.NewErrorInternalError(err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if err := processLanguage(form, requester.Language, status); err != nil { | 	if err := processLanguage(form, requester.Settings.Language, status); err != nil { | ||||||
| 		return nil, gtserror.NewErrorInternalError(err) | 		return nil, gtserror.NewErrorInternalError(err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -369,7 +374,7 @@ func processLanguage(form *apimodel.AdvancedStatusCreateForm, accountDefaultLang | ||||||
| func (p *Processor) processContent(ctx context.Context, parseMention gtsmodel.ParseMentionFunc, form *apimodel.AdvancedStatusCreateForm, status *gtsmodel.Status) error { | func (p *Processor) processContent(ctx context.Context, parseMention gtsmodel.ParseMentionFunc, form *apimodel.AdvancedStatusCreateForm, status *gtsmodel.Status) error { | ||||||
| 	if form.ContentType == "" { | 	if form.ContentType == "" { | ||||||
| 		// If content type wasn't specified, use the author's preferred content-type. | 		// If content type wasn't specified, use the author's preferred content-type. | ||||||
| 		contentType := apimodel.StatusContentType(status.Account.StatusContentType) | 		contentType := apimodel.StatusContentType(status.Account.Settings.StatusContentType) | ||||||
| 		form.ContentType = contentType | 		form.ContentType = contentType | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -362,9 +362,7 @@ func (suite *FromFediAPITestSuite) TestProcessAccountDelete() { | ||||||
| 	suite.Empty(dbAccount.AvatarRemoteURL) | 	suite.Empty(dbAccount.AvatarRemoteURL) | ||||||
| 	suite.Empty(dbAccount.HeaderMediaAttachmentID) | 	suite.Empty(dbAccount.HeaderMediaAttachmentID) | ||||||
| 	suite.Empty(dbAccount.HeaderRemoteURL) | 	suite.Empty(dbAccount.HeaderRemoteURL) | ||||||
| 	suite.Empty(dbAccount.Reason) |  | ||||||
| 	suite.Empty(dbAccount.Fields) | 	suite.Empty(dbAccount.Fields) | ||||||
| 	suite.True(*dbAccount.HideCollections) |  | ||||||
| 	suite.False(*dbAccount.Discoverable) | 	suite.False(*dbAccount.Discoverable) | ||||||
| 	suite.WithinDuration(time.Now(), dbAccount.SuspendedAt, 30*time.Second) | 	suite.WithinDuration(time.Now(), dbAccount.SuspendedAt, 30*time.Second) | ||||||
| 	suite.Equal(dbAccount.ID, dbAccount.SuspensionOrigin) | 	suite.Equal(dbAccount.ID, dbAccount.SuspensionOrigin) | ||||||
|  |  | ||||||
|  | @ -25,6 +25,7 @@ import ( | ||||||
| 	"io" | 	"io" | ||||||
| 	"os" | 	"os" | ||||||
| 
 | 
 | ||||||
|  | 	"github.com/superseriousbusiness/gotosocial/internal/config" | ||||||
| 	"github.com/superseriousbusiness/gotosocial/internal/log" | 	"github.com/superseriousbusiness/gotosocial/internal/log" | ||||||
| 	transmodel "github.com/superseriousbusiness/gotosocial/internal/trans/model" | 	transmodel "github.com/superseriousbusiness/gotosocial/internal/trans/model" | ||||||
| ) | ) | ||||||
|  | @ -73,6 +74,14 @@ func (i *importer) inputEntry(ctx context.Context, entry transmodel.Entry) error | ||||||
| 		if err := i.putInDB(ctx, account); err != nil { | 		if err := i.putInDB(ctx, account); err != nil { | ||||||
| 			return fmt.Errorf("inputEntry: error adding account to database: %s", err) | 			return fmt.Errorf("inputEntry: error adding account to database: %s", err) | ||||||
| 		} | 		} | ||||||
|  | 		if account.Domain == "" && account.Username != config.GetHost() { | ||||||
|  | 			// Local, non-instance account. | ||||||
|  | 			// Insert barebones settings model. | ||||||
|  | 			settings := &transmodel.AccountSettings{AccountID: account.ID} | ||||||
|  | 			if err := i.putInDB(ctx, settings); err != nil { | ||||||
|  | 				return fmt.Errorf("inputEntry: error adding account settings to database: %s", err) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
| 		log.Infof(ctx, "added account with id %s", account.ID) | 		log.Infof(ctx, "added account with id %s", account.ID) | ||||||
| 		return nil | 		return nil | ||||||
| 	case transmodel.TransBlock: | 	case transmodel.TransBlock: | ||||||
|  |  | ||||||
|  | @ -106,11 +106,6 @@ func (suite *ImportMinimalTestSuite) TestImportMinimalOK() { | ||||||
| 	suite.Equal(testAccountBefore.Memorial, testAccountAfter.Memorial) | 	suite.Equal(testAccountBefore.Memorial, testAccountAfter.Memorial) | ||||||
| 	suite.Equal(testAccountBefore.Bot, testAccountAfter.Bot) | 	suite.Equal(testAccountBefore.Bot, testAccountAfter.Bot) | ||||||
| 	suite.Equal(testAccountBefore.Locked, testAccountAfter.Locked) | 	suite.Equal(testAccountBefore.Locked, testAccountAfter.Locked) | ||||||
| 	suite.Equal(testAccountBefore.Reason, testAccountAfter.Reason) |  | ||||||
| 	suite.Equal(testAccountBefore.Privacy, testAccountAfter.Privacy) |  | ||||||
| 	suite.Equal(testAccountBefore.Sensitive, testAccountAfter.Sensitive) |  | ||||||
| 	suite.Equal(testAccountBefore.Language, testAccountAfter.Language) |  | ||||||
| 	suite.Equal(testAccountBefore.StatusContentType, testAccountAfter.StatusContentType) |  | ||||||
| 	suite.Equal(testAccountBefore.URI, testAccountAfter.URI) | 	suite.Equal(testAccountBefore.URI, testAccountAfter.URI) | ||||||
| 	suite.Equal(testAccountBefore.URL, testAccountAfter.URL) | 	suite.Equal(testAccountBefore.URL, testAccountAfter.URL) | ||||||
| 	suite.Equal(testAccountBefore.InboxURI, testAccountAfter.InboxURI) | 	suite.Equal(testAccountBefore.InboxURI, testAccountAfter.InboxURI) | ||||||
|  | @ -123,7 +118,6 @@ func (suite *ImportMinimalTestSuite) TestImportMinimalOK() { | ||||||
| 	suite.Equal(testAccountBefore.PublicKey, testAccountAfter.PublicKey) | 	suite.Equal(testAccountBefore.PublicKey, testAccountAfter.PublicKey) | ||||||
| 	suite.Equal(testAccountBefore.PublicKeyURI, testAccountAfter.PublicKeyURI) | 	suite.Equal(testAccountBefore.PublicKeyURI, testAccountAfter.PublicKeyURI) | ||||||
| 	suite.Equal(testAccountBefore.SuspendedAt, testAccountAfter.SuspendedAt) | 	suite.Equal(testAccountBefore.SuspendedAt, testAccountAfter.SuspendedAt) | ||||||
| 	suite.Equal(testAccountBefore.HideCollections, testAccountAfter.HideCollections) |  | ||||||
| 	suite.Equal(testAccountBefore.SuspensionOrigin, testAccountAfter.SuspensionOrigin) | 	suite.Equal(testAccountBefore.SuspensionOrigin, testAccountAfter.SuspensionOrigin) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -36,13 +36,8 @@ type Account struct { | ||||||
| 	NoteRaw               string          `json:"noteRaw,omitempty" bun:",nullzero"` | 	NoteRaw               string          `json:"noteRaw,omitempty" bun:",nullzero"` | ||||||
| 	Memorial              *bool           `json:"memorial"` | 	Memorial              *bool           `json:"memorial"` | ||||||
| 	Bot                   *bool           `json:"bot"` | 	Bot                   *bool           `json:"bot"` | ||||||
| 	Reason                string          `json:"reason,omitempty" bun:",nullzero"` |  | ||||||
| 	Locked                *bool           `json:"locked"` | 	Locked                *bool           `json:"locked"` | ||||||
| 	Discoverable          *bool           `json:"discoverable"` | 	Discoverable          *bool           `json:"discoverable"` | ||||||
| 	Privacy               string          `json:"privacy,omitempty" bun:",nullzero"` |  | ||||||
| 	Sensitive             *bool           `json:"sensitive"` |  | ||||||
| 	Language              string          `json:"language,omitempty" bun:",nullzero"` |  | ||||||
| 	StatusContentType     string          `json:"statusContentType,omitempty" bun:",nullzero"` |  | ||||||
| 	URI                   string          `json:"uri" bun:",nullzero"` | 	URI                   string          `json:"uri" bun:",nullzero"` | ||||||
| 	URL                   string          `json:"url" bun:",nullzero"` | 	URL                   string          `json:"url" bun:",nullzero"` | ||||||
| 	InboxURI              string          `json:"inboxURI" bun:",nullzero"` | 	InboxURI              string          `json:"inboxURI" bun:",nullzero"` | ||||||
|  | @ -59,6 +54,9 @@ type Account struct { | ||||||
| 	SensitizedAt          *time.Time      `json:"sensitizedAt,omitempty" bun:",nullzero"` | 	SensitizedAt          *time.Time      `json:"sensitizedAt,omitempty" bun:",nullzero"` | ||||||
| 	SilencedAt            *time.Time      `json:"silencedAt,omitempty" bun:",nullzero"` | 	SilencedAt            *time.Time      `json:"silencedAt,omitempty" bun:",nullzero"` | ||||||
| 	SuspendedAt           *time.Time      `json:"suspendedAt,omitempty" bun:",nullzero"` | 	SuspendedAt           *time.Time      `json:"suspendedAt,omitempty" bun:",nullzero"` | ||||||
| 	HideCollections       *bool           `json:"hideCollections"` |  | ||||||
| 	SuspensionOrigin      string          `json:"suspensionOrigin,omitempty" bun:",nullzero"` | 	SuspensionOrigin      string          `json:"suspensionOrigin,omitempty" bun:",nullzero"` | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | type AccountSettings struct { | ||||||
|  | 	AccountID string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -130,13 +130,8 @@ func (c *Converter) ASRepresentationToAccount(ctx context.Context, accountable a | ||||||
| 	// Extract account note (bio / summary). | 	// Extract account note (bio / summary). | ||||||
| 	acct.Note = ap.ExtractSummary(accountable) | 	acct.Note = ap.ExtractSummary(accountable) | ||||||
| 
 | 
 | ||||||
| 	// Assume: | 	// Assume not memorial (todo) | ||||||
| 	// - memorial (TODO) |  | ||||||
| 	// - sensitive (TODO) |  | ||||||
| 	// - hide collections (TODO) |  | ||||||
| 	acct.Memorial = util.Ptr(false) | 	acct.Memorial = util.Ptr(false) | ||||||
| 	acct.Sensitive = util.Ptr(false) |  | ||||||
| 	acct.HideCollections = util.Ptr(false) |  | ||||||
| 
 | 
 | ||||||
| 	// Extract 'manuallyApprovesFollowers' aka locked account (default = true). | 	// Extract 'manuallyApprovesFollowers' aka locked account (default = true). | ||||||
| 	manuallyApprovesFollowers := ap.GetManuallyApprovesFollowers(accountable) | 	manuallyApprovesFollowers := ap.GetManuallyApprovesFollowers(accountable) | ||||||
|  | @ -146,9 +141,6 @@ func (c *Converter) ASRepresentationToAccount(ctx context.Context, accountable a | ||||||
| 	discoverable := ap.GetDiscoverable(accountable) | 	discoverable := ap.GetDiscoverable(accountable) | ||||||
| 	acct.Discoverable = &discoverable | 	acct.Discoverable = &discoverable | ||||||
| 
 | 
 | ||||||
| 	// Assume not an RSS feed. |  | ||||||
| 	acct.EnableRSS = util.Ptr(false) |  | ||||||
| 
 |  | ||||||
| 	// Extract the URL property. | 	// Extract the URL property. | ||||||
| 	urls := ap.GetURL(accountable) | 	urls := ap.GetURL(accountable) | ||||||
| 	if len(urls) == 0 { | 	if len(urls) == 0 { | ||||||
|  |  | ||||||
|  | @ -78,14 +78,14 @@ func (c *Converter) AccountToAPIAccountSensitive(ctx context.Context, a *gtsmode | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	statusContentType := string(apimodel.StatusContentTypeDefault) | 	statusContentType := string(apimodel.StatusContentTypeDefault) | ||||||
| 	if a.StatusContentType != "" { | 	if a.Settings.StatusContentType != "" { | ||||||
| 		statusContentType = a.StatusContentType | 		statusContentType = a.Settings.StatusContentType | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	apiAccount.Source = &apimodel.Source{ | 	apiAccount.Source = &apimodel.Source{ | ||||||
| 		Privacy:             c.VisToAPIVis(ctx, a.Privacy), | 		Privacy:             c.VisToAPIVis(ctx, a.Settings.Privacy), | ||||||
| 		Sensitive:           *a.Sensitive, | 		Sensitive:           *a.Settings.Sensitive, | ||||||
| 		Language:            a.Language, | 		Language:            a.Settings.Language, | ||||||
| 		StatusContentType:   statusContentType, | 		StatusContentType:   statusContentType, | ||||||
| 		Note:                a.NoteRaw, | 		Note:                a.NoteRaw, | ||||||
| 		Fields:              c.fieldsToAPIFields(a.FieldsRaw), | 		Fields:              c.fieldsToAPIFields(a.FieldsRaw), | ||||||
|  | @ -170,10 +170,13 @@ func (c *Converter) AccountToAPIAccountPublic(ctx context.Context, a *gtsmodel.A | ||||||
| 	// Bits that vary between remote + local accounts: | 	// Bits that vary between remote + local accounts: | ||||||
| 	//   - Account (acct) string. | 	//   - Account (acct) string. | ||||||
| 	//   - Role. | 	//   - Role. | ||||||
|  | 	//   - Settings things (enableRSS, customCSS). | ||||||
| 
 | 
 | ||||||
| 	var ( | 	var ( | ||||||
| 		acct      string | 		acct      string | ||||||
| 		role      *apimodel.AccountRole | 		role      *apimodel.AccountRole | ||||||
|  | 		enableRSS bool | ||||||
|  | 		customCSS string | ||||||
| 	) | 	) | ||||||
| 
 | 
 | ||||||
| 	if a.IsRemote() { | 	if a.IsRemote() { | ||||||
|  | @ -203,6 +206,9 @@ func (c *Converter) AccountToAPIAccountPublic(ctx context.Context, a *gtsmodel.A | ||||||
| 			default: | 			default: | ||||||
| 				role = &apimodel.AccountRole{Name: apimodel.AccountRoleUser} | 				role = &apimodel.AccountRole{Name: apimodel.AccountRoleUser} | ||||||
| 			} | 			} | ||||||
|  | 
 | ||||||
|  | 			enableRSS = *a.Settings.EnableRSS | ||||||
|  | 			customCSS = a.Settings.CustomCSS | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		acct = a.Username // omit domain | 		acct = a.Username // omit domain | ||||||
|  | @ -239,7 +245,6 @@ func (c *Converter) AccountToAPIAccountPublic(ctx context.Context, a *gtsmodel.A | ||||||
| 		locked       = boolPtrDef("locked", a.Locked, true) | 		locked       = boolPtrDef("locked", a.Locked, true) | ||||||
| 		discoverable = boolPtrDef("discoverable", a.Discoverable, false) | 		discoverable = boolPtrDef("discoverable", a.Discoverable, false) | ||||||
| 		bot          = boolPtrDef("bot", a.Bot, false) | 		bot          = boolPtrDef("bot", a.Bot, false) | ||||||
| 		enableRSS    = boolPtrDef("enableRSS", a.EnableRSS, false) |  | ||||||
| 	) | 	) | ||||||
| 
 | 
 | ||||||
| 	// Remaining properties are simple and | 	// Remaining properties are simple and | ||||||
|  | @ -267,7 +272,7 @@ func (c *Converter) AccountToAPIAccountPublic(ctx context.Context, a *gtsmodel.A | ||||||
| 		Emojis:         apiEmojis, | 		Emojis:         apiEmojis, | ||||||
| 		Fields:         fields, | 		Fields:         fields, | ||||||
| 		Suspended:      !a.SuspendedAt.IsZero(), | 		Suspended:      !a.SuspendedAt.IsZero(), | ||||||
| 		CustomCSS:      a.CustomCSS, | 		CustomCSS:      customCSS, | ||||||
| 		EnableRSS:      enableRSS, | 		EnableRSS:      enableRSS, | ||||||
| 		Role:           role, | 		Role:           role, | ||||||
| 		Moved:          moved, | 		Moved:          moved, | ||||||
|  | @ -376,6 +381,10 @@ func (c *Converter) AccountToAdminAPIAccount(ctx context.Context, a *gtsmodel.Ac | ||||||
| 		createdByApplicationID string | 		createdByApplicationID string | ||||||
| 	) | 	) | ||||||
| 
 | 
 | ||||||
|  | 	if err := c.state.DB.PopulateAccount(ctx, a); err != nil { | ||||||
|  | 		log.Errorf(ctx, "error(s) populating account, will continue: %s", err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	if a.IsRemote() { | 	if a.IsRemote() { | ||||||
| 		// Domain may be in Punycode, | 		// Domain may be in Punycode, | ||||||
| 		// de-punify it just in case. | 		// de-punify it just in case. | ||||||
|  | @ -404,8 +413,8 @@ func (c *Converter) AccountToAdminAPIAccount(ctx context.Context, a *gtsmodel.Ac | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		locale = user.Locale | 		locale = user.Locale | ||||||
| 		if user.Account.Reason != "" { | 		if a.Settings.Reason != "" { | ||||||
| 			inviteRequest = &user.Account.Reason | 			inviteRequest = &a.Settings.Reason | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if *user.Admin { | 		if *user.Admin { | ||||||
|  |  | ||||||
|  | @ -26,6 +26,7 @@ EXPECT=$(cat << "EOF" | ||||||
|     "cache": { |     "cache": { | ||||||
|         "account-mem-ratio": 5, |         "account-mem-ratio": 5, | ||||||
|         "account-note-mem-ratio": 1, |         "account-note-mem-ratio": 1, | ||||||
|  |         "account-settings-mem-ratio": 0.1, | ||||||
|         "application-mem-ratio": 0.1, |         "application-mem-ratio": 0.1, | ||||||
|         "block-mem-ratio": 3, |         "block-mem-ratio": 3, | ||||||
|         "boost-of-ids-mem-ratio": 3, |         "boost-of-ids-mem-ratio": 3, | ||||||
|  |  | ||||||
|  | @ -70,6 +70,7 @@ var testModels = []interface{}{ | ||||||
| 	>smodel.Report{}, | 	>smodel.Report{}, | ||||||
| 	>smodel.Rule{}, | 	>smodel.Rule{}, | ||||||
| 	>smodel.AccountNote{}, | 	>smodel.AccountNote{}, | ||||||
|  | 	>smodel.AccountSettings{}, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // NewTestDB returns a new initialized, empty database for testing. | // NewTestDB returns a new initialized, empty database for testing. | ||||||
|  | @ -206,6 +207,12 @@ func StandardDBSetup(db db.DB, accounts map[string]*gtsmodel.Account) { | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	for _, v := range NewTestAccountSettings() { | ||||||
|  | 		if err := db.Put(ctx, v); err != nil { | ||||||
|  | 			log.Panic(nil, err) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	for _, v := range NewTestAttachments() { | 	for _, v := range NewTestAttachments() { | ||||||
| 		if err := db.Put(ctx, v); err != nil { | 		if err := db.Put(ctx, v); err != nil { | ||||||
| 			log.Panic(nil, err) | 			log.Panic(nil, err) | ||||||
|  |  | ||||||
|  | @ -286,6 +286,8 @@ func NewTestUsers() map[string]*gtsmodel.User { | ||||||
| 
 | 
 | ||||||
| // NewTestAccounts returns a map of accounts keyed by what type of account they are. | // NewTestAccounts returns a map of accounts keyed by what type of account they are. | ||||||
| func NewTestAccounts() map[string]*gtsmodel.Account { | func NewTestAccounts() map[string]*gtsmodel.Account { | ||||||
|  | 	settings := NewTestAccountSettings() | ||||||
|  | 
 | ||||||
| 	accounts := map[string]*gtsmodel.Account{ | 	accounts := map[string]*gtsmodel.Account{ | ||||||
| 		"instance_account": { | 		"instance_account": { | ||||||
| 			ID:                      "01AY6P665V14JJR0AFVRT7311Y", | 			ID:                      "01AY6P665V14JJR0AFVRT7311Y", | ||||||
|  | @ -301,12 +303,8 @@ func NewTestAccounts() map[string]*gtsmodel.Account { | ||||||
| 			CreatedAt:               TimeMustParse("2020-05-17T13:10:59Z"), | 			CreatedAt:               TimeMustParse("2020-05-17T13:10:59Z"), | ||||||
| 			UpdatedAt:               TimeMustParse("2020-05-17T13:10:59Z"), | 			UpdatedAt:               TimeMustParse("2020-05-17T13:10:59Z"), | ||||||
| 			Bot:                     util.Ptr(false), | 			Bot:                     util.Ptr(false), | ||||||
| 			Reason:                  "", |  | ||||||
| 			Locked:                  util.Ptr(false), | 			Locked:                  util.Ptr(false), | ||||||
| 			Discoverable:            util.Ptr(true), | 			Discoverable:            util.Ptr(true), | ||||||
| 			Privacy:                 gtsmodel.VisibilityPublic, |  | ||||||
| 			Sensitive:               util.Ptr(false), |  | ||||||
| 			Language:                "en", |  | ||||||
| 			URI:                     "http://localhost:8080/users/localhost:8080", | 			URI:                     "http://localhost:8080/users/localhost:8080", | ||||||
| 			URL:                     "http://localhost:8080/@localhost:8080", | 			URL:                     "http://localhost:8080/@localhost:8080", | ||||||
| 			PublicKeyURI:            "http://localhost:8080/users/localhost:8080#main-key", | 			PublicKeyURI:            "http://localhost:8080/users/localhost:8080#main-key", | ||||||
|  | @ -322,9 +320,7 @@ func NewTestAccounts() map[string]*gtsmodel.Account { | ||||||
| 			SensitizedAt:            time.Time{}, | 			SensitizedAt:            time.Time{}, | ||||||
| 			SilencedAt:              time.Time{}, | 			SilencedAt:              time.Time{}, | ||||||
| 			SuspendedAt:             time.Time{}, | 			SuspendedAt:             time.Time{}, | ||||||
| 			HideCollections:         util.Ptr(false), |  | ||||||
| 			SuspensionOrigin:        "", | 			SuspensionOrigin:        "", | ||||||
| 			EnableRSS:               util.Ptr(false), |  | ||||||
| 		}, | 		}, | ||||||
| 		"unconfirmed_account": { | 		"unconfirmed_account": { | ||||||
| 			ID:                      "01F8MH0BBE4FHXPH513MBVFHB0", | 			ID:                      "01F8MH0BBE4FHXPH513MBVFHB0", | ||||||
|  | @ -339,12 +335,8 @@ func NewTestAccounts() map[string]*gtsmodel.Account { | ||||||
| 			CreatedAt:               TimeMustParse("2022-06-04T13:12:00Z"), | 			CreatedAt:               TimeMustParse("2022-06-04T13:12:00Z"), | ||||||
| 			UpdatedAt:               TimeMustParse("2022-06-04T13:12:00Z"), | 			UpdatedAt:               TimeMustParse("2022-06-04T13:12:00Z"), | ||||||
| 			Bot:                     util.Ptr(false), | 			Bot:                     util.Ptr(false), | ||||||
| 			Reason:                  "hi, please let me in! I'm looking for somewhere neato bombeato to hang out.", |  | ||||||
| 			Locked:                  util.Ptr(false), | 			Locked:                  util.Ptr(false), | ||||||
| 			Discoverable:            util.Ptr(false), | 			Discoverable:            util.Ptr(false), | ||||||
| 			Privacy:                 gtsmodel.VisibilityPublic, |  | ||||||
| 			Sensitive:               util.Ptr(false), |  | ||||||
| 			Language:                "en", |  | ||||||
| 			URI:                     "http://localhost:8080/users/weed_lord420", | 			URI:                     "http://localhost:8080/users/weed_lord420", | ||||||
| 			URL:                     "http://localhost:8080/@weed_lord420", | 			URL:                     "http://localhost:8080/@weed_lord420", | ||||||
| 			FetchedAt:               time.Time{}, | 			FetchedAt:               time.Time{}, | ||||||
|  | @ -360,9 +352,8 @@ func NewTestAccounts() map[string]*gtsmodel.Account { | ||||||
| 			SensitizedAt:            time.Time{}, | 			SensitizedAt:            time.Time{}, | ||||||
| 			SilencedAt:              time.Time{}, | 			SilencedAt:              time.Time{}, | ||||||
| 			SuspendedAt:             time.Time{}, | 			SuspendedAt:             time.Time{}, | ||||||
| 			HideCollections:         util.Ptr(false), |  | ||||||
| 			SuspensionOrigin:        "", | 			SuspensionOrigin:        "", | ||||||
| 			EnableRSS:               util.Ptr(false), | 			Settings:                settings["unconfirmed_account"], | ||||||
| 		}, | 		}, | ||||||
| 		"admin_account": { | 		"admin_account": { | ||||||
| 			ID:                      "01F8MH17FWEB39HZJ76B6VXSKF", | 			ID:                      "01F8MH17FWEB39HZJ76B6VXSKF", | ||||||
|  | @ -378,12 +369,8 @@ func NewTestAccounts() map[string]*gtsmodel.Account { | ||||||
| 			CreatedAt:               TimeMustParse("2022-05-17T13:10:59Z"), | 			CreatedAt:               TimeMustParse("2022-05-17T13:10:59Z"), | ||||||
| 			UpdatedAt:               TimeMustParse("2022-05-17T13:10:59Z"), | 			UpdatedAt:               TimeMustParse("2022-05-17T13:10:59Z"), | ||||||
| 			Bot:                     util.Ptr(false), | 			Bot:                     util.Ptr(false), | ||||||
| 			Reason:                  "", |  | ||||||
| 			Locked:                  util.Ptr(false), | 			Locked:                  util.Ptr(false), | ||||||
| 			Discoverable:            util.Ptr(true), | 			Discoverable:            util.Ptr(true), | ||||||
| 			Privacy:                 gtsmodel.VisibilityPublic, |  | ||||||
| 			Sensitive:               util.Ptr(false), |  | ||||||
| 			Language:                "en", |  | ||||||
| 			URI:                     "http://localhost:8080/users/admin", | 			URI:                     "http://localhost:8080/users/admin", | ||||||
| 			URL:                     "http://localhost:8080/@admin", | 			URL:                     "http://localhost:8080/@admin", | ||||||
| 			PublicKeyURI:            "http://localhost:8080/users/admin#main-key", | 			PublicKeyURI:            "http://localhost:8080/users/admin#main-key", | ||||||
|  | @ -399,9 +386,8 @@ func NewTestAccounts() map[string]*gtsmodel.Account { | ||||||
| 			SensitizedAt:            time.Time{}, | 			SensitizedAt:            time.Time{}, | ||||||
| 			SilencedAt:              time.Time{}, | 			SilencedAt:              time.Time{}, | ||||||
| 			SuspendedAt:             time.Time{}, | 			SuspendedAt:             time.Time{}, | ||||||
| 			HideCollections:         util.Ptr(false), |  | ||||||
| 			SuspensionOrigin:        "", | 			SuspensionOrigin:        "", | ||||||
| 			EnableRSS:               util.Ptr(true), | 			Settings:                settings["admin_account"], | ||||||
| 		}, | 		}, | ||||||
| 		"local_account_1": { | 		"local_account_1": { | ||||||
| 			ID:                      "01F8MH1H7YV1Z7D2C8K2730QBF", | 			ID:                      "01F8MH1H7YV1Z7D2C8K2730QBF", | ||||||
|  | @ -417,12 +403,8 @@ func NewTestAccounts() map[string]*gtsmodel.Account { | ||||||
| 			CreatedAt:               TimeMustParse("2022-05-20T11:09:18Z"), | 			CreatedAt:               TimeMustParse("2022-05-20T11:09:18Z"), | ||||||
| 			UpdatedAt:               TimeMustParse("2022-05-20T11:09:18Z"), | 			UpdatedAt:               TimeMustParse("2022-05-20T11:09:18Z"), | ||||||
| 			Bot:                     util.Ptr(false), | 			Bot:                     util.Ptr(false), | ||||||
| 			Reason:                  "I wanna be on this damned webbed site so bad! Please! Wow", |  | ||||||
| 			Locked:                  util.Ptr(false), | 			Locked:                  util.Ptr(false), | ||||||
| 			Discoverable:            util.Ptr(true), | 			Discoverable:            util.Ptr(true), | ||||||
| 			Privacy:                 gtsmodel.VisibilityPublic, |  | ||||||
| 			Sensitive:               util.Ptr(false), |  | ||||||
| 			Language:                "en", |  | ||||||
| 			URI:                     "http://localhost:8080/users/the_mighty_zork", | 			URI:                     "http://localhost:8080/users/the_mighty_zork", | ||||||
| 			URL:                     "http://localhost:8080/@the_mighty_zork", | 			URL:                     "http://localhost:8080/@the_mighty_zork", | ||||||
| 			FetchedAt:               time.Time{}, | 			FetchedAt:               time.Time{}, | ||||||
|  | @ -438,9 +420,8 @@ func NewTestAccounts() map[string]*gtsmodel.Account { | ||||||
| 			SensitizedAt:            time.Time{}, | 			SensitizedAt:            time.Time{}, | ||||||
| 			SilencedAt:              time.Time{}, | 			SilencedAt:              time.Time{}, | ||||||
| 			SuspendedAt:             time.Time{}, | 			SuspendedAt:             time.Time{}, | ||||||
| 			HideCollections:         util.Ptr(false), |  | ||||||
| 			SuspensionOrigin:        "", | 			SuspensionOrigin:        "", | ||||||
| 			EnableRSS:               util.Ptr(true), | 			Settings:                settings["local_account_1"], | ||||||
| 		}, | 		}, | ||||||
| 		"local_account_2": { | 		"local_account_2": { | ||||||
| 			ID:                      "01F8MH5NBDF2MV7CTC4Q5128HF", | 			ID:                      "01F8MH5NBDF2MV7CTC4Q5128HF", | ||||||
|  | @ -475,12 +456,8 @@ func NewTestAccounts() map[string]*gtsmodel.Account { | ||||||
| 			CreatedAt:             TimeMustParse("2022-06-04T13:12:00Z"), | 			CreatedAt:             TimeMustParse("2022-06-04T13:12:00Z"), | ||||||
| 			UpdatedAt:             TimeMustParse("2022-06-04T13:12:00Z"), | 			UpdatedAt:             TimeMustParse("2022-06-04T13:12:00Z"), | ||||||
| 			Bot:                   util.Ptr(false), | 			Bot:                   util.Ptr(false), | ||||||
| 			Reason:                "", |  | ||||||
| 			Locked:                util.Ptr(true), | 			Locked:                util.Ptr(true), | ||||||
| 			Discoverable:          util.Ptr(false), | 			Discoverable:          util.Ptr(false), | ||||||
| 			Privacy:               gtsmodel.VisibilityFollowersOnly, |  | ||||||
| 			Sensitive:             util.Ptr(true), |  | ||||||
| 			Language:              "fr", |  | ||||||
| 			URI:                   "http://localhost:8080/users/1happyturtle", | 			URI:                   "http://localhost:8080/users/1happyturtle", | ||||||
| 			URL:                   "http://localhost:8080/@1happyturtle", | 			URL:                   "http://localhost:8080/@1happyturtle", | ||||||
| 			FetchedAt:             time.Time{}, | 			FetchedAt:             time.Time{}, | ||||||
|  | @ -496,9 +473,8 @@ func NewTestAccounts() map[string]*gtsmodel.Account { | ||||||
| 			SensitizedAt:          time.Time{}, | 			SensitizedAt:          time.Time{}, | ||||||
| 			SilencedAt:            time.Time{}, | 			SilencedAt:            time.Time{}, | ||||||
| 			SuspendedAt:           time.Time{}, | 			SuspendedAt:           time.Time{}, | ||||||
| 			HideCollections:       util.Ptr(false), |  | ||||||
| 			SuspensionOrigin:      "", | 			SuspensionOrigin:      "", | ||||||
| 			EnableRSS:             util.Ptr(false), | 			Settings:              settings["local_account_2"], | ||||||
| 		}, | 		}, | ||||||
| 		"remote_account_1": { | 		"remote_account_1": { | ||||||
| 			ID:                    "01F8MH5ZK5VRH73AKHQM6Y9VNX", | 			ID:                    "01F8MH5ZK5VRH73AKHQM6Y9VNX", | ||||||
|  | @ -514,8 +490,6 @@ func NewTestAccounts() map[string]*gtsmodel.Account { | ||||||
| 			Bot:                   util.Ptr(false), | 			Bot:                   util.Ptr(false), | ||||||
| 			Locked:                util.Ptr(false), | 			Locked:                util.Ptr(false), | ||||||
| 			Discoverable:          util.Ptr(true), | 			Discoverable:          util.Ptr(true), | ||||||
| 			Sensitive:             util.Ptr(false), |  | ||||||
| 			Language:              "en", |  | ||||||
| 			URI:                   "http://fossbros-anonymous.io/users/foss_satan", | 			URI:                   "http://fossbros-anonymous.io/users/foss_satan", | ||||||
| 			URL:                   "http://fossbros-anonymous.io/@foss_satan", | 			URL:                   "http://fossbros-anonymous.io/@foss_satan", | ||||||
| 			FetchedAt:             time.Time{}, | 			FetchedAt:             time.Time{}, | ||||||
|  | @ -532,9 +506,7 @@ func NewTestAccounts() map[string]*gtsmodel.Account { | ||||||
| 			SensitizedAt:          time.Time{}, | 			SensitizedAt:          time.Time{}, | ||||||
| 			SilencedAt:            time.Time{}, | 			SilencedAt:            time.Time{}, | ||||||
| 			SuspendedAt:           time.Time{}, | 			SuspendedAt:           time.Time{}, | ||||||
| 			HideCollections:       util.Ptr(false), |  | ||||||
| 			SuspensionOrigin:      "", | 			SuspensionOrigin:      "", | ||||||
| 			EnableRSS:             util.Ptr(false), |  | ||||||
| 		}, | 		}, | ||||||
| 		"remote_account_2": { | 		"remote_account_2": { | ||||||
| 			ID:                    "01FHMQX3GAABWSM0S2VZEC2SWC", | 			ID:                    "01FHMQX3GAABWSM0S2VZEC2SWC", | ||||||
|  | @ -550,8 +522,6 @@ func NewTestAccounts() map[string]*gtsmodel.Account { | ||||||
| 			Bot:                   util.Ptr(false), | 			Bot:                   util.Ptr(false), | ||||||
| 			Locked:                util.Ptr(true), | 			Locked:                util.Ptr(true), | ||||||
| 			Discoverable:          util.Ptr(true), | 			Discoverable:          util.Ptr(true), | ||||||
| 			Sensitive:             util.Ptr(false), |  | ||||||
| 			Language:              "en", |  | ||||||
| 			URI:                   "http://example.org/users/Some_User", | 			URI:                   "http://example.org/users/Some_User", | ||||||
| 			URL:                   "http://example.org/@Some_User", | 			URL:                   "http://example.org/@Some_User", | ||||||
| 			FetchedAt:             time.Time{}, | 			FetchedAt:             time.Time{}, | ||||||
|  | @ -568,9 +538,7 @@ func NewTestAccounts() map[string]*gtsmodel.Account { | ||||||
| 			SensitizedAt:          time.Time{}, | 			SensitizedAt:          time.Time{}, | ||||||
| 			SilencedAt:            time.Time{}, | 			SilencedAt:            time.Time{}, | ||||||
| 			SuspendedAt:           time.Time{}, | 			SuspendedAt:           time.Time{}, | ||||||
| 			HideCollections:       util.Ptr(false), |  | ||||||
| 			SuspensionOrigin:      "", | 			SuspensionOrigin:      "", | ||||||
| 			EnableRSS:             util.Ptr(false), |  | ||||||
| 		}, | 		}, | ||||||
| 		"remote_account_3": { | 		"remote_account_3": { | ||||||
| 			ID:                      "062G5WYKY35KKD12EMSM3F8PJ8", | 			ID:                      "062G5WYKY35KKD12EMSM3F8PJ8", | ||||||
|  | @ -586,8 +554,6 @@ func NewTestAccounts() map[string]*gtsmodel.Account { | ||||||
| 			Bot:                     util.Ptr(false), | 			Bot:                     util.Ptr(false), | ||||||
| 			Locked:                  util.Ptr(true), | 			Locked:                  util.Ptr(true), | ||||||
| 			Discoverable:            util.Ptr(true), | 			Discoverable:            util.Ptr(true), | ||||||
| 			Sensitive:               util.Ptr(false), |  | ||||||
| 			Language:                "en", |  | ||||||
| 			URI:                     "http://thequeenisstillalive.technology/users/her_fuckin_maj", | 			URI:                     "http://thequeenisstillalive.technology/users/her_fuckin_maj", | ||||||
| 			URL:                     "http://thequeenisstillalive.technology/@her_fuckin_maj", | 			URL:                     "http://thequeenisstillalive.technology/@her_fuckin_maj", | ||||||
| 			FetchedAt:               time.Time{}, | 			FetchedAt:               time.Time{}, | ||||||
|  | @ -604,10 +570,8 @@ func NewTestAccounts() map[string]*gtsmodel.Account { | ||||||
| 			SensitizedAt:            time.Time{}, | 			SensitizedAt:            time.Time{}, | ||||||
| 			SilencedAt:              time.Time{}, | 			SilencedAt:              time.Time{}, | ||||||
| 			SuspendedAt:             time.Time{}, | 			SuspendedAt:             time.Time{}, | ||||||
| 			HideCollections:         util.Ptr(false), |  | ||||||
| 			SuspensionOrigin:        "", | 			SuspensionOrigin:        "", | ||||||
| 			HeaderMediaAttachmentID: "01PFPMWK2FF0D9WMHEJHR07C3R", | 			HeaderMediaAttachmentID: "01PFPMWK2FF0D9WMHEJHR07C3R", | ||||||
| 			EnableRSS:               util.Ptr(false), |  | ||||||
| 		}, | 		}, | ||||||
| 		"remote_account_4": { | 		"remote_account_4": { | ||||||
| 			ID:                      "07GZRBAEMBNKGZ8Z9VSKSXKR98", | 			ID:                      "07GZRBAEMBNKGZ8Z9VSKSXKR98", | ||||||
|  | @ -622,8 +586,6 @@ func NewTestAccounts() map[string]*gtsmodel.Account { | ||||||
| 			Bot:                     util.Ptr(false), | 			Bot:                     util.Ptr(false), | ||||||
| 			Locked:                  util.Ptr(false), | 			Locked:                  util.Ptr(false), | ||||||
| 			Discoverable:            util.Ptr(false), | 			Discoverable:            util.Ptr(false), | ||||||
| 			Sensitive:               util.Ptr(false), |  | ||||||
| 			Language:                "de", |  | ||||||
| 			URI:                     "https://xn--xample-ova.org/users/%C3%BCser", | 			URI:                     "https://xn--xample-ova.org/users/%C3%BCser", | ||||||
| 			URL:                     "https://xn--xample-ova.org/users/@%C3%BCser", | 			URL:                     "https://xn--xample-ova.org/users/@%C3%BCser", | ||||||
| 			FetchedAt:               time.Time{}, | 			FetchedAt:               time.Time{}, | ||||||
|  | @ -640,10 +602,8 @@ func NewTestAccounts() map[string]*gtsmodel.Account { | ||||||
| 			SensitizedAt:            time.Time{}, | 			SensitizedAt:            time.Time{}, | ||||||
| 			SilencedAt:              time.Time{}, | 			SilencedAt:              time.Time{}, | ||||||
| 			SuspendedAt:             time.Time{}, | 			SuspendedAt:             time.Time{}, | ||||||
| 			HideCollections:         util.Ptr(false), |  | ||||||
| 			SuspensionOrigin:        "", | 			SuspensionOrigin:        "", | ||||||
| 			HeaderMediaAttachmentID: "", | 			HeaderMediaAttachmentID: "", | ||||||
| 			EnableRSS:               util.Ptr(false), |  | ||||||
| 		}, | 		}, | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -698,6 +658,55 @@ func NewTestAccounts() map[string]*gtsmodel.Account { | ||||||
| 	return accounts | 	return accounts | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func NewTestAccountSettings() map[string]*gtsmodel.AccountSettings { | ||||||
|  | 	return map[string]*gtsmodel.AccountSettings{ | ||||||
|  | 		"unconfirmed_account": { | ||||||
|  | 			AccountID:       "01F8MH0BBE4FHXPH513MBVFHB0", | ||||||
|  | 			CreatedAt:       TimeMustParse("2022-06-04T13:12:00Z"), | ||||||
|  | 			UpdatedAt:       TimeMustParse("2022-06-04T13:12:00Z"), | ||||||
|  | 			Reason:          "hi, please let me in! I'm looking for somewhere neato bombeato to hang out.", | ||||||
|  | 			Privacy:         gtsmodel.VisibilityPublic, | ||||||
|  | 			Sensitive:       util.Ptr(false), | ||||||
|  | 			Language:        "en", | ||||||
|  | 			EnableRSS:       util.Ptr(false), | ||||||
|  | 			HideCollections: util.Ptr(false), | ||||||
|  | 		}, | ||||||
|  | 		"admin_account": { | ||||||
|  | 			AccountID:       "01F8MH17FWEB39HZJ76B6VXSKF", | ||||||
|  | 			CreatedAt:       TimeMustParse("2022-05-17T13:10:59Z"), | ||||||
|  | 			UpdatedAt:       TimeMustParse("2022-05-17T13:10:59Z"), | ||||||
|  | 			Reason:          "", | ||||||
|  | 			Privacy:         gtsmodel.VisibilityPublic, | ||||||
|  | 			Sensitive:       util.Ptr(false), | ||||||
|  | 			Language:        "en", | ||||||
|  | 			EnableRSS:       util.Ptr(true), | ||||||
|  | 			HideCollections: util.Ptr(false), | ||||||
|  | 		}, | ||||||
|  | 		"local_account_1": { | ||||||
|  | 			AccountID:       "01F8MH1H7YV1Z7D2C8K2730QBF", | ||||||
|  | 			CreatedAt:       TimeMustParse("2022-05-20T11:09:18Z"), | ||||||
|  | 			UpdatedAt:       TimeMustParse("2022-05-20T11:09:18Z"), | ||||||
|  | 			Reason:          "I wanna be on this damned webbed site so bad! Please! Wow", | ||||||
|  | 			Privacy:         gtsmodel.VisibilityPublic, | ||||||
|  | 			Sensitive:       util.Ptr(false), | ||||||
|  | 			Language:        "en", | ||||||
|  | 			EnableRSS:       util.Ptr(true), | ||||||
|  | 			HideCollections: util.Ptr(false), | ||||||
|  | 		}, | ||||||
|  | 		"local_account_2": { | ||||||
|  | 			AccountID:       "01F8MH5NBDF2MV7CTC4Q5128HF", | ||||||
|  | 			CreatedAt:       TimeMustParse("2022-06-04T13:12:00Z"), | ||||||
|  | 			UpdatedAt:       TimeMustParse("2022-06-04T13:12:00Z"), | ||||||
|  | 			Reason:          "", | ||||||
|  | 			Privacy:         gtsmodel.VisibilityFollowersOnly, | ||||||
|  | 			Sensitive:       util.Ptr(true), | ||||||
|  | 			Language:        "fr", | ||||||
|  | 			EnableRSS:       util.Ptr(false), | ||||||
|  | 			HideCollections: util.Ptr(false), | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func NewTestTombstones() map[string]*gtsmodel.Tombstone { | func NewTestTombstones() map[string]*gtsmodel.Tombstone { | ||||||
| 	return map[string]*gtsmodel.Tombstone{ | 	return map[string]*gtsmodel.Tombstone{ | ||||||
| 		"https://somewhere.mysterious/users/rest_in_piss#main-key": { | 		"https://somewhere.mysterious/users/rest_in_piss#main-key": { | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue