Implement Rybbit changes

This commit is contained in:
FoxxMD 2025-07-29 15:34:57 -04:00
parent 8d7198941f
commit 6def58012a
14 changed files with 142 additions and 345 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 292 KiB

Before After
Before After

View file

@ -1,17 +1,13 @@
displayName: Traefik Umami Feeder
displayName: Traefik Rybbit Feeder
import: github.com/astappiev/traefik-umami-feeder
import: github.com/foxxmd/traefik-rybbit-feeder
type: middleware
summary: A Traefik plugin that feeds Umami Analytics with pageview events
summary: A Traefik plugin that feeds Rybbit Analytics with pageview events
iconPath: .assets/icon.png
testData:
umamiHost: ""
umamiToken: ""
umamiUsername: ""
umamiPassword: ""
umamiTeamId: ""
Host: ""
ApiKey: ""
websites:
domain: websiteId
createNewWebsites: false
localhost: siteId

View file

@ -1,11 +1,11 @@
http:
middlewares:
umami:
rybbit:
plugin:
umami-feeder:
rybbit-feeder:
debug: true
umamiHost: http://umami:3000
umamiUsername: "admin"
umamiPassword: "umami"
umamiTeamId: "8e39c6ad-e44a-4d3e-be98-015db2d62d40"
createNewWebsites: true
host: https://rybbit.mysite.com
apiKey: rb_a0938f333a88239c6250c2c3707a
websites:
"localhost": "1"
batchSize: 1

View file

@ -1,8 +1,6 @@
services:
traefik:
image: traefik:3
depends_on:
- umami
labels:
- "traefik.enable=true"
- "traefik.http.routers.traefik.service=api@internal"
@ -24,7 +22,7 @@ services:
- ./traefik.yml:/traefik.yml:ro
- ./config.yml:/config/config.yml:ro
# mount plugin
- ../:/plugins-local/src/github.com/astappiev/traefik-umami-feeder
- ../:/plugins-local/src/github.com/foxxmd/traefik-rybbit-feeder
restart: unless-stopped
@ -32,7 +30,7 @@ services:
image: containous/whoami
ports:
- target: 80
published: 80
published: 7654
protocol: tcp
labels:
- "traefik.enable=true"
@ -41,44 +39,10 @@ services:
- "traefik.http.routers.whoami.rule=Host(`localhost`)"
- "traefik.http.routers.whoami.entrypoints=whoami"
# middleware
- "traefik.http.routers.whoami.middlewares=umami@file"
- "traefik.http.routers.whoami.middlewares=rybbit@file"
networks:
- traefik
umami:
image: ghcr.io/umami-software/umami:postgresql-latest
depends_on:
postgres:
condition: service_healthy
environment:
- DEBUG=umami:middleware
- ENABLE_TEST_CONSOLE=1
- DATABASE_URL=postgres://umami:umami@postgres:5432/umami
ports:
- target: 3000
published: 3000
protocol: tcp
networks:
- traefik
- umami_postgres
postgres:
image: postgres:16
environment:
- POSTGRES_PASSWORD=umami
- POSTGRES_DB=umami
- POSTGRES_USER=umami
networks:
- umami_postgres
volumes:
- ./postgres:/var/lib/postgresql/data
healthcheck:
test: [ "CMD-SHELL", "sh -c 'pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}'" ]
interval: 10s
timeout: 3s
retries: 3
networks:
traefik:
umami_postgres:
name: traefik

View file

@ -18,13 +18,11 @@ entryPoints:
address: ":8080"
"whoami": # http
address: ":8081"
"umami": # umami dashboard
address: ":3000"
api:
dashboard: true
experimental:
localPlugins:
umami-feeder:
moduleName: "github.com/astappiev/traefik-umami-feeder"
rybbit-feeder:
moduleName: "github.com/foxxmd/traefik-rybbit-feeder"

2
go.mod
View file

@ -1,3 +1,3 @@
module github.com/astappiev/traefik-umami-feeder
module github.com/FoxxMD/traefik-rybbit-feeder
go 1.21

View file

@ -1,14 +1,13 @@
# Traefik Umami Feeder Plugin
# Traefik Rybbit Feeder Plugin
A [Traefik](https://traefik.io/traefik/) middleware plugin that sends visits to your [Umami](https://umami.is) instance.
A [Traefik](https://traefik.io/traefik/) middleware plugin that sends visits to your [Rybbit](https://www.rybbit.io/) instance.
It was created as an alternative to [traefik-umami-plugin](https://github.com/1cedsoda/traefik-umami-plugin) and
inspired by the [Plausible Feeder Traefik Plugin](https://github.com/safing/plausiblefeeder).
**This is a fork of [traefik-umami-feeder](https://github.com/astappiev/traefik-umami-feeder) modified to work with Rybbit.**
## Introduction
This plugin integrates your Traefik-proxied services with Umami, a simple, fast, privacy-focused analytics solution. It
captures basic request information (path, user-agent, referrer, screen size, IP) and forwards it to your Umami instance,
This plugin integrates your Traefik-proxied services with Rybbit, a simple, fast, privacy-focused analytics solution. It
captures basic request information (path, user-agent, referrer, IP) and forwards it to your Rybbit instance,
enabling server-side analytics.
Key features:
@ -26,51 +25,42 @@ Declare the plugin in your Traefik **static configuration**.
```yaml
experimental:
plugins:
umami-feeder:
moduleName: github.com/astappiev/traefik-umami-feeder
version: v1.3.0 # Replace with the latest version
rybbit-feeder:
moduleName: github.com/foxxmd/traefik-rybbit-feeder
version: v0.13.0 # Replace with the latest version
```
### Step 2. Configure the middleware
Once the plugin is declared, configure it as a middleware in your Traefik **dynamic configuration**.
You can specify which websites to track in two ways:
You will need to specify at least three pieces of data:
1. **Manual**: Directly provide a `websites` map, associating hostnames with their Umami Website IDs.
2. **Automatic**: Configure the plugin with your Umami API `umamiToken`, or `umamiUsername` and `umamiPassword`. The
plugin will then automatically fetch the list of websites and their IDs from your Umami instance.
* Optionally, use `umamiTeamId` to scope website retrieval to a specific team.
* Optionally, enable `createNewWebsites` to allow the plugin to create new website entries in Umami if they don't
already exist.
* `host` - Your Rybbit instance base URL. This is URL **without** `/api/track`.
* `apiKey` - The [**Api Key**](https://www.rybbit.io/docs/api#steps) generated for the website you wish to track.
* `websites` - A map of `domain` - `site-id` properties to tell the feeder what traffic should go to your Rybbit Site
See the [Middleware Options](#middleware-options) section for detailed configuration options.
There are additional, optional [Middleware Options](#middleware-options) to configure more behavior below.
```yaml
http:
middlewares:
my-umami-middleware:
my-rybbit-middleware:
plugin:
umami-feeder:
umamiHost: "http://umami:3000" # URL of your Umami instance
rybbit-feeder:
host: "http://rybbit.mysite.com" # URL of your Rybbit instance
# Option 1: Define the list of websites
# websites:
# "example.com": "d4617504-241c-4797-8eab-5939b367b3ad"
apiKey: rb_a0938250c2c2efd061c8250c2c3707a
# Option 2: Use Umami credentials to fetch websites
umamiUsername: "your-umami-username"
umamiPassword: "your-umami-password"
# umamiToken: "your-umami-api-token" # Alternative to username/password
# Optional: allow creation of new websites in Umami
createNewWebsites: true
websites:
# domain to capture traffic for and the site-id from Rybbit
"example.com": "1"
```
### Step 3. Attach the middleware to your routers
Apply the [configured middleware](https://doc.traefik.io/traefik/routing/routers/#middlewares_1) to the Traefik routers
you want to track with Umami. This is also done in your **dynamic configuration**.
you want to track with Rybbit. This is also done in your **dynamic configuration**.
Remember to use the
correct [provider namespace](https://doc.traefik.io/traefik/providers/overview/#provider-namespace) (e.g., `@file` if
@ -79,7 +69,7 @@ your middleware is defined in a file, `@docker` if defined via Docker labels).
**Example using Docker labels:**
```yaml
- "traefik.http.routers.whoami.middlewares=my-umami-middleware@file"
- "traefik.http.routers.whoami.middlewares=my-rybbit-middleware@file"
```
**Example using a dynamic configuration file (e.g., `dynamic_conf.yml`):**
@ -90,7 +80,7 @@ http:
whoami:
rule: "Host(`example.com`)"
middlewares:
- my-umami-middleware@file
- my-rybbit-middleware@file
```
**Example using static configuration (e.g., `traefik.yml`), by attaching the middleware to an entryPoint to apply it
@ -101,23 +91,18 @@ entryPoints:
web:
http:
middlewares:
- my-umami-middleware@file
- my-rybbit-middleware@file
```
## Middleware Options
| key | default | type | description |
|---------------------|-----------------|------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| ------------------- | :-------------- | :--------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `disabled` | `false` | `bool` | Set to `true` to disable the plugin. |
| `debug` | `false` | `bool` | Set to `true` for verbose logging. Useful for troubleshooting as plugins don't inherit Traefik's global log level. |
| `queueSize` | `1000` | `int` | Maximum number of tracking events to queue before sending to the Umami server. |
| `umamiHost` | **required** | `string` | URL of your Umami instance, reachable from Traefik (e.g., `http://umami:3000`). |
| `umamiToken` | - | `string` | [Umami API Token](https://umami.is/docs/api/authentication) for authenticating with your Umami instance. Use this *or* `umamiUsername`/`umamiPassword`. Required for automatic website fetching or creation. |
| `umamiUsername` | - | `string` | Username for Umami authentication. Use this with `umamiPassword` if not using `umamiToken`. Required for automatic website fetching or creation. |
| `umamiPassword` | - | `string` | Password for Umami authentication, used in conjunction with `umamiUsername`. |
| `umamiTeamId` | - | `string` | Optional. If using automatic mode, specifies the Umami Team ID to scope website fetching/creation. |
| `websites` | - | `map` | A map of `hostname: umamiWebsiteID`. Used for manual website configuration or to override/extend websites fetched in automatic mode. |
| `createNewWebsites` | `false` | `bool` | If `true` and using automatic mode, the plugin will attempt to create a new website entry in Umami if the domain is not found. |
| `host` | **required** | `string` | URL of your Rybbit instance, reachable from Traefik (e.g., `https://rybbit.mydomain.com`). |
| `apiKey` | **required** | `string` | [Rybbit API Key](https://www.rybbit.io/docs/api#steps) for authenticating with your Rybbit instance. |
| `websites` | **required** | `map` | A map of `hostname: site-id` |
| `trackErrors` | `false` | `bool` | If `true`, tracks errors (status codes >= 400). |
| `trackAllResources` | `false` | `bool` | If `true`, tracks requests for all resources. By default, only requests likely to be page views (e.g., HTML, or no specific extension) are tracked. |
| `trackExtensions` | `[see sources]` | `string[]` | A list of specific file extensions to track (e.g., `[".html", ".php"]`). |

View file

@ -1,4 +1,4 @@
package traefik_umami_feeder
package traefik_rybbit_feeder
import (
"bufio"

105
umami.go
View file

@ -1,4 +1,4 @@
package traefik_umami_feeder
package traefik_rybbit_feeder
import (
"context"
@ -21,29 +21,20 @@ type Config struct {
Disabled bool `json:"disabled"`
// Debug enables debug logging, be prepared for flooding.
Debug bool `json:"debug"`
// QueueSize defines the size of queue, i.e. the amount of events that are waiting to be submitted to Umami.
// QueueSize defines the size of queue, i.e. the amount of events that are waiting to be submitted to Rybbit.
QueueSize int `json:"queueSize"`
// BatchSize defines the amount of events that are submitted to Umami in one request.
// BatchSize defines the amount of events that are submitted to Rybbit in one request, should always be 1.
BatchSize int `json:"batchSize"`
// BatchMaxWait defines the maximum time to wait before submitting the batch.
// BatchMaxWait defines the maximum time to wait before submitting the batch. Should be 1 second.
BatchMaxWait time.Duration `json:"batchMaxWait"`
// UmamiHost is the URL of the Umami instance.
UmamiHost string `json:"umamiHost"`
// UmamiToken is an API KEY, which is optional, but either UmamiToken or Websites should be set.
UmamiToken string `json:"umamiToken"`
// UmamiUsername could be provided as an alternative to UmamiToken, used to retrieve the token.
UmamiUsername string `json:"umamiUsername"`
// UmamiPassword is required if UmamiUsername is set.
UmamiPassword string `json:"umamiPassword"`
// UmamiTeamId defines a team, which will be used to retrieve the websites.
UmamiTeamId string `json:"umamiTeamId"`
// Host is the URL of the Rybbit instance.
Host string `json:"host"`
// APIKey is the API Key generated in Site Settings for a Rybbit Website
APIKey string `json:"apiKey"`
// Websites is a map of domain to websiteId, which is required if UmamiToken is not set.
// If both UmamiToken and Websites are set, Websites will override/extend domains retrieved from the API.
// Websites is a map of domain to site-id, which is required
Websites map[string]string `json:"websites"`
// CreateNewWebsites when set to true, the plugin will create new websites using API, UmamiToken is required.
CreateNewWebsites bool `json:"createNewWebsites"`
// TrackErrors defines whether errors (status codes >= 400) should be tracked.
TrackErrors bool `json:"trackErrors"`
@ -73,14 +64,10 @@ func CreateConfig() *Config {
BatchMaxWait: 5 * time.Second,
TrackErrors: false,
UmamiHost: "",
UmamiToken: "",
UmamiUsername: "",
UmamiPassword: "",
UmamiTeamId: "",
Host: "",
APIKey: "",
Websites: map[string]string{},
CreateNewWebsites: false,
TrackAllResources: false,
TrackExtensions: []string{},
@ -99,14 +86,13 @@ type UmamiFeeder struct {
isDebug bool
isDisabled bool
logHandler *log.Logger
queue chan *UmamiEvent
queue chan *RybbitEvent
batchSize int
batchMaxWait time.Duration
umamiHost string
umamiToken string
umamiTeamId string
host string
apiKey string
websites map[string]string
websitesMutex sync.RWMutex
createNewWebsites bool
@ -131,16 +117,14 @@ func New(ctx context.Context, next http.Handler, config *Config, name string) (h
isDisabled: config.Disabled,
logHandler: log.New(os.Stdout, "", 0),
queue: make(chan *UmamiEvent, config.QueueSize),
queue: make(chan *RybbitEvent, config.QueueSize),
batchSize: config.BatchSize,
batchMaxWait: config.BatchMaxWait,
batchMaxWait: 1 * time.Second,
umamiHost: config.UmamiHost,
umamiToken: config.UmamiToken,
umamiTeamId: config.UmamiTeamId,
host: config.Host,
apiKey: config.APIKey,
websites: config.Websites,
websitesMutex: sync.RWMutex{},
createNewWebsites: config.CreateNewWebsites,
trackErrors: config.TrackErrors,
trackAllResources: config.TrackAllResources,
@ -154,6 +138,8 @@ func New(ctx context.Context, next http.Handler, config *Config, name string) (h
if !h.isDisabled {
h.isDisabled = true
h.debug("batchSize %d", h.batchSize)
h.debug("batchMaxWait %v", h.batchMaxWait)
go h.retryConnection(ctx, config)
}
@ -178,11 +164,11 @@ func (h *UmamiFeeder) retryConnection(ctx context.Context, config *Config) {
select {
case <-time.After(currentDelay):
retryAttempt++
h.debug("Attempting to connect to Umami (attempt #%d)...", retryAttempt)
h.debug("Attempting to connect to Rybbit (attempt #%d)...", retryAttempt)
err := h.connect(ctx, config)
if err == nil {
h.debug("Successfully connected to Umami. Verifying configuration...")
h.debug("Successfully connected to Rybbit. Verifying configuration...")
err = h.verifyConfig(config)
if err == nil {
@ -197,7 +183,7 @@ func (h *UmamiFeeder) retryConnection(ctx context.Context, config *Config) {
return // Exit retry goroutine, plugin remains disabled.
}
h.error("Failed to reconnect to Umami: " + err.Error())
h.error("Failed to reconnect to Rybbit: " + err.Error())
case <-ctx.Done():
h.debug("Context cancelled during retryConnection, stopping connection retries.")
return
@ -206,42 +192,21 @@ func (h *UmamiFeeder) retryConnection(ctx context.Context, config *Config) {
}
func (h *UmamiFeeder) connect(ctx context.Context, config *Config) error {
if h.umamiHost == "" {
return fmt.Errorf("`umamiHost` is not set")
if h.host == "" {
return fmt.Errorf("`host` is not set")
}
if config.UmamiUsername != "" && config.UmamiPassword != "" {
token, err := getToken(ctx, h.umamiHost, config.UmamiUsername, config.UmamiPassword)
if h.apiKey == "" {
return fmt.Errorf("`apiKey` should be set")
}
if len(h.websites) == 0 {
return fmt.Errorf("`websites` should not be empty")
}
_, err := sendRequest(ctx, h.host+"/health", nil, nil)
if err != nil {
return fmt.Errorf("failed to get token: %w", err)
}
if token == "" {
return fmt.Errorf("retrieved token is empty")
}
h.debug("token received %s", token)
h.umamiToken = token
}
if h.umamiToken == "" && len(h.websites) == 0 {
return fmt.Errorf("either `umamiToken` or `websites` should be set")
}
if h.umamiToken == "" && h.createNewWebsites {
return fmt.Errorf("`umamiToken` is required to create new websites")
}
if h.umamiToken != "" {
websites, err := fetchWebsites(ctx, h.umamiHost, h.umamiToken, h.umamiTeamId)
if err != nil {
return fmt.Errorf("failed to fetch websites: %w", err)
}
for _, website := range *websites {
if _, ok := h.websites[website.Domain]; ok {
continue
}
h.websites[website.Domain] = website.ID
}
h.debug("websites fetched: %v", h.websites)
return fmt.Errorf("Failed to get health for rybbit: %w", err)
}
return nil

View file

@ -1,4 +1,4 @@
package traefik_umami_feeder
package traefik_rybbit_feeder
import (
"context"
@ -9,11 +9,11 @@ import (
func TestTraefikUmamiFeeder(t *testing.T) {
cfg := CreateConfig()
cfg.UmamiHost = "http://localhost:3000"
cfg.UmamiUsername = "admin"
cfg.UmamiPassword = "umami"
cfg.UmamiTeamId = "8e39c6ad-e44a-4d3e-be98-015db2d62d40"
cfg.CreateNewWebsites = true
cfg.Host = "http://localhost:3000"
cfg.APIKey = "8e39c6ad-e44a-4d3e-be98-015db2d62d40"
cfg.Websites = map[string]string{
"localhost": "4",
}
ctx := context.Background()
next := http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {})

View file

@ -1,26 +0,0 @@
package traefik_umami_feeder
import "context"
type authRequest struct {
Username string `json:"username"`
Password string `json:"password"`
}
type authResponse struct {
Token string `json:"token"`
}
func getToken(ctx context.Context, umamiHost string, umamiUsername string, umamiPassword string) (string, error) {
var result authResponse
err := sendRequestAndParse(ctx, umamiHost+"/api/auth/login", authRequest{
Username: umamiUsername,
Password: umamiPassword,
}, nil, &result)
if err != nil {
return "", err
}
return result.Token, nil
}

View file

@ -1,4 +1,4 @@
package traefik_umami_feeder
package traefik_rybbit_feeder
import (
"bytes"

View file

@ -1,87 +0,0 @@
package traefik_umami_feeder
import (
"context"
"net/http"
"time"
)
type websitesResponse struct {
Data []Website `json:"data"`
Count int `json:"count"`
Page int `json:"page"`
PageSize int `json:"pageSize"`
OrderBy string `json:"orderBy"`
}
type Website struct {
ID string `json:"id,omitempty"`
Name string `json:"name,omitempty"`
TeamId string `json:"teamId,omitempty"`
Domain string `json:"domain,omitempty"`
CreatedAt time.Time `json:"createdAt,omitempty"`
}
func createWebsite(ctx context.Context, umamiHost string, umamiToken string, teamId string, websiteDomain string) (*Website, error) {
var headers = make(http.Header)
headers.Set("Authorization", "Bearer "+umamiToken)
var result Website
err := sendRequestAndParse(ctx, umamiHost+"/api/websites", Website{
Name: websiteDomain,
Domain: websiteDomain,
TeamId: teamId,
}, headers, &result)
if err != nil {
return nil, err
}
return &result, nil
}
func fetchWebsites(ctx context.Context, umamiHost string, umamiToken string, teamId string) (*[]Website, error) {
var headers = make(http.Header)
headers.Set("Authorization", "Bearer "+umamiToken)
url := umamiHost + "/api/websites?pageSize=200"
if len(teamId) != 0 {
url = umamiHost + "/api/teams/" + teamId + "/websites?pageSize=200"
}
var result websitesResponse
err := sendRequestAndParse(ctx, url, nil, headers, &result)
if err != nil {
return nil, err
}
return &result.Data, nil
}
func getWebsiteId(h *UmamiFeeder, hostname string) string {
h.websitesMutex.RLock()
websiteId, ok := h.websites[hostname]
h.websitesMutex.RUnlock()
if ok {
return websiteId
}
h.websitesMutex.Lock()
defer h.websitesMutex.Unlock()
// Create a background context for the API call
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
website, err := createWebsite(ctx, h.umamiHost, h.umamiToken, h.umamiTeamId, hostname)
if err != nil {
h.error("failed to create website: " + err.Error())
return ""
}
h.websites[website.Domain] = website.ID
h.debug("website created '%s': %s", website.Domain, website.ID)
return website.ID
}

View file

@ -1,4 +1,4 @@
package traefik_umami_feeder
package traefik_rybbit_feeder
import (
"context"
@ -8,48 +8,48 @@ import (
"time"
)
type UmamiEvent struct {
Website string `json:"website"` // Website ID
Hostname string `json:"hostname"` // Name of host
Language string `json:"language,omitempty"` // Language of visitor (ex. "en-US")
Referrer string `json:"referrer,omitempty"` // Referrer URL
Url string `json:"url"` // Page URL
Ip string `json:"ip,omitempty"` // IP address
UserAgent string `json:"userAgent,omitempty"` // User agent
Timestamp int64 `json:"timestamp,omitempty"` // UNIX timestamp in seconds
//Data map[string]interface{} `json:"data,omitempty"` // Additional data for the event
//Name string `json:"name,omitempty"` // Event name (for custom events)
//Screen string `json:"screen,omitempty"` // Screen resolution (ex. "1920x1080")
//Title string `json:"title,omitempty"` // Page title
type RybbitEvent struct {
APIKey string `json:"api_key"`
SiteID string `json:"site_id"`
Type string `json:"type"`
Pathname string `json:"pathname"`
Hostname string `json:"hostname,omitempty"`
IP string `json:"ip_address,omitempty"`
UserAgent string `json:"user_agent,omitempty"`
Language string `json:"language,omitempty"`
EventName string `json:"event_name,omitempty"`
Referrer string `json:"referrer,omitempty"`
Properties string `json:"properties,omitempty"`
}
type SendBody struct {
Payload *UmamiEvent `json:"payload"`
Payload *RybbitEvent `json:"payload"`
Type string `json:"type"`
}
func (h *UmamiFeeder) submitToFeed(req *http.Request, code int) {
hostname := parseDomainFromHost(req.Host)
websiteId := getWebsiteId(h, hostname)
websiteId, ok := h.websites[hostname]
if websiteId == "" {
h.error("tracking skipped, websiteId is unknown: " + hostname)
if !ok {
h.error("tracking skipped, site-id is unknown: " + hostname)
return
}
event := &UmamiEvent{
rEvent := &RybbitEvent{
APIKey: h.apiKey,
SiteID: websiteId,
Type: "pageview",
Pathname: req.URL.Path,
Hostname: hostname,
Language: parseAcceptLanguage(req.Header.Get("Accept-Language")),
Referrer: req.Referer(),
Url: req.URL.String(),
Ip: extractRemoteIP(req),
IP: extractRemoteIP(req),
UserAgent: req.Header.Get("User-Agent"),
Timestamp: time.Now().Unix(),
Website: websiteId,
Referrer: req.Referer(),
Language: parseAcceptLanguage(req.Header.Get("Accept-Language")),
}
select {
case h.queue <- event:
case h.queue <- rEvent:
default:
h.error("failed to submit event: queue full")
}
@ -108,7 +108,8 @@ func (h *UmamiFeeder) umamiEventFeeder(ctx context.Context) (err error) {
func (h *UmamiFeeder) reportEventsToUmami(ctx context.Context, events []*SendBody) {
h.debug("reporting %d events", len(events))
resp, err := sendRequest(ctx, h.umamiHost+"/api/batch", events, nil)
for _, value := range events {
resp, err := sendRequest(ctx, h.host+"/api/track", value.Payload, nil)
if err != nil {
h.error("failed to send tracking: " + err.Error())
return
@ -120,4 +121,5 @@ func (h *UmamiFeeder) reportEventsToUmami(ctx context.Context, events []*SendBod
defer func() {
_ = resp.Body.Close()
}()
}
}