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 // (the list filter; the Resolved badge also counts patterns and regex). const coveredSQL = `EXISTS (SELECT 1 FROM golem15_seo_redirects AS r WHERE r.match = 'exact' AND (r.source = golem15_seo_notfound.url OR (r.ignore_case AND lower(r.source) = lower(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{}) }