Files
sm-seo-plugin/models/notfound.go
Jakub Zych 7677d8d8af fix: the Open/Resolved and Chains filters apply an exact rule's slash, case and query options (WR-22)
coveredSQL and chainSQL compared the source literally, so /missing/ or
/missing?utm=x stayed open after /missing answered it, and /a -> /b/ was
no chain next to /b. Both now use one SQL form of the runtime's exact
match (prefiltered on the indexed source_key). Pattern and regex rules
remain with the per-row badge, which the list_not_found schema text now
says.
2026-10-27 15:41:18 +01:00

68 lines
2.2 KiB
Go

package models
import (
"time"
"gorm.io/gorm"
)
// NotFound is one address of the 404 log on golem15_seo_notfound (D-20):
// the path (with its query) that answered 404, how often, when first and
// last, and the referer of the last hit. The frontend reports each 404
// through the authenticated report endpoint.
type NotFound struct {
ID uint `gorm:"column:id;primaryKey"`
URL string `gorm:"column:url"`
Hits int64 `gorm:"column:hits"`
FirstSeen *time.Time `gorm:"column:first_seen"`
LastHit *time.Time `gorm:"column:last_hit"`
Referer string `gorm:"column:referer"`
}
func (NotFound) TableName() string { return "golem15_seo_notfound" }
// MorphName names a 404 log entry in history.
func (NotFound) MorphName() string { return `Golem15\SEO\Models\NotFound` }
// Fillable is empty: entries come from the report endpoint only, and the
// preview edits nothing.
func (NotFound) Fillable() []string { return []string{} }
// Rules is empty: nothing is edited.
func (NotFound) Rules() map[string]string { return map[string]string{} }
// The 404 log's status filter scope and its values.
const (
FilterNotFoundStatus = "FilterNotFoundStatus"
NotFoundOpen = "open"
NotFoundResolved = "resolved"
)
// FilterScopes lists the 404 log's filter scopes.
func (NotFound) FilterScopes() []string { return []string{FilterNotFoundStatus} }
// coveredSQL is true for an entry an exact redirect or 410 answers now,
// with the rule's case, trailing-slash and query options as the runtime
// matcher applies them (WR-22). Pattern and regex rules are left to the
// Resolved badge, which runs the compiled rule set.
var coveredSQL = `EXISTS (SELECT 1 FROM golem15_seo_redirects AS r WHERE ` + coversSQL("r", "golem15_seo_notfound.url") + `)`
// FilterScope narrows the 404 log to open or resolved entries; an unknown
// value matches nothing.
func (NotFound) FilterScope(name string, db *gorm.DB, value any) *gorm.DB {
if db == nil || name != FilterNotFoundStatus {
return db
}
switch s, _ := value.(string); s {
case NotFoundOpen:
return db.Where("NOT " + coveredSQL)
case NotFoundResolved:
return db.Where(coveredSQL)
}
return db.Where("1 = 0")
}
func init() {
Register(NotFound{})
}