github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/libkb/sync_trackers.go (about)

     1  // Copyright 2015 Keybase, Inc. All rights reserved. Use of
     2  // this source code is governed by the included BSD license.
     3  
     4  package libkb
     5  
     6  import (
     7  	"fmt"
     8  	"sync"
     9  	"time"
    10  
    11  	keybase1 "github.com/keybase/client/go/protocol/keybase1"
    12  )
    13  
    14  type FollowDirection int
    15  
    16  const (
    17  	FollowDirectionFollowing FollowDirection = 0
    18  	FollowDirectionFollowers FollowDirection = 1
    19  )
    20  
    21  func directionToReverse(direction FollowDirection) (reverse bool) {
    22  	return direction == FollowDirectionFollowing
    23  }
    24  
    25  type ServertrustTrackerSyncer struct {
    26  	sync.Mutex
    27  	Contextified
    28  	res       *keybase1.UserSummarySet
    29  	direction FollowDirection
    30  	dirty     bool
    31  	callerUID keybase1.UID
    32  }
    33  
    34  const cacheTimeout = 10 * time.Minute
    35  
    36  func (t *ServertrustTrackerSyncer) dbKey(u keybase1.UID) DbKey {
    37  	if t.direction == FollowDirectionFollowing {
    38  		return DbKeyUID(DBUnverifiedTrackersFollowing, u)
    39  	}
    40  	return DbKeyUID(DBUnverifiedTrackersFollowers, u)
    41  }
    42  
    43  func (t *ServertrustTrackerSyncer) loadFromStorage(m MetaContext, uid keybase1.UID, useExpiration bool) error {
    44  	var err error
    45  	var found bool
    46  	var tmp keybase1.UserSummarySet
    47  	defer m.Trace(fmt.Sprintf("loadFromStorage(%s)", uid), &err)()
    48  	found, err = t.G().LocalDb.GetInto(&tmp, t.dbKey(uid))
    49  	if err != nil {
    50  		return err
    51  	}
    52  	if !found {
    53  		m.Debug("| no cached copy found")
    54  		return nil
    55  	}
    56  	cachedAt := keybase1.FromTime(tmp.Time)
    57  	if useExpiration && time.Since(cachedAt) > cacheTimeout {
    58  		m.Debug("| expired; cached at %s", cachedAt)
    59  		return nil
    60  	}
    61  	m.Debug("| found a record, cached %s", cachedAt)
    62  	t.res = &tmp
    63  	return nil
    64  }
    65  
    66  func (t *ServertrustTrackerSyncer) getLoadedVersion() int {
    67  	ret := -1
    68  	if t.res != nil {
    69  		ret = t.res.Version
    70  	}
    71  	return ret
    72  }
    73  
    74  func (t *ServertrustTrackerSyncer) syncFromServer(m MetaContext, uid keybase1.UID, forceReload bool) (err error) {
    75  
    76  	defer m.Trace(fmt.Sprintf("syncFromServer(%s)", uid), &err)()
    77  
    78  	hargs := HTTPArgs{
    79  		"uid":        UIDArg(uid),
    80  		"reverse":    B{directionToReverse(t.direction)},
    81  		"autoCamel":  B{true},
    82  		"caller_uid": UIDArg(t.callerUID),
    83  	}
    84  	lv := t.getLoadedVersion()
    85  	if lv >= 0 && !forceReload {
    86  		hargs.Add("version", I{lv})
    87  	}
    88  	var res *APIRes
    89  	res, err = m.G().API.Get(m, APIArg{
    90  		Endpoint: "user/list_followers_for_display",
    91  		Args:     hargs,
    92  	})
    93  	m.Debug("| syncFromServer() -> %s", ErrToOk(err))
    94  	if err != nil {
    95  		return err
    96  	}
    97  	var tmp keybase1.UserSummarySet
    98  	if err = res.Body.UnmarshalAgain(&tmp); err != nil {
    99  		return
   100  	}
   101  	tmp.Time = keybase1.ToTime(time.Now())
   102  	if lv < 0 || tmp.Version > lv || forceReload {
   103  		m.Debug("| syncFromServer(): got update %d > %d (%d records)", tmp.Version, lv,
   104  			len(tmp.Users))
   105  		t.res = &tmp
   106  		t.dirty = true
   107  	} else {
   108  		m.Debug("| syncFromServer(): no change needed @ %d", lv)
   109  	}
   110  	return nil
   111  }
   112  
   113  func (t *ServertrustTrackerSyncer) store(m MetaContext, uid keybase1.UID) error {
   114  	var err error
   115  	if !t.dirty {
   116  		return err
   117  	}
   118  
   119  	if err = t.G().LocalDb.PutObj(t.dbKey(uid), nil, t.res); err != nil {
   120  		return err
   121  	}
   122  
   123  	t.dirty = false
   124  	return nil
   125  }
   126  
   127  func (t *ServertrustTrackerSyncer) needsLogin(m MetaContext) bool {
   128  	return false
   129  }
   130  
   131  func (t *ServertrustTrackerSyncer) Block(m MetaContext, badUIDs map[keybase1.UID]bool) (err error) {
   132  	defer m.Trace(fmt.Sprintf("ServertrustTrackerSyncer#Block(%+v)", badUIDs), &err)()
   133  	t.Lock()
   134  	defer t.Unlock()
   135  
   136  	if t.direction != FollowDirectionFollowers {
   137  		return fmt.Errorf("can only delete users out of followers cache")
   138  	}
   139  
   140  	if t.res == nil {
   141  		m.Debug("No followers loaded, so nothing to do")
   142  		return nil
   143  	}
   144  
   145  	err = t.loadFromStorage(m, t.callerUID, true)
   146  	if err != nil {
   147  		return err
   148  	}
   149  
   150  	var newUsers []keybase1.UserSummary
   151  	for _, userSummary := range t.res.Users {
   152  		if badUIDs[userSummary.Uid] {
   153  			m.Debug("Filtering bad user out of state: %s", userSummary.Uid)
   154  			t.dirty = true
   155  		} else {
   156  			newUsers = append(newUsers, userSummary)
   157  		}
   158  	}
   159  	t.res.Users = newUsers
   160  	err = t.store(m, t.callerUID)
   161  	return err
   162  }
   163  
   164  func (t *ServertrustTrackerSyncer) Result() keybase1.UserSummarySet {
   165  	if t.res == nil {
   166  		return keybase1.UserSummarySet{}
   167  	}
   168  
   169  	// Normalize usernames
   170  	var normalizedUsers []keybase1.UserSummary
   171  	for _, u := range t.res.Users {
   172  		normalizedUser := u
   173  		normalizedUser.Username = NewNormalizedUsername(u.Username).String()
   174  		normalizedUsers = append(normalizedUsers, normalizedUser)
   175  	}
   176  	t.res.Users = normalizedUsers
   177  
   178  	return *t.res
   179  }
   180  
   181  func NewServertrustTrackerSyncer(g *GlobalContext, callerUID keybase1.UID, direction FollowDirection) *ServertrustTrackerSyncer {
   182  	return &ServertrustTrackerSyncer{
   183  		Contextified: NewContextified(g),
   184  		direction:    direction,
   185  		callerUID:    callerUID,
   186  	}
   187  }