github.com/bitcubate/cryptojournal@v1.2.5-0.20171102134152-f578b3d788ab/src/lib/stats/stats.go (about) 1 package stats 2 3 import ( 4 "crypto/sha1" 5 "encoding/base64" 6 "fmt" 7 "net/http" 8 "strings" 9 "sync" 10 "time" 11 ) 12 13 // Put this in a separate package called stats 14 15 // PurgeInterval is the interval at which users are purged from the current list 16 var PurgeInterval = time.Minute * 5 17 18 // identifiers holds a hash of anonymised user records 19 // obviously an in-memory store is not suitable for very large sites 20 // but for smaller sites with a few hundred concurrent users it's fine 21 var identifiers = make(map[string]time.Time) 22 var mu sync.RWMutex 23 24 // RegisterHit registers a hit and ups user count if required 25 func RegisterHit(r *http.Request) { 26 27 // Use UA as well as ip for unique values per browser session 28 ua := r.Header.Get("User-Agent") 29 // Ignore obvious bots (Googlebot etc) 30 if strings.Contains(ua, "bot") { 31 return 32 } 33 // Ignore requests for xml (assumed to be feeds or sitemap) 34 if strings.HasSuffix(r.URL.Path, ".xml") { 35 return 36 } 37 38 // Extract the IP from the address 39 ip := r.RemoteAddr 40 forward := r.Header.Get("X-Forwarded-For") 41 if len(forward) > 0 { 42 ip = forward 43 } 44 45 // Hash for anonymity in our store 46 hasher := sha1.New() 47 hasher.Write([]byte(ip)) 48 hasher.Write([]byte(ua)) 49 id := base64.URLEncoding.EncodeToString(hasher.Sum(nil)) 50 51 // Insert the entry with current time 52 mu.Lock() 53 identifiers[id] = time.Now() 54 mu.Unlock() 55 } 56 57 // HandleUserCount serves a get request at /stats/users/count 58 func HandleUserCount(w http.ResponseWriter, r *http.Request) error { 59 60 // Render json of our count for the javascript to display 61 mu.RLock() 62 json := fmt.Sprintf("{\"users\":%d}", len(identifiers)) 63 mu.RUnlock() 64 _, err := w.Write([]byte(json)) 65 return err 66 } 67 68 // UserCount returns a count of users in the last 5 minutes 69 func UserCount() int { 70 mu.RLock() 71 defer mu.RUnlock() 72 return len(identifiers) 73 } 74 75 // Clean up users list at intervals 76 func init() { 77 purgeUsers() 78 } 79 80 // purgeUsers clears the users list of users who last acted PurgeInterval ago 81 func purgeUsers() { 82 83 mu.Lock() 84 for k, v := range identifiers { 85 purgeTime := time.Now().Add(-PurgeInterval) 86 if v.Before(purgeTime) { 87 delete(identifiers, k) 88 } 89 } 90 mu.Unlock() 91 92 time.AfterFunc(time.Second*60, purgeUsers) 93 94 // fmt.Printf("Purged users:%d", UserCount()) 95 }