- GET and PUT /_sitemap/api/v1/settings and POST /_sitemap/api/v1/rebuild resolve the administrator from a backend token, a personal API token or the MCP dispatch and require the sitemap permission (401/403 otherwise); bucket sitemap-api - PUT changes the fields sent with the settings screen's checks (422 validation_failed, nothing stored), merges the HTML sitemap addresses, writes content types, generates the IndexNow key and queues a rebuild - GET answers the settings, the content types, the index files with their counts, the left-out counts, the IndexNow state and the News source values - MCP tools sitemap_get_settings, sitemap_update_settings and sitemap_rebuild bound to those routes, listed with the permission - the content type settings write and the file listing move to classes, shared by the form, S1, the index and the API; S1 and the index now list the image and News files
83 lines
2.8 KiB
Go
83 lines
2.8 KiB
Go
package classes
|
|
|
|
import (
|
|
"time"
|
|
|
|
"git.golem15.com/golem15/sm-sitemap-plugin/models"
|
|
"git.golem15.com/golem15/summercms/modules/backpack"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// SitemapFile is one file of the sitemap index: its name, the number of
|
|
// addresses (pages for an image file, articles for the News file) and the
|
|
// newest lastmod of its addresses.
|
|
type SitemapFile struct {
|
|
Name string `json:"name"`
|
|
Count int `json:"count"`
|
|
LastMod *time.Time `json:"lastmod"`
|
|
}
|
|
|
|
// SitemapFiles lists the files the index links, in its order: the
|
|
// per-group files, the image files while images_enabled is on, then
|
|
// sitemap-news.xml while news_enabled is on and it lists an article.
|
|
func SitemapFiles(db *gorm.DB, app *backpack.App, s models.Settings, now time.Time) ([]SitemapFile, error) {
|
|
type fileRow struct {
|
|
Grp string `gorm:"column:grp"`
|
|
FileNo int `gorm:"column:file_no"`
|
|
N int `gorm:"column:n"`
|
|
LastMod *time.Time `gorm:"column:lastmod"`
|
|
}
|
|
var rows []fileRow
|
|
if err := db.Raw(`SELECT grp, file_no, COUNT(*) AS n, MAX(lastmod) AS lastmod FROM golem15_sitemap_urls
|
|
WHERE excluded_reason = '' AND file_no IS NOT NULL
|
|
GROUP BY grp, file_no ORDER BY grp, file_no`).Scan(&rows).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
if s.ImagesEnabled {
|
|
var images []fileRow
|
|
if err := db.Raw(`SELECT image_file_no AS file_no, COUNT(*) AS n, MAX(lastmod) AS lastmod FROM golem15_sitemap_urls
|
|
WHERE excluded_reason = '' AND image_file_no IS NOT NULL AND jsonb_array_length(images) > 0
|
|
GROUP BY image_file_no ORDER BY image_file_no`).Scan(&images).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
for i := range images {
|
|
images[i].Grp = ImagesGroup
|
|
}
|
|
rows = append(rows, images...)
|
|
}
|
|
out := make([]SitemapFile, 0, len(rows)+1)
|
|
for _, r := range rows {
|
|
out = append(out, SitemapFile{Name: FileName(r.Grp, r.FileNo), Count: r.N, LastMod: r.LastMod})
|
|
}
|
|
if s.NewsEnabled {
|
|
news, err := NewsEntries(db, app, s, now)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(news) > 0 {
|
|
newest := news[0].Published
|
|
out = append(out, SitemapFile{Name: NewsFile, Count: len(news), LastMod: &newest})
|
|
}
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
// LeftOut counts the addresses left out of the sitemaps by reason (a
|
|
// Filter's or the type switch's; deleted and unpublished rows are holes,
|
|
// not left out).
|
|
func LeftOut(db *gorm.DB) (map[string]int, error) {
|
|
var rows []struct {
|
|
Reason string `gorm:"column:excluded_reason"`
|
|
N int `gorm:"column:n"`
|
|
}
|
|
if err := db.Raw(`SELECT excluded_reason, COUNT(*) AS n FROM golem15_sitemap_urls
|
|
WHERE excluded_reason NOT IN ('', ?, ?) GROUP BY excluded_reason`, models.ReasonDeleted, models.ReasonUnpublished).Scan(&rows).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
out := make(map[string]int, len(rows))
|
|
for _, r := range rows {
|
|
out[r.Reason] = r.N
|
|
}
|
|
return out, nil
|
|
}
|