Repository navigation
Conversation
Media-type lessons (video, audio, PDF and file) can now carry a rich-text description and a set of downloadable resources alongside their media, so creators can provide notes, instructions and supporting files in place. - Add `description` (ProseMirror doc) and `attachments` (Media[]) to the lesson schemas in models/Lesson.ts, orm-models and common-models - Expose both fields on the GraphQL lesson, input and update types, with attachments resolved through getMedia - Seal temporary media in descriptions and attachments on create/update, and clean up attachment and description media when a lesson is deleted - Add a LessonAttachments component and surface Media, Description and Resources sections in the lesson editor - Render the description and resource download links in the lesson viewer for media-type lessons - Expose the new fields through the REST lesson routes and OpenAPI spec - Fix two media-selector buttons that submitted their enclosing form Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Follow-up fixes from a security and code review of the lesson description and resources feature. - Authorise media deletion for lesson attachments, not just lesson media. Removing a resource previously failed with a 403 and left the file orphaned in the media service - Render the description only when it has content, so media lessons no longer show an empty block under the player - Delete the media for attachments dropped by an update, which were previously left behind in the media service - Reject a description that is not a document object. Storing a bare string made every later GraphQL read of that lesson fail, leaving it unopenable in the dashboard and the viewer - Reject attachments without a media id instead of silently discarding them and reporting success - Persist the attachment list before deleting the underlying media, so a failed save can no longer leave a lesson pointing at a deleted file Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Description and resources were offered together on the four media lesson types. Neither fits every type, so they are now gated independently from one shared pair of constants in common-models, and documented. - Offer a description on video, audio, embed, pdf and file lessons, and resources on text, video, audio and embed lessons - Reject a description or resources sent for a lesson type that does not support them, so the REST and GraphQL APIs cannot store fields the editor will not show and the viewer will not render - Add a docs page covering both fields, with a per-type support table, and link it from the add content guide Follow-up fixes from a review of the same feature: - Save the lesson before deleting media dropped by an update, and keep a failed deletion from failing the update. A media id the media service no longer knows about previously made an attachment impossible to remove, since the save never ran - Keep lesson deletion working when cleanup of one media fails, which otherwise left the lesson, its evaluations and its activity undeleted - Require a description to be a ProseMirror document rather than any object, matching what the OpenAPI schema already declares - Treat a null description over REST as clearing it, instead of storing the string "null" and rejecting every later read - Skip resources that have no URL in the viewer, rather than rendering a download button that does nothing - Narrow lesson media deletion back to the lesson's own media now that removing a resource no longer deletes through that route Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
pdmarf
marked this pull request as draft
October 9, 2026 21:21
Follow-up fixes from a code review of the description and resources feature. - Only accept a new resource that this school uploaded and that no lesson already uses, including the lesson's own media. Removing a resource deletes its file, so any media id sent through the API could previously be deleted that way, including another school's - Only delete description and resource media that this school owns - Keep resources already on the lesson as stored rather than sealing them again, so a file the media service has lost no longer blocks every edit to the list, and store a repeated resource once - Treat only embedded media (image sources) in a description as the lesson's own. Links are no longer sealed on save or deleted when removed, so linking to another lesson's file is safe - Delete resource and description media in the domain cleanup script - Reopen a new text or embed lesson for editing after it is created, so resources can be added straight away - Treat a null description on REST create as none, matching update - Say "Resources saved" rather than "Lesson updated" when a resource is added or removed, since other edits are not saved with it - Hide a description that was cleared, while still showing one that holds only an image - Share lesson type support checks and media input shaping as helpers instead of repeating them, and move inline strings into strings.ts Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Reject a lesson's media as one of its resources on create, and media that any lesson already offers as a resource. - Only delete removed resource and description media that no lesson still uses as its media or a resource. - Return a resource the media service has lost as stored, so the lesson still loads and the editor can remove it. - Keep the file url on public resources, which are not refetched. - Reject non-list attachments. - Serialize resource saves in the editor. - Domain cleanup only deletes resource and description media the school owns. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
pdmarf
marked this pull request as ready for review
October 9, 2026 22:09
Member
|
Thanks for the PR. The review will take some time. |
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Lessons can now carry a rich-text description and a set of downloadable resources (attachments) alongside their main content, so creators can provide notes, instructions and supporting files in place.
Each field is enabled per lesson type from one shared pair of constants in
@courselit/common-models:Changes
Data model & API
description(ProseMirror doc) andattachments(Media[]) to the lesson schema (apps/web/models/Lesson.ts,orm-models,common-models)getMediaopenapi.mjsin syncnullmeans no description on create and clears it on updateMedia ownership and lifecycle
group) that no lesson already uses, including the lesson's own media. Removing an attachment deletes its file, so this stops an API caller from deleting arbitrary media by attaching and then removing itsrc) in a description is treated as the lesson's own; links are never sealed or deleted, so linking to another lesson's file is safepackages/scriptsdomain cleanup also deletes attachment and description media the school ownsUI
LessonAttachmentscomponent, and Media / Description / Resources sections in the lesson editorlessonTypeSupportsDescription/lessonTypeSupportsAttachments) and media-source extraction (extractMediaIDsFromNodeSources) live in@courselit/utilsDocs
apps/docsandapps/docs-new, linked from the add-content guideTesting
pnpm test: 98 suites / 856 tests pass, including new cases inapps/web/graphql/lessons/__tests__/logic.test.ts(ownership, reuse, stale media, links), the REST lesson route test and the lesson editor page testpnpm lintandpnpm prettierclean;tsc --noEmitclean forapps/webandpackages/scripts🤖 Generated with Claude Code