fix(ai): lessonUrl builder in one place; authoring follows core canView

- Learner links go through lessonUrl/courseUrl (src/web/urls.ts).
- Generation, the studio course list and file listing require an authoring
  right and core canView, so a global author needs a grant for a restricted
  course. Contract requests 8 (global author in canView) and 9
  (STC_AI_ALLOWED_BASE_URLS in AppConfig).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
Lucas Winther
2026-10-30 04:49:48 +01:00
co-authored by Claude Opus 5.5
parent ead7327bb1
commit f3ce2e34bf
7 changed files with 87 additions and 9 deletions
+22 -2
View File
@@ -57,5 +57,25 @@ Proposal: export these paths from `@stc/core` (e.g. `cachePaths(dataDir)`) so bo
## 7. Learner lesson URL
Citations carry `lessonId`; the assistant links them to `/c/<courseId>/<lessonId>` (a guess at WP-A's route). Proposal: a `lessonUrl(courseId, lessonId)`
helper in `@stc/contracts/web` so WP-A owns the route shape.
Citations carry `lessonId`; the assistant links them to `/c/<courseId>/<lessonId>`. Every such link goes through
`lessonUrl(courseId, lessonId)` / `courseUrl(courseId)` in `packages/feature-ai/src/web/urls.ts` (also exported from
`@stc/feature-ai/web`), so a different WP-A route is a one-line change. Proposal: move that helper into
`@stc/contracts/web` so WP-A owns the route shape.
## 8. A global `author` role in core's `canView`
Generation and the studio file listing now require an authoring right (owner, the `author` role or a `course_authors`
row) and core's `canView(user, course, acl, isAuthor)`. `canView` only knows per-course authors (`isAuthor`), so a user
with the global `author` role cannot reach a `restricted` course without an ACL grant or a `course_authors` row.
Spec section 6 allows author rights "on all courses or per course", and the studio branch (`feature-studio`
`server/feature.ts:82-85`) currently grants the global role on every course. Proposal: core exports one
`canAuthor(user, course, acl, isAuthor)` (or `canView` treats `roles.includes("author")` like `isAuthor`), and both
the studio and the AI package call it. Until then the AI package follows core's `canView` (the stricter rule).
## 9. `STC_AI_ALLOWED_BASE_URLS` in `AppConfig`
Learners' own keys (BYOK) go only to `https://api.anthropic.com`, `https://api.openai.com/v1` or an origin on the
allowlist (SSRF guard). The allowlist is `STC_AI_ALLOWED_BASE_URLS` (comma-separated exact origins) plus the admin
setting `allowedBaseUrls` in the server AI config. `AppConfig` is frozen, so the package reads the variable from
`process.env` (injectable through `aiFeature(ctx, { env })`). Proposal: add `aiAllowedBaseUrls: string[]` to
`AppConfig` and `loadConfig`.