feat: allow to ignore specific user-agents or ips
This commit is contained in:
parent
9d645b5c7e
commit
526bc5a671
2 changed files with 75 additions and 35 deletions
|
|
@ -8,6 +8,7 @@ summary: 'Traefik plugin that sends HTTP requests to Umami as pageview events.'
|
||||||
|
|
||||||
testData:
|
testData:
|
||||||
disabled: false
|
disabled: false
|
||||||
|
debug: false
|
||||||
umamiHost: ""
|
umamiHost: ""
|
||||||
umamiToken: ""
|
umamiToken: ""
|
||||||
umamiUsername: ""
|
umamiUsername: ""
|
||||||
|
|
@ -16,4 +17,5 @@ testData:
|
||||||
websites:
|
websites:
|
||||||
domain: websiteId
|
domain: websiteId
|
||||||
createNewWebsites: false
|
createNewWebsites: false
|
||||||
debug: false
|
ignoreUserAgents: []
|
||||||
|
ignoreIPs: []
|
||||||
|
|
|
||||||
106
umami.go
106
umami.go
|
|
@ -6,12 +6,17 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Config the plugin configuration.
|
// Config the plugin configuration.
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Disabled bool `json:"disabled"`
|
// basic plugin configuration
|
||||||
|
Disabled bool `json:"disabled"`
|
||||||
|
Debug bool `json:"debug"`
|
||||||
|
|
||||||
|
// Umami configuration
|
||||||
UmamiHost string `json:"umamiHost"`
|
UmamiHost string `json:"umamiHost"`
|
||||||
// it is optional, but either UmamiToken or Websites should be set
|
// it is optional, but either UmamiToken or Websites should be set
|
||||||
UmamiToken string `json:"umamiToken"`
|
UmamiToken string `json:"umamiToken"`
|
||||||
|
|
@ -19,25 +24,34 @@ type Config struct {
|
||||||
UmamiUsername string `json:"umamiUsername"`
|
UmamiUsername string `json:"umamiUsername"`
|
||||||
UmamiPassword string `json:"umamiPassword"`
|
UmamiPassword string `json:"umamiPassword"`
|
||||||
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
|
// if both UmamiToken and Websites are set, Websites will be used to override the websites in 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
|
// if createNewWebsites is set to true, the plugin will create new websites in the API, UmamiToken is required
|
||||||
CreateNewWebsites bool `json:"createNewWebsites"`
|
CreateNewWebsites bool `json:"createNewWebsites"`
|
||||||
Debug bool `json:"debug"`
|
|
||||||
|
// filters to ignore requests and do not send view events
|
||||||
|
IgnoreUserAgents []string `json:"ignoreUserAgents"`
|
||||||
|
IgnoreIPs []string `json:"ignoreIPs"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateConfig creates the default plugin configuration.
|
// CreateConfig creates the default plugin configuration.
|
||||||
func CreateConfig() *Config {
|
func CreateConfig() *Config {
|
||||||
return &Config{
|
return &Config{
|
||||||
Disabled: false,
|
Disabled: false,
|
||||||
UmamiHost: "",
|
Debug: false,
|
||||||
UmamiToken: "",
|
|
||||||
UmamiUsername: "",
|
UmamiHost: "",
|
||||||
UmamiPassword: "",
|
UmamiToken: "",
|
||||||
UmamiTeamId: "",
|
UmamiUsername: "",
|
||||||
|
UmamiPassword: "",
|
||||||
|
UmamiTeamId: "",
|
||||||
|
|
||||||
Websites: map[string]string{},
|
Websites: map[string]string{},
|
||||||
CreateNewWebsites: false,
|
CreateNewWebsites: false,
|
||||||
Debug: false,
|
|
||||||
|
IgnoreUserAgents: []string{},
|
||||||
|
IgnoreIPs: []string{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -49,11 +63,14 @@ type UmamiFeeder struct {
|
||||||
disabled bool
|
disabled bool
|
||||||
logHandler *log.Logger
|
logHandler *log.Logger
|
||||||
|
|
||||||
UmamiHost string
|
umamiHost string
|
||||||
UmamiToken string
|
umamiToken string
|
||||||
UmamiTeamId string
|
umamiTeamId string
|
||||||
Websites map[string]string
|
websites map[string]string
|
||||||
CreateNewWebsites bool
|
createNewWebsites bool
|
||||||
|
|
||||||
|
ignoreUserAgents []string
|
||||||
|
ignoreIPs []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// New created a new Demo plugin.
|
// New created a new Demo plugin.
|
||||||
|
|
@ -66,16 +83,19 @@ func New(ctx context.Context, next http.Handler, config *Config, name string) (h
|
||||||
disabled: config.Disabled,
|
disabled: config.Disabled,
|
||||||
logHandler: log.New(os.Stdout, "", 0),
|
logHandler: log.New(os.Stdout, "", 0),
|
||||||
|
|
||||||
UmamiHost: config.UmamiHost,
|
umamiHost: config.UmamiHost,
|
||||||
UmamiToken: config.UmamiToken,
|
umamiToken: config.UmamiToken,
|
||||||
UmamiTeamId: config.UmamiTeamId,
|
umamiTeamId: config.UmamiTeamId,
|
||||||
Websites: config.Websites,
|
websites: config.Websites,
|
||||||
CreateNewWebsites: config.CreateNewWebsites,
|
createNewWebsites: config.CreateNewWebsites,
|
||||||
|
|
||||||
|
ignoreUserAgents: config.IgnoreUserAgents,
|
||||||
|
ignoreIPs: config.IgnoreIPs,
|
||||||
}
|
}
|
||||||
|
|
||||||
if !h.disabled {
|
if !h.disabled {
|
||||||
if config.UmamiUsername != "" && config.UmamiPassword != "" {
|
if config.UmamiUsername != "" && config.UmamiPassword != "" {
|
||||||
token, err := getToken(h.UmamiHost, config.UmamiUsername, config.UmamiPassword)
|
token, err := getToken(h.umamiHost, config.UmamiUsername, config.UmamiPassword)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to get token: %w", err)
|
return nil, fmt.Errorf("failed to get token: %w", err)
|
||||||
}
|
}
|
||||||
|
|
@ -83,31 +103,31 @@ func New(ctx context.Context, next http.Handler, config *Config, name string) (h
|
||||||
return nil, fmt.Errorf("retrieved token is empty")
|
return nil, fmt.Errorf("retrieved token is empty")
|
||||||
}
|
}
|
||||||
h.trace("token received %s", token)
|
h.trace("token received %s", token)
|
||||||
h.UmamiToken = token
|
h.umamiToken = token
|
||||||
}
|
}
|
||||||
|
|
||||||
if h.UmamiHost == "" {
|
if h.umamiHost == "" {
|
||||||
return nil, fmt.Errorf("`umamiHost` is not set")
|
return nil, fmt.Errorf("`umamiHost` is not set")
|
||||||
}
|
}
|
||||||
if h.UmamiToken == "" && len(h.Websites) == 0 {
|
if h.umamiToken == "" && len(h.websites) == 0 {
|
||||||
return nil, fmt.Errorf("either `umamiToken` or `websites` should be set")
|
return nil, fmt.Errorf("either `umamiToken` or `websites` should be set")
|
||||||
}
|
}
|
||||||
if h.UmamiToken == "" && h.CreateNewWebsites {
|
if h.umamiToken == "" && h.createNewWebsites {
|
||||||
return nil, fmt.Errorf("`umamiToken` is required to create new websites")
|
return nil, fmt.Errorf("`umamiToken` is required to create new websites")
|
||||||
}
|
}
|
||||||
|
|
||||||
if h.UmamiToken != "" {
|
if h.umamiToken != "" {
|
||||||
websites, err := fetchWebsites(h.UmamiHost, h.UmamiToken, h.UmamiTeamId)
|
websites, err := fetchWebsites(h.umamiHost, h.umamiToken, h.umamiTeamId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to fetch websites: %w", err)
|
return nil, fmt.Errorf("failed to fetch websites: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, website := range *websites {
|
for _, website := range *websites {
|
||||||
if _, ok := h.Websites[website.Domain]; ok {
|
if _, ok := h.websites[website.Domain]; ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
h.Websites[website.Domain] = website.ID
|
h.websites[website.Domain] = website.ID
|
||||||
h.trace("fetched websiteId for: %s", website.Domain)
|
h.trace("fetched websiteId for: %s", website.Domain)
|
||||||
}
|
}
|
||||||
h.log("websites fetched")
|
h.log("websites fetched")
|
||||||
|
|
@ -130,12 +150,30 @@ func (h *UmamiFeeder) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *UmamiFeeder) shouldBeTracked(req *http.Request) bool {
|
func (h *UmamiFeeder) shouldBeTracked(req *http.Request) bool {
|
||||||
if h.CreateNewWebsites {
|
if len(h.ignoreUserAgents) > 0 {
|
||||||
|
userAgent := req.UserAgent()
|
||||||
|
for _, disabledUserAgent := range h.ignoreUserAgents {
|
||||||
|
if strings.Contains(userAgent, disabledUserAgent) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(h.ignoreIPs) > 0 {
|
||||||
|
requestIp := req.RemoteAddr
|
||||||
|
for _, disabledIp := range h.ignoreIPs {
|
||||||
|
if requestIp == disabledIp {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if h.createNewWebsites {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
hostname := parseDomainFromHost(req.Host)
|
hostname := parseDomainFromHost(req.Host)
|
||||||
if _, ok := h.Websites[hostname]; ok {
|
if _, ok := h.websites[hostname]; ok {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -144,15 +182,15 @@ func (h *UmamiFeeder) shouldBeTracked(req *http.Request) bool {
|
||||||
|
|
||||||
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]
|
||||||
if !ok {
|
if !ok {
|
||||||
website, err := createWebsite(h.UmamiHost, h.UmamiToken, h.UmamiTeamId, hostname)
|
website, err := createWebsite(h.umamiHost, h.umamiToken, h.umamiTeamId, hostname)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
h.log("failed to create website: " + err.Error())
|
h.log("failed to create website: " + err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
h.Websites[website.Domain] = website.ID
|
h.websites[website.Domain] = website.ID
|
||||||
websiteId = website.ID
|
websiteId = website.ID
|
||||||
h.trace("created website for: %s", website.Domain)
|
h.trace("created website for: %s", website.Domain)
|
||||||
}
|
}
|
||||||
|
|
@ -160,7 +198,7 @@ func (h *UmamiFeeder) trackRequest(req *http.Request) {
|
||||||
sendBody, sendHeaders := buildSendBody(req, websiteId)
|
sendBody, sendHeaders := buildSendBody(req, websiteId)
|
||||||
h.trace("sending tracking request %s with body %v %v", req.URL, sendBody, sendHeaders)
|
h.trace("sending tracking request %s with body %v %v", req.URL, sendBody, sendHeaders)
|
||||||
|
|
||||||
_, err := sendRequest(h.UmamiHost+"/api/send", sendBody, sendHeaders)
|
_, err := sendRequest(h.umamiHost+"/api/send", sendBody, sendHeaders)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
h.trace("failed to send tracking: " + err.Error())
|
h.trace("failed to send tracking: " + err.Error())
|
||||||
return
|
return
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue