@headless-lms/server 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -0
- package/dist/app/community-mail.d.ts +10 -10
- package/dist/app/community-mail.js +72 -28
- package/dist/app/community-mail.js.map +1 -1
- package/dist/app/container.d.ts +48 -10
- package/dist/app/container.js +268 -48
- package/dist/app/container.js.map +1 -1
- package/dist/app/notification-channels.d.ts +47 -0
- package/dist/app/notification-channels.js +175 -0
- package/dist/app/notification-channels.js.map +1 -0
- package/dist/app/notification-reads.d.ts +23 -0
- package/dist/app/notification-reads.js +74 -0
- package/dist/app/notification-reads.js.map +1 -0
- package/dist/app/platform.d.ts +14 -0
- package/dist/app/platform.js +14 -0
- package/dist/app/platform.js.map +1 -0
- package/dist/http/config.d.ts +4 -0
- package/dist/http/errors.d.ts +1 -0
- package/dist/http/errors.js +24 -4
- package/dist/http/errors.js.map +1 -1
- package/dist/http/fastify.d.ts +8 -0
- package/dist/http/mcp/catalogue.d.ts +11 -9
- package/dist/http/mcp/catalogue.js +15 -21
- package/dist/http/mcp/catalogue.js.map +1 -1
- package/dist/http/mcp/challenge.js +3 -3
- package/dist/http/mcp/challenge.js.map +1 -1
- package/dist/http/mcp/route.js +7 -7
- package/dist/http/mcp/route.js.map +1 -1
- package/dist/http/mcp/test-support.d.ts +6 -5
- package/dist/http/mcp/test-support.js +32 -11
- package/dist/http/mcp/test-support.js.map +1 -1
- package/dist/http/mcp/tool.d.ts +3 -9
- package/dist/http/mcp/tool.js +14 -6
- package/dist/http/mcp/tool.js.map +1 -1
- package/dist/http/mcp/tools/automations.js +10 -20
- package/dist/http/mcp/tools/automations.js.map +1 -1
- package/dist/http/mcp/tools/catalog.js +25 -42
- package/dist/http/mcp/tools/catalog.js.map +1 -1
- package/dist/http/mcp/tools/comments.js +9 -19
- package/dist/http/mcp/tools/comments.js.map +1 -1
- package/dist/http/mcp/tools/communities.js +16 -31
- package/dist/http/mcp/tools/communities.js.map +1 -1
- package/dist/http/mcp/tools/community-members.js +9 -16
- package/dist/http/mcp/tools/community-members.js.map +1 -1
- package/dist/http/mcp/tools/community-moderation.js +13 -25
- package/dist/http/mcp/tools/community-moderation.js.map +1 -1
- package/dist/http/mcp/tools/courses.js +11 -21
- package/dist/http/mcp/tools/courses.js.map +1 -1
- package/dist/http/mcp/tools/events.d.ts +6 -0
- package/dist/http/mcp/tools/events.js +170 -0
- package/dist/http/mcp/tools/events.js.map +1 -0
- package/dist/http/mcp/tools/index.d.ts +5 -1
- package/dist/http/mcp/tools/index.js +45 -10
- package/dist/http/mcp/tools/index.js.map +1 -1
- package/dist/http/mcp/tools/organization.d.ts +6 -0
- package/dist/http/mcp/tools/organization.js +214 -0
- package/dist/http/mcp/tools/organization.js.map +1 -0
- package/dist/http/mcp/tools/people.js +30 -49
- package/dist/http/mcp/tools/people.js.map +1 -1
- package/dist/http/plugins/auth.js +37 -8
- package/dist/http/plugins/auth.js.map +1 -1
- package/dist/http/plugins/error-handler.d.ts +5 -1
- package/dist/http/plugins/error-handler.js +13 -3
- package/dist/http/plugins/error-handler.js.map +1 -1
- package/dist/http/plugins/openapi.js +18 -2
- package/dist/http/plugins/openapi.js.map +1 -1
- package/dist/http/plugins/platform.d.ts +7 -0
- package/dist/http/plugins/platform.js +13 -0
- package/dist/http/plugins/platform.js.map +1 -0
- package/dist/http/routes/access.js +54 -36
- package/dist/http/routes/access.js.map +1 -1
- package/dist/http/routes/activities.js +45 -45
- package/dist/http/routes/activities.js.map +1 -1
- package/dist/http/routes/assets.js +30 -65
- package/dist/http/routes/assets.js.map +1 -1
- package/dist/http/routes/automations.js +37 -42
- package/dist/http/routes/automations.js.map +1 -1
- package/dist/http/routes/bundles.js +33 -36
- package/dist/http/routes/bundles.js.map +1 -1
- package/dist/http/routes/communities.js +144 -136
- package/dist/http/routes/communities.js.map +1 -1
- package/dist/http/routes/community-moderation.js +83 -137
- package/dist/http/routes/community-moderation.js.map +1 -1
- package/dist/http/routes/course-analytics.js +32 -15
- package/dist/http/routes/course-analytics.js.map +1 -1
- package/dist/http/routes/courses.js +25 -32
- package/dist/http/routes/courses.js.map +1 -1
- package/dist/http/routes/dashboard.js +9 -9
- package/dist/http/routes/dashboard.js.map +1 -1
- package/dist/http/routes/discussion.js +21 -39
- package/dist/http/routes/discussion.js.map +1 -1
- package/dist/http/routes/downloads.js +41 -47
- package/dist/http/routes/downloads.js.map +1 -1
- package/dist/http/routes/entitlements.js +19 -23
- package/dist/http/routes/entitlements.js.map +1 -1
- package/dist/http/routes/events.d.ts +7 -0
- package/dist/http/routes/events.js +333 -0
- package/dist/http/routes/events.js.map +1 -0
- package/dist/http/routes/integrations.js +40 -73
- package/dist/http/routes/integrations.js.map +1 -1
- package/dist/http/routes/member/assets.js +10 -13
- package/dist/http/routes/member/assets.js.map +1 -1
- package/dist/http/routes/member/chats.js +177 -75
- package/dist/http/routes/member/chats.js.map +1 -1
- package/dist/http/routes/member/comments.js +65 -26
- package/dist/http/routes/member/comments.js.map +1 -1
- package/dist/http/routes/member/community.js +68 -51
- package/dist/http/routes/member/community.js.map +1 -1
- package/dist/http/routes/member/courses.js +12 -19
- package/dist/http/routes/member/courses.js.map +1 -1
- package/dist/http/routes/member/devices.d.ts +7 -0
- package/dist/http/routes/member/devices.js +97 -0
- package/dist/http/routes/member/devices.js.map +1 -0
- package/dist/http/routes/member/downloads.js +6 -9
- package/dist/http/routes/member/downloads.js.map +1 -1
- package/dist/http/routes/member/events.d.ts +7 -0
- package/dist/http/routes/member/events.js +386 -0
- package/dist/http/routes/member/events.js.map +1 -0
- package/dist/http/routes/member/files.js +71 -46
- package/dist/http/routes/member/files.js.map +1 -1
- package/dist/http/routes/member/index.js +6 -0
- package/dist/http/routes/member/index.js.map +1 -1
- package/dist/http/routes/member/notification-settings.d.ts +7 -0
- package/dist/http/routes/member/notification-settings.js +100 -0
- package/dist/http/routes/member/notification-settings.js.map +1 -0
- package/dist/http/routes/member/notifications.js +69 -45
- package/dist/http/routes/member/notifications.js.map +1 -1
- package/dist/http/routes/member/org.js +5 -15
- package/dist/http/routes/member/org.js.map +1 -1
- package/dist/http/routes/member/posts.js +94 -95
- package/dist/http/routes/member/posts.js.map +1 -1
- package/dist/http/routes/member/profile.js +15 -6
- package/dist/http/routes/member/profile.js.map +1 -1
- package/dist/http/routes/member/progress.js +7 -11
- package/dist/http/routes/member/progress.js.map +1 -1
- package/dist/http/routes/organizations.js +415 -110
- package/dist/http/routes/organizations.js.map +1 -1
- package/dist/http/routes/platform.d.ts +7 -0
- package/dist/http/routes/platform.js +61 -0
- package/dist/http/routes/platform.js.map +1 -0
- package/dist/http/routes/students.js +12 -75
- package/dist/http/routes/students.js.map +1 -1
- package/dist/http/routes/webhooks.js +30 -39
- package/dist/http/routes/webhooks.js.map +1 -1
- package/dist/http/routes.js +9 -0
- package/dist/http/routes.js.map +1 -1
- package/dist/http/schemas/access.d.ts +346 -64
- package/dist/http/schemas/access.js +39 -12
- package/dist/http/schemas/access.js.map +1 -1
- package/dist/http/schemas/assets.d.ts +13 -8
- package/dist/http/schemas/assets.js +6 -5
- package/dist/http/schemas/assets.js.map +1 -1
- package/dist/http/schemas/automations.d.ts +4 -8
- package/dist/http/schemas/chats.d.ts +238 -36
- package/dist/http/schemas/chats.js +14 -7
- package/dist/http/schemas/chats.js.map +1 -1
- package/dist/http/schemas/community.d.ts +140 -33
- package/dist/http/schemas/community.js +25 -9
- package/dist/http/schemas/community.js.map +1 -1
- package/dist/http/schemas/course-analytics.d.ts +51 -1
- package/dist/http/schemas/course-analytics.js +19 -1
- package/dist/http/schemas/course-analytics.js.map +1 -1
- package/dist/http/schemas/dashboard.d.ts +1 -0
- package/dist/http/schemas/dashboard.js +2 -1
- package/dist/http/schemas/dashboard.js.map +1 -1
- package/dist/http/schemas/discussion.d.ts +45 -73
- package/dist/http/schemas/discussion.js +16 -4
- package/dist/http/schemas/discussion.js.map +1 -1
- package/dist/http/schemas/entitlements.d.ts +9 -12
- package/dist/http/schemas/entitlements.js +11 -5
- package/dist/http/schemas/entitlements.js.map +1 -1
- package/dist/http/schemas/events.d.ts +527 -0
- package/dist/http/schemas/events.js +64 -0
- package/dist/http/schemas/events.js.map +1 -0
- package/dist/http/schemas/files.d.ts +15 -5
- package/dist/http/schemas/files.js +10 -2
- package/dist/http/schemas/files.js.map +1 -1
- package/dist/http/schemas/index.d.ts +14 -12
- package/dist/http/schemas/index.js +14 -12
- package/dist/http/schemas/invites.d.ts +3 -47
- package/dist/http/schemas/invites.js +4 -25
- package/dist/http/schemas/invites.js.map +1 -1
- package/dist/http/schemas/member.d.ts +94 -7
- package/dist/http/schemas/member.js +7 -3
- package/dist/http/schemas/member.js.map +1 -1
- package/dist/http/schemas/members.d.ts +329 -65
- package/dist/http/schemas/members.js +67 -25
- package/dist/http/schemas/members.js.map +1 -1
- package/dist/http/schemas/notifications.d.ts +502 -175
- package/dist/http/schemas/notifications.js +148 -28
- package/dist/http/schemas/notifications.js.map +1 -1
- package/dist/http/schemas/platform.d.ts +37 -0
- package/dist/http/schemas/platform.js +19 -0
- package/dist/http/schemas/platform.js.map +1 -0
- package/dist/http/schemas/posts.d.ts +92 -38
- package/dist/http/schemas/posts.js +4 -4
- package/dist/http/schemas/posts.js.map +1 -1
- package/dist/http/schemas/profile.d.ts +1 -1
- package/dist/http/schemas/shared.d.ts +10 -13
- package/dist/http/schemas/shared.js +3 -11
- package/dist/http/schemas/shared.js.map +1 -1
- package/dist/http/schemas/students.d.ts +22 -0
- package/dist/http/schemas/students.js +3 -1
- package/dist/http/schemas/students.js.map +1 -1
- package/dist/http/scope.d.ts +9 -12
- package/dist/http/scope.js +30 -35
- package/dist/http/scope.js.map +1 -1
- package/dist/http/server.js +9 -0
- package/dist/http/server.js.map +1 -1
- package/dist/http/shutdown.d.ts +11 -0
- package/dist/http/shutdown.js +40 -0
- package/dist/http/shutdown.js.map +1 -0
- package/dist/http/sse.d.ts +19 -0
- package/dist/http/sse.js +104 -0
- package/dist/http/sse.js.map +1 -0
- package/dist/http/test-caller.d.ts +13 -0
- package/dist/http/test-caller.js +14 -0
- package/dist/http/test-caller.js.map +1 -0
- package/dist/http/well-known.js +2 -2
- package/dist/http/well-known.js.map +1 -1
- package/dist/index.d.ts +5 -2
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/packages/server/package.js +1 -1
- package/package.json +7 -6
- package/dist/http/routes/member/actor.d.ts +0 -14
- package/dist/http/routes/member/actor.js +0 -11
- package/dist/http/routes/member/actor.js.map +0 -1
package/README.md
CHANGED
|
@@ -10,14 +10,28 @@ integration plugins. The `headless-lms` bin lives in `@headless-lms/cli`.
|
|
|
10
10
|
|
|
11
11
|
```ts
|
|
12
12
|
import { createContainer, buildServer } from '@headless-lms/server';
|
|
13
|
+
import { PostgresPubSub } from '@headless-lms/adapter-pubsub-postgres';
|
|
13
14
|
|
|
14
15
|
const container = await createContainer(config, {
|
|
15
16
|
pluginsDir, // one folder per integration (directory name = integration id)
|
|
17
|
+
adapters: {
|
|
18
|
+
pubsub: (logger) => new PostgresPubSub({ connectionString: config.pubsubDatabaseUrl, logger }),
|
|
19
|
+
},
|
|
16
20
|
});
|
|
17
21
|
const app = await buildServer(config, container);
|
|
18
22
|
await app.listen({ port: config.port, host: config.host });
|
|
23
|
+
await container.pubsub?.start();
|
|
24
|
+
await container.chatHub?.start();
|
|
25
|
+
container.outboxRelay.start();
|
|
26
|
+
await container.automationEngine.start();
|
|
19
27
|
```
|
|
20
28
|
|
|
29
|
+
The container constructs the outbox relay, the automation engine, `PubSub` and the
|
|
30
|
+
chat hub but starts none of them; the entry point starts them after `listen`, `PubSub`
|
|
31
|
+
and the hub before the relay. `buildServer` closes the hub in `preClose`, so open chat
|
|
32
|
+
streams receive their closing frame, and on close stops the relay, the engine and then
|
|
33
|
+
`PubSub`.
|
|
34
|
+
|
|
21
35
|
Public surface (`src/index.ts`): `createContainer`, `buildServer`,
|
|
22
36
|
`loadIntegrations`, the operational function `runMigrations` (re-exported from
|
|
23
37
|
`@headless-lms/adapter-db`, which ships the `drizzle/` migration assets;
|
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
import { ContentService } from "@headless-lms/core/content";
|
|
2
2
|
import { EntitlementsService } from "@headless-lms/core/entitlements";
|
|
3
|
-
import { ChatService, DiscussionService, PostService } from "@headless-lms/core/discussion";
|
|
3
|
+
import { ChatService, DiscussionService, PostService, SubscriptionService } from "@headless-lms/core/discussion";
|
|
4
4
|
import { IdentityService } from "@headless-lms/core/identity";
|
|
5
5
|
import { OrganizationService } from "@headless-lms/core/organizations";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
6
|
+
import { CommunityMailReads, GrantWriter } from "@headless-lms/core/automations";
|
|
7
|
+
import { BodyParser } from "@headless-lms/core/shared/ports";
|
|
8
8
|
//#region src/app/community-mail.d.ts
|
|
9
9
|
type CommunityMailDeps = {
|
|
10
|
-
content: Pick<ContentService, '
|
|
11
|
-
entitlements: Pick<EntitlementsService, 'openSpacesFor' | 'communitiesOpenedBy' | 'communityMembersAmong' | 'spaceOpenersAmong' | 'spaceOpeners' | '
|
|
12
|
-
organizations: Pick<OrganizationService, 'getOrgUserById' | '
|
|
10
|
+
content: Pick<ContentService, 'findCommunity' | 'getSpace' | 'listCommunitiesById' | 'findCourse' | 'findDownload' | 'findEvent' | 'findBundle'>;
|
|
11
|
+
entitlements: Pick<EntitlementsService, 'openSpacesFor' | 'communitiesOpenedBy' | 'communityMembersAmong' | 'spaceOpenersAmong' | 'spaceOpeners' | 'countActiveSpaceMembers' | 'communityIdsOpenedFor' | 'grant'>;
|
|
12
|
+
organizations: Pick<OrganizationService, 'getById' | 'getOrgUserById' | 'actorFor' | 'systemActor' | 'holders' | 'getRole' | 'permissionsOfMany'>;
|
|
13
13
|
identity: Pick<IdentityService, 'displayName'>;
|
|
14
|
-
posts: Pick<PostService, '
|
|
15
|
-
|
|
14
|
+
posts: Pick<PostService, 'find'>;
|
|
15
|
+
subscriptions: Pick<SubscriptionService, 'followerIds' | 'itemSubscriptions'>;
|
|
16
|
+
discussion: Pick<DiscussionService, 'getComment'>;
|
|
16
17
|
chats: Pick<ChatService, 'chatSummary' | 'latestMessageBody' | 'isBlocked'>;
|
|
17
|
-
|
|
18
|
+
bodies: BodyParser;
|
|
18
19
|
};
|
|
19
20
|
declare function communityMail(deps: CommunityMailDeps): {
|
|
20
21
|
mail: Omit<CommunityMailReads, 'recentMessageRecipients'>;
|
|
21
22
|
grants: GrantWriter;
|
|
22
|
-
scheduledWork: ScheduledWork;
|
|
23
23
|
};
|
|
24
24
|
//#endregion
|
|
25
25
|
export { CommunityMailDeps, communityMail };
|
|
@@ -1,12 +1,14 @@
|
|
|
1
|
+
import { NotFoundError } from "@headless-lms/core/shared/errors";
|
|
1
2
|
//#region src/app/community-mail.ts
|
|
2
3
|
function communityMail(deps) {
|
|
4
|
+
const system = (orgId) => deps.organizations.systemActor(orgId);
|
|
3
5
|
return {
|
|
4
6
|
mail: {
|
|
5
7
|
async communityTitle(orgId, communityId) {
|
|
6
|
-
return (await deps.content.
|
|
8
|
+
return (await deps.content.findCommunity(communityId, system(orgId)))?.title ?? null;
|
|
7
9
|
},
|
|
8
10
|
async space(orgId, spaceId) {
|
|
9
|
-
const space = await deps.content.getSpace(
|
|
11
|
+
const space = await deps.content.getSpace(spaceId, system(orgId));
|
|
10
12
|
return space ? {
|
|
11
13
|
name: space.name,
|
|
12
14
|
communityId: space.communityId,
|
|
@@ -14,29 +16,77 @@ function communityMail(deps) {
|
|
|
14
16
|
} : null;
|
|
15
17
|
},
|
|
16
18
|
async openSpaceNames(orgId, orgUserId, communityId) {
|
|
17
|
-
const
|
|
18
|
-
if (!
|
|
19
|
-
return (await deps.entitlements.openSpacesFor(
|
|
20
|
-
id: orgUser.id,
|
|
21
|
-
role: orgUser.role
|
|
22
|
-
}, communityId)).map((space) => space.name);
|
|
19
|
+
const person = await deps.organizations.actorFor(orgId, orgUserId);
|
|
20
|
+
if (!person) return [];
|
|
21
|
+
return (await deps.entitlements.openSpacesFor(communityId, person)).map((space) => space.name);
|
|
23
22
|
},
|
|
24
23
|
communitiesOpenedBy: (orgId, grant) => deps.entitlements.communitiesOpenedBy(orgId, grant),
|
|
25
24
|
communityMembersAmong: (orgId, communityId, ids) => deps.entitlements.communityMembersAmong(orgId, communityId, ids),
|
|
26
25
|
spaceOpenersAmong: (orgId, spaceId, ids) => deps.entitlements.spaceOpenersAmong(orgId, spaceId, ids),
|
|
27
26
|
spaceOpeners: (orgId, spaceId) => deps.entitlements.spaceOpeners(orgId, spaceId),
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
holders: (orgId, permission, on) => deps.organizations.holders(orgId, permission, on),
|
|
28
|
+
async heldOnOrg(orgId, orgUserId, permissions) {
|
|
29
|
+
const held = (await deps.organizations.permissionsOfMany(orgId, [orgUserId])).get(orgUserId);
|
|
30
|
+
return held ? permissions.filter((permission) => held.has(permission)) : [];
|
|
31
|
+
},
|
|
32
|
+
async role(orgId, roleKey) {
|
|
33
|
+
try {
|
|
34
|
+
const role = await deps.organizations.getRole(roleKey, system(orgId));
|
|
35
|
+
return {
|
|
36
|
+
name: role.name,
|
|
37
|
+
permissions: role.permissions
|
|
38
|
+
};
|
|
39
|
+
} catch (err) {
|
|
40
|
+
if (err instanceof NotFoundError) return null;
|
|
41
|
+
throw err;
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
async orgName(orgId) {
|
|
45
|
+
return (await deps.organizations.getById(orgId))?.name ?? null;
|
|
46
|
+
},
|
|
47
|
+
async contentInfo(orgId, item) {
|
|
48
|
+
const actor = system(orgId);
|
|
49
|
+
if ("bundleId" in item) {
|
|
50
|
+
const bundle = await deps.content.findBundle(item.bundleId, actor);
|
|
51
|
+
return bundle ? {
|
|
52
|
+
title: bundle.name,
|
|
53
|
+
type: "bundle"
|
|
54
|
+
} : null;
|
|
55
|
+
}
|
|
56
|
+
const course = await deps.content.findCourse(item.contentId, actor);
|
|
57
|
+
if (course) return {
|
|
58
|
+
title: course.title,
|
|
59
|
+
type: course.type
|
|
60
|
+
};
|
|
61
|
+
const download = await deps.content.findDownload(item.contentId, actor);
|
|
62
|
+
if (download) return {
|
|
63
|
+
title: download.title,
|
|
64
|
+
type: download.type
|
|
65
|
+
};
|
|
66
|
+
const community = await deps.content.findCommunity(item.contentId, actor);
|
|
67
|
+
if (community) return {
|
|
68
|
+
title: community.title,
|
|
69
|
+
type: community.type
|
|
70
|
+
};
|
|
71
|
+
const event = await deps.content.findEvent(item.contentId, actor);
|
|
72
|
+
return event ? {
|
|
73
|
+
title: event.title,
|
|
74
|
+
type: event.type
|
|
75
|
+
} : null;
|
|
76
|
+
},
|
|
30
77
|
async displayName(orgId, orgUserId) {
|
|
31
78
|
const orgUser = await deps.organizations.getOrgUserById(orgId, orgUserId);
|
|
32
79
|
return deps.identity.displayName(orgUser?.userId ?? null);
|
|
33
80
|
},
|
|
34
|
-
|
|
81
|
+
postText: (body) => deps.bodies.parse(body).text,
|
|
82
|
+
followerIds: (orgId, postId) => deps.subscriptions.followerIds(orgId, postId),
|
|
35
83
|
async comment(orgId, commentId) {
|
|
36
84
|
const comment = await deps.discussion.getComment(orgId, commentId);
|
|
37
85
|
return comment ? {
|
|
38
86
|
orgUserId: comment.orgUserId,
|
|
39
87
|
postId: comment.postId,
|
|
88
|
+
activityId: comment.activityId,
|
|
89
|
+
parentId: comment.parentId,
|
|
40
90
|
body: comment.body
|
|
41
91
|
} : null;
|
|
42
92
|
},
|
|
@@ -52,42 +102,36 @@ function communityMail(deps) {
|
|
|
52
102
|
},
|
|
53
103
|
async chat(orgId, chatId) {
|
|
54
104
|
const summary = await deps.chats.chatSummary(orgId, chatId);
|
|
55
|
-
|
|
105
|
+
if (!summary) return null;
|
|
106
|
+
const mutes = new Map((await deps.subscriptions.itemSubscriptions(orgId, {
|
|
107
|
+
type: "chat",
|
|
108
|
+
id: chatId
|
|
109
|
+
})).filter((subscription) => subscription.state === "muted").map((subscription) => [subscription.orgUserId, subscription.mutedUntil]));
|
|
110
|
+
return {
|
|
56
111
|
communityId: summary.chat.communityId,
|
|
57
112
|
kind: summary.chat.kind,
|
|
58
113
|
status: summary.chat.status,
|
|
59
114
|
createdBy: summary.chat.createdBy,
|
|
60
115
|
participants: summary.participants.map((participant) => ({
|
|
61
116
|
orgUserId: participant.orgUserId,
|
|
62
|
-
muted: participant.
|
|
63
|
-
mutedUntil: participant.
|
|
117
|
+
muted: mutes.has(participant.orgUserId),
|
|
118
|
+
mutedUntil: mutes.get(participant.orgUserId) ?? null,
|
|
64
119
|
leftAt: participant.leftAt
|
|
65
120
|
}))
|
|
66
|
-
}
|
|
121
|
+
};
|
|
67
122
|
},
|
|
68
123
|
latestMessageBody: (orgId, chatId) => deps.chats.latestMessageBody(orgId, chatId),
|
|
69
124
|
pairBlocked: (orgId, communityId, a, b) => deps.chats.isBlocked(orgId, communityId, a, b),
|
|
70
125
|
activeMemberCount: (orgId, spaceId) => deps.entitlements.countActiveSpaceMembers(orgId, spaceId),
|
|
71
126
|
async memberCommunityTitles(orgId, orgUserId) {
|
|
72
127
|
const ids = await deps.entitlements.communityIdsOpenedFor(orgId, orgUserId);
|
|
73
|
-
return (await deps.content.listCommunitiesById(
|
|
128
|
+
return (await deps.content.listCommunitiesById(ids, system(orgId))).map((community) => ({
|
|
74
129
|
id: community.id,
|
|
75
130
|
title: community.title
|
|
76
131
|
}));
|
|
77
132
|
}
|
|
78
133
|
},
|
|
79
|
-
grants: { grant: (orgId, input) => deps.entitlements.
|
|
80
|
-
scheduledWork: {
|
|
81
|
-
publishDuePosts: (orgId, now) => deps.posts.publishDue(orgId, now),
|
|
82
|
-
hasDuePosts: (orgId, now) => deps.posts.hasDue(orgId, now),
|
|
83
|
-
expireDueGrants: (orgId, now) => deps.entitlements.expireDue(orgId, now),
|
|
84
|
-
hasDueGrants: (orgId, now) => deps.entitlements.hasDueNotices(orgId, now),
|
|
85
|
-
sendDigest: (orgId, now) => deps.discussion.sendDigest(orgId, now),
|
|
86
|
-
removeUnattachedUploads: async (orgId, now) => {
|
|
87
|
-
await deps.assets.removeUnattached(orgId, now);
|
|
88
|
-
},
|
|
89
|
-
hasUnattachedUploads: (orgId, now) => deps.assets.hasUnattached(orgId, now)
|
|
90
|
-
}
|
|
134
|
+
grants: { grant: (orgId, input) => deps.entitlements.grant(input, system(orgId)) }
|
|
91
135
|
};
|
|
92
136
|
}
|
|
93
137
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"community-mail.js","names":[],"sources":["../../src/app/community-mail.ts"],"sourcesContent":["import type { AssetsService } from '@headless-lms/core/assets';\nimport type {\n CommunityMailReads,\n GrantWriter,\n ScheduledWork,\n} from '@headless-lms/core/automations';\nimport type { ContentService } from '@headless-lms/core/content';\nimport type { ChatService, DiscussionService, PostService } from '@headless-lms/core/discussion';\nimport type { EntitlementsService } from '@headless-lms/core/entitlements';\nimport type { IdentityService } from '@headless-lms/core/identity';\nimport type { OrganizationService } from '@headless-lms/core/organizations';\n\nexport type CommunityMailDeps = {\n content: Pick<ContentService, 'getCommunity' | 'getSpace' | 'listCommunitiesById'>;\n entitlements: Pick<\n EntitlementsService,\n | 'openSpacesFor'\n | 'communitiesOpenedBy'\n | 'communityMembersAmong'\n | 'spaceOpenersAmong'\n | 'spaceOpeners'\n | 'communityModeratorIds'\n | 'countActiveSpaceMembers'\n | 'communityIdsOpenedFor'\n | 'ensureGrant'\n | 'expireDue'\n | 'hasDueNotices'\n >;\n organizations: Pick<OrganizationService, 'getOrgUserById' | 'staffOrgUserIds'>;\n identity: Pick<IdentityService, 'displayName'>;\n posts: Pick<PostService, 'followerIds' | 'find' | 'publishDue' | 'hasDue'>;\n discussion: Pick<DiscussionService, 'getComment' | 'sendDigest'>;\n chats: Pick<ChatService, 'chatSummary' | 'latestMessageBody' | 'isBlocked'>;\n assets: Pick<AssetsService, 'removeUnattached' | 'hasUnattached'>;\n};\n\nexport function communityMail(deps: CommunityMailDeps): {\n mail: Omit<CommunityMailReads, 'recentMessageRecipients'>;\n grants: GrantWriter;\n scheduledWork: ScheduledWork;\n} {\n const mail: Omit<CommunityMailReads, 'recentMessageRecipients'> = {\n async communityTitle(orgId, communityId) {\n return (await deps.content.getCommunity(orgId, communityId))?.title ?? null;\n },\n async space(orgId, spaceId) {\n const space = await deps.content.getSpace(orgId, spaceId);\n return space\n ? { name: space.name, communityId: space.communityId, capacity: space.capacity }\n : null;\n },\n async openSpaceNames(orgId, orgUserId, communityId) {\n const orgUser = await deps.organizations.getOrgUserById(orgId, orgUserId);\n if (!orgUser) {\n return [];\n }\n const spaces = await deps.entitlements.openSpacesFor(\n orgId,\n { id: orgUser.id, role: orgUser.role },\n communityId,\n );\n return spaces.map((space) => space.name);\n },\n communitiesOpenedBy: (orgId, grant) => deps.entitlements.communitiesOpenedBy(orgId, grant),\n communityMembersAmong: (orgId, communityId, ids) =>\n deps.entitlements.communityMembersAmong(orgId, communityId, ids),\n spaceOpenersAmong: (orgId, spaceId, ids) =>\n deps.entitlements.spaceOpenersAmong(orgId, spaceId, ids),\n spaceOpeners: (orgId, spaceId) => deps.entitlements.spaceOpeners(orgId, spaceId),\n moderationAudience: (orgId, communityId) =>\n deps.entitlements.communityModeratorIds(orgId, communityId),\n staffIds: (orgId) => deps.organizations.staffOrgUserIds(orgId),\n async displayName(orgId, orgUserId) {\n const orgUser = await deps.organizations.getOrgUserById(orgId, orgUserId);\n return deps.identity.displayName(orgUser?.userId ?? null);\n },\n followerIds: (orgId, postId) => deps.posts.followerIds(orgId, postId),\n async comment(orgId, commentId) {\n const comment = await deps.discussion.getComment(orgId, commentId);\n return comment\n ? { orgUserId: comment.orgUserId, postId: comment.postId, body: comment.body }\n : null;\n },\n async post(orgId, postId) {\n const post = await deps.posts.find(orgId, postId);\n return post\n ? {\n title: post.title,\n body: post.body,\n spaceId: post.spaceId,\n orgUserId: post.orgUserId,\n communityId: post.communityId,\n }\n : null;\n },\n async chat(orgId, chatId) {\n const summary = await deps.chats.chatSummary(orgId, chatId);\n return summary\n ? {\n communityId: summary.chat.communityId,\n kind: summary.chat.kind,\n status: summary.chat.status,\n createdBy: summary.chat.createdBy,\n participants: summary.participants.map((participant) => ({\n orgUserId: participant.orgUserId,\n muted: participant.muted,\n mutedUntil: participant.mutedUntil,\n leftAt: participant.leftAt,\n })),\n }\n : null;\n },\n latestMessageBody: (orgId, chatId) => deps.chats.latestMessageBody(orgId, chatId),\n pairBlocked: (orgId, communityId, a, b) => deps.chats.isBlocked(orgId, communityId, a, b),\n activeMemberCount: (orgId, spaceId) =>\n deps.entitlements.countActiveSpaceMembers(orgId, spaceId),\n async memberCommunityTitles(orgId, orgUserId) {\n const ids = await deps.entitlements.communityIdsOpenedFor(orgId, orgUserId);\n const communities = await deps.content.listCommunitiesById(orgId, ids);\n return communities.map((community) => ({ id: community.id, title: community.title }));\n },\n };\n return {\n mail,\n grants: {\n grant: (orgId, input) => deps.entitlements.ensureGrant(orgId, input),\n },\n scheduledWork: {\n publishDuePosts: (orgId, now) => deps.posts.publishDue(orgId, now),\n hasDuePosts: (orgId, now) => deps.posts.hasDue(orgId, now),\n expireDueGrants: (orgId, now) => deps.entitlements.expireDue(orgId, now),\n hasDueGrants: (orgId, now) => deps.entitlements.hasDueNotices(orgId, now),\n sendDigest: (orgId, now) => deps.discussion.sendDigest(orgId, now),\n removeUnattachedUploads: async (orgId, now) => {\n await deps.assets.removeUnattached(orgId, now);\n },\n hasUnattachedUploads: (orgId, now) => deps.assets.hasUnattached(orgId, now),\n },\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"community-mail.js","names":[],"sources":["../../src/app/community-mail.ts"],"sourcesContent":["import type { CommunityMailReads, GrantWriter } from '@headless-lms/core/automations';\nimport type { ContentService } from '@headless-lms/core/content';\nimport type {\n ChatService,\n DiscussionService,\n PostService,\n SubscriptionService,\n} from '@headless-lms/core/discussion';\nimport type { EntitlementsService } from '@headless-lms/core/entitlements';\nimport type { IdentityService } from '@headless-lms/core/identity';\nimport type { OrganizationService } from '@headless-lms/core/organizations';\nimport { NotFoundError } from '@headless-lms/core/shared/errors';\nimport type { BodyParser } from '@headless-lms/core/shared/ports';\n\nexport type CommunityMailDeps = {\n content: Pick<\n ContentService,\n | 'findCommunity'\n | 'getSpace'\n | 'listCommunitiesById'\n | 'findCourse'\n | 'findDownload'\n | 'findEvent'\n | 'findBundle'\n >;\n entitlements: Pick<\n EntitlementsService,\n | 'openSpacesFor'\n | 'communitiesOpenedBy'\n | 'communityMembersAmong'\n | 'spaceOpenersAmong'\n | 'spaceOpeners'\n | 'countActiveSpaceMembers'\n | 'communityIdsOpenedFor'\n | 'grant'\n >;\n organizations: Pick<\n OrganizationService,\n | 'getById'\n | 'getOrgUserById'\n | 'actorFor'\n | 'systemActor'\n | 'holders'\n | 'getRole'\n | 'permissionsOfMany'\n >;\n identity: Pick<IdentityService, 'displayName'>;\n posts: Pick<PostService, 'find'>;\n subscriptions: Pick<SubscriptionService, 'followerIds' | 'itemSubscriptions'>;\n discussion: Pick<DiscussionService, 'getComment'>;\n chats: Pick<ChatService, 'chatSummary' | 'latestMessageBody' | 'isBlocked'>;\n bodies: BodyParser;\n};\n\nexport function communityMail(deps: CommunityMailDeps): {\n mail: Omit<CommunityMailReads, 'recentMessageRecipients'>;\n grants: GrantWriter;\n} {\n const system = (orgId: string) => deps.organizations.systemActor(orgId);\n const mail: Omit<CommunityMailReads, 'recentMessageRecipients'> = {\n async communityTitle(orgId, communityId) {\n return (await deps.content.findCommunity(communityId, system(orgId)))?.title ?? null;\n },\n async space(orgId, spaceId) {\n const space = await deps.content.getSpace(spaceId, system(orgId));\n return space\n ? { name: space.name, communityId: space.communityId, capacity: space.capacity }\n : null;\n },\n async openSpaceNames(orgId, orgUserId, communityId) {\n const person = await deps.organizations.actorFor(orgId, orgUserId);\n if (!person) {\n return [];\n }\n const spaces = await deps.entitlements.openSpacesFor(communityId, person);\n return spaces.map((space) => space.name);\n },\n communitiesOpenedBy: (orgId, grant) => deps.entitlements.communitiesOpenedBy(orgId, grant),\n communityMembersAmong: (orgId, communityId, ids) =>\n deps.entitlements.communityMembersAmong(orgId, communityId, ids),\n spaceOpenersAmong: (orgId, spaceId, ids) =>\n deps.entitlements.spaceOpenersAmong(orgId, spaceId, ids),\n spaceOpeners: (orgId, spaceId) => deps.entitlements.spaceOpeners(orgId, spaceId),\n holders: (orgId, permission, on) => deps.organizations.holders(orgId, permission, on),\n async heldOnOrg(orgId, orgUserId, permissions) {\n const held = (await deps.organizations.permissionsOfMany(orgId, [orgUserId])).get(orgUserId);\n return held ? permissions.filter((permission) => held.has(permission)) : [];\n },\n async role(orgId, roleKey) {\n try {\n const role = await deps.organizations.getRole(roleKey, system(orgId));\n return { name: role.name, permissions: role.permissions };\n } catch (err) {\n if (err instanceof NotFoundError) {\n return null;\n }\n throw err;\n }\n },\n async orgName(orgId) {\n return (await deps.organizations.getById(orgId))?.name ?? null;\n },\n async contentInfo(orgId, item) {\n const actor = system(orgId);\n if ('bundleId' in item) {\n const bundle = await deps.content.findBundle(item.bundleId, actor);\n return bundle ? { title: bundle.name, type: 'bundle' } : null;\n }\n const course = await deps.content.findCourse(item.contentId, actor);\n if (course) {\n return { title: course.title, type: course.type };\n }\n const download = await deps.content.findDownload(item.contentId, actor);\n if (download) {\n return { title: download.title, type: download.type };\n }\n const community = await deps.content.findCommunity(item.contentId, actor);\n if (community) {\n return { title: community.title, type: community.type };\n }\n const event = await deps.content.findEvent(item.contentId, actor);\n return event ? { title: event.title, type: event.type } : null;\n },\n async displayName(orgId, orgUserId) {\n const orgUser = await deps.organizations.getOrgUserById(orgId, orgUserId);\n return deps.identity.displayName(orgUser?.userId ?? null);\n },\n postText: (body) => deps.bodies.parse(body).text,\n followerIds: (orgId, postId) => deps.subscriptions.followerIds(orgId, postId),\n async comment(orgId, commentId) {\n const comment = await deps.discussion.getComment(orgId, commentId);\n return comment\n ? {\n orgUserId: comment.orgUserId,\n postId: comment.postId,\n activityId: comment.activityId,\n parentId: comment.parentId,\n body: comment.body,\n }\n : null;\n },\n async post(orgId, postId) {\n const post = await deps.posts.find(orgId, postId);\n return post\n ? {\n title: post.title,\n body: post.body,\n spaceId: post.spaceId,\n orgUserId: post.orgUserId,\n communityId: post.communityId,\n }\n : null;\n },\n async chat(orgId, chatId) {\n const summary = await deps.chats.chatSummary(orgId, chatId);\n if (!summary) {\n return null;\n }\n const mutes = new Map(\n (await deps.subscriptions.itemSubscriptions(orgId, { type: 'chat', id: chatId }))\n .filter((subscription) => subscription.state === 'muted')\n .map((subscription) => [subscription.orgUserId, subscription.mutedUntil]),\n );\n return {\n communityId: summary.chat.communityId,\n kind: summary.chat.kind,\n status: summary.chat.status,\n createdBy: summary.chat.createdBy,\n participants: summary.participants.map((participant) => ({\n orgUserId: participant.orgUserId,\n muted: mutes.has(participant.orgUserId),\n mutedUntil: mutes.get(participant.orgUserId) ?? null,\n leftAt: participant.leftAt,\n })),\n };\n },\n latestMessageBody: (orgId, chatId) => deps.chats.latestMessageBody(orgId, chatId),\n pairBlocked: (orgId, communityId, a, b) => deps.chats.isBlocked(orgId, communityId, a, b),\n activeMemberCount: (orgId, spaceId) =>\n deps.entitlements.countActiveSpaceMembers(orgId, spaceId),\n async memberCommunityTitles(orgId, orgUserId) {\n const ids = await deps.entitlements.communityIdsOpenedFor(orgId, orgUserId);\n const communities = await deps.content.listCommunitiesById(ids, system(orgId));\n return communities.map((community) => ({ id: community.id, title: community.title }));\n },\n };\n return {\n mail,\n grants: {\n grant: (orgId, input) => deps.entitlements.grant(input, system(orgId)),\n },\n };\n}\n"],"mappings":"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"}
|
package/dist/app/container.d.ts
CHANGED
|
@@ -1,22 +1,24 @@
|
|
|
1
1
|
import { McpEndpoints } from "./mcp-endpoints.js";
|
|
2
|
+
import { PlatformConfig, PlatformKeyCheck } from "./platform.js";
|
|
3
|
+
import { DatabasePoolOptions } from "@headless-lms/adapter-db";
|
|
2
4
|
import { PollingOutboxRelayConfig } from "@headless-lms/adapter-defaults/events/outbox-relay";
|
|
3
5
|
import { LogLevel, PinoInstance, RequestLogContext } from "@headless-lms/adapter-defaults/logging";
|
|
4
6
|
import { BetterAuth } from "@headless-lms/adapter-auth";
|
|
5
|
-
import { ContentService } from "@headless-lms/core/content";
|
|
7
|
+
import { CommunityNotificationCategories, ContentService } from "@headless-lms/core/content";
|
|
6
8
|
import { EntitlementsServiceImpl } from "@headless-lms/core/entitlements";
|
|
7
9
|
import { ProgressServiceImpl } from "@headless-lms/core/progress";
|
|
8
|
-
import { ChatServiceImpl, DiscussionServiceImpl, PostServiceImpl } from "@headless-lms/core/discussion";
|
|
9
|
-
import { IdentityServiceImpl,
|
|
10
|
-
import { OrgAdmin, OrganizationServiceImpl } from "@headless-lms/core/organizations";
|
|
10
|
+
import { ChatHub, ChatHubOptions, ChatServiceImpl, DiscussionServiceImpl, PostServiceImpl, SubscriptionServiceImpl } from "@headless-lms/core/discussion";
|
|
11
|
+
import { DevicesServiceImpl, IdentityServiceImpl, NotificationSettingsServiceImpl, SessionAdmin } from "@headless-lms/core/identity";
|
|
12
|
+
import { NotificationCatalog, OrgAdmin, OrgNotificationCategories, OrganizationServiceImpl, RoleDefinition } from "@headless-lms/core/organizations";
|
|
11
13
|
import { AssetsServiceImpl } from "@headless-lms/core/assets";
|
|
12
14
|
import { IntegrationsServiceImpl } from "@headless-lms/core/integrations";
|
|
13
|
-
import { AutomationsServiceImpl } from "@headless-lms/core/automations";
|
|
15
|
+
import { AutomationsServiceImpl, NotificationCategories, NotificationsServiceImpl } from "@headless-lms/core/automations";
|
|
16
|
+
import { Mailer } from "@headless-lms/core/shared/mailer";
|
|
14
17
|
import { AdminReportServiceImpl } from "@headless-lms/core/reporting/admin";
|
|
15
18
|
import { MemberReportServiceImpl } from "@headless-lms/core/reporting/member";
|
|
16
|
-
import { Mailer } from "@headless-lms/core/shared/mailer";
|
|
17
19
|
import { SettingsService } from "@headless-lms/core/shared/settings";
|
|
18
|
-
import { AutomationEngine, WebhookSender } from "@headless-lms/core/types";
|
|
19
|
-
import { CredentialStore, EmailSender, Logger, ObjectStorage, OutboxRelay, TemplateContext, TemplateRenderer } from "@headless-lms/core/shared/ports";
|
|
20
|
+
import { AutomationEngine, PushConfig, WebhookSender } from "@headless-lms/core/types";
|
|
21
|
+
import { BodyParser, ChannelRegistry, CredentialStore, EmailSender, Logger, NotificationChannel, NotificationTypeRegistry, ObjectStorage, OutboxRelay, PubSub, PushSender, Scheduler, TemplateContext, TemplateRenderer } from "@headless-lms/core/shared/ports";
|
|
20
22
|
//#region src/app/container.d.ts
|
|
21
23
|
/** Installation-supplied ports; an absent slot falls back to a fail-loudly stub. */
|
|
22
24
|
interface AdapterOverrides {
|
|
@@ -25,17 +27,30 @@ interface AdapterOverrides {
|
|
|
25
27
|
/** Resolves email templates to rendered content. Absent → fail-loudly stub. */
|
|
26
28
|
templates?: TemplateRenderer;
|
|
27
29
|
/** Durable automation engine, or a factory given the automations logger. Absent → InlineAutomationEngine (in-process, one attempt per action). */
|
|
28
|
-
workflows?: AutomationEngine | ((logger: Logger) => AutomationEngine);
|
|
30
|
+
workflows?: (AutomationEngine & Scheduler) | ((logger: Logger) => AutomationEngine & Scheduler);
|
|
29
31
|
/** Outbound HTTP for webhook deliveries. Absent → FetchWebhookSender (public addresses only, 10s timeout). */
|
|
30
32
|
webhooks?: WebhookSender;
|
|
33
|
+
/** Notification channels beyond in-app, email and push; each key must be new. */
|
|
34
|
+
notificationChannels?: NotificationChannel[];
|
|
35
|
+
/** Push credentials per provider. Absent → no provider is configured and devices cannot register. */
|
|
36
|
+
push?: PushConfig;
|
|
37
|
+
pushSender?: PushSender;
|
|
38
|
+
/** Reads post bodies: their text, mentions and images. Absent → PlainBodyParser (plain text, no images). */
|
|
39
|
+
bodyParser?: BodyParser;
|
|
40
|
+
/** Delivery to every API instance, or a factory given its logger. Absent → no live chat; the chat stream answers 503. */
|
|
41
|
+
pubsub?: PubSub | ((logger: Logger) => PubSub);
|
|
31
42
|
}
|
|
32
43
|
interface BuildContainerOptions {
|
|
33
44
|
/** Installation's plugins folder, scanned by loadIntegrations. Absent → no integrations. */
|
|
34
45
|
pluginsDir?: string;
|
|
35
46
|
adapters?: AdapterOverrides;
|
|
47
|
+
roles?: readonly RoleDefinition[];
|
|
48
|
+
/** Live chat hub timings. Absent → the defaults (1 s state flush, 30 s session check). */
|
|
49
|
+
live?: ChatHubOptions;
|
|
36
50
|
}
|
|
37
51
|
interface Config {
|
|
38
52
|
databaseUrl: string;
|
|
53
|
+
databasePool?: DatabasePoolOptions;
|
|
39
54
|
authBaseURL: string;
|
|
40
55
|
authSecret: string;
|
|
41
56
|
trustedOrigins: string[];
|
|
@@ -60,8 +75,10 @@ interface Config {
|
|
|
60
75
|
* ServerConfig.deliveryExpirySeconds — createContainer threads it through. */
|
|
61
76
|
deliveryExpirySeconds: number;
|
|
62
77
|
memberImageMaxBytes: number;
|
|
78
|
+
memberFileMaxBytes: number;
|
|
63
79
|
/** Let the default webhook sender reach loopback and private network addresses. Local development only. */
|
|
64
80
|
webhookAllowPrivateNetwork?: boolean;
|
|
81
|
+
platform?: PlatformConfig;
|
|
65
82
|
}
|
|
66
83
|
/** Tuning for the transactional-outbox relay. Every field is optional; the
|
|
67
84
|
* container resolves against OUTBOX_DEFAULTS. */
|
|
@@ -72,6 +89,7 @@ interface OutboxConfig {
|
|
|
72
89
|
pollIntervalMs?: number;
|
|
73
90
|
/** Max rows fetched/dispatched per tick. Default 100. */
|
|
74
91
|
batchSize?: number;
|
|
92
|
+
concurrency?: number;
|
|
75
93
|
}
|
|
76
94
|
declare const OUTBOX_DEFAULTS: PollingOutboxRelayConfig;
|
|
77
95
|
declare function resolveOutboxConfig(config?: OutboxConfig): PollingOutboxRelayConfig;
|
|
@@ -85,9 +103,9 @@ declare function resolveLoggingConfig(config?: LoggingConfig): Required<LoggingC
|
|
|
85
103
|
interface Container {
|
|
86
104
|
auth: BetterAuth;
|
|
87
105
|
mcp: McpEndpoints;
|
|
106
|
+
platform: PlatformKeyCheck | null;
|
|
88
107
|
orgProvider: OrgAdmin;
|
|
89
108
|
identity: IdentityServiceImpl;
|
|
90
|
-
notifications: NotificationServiceImpl;
|
|
91
109
|
organizations: OrganizationServiceImpl;
|
|
92
110
|
content: ContentService;
|
|
93
111
|
entitlements: EntitlementsServiceImpl;
|
|
@@ -95,9 +113,21 @@ interface Container {
|
|
|
95
113
|
discussion: DiscussionServiceImpl;
|
|
96
114
|
posts: PostServiceImpl;
|
|
97
115
|
chats: ChatServiceImpl;
|
|
116
|
+
subscriptions: SubscriptionServiceImpl;
|
|
98
117
|
assets: AssetsServiceImpl;
|
|
99
118
|
integrations: IntegrationsServiceImpl;
|
|
100
119
|
automations: AutomationsServiceImpl;
|
|
120
|
+
notificationTypes: NotificationTypeRegistry;
|
|
121
|
+
notificationChannels: ChannelRegistry;
|
|
122
|
+
notificationCategories: {
|
|
123
|
+
community: CommunityNotificationCategories;
|
|
124
|
+
org: OrgNotificationCategories;
|
|
125
|
+
};
|
|
126
|
+
notificationCatalog: NotificationCatalog;
|
|
127
|
+
notificationCategoryLists: NotificationCategories;
|
|
128
|
+
notificationSettings: NotificationSettingsServiceImpl;
|
|
129
|
+
devices: DevicesServiceImpl;
|
|
130
|
+
notificationList: NotificationsServiceImpl;
|
|
101
131
|
/** Cross-cutting settings store. Not a context — a leaf every context may use;
|
|
102
132
|
* inject `settings.for('<namespace>')` into a service, never the service itself. */
|
|
103
133
|
settings: SettingsService;
|
|
@@ -117,6 +147,14 @@ interface Container {
|
|
|
117
147
|
* the installation's entry point starts it after listen (gen-openapi must
|
|
118
148
|
* not run a worker). buildServer stops it onClose. */
|
|
119
149
|
automationEngine: AutomationEngine;
|
|
150
|
+
scheduler: Scheduler;
|
|
151
|
+
/** The PubSub adapter — constructed but NEVER started by the container; the
|
|
152
|
+
* installation's entry point starts it after listen. buildServer stops it
|
|
153
|
+
* onClose. Null without one. */
|
|
154
|
+
pubsub: PubSub | null;
|
|
155
|
+
/** This process's live chat hub; started by the entry point after `pubsub`,
|
|
156
|
+
* closed by buildServer preClose. Null without a PubSub. */
|
|
157
|
+
chatHub: ChatHub | null;
|
|
120
158
|
/** Root logger port — components receive children bound with { name }. */
|
|
121
159
|
logger: Logger;
|
|
122
160
|
/** The raw pino root; buildServer hands it to Fastify so HTTP shares the stream. */
|