feat: migrate definitions into additional addresses

- migration 202610280002 copies the served definition's url items into
  extra_urls, frontend_url into site_url and frontend_default_locale into
  default_locale; reference items are dropped
- the definitions table and the Phase 17 columns keep their data, so a
  rollback loses nothing (TestMigrateDefinitions)
This commit is contained in:
Jakub Zych
2026-11-04 11:14:01 +01:00
parent 84cb27a810
commit 62fc7d71a3
2 changed files with 159 additions and 1 deletions

View File

@@ -1,6 +1,10 @@
package updates
import (
"encoding/json"
"strings"
"git.golem15.com/golem15/sm-sitemap-plugin/models"
"github.com/go-gormigrate/gormigrate/v2"
"gorm.io/gorm"
)
@@ -33,7 +37,10 @@ var updateSettings = []*gormigrate.Migration{
return err
}
}
return tx.Exec(`INSERT INTO golem15_sitemap_settings (id) VALUES (1) ON CONFLICT (id) DO NOTHING`).Error
if err := tx.Exec(`INSERT INTO golem15_sitemap_settings (id) VALUES (1) ON CONFLICT (id) DO NOTHING`).Error; err != nil {
return err
}
return copyDefinitions(tx)
},
Rollback: func(tx *gorm.DB) error {
for _, name := range []string{"site_url", "file_size", "default_locale", "extra_urls", "rebuild_pending_until", "numbered_size", "built_at", "rebuild_requested_at"} {
@@ -49,3 +56,56 @@ var updateSettings = []*gormigrate.Migration{
func init() {
Register(updateSettings...)
}
// 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.
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 {
return err
}
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) {
continue
}
seen[u] = true
rows = append(rows, models.ExtraURL{URL: u})
}
}
raw, err := json.Marshal(rows)
if err != nil {
return err
}
var old struct {
FrontendURL *string `gorm:"column:frontend_url"`
Locale *string `gorm:"column:frontend_default_locale"`
}
if err := tx.Raw(`SELECT frontend_url, frontend_default_locale FROM golem15_sitemap_settings WHERE id = 1`).Scan(&old).Error; err != nil {
return err
}
var site, locale any
if u := strings.TrimSpace(models.Text(old.FrontendURL)); u != "" && models.AllowedFrontendURL(u) {
site = u
}
if l := strings.TrimSpace(models.Text(old.Locale)); l != "" {
locale = l
}
return tx.Exec(`UPDATE golem15_sitemap_settings SET extra_urls = ?::jsonb, site_url = ?, default_locale = ? WHERE id = 1`, string(raw), site, locale).Error
}