@headless-lms/adapter-db 0.2.0 → 0.4.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/index.d.ts +12 -6
- package/dist/index.js +12 -6
- package/dist/repositories/admin.d.ts +23 -0
- package/dist/repositories/admin.js +393 -0
- package/dist/repositories/admin.js.map +1 -0
- package/dist/repositories/assets.d.ts +6 -3
- package/dist/repositories/assets.js +46 -9
- package/dist/repositories/assets.js.map +1 -1
- package/dist/repositories/automations.d.ts +5 -2
- package/dist/repositories/automations.js +29 -2
- package/dist/repositories/automations.js.map +1 -1
- package/dist/repositories/chats.d.ts +55 -0
- package/dist/repositories/chats.js +469 -0
- package/dist/repositories/chats.js.map +1 -0
- package/dist/repositories/content.d.ts +24 -1
- package/dist/repositories/content.js +316 -9
- package/dist/repositories/content.js.map +1 -1
- package/dist/repositories/devices.d.ts +17 -0
- package/dist/repositories/devices.js +75 -0
- package/dist/repositories/devices.js.map +1 -0
- package/dist/repositories/digest-runs.d.ts +14 -0
- package/dist/repositories/digest-runs.js +39 -0
- package/dist/repositories/digest-runs.js.map +1 -0
- package/dist/repositories/discussion.d.ts +16 -4
- package/dist/repositories/discussion.js +78 -18
- package/dist/repositories/discussion.js.map +1 -1
- package/dist/repositories/entitlements.d.ts +31 -9
- package/dist/repositories/entitlements.js +213 -17
- package/dist/repositories/entitlements.js.map +1 -1
- package/dist/repositories/grant-covers.d.ts +20 -0
- package/dist/repositories/grant-covers.js +55 -0
- package/dist/repositories/grant-covers.js.map +1 -0
- package/dist/repositories/identity.d.ts +4 -2
- package/dist/repositories/identity.js +32 -1
- package/dist/repositories/identity.js.map +1 -1
- package/dist/repositories/inserted-row.d.ts +5 -0
- package/dist/repositories/inserted-row.js +7 -0
- package/dist/repositories/inserted-row.js.map +1 -0
- package/dist/repositories/member.d.ts +16 -0
- package/dist/repositories/member.js +63 -0
- package/dist/repositories/member.js.map +1 -0
- package/dist/repositories/members.js +2 -2
- package/dist/repositories/members.js.map +1 -1
- package/dist/repositories/mentions.d.ts +17 -0
- package/dist/repositories/mentions.js +55 -0
- package/dist/repositories/mentions.js.map +1 -0
- package/dist/repositories/notification-settings.d.ts +19 -0
- package/dist/repositories/notification-settings.js +67 -0
- package/dist/repositories/notification-settings.js.map +1 -0
- package/dist/repositories/notifications.d.ts +30 -0
- package/dist/repositories/notifications.js +178 -0
- package/dist/repositories/notifications.js.map +1 -0
- package/dist/repositories/org-user-profile.d.ts +214 -1
- package/dist/repositories/org-user-profile.js +23 -2
- package/dist/repositories/org-user-profile.js.map +1 -1
- package/dist/repositories/organizations.d.ts +4 -2
- package/dist/repositories/organizations.js +23 -8
- package/dist/repositories/organizations.js.map +1 -1
- package/dist/repositories/pg-errors.js +3 -1
- package/dist/repositories/pg-errors.js.map +1 -1
- package/dist/repositories/posts.d.ts +35 -0
- package/dist/repositories/posts.js +252 -0
- package/dist/repositories/posts.js.map +1 -0
- package/dist/repositories/subscriptions.d.ts +18 -0
- package/dist/repositories/subscriptions.js +72 -0
- package/dist/repositories/subscriptions.js.map +1 -0
- package/dist/schema/assets.d.ts +19 -2
- package/dist/schema/assets.js +10 -4
- package/dist/schema/assets.js.map +1 -1
- package/dist/schema/automations.d.ts +545 -2
- package/dist/schema/automations.js +73 -3
- package/dist/schema/automations.js.map +1 -1
- package/dist/schema/better-auth.d.ts +1613 -238
- package/dist/schema/better-auth.js +107 -22
- package/dist/schema/better-auth.js.map +1 -1
- package/dist/schema/content.d.ts +642 -5
- package/dist/schema/content.js +119 -10
- package/dist/schema/content.js.map +1 -1
- package/dist/schema/discussion.d.ts +2182 -42
- package/dist/schema/discussion.js +461 -11
- package/dist/schema/discussion.js.map +1 -1
- package/dist/schema/entitlements.d.ts +432 -1
- package/dist/schema/entitlements.js +109 -4
- package/dist/schema/entitlements.js.map +1 -1
- package/dist/schema/identity.d.ts +517 -1
- package/dist/schema/identity.js +76 -2
- package/dist/schema/identity.js.map +1 -1
- package/dist/schema/index.d.ts +7 -7
- package/dist/schema/index.js +35 -8
- package/dist/schema/organizations.d.ts +4 -4
- package/dist/schema/organizations.js +2 -0
- package/dist/schema/organizations.js.map +1 -1
- package/drizzle/0003_community.sql +340 -0
- package/drizzle/0004_assets_sources.sql +1 -0
- package/drizzle/0005_notices_and_seed_index.sql +12 -0
- package/drizzle/0006_community_review_fixes.sql +24 -0
- package/drizzle/0007_notifications.sql +37 -0
- package/drizzle/0008_content_access.sql +13 -0
- package/drizzle/0009_community_indexes.sql +56 -0
- package/drizzle/0010_mcp_access.sql +114 -0
- package/drizzle/0011_digest_runs.sql +8 -0
- package/drizzle/0012_subscriptions.sql +16 -0
- package/drizzle/0013_mentions.sql +10 -0
- package/drizzle/0014_notification_settings_devices.sql +36 -0
- package/drizzle/0015_automations_drop_source.sql +2 -0
- package/drizzle/0016_notifications_automations.sql +48 -0
- package/drizzle/meta/0003_snapshot.json +7061 -0
- package/drizzle/meta/0004_snapshot.json +7068 -0
- package/drizzle/meta/0005_snapshot.json +7180 -0
- package/drizzle/meta/0006_snapshot.json +7298 -0
- package/drizzle/meta/0007_snapshot.json +7620 -0
- package/drizzle/meta/0008_snapshot.json +7666 -0
- package/drizzle/meta/0009_snapshot.json +8185 -0
- package/drizzle/meta/0010_snapshot.json +8813 -0
- package/drizzle/meta/0011_snapshot.json +8868 -0
- package/drizzle/meta/0012_snapshot.json +8873 -0
- package/drizzle/meta/0013_snapshot.json +8943 -0
- package/drizzle/meta/0014_snapshot.json +9305 -0
- package/drizzle/meta/0015_snapshot.json +9270 -0
- package/drizzle/meta/0016_snapshot.json +9230 -0
- package/drizzle/meta/_journal.json +99 -1
- package/package.json +2 -2
- package/dist/repositories/course-analytics.d.ts +0 -15
- package/dist/repositories/course-analytics.js +0 -137
- package/dist/repositories/course-analytics.js.map +0 -1
- package/dist/repositories/dashboard.d.ts +0 -14
- package/dist/repositories/dashboard.js +0 -57
- package/dist/repositories/dashboard.js.map +0 -1
- package/dist/repositories/learn.d.ts +0 -19
- package/dist/repositories/learn.js +0 -56
- package/dist/repositories/learn.js.map +0 -1
- package/dist/repositories/students.d.ts +0 -16
- package/dist/repositories/students.js +0 -160
- package/dist/repositories/students.js.map +0 -1
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { notificationSettings } from "../schema/identity.js";
|
|
2
|
+
import { orgUsers } from "../schema/organizations.js";
|
|
3
|
+
import "../schema/index.js";
|
|
4
|
+
import { translateDbErrors } from "./pg-errors.js";
|
|
5
|
+
import { and, eq, inArray, sql } from "drizzle-orm";
|
|
6
|
+
import { genId } from "@headless-lms/core/shared/id";
|
|
7
|
+
import { noopLogger } from "@headless-lms/core/shared/logger";
|
|
8
|
+
//#region src/repositories/notification-settings.ts
|
|
9
|
+
const ns = notificationSettings;
|
|
10
|
+
const communityKey = sql`coalesce(${ns.communityId}, '')`;
|
|
11
|
+
const spaceKey = sql`coalesce(${ns.spaceId}, '')`;
|
|
12
|
+
function matchesKey(key) {
|
|
13
|
+
return sql`(${communityKey} = ${key.communityId ?? ""} and ${spaceKey} = ${key.spaceId ?? ""} and ${ns.category} = ${key.category} and ${ns.channel} = ${key.channel})`;
|
|
14
|
+
}
|
|
15
|
+
var DrizzleNotificationSettingsRepository = class {
|
|
16
|
+
db;
|
|
17
|
+
logger;
|
|
18
|
+
constructor(db, logger = noopLogger) {
|
|
19
|
+
this.db = db;
|
|
20
|
+
this.logger = logger;
|
|
21
|
+
}
|
|
22
|
+
async forUsers(orgId, userIds, communityId, categories) {
|
|
23
|
+
if (userIds.length === 0) return [];
|
|
24
|
+
const conditions = [
|
|
25
|
+
eq(ns.orgId, orgId),
|
|
26
|
+
inArray(ns.userId, userIds),
|
|
27
|
+
sql`${communityKey} = ${communityId ?? ""}`
|
|
28
|
+
];
|
|
29
|
+
if (categories !== null) {
|
|
30
|
+
if (categories.length === 0) return [];
|
|
31
|
+
conditions.push(inArray(ns.category, categories));
|
|
32
|
+
}
|
|
33
|
+
return this.db.select({
|
|
34
|
+
userId: ns.userId,
|
|
35
|
+
communityId: ns.communityId,
|
|
36
|
+
spaceId: ns.spaceId,
|
|
37
|
+
category: ns.category,
|
|
38
|
+
channel: ns.channel,
|
|
39
|
+
enabled: ns.enabled
|
|
40
|
+
}).from(ns).where(and(...conditions));
|
|
41
|
+
}
|
|
42
|
+
async apply(orgId, userId, upserts, deletes, at) {
|
|
43
|
+
if (upserts.length === 0 && deletes.length === 0) return;
|
|
44
|
+
await this.db.transaction(async (tx) => {
|
|
45
|
+
if (deletes.length > 0) await tx.delete(ns).where(and(eq(ns.orgId, orgId), eq(ns.userId, userId), sql`(${sql.join(deletes.map(matchesKey), sql` or `)})`));
|
|
46
|
+
if (upserts.length > 0) {
|
|
47
|
+
const rows = upserts.map((row) => sql`(${orgId}, ${genId("notificationSetting")}, ${userId}, ${row.communityId}, ${row.spaceId}, ${row.category}, ${row.channel}, ${row.enabled}, ${at.toISOString()}::timestamptz)`);
|
|
48
|
+
await tx.execute(sql`insert into notification_settings (org_id, id, user_id, community_id, space_id, category, channel, enabled, updated_at) values ${sql.join(rows, sql`, `)} on conflict (org_id, user_id, coalesce(community_id, ''), coalesce(space_id, ''), category, channel) do update set enabled = excluded.enabled, updated_at = excluded.updated_at`);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
this.logger.debug("notification settings applied", {
|
|
52
|
+
orgId,
|
|
53
|
+
userId,
|
|
54
|
+
upserts: upserts.length,
|
|
55
|
+
deletes: deletes.length
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
async switchedOn(orgId, communityId, category, spaceId) {
|
|
59
|
+
const levels = spaceId === null ? [""] : [spaceId, ""];
|
|
60
|
+
return (await this.db.selectDistinct({ orgUserId: orgUsers.id }).from(ns).innerJoin(orgUsers, and(eq(orgUsers.orgId, ns.orgId), eq(orgUsers.userId, ns.userId))).where(and(eq(ns.orgId, orgId), sql`${communityKey} = ${communityId}`, eq(ns.category, category), sql`${spaceKey} in (${sql.join(levels.map((level) => sql`${level}`), sql`, `)})`, sql`${ns.enabled}`))).map((row) => row.orgUserId);
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
translateDbErrors(DrizzleNotificationSettingsRepository);
|
|
64
|
+
//#endregion
|
|
65
|
+
export { DrizzleNotificationSettingsRepository };
|
|
66
|
+
|
|
67
|
+
//# sourceMappingURL=notification-settings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notification-settings.js","names":[],"sources":["../../src/repositories/notification-settings.ts"],"sourcesContent":["import { and, eq, inArray, sql, type SQL } from \"drizzle-orm\";\nimport type { NodePgDatabase } from \"drizzle-orm/node-postgres\";\nimport type { NotificationSettingsRepository, SettingKey } from \"@headless-lms/core/identity\";\nimport type { Logger, StoredSetting } from \"@headless-lms/core/types\";\nimport { genId } from \"@headless-lms/core/shared/id\";\nimport { noopLogger } from \"@headless-lms/core/shared/logger\";\nimport { notificationSettings, orgUsers } from \"../schema/index.js\";\nimport { translateDbErrors } from \"./pg-errors.js\";\n\nconst ns = notificationSettings;\nconst communityKey = sql`coalesce(${ns.communityId}, '')`;\nconst spaceKey = sql`coalesce(${ns.spaceId}, '')`;\n\nfunction matchesKey(key: SettingKey): SQL {\n return sql`(${communityKey} = ${key.communityId ?? \"\"} and ${spaceKey} = ${key.spaceId ?? \"\"} and ${ns.category} = ${key.category} and ${ns.channel} = ${key.channel})`;\n}\n\nexport class DrizzleNotificationSettingsRepository implements NotificationSettingsRepository {\n constructor(\n private readonly db: NodePgDatabase,\n private readonly logger: Logger = noopLogger,\n ) {}\n\n async forUsers(\n orgId: string,\n userIds: string[],\n communityId: string | null,\n categories: string[] | null,\n ): Promise<(StoredSetting & { userId: string })[]> {\n if (userIds.length === 0) {\n return [];\n }\n const conditions = [\n eq(ns.orgId, orgId),\n inArray(ns.userId, userIds),\n sql`${communityKey} = ${communityId ?? \"\"}`,\n ];\n if (categories !== null) {\n if (categories.length === 0) {\n return [];\n }\n conditions.push(inArray(ns.category, categories));\n }\n return this.db\n .select({\n userId: ns.userId,\n communityId: ns.communityId,\n spaceId: ns.spaceId,\n category: ns.category,\n channel: ns.channel,\n enabled: ns.enabled,\n })\n .from(ns)\n .where(and(...conditions));\n }\n\n async apply(\n orgId: string,\n userId: string,\n upserts: (SettingKey & { enabled: boolean })[],\n deletes: SettingKey[],\n at: Date,\n ): Promise<void> {\n if (upserts.length === 0 && deletes.length === 0) {\n return;\n }\n await this.db.transaction(async (tx) => {\n if (deletes.length > 0) {\n await tx\n .delete(ns)\n .where(\n and(\n eq(ns.orgId, orgId),\n eq(ns.userId, userId),\n sql`(${sql.join(deletes.map(matchesKey), sql` or `)})`,\n ),\n );\n }\n if (upserts.length > 0) {\n const rows = upserts.map(\n (row) =>\n sql`(${orgId}, ${genId(\"notificationSetting\")}, ${userId}, ${row.communityId}, ${row.spaceId}, ${row.category}, ${row.channel}, ${row.enabled}, ${at.toISOString()}::timestamptz)`,\n );\n await tx.execute(\n sql`insert into notification_settings (org_id, id, user_id, community_id, space_id, category, channel, enabled, updated_at) values ${sql.join(rows, sql`, `)} on conflict (org_id, user_id, coalesce(community_id, ''), coalesce(space_id, ''), category, channel) do update set enabled = excluded.enabled, updated_at = excluded.updated_at`,\n );\n }\n });\n this.logger.debug(\"notification settings applied\", {\n orgId,\n userId,\n upserts: upserts.length,\n deletes: deletes.length,\n });\n }\n\n async switchedOn(\n orgId: string,\n communityId: string,\n category: string,\n spaceId: string | null,\n ): Promise<string[]> {\n const levels = spaceId === null ? [\"\"] : [spaceId, \"\"];\n const rows = await this.db\n .selectDistinct({ orgUserId: orgUsers.id })\n .from(ns)\n .innerJoin(orgUsers, and(eq(orgUsers.orgId, ns.orgId), eq(orgUsers.userId, ns.userId)))\n .where(\n and(\n eq(ns.orgId, orgId),\n sql`${communityKey} = ${communityId}`,\n eq(ns.category, category),\n sql`${spaceKey} in (${sql.join(\n levels.map((level) => sql`${level}`),\n sql`, `,\n )})`,\n sql`${ns.enabled}`,\n ),\n );\n return rows.map((row) => row.orgUserId);\n }\n}\ntranslateDbErrors(DrizzleNotificationSettingsRepository);\n"],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { DbExecutor } from "../client.js";
|
|
2
|
+
import { Delivery, NotificationFilter, StoredNotification } from "@headless-lms/core/types";
|
|
3
|
+
import { ListedNotification, NewDelivery, NewNotification, NotificationPage, NotificationsRepository, UnreadCounts } from "@headless-lms/core/automations";
|
|
4
|
+
//#region src/repositories/notifications.d.ts
|
|
5
|
+
declare class DrizzleNotificationsRepository implements NotificationsRepository {
|
|
6
|
+
private readonly db;
|
|
7
|
+
constructor(db: DbExecutor);
|
|
8
|
+
insert(orgId: string, rows: NewNotification[], deliveries: NewDelivery[]): Promise<void>;
|
|
9
|
+
pendingDeliveriesForEvent(orgId: string, sourceEventId: string): Promise<{
|
|
10
|
+
id: string;
|
|
11
|
+
}[]>;
|
|
12
|
+
findDelivery(orgId: string, deliveryId: string): Promise<{
|
|
13
|
+
delivery: Delivery;
|
|
14
|
+
notification: StoredNotification;
|
|
15
|
+
} | null>;
|
|
16
|
+
markSent(orgId: string, deliveryId: string, at: Date): Promise<void>;
|
|
17
|
+
recordError(orgId: string, deliveryId: string, error: string): Promise<void>;
|
|
18
|
+
markFailed(orgId: string, deliveryId: string, error: string): Promise<void>;
|
|
19
|
+
page(orgId: string, userId: string, filter: NotificationFilter, page: {
|
|
20
|
+
page: number;
|
|
21
|
+
pageSize: number;
|
|
22
|
+
}): Promise<NotificationPage>;
|
|
23
|
+
find(orgId: string, userId: string, notificationId: string): Promise<ListedNotification | null>;
|
|
24
|
+
unreadCounts(orgId: string, userId: string, communityIds: string[]): Promise<UnreadCounts>;
|
|
25
|
+
markRead(orgId: string, userId: string, notificationId: string, at: Date): Promise<boolean>;
|
|
26
|
+
markAllRead(orgId: string, userId: string, filter: NotificationFilter, at: Date): Promise<number>;
|
|
27
|
+
}
|
|
28
|
+
//#endregion
|
|
29
|
+
export { DrizzleNotificationsRepository };
|
|
30
|
+
//# sourceMappingURL=notifications.d.ts.map
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { notificationDeliveries, notifications } from "../schema/automations.js";
|
|
2
|
+
import "../schema/index.js";
|
|
3
|
+
import { translateDbErrors } from "./pg-errors.js";
|
|
4
|
+
import { and, arrayOverlaps, desc, eq, inArray, isNotNull, isNull, or, sql } from "drizzle-orm";
|
|
5
|
+
//#region src/repositories/notifications.ts
|
|
6
|
+
const n = notifications;
|
|
7
|
+
const d = notificationDeliveries;
|
|
8
|
+
function ref(type, id) {
|
|
9
|
+
return type && id ? {
|
|
10
|
+
type,
|
|
11
|
+
id
|
|
12
|
+
} : null;
|
|
13
|
+
}
|
|
14
|
+
function toStored(row) {
|
|
15
|
+
return {
|
|
16
|
+
id: row.id,
|
|
17
|
+
orgId: row.orgId,
|
|
18
|
+
userId: row.userId,
|
|
19
|
+
type: row.type,
|
|
20
|
+
scope: row.scope,
|
|
21
|
+
reasons: row.reasons,
|
|
22
|
+
matchedReason: row.matchedReason,
|
|
23
|
+
category: row.category,
|
|
24
|
+
communityId: row.communityId,
|
|
25
|
+
spaceId: row.spaceId,
|
|
26
|
+
actorOrgUserId: row.actorOrgUserId,
|
|
27
|
+
subject: ref(row.subjectType, row.subjectId),
|
|
28
|
+
parent: ref(row.parentType, row.parentId),
|
|
29
|
+
display: row.display,
|
|
30
|
+
createdAt: row.createdAt
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
function toDelivery(row) {
|
|
34
|
+
return {
|
|
35
|
+
id: row.id,
|
|
36
|
+
notificationId: row.notificationId,
|
|
37
|
+
channel: row.channel,
|
|
38
|
+
status: row.status,
|
|
39
|
+
lastError: row.lastError,
|
|
40
|
+
createdAt: row.createdAt,
|
|
41
|
+
sentAt: row.sentAt
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function listed(orgId, userId, filter) {
|
|
45
|
+
const conditions = [
|
|
46
|
+
eq(n.orgId, orgId),
|
|
47
|
+
eq(n.userId, userId),
|
|
48
|
+
eq(n.inApp, true)
|
|
49
|
+
];
|
|
50
|
+
if (filter.scope === "org") conditions.push(isNull(n.communityId));
|
|
51
|
+
if (filter.scope === "community") conditions.push(isNotNull(n.communityId));
|
|
52
|
+
if (filter.communityId !== void 0) {
|
|
53
|
+
const community = eq(n.communityId, filter.communityId);
|
|
54
|
+
conditions.push(filter.includeOrg ? or(community, isNull(n.communityId)) : community);
|
|
55
|
+
}
|
|
56
|
+
if (filter.category && filter.category.length > 0) conditions.push(inArray(n.category, filter.category));
|
|
57
|
+
if (filter.reason && filter.reason.length > 0) conditions.push(arrayOverlaps(n.reasons, filter.reason));
|
|
58
|
+
if (filter.type && filter.type.length > 0) conditions.push(inArray(n.type, filter.type));
|
|
59
|
+
if (filter.unread === true) conditions.push(isNull(n.readAt));
|
|
60
|
+
return conditions;
|
|
61
|
+
}
|
|
62
|
+
var DrizzleNotificationsRepository = class {
|
|
63
|
+
db;
|
|
64
|
+
constructor(db) {
|
|
65
|
+
this.db = db;
|
|
66
|
+
}
|
|
67
|
+
async insert(orgId, rows, deliveries) {
|
|
68
|
+
if (rows.length === 0) return;
|
|
69
|
+
await this.db.transaction(async (tx) => {
|
|
70
|
+
const inserted = await tx.insert(n).values(rows.map((row) => ({
|
|
71
|
+
orgId,
|
|
72
|
+
id: row.id,
|
|
73
|
+
userId: row.userId,
|
|
74
|
+
sourceEventId: row.sourceEventId,
|
|
75
|
+
type: row.type,
|
|
76
|
+
scope: row.scope,
|
|
77
|
+
reasons: row.reasons,
|
|
78
|
+
matchedReason: row.matchedReason,
|
|
79
|
+
category: row.category,
|
|
80
|
+
communityId: row.communityId,
|
|
81
|
+
spaceId: row.spaceId,
|
|
82
|
+
actorOrgUserId: row.actorOrgUserId,
|
|
83
|
+
subjectType: row.subject?.type ?? null,
|
|
84
|
+
subjectId: row.subject?.id ?? null,
|
|
85
|
+
parentType: row.parent?.type ?? null,
|
|
86
|
+
parentId: row.parent?.id ?? null,
|
|
87
|
+
display: row.display,
|
|
88
|
+
inApp: row.inApp,
|
|
89
|
+
createdAt: row.createdAt
|
|
90
|
+
}))).onConflictDoNothing().returning({ id: n.id });
|
|
91
|
+
const kept = new Set(inserted.map((row) => row.id));
|
|
92
|
+
const pending = deliveries.filter((delivery) => kept.has(delivery.notificationId));
|
|
93
|
+
if (pending.length === 0) return;
|
|
94
|
+
await tx.insert(d).values(pending.map((delivery) => ({
|
|
95
|
+
orgId,
|
|
96
|
+
id: delivery.id,
|
|
97
|
+
notificationId: delivery.notificationId,
|
|
98
|
+
channel: delivery.channel,
|
|
99
|
+
status: "pending"
|
|
100
|
+
}))).onConflictDoNothing();
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
async pendingDeliveriesForEvent(orgId, sourceEventId) {
|
|
104
|
+
return this.db.select({ id: d.id }).from(d).innerJoin(n, and(eq(n.orgId, d.orgId), eq(n.id, d.notificationId))).where(and(eq(n.orgId, orgId), eq(n.sourceEventId, sourceEventId), eq(d.status, "pending"))).orderBy(d.id);
|
|
105
|
+
}
|
|
106
|
+
async findDelivery(orgId, deliveryId) {
|
|
107
|
+
const [row] = await this.db.select({
|
|
108
|
+
delivery: d,
|
|
109
|
+
notification: n
|
|
110
|
+
}).from(d).innerJoin(n, and(eq(n.orgId, d.orgId), eq(n.id, d.notificationId))).where(and(eq(d.orgId, orgId), eq(d.id, deliveryId))).limit(1);
|
|
111
|
+
return row ? {
|
|
112
|
+
delivery: toDelivery(row.delivery),
|
|
113
|
+
notification: toStored(row.notification)
|
|
114
|
+
} : null;
|
|
115
|
+
}
|
|
116
|
+
async markSent(orgId, deliveryId, at) {
|
|
117
|
+
await this.db.update(d).set({
|
|
118
|
+
status: "sent",
|
|
119
|
+
sentAt: at
|
|
120
|
+
}).where(and(eq(d.orgId, orgId), eq(d.id, deliveryId), eq(d.status, "pending")));
|
|
121
|
+
}
|
|
122
|
+
async recordError(orgId, deliveryId, error) {
|
|
123
|
+
await this.db.update(d).set({ lastError: error }).where(and(eq(d.orgId, orgId), eq(d.id, deliveryId), eq(d.status, "pending")));
|
|
124
|
+
}
|
|
125
|
+
async markFailed(orgId, deliveryId, error) {
|
|
126
|
+
await this.db.update(d).set({
|
|
127
|
+
status: "failed",
|
|
128
|
+
lastError: error
|
|
129
|
+
}).where(and(eq(d.orgId, orgId), eq(d.id, deliveryId), eq(d.status, "pending")));
|
|
130
|
+
}
|
|
131
|
+
async page(orgId, userId, filter, page) {
|
|
132
|
+
const where = and(...listed(orgId, userId, filter));
|
|
133
|
+
const [rows, counts] = await Promise.all([this.db.select().from(n).where(where).orderBy(desc(n.createdAt), desc(n.id)).limit(page.pageSize).offset((page.page - 1) * page.pageSize), this.db.select({
|
|
134
|
+
total: sql`count(*)::int`,
|
|
135
|
+
unread: sql`(count(*) filter (where ${n.readAt} is null))::int`
|
|
136
|
+
}).from(n).where(where)]);
|
|
137
|
+
return {
|
|
138
|
+
items: rows.map((row) => ({
|
|
139
|
+
...toStored(row),
|
|
140
|
+
readAt: row.readAt
|
|
141
|
+
})),
|
|
142
|
+
total: Number(counts[0]?.total ?? 0),
|
|
143
|
+
unreadCount: Number(counts[0]?.unread ?? 0)
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
async find(orgId, userId, notificationId) {
|
|
147
|
+
const [row] = await this.db.select().from(n).where(and(eq(n.orgId, orgId), eq(n.userId, userId), eq(n.id, notificationId), eq(n.inApp, true))).limit(1);
|
|
148
|
+
return row ? {
|
|
149
|
+
...toStored(row),
|
|
150
|
+
readAt: row.readAt
|
|
151
|
+
} : null;
|
|
152
|
+
}
|
|
153
|
+
async unreadCounts(orgId, userId, communityIds) {
|
|
154
|
+
const unread = and(eq(n.orgId, orgId), eq(n.userId, userId), eq(n.inApp, true), isNull(n.readAt));
|
|
155
|
+
const [org, communities] = await Promise.all([this.db.select({ unread: sql`count(*)::int` }).from(n).where(and(unread, isNull(n.communityId))), communityIds.length === 0 ? Promise.resolve([]) : this.db.select({
|
|
156
|
+
communityId: n.communityId,
|
|
157
|
+
unread: sql`count(*)::int`
|
|
158
|
+
}).from(n).where(and(unread, inArray(n.communityId, communityIds))).groupBy(n.communityId)]);
|
|
159
|
+
return {
|
|
160
|
+
org: Number(org[0]?.unread ?? 0),
|
|
161
|
+
communities: communities.flatMap((row) => row.communityId ? [{
|
|
162
|
+
communityId: row.communityId,
|
|
163
|
+
unread: Number(row.unread)
|
|
164
|
+
}] : [])
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
async markRead(orgId, userId, notificationId, at) {
|
|
168
|
+
return (await this.db.update(n).set({ readAt: sql`coalesce(${n.readAt}, ${at.toISOString()}::timestamptz)` }).where(and(eq(n.orgId, orgId), eq(n.userId, userId), eq(n.id, notificationId), eq(n.inApp, true))).returning({ id: n.id })).length > 0;
|
|
169
|
+
}
|
|
170
|
+
async markAllRead(orgId, userId, filter, at) {
|
|
171
|
+
return (await this.db.update(n).set({ readAt: at }).where(and(...listed(orgId, userId, filter), isNull(n.readAt))).returning({ id: n.id })).length;
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
translateDbErrors(DrizzleNotificationsRepository);
|
|
175
|
+
//#endregion
|
|
176
|
+
export { DrizzleNotificationsRepository };
|
|
177
|
+
|
|
178
|
+
//# sourceMappingURL=notifications.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notifications.js","names":[],"sources":["../../src/repositories/notifications.ts"],"sourcesContent":["import {\n and,\n arrayOverlaps,\n desc,\n eq,\n inArray,\n isNotNull,\n isNull,\n or,\n sql,\n type SQL,\n} from \"drizzle-orm\";\nimport type {\n ListedNotification,\n NewDelivery,\n NewNotification,\n NotificationPage,\n NotificationsRepository,\n UnreadCounts,\n} from \"@headless-lms/core/automations\";\nimport type {\n Delivery,\n NotificationFilter,\n StoredNotification,\n SubjectRef,\n} from \"@headless-lms/core/types\";\nimport type { DbExecutor } from \"../client.js\";\nimport { notificationDeliveries, notifications } from \"../schema/index.js\";\nimport { translateDbErrors } from \"./pg-errors.js\";\n\nconst n = notifications;\nconst d = notificationDeliveries;\n\ntype NotificationRow = typeof notifications.$inferSelect;\ntype DeliveryRow = typeof notificationDeliveries.$inferSelect;\n\nfunction ref(type: string | null, id: string | null): SubjectRef | null {\n return type && id ? { type: type as SubjectRef[\"type\"], id } : null;\n}\n\nfunction toStored(row: NotificationRow): StoredNotification {\n return {\n id: row.id,\n orgId: row.orgId,\n userId: row.userId,\n type: row.type,\n scope: row.scope,\n reasons: row.reasons,\n matchedReason: row.matchedReason,\n category: row.category,\n communityId: row.communityId,\n spaceId: row.spaceId,\n actorOrgUserId: row.actorOrgUserId,\n subject: ref(row.subjectType, row.subjectId),\n parent: ref(row.parentType, row.parentId),\n display: row.display,\n createdAt: row.createdAt,\n };\n}\n\nfunction toDelivery(row: DeliveryRow): Delivery {\n return {\n id: row.id,\n notificationId: row.notificationId,\n channel: row.channel,\n status: row.status,\n lastError: row.lastError,\n createdAt: row.createdAt,\n sentAt: row.sentAt,\n };\n}\n\nfunction listed(orgId: string, userId: string, filter: NotificationFilter): SQL[] {\n const conditions: SQL[] = [eq(n.orgId, orgId), eq(n.userId, userId), eq(n.inApp, true)];\n if (filter.scope === \"org\") {\n conditions.push(isNull(n.communityId));\n }\n if (filter.scope === \"community\") {\n conditions.push(isNotNull(n.communityId));\n }\n if (filter.communityId !== undefined) {\n const community = eq(n.communityId, filter.communityId);\n conditions.push(filter.includeOrg ? or(community, isNull(n.communityId))! : community);\n }\n if (filter.category && filter.category.length > 0) {\n conditions.push(inArray(n.category, filter.category));\n }\n if (filter.reason && filter.reason.length > 0) {\n conditions.push(arrayOverlaps(n.reasons, filter.reason));\n }\n if (filter.type && filter.type.length > 0) {\n conditions.push(inArray(n.type, filter.type));\n }\n if (filter.unread === true) {\n conditions.push(isNull(n.readAt));\n }\n return conditions;\n}\n\nexport class DrizzleNotificationsRepository implements NotificationsRepository {\n constructor(private readonly db: DbExecutor) {}\n\n async insert(orgId: string, rows: NewNotification[], deliveries: NewDelivery[]): Promise<void> {\n if (rows.length === 0) {\n return;\n }\n await this.db.transaction(async (tx) => {\n const inserted = await tx\n .insert(n)\n .values(\n rows.map((row) => ({\n orgId,\n id: row.id,\n userId: row.userId,\n sourceEventId: row.sourceEventId,\n type: row.type,\n scope: row.scope,\n reasons: row.reasons,\n matchedReason: row.matchedReason,\n category: row.category,\n communityId: row.communityId,\n spaceId: row.spaceId,\n actorOrgUserId: row.actorOrgUserId,\n subjectType: row.subject?.type ?? null,\n subjectId: row.subject?.id ?? null,\n parentType: row.parent?.type ?? null,\n parentId: row.parent?.id ?? null,\n display: row.display,\n inApp: row.inApp,\n createdAt: row.createdAt,\n })),\n )\n .onConflictDoNothing()\n .returning({ id: n.id });\n const kept = new Set(inserted.map((row) => row.id));\n const pending = deliveries.filter((delivery) => kept.has(delivery.notificationId));\n if (pending.length === 0) {\n return;\n }\n await tx\n .insert(d)\n .values(\n pending.map((delivery) => ({\n orgId,\n id: delivery.id,\n notificationId: delivery.notificationId,\n channel: delivery.channel,\n status: \"pending\" as const,\n })),\n )\n .onConflictDoNothing();\n });\n }\n\n async pendingDeliveriesForEvent(orgId: string, sourceEventId: string): Promise<{ id: string }[]> {\n return this.db\n .select({ id: d.id })\n .from(d)\n .innerJoin(n, and(eq(n.orgId, d.orgId), eq(n.id, d.notificationId)))\n .where(and(eq(n.orgId, orgId), eq(n.sourceEventId, sourceEventId), eq(d.status, \"pending\")))\n .orderBy(d.id);\n }\n\n async findDelivery(\n orgId: string,\n deliveryId: string,\n ): Promise<{ delivery: Delivery; notification: StoredNotification } | null> {\n const [row] = await this.db\n .select({ delivery: d, notification: n })\n .from(d)\n .innerJoin(n, and(eq(n.orgId, d.orgId), eq(n.id, d.notificationId)))\n .where(and(eq(d.orgId, orgId), eq(d.id, deliveryId)))\n .limit(1);\n return row\n ? { delivery: toDelivery(row.delivery), notification: toStored(row.notification) }\n : null;\n }\n\n async markSent(orgId: string, deliveryId: string, at: Date): Promise<void> {\n await this.db\n .update(d)\n .set({ status: \"sent\", sentAt: at })\n .where(and(eq(d.orgId, orgId), eq(d.id, deliveryId), eq(d.status, \"pending\")));\n }\n\n async recordError(orgId: string, deliveryId: string, error: string): Promise<void> {\n await this.db\n .update(d)\n .set({ lastError: error })\n .where(and(eq(d.orgId, orgId), eq(d.id, deliveryId), eq(d.status, \"pending\")));\n }\n\n async markFailed(orgId: string, deliveryId: string, error: string): Promise<void> {\n await this.db\n .update(d)\n .set({ status: \"failed\", lastError: error })\n .where(and(eq(d.orgId, orgId), eq(d.id, deliveryId), eq(d.status, \"pending\")));\n }\n\n async page(\n orgId: string,\n userId: string,\n filter: NotificationFilter,\n page: { page: number; pageSize: number },\n ): Promise<NotificationPage> {\n const where = and(...listed(orgId, userId, filter));\n const [rows, counts] = await Promise.all([\n this.db\n .select()\n .from(n)\n .where(where)\n .orderBy(desc(n.createdAt), desc(n.id))\n .limit(page.pageSize)\n .offset((page.page - 1) * page.pageSize),\n this.db\n .select({\n total: sql<number>`count(*)::int`,\n unread: sql<number>`(count(*) filter (where ${n.readAt} is null))::int`,\n })\n .from(n)\n .where(where),\n ]);\n return {\n items: rows.map((row) => ({ ...toStored(row), readAt: row.readAt })),\n total: Number(counts[0]?.total ?? 0),\n unreadCount: Number(counts[0]?.unread ?? 0),\n };\n }\n\n async find(\n orgId: string,\n userId: string,\n notificationId: string,\n ): Promise<ListedNotification | null> {\n const [row] = await this.db\n .select()\n .from(n)\n .where(\n and(eq(n.orgId, orgId), eq(n.userId, userId), eq(n.id, notificationId), eq(n.inApp, true)),\n )\n .limit(1);\n return row ? { ...toStored(row), readAt: row.readAt } : null;\n }\n\n async unreadCounts(orgId: string, userId: string, communityIds: string[]): Promise<UnreadCounts> {\n const unread = and(\n eq(n.orgId, orgId),\n eq(n.userId, userId),\n eq(n.inApp, true),\n isNull(n.readAt),\n );\n const [org, communities] = await Promise.all([\n this.db\n .select({ unread: sql<number>`count(*)::int` })\n .from(n)\n .where(and(unread, isNull(n.communityId))),\n communityIds.length === 0\n ? Promise.resolve([])\n : this.db\n .select({ communityId: n.communityId, unread: sql<number>`count(*)::int` })\n .from(n)\n .where(and(unread, inArray(n.communityId, communityIds)))\n .groupBy(n.communityId),\n ]);\n return {\n org: Number(org[0]?.unread ?? 0),\n communities: communities.flatMap((row) =>\n row.communityId ? [{ communityId: row.communityId, unread: Number(row.unread) }] : [],\n ),\n };\n }\n\n async markRead(\n orgId: string,\n userId: string,\n notificationId: string,\n at: Date,\n ): Promise<boolean> {\n const rows = await this.db\n .update(n)\n .set({ readAt: sql`coalesce(${n.readAt}, ${at.toISOString()}::timestamptz)` })\n .where(\n and(eq(n.orgId, orgId), eq(n.userId, userId), eq(n.id, notificationId), eq(n.inApp, true)),\n )\n .returning({ id: n.id });\n return rows.length > 0;\n }\n\n async markAllRead(\n orgId: string,\n userId: string,\n filter: NotificationFilter,\n at: Date,\n ): Promise<number> {\n const rows = await this.db\n .update(n)\n .set({ readAt: at })\n .where(and(...listed(orgId, userId, filter), isNull(n.readAt)))\n .returning({ id: n.id });\n return rows.length;\n }\n}\n\ntranslateDbErrors(DrizzleNotificationsRepository);\n"],"mappings":"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"}
|
|
@@ -87,6 +87,219 @@ declare const orgUserProfileColumns: {
|
|
|
87
87
|
generated: undefined;
|
|
88
88
|
}, {}, {}>;
|
|
89
89
|
};
|
|
90
|
+
declare const authorProfileColumns: {
|
|
91
|
+
role: import("drizzle-orm/pg-core").PgColumn<{
|
|
92
|
+
name: "role";
|
|
93
|
+
tableName: "org_users";
|
|
94
|
+
dataType: "string";
|
|
95
|
+
columnType: "PgText";
|
|
96
|
+
data: "student" | "owner" | "admin" | "instructor" | "moderator";
|
|
97
|
+
driverParam: string;
|
|
98
|
+
notNull: true;
|
|
99
|
+
hasDefault: false;
|
|
100
|
+
isPrimaryKey: false;
|
|
101
|
+
isAutoincrement: false;
|
|
102
|
+
hasRuntimeDefault: false;
|
|
103
|
+
enumValues: ["owner", "admin", "instructor", "moderator", "student"];
|
|
104
|
+
baseColumn: never;
|
|
105
|
+
identity: undefined;
|
|
106
|
+
generated: undefined;
|
|
107
|
+
}, {}, {}>;
|
|
108
|
+
displayName: import("drizzle-orm/pg-core").PgColumn<{
|
|
109
|
+
name: "display_name";
|
|
110
|
+
tableName: "user_profiles";
|
|
111
|
+
dataType: "string";
|
|
112
|
+
columnType: "PgText";
|
|
113
|
+
data: string;
|
|
114
|
+
driverParam: string;
|
|
115
|
+
notNull: false;
|
|
116
|
+
hasDefault: false;
|
|
117
|
+
isPrimaryKey: false;
|
|
118
|
+
isAutoincrement: false;
|
|
119
|
+
hasRuntimeDefault: false;
|
|
120
|
+
enumValues: [string, ...string[]];
|
|
121
|
+
baseColumn: never;
|
|
122
|
+
identity: undefined;
|
|
123
|
+
generated: undefined;
|
|
124
|
+
}, {}, {}>;
|
|
125
|
+
headline: import("drizzle-orm/pg-core").PgColumn<{
|
|
126
|
+
name: "headline";
|
|
127
|
+
tableName: "user_profiles";
|
|
128
|
+
dataType: "string";
|
|
129
|
+
columnType: "PgText";
|
|
130
|
+
data: string;
|
|
131
|
+
driverParam: string;
|
|
132
|
+
notNull: false;
|
|
133
|
+
hasDefault: false;
|
|
134
|
+
isPrimaryKey: false;
|
|
135
|
+
isAutoincrement: false;
|
|
136
|
+
hasRuntimeDefault: false;
|
|
137
|
+
enumValues: [string, ...string[]];
|
|
138
|
+
baseColumn: never;
|
|
139
|
+
identity: undefined;
|
|
140
|
+
generated: undefined;
|
|
141
|
+
}, {}, {}>;
|
|
142
|
+
imageAssetOrgId: import("drizzle-orm/pg-core").PgColumn<{
|
|
143
|
+
name: "image_asset_org_id";
|
|
144
|
+
tableName: "user_profiles";
|
|
145
|
+
dataType: "string";
|
|
146
|
+
columnType: "PgText";
|
|
147
|
+
data: string;
|
|
148
|
+
driverParam: string;
|
|
149
|
+
notNull: false;
|
|
150
|
+
hasDefault: false;
|
|
151
|
+
isPrimaryKey: false;
|
|
152
|
+
isAutoincrement: false;
|
|
153
|
+
hasRuntimeDefault: false;
|
|
154
|
+
enumValues: [string, ...string[]];
|
|
155
|
+
baseColumn: never;
|
|
156
|
+
identity: undefined;
|
|
157
|
+
generated: undefined;
|
|
158
|
+
}, {}, {}>;
|
|
159
|
+
imageAssetId: import("drizzle-orm/pg-core").PgColumn<{
|
|
160
|
+
name: "image_asset_id";
|
|
161
|
+
tableName: "user_profiles";
|
|
162
|
+
dataType: "string";
|
|
163
|
+
columnType: "PgText";
|
|
164
|
+
data: string;
|
|
165
|
+
driverParam: string;
|
|
166
|
+
notNull: false;
|
|
167
|
+
hasDefault: false;
|
|
168
|
+
isPrimaryKey: false;
|
|
169
|
+
isAutoincrement: false;
|
|
170
|
+
hasRuntimeDefault: false;
|
|
171
|
+
enumValues: [string, ...string[]];
|
|
172
|
+
baseColumn: never;
|
|
173
|
+
identity: undefined;
|
|
174
|
+
generated: undefined;
|
|
175
|
+
}, {}, {}>;
|
|
176
|
+
id: import("drizzle-orm/pg-core").PgColumn<{
|
|
177
|
+
name: "id";
|
|
178
|
+
tableName: "org_users";
|
|
179
|
+
dataType: "string";
|
|
180
|
+
columnType: "PgText";
|
|
181
|
+
data: string;
|
|
182
|
+
driverParam: string;
|
|
183
|
+
notNull: true;
|
|
184
|
+
hasDefault: true;
|
|
185
|
+
isPrimaryKey: false;
|
|
186
|
+
isAutoincrement: false;
|
|
187
|
+
hasRuntimeDefault: true;
|
|
188
|
+
enumValues: [string, ...string[]];
|
|
189
|
+
baseColumn: never;
|
|
190
|
+
identity: undefined;
|
|
191
|
+
generated: undefined;
|
|
192
|
+
}, {}, {}>;
|
|
193
|
+
firstName: import("drizzle-orm/pg-core").PgColumn<{
|
|
194
|
+
name: "first_name";
|
|
195
|
+
tableName: "users";
|
|
196
|
+
dataType: "string";
|
|
197
|
+
columnType: "PgText";
|
|
198
|
+
data: string;
|
|
199
|
+
driverParam: string;
|
|
200
|
+
notNull: false;
|
|
201
|
+
hasDefault: false;
|
|
202
|
+
isPrimaryKey: false;
|
|
203
|
+
isAutoincrement: false;
|
|
204
|
+
hasRuntimeDefault: false;
|
|
205
|
+
enumValues: [string, ...string[]];
|
|
206
|
+
baseColumn: never;
|
|
207
|
+
identity: undefined;
|
|
208
|
+
generated: undefined;
|
|
209
|
+
}, {}, {}>;
|
|
210
|
+
lastName: import("drizzle-orm/pg-core").PgColumn<{
|
|
211
|
+
name: "last_name";
|
|
212
|
+
tableName: "users";
|
|
213
|
+
dataType: "string";
|
|
214
|
+
columnType: "PgText";
|
|
215
|
+
data: string;
|
|
216
|
+
driverParam: string;
|
|
217
|
+
notNull: false;
|
|
218
|
+
hasDefault: false;
|
|
219
|
+
isPrimaryKey: false;
|
|
220
|
+
isAutoincrement: false;
|
|
221
|
+
hasRuntimeDefault: false;
|
|
222
|
+
enumValues: [string, ...string[]];
|
|
223
|
+
baseColumn: never;
|
|
224
|
+
identity: undefined;
|
|
225
|
+
generated: undefined;
|
|
226
|
+
}, {}, {}>;
|
|
227
|
+
email: import("drizzle-orm/pg-core").PgColumn<{
|
|
228
|
+
name: "email";
|
|
229
|
+
tableName: "users";
|
|
230
|
+
dataType: "string";
|
|
231
|
+
columnType: "PgText";
|
|
232
|
+
data: string;
|
|
233
|
+
driverParam: string;
|
|
234
|
+
notNull: true;
|
|
235
|
+
hasDefault: false;
|
|
236
|
+
isPrimaryKey: false;
|
|
237
|
+
isAutoincrement: false;
|
|
238
|
+
hasRuntimeDefault: false;
|
|
239
|
+
enumValues: [string, ...string[]];
|
|
240
|
+
baseColumn: never;
|
|
241
|
+
identity: undefined;
|
|
242
|
+
generated: undefined;
|
|
243
|
+
}, {}, {}>;
|
|
244
|
+
image: import("drizzle-orm/pg-core").PgColumn<{
|
|
245
|
+
name: "image";
|
|
246
|
+
tableName: "ba_user";
|
|
247
|
+
dataType: "string";
|
|
248
|
+
columnType: "PgText";
|
|
249
|
+
data: string;
|
|
250
|
+
driverParam: string;
|
|
251
|
+
notNull: false;
|
|
252
|
+
hasDefault: false;
|
|
253
|
+
isPrimaryKey: false;
|
|
254
|
+
isAutoincrement: false;
|
|
255
|
+
hasRuntimeDefault: false;
|
|
256
|
+
enumValues: [string, ...string[]];
|
|
257
|
+
baseColumn: never;
|
|
258
|
+
identity: undefined;
|
|
259
|
+
generated: undefined;
|
|
260
|
+
}, {}, {}>;
|
|
261
|
+
};
|
|
262
|
+
declare const studentFirstName: import("drizzle-orm").SQL<string | null>;
|
|
263
|
+
declare const studentLastName: import("drizzle-orm").SQL<string | null>;
|
|
264
|
+
declare const studentProfileColumns: {
|
|
265
|
+
id: import("drizzle-orm/pg-core").PgColumn<{
|
|
266
|
+
name: "id";
|
|
267
|
+
tableName: "org_users";
|
|
268
|
+
dataType: "string";
|
|
269
|
+
columnType: "PgText";
|
|
270
|
+
data: string;
|
|
271
|
+
driverParam: string;
|
|
272
|
+
notNull: true;
|
|
273
|
+
hasDefault: true;
|
|
274
|
+
isPrimaryKey: false;
|
|
275
|
+
isAutoincrement: false;
|
|
276
|
+
hasRuntimeDefault: true;
|
|
277
|
+
enumValues: [string, ...string[]];
|
|
278
|
+
baseColumn: never;
|
|
279
|
+
identity: undefined;
|
|
280
|
+
generated: undefined;
|
|
281
|
+
}, {}, {}>;
|
|
282
|
+
firstName: import("drizzle-orm").SQL<string | null>;
|
|
283
|
+
lastName: import("drizzle-orm").SQL<string | null>;
|
|
284
|
+
email: import("drizzle-orm/pg-core").PgColumn<{
|
|
285
|
+
name: "email";
|
|
286
|
+
tableName: "users";
|
|
287
|
+
dataType: "string";
|
|
288
|
+
columnType: "PgText";
|
|
289
|
+
data: string;
|
|
290
|
+
driverParam: string;
|
|
291
|
+
notNull: true;
|
|
292
|
+
hasDefault: false;
|
|
293
|
+
isPrimaryKey: false;
|
|
294
|
+
isAutoincrement: false;
|
|
295
|
+
hasRuntimeDefault: false;
|
|
296
|
+
enumValues: [string, ...string[]];
|
|
297
|
+
baseColumn: never;
|
|
298
|
+
identity: undefined;
|
|
299
|
+
generated: undefined;
|
|
300
|
+
}, {}, {}>;
|
|
301
|
+
image: import("drizzle-orm").SQL<string | null>;
|
|
302
|
+
};
|
|
90
303
|
//#endregion
|
|
91
|
-
export { orgUserProfileColumns };
|
|
304
|
+
export { authorProfileColumns, orgUserProfileColumns, studentFirstName, studentLastName, studentProfileColumns };
|
|
92
305
|
//# sourceMappingURL=org-user-profile.d.ts.map
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { users } from "../schema/identity.js";
|
|
1
|
+
import { userProfiles, users } from "../schema/identity.js";
|
|
2
2
|
import { orgUsers } from "../schema/organizations.js";
|
|
3
3
|
import { user } from "../schema/better-auth.js";
|
|
4
|
+
import { sql } from "drizzle-orm";
|
|
4
5
|
//#region src/repositories/org-user-profile.ts
|
|
5
6
|
/** Spread into a `.select({ ... })` to get the profile columns. */
|
|
6
7
|
const orgUserProfileColumns = {
|
|
@@ -10,7 +11,27 @@ const orgUserProfileColumns = {
|
|
|
10
11
|
email: users.email,
|
|
11
12
|
image: user.image
|
|
12
13
|
};
|
|
14
|
+
const authorProfileColumns = {
|
|
15
|
+
...orgUserProfileColumns,
|
|
16
|
+
role: orgUsers.role,
|
|
17
|
+
displayName: userProfiles.displayName,
|
|
18
|
+
headline: userProfiles.headline,
|
|
19
|
+
imageAssetOrgId: userProfiles.imageAssetOrgId,
|
|
20
|
+
imageAssetId: userProfiles.imageAssetId
|
|
21
|
+
};
|
|
22
|
+
const profileVisible = sql`(${orgUsers.status} <> 'invited' or (${users.externalId} is null and not exists (
|
|
23
|
+
select 1 from org_users other where other.user_id = ${users.id} and other.org_id <> ${orgUsers.orgId}
|
|
24
|
+
)))`;
|
|
25
|
+
const studentFirstName = sql`case when ${profileVisible} then ${users.firstName} end`;
|
|
26
|
+
const studentLastName = sql`case when ${profileVisible} then ${users.lastName} end`;
|
|
27
|
+
const studentProfileColumns = {
|
|
28
|
+
id: orgUsers.id,
|
|
29
|
+
firstName: studentFirstName,
|
|
30
|
+
lastName: studentLastName,
|
|
31
|
+
email: users.email,
|
|
32
|
+
image: sql`case when ${profileVisible} then ${user.image} end`
|
|
33
|
+
};
|
|
13
34
|
//#endregion
|
|
14
|
-
export { orgUserProfileColumns };
|
|
35
|
+
export { authorProfileColumns, orgUserProfileColumns, studentFirstName, studentLastName, studentProfileColumns };
|
|
15
36
|
|
|
16
37
|
//# sourceMappingURL=org-user-profile.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"org-user-profile.js","names":[],"sources":["../../src/repositories/org-user-profile.ts"],"sourcesContent":["// The one display join for an org user's profile.\n//\n// The names and email live on the identity `users` row the org_users row links\n// to; the avatar lives on the auth engine's mirrored `user` table, reached\n// through that same identity row. The identity join is INNER (user_id is NOT\n// NULL); the auth join is LEFT, since only the avatar depends on it.\n//\n// Sanctioned by .eslintrc.cjs — \"db repositories read the auth adapter's\n// mirrored `user` table for display joins\".\n//\n// No `joinOrgUserProfile` helper: Drizzle's query builder type narrows on every\n// chained call, so a generic wrapper around `.innerJoin(...).leftJoin(...)`\n// can't be typed without widening it to `unknown` — each repository chains the\n// two joins inline instead.\nimport { orgUsers } from \"../schema/organizations.js\";\nimport { users } from \"../schema/identity.js\";\nimport { user } from \"../schema/better-auth.js\";\n\n/** Spread into a `.select({ ... })` to get the profile columns. */\nexport const orgUserProfileColumns = {\n id: orgUsers.id,\n firstName: users.firstName,\n lastName: users.lastName,\n email: users.email,\n image: user.image,\n};\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"org-user-profile.js","names":[],"sources":["../../src/repositories/org-user-profile.ts"],"sourcesContent":["// The one display join for an org user's profile.\n//\n// The names and email live on the identity `users` row the org_users row links\n// to; the avatar lives on the auth engine's mirrored `user` table, reached\n// through that same identity row. The identity join is INNER (user_id is NOT\n// NULL); the auth join is LEFT, since only the avatar depends on it.\n//\n// Sanctioned by .eslintrc.cjs — \"db repositories read the auth adapter's\n// mirrored `user` table for display joins\".\n//\n// No `joinOrgUserProfile` helper: Drizzle's query builder type narrows on every\n// chained call, so a generic wrapper around `.innerJoin(...).leftJoin(...)`\n// can't be typed without widening it to `unknown` — each repository chains the\n// two joins inline instead.\nimport { sql } from \"drizzle-orm\";\nimport { orgUsers } from \"../schema/organizations.js\";\nimport { userProfiles, users } from \"../schema/identity.js\";\nimport { user } from \"../schema/better-auth.js\";\n\n/** Spread into a `.select({ ... })` to get the profile columns. */\nexport const orgUserProfileColumns = {\n id: orgUsers.id,\n firstName: users.firstName,\n lastName: users.lastName,\n email: users.email,\n image: user.image,\n};\n\nexport const authorProfileColumns = {\n ...orgUserProfileColumns,\n role: orgUsers.role,\n displayName: userProfiles.displayName,\n headline: userProfiles.headline,\n imageAssetOrgId: userProfiles.imageAssetOrgId,\n imageAssetId: userProfiles.imageAssetId,\n};\n\n// A person is shared across organizations. Until an invited student accepts,\n// the inviting org sees the name and avatar only when the profile is its own:\n// no account yet and no other organization.\nconst profileVisible = sql`(${orgUsers.status} <> 'invited' or (${users.externalId} is null and not exists (\n select 1 from org_users other where other.user_id = ${users.id} and other.org_id <> ${orgUsers.orgId}\n)))`;\n\nexport const studentFirstName = sql<\n string | null\n>`case when ${profileVisible} then ${users.firstName} end`;\nexport const studentLastName = sql<\n string | null\n>`case when ${profileVisible} then ${users.lastName} end`;\n\nexport const studentProfileColumns = {\n id: orgUsers.id,\n firstName: studentFirstName,\n lastName: studentLastName,\n email: users.email,\n image: sql<string | null>`case when ${profileVisible} then ${user.image} end`,\n};\n"],"mappings":";;;;;;AAoBA,MAAa,wBAAwB;CACnC,IAAI,SAAS;CACb,WAAW,MAAM;CACjB,UAAU,MAAM;CAChB,OAAO,MAAM;CACb,OAAO,KAAK;AACd;AAEA,MAAa,uBAAuB;CAClC,GAAG;CACH,MAAM,SAAS;CACf,aAAa,aAAa;CAC1B,UAAU,aAAa;CACvB,iBAAiB,aAAa;CAC9B,cAAc,aAAa;AAC7B;AAKA,MAAM,iBAAiB,GAAG,IAAI,SAAS,OAAO,oBAAoB,MAAM,WAAW;wDAC3B,MAAM,GAAG,uBAAuB,SAAS,MAAM;;AAGvG,MAAa,mBAAmB,GAE/B,aAAa,eAAe,QAAQ,MAAM,UAAU;AACrD,MAAa,kBAAkB,GAE9B,aAAa,eAAe,QAAQ,MAAM,SAAS;AAEpD,MAAa,wBAAwB;CACnC,IAAI,SAAS;CACb,WAAW;CACX,UAAU;CACV,OAAO,MAAM;CACb,OAAO,GAAkB,aAAa,eAAe,QAAQ,KAAK,MAAM;AAC1E"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { NodePgDatabase } from "drizzle-orm/node-postgres";
|
|
2
|
-
import { CreateOrgUserInput, Invite, NewInviteRow, NewOrganizationRow, OrgUser, Organization, OrganizationsRepository, UpdateOrgUserInput, UpdateOrganizationInput } from "@headless-lms/core/organizations";
|
|
2
|
+
import { CreateOrgUserInput, Invite, NewInviteRow, NewOrganizationRow, OrgUser, Organization, OrganizationsRepository, Role, UpdateOrgUserInput, UpdateOrganizationInput } from "@headless-lms/core/organizations";
|
|
3
3
|
import { Logger } from "@headless-lms/core/shared/ports";
|
|
4
4
|
//#region src/repositories/organizations.d.ts
|
|
5
5
|
declare class DrizzleOrganizationsRepository implements OrganizationsRepository {
|
|
@@ -10,6 +10,7 @@ declare class DrizzleOrganizationsRepository implements OrganizationsRepository
|
|
|
10
10
|
update(id: string, input: UpdateOrganizationInput): Promise<Organization | null>;
|
|
11
11
|
delete(id: string): Promise<Organization>;
|
|
12
12
|
findById(id: string): Promise<Organization | null>;
|
|
13
|
+
listIds(): Promise<string[]>;
|
|
13
14
|
findByExternalId(externalId: string): Promise<Organization | null>;
|
|
14
15
|
findBySlug(slug: string): Promise<Organization | null>;
|
|
15
16
|
upsertPendingInvite(orgId: string, input: NewInviteRow): Promise<Invite>;
|
|
@@ -20,10 +21,11 @@ declare class DrizzleOrganizationsRepository implements OrganizationsRepository
|
|
|
20
21
|
findOrgUser(orgId: string, userId: string): Promise<OrgUser | null>;
|
|
21
22
|
findOrgUsersByUser(userId: string): Promise<OrgUser[]>;
|
|
22
23
|
findOrgUserById(orgId: string, id: string): Promise<OrgUser | null>;
|
|
24
|
+
findOrgUsersByIds(orgId: string, ids: string[]): Promise<OrgUser[]>;
|
|
25
|
+
findOrgUserIdsByRole(orgId: string, roles: Role[]): Promise<string[]>;
|
|
23
26
|
createOrgUser(input: CreateOrgUserInput): Promise<OrgUser>;
|
|
24
27
|
updateOrgUser(orgId: string, id: string, patch: UpdateOrgUserInput): Promise<OrgUser | null>;
|
|
25
28
|
findStudentOrgUsers(userId: string): Promise<OrgUser[]>;
|
|
26
|
-
cancelPendingInvite(orgId: string, email: string): Promise<Invite | null>;
|
|
27
29
|
deleteOrgUser(orgId: string, id: string): Promise<OrgUser | null>;
|
|
28
30
|
}
|
|
29
31
|
//#endregion
|