70 lines
1.6 KiB
Go
70 lines
1.6 KiB
Go
|
package goatcounter
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"log/slog"
|
||
|
"net/url"
|
||
|
)
|
||
|
|
||
|
type SiteClient interface {
|
||
|
URL() *url.URL
|
||
|
Count(context.Context, ...Hit) (uint64, error)
|
||
|
}
|
||
|
|
||
|
type MultiSiteClient struct {
|
||
|
siteToClient map[string]SiteClient
|
||
|
ignoreSites map[string]struct{}
|
||
|
}
|
||
|
|
||
|
func NewMultiSiteClient(siteToClient map[string]SiteClient, ignoreGoatSites bool) *MultiSiteClient {
|
||
|
ignoreSites := map[string]struct{}{}
|
||
|
if ignoreGoatSites {
|
||
|
for _, client := range siteToClient {
|
||
|
ignoreSite := client.URL().Host
|
||
|
slog.Debug("ignoring goat site", slog.String("site", ignoreSite))
|
||
|
|
||
|
ignoreSites[ignoreSite] = struct{}{}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return &MultiSiteClient{
|
||
|
siteToClient: siteToClient,
|
||
|
ignoreSites: ignoreSites,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (m *MultiSiteClient) Count(ctx context.Context, hits ...Hit) (counted uint64, err error) {
|
||
|
siteHits := map[string][]Hit{}
|
||
|
for _, hit := range hits {
|
||
|
siteHits[hit.Host] = append(siteHits[hit.Host], hit)
|
||
|
}
|
||
|
|
||
|
for site, hits := range siteHits {
|
||
|
hitCount := len(hits)
|
||
|
logger := slog.With(slog.String("site", site), slog.Int("count", hitCount))
|
||
|
|
||
|
if _, ignore := m.ignoreSites[site]; ignore {
|
||
|
logger.DebugContext(ctx, "ignoring hits for site")
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
client, ok := m.siteToClient[site]
|
||
|
if !ok {
|
||
|
logger.ErrorContext(ctx, "no client for site, skipping")
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
subCount, err := client.Count(ctx, hits...)
|
||
|
if err != nil {
|
||
|
logger.ErrorContext(ctx, "failed to count hits", slog.String("err", err.Error()))
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
logger.InfoContext(ctx, "counted hits", slog.Uint64("counted", subCount))
|
||
|
|
||
|
counted += subCount
|
||
|
}
|
||
|
|
||
|
return counted, nil
|
||
|
}
|