mirror of
https://github.com/Patiun/com.gregkilmer.www.git
synced 2026-11-07 19:58:24 +00:00
Phase 6: sync jobs, inquiry and order forms
- Sync scheduler in the content service with a per-job cache (sync_cache), sync_runs reporting and Healthchecks pings. Jobs: Archidekt, BoardGameGeek, cover art (Open Library, TMDB, AniList), Suwayomi and Kavita reading progress, Bambuddy filament and Homebox material stock. A note's own value always wins over a synced one. - PersonalReader: updates only Library notes with a matching anilistId, changes to Greg's notes are held for review; Reading series with no note show as suggestions on the admin Overview. - /api/inquiries and /api/orders: Turnstile, rate limits, a saved copy in the content service (intake token) plus email over SMTP. Orders are re-checked against the live notes: accepted only when the category's ordersOpen is true and the line has capacity. - Admin Inbox for saved submissions. Options' inStock is optional so synced stock can fill it. All settings from process.env; .env.example per service. - Sources could not be reached from the build environment; parsers are tested against fixtures labelled as not live. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012aSbo2D1qP8cMFQCykf7kx
This commit is contained in:
+38
-2
@@ -54,7 +54,7 @@ gives "Oct 11, 2026".
|
||||
| `/tabletop` | Systems, campaigns, card games and decks as anchors |
|
||||
| `/now` | Every status; each is an anchor |
|
||||
| `/blog`, `/blog/topic/<topic>`, `/blog/<slug>`, `/rss.xml` | Blog, topics, posts, feed |
|
||||
| `/store`, `/store/<line>`, `/store/order` | Store, product lines, order form |
|
||||
| `/store`, `/store/<line>`, `/store/order`, `/store/order/sent` | Store, product lines, order form and its confirmation |
|
||||
| anything else | 404, Off the star chart |
|
||||
|
||||
## Rules for pages
|
||||
@@ -111,4 +111,40 @@ The highlights row and Selected projects come from the `/resume` Page text note'
|
||||
| Blog, topics, posts, RSS | `pages/blog/`, `rss.xml.ts` | `lib/blog.ts`, `components/blog` |
|
||||
| Store, lines, order | `pages/store/` | `lib/store.ts`, `components/store` |
|
||||
|
||||
Forms post to `/api/inquiries` and `/api/orders`, which arrive in phase 6.
|
||||
## Forms
|
||||
|
||||
The Inquiries form posts to `POST /api/inquiries` and the order form to
|
||||
`POST /api/orders` (`src/pages/api/`). Both are plain multipart posts that
|
||||
answer with a 303 redirect, so they work without JavaScript. The rules are
|
||||
plain functions in `src/lib/forms.ts` (tested in `services/site/test`); the
|
||||
network calls are in `src/lib/forms-server.ts`.
|
||||
|
||||
1. A filled honeypot field (`leave_blank`) gets the confirmation page and nothing else.
|
||||
2. The answers are checked. Inquiries take only the chosen type's `job_*`,
|
||||
`freelance_*` or `patiun_*` fields; others are ignored.
|
||||
3. A per-address rate limit, then Cloudflare Turnstile when
|
||||
`TURNSTILE_SECRET_KEY` is set (a missing or bad token, or Cloudflare being
|
||||
unreachable, turns the post down).
|
||||
4. Orders only: the line is checked against the live store notes, fetched
|
||||
from the content service then and there. An order goes through only for a
|
||||
line sold here, in a category with `ordersOpen: true`, with an open slot
|
||||
(made to order) or stock, and with in-stock options. Otherwise it is
|
||||
turned down, whatever was posted.
|
||||
5. The copy is saved through the content service with `CONTENT_INTAKE_TOKEN`,
|
||||
then emailed (SMTP via nodemailer) to `CONTACT_TO` or `ORDER_TO`, and the
|
||||
email's result is recorded on the copy. A failed email still shows the
|
||||
visitor the confirmation; the admin Inbox flags it. If saving fails the
|
||||
email is still tried; only when neither works does the form show an error.
|
||||
6. Success redirects to `/inquiries/sent` (cookie `gk-inquiry`) or
|
||||
`/store/order/sent` (cookie `gk-order`): URL-encoded JSON, httpOnly,
|
||||
SameSite Lax, ten minutes, secure in production. A turned-down post goes
|
||||
back to the form with `?error=<code>` and a `gk-inquiry-draft` or
|
||||
`gk-order-draft` cookie holding the reason, the marked fields and the
|
||||
answers (an attachment has to be added again).
|
||||
|
||||
Settings are in `services/site/.env.example`: `CONTENT_INTAKE_TOKEN`,
|
||||
`PUBLIC_TURNSTILE_SITE_KEY`, `TURNSTILE_SECRET_KEY`, `SMTP_HOST`, `SMTP_PORT`,
|
||||
`SMTP_USER`, `SMTP_PASS`, `SMTP_FROM`, `SMTP_ALLOW_SELFSIGNED`, `CONTACT_TO`,
|
||||
`ORDER_TO`, `CONTACT_RATE_LIMIT`, `CONTACT_RATE_WINDOW_MIN`, `ORDER_RATE_LIMIT`,
|
||||
`ORDER_RATE_WINDOW_MIN`, `FORM_MAX_ATTACHMENT_MB`. The Turnstile widget needs
|
||||
JavaScript; with the secret set, a visitor without it cannot send a form.
|
||||
|
||||
Reference in New Issue
Block a user