fix: exact redirects with a trailing slash or an upper-case query fire at runtime (CR-03)

The resolver fetched exact rules with lower(source) IN keys, while the keys
drop one trailing slash and keep the query as written, so a source stored
as /old/ or /list?Page=2 was never read. A new migration adds the stored
generated column source_key (the SQL form of exactKey) with an index for
exact rules, and the fetch compares it.
This commit is contained in:
2026-10-27 15:20:19 +01:00
parent b19cd069a5
commit 2d81c261b5
3 changed files with 92 additions and 4 deletions

View File

@@ -117,7 +117,9 @@ func toggleSlash(p string) string {
}
// exactKey is the lookup key of an exact source: lowercased, without a
// trailing slash on its path, with its query as written.
// trailing slash on its path, with its query as written. The column
// golem15_seo_redirects.source_key computes the same key in SQL (migration
// 202610280001); change both together.
func exactKey(source string) string {
p, q, hasQuery := strings.Cut(source, "?")
key := strings.ToLower(trimSlash(p))
@@ -225,8 +227,9 @@ func LoadRedirects(db *gorm.DB) ([]models.Redirect, error) {
}
// LoadRedirectSet reads the rule set for the resolver: every pattern and
// regex rule now, exact rules per path as the set needs them (indexed on
// lower(source)).
// regex rule now, exact rules per path as the set needs them, by the
// generated source_key column, which holds exactKey(source) (so the fetch
// sees the same rules as an in-memory set).
func LoadRedirectSet(db *gorm.DB) (*RedirectSet, error) {
var rows []models.Redirect
if err := db.Where("match <> ?", models.RedirectExact).Order("id ASC").Find(&rows).Error; err != nil {
@@ -235,7 +238,7 @@ func LoadRedirectSet(db *gorm.DB) (*RedirectSet, error) {
s := NewRedirectSet(rows)
s.fetch = func(keys []string) ([]models.Redirect, error) {
var out []models.Redirect
err := db.Where("match = ? AND lower(source) IN ?", models.RedirectExact, keys).Order("id ASC").Find(&out).Error
err := db.Where("match = ? AND source_key IN ?", models.RedirectExact, keys).Order("id ASC").Find(&out).Error
return out, err
}
return s, nil