Repository navigation
Feat/studio activities - #1013
Conversation
Conductor owns everything around an H5P activity: title, authorship, collaborators, visibility, subjects, tags and licensing. h5p-service owns the content and addresses it by the integer content id Studio assigned. - Activity and ActivityCollection models. - Commons read API: /commons/activities, /commons/activities/:activityID, /commons/activities/h5p/:h5pContentID, /commons/activities/author/:uuid, and /commons/activities-adapt-feed, the CSV that replaces Studio's /api/v2/nodes for ADAPT. - Commons activity page at /activity/:activityID, embedding from CLIENT__H5P_EMBED_ORIGIN. - Ignores studio_main/ (a copy of the legacy Studio install, which holds credentials) and migration-reports/.
…orters Reads a Studio MySQL database read-only. Each importer is a dry run unless given --commit, upserts by natural key so a re-run resumes, and pseudonymises identities unless given --with-identities. Pseudonymised records are stamped dataClass "synthetic". - StudioImportActivities, StudioImportCollections and StudioImportLegacyPaths (with the StudioLegacyNode model). - StudioSurvey and StudioArchiveExport. - SQL fixtures with synthetic data for running against ephemeral databases. - mysql2 3.24.6 as a devDependency. All three importers ran on staging with the full corpus on 2026-10-26.
GET /commons/studio-legacy/:kind(h5p|node|collection)/:value answers with a redirect to the activity's Commons page, using the paths and aliases the legacy path importer recorded. Cloudflare sends old Studio page URLs here after cutover. LibreTexts instance only.
- My H5P at /h5p: the signed-in user's owned and collaborating activities, with search and filters (GET /activities/mine). - Create and edit screen at /h5p/new and /h5p/:activityID/edit. It frames h5p-service's editor page and saves metadata alongside content. - Authoring API: POST /activities, PATCH and DELETE /activities/:activityID, editor tokens, subjects, and a cleanup job for activities whose first save never happened (PUT /activities/automation/cleanup, LibreAPI key). - Conductor checks ownership and signs Ed25519 capability tokens for h5p-service (H5P_CAPABILITY_PRIVATE_KEY, H5P_SERVICE_URL). scripts/generate-h5p-capability-keys.mjs creates the key pair. Hidden unless CLIENT__H5P_AUTHORING_ENABLED is "true". The API answers 503 (err93) until H5P_SERVICE_URL and the private key are set.
Replacements for the five Studio API paths ADAPT calls, in Studio's exact
shapes: bare arrays, string values, Drupal's escaping and date formats.
/api/h5p/all?changed=, /api/h5p/{id} -> /studio/h5p/:idOrAll
/api/author/{email} -> /studio/author/:email (Basic auth)
/api/collections/list, /{cid} -> /studio/collections/...
The studio.libretexts.org Worker forwards each path here once it passes
scripts/diff-studio-compat.mjs against responses captured from Studio.
All five diff clean on staging.
These and /commons/activities-adapt-feed answer 404 outside
APP_ENV=production unless ADAPT_FEEDS_OUTSIDE_PRODUCTION=true. In
production they serve only dataClass "production" records.
/studio/author needs STUDIO_COMPAT_BASIC_USER and
STUDIO_COMPAT_BASIC_PASSWORD, and refuses everyone until both are set.
There was a problem hiding this comment.
Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota.
An organization owner can choose the repository organization as the billing source for reviews requested by members with a paid Copilot license. Learn about Copilot code review billing.
|
Severity: High — activity.ts:151: Migrated authors without Conductor accounts never regain ownership. The importer leaves Severity: Medium — Edit.tsx:397: After a metadata-save failure, subsequent Save clicks retry metadata and return without saving the editor. If the author makes further content changes before retrying, the UI reports “Activity saved” while those changes remain unsaved. Check editor/display-option changes before taking this shortcut. Severity: Low — activities.ts:251: The anonymous H5P-ID lookup returns the entire activity document, including collaborators and internal identity/migration fields that the regular activity endpoint explicitly excludes. Use an allowlisted projection, ideally returning only the identifier needed for resolution. |
| * trailing slash, lowercase. The import and the resolver must agree on this. | ||
| */ | ||
| export function normalizeStudioAlias(alias: string): string { | ||
| const trimmed = alias.trim().replace(/\/+$/, ""); |
The link showed on every deployment, so merging to master would have shown production users an empty My H5P before the Studio import. It now follows CLIENT__H5P_AUTHORING_ENABLED, like New H5P and Edit. Also polishes My H5P and the editor screen: - New H5P comes after Upload, in the primary colour. - Save and Delete get icons, and Delete is centred. - The delete dialog's warning moves out of Dialog.Description into the body. - Clearer helper text for the description and the metadata fields.
POST /activities and the new-activity editor token answer 403 (err94) unless the user's verifiedInstructor is true. Owners and collaborators still edit without verification. My H5P hides New H5P and Upload for everyone else, and /h5p/new explains the rule and links to the verification request.
Replaces Studio's adapt cron. PUT /activities/automation/adapt-ids calls ADAPT's PATCH get-adapt-question-id-by-h5p-id for up to 100 production activities with no adaptID (500 with ?limit=), least recently checked first, and records each attempt in adaptIDCheckedAt. Studio always took the 50 newest nodes, so activities ADAPT never imported blocked the rest. The Bearer token is STUDIO_COMPAT_BASIC_PASSWORD, which is ADAPT's H5P_API_PASSWORD. ADAPT_ID_SYNC_EMAIL picks between several ADAPT questions on one content id, as Studio's fixed address did. A found id moves updatedAt, as Studio's node save moved changed, so ADAPT's ?changed= poll sees it. Outside production the route answers 404 and the service refuses to contact ADAPT.
There was a problem hiding this comment.
Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota.
An organization owner can choose the repository organization as the billing source for reviews requested by members with a paid Copilot license. Learn about Copilot code review billing.
There was a problem hiding this comment.
Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota.
An organization owner can choose the repository organization as the billing source for reviews requested by members with a paid Copilot license. Learn about Copilot code review billing.
| { | ||
| orgID: process.env.ORG_ID, | ||
| // The validator limits machine names to letters, digits, "_", "." and "-". | ||
| library: { $regex: `^${machineName.replace(/[.-]/g, "\\$&")} ` }, |
There was a problem hiding this comment.
Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota.
An organization owner can choose the repository organization as the billing source for reviews requested by members with a paid Copilot license. Learn about Copilot code review billing.
No description provided.