Jul 28 - Update schedule page's referred time
This commit is contained in:
@@ -223,8 +223,26 @@ shows only `is_published` topics (sections with no published topics are hidden).
|
||||
the posted time must be in that list, the date must parse, be today-or-later,
|
||||
within `DEMO_MAX_DAYS_AHEAD`, and be a weekday. Dates/times are stored exactly
|
||||
as picked; `DEMO_TIMEZONE_LABEL` is display-only (no tz conversion anywhere).
|
||||
- **One appointment per slot:** a request for a (date, time) that already has a
|
||||
non-`cancelled` row is rejected with a flash. Cancelling frees the slot.
|
||||
- **One appointment per slot**, enforced in three places that share the same
|
||||
helpers (`taken_slots`, `available_slots`, `demo_day_status` in `app.py`):
|
||||
1. `GET /demo/slots?date=YYYY-MM-DD` → JSON `{open, reason, slots[]}`;
|
||||
`static/js/demo-booking.js` rebuilds the time `<select>` on every date
|
||||
change, so a booked time is never offered. Closed days (weekend / past /
|
||||
beyond `DEMO_MAX_DAYS_AHEAD`) come back `open:false` with the reason, and
|
||||
the picker is emptied + disabled. Stale responses are dropped by a request
|
||||
counter; a fetch failure fails OPEN (keeps the list, server still decides).
|
||||
2. The server renders only free slots when the form already has a date (so a
|
||||
re-render after a validation error can't offer a slot taken meanwhile), and
|
||||
the whole grid when it doesn't — the no-JS path still works.
|
||||
3. `POST /demo` remains the authority: a (date, time) with a non-`cancelled`
|
||||
row is rejected with "that time was just taken". Cancelling frees the slot.
|
||||
- The endpoint is public and read-only: it reveals which times are free —
|
||||
exactly what the form shows anyway — and nothing about who booked them.
|
||||
- Two people submitting the same free slot in the same instant can still both
|
||||
pass the check-then-insert; the second one is simply a duplicate row for the
|
||||
owner to sort out. Closing that needs a DB-level constraint (a generated
|
||||
`slot_key` NULL-ed for cancelled rows + UNIQUE index), which we have not
|
||||
added.
|
||||
- **Abuse control:** hidden `website` honeypot (filled → fake success, nothing
|
||||
stored) + per-IP hourly cap `DEMO_RATE_LIMIT` held in `_demo_hits` (in-memory,
|
||||
therefore per gunicorn worker — it's a nuisance filter, not a DDoS defence;
|
||||
|
||||
Reference in New Issue
Block a user