fix: the definitions migration keeps and logs every url item

Migration 202610280002 converts the Phase 17 url items through
models.ConvertLegacyItems: a relative path gets its leading slash,
spaces and the bytes " < > \ are percent-encoded, and a malformed item
no longer drops the whole definition. Each url item that cannot be
copied is logged at warn level with its label, value and reason.
This commit is contained in:
Jakub Zych
2026-11-04 23:05:45 +01:00
parent 2200e46d98
commit b37b2265ed
5 changed files with 193 additions and 22 deletions

View File

@@ -5,6 +5,7 @@ import (
"strings"
"git.golem15.com/golem15/sm-sitemap-plugin/models"
"git.golem15.com/golem15/summercms/modules/diary"
"github.com/go-gormigrate/gormigrate/v2"
"gorm.io/gorm"
)
@@ -57,15 +58,22 @@ func init() {
Register(updateSettings...)
}
// migrationLog is the migrations' logger (component sitemap).
var migrationLog = diary.For("sitemap")
// copyDefinitions moves the Phase 17 data into the new columns (D-32): the
// url items of the definition /sitemap.xml served (theme default, the
// first one) become the additional addresses as given, without a lastmod
// (an address that models.AllowedAddress refuses, or one already copied,
// is skipped); frontend_url becomes site_url when models.AllowedFrontendURL
// accepts it, and frontend_default_locale default_locale. Reference items
// are dropped: the content types list their records. The definitions table
// and the Phase 17 columns are left as they are, so a rollback (which drops
// the new columns) keeps every value.
// first one) become the additional addresses, without a lastmod, through
// models.ConvertLegacyItems (P33-66): a relative path gets its leading
// slash, spaces and the bytes " < > \ are percent-encoded, a duplicate is
// kept once, and each url item that still cannot be an address (or is
// malformed) is logged at warn level with its label, value and reason
// while the other items are copied. frontend_url becomes site_url when
// models.AllowedFrontendURL accepts it, and frontend_default_locale
// default_locale. Reference items are dropped: the content types list
// their records. The definitions table and the Phase 17 columns are left
// as they are, so a rollback (which drops the new columns) keeps every
// value.
func copyDefinitions(tx *gorm.DB) error {
var data []string
if err := tx.Raw(`SELECT COALESCE(data, '') FROM golem15_sitemap_definitions WHERE theme = 'default' ORDER BY id ASC LIMIT 1`).Scan(&data).Error; err != nil {
@@ -73,20 +81,14 @@ func copyDefinitions(tx *gorm.DB) error {
}
rows := []models.ExtraURL{}
if len(data) > 0 {
items, _, err := models.ParseLegacyItems([]byte(data[0]))
if err != nil {
// A definition the Phase 17 code could not read either: nothing
// is copied, and the row stays for a manual look.
items = nil
}
seen := map[string]bool{}
for _, item := range items {
u := strings.TrimSpace(item.URL)
if item.Type != models.LegacyItemURL || seen[u] || !models.AllowedAddress(u) {
var dropped []models.DroppedItem
rows, dropped = models.ConvertLegacyItems([]byte(data[0]))
for _, d := range dropped {
if d.Label == "" && d.URL == "" {
migrationLog.Warn("golem15.sitemap: definition not copied", "reason", d.Reason)
continue
}
seen[u] = true
rows = append(rows, models.ExtraURL{URL: u})
migrationLog.Warn("golem15.sitemap: definition url item not copied", "item", d.Label, "url", d.URL, "reason", d.Reason)
}
}
raw, err := json.Marshal(rows)