Skip to content

fix: custom pages overwrite each other in page sync (all share name "legacy") - #85

Open
k-r-a-s-s wants to merge 2 commits into
raisely:masterfrom
k-r-a-s-s:fix/page-sync-filename-collision
Open

fix: custom pages overwrite each other in page sync (all share name "legacy")#85
k-r-a-s-s wants to merge 2 commits into
raisely:masterfrom
k-r-a-s-s:fix/page-sync-filename-collision

Conversation

@k-r-a-s-s

@k-r-a-s-sk-r-a-s-s commented Aug 27, 2026

Copy link
Copy Markdown

Problem

raisely update / raisely init write each synced page to pages/<page.name>.json. Template pages have unique names (home, dashboard, profile, …), but every custom page-builder page has name: "legacy" — so on any campaign with more than one custom page, all of them are written to the same legacy.json, last write wins.

Observed on our campaign: the API returns 72 pages, 55 of which are custom (name: "legacy"). After a sync, only the ~20 uniquely-named template pages plus a single legacy.json exist locally — 54 custom pages never land on disk, and legacy.json holds whichever custom page the API happened to return last (which can change between syncs).

Beyond the missing files, this is a correctness hazard: raisely deploy PATCHes every local page JSON by its embedded uuid, so a stale legacy.json snapshot of a semi-random page can silently revert that page's body/status/path in the admin.

Fix

Extract the naming logic into pageFileNames(pages), which computes names for a campaign's page set as a whole:

  • a page's name is used only when it's unique within the campaign (all existing template-page filenames are unchanged: home.json, dashboard.json, …);
  • pages with a shared or missing name are named from their path (/cause-areascause-areas.json), matching the existing fallback already used for unnamed pages;
  • residual collisions after character sanitization get a short uuid suffix so no two pages can ever share a file.

Deploy needs no changes — it already resolves the target page from the uuid inside each file, not the filename.

Notes

  • On repos synced with the current behaviour, the first sync after this fix writes custom pages as new path-named files; the old legacy.json is left behind and can be deleted (it duplicates one of the new files' uuid).
  • Added tests/sync.test.js covering unique names, the shared-legacy fallback, unnamed pages, sanitization, and suffix disambiguation. Full suite passes (111/111).

🤖 Generated with Claude Code


Note

Medium Risk
Sync output filenames change for campaigns with multiple custom pages, which can leave stale legacy.json locally but fixes silent data loss and deploy correctness risk from wrong page bodies keyed by uuid.

Overview
Fixes page sync so multiple custom (page-builder) pages no longer overwrite a single legacy.json when they all share name: "legacy".

pageFileNames(pages) replaces per-page pageFileName: it assigns filenames across the whole campaign—unique page.name values still map to stable template files (home.json, etc.), while shared or missing names use path-based names with sanitization, and uuid suffixes when paths collide after sanitization. A two-pass rule reserves unique name-based filenames so API order cannot steal e.g. profile.json from a template page.

syncPages writes each page using the computed filename; deploy behavior is unchanged because targets are resolved by embedded uuid, not filename. New tests/sync.test.js covers shared legacy, unnamed pages, sanitization, template reservation, and collision suffixes.

Reviewed by Cursor Bugbot for commit 5489e7d. Bugbot is set up for automated code reviews on this repo. Configure here.

syncPages names each local file by page.name, but every custom
(page-builder) page shares the name "legacy". On campaigns with more
than one custom page, all of them are written to the same legacy.json
(last write wins), so most custom pages never appear locally and the
surviving file's identity changes between syncs.
Name files by the page's unique name when it is unique across the
campaign, and fall back to the page path otherwise (cause-areas.json,
terms.json, ...). Residual collisions after sanitization are
disambiguated with a short uuid suffix. Deploy is unaffected: pages are
PATCHed by the uuid stored inside each file.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@cursorcursorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

Reviewed by Cursor Bugbot for commit 72243dd. Configure here.

Comment threadsrc/actions/sync.js Outdated
Two-pass assignment so a custom page whose path sanitizes to a template
page's name (e.g. /profile vs the profile template at /:id) can never
take that template's filename, regardless of API order. The custom page
gets the uuid-suffixed name instead.
Addresses Bugbot review on raisely#85.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@k-r-a-s-s

Copy link
Copy Markdown
Author

Good catch from Bugbot — fixed in 5489e7d. pageFileNames now assigns names in two passes: unique-name (template) filenames are reserved up front, then shared/unnamed pages get path-based names around them, so a custom page at e.g. /profile can never displace profile.json regardless of API order (it gets the uuid-suffixed name instead). Added a regression test for exactly that ordering; suite passes 112/112.

k-r-a-s-s pushed a commit to High-Impact-Athletes/raisely-cli that referenced this pull request Aug 29, 2026
Two-pass assignment so a custom page whose path sanitizes to a template
page's name (e.g. /profile vs the profile template at /:id) can never
take that template's filename, regardless of API order. The custom page
gets the uuid-suffixed name instead.
Addresses Bugbot review on raisely#85.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@k-r-a-s-s