package db
import (
"context"
"errors"
"fmt"
"net"
"net/http"
"strings"
"time"
)
const (
AnalyticsGranularityDaily = "1d"
AnalyticsGranularityWeekly = "1w"
)
const (
Day = time.Hour * 24
AnalyticsRetention90d = Day * 90
AnalyticsRetention366d = Day * 366
AnalyticsRetention732d = Day * 732
AnalyticsRetention90dStr = "90d"
AnalyticsRetention366dStr = "366d"
AnalyticsRetention732dStr = "732d"
)
var ErrAnalyticsIp = errors.New("error parsing ip for analytics")
func (d *DB) InsertAnalytics(backhalfId int, r *http.Request) error {
cfg := d.GetConfig()
if d.adb == nil || cfg.AnalyticsDisable {
return nil
}
sql := `SELECT b.analytics_collect, b.analytics_granularity FROM back_half b
WHERE b.id = $1;`
row := d.db.QueryRow(sql, backhalfId)
var shouldCollect bool
var granularity string
err := row.Scan(&shouldCollect, &granularity)
if err != nil {
return err
}
if !shouldCollect {
return nil
}
ipStr, _, _ := strings.Cut(r.RemoteAddr, ":")
ip := net.ParseIP(ipStr)
if ip == nil {
return fmt.Errorf("%w: %s", ErrAnalyticsIp, ipStr)
}
ua := r.Header.Get("User-Agent")
upsertUaSql := `INSERT INTO ua (name) VALUES ($1)
ON CONFLICT DO UPDATE SET name = excluded.name
RETURNING id;`
aWriteTimeout := time.Duration(cfg.AnalyticsWriteTimeout) * time.Second
ctx, cancel := context.WithTimeout(context.Background(), aWriteTimeout)
var uaId int
row = d.adb.QueryRowContext(ctx, upsertUaSql, ua)
err = row.Scan(&uaId)
cancel()
if err != nil {
return err
}
ctx, cancel = context.WithTimeout(context.Background(), aWriteTimeout)
defer cancel()
var upsertAnalyticsSql = `INSERT INTO analytics
(back_half_id, start, netmask, ua_id, count) VALUES ($1, $2, $3, $4, $5)
ON CONFLICT DO UPDATE SET count = excluded.count + new.count;`
_, err = d.adb.ExecContext(ctx, upsertAnalyticsSql, backhalfId, ua)
if err != nil {
return err
}
return nil
}