Browse the file manager ("Dateien") as a filesystem
Many teachers never use topics or boards; their material sits in the course's file area, and the tools answered "0 files" for courses holding dozens of worksheets — 21 of 26 courses on the live account. Persönliche, Kurs-, Team- and Geteilte Dateien live in the legacy file store, not in files-storage, and its service is not in the public ingress. The only way in is the legacy client: HTML listings, and GET /files/signedurl for a pre-signed download. core/legacy-files.ts turns that into one path tree — /my, /courses/<course>, /teams/<team>, /shared — resolving names that contain "/", ids anywhere in a path, and wrong or ambiguous names with a message saying what is there. A listing that does not parse throws; it never reads as an empty folder. Some of the legacy client's GET routes write (GET /files/share/ mints a share token), so getFileManagerPage allows only the listing routes, by pattern. Signed URLs are fetched with no credentials and must be https. - MCP: fs_list, fs_tree, fs_find and fs_read; get_course lists course files. - CLI: schulcloud fs ls, tree, find and get, recursive and resumable. - API: /api/fs/list, tree, find and file. - Index: the crawl walks the file manager (INDEX_FILE_MANAGER, on by default), so search covers the text inside those files and sync mirrors them under <course>/Kurs-Dateien. The local instance gains a fixture for all four areas. It needed a loopback, so signed URLs open from the host, and a pre-created bucket, since MinIO does not implement PutBucketCors. 135 tests. Smoke 55/55 live; 57/57 and 55/55 on the local instance. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -89,6 +89,7 @@ Every one of these is marked in the env files at the line it affects.
|
||||
|
||||
```
|
||||
docker-compose.yml the stack; profiles: (default) | tools | av | preview
|
||||
(minio-loopback: see "Simulating a teacher")
|
||||
env/ one file per service, all values local-only
|
||||
proxy/nginx.conf GENERATED — the single origin, see scripts/gen-proxy-conf.py
|
||||
scripts/seed.sh loads the demo school via the management app
|
||||
@@ -183,14 +184,40 @@ node scripts/simulate-teacher.mjs create # course, room, topic, task, board,
|
||||
# Etherpad pad, folder, files
|
||||
node scripts/simulate-teacher.mjs update # rename and rewrite all of it
|
||||
node scripts/simulate-teacher.mjs delete # remove it again
|
||||
node scripts/simulate-teacher.mjs files # only the file-manager part, onto an existing fixture
|
||||
```
|
||||
|
||||
It also fills the **file manager** ("Dateien") — the legacy file system behind
|
||||
Persönliche, Kurs-, Team- and Geteilte Dateien, a separate store from the board
|
||||
files above — with a course folder tree ("Arbeitsblätter/Woche 1"), a team
|
||||
folder, a folder in the demo student's own files, and a teacher's file shared
|
||||
read-only with the student. The student is added to the adopted team so the
|
||||
team files are visible from the account under test. Every file carries a unique
|
||||
search term, so search over the index can be checked per area.
|
||||
|
||||
Legacy file uploads only work because of two changes to the stack:
|
||||
|
||||
- **`minio-loopback`.** The legacy service signs upload and download URLs for
|
||||
its storage provider's single endpoint, and uses that endpoint for its own S3
|
||||
calls. `minio:9000` works inside the compose network and nowhere else, so the
|
||||
browser and the MCP server on the host were handed URLs they could not open.
|
||||
`seed.sh` now registers `http://localhost:9900`, and this socat sidecar,
|
||||
sharing the api container's network namespace, forwards that address to
|
||||
MinIO — the same url then works from the api, the browser and the host.
|
||||
After recreating `api`, recreate `minio-loopback` too: it lives in api's
|
||||
network namespace.
|
||||
- **The school bucket is created up front.** The legacy service makes
|
||||
`bucket-<schoolId>` on first upload and then calls `PutBucketCors`, which
|
||||
MinIO does not implement, so the first upload failed with *"A header you
|
||||
provided implies functionality that is not implemented"*. `minio-init.sh`
|
||||
creates the demo school's bucket, and an existing bucket skips both calls.
|
||||
|
||||
Ids are kept in `.simulate-teacher.json` between phases, so the MCP server can
|
||||
be pointed at the instance in between:
|
||||
|
||||
```bash
|
||||
eval "$(./scripts/mcp-env.sh)" # as the demo student
|
||||
cd .. && npm run smoke # 39 checks against the local instance
|
||||
cd .. && npm run smoke # 57 checks against the local instance
|
||||
```
|
||||
|
||||
`mcp-env.sh` points the index at its own database, `schulcloud_local`, and the
|
||||
|
||||
Reference in New Issue
Block a user