The ❌ delete buttons called wrap.closest('.array-items') AFTER remove();
a detached node has no ancestors, so closest() returned null and
reindexItems() silently skipped. Remaining items kept their old indices,
collect() produced sparse arrays (null holes) and saves failed schema
validation, e.g. '$.details.services[1].specs.items[0]: string érték
szükséges'. Capture the container before remove() for both str-item and
obj-card delete handlers.
Regression test runs the real browser script in jsdom and clicks the
actual delete buttons (nested string array + object card reindexing).
Closes MITHOME-67
Companion to the cms-logo.js extraction: keeps content-editor.js under
the 400-line hard limit. No behavior change — covered by
test-content-editor-versions.js.
- 🎨 Logó page in the CMS bottom bar: replace the website header logo and
the CMS login icon with a PNG upload (magic-byte validation, 1 MiB cap)
- the replaced logo gets a timestamped backup in .content-backups; every
upload is audited (logo_updated)
- /logo.png?variant=header serves the header variant for the preview
- publish stages proto/public too, so logo changes ride the same
commit+deploy pipeline as content
- route handling extracted to scripts/cms-logo.js to stay under the
400-line limit
- integration test: upload+replace+backup, variant preview, 415/413/400,
CSRF, auth
Closes MITHOME-65
- GET /versions lists the automatic backups of the selected file (timestamp,
size); ?show=<backup> renders a line diff against the current content
- POST /restore validates the backup against the content schema and restores
it atomically; the pre-restore state gets a fresh backup first, so a
restore itself is reversible; audited as version_restored
- dependency-free LCS line diff (scripts/cms-diff.js) with add/del
highlighting and context trimming; backup names validated against a strict
pattern (path traversal impossible)
- new 🕘 Verziók entry in the CMS bottom bar
- refactor: security/infra helpers extracted to scripts/cms-core.js to keep
content-editor.js under the 400-line hard limit
- integration test: list, diff, restore + reversibility backup, traversal
rejection, CSRF enforcement, auth
Closes MITHOME-64
'Is the fix live?' becomes a single check instead of an SSH session:
- CMS: git short SHA read at startup, shown in the bottom bar (v<sha>),
served by the public GET /version endpoint, recorded in a startup audit
entry
- Website: deploy.sh exports DEPLOY_VERSION (git SHA), Dockerfile bakes it
via build ARG into the runtime env, /api/health reports it as
deployVersion, smoke test asserts a non-'unversioned' stamp
Closes MITHOME-63
Safari-specific deviations fixed:
1. Safari shows its native auth dialog on fetch() calls answered with a
401 + WWW-Authenticate challenge (e.g. save with an expired session).
All CMS 401 responses now omit WWW-Authenticate; browsers use the styled
/login page instead.
2. Safari caches Basic credentials and resends them automatically, which
made logout ineffective (a navigation after logout went straight back
into the editor). Browser navigations (GET + text/html) now authenticate
ONLY via the session cookie; Basic Auth remains valid for non-browser
clients (curl, API).
3. /login and redirects send Cache-Control: no-store so Safari does not
cache the login page or the 302.
Closes MITHOME-62
A Content Editor tab left open across a deploy (or a save from another tab)
held the pre-deploy content; one Save would silently overwrite the newer
file. The editor page now embeds a SHA-256 fingerprint of the file content
at load time, /save requires it back in X-Content-Hash and compares against
the current file: mismatch (or a missing header) answers 409 with an
explanatory message and writes nothing. The client offers a reload on 409.
Integration test covers: matching hash saves, stale hash rejected with the
file untouched, missing hash rejected, retry with the fresh hash succeeds.
Closes MITHOME-61
Review follow-up on MITHOME-59:
- no-op publish no longer triggers a background deploy (deploy moved behind
a deterministic hadChanges flag)
- replace output-regex classification ('Already up to date.' also appears on
real publishes when the remote did not move, which misclassified them as
no_changes) with an explicit __NO_CONTENT_CHANGES__ marker echoed by the
shell skip-branch
- failed git pull --rebase is aborted immediately so the repo is never left
mid-rebase; the error is reported and nothing is pushed or deployed
- command + interpretation extracted to scripts/cms-publish.js
- CONTENT_EDITOR_CONTENT_DIR / CONTENT_EDITOR_DEPLOY_CMD env overrides enable
an integration test against throwaway git repos covering: no-change skip,
real publish + deploy, rebase conflict abort
- .gitignore: drop patterns already covered by .env.*
- user guide: new no-changes message
Closes MITHOME-60
- logout asks for confirmation, then invalidates the server-side session
and navigates to a public /login page (logo, form, error messages)
- POST /login validates credentials (timing-safe) and issues an HttpOnly
SameSite=Strict session cookie (8h, Secure behind HTTPS); Basic Auth
stays valid in parallel for curl/API use
- unauthenticated browser navigations redirect to /login; non-browser
requests keep the 401 challenge
- failed form logins share the auth rate-limit budget with Basic attempts
- save/publish redirect to /login when the session expired
- refactor: templates and browser script extracted to scripts/cms-pages.js
and scripts/cms-editor-client.js, session logic to scripts/cms-session.js
(content-editor.js back under the 400-line limit)
- user guide updated (login page, confirmation, 8h session)
Closes MITHOME-58
- docs/felhasznaloi-utmutato.md: user guide for the website and the CMS
(login, editing, arrays, save/validation, publish, security)
- /guide endpoint renders the markdown auth-protected via a dependency-free
renderer (scripts/markdown-render.js) in the CMS dark theme
- new ❓ Súgó entry in the CMS bottom bar
- steering rule: the guide must be updated in the same commit as any CMS or
website feature change
Closes MITHOME-57
Basic Auth credentials are cached by the browser until it closes, so the
editor had no real logout. Add a /logout endpoint (always answers 401 with
a challenge; deliberately exempt from the auth rate limiter so logging out
never locks the user out) and a Kilépés button that overwrites the cached
credentials with an invalid pair via fetch, then reloads into the login
prompt.
Closes MITHOME-56
reindexItems replaced the FIRST [n] index in a data-path, which for nested
arrays (e.g. services[1].specs.items) rewrote the OUTER array index instead
of the item's own index. Deleting or adding an item scattered paths across
services[0..n], produced sparse arrays (null items) and schema errors like
'$.details.services[1].specs.items[0]: string érték szükséges'.
Rewrite only the index directly following the reindexed array's path prefix;
drop the now-redundant str-item special case.
Closes MITHOME-30
A previous run could merge the '---' separator with the next section
header (---## ✅ Befejezett), breaking section detection. Prefix/suffix
are now trimmed and rejoined with fixed separators. Regression test
added; also create Plane issues for local-only completed work
(MITHOME-28 Custom CMS, MITHOME-29 Webmail) to avoid ID collisions.
Add plane-sync.js one-way (Plane -> TODO.md) synchronization for the
MITHOME project. Config reads from .mcp.json with .env override. CLI:
--list, --dry-run, --yes, --project, --verbose, --help. Interactive
discrepancy resolution (status mismatches, orphans, new issues), backup
before write, rate limiting. Pure functions covered by node:test unit
tests. Includes PLANE-SYNC-GUIDE.md and TODO.md changelog entry.
- Updated README.md with project details, quick start instructions, and tech stack.
- Expanded TODO.md to reflect current project status and backlog items, including Linear ticket synchronization.
- Added mobile menu toggle functionality in Header component with corresponding tests for user interactions.
- Configured Next.js for Docker deployment and optimized build settings.