[chore/refactor] start moving account preferences to its own separate struct
This commit is contained in:
parent
9770d54237
commit
8a36e11957
|
@ -398,9 +398,6 @@ func (suite *InboxPostTestSuite) TestPostUpdate() {
|
||||||
suite.EqualValues(requestingAccount.Reason, dbUpdatedAccount.Reason)
|
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 +411,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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -466,7 +462,6 @@ func (suite *InboxPostTestSuite) TestPostDelete() {
|
||||||
suite.Empty(dbAccount.HeaderRemoteURL)
|
suite.Empty(dbAccount.HeaderRemoteURL)
|
||||||
suite.Empty(dbAccount.Reason)
|
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)
|
||||||
|
|
|
@ -477,7 +477,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]"], dbAccount.StatusContentType)
|
suite.Equal(data["source[status_content_type]"], dbAccount.Preferences.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(5, apimodelAccount.StatusesCount)
|
suite.Equal(5, 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.Preferences.StatusLanguage, apimodelAccount.Source.Language)
|
||||||
suite.Equal(testAccount.NoteRaw, apimodelAccount.Source.Note)
|
suite.Equal(testAccount.NoteRaw, apimodelAccount.Source.Note)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -229,6 +229,7 @@ func (c *Caches) setuphooks() {
|
||||||
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.AccountPreferences().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)
|
||||||
|
|
|
@ -32,6 +32,7 @@ import (
|
||||||
type GTSCaches struct {
|
type GTSCaches struct {
|
||||||
account *result.Cache[*gtsmodel.Account]
|
account *result.Cache[*gtsmodel.Account]
|
||||||
accountNote *result.Cache[*gtsmodel.AccountNote]
|
accountNote *result.Cache[*gtsmodel.AccountNote]
|
||||||
|
accountPreferences *result.Cache[*gtsmodel.AccountPreferences]
|
||||||
block *result.Cache[*gtsmodel.Block]
|
block *result.Cache[*gtsmodel.Block]
|
||||||
blockIDs *SliceCache[string]
|
blockIDs *SliceCache[string]
|
||||||
boostOfIDs *SliceCache[string]
|
boostOfIDs *SliceCache[string]
|
||||||
|
@ -67,6 +68,7 @@ type GTSCaches struct {
|
||||||
func (c *GTSCaches) Init() {
|
func (c *GTSCaches) Init() {
|
||||||
c.initAccount()
|
c.initAccount()
|
||||||
c.initAccountNote()
|
c.initAccountNote()
|
||||||
|
c.initAccountPreferences()
|
||||||
c.initBlock()
|
c.initBlock()
|
||||||
c.initBlockIDs()
|
c.initBlockIDs()
|
||||||
c.initBoostOfIDs()
|
c.initBoostOfIDs()
|
||||||
|
@ -117,6 +119,11 @@ func (c *GTSCaches) AccountNote() *result.Cache[*gtsmodel.AccountNote] {
|
||||||
return c.accountNote
|
return c.accountNote
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AccountPreferences provides access to the gtsmodel AccountPreferences database cache.
|
||||||
|
func (c *GTSCaches) AccountPreferences() *result.Cache[*gtsmodel.AccountPreferences] {
|
||||||
|
return c.accountPreferences
|
||||||
|
}
|
||||||
|
|
||||||
// Block provides access to the gtsmodel Block (account) database cache.
|
// Block provides access to the gtsmodel Block (account) database cache.
|
||||||
func (c *GTSCaches) Block() *result.Cache[*gtsmodel.Block] {
|
func (c *GTSCaches) Block() *result.Cache[*gtsmodel.Block] {
|
||||||
return c.block
|
return c.block
|
||||||
|
@ -303,6 +310,26 @@ func (c *GTSCaches) initAccountNote() {
|
||||||
c.accountNote.IgnoreErrors(ignoreErrors)
|
c.accountNote.IgnoreErrors(ignoreErrors)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *GTSCaches) initAccountPreferences() {
|
||||||
|
// Calculate maximum cache size.
|
||||||
|
cap := calculateResultCacheMax(
|
||||||
|
sizeofAccountPreferences(), // model in-mem size.
|
||||||
|
config.GetCacheAccountPreferencesMemRatio(),
|
||||||
|
)
|
||||||
|
log.Infof(nil, "AccountPreferences cache size = %d", cap)
|
||||||
|
|
||||||
|
c.accountPreferences = result.New([]result.Lookup{
|
||||||
|
{Name: "ID"},
|
||||||
|
{Name: "AccountID"},
|
||||||
|
}, func(p1 *gtsmodel.AccountPreferences) *gtsmodel.AccountPreferences {
|
||||||
|
p2 := new(gtsmodel.AccountPreferences)
|
||||||
|
*p2 = *p1
|
||||||
|
return p2
|
||||||
|
}, cap)
|
||||||
|
|
||||||
|
c.accountPreferences.IgnoreErrors(ignoreErrors)
|
||||||
|
}
|
||||||
|
|
||||||
func (c *GTSCaches) initBlock() {
|
func (c *GTSCaches) initBlock() {
|
||||||
// Calculate maximum cache size.
|
// Calculate maximum cache size.
|
||||||
cap := calculateResultCacheMax(
|
cap := calculateResultCacheMax(
|
||||||
|
|
|
@ -155,6 +155,7 @@ func totalOfRatios() float64 {
|
||||||
return 0 +
|
return 0 +
|
||||||
config.GetCacheAccountMemRatio() +
|
config.GetCacheAccountMemRatio() +
|
||||||
config.GetCacheAccountNoteMemRatio() +
|
config.GetCacheAccountNoteMemRatio() +
|
||||||
|
config.GetCacheAccountPreferencesMemRatio() +
|
||||||
config.GetCacheBlockMemRatio() +
|
config.GetCacheBlockMemRatio() +
|
||||||
config.GetCacheBlockIDsMemRatio() +
|
config.GetCacheBlockIDsMemRatio() +
|
||||||
config.GetCacheBoostOfIDsMemRatio() +
|
config.GetCacheBoostOfIDsMemRatio() +
|
||||||
|
@ -199,9 +200,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,
|
||||||
|
@ -216,9 +214,7 @@ func sizeofAccount() uintptr {
|
||||||
SensitizedAt: time.Time{},
|
SensitizedAt: time.Time{},
|
||||||
SilencedAt: time.Now(),
|
SilencedAt: time.Now(),
|
||||||
SuspendedAt: time.Now(),
|
SuspendedAt: time.Now(),
|
||||||
HideCollections: func() *bool { ok := true; return &ok }(),
|
|
||||||
SuspensionOrigin: "",
|
SuspensionOrigin: "",
|
||||||
EnableRSS: func() *bool { ok := true; return &ok }(),
|
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -231,6 +227,19 @@ func sizeofAccountNote() uintptr {
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func sizeofAccountPreferences() uintptr {
|
||||||
|
return uintptr(size.Of(>smodel.AccountPreferences{
|
||||||
|
ID: exampleID,
|
||||||
|
AccountID: exampleID,
|
||||||
|
StatusLanguage: "fr",
|
||||||
|
StatusPrivacy: gtsmodel.VisibilityFollowersOnly,
|
||||||
|
StatusSensitive: func() *bool { ok := true; return &ok }(),
|
||||||
|
StatusContentType: "text/plain",
|
||||||
|
HideCollections: func() *bool { ok := true; return &ok }(),
|
||||||
|
EnableRSS: func() *bool { ok := true; return &ok }(),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
func sizeofBlock() uintptr {
|
func sizeofBlock() uintptr {
|
||||||
return uintptr(size.Of(>smodel.Block{
|
return uintptr(size.Of(>smodel.Block{
|
||||||
ID: exampleID,
|
ID: exampleID,
|
||||||
|
|
|
@ -178,6 +178,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"`
|
||||||
|
AccountPreferencesMemRatio float64 `name:"account-preferences-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"`
|
||||||
BoostOfIDsMemRatio float64 `name:"boost-of-ids-mem-ratio"`
|
BoostOfIDsMemRatio float64 `name:"boost-of-ids-mem-ratio"`
|
||||||
|
|
|
@ -147,6 +147,7 @@ var Defaults = Configuration{
|
||||||
// be able to make some more sense :D
|
// be able to make some more sense :D
|
||||||
AccountMemRatio: 18,
|
AccountMemRatio: 18,
|
||||||
AccountNoteMemRatio: 0.1,
|
AccountNoteMemRatio: 0.1,
|
||||||
|
AccountPreferencesMemRatio: 2,
|
||||||
BlockMemRatio: 3,
|
BlockMemRatio: 3,
|
||||||
BlockIDsMemRatio: 3,
|
BlockIDsMemRatio: 3,
|
||||||
BoostOfIDsMemRatio: 3,
|
BoostOfIDsMemRatio: 3,
|
||||||
|
|
|
@ -2499,6 +2499,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) }
|
||||||
|
|
||||||
|
// GetCacheAccountPreferencesMemRatio safely fetches the Configuration value for state's 'Cache.AccountPreferencesMemRatio' field
|
||||||
|
func (st *ConfigState) GetCacheAccountPreferencesMemRatio() (v float64) {
|
||||||
|
st.mutex.RLock()
|
||||||
|
v = st.config.Cache.AccountPreferencesMemRatio
|
||||||
|
st.mutex.RUnlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetCacheAccountPreferencesMemRatio safely sets the Configuration value for state's 'Cache.AccountPreferencesMemRatio' field
|
||||||
|
func (st *ConfigState) SetCacheAccountPreferencesMemRatio(v float64) {
|
||||||
|
st.mutex.Lock()
|
||||||
|
defer st.mutex.Unlock()
|
||||||
|
st.config.Cache.AccountPreferencesMemRatio = v
|
||||||
|
st.reloadToViper()
|
||||||
|
}
|
||||||
|
|
||||||
|
// CacheAccountPreferencesMemRatioFlag returns the flag name for the 'Cache.AccountPreferencesMemRatio' field
|
||||||
|
func CacheAccountPreferencesMemRatioFlag() string { return "cache-account-preferences-mem-ratio" }
|
||||||
|
|
||||||
|
// GetCacheAccountPreferencesMemRatio safely fetches the value for global configuration 'Cache.AccountPreferencesMemRatio' field
|
||||||
|
func GetCacheAccountPreferencesMemRatio() float64 { return global.GetCacheAccountPreferencesMemRatio() }
|
||||||
|
|
||||||
|
// SetCacheAccountPreferencesMemRatio safely sets the value for global configuration 'Cache.AccountPreferencesMemRatio' field
|
||||||
|
func SetCacheAccountPreferencesMemRatio(v float64) { global.SetCacheAccountPreferencesMemRatio(v) }
|
||||||
|
|
||||||
// GetCacheBlockMemRatio safely fetches the Configuration value for state's 'Cache.BlockMemRatio' field
|
// GetCacheBlockMemRatio safely fetches the Configuration value for state's 'Cache.BlockMemRatio' field
|
||||||
func (st *ConfigState) GetCacheBlockMemRatio() (v float64) {
|
func (st *ConfigState) GetCacheBlockMemRatio() (v float64) {
|
||||||
st.mutex.RLock()
|
st.mutex.RLock()
|
||||||
|
|
|
@ -70,6 +70,16 @@ type Account interface {
|
||||||
// GetAccountCustomCSSByUsername returns the custom css of an account on this instance with the given username.
|
// GetAccountCustomCSSByUsername returns the custom css of an account on this instance with the given username.
|
||||||
GetAccountCustomCSSByUsername(ctx context.Context, username string) (string, error)
|
GetAccountCustomCSSByUsername(ctx context.Context, username string) (string, error)
|
||||||
|
|
||||||
|
// GetAccountPreferencesByAccountID returns preferences the the given *LOCAL* account.
|
||||||
|
// Will return an error for non-local accounts, or accounts with no preferences stored.
|
||||||
|
GetAccountPreferencesByAccountID(ctx context.Context, accountID string) (*gtsmodel.AccountPreferences, error)
|
||||||
|
|
||||||
|
// PutAccountPreferences inserts the given accountPreferences.
|
||||||
|
PutAccountPreferences(ctx context.Context, accountPreferences *gtsmodel.AccountPreferences) error
|
||||||
|
|
||||||
|
// UpdateAccountPreferences updates the given accountPreferences by ID.
|
||||||
|
UpdateAccountPreferences(ctx context.Context, accountPreferences *gtsmodel.AccountPreferences, columns ...string) error
|
||||||
|
|
||||||
// GetAccountFaves fetches faves/likes created by the target accountID.
|
// GetAccountFaves fetches faves/likes created by the target accountID.
|
||||||
GetAccountFaves(ctx context.Context, accountID string) ([]*gtsmodel.StatusFave, error)
|
GetAccountFaves(ctx context.Context, accountID string) ([]*gtsmodel.StatusFave, error)
|
||||||
|
|
||||||
|
|
|
@ -290,6 +290,16 @@ func (a *accountDB) PopulateAccount(ctx context.Context, account *gtsmodel.Accou
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if account.PreferencesID != "" && account.Preferences == nil {
|
||||||
|
account.Preferences, err = a.getAccountPreferencesByID(
|
||||||
|
ctx, // these are already barebones
|
||||||
|
account.PreferencesID,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
errs.Appendf("error populating account preferences: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return errs.Combine()
|
return errs.Combine()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -456,12 +466,93 @@ 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) {
|
||||||
account, err := a.GetAccountByUsernameDomain(ctx, username, "")
|
account, err := a.GetAccountByUsernameDomain(
|
||||||
|
gtscontext.SetBarebones(ctx),
|
||||||
|
username,
|
||||||
|
"",
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
return account.CustomCSS, nil
|
if account.PreferencesID == "" {
|
||||||
|
return "", gtserror.Newf("no preferences stored for account %s", account.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
accountPrefs, err := a.state.DB.GetAccountPreferencesByAccountID(ctx, account.ID)
|
||||||
|
if err != nil {
|
||||||
|
return "", gtserror.Newf("error getting preferences for account %s: %w", account.ID, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return accountPrefs.CustomCSS, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *accountDB) GetAccountPreferencesByAccountID(ctx context.Context, accountID string) (*gtsmodel.AccountPreferences, error) {
|
||||||
|
accountPrefs, err := a.state.Caches.GTS.AccountPreferences().Load("AccountID", func() (*gtsmodel.AccountPreferences, error) {
|
||||||
|
var accountPrefs gtsmodel.AccountPreferences
|
||||||
|
|
||||||
|
// Not cached! Perform database query
|
||||||
|
if err := a.db.NewSelect().
|
||||||
|
Model(&accountPrefs).
|
||||||
|
Where("? = ?", bun.Ident("account_preferences.account_id"), accountID).
|
||||||
|
Scan(ctx); err != nil {
|
||||||
|
return nil, a.db.ProcessError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &accountPrefs, nil
|
||||||
|
}, accountID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return accountPrefs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *accountDB) getAccountPreferencesByID(ctx context.Context, id string) (*gtsmodel.AccountPreferences, error) {
|
||||||
|
accountPrefs, err := a.state.Caches.GTS.AccountPreferences().Load("ID", func() (*gtsmodel.AccountPreferences, error) {
|
||||||
|
var accountPrefs gtsmodel.AccountPreferences
|
||||||
|
|
||||||
|
// Not cached! Perform database query
|
||||||
|
if err := a.db.NewSelect().
|
||||||
|
Model(&accountPrefs).
|
||||||
|
Where("? = ?", bun.Ident("account_preferences.id"), id).
|
||||||
|
Scan(ctx); err != nil {
|
||||||
|
return nil, a.db.ProcessError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &accountPrefs, nil
|
||||||
|
}, id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return accountPrefs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *accountDB) PutAccountPreferences(ctx context.Context, accountPrefs *gtsmodel.AccountPreferences) error {
|
||||||
|
return a.state.Caches.GTS.AccountPreferences().Store(accountPrefs, func() error {
|
||||||
|
// insert the account preferences
|
||||||
|
_, err := a.db.NewInsert().Model(accountPrefs).Exec(ctx)
|
||||||
|
return a.db.ProcessError(err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *accountDB) UpdateAccountPreferences(ctx context.Context, accountPrefs *gtsmodel.AccountPreferences, columns ...string) error {
|
||||||
|
accountPrefs.UpdatedAt = time.Now()
|
||||||
|
if len(columns) > 0 {
|
||||||
|
// If we're updating by column, ensure "updated_at" is included.
|
||||||
|
columns = append(columns, "updated_at")
|
||||||
|
}
|
||||||
|
|
||||||
|
return a.state.Caches.GTS.AccountPreferences().Store(accountPrefs, func() error {
|
||||||
|
_, err := a.db.
|
||||||
|
NewUpdate().
|
||||||
|
Model(accountPrefs).
|
||||||
|
Where("? = ?", bun.Ident("account_preferences.id"), accountPrefs.ID).
|
||||||
|
Column(columns...).
|
||||||
|
Exec(ctx)
|
||||||
|
return a.db.ProcessError(err)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *accountDB) GetAccountsUsingEmoji(ctx context.Context, emojiID string) ([]*gtsmodel.Account, error) {
|
func (a *accountDB) GetAccountsUsingEmoji(ctx context.Context, emojiID string) ([]*gtsmodel.Account, error) {
|
||||||
|
|
|
@ -333,14 +333,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.False(*newAccount.Memorial)
|
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() {
|
||||||
|
|
|
@ -30,6 +30,7 @@ import (
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/ap"
|
"github.com/superseriousbusiness/gotosocial/internal/ap"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/gtscontext"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/id"
|
"github.com/superseriousbusiness/gotosocial/internal/id"
|
||||||
|
@ -95,7 +96,10 @@ func (a *adminDB) NewSignup(ctx context.Context, newSignup gtsmodel.NewSignup) (
|
||||||
// If something went wrong previously while doing a new
|
// If something went wrong previously while doing a new
|
||||||
// sign up with this username, we might already have an
|
// sign up with this username, we might already have an
|
||||||
// account, so check first.
|
// account, so check first.
|
||||||
account, err := a.state.DB.GetAccountByUsernameDomain(ctx, newSignup.Username, "")
|
account, err := a.state.DB.GetAccountByUsernameDomain(
|
||||||
|
gtscontext.SetBarebones(ctx),
|
||||||
|
newSignup.Username, "",
|
||||||
|
)
|
||||||
if err != nil && !errors.Is(err, db.ErrNoEntries) {
|
if err != nil && !errors.Is(err, db.ErrNoEntries) {
|
||||||
// Real error occurred.
|
// Real error occurred.
|
||||||
err := gtserror.Newf("error checking for existing account: %w", err)
|
err := gtserror.Newf("error checking for existing account: %w", err)
|
||||||
|
@ -124,7 +128,6 @@ func (a *adminDB) NewSignup(ctx context.Context, newSignup gtsmodel.NewSignup) (
|
||||||
Username: newSignup.Username,
|
Username: newSignup.Username,
|
||||||
DisplayName: newSignup.Username,
|
DisplayName: newSignup.Username,
|
||||||
Reason: newSignup.Reason,
|
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 +139,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,
|
||||||
|
PreferencesID: id.NewULID(),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert the new account!
|
// Insert the new account!
|
||||||
|
@ -145,6 +149,33 @@ func (a *adminDB) NewSignup(ctx context.Context, newSignup gtsmodel.NewSignup) (
|
||||||
}
|
}
|
||||||
|
|
||||||
// Created or already had an account.
|
// Created or already had an account.
|
||||||
|
// Create account preferences
|
||||||
|
// if not done already.
|
||||||
|
accountPrefs, err := a.state.DB.GetAccountPreferencesByAccountID(ctx, account.ID)
|
||||||
|
if err != nil && !errors.Is(err, db.ErrNoEntries) {
|
||||||
|
// Real error.
|
||||||
|
err := gtserror.Newf("error checking for existing account preferences: %w", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if accountPrefs == nil {
|
||||||
|
// No preferences created for
|
||||||
|
// this account yet, do it now.
|
||||||
|
accountPrefs = >smodel.AccountPreferences{
|
||||||
|
ID: account.PreferencesID,
|
||||||
|
AccountID: account.ID,
|
||||||
|
StatusPrivacy: gtsmodel.VisibilityDefault,
|
||||||
|
StatusLanguage: newSignup.Locale,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := a.state.DB.PutAccountPreferences(ctx, accountPrefs); err != nil {
|
||||||
|
err := gtserror.Newf("db error inserting account preferences: %w", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
account.Preferences = accountPrefs
|
||||||
|
}
|
||||||
|
|
||||||
// Ensure user not already created.
|
// Ensure user not already created.
|
||||||
user, err := a.state.DB.GetUserByAccountID(ctx, account.ID)
|
user, err := a.state.DB.GetUserByAccountID(ctx, account.ID)
|
||||||
if err != nil && !errors.Is(err, db.ErrNoEntries) {
|
if err != nil && !errors.Is(err, db.ErrNoEntries) {
|
||||||
|
|
|
@ -94,10 +94,6 @@ func (suite *BasicTestSuite) TestPutAccountWithBunDefaultFields() {
|
||||||
// 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 +109,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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -47,8 +47,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?
|
||||||
|
@ -58,11 +58,6 @@ type Account struct {
|
||||||
Reason string `bun:""` // What reason was given for signing up when this account was created?
|
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
|
||||||
|
@ -78,9 +73,9 @@ 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
|
PreferencesID string `bun:"type:CHAR(26),nullzero"` // ID of the LocalAccountPreferences entry for this account. Only set for local accounts.
|
||||||
|
Preferences *AccountPreferences `bun:"-"` // Preferences corresponding to PreferencesID. Local accounts only.
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsLocal returns whether account is a local user account.
|
// IsLocal returns whether account is a local user account.
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
// 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"
|
||||||
|
|
||||||
|
type AccountPreferences struct {
|
||||||
|
ID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // ID of this item in the database.
|
||||||
|
CreatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // Creation time of this item.
|
||||||
|
UpdatedAt time.Time `bun:"type:timestamptz,nullzero,notnull,default:current_timestamp"` // Last updated time of this time.
|
||||||
|
AccountID string `bun:"type:CHAR(26),pk,nullzero,notnull,unique"` // ID of the account to which these preferences correspond.
|
||||||
|
StatusLanguage string `bun:",nullzero,notnull,default:'en'"` // Default post language for this account.
|
||||||
|
StatusPrivacy Visibility `bun:",nullzero"` // Default post privacy for this account.
|
||||||
|
StatusSensitive *bool `bun:",nullzero,notnull,default:false"` // Set posts from this account to sensitive by default.
|
||||||
|
StatusContentType string `bun:",nullzero"` // Default format for statuses posted by this account.
|
||||||
|
HideCollections *bool `bun:",nullzero,notnull,default:false"` // Hide this account's collections.
|
||||||
|
EnableRSS *bool `bun:",nullzero,notnull,default:false"` // Enable RSS feed subscription for this account's public posts at [URL]/feed
|
||||||
|
CustomCSS string `bun:",nullzero"` // Custom CSS that should be displayed for this Account's profile and statuses.
|
||||||
|
}
|
|
@ -114,14 +114,6 @@ func (c *converter) ASRepresentationToAccount(ctx context.Context, accountable a
|
||||||
memorial := false
|
memorial := false
|
||||||
acct.Memorial = &memorial
|
acct.Memorial = &memorial
|
||||||
|
|
||||||
// assume not sensitive (todo)
|
|
||||||
sensitive := false
|
|
||||||
acct.Sensitive = &sensitive
|
|
||||||
|
|
||||||
// assume not hide collections (todo)
|
|
||||||
hideCollections := false
|
|
||||||
acct.HideCollections = &hideCollections
|
|
||||||
|
|
||||||
// locked aka manuallyApprovesFollowers
|
// locked aka manuallyApprovesFollowers
|
||||||
locked := true
|
locked := true
|
||||||
acct.Locked = &locked // assume locked by default
|
acct.Locked = &locked // assume locked by default
|
||||||
|
@ -140,10 +132,6 @@ func (c *converter) ASRepresentationToAccount(ctx context.Context, accountable a
|
||||||
acct.Discoverable = &d
|
acct.Discoverable = &d
|
||||||
}
|
}
|
||||||
|
|
||||||
// assume not rss feed
|
|
||||||
enableRSS := false
|
|
||||||
acct.EnableRSS = &enableRSS
|
|
||||||
|
|
||||||
// url property
|
// url property
|
||||||
url, err := ap.ExtractURL(accountable)
|
url, err := ap.ExtractURL(accountable)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
|
|
@ -59,29 +59,47 @@ func toMastodonVersion(in string) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *converter) AccountToAPIAccountSensitive(ctx context.Context, a *gtsmodel.Account) (*apimodel.Account, error) {
|
func (c *converter) AccountToAPIAccountSensitive(ctx context.Context, a *gtsmodel.Account) (*apimodel.Account, error) {
|
||||||
// we can build this sensitive account easily by first getting the public account....
|
// Build sensitive view of account by first
|
||||||
|
// getting the public account and then adding
|
||||||
|
// additional information for the eyes of this
|
||||||
|
// account owner only.
|
||||||
apiAccount, err := c.AccountToAPIAccountPublic(ctx, a)
|
apiAccount, err := c.AccountToAPIAccountPublic(ctx, a)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// then adding the Source object to it...
|
// Ensure account preferences populated.
|
||||||
|
// Accounts passed in to this function
|
||||||
// check pending follow requests aimed at this account
|
// should always be local accounts, so
|
||||||
frc, err := c.db.CountAccountFollowRequests(ctx, a.ID)
|
// a missing account preferences struct
|
||||||
|
// is a real problem!
|
||||||
|
if a.Preferences == nil {
|
||||||
|
var err error
|
||||||
|
a.Preferences, err = c.db.GetAccountPreferencesByAccountID(ctx, a.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error counting follow requests: %s", err)
|
return nil, gtserror.Newf("error getting account preferences: %w", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
statusContentType := string(apimodel.StatusContentTypeDefault)
|
// Count pending follow requests aimed at this account.
|
||||||
if a.StatusContentType != "" {
|
frc, err := c.db.CountAccountFollowRequests(ctx, a.ID)
|
||||||
statusContentType = a.StatusContentType
|
if err != nil {
|
||||||
|
return nil, gtserror.Newf("error counting follow requests: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use default status content type
|
||||||
|
// if account has no other preference.
|
||||||
|
var statusContentType string
|
||||||
|
if a.Preferences.StatusContentType != "" {
|
||||||
|
statusContentType = a.Preferences.StatusContentType
|
||||||
|
} else {
|
||||||
|
statusContentType = string(apimodel.StatusContentTypeDefault)
|
||||||
}
|
}
|
||||||
|
|
||||||
apiAccount.Source = &apimodel.Source{
|
apiAccount.Source = &apimodel.Source{
|
||||||
Privacy: c.VisToAPIVis(ctx, a.Privacy),
|
Privacy: c.VisToAPIVis(ctx, a.Preferences.StatusPrivacy),
|
||||||
Sensitive: *a.Sensitive,
|
Sensitive: *a.Preferences.StatusSensitive,
|
||||||
Language: a.Language,
|
Language: a.Preferences.StatusLanguage,
|
||||||
StatusContentType: statusContentType,
|
StatusContentType: statusContentType,
|
||||||
Note: a.NoteRaw,
|
Note: a.NoteRaw,
|
||||||
Fields: c.fieldsToAPIFields(a.FieldsRaw),
|
Fields: c.fieldsToAPIFields(a.FieldsRaw),
|
||||||
|
@ -104,23 +122,23 @@ func (c *converter) AccountToAPIAccountPublic(ctx context.Context, a *gtsmodel.A
|
||||||
|
|
||||||
followersCount, err := c.db.CountAccountFollowers(ctx, a.ID)
|
followersCount, err := c.db.CountAccountFollowers(ctx, a.ID)
|
||||||
if err != nil && !errors.Is(err, db.ErrNoEntries) {
|
if err != nil && !errors.Is(err, db.ErrNoEntries) {
|
||||||
return nil, fmt.Errorf("AccountToAPIAccountPublic: error counting followers: %w", err)
|
return nil, gtserror.Newf("error counting followers: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
followingCount, err := c.db.CountAccountFollows(ctx, a.ID)
|
followingCount, err := c.db.CountAccountFollows(ctx, a.ID)
|
||||||
if err != nil && !errors.Is(err, db.ErrNoEntries) {
|
if err != nil && !errors.Is(err, db.ErrNoEntries) {
|
||||||
return nil, fmt.Errorf("AccountToAPIAccountPublic: error counting following: %w", err)
|
return nil, gtserror.Newf("error counting following: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
statusesCount, err := c.db.CountAccountStatuses(ctx, a.ID)
|
statusesCount, err := c.db.CountAccountStatuses(ctx, a.ID)
|
||||||
if err != nil && !errors.Is(err, db.ErrNoEntries) {
|
if err != nil && !errors.Is(err, db.ErrNoEntries) {
|
||||||
return nil, fmt.Errorf("AccountToAPIAccountPublic: error counting statuses: %w", err)
|
return nil, gtserror.Newf("error counting statuses: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var lastStatusAt *string
|
var lastStatusAt *string
|
||||||
lastPosted, err := c.db.GetAccountLastPosted(ctx, a.ID, false)
|
lastPosted, err := c.db.GetAccountLastPosted(ctx, a.ID, false)
|
||||||
if err != nil && !errors.Is(err, db.ErrNoEntries) {
|
if err != nil && !errors.Is(err, db.ErrNoEntries) {
|
||||||
return nil, fmt.Errorf("AccountToAPIAccountPublic: error counting statuses: %w", err)
|
return nil, gtserror.Newf("error counting statuses: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !lastPosted.IsZero() {
|
if !lastPosted.IsZero() {
|
||||||
|
@ -166,25 +184,32 @@ func (c *converter) AccountToAPIAccountPublic(ctx context.Context, a *gtsmodel.A
|
||||||
var (
|
var (
|
||||||
acct string
|
acct string
|
||||||
role *apimodel.AccountRole
|
role *apimodel.AccountRole
|
||||||
|
customCSS string
|
||||||
|
enableRSS bool
|
||||||
)
|
)
|
||||||
|
|
||||||
if a.IsRemote() {
|
switch {
|
||||||
// Domain may be in Punycode,
|
|
||||||
// de-punify it just in case.
|
// Only extra thing we can do for
|
||||||
|
// remote accounts is properly
|
||||||
|
// de-punify their domain.
|
||||||
|
case a.IsRemote():
|
||||||
d, err := util.DePunify(a.Domain)
|
d, err := util.DePunify(a.Domain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("AccountToAPIAccountPublic: error de-punifying domain %s for account id %s: %w", a.Domain, a.ID, err)
|
return nil, gtserror.Newf("error de-punifying domain %s for account %s: %w", a.Domain, a.ID, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
acct = a.Username + "@" + d
|
acct = a.Username + "@" + d
|
||||||
} else {
|
|
||||||
// This is a local account, try to
|
// This is a non-instance local
|
||||||
// fetch more info. Skip for instance
|
// account; fetch more info using
|
||||||
// accounts since they have no user.
|
// the account User. We skip this
|
||||||
if !a.IsInstance() {
|
// step for instance accounts since
|
||||||
|
// they have no user.
|
||||||
|
case !a.IsInstance():
|
||||||
user, err := c.db.GetUserByAccountID(ctx, a.ID)
|
user, err := c.db.GetUserByAccountID(ctx, a.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("AccountToAPIAccountPublic: error getting user from database for account id %s: %w", a.ID, err)
|
return nil, gtserror.Newf("db error getting user for account %s: %w", a.ID, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
|
@ -195,9 +220,17 @@ func (c *converter) AccountToAPIAccountPublic(ctx context.Context, a *gtsmodel.A
|
||||||
default:
|
default:
|
||||||
role = &apimodel.AccountRole{Name: apimodel.AccountRoleUser}
|
role = &apimodel.AccountRole{Name: apimodel.AccountRoleUser}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
acct = a.Username // omit domain
|
fallthrough // to below lines
|
||||||
|
|
||||||
|
// Fetch info which applies to
|
||||||
|
// local instance account and
|
||||||
|
// to actual local accounts.
|
||||||
|
default:
|
||||||
|
if a.Preferences != nil {
|
||||||
|
customCSS = a.Preferences.CustomCSS
|
||||||
|
enableRSS = *a.Preferences.EnableRSS
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remaining properties are simple and
|
// Remaining properties are simple and
|
||||||
|
@ -225,8 +258,8 @@ 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: *a.EnableRSS,
|
EnableRSS: enableRSS,
|
||||||
Role: role,
|
Role: role,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -279,7 +312,7 @@ func (c *converter) AccountToAPIAccountBlocked(ctx context.Context, a *gtsmodel.
|
||||||
if !a.IsInstance() {
|
if !a.IsInstance() {
|
||||||
user, err := c.db.GetUserByAccountID(ctx, a.ID)
|
user, err := c.db.GetUserByAccountID(ctx, a.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("AccountToAPIAccountPublic: error getting user from database for account id %s: %w", a.ID, err)
|
return nil, gtserror.Newf("error getting user from database for account id %s: %w", a.ID, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
|
|
Loading…
Reference in New Issue