sebastian
a commité dans master sur sebastian/PackIt
7e2c7d34b6 Persist checklists offline, protect admin writes, harden config and links
Four fixes, all exercised against a throwaway copy of the production
database.
Checklist state and offline support
Ticks were DOM-only, so a reload or an evicted tab lost the lot -- on
the one screen people use over hours. They are now stored per list
hash in localStorage and restored on load, with ids for items no
longer on the list pruned. Every storage access is guarded, so a
private window degrades to not remembering rather than breaking. A
Reset button appears once something is ticked.
sw.js caches the shell and every list already opened, which is what
makes an installed PackIt useful where there is no signal.
Navigations are network-first so an online visit still reflects the
latest admin changes; redirected responses are not cached, as a
replayed redirect breaks navigation. The admin panel is never cached.
CSRF, session and login throttling
Every admin POST acted on $_POST['action'] with no token check: any
page an admin visited while logged in could delete the item
catalogue. All 16 POST forms now carry a per-session token and all 6
handlers verify it, login.php included. The session cookie is
HttpOnly, SameSite=Strict, scoped to /admin/ and Secure over HTTPS.
The old sleep(1) is replaced by a logged attempt table: 10 failures
from one IP in 15 minutes locks that IP out. Failures against a
username buy a growing delay instead of a lockout, because locking
the username would let anyone shut the real admin out of the panel.
Error exposure and credentials
display_errors was forced on and db.php printed the raw PDO message,
so a dropped connection showed visitors the host, database and user.
db.php is now the bootstrap: $appEnv drives error display, with
fail_hard() and an exception handler logging the cause and showing a
generic message. Anything but the exact string 'development' is
production, so a typo fails closed.
config.php is untracked and git-ignored, with config.sample.php as
the template. The password in it is still in git history and needs
rotating separately.
Collision-safe short links
A 5-character SHA-256 prefix collides in the low thousands of lists,
and the UNIQUE column turned that into an uncaught PDOException
instead of a list. allocate_selection_hash() walks the prefix from 5
to 16 characters, reusing the length already ours and stepping up
only on a real collision; the insert race is caught and re-checked.
The column drops from CHAR(64) to VARCHAR(16), left over from the old
long hashes, and activity_ids gets the index every list creation
needs.
Existing databases need migrations/001_hardening.sql; it deletes rows
still holding a 64-character hash, which packing.php has rejected since
links were shortened.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
8361662f70 Group activities by category in the mappings selector
Long activity names overflowed the fixed-width selector box, pushing the
floated category label onto a second line where the selection border cut
through it. List each category once as a heading and its activities
below, and let the links wrap inside their own border.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
9f70eb8799 Keep admin sidebar fixed to the window height
The sidebar stretched with the document, so on long pages the logout
button sat at the bottom of the content and was only reachable after
scrolling. Make it a sticky, viewport-tall column that scrolls
internally if it ever gets taller than the window.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
3e3e354276 Warn about similar items when adding or renaming an item
The items admin page had no way to tell whether a name already existed,
which made near-duplicates easy to create — especially with German
compounds ("Skisocken" vs "Ski Socken") and typos.
Both the add form and the inline rename now show an advisory panel
listing existing items that resemble what is being typed. Matching folds
case, diacritics and punctuation, compares word sets in any order, looks
inside compounds, and falls back to a bounded Levenshtein distance so
typos still match. An exact match is highlighted, but nothing is ever
blocked: the panel only informs, and both writes are unchanged.
The item list is already fetched to render the table, so it is reused as
embedded JSON — no extra query and no AJAX. In the table the panel
floats below the field so rows keep their height while editing, and a
row never flags itself or warns until the name actually differs.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
f97f2e4917 Fix tick boxes printing as solid black rectangles
The print stylesheet lightened the body, rows, titles and pills, but never
.item-check, which carries the dark page background. Browsers that print
background graphics (Safari, and any print dialog with the option ticked,
which is common when saving as PDF) therefore filled every box solid black,
leaving nothing to cross by hand. Chrome has the option off by default,
which is why this only showed up for some people.
The boxes are now explicitly blank on paper, checked rows drop their green
fill and keep readable text, and the checkmark uses currentColor so it is
white on screen and black in print.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- Comparer ces 6 commits »
il y a 3 semaines