12
"github.com/xelbot/yetacache"
13
"xelbot.com/reprogl/container"
14
"xelbot.com/reprogl/models"
15
"xelbot.com/reprogl/models/repositories"
16
trackmodels "xelbot.com/reprogl/utils/tracking/models"
20
regexpArticle = regexp.MustCompile(`^\/article\/(?P<slug>[^/?#]+)`)
21
slugIndex = regexpArticle.SubexpIndex("slug")
22
trackingCache *yetacache.Cache[string, int8]
26
trackingCache = yetacache.New[string, int8](container.TrackExpiration, container.CleanUpInterval)
29
func CreateActivity(req *http.Request) *trackmodels.Activity {
30
ip := net.ParseIP(container.RealRemoteAddress(req))
35
activity := &trackmodels.Activity{
37
IsCDN: container.IsCDN(req),
39
UserAgent: req.UserAgent(),
40
RequestedURI: req.URL.RequestURI(),
44
setupBrowserPassiveFingerprint(req, activity)
49
func SaveActivity(activity *trackmodels.Activity, app *container.Application) {
51
userAgentId, articleId int
54
if strings.HasPrefix(activity.RequestedURI, "/_fragment/") && activity.Status == http.StatusOK {
58
repo := repositories.TrackingRepository{DB: app.DB}
59
agent, err := repo.GetAgentByHash(container.MD5(activity.UserAgent))
61
if errors.Is(err, models.RecordNotFound) {
62
userAgentId, err = repo.SaveTrackingAgent(activity)
72
userAgentId = agent.ID
75
location, err := findLocationByIP(activity.Addr, app)
77
activity.LocationID = location.ID
80
matches := regexpArticle.FindStringSubmatch(activity.RequestedURI)
82
articleRepo := repositories.ArticleRepository{DB: app.DB}
83
articleId = articleRepo.GetIdBySlug(matches[slugIndex])
86
if !trackingCache.Has(activity.FingerPrint) ||
87
activity.Status != http.StatusOK ||
88
activity.Method != "GET" {
89
err = repo.SaveTracking(activity, userAgentId, articleId)
93
ipKey := ipAddrKey(activity.Addr)
94
cache := app.GetIntCache()
100
trackingCache.Set(activity.FingerPrint, 1, yetacache.DefaultTTL)
104
func findLocationByIP(ip net.IP, app *container.Application) (*models.Geolocation, error) {
105
ipKey := ipAddrKey(ip)
107
cache := app.GetIntCache()
108
if locationID, found := cache.Get(ipKey); found {
109
app.InfoLog.Printf("[CACHE] IP %s HIT\n", ip.String())
111
return &models.Geolocation{IpAddr: ip.String(), ID: locationID}, nil
113
app.InfoLog.Printf("[CACHE] IP %s MISS\n", ip.String())
116
geolocationRepo := repositories.GeolocationRepository{DB: app.DB}
117
location, err := geolocationRepo.FindByIP(ip)
122
cache.Set(ipKey, location.ID, 168*time.Hour)
127
func setupBrowserPassiveFingerprint(req *http.Request, a *trackmodels.Activity) {
128
a.FingerPrint = container.MD5(
133
req.URL.RequestURI(),
139
func ipAddrKey(ip net.IP) string {
140
return "IP_" + ip.String()