@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
|
@@ -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 or no longer available.",
|
|
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 or no longer available.",
|
|
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 (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 or no longer available.',\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 or no longer available.',\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 (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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"}
|
|
@@ -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 { AddBundleItem, Bundle, BundleIdParam, BundleItem, BundleItemParams, BundlesPage, BundlesQuery, CreateBundle, UpdateBundle } from "../schemas/bundles.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/bundles.ts
|
|
8
8
|
async function bundlesRoutes(app, container) {
|
|
9
9
|
const r = app.withTypeProvider();
|
|
@@ -15,9 +15,13 @@ async function bundlesRoutes(app, container) {
|
|
|
15
15
|
schema: {
|
|
16
16
|
operationId: "listBundles",
|
|
17
17
|
tags: ["Content"],
|
|
18
|
-
summary: "List bundles",
|
|
18
|
+
summary: "List bundles (staff)",
|
|
19
|
+
description: "Staff only (owner, admin, instructor).",
|
|
19
20
|
querystring: BundlesQuery,
|
|
20
|
-
response: {
|
|
21
|
+
response: {
|
|
22
|
+
200: BundlesPage,
|
|
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 bundlesRoutes(app, container) {
|
|
|
31
35
|
schema: {
|
|
32
36
|
operationId: "getBundle",
|
|
33
37
|
tags: ["Content"],
|
|
34
|
-
summary: "Get a bundle
|
|
38
|
+
summary: "Get a bundle (staff)",
|
|
39
|
+
description: "Staff only (owner, admin, instructor).",
|
|
35
40
|
params: BundleIdParam,
|
|
36
41
|
response: {
|
|
37
42
|
200: Bundle,
|
|
38
|
-
404
|
|
43
|
+
...refusals(403, 404)
|
|
39
44
|
}
|
|
40
45
|
},
|
|
41
46
|
handler: async (req) => {
|
|
@@ -52,11 +57,12 @@ async function bundlesRoutes(app, container) {
|
|
|
52
57
|
schema: {
|
|
53
58
|
operationId: "createBundle",
|
|
54
59
|
tags: ["Content"],
|
|
55
|
-
summary: "Create a bundle, optionally with initial content",
|
|
60
|
+
summary: "Create a bundle, optionally with initial content (staff)",
|
|
61
|
+
description: "Staff only (owner, admin, instructor). 404 when an initial content id is unknown.",
|
|
56
62
|
body: CreateBundle,
|
|
57
63
|
response: {
|
|
58
64
|
201: Bundle,
|
|
59
|
-
404
|
|
65
|
+
...refusals(403, 404)
|
|
60
66
|
}
|
|
61
67
|
},
|
|
62
68
|
handler: async (req, reply) => {
|
|
@@ -72,12 +78,13 @@ async function bundlesRoutes(app, container) {
|
|
|
72
78
|
schema: {
|
|
73
79
|
operationId: "updateBundle",
|
|
74
80
|
tags: ["Content"],
|
|
75
|
-
summary: "
|
|
81
|
+
summary: "Rename a bundle (staff)",
|
|
82
|
+
description: "Staff only (owner, admin, instructor).",
|
|
76
83
|
params: BundleIdParam,
|
|
77
84
|
body: UpdateBundle,
|
|
78
85
|
response: {
|
|
79
86
|
200: Bundle,
|
|
80
|
-
404
|
|
87
|
+
...refusals(403, 404)
|
|
81
88
|
}
|
|
82
89
|
},
|
|
83
90
|
handler: async (req) => {
|
|
@@ -92,11 +99,12 @@ async function bundlesRoutes(app, container) {
|
|
|
92
99
|
schema: {
|
|
93
100
|
operationId: "deleteBundle",
|
|
94
101
|
tags: ["Content"],
|
|
95
|
-
summary: "Delete a bundle (
|
|
102
|
+
summary: "Delete a bundle (staff)",
|
|
103
|
+
description: "Staff only (owner, admin, instructor). Its content survives; access granted through the bundle ends.",
|
|
96
104
|
params: BundleIdParam,
|
|
97
105
|
response: {
|
|
98
106
|
204: z.void(),
|
|
99
|
-
404
|
|
107
|
+
...refusals(403, 404)
|
|
100
108
|
}
|
|
101
109
|
},
|
|
102
110
|
handler: async (req, reply) => {
|
|
@@ -112,11 +120,12 @@ async function bundlesRoutes(app, container) {
|
|
|
112
120
|
schema: {
|
|
113
121
|
operationId: "listBundleItems",
|
|
114
122
|
tags: ["Content"],
|
|
115
|
-
summary: "List a bundle's content
|
|
123
|
+
summary: "List a bundle's content (staff)",
|
|
124
|
+
description: "Staff only (owner, admin, instructor).",
|
|
116
125
|
params: BundleIdParam,
|
|
117
126
|
response: {
|
|
118
127
|
200: z.array(BundleItem),
|
|
119
|
-
|
|
128
|
+
...refusals(403)
|
|
120
129
|
}
|
|
121
130
|
},
|
|
122
131
|
handler: async (req) => {
|
|
@@ -131,12 +140,13 @@ async function bundlesRoutes(app, container) {
|
|
|
131
140
|
schema: {
|
|
132
141
|
operationId: "addBundleItem",
|
|
133
142
|
tags: ["Content"],
|
|
134
|
-
summary: "Add
|
|
143
|
+
summary: "Add content to a bundle (staff)",
|
|
144
|
+
description: "Staff only (owner, admin, instructor). Everyone who holds the bundle can open the content from then on. Returns the bundle's content.",
|
|
135
145
|
params: BundleIdParam,
|
|
136
146
|
body: AddBundleItem,
|
|
137
147
|
response: {
|
|
138
148
|
200: z.array(BundleItem),
|
|
139
|
-
404
|
|
149
|
+
...refusals(403, 404)
|
|
140
150
|
}
|
|
141
151
|
},
|
|
142
152
|
handler: async (req) => {
|
|
@@ -151,11 +161,12 @@ async function bundlesRoutes(app, container) {
|
|
|
151
161
|
schema: {
|
|
152
162
|
operationId: "removeBundleItem",
|
|
153
163
|
tags: ["Content"],
|
|
154
|
-
summary: "Remove
|
|
164
|
+
summary: "Remove content from a bundle (staff)",
|
|
165
|
+
description: "Staff only (owner, admin, instructor). The content survives; access to it through the bundle ends. Returns the bundle's content.",
|
|
155
166
|
params: BundleItemParams,
|
|
156
167
|
response: {
|
|
157
168
|
200: z.array(BundleItem),
|
|
158
|
-
404
|
|
169
|
+
...refusals(403, 404)
|
|
159
170
|
}
|
|
160
171
|
},
|
|
161
172
|
handler: async (req) => {
|