docs: imported courses in the studio (media kept out of git, moved to <data>/media/<id>)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
4a49e28fcc
commit
f834088588
@@ -22,7 +22,7 @@ Layout after the review (controller ruling on I4):
|
||||
|
||||
Proposal: main.ts calls `createCourseRepository(studioCourseDirs(config))`, or `loadConfig` appends both dirs.
|
||||
|
||||
Imported courses (`source.kind` other than `authored`, with their root under `<dataDir>/courses`) are read-only in the studio for v1, with the message "Imported course: read-only in the studio for now". There is a `TODO(studio)` in `feature.ts` (`readOnlyMessage`) for converting one into a studio-managed repo. The studio refuses to create a course whose id already exists as `<dataDir>/courses/<id>`. WP-B should likewise refuse an import id that `<dataDir>/studio/repos/<id>.git` already uses.
|
||||
Imported courses (`source.kind` other than `authored`, with their root under `<dataDir>/courses`) are read-only in the studio for v1, with the message "Imported course: read-only in the studio for now". There was a `TODO(studio)` in `feature.ts` (`readOnlyMessage`) for converting one into a studio-managed repo; v1.1 S resolved it with the owner-only "Make editable in the studio" action (see `docs/operations.md`, "Imported courses in the studio"). The studio refuses to create a course whose id already exists as `<dataDir>/courses/<id>`. WP-B should likewise refuse an import id that `<dataDir>/studio/repos/<id>.git` already uses.
|
||||
|
||||
## 3. Live preview: swap in the learner `LessonView`
|
||||
|
||||
|
||||
+43
-1
@@ -22,7 +22,7 @@ Mounts (all course mounts are read-only; STCourses never changes your originals)
|
||||
|
||||
| Host | Container | What |
|
||||
|---|---|---|
|
||||
| volume `stc-data` | `/data` | database (SQLite), imported courses (`/data/courses`), studio repos (`/data/studio/repos`) and their published worktrees (`/data/worktrees`), caches (`/data/cache`), AI drafts for non-studio courses (`/data/drafts`) |
|
||||
| volume `stc-data` | `/data` | database (SQLite), imported courses (`/data/courses`), studio repos (`/data/studio/repos`) and their published worktrees (`/data/worktrees`), media of imports made editable in the studio (`/data/media`), caches (`/data/cache`), AI drafts for non-studio courses (`/data/drafts`) |
|
||||
| `STC_COURSES_HOST` (default `./courses`) | `/courses` | course repos, one folder with a `stcourse.yaml` per course |
|
||||
| `STC_IMPORT_HOST` (default `./import-inbox`) | `/import` | files the owner may import (Packt zips, video folders, PDFs, code zips) |
|
||||
|
||||
@@ -78,6 +78,48 @@ Without Docker: `bun install`, then `STC_SECRET=… NODE_ENV=production bun apps
|
||||
- Course media (`/api/media/…`) is sent with `X-Content-Type-Options: nosniff`, and SVG, HTML and JavaScript files
|
||||
with `Content-Security-Policy: sandbox`, so author content cannot run script on the app's origin.
|
||||
|
||||
## Imported courses in the studio
|
||||
|
||||
Imported courses (`<data>/courses/<id>`) are read-only in the studio until the owner chooses **Make editable in the
|
||||
studio** on the course's studio page. That runs a background job (`studio.convert-import`) which:
|
||||
|
||||
1. commits the manifest, lessons, transcripts, small images, small PDFs and code into a new studio repo,
|
||||
`<data>/studio/repos/<id>.git`, checked out as `<data>/worktrees/<id>/published`. The manifest is committed
|
||||
unchanged: the course keeps its id, its visibility (restricted after an import) and its `source.kind`, so only the
|
||||
owner can later make it public or signed-in. ACL entries, progress and notes are keyed by course and lesson id
|
||||
and are kept;
|
||||
2. keeps **large media out of git**: every video or audio file (`mp4 m4v webm mov mkv avi mp3 m4a wav ogg oga opus
|
||||
flac`) and any other file over 8 MiB. Each is listed with its path, size and SHA-256 in `.stcourses/media.json`
|
||||
in the repo and excluded by one anchored line in the repo's root `.gitignore` (a `.gitignore` the course already
|
||||
had is kept above that block). Git LFS is not used: it is not in the image and would need an LFS server;
|
||||
3. **moves the import folder to `<data>/media/<id>`** (decided over keeping it in place: a manifest left in
|
||||
`<data>/courses/<id>` would be found as a second course with the same id, and the importer's "add to course" and
|
||||
transcription jobs would keep changing a folder learners no longer see). `<data>/media` is never scanned for
|
||||
courses and nothing writes to it afterwards. File permissions are left alone, because an import made in `link`
|
||||
mode shares its files with your originals;
|
||||
4. puts every referenced file into the published worktree, and later into each author's draft worktree, as a
|
||||
**hard link** to `<data>/media/<id>/<path>` (a copy if they are on different filesystems). The manifest paths are
|
||||
unchanged, so `/api/media/<id>/…` serves the same URLs with Range support, and no extra disk space is used.
|
||||
|
||||
In the studio, referenced media cannot be replaced or deleted (save, upload and conflict resolution refuse them with
|
||||
409); upload a new file under another name instead. A studio write never goes through a hard link: a file with more
|
||||
than one link is replaced, not overwritten.
|
||||
|
||||
Interrupted conversions are safe to retry, and a job left running by a restart is retried automatically: if no
|
||||
published worktree was made, the partial repo is removed and the conversion starts again; if the repo exists but the
|
||||
import folder was not moved yet, only the move is done.
|
||||
|
||||
Things to know:
|
||||
|
||||
- Back up `<data>/media` together with `<data>/studio`: the repo alone does not contain the videos.
|
||||
- **Sync** pushes only the git repo. A clone of the remote has the manifest, lessons and `.stcourses/media.json`, but
|
||||
not the media files themselves; copy `<data>/media/<id>` alongside if you move the course to another server.
|
||||
- If a worktree loses a media file (for example after a manual `git clean -x`), the next publish links it back into
|
||||
the published worktree, and a new draft worktree gets every file. A file missing from `<data>/media/<id>` shows
|
||||
as a course diagnostic ("lesson file not found"), never a crash.
|
||||
- After conversion the importer can no longer add decks, code or transcripts to that course (it only edits
|
||||
`<data>/courses`). Add them in the studio.
|
||||
|
||||
## Memory budget
|
||||
|
||||
The deployment must stay under 2 GB idle. Measured with this compose file (`--profile postgres`), the image built
|
||||
|
||||
Reference in New Issue
Block a user