@headless-lms/server 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/http/mcp/tools/communities.js +1 -1
- package/dist/http/mcp/tools/communities.js.map +1 -1
- package/dist/http/routes/access.js +1 -1
- package/dist/http/routes/access.js.map +1 -1
- package/dist/http/routes/communities.js +1 -1
- package/dist/http/routes/communities.js.map +1 -1
- package/dist/http/routes/member/files.js +18 -1
- package/dist/http/routes/member/files.js.map +1 -1
- package/dist/http/routes/platform.js +1 -1
- package/dist/http/routes/platform.js.map +1 -1
- package/dist/http/schemas/access.d.ts +3 -3
- package/dist/http/schemas/assets.d.ts +20 -20
- package/dist/http/schemas/chats.d.ts +12 -12
- package/dist/http/schemas/community.d.ts +46 -19
- package/dist/http/schemas/community.js +6 -3
- package/dist/http/schemas/community.js.map +1 -1
- package/dist/http/schemas/events.d.ts +12 -12
- package/dist/http/schemas/files.d.ts +10 -1
- package/dist/http/schemas/files.js +6 -1
- package/dist/http/schemas/files.js.map +1 -1
- package/dist/http/schemas/index.d.ts +2 -2
- package/dist/http/schemas/index.js +2 -2
- package/dist/http/schemas/integrations.d.ts +4 -4
- package/dist/http/schemas/members.d.ts +13 -13
- package/dist/http/schemas/notifications.d.ts +27 -27
- package/dist/http/schemas/platform.d.ts +2 -1
- package/dist/http/schemas/platform.js +2 -2
- package/dist/http/schemas/platform.js.map +1 -1
- package/dist/http/schemas/students.d.ts +5 -5
- package/dist/packages/server/package.js +1 -1
- package/package.json +5 -5
|
@@ -66,7 +66,7 @@ const communityTools = [
|
|
|
66
66
|
defineTool({
|
|
67
67
|
name: "update_space",
|
|
68
68
|
title: "Update space",
|
|
69
|
-
description: "Change a space name, description, access rule, capacity or
|
|
69
|
+
description: "Change a space name, description, access rule, capacity, settings, cover, background or icon. settings merges into the current settings. Each image must be a ready image from list_media; null clears it. Fields left out stay as they are.",
|
|
70
70
|
kind: "write",
|
|
71
71
|
idempotent: true,
|
|
72
72
|
openWorld: false,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"communities.js","names":[],"sources":["../../../../src/http/mcp/tools/communities.ts"],"sourcesContent":["import {\n CommunitiesQuery,\n CommunityIdParam,\n CommunitySpaceParam,\n CreateCommunity,\n CreateSpace,\n OrderedIds,\n PatchDmSettings,\n SetSpaceTopics,\n SpaceLinkBody,\n SpaceLinkParam,\n UpdateCommunity,\n UpdateSpace,\n} from '../../schemas/index.js';\nimport { defineTool, type Tool } from '../tool.js';\n\nexport const communityTools: Tool[] = [\n defineTool({\n name: 'list_communities',\n title: 'List communities',\n description:\n 'Search and page through the communities, drafts included, each with its status and direct-message settings. Filter by status, search by title.',\n kind: 'read',\n idempotent: true,\n openWorld: false,\n input: CommunitiesQuery,\n call: (input, { container, caller }) => container.content.listCommunities(input, caller.actor),\n }),\n defineTool({\n name: 'list_spaces',\n title: 'List spaces',\n description:\n 'The spaces of a community, in order, each with its access rule, topics and links.',\n kind: 'read',\n idempotent: true,\n openWorld: false,\n input: CommunityIdParam,\n call: ({ communityId }, { container, caller }) =>\n container.content.listSpaces(communityId, caller.actor),\n }),\n defineTool({\n name: 'create_community',\n title: 'Create community',\n description:\n 'Create a draft community with the default direct-message settings and no spaces. Add spaces with create_space and publish it with update_community.',\n kind: 'write',\n idempotent: false,\n openWorld: false,\n input: CreateCommunity,\n call: (input, { container, caller }) => container.content.createCommunity(input, caller.actor),\n }),\n defineTool({\n name: 'update_community',\n title: 'Update community',\n description:\n 'Change a community title, description or cover image, or publish and unpublish it with status. The cover must be a ready image from list_media; null clears it. Fields left out stay as they are.',\n kind: 'write',\n idempotent: true,\n openWorld: false,\n input: UpdateCommunity.extend(CommunityIdParam.shape),\n call: ({ communityId, ...input }, { container, caller }) =>\n container.content.updateCommunity(communityId, input, caller.actor),\n }),\n defineTool({\n name: 'update_community_messaging',\n title: 'Update community messaging',\n description:\n 'Change the direct-message settings of a community: who may message whom from now on. Settings left out stay as they are.',\n kind: 'write',\n idempotent: true,\n openWorld: false,\n input: PatchDmSettings.extend(CommunityIdParam.shape),\n call: ({ communityId, ...input }, { container, caller }) =>\n container.content.patchCommunitySettings(communityId, input, caller.actor),\n }),\n defineTool({\n name: 'create_space',\n title: 'Create space',\n description:\n 'Add a space at the end of a community. Its access rule decides who can open it: all members, by request or by invite. A space open to all members cannot have a capacity.',\n kind: 'write',\n idempotent: false,\n openWorld: false,\n input: CreateSpace.extend(CommunityIdParam.shape),\n call: ({ communityId, ...input }, { container, caller }) =>\n container.content.createSpace(communityId, input, caller.actor),\n }),\n defineTool({\n name: 'update_space',\n title: 'Update space',\n description:\n 'Change a space name, description, access rule, capacity or settings. settings merges into the current settings. Fields left out stay as they are.',\n kind: 'write',\n idempotent: true,\n openWorld: false,\n input: UpdateSpace.extend(CommunitySpaceParam.shape),\n call: ({ communityId, spaceId, ...input }, { container, caller }) =>\n container.content.updateSpace(communityId, spaceId, input, caller.actor),\n }),\n defineTool({\n name: 'reorder_spaces',\n title: 'Reorder spaces',\n description:\n 'Set the space order of a community. Pass every space id of the community in the new order.',\n kind: 'write',\n idempotent: true,\n openWorld: false,\n input: OrderedIds.extend(CommunityIdParam.shape),\n call: ({ communityId, ids }, { container, caller }) =>\n container.content.reorderSpaces(communityId, ids, caller.actor),\n }),\n defineTool({\n name: 'set_space_topics',\n title: 'Set space topics',\n description:\n 'Replace the topics of a space. An entry with an id keeps and renames that topic, one without adds a topic, and a topic left out is deleted. A topic that posts use cannot be deleted.',\n kind: 'write',\n idempotent: true,\n openWorld: false,\n input: SetSpaceTopics.extend(CommunitySpaceParam.shape),\n call: ({ communityId, spaceId, ...input }, { container, caller }) =>\n container.content.setSpaceTopics(communityId, spaceId, input, caller.actor),\n }),\n defineTool({\n name: 'add_space_link',\n title: 'Add space link',\n description:\n 'Link a course, download or community into a space, after its other links. Returns the space.',\n kind: 'write',\n idempotent: false,\n openWorld: false,\n input: SpaceLinkBody.extend(CommunitySpaceParam.shape),\n call: ({ communityId, spaceId, contentId }, { container, caller }) =>\n container.content.addSpaceLink(communityId, spaceId, contentId, caller.actor),\n }),\n defineTool({\n name: 'reorder_space_links',\n title: 'Reorder space links',\n description: 'Set the link order of a space. Pass every link id of the space in the new order.',\n kind: 'write',\n idempotent: true,\n openWorld: false,\n input: OrderedIds.extend(CommunitySpaceParam.shape),\n call: ({ communityId, spaceId, ids }, { container, caller }) =>\n container.content.reorderSpaceLinks(communityId, spaceId, ids, caller.actor),\n }),\n defineTool({\n name: 'remove_space_link',\n title: 'Remove space link',\n description:\n 'Take a link out of a space. The linked content itself is kept. Returns the space.',\n kind: 'destructive',\n idempotent: true,\n openWorld: false,\n input: SpaceLinkParam,\n call: ({ communityId, spaceId, linkId }, { container, caller }) =>\n container.content.removeSpaceLink(communityId, spaceId, linkId, caller.actor),\n }),\n defineTool({\n name: 'delete_space',\n title: 'Delete space',\n description: 'Permanently delete a space with its posts, comments and members.',\n kind: 'destructive',\n idempotent: true,\n openWorld: false,\n input: CommunitySpaceParam,\n call: ({ communityId, spaceId }, { container, caller }) =>\n container.content.deleteSpace(communityId, spaceId, caller.actor),\n }),\n defineTool({\n name: 'delete_community',\n title: 'Delete community',\n description:\n 'Permanently delete a community with its spaces. Links to it from other spaces are removed.',\n kind: 'destructive',\n idempotent: true,\n openWorld: false,\n input: CommunityIdParam,\n call: ({ communityId }, { container, caller }) =>\n container.content.deleteCommunity(communityId, caller.actor),\n }),\n defineTool({\n name: 'get_community_analytics',\n title: 'Get community analytics',\n description: 'Membership, activity and moderation counts for one community.',\n kind: 'read',\n idempotent: true,\n openWorld: false,\n input: CommunityIdParam,\n call: ({ communityId }, { container, caller }) =>\n container.reporting.admin.communityAnalytics(communityId, caller.actor),\n }),\n];\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"communities.js","names":[],"sources":["../../../../src/http/mcp/tools/communities.ts"],"sourcesContent":["import {\n CommunitiesQuery,\n CommunityIdParam,\n CommunitySpaceParam,\n CreateCommunity,\n CreateSpace,\n OrderedIds,\n PatchDmSettings,\n SetSpaceTopics,\n SpaceLinkBody,\n SpaceLinkParam,\n UpdateCommunity,\n UpdateSpace,\n} from '../../schemas/index.js';\nimport { defineTool, type Tool } from '../tool.js';\n\nexport const communityTools: Tool[] = [\n defineTool({\n name: 'list_communities',\n title: 'List communities',\n description:\n 'Search and page through the communities, drafts included, each with its status and direct-message settings. Filter by status, search by title.',\n kind: 'read',\n idempotent: true,\n openWorld: false,\n input: CommunitiesQuery,\n call: (input, { container, caller }) => container.content.listCommunities(input, caller.actor),\n }),\n defineTool({\n name: 'list_spaces',\n title: 'List spaces',\n description:\n 'The spaces of a community, in order, each with its access rule, topics and links.',\n kind: 'read',\n idempotent: true,\n openWorld: false,\n input: CommunityIdParam,\n call: ({ communityId }, { container, caller }) =>\n container.content.listSpaces(communityId, caller.actor),\n }),\n defineTool({\n name: 'create_community',\n title: 'Create community',\n description:\n 'Create a draft community with the default direct-message settings and no spaces. Add spaces with create_space and publish it with update_community.',\n kind: 'write',\n idempotent: false,\n openWorld: false,\n input: CreateCommunity,\n call: (input, { container, caller }) => container.content.createCommunity(input, caller.actor),\n }),\n defineTool({\n name: 'update_community',\n title: 'Update community',\n description:\n 'Change a community title, description or cover image, or publish and unpublish it with status. The cover must be a ready image from list_media; null clears it. Fields left out stay as they are.',\n kind: 'write',\n idempotent: true,\n openWorld: false,\n input: UpdateCommunity.extend(CommunityIdParam.shape),\n call: ({ communityId, ...input }, { container, caller }) =>\n container.content.updateCommunity(communityId, input, caller.actor),\n }),\n defineTool({\n name: 'update_community_messaging',\n title: 'Update community messaging',\n description:\n 'Change the direct-message settings of a community: who may message whom from now on. Settings left out stay as they are.',\n kind: 'write',\n idempotent: true,\n openWorld: false,\n input: PatchDmSettings.extend(CommunityIdParam.shape),\n call: ({ communityId, ...input }, { container, caller }) =>\n container.content.patchCommunitySettings(communityId, input, caller.actor),\n }),\n defineTool({\n name: 'create_space',\n title: 'Create space',\n description:\n 'Add a space at the end of a community. Its access rule decides who can open it: all members, by request or by invite. A space open to all members cannot have a capacity.',\n kind: 'write',\n idempotent: false,\n openWorld: false,\n input: CreateSpace.extend(CommunityIdParam.shape),\n call: ({ communityId, ...input }, { container, caller }) =>\n container.content.createSpace(communityId, input, caller.actor),\n }),\n defineTool({\n name: 'update_space',\n title: 'Update space',\n description:\n 'Change a space name, description, access rule, capacity, settings, cover, background or icon. settings merges into the current settings. Each image must be a ready image from list_media; null clears it. Fields left out stay as they are.',\n kind: 'write',\n idempotent: true,\n openWorld: false,\n input: UpdateSpace.extend(CommunitySpaceParam.shape),\n call: ({ communityId, spaceId, ...input }, { container, caller }) =>\n container.content.updateSpace(communityId, spaceId, input, caller.actor),\n }),\n defineTool({\n name: 'reorder_spaces',\n title: 'Reorder spaces',\n description:\n 'Set the space order of a community. Pass every space id of the community in the new order.',\n kind: 'write',\n idempotent: true,\n openWorld: false,\n input: OrderedIds.extend(CommunityIdParam.shape),\n call: ({ communityId, ids }, { container, caller }) =>\n container.content.reorderSpaces(communityId, ids, caller.actor),\n }),\n defineTool({\n name: 'set_space_topics',\n title: 'Set space topics',\n description:\n 'Replace the topics of a space. An entry with an id keeps and renames that topic, one without adds a topic, and a topic left out is deleted. A topic that posts use cannot be deleted.',\n kind: 'write',\n idempotent: true,\n openWorld: false,\n input: SetSpaceTopics.extend(CommunitySpaceParam.shape),\n call: ({ communityId, spaceId, ...input }, { container, caller }) =>\n container.content.setSpaceTopics(communityId, spaceId, input, caller.actor),\n }),\n defineTool({\n name: 'add_space_link',\n title: 'Add space link',\n description:\n 'Link a course, download or community into a space, after its other links. Returns the space.',\n kind: 'write',\n idempotent: false,\n openWorld: false,\n input: SpaceLinkBody.extend(CommunitySpaceParam.shape),\n call: ({ communityId, spaceId, contentId }, { container, caller }) =>\n container.content.addSpaceLink(communityId, spaceId, contentId, caller.actor),\n }),\n defineTool({\n name: 'reorder_space_links',\n title: 'Reorder space links',\n description: 'Set the link order of a space. Pass every link id of the space in the new order.',\n kind: 'write',\n idempotent: true,\n openWorld: false,\n input: OrderedIds.extend(CommunitySpaceParam.shape),\n call: ({ communityId, spaceId, ids }, { container, caller }) =>\n container.content.reorderSpaceLinks(communityId, spaceId, ids, caller.actor),\n }),\n defineTool({\n name: 'remove_space_link',\n title: 'Remove space link',\n description:\n 'Take a link out of a space. The linked content itself is kept. Returns the space.',\n kind: 'destructive',\n idempotent: true,\n openWorld: false,\n input: SpaceLinkParam,\n call: ({ communityId, spaceId, linkId }, { container, caller }) =>\n container.content.removeSpaceLink(communityId, spaceId, linkId, caller.actor),\n }),\n defineTool({\n name: 'delete_space',\n title: 'Delete space',\n description: 'Permanently delete a space with its posts, comments and members.',\n kind: 'destructive',\n idempotent: true,\n openWorld: false,\n input: CommunitySpaceParam,\n call: ({ communityId, spaceId }, { container, caller }) =>\n container.content.deleteSpace(communityId, spaceId, caller.actor),\n }),\n defineTool({\n name: 'delete_community',\n title: 'Delete community',\n description:\n 'Permanently delete a community with its spaces. Links to it from other spaces are removed.',\n kind: 'destructive',\n idempotent: true,\n openWorld: false,\n input: CommunityIdParam,\n call: ({ communityId }, { container, caller }) =>\n container.content.deleteCommunity(communityId, caller.actor),\n }),\n defineTool({\n name: 'get_community_analytics',\n title: 'Get community analytics',\n description: 'Membership, activity and moderation counts for one community.',\n kind: 'read',\n idempotent: true,\n openWorld: false,\n input: CommunityIdParam,\n call: ({ communityId }, { container, caller }) =>\n container.reporting.admin.communityAnalytics(communityId, caller.actor),\n }),\n];\n"],"mappings":"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"}
|
|
@@ -119,7 +119,7 @@ async function accessRoutes(app, container) {
|
|
|
119
119
|
operationId: "createApiKey",
|
|
120
120
|
tags,
|
|
121
121
|
summary: "Create an API key",
|
|
122
|
-
description: `Requires \`api_keys:write
|
|
122
|
+
description: `Requires \`api_keys:write\` and a signed-in person: a request made with an API key or an app token gets 403. The key acts as you in this organization with the permissions of the role given, never beyond what you hold at the time of each request. Send it in the \`x-api-key\` header or as \`Authorization: Bearer <key>\`; every route under /api and /mcp accepts it. 403 when the role holds a permission you hold nowhere; 404 when the organization has no such role. The key is returned once.`,
|
|
123
123
|
body: CreateApiKey,
|
|
124
124
|
response: {
|
|
125
125
|
201: CreatedApiKey,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"access.js","names":[],"sources":["../../../src/http/routes/access.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 {\n ApiKeyIdParam,\n ApiKeyList,\n ApiKeyRoleList,\n ClientIdParam,\n ConnectedAppList,\n CreateApiKey,\n CreatedApiKey,\n PermissionDefinitionList,\n ViewerPermissions,\n refusals,\n} from '../schemas/index.js';\nimport { resolveActor } from '../scope.js';\n\nexport async function accessRoutes(app: FastifyInstance, container: Container): Promise<void> {\n const r = app.withTypeProvider<ZodTypeProvider>();\n const tags = ['Access'];\n const organizations = container.organizations;\n\n r.route({\n method: 'GET',\n url: '/api/access/permissions/list',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'listPermissions',\n tags,\n summary: 'Every permission, with its label, description and level',\n description: `Any active member of the organization. For role screens, the key-creation screen and the consent screen.`,\n response: { 200: PermissionDefinitionList, ...refusals(403) },\n },\n handler: async (req) => {\n await resolveActor(container, req);\n return organizations.permissionList();\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/access/permissions',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'getMyPermissions',\n tags,\n summary: 'Your effective permissions in this organization',\n description: `Any active member of the organization. Your org role, whether you own the organization, the permissions you hold on the organization, and the ones you hold on each content item.`,\n response: { 200: ViewerPermissions, ...refusals(403) },\n },\n handler: async (req) => organizations.viewerPermissions(await resolveActor(container, req)),\n });\n\n r.route({\n method: 'GET',\n url: '/api/access/apps',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'listConnectedApps',\n tags,\n summary: 'Apps connected to this organization as you',\n description: `Any active member of the organization. The apps you connected to this organization, each with the scopes you granted and when.`,\n response: { 200: ConnectedAppList, ...refusals(403) },\n },\n handler: async (req) => organizations.listConnectedApps(await resolveActor(container, req)),\n });\n\n r.route({\n method: 'DELETE',\n url: '/api/access/apps/:clientId',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'revokeConnectedApp',\n tags,\n summary: 'Disconnect an app',\n description: `Any active member of the organization. Revokes your consent and tokens for the app in this organization; its next call is refused.`,\n params: ClientIdParam,\n response: { 204: z.void(), ...refusals(403) },\n },\n handler: async (req, reply) => {\n await organizations.revokeConnectedApp(\n req.params.clientId,\n await resolveActor(container, req),\n );\n return reply.code(204).send();\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/access/api-keys',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'listApiKeys',\n tags,\n summary: 'Your API keys for this organization',\n description: `Requires \\`api_keys:read\\`. Your keys for this organization with their role and expiry. The key itself is never returned again after it is created.`,\n response: { 200: ApiKeyList, ...refusals(403) },\n },\n handler: async (req) => organizations.listApiKeys(await resolveActor(container, req)),\n });\n\n r.route({\n method: 'GET',\n url: '/api/access/api-keys/roles',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'listApiKeyRoles',\n tags,\n summary: 'The roles you can put on an API key',\n description: `Requires \\`api_keys:write\\`. The organization's roles whose every permission you hold somewhere.`,\n response: { 200: ApiKeyRoleList, ...refusals(403) },\n },\n handler: async (req) => organizations.listApiKeyRoles(await resolveActor(container, req)),\n });\n\n r.route({\n method: 'POST',\n url: '/api/access/api-keys',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'createApiKey',\n tags,\n summary: 'Create an API key',\n description: `Requires \\`api_keys:write
|
|
1
|
+
{"version":3,"file":"access.js","names":[],"sources":["../../../src/http/routes/access.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 {\n ApiKeyIdParam,\n ApiKeyList,\n ApiKeyRoleList,\n ClientIdParam,\n ConnectedAppList,\n CreateApiKey,\n CreatedApiKey,\n PermissionDefinitionList,\n ViewerPermissions,\n refusals,\n} from '../schemas/index.js';\nimport { resolveActor } from '../scope.js';\n\nexport async function accessRoutes(app: FastifyInstance, container: Container): Promise<void> {\n const r = app.withTypeProvider<ZodTypeProvider>();\n const tags = ['Access'];\n const organizations = container.organizations;\n\n r.route({\n method: 'GET',\n url: '/api/access/permissions/list',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'listPermissions',\n tags,\n summary: 'Every permission, with its label, description and level',\n description: `Any active member of the organization. For role screens, the key-creation screen and the consent screen.`,\n response: { 200: PermissionDefinitionList, ...refusals(403) },\n },\n handler: async (req) => {\n await resolveActor(container, req);\n return organizations.permissionList();\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/access/permissions',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'getMyPermissions',\n tags,\n summary: 'Your effective permissions in this organization',\n description: `Any active member of the organization. Your org role, whether you own the organization, the permissions you hold on the organization, and the ones you hold on each content item.`,\n response: { 200: ViewerPermissions, ...refusals(403) },\n },\n handler: async (req) => organizations.viewerPermissions(await resolveActor(container, req)),\n });\n\n r.route({\n method: 'GET',\n url: '/api/access/apps',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'listConnectedApps',\n tags,\n summary: 'Apps connected to this organization as you',\n description: `Any active member of the organization. The apps you connected to this organization, each with the scopes you granted and when.`,\n response: { 200: ConnectedAppList, ...refusals(403) },\n },\n handler: async (req) => organizations.listConnectedApps(await resolveActor(container, req)),\n });\n\n r.route({\n method: 'DELETE',\n url: '/api/access/apps/:clientId',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'revokeConnectedApp',\n tags,\n summary: 'Disconnect an app',\n description: `Any active member of the organization. Revokes your consent and tokens for the app in this organization; its next call is refused.`,\n params: ClientIdParam,\n response: { 204: z.void(), ...refusals(403) },\n },\n handler: async (req, reply) => {\n await organizations.revokeConnectedApp(\n req.params.clientId,\n await resolveActor(container, req),\n );\n return reply.code(204).send();\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/access/api-keys',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'listApiKeys',\n tags,\n summary: 'Your API keys for this organization',\n description: `Requires \\`api_keys:read\\`. Your keys for this organization with their role and expiry. The key itself is never returned again after it is created.`,\n response: { 200: ApiKeyList, ...refusals(403) },\n },\n handler: async (req) => organizations.listApiKeys(await resolveActor(container, req)),\n });\n\n r.route({\n method: 'GET',\n url: '/api/access/api-keys/roles',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'listApiKeyRoles',\n tags,\n summary: 'The roles you can put on an API key',\n description: `Requires \\`api_keys:write\\`. The organization's roles whose every permission you hold somewhere.`,\n response: { 200: ApiKeyRoleList, ...refusals(403) },\n },\n handler: async (req) => organizations.listApiKeyRoles(await resolveActor(container, req)),\n });\n\n r.route({\n method: 'POST',\n url: '/api/access/api-keys',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'createApiKey',\n tags,\n summary: 'Create an API key',\n description: `Requires \\`api_keys:write\\` and a signed-in person: a request made with an API key or an app token gets 403. The key acts as you in this organization with the permissions of the role given, never beyond what you hold at the time of each request. Send it in the \\`x-api-key\\` header or as \\`Authorization: Bearer <key>\\`; every route under /api and /mcp accepts it. 403 when the role holds a permission you hold nowhere; 404 when the organization has no such role. The key is returned once.`,\n body: CreateApiKey,\n response: { 201: CreatedApiKey, ...refusals(400, 403, 404) },\n },\n handler: async (req, reply) => {\n const created = await organizations.createApiKey(\n req.body,\n await resolveActor(container, req),\n );\n return reply.code(201).send(created);\n },\n });\n\n r.route({\n method: 'DELETE',\n url: '/api/access/api-keys/:id',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'deleteApiKey',\n tags,\n summary: 'Revoke an API key',\n description: `Requires \\`api_keys:delete\\`. 404 when the key is not one of yours in this organization.`,\n params: ApiKeyIdParam,\n response: { 204: z.void(), ...refusals(403, 404) },\n },\n handler: async (req, reply) => {\n await organizations.deleteApiKey(req.params.id, await resolveActor(container, req));\n return reply.code(204).send();\n },\n });\n}\n"],"mappings":"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"}
|
|
@@ -350,7 +350,7 @@ async function communitiesRoutes(app, container) {
|
|
|
350
350
|
operationId: "updateSpace",
|
|
351
351
|
tags,
|
|
352
352
|
summary: "Update a space",
|
|
353
|
-
description: `Requires \`communities:write\`. Changes only the fields sent; \`settings\` merges into the current settings. An \`all_members\` space cannot have a capacity (409).`,
|
|
353
|
+
description: `Requires \`communities:write\`. Changes only the fields sent; \`settings\` merges into the current settings. An \`all_members\` space cannot have a capacity (409). \`coverAssetId\`, \`backgroundAssetId\` and \`iconAssetId\` take ready images from the media library, or null to clear one; without \`media:read\`, only images uploaded for the community (409 or 403 otherwise). In a published community the images are public.`,
|
|
354
354
|
params: CommunitySpaceParam,
|
|
355
355
|
body: UpdateSpace,
|
|
356
356
|
response: {
|
|
@@ -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 { 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
|
+
{"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). \\`coverAssetId\\`, \\`backgroundAssetId\\` and \\`iconAssetId\\` take ready images from the media library, or null to clear one; without \\`media:read\\`, only images uploaded for the community (409 or 403 otherwise). In a published community the images are public.`,\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":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { refusals } from "../../schemas/shared.js";
|
|
2
2
|
import { resolveActor } from "../../scope.js";
|
|
3
|
-
import { ActivityFileParams, CommunityThumbnailParams, CourseThumbnailParams, DownloadFileParams, DownloadThumbnailParams, EventFileParams, EventThumbnailParams, MessageAttachmentParams, PostAssetParams, PostCoverParams, UploadFileParams } from "../../schemas/files.js";
|
|
3
|
+
import { ActivityFileParams, CommunityThumbnailParams, CourseThumbnailParams, DownloadFileParams, DownloadThumbnailParams, EventFileParams, EventThumbnailParams, MessageAttachmentParams, PostAssetParams, PostCoverParams, SpaceImageParams, UploadFileParams } from "../../schemas/files.js";
|
|
4
4
|
import "../../schemas/index.js";
|
|
5
5
|
import { z } from "zod";
|
|
6
6
|
//#region src/http/routes/member/files.ts
|
|
@@ -100,6 +100,23 @@ async function memberFileRoutes(app, container) {
|
|
|
100
100
|
return redirect(reply, await member.thumbnailFile("community", req.params.communityId, actor));
|
|
101
101
|
}
|
|
102
102
|
});
|
|
103
|
+
r.route({
|
|
104
|
+
method: "GET",
|
|
105
|
+
url: "/api/member/spaces/:spaceId/images/:image",
|
|
106
|
+
preHandler: app.requireOrgSession,
|
|
107
|
+
schema: {
|
|
108
|
+
operationId: "getSpaceImage",
|
|
109
|
+
tags,
|
|
110
|
+
summary: "Redirect to a space's cover, background or icon",
|
|
111
|
+
description: "For a space in an unpublished community, which only a holder of `communities:read` on the community may fetch: a space in a published community carries public `coverUrl`, `backgroundUrl` and `iconUrl` instead. 404 when the space has no such image.",
|
|
112
|
+
params: SpaceImageParams,
|
|
113
|
+
response: responses
|
|
114
|
+
},
|
|
115
|
+
handler: async (req, reply) => {
|
|
116
|
+
const actor = await resolveActor(container, req);
|
|
117
|
+
return redirect(reply, await member.spaceImageFile(req.params.spaceId, req.params.image, actor));
|
|
118
|
+
}
|
|
119
|
+
});
|
|
103
120
|
r.route({
|
|
104
121
|
method: "GET",
|
|
105
122
|
url: "/api/member/activities/:activityId/assets/:assetId",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"files.js","names":[],"sources":["../../../../src/http/routes/member/files.ts"],"sourcesContent":["// Member file proxies. Each route asks the context that owns the file, then\n// redirects to a short-lived signed storage URL. Every refusal is a 404, so a\n// response never confirms that a file exists. A 403 only means the caller is\n// not an active member of the organization.\nimport type { FastifyInstance, FastifyReply } from 'fastify';\nimport type { ZodTypeProvider } from 'fastify-type-provider-zod';\nimport { z } from 'zod';\nimport {\n ActivityFileParams,\n CommunityThumbnailParams,\n CourseThumbnailParams,\n DownloadFileParams,\n DownloadThumbnailParams,\n EventFileParams,\n EventThumbnailParams,\n MessageAttachmentParams,\n PostCoverParams,\n PostAssetParams,\n UploadFileParams,\n refusals,\n} from '../../schemas/index.js';\nimport { resolveActor } from '../../scope.js';\nimport type { Container } from '../../../app/container.js';\n\nconst tags = ['Files'];\nconst responses = { 302: z.void(), ...refusals(403, 404) };\n\nfunction redirect(reply: FastifyReply, url: string) {\n return reply\n .header('cache-control', 'no-store')\n .header('referrer-policy', 'no-referrer')\n .redirect(url, 302);\n}\n\nexport async function memberFileRoutes(app: FastifyInstance, container: Container): Promise<void> {\n const r = app.withTypeProvider<ZodTypeProvider>();\n const member = container.reporting.member;\n\n r.route({\n method: 'GET',\n url: '/api/member/courses/:courseId/thumbnail',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'getCourseThumbnail',\n tags,\n summary: \"Redirect to a course's thumbnail\",\n description:\n 'For an unpublished course, which only a holder of `courses:read` on it may fetch: a published course carries a public `thumbnailUrl` instead. 404 when the course has no thumbnail.',\n params: CourseThumbnailParams,\n response: responses,\n },\n handler: async (req, reply) => {\n const actor = await resolveActor(container, req);\n const url = await member.thumbnailFile('course', req.params.courseId, actor);\n return redirect(reply, url);\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/member/events/:eventId/thumbnail',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'getEventThumbnail',\n tags,\n summary: \"Redirect to an event's cover\",\n description:\n 'For a draft event, which only a holder of `events:read` on it may fetch: a published or canceled event carries a public `thumbnailUrl` instead. 404 when the event read returns 404 or the event has no cover.',\n params: EventThumbnailParams,\n response: responses,\n },\n handler: async (req, reply) => {\n const actor = await resolveActor(container, req);\n const url = await member.thumbnailFile('event', req.params.eventId, actor);\n return redirect(reply, url);\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/member/events/:eventId/assets/:assetId',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'getEventFile',\n tags,\n summary: 'Redirect to a file attached to an event',\n description:\n 'For a holder of `events:read` on the event or the org, or an attendee of the published or canceled event. Saved under its display name. 404 otherwise, or when the file is not attached to the event.',\n params: EventFileParams,\n response: responses,\n },\n handler: async (req, reply) => {\n const actor = await resolveActor(container, req);\n const url = await member.eventFile(req.params.eventId, req.params.assetId, actor);\n return redirect(reply, url);\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/member/downloads/:downloadId/thumbnail',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'getDownloadThumbnail',\n tags,\n summary: \"Redirect to a download's thumbnail\",\n description:\n 'For an unpublished download, which only a holder of `downloads:read` on it may fetch: a published download carries a public `thumbnailUrl` instead. 404 when the download has no thumbnail.',\n params: DownloadThumbnailParams,\n response: responses,\n },\n handler: async (req, reply) => {\n const actor = await resolveActor(container, req);\n const url = await member.thumbnailFile('download', req.params.downloadId, actor);\n return redirect(reply, url);\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/member/communities/:communityId/thumbnail',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'getCommunityThumbnail',\n tags,\n summary: \"Redirect to a community's thumbnail\",\n description:\n 'For an unpublished community, which only a holder of `communities:read` on it may fetch: a published community carries a public `thumbnailUrl` instead. 404 when the community has no thumbnail.',\n params: CommunityThumbnailParams,\n response: responses,\n },\n handler: async (req, reply) => {\n const actor = await resolveActor(container, req);\n const url = await member.thumbnailFile('community', req.params.communityId, actor);\n return redirect(reply, url);\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/member/activities/:activityId/assets/:assetId',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'getActivityFile',\n tags,\n summary: 'Redirect to a file used in a lesson',\n description:\n 'Served when the person can open the course, the course and the lesson are published, and the lesson lists the file. A holder of `courses:read` on the course may fetch any file a lesson lists. Use the URL as an img, video or audio src.',\n params: ActivityFileParams,\n response: responses,\n },\n handler: async (req, reply) => {\n const actor = await resolveActor(container, req);\n const url = await member.activityFile(req.params.activityId, req.params.assetId, actor);\n return redirect(reply, url);\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/member/downloads/:downloadId/assets/:assetId',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'getDownloadFile',\n tags,\n summary: 'Redirect to a file in a download, saved under its display name',\n description:\n 'Served when the person can open the download, it is published, and it lists the file. A holder of `downloads:read` on the download may fetch it unpublished. The storage link lives DELIVERY_URL_EXPIRY seconds.',\n params: DownloadFileParams,\n response: responses,\n },\n handler: async (req, reply) => {\n const actor = await resolveActor(container, req);\n const url = await member.downloadFile(req.params.downloadId, req.params.assetId, actor);\n return redirect(reply, url);\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/member/posts/:postId/cover',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'getPostCover',\n tags,\n summary: \"Redirect to a post's cover image\",\n description:\n 'For a post that is not published, served to whoever can read it: a published post carries a public `coverUrl` instead. 404 when the post has no cover.',\n params: PostCoverParams,\n response: responses,\n },\n handler: async (req, reply) => {\n const actor = await resolveActor(container, req);\n const url = await container.posts.coverFile(req.params.postId, actor);\n return redirect(reply, url);\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/member/posts/:postId/assets/:assetId',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'getPostAsset',\n tags,\n summary: \"Redirect to a file in a post's body\",\n description:\n 'Served to whoever can read the post while its body shows the file. Images, video, audio and PDFs open in place; any other file downloads under its name.',\n params: PostAssetParams,\n response: responses,\n },\n handler: async (req, reply) => {\n const actor = await resolveActor(container, req);\n const url = await container.posts.assetFile(req.params.postId, req.params.assetId, actor);\n return redirect(reply, url);\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/member/chats/:chatId/messages/:messageId/attachments/:assetId',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'getMessageAttachment',\n tags,\n summary: 'Redirect to an image attached to a chat message',\n description:\n \"Served to the chat's participants while the message stands, to holders of `communities:moderate` on the community also after it is deleted, and to them while the message is flagged.\",\n params: MessageAttachmentParams,\n response: responses,\n },\n handler: async (req, reply) => {\n const actor = await resolveActor(container, req);\n const url = await container.chats.attachmentFile(\n req.params.chatId,\n req.params.messageId,\n req.params.assetId,\n actor,\n );\n return redirect(reply, url);\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/member/uploads/:id/file',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'getMemberUploadFile',\n tags,\n summary: 'Redirect to an image you uploaded',\n description: 'Served only to the person who uploaded it, once it is finalized.',\n params: UploadFileParams,\n response: responses,\n },\n handler: async (req, reply) => {\n const actor = await resolveActor(container, req);\n const url = await container.assets.ownUploadFile(req.params.id, actor);\n return redirect(reply, url);\n },\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"files.js","names":[],"sources":["../../../../src/http/routes/member/files.ts"],"sourcesContent":["// Member file proxies. Each route asks the context that owns the file, then\n// redirects to a short-lived signed storage URL. Every refusal is a 404, so a\n// response never confirms that a file exists. A 403 only means the caller is\n// not an active member of the organization.\nimport type { FastifyInstance, FastifyReply } from 'fastify';\nimport type { ZodTypeProvider } from 'fastify-type-provider-zod';\nimport { z } from 'zod';\nimport {\n ActivityFileParams,\n CommunityThumbnailParams,\n CourseThumbnailParams,\n DownloadFileParams,\n DownloadThumbnailParams,\n EventFileParams,\n EventThumbnailParams,\n MessageAttachmentParams,\n PostCoverParams,\n PostAssetParams,\n SpaceImageParams,\n UploadFileParams,\n refusals,\n} from '../../schemas/index.js';\nimport { resolveActor } from '../../scope.js';\nimport type { Container } from '../../../app/container.js';\n\nconst tags = ['Files'];\nconst responses = { 302: z.void(), ...refusals(403, 404) };\n\nfunction redirect(reply: FastifyReply, url: string) {\n return reply\n .header('cache-control', 'no-store')\n .header('referrer-policy', 'no-referrer')\n .redirect(url, 302);\n}\n\nexport async function memberFileRoutes(app: FastifyInstance, container: Container): Promise<void> {\n const r = app.withTypeProvider<ZodTypeProvider>();\n const member = container.reporting.member;\n\n r.route({\n method: 'GET',\n url: '/api/member/courses/:courseId/thumbnail',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'getCourseThumbnail',\n tags,\n summary: \"Redirect to a course's thumbnail\",\n description:\n 'For an unpublished course, which only a holder of `courses:read` on it may fetch: a published course carries a public `thumbnailUrl` instead. 404 when the course has no thumbnail.',\n params: CourseThumbnailParams,\n response: responses,\n },\n handler: async (req, reply) => {\n const actor = await resolveActor(container, req);\n const url = await member.thumbnailFile('course', req.params.courseId, actor);\n return redirect(reply, url);\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/member/events/:eventId/thumbnail',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'getEventThumbnail',\n tags,\n summary: \"Redirect to an event's cover\",\n description:\n 'For a draft event, which only a holder of `events:read` on it may fetch: a published or canceled event carries a public `thumbnailUrl` instead. 404 when the event read returns 404 or the event has no cover.',\n params: EventThumbnailParams,\n response: responses,\n },\n handler: async (req, reply) => {\n const actor = await resolveActor(container, req);\n const url = await member.thumbnailFile('event', req.params.eventId, actor);\n return redirect(reply, url);\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/member/events/:eventId/assets/:assetId',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'getEventFile',\n tags,\n summary: 'Redirect to a file attached to an event',\n description:\n 'For a holder of `events:read` on the event or the org, or an attendee of the published or canceled event. Saved under its display name. 404 otherwise, or when the file is not attached to the event.',\n params: EventFileParams,\n response: responses,\n },\n handler: async (req, reply) => {\n const actor = await resolveActor(container, req);\n const url = await member.eventFile(req.params.eventId, req.params.assetId, actor);\n return redirect(reply, url);\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/member/downloads/:downloadId/thumbnail',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'getDownloadThumbnail',\n tags,\n summary: \"Redirect to a download's thumbnail\",\n description:\n 'For an unpublished download, which only a holder of `downloads:read` on it may fetch: a published download carries a public `thumbnailUrl` instead. 404 when the download has no thumbnail.',\n params: DownloadThumbnailParams,\n response: responses,\n },\n handler: async (req, reply) => {\n const actor = await resolveActor(container, req);\n const url = await member.thumbnailFile('download', req.params.downloadId, actor);\n return redirect(reply, url);\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/member/communities/:communityId/thumbnail',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'getCommunityThumbnail',\n tags,\n summary: \"Redirect to a community's thumbnail\",\n description:\n 'For an unpublished community, which only a holder of `communities:read` on it may fetch: a published community carries a public `thumbnailUrl` instead. 404 when the community has no thumbnail.',\n params: CommunityThumbnailParams,\n response: responses,\n },\n handler: async (req, reply) => {\n const actor = await resolveActor(container, req);\n const url = await member.thumbnailFile('community', req.params.communityId, actor);\n return redirect(reply, url);\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/member/spaces/:spaceId/images/:image',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'getSpaceImage',\n tags,\n summary: \"Redirect to a space's cover, background or icon\",\n description:\n 'For a space in an unpublished community, which only a holder of `communities:read` on the community may fetch: a space in a published community carries public `coverUrl`, `backgroundUrl` and `iconUrl` instead. 404 when the space has no such image.',\n params: SpaceImageParams,\n response: responses,\n },\n handler: async (req, reply) => {\n const actor = await resolveActor(container, req);\n const url = await member.spaceImageFile(req.params.spaceId, req.params.image, actor);\n return redirect(reply, url);\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/member/activities/:activityId/assets/:assetId',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'getActivityFile',\n tags,\n summary: 'Redirect to a file used in a lesson',\n description:\n 'Served when the person can open the course, the course and the lesson are published, and the lesson lists the file. A holder of `courses:read` on the course may fetch any file a lesson lists. Use the URL as an img, video or audio src.',\n params: ActivityFileParams,\n response: responses,\n },\n handler: async (req, reply) => {\n const actor = await resolveActor(container, req);\n const url = await member.activityFile(req.params.activityId, req.params.assetId, actor);\n return redirect(reply, url);\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/member/downloads/:downloadId/assets/:assetId',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'getDownloadFile',\n tags,\n summary: 'Redirect to a file in a download, saved under its display name',\n description:\n 'Served when the person can open the download, it is published, and it lists the file. A holder of `downloads:read` on the download may fetch it unpublished. The storage link lives DELIVERY_URL_EXPIRY seconds.',\n params: DownloadFileParams,\n response: responses,\n },\n handler: async (req, reply) => {\n const actor = await resolveActor(container, req);\n const url = await member.downloadFile(req.params.downloadId, req.params.assetId, actor);\n return redirect(reply, url);\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/member/posts/:postId/cover',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'getPostCover',\n tags,\n summary: \"Redirect to a post's cover image\",\n description:\n 'For a post that is not published, served to whoever can read it: a published post carries a public `coverUrl` instead. 404 when the post has no cover.',\n params: PostCoverParams,\n response: responses,\n },\n handler: async (req, reply) => {\n const actor = await resolveActor(container, req);\n const url = await container.posts.coverFile(req.params.postId, actor);\n return redirect(reply, url);\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/member/posts/:postId/assets/:assetId',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'getPostAsset',\n tags,\n summary: \"Redirect to a file in a post's body\",\n description:\n 'Served to whoever can read the post while its body shows the file. Images, video, audio and PDFs open in place; any other file downloads under its name.',\n params: PostAssetParams,\n response: responses,\n },\n handler: async (req, reply) => {\n const actor = await resolveActor(container, req);\n const url = await container.posts.assetFile(req.params.postId, req.params.assetId, actor);\n return redirect(reply, url);\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/member/chats/:chatId/messages/:messageId/attachments/:assetId',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'getMessageAttachment',\n tags,\n summary: 'Redirect to an image attached to a chat message',\n description:\n \"Served to the chat's participants while the message stands, to holders of `communities:moderate` on the community also after it is deleted, and to them while the message is flagged.\",\n params: MessageAttachmentParams,\n response: responses,\n },\n handler: async (req, reply) => {\n const actor = await resolveActor(container, req);\n const url = await container.chats.attachmentFile(\n req.params.chatId,\n req.params.messageId,\n req.params.assetId,\n actor,\n );\n return redirect(reply, url);\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/member/uploads/:id/file',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'getMemberUploadFile',\n tags,\n summary: 'Redirect to an image you uploaded',\n description: 'Served only to the person who uploaded it, once it is finalized.',\n params: UploadFileParams,\n response: responses,\n },\n handler: async (req, reply) => {\n const actor = await resolveActor(container, req);\n const url = await container.assets.ownUploadFile(req.params.id, actor);\n return redirect(reply, url);\n },\n });\n}\n"],"mappings":"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"}
|
|
@@ -15,7 +15,7 @@ async function platformRoutes(app, container) {
|
|
|
15
15
|
operationId: "createPlatformOrganization",
|
|
16
16
|
tags,
|
|
17
17
|
summary: "Create an organization and its first API key",
|
|
18
|
-
description: `Requires the platform key as \`Authorization: Bearer <platform key>\`. Creates the organization, owned by the platform account, and an API key for the platform account with the Owner role and no expiry. The key is returned once. 409 when the slug is in use.`,
|
|
18
|
+
description: `Requires the platform key as \`Authorization: Bearer <platform key>\`. Creates the organization, owned by the platform account, with its metadata, and an API key for the platform account with the Owner role and no expiry. The key is returned once. 409 when the slug is in use.`,
|
|
19
19
|
body: CreatePlatformOrganization,
|
|
20
20
|
response: {
|
|
21
21
|
201: PlatformOrganization,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"platform.js","names":[],"sources":["../../../src/http/routes/platform.ts"],"sourcesContent":["import type { FastifyInstance } from 'fastify';\nimport type { ZodTypeProvider } from 'fastify-type-provider-zod';\nimport type { Container } from '../../app/container.js';\nimport {\n CreatedApiKey,\n CreatePlatformApiKey,\n CreatePlatformOrganization,\n PlatformOrganization,\n PlatformOrgIdParam,\n refusals,\n} from '../schemas/index.js';\n\nexport async function platformRoutes(app: FastifyInstance, container: Container): Promise<void> {\n const r = app.withTypeProvider<ZodTypeProvider>();\n const tags = ['Platform'];\n const organizations = container.organizations;\n\n r.route({\n method: 'POST',\n url: '/api/platform/organizations',\n preHandler: app.requirePlatform,\n schema: {\n operationId: 'createPlatformOrganization',\n tags,\n summary: 'Create an organization and its first API key',\n description: `Requires the platform key as \\`Authorization: Bearer <platform key>\\`. Creates the organization, owned by the platform account, and an API key for the platform account with the Owner role and no expiry. The key is returned once. 409 when the slug is in use.`,\n body: CreatePlatformOrganization,\n response: { 201: PlatformOrganization, ...refusals(400, 409) },\n },\n handler: async (req, reply) => {\n const created = await organizations.createPlatformOrganization(req.body);\n return reply.code(201).send({\n organization: {\n ...created.organization,\n createdAt: created.organization.createdAt.toISOString(),\n },\n apiKey: created.apiKey,\n });\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/platform/organizations/:orgId/api-keys',\n preHandler: app.requirePlatform,\n schema: {\n operationId: 'createPlatformApiKey',\n tags,\n summary: 'Create another API key for an organization',\n description: `Requires the platform key as \\`Authorization: Bearer <platform key>\\`. Creates an API key for the platform account in the organization, with the Owner role and no expiry, to replace a lost or leaked one; revoke the old key with the new one. The key is returned once. 404 when the platform account is not an active member of the organization.`,\n params: PlatformOrgIdParam,\n body: CreatePlatformApiKey,\n response: { 201: CreatedApiKey, ...refusals(400, 404) },\n },\n handler: async (req, reply) => {\n const created = await organizations.createPlatformApiKey(req.params.orgId, req.body.name);\n return reply.code(201).send(created);\n },\n });\n}\n"],"mappings":";;;;;AAYA,eAAsB,eAAe,KAAsB,WAAqC;CAC9F,MAAM,IAAI,IAAI,iBAAkC;CAChD,MAAM,OAAO,CAAC,UAAU;CACxB,MAAM,gBAAgB,UAAU;CAEhC,EAAE,MAAM;EACN,QAAQ;EACR,KAAK;EACL,YAAY,IAAI;EAChB,QAAQ;GACN,aAAa;GACb;GACA,SAAS;GACT,aAAa;GACb,MAAM;GACN,UAAU;IAAE,KAAK;IAAsB,GAAG,SAAS,KAAK,GAAG;GAAE;EAC/D;EACA,SAAS,OAAO,KAAK,UAAU;GAC7B,MAAM,UAAU,MAAM,cAAc,2BAA2B,IAAI,IAAI;GACvE,OAAO,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK;IAC1B,cAAc;KACZ,GAAG,QAAQ;KACX,WAAW,QAAQ,aAAa,UAAU,YAAY;IACxD;IACA,QAAQ,QAAQ;GAClB,CAAC;EACH;CACF,CAAC;CAED,EAAE,MAAM;EACN,QAAQ;EACR,KAAK;EACL,YAAY,IAAI;EAChB,QAAQ;GACN,aAAa;GACb;GACA,SAAS;GACT,aAAa;GACb,QAAQ;GACR,MAAM;GACN,UAAU;IAAE,KAAK;IAAe,GAAG,SAAS,KAAK,GAAG;GAAE;EACxD;EACA,SAAS,OAAO,KAAK,UAAU;GAC7B,MAAM,UAAU,MAAM,cAAc,qBAAqB,IAAI,OAAO,OAAO,IAAI,KAAK,IAAI;GACxF,OAAO,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,OAAO;EACrC;CACF,CAAC;AACH"}
|
|
1
|
+
{"version":3,"file":"platform.js","names":[],"sources":["../../../src/http/routes/platform.ts"],"sourcesContent":["import type { FastifyInstance } from 'fastify';\nimport type { ZodTypeProvider } from 'fastify-type-provider-zod';\nimport type { Container } from '../../app/container.js';\nimport {\n CreatedApiKey,\n CreatePlatformApiKey,\n CreatePlatformOrganization,\n PlatformOrganization,\n PlatformOrgIdParam,\n refusals,\n} from '../schemas/index.js';\n\nexport async function platformRoutes(app: FastifyInstance, container: Container): Promise<void> {\n const r = app.withTypeProvider<ZodTypeProvider>();\n const tags = ['Platform'];\n const organizations = container.organizations;\n\n r.route({\n method: 'POST',\n url: '/api/platform/organizations',\n preHandler: app.requirePlatform,\n schema: {\n operationId: 'createPlatformOrganization',\n tags,\n summary: 'Create an organization and its first API key',\n description: `Requires the platform key as \\`Authorization: Bearer <platform key>\\`. Creates the organization, owned by the platform account, with its metadata, and an API key for the platform account with the Owner role and no expiry. The key is returned once. 409 when the slug is in use.`,\n body: CreatePlatformOrganization,\n response: { 201: PlatformOrganization, ...refusals(400, 409) },\n },\n handler: async (req, reply) => {\n const created = await organizations.createPlatformOrganization(req.body);\n return reply.code(201).send({\n organization: {\n ...created.organization,\n createdAt: created.organization.createdAt.toISOString(),\n },\n apiKey: created.apiKey,\n });\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/platform/organizations/:orgId/api-keys',\n preHandler: app.requirePlatform,\n schema: {\n operationId: 'createPlatformApiKey',\n tags,\n summary: 'Create another API key for an organization',\n description: `Requires the platform key as \\`Authorization: Bearer <platform key>\\`. Creates an API key for the platform account in the organization, with the Owner role and no expiry, to replace a lost or leaked one; revoke the old key with the new one. The key is returned once. 404 when the platform account is not an active member of the organization.`,\n params: PlatformOrgIdParam,\n body: CreatePlatformApiKey,\n response: { 201: CreatedApiKey, ...refusals(400, 404) },\n },\n handler: async (req, reply) => {\n const created = await organizations.createPlatformApiKey(req.params.orgId, req.body.name);\n return reply.code(201).send(created);\n },\n });\n}\n"],"mappings":";;;;;AAYA,eAAsB,eAAe,KAAsB,WAAqC;CAC9F,MAAM,IAAI,IAAI,iBAAkC;CAChD,MAAM,OAAO,CAAC,UAAU;CACxB,MAAM,gBAAgB,UAAU;CAEhC,EAAE,MAAM;EACN,QAAQ;EACR,KAAK;EACL,YAAY,IAAI;EAChB,QAAQ;GACN,aAAa;GACb;GACA,SAAS;GACT,aAAa;GACb,MAAM;GACN,UAAU;IAAE,KAAK;IAAsB,GAAG,SAAS,KAAK,GAAG;GAAE;EAC/D;EACA,SAAS,OAAO,KAAK,UAAU;GAC7B,MAAM,UAAU,MAAM,cAAc,2BAA2B,IAAI,IAAI;GACvE,OAAO,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK;IAC1B,cAAc;KACZ,GAAG,QAAQ;KACX,WAAW,QAAQ,aAAa,UAAU,YAAY;IACxD;IACA,QAAQ,QAAQ;GAClB,CAAC;EACH;CACF,CAAC;CAED,EAAE,MAAM;EACN,QAAQ;EACR,KAAK;EACL,YAAY,IAAI;EAChB,QAAQ;GACN,aAAa;GACb;GACA,SAAS;GACT,aAAa;GACb,QAAQ;GACR,MAAM;GACN,UAAU;IAAE,KAAK;IAAe,GAAG,SAAS,KAAK,GAAG;GAAE;EACxD;EACA,SAAS,OAAO,KAAK,UAAU;GAC7B,MAAM,UAAU,MAAM,cAAc,qBAAqB,IAAI,OAAO,OAAO,IAAI,KAAK,IAAI;GACxF,OAAO,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,OAAO;EACrC;CACF,CAAC;AACH"}
|
|
@@ -46,9 +46,9 @@ declare const Permission: z.ZodEnum<{
|
|
|
46
46
|
"automations:delete": "automations:delete";
|
|
47
47
|
}>;
|
|
48
48
|
declare const Level: z.ZodUnion<readonly [z.ZodLiteral<"org">, z.ZodLazy<z.ZodEnum<{
|
|
49
|
-
community: "community";
|
|
50
49
|
course: "course";
|
|
51
50
|
download: "download";
|
|
51
|
+
community: "community";
|
|
52
52
|
event: "event";
|
|
53
53
|
}>>]>;
|
|
54
54
|
declare const PermissionDefinition: z.ZodObject<{
|
|
@@ -101,9 +101,9 @@ declare const PermissionDefinition: z.ZodObject<{
|
|
|
101
101
|
label: z.ZodString;
|
|
102
102
|
description: z.ZodString;
|
|
103
103
|
about: z.ZodUnion<readonly [z.ZodLiteral<"org">, z.ZodLazy<z.ZodEnum<{
|
|
104
|
-
community: "community";
|
|
105
104
|
course: "course";
|
|
106
105
|
download: "download";
|
|
106
|
+
community: "community";
|
|
107
107
|
event: "event";
|
|
108
108
|
}>>]>;
|
|
109
109
|
implies: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
@@ -202,9 +202,9 @@ declare const PermissionDefinitionList: z.ZodArray<z.ZodObject<{
|
|
|
202
202
|
label: z.ZodString;
|
|
203
203
|
description: z.ZodString;
|
|
204
204
|
about: z.ZodUnion<readonly [z.ZodLiteral<"org">, z.ZodLazy<z.ZodEnum<{
|
|
205
|
-
community: "community";
|
|
206
205
|
course: "course";
|
|
207
206
|
download: "download";
|
|
207
|
+
community: "community";
|
|
208
208
|
event: "event";
|
|
209
209
|
}>>]>;
|
|
210
210
|
implies: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
@@ -8,8 +8,8 @@ declare const AssetKind: z.ZodEnum<{
|
|
|
8
8
|
}>;
|
|
9
9
|
type AssetKind = z.infer<typeof AssetKind>;
|
|
10
10
|
declare const AssetSource: z.ZodEnum<{
|
|
11
|
-
member: "member";
|
|
12
11
|
library: "library";
|
|
12
|
+
member: "member";
|
|
13
13
|
}>;
|
|
14
14
|
type AssetSource = z.infer<typeof AssetSource>;
|
|
15
15
|
declare const AssetStatus: z.ZodEnum<{
|
|
@@ -18,19 +18,19 @@ declare const AssetStatus: z.ZodEnum<{
|
|
|
18
18
|
}>;
|
|
19
19
|
type AssetStatus = z.infer<typeof AssetStatus>;
|
|
20
20
|
declare const Asset: z.ZodObject<{
|
|
21
|
-
orgId: z.ZodString;
|
|
22
21
|
id: z.ZodString;
|
|
22
|
+
createdAt: z.ZodCoercedDate<unknown>;
|
|
23
23
|
status: z.ZodEnum<{
|
|
24
24
|
pending: "pending";
|
|
25
25
|
ready: "ready";
|
|
26
26
|
}>;
|
|
27
|
-
|
|
28
|
-
updatedAt: z.ZodCoercedDate<unknown>;
|
|
27
|
+
contentType: z.ZodString;
|
|
29
28
|
contentId: z.ZodNullable<z.ZodString>;
|
|
29
|
+
orgId: z.ZodString;
|
|
30
|
+
updatedAt: z.ZodCoercedDate<unknown>;
|
|
31
|
+
uploadedBy: z.ZodString;
|
|
30
32
|
filename: z.ZodString;
|
|
31
|
-
contentType: z.ZodString;
|
|
32
33
|
size: z.ZodNumber;
|
|
33
|
-
uploadedBy: z.ZodString;
|
|
34
34
|
kind: z.ZodEnum<{
|
|
35
35
|
image: "image";
|
|
36
36
|
video: "video";
|
|
@@ -38,8 +38,8 @@ declare const Asset: z.ZodObject<{
|
|
|
38
38
|
document: "document";
|
|
39
39
|
}>;
|
|
40
40
|
source: z.ZodEnum<{
|
|
41
|
-
member: "member";
|
|
42
41
|
library: "library";
|
|
42
|
+
member: "member";
|
|
43
43
|
}>;
|
|
44
44
|
url: z.ZodNullable<z.ZodString>;
|
|
45
45
|
downloadUrl: z.ZodNullable<z.ZodString>;
|
|
@@ -57,27 +57,27 @@ declare const AssetsQuery: z.ZodObject<{
|
|
|
57
57
|
document: "document";
|
|
58
58
|
}>>;
|
|
59
59
|
source: z.ZodOptional<z.ZodEnum<{
|
|
60
|
-
member: "member";
|
|
61
60
|
library: "library";
|
|
61
|
+
member: "member";
|
|
62
62
|
}>>;
|
|
63
63
|
contentId: z.ZodOptional<z.ZodString>;
|
|
64
64
|
}, z.core.$strip>;
|
|
65
65
|
type AssetsQuery = z.infer<typeof AssetsQuery>;
|
|
66
66
|
declare const AssetsPage: z.ZodObject<{
|
|
67
67
|
rows: z.ZodArray<z.ZodObject<{
|
|
68
|
-
orgId: z.ZodString;
|
|
69
68
|
id: z.ZodString;
|
|
69
|
+
createdAt: z.ZodCoercedDate<unknown>;
|
|
70
70
|
status: z.ZodEnum<{
|
|
71
71
|
pending: "pending";
|
|
72
72
|
ready: "ready";
|
|
73
73
|
}>;
|
|
74
|
-
|
|
75
|
-
updatedAt: z.ZodCoercedDate<unknown>;
|
|
74
|
+
contentType: z.ZodString;
|
|
76
75
|
contentId: z.ZodNullable<z.ZodString>;
|
|
76
|
+
orgId: z.ZodString;
|
|
77
|
+
updatedAt: z.ZodCoercedDate<unknown>;
|
|
78
|
+
uploadedBy: z.ZodString;
|
|
77
79
|
filename: z.ZodString;
|
|
78
|
-
contentType: z.ZodString;
|
|
79
80
|
size: z.ZodNumber;
|
|
80
|
-
uploadedBy: z.ZodString;
|
|
81
81
|
kind: z.ZodEnum<{
|
|
82
82
|
image: "image";
|
|
83
83
|
video: "video";
|
|
@@ -85,8 +85,8 @@ declare const AssetsPage: z.ZodObject<{
|
|
|
85
85
|
document: "document";
|
|
86
86
|
}>;
|
|
87
87
|
source: z.ZodEnum<{
|
|
88
|
-
member: "member";
|
|
89
88
|
library: "library";
|
|
89
|
+
member: "member";
|
|
90
90
|
}>;
|
|
91
91
|
url: z.ZodNullable<z.ZodString>;
|
|
92
92
|
downloadUrl: z.ZodNullable<z.ZodString>;
|
|
@@ -117,19 +117,19 @@ declare const UploadTicket: z.ZodObject<{
|
|
|
117
117
|
expiresInSeconds: z.ZodNumber;
|
|
118
118
|
headers: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
119
119
|
asset: z.ZodObject<{
|
|
120
|
-
orgId: z.ZodString;
|
|
121
120
|
id: z.ZodString;
|
|
121
|
+
createdAt: z.ZodCoercedDate<unknown>;
|
|
122
122
|
status: z.ZodEnum<{
|
|
123
123
|
pending: "pending";
|
|
124
124
|
ready: "ready";
|
|
125
125
|
}>;
|
|
126
|
-
|
|
127
|
-
updatedAt: z.ZodCoercedDate<unknown>;
|
|
126
|
+
contentType: z.ZodString;
|
|
128
127
|
contentId: z.ZodNullable<z.ZodString>;
|
|
128
|
+
orgId: z.ZodString;
|
|
129
|
+
updatedAt: z.ZodCoercedDate<unknown>;
|
|
130
|
+
uploadedBy: z.ZodString;
|
|
129
131
|
filename: z.ZodString;
|
|
130
|
-
contentType: z.ZodString;
|
|
131
132
|
size: z.ZodNumber;
|
|
132
|
-
uploadedBy: z.ZodString;
|
|
133
133
|
kind: z.ZodEnum<{
|
|
134
134
|
image: "image";
|
|
135
135
|
video: "video";
|
|
@@ -137,8 +137,8 @@ declare const UploadTicket: z.ZodObject<{
|
|
|
137
137
|
document: "document";
|
|
138
138
|
}>;
|
|
139
139
|
source: z.ZodEnum<{
|
|
140
|
-
member: "member";
|
|
141
140
|
library: "library";
|
|
141
|
+
member: "member";
|
|
142
142
|
}>;
|
|
143
143
|
url: z.ZodNullable<z.ZodString>;
|
|
144
144
|
downloadUrl: z.ZodNullable<z.ZodString>;
|