- /_seo/api/v1 routes for the effective SEO of a URL with sources and owner, URL rules, redirects (loops refused with redirect_loop), the 404 log actions, robots.txt and its tester, settings and history - the CMS-data issue report: missing and duplicate titles and descriptions, rules over records, redirect chains, canonicals to noindex or redirected addresses, noindex addresses robots.txt blocks - 24 MCP tools on those routes (backend guard, golem15.seo.manage) - AdminOperations maps every admin operation to its tool; TestEveryAdminOperationHasTool and TestMCPReadToolsAreGET guard drift - robots.txt writes go through classes.SaveRobots from the admin too
65 lines
1.9 KiB
Go
65 lines
1.9 KiB
Go
package classes
|
|
|
|
import (
|
|
"context"
|
|
"strconv"
|
|
|
|
"git.golem15.com/golem15/sm-seo-plugin/models"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// LoadRedirect returns redirect id; ErrNoRecord when there is none.
|
|
func LoadRedirect(db *gorm.DB, id uint) (models.Redirect, error) {
|
|
var rows []models.Redirect
|
|
if err := db.Where("id = ?", id).Limit(1).Find(&rows).Error; err != nil {
|
|
return models.Redirect{}, err
|
|
}
|
|
if len(rows) == 0 {
|
|
return models.Redirect{}, ErrNoRecord
|
|
}
|
|
return rows[0], nil
|
|
}
|
|
|
|
// LoadNotFound returns 404 log entry id; ErrNoRecord when there is none.
|
|
func LoadNotFound(db *gorm.DB, id uint) (models.NotFound, error) {
|
|
var rows []models.NotFound
|
|
if err := db.Where("id = ?", id).Limit(1).Find(&rows).Error; err != nil {
|
|
return models.NotFound{}, err
|
|
}
|
|
if len(rows) == 0 {
|
|
return models.NotFound{}, ErrNoRecord
|
|
}
|
|
return rows[0], nil
|
|
}
|
|
|
|
// NotFoundValues are a 404 log entry's values for history.
|
|
func NotFoundValues(n models.NotFound) map[string]any {
|
|
return map[string]any{"url": n.URL, "hits": n.Hits, "referer": n.Referer}
|
|
}
|
|
|
|
// DeleteNotFound removes a 404 log entry inside tx and records it in
|
|
// history; the address is logged again on its next 404.
|
|
func (s *Service) DeleteNotFound(ctx context.Context, tx *gorm.DB, n models.NotFound) error {
|
|
if err := tx.Delete(&models.NotFound{}, n.ID).Error; err != nil {
|
|
return err
|
|
}
|
|
return s.Record(ctx, tx, Change{
|
|
Kind: models.SubjectNotFound,
|
|
SubjectID: strconv.FormatUint(uint64(n.ID), 10),
|
|
What: s.Phrase("golem15.seo::lang.history.what_notfound", map[string]string{"url": n.URL}),
|
|
Before: NotFoundValues(n),
|
|
})
|
|
}
|
|
|
|
// RedirectOrigin is the origin of a redirect created through ctx: mcp
|
|
// through MCP, import through an import, else manual.
|
|
func RedirectOrigin(ctx context.Context) string {
|
|
switch ViaOf(ctx) {
|
|
case models.ViaMCP:
|
|
return models.OriginMCP
|
|
case models.ViaImport:
|
|
return models.OriginImport
|
|
}
|
|
return models.OriginManual
|
|
}
|