@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,7 +1,7 @@
|
|
|
1
1
|
import { refusals } from "../../schemas/shared.js";
|
|
2
|
-
import {
|
|
2
|
+
import { resolveActor } from "../../scope.js";
|
|
3
|
+
import { ActivityComments, Comment, CommentIdParam, CommentReport, CommentView, DiscussionActivityParam, EditComment, PostComment, Reactions, ReportComment, ResolveCommentFlags, SetReaction } from "../../schemas/discussion.js";
|
|
3
4
|
import "../../schemas/index.js";
|
|
4
|
-
import { actorOf } from "./actor.js";
|
|
5
5
|
import { z } from "zod";
|
|
6
6
|
//#region src/http/routes/member/comments.ts
|
|
7
7
|
async function memberCommentsRoutes(app, container) {
|
|
@@ -16,7 +16,7 @@ async function memberCommentsRoutes(app, container) {
|
|
|
16
16
|
operationId: "listActivityComments",
|
|
17
17
|
tags,
|
|
18
18
|
summary: "Read an activity's comments",
|
|
19
|
-
description: "For
|
|
19
|
+
description: "For anyone who can open the course: through a grant, or with `courses:read` on it. Returns the activity's resolved comment settings with the comments; removed comments keep their place without their body.",
|
|
20
20
|
params: DiscussionActivityParam,
|
|
21
21
|
response: {
|
|
22
22
|
200: ActivityComments,
|
|
@@ -24,8 +24,8 @@ async function memberCommentsRoutes(app, container) {
|
|
|
24
24
|
}
|
|
25
25
|
},
|
|
26
26
|
handler: async (req) => {
|
|
27
|
-
const
|
|
28
|
-
return discussion.activityComments(req.
|
|
27
|
+
const actor = await resolveActor(container, req);
|
|
28
|
+
return discussion.activityComments(req.params.activityId, actor);
|
|
29
29
|
}
|
|
30
30
|
});
|
|
31
31
|
r.route({
|
|
@@ -36,7 +36,7 @@ async function memberCommentsRoutes(app, container) {
|
|
|
36
36
|
operationId: "postComment",
|
|
37
37
|
tags,
|
|
38
38
|
summary: "Post a comment or reply on an activity",
|
|
39
|
-
description: "For
|
|
39
|
+
description: "For anyone who can open the course, while discussion is on for the activity (403 otherwise). Lands pending when the course requires review and you do not hold `courses:moderate` on it. Replies nest one level and need threading on.",
|
|
40
40
|
params: DiscussionActivityParam,
|
|
41
41
|
body: PostComment,
|
|
42
42
|
response: {
|
|
@@ -45,13 +45,12 @@ async function memberCommentsRoutes(app, container) {
|
|
|
45
45
|
}
|
|
46
46
|
},
|
|
47
47
|
handler: async (req, reply) => {
|
|
48
|
-
const
|
|
49
|
-
const comment = await discussion.postComment(
|
|
50
|
-
actor: orgUser,
|
|
48
|
+
const actor = await resolveActor(container, req);
|
|
49
|
+
const comment = await discussion.postComment({
|
|
51
50
|
activityId: req.params.activityId,
|
|
52
51
|
parentId: req.body.parentId,
|
|
53
52
|
body: req.body.body
|
|
54
|
-
});
|
|
53
|
+
}, actor);
|
|
55
54
|
return reply.code(201).send(comment);
|
|
56
55
|
}
|
|
57
56
|
});
|
|
@@ -63,7 +62,7 @@ async function memberCommentsRoutes(app, container) {
|
|
|
63
62
|
operationId: "editComment",
|
|
64
63
|
tags,
|
|
65
64
|
summary: "Revise your own comment",
|
|
66
|
-
description: "Author only; not even
|
|
65
|
+
description: "Author only; not even a moderator may revise it. Discussion must be open where the comment is, and a removed comment cannot be revised.",
|
|
67
66
|
params: CommentIdParam,
|
|
68
67
|
body: EditComment,
|
|
69
68
|
response: {
|
|
@@ -72,8 +71,8 @@ async function memberCommentsRoutes(app, container) {
|
|
|
72
71
|
}
|
|
73
72
|
},
|
|
74
73
|
handler: async (req) => {
|
|
75
|
-
const
|
|
76
|
-
return discussion.edit(req.
|
|
74
|
+
const actor = await resolveActor(container, req);
|
|
75
|
+
return discussion.edit(req.params.commentId, req.body.body, actor);
|
|
77
76
|
}
|
|
78
77
|
});
|
|
79
78
|
r.route({
|
|
@@ -84,7 +83,7 @@ async function memberCommentsRoutes(app, container) {
|
|
|
84
83
|
operationId: "deleteComment",
|
|
85
84
|
tags,
|
|
86
85
|
summary: "Delete your comment, or one you moderate",
|
|
87
|
-
description: "
|
|
86
|
+
description: "The author can delete their own comment; anyone else needs `communities:moderate` on the community for a post comment, or `courses:moderate` on the course for a lesson comment. The comment is marked removed and keeps its place in the thread. Deleting a removed comment returns it unchanged.",
|
|
88
87
|
params: CommentIdParam,
|
|
89
88
|
response: {
|
|
90
89
|
200: CommentView,
|
|
@@ -92,8 +91,8 @@ async function memberCommentsRoutes(app, container) {
|
|
|
92
91
|
}
|
|
93
92
|
},
|
|
94
93
|
handler: async (req) => {
|
|
95
|
-
const
|
|
96
|
-
return discussion.remove(req.
|
|
94
|
+
const actor = await resolveActor(container, req);
|
|
95
|
+
return discussion.remove(req.params.commentId, "any", actor);
|
|
97
96
|
}
|
|
98
97
|
});
|
|
99
98
|
r.route({
|
|
@@ -113,8 +112,8 @@ async function memberCommentsRoutes(app, container) {
|
|
|
113
112
|
}
|
|
114
113
|
},
|
|
115
114
|
handler: async (req) => {
|
|
116
|
-
const
|
|
117
|
-
return discussion.setReaction(req.
|
|
115
|
+
const actor = await resolveActor(container, req);
|
|
116
|
+
return discussion.setReaction(req.params.commentId, req.body.emoji, actor);
|
|
118
117
|
}
|
|
119
118
|
});
|
|
120
119
|
r.route({
|
|
@@ -133,8 +132,8 @@ async function memberCommentsRoutes(app, container) {
|
|
|
133
132
|
}
|
|
134
133
|
},
|
|
135
134
|
handler: async (req) => {
|
|
136
|
-
const
|
|
137
|
-
return discussion.setReaction(req.
|
|
135
|
+
const actor = await resolveActor(container, req);
|
|
136
|
+
return discussion.setReaction(req.params.commentId, null, actor);
|
|
138
137
|
}
|
|
139
138
|
});
|
|
140
139
|
r.route({
|
|
@@ -154,19 +153,19 @@ async function memberCommentsRoutes(app, container) {
|
|
|
154
153
|
}
|
|
155
154
|
},
|
|
156
155
|
handler: async (req) => {
|
|
157
|
-
const
|
|
158
|
-
return discussion.reportComment(req.
|
|
156
|
+
const actor = await resolveActor(container, req);
|
|
157
|
+
return discussion.reportComment(req.params.commentId, req.body.reason, actor);
|
|
159
158
|
}
|
|
160
159
|
});
|
|
161
160
|
r.route({
|
|
162
161
|
method: "POST",
|
|
163
|
-
url: "/api/member/comments/:commentId/flags/
|
|
162
|
+
url: "/api/member/comments/:commentId/flags/resolve",
|
|
164
163
|
preHandler: app.requireOrgSession,
|
|
165
164
|
schema: {
|
|
166
165
|
operationId: "resolveCommentFlags",
|
|
167
166
|
tags,
|
|
168
167
|
summary: "Dismiss the open flags on a comment in a space you moderate",
|
|
169
|
-
description: "
|
|
168
|
+
description: "Needs `courses:moderate` on the course for an activity comment, or `communities:moderate` on the community for a post comment. Closes every open flag and leaves the comment up.",
|
|
170
169
|
params: CommentIdParam,
|
|
171
170
|
body: ResolveCommentFlags,
|
|
172
171
|
response: {
|
|
@@ -175,11 +174,51 @@ async function memberCommentsRoutes(app, container) {
|
|
|
175
174
|
}
|
|
176
175
|
},
|
|
177
176
|
handler: async (req, reply) => {
|
|
178
|
-
const
|
|
179
|
-
await discussion.resolveReports(req.
|
|
177
|
+
const actor = await resolveActor(container, req);
|
|
178
|
+
await discussion.resolveReports(req.params.commentId, "any", actor);
|
|
180
179
|
return reply.code(204).send();
|
|
181
180
|
}
|
|
182
181
|
});
|
|
182
|
+
r.route({
|
|
183
|
+
method: "POST",
|
|
184
|
+
url: "/api/member/comments/:commentId/restore",
|
|
185
|
+
preHandler: app.requireOrgSession,
|
|
186
|
+
schema: {
|
|
187
|
+
operationId: "restoreMemberComment",
|
|
188
|
+
tags,
|
|
189
|
+
summary: "Restore a removed comment where you moderate",
|
|
190
|
+
description: "Needs `courses:moderate` on the course for an activity comment, or `communities:moderate` on the community for a post comment. Publishes the comment again; a comment that is not removed is returned unchanged.",
|
|
191
|
+
params: CommentIdParam,
|
|
192
|
+
response: {
|
|
193
|
+
200: CommentView,
|
|
194
|
+
...refusals(403, 404)
|
|
195
|
+
}
|
|
196
|
+
},
|
|
197
|
+
handler: async (req) => {
|
|
198
|
+
const actor = await resolveActor(container, req);
|
|
199
|
+
return discussion.restore(req.params.commentId, "any", actor);
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
r.route({
|
|
203
|
+
method: "POST",
|
|
204
|
+
url: "/api/member/comments/:commentId/approve",
|
|
205
|
+
preHandler: app.requireOrgSession,
|
|
206
|
+
schema: {
|
|
207
|
+
operationId: "approveMemberComment",
|
|
208
|
+
tags,
|
|
209
|
+
summary: "Publish a comment awaiting review where you moderate",
|
|
210
|
+
description: "Needs `courses:moderate` on the course for an activity comment, or `communities:moderate` on the community for a post comment. 403 when the comment is not awaiting review.",
|
|
211
|
+
params: CommentIdParam,
|
|
212
|
+
response: {
|
|
213
|
+
200: Comment,
|
|
214
|
+
...refusals(403, 404)
|
|
215
|
+
}
|
|
216
|
+
},
|
|
217
|
+
handler: async (req) => {
|
|
218
|
+
const actor = await resolveActor(container, req);
|
|
219
|
+
return discussion.approve(req.params.commentId, actor);
|
|
220
|
+
}
|
|
221
|
+
});
|
|
183
222
|
}
|
|
184
223
|
//#endregion
|
|
185
224
|
export { memberCommentsRoutes };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"comments.js","names":[],"sources":["../../../../src/http/routes/member/comments.ts"],"sourcesContent":["import type { FastifyInstance } from 'fastify';\nimport type { ZodTypeProvider } from 'fastify-type-provider-zod';\nimport { z } from 'zod';\nimport {\n ActivityComments,\n CommentIdParam,\n CommentReport,\n CommentView,\n DiscussionActivityParam,\n EditComment,\n PostComment,\n Reactions,\n ReportComment,\n ResolveCommentFlags,\n SetReaction,\n refusals,\n} from '../../schemas/index.js';\nimport type { Container } from '../../../app/container.js';\nimport { actorOf } from './actor.js';\n\nexport async function memberCommentsRoutes(\n app: FastifyInstance,\n container: Container,\n): Promise<void> {\n const r = app.withTypeProvider<ZodTypeProvider>();\n const discussion = container.discussion;\n const tags = ['Discussion'];\n\n r.route({\n method: 'GET',\n url: '/api/member/activities/:activityId/comments',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'listActivityComments',\n tags,\n summary: \"Read an activity's comments\",\n description:\n \"For a student who can open the course, and staff. Returns the activity's resolved comment settings with the comments; removed comments keep their place without their body.\",\n params: DiscussionActivityParam,\n response: { 200: ActivityComments, ...refusals(403, 404) },\n },\n handler: async (req) => {\n const orgUser = await actorOf(container, req.orgId, req.userId);\n return discussion.activityComments(req.orgId, req.params.activityId, orgUser);\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/member/activities/:activityId/comments',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'postComment',\n tags,\n summary: 'Post a comment or reply on an activity',\n description:\n 'For a student who can open the course, and staff, while discussion is on for the activity (403 otherwise). Lands pending when the course requires review and you are not staff. Replies nest one level and need threading on.',\n params: DiscussionActivityParam,\n body: PostComment,\n response: { 201: CommentView, ...refusals(403, 404) },\n },\n handler: async (req, reply) => {\n const orgUser = await actorOf(container, req.orgId, req.userId);\n const comment = await discussion.postComment(req.orgId, {\n actor: orgUser,\n activityId: req.params.activityId,\n parentId: req.body.parentId,\n body: req.body.body,\n });\n return reply.code(201).send(comment);\n },\n });\n\n r.route({\n method: 'PATCH',\n url: '/api/member/comments/:commentId',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'editComment',\n tags,\n summary: 'Revise your own comment',\n description:\n 'Author only; not even staff may revise it. Discussion must be open where the comment is, and a removed comment cannot be revised.',\n params: CommentIdParam,\n body: EditComment,\n response: { 200: CommentView, ...refusals(403, 404) },\n },\n handler: async (req) => {\n const orgUser = await actorOf(container, req.orgId, req.userId);\n return discussion.edit(req.orgId, req.params.commentId, orgUser, req.body.body);\n },\n });\n\n r.route({\n method: 'DELETE',\n url: '/api/member/comments/:commentId',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'deleteComment',\n tags,\n summary: 'Delete your comment, or one you moderate',\n description:\n 'For the author, staff on an activity comment, or a moderator on a post comment. The comment is marked removed and keeps its place in the thread. Deleting a removed comment returns it unchanged.',\n params: CommentIdParam,\n response: { 200: CommentView, ...refusals(403, 404) },\n },\n handler: async (req) => {\n const orgUser = await actorOf(container, req.orgId, req.userId);\n return discussion.remove(req.orgId, req.params.commentId, orgUser);\n },\n });\n\n r.route({\n method: 'PUT',\n url: '/api/member/comments/:commentId/reaction',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'setCommentReaction',\n tags,\n summary: 'Set your reaction on a comment',\n description:\n 'For anyone who can read the comment, while reactions are on where it is (403 otherwise). Replaces any reaction you already had and returns the new counts.',\n params: CommentIdParam,\n body: SetReaction,\n response: { 200: Reactions, ...refusals(403, 404) },\n },\n handler: async (req) => {\n const orgUser = await actorOf(container, req.orgId, req.userId);\n return discussion.setReaction(req.orgId, req.params.commentId, orgUser, req.body.emoji);\n },\n });\n\n r.route({\n method: 'DELETE',\n url: '/api/member/comments/:commentId/reaction',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'clearCommentReaction',\n tags,\n summary: 'Withdraw your reaction from a comment',\n description: 'For anyone who can read the comment. Returns the new counts.',\n params: CommentIdParam,\n response: { 200: Reactions, ...refusals(403, 404) },\n },\n handler: async (req) => {\n const orgUser = await actorOf(container, req.orgId, req.userId);\n return discussion.setReaction(req.orgId, req.params.commentId, orgUser, null);\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/member/comments/:commentId/reports',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'reportComment',\n tags,\n summary: 'Flag a comment for the moderators',\n description:\n 'For anyone who can read the comment. Flagging it again while your flag is open returns that flag.',\n params: CommentIdParam,\n body: ReportComment,\n response: { 200: CommentReport, ...refusals(403, 404) },\n },\n handler: async (req) => {\n const orgUser = await actorOf(container, req.orgId, req.userId);\n return discussion.reportComment(req.orgId, req.params.commentId, orgUser, req.body.reason);\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/member/comments/:commentId/flags/resolution',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'resolveCommentFlags',\n tags,\n summary: 'Dismiss the open flags on a comment in a space you moderate',\n description:\n 'For a moderator of a post comment, or staff for an activity comment. Closes every open flag and leaves the comment up.',\n params: CommentIdParam,\n body: ResolveCommentFlags,\n response: { 204: z.void(), ...refusals(403, 404) },\n },\n handler: async (req, reply) => {\n const orgUser = await actorOf(container, req.orgId, req.userId);\n await discussion.resolveReports(req.orgId, req.params.commentId, orgUser);\n return reply.code(204).send();\n },\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"comments.js","names":[],"sources":["../../../../src/http/routes/member/comments.ts"],"sourcesContent":["import type { FastifyInstance } from 'fastify';\nimport type { ZodTypeProvider } from 'fastify-type-provider-zod';\nimport { z } from 'zod';\nimport {\n ActivityComments,\n Comment,\n CommentIdParam,\n CommentReport,\n CommentView,\n DiscussionActivityParam,\n EditComment,\n PostComment,\n Reactions,\n ReportComment,\n ResolveCommentFlags,\n SetReaction,\n refusals,\n} from '../../schemas/index.js';\nimport type { Container } from '../../../app/container.js';\nimport { resolveActor } from '../../scope.js';\n\nexport async function memberCommentsRoutes(\n app: FastifyInstance,\n container: Container,\n): Promise<void> {\n const r = app.withTypeProvider<ZodTypeProvider>();\n const discussion = container.discussion;\n const tags = ['Discussion'];\n\n r.route({\n method: 'GET',\n url: '/api/member/activities/:activityId/comments',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'listActivityComments',\n tags,\n summary: \"Read an activity's comments\",\n description:\n \"For anyone who can open the course: through a grant, or with `courses:read` on it. Returns the activity's resolved comment settings with the comments; removed comments keep their place without their body.\",\n params: DiscussionActivityParam,\n response: { 200: ActivityComments, ...refusals(403, 404) },\n },\n handler: async (req) => {\n const actor = await resolveActor(container, req);\n return discussion.activityComments(req.params.activityId, actor);\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/member/activities/:activityId/comments',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'postComment',\n tags,\n summary: 'Post a comment or reply on an activity',\n description:\n 'For anyone who can open the course, while discussion is on for the activity (403 otherwise). Lands pending when the course requires review and you do not hold `courses:moderate` on it. Replies nest one level and need threading on.',\n params: DiscussionActivityParam,\n body: PostComment,\n response: { 201: CommentView, ...refusals(403, 404) },\n },\n handler: async (req, reply) => {\n const actor = await resolveActor(container, req);\n const comment = await discussion.postComment(\n { activityId: req.params.activityId, parentId: req.body.parentId, body: req.body.body },\n actor,\n );\n return reply.code(201).send(comment);\n },\n });\n\n r.route({\n method: 'PATCH',\n url: '/api/member/comments/:commentId',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'editComment',\n tags,\n summary: 'Revise your own comment',\n description:\n 'Author only; not even a moderator may revise it. Discussion must be open where the comment is, and a removed comment cannot be revised.',\n params: CommentIdParam,\n body: EditComment,\n response: { 200: CommentView, ...refusals(403, 404) },\n },\n handler: async (req) => {\n const actor = await resolveActor(container, req);\n return discussion.edit(req.params.commentId, req.body.body, actor);\n },\n });\n\n r.route({\n method: 'DELETE',\n url: '/api/member/comments/:commentId',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'deleteComment',\n tags,\n summary: 'Delete your comment, or one you moderate',\n description:\n 'The author can delete their own comment; anyone else needs `communities:moderate` on the community for a post comment, or `courses:moderate` on the course for a lesson comment. The comment is marked removed and keeps its place in the thread. Deleting a removed comment returns it unchanged.',\n params: CommentIdParam,\n response: { 200: CommentView, ...refusals(403, 404) },\n },\n handler: async (req) => {\n const actor = await resolveActor(container, req);\n return discussion.remove(req.params.commentId, 'any', actor);\n },\n });\n\n r.route({\n method: 'PUT',\n url: '/api/member/comments/:commentId/reaction',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'setCommentReaction',\n tags,\n summary: 'Set your reaction on a comment',\n description:\n 'For anyone who can read the comment, while reactions are on where it is (403 otherwise). Replaces any reaction you already had and returns the new counts.',\n params: CommentIdParam,\n body: SetReaction,\n response: { 200: Reactions, ...refusals(403, 404) },\n },\n handler: async (req) => {\n const actor = await resolveActor(container, req);\n return discussion.setReaction(req.params.commentId, req.body.emoji, actor);\n },\n });\n\n r.route({\n method: 'DELETE',\n url: '/api/member/comments/:commentId/reaction',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'clearCommentReaction',\n tags,\n summary: 'Withdraw your reaction from a comment',\n description: 'For anyone who can read the comment. Returns the new counts.',\n params: CommentIdParam,\n response: { 200: Reactions, ...refusals(403, 404) },\n },\n handler: async (req) => {\n const actor = await resolveActor(container, req);\n return discussion.setReaction(req.params.commentId, null, actor);\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/member/comments/:commentId/reports',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'reportComment',\n tags,\n summary: 'Flag a comment for the moderators',\n description:\n 'For anyone who can read the comment. Flagging it again while your flag is open returns that flag.',\n params: CommentIdParam,\n body: ReportComment,\n response: { 200: CommentReport, ...refusals(403, 404) },\n },\n handler: async (req) => {\n const actor = await resolveActor(container, req);\n return discussion.reportComment(req.params.commentId, req.body.reason, actor);\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/member/comments/:commentId/flags/resolve',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'resolveCommentFlags',\n tags,\n summary: 'Dismiss the open flags on a comment in a space you moderate',\n description:\n 'Needs `courses:moderate` on the course for an activity comment, or `communities:moderate` on the community for a post comment. Closes every open flag and leaves the comment up.',\n params: CommentIdParam,\n body: ResolveCommentFlags,\n response: { 204: z.void(), ...refusals(403, 404) },\n },\n handler: async (req, reply) => {\n const actor = await resolveActor(container, req);\n await discussion.resolveReports(req.params.commentId, 'any', actor);\n return reply.code(204).send();\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/member/comments/:commentId/restore',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'restoreMemberComment',\n tags,\n summary: 'Restore a removed comment where you moderate',\n description:\n 'Needs `courses:moderate` on the course for an activity comment, or `communities:moderate` on the community for a post comment. Publishes the comment again; a comment that is not removed is returned unchanged.',\n params: CommentIdParam,\n response: { 200: CommentView, ...refusals(403, 404) },\n },\n handler: async (req) => {\n const actor = await resolveActor(container, req);\n return discussion.restore(req.params.commentId, 'any', actor);\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/member/comments/:commentId/approve',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'approveMemberComment',\n tags,\n summary: 'Publish a comment awaiting review where you moderate',\n description:\n 'Needs `courses:moderate` on the course for an activity comment, or `communities:moderate` on the community for a post comment. 403 when the comment is not awaiting review.',\n params: CommentIdParam,\n response: { 200: Comment, ...refusals(403, 404) },\n },\n handler: async (req) => {\n const actor = await resolveActor(container, req);\n return discussion.approve(req.params.commentId, actor);\n },\n });\n}\n"],"mappings":"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"}
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { PageQuery, refusals } from "../../schemas/shared.js";
|
|
2
|
+
import { resolveActor } from "../../scope.js";
|
|
2
3
|
import { SetMute } from "../../schemas/discussion.js";
|
|
3
4
|
import { CommunityHome, CommunityHomeSpace, CommunitySpaceParam, DirectoryPage, DirectoryQuery, SpaceIdParam, SpaceMember, SpaceMemberParam, SpaceMembersPage } from "../../schemas/community.js";
|
|
4
5
|
import { CommunityParam, OrgUserBody } from "../../schemas/chats.js";
|
|
5
6
|
import "../../schemas/index.js";
|
|
6
|
-
import { actorOf } from "./actor.js";
|
|
7
7
|
import { z } from "zod";
|
|
8
|
-
import { NotFoundError } from "@headless-lms/core/shared/errors";
|
|
9
8
|
//#region src/http/routes/member/community.ts
|
|
10
9
|
async function memberCommunityRoutes(app, container) {
|
|
11
10
|
const r = app.withTypeProvider();
|
|
@@ -18,7 +17,7 @@ async function memberCommunityRoutes(app, container) {
|
|
|
18
17
|
operationId: "getMemberCommunityHome",
|
|
19
18
|
tags,
|
|
20
19
|
summary: "The spaces a member can see in a community",
|
|
21
|
-
description: "For a member of the community. Lists its spaces in order. Spaces the viewer can open come back in full; spaces they can see but not open come back locked, with only their name, count, rule and the viewer's standing. A by-invite space the viewer is not invited to or a member of is left out. 404 when the viewer is not a member.",
|
|
20
|
+
description: "For a member of the community, through a grant or `communities:read` on it. Lists its spaces in order. Spaces the viewer can open come back in full; spaces they can see but not open come back locked, with only their name, count, rule and the viewer's standing. A by-invite space the viewer is not invited to or a member of is left out, unless the viewer opens every space. `viewer` carries the viewer's permissions on the community, whether they open every space, and their team flag and role name. 404 when the viewer is not a member.",
|
|
22
21
|
params: CommunityParam,
|
|
23
22
|
response: {
|
|
24
23
|
200: CommunityHome,
|
|
@@ -26,10 +25,8 @@ async function memberCommunityRoutes(app, container) {
|
|
|
26
25
|
}
|
|
27
26
|
},
|
|
28
27
|
handler: async (req) => {
|
|
29
|
-
const actor = await
|
|
30
|
-
|
|
31
|
-
if (!home) throw new NotFoundError("Community", req.params.communityId);
|
|
32
|
-
return home;
|
|
28
|
+
const actor = await resolveActor(container, req);
|
|
29
|
+
return container.reporting.member.communityHome(req.params.communityId, actor);
|
|
33
30
|
}
|
|
34
31
|
});
|
|
35
32
|
r.route({
|
|
@@ -48,10 +45,8 @@ async function memberCommunityRoutes(app, container) {
|
|
|
48
45
|
}
|
|
49
46
|
},
|
|
50
47
|
handler: async (req) => {
|
|
51
|
-
const actor = await
|
|
52
|
-
|
|
53
|
-
if (!space) throw new NotFoundError("Space", req.params.spaceId);
|
|
54
|
-
return space;
|
|
48
|
+
const actor = await resolveActor(container, req);
|
|
49
|
+
return container.reporting.member.communitySpace(req.params.communityId, req.params.spaceId, actor);
|
|
55
50
|
}
|
|
56
51
|
});
|
|
57
52
|
r.route({
|
|
@@ -62,7 +57,7 @@ async function memberCommunityRoutes(app, container) {
|
|
|
62
57
|
operationId: "getMemberCommunityDirectory",
|
|
63
58
|
tags,
|
|
64
59
|
summary: "People in a community",
|
|
65
|
-
description: "For a member of the community: everyone who holds it, with their public profile. 404 when the viewer is not a member.",
|
|
60
|
+
description: "For a member of the community: everyone who holds it or holds `communities:read` on it, with their public profile, whether they are team here and under which role name, and whether they open every space. 404 when the viewer is not a member.",
|
|
66
61
|
params: CommunityParam,
|
|
67
62
|
querystring: DirectoryQuery,
|
|
68
63
|
response: {
|
|
@@ -71,10 +66,8 @@ async function memberCommunityRoutes(app, container) {
|
|
|
71
66
|
}
|
|
72
67
|
},
|
|
73
68
|
handler: async (req) => {
|
|
74
|
-
const actor = await
|
|
75
|
-
|
|
76
|
-
if (!page) throw new NotFoundError("Community", req.params.communityId);
|
|
77
|
-
return page;
|
|
69
|
+
const actor = await resolveActor(container, req);
|
|
70
|
+
return container.reporting.member.directory(req.params.communityId, req.query, actor);
|
|
78
71
|
}
|
|
79
72
|
});
|
|
80
73
|
r.route({
|
|
@@ -85,7 +78,7 @@ async function memberCommunityRoutes(app, container) {
|
|
|
85
78
|
operationId: "listSpaceMembers",
|
|
86
79
|
tags,
|
|
87
80
|
summary: "List the members of a space",
|
|
88
|
-
description: "For anyone who can open the space. Members see the active members;
|
|
81
|
+
description: "For anyone who can open the space. Members see the active members; holders of `communities:moderate` on the community also see open requests and invitations. Who opens every space holds no place, and is left out.",
|
|
89
82
|
params: CommunitySpaceParam,
|
|
90
83
|
querystring: PageQuery,
|
|
91
84
|
response: {
|
|
@@ -94,8 +87,8 @@ async function memberCommunityRoutes(app, container) {
|
|
|
94
87
|
}
|
|
95
88
|
},
|
|
96
89
|
handler: async (req) => {
|
|
97
|
-
const actor = await
|
|
98
|
-
return container.entitlements.listSpaceMembers(req.
|
|
90
|
+
const actor = await resolveActor(container, req);
|
|
91
|
+
return container.entitlements.listSpaceMembers(req.params.communityId, req.params.spaceId, req.query, actor);
|
|
99
92
|
}
|
|
100
93
|
});
|
|
101
94
|
r.route({
|
|
@@ -106,7 +99,7 @@ async function memberCommunityRoutes(app, container) {
|
|
|
106
99
|
operationId: "requestSpaceMembership",
|
|
107
100
|
tags,
|
|
108
101
|
summary: "Ask to join a space",
|
|
109
|
-
description: "For a member of the community who
|
|
102
|
+
description: "For a member of the community who does not open every space. Opens a request on a `by_request` space for a moderator to approve. 409 when the space is not `by_request` or you are already placed in it.",
|
|
110
103
|
params: CommunitySpaceParam,
|
|
111
104
|
response: {
|
|
112
105
|
201: SpaceMember,
|
|
@@ -114,8 +107,8 @@ async function memberCommunityRoutes(app, container) {
|
|
|
114
107
|
}
|
|
115
108
|
},
|
|
116
109
|
handler: async (req, reply) => {
|
|
117
|
-
const actor = await
|
|
118
|
-
const member = await container.entitlements.requestJoin(req.
|
|
110
|
+
const actor = await resolveActor(container, req);
|
|
111
|
+
const member = await container.entitlements.requestJoin(req.params.communityId, req.params.spaceId, actor);
|
|
119
112
|
return reply.code(201).send(member);
|
|
120
113
|
}
|
|
121
114
|
});
|
|
@@ -127,7 +120,7 @@ async function memberCommunityRoutes(app, container) {
|
|
|
127
120
|
operationId: "inviteSpaceMember",
|
|
128
121
|
tags,
|
|
129
122
|
summary: "Invite a person into a space you moderate",
|
|
130
|
-
description:
|
|
123
|
+
description: `Requires \`communities:moderate\`. On the community, as its member. Invites another member 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.`,
|
|
131
124
|
params: CommunitySpaceParam,
|
|
132
125
|
body: OrgUserBody,
|
|
133
126
|
response: {
|
|
@@ -136,8 +129,8 @@ async function memberCommunityRoutes(app, container) {
|
|
|
136
129
|
}
|
|
137
130
|
},
|
|
138
131
|
handler: async (req, reply) => {
|
|
139
|
-
const actor = await
|
|
140
|
-
const member = await container.entitlements.invite(req.
|
|
132
|
+
const actor = await resolveActor(container, req);
|
|
133
|
+
const member = await container.entitlements.invite(req.params.communityId, req.params.spaceId, req.body.orgUserId, actor);
|
|
141
134
|
return reply.code(201).send(member);
|
|
142
135
|
}
|
|
143
136
|
});
|
|
@@ -149,7 +142,7 @@ async function memberCommunityRoutes(app, container) {
|
|
|
149
142
|
operationId: "addSpaceMember",
|
|
150
143
|
tags,
|
|
151
144
|
summary: "Add a person to a space you moderate",
|
|
152
|
-
description:
|
|
145
|
+
description: `Requires \`communities:moderate\`. On the community, as its member. Places another member straight into the space, with no request or invitation. 403 when the person opens every space; 409 when the space is open to all members, the person is already placed, or the space is full.`,
|
|
153
146
|
params: CommunitySpaceParam,
|
|
154
147
|
body: OrgUserBody,
|
|
155
148
|
response: {
|
|
@@ -158,20 +151,20 @@ async function memberCommunityRoutes(app, container) {
|
|
|
158
151
|
}
|
|
159
152
|
},
|
|
160
153
|
handler: async (req, reply) => {
|
|
161
|
-
const actor = await
|
|
162
|
-
const member = await container.entitlements.addSpaceMember(req.
|
|
154
|
+
const actor = await resolveActor(container, req);
|
|
155
|
+
const member = await container.entitlements.addSpaceMember(req.params.communityId, req.params.spaceId, req.body.orgUserId, actor);
|
|
163
156
|
return reply.code(201).send(member);
|
|
164
157
|
}
|
|
165
158
|
});
|
|
166
159
|
r.route({
|
|
167
160
|
method: "POST",
|
|
168
|
-
url: "/api/member/communities/:communityId/spaces/:spaceId/
|
|
161
|
+
url: "/api/member/communities/:communityId/spaces/:spaceId/accept",
|
|
169
162
|
preHandler: app.requireOrgSession,
|
|
170
163
|
schema: {
|
|
171
164
|
operationId: "acceptSpaceInvite",
|
|
172
165
|
tags,
|
|
173
166
|
summary: "Accept your invitation to a space",
|
|
174
|
-
description: "For a member of the community who
|
|
167
|
+
description: "For a member of the community who does not open every space. Makes you an active member. 409 when you have no open invitation, are already a member, or the space is full.",
|
|
175
168
|
params: CommunitySpaceParam,
|
|
176
169
|
response: {
|
|
177
170
|
200: SpaceMember,
|
|
@@ -179,19 +172,19 @@ async function memberCommunityRoutes(app, container) {
|
|
|
179
172
|
}
|
|
180
173
|
},
|
|
181
174
|
handler: async (req) => {
|
|
182
|
-
const actor = await
|
|
183
|
-
return container.entitlements.accept(req.
|
|
175
|
+
const actor = await resolveActor(container, req);
|
|
176
|
+
return container.entitlements.accept(req.params.communityId, req.params.spaceId, actor);
|
|
184
177
|
}
|
|
185
178
|
});
|
|
186
179
|
r.route({
|
|
187
180
|
method: "POST",
|
|
188
|
-
url: "/api/member/communities/:communityId/spaces/:spaceId/members/:orgUserId/
|
|
181
|
+
url: "/api/member/communities/:communityId/spaces/:spaceId/members/:orgUserId/approve",
|
|
189
182
|
preHandler: app.requireOrgSession,
|
|
190
183
|
schema: {
|
|
191
184
|
operationId: "approveSpaceRequest",
|
|
192
185
|
tags,
|
|
193
186
|
summary: "Approve a person's request to join a space you moderate",
|
|
194
|
-
description:
|
|
187
|
+
description: `Requires \`communities:moderate\`. On the community, as its member. 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.`,
|
|
195
188
|
params: SpaceMemberParam,
|
|
196
189
|
response: {
|
|
197
190
|
200: SpaceMember,
|
|
@@ -199,8 +192,8 @@ async function memberCommunityRoutes(app, container) {
|
|
|
199
192
|
}
|
|
200
193
|
},
|
|
201
194
|
handler: async (req) => {
|
|
202
|
-
const actor = await
|
|
203
|
-
return container.entitlements.approve(req.
|
|
195
|
+
const actor = await resolveActor(container, req);
|
|
196
|
+
return container.entitlements.approve(req.params.communityId, req.params.spaceId, req.params.orgUserId, actor);
|
|
204
197
|
}
|
|
205
198
|
});
|
|
206
199
|
r.route({
|
|
@@ -211,7 +204,7 @@ async function memberCommunityRoutes(app, container) {
|
|
|
211
204
|
operationId: "declineSpaceRequest",
|
|
212
205
|
tags,
|
|
213
206
|
summary: "Decline a person's request to join a space you moderate",
|
|
214
|
-
description:
|
|
207
|
+
description: `Requires \`communities:moderate\`. On the community, as its member. Closes the open request. 404 when the person has no open request; 409 when they are already a member.`,
|
|
215
208
|
params: SpaceMemberParam,
|
|
216
209
|
response: {
|
|
217
210
|
200: SpaceMember,
|
|
@@ -219,19 +212,19 @@ async function memberCommunityRoutes(app, container) {
|
|
|
219
212
|
}
|
|
220
213
|
},
|
|
221
214
|
handler: async (req) => {
|
|
222
|
-
const actor = await
|
|
223
|
-
return container.entitlements.decline(req.
|
|
215
|
+
const actor = await resolveActor(container, req);
|
|
216
|
+
return container.entitlements.decline(req.params.communityId, req.params.spaceId, req.params.orgUserId, actor);
|
|
224
217
|
}
|
|
225
218
|
});
|
|
226
219
|
r.route({
|
|
227
220
|
method: "POST",
|
|
228
|
-
url: "/api/member/communities/:communityId/spaces/:spaceId/
|
|
221
|
+
url: "/api/member/communities/:communityId/spaces/:spaceId/refuse",
|
|
229
222
|
preHandler: app.requireOrgSession,
|
|
230
223
|
schema: {
|
|
231
224
|
operationId: "refuseSpaceInvite",
|
|
232
225
|
tags,
|
|
233
226
|
summary: "Refuse your invitation to a space",
|
|
234
|
-
description: "For a member of the community who
|
|
227
|
+
description: "For a member of the community who does not open every space. 409 when you have no open invitation or are already a member.",
|
|
235
228
|
params: CommunitySpaceParam,
|
|
236
229
|
response: {
|
|
237
230
|
200: SpaceMember,
|
|
@@ -239,8 +232,8 @@ async function memberCommunityRoutes(app, container) {
|
|
|
239
232
|
}
|
|
240
233
|
},
|
|
241
234
|
handler: async (req) => {
|
|
242
|
-
const actor = await
|
|
243
|
-
return container.entitlements.refuse(req.
|
|
235
|
+
const actor = await resolveActor(container, req);
|
|
236
|
+
return container.entitlements.refuse(req.params.communityId, req.params.spaceId, actor);
|
|
244
237
|
}
|
|
245
238
|
});
|
|
246
239
|
r.route({
|
|
@@ -251,7 +244,7 @@ async function memberCommunityRoutes(app, container) {
|
|
|
251
244
|
operationId: "leaveSpace",
|
|
252
245
|
tags,
|
|
253
246
|
summary: "Leave a space",
|
|
254
|
-
description: "For a member of the community who
|
|
247
|
+
description: "For a member of the community who does not open every space. 409 when you are not a member of the space.",
|
|
255
248
|
params: CommunitySpaceParam,
|
|
256
249
|
response: {
|
|
257
250
|
200: SpaceMember,
|
|
@@ -259,19 +252,19 @@ async function memberCommunityRoutes(app, container) {
|
|
|
259
252
|
}
|
|
260
253
|
},
|
|
261
254
|
handler: async (req) => {
|
|
262
|
-
const actor = await
|
|
263
|
-
return container.entitlements.leave(req.
|
|
255
|
+
const actor = await resolveActor(container, req);
|
|
256
|
+
return container.entitlements.leave(req.params.communityId, req.params.spaceId, actor);
|
|
264
257
|
}
|
|
265
258
|
});
|
|
266
259
|
r.route({
|
|
267
260
|
method: "POST",
|
|
268
|
-
url: "/api/member/communities/:communityId/spaces/:spaceId/members/:orgUserId/
|
|
261
|
+
url: "/api/member/communities/:communityId/spaces/:spaceId/members/:orgUserId/remove",
|
|
269
262
|
preHandler: app.requireOrgSession,
|
|
270
263
|
schema: {
|
|
271
264
|
operationId: "removeSpaceMember",
|
|
272
265
|
tags,
|
|
273
266
|
summary: "Remove a person from a space you moderate",
|
|
274
|
-
description:
|
|
267
|
+
description: `Requires \`communities:moderate\`. On the community, as its member. 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.`,
|
|
275
268
|
params: SpaceMemberParam,
|
|
276
269
|
response: {
|
|
277
270
|
200: SpaceMember,
|
|
@@ -279,8 +272,8 @@ async function memberCommunityRoutes(app, container) {
|
|
|
279
272
|
}
|
|
280
273
|
},
|
|
281
274
|
handler: async (req) => {
|
|
282
|
-
const actor = await
|
|
283
|
-
return container.entitlements.remove(req.
|
|
275
|
+
const actor = await resolveActor(container, req);
|
|
276
|
+
return container.entitlements.remove(req.params.communityId, req.params.spaceId, req.params.orgUserId, actor);
|
|
284
277
|
}
|
|
285
278
|
});
|
|
286
279
|
r.route({
|
|
@@ -300,8 +293,8 @@ async function memberCommunityRoutes(app, container) {
|
|
|
300
293
|
}
|
|
301
294
|
},
|
|
302
295
|
handler: async (req, reply) => {
|
|
303
|
-
const actor = await
|
|
304
|
-
await container.subscriptions.setSpace(req.
|
|
296
|
+
const actor = await resolveActor(container, req);
|
|
297
|
+
await container.subscriptions.setSpace(req.params.spaceId, req.body, actor);
|
|
305
298
|
return reply.code(204).send();
|
|
306
299
|
}
|
|
307
300
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"community.js","names":[],"sources":["../../../../src/http/routes/member/community.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 { actorOf } from './actor.js';\nimport {\n CommunityHome,\n CommunityHomeSpace,\n CommunityParam,\n CommunitySpaceParam,\n DirectoryPage,\n DirectoryQuery,\n OrgUserBody,\n PageQuery,\n SetMute,\n SpaceIdParam,\n SpaceMember,\n SpaceMemberParam,\n SpaceMembersPage,\n refusals,\n} from '../../schemas/index.js';\n\nexport async function memberCommunityRoutes(\n app: FastifyInstance,\n container: Container,\n): Promise<void> {\n const r = app.withTypeProvider<ZodTypeProvider>();\n const tags = ['Communities'];\n\n r.route({\n method: 'GET',\n url: '/api/member/communities/:communityId/home',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'getMemberCommunityHome',\n tags,\n summary: 'The spaces a member can see in a community',\n description:\n \"For a member of the community. Lists its spaces in order. Spaces the viewer can open come back in full; spaces they can see but not open come back locked, with only their name, count, rule and the viewer's standing. A by-invite space the viewer is not invited to or a member of is left out. 404 when the viewer is not a member.\",\n params: CommunityParam,\n response: { 200: CommunityHome, ...refusals(403, 404) },\n },\n handler: async (req) => {\n const actor = await actorOf(container, req.orgId, req.userId);\n const home = await container.reporting.member.communityHome(\n req.orgId,\n actor,\n req.params.communityId,\n );\n if (!home) {\n throw new NotFoundError('Community', req.params.communityId);\n }\n return home;\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/member/communities/:communityId/spaces/:spaceId',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'getMemberSpace',\n tags,\n summary: 'One space as a member sees it',\n description:\n 'For a member of the community: the space with its topics, links, settings and where the viewer stands. A space the viewer cannot open comes back locked, without its content. 404 when the viewer is not a member, the space is not in the community, or it is a by-invite space the viewer is not invited to or a member of.',\n params: CommunitySpaceParam,\n response: { 200: CommunityHomeSpace, ...refusals(403, 404) },\n },\n handler: async (req) => {\n const actor = await actorOf(container, req.orgId, req.userId);\n const space = await container.reporting.member.communitySpace(\n req.orgId,\n actor,\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: 'GET',\n url: '/api/member/communities/:communityId/directory',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'getMemberCommunityDirectory',\n tags,\n summary: 'People in a community',\n description:\n 'For a member of the community: everyone who holds it, with their public profile. 404 when the viewer is not a member.',\n params: CommunityParam,\n querystring: DirectoryQuery,\n response: { 200: DirectoryPage, ...refusals(403, 404) },\n },\n handler: async (req) => {\n const actor = await actorOf(container, req.orgId, req.userId);\n const page = await container.reporting.member.directory(\n req.orgId,\n actor,\n req.params.communityId,\n req.query,\n );\n if (!page) {\n throw new NotFoundError('Community', req.params.communityId);\n }\n return page;\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/member/communities/:communityId/spaces/:spaceId/members',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'listSpaceMembers',\n tags,\n summary: 'List the members of a space',\n description:\n 'For anyone who can open the space. Members see the active members; moderators also see open requests and invitations.',\n params: CommunitySpaceParam,\n querystring: PageQuery,\n response: { 200: SpaceMembersPage, ...refusals(403, 404) },\n },\n handler: async (req) => {\n const actor = await actorOf(container, req.orgId, req.userId);\n return container.entitlements.listSpaceMembers(\n req.orgId,\n actor,\n req.params.communityId,\n req.params.spaceId,\n req.query,\n );\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/member/communities/:communityId/spaces/:spaceId/requests',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'requestSpaceMembership',\n tags,\n summary: 'Ask to join a space',\n description:\n 'For a member of the community who is not staff. Opens a request on a `by_request` space for a moderator to approve. 409 when the space is not `by_request` or you are already placed in it.',\n params: CommunitySpaceParam,\n response: { 201: SpaceMember, ...refusals(403, 404, 409) },\n },\n handler: async (req, reply) => {\n const actor = await actorOf(container, req.orgId, req.userId);\n const member = await container.entitlements.requestJoin(\n req.orgId,\n actor,\n req.params.communityId,\n req.params.spaceId,\n );\n return reply.code(201).send(member);\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/member/communities/:communityId/spaces/:spaceId/invites',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'inviteSpaceMember',\n tags,\n summary: 'Invite a person into a space you moderate',\n description:\n 'For a moderator who is a member of the community. Invites another member into a `by_invite` space; they join when they accept. 403 when the person is staff or does not hold the community; 409 when the space is not invite-only or the person is already placed.',\n params: CommunitySpaceParam,\n body: OrgUserBody,\n response: { 201: SpaceMember, ...refusals(403, 404, 409) },\n },\n handler: async (req, reply) => {\n const actor = await actorOf(container, req.orgId, req.userId);\n const member = await container.entitlements.invite(\n req.orgId,\n actor,\n req.params.communityId,\n req.params.spaceId,\n req.body.orgUserId,\n );\n return reply.code(201).send(member);\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/member/communities/:communityId/spaces/:spaceId/members',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'addSpaceMember',\n tags,\n summary: 'Add a person to a space you moderate',\n description:\n 'For a moderator who is a member of the community. Places another member straight into the space, with no request or invitation. 409 when the space is open to all members, the person is already placed, or the space is full.',\n params: CommunitySpaceParam,\n body: OrgUserBody,\n response: { 201: SpaceMember, ...refusals(403, 404, 409) },\n },\n handler: async (req, reply) => {\n const actor = await actorOf(container, req.orgId, req.userId);\n const member = await container.entitlements.addSpaceMember(\n req.orgId,\n actor,\n req.params.communityId,\n req.params.spaceId,\n req.body.orgUserId,\n );\n return reply.code(201).send(member);\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/member/communities/:communityId/spaces/:spaceId/acceptance',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'acceptSpaceInvite',\n tags,\n summary: 'Accept your invitation to a space',\n description:\n 'For a member of the community who is not staff. Makes you an active member. 409 when you have no open invitation, are already a member, or the space is full.',\n params: CommunitySpaceParam,\n response: { 200: SpaceMember, ...refusals(403, 404, 409) },\n },\n handler: async (req) => {\n const actor = await actorOf(container, req.orgId, req.userId);\n return container.entitlements.accept(\n req.orgId,\n actor,\n req.params.communityId,\n req.params.spaceId,\n );\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/member/communities/:communityId/spaces/:spaceId/members/:orgUserId/approval',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'approveSpaceRequest',\n tags,\n summary: \"Approve a person's request to join a space you moderate\",\n description:\n 'For a moderator who is a member of the community. Makes the requester an active member. 404 when the person has no open request; 409 when the space is full or they are already a member.',\n params: SpaceMemberParam,\n response: { 200: SpaceMember, ...refusals(403, 404, 409) },\n },\n handler: async (req) => {\n const actor = await actorOf(container, req.orgId, req.userId);\n return container.entitlements.approve(\n req.orgId,\n actor,\n req.params.communityId,\n req.params.spaceId,\n req.params.orgUserId,\n );\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/member/communities/:communityId/spaces/:spaceId/members/:orgUserId/decline',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'declineSpaceRequest',\n tags,\n summary: \"Decline a person's request to join a space you moderate\",\n description:\n 'For a moderator who is a member of the community. Closes the open request. 404 when the person has no open request; 409 when they are already a member.',\n params: SpaceMemberParam,\n response: { 200: SpaceMember, ...refusals(403, 404, 409) },\n },\n handler: async (req) => {\n const actor = await actorOf(container, req.orgId, req.userId);\n return container.entitlements.decline(\n req.orgId,\n actor,\n req.params.communityId,\n req.params.spaceId,\n req.params.orgUserId,\n );\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/member/communities/:communityId/spaces/:spaceId/refusal',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'refuseSpaceInvite',\n tags,\n summary: 'Refuse your invitation to a space',\n description:\n 'For a member of the community who is not staff. 409 when you have no open invitation or are already a member.',\n params: CommunitySpaceParam,\n response: { 200: SpaceMember, ...refusals(403, 404, 409) },\n },\n handler: async (req) => {\n const actor = await actorOf(container, req.orgId, req.userId);\n return container.entitlements.refuse(\n req.orgId,\n actor,\n req.params.communityId,\n req.params.spaceId,\n );\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/member/communities/:communityId/spaces/:spaceId/departure',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'leaveSpace',\n tags,\n summary: 'Leave a space',\n description:\n 'For a member of the community who is not staff. 409 when you are not a member of the space.',\n params: CommunitySpaceParam,\n response: { 200: SpaceMember, ...refusals(403, 404, 409) },\n },\n handler: async (req) => {\n const actor = await actorOf(container, req.orgId, req.userId);\n return container.entitlements.leave(\n req.orgId,\n actor,\n req.params.communityId,\n req.params.spaceId,\n );\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/member/communities/:communityId/spaces/:spaceId/members/:orgUserId/removal',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'removeSpaceMember',\n tags,\n summary: 'Remove a person from a space you moderate',\n description:\n 'For a moderator who is a member of the community. Revokes the placement, whether it is a membership, a request or an invitation. 409 when the space is open to all members or the placement is already revoked.',\n params: SpaceMemberParam,\n response: { 200: SpaceMember, ...refusals(403, 404, 409) },\n },\n handler: async (req) => {\n const actor = await actorOf(container, req.orgId, req.userId);\n return container.entitlements.remove(\n req.orgId,\n actor,\n req.params.communityId,\n req.params.spaceId,\n req.params.orgUserId,\n );\n },\n });\n\n r.route({\n method: 'PUT',\n url: '/api/member/spaces/:spaceId/subscription',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'setSpaceSubscription',\n tags,\n summary: 'Mute a space, or clear your mute',\n description:\n 'For anyone who can open the space. `muted` stops its activity notifying you, until `mutedUntil` when given; mentions, actions on you and moderation work still notify. `none` clears the mute.',\n params: SpaceIdParam,\n body: SetMute,\n response: { 204: z.void(), ...refusals(403, 404) },\n },\n handler: async (req, reply) => {\n const actor = await actorOf(container, req.orgId, req.userId);\n await container.subscriptions.setSpace(req.orgId, actor, req.params.spaceId, req.body);\n return reply.code(204).send();\n },\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"community.js","names":[],"sources":["../../../../src/http/routes/member/community.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 CommunityHome,\n CommunityHomeSpace,\n CommunityParam,\n CommunitySpaceParam,\n DirectoryPage,\n DirectoryQuery,\n OrgUserBody,\n PageQuery,\n SetMute,\n SpaceIdParam,\n SpaceMember,\n SpaceMemberParam,\n SpaceMembersPage,\n refusals,\n} from '../../schemas/index.js';\n\nexport async function memberCommunityRoutes(\n app: FastifyInstance,\n container: Container,\n): Promise<void> {\n const r = app.withTypeProvider<ZodTypeProvider>();\n const tags = ['Communities'];\n\n r.route({\n method: 'GET',\n url: '/api/member/communities/:communityId/home',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'getMemberCommunityHome',\n tags,\n summary: 'The spaces a member can see in a community',\n description:\n \"For a member of the community, through a grant or `communities:read` on it. Lists its spaces in order. Spaces the viewer can open come back in full; spaces they can see but not open come back locked, with only their name, count, rule and the viewer's standing. A by-invite space the viewer is not invited to or a member of is left out, unless the viewer opens every space. `viewer` carries the viewer's permissions on the community, whether they open every space, and their team flag and role name. 404 when the viewer is not a member.\",\n params: CommunityParam,\n response: { 200: CommunityHome, ...refusals(403, 404) },\n },\n handler: async (req) => {\n const actor = await resolveActor(container, req);\n return container.reporting.member.communityHome(req.params.communityId, actor);\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/member/communities/:communityId/spaces/:spaceId',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'getMemberSpace',\n tags,\n summary: 'One space as a member sees it',\n description:\n 'For a member of the community: the space with its topics, links, settings and where the viewer stands. A space the viewer cannot open comes back locked, without its content. 404 when the viewer is not a member, the space is not in the community, or it is a by-invite space the viewer is not invited to or a member of.',\n params: CommunitySpaceParam,\n response: { 200: CommunityHomeSpace, ...refusals(403, 404) },\n },\n handler: async (req) => {\n const actor = await resolveActor(container, req);\n return container.reporting.member.communitySpace(\n req.params.communityId,\n req.params.spaceId,\n actor,\n );\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/member/communities/:communityId/directory',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'getMemberCommunityDirectory',\n tags,\n summary: 'People in a community',\n description:\n 'For a member of the community: everyone who holds it or holds `communities:read` on it, with their public profile, whether they are team here and under which role name, and whether they open every space. 404 when the viewer is not a member.',\n params: CommunityParam,\n querystring: DirectoryQuery,\n response: { 200: DirectoryPage, ...refusals(403, 404) },\n },\n handler: async (req) => {\n const actor = await resolveActor(container, req);\n return container.reporting.member.directory(req.params.communityId, req.query, actor);\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/member/communities/:communityId/spaces/:spaceId/members',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'listSpaceMembers',\n tags,\n summary: 'List the members of a space',\n description:\n 'For anyone who can open the space. Members see the active members; holders of `communities:moderate` on the community also see open requests and invitations. Who opens every space holds no place, and is left out.',\n params: CommunitySpaceParam,\n querystring: PageQuery,\n response: { 200: SpaceMembersPage, ...refusals(403, 404) },\n },\n handler: async (req) => {\n const actor = await resolveActor(container, req);\n return container.entitlements.listSpaceMembers(\n req.params.communityId,\n req.params.spaceId,\n req.query,\n actor,\n );\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/member/communities/:communityId/spaces/:spaceId/requests',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'requestSpaceMembership',\n tags,\n summary: 'Ask to join a space',\n description:\n 'For a member of the community who does not open every space. Opens a request on a `by_request` space for a moderator to approve. 409 when the space is not `by_request` or you are already placed in it.',\n params: CommunitySpaceParam,\n response: { 201: SpaceMember, ...refusals(403, 404, 409) },\n },\n handler: async (req, reply) => {\n const actor = await resolveActor(container, req);\n const member = await container.entitlements.requestJoin(\n req.params.communityId,\n req.params.spaceId,\n actor,\n );\n return reply.code(201).send(member);\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/member/communities/:communityId/spaces/:spaceId/invites',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'inviteSpaceMember',\n tags,\n summary: 'Invite a person into a space you moderate',\n description: `Requires \\`communities:moderate\\`. On the community, as its member. Invites another member 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.`,\n params: CommunitySpaceParam,\n body: OrgUserBody,\n response: { 201: SpaceMember, ...refusals(403, 404, 409) },\n },\n handler: async (req, reply) => {\n const actor = await resolveActor(container, req);\n const member = await container.entitlements.invite(\n req.params.communityId,\n req.params.spaceId,\n req.body.orgUserId,\n actor,\n );\n return reply.code(201).send(member);\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/member/communities/:communityId/spaces/:spaceId/members',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'addSpaceMember',\n tags,\n summary: 'Add a person to a space you moderate',\n description: `Requires \\`communities:moderate\\`. On the community, as its member. Places another member straight into the space, with no request or invitation. 403 when the person opens every space; 409 when the space is open to all members, the person is already placed, or the space is full.`,\n params: CommunitySpaceParam,\n body: OrgUserBody,\n response: { 201: SpaceMember, ...refusals(403, 404, 409) },\n },\n handler: async (req, reply) => {\n const actor = await resolveActor(container, req);\n const member = await container.entitlements.addSpaceMember(\n req.params.communityId,\n req.params.spaceId,\n req.body.orgUserId,\n actor,\n );\n return reply.code(201).send(member);\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/member/communities/:communityId/spaces/:spaceId/accept',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'acceptSpaceInvite',\n tags,\n summary: 'Accept your invitation to a space',\n description:\n 'For a member of the community who does not open every space. Makes you an active member. 409 when you have no open invitation, are already a member, or the space is full.',\n params: CommunitySpaceParam,\n response: { 200: SpaceMember, ...refusals(403, 404, 409) },\n },\n handler: async (req) => {\n const actor = await resolveActor(container, req);\n return container.entitlements.accept(req.params.communityId, req.params.spaceId, actor);\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/member/communities/:communityId/spaces/:spaceId/members/:orgUserId/approve',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'approveSpaceRequest',\n tags,\n summary: \"Approve a person's request to join a space you moderate\",\n description: `Requires \\`communities:moderate\\`. On the community, as its member. Makes the requester an active member. 404 when the person has no open request; 409 when the space is full or they are already a member.`,\n params: SpaceMemberParam,\n response: { 200: SpaceMember, ...refusals(403, 404, 409) },\n },\n handler: async (req) => {\n const actor = await resolveActor(container, req);\n return container.entitlements.approve(\n req.params.communityId,\n req.params.spaceId,\n req.params.orgUserId,\n actor,\n );\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/member/communities/:communityId/spaces/:spaceId/members/:orgUserId/decline',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'declineSpaceRequest',\n tags,\n summary: \"Decline a person's request to join a space you moderate\",\n description: `Requires \\`communities:moderate\\`. On the community, as its member. Closes the open request. 404 when the person has no open request; 409 when they are already a member.`,\n params: SpaceMemberParam,\n response: { 200: SpaceMember, ...refusals(403, 404, 409) },\n },\n handler: async (req) => {\n const actor = await resolveActor(container, req);\n return container.entitlements.decline(\n req.params.communityId,\n req.params.spaceId,\n req.params.orgUserId,\n actor,\n );\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/member/communities/:communityId/spaces/:spaceId/refuse',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'refuseSpaceInvite',\n tags,\n summary: 'Refuse your invitation to a space',\n description:\n 'For a member of the community who does not open every space. 409 when you have no open invitation or are already a member.',\n params: CommunitySpaceParam,\n response: { 200: SpaceMember, ...refusals(403, 404, 409) },\n },\n handler: async (req) => {\n const actor = await resolveActor(container, req);\n return container.entitlements.refuse(req.params.communityId, req.params.spaceId, actor);\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/member/communities/:communityId/spaces/:spaceId/departure',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'leaveSpace',\n tags,\n summary: 'Leave a space',\n description:\n 'For a member of the community who does not open every space. 409 when you are not a member of the space.',\n params: CommunitySpaceParam,\n response: { 200: SpaceMember, ...refusals(403, 404, 409) },\n },\n handler: async (req) => {\n const actor = await resolveActor(container, req);\n return container.entitlements.leave(req.params.communityId, req.params.spaceId, actor);\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/member/communities/:communityId/spaces/:spaceId/members/:orgUserId/remove',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'removeSpaceMember',\n tags,\n summary: 'Remove a person from a space you moderate',\n description: `Requires \\`communities:moderate\\`. On the community, as its member. Revokes the placement, whether it is a membership, a request or an invitation. 409 when the space is open to all members or the placement is already revoked.`,\n params: SpaceMemberParam,\n response: { 200: SpaceMember, ...refusals(403, 404, 409) },\n },\n handler: async (req) => {\n const actor = await resolveActor(container, req);\n return container.entitlements.remove(\n req.params.communityId,\n req.params.spaceId,\n req.params.orgUserId,\n actor,\n );\n },\n });\n\n r.route({\n method: 'PUT',\n url: '/api/member/spaces/:spaceId/subscription',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'setSpaceSubscription',\n tags,\n summary: 'Mute a space, or clear your mute',\n description:\n 'For anyone who can open the space. `muted` stops its activity notifying you, until `mutedUntil` when given; mentions, actions on you and moderation work still notify. `none` clears the mute.',\n params: SpaceIdParam,\n body: SetMute,\n response: { 204: z.void(), ...refusals(403, 404) },\n },\n handler: async (req, reply) => {\n const actor = await resolveActor(container, req);\n await container.subscriptions.setSpace(req.params.spaceId, req.body, actor);\n return reply.code(204).send();\n },\n });\n}\n"],"mappings":"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"}
|