3
0
mirror of https://github.com/ergochat/ergo.git synced 2025-12-08 17:07:36 +01:00

add persistence for user metadata

This commit is contained in:
Shivaram Lingamneni 2025-12-07 03:04:16 -05:00
parent f91d1d94f6
commit 0ce9016098
3 changed files with 80 additions and 3 deletions

View File

@ -52,6 +52,7 @@ const (
// (not to be confused with their amodes, which a non-always-on client can have): // (not to be confused with their amodes, which a non-always-on client can have):
keyAccountChannelToModes = "account.channeltomodes %s" keyAccountChannelToModes = "account.channeltomodes %s"
keyAccountPushSubscriptions = "account.pushsubscriptions %s" keyAccountPushSubscriptions = "account.pushsubscriptions %s"
keyAccountMetadata = "account.metadata %s"
maxCertfpsPerAccount = 5 maxCertfpsPerAccount = 5
) )
@ -137,6 +138,7 @@ func (am *AccountManager) createAlwaysOnClients(config *Config) {
am.loadModes(accountName), am.loadModes(accountName),
am.loadRealname(accountName), am.loadRealname(accountName),
am.loadPushSubscriptions(accountName), am.loadPushSubscriptions(accountName),
am.loadMetadata(accountName),
) )
} }
} }
@ -751,6 +753,40 @@ func (am *AccountManager) loadPushSubscriptions(account string) (result []stored
} }
} }
func (am *AccountManager) saveMetadata(account string, metadata map[string]string) {
j, err := json.Marshal(metadata)
if err != nil {
am.server.logger.Error("internal", "error storing metadata", err.Error())
return
}
val := string(j)
key := fmt.Sprintf(keyAccountMetadata, account)
am.server.store.Update(func(tx *buntdb.Tx) error {
tx.Set(key, val, nil)
return nil
})
return
}
func (am *AccountManager) loadMetadata(account string) (result map[string]string) {
key := fmt.Sprintf(keyAccountMetadata, account)
var val string
am.server.store.View(func(tx *buntdb.Tx) error {
val, _ = tx.Get(key)
return nil
})
if val == "" {
return nil
}
if err := json.Unmarshal([]byte(val), &result); err == nil {
return result
} else {
am.server.logger.Error("internal", "error loading metadata", err.Error())
return nil
}
}
func (am *AccountManager) addRemoveCertfp(account, certfp string, add bool, hasPrivs bool) (err error) { func (am *AccountManager) addRemoveCertfp(account, certfp string, add bool, hasPrivs bool) (err error) {
certfp, err = utils.NormalizeCertfp(certfp) certfp, err = utils.NormalizeCertfp(certfp)
if err != nil { if err != nil {
@ -1880,6 +1916,7 @@ func (am *AccountManager) Unregister(account string, erase bool) error {
pwResetKey := fmt.Sprintf(keyAccountPwReset, casefoldedAccount) pwResetKey := fmt.Sprintf(keyAccountPwReset, casefoldedAccount)
emailChangeKey := fmt.Sprintf(keyAccountEmailChange, casefoldedAccount) emailChangeKey := fmt.Sprintf(keyAccountEmailChange, casefoldedAccount)
pushSubscriptionsKey := fmt.Sprintf(keyAccountPushSubscriptions, casefoldedAccount) pushSubscriptionsKey := fmt.Sprintf(keyAccountPushSubscriptions, casefoldedAccount)
metadataKey := fmt.Sprintf(keyAccountMetadata, casefoldedAccount)
var clients []*Client var clients []*Client
defer func() { defer func() {
@ -1939,6 +1976,7 @@ func (am *AccountManager) Unregister(account string, erase bool) error {
tx.Delete(pwResetKey) tx.Delete(pwResetKey)
tx.Delete(emailChangeKey) tx.Delete(emailChangeKey)
tx.Delete(pushSubscriptionsKey) tx.Delete(pushSubscriptionsKey)
tx.Delete(metadataKey)
return nil return nil
}) })

View File

@ -428,7 +428,7 @@ func (server *Server) RunClient(conn IRCConn) {
client.run(session) client.run(session)
} }
func (server *Server) AddAlwaysOnClient(account ClientAccount, channelToStatus map[string]alwaysOnChannelStatus, lastSeen, readMarkers map[string]time.Time, uModes modes.Modes, realname string, pushSubscriptions []storedPushSubscription) { func (server *Server) AddAlwaysOnClient(account ClientAccount, channelToStatus map[string]alwaysOnChannelStatus, lastSeen, readMarkers map[string]time.Time, uModes modes.Modes, realname string, pushSubscriptions []storedPushSubscription, metadata map[string]string) {
now := time.Now().UTC() now := time.Now().UTC()
config := server.Config() config := server.Config()
if lastSeen == nil && account.Settings.AutoreplayMissed { if lastSeen == nil && account.Settings.AutoreplayMissed {
@ -513,6 +513,10 @@ func (server *Server) AddAlwaysOnClient(account ClientAccount, channelToStatus m
} }
} }
client.rebuildPushSubscriptionCache() client.rebuildPushSubscriptionCache()
if len(metadata) != 0 {
client.metadata = metadata
}
} }
func (client *Client) resizeHistory(config *Config) { func (client *Client) resizeHistory(config *Config) {
@ -1850,6 +1854,7 @@ const (
IncludeUserModes IncludeUserModes
IncludeRealname IncludeRealname
IncludePushSubscriptions IncludePushSubscriptions
IncludeMetadata
) )
func (client *Client) markDirty(dirtyBits uint) { func (client *Client) markDirty(dirtyBits uint) {
@ -1931,6 +1936,9 @@ func (client *Client) performWrite(additionalDirtyBits uint) {
if (dirtyBits & IncludePushSubscriptions) != 0 { if (dirtyBits & IncludePushSubscriptions) != 0 {
client.server.accounts.savePushSubscriptions(account, client.getPushSubscriptions(true)) client.server.accounts.savePushSubscriptions(account, client.getPushSubscriptions(true))
} }
if (dirtyBits & IncludeMetadata) != 0 {
client.server.accounts.saveMetadata(account, client.ListMetadata())
}
} }
// Blocking store; see Channel.Store and Socket.BlockingWrite // Blocking store; see Channel.Store and Socket.BlockingWrite

View File

@ -963,9 +963,18 @@ func (client *Client) GetMetadata(key string) (string, bool) {
} }
func (client *Client) SetMetadata(key string, value string, limit int) (updated bool, err error) { func (client *Client) SetMetadata(key string, value string, limit int) (updated bool, err error) {
var alwaysOn bool
defer func() {
if alwaysOn && updated {
client.markDirty(IncludeMetadata)
}
}()
client.stateMutex.Lock() client.stateMutex.Lock()
defer client.stateMutex.Unlock() defer client.stateMutex.Unlock()
alwaysOn = client.registered && client.alwaysOn
if client.metadata == nil { if client.metadata == nil {
client.metadata = make(map[string]string) client.metadata = make(map[string]string)
} }
@ -982,11 +991,20 @@ func (client *Client) SetMetadata(key string, value string, limit int) (updated
} }
func (client *Client) UpdateMetadataFromPrereg(preregData map[string]string, limit int) (updates map[string]string) { func (client *Client) UpdateMetadataFromPrereg(preregData map[string]string, limit int) (updates map[string]string) {
var alwaysOn bool
defer func() {
if alwaysOn && len(updates) > 0 {
client.markDirty(IncludeMetadata)
}
}()
updates = make(map[string]string, len(preregData)) updates = make(map[string]string, len(preregData))
client.stateMutex.Lock() client.stateMutex.Lock()
defer client.stateMutex.Unlock() defer client.stateMutex.Unlock()
alwaysOn = client.registered && client.alwaysOn
if client.metadata == nil { if client.metadata == nil {
client.metadata = make(map[string]string) client.metadata = make(map[string]string)
} }
@ -1003,6 +1021,7 @@ func (client *Client) UpdateMetadataFromPrereg(preregData map[string]string, lim
client.metadata[k] = v client.metadata[k] = v
updates[k] = v updates[k] = v
} }
return return
} }
@ -1014,6 +1033,12 @@ func (client *Client) ListMetadata() map[string]string {
} }
func (client *Client) DeleteMetadata(key string) (updated bool) { func (client *Client) DeleteMetadata(key string) (updated bool) {
defer func() {
if updated {
client.markDirty(IncludeMetadata)
}
}()
client.stateMutex.Lock() client.stateMutex.Lock()
defer client.stateMutex.Unlock() defer client.stateMutex.Unlock()
@ -1024,11 +1049,17 @@ func (client *Client) DeleteMetadata(key string) (updated bool) {
return updated return updated
} }
func (client *Client) ClearMetadata() map[string]string { func (client *Client) ClearMetadata() (oldMap map[string]string) {
defer func() {
if len(oldMap) > 0 {
client.markDirty(IncludeMetadata)
}
}()
client.stateMutex.Lock() client.stateMutex.Lock()
defer client.stateMutex.Unlock() defer client.stateMutex.Unlock()
oldMap := client.metadata oldMap = client.metadata
client.metadata = nil client.metadata = nil
return oldMap return oldMap