- GET /_sitemap/api/v1/html?locale= answers the overview: the listed
addresses of the XML sitemaps by group, in the order pages, offers,
services, categories (as a tree), posts (newest first), tags, authors,
other content types, each with its title in the locale, total, the
first html_page_size links and more
- GET /_sitemap/api/v1/html/{group}?locale=&page= answers one page of a
group; past the last page, an unknown group or the HTML sitemap off
answer 404; every answer is noindex; per-IP bucket sitemap-html
- the HTML sitemap tab: html_enabled, the address per locale (html_paths)
and html_page_size 50 to 500, with errors.html_path and errors.page_size
- contract.Listing.Settings answers HTMLEnabled, HTMLPaths, HTMLPageSize
and KeyFile
117 lines
3.6 KiB
Go
117 lines
3.6 KiB
Go
package api
|
|
|
|
import (
|
|
"net/http"
|
|
"regexp"
|
|
"strconv"
|
|
|
|
"git.golem15.com/golem15/sm-sitemap-plugin/classes"
|
|
"git.golem15.com/golem15/summercms/modules/backpack"
|
|
"git.golem15.com/golem15/summercms/modules/wire"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// GroupPattern is the {group} constraint of the HTML sitemap group route:
|
|
// a file group slug.
|
|
const GroupPattern = `[a-z0-9][a-z0-9-]{0,63}`
|
|
|
|
// localePattern is the form a locale query value must have.
|
|
var localePattern = regexp.MustCompile(`^[a-z]{2,3}([-_][A-Za-z0-9]{2,8})*$`)
|
|
|
|
// pagePattern is the form of the page query value: 1 to 6 digits.
|
|
var pagePattern = regexp.MustCompile(`^[0-9]{1,6}$`)
|
|
|
|
// htmlRequest checks the shared parts of an HTML sitemap request and
|
|
// returns the settings and database, or answers 404 (the HTML sitemap is
|
|
// off, a malformed locale) or 500 itself.
|
|
func htmlRequest(app func() *backpack.App, w http.ResponseWriter, r *http.Request) (classes.HTMLSitemap, bool) {
|
|
w.Header().Set("X-Robots-Tag", "noindex")
|
|
w.Header().Set("Cache-Control", "no-cache, private")
|
|
a := app()
|
|
if a == nil {
|
|
http.NotFound(w, r)
|
|
return classes.HTMLSitemap{}, false
|
|
}
|
|
if loc := r.URL.Query().Get("locale"); loc != "" && !localePattern.MatchString(loc) {
|
|
http.NotFound(w, r)
|
|
return classes.HTMLSitemap{}, false
|
|
}
|
|
db, ok := a.Lookup[*gorm.DB]()
|
|
if !ok || db == nil {
|
|
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
|
return classes.HTMLSitemap{}, false
|
|
}
|
|
return classes.HTMLSitemap{App: a, DB: db.WithContext(r.Context())}, true
|
|
}
|
|
|
|
// HTMLOverview serves GET /_sitemap/api/v1/html?locale=: the HTML sitemap
|
|
// overview (D-34; classes.HTMLSitemap.Overview). It lists exactly the
|
|
// addresses of the XML sitemaps and answers 404 while the HTML sitemap is
|
|
// off. Every answer carries X-Robots-Tag: noindex.
|
|
func HTMLOverview(app func() *backpack.App) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
h, ok := htmlRequest(app, w, r)
|
|
if !ok {
|
|
return
|
|
}
|
|
settings, err := classes.LoadSettings(h.DB)
|
|
if err != nil {
|
|
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
if !settings.HTMLEnabled {
|
|
http.NotFound(w, r)
|
|
return
|
|
}
|
|
out, err := h.Overview(r.Context(), settings, r.URL.Query().Get("locale"))
|
|
if err != nil {
|
|
log.Error("golem15.sitemap: html overview", "error", err)
|
|
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
wire.WriteJSON(w, http.StatusOK, out)
|
|
}
|
|
}
|
|
|
|
// HTMLGroup serves GET /_sitemap/api/v1/html/{group}?locale=&page=: one
|
|
// page (from 1, the default) of a group (classes.HTMLSitemap.Group). An
|
|
// unknown group, a page past the last, a malformed page or the HTML
|
|
// sitemap off answer 404.
|
|
func HTMLGroup(app func() *backpack.App) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
h, ok := htmlRequest(app, w, r)
|
|
if !ok {
|
|
return
|
|
}
|
|
page := 1
|
|
if raw := r.URL.Query().Get("page"); raw != "" {
|
|
n, err := strconv.Atoi(raw)
|
|
if !pagePattern.MatchString(raw) || err != nil || n < 1 {
|
|
http.NotFound(w, r)
|
|
return
|
|
}
|
|
page = n
|
|
}
|
|
settings, err := classes.LoadSettings(h.DB)
|
|
if err != nil {
|
|
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
if !settings.HTMLEnabled {
|
|
http.NotFound(w, r)
|
|
return
|
|
}
|
|
out, found, err := h.Group(r.Context(), settings, r.URL.Query().Get("locale"), r.PathValue("group"), page)
|
|
if err != nil {
|
|
log.Error("golem15.sitemap: html group", "error", err)
|
|
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
if !found {
|
|
http.NotFound(w, r)
|
|
return
|
|
}
|
|
wire.WriteJSON(w, http.StatusOK, out)
|
|
}
|
|
}
|