- /_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
36 lines
1.2 KiB
Go
36 lines
1.2 KiB
Go
package api
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
|
|
"git.golem15.com/golem15/sm-seo-plugin/classes"
|
|
)
|
|
|
|
// ListIssuesInput is the query of GET /_seo/api/v1/issues.
|
|
type ListIssuesInput struct {
|
|
Kind string `json:"kind,omitempty" query:"kind" jsonschema:"one kind only: missing_title, missing_description, duplicate_title, duplicate_description, rule_overrides_record, redirect_chain, canonical_noindex, canonical_redirected or noindex_blocked"`
|
|
}
|
|
|
|
// ListIssues serves GET /_seo/api/v1/issues (D-13, D-14 CMS part): the
|
|
// CMS-data issue report, each issue with its URL, kind, details and the
|
|
// tool that fixes it, and a count per kind.
|
|
func ListIssues(service func() *classes.Service) http.HandlerFunc {
|
|
return manage(service, func(ctx context.Context, svc *classes.Service, r *http.Request) (int, any, error) {
|
|
issues, err := svc.Issues(ctx)
|
|
if err != nil {
|
|
return 0, nil, err
|
|
}
|
|
kind := r.URL.Query().Get("kind")
|
|
counts := map[string]int{}
|
|
out := make([]classes.Issue, 0, len(issues))
|
|
for _, issue := range issues {
|
|
counts[issue.Kind]++
|
|
if kind == "" || issue.Kind == kind {
|
|
out = append(out, issue)
|
|
}
|
|
}
|
|
return http.StatusOK, map[string]any{"data": out, "counts": counts, "total": len(out)}, nil
|
|
})
|
|
}
|