From 3e5c982284823c473a9482d9955009c9cd8f0172 Mon Sep 17 00:00:00 2001 From: Oleh Astappiev Date: Wed, 16 Oct 2024 16:24:28 +0200 Subject: [PATCH] feat: add `umamiTeamId` to organize websites --- .traefik.yml | 1 + demo/config.yml | 1 + readme.md | 6 +++++- umami.go | 8 ++++++-- umami_websites.go | 17 ++++++++++++----- 5 files changed, 25 insertions(+), 8 deletions(-) diff --git a/.traefik.yml b/.traefik.yml index be53505..0b724f1 100644 --- a/.traefik.yml +++ b/.traefik.yml @@ -11,6 +11,7 @@ testData: umamiToken: "" umamiUsername: "" umamiPassword: "" + umamiTeamId: "" websites: domain: websiteId createNewWebsites: false diff --git a/demo/config.yml b/demo/config.yml index 742c3f2..1229fea 100644 --- a/demo/config.yml +++ b/demo/config.yml @@ -7,4 +7,5 @@ http: umamiHost: http://umami:3000 umamiUsername: "admin" umamiPassword: "umami" + umamiTeamId: "8e39c6ad-e44a-4d3e-be98-015db2d62d40" createNewWebsites: true diff --git a/readme.md b/readme.md index d9c3da0..785eac0 100644 --- a/readme.md +++ b/readme.md @@ -56,7 +56,8 @@ http: ``` You have an option to give a list of domains to track (and their website IDs on Umami). \ -Or, you can give a token and the list will be fetched from Umami. For this, you need either [retrieve the token yourself](https://umami.is/docs/api/authentication), or use +Or, you can give a token and the list will be fetched from Umami. For this, you need +either [retrieve the token yourself](https://umami.is/docs/api/authentication), or use username/password instead. After that, you need to add the middleware to a [router](https://doc.traefik.io/traefik/routing/routers/#middlewares_1). @@ -64,11 +65,13 @@ Remember to reference the correct [provider namespace](https://doc.traefik.io/traefik/providers/overview/#provider-namespace). E.g. as Docker labels: + ```yaml - "traefik.http.routers.whoami.middlewares=my-umami-middleware@file" ``` Or, for all routers in a static configuration: + ```yaml entryPoints: web: @@ -85,6 +88,7 @@ entryPoints: | `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 | | `umamiPassword` | - | `string` | Only in combination with `umamiUsername` | +| `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 | | `createNewWebsites` | false | `bool` | If set to `true`, will try to create a new website on Umami, if domain not found there | | `debug` | false | `bool` | Something doesn't work? Set to `true` to see more logs (plugins doesn't have access to Traefik's log level) | diff --git a/umami.go b/umami.go index 2b825f1..695b876 100644 --- a/umami.go +++ b/umami.go @@ -17,6 +17,7 @@ type Config struct { // as an alternative to UmamiToken, you can set UmamiUsername and UmamiPassword to authenticate UmamiUsername string `json:"umamiUsername"` UmamiPassword string `json:"umamiPassword"` + UmamiTeamId string `json:"umamiTeamId"` // if both UmamiToken and Websites are set, Websites will be used to override the websites in the API Websites map[string]string `json:"websites"` // if createNewWebsites is set to true, the plugin will create new websites in the API, UmamiToken is required @@ -31,6 +32,7 @@ func CreateConfig() *Config { UmamiToken: "", UmamiUsername: "", UmamiPassword: "", + UmamiTeamId: "", Websites: map[string]string{}, CreateNewWebsites: false, Debug: false, @@ -46,6 +48,7 @@ type UmamiFeeder struct { UmamiHost string UmamiToken string + UmamiTeamId string Websites map[string]string CreateNewWebsites bool } @@ -61,6 +64,7 @@ func New(ctx context.Context, next http.Handler, config *Config, name string) (h UmamiHost: config.UmamiHost, UmamiToken: config.UmamiToken, + UmamiTeamId: config.UmamiTeamId, Websites: config.Websites, CreateNewWebsites: config.CreateNewWebsites, } @@ -88,7 +92,7 @@ func New(ctx context.Context, next http.Handler, config *Config, name string) (h } if h.UmamiToken != "" { - websites, err := fetchWebsites(h.UmamiHost, h.UmamiToken) + websites, err := fetchWebsites(h.UmamiHost, h.UmamiToken, h.UmamiTeamId) if err != nil { return nil, fmt.Errorf("failed to fetch websites: %w", err) } @@ -134,7 +138,7 @@ func (h *UmamiFeeder) trackRequest(req *http.Request) { hostname := parseDomainFromHost(req.Host) websiteId, ok := h.Websites[hostname] if !ok { - website, err := createWebsite(h.UmamiHost, h.UmamiToken, hostname) + website, err := createWebsite(h.UmamiHost, h.UmamiToken, h.UmamiTeamId, hostname) if err != nil { h.log("failed to create website: " + err.Error()) return diff --git a/umami_websites.go b/umami_websites.go index 79a1207..5373d50 100644 --- a/umami_websites.go +++ b/umami_websites.go @@ -15,12 +15,13 @@ type WebsitesResponse struct { type Website struct { ID string `json:"id,omitempty"` - Name string `json:"name"` - Domain string `json:"domain"` + Name string `json:"name,omitempty"` + TeamId string `json:"teamId,omitempty"` + Domain string `json:"domain,omitempty"` CreatedAt time.Time `json:"createdAt,omitempty"` } -func createWebsite(umamiHost string, umamiToken string, websiteDomain string) (*Website, error) { +func createWebsite(umamiHost string, umamiToken string, teamId string, websiteDomain string) (*Website, error) { var headers = make(http.Header) headers.Set("Authorization", "Bearer "+umamiToken) @@ -28,6 +29,7 @@ func createWebsite(umamiHost string, umamiToken string, websiteDomain string) (* err := sendRequestAndParse(umamiHost+"/api/websites", Website{ Name: websiteDomain, Domain: websiteDomain, + TeamId: teamId, }, headers, &result) if err != nil { @@ -37,12 +39,17 @@ func createWebsite(umamiHost string, umamiToken string, websiteDomain string) (* return &result, nil } -func fetchWebsites(umamiHost string, umamiToken string) (*[]Website, error) { +func fetchWebsites(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(umamiHost+"/api/websites?pageSize=200", nil, headers, &result) + err := sendRequestAndParse(url, nil, headers, &result) if err != nil { return nil, err