diff --git a/bskyweb/cmd/bskyweb/filters.go b/bskyweb/cmd/bskyweb/filters.go new file mode 100644 index 000000000..0b882780b --- /dev/null +++ b/bskyweb/cmd/bskyweb/filters.go @@ -0,0 +1,28 @@ +package main + +import ( + "net/url" + + "github.com/flosch/pongo2/v6" +) + +func init() { + pongo2.RegisterFilter("canonical", filterCanonical) +} + +func filterCanonical(in *pongo2.Value, param *pongo2.Value) (*pongo2.Value, *pongo2.Error) { + urlStr := in.String() + + parsedURL, err := url.Parse(urlStr) + if err != nil { + // If parsing fails, return the original URL + return in, nil + } + + // Remove query parameters and fragment + parsedURL.RawQuery = "" + parsedURL.Fragment = "" + + // Return the cleaned URL + return pongo2.AsValue(parsedURL.String()), nil +} -- 2.51.2 From 5c04c9e3e33b817bbd08f80cca4d66888bd958aa Mon Sep 17 00:00:00 2001 From: Caidan Williams Date: Mon, 25 Aug 2025 15:25:48 -0700 Subject: [PATCH 2/5] feat: apply canonical filter to clean URLs in post template Uses canonical filter for canonical link tags while preserving original URLs with parameters for og:url metadata, improving SEO through proper URL canonicalization --- bskyweb/templates/post.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bskyweb/templates/post.html b/bskyweb/templates/post.html index 963af2065..a290b94e7 100644 --- a/bskyweb/templates/post.html +++ b/bskyweb/templates/post.html @@ -14,7 +14,7 @@ {%- if requestURI %} - + {% endif -%} {%- if postView.Author.DisplayName %} -- 2.51.2 From f9118b2729ccfe6a651e9a1b3f033ebca2251c6d Mon Sep 17 00:00:00 2001 From: Caidan Williams Date: Mon, 25 Aug 2025 15:26:42 -0700 Subject: [PATCH 3/5] feat: apply canonical filter to profile template URLs for SEO Use canonical filter on requestURI in canonical link tag while preserving original URL with parameters for og:url metadata. This provides clean canonical URLs for search engines while maintaining full Open Graph URLs. --- bskyweb/templates/profile.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bskyweb/templates/profile.html b/bskyweb/templates/profile.html index af4180dc1..449caa39f 100644 --- a/bskyweb/templates/profile.html +++ b/bskyweb/templates/profile.html @@ -15,7 +15,7 @@ {%- if requestURI %} - + {% endif -%} {%- if profileView.DisplayName %} -- 2.51.2 From 685650b2964ffa5e86c122a410e4b9713c01dcf7 Mon Sep 17 00:00:00 2001 From: Caidan Williams Date: Mon, 25 Aug 2025 15:28:09 -0700 Subject: [PATCH 4/5] test: add comprehensive coverage for canonical URL filter - Verify clean URL passthrough behavior - Test query parameter removal functionality - Test fragment removal functionality - Test combined query params and fragments handling - Ensure graceful degradation for malformed URLs --- bskyweb/cmd/bskyweb/filters_test.go | 61 +++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 bskyweb/cmd/bskyweb/filters_test.go diff --git a/bskyweb/cmd/bskyweb/filters_test.go b/bskyweb/cmd/bskyweb/filters_test.go new file mode 100644 index 000000000..2a3fc5b86 --- /dev/null +++ b/bskyweb/cmd/bskyweb/filters_test.go @@ -0,0 +1,61 @@ +package main + +import ( + "testing" + + "github.com/flosch/pongo2/v6" +) + +func TestCanonicalFilter(t *testing.T) { + tests := []struct { + name string + input string + expected string + }{ + { + name: "clean URL", + input: "https://bsky.app/profile/user", + expected: "https://bsky.app/profile/user", + }, + { + name: "URL with query params", + input: "https://bsky.app/profile/user?utm_source=test", + expected: "https://bsky.app/profile/user", + }, + { + name: "URL with multiple params", + input: "https://bsky.app/profile/user?utm_source=twitter&utm_campaign=test", + expected: "https://bsky.app/profile/user", + }, + { + name: "URL with fragment", + input: "https://bsky.app/profile/user#section", + expected: "https://bsky.app/profile/user", + }, + { + name: "URL with both params and fragment", + input: "https://bsky.app/profile/user?param=1#section", + expected: "https://bsky.app/profile/user", + }, + { + name: "malformed URL", + input: "not-a-url", + expected: "not-a-url", // Should return original on error + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + inputValue := pongo2.AsValue(tt.input) + result, err := filterCanonical(inputValue, nil) + if err != nil { + t.Errorf("filterCanonical() error = %v", err) + return + } + + if result.String() != tt.expected { + t.Errorf("filterCanonical() = %v, want %v", result.String(), tt.expected) + } + }) + } +} -- 2.51.2 From eb45154823323b0c7ecbd49b5896118b106cd762 Mon Sep 17 00:00:00 2001 From: Caidan Williams Date: Mon, 25 Aug 2025 17:17:49 -0700 Subject: [PATCH 5/5] refactor: rename canonical filter to canonicalize_url for better clarity - Rename filter from 'canonical' to 'canonicalize_url' to follow Pongo2 naming conventions - Update function name from filterCanonical to filterCanonicalizeURL - Update template usage in post.html and profile.html - Update test function name and all references --- bskyweb/cmd/bskyweb/filters.go | 4 ++-- bskyweb/cmd/bskyweb/filters_test.go | 8 ++++---- bskyweb/templates/post.html | 2 +- bskyweb/templates/profile.html | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/bskyweb/cmd/bskyweb/filters.go b/bskyweb/cmd/bskyweb/filters.go index 0b882780b..a92cc606b 100644 --- a/bskyweb/cmd/bskyweb/filters.go +++ b/bskyweb/cmd/bskyweb/filters.go @@ -7,10 +7,10 @@ import ( ) func init() { - pongo2.RegisterFilter("canonical", filterCanonical) + pongo2.RegisterFilter("canonicalize_url", filterCanonicalizeURL) } -func filterCanonical(in *pongo2.Value, param *pongo2.Value) (*pongo2.Value, *pongo2.Error) { +func filterCanonicalizeURL(in *pongo2.Value, param *pongo2.Value) (*pongo2.Value, *pongo2.Error) { urlStr := in.String() parsedURL, err := url.Parse(urlStr) diff --git a/bskyweb/cmd/bskyweb/filters_test.go b/bskyweb/cmd/bskyweb/filters_test.go index 2a3fc5b86..a63ad0317 100644 --- a/bskyweb/cmd/bskyweb/filters_test.go +++ b/bskyweb/cmd/bskyweb/filters_test.go @@ -6,7 +6,7 @@ import ( "github.com/flosch/pongo2/v6" ) -func TestCanonicalFilter(t *testing.T) { +func TestCanonicalizeURLFilter(t *testing.T) { tests := []struct { name string input string @@ -47,14 +47,14 @@ func TestCanonicalFilter(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { inputValue := pongo2.AsValue(tt.input) - result, err := filterCanonical(inputValue, nil) + result, err := filterCanonicalizeURL(inputValue, nil) if err != nil { - t.Errorf("filterCanonical() error = %v", err) + t.Errorf("filterCanonicalizeURL() error = %v", err) return } if result.String() != tt.expected { - t.Errorf("filterCanonical() = %v, want %v", result.String(), tt.expected) + t.Errorf("filterCanonicalizeURL() = %v, want %v", result.String(), tt.expected) } }) } diff --git a/bskyweb/templates/post.html b/bskyweb/templates/post.html index a290b94e7..1f3f6da4e 100644 --- a/bskyweb/templates/post.html +++ b/bskyweb/templates/post.html @@ -14,7 +14,7 @@ {%- if requestURI %} - + {% endif -%} {%- if postView.Author.DisplayName %} diff --git a/bskyweb/templates/profile.html b/bskyweb/templates/profile.html index 449caa39f..8506a9cff 100644 --- a/bskyweb/templates/profile.html +++ b/bskyweb/templates/profile.html @@ -15,7 +15,7 @@ {%- if requestURI %} - + {% endif -%} {%- if profileView.DisplayName %}