- /_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
290 lines
11 KiB
Go
290 lines
11 KiB
Go
package seo
|
|
|
|
import (
|
|
"net/http"
|
|
|
|
apicontrollers "git.golem15.com/golem15/sm-seo-plugin/controllers/api"
|
|
"git.golem15.com/golem15/summercms/modules/pact"
|
|
)
|
|
|
|
var _ pact.HasMCP = (*Plugin)(nil)
|
|
|
|
// mcpGuard is the guard every SEO tool runs as: the backend account linked
|
|
// to the MCP user. The routes resolve that administrator themselves.
|
|
const mcpGuard = "backend"
|
|
|
|
// mcpPermission is the one SEO permission (D-15) every tool is listed with;
|
|
// the routes check it on every call.
|
|
const mcpPermission = apicontrollers.PermissionManage
|
|
|
|
// MCP declares the SEO tools (D-10, P-24): one per operation of the SEO
|
|
// admin (operations.go maps each admin operation to its tool), each bound
|
|
// to a /_seo/api/v1 management route, run as the backend account linked to
|
|
// the MCP user and listed only when that account has golem15.seo.manage.
|
|
// pier prefixes them with seo_. GET tools are read-only, so a read grant
|
|
// sees only those; deletes and "mark as gone" are destructive.
|
|
func (p *Plugin) MCP() pact.MCP {
|
|
destructive := true
|
|
return pact.MCP{Tools: []pact.MCPTool{
|
|
// Pages addressed by URL (D-11, D-13).
|
|
{
|
|
Name: "get_url_seo",
|
|
Title: "Get the SEO of a URL",
|
|
Description: "Shows the effective SEO of a page addressed by URL: every value with its source (URL rule, record, template, global settings, content or default), the redirect decision, the owner a change would be written to (the record or a URL rule) and the warnings of the SEO tab.",
|
|
Guard: mcpGuard,
|
|
Method: http.MethodGet,
|
|
Path: "/_seo/api/v1/seo",
|
|
Input: apicontrollers.GetSEOInput{},
|
|
Permissions: []string{mcpPermission},
|
|
},
|
|
{
|
|
Name: "set_url_seo",
|
|
Title: "Set the SEO of a URL",
|
|
Description: "Changes the SEO values of a page addressed by URL. A record behind the URL gets the change in its SEO tab in the URL's locale; otherwise, or with force_rule, an exact URL rule is written. The answer says where it was written.",
|
|
Guard: mcpGuard,
|
|
Method: http.MethodPut,
|
|
Path: "/_seo/api/v1/seo",
|
|
Input: apicontrollers.SetSEOInput{},
|
|
Permissions: []string{mcpPermission},
|
|
},
|
|
|
|
// URL rules (D-02).
|
|
{
|
|
Name: "list_rules",
|
|
Title: "List URL rules",
|
|
Description: "Lists the URL rules (SEO for an address with or without a record behind it) by address, with a search text and the match kind.",
|
|
Guard: mcpGuard,
|
|
Method: http.MethodGet,
|
|
Path: "/_seo/api/v1/rules",
|
|
Input: apicontrollers.ListRulesInput{},
|
|
Permissions: []string{mcpPermission},
|
|
},
|
|
{
|
|
Name: "show_rule",
|
|
Title: "Show a URL rule",
|
|
Description: "Shows one URL rule by id.",
|
|
Guard: mcpGuard,
|
|
Method: http.MethodGet,
|
|
Path: "/_seo/api/v1/rules/{id}",
|
|
Input: apicontrollers.RuleIDInput{},
|
|
Permissions: []string{mcpPermission},
|
|
},
|
|
{
|
|
Name: "create_rule",
|
|
Title: "Create a URL rule",
|
|
Description: "Creates a URL rule: an exact address or a pattern where * matches any text, with the values it sets. A rule wins over the record's SEO tab.",
|
|
Guard: mcpGuard,
|
|
Method: http.MethodPost,
|
|
Path: "/_seo/api/v1/rules",
|
|
Input: apicontrollers.CreateRuleInput{},
|
|
Permissions: []string{mcpPermission},
|
|
},
|
|
{
|
|
Name: "update_rule",
|
|
Title: "Update a URL rule",
|
|
Description: "Changes the fields sent of one URL rule by id.",
|
|
Guard: mcpGuard,
|
|
Method: http.MethodPut,
|
|
Path: "/_seo/api/v1/rules/{id}",
|
|
Input: apicontrollers.UpdateRuleInput{},
|
|
Permissions: []string{mcpPermission},
|
|
},
|
|
{
|
|
Name: "delete_rule",
|
|
Title: "Delete a URL rule",
|
|
Description: "Deletes one URL rule by id; history keeps its values.",
|
|
Guard: mcpGuard,
|
|
Method: http.MethodDelete,
|
|
Path: "/_seo/api/v1/rules/{id}",
|
|
Input: apicontrollers.RuleIDInput{},
|
|
Permissions: []string{mcpPermission},
|
|
Destructive: &destructive,
|
|
},
|
|
|
|
// Redirects (D-16 to D-19).
|
|
{
|
|
Name: "list_redirects",
|
|
Title: "List redirects",
|
|
Description: "Lists redirects, newest change first, with a search text and the status (301, 302, 410) and origin filters.",
|
|
Guard: mcpGuard,
|
|
Method: http.MethodGet,
|
|
Path: "/_seo/api/v1/redirects",
|
|
Input: apicontrollers.ListRedirectsInput{},
|
|
Permissions: []string{mcpPermission},
|
|
},
|
|
{
|
|
Name: "show_redirect",
|
|
Title: "Show a redirect",
|
|
Description: "Shows one redirect by id.",
|
|
Guard: mcpGuard,
|
|
Method: http.MethodGet,
|
|
Path: "/_seo/api/v1/redirects/{id}",
|
|
Input: apicontrollers.RedirectIDInput{},
|
|
Permissions: []string{mcpPermission},
|
|
},
|
|
{
|
|
Name: "create_redirect",
|
|
Title: "Create a redirect",
|
|
Description: "Creates a 301, 302 or 410 redirect from a path, a pattern or a regular expression. A redirect that would create a loop is refused with redirect_loop and the loop's path; a chain is saved with a warning.",
|
|
Guard: mcpGuard,
|
|
Method: http.MethodPost,
|
|
Path: "/_seo/api/v1/redirects",
|
|
Input: apicontrollers.CreateRedirectInput{},
|
|
Permissions: []string{mcpPermission},
|
|
},
|
|
{
|
|
Name: "update_redirect",
|
|
Title: "Update a redirect",
|
|
Description: "Changes the fields sent of one redirect by id; a loop is refused like a create.",
|
|
Guard: mcpGuard,
|
|
Method: http.MethodPut,
|
|
Path: "/_seo/api/v1/redirects/{id}",
|
|
Input: apicontrollers.UpdateRedirectInput{},
|
|
Permissions: []string{mcpPermission},
|
|
},
|
|
{
|
|
Name: "delete_redirect",
|
|
Title: "Delete a redirect",
|
|
Description: "Deletes one redirect by id; history keeps its values.",
|
|
Guard: mcpGuard,
|
|
Method: http.MethodDelete,
|
|
Path: "/_seo/api/v1/redirects/{id}",
|
|
Input: apicontrollers.RedirectIDInput{},
|
|
Permissions: []string{mcpPermission},
|
|
Destructive: &destructive,
|
|
},
|
|
|
|
// 404 log (D-20).
|
|
{
|
|
Name: "list_not_found",
|
|
Title: "List the 404 log",
|
|
Description: "Lists the addresses that answered 404, most hits first, with whether a redirect answers them now (status open or resolved).",
|
|
Guard: mcpGuard,
|
|
Method: http.MethodGet,
|
|
Path: "/_seo/api/v1/notfound",
|
|
Input: apicontrollers.ListNotFoundInput{},
|
|
Permissions: []string{mcpPermission},
|
|
},
|
|
{
|
|
Name: "redirect_not_found",
|
|
Title: "Redirect a 404 address",
|
|
Description: "Creates a 301 from a logged 404 address to a target. Refused when a rule already answers the address.",
|
|
Guard: mcpGuard,
|
|
Method: http.MethodPost,
|
|
Path: "/_seo/api/v1/notfound/{id}/redirect",
|
|
Input: apicontrollers.RedirectNotFoundInput{},
|
|
Permissions: []string{mcpPermission},
|
|
},
|
|
{
|
|
Name: "gone_not_found",
|
|
Title: "Mark a 404 address as gone",
|
|
Description: "Creates a 410 for a logged 404 address, which tells search engines to drop it. Refused when a rule already answers the address.",
|
|
Guard: mcpGuard,
|
|
Method: http.MethodPost,
|
|
Path: "/_seo/api/v1/notfound/{id}/gone",
|
|
Input: apicontrollers.NotFoundIDInput{},
|
|
Permissions: []string{mcpPermission},
|
|
Destructive: &destructive,
|
|
},
|
|
{
|
|
Name: "delete_not_found",
|
|
Title: "Remove a 404 log entry",
|
|
Description: "Removes one entry from the 404 log; it comes back on its next 404.",
|
|
Guard: mcpGuard,
|
|
Method: http.MethodDelete,
|
|
Path: "/_seo/api/v1/notfound/{id}",
|
|
Input: apicontrollers.NotFoundIDInput{},
|
|
Permissions: []string{mcpPermission},
|
|
Destructive: &destructive,
|
|
},
|
|
|
|
// robots.txt and AI bots (D-22, D-24, D-25, D-99).
|
|
{
|
|
Name: "show_robots",
|
|
Title: "Show robots.txt",
|
|
Description: "Shows the robots.txt rules, the AI bot switches, the file crawlers get in production and whether the site runs outside production (crawlers then get Disallow: /).",
|
|
Guard: mcpGuard,
|
|
Method: http.MethodGet,
|
|
Path: "/_seo/api/v1/robots",
|
|
Input: struct{}{},
|
|
Permissions: []string{mcpPermission},
|
|
},
|
|
{
|
|
Name: "update_robots",
|
|
Title: "Update robots.txt",
|
|
Description: "Changes the robots.txt rules and the AI bot switches sent. The answer lists noindex addresses the new rules keep Googlebot from.",
|
|
Guard: mcpGuard,
|
|
Method: http.MethodPut,
|
|
Path: "/_seo/api/v1/robots",
|
|
Input: apicontrollers.UpdateRobotsInput{},
|
|
Permissions: []string{mcpPermission},
|
|
},
|
|
{
|
|
Name: "test_robots",
|
|
Title: "Test an address against robots.txt",
|
|
Description: "Answers whether a crawler may fetch an address under the saved production robots.txt, with Google's matcher and the deciding line.",
|
|
Guard: mcpGuard,
|
|
Method: http.MethodGet,
|
|
Path: "/_seo/api/v1/robots/test",
|
|
Input: apicontrollers.TestRobotsInput{},
|
|
Permissions: []string{mcpPermission},
|
|
},
|
|
|
|
// Settings (D-03, D-05, D-07, D-20b, D-21, D-23, D-104).
|
|
{
|
|
Name: "show_settings",
|
|
Title: "Show the SEO settings",
|
|
Description: "Shows the SEO settings (templates, defaults, the query parameter list, indexing paths, the address form, verification) and the settings of every content type.",
|
|
Guard: mcpGuard,
|
|
Method: http.MethodGet,
|
|
Path: "/_seo/api/v1/settings",
|
|
Input: struct{}{},
|
|
Permissions: []string{mcpPermission},
|
|
},
|
|
{
|
|
Name: "update_settings",
|
|
Title: "Update the SEO settings",
|
|
Description: "Changes the SEO settings sent and, under types, the settings of content types by key, with the checks of the settings screen.",
|
|
Guard: mcpGuard,
|
|
Method: http.MethodPut,
|
|
Path: "/_seo/api/v1/settings",
|
|
Input: apicontrollers.UpdateSettingsInput{},
|
|
Permissions: []string{mcpPermission},
|
|
},
|
|
|
|
// History (D-13).
|
|
{
|
|
Name: "list_history",
|
|
Title: "List SEO history",
|
|
Description: "Lists SEO changes, newest first, with author, time, via (admin, mcp, import, auto, undo), before and after values, filtered by via, kind or batch.",
|
|
Guard: mcpGuard,
|
|
Method: http.MethodGet,
|
|
Path: "/_seo/api/v1/history",
|
|
Input: apicontrollers.ListHistoryInput{},
|
|
Permissions: []string{mcpPermission},
|
|
},
|
|
{
|
|
Name: "show_history",
|
|
Title: "Show an SEO change",
|
|
Description: "Shows one SEO change by id with its before and after values.",
|
|
Guard: mcpGuard,
|
|
Method: http.MethodGet,
|
|
Path: "/_seo/api/v1/history/{id}",
|
|
Input: apicontrollers.HistoryIDInput{},
|
|
Permissions: []string{mcpPermission},
|
|
},
|
|
|
|
// Audit (D-13, D-14 CMS part).
|
|
{
|
|
Name: "list_issues",
|
|
Title: "List SEO issues",
|
|
Description: "Lists the CMS-data SEO issues: missing and duplicate titles and descriptions, URL rules overriding a record, redirect chains, canonicals pointing to noindex or redirected addresses, and noindex addresses robots.txt blocks; each names the tool that fixes it.",
|
|
Guard: mcpGuard,
|
|
Method: http.MethodGet,
|
|
Path: "/_seo/api/v1/issues",
|
|
Input: apicontrollers.ListIssuesInput{},
|
|
Permissions: []string{mcpPermission},
|
|
},
|
|
}}
|
|
}
|