@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/dist/app/container.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
import { buildNotificationRegistries, configuredPushSender } from "./notification-channels.js";
|
|
1
2
|
import { loadIntegrations } from "./integrations.js";
|
|
2
3
|
import { mcpEndpoints } from "./mcp-endpoints.js";
|
|
3
4
|
import { communityMail } from "./community-mail.js";
|
|
4
|
-
import {
|
|
5
|
+
import { notificationReads } from "./notification-reads.js";
|
|
6
|
+
import { platformKeyCheck } from "./platform.js";
|
|
7
|
+
import { DrizzleAdminRepository, DrizzleAssetsRepository, DrizzleAutomationRunsRepository, DrizzleAutomationsRepository, DrizzleChatRepository, DrizzleConnectionsRepository, DrizzleContentRepository, DrizzleCredentialStore, DrizzleDevicesRepository, DrizzleDigestRunRepository, DrizzleDiscussionRepository, DrizzleEntitlementsRepository, DrizzleIdentityRepository, DrizzleMemberRepository, DrizzleMembersRepository, DrizzleMentionsRepository, DrizzleNotificationSettingsRepository, DrizzleNotificationsRepository, DrizzleOrganizationsRepository, DrizzleOutboxAppender, DrizzleOutboxStore, DrizzlePostRepository, DrizzleProgressRepository, DrizzleRolesRepository, DrizzleSettingsRepository, DrizzleSubscriptionsRepository, DrizzleUnitOfWork, createDb } from "@headless-lms/adapter-db";
|
|
5
8
|
import { InMemoryEventBus } from "@headless-lms/adapter-defaults/events";
|
|
6
9
|
import { PollingOutboxRelay } from "@headless-lms/adapter-defaults/events/outbox-relay";
|
|
7
10
|
import { InlineAutomationEngine } from "@headless-lms/adapter-defaults/workflows";
|
|
@@ -9,25 +12,27 @@ import { FetchWebhookSender } from "@headless-lms/adapter-defaults/webhooks";
|
|
|
9
12
|
import { EmailAdapter, StubTemplateRenderer } from "@headless-lms/adapter-defaults/email";
|
|
10
13
|
import { createRootLogger, requestLogContext } from "@headless-lms/adapter-defaults/logging";
|
|
11
14
|
import { StorageAdapter } from "@headless-lms/adapter-defaults/storage";
|
|
15
|
+
import { PlainBodyParser } from "@headless-lms/adapter-defaults/body";
|
|
12
16
|
import { BetterAuth } from "@headless-lms/adapter-auth";
|
|
13
|
-
import { ContentService } from "@headless-lms/core/content";
|
|
14
|
-
import { EntitlementsServiceImpl } from "@headless-lms/core/entitlements";
|
|
17
|
+
import { CommunityNotificationCategories, ContentItemTitles, ContentService, contentJobs } from "@headless-lms/core/content";
|
|
18
|
+
import { EntitlementsServiceImpl, entitlementsJobs, entitlementsRecurringJobs } from "@headless-lms/core/entitlements";
|
|
15
19
|
import { ProgressServiceImpl } from "@headless-lms/core/progress";
|
|
16
|
-
import { ChatServiceImpl, DiscussionServiceImpl, PostServiceImpl } from "@headless-lms/core/discussion";
|
|
17
|
-
import { IdentityServiceImpl,
|
|
18
|
-
import {
|
|
19
|
-
import { AssetsServiceImpl, createFileUrls } from "@headless-lms/core/assets";
|
|
20
|
+
import { ChatLiveForwarder, ChatServiceImpl, DigestServiceImpl, DiscussionItemTitles, DiscussionServiceImpl, PostServiceImpl, SubscriptionServiceImpl, discussionJobs, discussionRecurringJobs } from "@headless-lms/core/discussion";
|
|
21
|
+
import { DevicesServiceImpl, IdentityServiceImpl, NotificationSettingsServiceImpl } from "@headless-lms/core/identity";
|
|
22
|
+
import { NotificationCatalog, OrgNotificationCategories, OrganizationServiceImpl } from "@headless-lms/core/organizations";
|
|
23
|
+
import { AssetsServiceImpl, assetsJobs, assetsRecurringJobs, createFileUrls } from "@headless-lms/core/assets";
|
|
20
24
|
import { IntegrationsServiceImpl } from "@headless-lms/core/integrations";
|
|
21
|
-
import { AutomationsServiceImpl } from "@headless-lms/core/automations";
|
|
25
|
+
import { AutomationsServiceImpl, DEFAULT_COMMUNITY_CATEGORIES, DEFAULT_ORG_CATEGORIES, NOTIFICATION_TYPES, NotificationDelivery, NotificationProcessor, NotificationsServiceImpl, RECIPIENT_RULES, SchedulerChatDebounce, notificationJobs } from "@headless-lms/core/automations";
|
|
26
|
+
import { Mailer } from "@headless-lms/core/shared/mailer";
|
|
22
27
|
import { AdminReportServiceImpl } from "@headless-lms/core/reporting/admin";
|
|
23
28
|
import { MemberReportServiceImpl } from "@headless-lms/core/reporting/member";
|
|
24
|
-
import { Mailer } from "@headless-lms/core/shared/mailer";
|
|
25
29
|
import { SettingsService } from "@headless-lms/core/shared/settings";
|
|
26
30
|
//#region src/app/container.ts
|
|
27
31
|
const OUTBOX_DEFAULTS = {
|
|
28
32
|
enabled: true,
|
|
29
33
|
pollIntervalMs: 1e3,
|
|
30
|
-
batchSize: 100
|
|
34
|
+
batchSize: 100,
|
|
35
|
+
concurrency: 16
|
|
31
36
|
};
|
|
32
37
|
function resolveOutboxConfig(config = {}) {
|
|
33
38
|
const overrides = Object.fromEntries(Object.entries(config).filter(([, value]) => value !== void 0));
|
|
@@ -51,12 +56,15 @@ async function buildContainer(config, options) {
|
|
|
51
56
|
const automationsLogger = logger.child({ name: "automations" });
|
|
52
57
|
const reportingLogger = logger.child({ name: "reporting" });
|
|
53
58
|
const outboxLogger = logger.child({ name: "outbox" });
|
|
54
|
-
const db = createDb(config.databaseUrl);
|
|
59
|
+
const db = createDb(config.databaseUrl, logger.child({ name: "db" }), config.databasePool);
|
|
55
60
|
const email = options?.adapters?.email ?? new EmailAdapter(logger.child({ name: "email" }));
|
|
56
61
|
const storage = options?.adapters?.storage ?? new StorageAdapter(logger.child({ name: "storage" }));
|
|
57
62
|
const templates = options?.adapters?.templates ?? new StubTemplateRenderer(logger.child({ name: "email" }));
|
|
63
|
+
const bodies = options?.adapters?.bodyParser ?? new PlainBodyParser();
|
|
58
64
|
const workflows = options?.adapters?.workflows;
|
|
59
65
|
const automationEngine = typeof workflows === "function" ? workflows(automationsLogger) : workflows ?? new InlineAutomationEngine(automationsLogger);
|
|
66
|
+
const pubsubOption = options?.adapters?.pubsub;
|
|
67
|
+
const pubsub = typeof pubsubOption === "function" ? pubsubOption(logger.child({ name: "pubsub" })) : pubsubOption ?? null;
|
|
60
68
|
const mailer = new Mailer(templates, email, {
|
|
61
69
|
...config.emailBranding ?? {
|
|
62
70
|
brandName: "Headless LMS",
|
|
@@ -64,7 +72,7 @@ async function buildContainer(config, options) {
|
|
|
64
72
|
},
|
|
65
73
|
studentPortalUrl: config.studentPortalUrl
|
|
66
74
|
});
|
|
67
|
-
const fileUrls = createFileUrls(config.authBaseURL);
|
|
75
|
+
const fileUrls = createFileUrls(config.authBaseURL, (key) => storage.publicUrl(key));
|
|
68
76
|
const assets = new AssetsServiceImpl({
|
|
69
77
|
storage,
|
|
70
78
|
repo: new DrizzleAssetsRepository(db, assetsLogger),
|
|
@@ -73,6 +81,7 @@ async function buildContainer(config, options) {
|
|
|
73
81
|
outbox: new DrizzleOutboxAppender(tx, outboxLogger)
|
|
74
82
|
})),
|
|
75
83
|
memberImageMaxBytes: config.memberImageMaxBytes,
|
|
84
|
+
memberFileMaxBytes: config.memberFileMaxBytes,
|
|
76
85
|
downloadLinkExpirySeconds: config.deliveryExpirySeconds,
|
|
77
86
|
fileUrls,
|
|
78
87
|
logger: assetsLogger
|
|
@@ -89,31 +98,49 @@ async function buildContainer(config, options) {
|
|
|
89
98
|
images: assets,
|
|
90
99
|
fileUrls
|
|
91
100
|
});
|
|
92
|
-
const notifications = new NotificationServiceImpl({
|
|
93
|
-
repo: new DrizzleNotificationsRepository(db),
|
|
94
|
-
logger: logger.child({ name: "notifications" })
|
|
95
|
-
});
|
|
96
101
|
const organizationsRepo = new DrizzleOrganizationsRepository(db, logger.child({ name: "org" }));
|
|
97
102
|
const organizationsUow = new DrizzleUnitOfWork(db, (tx) => ({
|
|
98
103
|
organizations: new DrizzleOrganizationsRepository(tx, logger.child({ name: "org" })),
|
|
104
|
+
roles: new DrizzleRolesRepository(tx),
|
|
99
105
|
outbox: new DrizzleOutboxAppender(tx, logger.child({ name: "org" }))
|
|
100
106
|
}));
|
|
101
107
|
const orgAdminRef = { current: void 0 };
|
|
108
|
+
const platform = config.platform ? platformKeyCheck(config.platform) : null;
|
|
102
109
|
const organizations = new OrganizationServiceImpl({
|
|
103
110
|
repo: organizationsRepo,
|
|
111
|
+
roles: new DrizzleRolesRepository(db),
|
|
112
|
+
...options?.roles && { defaultRoles: options.roles },
|
|
113
|
+
apiKeys: () => {
|
|
114
|
+
if (!orgAdminRef.current) throw new Error("api keys not initialised");
|
|
115
|
+
return orgAdminRef.current;
|
|
116
|
+
},
|
|
117
|
+
accounts: () => {
|
|
118
|
+
if (!orgAdminRef.current) throw new Error("accounts not initialised");
|
|
119
|
+
return orgAdminRef.current;
|
|
120
|
+
},
|
|
121
|
+
...config.platform && { platform: {
|
|
122
|
+
email: config.platform.accountEmail,
|
|
123
|
+
name: config.platform.accountName ?? "Platform"
|
|
124
|
+
} },
|
|
104
125
|
membersRepo: new DrizzleMembersRepository(db, logger.child({ name: "org" })),
|
|
105
126
|
people: identity,
|
|
127
|
+
images: assets,
|
|
106
128
|
uow: organizationsUow,
|
|
107
129
|
orgAdmin: () => {
|
|
108
130
|
if (!orgAdminRef.current) throw new Error("orgAdmin not initialised");
|
|
109
131
|
return orgAdminRef.current;
|
|
110
132
|
},
|
|
133
|
+
sessions: () => {
|
|
134
|
+
if (!orgAdminRef.current) throw new Error("sessions not initialised");
|
|
135
|
+
return orgAdminRef.current;
|
|
136
|
+
},
|
|
111
137
|
logger: logger.child({ name: "org" }),
|
|
112
138
|
mailer,
|
|
113
139
|
inviteUrls: {
|
|
114
140
|
studentPortalUrl: config.studentPortalUrl,
|
|
115
141
|
adminAppUrl: config.adminAppUrl
|
|
116
|
-
}
|
|
142
|
+
},
|
|
143
|
+
grants: () => entitlements
|
|
117
144
|
});
|
|
118
145
|
const settings = new SettingsService(new DrizzleSettingsRepository(db, logger.child({ name: "settings" })));
|
|
119
146
|
const contentUow = new DrizzleUnitOfWork(db, (tx) => ({
|
|
@@ -126,28 +153,74 @@ async function buildContainer(config, options) {
|
|
|
126
153
|
libraryAssets: assets,
|
|
127
154
|
files: assets,
|
|
128
155
|
fileUrls,
|
|
156
|
+
scheduler: automationEngine,
|
|
129
157
|
logger: contentLogger
|
|
130
158
|
});
|
|
159
|
+
const pushConfig = options?.adapters?.push ?? {};
|
|
160
|
+
const pushSender = configuredPushSender(pushConfig, options?.adapters?.pushSender);
|
|
161
|
+
const notificationRegistries = buildNotificationRegistries({
|
|
162
|
+
declarations: NOTIFICATION_TYPES,
|
|
163
|
+
defaults: {
|
|
164
|
+
community: DEFAULT_COMMUNITY_CATEGORIES,
|
|
165
|
+
org: DEFAULT_ORG_CATEGORIES
|
|
166
|
+
},
|
|
167
|
+
mailer: options?.adapters?.email ? mailer : null,
|
|
168
|
+
emailOf: async (_orgId, userId) => (await identity.getUserById(userId))?.email ?? null,
|
|
169
|
+
push: pushSender ? {
|
|
170
|
+
sender: pushSender,
|
|
171
|
+
activeDevices: (orgId, userId, since) => devices.activeDevices(orgId, userId, since)
|
|
172
|
+
} : null,
|
|
173
|
+
channels: options?.adapters?.notificationChannels ?? []
|
|
174
|
+
});
|
|
175
|
+
const notificationCategories = {
|
|
176
|
+
community: new CommunityNotificationCategories({
|
|
177
|
+
communities: new DrizzleContentRepository(db, contentLogger),
|
|
178
|
+
settings,
|
|
179
|
+
types: notificationRegistries.types,
|
|
180
|
+
channels: notificationRegistries.channels,
|
|
181
|
+
logger: contentLogger
|
|
182
|
+
}),
|
|
183
|
+
org: new OrgNotificationCategories({
|
|
184
|
+
settings,
|
|
185
|
+
types: notificationRegistries.types,
|
|
186
|
+
channels: notificationRegistries.channels,
|
|
187
|
+
logger: logger.child({ name: "org" })
|
|
188
|
+
})
|
|
189
|
+
};
|
|
190
|
+
const notificationCatalog = new NotificationCatalog({
|
|
191
|
+
types: notificationRegistries.types,
|
|
192
|
+
channels: notificationRegistries.channels
|
|
193
|
+
});
|
|
194
|
+
const notificationCategoryLists = { listFor: async (orgId, communityId) => (communityId ? await notificationCategories.community.get(communityId, organizations.systemActor(orgId)) : await notificationCategories.org.get(orgId, organizations.systemActor(orgId))).categories };
|
|
131
195
|
const entitlementsUow = new DrizzleUnitOfWork(db, (tx) => ({
|
|
132
196
|
entitlements: new DrizzleEntitlementsRepository(tx, entitlementsLogger),
|
|
133
197
|
spaces: new DrizzleContentRepository(tx, contentLogger),
|
|
134
198
|
outbox: new DrizzleOutboxAppender(tx, outboxLogger)
|
|
135
199
|
}));
|
|
136
|
-
const authors =
|
|
137
|
-
roleOf: async (orgId, orgUserId) => {
|
|
138
|
-
return (await organizationsRepo.findOrgUserById(orgId, orgUserId))?.role ?? null;
|
|
139
|
-
},
|
|
140
|
-
rolesOf: async (orgId, orgUserIds) => {
|
|
141
|
-
const rows = await organizationsRepo.findOrgUsersByIds(orgId, orgUserIds);
|
|
142
|
-
return Object.fromEntries(rows.map((row) => [row.id, row.role]));
|
|
143
|
-
}
|
|
144
|
-
};
|
|
200
|
+
const authors = organizations;
|
|
145
201
|
const entitlements = new EntitlementsServiceImpl({
|
|
146
202
|
repo: new DrizzleEntitlementsRepository(db, entitlementsLogger),
|
|
147
203
|
content,
|
|
204
|
+
permissions: organizations,
|
|
148
205
|
uow: entitlementsUow,
|
|
149
206
|
logger: entitlementsLogger
|
|
150
207
|
});
|
|
208
|
+
const identityLogger = logger.child({ name: "identity" });
|
|
209
|
+
const notificationSettings = new NotificationSettingsServiceImpl({
|
|
210
|
+
repo: new DrizzleNotificationSettingsRepository(db, identityLogger),
|
|
211
|
+
categories: {
|
|
212
|
+
communityList: async (orgId, communityId) => (await notificationCategories.community.get(communityId, organizations.systemActor(orgId))).categories,
|
|
213
|
+
orgList: async (orgId) => (await notificationCategories.org.get(orgId, organizations.systemActor(orgId))).categories
|
|
214
|
+
},
|
|
215
|
+
access: { canOpen: (orgId, actor, communityId) => entitlements.isCommunityMember(communityId, actor) },
|
|
216
|
+
channels: notificationRegistries.channels,
|
|
217
|
+
logger: identityLogger
|
|
218
|
+
});
|
|
219
|
+
const devices = new DevicesServiceImpl({
|
|
220
|
+
repo: new DrizzleDevicesRepository(db, identityLogger),
|
|
221
|
+
push: pushConfig,
|
|
222
|
+
logger: identityLogger
|
|
223
|
+
});
|
|
151
224
|
const progressUow = new DrizzleUnitOfWork(db, (tx) => ({
|
|
152
225
|
progress: new DrizzleProgressRepository(tx, progressLogger),
|
|
153
226
|
outbox: new DrizzleOutboxAppender(tx, outboxLogger)
|
|
@@ -156,12 +229,15 @@ async function buildContainer(config, options) {
|
|
|
156
229
|
repo: new DrizzleProgressRepository(db, progressLogger),
|
|
157
230
|
content,
|
|
158
231
|
access: entitlements,
|
|
232
|
+
system: organizations,
|
|
159
233
|
uow: progressUow,
|
|
160
234
|
logger: progressLogger
|
|
161
235
|
});
|
|
162
236
|
const postReads = new DrizzlePostRepository(db, discussionLogger);
|
|
163
237
|
const discussionReads = new DrizzleDiscussionRepository(db, discussionLogger);
|
|
164
238
|
const chatReads = new DrizzleChatRepository(db, discussionLogger);
|
|
239
|
+
const subscriptionReads = new DrizzleSubscriptionsRepository(db, discussionLogger);
|
|
240
|
+
const mentionReads = new DrizzleMentionsRepository(db, discussionLogger);
|
|
165
241
|
const posts = new PostServiceImpl({
|
|
166
242
|
repo: postReads,
|
|
167
243
|
access: entitlements,
|
|
@@ -169,12 +245,17 @@ async function buildContainer(config, options) {
|
|
|
169
245
|
authors,
|
|
170
246
|
profiles: discussionReads,
|
|
171
247
|
comments: discussionReads,
|
|
248
|
+
subscriptions: subscriptionReads,
|
|
249
|
+
mentions: mentionReads,
|
|
172
250
|
images: assets,
|
|
173
251
|
fileUrls,
|
|
252
|
+
bodies,
|
|
174
253
|
uow: new DrizzleUnitOfWork(db, (tx) => ({
|
|
175
254
|
posts: new DrizzlePostRepository(tx, discussionLogger),
|
|
255
|
+
mentions: new DrizzleMentionsRepository(tx, discussionLogger),
|
|
176
256
|
outbox: new DrizzleOutboxAppender(tx, outboxLogger)
|
|
177
257
|
})),
|
|
258
|
+
scheduler: automationEngine,
|
|
178
259
|
logger: discussionLogger
|
|
179
260
|
});
|
|
180
261
|
const chats = new ChatServiceImpl({
|
|
@@ -183,12 +264,17 @@ async function buildContainer(config, options) {
|
|
|
183
264
|
content,
|
|
184
265
|
authors,
|
|
185
266
|
profiles: discussionReads,
|
|
267
|
+
subscriptions: subscriptionReads,
|
|
268
|
+
mentions: mentionReads,
|
|
186
269
|
images: assets,
|
|
187
270
|
fileUrls,
|
|
188
271
|
uow: new DrizzleUnitOfWork(db, (tx) => ({
|
|
189
272
|
chats: new DrizzleChatRepository(tx, discussionLogger),
|
|
273
|
+
mentions: new DrizzleMentionsRepository(tx, discussionLogger),
|
|
190
274
|
outbox: new DrizzleOutboxAppender(tx, outboxLogger)
|
|
191
275
|
})),
|
|
276
|
+
...pubsub && { pubsub },
|
|
277
|
+
...options?.live && { live: options.live },
|
|
192
278
|
logger: discussionLogger
|
|
193
279
|
});
|
|
194
280
|
const discussion = new DiscussionServiceImpl({
|
|
@@ -196,17 +282,68 @@ async function buildContainer(config, options) {
|
|
|
196
282
|
access: entitlements,
|
|
197
283
|
content,
|
|
198
284
|
posts,
|
|
199
|
-
|
|
285
|
+
spaces: entitlements,
|
|
286
|
+
authors,
|
|
287
|
+
mentions: mentionReads,
|
|
200
288
|
fileUrls,
|
|
201
289
|
uow: new DrizzleUnitOfWork(db, (tx) => ({
|
|
202
290
|
discussion: new DrizzleDiscussionRepository(tx, discussionLogger),
|
|
291
|
+
mentions: new DrizzleMentionsRepository(tx, discussionLogger),
|
|
203
292
|
outbox: new DrizzleOutboxAppender(tx, outboxLogger)
|
|
204
293
|
})),
|
|
205
294
|
settings,
|
|
206
295
|
logger: discussionLogger
|
|
207
296
|
});
|
|
297
|
+
const subscriptions = new SubscriptionServiceImpl({
|
|
298
|
+
repo: subscriptionReads,
|
|
299
|
+
access: entitlements,
|
|
300
|
+
posts,
|
|
301
|
+
chats,
|
|
302
|
+
uow: new DrizzleUnitOfWork(db, (tx) => ({
|
|
303
|
+
subscriptions: new DrizzleSubscriptionsRepository(tx, discussionLogger),
|
|
304
|
+
outbox: new DrizzleOutboxAppender(tx, outboxLogger)
|
|
305
|
+
})),
|
|
306
|
+
logger: discussionLogger
|
|
307
|
+
});
|
|
308
|
+
const digests = new DigestServiceImpl({
|
|
309
|
+
schedules: { communitiesWithDigest: () => content.communitiesWithDigest() },
|
|
310
|
+
runs: new DrizzleDigestRunRepository(db, discussionLogger),
|
|
311
|
+
uow: new DrizzleUnitOfWork(db, (tx) => ({
|
|
312
|
+
digests: new DrizzleDigestRunRepository(tx, discussionLogger),
|
|
313
|
+
outbox: new DrizzleOutboxAppender(tx, outboxLogger)
|
|
314
|
+
})),
|
|
315
|
+
logger: discussionLogger
|
|
316
|
+
});
|
|
317
|
+
const notificationsRepo = new DrizzleNotificationsRepository(db);
|
|
318
|
+
const notificationList = new NotificationsServiceImpl({
|
|
319
|
+
repo: notificationsRepo,
|
|
320
|
+
logger: automationsLogger
|
|
321
|
+
});
|
|
322
|
+
const itemTitles = {
|
|
323
|
+
discussion: new DiscussionItemTitles({
|
|
324
|
+
posts,
|
|
325
|
+
chats,
|
|
326
|
+
comments: discussionReads,
|
|
327
|
+
messages: chatReads,
|
|
328
|
+
profiles: discussionReads,
|
|
329
|
+
activities: content,
|
|
330
|
+
access: entitlements,
|
|
331
|
+
actors: organizations
|
|
332
|
+
}),
|
|
333
|
+
content: new ContentItemTitles({
|
|
334
|
+
content,
|
|
335
|
+
access: entitlements,
|
|
336
|
+
organizations
|
|
337
|
+
})
|
|
338
|
+
};
|
|
208
339
|
const reporting = {
|
|
209
|
-
admin: new AdminReportServiceImpl({
|
|
340
|
+
admin: new AdminReportServiceImpl({
|
|
341
|
+
repo: new DrizzleAdminRepository(db, reportingLogger),
|
|
342
|
+
permissions: organizations,
|
|
343
|
+
spaces: entitlements,
|
|
344
|
+
events: content,
|
|
345
|
+
fileUrls
|
|
346
|
+
}),
|
|
210
347
|
member: new MemberReportServiceImpl({
|
|
211
348
|
repo: new DrizzleMemberRepository(db, reportingLogger),
|
|
212
349
|
content,
|
|
@@ -214,9 +351,14 @@ async function buildContainer(config, options) {
|
|
|
214
351
|
files: assets,
|
|
215
352
|
fileUrls,
|
|
216
353
|
access: entitlements,
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
354
|
+
people: organizations,
|
|
355
|
+
notifications: notificationList,
|
|
356
|
+
titles: itemTitles,
|
|
357
|
+
subscriptions,
|
|
358
|
+
notificationCategories,
|
|
359
|
+
notificationSettings,
|
|
360
|
+
channels: notificationRegistries.channels,
|
|
361
|
+
studentPortalUrl: config.studentPortalUrl
|
|
220
362
|
})
|
|
221
363
|
};
|
|
222
364
|
const credentialStore = new DrizzleCredentialStore(db, config.credentialStoreKey, integrationsLogger);
|
|
@@ -249,23 +391,30 @@ async function buildContainer(config, options) {
|
|
|
249
391
|
} : null;
|
|
250
392
|
},
|
|
251
393
|
contentInfo: async (orgId, contentId) => {
|
|
252
|
-
const
|
|
394
|
+
const system = organizations.systemActor(orgId);
|
|
395
|
+
const course = await content.findCourse(contentId, system);
|
|
253
396
|
if (course) return {
|
|
254
397
|
id: course.id,
|
|
255
398
|
title: course.title,
|
|
256
399
|
type: course.type
|
|
257
400
|
};
|
|
258
|
-
const download = await content.
|
|
401
|
+
const download = await content.findDownload(contentId, system);
|
|
259
402
|
if (download) return {
|
|
260
403
|
id: download.id,
|
|
261
404
|
title: download.title,
|
|
262
405
|
type: download.type
|
|
263
406
|
};
|
|
264
|
-
const community = await content.
|
|
265
|
-
|
|
407
|
+
const community = await content.findCommunity(contentId, system);
|
|
408
|
+
if (community) return {
|
|
266
409
|
id: community.id,
|
|
267
410
|
title: community.title,
|
|
268
411
|
type: community.type
|
|
412
|
+
};
|
|
413
|
+
const event = await content.findEvent(contentId, system);
|
|
414
|
+
return event ? {
|
|
415
|
+
id: event.id,
|
|
416
|
+
title: event.title,
|
|
417
|
+
type: event.type
|
|
269
418
|
} : null;
|
|
270
419
|
}
|
|
271
420
|
};
|
|
@@ -276,9 +425,35 @@ async function buildContainer(config, options) {
|
|
|
276
425
|
organizations,
|
|
277
426
|
identity,
|
|
278
427
|
posts,
|
|
428
|
+
subscriptions,
|
|
279
429
|
discussion,
|
|
280
430
|
chats,
|
|
281
|
-
|
|
431
|
+
bodies
|
|
432
|
+
});
|
|
433
|
+
const recipientReads = notificationReads({
|
|
434
|
+
content,
|
|
435
|
+
entitlements,
|
|
436
|
+
organizations,
|
|
437
|
+
identity,
|
|
438
|
+
posts,
|
|
439
|
+
subscriptions,
|
|
440
|
+
discussion,
|
|
441
|
+
chats,
|
|
442
|
+
bodies,
|
|
443
|
+
settings: notificationSettings
|
|
444
|
+
});
|
|
445
|
+
const notificationProcessor = new NotificationProcessor({
|
|
446
|
+
rules: RECIPIENT_RULES,
|
|
447
|
+
reads: recipientReads,
|
|
448
|
+
types: notificationRegistries.types,
|
|
449
|
+
channels: notificationRegistries.channels,
|
|
450
|
+
categories: notificationCategoryLists,
|
|
451
|
+
settings: notificationSettings,
|
|
452
|
+
devices,
|
|
453
|
+
repo: notificationsRepo,
|
|
454
|
+
engine: automationEngine,
|
|
455
|
+
debounce: new SchedulerChatDebounce(automationEngine),
|
|
456
|
+
logger: automationsLogger
|
|
282
457
|
});
|
|
283
458
|
const automations = new AutomationsServiceImpl({
|
|
284
459
|
repo: new DrizzleAutomationsRepository(db, automationsLogger),
|
|
@@ -288,16 +463,52 @@ async function buildContainer(config, options) {
|
|
|
288
463
|
mailer,
|
|
289
464
|
lookups: mailerLookups,
|
|
290
465
|
integrations,
|
|
466
|
+
systemActor: (orgId) => organizations.systemActor(orgId),
|
|
291
467
|
credentials: credentialStore,
|
|
292
468
|
webhooks: options?.adapters?.webhooks ?? new FetchWebhookSender({ allowPrivateNetwork: config.webhookAllowPrivateNetwork }),
|
|
293
469
|
mail: wired.mail,
|
|
294
470
|
grants: wired.grants,
|
|
295
|
-
|
|
296
|
-
|
|
471
|
+
delivery: new NotificationDelivery({
|
|
472
|
+
repo: notificationsRepo,
|
|
473
|
+
channels: notificationRegistries.channels,
|
|
474
|
+
devices,
|
|
475
|
+
logger: automationsLogger
|
|
476
|
+
}),
|
|
477
|
+
processor: notificationProcessor,
|
|
297
478
|
logger: automationsLogger
|
|
298
479
|
});
|
|
299
480
|
automationEngine.register(automations);
|
|
481
|
+
automationEngine.registerJobs([
|
|
482
|
+
...discussionJobs({
|
|
483
|
+
posts,
|
|
484
|
+
digests,
|
|
485
|
+
logger: discussionLogger
|
|
486
|
+
}),
|
|
487
|
+
...contentJobs({
|
|
488
|
+
events: content,
|
|
489
|
+
logger: contentLogger
|
|
490
|
+
}),
|
|
491
|
+
...entitlementsJobs({ grants: entitlements }),
|
|
492
|
+
...assetsJobs({
|
|
493
|
+
assets,
|
|
494
|
+
orgs: organizations,
|
|
495
|
+
logger: assetsLogger
|
|
496
|
+
}),
|
|
497
|
+
...notificationJobs({
|
|
498
|
+
processor: notificationProcessor,
|
|
499
|
+
reads: recipientReads,
|
|
500
|
+
logger: automationsLogger
|
|
501
|
+
})
|
|
502
|
+
], [
|
|
503
|
+
...discussionRecurringJobs,
|
|
504
|
+
...entitlementsRecurringJobs,
|
|
505
|
+
...assetsRecurringJobs
|
|
506
|
+
]);
|
|
300
507
|
const eventBus = new InMemoryEventBus();
|
|
508
|
+
if (pubsub) new ChatLiveForwarder({
|
|
509
|
+
pubsub,
|
|
510
|
+
logger: discussionLogger
|
|
511
|
+
}).attach(eventBus);
|
|
301
512
|
eventBus.subscribeAll((event) => automations.handle(event));
|
|
302
513
|
const outboxConfig = resolveOutboxConfig(config.outbox);
|
|
303
514
|
const outboxRelay = new PollingOutboxRelay(new DrizzleOutboxStore(db, outboxLogger), eventBus, outboxConfig, outboxLogger);
|
|
@@ -356,6 +567,10 @@ async function buildContainer(config, options) {
|
|
|
356
567
|
beforeCreateSession: authHook("beforeCreateSession", async (session) => {
|
|
357
568
|
const person = await identity.getUserByExternalId(session.userId);
|
|
358
569
|
if (!person) return;
|
|
570
|
+
if (organizations.isPlatformAccount(person.email)) {
|
|
571
|
+
authLogger.warn("sign-in refused for the platform account", { userId: person.id });
|
|
572
|
+
return false;
|
|
573
|
+
}
|
|
359
574
|
const orgs = await organizations.getOrgUsersForUser(person.id);
|
|
360
575
|
if (orgs.length !== 1) return;
|
|
361
576
|
const org = await organizations.getById(orgs[0].orgId);
|
|
@@ -366,7 +581,7 @@ async function buildContainer(config, options) {
|
|
|
366
581
|
} };
|
|
367
582
|
}),
|
|
368
583
|
beforeCreateOrganization: authHook("beforeCreateOrganization", async ({ organization: org, user: baUser }) => {
|
|
369
|
-
const domainOrg = await organizations.
|
|
584
|
+
const domainOrg = await organizations.provisionOrganization({
|
|
370
585
|
ownerId: baUser.id,
|
|
371
586
|
...org,
|
|
372
587
|
logo: org.logo ?? void 0
|
|
@@ -376,22 +591,15 @@ async function buildContainer(config, options) {
|
|
|
376
591
|
id: domainOrg.id
|
|
377
592
|
} };
|
|
378
593
|
}),
|
|
379
|
-
beforeUpdateOrganization: authHook("beforeUpdateOrganization", async ({ organization }) => {
|
|
380
|
-
await organizations.updateOrganization(organization.id, organization);
|
|
381
|
-
}),
|
|
382
594
|
beforeDeleteOrganization: authHook("beforeDeleteOrganization", async ({ organization }) => {
|
|
383
595
|
await organizations.deleteOrganization(organization.id);
|
|
384
596
|
}),
|
|
385
597
|
beforeAddMember: authHook("beforeAddMember", async ({ member }) => {
|
|
386
598
|
await organizations.addOrgUser({
|
|
387
599
|
orgId: member.organizationId,
|
|
388
|
-
userId: member.userId
|
|
389
|
-
role: parseRole(member.role)
|
|
600
|
+
userId: member.userId
|
|
390
601
|
});
|
|
391
602
|
}),
|
|
392
|
-
beforeUpdateMemberRole: authHook("beforeUpdateMemberRole", async ({ member, newRole }) => {
|
|
393
|
-
await organizations.updateOrgUser(member.organizationId, member.userId, { role: parseRole(newRole) });
|
|
394
|
-
}),
|
|
395
603
|
beforeRemoveMember: authHook("beforeRemoveMember", async ({ member }) => {
|
|
396
604
|
await organizations.removeOrgUser(member.organizationId, member.userId);
|
|
397
605
|
})
|
|
@@ -404,8 +612,8 @@ async function buildContainer(config, options) {
|
|
|
404
612
|
return {
|
|
405
613
|
auth,
|
|
406
614
|
mcp,
|
|
615
|
+
platform,
|
|
407
616
|
identity,
|
|
408
|
-
notifications,
|
|
409
617
|
organizations,
|
|
410
618
|
orgProvider: auth,
|
|
411
619
|
content,
|
|
@@ -414,9 +622,18 @@ async function buildContainer(config, options) {
|
|
|
414
622
|
discussion,
|
|
415
623
|
posts,
|
|
416
624
|
chats,
|
|
625
|
+
subscriptions,
|
|
417
626
|
assets,
|
|
418
627
|
integrations,
|
|
419
628
|
automations,
|
|
629
|
+
notificationTypes: notificationRegistries.types,
|
|
630
|
+
notificationChannels: notificationRegistries.channels,
|
|
631
|
+
notificationCategories,
|
|
632
|
+
notificationCatalog,
|
|
633
|
+
notificationCategoryLists,
|
|
634
|
+
notificationSettings,
|
|
635
|
+
devices,
|
|
636
|
+
notificationList,
|
|
420
637
|
settings,
|
|
421
638
|
reporting,
|
|
422
639
|
storage,
|
|
@@ -424,6 +641,9 @@ async function buildContainer(config, options) {
|
|
|
424
641
|
credentials: credentialStore,
|
|
425
642
|
outboxRelay,
|
|
426
643
|
automationEngine,
|
|
644
|
+
scheduler: automationEngine,
|
|
645
|
+
pubsub,
|
|
646
|
+
chatHub: chats.hub,
|
|
427
647
|
logger,
|
|
428
648
|
loggerInstance,
|
|
429
649
|
requestContext: requestLogContext,
|