feat: an attempt to avoid tracking of assets
This commit is contained in:
parent
93959ce812
commit
f0193f763d
3 changed files with 87 additions and 27 deletions
|
|
@ -7,8 +7,6 @@ summary: A Traefik plugin that feeds Umami Analytics with pageview events
|
||||||
iconPath: .assets/icon.png
|
iconPath: .assets/icon.png
|
||||||
|
|
||||||
testData:
|
testData:
|
||||||
disabled: false
|
|
||||||
debug: false
|
|
||||||
umamiHost: ""
|
umamiHost: ""
|
||||||
umamiToken: ""
|
umamiToken: ""
|
||||||
umamiUsername: ""
|
umamiUsername: ""
|
||||||
|
|
@ -17,5 +15,3 @@ testData:
|
||||||
websites:
|
websites:
|
||||||
domain: websiteId
|
domain: websiteId
|
||||||
createNewWebsites: false
|
createNewWebsites: false
|
||||||
ignoreUserAgents: []
|
|
||||||
ignoreIPs: []
|
|
||||||
|
|
|
||||||
|
|
@ -83,7 +83,7 @@ entryPoints:
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
| key | default | type | description |
|
| key | default | type | description |
|
||||||
|---------------------|---------|----------|-------------------------------------------------------------------------------------------------------------|
|
|---------------------|---------|------------|---------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| `umamiHost` | - | `string` | Umami server url, reachable from within traefik (container), e.g. `http://umami:3000` |
|
| `umamiHost` | - | `string` | Umami server url, reachable from within traefik (container), e.g. `http://umami:3000` |
|
||||||
| `umamiToken` | - | `string` | An API Token, used to automatize work with websites, not needed if you provide `websites` |
|
| `umamiToken` | - | `string` | An API Token, used to automatize work with websites, not needed if you provide `websites` |
|
||||||
| `umamiUsername` | - | `string` | An alternative to `umamiToken`, you can provide an username and password |
|
| `umamiUsername` | - | `string` | An alternative to `umamiToken`, you can provide an username and password |
|
||||||
|
|
@ -91,4 +91,6 @@ entryPoints:
|
||||||
| `umamiTeamId` | - | `string` | In order to organize websites, you can use Umami Teams |
|
| `umamiTeamId` | - | `string` | In order to organize websites, you can use Umami Teams |
|
||||||
| `websites` | - | `map` | A map of hostnames and their associated Umami IDs. Can also be used to override or extend fetched websites |
|
| `websites` | - | `map` | A map of hostnames and their associated Umami IDs. Can also be used to override or extend fetched websites |
|
||||||
| `createNewWebsites` | false | `bool` | If set to `true`, will try to create a new website on Umami, if domain not found there |
|
| `createNewWebsites` | false | `bool` | If set to `true`, will try to create a new website on Umami, if domain not found there |
|
||||||
|
| `trackAllResources` | false | `bool` | Defines whether all requests for any resource should be tracked. By default, only requests that are believed to contain content are tracked |
|
||||||
|
| `trackExtensions` | | `string[]` | Defines an alternative list of file extensions that should be tracked |
|
||||||
| `debug` | false | `bool` | Something doesn't work? Set to `true` to see more logs (plugins doesn't have access to Traefik's log level) |
|
| `debug` | false | `bool` | Something doesn't work? Set to `true` to see more logs (plugins doesn't have access to Traefik's log level) |
|
||||||
|
|
|
||||||
82
umami.go
82
umami.go
|
|
@ -6,32 +6,44 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Config the plugin configuration.
|
// Config the plugin configuration.
|
||||||
type Config struct {
|
type Config struct {
|
||||||
// basic plugin configuration
|
// Disabled disables the plugin.
|
||||||
Disabled bool `json:"disabled"`
|
Disabled bool `json:"disabled"`
|
||||||
|
// Debug enables debug logging, be prepared for flooding.
|
||||||
Debug bool `json:"debug"`
|
Debug bool `json:"debug"`
|
||||||
|
|
||||||
// Umami configuration
|
// UmamiHost is the URL of the Umami instance.
|
||||||
UmamiHost string `json:"umamiHost"`
|
UmamiHost string `json:"umamiHost"`
|
||||||
// it is optional, but either UmamiToken or Websites should be set
|
// UmamiToken is an API KEY, which is optional, but either UmamiToken or Websites should be set.
|
||||||
UmamiToken string `json:"umamiToken"`
|
UmamiToken string `json:"umamiToken"`
|
||||||
// as an alternative to UmamiToken, you can set UmamiUsername and UmamiPassword to authenticate
|
// UmamiUsername could be provided as an alternative to UmamiToken, used to retrieve the token.
|
||||||
UmamiUsername string `json:"umamiUsername"`
|
UmamiUsername string `json:"umamiUsername"`
|
||||||
|
// UmamiPassword is required if UmamiUsername is set.
|
||||||
UmamiPassword string `json:"umamiPassword"`
|
UmamiPassword string `json:"umamiPassword"`
|
||||||
|
// UmamiTeamId defines a team, which will be used to retrieve the websites.
|
||||||
UmamiTeamId string `json:"umamiTeamId"`
|
UmamiTeamId string `json:"umamiTeamId"`
|
||||||
|
|
||||||
// if both UmamiToken and Websites are set, Websites will be used to override the websites in the API
|
// 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 map[string]string `json:"websites"`
|
Websites map[string]string `json:"websites"`
|
||||||
// if createNewWebsites is set to true, the plugin will create new websites in the API, UmamiToken is required
|
// CreateNewWebsites when set to true, the plugin will create new websites using API, UmamiToken is required.
|
||||||
CreateNewWebsites bool `json:"createNewWebsites"`
|
CreateNewWebsites bool `json:"createNewWebsites"`
|
||||||
|
|
||||||
// filters to ignore requests and do not send view events
|
// TrackAllResources defines whether all requests for any resource should be tracked.
|
||||||
|
// By default, only requests that are believed to contain content are tracked.
|
||||||
|
TrackAllResources bool `json:"trackAllResources"`
|
||||||
|
// TrackExtensions defines an alternative list of file extensions that should be tracked.
|
||||||
|
TrackExtensions []string `json:"trackExtensions"`
|
||||||
|
|
||||||
|
// IgnoreUserAgents is a list of user agents that should be ignored.
|
||||||
IgnoreUserAgents []string `json:"ignoreUserAgents"`
|
IgnoreUserAgents []string `json:"ignoreUserAgents"`
|
||||||
|
// IgnoreIPs is a list of IPs that should be ignored.
|
||||||
IgnoreIPs []string `json:"ignoreIPs"`
|
IgnoreIPs []string `json:"ignoreIPs"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -50,6 +62,9 @@ func CreateConfig() *Config {
|
||||||
Websites: map[string]string{},
|
Websites: map[string]string{},
|
||||||
CreateNewWebsites: false,
|
CreateNewWebsites: false,
|
||||||
|
|
||||||
|
TrackAllResources: false,
|
||||||
|
TrackExtensions: []string{},
|
||||||
|
|
||||||
IgnoreUserAgents: []string{},
|
IgnoreUserAgents: []string{},
|
||||||
IgnoreIPs: []string{},
|
IgnoreIPs: []string{},
|
||||||
}
|
}
|
||||||
|
|
@ -69,6 +84,9 @@ type UmamiFeeder struct {
|
||||||
websites map[string]string
|
websites map[string]string
|
||||||
createNewWebsites bool
|
createNewWebsites bool
|
||||||
|
|
||||||
|
trackAllResources bool
|
||||||
|
trackExtensions []string
|
||||||
|
|
||||||
ignoreUserAgents []string
|
ignoreUserAgents []string
|
||||||
ignoreIPs []string
|
ignoreIPs []string
|
||||||
}
|
}
|
||||||
|
|
@ -89,6 +107,9 @@ func New(ctx context.Context, next http.Handler, config *Config, name string) (h
|
||||||
websites: config.Websites,
|
websites: config.Websites,
|
||||||
createNewWebsites: config.CreateNewWebsites,
|
createNewWebsites: config.CreateNewWebsites,
|
||||||
|
|
||||||
|
trackAllResources: config.TrackAllResources,
|
||||||
|
trackExtensions: config.TrackExtensions,
|
||||||
|
|
||||||
ignoreUserAgents: config.IgnoreUserAgents,
|
ignoreUserAgents: config.IgnoreUserAgents,
|
||||||
ignoreIPs: config.IgnoreIPs,
|
ignoreIPs: config.IgnoreIPs,
|
||||||
}
|
}
|
||||||
|
|
@ -149,17 +170,17 @@ func (h *UmamiFeeder) verifyConfig(config *Config) error {
|
||||||
|
|
||||||
func (h *UmamiFeeder) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
func (h *UmamiFeeder) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||||
if !h.isDisabled {
|
if !h.isDisabled {
|
||||||
if h.shouldBeTracked(req) {
|
if h.shouldTrack(req) {
|
||||||
go h.trackRequest(req)
|
go h.trackRequest(req)
|
||||||
} else {
|
} else {
|
||||||
h.debug("Tracking skipped %s", req.URL)
|
h.debug("ignoring request to %s%s", req.Host, req.URL)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
h.next.ServeHTTP(rw, req)
|
h.next.ServeHTTP(rw, req)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *UmamiFeeder) shouldBeTracked(req *http.Request) bool {
|
func (h *UmamiFeeder) shouldTrack(req *http.Request) bool {
|
||||||
if len(h.ignoreUserAgents) > 0 {
|
if len(h.ignoreUserAgents) > 0 {
|
||||||
userAgent := req.UserAgent()
|
userAgent := req.UserAgent()
|
||||||
for _, disabledUserAgent := range h.ignoreUserAgents {
|
for _, disabledUserAgent := range h.ignoreUserAgents {
|
||||||
|
|
@ -178,6 +199,10 @@ func (h *UmamiFeeder) shouldBeTracked(req *http.Request) bool {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !h.shouldTrackResource(req.URL.Path) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
if h.createNewWebsites {
|
if h.createNewWebsites {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
@ -190,6 +215,43 @@ func (h *UmamiFeeder) shouldBeTracked(req *http.Request) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *UmamiFeeder) shouldTrackResource(url string) bool {
|
||||||
|
if h.trackAllResources {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
pathExt := path.Ext(url)
|
||||||
|
|
||||||
|
// If a custom file extension list is defined, check if the resource matches it. If not, do not report.
|
||||||
|
if len(h.trackExtensions) > 0 {
|
||||||
|
for _, suffix := range h.trackExtensions {
|
||||||
|
if suffix == pathExt {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the suffix is regarded to be "content".
|
||||||
|
switch pathExt {
|
||||||
|
case ".htm":
|
||||||
|
case ".html":
|
||||||
|
case ".xhtml":
|
||||||
|
case ".jsf":
|
||||||
|
case ".md":
|
||||||
|
case ".php":
|
||||||
|
case ".rss":
|
||||||
|
case ".rtf":
|
||||||
|
case ".txt":
|
||||||
|
case ".xml":
|
||||||
|
case ".pdf":
|
||||||
|
case "":
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func (h *UmamiFeeder) trackRequest(req *http.Request) {
|
func (h *UmamiFeeder) trackRequest(req *http.Request) {
|
||||||
hostname := parseDomainFromHost(req.Host)
|
hostname := parseDomainFromHost(req.Host)
|
||||||
websiteId, ok := h.websites[hostname]
|
websiteId, ok := h.websites[hostname]
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue