Commit Graph

3 Commits

Author SHA1 Message Date
MechaCat02
3a168e37e5 local-instance: simulate a teacher, and run the files-storage consumer
`scripts/simulate-teacher.mjs` creates, edits and deletes what teachers
create — course, room, topic, task, board, columns, cards, rich text,
link, Etherpad pad, folder, files — so the MCP server can be exercised
against content the real account has never held. It is the only thing
here that writes to a Schulcloud and refuses any non-localhost address.
`scripts/mcp-env.sh` points the server and CLI at the instance.

Two gaps it exposed in the stack itself:

The files-storage AMQP consumer is a separate entrypoint, and we were
running only the HTTP one. Nothing was bound to the `files-storage`
exchange, so `TaskService.delete` — which awaits deleteFilesOfParent
over AMQP before touching the task — hung until the request timeout.
Deleting any task or topic answered 408 with the entity still there.

The demo data is dated 2017-2018 and the v3 endpoints filter on those
dates, so a student saw no tasks at all. seed.sh now brings courses and
homework into the present, which is the difference between a fixture
that exercises the student-facing surface and one that looks empty.

Teams turn out to be uncreatable through the API (the legacy service
registers no `create`, v3 has no route), and a new board is unpublished
and 403s for students, so the simulation adopts a seeded team and leaves
one board a draft on purpose.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-13 15:40:46 +02:00
MechaCat02
0ae9198428 Run Etherpad by default: without it every topic page 500s
The legacy client requests an Etherpad session on every topic page whose
lesson has contents, without ever checking whether the lesson contains a
pad — controllers/topics.js collects `etherpadPads` and then ignores it.
With Etherpad unreachable the request fails, `validUntil` comes back
undefined, and `new Date(undefined * 1000)` makes Express reject the
session cookie: "option expires is invalid", rendered as a 500.

So Etherpad was only nominally optional. Moving it out of the `tools`
profile also matches the live deployment, which always runs it.

The topic pages that still 500 are courses the signed-in user is not a
member of; the same page returns 200 for its own teacher. That is the
legacy client rendering a 403 as a 500, upstream behaviour we don't own.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-13 14:34:12 +02:00
MechaCat02
a3aded110c Add a local Schulcloud instance modelled on the live target
A Docker Compose stack that runs the deployed images
(quay.io/schulcloudverbund/*, thr theme, tag 33.40 — the versions
schulcloud-thueringen.de reports) rather than a rebuild of main, so what we
develop against is the deployed artefact. It exists to produce the states we
can otherwise only observe read-only: log in as the teacher, grade, then read
it back the way the MCP server does.

Faithful where it matters and honest where it isn't:

- Feature flags in env/api.env are a replay of GET /api/v3/config/public from
  the live instance, not a hand-picked set; instance identity mirrors the thr
  group_vars from dof_app_deploy.
- The proxy is generated from the deployment's own ingress table
  (scripts/gen-proxy-conf.py) so the legacy-client / SPA / API path split
  matches production; getting it wrong tests a different application.
- Valkey runs in `single` mode so the JWT whitelist expires sessions the way
  production does, rather than the in-memory shortcut that hides it.
- No external OAuth / Schulportal login (excluded by request and not
  reproducible locally), no BigBlueButton; each divergence is marked at the
  line it affects. Everything binds to 127.0.0.1 and uses the upstream dev
  credentials, which are public.

Profiles keep the heavy pieces opt-in: `tools` adds Etherpad/H5P/tldraw/
Collabora, `av` adds ClamAV, `preview` adds thumbnailing.

seed.sh loads the upstream demo school (the same call the deployment's init job
makes) and registers MinIO as the legacy storage provider, which has no seed
data on purpose. The demo data already contains the grading states that are
hard to obtain from the real account — a feedback-only grade and a 100% one —
which is what surfaced the past-due submitted-text scrape gap.

One config finding baked in: file-storage and h5p validate a token's
issuer/audience against JWT_DOMAIN (default "localhost"), while the API stamps
SC_DOMAIN; without keeping them equal, the homework page's file lookups 401.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-13 14:17:30 +02:00