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.
68 lines
2.2 KiB
Go
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{})
|
|
}
|