@headless-lms/server 0.2.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/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 +18 -14
- package/dist/app/container.js +136 -43
- 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 +2 -1
- package/dist/http/plugins/error-handler.js +49 -65
- package/dist/http/plugins/error-handler.js.map +1 -1
- package/dist/http/plugins/openapi.js +49 -2
- 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 +68 -23
- package/dist/http/routes/activities.js.map +1 -1
- package/dist/http/routes/assets.js +60 -55
- package/dist/http/routes/assets.js.map +1 -1
- package/dist/http/routes/automations.js +44 -24
- package/dist/http/routes/automations.js.map +1 -1
- package/dist/http/routes/bundles.js +29 -18
- package/dist/http/routes/bundles.js.map +1 -1
- 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.js +9 -7
- package/dist/http/routes/course-analytics.js.map +1 -1
- package/dist/http/routes/courses.js +30 -15
- package/dist/http/routes/courses.js.map +1 -1
- package/dist/http/routes/dashboard.js +15 -6
- package/dist/http/routes/dashboard.js.map +1 -1
- package/dist/http/routes/discussion.js +34 -26
- package/dist/http/routes/discussion.js.map +1 -1
- package/dist/http/routes/downloads.js +48 -30
- 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 +32 -47
- package/dist/http/routes/students.js.map +1 -1
- package/dist/http/routes/webhooks.js +26 -18
- package/dist/http/routes/webhooks.js.map +1 -1
- package/dist/http/routes.js +8 -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 +3 -1
- package/dist/http/schemas/activities.js +4 -4
- package/dist/http/schemas/activities.js.map +1 -1
- package/dist/http/schemas/assets.d.ts +73 -74
- package/dist/http/schemas/assets.js +23 -13
- package/dist/http/schemas/assets.js.map +1 -1
- package/dist/http/schemas/automations.d.ts +10 -0
- package/dist/http/schemas/bundles.js +6 -6
- package/dist/http/schemas/bundles.js.map +1 -1
- 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 +4 -2
- package/dist/http/schemas/content.js +11 -8
- package/dist/http/schemas/content.js.map +1 -1
- package/dist/http/schemas/discussion.d.ts +76 -14
- package/dist/http/schemas/discussion.js +12 -11
- package/dist/http/schemas/discussion.js.map +1 -1
- package/dist/http/schemas/downloads.d.ts +11 -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 +6 -6
- package/dist/http/schemas/entitlements.js +6 -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 +15 -8
- package/dist/http/schemas/index.js +15 -8
- package/dist/http/schemas/integrations.d.ts +2 -2
- package/dist/http/schemas/invites.d.ts +4 -0
- package/dist/http/schemas/invites.js +1 -0
- package/dist/http/schemas/invites.js.map +1 -1
- package/dist/http/schemas/{learn.d.ts → member.d.ts} +31 -18
- 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/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/shared.d.ts +25 -1
- package/dist/http/schemas/shared.js +19 -2
- package/dist/http/schemas/shared.js.map +1 -1
- package/dist/http/schemas/students.d.ts +4 -10
- package/dist/http/schemas/students.js +1 -7
- 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 +6 -1
- 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 +2 -1
- 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 -26
- 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 -98
- 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
- package/dist/package.js +0 -6
- package/dist/package.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"activities.js","names":[],"sources":["../../../src/http/routes/activities.ts"],"sourcesContent":["// HTTP routes for course modules + activities. Write operations return the\n// course's full, reordered module list (how the editor re-renders). Nested under\n// courses. All require a session with an active organization; handlers resolve\n// the session's active org to the domain org id and scope every call by it.\nimport type { FastifyInstance } from 'fastify';\nimport type { ZodTypeProvider } from 'fastify-type-provider-zod';\nimport {\n ActivityAssetList,\n ActivityList,\n ActivityPathParam,\n CourseIdPathParam,\n CreateModule,\n ModuleList,\n ModulePathParam,\n ReorderInput,\n SaveActivity,\n UpdateModule,\n} from '../schemas/index.js';\nimport type { Container } from '../../app/container.js';\nimport { resolveScope } from '../scope.js';\n\nexport async function activitiesRoutes(app: FastifyInstance, container: Container): Promise<void> {\n const r = app.withTypeProvider<ZodTypeProvider>();\n // Modules/activities are part of the content aggregate; grouped under the Courses tag.\n const content = container.content;\n const tags = ['Content'];\n\n r.route({\n method: 'GET',\n url: '/api/courses/:courseId/modules',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'listModules',\n tags,\n summary: \"List a course's modules\",\n params: CourseIdPathParam,\n response: { 200: ModuleList },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return content.listCourseModules(scope.orgId, req.params.courseId);\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/courses/:courseId/activities',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'listActivities',\n tags,\n summary: \"List a course's activities\",\n params: CourseIdPathParam,\n response: { 200: ActivityList },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return content.listCourseActivities(scope.orgId, req.params.courseId);\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/courses/:courseId/activity-assets',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'listActivityAssets',\n tags,\n summary: \"List a course's activity→asset links\",\n params: CourseIdPathParam,\n response: { 200: ActivityAssetList },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return content.listCourseActivityAssets(scope.orgId, req.params.courseId);\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/courses/:courseId/modules',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'createModule',\n tags,\n summary: 'Add a module',\n params: CourseIdPathParam,\n body: CreateModule,\n response: { 200: ModuleList },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return content.createModule(scope.orgId, req.params.courseId, req.body.title);\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/courses/:courseId/modules/reorder',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'reorderModules',\n tags,\n summary: 'Reorder modules',\n params: CourseIdPathParam,\n body: ReorderInput,\n response: { 200: ModuleList },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return content.reorderModules(scope.orgId, req.params.courseId, req.body.orderedIds);\n },\n });\n\n r.route({\n method: 'PATCH',\n url: '/api/courses/:courseId/modules/:moduleId',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'updateModule',\n tags,\n summary: 'Rename a module',\n params: ModulePathParam,\n body: UpdateModule,\n response: { 200: ModuleList },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return content.updateModule(\n scope.orgId,\n req.params.courseId,\n req.params.moduleId,\n req.body.title,\n );\n },\n });\n\n r.route({\n method: 'DELETE',\n url: '/api/courses/:courseId/modules/:moduleId',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'deleteModule',\n tags,\n summary: 'Delete a module',\n params: ModulePathParam,\n response: { 200: ModuleList },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return content.deleteModule(scope.orgId, req.params.courseId, req.params.moduleId);\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/courses/:courseId/modules/:moduleId/activities/reorder',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'reorderActivities',\n tags,\n summary: 'Reorder activities in a module',\n params: ModulePathParam,\n body: ReorderInput,\n response: { 200: ModuleList },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return content.reorderActivities(\n scope.orgId,\n req.params.courseId,\n req.params.moduleId,\n req.body.orderedIds,\n );\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/courses/:courseId/modules/:moduleId/activities',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'createActivity',\n tags,\n summary: 'Add an activity',\n params: ModulePathParam,\n body: SaveActivity,\n response: { 200: ModuleList },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return content.saveActivity(scope.orgId, req.params.courseId, req.params.moduleId, req.body);\n },\n });\n\n r.route({\n method: 'PATCH',\n url: '/api/courses/:courseId/modules/:moduleId/activities/:activityId',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'updateActivity',\n tags,\n summary: 'Update an activity',\n params: ActivityPathParam,\n body: SaveActivity,\n response: { 200: ModuleList },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return content.saveActivity(\n scope.orgId,\n req.params.courseId,\n req.params.moduleId,\n req.body,\n req.params.activityId,\n );\n },\n });\n\n r.route({\n method: 'DELETE',\n url: '/api/courses/:courseId/modules/:moduleId/activities/:activityId',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'deleteActivity',\n tags,\n summary: 'Delete an activity',\n params: ActivityPathParam,\n response: { 200: ModuleList },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return content.deleteActivity(\n scope.orgId,\n req.params.courseId,\n req.params.moduleId,\n req.params.activityId,\n );\n },\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"activities.js","names":[],"sources":["../../../src/http/routes/activities.ts"],"sourcesContent":["// HTTP routes for course modules + activities. Write operations return the\n// course's full, reordered module list (how the editor re-renders). Nested under\n// courses. All require a session with an active organization; handlers resolve\n// the session's active org to the domain org id and scope every call by it.\nimport type { FastifyInstance } from 'fastify';\nimport type { ZodTypeProvider } from 'fastify-type-provider-zod';\nimport {\n ActivityAssetList,\n ActivityList,\n ActivityPathParam,\n CourseIdPathParam,\n CreateModule,\n ModuleList,\n ModulePathParam,\n ReorderInput,\n SaveActivity,\n UpdateModule,\n refusals,\n} from '../schemas/index.js';\nimport type { Container } from '../../app/container.js';\nimport { resolveScope } from '../scope.js';\n\nexport async function activitiesRoutes(app: FastifyInstance, container: Container): Promise<void> {\n const r = app.withTypeProvider<ZodTypeProvider>();\n // Modules/activities are part of the content aggregate; grouped under the Courses tag.\n const content = container.content;\n const tags = ['Content'];\n\n r.route({\n method: 'GET',\n url: '/api/courses/:courseId/modules',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'listModules',\n tags,\n summary: \"List a course's modules (staff)\",\n description:\n 'Staff only (owner, admin, instructor). Modules in order, each with its activities.',\n params: CourseIdPathParam,\n response: { 200: ModuleList, ...refusals(403) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return content.listCourseModules(scope.orgId, req.params.courseId);\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/courses/:courseId/activities',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'listActivities',\n tags,\n summary: \"List a course's activities (staff)\",\n description: 'Staff only (owner, admin, instructor). Every activity, published or not.',\n params: CourseIdPathParam,\n response: { 200: ActivityList, ...refusals(403) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return content.listCourseActivityViews(scope.orgId, req.params.courseId);\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/courses/:courseId/activity-assets',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'listActivityAssets',\n tags,\n summary: 'List the library files each activity of a course uses (staff)',\n description:\n 'Staff only (owner, admin, instructor). One row per activity and file, as recorded when the activity was saved.',\n params: CourseIdPathParam,\n response: { 200: ActivityAssetList, ...refusals(403) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return content.listCourseActivityAssets(scope.orgId, req.params.courseId);\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/courses/:courseId/modules',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'createModule',\n tags,\n summary: 'Add a module (staff)',\n description:\n \"Staff only (owner, admin, instructor). Added last. Returns the course's modules.\",\n params: CourseIdPathParam,\n body: CreateModule,\n response: { 200: ModuleList, ...refusals(403) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return content.createModule(scope.orgId, req.params.courseId, req.body.title);\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/courses/:courseId/modules/reorder',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'reorderModules',\n tags,\n summary: \"Reorder a course's modules (staff)\",\n description:\n \"Staff only (owner, admin, instructor). Ids that are not modules of the course are ignored. Returns the course's modules.\",\n params: CourseIdPathParam,\n body: ReorderInput,\n response: { 200: ModuleList, ...refusals(403) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return content.reorderModules(scope.orgId, req.params.courseId, req.body.orderedIds);\n },\n });\n\n r.route({\n method: 'PATCH',\n url: '/api/courses/:courseId/modules/:moduleId',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'updateModule',\n tags,\n summary: 'Rename a module (staff)',\n description: \"Staff only (owner, admin, instructor). Returns the course's modules.\",\n params: ModulePathParam,\n body: UpdateModule,\n response: { 200: ModuleList, ...refusals(403, 404) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return content.updateModule(\n scope.orgId,\n req.params.courseId,\n req.params.moduleId,\n req.body.title,\n );\n },\n });\n\n r.route({\n method: 'DELETE',\n url: '/api/courses/:courseId/modules/:moduleId',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'deleteModule',\n tags,\n summary: 'Delete a module (staff)',\n description: \"Staff only (owner, admin, instructor). Returns the course's modules.\",\n params: ModulePathParam,\n response: { 200: ModuleList, ...refusals(403, 404) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return content.deleteModule(scope.orgId, req.params.courseId, req.params.moduleId);\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/courses/:courseId/modules/:moduleId/activities/reorder',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'reorderActivities',\n tags,\n summary: 'Reorder the activities in a module (staff)',\n description:\n \"Staff only (owner, admin, instructor). Send every activity id of the module once (409 otherwise). Returns the course's modules.\",\n params: ModulePathParam,\n body: ReorderInput,\n response: { 200: ModuleList, ...refusals(403, 404, 409) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return content.reorderActivities(\n scope.orgId,\n req.params.courseId,\n req.params.moduleId,\n req.body.orderedIds,\n );\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/courses/:courseId/modules/:moduleId/activities',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'createActivity',\n tags,\n summary: 'Add an activity (staff)',\n description:\n \"Staff only (owner, admin, instructor). Records the library files the activity uses; each must be a ready library file (409 otherwise). Returns the course's modules.\",\n params: ModulePathParam,\n body: SaveActivity,\n response: { 200: ModuleList, ...refusals(403, 404, 409) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return content.saveActivity(scope.orgId, req.params.courseId, req.params.moduleId, req.body);\n },\n });\n\n r.route({\n method: 'PATCH',\n url: '/api/courses/:courseId/modules/:moduleId/activities/:activityId',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'updateActivity',\n tags,\n summary: 'Update an activity (staff)',\n description:\n \"Staff only (owner, admin, instructor). Replaces its content and records the library files it uses; each must be a ready library file (409 otherwise). Returns the course's modules.\",\n params: ActivityPathParam,\n body: SaveActivity,\n response: { 200: ModuleList, ...refusals(403, 404, 409) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return content.saveActivity(\n scope.orgId,\n req.params.courseId,\n req.params.moduleId,\n req.body,\n req.params.activityId,\n );\n },\n });\n\n r.route({\n method: 'DELETE',\n url: '/api/courses/:courseId/modules/:moduleId/activities/:activityId',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'deleteActivity',\n tags,\n summary: 'Delete an activity (staff)',\n description: \"Staff only (owner, admin, instructor). Returns the course's modules.\",\n params: ActivityPathParam,\n response: { 200: ModuleList, ...refusals(403, 404) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return content.deleteActivity(\n scope.orgId,\n req.params.courseId,\n req.params.moduleId,\n req.params.activityId,\n );\n },\n });\n}\n"],"mappings":";;;;;AAsBA,eAAsB,iBAAiB,KAAsB,WAAqC;CAChG,MAAM,IAAI,IAAI,iBAAkC;CAEhD,MAAM,UAAU,UAAU;CAC1B,MAAM,OAAO,CAAC,SAAS;CAEvB,EAAE,MAAM;EACN,QAAQ;EACR,KAAK;EACL,YAAY,IAAI;EAChB,QAAQ;GACN,aAAa;GACb;GACA,SAAS;GACT,aACE;GACF,QAAQ;GACR,UAAU;IAAE,KAAK;IAAY,GAAG,SAAS,GAAG;GAAE;EAChD;EACA,SAAS,OAAO,QAAQ;GACtB,MAAM,QAAQ,MAAM,aAAa,WAAW,GAAG;GAC/C,OAAO,QAAQ,kBAAkB,MAAM,OAAO,IAAI,OAAO,QAAQ;EACnE;CACF,CAAC;CAED,EAAE,MAAM;EACN,QAAQ;EACR,KAAK;EACL,YAAY,IAAI;EAChB,QAAQ;GACN,aAAa;GACb;GACA,SAAS;GACT,aAAa;GACb,QAAQ;GACR,UAAU;IAAE,KAAK;IAAc,GAAG,SAAS,GAAG;GAAE;EAClD;EACA,SAAS,OAAO,QAAQ;GACtB,MAAM,QAAQ,MAAM,aAAa,WAAW,GAAG;GAC/C,OAAO,QAAQ,wBAAwB,MAAM,OAAO,IAAI,OAAO,QAAQ;EACzE;CACF,CAAC;CAED,EAAE,MAAM;EACN,QAAQ;EACR,KAAK;EACL,YAAY,IAAI;EAChB,QAAQ;GACN,aAAa;GACb;GACA,SAAS;GACT,aACE;GACF,QAAQ;GACR,UAAU;IAAE,KAAK;IAAmB,GAAG,SAAS,GAAG;GAAE;EACvD;EACA,SAAS,OAAO,QAAQ;GACtB,MAAM,QAAQ,MAAM,aAAa,WAAW,GAAG;GAC/C,OAAO,QAAQ,yBAAyB,MAAM,OAAO,IAAI,OAAO,QAAQ;EAC1E;CACF,CAAC;CAED,EAAE,MAAM;EACN,QAAQ;EACR,KAAK;EACL,YAAY,IAAI;EAChB,QAAQ;GACN,aAAa;GACb;GACA,SAAS;GACT,aACE;GACF,QAAQ;GACR,MAAM;GACN,UAAU;IAAE,KAAK;IAAY,GAAG,SAAS,GAAG;GAAE;EAChD;EACA,SAAS,OAAO,QAAQ;GACtB,MAAM,QAAQ,MAAM,aAAa,WAAW,GAAG;GAC/C,OAAO,QAAQ,aAAa,MAAM,OAAO,IAAI,OAAO,UAAU,IAAI,KAAK,KAAK;EAC9E;CACF,CAAC;CAED,EAAE,MAAM;EACN,QAAQ;EACR,KAAK;EACL,YAAY,IAAI;EAChB,QAAQ;GACN,aAAa;GACb;GACA,SAAS;GACT,aACE;GACF,QAAQ;GACR,MAAM;GACN,UAAU;IAAE,KAAK;IAAY,GAAG,SAAS,GAAG;GAAE;EAChD;EACA,SAAS,OAAO,QAAQ;GACtB,MAAM,QAAQ,MAAM,aAAa,WAAW,GAAG;GAC/C,OAAO,QAAQ,eAAe,MAAM,OAAO,IAAI,OAAO,UAAU,IAAI,KAAK,UAAU;EACrF;CACF,CAAC;CAED,EAAE,MAAM;EACN,QAAQ;EACR,KAAK;EACL,YAAY,IAAI;EAChB,QAAQ;GACN,aAAa;GACb;GACA,SAAS;GACT,aAAa;GACb,QAAQ;GACR,MAAM;GACN,UAAU;IAAE,KAAK;IAAY,GAAG,SAAS,KAAK,GAAG;GAAE;EACrD;EACA,SAAS,OAAO,QAAQ;GACtB,MAAM,QAAQ,MAAM,aAAa,WAAW,GAAG;GAC/C,OAAO,QAAQ,aACb,MAAM,OACN,IAAI,OAAO,UACX,IAAI,OAAO,UACX,IAAI,KAAK,KACX;EACF;CACF,CAAC;CAED,EAAE,MAAM;EACN,QAAQ;EACR,KAAK;EACL,YAAY,IAAI;EAChB,QAAQ;GACN,aAAa;GACb;GACA,SAAS;GACT,aAAa;GACb,QAAQ;GACR,UAAU;IAAE,KAAK;IAAY,GAAG,SAAS,KAAK,GAAG;GAAE;EACrD;EACA,SAAS,OAAO,QAAQ;GACtB,MAAM,QAAQ,MAAM,aAAa,WAAW,GAAG;GAC/C,OAAO,QAAQ,aAAa,MAAM,OAAO,IAAI,OAAO,UAAU,IAAI,OAAO,QAAQ;EACnF;CACF,CAAC;CAED,EAAE,MAAM;EACN,QAAQ;EACR,KAAK;EACL,YAAY,IAAI;EAChB,QAAQ;GACN,aAAa;GACb;GACA,SAAS;GACT,aACE;GACF,QAAQ;GACR,MAAM;GACN,UAAU;IAAE,KAAK;IAAY,GAAG,SAAS,KAAK,KAAK,GAAG;GAAE;EAC1D;EACA,SAAS,OAAO,QAAQ;GACtB,MAAM,QAAQ,MAAM,aAAa,WAAW,GAAG;GAC/C,OAAO,QAAQ,kBACb,MAAM,OACN,IAAI,OAAO,UACX,IAAI,OAAO,UACX,IAAI,KAAK,UACX;EACF;CACF,CAAC;CAED,EAAE,MAAM;EACN,QAAQ;EACR,KAAK;EACL,YAAY,IAAI;EAChB,QAAQ;GACN,aAAa;GACb;GACA,SAAS;GACT,aACE;GACF,QAAQ;GACR,MAAM;GACN,UAAU;IAAE,KAAK;IAAY,GAAG,SAAS,KAAK,KAAK,GAAG;GAAE;EAC1D;EACA,SAAS,OAAO,QAAQ;GACtB,MAAM,QAAQ,MAAM,aAAa,WAAW,GAAG;GAC/C,OAAO,QAAQ,aAAa,MAAM,OAAO,IAAI,OAAO,UAAU,IAAI,OAAO,UAAU,IAAI,IAAI;EAC7F;CACF,CAAC;CAED,EAAE,MAAM;EACN,QAAQ;EACR,KAAK;EACL,YAAY,IAAI;EAChB,QAAQ;GACN,aAAa;GACb;GACA,SAAS;GACT,aACE;GACF,QAAQ;GACR,MAAM;GACN,UAAU;IAAE,KAAK;IAAY,GAAG,SAAS,KAAK,KAAK,GAAG;GAAE;EAC1D;EACA,SAAS,OAAO,QAAQ;GACtB,MAAM,QAAQ,MAAM,aAAa,WAAW,GAAG;GAC/C,OAAO,QAAQ,aACb,MAAM,OACN,IAAI,OAAO,UACX,IAAI,OAAO,UACX,IAAI,MACJ,IAAI,OAAO,UACb;EACF;CACF,CAAC;CAED,EAAE,MAAM;EACN,QAAQ;EACR,KAAK;EACL,YAAY,IAAI;EAChB,QAAQ;GACN,aAAa;GACb;GACA,SAAS;GACT,aAAa;GACb,QAAQ;GACR,UAAU;IAAE,KAAK;IAAY,GAAG,SAAS,KAAK,GAAG;GAAE;EACrD;EACA,SAAS,OAAO,QAAQ;GACtB,MAAM,QAAQ,MAAM,aAAa,WAAW,GAAG;GAC/C,OAAO,QAAQ,eACb,MAAM,OACN,IAAI,OAAO,UACX,IAAI,OAAO,UACX,IAAI,OAAO,UACb;EACF;CACF,CAAC;AACH"}
|
|
@@ -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,55 +116,58 @@ async function assetsRoutes(app, container) {
|
|
|
114
116
|
}
|
|
115
117
|
});
|
|
116
118
|
r.route({
|
|
117
|
-
method: "
|
|
118
|
-
url: "/api/assets/:id
|
|
119
|
-
preHandler:
|
|
120
|
-
preValidation: (req, _reply, done) => {
|
|
121
|
-
req.body ??= {};
|
|
122
|
-
done();
|
|
123
|
-
},
|
|
119
|
+
method: "DELETE",
|
|
120
|
+
url: "/api/assets/:id",
|
|
121
|
+
preHandler: staff,
|
|
124
122
|
schema: {
|
|
125
|
-
operationId: "
|
|
123
|
+
operationId: "deleteAsset",
|
|
126
124
|
tags,
|
|
127
|
-
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.",
|
|
128
127
|
params: AssetIdParam,
|
|
129
|
-
body: RequestDownload,
|
|
130
128
|
response: {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
401: ErrorBody,
|
|
134
|
-
404: ErrorBody
|
|
129
|
+
204: z.void(),
|
|
130
|
+
...refusals(403, 404, 409)
|
|
135
131
|
}
|
|
136
132
|
},
|
|
137
133
|
handler: async (req, reply) => {
|
|
138
134
|
const orgId = await resolveOrgId(req, reply);
|
|
139
135
|
if (!orgId) return;
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
return ticket;
|
|
136
|
+
if (!await assets.remove(orgId, req.params.id)) throw new NotFoundError("Asset", req.params.id);
|
|
137
|
+
return reply.code(204).send();
|
|
143
138
|
}
|
|
144
139
|
});
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
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,
|
|
149
154
|
schema: {
|
|
150
|
-
operationId
|
|
155
|
+
operationId,
|
|
151
156
|
tags,
|
|
152
|
-
summary
|
|
157
|
+
summary,
|
|
158
|
+
description: "Staff only (owner, admin, instructor). Ready library files of the organization; member uploads and pending files answer 404.",
|
|
153
159
|
params: AssetIdParam,
|
|
154
160
|
response: {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
401: ErrorBody,
|
|
158
|
-
404: ErrorBody
|
|
161
|
+
302: z.void(),
|
|
162
|
+
...refusals(403, 404)
|
|
159
163
|
}
|
|
160
164
|
},
|
|
161
165
|
handler: async (req, reply) => {
|
|
162
166
|
const orgId = await resolveOrgId(req, reply);
|
|
163
167
|
if (!orgId) return;
|
|
164
|
-
|
|
165
|
-
|
|
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);
|
|
166
171
|
}
|
|
167
172
|
});
|
|
168
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 { ErrorBody } from "../schemas/shared.js";
|
|
3
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,9 +16,13 @@ async function automationsRoutes(app, container) {
|
|
|
16
16
|
schema: {
|
|
17
17
|
operationId: "listAutomations",
|
|
18
18
|
tags,
|
|
19
|
-
summary: "List automations
|
|
19
|
+
summary: "List automations (staff)",
|
|
20
|
+
description: "Staff only (owner, admin, instructor). Optionally only workflows or only webhooks.",
|
|
20
21
|
querystring: AutomationsQuery,
|
|
21
|
-
response: {
|
|
22
|
+
response: {
|
|
23
|
+
200: z.array(Automation),
|
|
24
|
+
...refusals(403)
|
|
25
|
+
}
|
|
22
26
|
},
|
|
23
27
|
handler: async (req) => {
|
|
24
28
|
const scope = await resolveScope(container, req);
|
|
@@ -32,8 +36,12 @@ async function automationsRoutes(app, container) {
|
|
|
32
36
|
schema: {
|
|
33
37
|
operationId: "listAutomationActions",
|
|
34
38
|
tags,
|
|
35
|
-
summary: "List the actions automations can use",
|
|
36
|
-
|
|
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
|
+
}
|
|
37
45
|
},
|
|
38
46
|
handler: async (req) => {
|
|
39
47
|
await resolveScope(container, req);
|
|
@@ -47,8 +55,12 @@ async function automationsRoutes(app, container) {
|
|
|
47
55
|
schema: {
|
|
48
56
|
operationId: "listAutomationTriggers",
|
|
49
57
|
tags,
|
|
50
|
-
summary: "List the domain events automations can react to",
|
|
51
|
-
|
|
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
|
+
}
|
|
52
64
|
},
|
|
53
65
|
handler: async (req) => {
|
|
54
66
|
await resolveScope(container, req);
|
|
@@ -62,9 +74,13 @@ async function automationsRoutes(app, container) {
|
|
|
62
74
|
schema: {
|
|
63
75
|
operationId: "createAutomation",
|
|
64
76
|
tags,
|
|
65
|
-
summary: "Create a workflow automation",
|
|
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.",
|
|
66
79
|
body: CreateAutomationBody,
|
|
67
|
-
response: {
|
|
80
|
+
response: {
|
|
81
|
+
201: Automation,
|
|
82
|
+
...refusals(400, 403)
|
|
83
|
+
}
|
|
68
84
|
},
|
|
69
85
|
handler: async (req, reply) => {
|
|
70
86
|
const scope = await resolveScope(container, req);
|
|
@@ -79,11 +95,12 @@ async function automationsRoutes(app, container) {
|
|
|
79
95
|
schema: {
|
|
80
96
|
operationId: "getAutomation",
|
|
81
97
|
tags,
|
|
82
|
-
summary: "Get an automation
|
|
98
|
+
summary: "Get an automation (staff)",
|
|
99
|
+
description: "Staff only (owner, admin, instructor). Workflows and webhooks alike.",
|
|
83
100
|
params: AutomationIdParam,
|
|
84
101
|
response: {
|
|
85
102
|
200: Automation,
|
|
86
|
-
404
|
|
103
|
+
...refusals(403, 404)
|
|
87
104
|
}
|
|
88
105
|
},
|
|
89
106
|
handler: async (req) => {
|
|
@@ -100,13 +117,13 @@ async function automationsRoutes(app, container) {
|
|
|
100
117
|
schema: {
|
|
101
118
|
operationId: "updateAutomation",
|
|
102
119
|
tags,
|
|
103
|
-
summary: "Update a workflow automation (
|
|
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.",
|
|
104
122
|
params: AutomationIdParam,
|
|
105
123
|
body: UpdateAutomationBody,
|
|
106
124
|
response: {
|
|
107
125
|
200: Automation,
|
|
108
|
-
404
|
|
109
|
-
409: ErrorBody
|
|
126
|
+
...refusals(400, 403, 404, 409)
|
|
110
127
|
}
|
|
111
128
|
},
|
|
112
129
|
handler: async (req) => {
|
|
@@ -123,12 +140,12 @@ async function automationsRoutes(app, container) {
|
|
|
123
140
|
schema: {
|
|
124
141
|
operationId: "deleteAutomation",
|
|
125
142
|
tags,
|
|
126
|
-
summary: "Delete a workflow automation (
|
|
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.",
|
|
127
145
|
params: AutomationIdParam,
|
|
128
146
|
response: {
|
|
129
147
|
204: z.void(),
|
|
130
|
-
404
|
|
131
|
-
409: ErrorBody
|
|
148
|
+
...refusals(403, 404, 409)
|
|
132
149
|
}
|
|
133
150
|
},
|
|
134
151
|
handler: async (req, reply) => {
|
|
@@ -144,10 +161,14 @@ async function automationsRoutes(app, container) {
|
|
|
144
161
|
schema: {
|
|
145
162
|
operationId: "listAutomationRuns",
|
|
146
163
|
tags,
|
|
147
|
-
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.",
|
|
148
166
|
params: AutomationIdParam,
|
|
149
167
|
querystring: AutomationRunsQuery,
|
|
150
|
-
response: {
|
|
168
|
+
response: {
|
|
169
|
+
200: AutomationRunsPage,
|
|
170
|
+
...refusals(403)
|
|
171
|
+
}
|
|
151
172
|
},
|
|
152
173
|
handler: async (req) => {
|
|
153
174
|
const scope = await resolveScope(container, req);
|
|
@@ -161,13 +182,12 @@ async function automationsRoutes(app, container) {
|
|
|
161
182
|
schema: {
|
|
162
183
|
operationId: "rerunAutomationRun",
|
|
163
184
|
tags,
|
|
164
|
-
summary: "Run a finished run again",
|
|
165
|
-
description: "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).",
|
|
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).",
|
|
166
187
|
params: AutomationRunParams,
|
|
167
188
|
response: {
|
|
168
189
|
200: AutomationRun,
|
|
169
|
-
404
|
|
170
|
-
409: ErrorBody
|
|
190
|
+
...refusals(403, 404, 409)
|
|
171
191
|
}
|
|
172
192
|
},
|
|
173
193
|
handler: async (req) => {
|
|
@@ -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 AutomationRun,\n AutomationRunParams,\n AutomationRunsPage,\n AutomationRunsQuery,\n AutomationsQuery,\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, optionally only workflows or only webhooks',\n querystring: AutomationsQuery,\n response: { 200: z.array(Automation) },\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',\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 a workflow 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 a workflow automation (webhooks change through the webhook endpoints)',\n params: AutomationIdParam,\n body: UpdateAutomationBody,\n response: { 200: Automation, 404: ErrorBody, 409: 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 a workflow automation (webhooks are deleted through the webhook endpoints)',\n params: AutomationIdParam,\n response: { 204: z.void(), 404: ErrorBody, 409: 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:\n \"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 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',\n description:\n \"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, 404: ErrorBody, 409: ErrorBody },\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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"}
|
|
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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"}
|