@headless-lms/server 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -0
- package/dist/app/community-mail.d.ts +5 -3
- package/dist/app/community-mail.js +60 -12
- package/dist/app/community-mail.js.map +1 -1
- package/dist/app/container.d.ts +24 -3
- package/dist/app/container.js +102 -38
- package/dist/app/container.js.map +1 -1
- package/dist/app/notification-channels.js +1 -0
- package/dist/app/notification-channels.js.map +1 -1
- package/dist/app/notification-reads.d.ts +3 -3
- package/dist/app/notification-reads.js +7 -28
- package/dist/app/notification-reads.js.map +1 -1
- package/dist/app/platform.d.ts +14 -0
- package/dist/app/platform.js +14 -0
- package/dist/app/platform.js.map +1 -0
- package/dist/http/config.d.ts +4 -0
- package/dist/http/errors.js +9 -4
- package/dist/http/errors.js.map +1 -1
- package/dist/http/fastify.d.ts +8 -0
- package/dist/http/mcp/catalogue.d.ts +11 -9
- package/dist/http/mcp/catalogue.js +15 -21
- package/dist/http/mcp/catalogue.js.map +1 -1
- package/dist/http/mcp/challenge.js +3 -3
- package/dist/http/mcp/challenge.js.map +1 -1
- package/dist/http/mcp/route.js +7 -7
- package/dist/http/mcp/route.js.map +1 -1
- package/dist/http/mcp/test-support.d.ts +6 -5
- package/dist/http/mcp/test-support.js +32 -11
- package/dist/http/mcp/test-support.js.map +1 -1
- package/dist/http/mcp/tool.d.ts +3 -9
- package/dist/http/mcp/tool.js +14 -6
- package/dist/http/mcp/tool.js.map +1 -1
- package/dist/http/mcp/tools/automations.js +10 -20
- package/dist/http/mcp/tools/automations.js.map +1 -1
- package/dist/http/mcp/tools/catalog.js +25 -42
- package/dist/http/mcp/tools/catalog.js.map +1 -1
- package/dist/http/mcp/tools/comments.js +9 -19
- package/dist/http/mcp/tools/comments.js.map +1 -1
- package/dist/http/mcp/tools/communities.js +16 -31
- package/dist/http/mcp/tools/communities.js.map +1 -1
- package/dist/http/mcp/tools/community-members.js +9 -16
- package/dist/http/mcp/tools/community-members.js.map +1 -1
- package/dist/http/mcp/tools/community-moderation.js +13 -25
- package/dist/http/mcp/tools/community-moderation.js.map +1 -1
- package/dist/http/mcp/tools/courses.js +11 -21
- package/dist/http/mcp/tools/courses.js.map +1 -1
- package/dist/http/mcp/tools/events.d.ts +6 -0
- package/dist/http/mcp/tools/events.js +170 -0
- package/dist/http/mcp/tools/events.js.map +1 -0
- package/dist/http/mcp/tools/index.d.ts +5 -1
- package/dist/http/mcp/tools/index.js +45 -10
- package/dist/http/mcp/tools/index.js.map +1 -1
- package/dist/http/mcp/tools/organization.d.ts +6 -0
- package/dist/http/mcp/tools/organization.js +214 -0
- package/dist/http/mcp/tools/organization.js.map +1 -0
- package/dist/http/mcp/tools/people.js +30 -49
- package/dist/http/mcp/tools/people.js.map +1 -1
- package/dist/http/plugins/auth.js +37 -8
- package/dist/http/plugins/auth.js.map +1 -1
- package/dist/http/plugins/error-handler.d.ts +5 -1
- package/dist/http/plugins/error-handler.js +10 -2
- package/dist/http/plugins/error-handler.js.map +1 -1
- package/dist/http/plugins/openapi.js +18 -2
- package/dist/http/plugins/openapi.js.map +1 -1
- package/dist/http/plugins/platform.d.ts +7 -0
- package/dist/http/plugins/platform.js +13 -0
- package/dist/http/plugins/platform.js.map +1 -0
- package/dist/http/routes/access.js +54 -36
- package/dist/http/routes/access.js.map +1 -1
- package/dist/http/routes/activities.js +45 -45
- package/dist/http/routes/activities.js.map +1 -1
- package/dist/http/routes/assets.js +30 -65
- package/dist/http/routes/assets.js.map +1 -1
- package/dist/http/routes/automations.js +37 -42
- package/dist/http/routes/automations.js.map +1 -1
- package/dist/http/routes/bundles.js +33 -36
- package/dist/http/routes/bundles.js.map +1 -1
- package/dist/http/routes/communities.js +93 -145
- package/dist/http/routes/communities.js.map +1 -1
- package/dist/http/routes/community-moderation.js +83 -137
- package/dist/http/routes/community-moderation.js.map +1 -1
- package/dist/http/routes/course-analytics.js +32 -15
- package/dist/http/routes/course-analytics.js.map +1 -1
- package/dist/http/routes/courses.js +25 -32
- package/dist/http/routes/courses.js.map +1 -1
- package/dist/http/routes/dashboard.js +9 -9
- package/dist/http/routes/dashboard.js.map +1 -1
- package/dist/http/routes/discussion.js +21 -39
- package/dist/http/routes/discussion.js.map +1 -1
- package/dist/http/routes/downloads.js +41 -47
- package/dist/http/routes/downloads.js.map +1 -1
- package/dist/http/routes/entitlements.js +19 -23
- package/dist/http/routes/entitlements.js.map +1 -1
- package/dist/http/routes/events.d.ts +7 -0
- package/dist/http/routes/events.js +333 -0
- package/dist/http/routes/events.js.map +1 -0
- package/dist/http/routes/integrations.js +40 -73
- package/dist/http/routes/integrations.js.map +1 -1
- package/dist/http/routes/member/assets.js +10 -13
- package/dist/http/routes/member/assets.js.map +1 -1
- package/dist/http/routes/member/chats.js +170 -47
- package/dist/http/routes/member/chats.js.map +1 -1
- package/dist/http/routes/member/comments.js +65 -26
- package/dist/http/routes/member/comments.js.map +1 -1
- package/dist/http/routes/member/community.js +45 -52
- package/dist/http/routes/member/community.js.map +1 -1
- package/dist/http/routes/member/courses.js +12 -19
- package/dist/http/routes/member/courses.js.map +1 -1
- package/dist/http/routes/member/devices.js +9 -10
- package/dist/http/routes/member/devices.js.map +1 -1
- package/dist/http/routes/member/downloads.js +6 -9
- package/dist/http/routes/member/downloads.js.map +1 -1
- package/dist/http/routes/member/events.d.ts +7 -0
- package/dist/http/routes/member/events.js +386 -0
- package/dist/http/routes/member/events.js.map +1 -0
- package/dist/http/routes/member/files.js +71 -46
- package/dist/http/routes/member/files.js.map +1 -1
- package/dist/http/routes/member/index.js +2 -0
- package/dist/http/routes/member/index.js.map +1 -1
- package/dist/http/routes/member/notification-settings.js +15 -23
- package/dist/http/routes/member/notification-settings.js.map +1 -1
- package/dist/http/routes/member/notifications.js +16 -16
- package/dist/http/routes/member/notifications.js.map +1 -1
- package/dist/http/routes/member/org.js +5 -15
- package/dist/http/routes/member/org.js.map +1 -1
- package/dist/http/routes/member/posts.js +67 -68
- package/dist/http/routes/member/posts.js.map +1 -1
- package/dist/http/routes/member/profile.js +15 -6
- package/dist/http/routes/member/profile.js.map +1 -1
- package/dist/http/routes/member/progress.js +7 -11
- package/dist/http/routes/member/progress.js.map +1 -1
- package/dist/http/routes/organizations.js +319 -135
- package/dist/http/routes/organizations.js.map +1 -1
- package/dist/http/routes/platform.d.ts +7 -0
- package/dist/http/routes/platform.js +61 -0
- package/dist/http/routes/platform.js.map +1 -0
- package/dist/http/routes/students.js +12 -75
- package/dist/http/routes/students.js.map +1 -1
- package/dist/http/routes/webhooks.js +30 -39
- package/dist/http/routes/webhooks.js.map +1 -1
- package/dist/http/routes.js +9 -0
- package/dist/http/routes.js.map +1 -1
- package/dist/http/schemas/access.d.ts +346 -64
- package/dist/http/schemas/access.js +39 -12
- package/dist/http/schemas/access.js.map +1 -1
- package/dist/http/schemas/assets.d.ts +19 -14
- package/dist/http/schemas/assets.js +6 -5
- package/dist/http/schemas/assets.js.map +1 -1
- package/dist/http/schemas/automations.d.ts +4 -4
- package/dist/http/schemas/chats.d.ts +211 -32
- package/dist/http/schemas/chats.js +13 -4
- package/dist/http/schemas/chats.js.map +1 -1
- package/dist/http/schemas/community.d.ts +91 -44
- package/dist/http/schemas/community.js +21 -8
- package/dist/http/schemas/community.js.map +1 -1
- package/dist/http/schemas/course-analytics.d.ts +51 -1
- package/dist/http/schemas/course-analytics.js +19 -1
- package/dist/http/schemas/course-analytics.js.map +1 -1
- package/dist/http/schemas/dashboard.d.ts +1 -0
- package/dist/http/schemas/dashboard.js +2 -1
- package/dist/http/schemas/dashboard.js.map +1 -1
- package/dist/http/schemas/discussion.d.ts +22 -72
- package/dist/http/schemas/discussion.js +2 -2
- package/dist/http/schemas/discussion.js.map +1 -1
- package/dist/http/schemas/entitlements.d.ts +9 -12
- package/dist/http/schemas/entitlements.js +11 -5
- package/dist/http/schemas/entitlements.js.map +1 -1
- package/dist/http/schemas/events.d.ts +527 -0
- package/dist/http/schemas/events.js +64 -0
- package/dist/http/schemas/events.js.map +1 -0
- package/dist/http/schemas/files.d.ts +15 -5
- package/dist/http/schemas/files.js +10 -2
- package/dist/http/schemas/files.js.map +1 -1
- package/dist/http/schemas/index.d.ts +11 -9
- package/dist/http/schemas/index.js +11 -9
- package/dist/http/schemas/integrations.d.ts +4 -4
- package/dist/http/schemas/invites.d.ts +3 -47
- package/dist/http/schemas/invites.js +4 -25
- package/dist/http/schemas/invites.js.map +1 -1
- package/dist/http/schemas/member.d.ts +94 -7
- package/dist/http/schemas/member.js +7 -3
- package/dist/http/schemas/member.js.map +1 -1
- package/dist/http/schemas/members.d.ts +333 -69
- package/dist/http/schemas/members.js +67 -25
- package/dist/http/schemas/members.js.map +1 -1
- package/dist/http/schemas/notifications.d.ts +57 -53
- package/dist/http/schemas/platform.d.ts +37 -0
- package/dist/http/schemas/platform.js +19 -0
- package/dist/http/schemas/platform.js.map +1 -0
- package/dist/http/schemas/posts.d.ts +55 -35
- package/dist/http/schemas/posts.js +4 -4
- package/dist/http/schemas/posts.js.map +1 -1
- package/dist/http/schemas/profile.d.ts +1 -1
- package/dist/http/schemas/shared.d.ts +1 -11
- package/dist/http/schemas/shared.js +1 -10
- package/dist/http/schemas/shared.js.map +1 -1
- package/dist/http/schemas/students.d.ts +25 -3
- package/dist/http/schemas/students.js +3 -1
- package/dist/http/schemas/students.js.map +1 -1
- package/dist/http/scope.d.ts +9 -13
- package/dist/http/scope.js +29 -40
- package/dist/http/scope.js.map +1 -1
- package/dist/http/server.js +9 -0
- package/dist/http/server.js.map +1 -1
- package/dist/http/shutdown.d.ts +11 -0
- package/dist/http/shutdown.js +40 -0
- package/dist/http/shutdown.js.map +1 -0
- package/dist/http/sse.d.ts +19 -0
- package/dist/http/sse.js +104 -0
- package/dist/http/sse.js.map +1 -0
- package/dist/http/test-caller.d.ts +13 -0
- package/dist/http/test-caller.js +14 -0
- package/dist/http/test-caller.js.map +1 -0
- package/dist/http/well-known.js +2 -2
- package/dist/http/well-known.js.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/packages/server/package.js +1 -1
- package/package.json +7 -6
- package/dist/http/routes/member/actor.d.ts +0 -14
- package/dist/http/routes/member/actor.js +0 -11
- package/dist/http/routes/member/actor.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"communities.js","names":[],"sources":["../../../src/http/routes/communities.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 { resolveConfiguratorScope, resolveScope } from '../scope.js';\nimport {\n Community,\n CommunityAnalytics,\n CommunityIdParam,\n CommunitySpaceParam,\n CommunitiesPage,\n CommunitiesQuery,\n CreateCommunity,\n CreateSpace,\n MessageQueue,\n NotificationCategoryList,\n OrderedIds,\n PatchCommunitySettings,\n ReplaceNotificationCategories,\n SetSpaceTopics,\n SpaceDetail,\n SpaceLinkBody,\n SpaceLinkParam,\n UpdateCommunity,\n UpdateSpace,\n refusals,\n} from '../schemas/index.js';\n\nexport async function communitiesRoutes(app: FastifyInstance, container: Container): Promise<void> {\n const r = app.withTypeProvider<ZodTypeProvider>();\n const content = container.content;\n const tags = ['Communities'];\n\n r.route({\n method: 'GET',\n url: '/api/communities',\n schema: {\n operationId: 'listCommunities',\n tags,\n summary: 'List communities (staff)',\n description:\n 'Staff only (owner, admin, instructor). Pages every community in the organization, drafts included, filtered by `status` and searched by title.',\n querystring: CommunitiesQuery,\n response: { 200: CommunitiesPage, ...refusals(403) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return content.listCommunities(scope.orgId, req.query);\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/communities',\n schema: {\n operationId: 'createCommunity',\n tags,\n summary: 'Create a community (staff)',\n description:\n 'Owner or admin only. The community starts as a draft with the default direct-message settings and no spaces. Members see it once it is published and they hold it.',\n body: CreateCommunity,\n response: { 201: Community, ...refusals(403, 409) },\n },\n handler: async (req, reply) => {\n const scope = await resolveScope(container, req);\n const community = await content.createCommunity(scope.orgId, req.body, {\n id: scope.orgUserId,\n role: scope.role,\n });\n req.log.info({ communityId: community.id }, 'community created');\n return reply.code(201).send(community);\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/communities/:communityId',\n schema: {\n operationId: 'getCommunity',\n tags,\n summary: 'Get a community (staff)',\n description:\n 'Staff only (owner, admin, instructor). Any community of the organization, drafts included.',\n params: CommunityIdParam,\n response: { 200: Community, ...refusals(403, 404) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n const community = await content.getCommunity(scope.orgId, req.params.communityId);\n if (!community) {\n throw new NotFoundError('Community', req.params.communityId);\n }\n return community;\n },\n });\n\n r.route({\n method: 'PATCH',\n url: '/api/communities/:communityId',\n schema: {\n operationId: 'updateCommunity',\n tags,\n summary: 'Update a community (staff)',\n description:\n 'Owner or admin only. Changes only the fields sent. `thumbnailAssetId` must be a ready library image (409 otherwise); null clears it.',\n params: CommunityIdParam,\n body: UpdateCommunity,\n response: { 200: Community, ...refusals(403, 404, 409) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return content.updateCommunity(scope.orgId, req.params.communityId, req.body, {\n id: scope.orgUserId,\n role: scope.role,\n });\n },\n });\n\n r.route({\n method: 'DELETE',\n url: '/api/communities/:communityId',\n schema: {\n operationId: 'deleteCommunity',\n tags,\n summary: 'Delete a community (staff)',\n description:\n 'Owner or admin only. Links to the community from other spaces are removed with it.',\n params: CommunityIdParam,\n response: { 204: z.void(), ...refusals(403, 404) },\n },\n handler: async (req, reply) => {\n const scope = await resolveScope(container, req);\n await content.deleteCommunity(scope.orgId, req.params.communityId, {\n id: scope.orgUserId,\n role: scope.role,\n });\n return reply.code(204).send();\n },\n });\n\n r.route({\n method: 'PATCH',\n url: '/api/communities/:communityId/settings',\n schema: {\n operationId: 'patchCommunitySettings',\n tags,\n summary: 'Change the direct-message and digest settings of a community (staff)',\n description:\n 'Owner or admin only. Changes only the settings sent; the rest keep their values. The direct-message settings decide who may message whom from then on. `digest` sets when the community digest goes out, or `null` stops it; the first digest after a change goes out at the next slot.',\n params: CommunityIdParam,\n body: PatchCommunitySettings,\n response: { 200: Community, ...refusals(403, 404) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return content.patchCommunitySettings(scope.orgId, req.params.communityId, req.body, {\n id: scope.orgUserId,\n role: scope.role,\n });\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/communities/:communityId/notification-categories',\n schema: {\n operationId: 'getCommunityNotificationCategories',\n tags: ['Notifications'],\n summary: \"Get a community's notification categories (staff)\",\n description:\n 'Owner or admin only. The list in use for the community, with `source` saying whether it is the default or a stored list.',\n params: CommunityIdParam,\n response: { 200: NotificationCategoryList, ...refusals(403, 404) },\n },\n handler: async (req) => {\n const scope = await resolveConfiguratorScope(container, req);\n return container.notificationCategories.community.get(scope.orgId, req.params.communityId);\n },\n });\n\n r.route({\n method: 'PUT',\n url: '/api/communities/:communityId/notification-categories',\n schema: {\n operationId: 'replaceCommunityNotificationCategories',\n tags: ['Notifications'],\n summary: \"Replace a community's notification categories (staff)\",\n description:\n 'Owner or admin only. Stores the whole list for this community; other communities keep theirs. 422 when a key repeats, a type cannot be in a community list, a reason is not allowed for its type, a channel is not configured, `perSpace` is set on a type that cannot occur in a space, `spaceDefaults` appears without `perSpace` or names a channel the category does not offer, or earlier categories already cover everything a category covers.',\n params: CommunityIdParam,\n body: ReplaceNotificationCategories,\n response: { 200: NotificationCategoryList, ...refusals(400, 403, 404, 422) },\n },\n handler: async (req) => {\n const scope = await resolveConfiguratorScope(container, req);\n return container.notificationCategories.community.replace(\n scope.orgId,\n req.params.communityId,\n req.body.categories,\n );\n },\n });\n\n r.route({\n method: 'DELETE',\n url: '/api/communities/:communityId/notification-categories',\n schema: {\n operationId: 'resetCommunityNotificationCategories',\n tags: ['Notifications'],\n summary: 'Go back to the default notification categories for a community (staff)',\n description: 'Owner or admin only. Removes the stored list; the default applies again.',\n params: CommunityIdParam,\n response: { 204: z.void(), ...refusals(403, 404) },\n },\n handler: async (req, reply) => {\n const scope = await resolveConfiguratorScope(container, req);\n await container.notificationCategories.community.reset(scope.orgId, req.params.communityId);\n return reply.code(204).send();\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/communities/:communityId/publish',\n schema: {\n operationId: 'publishCommunity',\n tags,\n summary: 'Publish a community (staff)',\n description: 'Owner or admin only. Makes the community visible to the people who hold it.',\n params: CommunityIdParam,\n response: { 200: Community, ...refusals(403, 404, 409) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return content.publishCommunity(scope.orgId, req.params.communityId, {\n id: scope.orgUserId,\n role: scope.role,\n });\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/communities/:communityId/unpublish',\n schema: {\n operationId: 'unpublishCommunity',\n tags,\n summary: 'Unpublish a community (staff)',\n description:\n 'Owner or admin only. Hides the community from members again; nothing in it is deleted.',\n params: CommunityIdParam,\n response: { 200: Community, ...refusals(403, 404, 409) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return content.unpublishCommunity(scope.orgId, req.params.communityId, {\n id: scope.orgUserId,\n role: scope.role,\n });\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/communities/:communityId/analytics',\n schema: {\n operationId: 'getCommunityAnalytics',\n tags,\n summary: 'Staff analytics for a community',\n description:\n 'Staff only (owner, admin, instructor). Membership, activity and moderation counts for one community.',\n params: CommunityIdParam,\n response: { 200: CommunityAnalytics, ...refusals(403, 404) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n const stats = await container.reporting.admin.communityAnalytics(\n scope.orgId,\n req.params.communityId,\n );\n if (!stats) {\n throw new NotFoundError('Community', req.params.communityId);\n }\n return stats;\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/communities/:communityId/message-queue',\n schema: {\n operationId: 'getCommunityMessageQueue',\n tags,\n summary: 'Open message flags in a community (staff)',\n description:\n \"Staff only (owner, admin, instructor). The messages in the community's chats with open flags, with the flags and the chat each is in. Holds at most the 100 oldest open flags; working the queue brings in the next ones.\",\n params: CommunityIdParam,\n response: { 200: MessageQueue, ...refusals(403, 404) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return container.chats.messageQueue(\n scope.orgId,\n { id: scope.orgUserId, role: scope.role },\n req.params.communityId,\n );\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/communities/:communityId/spaces',\n schema: {\n operationId: 'listCommunitySpaces',\n tags,\n summary: 'List the spaces in a community (staff)',\n description:\n 'Staff only (owner, admin, instructor). Every space in order, with its topics and links.',\n params: CommunityIdParam,\n response: { 200: z.array(SpaceDetail), ...refusals(403, 404) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return content.listSpaces(scope.orgId, req.params.communityId);\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/communities/:communityId/spaces',\n schema: {\n operationId: 'createSpace',\n tags,\n summary: 'Create a space (staff)',\n description:\n 'Owner or admin only. The space is added last. An `all_members` space cannot have a capacity (409).',\n params: CommunityIdParam,\n body: CreateSpace,\n response: { 201: SpaceDetail, ...refusals(403, 404, 409) },\n },\n handler: async (req, reply) => {\n const scope = await resolveScope(container, req);\n const space = await content.createSpace(scope.orgId, req.params.communityId, req.body, {\n id: scope.orgUserId,\n role: scope.role,\n });\n return reply.code(201).send(space);\n },\n });\n\n r.route({\n method: 'PUT',\n url: '/api/communities/:communityId/spaces/order',\n schema: {\n operationId: 'reorderSpaces',\n tags,\n summary: 'Reorder the spaces in a community (staff)',\n description:\n 'Owner or admin only. Send every space id of the community exactly once (409 otherwise).',\n params: CommunityIdParam,\n body: OrderedIds,\n response: { 200: z.array(SpaceDetail), ...refusals(403, 404, 409) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return content.reorderSpaces(scope.orgId, req.params.communityId, req.body.ids, {\n id: scope.orgUserId,\n role: scope.role,\n });\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/communities/:communityId/spaces/:spaceId',\n schema: {\n operationId: 'getSpace',\n tags,\n summary: 'Get a space (staff)',\n description: 'Staff only (owner, admin, instructor). The space with its topics and links.',\n params: CommunitySpaceParam,\n response: { 200: SpaceDetail, ...refusals(403, 404) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n const space = await content.getCommunitySpace(\n scope.orgId,\n req.params.communityId,\n req.params.spaceId,\n );\n if (!space) {\n throw new NotFoundError('Space', req.params.spaceId);\n }\n return space;\n },\n });\n\n r.route({\n method: 'PATCH',\n url: '/api/communities/:communityId/spaces/:spaceId',\n schema: {\n operationId: 'updateSpace',\n tags,\n summary: 'Update a space (staff)',\n description:\n 'Owner or admin only. Changes only the fields sent; `settings` merges into the current settings. An `all_members` space cannot have a capacity (409).',\n params: CommunitySpaceParam,\n body: UpdateSpace,\n response: { 200: SpaceDetail, ...refusals(403, 404, 409) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return content.updateSpace(\n scope.orgId,\n req.params.communityId,\n req.params.spaceId,\n req.body,\n { id: scope.orgUserId, role: scope.role },\n );\n },\n });\n\n r.route({\n method: 'DELETE',\n url: '/api/communities/:communityId/spaces/:spaceId',\n schema: {\n operationId: 'deleteSpace',\n tags,\n summary: 'Delete a space (staff)',\n description: 'Owner or admin only. The space and everything in it go.',\n params: CommunitySpaceParam,\n response: { 204: z.void(), ...refusals(403, 404) },\n },\n handler: async (req, reply) => {\n const scope = await resolveScope(container, req);\n await content.deleteSpace(scope.orgId, req.params.communityId, req.params.spaceId, {\n id: scope.orgUserId,\n role: scope.role,\n });\n return reply.code(204).send();\n },\n });\n\n r.route({\n method: 'PUT',\n url: '/api/communities/:communityId/spaces/:spaceId/topics',\n schema: {\n operationId: 'setSpaceTopics',\n tags,\n summary: 'Replace the topics on a space (staff)',\n description:\n 'Owner or admin only. The list replaces the current topics: an entry with an `id` keeps and renames that topic, one without adds a topic, and a topic left out is deleted. Names must be unique (409), and a topic that posts use cannot be deleted (409).',\n params: CommunitySpaceParam,\n body: SetSpaceTopics,\n response: { 200: SpaceDetail, ...refusals(403, 404, 409) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return content.setSpaceTopics(\n scope.orgId,\n req.params.communityId,\n req.params.spaceId,\n req.body,\n { id: scope.orgUserId, role: scope.role },\n );\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/communities/:communityId/spaces/:spaceId/links',\n schema: {\n operationId: 'addSpaceLink',\n tags,\n summary: 'Link a course, download, or community into a space (staff)',\n description:\n 'Owner or admin only. The link is added last. Linking the same content twice is a 409.',\n params: CommunitySpaceParam,\n body: SpaceLinkBody,\n response: { 201: SpaceDetail, ...refusals(403, 404, 409) },\n },\n handler: async (req, reply) => {\n const scope = await resolveScope(container, req);\n const space = await content.addSpaceLink(\n scope.orgId,\n req.params.communityId,\n req.params.spaceId,\n req.body.contentId,\n { id: scope.orgUserId, role: scope.role },\n );\n return reply.code(201).send(space);\n },\n });\n\n r.route({\n method: 'DELETE',\n url: '/api/communities/:communityId/spaces/:spaceId/links/:linkId',\n schema: {\n operationId: 'removeSpaceLink',\n tags,\n summary: 'Remove a link from a space (staff)',\n description: 'Owner or admin only. The linked content itself is untouched.',\n params: SpaceLinkParam,\n response: { 200: SpaceDetail, ...refusals(403, 404) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return content.removeSpaceLink(\n scope.orgId,\n req.params.communityId,\n req.params.spaceId,\n req.params.linkId,\n { id: scope.orgUserId, role: scope.role },\n );\n },\n });\n\n r.route({\n method: 'PUT',\n url: '/api/communities/:communityId/spaces/:spaceId/links/order',\n schema: {\n operationId: 'reorderSpaceLinks',\n tags,\n summary: 'Reorder the links in a space (staff)',\n description:\n 'Owner or admin only. Send every link id of the space exactly once (409 otherwise).',\n params: CommunitySpaceParam,\n body: OrderedIds,\n response: { 200: SpaceDetail, ...refusals(403, 404, 409) },\n },\n handler: async (req) => {\n const scope = await resolveScope(container, req);\n return content.reorderSpaceLinks(\n scope.orgId,\n req.params.communityId,\n req.params.spaceId,\n req.body.ids,\n { id: scope.orgUserId, role: scope.role },\n );\n },\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"communities.js","names":[],"sources":["../../../src/http/routes/communities.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 { resolveActor } from '../scope.js';\nimport {\n Community,\n CommunityAnalytics,\n CommunityIdParam,\n CommunitySpaceParam,\n CommunitiesPage,\n CommunitiesQuery,\n CreateCommunity,\n CreateSpace,\n MessageQueue,\n NotificationCategoryList,\n OrderedIds,\n PatchCommunitySettings,\n ReplaceNotificationCategories,\n SetSpaceTopics,\n SpaceDetail,\n SpaceLinkBody,\n SpaceLinkParam,\n UpdateCommunity,\n UpdateSpace,\n refusals,\n} from '../schemas/index.js';\n\nexport async function communitiesRoutes(app: FastifyInstance, container: Container): Promise<void> {\n const r = app.withTypeProvider<ZodTypeProvider>();\n const content = container.content;\n const tags = ['Communities'];\n\n r.route({\n method: 'GET',\n url: '/api/communities',\n schema: {\n operationId: 'listCommunities',\n tags,\n summary: 'List communities',\n description: `Requires one of \\`communities:read\\`, \\`communities:moderate\\`. Pages every community in the organization, drafts included, filtered by \\`status\\` and searched by title.`,\n querystring: CommunitiesQuery,\n response: { 200: CommunitiesPage, ...refusals(403) },\n },\n handler: async (req) => {\n const actor = await resolveActor(container, req);\n return content.listCommunities(req.query, actor);\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/communities',\n schema: {\n operationId: 'createCommunity',\n tags,\n summary: 'Create a community',\n description: `Requires \\`communities:write\\`. The community starts as a draft with the default direct-message settings and no spaces. Members see it once it is published and they hold it.`,\n body: CreateCommunity,\n response: { 201: Community, ...refusals(403, 409) },\n },\n handler: async (req, reply) => {\n const actor = await resolveActor(container, req);\n const community = await content.createCommunity(req.body, actor);\n req.log.info({ communityId: community.id }, 'community created');\n return reply.code(201).send(community);\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/communities/:communityId',\n schema: {\n operationId: 'getCommunity',\n tags,\n summary: 'Get a community',\n description: `Requires one of \\`communities:read\\`, \\`communities:moderate\\`. Any community of the organization, drafts included.`,\n params: CommunityIdParam,\n response: { 200: Community, ...refusals(403, 404) },\n },\n handler: async (req) => {\n const actor = await resolveActor(container, req);\n return content.getCommunity(req.params.communityId, actor);\n },\n });\n\n r.route({\n method: 'PATCH',\n url: '/api/communities/:communityId',\n schema: {\n operationId: 'updateCommunity',\n tags,\n summary: 'Update a community',\n description: `Requires \\`communities:write\\`. Changes only the fields sent. \\`thumbnailAssetId\\` must be a ready library image (409 otherwise); null clears it. A file it does not already use must be uploaded for it, or the caller needs \\`media:read\\` (403 otherwise).`,\n params: CommunityIdParam,\n body: UpdateCommunity,\n response: { 200: Community, ...refusals(403, 404, 409) },\n },\n handler: async (req) => {\n const actor = await resolveActor(container, req);\n return content.updateCommunity(req.params.communityId, req.body, actor);\n },\n });\n\n r.route({\n method: 'DELETE',\n url: '/api/communities/:communityId',\n schema: {\n operationId: 'deleteCommunity',\n tags,\n summary: 'Delete a community',\n description: `Requires \\`communities:delete\\`. Links to the community from other spaces are removed with it.`,\n params: CommunityIdParam,\n response: { 204: z.void(), ...refusals(403, 404) },\n },\n handler: async (req, reply) => {\n const actor = await resolveActor(container, req);\n await content.deleteCommunity(req.params.communityId, actor);\n return reply.code(204).send();\n },\n });\n\n r.route({\n method: 'PATCH',\n url: '/api/communities/:communityId/settings',\n schema: {\n operationId: 'patchCommunitySettings',\n tags,\n summary: 'Change the direct-message and digest settings of a community',\n description: `Requires \\`communities:write\\`. Changes only the settings sent; the rest keep their values. The direct-message settings decide who may message whom from then on. \\`digest\\` sets when the community digest goes out, or \\`null\\` stops it; the first digest after a change goes out at the next slot.`,\n params: CommunityIdParam,\n body: PatchCommunitySettings,\n response: { 200: Community, ...refusals(403, 404) },\n },\n handler: async (req) => {\n const actor = await resolveActor(container, req);\n return content.patchCommunitySettings(req.params.communityId, req.body, actor);\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/communities/:communityId/notification-categories',\n schema: {\n operationId: 'getCommunityNotificationCategories',\n tags: ['Notifications'],\n summary: \"Get a community's notification categories\",\n description: `Requires \\`communities:read\\`. On the community. The list in use for the community, with \\`source\\` saying whether it is the default or a stored list.`,\n params: CommunityIdParam,\n response: { 200: NotificationCategoryList, ...refusals(403, 404) },\n },\n handler: async (req) => {\n const actor = await resolveActor(container, req);\n return container.notificationCategories.community.get(req.params.communityId, actor);\n },\n });\n\n r.route({\n method: 'PUT',\n url: '/api/communities/:communityId/notification-categories',\n schema: {\n operationId: 'replaceCommunityNotificationCategories',\n tags: ['Notifications'],\n summary: \"Replace a community's notification categories\",\n description: `Requires \\`communities:write\\`. On the community. Stores the whole list for this community; other communities keep theirs. 422 when a key repeats, a type cannot be in a community list, a reason is not allowed for its type, a channel is not configured, \\`perSpace\\` is set on a type that cannot occur in a space, \\`spaceDefaults\\` appears without \\`perSpace\\` or names a channel the category does not offer, or earlier categories already cover everything a category covers.`,\n params: CommunityIdParam,\n body: ReplaceNotificationCategories,\n response: { 200: NotificationCategoryList, ...refusals(400, 403, 404, 422) },\n },\n handler: async (req) => {\n const actor = await resolveActor(container, req);\n return container.notificationCategories.community.replace(\n req.params.communityId,\n req.body.categories,\n actor,\n );\n },\n });\n\n r.route({\n method: 'DELETE',\n url: '/api/communities/:communityId/notification-categories',\n schema: {\n operationId: 'resetCommunityNotificationCategories',\n tags: ['Notifications'],\n summary: 'Go back to the default notification categories for a community',\n description: `Requires \\`communities:write\\`. On the community. Removes the stored list; the default applies again.`,\n params: CommunityIdParam,\n response: { 204: z.void(), ...refusals(403, 404) },\n },\n handler: async (req, reply) => {\n const actor = await resolveActor(container, req);\n await container.notificationCategories.community.reset(req.params.communityId, actor);\n return reply.code(204).send();\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/communities/:communityId/publish',\n schema: {\n operationId: 'publishCommunity',\n tags,\n summary: 'Publish a community',\n description: `Requires \\`communities:write\\`. Makes the community visible to the people who hold it.`,\n params: CommunityIdParam,\n response: { 200: Community, ...refusals(403, 404, 409) },\n },\n handler: async (req) => {\n const actor = await resolveActor(container, req);\n return content.publishCommunity(req.params.communityId, actor);\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/communities/:communityId/unpublish',\n schema: {\n operationId: 'unpublishCommunity',\n tags,\n summary: 'Unpublish a community',\n description: `Requires \\`communities:write\\`. Hides the community from members again; nothing in it is deleted.`,\n params: CommunityIdParam,\n response: { 200: Community, ...refusals(403, 404, 409) },\n },\n handler: async (req) => {\n const actor = await resolveActor(container, req);\n return content.unpublishCommunity(req.params.communityId, actor);\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/communities/:communityId/analytics',\n schema: {\n operationId: 'getCommunityAnalytics',\n tags,\n summary: 'Analytics for a community',\n description: `Requires \\`communities:read\\`. Membership, activity and moderation counts for one community.`,\n params: CommunityIdParam,\n response: { 200: CommunityAnalytics, ...refusals(403, 404) },\n },\n handler: async (req) => {\n const actor = await resolveActor(container, req);\n return container.reporting.admin.communityAnalytics(req.params.communityId, actor);\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/communities/:communityId/message-queue',\n schema: {\n operationId: 'getCommunityMessageQueue',\n tags,\n summary: 'Open message flags in a community',\n description: `Requires \\`communities:moderate\\`. The messages in the community's chats with open flags, with the flags and the chat each is in. Holds at most the 100 oldest open flags; working the queue brings in the next ones.`,\n params: CommunityIdParam,\n response: { 200: MessageQueue, ...refusals(403, 404) },\n },\n handler: async (req) => {\n const actor = await resolveActor(container, req);\n return container.chats.messageQueue(req.params.communityId, actor);\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/communities/:communityId/spaces',\n schema: {\n operationId: 'listCommunitySpaces',\n tags,\n summary: 'List the spaces in a community',\n description: `Requires one of \\`communities:read\\`, \\`communities:moderate\\`. Every space in order, with its topics and links.`,\n params: CommunityIdParam,\n response: { 200: z.array(SpaceDetail), ...refusals(403, 404) },\n },\n handler: async (req) => {\n const actor = await resolveActor(container, req);\n return content.listSpaces(req.params.communityId, actor);\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/communities/:communityId/spaces',\n schema: {\n operationId: 'createSpace',\n tags,\n summary: 'Create a space',\n description: `Requires \\`communities:write\\`. The space is added last. An \\`all_members\\` space cannot have a capacity (409).`,\n params: CommunityIdParam,\n body: CreateSpace,\n response: { 201: SpaceDetail, ...refusals(403, 404, 409) },\n },\n handler: async (req, reply) => {\n const actor = await resolveActor(container, req);\n const space = await content.createSpace(req.params.communityId, req.body, actor);\n return reply.code(201).send(space);\n },\n });\n\n r.route({\n method: 'PUT',\n url: '/api/communities/:communityId/spaces/order',\n schema: {\n operationId: 'reorderSpaces',\n tags,\n summary: 'Reorder the spaces in a community',\n description: `Requires \\`communities:write\\`. Send every space id of the community exactly once (409 otherwise).`,\n params: CommunityIdParam,\n body: OrderedIds,\n response: { 200: z.array(SpaceDetail), ...refusals(403, 404, 409) },\n },\n handler: async (req) => {\n const actor = await resolveActor(container, req);\n return content.reorderSpaces(req.params.communityId, req.body.ids, actor);\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/communities/:communityId/spaces/:spaceId',\n schema: {\n operationId: 'getSpace',\n tags,\n summary: 'Get a space',\n description: `Requires \\`communities:read\\`. The space with its topics and links.`,\n params: CommunitySpaceParam,\n response: { 200: SpaceDetail, ...refusals(403, 404) },\n },\n handler: async (req) => {\n const actor = await resolveActor(container, req);\n return content.getCommunitySpace(req.params.communityId, req.params.spaceId, actor);\n },\n });\n\n r.route({\n method: 'PATCH',\n url: '/api/communities/:communityId/spaces/:spaceId',\n schema: {\n operationId: 'updateSpace',\n tags,\n summary: 'Update a space',\n description: `Requires \\`communities:write\\`. Changes only the fields sent; \\`settings\\` merges into the current settings. An \\`all_members\\` space cannot have a capacity (409).`,\n params: CommunitySpaceParam,\n body: UpdateSpace,\n response: { 200: SpaceDetail, ...refusals(403, 404, 409) },\n },\n handler: async (req) => {\n const actor = await resolveActor(container, req);\n return content.updateSpace(req.params.communityId, req.params.spaceId, req.body, actor);\n },\n });\n\n r.route({\n method: 'DELETE',\n url: '/api/communities/:communityId/spaces/:spaceId',\n schema: {\n operationId: 'deleteSpace',\n tags,\n summary: 'Delete a space',\n description: `Requires \\`communities:write\\`. The space and everything in it go.`,\n params: CommunitySpaceParam,\n response: { 204: z.void(), ...refusals(403, 404) },\n },\n handler: async (req, reply) => {\n const actor = await resolveActor(container, req);\n await content.deleteSpace(req.params.communityId, req.params.spaceId, actor);\n return reply.code(204).send();\n },\n });\n\n r.route({\n method: 'PUT',\n url: '/api/communities/:communityId/spaces/:spaceId/topics',\n schema: {\n operationId: 'setSpaceTopics',\n tags,\n summary: 'Replace the topics on a space',\n description: `Requires \\`communities:write\\`. The list replaces the current topics: an entry with an \\`id\\` keeps and renames that topic, one without adds a topic, and a topic left out is deleted. Names must be unique (409), and a topic that posts use cannot be deleted (409).`,\n params: CommunitySpaceParam,\n body: SetSpaceTopics,\n response: { 200: SpaceDetail, ...refusals(403, 404, 409) },\n },\n handler: async (req) => {\n const actor = await resolveActor(container, req);\n return content.setSpaceTopics(req.params.communityId, req.params.spaceId, req.body, actor);\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/communities/:communityId/spaces/:spaceId/links',\n schema: {\n operationId: 'addSpaceLink',\n tags,\n summary: 'Link a course, download, or community into a space',\n description: `Requires \\`communities:write\\`. The link is added last. Linking the same content twice is a 409.`,\n params: CommunitySpaceParam,\n body: SpaceLinkBody,\n response: { 201: SpaceDetail, ...refusals(403, 404, 409) },\n },\n handler: async (req, reply) => {\n const actor = await resolveActor(container, req);\n const space = await content.addSpaceLink(\n req.params.communityId,\n req.params.spaceId,\n req.body.contentId,\n actor,\n );\n return reply.code(201).send(space);\n },\n });\n\n r.route({\n method: 'DELETE',\n url: '/api/communities/:communityId/spaces/:spaceId/links/:linkId',\n schema: {\n operationId: 'removeSpaceLink',\n tags,\n summary: 'Remove a link from a space',\n description: `Requires \\`communities:write\\`. The linked content itself is untouched.`,\n params: SpaceLinkParam,\n response: { 200: SpaceDetail, ...refusals(403, 404) },\n },\n handler: async (req) => {\n const actor = await resolveActor(container, req);\n return content.removeSpaceLink(\n req.params.communityId,\n req.params.spaceId,\n req.params.linkId,\n actor,\n );\n },\n });\n\n r.route({\n method: 'PUT',\n url: '/api/communities/:communityId/spaces/:spaceId/links/order',\n schema: {\n operationId: 'reorderSpaceLinks',\n tags,\n summary: 'Reorder the links in a space',\n description: `Requires \\`communities:write\\`. Send every link id of the space exactly once (409 otherwise).`,\n params: CommunitySpaceParam,\n body: OrderedIds,\n response: { 200: SpaceDetail, ...refusals(403, 404, 409) },\n },\n handler: async (req) => {\n const actor = await resolveActor(container, req);\n return content.reorderSpaceLinks(\n req.params.communityId,\n req.params.spaceId,\n req.body.ids,\n actor,\n );\n },\n });\n}\n"],"mappings":";;;;;;;;AA4BA,eAAsB,kBAAkB,KAAsB,WAAqC;CACjG,MAAM,IAAI,IAAI,iBAAkC;CAChD,MAAM,UAAU,UAAU;CAC1B,MAAM,OAAO,CAAC,aAAa;CAE3B,EAAE,MAAM;EACN,QAAQ;EACR,KAAK;EACL,QAAQ;GACN,aAAa;GACb;GACA,SAAS;GACT,aAAa;GACb,aAAa;GACb,UAAU;IAAE,KAAK;IAAiB,GAAG,SAAS,GAAG;GAAE;EACrD;EACA,SAAS,OAAO,QAAQ;GACtB,MAAM,QAAQ,MAAM,aAAa,WAAW,GAAG;GAC/C,OAAO,QAAQ,gBAAgB,IAAI,OAAO,KAAK;EACjD;CACF,CAAC;CAED,EAAE,MAAM;EACN,QAAQ;EACR,KAAK;EACL,QAAQ;GACN,aAAa;GACb;GACA,SAAS;GACT,aAAa;GACb,MAAM;GACN,UAAU;IAAE,KAAK;IAAW,GAAG,SAAS,KAAK,GAAG;GAAE;EACpD;EACA,SAAS,OAAO,KAAK,UAAU;GAC7B,MAAM,QAAQ,MAAM,aAAa,WAAW,GAAG;GAC/C,MAAM,YAAY,MAAM,QAAQ,gBAAgB,IAAI,MAAM,KAAK;GAC/D,IAAI,IAAI,KAAK,EAAE,aAAa,UAAU,GAAG,GAAG,mBAAmB;GAC/D,OAAO,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,SAAS;EACvC;CACF,CAAC;CAED,EAAE,MAAM;EACN,QAAQ;EACR,KAAK;EACL,QAAQ;GACN,aAAa;GACb;GACA,SAAS;GACT,aAAa;GACb,QAAQ;GACR,UAAU;IAAE,KAAK;IAAW,GAAG,SAAS,KAAK,GAAG;GAAE;EACpD;EACA,SAAS,OAAO,QAAQ;GACtB,MAAM,QAAQ,MAAM,aAAa,WAAW,GAAG;GAC/C,OAAO,QAAQ,aAAa,IAAI,OAAO,aAAa,KAAK;EAC3D;CACF,CAAC;CAED,EAAE,MAAM;EACN,QAAQ;EACR,KAAK;EACL,QAAQ;GACN,aAAa;GACb;GACA,SAAS;GACT,aAAa;GACb,QAAQ;GACR,MAAM;GACN,UAAU;IAAE,KAAK;IAAW,GAAG,SAAS,KAAK,KAAK,GAAG;GAAE;EACzD;EACA,SAAS,OAAO,QAAQ;GACtB,MAAM,QAAQ,MAAM,aAAa,WAAW,GAAG;GAC/C,OAAO,QAAQ,gBAAgB,IAAI,OAAO,aAAa,IAAI,MAAM,KAAK;EACxE;CACF,CAAC;CAED,EAAE,MAAM;EACN,QAAQ;EACR,KAAK;EACL,QAAQ;GACN,aAAa;GACb;GACA,SAAS;GACT,aAAa;GACb,QAAQ;GACR,UAAU;IAAE,KAAK,EAAE,KAAK;IAAG,GAAG,SAAS,KAAK,GAAG;GAAE;EACnD;EACA,SAAS,OAAO,KAAK,UAAU;GAC7B,MAAM,QAAQ,MAAM,aAAa,WAAW,GAAG;GAC/C,MAAM,QAAQ,gBAAgB,IAAI,OAAO,aAAa,KAAK;GAC3D,OAAO,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK;EAC9B;CACF,CAAC;CAED,EAAE,MAAM;EACN,QAAQ;EACR,KAAK;EACL,QAAQ;GACN,aAAa;GACb;GACA,SAAS;GACT,aAAa;GACb,QAAQ;GACR,MAAM;GACN,UAAU;IAAE,KAAK;IAAW,GAAG,SAAS,KAAK,GAAG;GAAE;EACpD;EACA,SAAS,OAAO,QAAQ;GACtB,MAAM,QAAQ,MAAM,aAAa,WAAW,GAAG;GAC/C,OAAO,QAAQ,uBAAuB,IAAI,OAAO,aAAa,IAAI,MAAM,KAAK;EAC/E;CACF,CAAC;CAED,EAAE,MAAM;EACN,QAAQ;EACR,KAAK;EACL,QAAQ;GACN,aAAa;GACb,MAAM,CAAC,eAAe;GACtB,SAAS;GACT,aAAa;GACb,QAAQ;GACR,UAAU;IAAE,KAAK;IAA0B,GAAG,SAAS,KAAK,GAAG;GAAE;EACnE;EACA,SAAS,OAAO,QAAQ;GACtB,MAAM,QAAQ,MAAM,aAAa,WAAW,GAAG;GAC/C,OAAO,UAAU,uBAAuB,UAAU,IAAI,IAAI,OAAO,aAAa,KAAK;EACrF;CACF,CAAC;CAED,EAAE,MAAM;EACN,QAAQ;EACR,KAAK;EACL,QAAQ;GACN,aAAa;GACb,MAAM,CAAC,eAAe;GACtB,SAAS;GACT,aAAa;GACb,QAAQ;GACR,MAAM;GACN,UAAU;IAAE,KAAK;IAA0B,GAAG,SAAS,KAAK,KAAK,KAAK,GAAG;GAAE;EAC7E;EACA,SAAS,OAAO,QAAQ;GACtB,MAAM,QAAQ,MAAM,aAAa,WAAW,GAAG;GAC/C,OAAO,UAAU,uBAAuB,UAAU,QAChD,IAAI,OAAO,aACX,IAAI,KAAK,YACT,KACF;EACF;CACF,CAAC;CAED,EAAE,MAAM;EACN,QAAQ;EACR,KAAK;EACL,QAAQ;GACN,aAAa;GACb,MAAM,CAAC,eAAe;GACtB,SAAS;GACT,aAAa;GACb,QAAQ;GACR,UAAU;IAAE,KAAK,EAAE,KAAK;IAAG,GAAG,SAAS,KAAK,GAAG;GAAE;EACnD;EACA,SAAS,OAAO,KAAK,UAAU;GAC7B,MAAM,QAAQ,MAAM,aAAa,WAAW,GAAG;GAC/C,MAAM,UAAU,uBAAuB,UAAU,MAAM,IAAI,OAAO,aAAa,KAAK;GACpF,OAAO,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK;EAC9B;CACF,CAAC;CAED,EAAE,MAAM;EACN,QAAQ;EACR,KAAK;EACL,QAAQ;GACN,aAAa;GACb;GACA,SAAS;GACT,aAAa;GACb,QAAQ;GACR,UAAU;IAAE,KAAK;IAAW,GAAG,SAAS,KAAK,KAAK,GAAG;GAAE;EACzD;EACA,SAAS,OAAO,QAAQ;GACtB,MAAM,QAAQ,MAAM,aAAa,WAAW,GAAG;GAC/C,OAAO,QAAQ,iBAAiB,IAAI,OAAO,aAAa,KAAK;EAC/D;CACF,CAAC;CAED,EAAE,MAAM;EACN,QAAQ;EACR,KAAK;EACL,QAAQ;GACN,aAAa;GACb;GACA,SAAS;GACT,aAAa;GACb,QAAQ;GACR,UAAU;IAAE,KAAK;IAAW,GAAG,SAAS,KAAK,KAAK,GAAG;GAAE;EACzD;EACA,SAAS,OAAO,QAAQ;GACtB,MAAM,QAAQ,MAAM,aAAa,WAAW,GAAG;GAC/C,OAAO,QAAQ,mBAAmB,IAAI,OAAO,aAAa,KAAK;EACjE;CACF,CAAC;CAED,EAAE,MAAM;EACN,QAAQ;EACR,KAAK;EACL,QAAQ;GACN,aAAa;GACb;GACA,SAAS;GACT,aAAa;GACb,QAAQ;GACR,UAAU;IAAE,KAAK;IAAoB,GAAG,SAAS,KAAK,GAAG;GAAE;EAC7D;EACA,SAAS,OAAO,QAAQ;GACtB,MAAM,QAAQ,MAAM,aAAa,WAAW,GAAG;GAC/C,OAAO,UAAU,UAAU,MAAM,mBAAmB,IAAI,OAAO,aAAa,KAAK;EACnF;CACF,CAAC;CAED,EAAE,MAAM;EACN,QAAQ;EACR,KAAK;EACL,QAAQ;GACN,aAAa;GACb;GACA,SAAS;GACT,aAAa;GACb,QAAQ;GACR,UAAU;IAAE,KAAK;IAAc,GAAG,SAAS,KAAK,GAAG;GAAE;EACvD;EACA,SAAS,OAAO,QAAQ;GACtB,MAAM,QAAQ,MAAM,aAAa,WAAW,GAAG;GAC/C,OAAO,UAAU,MAAM,aAAa,IAAI,OAAO,aAAa,KAAK;EACnE;CACF,CAAC;CAED,EAAE,MAAM;EACN,QAAQ;EACR,KAAK;EACL,QAAQ;GACN,aAAa;GACb;GACA,SAAS;GACT,aAAa;GACb,QAAQ;GACR,UAAU;IAAE,KAAK,EAAE,MAAM,WAAW;IAAG,GAAG,SAAS,KAAK,GAAG;GAAE;EAC/D;EACA,SAAS,OAAO,QAAQ;GACtB,MAAM,QAAQ,MAAM,aAAa,WAAW,GAAG;GAC/C,OAAO,QAAQ,WAAW,IAAI,OAAO,aAAa,KAAK;EACzD;CACF,CAAC;CAED,EAAE,MAAM;EACN,QAAQ;EACR,KAAK;EACL,QAAQ;GACN,aAAa;GACb;GACA,SAAS;GACT,aAAa;GACb,QAAQ;GACR,MAAM;GACN,UAAU;IAAE,KAAK;IAAa,GAAG,SAAS,KAAK,KAAK,GAAG;GAAE;EAC3D;EACA,SAAS,OAAO,KAAK,UAAU;GAC7B,MAAM,QAAQ,MAAM,aAAa,WAAW,GAAG;GAC/C,MAAM,QAAQ,MAAM,QAAQ,YAAY,IAAI,OAAO,aAAa,IAAI,MAAM,KAAK;GAC/E,OAAO,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,KAAK;EACnC;CACF,CAAC;CAED,EAAE,MAAM;EACN,QAAQ;EACR,KAAK;EACL,QAAQ;GACN,aAAa;GACb;GACA,SAAS;GACT,aAAa;GACb,QAAQ;GACR,MAAM;GACN,UAAU;IAAE,KAAK,EAAE,MAAM,WAAW;IAAG,GAAG,SAAS,KAAK,KAAK,GAAG;GAAE;EACpE;EACA,SAAS,OAAO,QAAQ;GACtB,MAAM,QAAQ,MAAM,aAAa,WAAW,GAAG;GAC/C,OAAO,QAAQ,cAAc,IAAI,OAAO,aAAa,IAAI,KAAK,KAAK,KAAK;EAC1E;CACF,CAAC;CAED,EAAE,MAAM;EACN,QAAQ;EACR,KAAK;EACL,QAAQ;GACN,aAAa;GACb;GACA,SAAS;GACT,aAAa;GACb,QAAQ;GACR,UAAU;IAAE,KAAK;IAAa,GAAG,SAAS,KAAK,GAAG;GAAE;EACtD;EACA,SAAS,OAAO,QAAQ;GACtB,MAAM,QAAQ,MAAM,aAAa,WAAW,GAAG;GAC/C,OAAO,QAAQ,kBAAkB,IAAI,OAAO,aAAa,IAAI,OAAO,SAAS,KAAK;EACpF;CACF,CAAC;CAED,EAAE,MAAM;EACN,QAAQ;EACR,KAAK;EACL,QAAQ;GACN,aAAa;GACb;GACA,SAAS;GACT,aAAa;GACb,QAAQ;GACR,MAAM;GACN,UAAU;IAAE,KAAK;IAAa,GAAG,SAAS,KAAK,KAAK,GAAG;GAAE;EAC3D;EACA,SAAS,OAAO,QAAQ;GACtB,MAAM,QAAQ,MAAM,aAAa,WAAW,GAAG;GAC/C,OAAO,QAAQ,YAAY,IAAI,OAAO,aAAa,IAAI,OAAO,SAAS,IAAI,MAAM,KAAK;EACxF;CACF,CAAC;CAED,EAAE,MAAM;EACN,QAAQ;EACR,KAAK;EACL,QAAQ;GACN,aAAa;GACb;GACA,SAAS;GACT,aAAa;GACb,QAAQ;GACR,UAAU;IAAE,KAAK,EAAE,KAAK;IAAG,GAAG,SAAS,KAAK,GAAG;GAAE;EACnD;EACA,SAAS,OAAO,KAAK,UAAU;GAC7B,MAAM,QAAQ,MAAM,aAAa,WAAW,GAAG;GAC/C,MAAM,QAAQ,YAAY,IAAI,OAAO,aAAa,IAAI,OAAO,SAAS,KAAK;GAC3E,OAAO,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK;EAC9B;CACF,CAAC;CAED,EAAE,MAAM;EACN,QAAQ;EACR,KAAK;EACL,QAAQ;GACN,aAAa;GACb;GACA,SAAS;GACT,aAAa;GACb,QAAQ;GACR,MAAM;GACN,UAAU;IAAE,KAAK;IAAa,GAAG,SAAS,KAAK,KAAK,GAAG;GAAE;EAC3D;EACA,SAAS,OAAO,QAAQ;GACtB,MAAM,QAAQ,MAAM,aAAa,WAAW,GAAG;GAC/C,OAAO,QAAQ,eAAe,IAAI,OAAO,aAAa,IAAI,OAAO,SAAS,IAAI,MAAM,KAAK;EAC3F;CACF,CAAC;CAED,EAAE,MAAM;EACN,QAAQ;EACR,KAAK;EACL,QAAQ;GACN,aAAa;GACb;GACA,SAAS;GACT,aAAa;GACb,QAAQ;GACR,MAAM;GACN,UAAU;IAAE,KAAK;IAAa,GAAG,SAAS,KAAK,KAAK,GAAG;GAAE;EAC3D;EACA,SAAS,OAAO,KAAK,UAAU;GAC7B,MAAM,QAAQ,MAAM,aAAa,WAAW,GAAG;GAC/C,MAAM,QAAQ,MAAM,QAAQ,aAC1B,IAAI,OAAO,aACX,IAAI,OAAO,SACX,IAAI,KAAK,WACT,KACF;GACA,OAAO,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,KAAK;EACnC;CACF,CAAC;CAED,EAAE,MAAM;EACN,QAAQ;EACR,KAAK;EACL,QAAQ;GACN,aAAa;GACb;GACA,SAAS;GACT,aAAa;GACb,QAAQ;GACR,UAAU;IAAE,KAAK;IAAa,GAAG,SAAS,KAAK,GAAG;GAAE;EACtD;EACA,SAAS,OAAO,QAAQ;GACtB,MAAM,QAAQ,MAAM,aAAa,WAAW,GAAG;GAC/C,OAAO,QAAQ,gBACb,IAAI,OAAO,aACX,IAAI,OAAO,SACX,IAAI,OAAO,QACX,KACF;EACF;CACF,CAAC;CAED,EAAE,MAAM;EACN,QAAQ;EACR,KAAK;EACL,QAAQ;GACN,aAAa;GACb;GACA,SAAS;GACT,aAAa;GACb,QAAQ;GACR,MAAM;GACN,UAAU;IAAE,KAAK;IAAa,GAAG,SAAS,KAAK,KAAK,GAAG;GAAE;EAC3D;EACA,SAAS,OAAO,QAAQ;GACtB,MAAM,QAAQ,MAAM,aAAa,WAAW,GAAG;GAC/C,OAAO,QAAQ,kBACb,IAAI,OAAO,aACX,IAAI,OAAO,SACX,IAAI,KAAK,KACT,KACF;EACF;CACF,CAAC;AACH"}
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { PageQuery, refusals } from "../schemas/shared.js";
|
|
2
|
-
import {
|
|
2
|
+
import { resolveActor } from "../scope.js";
|
|
3
3
|
import { CommentView, ResolveCommentFlags } from "../schemas/discussion.js";
|
|
4
4
|
import { CommunityChatParam, CommunityCommentParam, CommunityIdParam, CommunityMembersPage, CommunityMembersQuery, CommunityMessageParam, CommunityPostParam, CommunitySpaceParam, SpaceMember, SpaceMemberParam, SpaceMembersPage } from "../schemas/community.js";
|
|
5
5
|
import { PostView, ResolvePostFlags, SpaceQueue } from "../schemas/posts.js";
|
|
6
6
|
import { Chat, OrgUserBody, ResolveMessageFlags } from "../schemas/chats.js";
|
|
7
7
|
import "../schemas/index.js";
|
|
8
8
|
import { z } from "zod";
|
|
9
|
-
import { NotFoundError } from "@headless-lms/core/shared/errors";
|
|
10
9
|
//#region src/http/routes/community-moderation.ts
|
|
11
10
|
async function communityModerationRoutes(app, container) {
|
|
12
11
|
const r = app.withTypeProvider();
|
|
@@ -17,8 +16,8 @@ async function communityModerationRoutes(app, container) {
|
|
|
17
16
|
schema: {
|
|
18
17
|
operationId: "listCommunityMembers",
|
|
19
18
|
tags,
|
|
20
|
-
summary: "List the people who hold a community
|
|
21
|
-
description:
|
|
19
|
+
summary: "List the people who hold a community",
|
|
20
|
+
description: `Requires \`access:read\`. Everyone with active access to the community, granted directly or through a bundle, paged and searched by name or email.`,
|
|
22
21
|
params: CommunityIdParam,
|
|
23
22
|
querystring: CommunityMembersQuery,
|
|
24
23
|
response: {
|
|
@@ -27,10 +26,8 @@ async function communityModerationRoutes(app, container) {
|
|
|
27
26
|
}
|
|
28
27
|
},
|
|
29
28
|
handler: async (req) => {
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
if (!page) throw new NotFoundError("Community", req.params.communityId);
|
|
33
|
-
return page;
|
|
29
|
+
const actor = await resolveActor(container, req);
|
|
30
|
+
return container.reporting.admin.communityMembers(req.params.communityId, req.query, actor);
|
|
34
31
|
}
|
|
35
32
|
});
|
|
36
33
|
r.route({
|
|
@@ -39,8 +36,8 @@ async function communityModerationRoutes(app, container) {
|
|
|
39
36
|
schema: {
|
|
40
37
|
operationId: "listCommunitySpaceMembers",
|
|
41
38
|
tags,
|
|
42
|
-
summary: "List the members, requests and invitations of a space
|
|
43
|
-
description:
|
|
39
|
+
summary: "List the members, requests and invitations of a space",
|
|
40
|
+
description: `For a person who can open the space. Its members; a holder of \`communities:moderate\` on the community also sees its open requests to join and open invitations. People who open every space of the community through their role are not listed. 404 when you cannot open the space.`,
|
|
44
41
|
params: CommunitySpaceParam,
|
|
45
42
|
querystring: PageQuery,
|
|
46
43
|
response: {
|
|
@@ -49,11 +46,8 @@ async function communityModerationRoutes(app, container) {
|
|
|
49
46
|
}
|
|
50
47
|
},
|
|
51
48
|
handler: async (req) => {
|
|
52
|
-
const
|
|
53
|
-
return container.entitlements.listSpaceMembers(
|
|
54
|
-
id: scope.orgUserId,
|
|
55
|
-
role: scope.role
|
|
56
|
-
}, req.params.communityId, req.params.spaceId, req.query);
|
|
49
|
+
const actor = await resolveActor(container, req);
|
|
50
|
+
return container.entitlements.listSpaceMembers(req.params.communityId, req.params.spaceId, req.query, actor);
|
|
57
51
|
}
|
|
58
52
|
});
|
|
59
53
|
r.route({
|
|
@@ -62,8 +56,8 @@ async function communityModerationRoutes(app, container) {
|
|
|
62
56
|
schema: {
|
|
63
57
|
operationId: "addCommunitySpaceMember",
|
|
64
58
|
tags,
|
|
65
|
-
summary: "Add a person to a space
|
|
66
|
-
description:
|
|
59
|
+
summary: "Add a person to a space",
|
|
60
|
+
description: `Requires \`communities:moderate\`. 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
61
|
params: CommunitySpaceParam,
|
|
68
62
|
body: OrgUserBody,
|
|
69
63
|
response: {
|
|
@@ -72,11 +66,8 @@ async function communityModerationRoutes(app, container) {
|
|
|
72
66
|
}
|
|
73
67
|
},
|
|
74
68
|
handler: async (req, reply) => {
|
|
75
|
-
const
|
|
76
|
-
const member = await container.entitlements.addSpaceMember(
|
|
77
|
-
id: scope.orgUserId,
|
|
78
|
-
role: scope.role
|
|
79
|
-
}, req.params.communityId, req.params.spaceId, req.body.orgUserId);
|
|
69
|
+
const actor = await resolveActor(container, req);
|
|
70
|
+
const member = await container.entitlements.addSpaceMember(req.params.communityId, req.params.spaceId, req.body.orgUserId, actor);
|
|
80
71
|
return reply.code(201).send(member);
|
|
81
72
|
}
|
|
82
73
|
});
|
|
@@ -86,8 +77,8 @@ async function communityModerationRoutes(app, container) {
|
|
|
86
77
|
schema: {
|
|
87
78
|
operationId: "inviteCommunitySpaceMember",
|
|
88
79
|
tags,
|
|
89
|
-
summary: "Invite a person into a space
|
|
90
|
-
description:
|
|
80
|
+
summary: "Invite a person into a space",
|
|
81
|
+
description: `Requires \`communities:moderate\`. Invites a member of the community into a \`by_invite\` space; they join when they accept. 403 when the person opens every space or is not a member of the community; 409 when the space is not invite-only or the person is already placed.`,
|
|
91
82
|
params: CommunitySpaceParam,
|
|
92
83
|
body: OrgUserBody,
|
|
93
84
|
response: {
|
|
@@ -96,22 +87,19 @@ async function communityModerationRoutes(app, container) {
|
|
|
96
87
|
}
|
|
97
88
|
},
|
|
98
89
|
handler: async (req, reply) => {
|
|
99
|
-
const
|
|
100
|
-
const member = await container.entitlements.invite(
|
|
101
|
-
id: scope.orgUserId,
|
|
102
|
-
role: scope.role
|
|
103
|
-
}, req.params.communityId, req.params.spaceId, req.body.orgUserId);
|
|
90
|
+
const actor = await resolveActor(container, req);
|
|
91
|
+
const member = await container.entitlements.invite(req.params.communityId, req.params.spaceId, req.body.orgUserId, actor);
|
|
104
92
|
return reply.code(201).send(member);
|
|
105
93
|
}
|
|
106
94
|
});
|
|
107
95
|
r.route({
|
|
108
96
|
method: "POST",
|
|
109
|
-
url: "/api/communities/:communityId/spaces/:spaceId/members/:orgUserId/
|
|
97
|
+
url: "/api/communities/:communityId/spaces/:spaceId/members/:orgUserId/approve",
|
|
110
98
|
schema: {
|
|
111
99
|
operationId: "approveCommunitySpaceRequest",
|
|
112
100
|
tags,
|
|
113
|
-
summary: "Approve a person's request to join a space
|
|
114
|
-
description:
|
|
101
|
+
summary: "Approve a person's request to join a space",
|
|
102
|
+
description: `Requires \`communities:moderate\`. 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
103
|
params: SpaceMemberParam,
|
|
116
104
|
response: {
|
|
117
105
|
200: SpaceMember,
|
|
@@ -119,11 +107,8 @@ async function communityModerationRoutes(app, container) {
|
|
|
119
107
|
}
|
|
120
108
|
},
|
|
121
109
|
handler: async (req) => {
|
|
122
|
-
const
|
|
123
|
-
return container.entitlements.approve(
|
|
124
|
-
id: scope.orgUserId,
|
|
125
|
-
role: scope.role
|
|
126
|
-
}, req.params.communityId, req.params.spaceId, req.params.orgUserId);
|
|
110
|
+
const actor = await resolveActor(container, req);
|
|
111
|
+
return container.entitlements.approve(req.params.communityId, req.params.spaceId, req.params.orgUserId, actor);
|
|
127
112
|
}
|
|
128
113
|
});
|
|
129
114
|
r.route({
|
|
@@ -132,8 +117,8 @@ async function communityModerationRoutes(app, container) {
|
|
|
132
117
|
schema: {
|
|
133
118
|
operationId: "declineCommunitySpaceRequest",
|
|
134
119
|
tags,
|
|
135
|
-
summary: "Decline a person's request to join a space
|
|
136
|
-
description:
|
|
120
|
+
summary: "Decline a person's request to join a space",
|
|
121
|
+
description: `Requires \`communities:moderate\`. Closes the open request. 404 when the person has no open request; 409 when they are already a member.`,
|
|
137
122
|
params: SpaceMemberParam,
|
|
138
123
|
response: {
|
|
139
124
|
200: SpaceMember,
|
|
@@ -141,21 +126,18 @@ async function communityModerationRoutes(app, container) {
|
|
|
141
126
|
}
|
|
142
127
|
},
|
|
143
128
|
handler: async (req) => {
|
|
144
|
-
const
|
|
145
|
-
return container.entitlements.decline(
|
|
146
|
-
id: scope.orgUserId,
|
|
147
|
-
role: scope.role
|
|
148
|
-
}, req.params.communityId, req.params.spaceId, req.params.orgUserId);
|
|
129
|
+
const actor = await resolveActor(container, req);
|
|
130
|
+
return container.entitlements.decline(req.params.communityId, req.params.spaceId, req.params.orgUserId, actor);
|
|
149
131
|
}
|
|
150
132
|
});
|
|
151
133
|
r.route({
|
|
152
134
|
method: "POST",
|
|
153
|
-
url: "/api/communities/:communityId/spaces/:spaceId/members/:orgUserId/
|
|
135
|
+
url: "/api/communities/:communityId/spaces/:spaceId/members/:orgUserId/remove",
|
|
154
136
|
schema: {
|
|
155
137
|
operationId: "removeCommunitySpaceMember",
|
|
156
138
|
tags,
|
|
157
|
-
summary: "Remove a person from a space
|
|
158
|
-
description:
|
|
139
|
+
summary: "Remove a person from a space",
|
|
140
|
+
description: `Requires \`communities:moderate\`. 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
141
|
params: SpaceMemberParam,
|
|
160
142
|
response: {
|
|
161
143
|
200: SpaceMember,
|
|
@@ -163,11 +145,8 @@ async function communityModerationRoutes(app, container) {
|
|
|
163
145
|
}
|
|
164
146
|
},
|
|
165
147
|
handler: async (req) => {
|
|
166
|
-
const
|
|
167
|
-
return container.entitlements.remove(
|
|
168
|
-
id: scope.orgUserId,
|
|
169
|
-
role: scope.role
|
|
170
|
-
}, req.params.communityId, req.params.spaceId, req.params.orgUserId);
|
|
148
|
+
const actor = await resolveActor(container, req);
|
|
149
|
+
return container.entitlements.remove(req.params.communityId, req.params.spaceId, req.params.orgUserId, actor);
|
|
171
150
|
}
|
|
172
151
|
});
|
|
173
152
|
r.route({
|
|
@@ -176,8 +155,8 @@ async function communityModerationRoutes(app, container) {
|
|
|
176
155
|
schema: {
|
|
177
156
|
operationId: "getCommunitySpaceQueue",
|
|
178
157
|
tags,
|
|
179
|
-
summary: "The moderation queue of a space
|
|
180
|
-
description:
|
|
158
|
+
summary: "The moderation queue of a space",
|
|
159
|
+
description: `Requires \`communities:moderate\`. 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
160
|
params: CommunitySpaceParam,
|
|
182
161
|
response: {
|
|
183
162
|
200: SpaceQueue,
|
|
@@ -185,21 +164,18 @@ async function communityModerationRoutes(app, container) {
|
|
|
185
164
|
}
|
|
186
165
|
},
|
|
187
166
|
handler: async (req) => {
|
|
188
|
-
const
|
|
189
|
-
return container.posts.moderationQueue(
|
|
190
|
-
id: scope.orgUserId,
|
|
191
|
-
role: scope.role
|
|
192
|
-
}, req.params.communityId, req.params.spaceId);
|
|
167
|
+
const actor = await resolveActor(container, req);
|
|
168
|
+
return container.posts.moderationQueue(req.params.communityId, req.params.spaceId, actor);
|
|
193
169
|
}
|
|
194
170
|
});
|
|
195
171
|
r.route({
|
|
196
172
|
method: "POST",
|
|
197
|
-
url: "/api/communities/:communityId/posts/:postId/
|
|
173
|
+
url: "/api/communities/:communityId/posts/:postId/approve",
|
|
198
174
|
schema: {
|
|
199
175
|
operationId: "approveCommunityPost",
|
|
200
176
|
tags,
|
|
201
|
-
summary: "Publish a post that is awaiting review
|
|
202
|
-
description:
|
|
177
|
+
summary: "Publish a post that is awaiting review",
|
|
178
|
+
description: `Requires \`communities:moderate\`. 409 when the post is not awaiting review.`,
|
|
203
179
|
params: CommunityPostParam,
|
|
204
180
|
response: {
|
|
205
181
|
200: PostView,
|
|
@@ -207,21 +183,18 @@ async function communityModerationRoutes(app, container) {
|
|
|
207
183
|
}
|
|
208
184
|
},
|
|
209
185
|
handler: async (req) => {
|
|
210
|
-
const
|
|
211
|
-
return container.posts.approve(
|
|
212
|
-
id: scope.orgUserId,
|
|
213
|
-
role: scope.role
|
|
214
|
-
}, req.params.communityId);
|
|
186
|
+
const actor = await resolveActor(container, req);
|
|
187
|
+
return container.posts.approve(req.params.postId, req.params.communityId, actor);
|
|
215
188
|
}
|
|
216
189
|
});
|
|
217
190
|
r.route({
|
|
218
191
|
method: "POST",
|
|
219
|
-
url: "/api/communities/:communityId/posts/:postId/
|
|
192
|
+
url: "/api/communities/:communityId/posts/:postId/remove",
|
|
220
193
|
schema: {
|
|
221
194
|
operationId: "removeCommunityPost",
|
|
222
195
|
tags,
|
|
223
|
-
summary: "Remove a post
|
|
224
|
-
description:
|
|
196
|
+
summary: "Remove a post",
|
|
197
|
+
description: `The author can remove their own post; anyone else needs \`communities:moderate\` on the community. Takes a published or pending post down; removing someone else's post resolves its open flags as \`removed\`. 409 when it is already removed or was never submitted.`,
|
|
225
198
|
params: CommunityPostParam,
|
|
226
199
|
response: {
|
|
227
200
|
200: PostView,
|
|
@@ -229,21 +202,18 @@ async function communityModerationRoutes(app, container) {
|
|
|
229
202
|
}
|
|
230
203
|
},
|
|
231
204
|
handler: async (req) => {
|
|
232
|
-
const
|
|
233
|
-
return container.posts.remove(
|
|
234
|
-
id: scope.orgUserId,
|
|
235
|
-
role: scope.role
|
|
236
|
-
}, req.params.communityId);
|
|
205
|
+
const actor = await resolveActor(container, req);
|
|
206
|
+
return container.posts.remove(req.params.postId, req.params.communityId, actor);
|
|
237
207
|
}
|
|
238
208
|
});
|
|
239
209
|
r.route({
|
|
240
210
|
method: "POST",
|
|
241
|
-
url: "/api/communities/:communityId/posts/:postId/
|
|
211
|
+
url: "/api/communities/:communityId/posts/:postId/restore",
|
|
242
212
|
schema: {
|
|
243
213
|
operationId: "restoreCommunityPost",
|
|
244
214
|
tags,
|
|
245
|
-
summary: "Restore a removed post
|
|
246
|
-
description:
|
|
215
|
+
summary: "Restore a removed post",
|
|
216
|
+
description: `Requires \`communities:moderate\`. 409 when the post is not removed.`,
|
|
247
217
|
params: CommunityPostParam,
|
|
248
218
|
response: {
|
|
249
219
|
200: PostView,
|
|
@@ -251,21 +221,18 @@ async function communityModerationRoutes(app, container) {
|
|
|
251
221
|
}
|
|
252
222
|
},
|
|
253
223
|
handler: async (req) => {
|
|
254
|
-
const
|
|
255
|
-
return container.posts.restore(
|
|
256
|
-
id: scope.orgUserId,
|
|
257
|
-
role: scope.role
|
|
258
|
-
}, req.params.communityId);
|
|
224
|
+
const actor = await resolveActor(container, req);
|
|
225
|
+
return container.posts.restore(req.params.postId, req.params.communityId, actor);
|
|
259
226
|
}
|
|
260
227
|
});
|
|
261
228
|
r.route({
|
|
262
229
|
method: "POST",
|
|
263
|
-
url: "/api/communities/:communityId/posts/:postId/flags/
|
|
230
|
+
url: "/api/communities/:communityId/posts/:postId/flags/resolve",
|
|
264
231
|
schema: {
|
|
265
232
|
operationId: "resolveCommunityPostFlags",
|
|
266
233
|
tags,
|
|
267
|
-
summary: "Dismiss the open flags on a post
|
|
268
|
-
description:
|
|
234
|
+
summary: "Dismiss the open flags on a post",
|
|
235
|
+
description: `Requires \`communities:moderate\`. Closes every open flag on the post and leaves it up.`,
|
|
269
236
|
params: CommunityPostParam,
|
|
270
237
|
body: ResolvePostFlags,
|
|
271
238
|
response: {
|
|
@@ -274,22 +241,19 @@ async function communityModerationRoutes(app, container) {
|
|
|
274
241
|
}
|
|
275
242
|
},
|
|
276
243
|
handler: async (req, reply) => {
|
|
277
|
-
const
|
|
278
|
-
await container.posts.resolveFlags(
|
|
279
|
-
id: scope.orgUserId,
|
|
280
|
-
role: scope.role
|
|
281
|
-
}, req.body.resolution, req.params.communityId);
|
|
244
|
+
const actor = await resolveActor(container, req);
|
|
245
|
+
await container.posts.resolveFlags(req.params.postId, req.body.resolution, req.params.communityId, actor);
|
|
282
246
|
return reply.code(204).send();
|
|
283
247
|
}
|
|
284
248
|
});
|
|
285
249
|
r.route({
|
|
286
250
|
method: "POST",
|
|
287
|
-
url: "/api/communities/:communityId/comments/:commentId/
|
|
251
|
+
url: "/api/communities/:communityId/comments/:commentId/remove",
|
|
288
252
|
schema: {
|
|
289
253
|
operationId: "removeCommunityComment",
|
|
290
254
|
tags,
|
|
291
|
-
summary: "Remove a comment on a post
|
|
292
|
-
description:
|
|
255
|
+
summary: "Remove a comment on a post",
|
|
256
|
+
description: `The author can remove their own comment; anyone else needs \`communities:moderate\` on the community. Hides the comment; removing someone else's comment resolves its open flags as \`removed\`. Removing a removed comment returns it unchanged.`,
|
|
293
257
|
params: CommunityCommentParam,
|
|
294
258
|
response: {
|
|
295
259
|
200: CommentView,
|
|
@@ -297,21 +261,18 @@ async function communityModerationRoutes(app, container) {
|
|
|
297
261
|
}
|
|
298
262
|
},
|
|
299
263
|
handler: async (req) => {
|
|
300
|
-
const
|
|
301
|
-
return container.discussion.remove(
|
|
302
|
-
id: scope.orgUserId,
|
|
303
|
-
role: scope.role
|
|
304
|
-
}, req.params.communityId);
|
|
264
|
+
const actor = await resolveActor(container, req);
|
|
265
|
+
return container.discussion.remove(req.params.commentId, { communityId: req.params.communityId }, actor);
|
|
305
266
|
}
|
|
306
267
|
});
|
|
307
268
|
r.route({
|
|
308
269
|
method: "POST",
|
|
309
|
-
url: "/api/communities/:communityId/comments/:commentId/
|
|
270
|
+
url: "/api/communities/:communityId/comments/:commentId/restore",
|
|
310
271
|
schema: {
|
|
311
272
|
operationId: "restoreCommunityComment",
|
|
312
273
|
tags,
|
|
313
|
-
summary: "Restore a removed comment on a post
|
|
314
|
-
description:
|
|
274
|
+
summary: "Restore a removed comment on a post",
|
|
275
|
+
description: `Requires one of \`courses:moderate\`, \`communities:moderate\`. Publishes the comment again. Restoring a comment that is not removed returns it unchanged.`,
|
|
315
276
|
params: CommunityCommentParam,
|
|
316
277
|
response: {
|
|
317
278
|
200: CommentView,
|
|
@@ -319,21 +280,18 @@ async function communityModerationRoutes(app, container) {
|
|
|
319
280
|
}
|
|
320
281
|
},
|
|
321
282
|
handler: async (req) => {
|
|
322
|
-
const
|
|
323
|
-
return container.discussion.restore(
|
|
324
|
-
id: scope.orgUserId,
|
|
325
|
-
role: scope.role
|
|
326
|
-
}, req.params.communityId);
|
|
283
|
+
const actor = await resolveActor(container, req);
|
|
284
|
+
return container.discussion.restore(req.params.commentId, { communityId: req.params.communityId }, actor);
|
|
327
285
|
}
|
|
328
286
|
});
|
|
329
287
|
r.route({
|
|
330
288
|
method: "POST",
|
|
331
|
-
url: "/api/communities/:communityId/comments/:commentId/flags/
|
|
289
|
+
url: "/api/communities/:communityId/comments/:commentId/flags/resolve",
|
|
332
290
|
schema: {
|
|
333
291
|
operationId: "resolveCommunityCommentFlags",
|
|
334
292
|
tags,
|
|
335
|
-
summary: "Dismiss the open flags on a comment
|
|
336
|
-
description:
|
|
293
|
+
summary: "Dismiss the open flags on a comment",
|
|
294
|
+
description: `Requires one of \`courses:moderate\`, \`communities:moderate\`. Closes every open flag on the comment and leaves it up.`,
|
|
337
295
|
params: CommunityCommentParam,
|
|
338
296
|
body: ResolveCommentFlags,
|
|
339
297
|
response: {
|
|
@@ -342,22 +300,19 @@ async function communityModerationRoutes(app, container) {
|
|
|
342
300
|
}
|
|
343
301
|
},
|
|
344
302
|
handler: async (req, reply) => {
|
|
345
|
-
const
|
|
346
|
-
await container.discussion.resolveReports(
|
|
347
|
-
id: scope.orgUserId,
|
|
348
|
-
role: scope.role
|
|
349
|
-
}, req.params.communityId);
|
|
303
|
+
const actor = await resolveActor(container, req);
|
|
304
|
+
await container.discussion.resolveReports(req.params.commentId, { communityId: req.params.communityId }, actor);
|
|
350
305
|
return reply.code(204).send();
|
|
351
306
|
}
|
|
352
307
|
});
|
|
353
308
|
r.route({
|
|
354
309
|
method: "POST",
|
|
355
|
-
url: "/api/communities/:communityId/messages/:messageId/flags/
|
|
310
|
+
url: "/api/communities/:communityId/messages/:messageId/flags/resolve",
|
|
356
311
|
schema: {
|
|
357
312
|
operationId: "resolveCommunityMessageFlags",
|
|
358
313
|
tags,
|
|
359
|
-
summary: "Dismiss the open flags on a message
|
|
360
|
-
description:
|
|
314
|
+
summary: "Dismiss the open flags on a message",
|
|
315
|
+
description: `Requires \`communities:moderate\`. Closes every open flag on the message and leaves it up.`,
|
|
361
316
|
params: CommunityMessageParam,
|
|
362
317
|
body: ResolveMessageFlags,
|
|
363
318
|
response: {
|
|
@@ -366,11 +321,8 @@ async function communityModerationRoutes(app, container) {
|
|
|
366
321
|
}
|
|
367
322
|
},
|
|
368
323
|
handler: async (req, reply) => {
|
|
369
|
-
const
|
|
370
|
-
await container.chats.resolveMessageFlags(
|
|
371
|
-
id: scope.orgUserId,
|
|
372
|
-
role: scope.role
|
|
373
|
-
}, req.params.messageId, req.body.resolution, req.params.communityId);
|
|
324
|
+
const actor = await resolveActor(container, req);
|
|
325
|
+
await container.chats.resolveMessageFlags(req.params.messageId, req.body.resolution, req.params.communityId, actor);
|
|
374
326
|
return reply.code(204).send();
|
|
375
327
|
}
|
|
376
328
|
});
|
|
@@ -380,8 +332,8 @@ async function communityModerationRoutes(app, container) {
|
|
|
380
332
|
schema: {
|
|
381
333
|
operationId: "lockCommunityChat",
|
|
382
334
|
tags,
|
|
383
|
-
summary: "Lock a chat
|
|
384
|
-
description:
|
|
335
|
+
summary: "Lock a chat",
|
|
336
|
+
description: `Requires \`communities:moderate\`. 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
337
|
params: CommunityChatParam,
|
|
386
338
|
response: {
|
|
387
339
|
200: Chat,
|
|
@@ -389,11 +341,8 @@ async function communityModerationRoutes(app, container) {
|
|
|
389
341
|
}
|
|
390
342
|
},
|
|
391
343
|
handler: async (req) => {
|
|
392
|
-
const
|
|
393
|
-
return container.chats.lock(
|
|
394
|
-
id: scope.orgUserId,
|
|
395
|
-
role: scope.role
|
|
396
|
-
}, req.params.chatId, req.params.communityId);
|
|
344
|
+
const actor = await resolveActor(container, req);
|
|
345
|
+
return container.chats.lock(req.params.chatId, req.params.communityId, actor);
|
|
397
346
|
}
|
|
398
347
|
});
|
|
399
348
|
r.route({
|
|
@@ -402,8 +351,8 @@ async function communityModerationRoutes(app, container) {
|
|
|
402
351
|
schema: {
|
|
403
352
|
operationId: "unlockCommunityChat",
|
|
404
353
|
tags,
|
|
405
|
-
summary: "Unlock a chat
|
|
406
|
-
description:
|
|
354
|
+
summary: "Unlock a chat",
|
|
355
|
+
description: `Requires \`communities:moderate\`. Lets participants send messages again. Unlocking an unlocked chat returns it unchanged.`,
|
|
407
356
|
params: CommunityChatParam,
|
|
408
357
|
response: {
|
|
409
358
|
200: Chat,
|
|
@@ -411,11 +360,8 @@ async function communityModerationRoutes(app, container) {
|
|
|
411
360
|
}
|
|
412
361
|
},
|
|
413
362
|
handler: async (req) => {
|
|
414
|
-
const
|
|
415
|
-
return container.chats.unlock(
|
|
416
|
-
id: scope.orgUserId,
|
|
417
|
-
role: scope.role
|
|
418
|
-
}, req.params.chatId, req.params.communityId);
|
|
363
|
+
const actor = await resolveActor(container, req);
|
|
364
|
+
return container.chats.unlock(req.params.chatId, req.params.communityId, actor);
|
|
419
365
|
}
|
|
420
366
|
});
|
|
421
367
|
}
|