@headless-lms/server 0.2.0 → 0.4.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 +24 -0
- package/dist/app/community-mail.js +92 -0
- package/dist/app/community-mail.js.map +1 -0
- package/dist/app/container.d.ts +40 -19
- package/dist/app/container.js +294 -45
- 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/app/notification-channels.d.ts +47 -0
- package/dist/app/notification-channels.js +174 -0
- package/dist/app/notification-channels.js.map +1 -0
- package/dist/app/notification-reads.d.ts +23 -0
- package/dist/app/notification-reads.js +95 -0
- package/dist/app/notification-reads.js.map +1 -0
- package/dist/http/config.d.ts +2 -1
- package/dist/http/errors.d.ts +15 -0
- package/dist/http/errors.js +162 -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 +51 -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 +522 -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 +435 -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 +312 -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/devices.d.ts +7 -0
- package/dist/http/routes/member/devices.js +98 -0
- package/dist/http/routes/member/devices.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 +35 -0
- package/dist/http/routes/member/index.js.map +1 -0
- package/dist/http/routes/member/notification-settings.d.ts +7 -0
- package/dist/http/routes/member/notification-settings.js +108 -0
- package/dist/http/routes/member/notification-settings.js.map +1 -0
- package/dist/http/routes/member/notifications.d.ts +7 -0
- package/dist/http/routes/member/notifications.js +125 -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 +171 -25
- 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 +79 -80
- package/dist/http/schemas/assets.js +23 -13
- package/dist/http/schemas/assets.js.map +1 -1
- package/dist/http/schemas/automations.d.ts +6 -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 +409 -0
- package/dist/http/schemas/chats.js +56 -0
- package/dist/http/schemas/chats.js.map +1 -0
- package/dist/http/schemas/community.d.ts +597 -0
- package/dist/http/schemas/community.js +132 -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 +98 -14
- package/dist/http/schemas/discussion.js +25 -12
- 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 +4 -4
- 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 +35 -2
- package/dist/http/schemas/members.js +17 -3
- package/dist/http/schemas/members.js.map +1 -1
- package/dist/http/schemas/notifications.d.ts +550 -0
- package/dist/http/schemas/notifications.js +164 -0
- package/dist/http/schemas/notifications.js.map +1 -0
- package/dist/http/schemas/posts.d.ts +350 -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 +32 -1
- package/dist/http/schemas/shared.js +20 -2
- package/dist/http/schemas/shared.js.map +1 -1
- package/dist/http/schemas/students.d.ts +1 -7
- package/dist/http/schemas/students.js +1 -7
- package/dist/http/schemas/students.js.map +1 -1
- package/dist/http/scope.d.ts +3 -1
- package/dist/http/scope.js +26 -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 +3 -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
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
import { PageQuery, refusals } from "../schemas/shared.js";
|
|
2
|
+
import { resolveScope } from "../scope.js";
|
|
3
|
+
import { CommentView, ResolveCommentFlags } from "../schemas/discussion.js";
|
|
4
|
+
import { CommunityChatParam, CommunityCommentParam, CommunityIdParam, CommunityMembersPage, CommunityMembersQuery, CommunityMessageParam, CommunityPostParam, CommunitySpaceParam, SpaceMember, SpaceMemberParam, SpaceMembersPage } from "../schemas/community.js";
|
|
5
|
+
import { PostView, ResolvePostFlags, SpaceQueue } from "../schemas/posts.js";
|
|
6
|
+
import { Chat, OrgUserBody, ResolveMessageFlags } from "../schemas/chats.js";
|
|
7
|
+
import "../schemas/index.js";
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
import { NotFoundError } from "@headless-lms/core/shared/errors";
|
|
10
|
+
//#region src/http/routes/community-moderation.ts
|
|
11
|
+
async function communityModerationRoutes(app, container) {
|
|
12
|
+
const r = app.withTypeProvider();
|
|
13
|
+
const tags = ["Communities"];
|
|
14
|
+
r.route({
|
|
15
|
+
method: "GET",
|
|
16
|
+
url: "/api/communities/:communityId/members",
|
|
17
|
+
schema: {
|
|
18
|
+
operationId: "listCommunityMembers",
|
|
19
|
+
tags,
|
|
20
|
+
summary: "List the people who hold a community (staff)",
|
|
21
|
+
description: "Staff only (owner, admin, instructor). Everyone with active access to the community, granted directly or through a bundle, paged and searched by name or email.",
|
|
22
|
+
params: CommunityIdParam,
|
|
23
|
+
querystring: CommunityMembersQuery,
|
|
24
|
+
response: {
|
|
25
|
+
200: CommunityMembersPage,
|
|
26
|
+
...refusals(403, 404)
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
handler: async (req) => {
|
|
30
|
+
const scope = await resolveScope(container, req);
|
|
31
|
+
const page = await container.reporting.admin.communityMembers(scope.orgId, req.params.communityId, req.query);
|
|
32
|
+
if (!page) throw new NotFoundError("Community", req.params.communityId);
|
|
33
|
+
return page;
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
r.route({
|
|
37
|
+
method: "GET",
|
|
38
|
+
url: "/api/communities/:communityId/spaces/:spaceId/members",
|
|
39
|
+
schema: {
|
|
40
|
+
operationId: "listCommunitySpaceMembers",
|
|
41
|
+
tags,
|
|
42
|
+
summary: "List the members, requests and invitations of a space (staff)",
|
|
43
|
+
description: "Staff only (owner, admin, instructor). The space's members, its open requests to join and its open invitations.",
|
|
44
|
+
params: CommunitySpaceParam,
|
|
45
|
+
querystring: PageQuery,
|
|
46
|
+
response: {
|
|
47
|
+
200: SpaceMembersPage,
|
|
48
|
+
...refusals(403, 404)
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
handler: async (req) => {
|
|
52
|
+
const scope = await resolveScope(container, req);
|
|
53
|
+
return container.entitlements.listSpaceMembers(scope.orgId, {
|
|
54
|
+
id: scope.orgUserId,
|
|
55
|
+
role: scope.role
|
|
56
|
+
}, req.params.communityId, req.params.spaceId, req.query);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
r.route({
|
|
60
|
+
method: "POST",
|
|
61
|
+
url: "/api/communities/:communityId/spaces/:spaceId/members",
|
|
62
|
+
schema: {
|
|
63
|
+
operationId: "addCommunitySpaceMember",
|
|
64
|
+
tags,
|
|
65
|
+
summary: "Add a person to a space (staff)",
|
|
66
|
+
description: "Staff only (owner, admin, instructor). Places a member of the community straight into the space, with no request or invitation. 409 when the space is open to all members, the person is already placed, or the space is full.",
|
|
67
|
+
params: CommunitySpaceParam,
|
|
68
|
+
body: OrgUserBody,
|
|
69
|
+
response: {
|
|
70
|
+
201: SpaceMember,
|
|
71
|
+
...refusals(403, 404, 409)
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
handler: async (req, reply) => {
|
|
75
|
+
const scope = await resolveScope(container, req);
|
|
76
|
+
const member = await container.entitlements.addSpaceMember(scope.orgId, {
|
|
77
|
+
id: scope.orgUserId,
|
|
78
|
+
role: scope.role
|
|
79
|
+
}, req.params.communityId, req.params.spaceId, req.body.orgUserId);
|
|
80
|
+
return reply.code(201).send(member);
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
r.route({
|
|
84
|
+
method: "POST",
|
|
85
|
+
url: "/api/communities/:communityId/spaces/:spaceId/invites",
|
|
86
|
+
schema: {
|
|
87
|
+
operationId: "inviteCommunitySpaceMember",
|
|
88
|
+
tags,
|
|
89
|
+
summary: "Invite a person into a space (staff)",
|
|
90
|
+
description: "Staff only (owner, admin, instructor). Invites a member of the community into a `by_invite` space; they join when they accept. 403 when the person is staff or does not hold the community; 409 when the space is not invite-only or the person is already placed.",
|
|
91
|
+
params: CommunitySpaceParam,
|
|
92
|
+
body: OrgUserBody,
|
|
93
|
+
response: {
|
|
94
|
+
201: SpaceMember,
|
|
95
|
+
...refusals(403, 404, 409)
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
handler: async (req, reply) => {
|
|
99
|
+
const scope = await resolveScope(container, req);
|
|
100
|
+
const member = await container.entitlements.invite(scope.orgId, {
|
|
101
|
+
id: scope.orgUserId,
|
|
102
|
+
role: scope.role
|
|
103
|
+
}, req.params.communityId, req.params.spaceId, req.body.orgUserId);
|
|
104
|
+
return reply.code(201).send(member);
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
r.route({
|
|
108
|
+
method: "POST",
|
|
109
|
+
url: "/api/communities/:communityId/spaces/:spaceId/members/:orgUserId/approval",
|
|
110
|
+
schema: {
|
|
111
|
+
operationId: "approveCommunitySpaceRequest",
|
|
112
|
+
tags,
|
|
113
|
+
summary: "Approve a person's request to join a space (staff)",
|
|
114
|
+
description: "Staff only (owner, admin, instructor). Makes the requester an active member. 404 when the person has no open request; 409 when the space is full or they are already a member.",
|
|
115
|
+
params: SpaceMemberParam,
|
|
116
|
+
response: {
|
|
117
|
+
200: SpaceMember,
|
|
118
|
+
...refusals(403, 404, 409)
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
handler: async (req) => {
|
|
122
|
+
const scope = await resolveScope(container, req);
|
|
123
|
+
return container.entitlements.approve(scope.orgId, {
|
|
124
|
+
id: scope.orgUserId,
|
|
125
|
+
role: scope.role
|
|
126
|
+
}, req.params.communityId, req.params.spaceId, req.params.orgUserId);
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
r.route({
|
|
130
|
+
method: "POST",
|
|
131
|
+
url: "/api/communities/:communityId/spaces/:spaceId/members/:orgUserId/decline",
|
|
132
|
+
schema: {
|
|
133
|
+
operationId: "declineCommunitySpaceRequest",
|
|
134
|
+
tags,
|
|
135
|
+
summary: "Decline a person's request to join a space (staff)",
|
|
136
|
+
description: "Staff only (owner, admin, instructor). Closes the open request. 404 when the person has no open request; 409 when they are already a member.",
|
|
137
|
+
params: SpaceMemberParam,
|
|
138
|
+
response: {
|
|
139
|
+
200: SpaceMember,
|
|
140
|
+
...refusals(403, 404, 409)
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
handler: async (req) => {
|
|
144
|
+
const scope = await resolveScope(container, req);
|
|
145
|
+
return container.entitlements.decline(scope.orgId, {
|
|
146
|
+
id: scope.orgUserId,
|
|
147
|
+
role: scope.role
|
|
148
|
+
}, req.params.communityId, req.params.spaceId, req.params.orgUserId);
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
r.route({
|
|
152
|
+
method: "POST",
|
|
153
|
+
url: "/api/communities/:communityId/spaces/:spaceId/members/:orgUserId/removal",
|
|
154
|
+
schema: {
|
|
155
|
+
operationId: "removeCommunitySpaceMember",
|
|
156
|
+
tags,
|
|
157
|
+
summary: "Remove a person from a space (staff)",
|
|
158
|
+
description: "Staff only (owner, admin, instructor). Revokes the placement, whether it is a membership, a request or an invitation. 409 when the space is open to all members or the placement is already revoked.",
|
|
159
|
+
params: SpaceMemberParam,
|
|
160
|
+
response: {
|
|
161
|
+
200: SpaceMember,
|
|
162
|
+
...refusals(403, 404, 409)
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
handler: async (req) => {
|
|
166
|
+
const scope = await resolveScope(container, req);
|
|
167
|
+
return container.entitlements.remove(scope.orgId, {
|
|
168
|
+
id: scope.orgUserId,
|
|
169
|
+
role: scope.role
|
|
170
|
+
}, req.params.communityId, req.params.spaceId, req.params.orgUserId);
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
r.route({
|
|
174
|
+
method: "GET",
|
|
175
|
+
url: "/api/communities/:communityId/spaces/:spaceId/queue",
|
|
176
|
+
schema: {
|
|
177
|
+
operationId: "getCommunitySpaceQueue",
|
|
178
|
+
tags,
|
|
179
|
+
summary: "The moderation queue of a space (staff)",
|
|
180
|
+
description: "Staff only (owner, admin, instructor). Posts awaiting review, and the open flags on posts and on comments in the space. Each list holds at most the 100 oldest entries: pending posts by creation, flags by when they were raised; working the queue brings in the next ones.",
|
|
181
|
+
params: CommunitySpaceParam,
|
|
182
|
+
response: {
|
|
183
|
+
200: SpaceQueue,
|
|
184
|
+
...refusals(403, 404)
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
handler: async (req) => {
|
|
188
|
+
const scope = await resolveScope(container, req);
|
|
189
|
+
return container.posts.moderationQueue(scope.orgId, {
|
|
190
|
+
id: scope.orgUserId,
|
|
191
|
+
role: scope.role
|
|
192
|
+
}, req.params.communityId, req.params.spaceId);
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
r.route({
|
|
196
|
+
method: "POST",
|
|
197
|
+
url: "/api/communities/:communityId/posts/:postId/approval",
|
|
198
|
+
schema: {
|
|
199
|
+
operationId: "approveCommunityPost",
|
|
200
|
+
tags,
|
|
201
|
+
summary: "Publish a post that is awaiting review (staff)",
|
|
202
|
+
description: "Staff only (owner, admin, instructor). 409 when the post is not awaiting review.",
|
|
203
|
+
params: CommunityPostParam,
|
|
204
|
+
response: {
|
|
205
|
+
200: PostView,
|
|
206
|
+
...refusals(403, 404, 409)
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
handler: async (req) => {
|
|
210
|
+
const scope = await resolveScope(container, req);
|
|
211
|
+
return container.posts.approve(scope.orgId, req.params.postId, {
|
|
212
|
+
id: scope.orgUserId,
|
|
213
|
+
role: scope.role
|
|
214
|
+
}, req.params.communityId);
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
r.route({
|
|
218
|
+
method: "POST",
|
|
219
|
+
url: "/api/communities/:communityId/posts/:postId/removal",
|
|
220
|
+
schema: {
|
|
221
|
+
operationId: "removeCommunityPost",
|
|
222
|
+
tags,
|
|
223
|
+
summary: "Remove a post (staff)",
|
|
224
|
+
description: "Staff only (owner, admin, instructor). Takes a published or pending post down and resolves its open flags as `removed`. 409 when it is already removed or was never submitted.",
|
|
225
|
+
params: CommunityPostParam,
|
|
226
|
+
response: {
|
|
227
|
+
200: PostView,
|
|
228
|
+
...refusals(403, 404, 409)
|
|
229
|
+
}
|
|
230
|
+
},
|
|
231
|
+
handler: async (req) => {
|
|
232
|
+
const scope = await resolveScope(container, req);
|
|
233
|
+
return container.posts.remove(scope.orgId, req.params.postId, {
|
|
234
|
+
id: scope.orgUserId,
|
|
235
|
+
role: scope.role
|
|
236
|
+
}, req.params.communityId);
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
r.route({
|
|
240
|
+
method: "POST",
|
|
241
|
+
url: "/api/communities/:communityId/posts/:postId/restoration",
|
|
242
|
+
schema: {
|
|
243
|
+
operationId: "restoreCommunityPost",
|
|
244
|
+
tags,
|
|
245
|
+
summary: "Restore a removed post (staff)",
|
|
246
|
+
description: "Staff only (owner, admin, instructor). 409 when the post is not removed.",
|
|
247
|
+
params: CommunityPostParam,
|
|
248
|
+
response: {
|
|
249
|
+
200: PostView,
|
|
250
|
+
...refusals(403, 404, 409)
|
|
251
|
+
}
|
|
252
|
+
},
|
|
253
|
+
handler: async (req) => {
|
|
254
|
+
const scope = await resolveScope(container, req);
|
|
255
|
+
return container.posts.restore(scope.orgId, req.params.postId, {
|
|
256
|
+
id: scope.orgUserId,
|
|
257
|
+
role: scope.role
|
|
258
|
+
}, req.params.communityId);
|
|
259
|
+
}
|
|
260
|
+
});
|
|
261
|
+
r.route({
|
|
262
|
+
method: "POST",
|
|
263
|
+
url: "/api/communities/:communityId/posts/:postId/flags/resolution",
|
|
264
|
+
schema: {
|
|
265
|
+
operationId: "resolveCommunityPostFlags",
|
|
266
|
+
tags,
|
|
267
|
+
summary: "Dismiss the open flags on a post (staff)",
|
|
268
|
+
description: "Staff only (owner, admin, instructor). Closes every open flag on the post and leaves it up.",
|
|
269
|
+
params: CommunityPostParam,
|
|
270
|
+
body: ResolvePostFlags,
|
|
271
|
+
response: {
|
|
272
|
+
204: z.void(),
|
|
273
|
+
...refusals(403, 404)
|
|
274
|
+
}
|
|
275
|
+
},
|
|
276
|
+
handler: async (req, reply) => {
|
|
277
|
+
const scope = await resolveScope(container, req);
|
|
278
|
+
await container.posts.resolveFlags(scope.orgId, req.params.postId, {
|
|
279
|
+
id: scope.orgUserId,
|
|
280
|
+
role: scope.role
|
|
281
|
+
}, req.body.resolution, req.params.communityId);
|
|
282
|
+
return reply.code(204).send();
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
r.route({
|
|
286
|
+
method: "POST",
|
|
287
|
+
url: "/api/communities/:communityId/comments/:commentId/removal",
|
|
288
|
+
schema: {
|
|
289
|
+
operationId: "removeCommunityComment",
|
|
290
|
+
tags,
|
|
291
|
+
summary: "Remove a comment on a post (staff)",
|
|
292
|
+
description: "Staff only (owner, admin, instructor). Hides the comment and resolves its open flags as `removed`. Removing a removed comment returns it unchanged.",
|
|
293
|
+
params: CommunityCommentParam,
|
|
294
|
+
response: {
|
|
295
|
+
200: CommentView,
|
|
296
|
+
...refusals(403, 404)
|
|
297
|
+
}
|
|
298
|
+
},
|
|
299
|
+
handler: async (req) => {
|
|
300
|
+
const scope = await resolveScope(container, req);
|
|
301
|
+
return container.discussion.remove(scope.orgId, req.params.commentId, {
|
|
302
|
+
id: scope.orgUserId,
|
|
303
|
+
role: scope.role
|
|
304
|
+
}, req.params.communityId);
|
|
305
|
+
}
|
|
306
|
+
});
|
|
307
|
+
r.route({
|
|
308
|
+
method: "POST",
|
|
309
|
+
url: "/api/communities/:communityId/comments/:commentId/restoration",
|
|
310
|
+
schema: {
|
|
311
|
+
operationId: "restoreCommunityComment",
|
|
312
|
+
tags,
|
|
313
|
+
summary: "Restore a removed comment on a post (staff)",
|
|
314
|
+
description: "Staff only (owner, admin, instructor). Publishes the comment again. Restoring a comment that is not removed returns it unchanged.",
|
|
315
|
+
params: CommunityCommentParam,
|
|
316
|
+
response: {
|
|
317
|
+
200: CommentView,
|
|
318
|
+
...refusals(403, 404)
|
|
319
|
+
}
|
|
320
|
+
},
|
|
321
|
+
handler: async (req) => {
|
|
322
|
+
const scope = await resolveScope(container, req);
|
|
323
|
+
return container.discussion.restore(scope.orgId, req.params.commentId, {
|
|
324
|
+
id: scope.orgUserId,
|
|
325
|
+
role: scope.role
|
|
326
|
+
}, req.params.communityId);
|
|
327
|
+
}
|
|
328
|
+
});
|
|
329
|
+
r.route({
|
|
330
|
+
method: "POST",
|
|
331
|
+
url: "/api/communities/:communityId/comments/:commentId/flags/resolution",
|
|
332
|
+
schema: {
|
|
333
|
+
operationId: "resolveCommunityCommentFlags",
|
|
334
|
+
tags,
|
|
335
|
+
summary: "Dismiss the open flags on a comment (staff)",
|
|
336
|
+
description: "Staff only (owner, admin, instructor). Closes every open flag on the comment and leaves it up.",
|
|
337
|
+
params: CommunityCommentParam,
|
|
338
|
+
body: ResolveCommentFlags,
|
|
339
|
+
response: {
|
|
340
|
+
204: z.void(),
|
|
341
|
+
...refusals(403, 404)
|
|
342
|
+
}
|
|
343
|
+
},
|
|
344
|
+
handler: async (req, reply) => {
|
|
345
|
+
const scope = await resolveScope(container, req);
|
|
346
|
+
await container.discussion.resolveReports(scope.orgId, req.params.commentId, {
|
|
347
|
+
id: scope.orgUserId,
|
|
348
|
+
role: scope.role
|
|
349
|
+
}, req.params.communityId);
|
|
350
|
+
return reply.code(204).send();
|
|
351
|
+
}
|
|
352
|
+
});
|
|
353
|
+
r.route({
|
|
354
|
+
method: "POST",
|
|
355
|
+
url: "/api/communities/:communityId/messages/:messageId/flags/resolution",
|
|
356
|
+
schema: {
|
|
357
|
+
operationId: "resolveCommunityMessageFlags",
|
|
358
|
+
tags,
|
|
359
|
+
summary: "Dismiss the open flags on a message (staff)",
|
|
360
|
+
description: "Staff only (owner, admin, instructor). Closes every open flag on the message and leaves it up.",
|
|
361
|
+
params: CommunityMessageParam,
|
|
362
|
+
body: ResolveMessageFlags,
|
|
363
|
+
response: {
|
|
364
|
+
204: z.void(),
|
|
365
|
+
...refusals(403, 404)
|
|
366
|
+
}
|
|
367
|
+
},
|
|
368
|
+
handler: async (req, reply) => {
|
|
369
|
+
const scope = await resolveScope(container, req);
|
|
370
|
+
await container.chats.resolveMessageFlags(scope.orgId, {
|
|
371
|
+
id: scope.orgUserId,
|
|
372
|
+
role: scope.role
|
|
373
|
+
}, req.params.messageId, req.body.resolution, req.params.communityId);
|
|
374
|
+
return reply.code(204).send();
|
|
375
|
+
}
|
|
376
|
+
});
|
|
377
|
+
r.route({
|
|
378
|
+
method: "POST",
|
|
379
|
+
url: "/api/communities/:communityId/chats/:chatId/lock",
|
|
380
|
+
schema: {
|
|
381
|
+
operationId: "lockCommunityChat",
|
|
382
|
+
tags,
|
|
383
|
+
summary: "Lock a chat (staff)",
|
|
384
|
+
description: "Staff only (owner, admin, instructor). Nobody can send or edit messages until it is unlocked; its open message flags are resolved as `locked`. Locking a locked chat returns it unchanged.",
|
|
385
|
+
params: CommunityChatParam,
|
|
386
|
+
response: {
|
|
387
|
+
200: Chat,
|
|
388
|
+
...refusals(403, 404)
|
|
389
|
+
}
|
|
390
|
+
},
|
|
391
|
+
handler: async (req) => {
|
|
392
|
+
const scope = await resolveScope(container, req);
|
|
393
|
+
return container.chats.lock(scope.orgId, {
|
|
394
|
+
id: scope.orgUserId,
|
|
395
|
+
role: scope.role
|
|
396
|
+
}, req.params.chatId, req.params.communityId);
|
|
397
|
+
}
|
|
398
|
+
});
|
|
399
|
+
r.route({
|
|
400
|
+
method: "POST",
|
|
401
|
+
url: "/api/communities/:communityId/chats/:chatId/unlock",
|
|
402
|
+
schema: {
|
|
403
|
+
operationId: "unlockCommunityChat",
|
|
404
|
+
tags,
|
|
405
|
+
summary: "Unlock a chat (staff)",
|
|
406
|
+
description: "Staff only (owner, admin, instructor). Lets participants send messages again. Unlocking an unlocked chat returns it unchanged.",
|
|
407
|
+
params: CommunityChatParam,
|
|
408
|
+
response: {
|
|
409
|
+
200: Chat,
|
|
410
|
+
...refusals(403, 404)
|
|
411
|
+
}
|
|
412
|
+
},
|
|
413
|
+
handler: async (req) => {
|
|
414
|
+
const scope = await resolveScope(container, req);
|
|
415
|
+
return container.chats.unlock(scope.orgId, {
|
|
416
|
+
id: scope.orgUserId,
|
|
417
|
+
role: scope.role
|
|
418
|
+
}, req.params.chatId, req.params.communityId);
|
|
419
|
+
}
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
//#endregion
|
|
423
|
+
export { communityModerationRoutes };
|
|
424
|
+
|
|
425
|
+
//# sourceMappingURL=community-moderation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"community-moderation.js","names":[],"sources":["../../../src/http/routes/community-moderation.ts"],"sourcesContent":["import type { FastifyInstance } from 'fastify';\nimport type { ZodTypeProvider } from 'fastify-type-provider-zod';\nimport { z } from 'zod';\nimport type { Container } from '../../app/container.js';\nimport { NotFoundError } from '@headless-lms/core/shared/errors';\nimport { resolveScope } from '../scope.js';\nimport {\n Chat,\n CommentView,\n CommunityChatParam,\n CommunityCommentParam,\n CommunityIdParam,\n CommunityMembersPage,\n CommunityMembersQuery,\n CommunityMessageParam,\n CommunityPostParam,\n CommunitySpaceParam,\n OrgUserBody,\n PageQuery,\n PostView,\n ResolveCommentFlags,\n ResolveMessageFlags,\n ResolvePostFlags,\n SpaceMember,\n SpaceMemberParam,\n SpaceMembersPage,\n SpaceQueue,\n refusals,\n} from '../schemas/index.js';\n\nexport async function communityModerationRoutes(\n app: FastifyInstance,\n container: Container,\n): Promise<void> {\n const r = app.withTypeProvider<ZodTypeProvider>();\n const tags = ['Communities'];\n\n r.route({\n method: 'GET',\n url: '/api/communities/:communityId/members',\n schema: {\n operationId: 'listCommunityMembers',\n tags,\n summary: 'List the people who hold a community (staff)',\n description:\n 'Staff only (owner, admin, instructor). Everyone with active access to the community, granted directly or through a bundle, paged and searched by name or email.',\n params: CommunityIdParam,\n querystring: CommunityMembersQuery,\n response: { 200: CommunityMembersPage, ...refusals(403, 404) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n const page = await container.reporting.admin.communityMembers(\n scope.orgId,\n req.params.communityId,\n req.query,\n );\n if (!page) {\n throw new NotFoundError('Community', req.params.communityId);\n }\n return page;\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/communities/:communityId/spaces/:spaceId/members',\n schema: {\n operationId: 'listCommunitySpaceMembers',\n tags,\n summary: 'List the members, requests and invitations of a space (staff)',\n description:\n \"Staff only (owner, admin, instructor). The space's members, its open requests to join and its open invitations.\",\n params: CommunitySpaceParam,\n querystring: PageQuery,\n response: { 200: SpaceMembersPage, ...refusals(403, 404) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return container.entitlements.listSpaceMembers(\n scope.orgId,\n { id: scope.orgUserId, role: scope.role },\n req.params.communityId,\n req.params.spaceId,\n req.query,\n );\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/communities/:communityId/spaces/:spaceId/members',\n schema: {\n operationId: 'addCommunitySpaceMember',\n tags,\n summary: 'Add a person to a space (staff)',\n description:\n 'Staff only (owner, admin, instructor). Places a member of the community straight into the space, with no request or invitation. 409 when the space is open to all members, the person is already placed, or the space is full.',\n params: CommunitySpaceParam,\n body: OrgUserBody,\n response: { 201: SpaceMember, ...refusals(403, 404, 409) },\n },\n handler: async (req, reply) => {\n const scope = await resolveScope(container, req);\n const member = await container.entitlements.addSpaceMember(\n scope.orgId,\n { id: scope.orgUserId, role: scope.role },\n req.params.communityId,\n req.params.spaceId,\n req.body.orgUserId,\n );\n return reply.code(201).send(member);\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/communities/:communityId/spaces/:spaceId/invites',\n schema: {\n operationId: 'inviteCommunitySpaceMember',\n tags,\n summary: 'Invite a person into a space (staff)',\n description:\n 'Staff only (owner, admin, instructor). Invites a member of the community into a `by_invite` space; they join when they accept. 403 when the person is staff or does not hold the community; 409 when the space is not invite-only or the person is already placed.',\n params: CommunitySpaceParam,\n body: OrgUserBody,\n response: { 201: SpaceMember, ...refusals(403, 404, 409) },\n },\n handler: async (req, reply) => {\n const scope = await resolveScope(container, req);\n const member = await container.entitlements.invite(\n scope.orgId,\n { id: scope.orgUserId, role: scope.role },\n req.params.communityId,\n req.params.spaceId,\n req.body.orgUserId,\n );\n return reply.code(201).send(member);\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/communities/:communityId/spaces/:spaceId/members/:orgUserId/approval',\n schema: {\n operationId: 'approveCommunitySpaceRequest',\n tags,\n summary: \"Approve a person's request to join a space (staff)\",\n description:\n 'Staff only (owner, admin, instructor). Makes the requester an active member. 404 when the person has no open request; 409 when the space is full or they are already a member.',\n params: SpaceMemberParam,\n response: { 200: SpaceMember, ...refusals(403, 404, 409) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return container.entitlements.approve(\n scope.orgId,\n { id: scope.orgUserId, role: scope.role },\n req.params.communityId,\n req.params.spaceId,\n req.params.orgUserId,\n );\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/communities/:communityId/spaces/:spaceId/members/:orgUserId/decline',\n schema: {\n operationId: 'declineCommunitySpaceRequest',\n tags,\n summary: \"Decline a person's request to join a space (staff)\",\n description:\n 'Staff only (owner, admin, instructor). Closes the open request. 404 when the person has no open request; 409 when they are already a member.',\n params: SpaceMemberParam,\n response: { 200: SpaceMember, ...refusals(403, 404, 409) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return container.entitlements.decline(\n scope.orgId,\n { id: scope.orgUserId, role: scope.role },\n req.params.communityId,\n req.params.spaceId,\n req.params.orgUserId,\n );\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/communities/:communityId/spaces/:spaceId/members/:orgUserId/removal',\n schema: {\n operationId: 'removeCommunitySpaceMember',\n tags,\n summary: 'Remove a person from a space (staff)',\n description:\n 'Staff only (owner, admin, instructor). Revokes the placement, whether it is a membership, a request or an invitation. 409 when the space is open to all members or the placement is already revoked.',\n params: SpaceMemberParam,\n response: { 200: SpaceMember, ...refusals(403, 404, 409) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return container.entitlements.remove(\n scope.orgId,\n { id: scope.orgUserId, role: scope.role },\n req.params.communityId,\n req.params.spaceId,\n req.params.orgUserId,\n );\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/communities/:communityId/spaces/:spaceId/queue',\n schema: {\n operationId: 'getCommunitySpaceQueue',\n tags,\n summary: 'The moderation queue of a space (staff)',\n description:\n 'Staff only (owner, admin, instructor). Posts awaiting review, and the open flags on posts and on comments in the space. Each list holds at most the 100 oldest entries: pending posts by creation, flags by when they were raised; working the queue brings in the next ones.',\n params: CommunitySpaceParam,\n response: { 200: SpaceQueue, ...refusals(403, 404) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return container.posts.moderationQueue(\n scope.orgId,\n { id: scope.orgUserId, role: scope.role },\n req.params.communityId,\n req.params.spaceId,\n );\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/communities/:communityId/posts/:postId/approval',\n schema: {\n operationId: 'approveCommunityPost',\n tags,\n summary: 'Publish a post that is awaiting review (staff)',\n description:\n 'Staff only (owner, admin, instructor). 409 when the post is not awaiting review.',\n params: CommunityPostParam,\n response: { 200: PostView, ...refusals(403, 404, 409) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return container.posts.approve(\n scope.orgId,\n req.params.postId,\n { id: scope.orgUserId, role: scope.role },\n req.params.communityId,\n );\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/communities/:communityId/posts/:postId/removal',\n schema: {\n operationId: 'removeCommunityPost',\n tags,\n summary: 'Remove a post (staff)',\n description:\n 'Staff only (owner, admin, instructor). Takes a published or pending post down and resolves its open flags as `removed`. 409 when it is already removed or was never submitted.',\n params: CommunityPostParam,\n response: { 200: PostView, ...refusals(403, 404, 409) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return container.posts.remove(\n scope.orgId,\n req.params.postId,\n { id: scope.orgUserId, role: scope.role },\n req.params.communityId,\n );\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/communities/:communityId/posts/:postId/restoration',\n schema: {\n operationId: 'restoreCommunityPost',\n tags,\n summary: 'Restore a removed post (staff)',\n description: 'Staff only (owner, admin, instructor). 409 when the post is not removed.',\n params: CommunityPostParam,\n response: { 200: PostView, ...refusals(403, 404, 409) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return container.posts.restore(\n scope.orgId,\n req.params.postId,\n { id: scope.orgUserId, role: scope.role },\n req.params.communityId,\n );\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/communities/:communityId/posts/:postId/flags/resolution',\n schema: {\n operationId: 'resolveCommunityPostFlags',\n tags,\n summary: 'Dismiss the open flags on a post (staff)',\n description:\n 'Staff only (owner, admin, instructor). Closes every open flag on the post and leaves it up.',\n params: CommunityPostParam,\n body: ResolvePostFlags,\n response: { 204: z.void(), ...refusals(403, 404) },\n },\n handler: async (req, reply) => {\n const scope = await resolveScope(container, req);\n await container.posts.resolveFlags(\n scope.orgId,\n req.params.postId,\n { id: scope.orgUserId, role: scope.role },\n req.body.resolution,\n req.params.communityId,\n );\n return reply.code(204).send();\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/communities/:communityId/comments/:commentId/removal',\n schema: {\n operationId: 'removeCommunityComment',\n tags,\n summary: 'Remove a comment on a post (staff)',\n description:\n 'Staff only (owner, admin, instructor). Hides the comment and resolves its open flags as `removed`. Removing a removed comment returns it unchanged.',\n params: CommunityCommentParam,\n response: { 200: CommentView, ...refusals(403, 404) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return container.discussion.remove(\n scope.orgId,\n req.params.commentId,\n { id: scope.orgUserId, role: scope.role },\n req.params.communityId,\n );\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/communities/:communityId/comments/:commentId/restoration',\n schema: {\n operationId: 'restoreCommunityComment',\n tags,\n summary: 'Restore a removed comment on a post (staff)',\n description:\n 'Staff only (owner, admin, instructor). Publishes the comment again. Restoring a comment that is not removed returns it unchanged.',\n params: CommunityCommentParam,\n response: { 200: CommentView, ...refusals(403, 404) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return container.discussion.restore(\n scope.orgId,\n req.params.commentId,\n { id: scope.orgUserId, role: scope.role },\n req.params.communityId,\n );\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/communities/:communityId/comments/:commentId/flags/resolution',\n schema: {\n operationId: 'resolveCommunityCommentFlags',\n tags,\n summary: 'Dismiss the open flags on a comment (staff)',\n description:\n 'Staff only (owner, admin, instructor). Closes every open flag on the comment and leaves it up.',\n params: CommunityCommentParam,\n body: ResolveCommentFlags,\n response: { 204: z.void(), ...refusals(403, 404) },\n },\n handler: async (req, reply) => {\n const scope = await resolveScope(container, req);\n await container.discussion.resolveReports(\n scope.orgId,\n req.params.commentId,\n { id: scope.orgUserId, role: scope.role },\n req.params.communityId,\n );\n return reply.code(204).send();\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/communities/:communityId/messages/:messageId/flags/resolution',\n schema: {\n operationId: 'resolveCommunityMessageFlags',\n tags,\n summary: 'Dismiss the open flags on a message (staff)',\n description:\n 'Staff only (owner, admin, instructor). Closes every open flag on the message and leaves it up.',\n params: CommunityMessageParam,\n body: ResolveMessageFlags,\n response: { 204: z.void(), ...refusals(403, 404) },\n },\n handler: async (req, reply) => {\n const scope = await resolveScope(container, req);\n await container.chats.resolveMessageFlags(\n scope.orgId,\n { id: scope.orgUserId, role: scope.role },\n req.params.messageId,\n req.body.resolution,\n req.params.communityId,\n );\n return reply.code(204).send();\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/communities/:communityId/chats/:chatId/lock',\n schema: {\n operationId: 'lockCommunityChat',\n tags,\n summary: 'Lock a chat (staff)',\n description:\n 'Staff only (owner, admin, instructor). Nobody can send or edit messages until it is unlocked; its open message flags are resolved as `locked`. Locking a locked chat returns it unchanged.',\n params: CommunityChatParam,\n response: { 200: Chat, ...refusals(403, 404) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return container.chats.lock(\n scope.orgId,\n { id: scope.orgUserId, role: scope.role },\n req.params.chatId,\n req.params.communityId,\n );\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/communities/:communityId/chats/:chatId/unlock',\n schema: {\n operationId: 'unlockCommunityChat',\n tags,\n summary: 'Unlock a chat (staff)',\n description:\n 'Staff only (owner, admin, instructor). Lets participants send messages again. Unlocking an unlocked chat returns it unchanged.',\n params: CommunityChatParam,\n response: { 200: Chat, ...refusals(403, 404) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return container.chats.unlock(\n scope.orgId,\n { id: scope.orgUserId, role: scope.role },\n req.params.chatId,\n req.params.communityId,\n );\n },\n });\n}\n"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { refusals } from "../schemas/shared.js";
|
|
1
2
|
import { resolveScope } from "../scope.js";
|
|
2
|
-
import { ErrorBody } from "../schemas/shared.js";
|
|
3
3
|
import { CourseIdParam } from "../schemas/content.js";
|
|
4
4
|
import { EnrollmentSeries, EnrollmentSeriesQuery } from "../schemas/dashboard.js";
|
|
5
5
|
import { CourseAnalytics } from "../schemas/course-analytics.js";
|
|
@@ -15,16 +15,17 @@ async function courseAnalyticsRoutes(app, container) {
|
|
|
15
15
|
schema: {
|
|
16
16
|
operationId: "getCourseAnalytics",
|
|
17
17
|
tags: ["Reporting"],
|
|
18
|
-
summary: "Completion and engagement stats for a course",
|
|
18
|
+
summary: "Completion and engagement stats for a course (staff)",
|
|
19
|
+
description: "Staff only (owner, admin, instructor).",
|
|
19
20
|
params: CourseIdParam,
|
|
20
21
|
response: {
|
|
21
22
|
200: CourseAnalytics,
|
|
22
|
-
404
|
|
23
|
+
...refusals(403, 404)
|
|
23
24
|
}
|
|
24
25
|
},
|
|
25
26
|
handler: async (req) => {
|
|
26
27
|
const scope = await resolveScope(container, req);
|
|
27
|
-
const analytics = await container.reporting.
|
|
28
|
+
const analytics = await container.reporting.admin.courseAnalytics(scope.orgId, req.params.id);
|
|
28
29
|
if (!analytics) throw new NotFoundError("Course", req.params.id);
|
|
29
30
|
return analytics;
|
|
30
31
|
}
|
|
@@ -36,17 +37,18 @@ async function courseAnalyticsRoutes(app, container) {
|
|
|
36
37
|
schema: {
|
|
37
38
|
operationId: "getCourseEnrollmentSeries",
|
|
38
39
|
tags: ["Reporting"],
|
|
39
|
-
summary: "Enrollments granted per day for a course over a trailing window",
|
|
40
|
+
summary: "Enrollments granted per day for a course over a trailing window (staff)",
|
|
41
|
+
description: "Staff only (owner, admin, instructor).",
|
|
40
42
|
params: CourseIdParam,
|
|
41
43
|
querystring: EnrollmentSeriesQuery,
|
|
42
44
|
response: {
|
|
43
45
|
200: EnrollmentSeries,
|
|
44
|
-
404
|
|
46
|
+
...refusals(403, 404)
|
|
45
47
|
}
|
|
46
48
|
},
|
|
47
49
|
handler: async (req) => {
|
|
48
50
|
const scope = await resolveScope(container, req);
|
|
49
|
-
const series = await container.reporting.
|
|
51
|
+
const series = await container.reporting.admin.courseEnrollments(scope.orgId, req.params.id, req.query.days);
|
|
50
52
|
if (!series) throw new NotFoundError("Course", req.params.id);
|
|
51
53
|
return series;
|
|
52
54
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"course-analytics.js","names":[],"sources":["../../../src/http/routes/course-analytics.ts"],"sourcesContent":["// HTTP routes for per-course analytics (reporting).\nimport type { FastifyInstance } from 'fastify';\nimport type { ZodTypeProvider } from 'fastify-type-provider-zod';\nimport {\n CourseAnalytics,\n CourseIdParam,\n EnrollmentSeries,\n EnrollmentSeriesQuery,\n
|
|
1
|
+
{"version":3,"file":"course-analytics.js","names":[],"sources":["../../../src/http/routes/course-analytics.ts"],"sourcesContent":["// HTTP routes for per-course analytics (reporting).\nimport type { FastifyInstance } from 'fastify';\nimport type { ZodTypeProvider } from 'fastify-type-provider-zod';\nimport {\n CourseAnalytics,\n CourseIdParam,\n EnrollmentSeries,\n EnrollmentSeriesQuery,\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 courseAnalyticsRoutes(\n app: FastifyInstance,\n container: Container,\n): Promise<void> {\n const r = app.withTypeProvider<ZodTypeProvider>();\n\n r.route({\n method: 'GET',\n url: '/api/courses/:id/analytics',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'getCourseAnalytics',\n tags: ['Reporting'],\n summary: 'Completion and engagement stats for a course (staff)',\n description: 'Staff only (owner, admin, instructor).',\n params: CourseIdParam,\n response: { 200: CourseAnalytics, ...refusals(403, 404) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n const analytics = await container.reporting.admin.courseAnalytics(scope.orgId, req.params.id);\n if (!analytics) {\n throw new NotFoundError('Course', req.params.id);\n }\n return analytics;\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/courses/:id/analytics/enrollments',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'getCourseEnrollmentSeries',\n tags: ['Reporting'],\n summary: 'Enrollments granted per day for a course over a trailing window (staff)',\n description: 'Staff only (owner, admin, instructor).',\n params: CourseIdParam,\n querystring: EnrollmentSeriesQuery,\n response: { 200: EnrollmentSeries, ...refusals(403, 404) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n const series = await container.reporting.admin.courseEnrollments(\n scope.orgId,\n req.params.id,\n req.query.days,\n );\n if (!series) {\n throw new NotFoundError('Course', req.params.id);\n }\n return series;\n },\n });\n}\n"],"mappings":";;;;;;;;AAcA,eAAsB,sBACpB,KACA,WACe;CACf,MAAM,IAAI,IAAI,iBAAkC;CAEhD,EAAE,MAAM;EACN,QAAQ;EACR,KAAK;EACL,YAAY,IAAI;EAChB,QAAQ;GACN,aAAa;GACb,MAAM,CAAC,WAAW;GAClB,SAAS;GACT,aAAa;GACb,QAAQ;GACR,UAAU;IAAE,KAAK;IAAiB,GAAG,SAAS,KAAK,GAAG;GAAE;EAC1D;EACA,SAAS,OAAO,QAAQ;GACtB,MAAM,QAAQ,MAAM,aAAa,WAAW,GAAG;GAC/C,MAAM,YAAY,MAAM,UAAU,UAAU,MAAM,gBAAgB,MAAM,OAAO,IAAI,OAAO,EAAE;GAC5F,IAAI,CAAC,WACH,MAAM,IAAI,cAAc,UAAU,IAAI,OAAO,EAAE;GAEjD,OAAO;EACT;CACF,CAAC;CAED,EAAE,MAAM;EACN,QAAQ;EACR,KAAK;EACL,YAAY,IAAI;EAChB,QAAQ;GACN,aAAa;GACb,MAAM,CAAC,WAAW;GAClB,SAAS;GACT,aAAa;GACb,QAAQ;GACR,aAAa;GACb,UAAU;IAAE,KAAK;IAAkB,GAAG,SAAS,KAAK,GAAG;GAAE;EAC3D;EACA,SAAS,OAAO,QAAQ;GACtB,MAAM,QAAQ,MAAM,aAAa,WAAW,GAAG;GAC/C,MAAM,SAAS,MAAM,UAAU,UAAU,MAAM,kBAC7C,MAAM,OACN,IAAI,OAAO,IACX,IAAI,MAAM,IACZ;GACA,IAAI,CAAC,QACH,MAAM,IAAI,cAAc,UAAU,IAAI,OAAO,EAAE;GAEjD,OAAO;EACT;CACF,CAAC;AACH"}
|
|
@@ -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 { Course, CourseIdParam, CourseSettings, CoursesPage, CoursesQuery, CreateCourse, PatchCourseSettings, UpdateCourse } from "../schemas/content.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/courses.ts
|
|
8
8
|
async function coursesRoutes(app, container) {
|
|
9
9
|
const r = app.withTypeProvider();
|
|
@@ -15,9 +15,13 @@ async function coursesRoutes(app, container) {
|
|
|
15
15
|
schema: {
|
|
16
16
|
operationId: "listCourses",
|
|
17
17
|
tags: ["Content"],
|
|
18
|
-
summary: "List courses",
|
|
18
|
+
summary: "List courses (staff)",
|
|
19
|
+
description: "Staff only (owner, admin, instructor). Every course in the organization, drafts included, filtered by status and category and searched by title.",
|
|
19
20
|
querystring: CoursesQuery,
|
|
20
|
-
response: {
|
|
21
|
+
response: {
|
|
22
|
+
200: CoursesPage,
|
|
23
|
+
...refusals(403)
|
|
24
|
+
}
|
|
21
25
|
},
|
|
22
26
|
handler: async (req) => {
|
|
23
27
|
const scope = await resolveScope(container, req);
|
|
@@ -31,11 +35,12 @@ async function coursesRoutes(app, container) {
|
|
|
31
35
|
schema: {
|
|
32
36
|
operationId: "getCourse",
|
|
33
37
|
tags: ["Content"],
|
|
34
|
-
summary: "Get a course
|
|
38
|
+
summary: "Get a course (staff)",
|
|
39
|
+
description: "Staff only (owner, admin, instructor).",
|
|
35
40
|
params: CourseIdParam,
|
|
36
41
|
response: {
|
|
37
42
|
200: Course,
|
|
38
|
-
404
|
|
43
|
+
...refusals(403, 404)
|
|
39
44
|
}
|
|
40
45
|
},
|
|
41
46
|
handler: async (req) => {
|
|
@@ -52,9 +57,13 @@ async function coursesRoutes(app, container) {
|
|
|
52
57
|
schema: {
|
|
53
58
|
operationId: "createCourse",
|
|
54
59
|
tags: ["Content"],
|
|
55
|
-
summary: "Create a course",
|
|
60
|
+
summary: "Create a course (staff)",
|
|
61
|
+
description: "Staff only (owner, admin, instructor). The course starts as a draft. 409 when its slug is taken.",
|
|
56
62
|
body: CreateCourse,
|
|
57
|
-
response: {
|
|
63
|
+
response: {
|
|
64
|
+
201: Course,
|
|
65
|
+
...refusals(403, 409)
|
|
66
|
+
}
|
|
58
67
|
},
|
|
59
68
|
handler: async (req, reply) => {
|
|
60
69
|
const scope = await resolveScope(container, req);
|
|
@@ -69,12 +78,13 @@ async function coursesRoutes(app, container) {
|
|
|
69
78
|
schema: {
|
|
70
79
|
operationId: "updateCourse",
|
|
71
80
|
tags: ["Content"],
|
|
72
|
-
summary: "Update a course",
|
|
81
|
+
summary: "Update a course (staff)",
|
|
82
|
+
description: "Staff only (owner, admin, instructor). Changes only the fields sent. `thumbnailAssetId` must be a ready library image (409 otherwise); null clears it.",
|
|
73
83
|
params: CourseIdParam,
|
|
74
84
|
body: UpdateCourse,
|
|
75
85
|
response: {
|
|
76
86
|
200: Course,
|
|
77
|
-
404
|
|
87
|
+
...refusals(403, 404, 409)
|
|
78
88
|
}
|
|
79
89
|
},
|
|
80
90
|
handler: async (req) => {
|
|
@@ -89,12 +99,13 @@ async function coursesRoutes(app, container) {
|
|
|
89
99
|
schema: {
|
|
90
100
|
operationId: "updateCourseSettings",
|
|
91
101
|
tags: ["Content"],
|
|
92
|
-
summary: "Update a course settings",
|
|
102
|
+
summary: "Update a course's settings (staff)",
|
|
103
|
+
description: "Staff only (owner, admin, instructor). Merges the settings sent into the current ones.",
|
|
93
104
|
params: CourseIdParam,
|
|
94
105
|
body: PatchCourseSettings,
|
|
95
106
|
response: {
|
|
96
107
|
200: CourseSettings,
|
|
97
|
-
404
|
|
108
|
+
...refusals(403, 404)
|
|
98
109
|
}
|
|
99
110
|
},
|
|
100
111
|
handler: async (req) => {
|
|
@@ -110,16 +121,20 @@ async function coursesRoutes(app, container) {
|
|
|
110
121
|
schema: {
|
|
111
122
|
operationId: "deleteCourse",
|
|
112
123
|
tags: ["Content"],
|
|
113
|
-
summary: "Delete a course",
|
|
124
|
+
summary: "Delete a course (staff)",
|
|
125
|
+
description: "Staff only (owner, admin, instructor).",
|
|
114
126
|
params: CourseIdParam,
|
|
115
127
|
response: {
|
|
116
128
|
204: z.void(),
|
|
117
|
-
404
|
|
129
|
+
...refusals(403, 404)
|
|
118
130
|
}
|
|
119
131
|
},
|
|
120
132
|
handler: async (req, reply) => {
|
|
121
133
|
const scope = await resolveScope(container, req);
|
|
122
|
-
await courses.deleteCourse(scope.orgId, req.params.id
|
|
134
|
+
await courses.deleteCourse(scope.orgId, req.params.id, {
|
|
135
|
+
id: scope.orgUserId,
|
|
136
|
+
role: scope.role
|
|
137
|
+
});
|
|
123
138
|
return reply.code(204).send();
|
|
124
139
|
}
|
|
125
140
|
});
|