@headless-lms/server 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/_virtual/_rolldown/runtime.js +5 -0
- package/dist/app/community-mail.d.ts +26 -0
- package/dist/app/community-mail.js +96 -0
- package/dist/app/community-mail.js.map +1 -0
- package/dist/app/container.d.ts +22 -14
- package/dist/app/container.js +186 -62
- package/dist/app/container.js.map +1 -1
- package/dist/app/mcp-endpoints.d.ts +13 -0
- package/dist/app/mcp-endpoints.js +25 -0
- package/dist/app/mcp-endpoints.js.map +1 -0
- package/dist/http/config.d.ts +2 -1
- package/dist/http/errors.d.ts +14 -0
- package/dist/http/errors.js +147 -0
- package/dist/http/errors.js.map +1 -0
- package/dist/http/mcp/catalogue.d.ts +37 -0
- package/dist/http/mcp/catalogue.js +69 -0
- package/dist/http/mcp/catalogue.js.map +1 -0
- package/dist/http/mcp/challenge.d.ts +7 -0
- package/dist/http/mcp/challenge.js +35 -0
- package/dist/http/mcp/challenge.js.map +1 -0
- package/dist/http/mcp/input-schema.d.ts +7 -0
- package/dist/http/mcp/input-schema.js +28 -0
- package/dist/http/mcp/input-schema.js.map +1 -0
- package/dist/http/mcp/route.d.ts +8 -0
- package/dist/http/mcp/route.js +75 -0
- package/dist/http/mcp/route.js.map +1 -0
- package/dist/http/mcp/server.d.ts +8 -0
- package/dist/http/mcp/server.js +31 -0
- package/dist/http/mcp/server.js.map +1 -0
- package/dist/http/mcp/test-support.d.ts +55 -0
- package/dist/http/mcp/test-support.js +151 -0
- package/dist/http/mcp/test-support.js.map +1 -0
- package/dist/http/mcp/tool.d.ts +48 -0
- package/dist/http/mcp/tool.js +58 -0
- package/dist/http/mcp/tool.js.map +1 -0
- package/dist/http/mcp/tools/automations.d.ts +6 -0
- package/dist/http/mcp/tools/automations.js +120 -0
- package/dist/http/mcp/tools/automations.js.map +1 -0
- package/dist/http/mcp/tools/catalog.d.ts +6 -0
- package/dist/http/mcp/tools/catalog.js +200 -0
- package/dist/http/mcp/tools/catalog.js.map +1 -0
- package/dist/http/mcp/tools/comments.d.ts +6 -0
- package/dist/http/mcp/tools/comments.js +70 -0
- package/dist/http/mcp/tools/comments.js.map +1 -0
- package/dist/http/mcp/tools/communities.d.ts +6 -0
- package/dist/http/mcp/tools/communities.js +175 -0
- package/dist/http/mcp/tools/communities.js.map +1 -0
- package/dist/http/mcp/tools/community-members.d.ts +6 -0
- package/dist/http/mcp/tools/community-members.js +89 -0
- package/dist/http/mcp/tools/community-members.js.map +1 -0
- package/dist/http/mcp/tools/community-moderation.d.ts +6 -0
- package/dist/http/mcp/tools/community-moderation.js +144 -0
- package/dist/http/mcp/tools/community-moderation.js.map +1 -0
- package/dist/http/mcp/tools/courses.d.ts +6 -0
- package/dist/http/mcp/tools/courses.js +121 -0
- package/dist/http/mcp/tools/courses.js.map +1 -0
- package/dist/http/mcp/tools/index.d.ts +6 -0
- package/dist/http/mcp/tools/index.js +23 -0
- package/dist/http/mcp/tools/index.js.map +1 -0
- package/dist/http/mcp/tools/people.d.ts +6 -0
- package/dist/http/mcp/tools/people.js +145 -0
- package/dist/http/mcp/tools/people.js.map +1 -0
- package/dist/http/plugins/auth.js +13 -5
- package/dist/http/plugins/auth.js.map +1 -1
- package/dist/http/plugins/error-handler.d.ts +3 -2
- package/dist/http/plugins/error-handler.js +52 -57
- package/dist/http/plugins/error-handler.js.map +1 -1
- package/dist/http/plugins/openapi.js +70 -20
- package/dist/http/plugins/openapi.js.map +1 -1
- package/dist/http/routes/access.d.ts +7 -0
- package/dist/http/routes/access.js +140 -0
- package/dist/http/routes/access.js.map +1 -0
- package/dist/http/routes/activities.js +97 -20
- package/dist/http/routes/activities.js.map +1 -1
- package/dist/http/routes/assets.js +60 -51
- package/dist/http/routes/assets.js.map +1 -1
- package/dist/http/routes/automations.js +64 -20
- package/dist/http/routes/automations.js.map +1 -1
- package/dist/http/routes/bundles.d.ts +7 -0
- package/dist/http/routes/bundles.js +181 -0
- package/dist/http/routes/bundles.js.map +1 -0
- package/dist/http/routes/communities.d.ts +7 -0
- package/dist/http/routes/communities.js +462 -0
- package/dist/http/routes/communities.js.map +1 -0
- package/dist/http/routes/community-moderation.d.ts +7 -0
- package/dist/http/routes/community-moderation.js +425 -0
- package/dist/http/routes/community-moderation.js.map +1 -0
- package/dist/http/routes/course-analytics.d.ts +7 -0
- package/dist/http/routes/course-analytics.js +60 -0
- package/dist/http/routes/course-analytics.js.map +1 -0
- package/dist/http/routes/courses.js +38 -23
- package/dist/http/routes/courses.js.map +1 -1
- package/dist/http/routes/dashboard.js +17 -8
- package/dist/http/routes/dashboard.js.map +1 -1
- package/dist/http/routes/discussion.js +35 -27
- package/dist/http/routes/discussion.js.map +1 -1
- package/dist/http/routes/downloads.js +58 -40
- package/dist/http/routes/downloads.js.map +1 -1
- package/dist/http/routes/entitlements.js +16 -7
- package/dist/http/routes/entitlements.js.map +1 -1
- package/dist/http/routes/integrations.js +38 -41
- package/dist/http/routes/integrations.js.map +1 -1
- package/dist/http/routes/member/actor.d.ts +14 -0
- package/dist/http/routes/member/actor.js +11 -0
- package/dist/http/routes/member/actor.js.map +1 -0
- package/dist/http/routes/member/assets.d.ts +7 -0
- package/dist/http/routes/member/assets.js +79 -0
- package/dist/http/routes/member/assets.js.map +1 -0
- package/dist/http/routes/member/chats.d.ts +7 -0
- package/dist/http/routes/member/chats.js +456 -0
- package/dist/http/routes/member/chats.js.map +1 -0
- package/dist/http/routes/member/comments.d.ts +7 -0
- package/dist/http/routes/member/comments.js +187 -0
- package/dist/http/routes/member/comments.js.map +1 -0
- package/dist/http/routes/member/community.d.ts +7 -0
- package/dist/http/routes/member/community.js +288 -0
- package/dist/http/routes/member/community.js.map +1 -0
- package/dist/http/routes/member/courses.d.ts +7 -0
- package/dist/http/routes/member/courses.js +101 -0
- package/dist/http/routes/member/courses.js.map +1 -0
- package/dist/http/routes/member/downloads.d.ts +7 -0
- package/dist/http/routes/member/downloads.js +56 -0
- package/dist/http/routes/member/downloads.js.map +1 -0
- package/dist/http/routes/member/files.d.ts +7 -0
- package/dist/http/routes/member/files.js +184 -0
- package/dist/http/routes/member/files.js.map +1 -0
- package/dist/http/routes/member/index.d.ts +7 -0
- package/dist/http/routes/member/index.js +31 -0
- package/dist/http/routes/member/index.js.map +1 -0
- package/dist/http/routes/member/notifications.d.ts +7 -0
- package/dist/http/routes/member/notifications.js +101 -0
- package/dist/http/routes/member/notifications.js.map +1 -0
- package/dist/http/routes/member/org.d.ts +7 -0
- package/dist/http/routes/member/org.js +56 -0
- package/dist/http/routes/member/org.js.map +1 -0
- package/dist/http/routes/member/posts.d.ts +7 -0
- package/dist/http/routes/member/posts.js +612 -0
- package/dist/http/routes/member/posts.js.map +1 -0
- package/dist/http/routes/member/profile.d.ts +7 -0
- package/dist/http/routes/member/profile.js +48 -0
- package/dist/http/routes/member/profile.js.map +1 -0
- package/dist/http/routes/member/progress.d.ts +7 -0
- package/dist/http/routes/member/progress.js +60 -0
- package/dist/http/routes/member/progress.js.map +1 -0
- package/dist/http/routes/organizations.js +49 -24
- package/dist/http/routes/organizations.js.map +1 -1
- package/dist/http/routes/students.js +43 -37
- package/dist/http/routes/students.js.map +1 -1
- package/dist/http/routes/webhooks.d.ts +7 -0
- package/dist/http/routes/webhooks.js +167 -0
- package/dist/http/routes/webhooks.js.map +1 -0
- package/dist/http/routes.js +14 -2
- package/dist/http/routes.js.map +1 -1
- package/dist/http/schemas/access.d.ts +146 -0
- package/dist/http/schemas/access.js +36 -0
- package/dist/http/schemas/access.js.map +1 -0
- package/dist/http/schemas/activities.d.ts +36 -1
- package/dist/http/schemas/activities.js +9 -4
- package/dist/http/schemas/activities.js.map +1 -1
- package/dist/http/schemas/assets.d.ts +76 -73
- package/dist/http/schemas/assets.js +23 -13
- package/dist/http/schemas/assets.js.map +1 -1
- package/dist/http/schemas/automations.d.ts +85 -8
- package/dist/http/schemas/automations.js +13 -2
- package/dist/http/schemas/automations.js.map +1 -1
- package/dist/http/schemas/bundles.d.ts +63 -0
- package/dist/http/schemas/bundles.js +23 -0
- package/dist/http/schemas/bundles.js.map +1 -0
- package/dist/http/schemas/chats.d.ts +386 -0
- package/dist/http/schemas/chats.js +58 -0
- package/dist/http/schemas/chats.js.map +1 -0
- package/dist/http/schemas/community.d.ts +537 -0
- package/dist/http/schemas/community.js +129 -0
- package/dist/http/schemas/community.js.map +1 -0
- package/dist/http/schemas/content.d.ts +22 -13
- package/dist/http/schemas/content.js +11 -9
- package/dist/http/schemas/content.js.map +1 -1
- package/dist/http/schemas/course-analytics.d.ts +29 -0
- package/dist/http/schemas/course-analytics.js +21 -0
- package/dist/http/schemas/course-analytics.js.map +1 -0
- package/dist/http/schemas/dashboard.js.map +1 -1
- package/dist/http/schemas/discussion.d.ts +85 -23
- package/dist/http/schemas/discussion.js +12 -11
- package/dist/http/schemas/discussion.js.map +1 -1
- package/dist/http/schemas/downloads.d.ts +13 -5
- package/dist/http/schemas/downloads.js +14 -11
- package/dist/http/schemas/downloads.js.map +1 -1
- package/dist/http/schemas/entitlements.d.ts +14 -6
- package/dist/http/schemas/entitlements.js +8 -6
- package/dist/http/schemas/entitlements.js.map +1 -1
- package/dist/http/schemas/files.d.ts +45 -0
- package/dist/http/schemas/files.js +25 -0
- package/dist/http/schemas/files.js.map +1 -0
- package/dist/http/schemas/index.d.ts +19 -10
- package/dist/http/schemas/index.js +19 -10
- package/dist/http/schemas/integrations.d.ts +4 -4
- package/dist/http/schemas/integrations.js.map +1 -1
- package/dist/http/schemas/invites.d.ts +4 -0
- package/dist/http/schemas/invites.js +3 -1
- package/dist/http/schemas/invites.js.map +1 -1
- package/dist/http/schemas/{learn.d.ts → member.d.ts} +42 -19
- package/dist/http/schemas/{learn.js → member.js} +17 -13
- package/dist/http/schemas/member.js.map +1 -0
- package/dist/http/schemas/members.d.ts +39 -6
- package/dist/http/schemas/members.js +17 -3
- package/dist/http/schemas/members.js.map +1 -1
- package/dist/http/schemas/notifications.d.ts +227 -0
- package/dist/http/schemas/notifications.js +44 -0
- package/dist/http/schemas/notifications.js.map +1 -0
- package/dist/http/schemas/organizations.js.map +1 -1
- package/dist/http/schemas/posts.d.ts +316 -0
- package/dist/http/schemas/posts.js +47 -0
- package/dist/http/schemas/posts.js.map +1 -0
- package/dist/http/schemas/profile.d.ts +23 -0
- package/dist/http/schemas/profile.js +15 -0
- package/dist/http/schemas/profile.js.map +1 -0
- package/dist/http/schemas/settings.js.map +1 -1
- package/dist/http/schemas/shared.d.ts +26 -1
- package/dist/http/schemas/shared.js +22 -3
- package/dist/http/schemas/shared.js.map +1 -1
- package/dist/http/schemas/students.d.ts +32 -10
- package/dist/http/schemas/students.js +19 -6
- package/dist/http/schemas/students.js.map +1 -1
- package/dist/http/scope.d.ts +2 -1
- package/dist/http/scope.js +20 -4
- package/dist/http/scope.js.map +1 -1
- package/dist/http/server.js +10 -3
- package/dist/http/server.js.map +1 -1
- package/dist/http/web-bridge.d.ts +1 -1
- package/dist/http/web-bridge.js +2 -2
- package/dist/http/web-bridge.js.map +1 -1
- package/dist/http/well-known.d.ts +7 -0
- package/dist/http/well-known.js +22 -0
- package/dist/http/well-known.js.map +1 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/node_modules/.pnpm/@jridgewell_sourcemap-codec@1.5.5/node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.js +78 -0
- package/dist/node_modules/.pnpm/@jridgewell_sourcemap-codec@1.5.5/node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.js.map +1 -0
- package/dist/node_modules/.pnpm/@vitest_expect@2.1.9/node_modules/@vitest/expect/dist/index.js +1267 -0
- package/dist/node_modules/.pnpm/@vitest_expect@2.1.9/node_modules/@vitest/expect/dist/index.js.map +1 -0
- package/dist/node_modules/.pnpm/@vitest_pretty-format@2.1.9/node_modules/@vitest/pretty-format/dist/index.js +814 -0
- package/dist/node_modules/.pnpm/@vitest_pretty-format@2.1.9/node_modules/@vitest/pretty-format/dist/index.js.map +1 -0
- package/dist/node_modules/.pnpm/@vitest_runner@2.1.9/node_modules/@vitest/runner/dist/chunk-tasks.js +43 -0
- package/dist/node_modules/.pnpm/@vitest_runner@2.1.9/node_modules/@vitest/runner/dist/chunk-tasks.js.map +1 -0
- package/dist/node_modules/.pnpm/@vitest_runner@2.1.9/node_modules/@vitest/runner/dist/index.js +527 -0
- package/dist/node_modules/.pnpm/@vitest_runner@2.1.9/node_modules/@vitest/runner/dist/index.js.map +1 -0
- package/dist/node_modules/.pnpm/@vitest_runner@2.1.9/node_modules/@vitest/runner/dist/utils.js +2 -0
- package/dist/node_modules/.pnpm/@vitest_snapshot@2.1.9/node_modules/@vitest/snapshot/dist/index.js +1737 -0
- package/dist/node_modules/.pnpm/@vitest_snapshot@2.1.9/node_modules/@vitest/snapshot/dist/index.js.map +1 -0
- package/dist/node_modules/.pnpm/@vitest_spy@2.1.9/node_modules/@vitest/spy/dist/index.js +137 -0
- package/dist/node_modules/.pnpm/@vitest_spy@2.1.9/node_modules/@vitest/spy/dist/index.js.map +1 -0
- package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/chunk-_commonjsHelpers.js +118 -0
- package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/chunk-_commonjsHelpers.js.map +1 -0
- package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/diff.js +1222 -0
- package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/diff.js.map +1 -0
- package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/error.js +91 -0
- package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/error.js.map +1 -0
- package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/helpers.js +113 -0
- package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/helpers.js.map +1 -0
- package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/index.js +449 -0
- package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/index.js.map +1 -0
- package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/source-map.js +163 -0
- package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/source-map.js.map +1 -0
- package/dist/node_modules/.pnpm/chai@5.3.3/node_modules/chai/index.js +3187 -0
- package/dist/node_modules/.pnpm/chai@5.3.3/node_modules/chai/index.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/arguments.js +11 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/arguments.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/array.js +16 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/array.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/bigint.js +11 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/bigint.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/class.js +15 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/class.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/date.js +13 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/date.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/error.js +34 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/error.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/function.js +12 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/function.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/helpers.js +122 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/helpers.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/html.js +39 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/html.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/index.js +99 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/index.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/map.js +25 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/map.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/number.js +14 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/number.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/object.js +21 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/object.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/promise.js +6 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/promise.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/regexp.js +12 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/regexp.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/set.js +18 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/set.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/string.js +24 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/string.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/symbol.js +9 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/symbol.js.map +1 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/typedarray.js +30 -0
- package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/typedarray.js.map +1 -0
- package/dist/node_modules/.pnpm/magic-string@0.30.21/node_modules/magic-string/dist/magic-string.es.js +939 -0
- package/dist/node_modules/.pnpm/magic-string@0.30.21/node_modules/magic-string/dist/magic-string.es.js.map +1 -0
- package/dist/node_modules/.pnpm/pathe@1.1.2/node_modules/pathe/dist/shared/pathe.ff20891b.js +82 -0
- package/dist/node_modules/.pnpm/pathe@1.1.2/node_modules/pathe/dist/shared/pathe.ff20891b.js.map +1 -0
- package/dist/node_modules/.pnpm/tinyrainbow@1.2.0/node_modules/tinyrainbow/dist/chunk-BVHSVHOK.js +82 -0
- package/dist/node_modules/.pnpm/tinyrainbow@1.2.0/node_modules/tinyrainbow/dist/chunk-BVHSVHOK.js.map +1 -0
- package/dist/node_modules/.pnpm/tinyrainbow@1.2.0/node_modules/tinyrainbow/dist/node.js +8 -0
- package/dist/node_modules/.pnpm/tinyrainbow@1.2.0/node_modules/tinyrainbow/dist/node.js.map +1 -0
- package/dist/node_modules/.pnpm/tinyspy@3.0.2/node_modules/tinyspy/dist/index.js +83 -0
- package/dist/node_modules/.pnpm/tinyspy@3.0.2/node_modules/tinyspy/dist/index.js.map +1 -0
- package/dist/node_modules/.pnpm/vitest@2.1.9_@types_node@22.20.0_jsdom@25.0.1_lightningcss@1.32.0/node_modules/vitest/dist/chunks/_commonjsHelpers.BFTU3MAI.js +9 -0
- package/dist/node_modules/.pnpm/vitest@2.1.9_@types_node@22.20.0_jsdom@25.0.1_lightningcss@1.32.0/node_modules/vitest/dist/chunks/_commonjsHelpers.BFTU3MAI.js.map +1 -0
- package/dist/node_modules/.pnpm/vitest@2.1.9_@types_node@22.20.0_jsdom@25.0.1_lightningcss@1.32.0/node_modules/vitest/dist/chunks/date.W2xKR2qe.js +51 -0
- package/dist/node_modules/.pnpm/vitest@2.1.9_@types_node@22.20.0_jsdom@25.0.1_lightningcss@1.32.0/node_modules/vitest/dist/chunks/date.W2xKR2qe.js.map +1 -0
- package/dist/node_modules/.pnpm/vitest@2.1.9_@types_node@22.20.0_jsdom@25.0.1_lightningcss@1.32.0/node_modules/vitest/dist/chunks/utils.C8RiOc4B.js +48 -0
- package/dist/node_modules/.pnpm/vitest@2.1.9_@types_node@22.20.0_jsdom@25.0.1_lightningcss@1.32.0/node_modules/vitest/dist/chunks/utils.C8RiOc4B.js.map +1 -0
- package/dist/node_modules/.pnpm/vitest@2.1.9_@types_node@22.20.0_jsdom@25.0.1_lightningcss@1.32.0/node_modules/vitest/dist/chunks/vi.DgezovHB.js +2426 -0
- package/dist/node_modules/.pnpm/vitest@2.1.9_@types_node@22.20.0_jsdom@25.0.1_lightningcss@1.32.0/node_modules/vitest/dist/chunks/vi.DgezovHB.js.map +1 -0
- package/dist/packages/server/package.js +6 -0
- package/dist/packages/server/package.js.map +1 -0
- package/package.json +9 -7
- package/dist/app/notifications.d.ts +0 -7
- package/dist/app/notifications.js +0 -23
- package/dist/app/notifications.js.map +0 -1
- package/dist/http/routes/learn/assets.d.ts +0 -8
- package/dist/http/routes/learn/assets.js +0 -32
- package/dist/http/routes/learn/assets.js.map +0 -1
- package/dist/http/routes/learn/comments.d.ts +0 -7
- package/dist/http/routes/learn/comments.js +0 -175
- package/dist/http/routes/learn/comments.js.map +0 -1
- package/dist/http/routes/learn/courses.d.ts +0 -7
- package/dist/http/routes/learn/courses.js +0 -75
- package/dist/http/routes/learn/courses.js.map +0 -1
- package/dist/http/routes/learn/downloads.d.ts +0 -7
- package/dist/http/routes/learn/downloads.js +0 -76
- package/dist/http/routes/learn/downloads.js.map +0 -1
- package/dist/http/routes/learn/index.d.ts +0 -7
- package/dist/http/routes/learn/index.js +0 -19
- package/dist/http/routes/learn/index.js.map +0 -1
- package/dist/http/routes/learn/org.d.ts +0 -7
- package/dist/http/routes/learn/org.js +0 -50
- package/dist/http/routes/learn/org.js.map +0 -1
- package/dist/http/routes/learn/progress.d.ts +0 -7
- package/dist/http/routes/learn/progress.js +0 -63
- package/dist/http/routes/learn/progress.js.map +0 -1
- package/dist/http/schemas/learn.js.map +0 -1
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { refusals } from "../schemas/shared.js";
|
|
2
|
+
import { resolveScope } from "../scope.js";
|
|
3
|
+
import { Asset, AssetIdParam, AssetsPage, AssetsQuery, RequestUpload, UploadTicket } from "../schemas/assets.js";
|
|
3
4
|
import "../schemas/index.js";
|
|
4
|
-
import { NotFoundError } from "@headless-lms/core/shared/errors";
|
|
5
5
|
import { z } from "zod";
|
|
6
|
+
import { NotFoundError } from "@headless-lms/core/shared/errors";
|
|
6
7
|
//#region src/http/routes/assets.ts
|
|
7
8
|
/** Resolve the session's active org to the domain org id, or 400 and return null. */
|
|
8
9
|
async function resolveOrgId(req, reply) {
|
|
@@ -19,27 +20,30 @@ async function assetsRoutes(app, container) {
|
|
|
19
20
|
const r = app.withTypeProvider();
|
|
20
21
|
const assets = container.assets;
|
|
21
22
|
const tags = ["Assets"];
|
|
23
|
+
const staff = async (req) => {
|
|
24
|
+
await resolveScope(container, req);
|
|
25
|
+
};
|
|
22
26
|
r.route({
|
|
23
27
|
method: "POST",
|
|
24
28
|
url: "/api/uploads",
|
|
25
|
-
preHandler:
|
|
29
|
+
preHandler: staff,
|
|
26
30
|
schema: {
|
|
27
31
|
operationId: "requestUpload",
|
|
28
32
|
tags,
|
|
29
|
-
summary: "Register
|
|
33
|
+
summary: "Register a library asset and get a presigned upload URL (staff)",
|
|
34
|
+
description: "Staff only (owner, admin, instructor). The asset stays pending until you upload the file and confirm it.",
|
|
30
35
|
body: RequestUpload,
|
|
31
36
|
response: {
|
|
32
37
|
201: UploadTicket,
|
|
33
|
-
|
|
34
|
-
401: ErrorBody
|
|
38
|
+
...refusals(403)
|
|
35
39
|
}
|
|
36
40
|
},
|
|
37
41
|
handler: async (req, reply) => {
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
42
|
+
const scope = await resolveScope(container, req);
|
|
43
|
+
const ticket = await assets.requestUpload(scope.orgId, {
|
|
44
|
+
...req.body,
|
|
45
|
+
source: "library",
|
|
46
|
+
uploadedBy: scope.orgUserId
|
|
43
47
|
});
|
|
44
48
|
return reply.code(201).send(ticket);
|
|
45
49
|
}
|
|
@@ -47,17 +51,16 @@ async function assetsRoutes(app, container) {
|
|
|
47
51
|
r.route({
|
|
48
52
|
method: "POST",
|
|
49
53
|
url: "/api/assets/:id/confirm",
|
|
50
|
-
preHandler:
|
|
54
|
+
preHandler: staff,
|
|
51
55
|
schema: {
|
|
52
56
|
operationId: "confirmAsset",
|
|
53
57
|
tags,
|
|
54
|
-
summary: "Confirm
|
|
58
|
+
summary: "Confirm a library upload completed (staff)",
|
|
59
|
+
description: "Staff only (owner, admin, instructor). Captures the stored size and content type: a pending library upload becomes ready; a ready one is returned unchanged. A member upload is finalized only by its uploader and is answered with 403.",
|
|
55
60
|
params: AssetIdParam,
|
|
56
61
|
response: {
|
|
57
62
|
200: Asset,
|
|
58
|
-
|
|
59
|
-
401: ErrorBody,
|
|
60
|
-
404: ErrorBody
|
|
63
|
+
...refusals(403, 404)
|
|
61
64
|
}
|
|
62
65
|
},
|
|
63
66
|
handler: async (req, reply) => {
|
|
@@ -71,16 +74,16 @@ async function assetsRoutes(app, container) {
|
|
|
71
74
|
r.route({
|
|
72
75
|
method: "GET",
|
|
73
76
|
url: "/api/assets",
|
|
74
|
-
preHandler:
|
|
77
|
+
preHandler: staff,
|
|
75
78
|
schema: {
|
|
76
79
|
operationId: "listAssets",
|
|
77
80
|
tags,
|
|
78
|
-
summary: "Browse the organization's media library",
|
|
81
|
+
summary: "Browse the organization's media library (staff)",
|
|
82
|
+
description: "Staff only (owner, admin, instructor). Library files by default; `source: member` lists members' uploads.",
|
|
79
83
|
querystring: AssetsQuery,
|
|
80
84
|
response: {
|
|
81
85
|
200: AssetsPage,
|
|
82
|
-
|
|
83
|
-
401: ErrorBody
|
|
86
|
+
...refusals(403)
|
|
84
87
|
}
|
|
85
88
|
},
|
|
86
89
|
handler: async (req, reply) => {
|
|
@@ -92,17 +95,16 @@ async function assetsRoutes(app, container) {
|
|
|
92
95
|
r.route({
|
|
93
96
|
method: "GET",
|
|
94
97
|
url: "/api/assets/:id",
|
|
95
|
-
preHandler:
|
|
98
|
+
preHandler: staff,
|
|
96
99
|
schema: {
|
|
97
100
|
operationId: "getAsset",
|
|
98
101
|
tags,
|
|
99
|
-
summary: "Get an asset's metadata",
|
|
102
|
+
summary: "Get an asset's metadata (staff)",
|
|
103
|
+
description: "Staff only (owner, admin, instructor).",
|
|
100
104
|
params: AssetIdParam,
|
|
101
105
|
response: {
|
|
102
106
|
200: Asset,
|
|
103
|
-
|
|
104
|
-
401: ErrorBody,
|
|
105
|
-
404: ErrorBody
|
|
107
|
+
...refusals(403, 404)
|
|
106
108
|
}
|
|
107
109
|
},
|
|
108
110
|
handler: async (req, reply) => {
|
|
@@ -114,51 +116,58 @@ async function assetsRoutes(app, container) {
|
|
|
114
116
|
}
|
|
115
117
|
});
|
|
116
118
|
r.route({
|
|
117
|
-
method: "
|
|
118
|
-
url: "/api/assets/:id
|
|
119
|
-
preHandler:
|
|
119
|
+
method: "DELETE",
|
|
120
|
+
url: "/api/assets/:id",
|
|
121
|
+
preHandler: staff,
|
|
120
122
|
schema: {
|
|
121
|
-
operationId: "
|
|
123
|
+
operationId: "deleteAsset",
|
|
122
124
|
tags,
|
|
123
|
-
summary: "
|
|
125
|
+
summary: "Delete an asset nothing references (staff)",
|
|
126
|
+
description: "Staff only (owner, admin, instructor). Removes the row and the stored file. 409 while content still uses it.",
|
|
124
127
|
params: AssetIdParam,
|
|
125
|
-
body: RequestDownload,
|
|
126
128
|
response: {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
401: ErrorBody,
|
|
130
|
-
404: ErrorBody
|
|
129
|
+
204: z.void(),
|
|
130
|
+
...refusals(403, 404, 409)
|
|
131
131
|
}
|
|
132
132
|
},
|
|
133
133
|
handler: async (req, reply) => {
|
|
134
134
|
const orgId = await resolveOrgId(req, reply);
|
|
135
135
|
if (!orgId) return;
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
return ticket;
|
|
136
|
+
if (!await assets.remove(orgId, req.params.id)) throw new NotFoundError("Asset", req.params.id);
|
|
137
|
+
return reply.code(204).send();
|
|
139
138
|
}
|
|
140
139
|
});
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
140
|
+
for (const { suffix, download, operationId, summary } of [{
|
|
141
|
+
suffix: "file",
|
|
142
|
+
download: false,
|
|
143
|
+
operationId: "getLibraryFile",
|
|
144
|
+
summary: "Redirect to a library file to show it (staff)"
|
|
145
|
+
}, {
|
|
146
|
+
suffix: "download",
|
|
147
|
+
download: true,
|
|
148
|
+
operationId: "downloadLibraryFile",
|
|
149
|
+
summary: "Redirect to a library file saved under its filename (staff)"
|
|
150
|
+
}]) r.route({
|
|
151
|
+
method: "GET",
|
|
152
|
+
url: `/api/assets/:id/${suffix}`,
|
|
153
|
+
preHandler: staff,
|
|
145
154
|
schema: {
|
|
146
|
-
operationId
|
|
155
|
+
operationId,
|
|
147
156
|
tags,
|
|
148
|
-
summary
|
|
157
|
+
summary,
|
|
158
|
+
description: "Staff only (owner, admin, instructor). Ready library files of the organization; member uploads and pending files answer 404.",
|
|
149
159
|
params: AssetIdParam,
|
|
150
160
|
response: {
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
401: ErrorBody,
|
|
154
|
-
404: ErrorBody
|
|
161
|
+
302: z.void(),
|
|
162
|
+
...refusals(403, 404)
|
|
155
163
|
}
|
|
156
164
|
},
|
|
157
165
|
handler: async (req, reply) => {
|
|
158
166
|
const orgId = await resolveOrgId(req, reply);
|
|
159
167
|
if (!orgId) return;
|
|
160
|
-
|
|
161
|
-
|
|
168
|
+
const url = await assets.libraryFile(orgId, req.params.id, { download });
|
|
169
|
+
if (!url) throw new NotFoundError("Asset", req.params.id);
|
|
170
|
+
return reply.header("cache-control", "no-store").header("referrer-policy", "no-referrer").redirect(url, 302);
|
|
162
171
|
}
|
|
163
172
|
});
|
|
164
173
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assets.js","names":[],"sources":["../../../src/http/routes/assets.ts"],"sourcesContent":["// HTTP routes for the assets media library
|
|
1
|
+
{"version":3,"file":"assets.js","names":[],"sources":["../../../src/http/routes/assets.ts"],"sourcesContent":["// HTTP routes for the assets media library. All require a session with an\n// active organization; assets are stored under the org's private prefix and\n// served through stable staff URLs that redirect to a signed storage URL.\n//\n// `req.orgId` is already the domain organization id these tables key on — the\n// session guard translates it from better-auth's id once per request.\nimport type { FastifyInstance, FastifyReply, FastifyRequest } from 'fastify';\nimport type { ZodTypeProvider } from 'fastify-type-provider-zod';\nimport { z } from 'zod';\nimport {\n Asset,\n AssetIdParam,\n AssetsPage,\n AssetsQuery,\n RequestUpload,\n UploadTicket,\n refusals,\n} from '../schemas/index.js';\nimport { NotFoundError } from '@headless-lms/core/shared/errors';\nimport type { Container } from '../../app/container.js';\nimport { resolveScope } from '../scope.js';\n\n/** Resolve the session's active org to the domain org id, or 400 and return null. */\nasync function resolveOrgId(req: FastifyRequest, reply: FastifyReply): Promise<string | null> {\n if (!req.orgId) {\n await reply.code(400).send({ error: 'no_active_org', message: 'No active organization' });\n return null;\n }\n return req.orgId;\n}\n\nexport async function assetsRoutes(app: FastifyInstance, container: Container): Promise<void> {\n const r = app.withTypeProvider<ZodTypeProvider>();\n const assets = container.assets;\n const tags = ['Assets'];\n const staff = async (req: FastifyRequest) => {\n await resolveScope(container, req);\n };\n\n // Register an asset + get a presigned upload URL. Kept at /api/uploads as the\n // \"start an upload\" action; the resulting asset lives in the library below.\n r.route({\n method: 'POST',\n url: '/api/uploads',\n preHandler: staff,\n schema: {\n operationId: 'requestUpload',\n tags,\n summary: 'Register a library asset and get a presigned upload URL (staff)',\n description:\n 'Staff only (owner, admin, instructor). The asset stays pending until you upload the file and confirm it.',\n body: RequestUpload,\n response: { 201: UploadTicket, ...refusals(403) },\n },\n handler: async (req, reply) => {\n const scope = await resolveScope(container, req);\n const ticket = await assets.requestUpload(scope.orgId, {\n ...req.body,\n source: 'library',\n uploadedBy: scope.orgUserId,\n });\n return reply.code(201).send(ticket);\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/assets/:id/confirm',\n preHandler: staff,\n schema: {\n operationId: 'confirmAsset',\n tags,\n summary: 'Confirm a library upload completed (staff)',\n description:\n 'Staff only (owner, admin, instructor). Captures the stored size and content type: a pending library upload becomes ready; a ready one is returned unchanged. A member upload is finalized only by its uploader and is answered with 403.',\n params: AssetIdParam,\n response: { 200: Asset, ...refusals(403, 404) },\n },\n handler: async (req, reply) => {\n const orgId = await resolveOrgId(req, reply);\n if (!orgId) {\n return;\n }\n const asset = await assets.confirm(orgId, req.params.id);\n if (!asset) {\n throw new NotFoundError('Asset', req.params.id);\n }\n return asset;\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/assets',\n preHandler: staff,\n schema: {\n operationId: 'listAssets',\n tags,\n summary: \"Browse the organization's media library (staff)\",\n description:\n \"Staff only (owner, admin, instructor). Library files by default; `source: member` lists members' uploads.\",\n querystring: AssetsQuery,\n response: { 200: AssetsPage, ...refusals(403) },\n },\n handler: async (req, reply) => {\n const orgId = await resolveOrgId(req, reply);\n if (!orgId) {\n return;\n }\n return assets.list(orgId, req.query);\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/assets/:id',\n preHandler: staff,\n schema: {\n operationId: 'getAsset',\n tags,\n summary: \"Get an asset's metadata (staff)\",\n description: 'Staff only (owner, admin, instructor).',\n params: AssetIdParam,\n response: { 200: Asset, ...refusals(403, 404) },\n },\n handler: async (req, reply) => {\n const orgId = await resolveOrgId(req, reply);\n if (!orgId) {\n return;\n }\n const asset = await assets.get(orgId, req.params.id);\n if (!asset) {\n throw new NotFoundError('Asset', req.params.id);\n }\n return asset;\n },\n });\n\n r.route({\n method: 'DELETE',\n url: '/api/assets/:id',\n preHandler: staff,\n schema: {\n operationId: 'deleteAsset',\n tags,\n summary: 'Delete an asset nothing references (staff)',\n description:\n 'Staff only (owner, admin, instructor). Removes the row and the stored file. 409 while content still uses it.',\n params: AssetIdParam,\n response: { 204: z.void(), ...refusals(403, 404, 409) },\n },\n handler: async (req, reply) => {\n const orgId = await resolveOrgId(req, reply);\n if (!orgId) {\n return;\n }\n const removed = await assets.remove(orgId, req.params.id);\n if (!removed) {\n throw new NotFoundError('Asset', req.params.id);\n }\n return reply.code(204).send();\n },\n });\n\n for (const { suffix, download, operationId, summary } of [\n {\n suffix: 'file',\n download: false,\n operationId: 'getLibraryFile',\n summary: 'Redirect to a library file to show it (staff)',\n },\n {\n suffix: 'download',\n download: true,\n operationId: 'downloadLibraryFile',\n summary: 'Redirect to a library file saved under its filename (staff)',\n },\n ]) {\n r.route({\n method: 'GET',\n url: `/api/assets/:id/${suffix}`,\n preHandler: staff,\n schema: {\n operationId,\n tags,\n summary,\n description:\n 'Staff only (owner, admin, instructor). Ready library files of the organization; member uploads and pending files answer 404.',\n params: AssetIdParam,\n response: { 302: z.void(), ...refusals(403, 404) },\n },\n handler: async (req, reply) => {\n const orgId = await resolveOrgId(req, reply);\n if (!orgId) {\n return;\n }\n const url = await assets.libraryFile(orgId, req.params.id, { download });\n if (!url) {\n throw new NotFoundError('Asset', req.params.id);\n }\n return reply\n .header('cache-control', 'no-store')\n .header('referrer-policy', 'no-referrer')\n .redirect(url, 302);\n },\n });\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
import { refusals } from "../schemas/shared.js";
|
|
1
2
|
import { resolveScope } from "../scope.js";
|
|
2
|
-
import {
|
|
3
|
-
import { Automation, AutomationIdParam, AutomationRunsPage, AutomationRunsQuery, AvailableAction, AvailableTriggers, CreateAutomationBody, UpdateAutomationBody } from "../schemas/automations.js";
|
|
3
|
+
import { Automation, AutomationIdParam, AutomationRun, AutomationRunParams, AutomationRunsPage, AutomationRunsQuery, AutomationsQuery, AvailableAction, AvailableTriggers, CreateAutomationBody, UpdateAutomationBody } from "../schemas/automations.js";
|
|
4
4
|
import "../schemas/index.js";
|
|
5
|
-
import { NotFoundError } from "@headless-lms/core/shared/errors";
|
|
6
5
|
import { z } from "zod";
|
|
6
|
+
import { NotFoundError } from "@headless-lms/core/shared/errors";
|
|
7
7
|
//#region src/http/routes/automations.ts
|
|
8
8
|
async function automationsRoutes(app, container) {
|
|
9
9
|
const r = app.withTypeProvider();
|
|
@@ -16,12 +16,17 @@ async function automationsRoutes(app, container) {
|
|
|
16
16
|
schema: {
|
|
17
17
|
operationId: "listAutomations",
|
|
18
18
|
tags,
|
|
19
|
-
summary: "List automations",
|
|
20
|
-
|
|
19
|
+
summary: "List automations (staff)",
|
|
20
|
+
description: "Staff only (owner, admin, instructor). Optionally only workflows or only webhooks.",
|
|
21
|
+
querystring: AutomationsQuery,
|
|
22
|
+
response: {
|
|
23
|
+
200: z.array(Automation),
|
|
24
|
+
...refusals(403)
|
|
25
|
+
}
|
|
21
26
|
},
|
|
22
27
|
handler: async (req) => {
|
|
23
28
|
const scope = await resolveScope(container, req);
|
|
24
|
-
return automations.list(scope.orgId);
|
|
29
|
+
return automations.list(scope.orgId, req.query);
|
|
25
30
|
}
|
|
26
31
|
});
|
|
27
32
|
r.route({
|
|
@@ -31,8 +36,12 @@ async function automationsRoutes(app, container) {
|
|
|
31
36
|
schema: {
|
|
32
37
|
operationId: "listAutomationActions",
|
|
33
38
|
tags,
|
|
34
|
-
summary: "List the actions automations can use",
|
|
35
|
-
|
|
39
|
+
summary: "List the actions automations can use (staff)",
|
|
40
|
+
description: "Staff only (owner, admin, instructor).",
|
|
41
|
+
response: {
|
|
42
|
+
200: z.array(AvailableAction),
|
|
43
|
+
...refusals(403)
|
|
44
|
+
}
|
|
36
45
|
},
|
|
37
46
|
handler: async (req) => {
|
|
38
47
|
await resolveScope(container, req);
|
|
@@ -46,8 +55,12 @@ async function automationsRoutes(app, container) {
|
|
|
46
55
|
schema: {
|
|
47
56
|
operationId: "listAutomationTriggers",
|
|
48
57
|
tags,
|
|
49
|
-
summary: "List the domain events automations can react to",
|
|
50
|
-
|
|
58
|
+
summary: "List the domain events automations can react to (staff)",
|
|
59
|
+
description: "Staff only (owner, admin, instructor).",
|
|
60
|
+
response: {
|
|
61
|
+
200: AvailableTriggers,
|
|
62
|
+
...refusals(403)
|
|
63
|
+
}
|
|
51
64
|
},
|
|
52
65
|
handler: async (req) => {
|
|
53
66
|
await resolveScope(container, req);
|
|
@@ -61,9 +74,13 @@ async function automationsRoutes(app, container) {
|
|
|
61
74
|
schema: {
|
|
62
75
|
operationId: "createAutomation",
|
|
63
76
|
tags,
|
|
64
|
-
summary: "Create
|
|
77
|
+
summary: "Create a workflow automation (staff)",
|
|
78
|
+
description: "Staff only (owner, admin, instructor). 400 when a trigger is reserved, not an event the system emits or an unsupported schedule, or an action is reserved for webhooks.",
|
|
65
79
|
body: CreateAutomationBody,
|
|
66
|
-
response: {
|
|
80
|
+
response: {
|
|
81
|
+
201: Automation,
|
|
82
|
+
...refusals(400, 403)
|
|
83
|
+
}
|
|
67
84
|
},
|
|
68
85
|
handler: async (req, reply) => {
|
|
69
86
|
const scope = await resolveScope(container, req);
|
|
@@ -78,11 +95,12 @@ async function automationsRoutes(app, container) {
|
|
|
78
95
|
schema: {
|
|
79
96
|
operationId: "getAutomation",
|
|
80
97
|
tags,
|
|
81
|
-
summary: "Get an automation
|
|
98
|
+
summary: "Get an automation (staff)",
|
|
99
|
+
description: "Staff only (owner, admin, instructor). Workflows and webhooks alike.",
|
|
82
100
|
params: AutomationIdParam,
|
|
83
101
|
response: {
|
|
84
102
|
200: Automation,
|
|
85
|
-
404
|
|
103
|
+
...refusals(403, 404)
|
|
86
104
|
}
|
|
87
105
|
},
|
|
88
106
|
handler: async (req) => {
|
|
@@ -99,12 +117,13 @@ async function automationsRoutes(app, container) {
|
|
|
99
117
|
schema: {
|
|
100
118
|
operationId: "updateAutomation",
|
|
101
119
|
tags,
|
|
102
|
-
summary: "Update
|
|
120
|
+
summary: "Update a workflow automation (staff)",
|
|
121
|
+
description: "Staff only (owner, admin, instructor). Webhooks change through the webhook endpoints (409). 400 when a trigger is reserved, not an event the system emits or an unsupported schedule, or an action is reserved for webhooks.",
|
|
103
122
|
params: AutomationIdParam,
|
|
104
123
|
body: UpdateAutomationBody,
|
|
105
124
|
response: {
|
|
106
125
|
200: Automation,
|
|
107
|
-
404
|
|
126
|
+
...refusals(400, 403, 404, 409)
|
|
108
127
|
}
|
|
109
128
|
},
|
|
110
129
|
handler: async (req) => {
|
|
@@ -121,11 +140,12 @@ async function automationsRoutes(app, container) {
|
|
|
121
140
|
schema: {
|
|
122
141
|
operationId: "deleteAutomation",
|
|
123
142
|
tags,
|
|
124
|
-
summary: "Delete
|
|
143
|
+
summary: "Delete a workflow automation (staff)",
|
|
144
|
+
description: "Staff only (owner, admin, instructor). Webhooks are deleted through the webhook endpoints, and system automations cannot be deleted (409). Its runs stay readable.",
|
|
125
145
|
params: AutomationIdParam,
|
|
126
146
|
response: {
|
|
127
147
|
204: z.void(),
|
|
128
|
-
404
|
|
148
|
+
...refusals(403, 404, 409)
|
|
129
149
|
}
|
|
130
150
|
},
|
|
131
151
|
handler: async (req, reply) => {
|
|
@@ -141,16 +161,40 @@ async function automationsRoutes(app, container) {
|
|
|
141
161
|
schema: {
|
|
142
162
|
operationId: "listAutomationRuns",
|
|
143
163
|
tags,
|
|
144
|
-
summary: "List an automation's runs
|
|
164
|
+
summary: "List an automation's runs (staff)",
|
|
165
|
+
description: "Staff only (owner, admin, instructor). A deleted automation's runs remain reachable as an audit trail, so an unknown id answers an empty page rather than 404. A run's event carries the fields a webhook delivery of that event would.",
|
|
145
166
|
params: AutomationIdParam,
|
|
146
167
|
querystring: AutomationRunsQuery,
|
|
147
|
-
response: {
|
|
168
|
+
response: {
|
|
169
|
+
200: AutomationRunsPage,
|
|
170
|
+
...refusals(403)
|
|
171
|
+
}
|
|
148
172
|
},
|
|
149
173
|
handler: async (req) => {
|
|
150
174
|
const scope = await resolveScope(container, req);
|
|
151
175
|
return automations.listRuns(scope.orgId, req.params.id, req.query);
|
|
152
176
|
}
|
|
153
177
|
});
|
|
178
|
+
r.route({
|
|
179
|
+
method: "POST",
|
|
180
|
+
url: "/api/automations/:id/runs/:runId/rerun",
|
|
181
|
+
preHandler: app.requireOrgSession,
|
|
182
|
+
schema: {
|
|
183
|
+
operationId: "rerunAutomationRun",
|
|
184
|
+
tags,
|
|
185
|
+
summary: "Run a finished run again (staff)",
|
|
186
|
+
description: "Staff only (owner, admin, instructor). Dispatches the same run again with the automation's current steps; the new outcome replaces the run's previous one. For a webhook this resends the delivery to its current URL. A run that is still running can't be rerun (409).",
|
|
187
|
+
params: AutomationRunParams,
|
|
188
|
+
response: {
|
|
189
|
+
200: AutomationRun,
|
|
190
|
+
...refusals(403, 404, 409)
|
|
191
|
+
}
|
|
192
|
+
},
|
|
193
|
+
handler: async (req) => {
|
|
194
|
+
const scope = await resolveScope(container, req);
|
|
195
|
+
return automations.rerun(scope.orgId, req.params.id, req.params.runId);
|
|
196
|
+
}
|
|
197
|
+
});
|
|
154
198
|
}
|
|
155
199
|
//#endregion
|
|
156
200
|
export { automationsRoutes };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"automations.js","names":[],"sources":["../../../src/http/routes/automations.ts"],"sourcesContent":["// HTTP routes for the automations context: rules that match a trigger (a\n// domain event type) against enabled automations and run an ordered list of\n// actions, plus the run history each automation accumulates.\nimport type { FastifyInstance } from 'fastify';\nimport { z } from 'zod';\nimport type { ZodTypeProvider } from 'fastify-type-provider-zod';\nimport {\n Automation,\n AutomationIdParam,\n AutomationRunsPage,\n AutomationRunsQuery,\n AvailableAction,\n AvailableTriggers,\n CreateAutomationBody,\n ErrorBody,\n UpdateAutomationBody,\n} from '../schemas/index.js';\nimport { NotFoundError } from '@headless-lms/core/shared/errors';\nimport type { Container } from '../../app/container.js';\nimport { resolveScope } from '../scope.js';\n\nexport async function automationsRoutes(app: FastifyInstance, container: Container): Promise<void> {\n const r = app.withTypeProvider<ZodTypeProvider>();\n const automations = container.automations;\n const tags = ['Automations'];\n\n r.route({\n method: 'GET',\n url: '/api/automations',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'listAutomations',\n tags,\n summary: 'List automations',\n response: { 200: z.array(Automation) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return automations.list(scope.orgId);\n },\n });\n\n // Before `/api/automations/:id` — otherwise `:id` would swallow these path segments.\n r.route({\n method: 'GET',\n url: '/api/automations/actions',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'listAutomationActions',\n tags,\n summary: 'List the actions automations can use',\n response: { 200: z.array(AvailableAction) },\n },\n handler: async (req) => {\n await resolveScope(container, req);\n return automations.availableActions();\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/automations/triggers',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'listAutomationTriggers',\n tags,\n summary: 'List the domain events automations can react to',\n response: { 200: AvailableTriggers },\n },\n handler: async (req) => {\n await resolveScope(container, req);\n return automations.availableTriggers();\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/automations',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'createAutomation',\n tags,\n summary: 'Create an automation',\n body: CreateAutomationBody,\n response: { 201: Automation },\n },\n handler: async (req, reply) => {\n const scope = await resolveScope(container, req);\n const automation = await automations.create(scope.orgId, req.body);\n return reply.code(201).send(automation);\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/automations/:id',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'getAutomation',\n tags,\n summary: 'Get an automation by id',\n params: AutomationIdParam,\n response: { 200: Automation, 404: ErrorBody },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n const automation = await automations.get(scope.orgId, req.params.id);\n if (!automation) {\n throw new NotFoundError('Automation', req.params.id);\n }\n return automation;\n },\n });\n\n r.route({\n method: 'PATCH',\n url: '/api/automations/:id',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'updateAutomation',\n tags,\n summary: 'Update an automation',\n params: AutomationIdParam,\n body: UpdateAutomationBody,\n response: { 200: Automation, 404: ErrorBody },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n const automation = await automations.update(scope.orgId, req.params.id, req.body);\n if (!automation) {\n throw new NotFoundError('Automation', req.params.id);\n }\n return automation;\n },\n });\n\n r.route({\n method: 'DELETE',\n url: '/api/automations/:id',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'deleteAutomation',\n tags,\n summary: 'Delete an automation',\n params: AutomationIdParam,\n response: { 204: z.void(), 404: ErrorBody },\n },\n handler: async (req, reply) => {\n const scope = await resolveScope(container, req);\n const deleted = await automations.delete(scope.orgId, req.params.id);\n if (!deleted) {\n throw new NotFoundError('Automation', req.params.id);\n }\n return reply.code(204).send();\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/automations/:id/runs',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'listAutomationRuns',\n tags,\n summary: \"List an automation's runs — a deleted automation's runs remain reachable (audit trail)\",\n params: AutomationIdParam,\n querystring: AutomationRunsQuery,\n response: { 200: AutomationRunsPage },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n // No existence pre-check: runs deliberately survive automation deletion (audit trail).\n return automations.listRuns(scope.orgId, req.params.id, req.query);\n },\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"automations.js","names":[],"sources":["../../../src/http/routes/automations.ts"],"sourcesContent":["// HTTP routes for the automations context: rules that match a trigger (a\n// domain event type) against enabled automations and run an ordered list of\n// actions, plus the run history each automation accumulates.\nimport type { FastifyInstance } from 'fastify';\nimport { z } from 'zod';\nimport type { ZodTypeProvider } from 'fastify-type-provider-zod';\nimport {\n Automation,\n AutomationIdParam,\n AutomationRun,\n AutomationRunParams,\n AutomationRunsPage,\n AutomationRunsQuery,\n AutomationsQuery,\n AvailableAction,\n AvailableTriggers,\n CreateAutomationBody,\n UpdateAutomationBody,\n refusals,\n} from '../schemas/index.js';\nimport { NotFoundError } from '@headless-lms/core/shared/errors';\nimport type { Container } from '../../app/container.js';\nimport { resolveScope } from '../scope.js';\n\nexport async function automationsRoutes(app: FastifyInstance, container: Container): Promise<void> {\n const r = app.withTypeProvider<ZodTypeProvider>();\n const automations = container.automations;\n const tags = ['Automations'];\n\n r.route({\n method: 'GET',\n url: '/api/automations',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'listAutomations',\n tags,\n summary: 'List automations (staff)',\n description:\n 'Staff only (owner, admin, instructor). Optionally only workflows or only webhooks.',\n querystring: AutomationsQuery,\n response: { 200: z.array(Automation), ...refusals(403) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return automations.list(scope.orgId, req.query);\n },\n });\n\n // Before `/api/automations/:id` — otherwise `:id` would swallow these path segments.\n r.route({\n method: 'GET',\n url: '/api/automations/actions',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'listAutomationActions',\n tags,\n summary: 'List the actions automations can use (staff)',\n description: 'Staff only (owner, admin, instructor).',\n response: { 200: z.array(AvailableAction), ...refusals(403) },\n },\n handler: async (req) => {\n await resolveScope(container, req);\n return automations.availableActions();\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/automations/triggers',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'listAutomationTriggers',\n tags,\n summary: 'List the domain events automations can react to (staff)',\n description: 'Staff only (owner, admin, instructor).',\n response: { 200: AvailableTriggers, ...refusals(403) },\n },\n handler: async (req) => {\n await resolveScope(container, req);\n return automations.availableTriggers();\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/automations',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'createAutomation',\n tags,\n summary: 'Create a workflow automation (staff)',\n description:\n 'Staff only (owner, admin, instructor). 400 when a trigger is reserved, not an event the system emits or an unsupported schedule, or an action is reserved for webhooks.',\n body: CreateAutomationBody,\n response: { 201: Automation, ...refusals(400, 403) },\n },\n handler: async (req, reply) => {\n const scope = await resolveScope(container, req);\n const automation = await automations.create(scope.orgId, req.body);\n return reply.code(201).send(automation);\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/automations/:id',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'getAutomation',\n tags,\n summary: 'Get an automation (staff)',\n description: 'Staff only (owner, admin, instructor). Workflows and webhooks alike.',\n params: AutomationIdParam,\n response: { 200: Automation, ...refusals(403, 404) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n const automation = await automations.get(scope.orgId, req.params.id);\n if (!automation) {\n throw new NotFoundError('Automation', req.params.id);\n }\n return automation;\n },\n });\n\n r.route({\n method: 'PATCH',\n url: '/api/automations/:id',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'updateAutomation',\n tags,\n summary: 'Update a workflow automation (staff)',\n description:\n 'Staff only (owner, admin, instructor). Webhooks change through the webhook endpoints (409). 400 when a trigger is reserved, not an event the system emits or an unsupported schedule, or an action is reserved for webhooks.',\n params: AutomationIdParam,\n body: UpdateAutomationBody,\n response: { 200: Automation, ...refusals(400, 403, 404, 409) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n const automation = await automations.update(scope.orgId, req.params.id, req.body);\n if (!automation) {\n throw new NotFoundError('Automation', req.params.id);\n }\n return automation;\n },\n });\n\n r.route({\n method: 'DELETE',\n url: '/api/automations/:id',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'deleteAutomation',\n tags,\n summary: 'Delete a workflow automation (staff)',\n description:\n 'Staff only (owner, admin, instructor). Webhooks are deleted through the webhook endpoints, and system automations cannot be deleted (409). Its runs stay readable.',\n params: AutomationIdParam,\n response: { 204: z.void(), ...refusals(403, 404, 409) },\n },\n handler: async (req, reply) => {\n const scope = await resolveScope(container, req);\n const deleted = await automations.delete(scope.orgId, req.params.id);\n if (!deleted) {\n throw new NotFoundError('Automation', req.params.id);\n }\n return reply.code(204).send();\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/automations/:id/runs',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'listAutomationRuns',\n tags,\n summary: \"List an automation's runs (staff)\",\n description:\n \"Staff only (owner, admin, instructor). A deleted automation's runs remain reachable as an audit trail, so an unknown id answers an empty page rather than 404. A run's event carries the fields a webhook delivery of that event would.\",\n params: AutomationIdParam,\n querystring: AutomationRunsQuery,\n response: { 200: AutomationRunsPage, ...refusals(403) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n // No existence pre-check: runs deliberately survive automation deletion (audit trail).\n return automations.listRuns(scope.orgId, req.params.id, req.query);\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/automations/:id/runs/:runId/rerun',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'rerunAutomationRun',\n tags,\n summary: 'Run a finished run again (staff)',\n description:\n \"Staff only (owner, admin, instructor). Dispatches the same run again with the automation's current steps; the new outcome replaces the run's previous one. For a webhook this resends the delivery to its current URL. A run that is still running can't be rerun (409).\",\n params: AutomationRunParams,\n response: { 200: AutomationRun, ...refusals(403, 404, 409) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return automations.rerun(scope.orgId, req.params.id, req.params.runId);\n },\n });\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Container } from "../../app/container.js";
|
|
2
|
+
import { FastifyInstance } from "fastify";
|
|
3
|
+
//#region src/http/routes/bundles.d.ts
|
|
4
|
+
declare function bundlesRoutes(app: FastifyInstance, container: Container): Promise<void>;
|
|
5
|
+
//#endregion
|
|
6
|
+
export { bundlesRoutes };
|
|
7
|
+
//# sourceMappingURL=bundles.d.ts.map
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { refusals } from "../schemas/shared.js";
|
|
2
|
+
import { resolveScope } from "../scope.js";
|
|
3
|
+
import { AddBundleItem, Bundle, BundleIdParam, BundleItem, BundleItemParams, BundlesPage, BundlesQuery, CreateBundle, UpdateBundle } from "../schemas/bundles.js";
|
|
4
|
+
import "../schemas/index.js";
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
import { NotFoundError } from "@headless-lms/core/shared/errors";
|
|
7
|
+
//#region src/http/routes/bundles.ts
|
|
8
|
+
async function bundlesRoutes(app, container) {
|
|
9
|
+
const r = app.withTypeProvider();
|
|
10
|
+
const content = container.content;
|
|
11
|
+
r.route({
|
|
12
|
+
method: "GET",
|
|
13
|
+
url: "/api/bundles",
|
|
14
|
+
preHandler: app.requireOrgSession,
|
|
15
|
+
schema: {
|
|
16
|
+
operationId: "listBundles",
|
|
17
|
+
tags: ["Content"],
|
|
18
|
+
summary: "List bundles (staff)",
|
|
19
|
+
description: "Staff only (owner, admin, instructor).",
|
|
20
|
+
querystring: BundlesQuery,
|
|
21
|
+
response: {
|
|
22
|
+
200: BundlesPage,
|
|
23
|
+
...refusals(403)
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
handler: async (req) => {
|
|
27
|
+
const scope = await resolveScope(container, req);
|
|
28
|
+
return content.listBundles(scope.orgId, req.query);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
r.route({
|
|
32
|
+
method: "GET",
|
|
33
|
+
url: "/api/bundles/:bundleId",
|
|
34
|
+
preHandler: app.requireOrgSession,
|
|
35
|
+
schema: {
|
|
36
|
+
operationId: "getBundle",
|
|
37
|
+
tags: ["Content"],
|
|
38
|
+
summary: "Get a bundle (staff)",
|
|
39
|
+
description: "Staff only (owner, admin, instructor).",
|
|
40
|
+
params: BundleIdParam,
|
|
41
|
+
response: {
|
|
42
|
+
200: Bundle,
|
|
43
|
+
...refusals(403, 404)
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
handler: async (req) => {
|
|
47
|
+
const scope = await resolveScope(container, req);
|
|
48
|
+
const bundle = await content.getBundle(scope.orgId, req.params.bundleId);
|
|
49
|
+
if (!bundle) throw new NotFoundError("Bundle", req.params.bundleId);
|
|
50
|
+
return bundle;
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
r.route({
|
|
54
|
+
method: "POST",
|
|
55
|
+
url: "/api/bundles",
|
|
56
|
+
preHandler: app.requireOrgSession,
|
|
57
|
+
schema: {
|
|
58
|
+
operationId: "createBundle",
|
|
59
|
+
tags: ["Content"],
|
|
60
|
+
summary: "Create a bundle, optionally with initial content (staff)",
|
|
61
|
+
description: "Staff only (owner, admin, instructor). 404 when an initial content id is unknown.",
|
|
62
|
+
body: CreateBundle,
|
|
63
|
+
response: {
|
|
64
|
+
201: Bundle,
|
|
65
|
+
...refusals(403, 404)
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
handler: async (req, reply) => {
|
|
69
|
+
const scope = await resolveScope(container, req);
|
|
70
|
+
const bundle = await content.createBundle(scope.orgId, req.body);
|
|
71
|
+
return reply.code(201).send(bundle);
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
r.route({
|
|
75
|
+
method: "PATCH",
|
|
76
|
+
url: "/api/bundles/:bundleId",
|
|
77
|
+
preHandler: app.requireOrgSession,
|
|
78
|
+
schema: {
|
|
79
|
+
operationId: "updateBundle",
|
|
80
|
+
tags: ["Content"],
|
|
81
|
+
summary: "Rename a bundle (staff)",
|
|
82
|
+
description: "Staff only (owner, admin, instructor).",
|
|
83
|
+
params: BundleIdParam,
|
|
84
|
+
body: UpdateBundle,
|
|
85
|
+
response: {
|
|
86
|
+
200: Bundle,
|
|
87
|
+
...refusals(403, 404)
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
handler: async (req) => {
|
|
91
|
+
const scope = await resolveScope(container, req);
|
|
92
|
+
return content.updateBundle(scope.orgId, req.params.bundleId, req.body);
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
r.route({
|
|
96
|
+
method: "DELETE",
|
|
97
|
+
url: "/api/bundles/:bundleId",
|
|
98
|
+
preHandler: app.requireOrgSession,
|
|
99
|
+
schema: {
|
|
100
|
+
operationId: "deleteBundle",
|
|
101
|
+
tags: ["Content"],
|
|
102
|
+
summary: "Delete a bundle (staff)",
|
|
103
|
+
description: "Staff only (owner, admin, instructor). Its content survives; access granted through the bundle ends.",
|
|
104
|
+
params: BundleIdParam,
|
|
105
|
+
response: {
|
|
106
|
+
204: z.void(),
|
|
107
|
+
...refusals(403, 404)
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
handler: async (req, reply) => {
|
|
111
|
+
const scope = await resolveScope(container, req);
|
|
112
|
+
await content.deleteBundle(scope.orgId, req.params.bundleId);
|
|
113
|
+
return reply.code(204).send();
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
r.route({
|
|
117
|
+
method: "GET",
|
|
118
|
+
url: "/api/bundles/:bundleId/items",
|
|
119
|
+
preHandler: app.requireOrgSession,
|
|
120
|
+
schema: {
|
|
121
|
+
operationId: "listBundleItems",
|
|
122
|
+
tags: ["Content"],
|
|
123
|
+
summary: "List a bundle's content (staff)",
|
|
124
|
+
description: "Staff only (owner, admin, instructor).",
|
|
125
|
+
params: BundleIdParam,
|
|
126
|
+
response: {
|
|
127
|
+
200: z.array(BundleItem),
|
|
128
|
+
...refusals(403)
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
handler: async (req) => {
|
|
132
|
+
const scope = await resolveScope(container, req);
|
|
133
|
+
return content.listBundleItems(scope.orgId, req.params.bundleId);
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
r.route({
|
|
137
|
+
method: "POST",
|
|
138
|
+
url: "/api/bundles/:bundleId/items",
|
|
139
|
+
preHandler: app.requireOrgSession,
|
|
140
|
+
schema: {
|
|
141
|
+
operationId: "addBundleItem",
|
|
142
|
+
tags: ["Content"],
|
|
143
|
+
summary: "Add content to a bundle (staff)",
|
|
144
|
+
description: "Staff only (owner, admin, instructor). Everyone who holds the bundle can open the content from then on. Returns the bundle's content.",
|
|
145
|
+
params: BundleIdParam,
|
|
146
|
+
body: AddBundleItem,
|
|
147
|
+
response: {
|
|
148
|
+
200: z.array(BundleItem),
|
|
149
|
+
...refusals(403, 404)
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
handler: async (req) => {
|
|
153
|
+
const scope = await resolveScope(container, req);
|
|
154
|
+
return content.addBundleItem(scope.orgId, req.params.bundleId, req.body.contentId);
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
r.route({
|
|
158
|
+
method: "DELETE",
|
|
159
|
+
url: "/api/bundles/:bundleId/items/:contentId",
|
|
160
|
+
preHandler: app.requireOrgSession,
|
|
161
|
+
schema: {
|
|
162
|
+
operationId: "removeBundleItem",
|
|
163
|
+
tags: ["Content"],
|
|
164
|
+
summary: "Remove content from a bundle (staff)",
|
|
165
|
+
description: "Staff only (owner, admin, instructor). The content survives; access to it through the bundle ends. Returns the bundle's content.",
|
|
166
|
+
params: BundleItemParams,
|
|
167
|
+
response: {
|
|
168
|
+
200: z.array(BundleItem),
|
|
169
|
+
...refusals(403, 404)
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
handler: async (req) => {
|
|
173
|
+
const scope = await resolveScope(container, req);
|
|
174
|
+
return content.removeBundleItem(scope.orgId, req.params.bundleId, req.params.contentId);
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
//#endregion
|
|
179
|
+
export { bundlesRoutes };
|
|
180
|
+
|
|
181
|
+
//# sourceMappingURL=bundles.js.map
|