pdf-quiz-generator/backend/app/services
Daniel 4ca7f6b1f2 feat: Cap replaces hCaptcha, self-hosted beside the app
Proof-of-work rather than a puzzle, and — the reason for it — nothing
about the person signing up is described to a third party in order to let
them in. Turnstile and then hCaptcha were both here; both told Cloudflare
who was at the door.

The `cap` service runs on the compose network with its own Redis
database, kept apart from the app's so a flush of one cannot clear the
other's challenges. The widget talks to /cap/ on this origin, proxied by
the frontend's nginx, so the browser reaches nobody else either. Caddy
passes the whole host through to that container, so it needed no change.

Two things that had to be found rather than read:

Cap's key API is undocumented. The routes are `/auth/login` and
`/server/keys`, and the Bearer value is base64 JSON of `{token, hash}` —
not the session token itself, which is why the obvious call returns
"Malformed session token". The site key and secret were created that way
rather than by hand in a dashboard.

And an nginx proxy_pass whose target is a variable passes the URI through
untouched: the trailing slash that strips a location prefix on a literal
target does nothing. Cap was being asked for /cap/<key>/challenge and
answering NOT_FOUND until the prefix was stripped by an explicit rewrite.

Verified end to end against the running service: a challenge is issued
through the public path, and a token that was never issued is refused
rather than waved through.

Also here: the register modal's Name and Email were bare labels that
neither wrapped their input nor named it, so a screen reader met two
boxes with no names and clicking the word did nothing.

And the knowledge profile paginates ten to a page and expands each row to
its two bars beside the next step. "Correct using hints" is missing from
that bar because /study-tools/recommendations does not carry it per
topic — inferring it from the lifetime figure would be a different set of
answers, so the bar is honestly two-tone until the backend offers it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TqXevQJhxFrM7jJg82cgZN
2026-09-12 06:14:14 +02:00
..
__init__.py Initial commit: PDF Quiz Generator app 2026-03-30 20:04:53 +00:00
ai_mode_service.py feat: the chat can put you into practice, and Settings stops crying wolf 2026-09-12 03:45:51 +02:00
ai_service.py refactor: name the study plans ourselves, and stop reserving 64k tokens a call 2026-09-11 01:41:18 +02:00
article_service.py fix: every cross-reference in the corpus was a dead link 2026-09-11 18:25:31 +02:00
article_writer.py feat: a real site footer, and better retrieval queries 2026-09-11 02:40:57 +02:00
attempt_expiry.py feat: right after a tip is its own slice 2026-09-12 01:51:03 +02:00
captcha.py feat: Cap replaces hCaptcha, self-hosted beside the app 2026-09-12 06:14:14 +02:00
certificate_service.py Add certificate generation, SCORM support, QTI export/import 2026-04-11 01:30:09 +02:00
clinical_library.py feat: short / long / clinical, and stop broad topics retrieving index lines 2026-09-10 18:21:21 +02:00
draft_questions.py fix: the other registration form, our own transcriber first, and a rail 2026-09-12 04:41:27 +02:00
email_service.py feat: cross-link the articles, and strip what the analysis page replaced 2026-09-11 03:44:44 +02:00
embedding_service.py feat: media as a searchable corpus, ready for a vision embedder 2026-09-10 02:22:52 +02:00
exam_blueprint.py feat: study plans built to the board's content outline 2026-09-11 21:40:17 +02:00
extraction_modes.py refactor: name the study plans ourselves, and stop reserving 64k tokens a call 2026-09-11 01:41:18 +02:00
invites.py feat: the player is a box; sharing and sign-up are the administrator's 2026-09-11 19:17:35 +02:00
knowledge_groups.py feat: an organ system is a fact about a topic, not about a keyword 2026-09-12 04:32:25 +02:00
pdf_service.py refactor: name the study plans ourselves, and stop reserving 64k tokens a call 2026-09-11 01:41:18 +02:00
question_figures.py fix: choosing is answering; figures say only what an educator wrote 2026-09-12 00:19:35 +02:00
quiz_builder.py feat: adaptive weighs the paper as well as the learner, and Session 2026-09-12 05:54:09 +02:00
quiz_service.py Junction table for shared questions; verification blocking; semantic bank search; bug fixes 2026-04-01 00:55:13 +02:00
search_service.py feat: media as a searchable corpus, ready for a vision embedder 2026-09-10 02:22:52 +02:00
site_settings.py feat: the tutor is an administrator's to allow, and a handbook explains the rest 2026-09-11 21:13:38 +02:00
storage_service.py feat: serve uploads through storage, and move all 3,852 files to MinIO 2026-09-10 10:23:34 +02:00
study_plan_context.py feat: an exam that runs out is a result; delete and reset go all the way down 2026-09-11 04:47:11 +02:00
vector_service.py fix: gateway-agnostic URL handling for TTS and embeddings, docs cleanup 2026-04-19 02:17:35 +02:00