@headless-lms/adapter-db 0.6.1 → 0.6.3
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/repositories/admin.js +29 -2
- package/dist/repositories/admin.js.map +1 -1
- package/dist/schema/assets.d.ts +1 -1
- package/dist/schema/automations.d.ts +3 -3
- package/dist/schema/better-auth.d.ts +17 -17
- package/dist/schema/entitlements.d.ts +2 -2
- package/dist/schema/organizations.d.ts +1 -1
- package/dist/schema/roles.d.ts +1 -1
- package/package.json +2 -2
|
@@ -15,7 +15,6 @@ import { and, asc, desc, eq, exists, ilike, inArray, or, sql } from "drizzle-orm
|
|
|
15
15
|
import { union } from "drizzle-orm/pg-core";
|
|
16
16
|
//#region src/repositories/admin.ts
|
|
17
17
|
const entitlementCountExpr = sql`count(${entitlements.id})`;
|
|
18
|
-
const avgProgressExpr = sql`0`;
|
|
19
18
|
function hasNoOrgRole() {
|
|
20
19
|
return sql`not exists (
|
|
21
20
|
select 1 from ${roleAssignments} ra
|
|
@@ -32,6 +31,33 @@ function invitedAs(roleKey) {
|
|
|
32
31
|
function isStudent(memberRoleKey) {
|
|
33
32
|
return and(hasNoOrgRole(), or(eq(orgUsers.status, "active"), and(eq(orgUsers.status, "invited"), invitedAs(memberRoleKey))));
|
|
34
33
|
}
|
|
34
|
+
const avgProgressExpr = sql`coalesce((
|
|
35
|
+
select round(avg(per_course.progress))
|
|
36
|
+
from (
|
|
37
|
+
select case
|
|
38
|
+
when count(a.id) = 0 then 0
|
|
39
|
+
else round(
|
|
40
|
+
least(count(pr.id) filter (where pr.completed_at is not null)::numeric / count(a.id), 1) * 100
|
|
41
|
+
)
|
|
42
|
+
end as progress
|
|
43
|
+
from (
|
|
44
|
+
select distinct ca.content_id
|
|
45
|
+
from content_access ca
|
|
46
|
+
join courses c on c.org_id = ca.org_id and c.id = ca.content_id
|
|
47
|
+
where ca.org_id = ${orgUsers.orgId} and ca.org_user_id = ${orgUsers.id}
|
|
48
|
+
) held
|
|
49
|
+
left join activities a
|
|
50
|
+
on a.org_id = ${orgUsers.orgId}
|
|
51
|
+
and a.course_id = held.content_id
|
|
52
|
+
and coalesce((a.settings ->> 'published')::boolean, true)
|
|
53
|
+
left join progress_records pr
|
|
54
|
+
on pr.org_id = ${orgUsers.orgId}
|
|
55
|
+
and pr.org_user_id = ${orgUsers.id}
|
|
56
|
+
and pr.target_type = 'activity'
|
|
57
|
+
and pr.target_id = a.id
|
|
58
|
+
group by held.content_id
|
|
59
|
+
) per_course
|
|
60
|
+
), 0)::int`;
|
|
35
61
|
const lastActiveExpr = sql`(
|
|
36
62
|
select max(pr.updated_at) from progress_records pr
|
|
37
63
|
where pr.org_id = ${orgUsers.orgId} and pr.org_user_id = ${orgUsers.id}
|
|
@@ -434,12 +460,13 @@ var DrizzleAdminRepository = class {
|
|
|
434
460
|
const descending = sort?.startsWith("-") ?? false;
|
|
435
461
|
const field = sort ? descending ? sort.slice(1) : sort : "firstName";
|
|
436
462
|
const dir = descending ? desc : asc;
|
|
463
|
+
const nullsLast = (expr) => descending ? sql`${expr} desc nulls last` : sql`${expr} asc nulls last`;
|
|
437
464
|
switch (field) {
|
|
438
465
|
case "email": return [dir(users.email)];
|
|
439
466
|
case "entitlementCount": return [dir(entitlementCountExpr)];
|
|
440
467
|
case "avgProgress": return [dir(avgProgressExpr)];
|
|
441
468
|
case "joinedAt": return [dir(orgUsers.createdAt)];
|
|
442
|
-
case "lastActiveAt": return [
|
|
469
|
+
case "lastActiveAt": return [nullsLast(lastActiveExpr)];
|
|
443
470
|
case "lastName": return [dir(studentLastName), dir(studentFirstName)];
|
|
444
471
|
default: return [dir(studentFirstName), dir(studentLastName)];
|
|
445
472
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"admin.js","names":[],"sources":["../../src/repositories/admin.ts"],"sourcesContent":["// reporting/admin — Drizzle repository.\nimport { and, asc, desc, eq, exists, ilike, inArray, or, sql, type SQL } from \"drizzle-orm\";\nimport { union } from \"drizzle-orm/pg-core\";\nimport type { NodePgDatabase } from \"drizzle-orm/node-postgres\";\nimport type {\n AdminReportRepository,\n CommunityAnalytics,\n CommunityMembersQuery,\n CourseActivityEngagement,\n CourseAnalytics,\n CourseEnrollmentPoint,\n CourseLearner,\n CourseLearnersQuery,\n EnrollmentPoint,\n EventAnalytics,\n EventAttendeeRow,\n LearnerGrantStatus,\n OverviewStats,\n Page,\n StudentAnalytics,\n StudentCourseProgress,\n StudentItem,\n StudentRow as StudentRecord,\n StudentsQuery,\n} from \"@headless-lms/core/reporting/admin\";\nimport {\n commentFlags,\n comments,\n communities,\n contentAccess,\n courses,\n entitlements,\n invites,\n messageFlags,\n messages,\n orgUsers,\n roleAssignments,\n postFlags,\n posts,\n spaces,\n users,\n} from \"../schema/index.js\";\nimport { user } from \"../schema/better-auth.js\";\nimport type { OrgUserProfile, OrgUserStatus } from \"@headless-lms/core/types\";\nimport type { Logger } from \"@headless-lms/core/shared/ports\";\nimport { noopLogger } from \"@headless-lms/core/shared/logger\";\nimport {\n orgUserProfileColumns,\n studentFirstName,\n studentLastName,\n studentProfileColumns,\n} from \"./org-user-profile.js\";\nimport { translateDbErrors } from \"./pg-errors.js\";\nimport { grantExpired, grantOpens } from \"./grant-covers.js\";\nimport { eventAnalyticsFigures, eventAttendeePage, upcomingEventCount } from \"./event-reports.js\";\n\nconst entitlementCountExpr = sql<number>`count(${entitlements.id})`;\n// Completion now lives in the progress domain; the students report no longer\n// derives a percentage from entitlements. Placeholder until wired to progress.\nconst avgProgressExpr = sql<number>`0`;\n// Last learning activity: the newest touch on any of the student's progress\n// records. A scalar subquery so it neither fans out the entitlement count nor\n// widens the GROUP BY.\nfunction hasNoOrgRole(): SQL {\n return sql`not exists (\n select 1 from ${roleAssignments} ra\n where ra.org_id = ${orgUsers.orgId} and ra.org_user_id = ${orgUsers.id} and ra.content_id is null\n )`;\n}\n\nfunction invitedAs(roleKey: string): SQL {\n return sql`not exists (\n select 1 from ${invites} i\n where i.org_id = ${orgUsers.orgId} and lower(i.email) = lower(${users.email})\n and i.status = 'pending' and i.role <> ${roleKey}\n )`;\n}\n\nfunction isStudent(memberRoleKey: string): SQL {\n return and(\n hasNoOrgRole(),\n or(\n eq(orgUsers.status, \"active\"),\n and(eq(orgUsers.status, \"invited\"), invitedAs(memberRoleKey)),\n ),\n )!;\n}\n\nconst lastActiveExpr = sql<Date | null>`(\n select max(pr.updated_at) from progress_records pr\n where pr.org_id = ${orgUsers.orgId} and pr.org_user_id = ${orgUsers.id}\n)`;\n\ninterface StudentRow {\n id: string;\n email: string;\n image: string | null;\n firstName: string | null;\n lastName: string | null;\n status: OrgUserStatus;\n createdAt: Date;\n entitlementCount: number;\n avgProgress: number;\n lastActiveAt: Date | null;\n}\n\nfunction toStudent(row: StudentRow, items: StudentItem[]): StudentRecord {\n return {\n items,\n id: row.id,\n email: row.email,\n image: row.image ?? null,\n firstName: row.firstName,\n lastName: row.lastName,\n status: row.status,\n entitlementCount: Number(row.entitlementCount),\n avgProgress: Number(row.avgProgress),\n joinedAt: row.createdAt.toISOString(),\n lastActiveAt: row.lastActiveAt ? new Date(row.lastActiveAt).toISOString() : null,\n };\n}\n\nexport class DrizzleAdminRepository implements AdminReportRepository {\n constructor(\n private readonly db: NodePgDatabase,\n private readonly logger: Logger = noopLogger,\n ) {}\n\n async overview(orgId: string): Promise<OverviewStats> {\n const [courseCounts] = await this.db\n .select({\n published: sql<number>`count(*) filter (where ${courses.status} = 'published')`,\n draft: sql<number>`count(*) filter (where ${courses.status} = 'draft')`,\n })\n .from(courses)\n .where(eq(courses.orgId, orgId));\n\n const [entitlementCounts] = await this.db\n .select({\n activeStudents: sql<number>`count(distinct ${contentAccess.orgUserId})`,\n expiringSoon: sql<number>`count(distinct ${contentAccess.entitlementId}) filter (where ${grantExpired(contentAccess.expiresAt, sql`now() + interval '14 days'`)})`,\n })\n .from(contentAccess)\n .where(eq(contentAccess.orgId, orgId));\n\n return {\n publishedCourses: Number(courseCounts?.published ?? 0),\n draftCourses: Number(courseCounts?.draft ?? 0),\n activeStudents: Number(entitlementCounts?.activeStudents ?? 0),\n expiringSoon: Number(entitlementCounts?.expiringSoon ?? 0),\n upcomingEvents: await upcomingEventCount(this.db, orgId),\n };\n }\n\n // Grants per day over the trailing window, zero-filled so the series has one\n // point per calendar day (UTC) even when nothing was granted.\n async enrollments(orgId: string, days: number): Promise<EnrollmentPoint[]> {\n const result = await this.db.execute(sql`\n select\n to_char(d.day, 'YYYY-MM-DD') as date,\n count(${entitlements.id})::int as count\n from generate_series(\n (now() at time zone 'utc')::date - (${days} - 1),\n (now() at time zone 'utc')::date,\n interval '1 day'\n ) as d(day)\n left join ${entitlements}\n on ${entitlements.orgId} = ${orgId}\n and (${entitlements.grantedAt} at time zone 'utc')::date = d.day::date\n group by d.day\n order by d.day\n `);\n const rows = result.rows as { date: string; count: number }[];\n return rows.map((r) => ({ date: r.date, count: Number(r.count) }));\n }\n\n private async courseExists(orgId: string, courseId: string): Promise<boolean> {\n const [row] = await this.db\n .select({ id: courses.id })\n .from(courses)\n .where(and(eq(courses.orgId, orgId), eq(courses.id, courseId)));\n return row !== undefined;\n }\n\n async courseAnalytics(orgId: string, courseId: string): Promise<CourseAnalytics | null> {\n if (!(await this.courseExists(orgId, courseId))) {\n return null;\n }\n\n const cohort = sql`\n select distinct org_user_id from content_access\n where org_id = ${orgId}\n and content_id = ${courseId}\n `;\n const publishedActivities = sql`\n select a.id, a.settings, a.seq, a.module_id\n from activities a\n where a.org_id = ${orgId}\n and a.course_id = ${courseId}\n and coalesce((a.settings ->> 'published')::boolean, true)\n `;\n\n const summaryResult = await this.db.execute(sql`\n with cohort as (${cohort}),\n acts as (${publishedActivities}),\n per_learner as (\n select c.org_user_id,\n count(pr.id) filter (where pr.completed_at is not null) as done\n from cohort c\n left join progress_records pr\n on pr.org_id = ${orgId}\n and pr.org_user_id = c.org_user_id\n and pr.target_type = 'activity'\n and pr.target_id in (select id from acts)\n group by c.org_user_id\n )\n select\n (select count(*) from cohort)::int as enrolled,\n (\n select count(distinct pr.org_user_id) from progress_records pr\n where pr.org_id = ${orgId}\n and pr.org_user_id in (select org_user_id from cohort)\n and (\n (pr.target_type = 'activity' and pr.target_id in (select id from acts))\n or (pr.target_type = 'course' and pr.target_id = ${courseId})\n )\n )::int as started,\n (\n select count(*) from progress_records pr\n where pr.org_id = ${orgId}\n and pr.org_user_id in (select org_user_id from cohort)\n and pr.target_type = 'course'\n and pr.target_id = ${courseId}\n and pr.completed_at is not null\n )::int as completed,\n coalesce((\n select round(avg(least(done::numeric / nullif((select count(*) from acts), 0), 1) * 100))\n from per_learner\n ), 0)::int as avg_progress\n `);\n const summary = summaryResult.rows[0] as {\n enrolled: number;\n started: number;\n completed: number;\n avg_progress: number;\n };\n\n const funnelResult = await this.db.execute(sql`\n with cohort as (${cohort})\n select\n a.id,\n coalesce(a.settings ->> 'title', '') as title,\n m.id as module_id,\n m.title as module_title,\n count(pr.id)::int as started,\n count(pr.id) filter (where pr.completed_at is not null)::int as completed\n from activities a\n join modules m on m.org_id = a.org_id and m.id = a.module_id\n left join progress_records pr\n on pr.org_id = ${orgId}\n and pr.target_type = 'activity'\n and pr.target_id = a.id\n and pr.org_user_id in (select org_user_id from cohort)\n where a.org_id = ${orgId}\n and a.course_id = ${courseId}\n and coalesce((a.settings ->> 'published')::boolean, true)\n group by a.id, a.settings, m.id, m.title, m.seq, a.seq\n order by m.seq, a.seq\n `);\n const activities: CourseActivityEngagement[] = (\n funnelResult.rows as {\n id: string;\n title: string;\n module_id: string;\n module_title: string;\n started: number;\n completed: number;\n }[]\n ).map((r) => ({\n activityId: r.id,\n title: r.title,\n moduleId: r.module_id,\n moduleTitle: r.module_title,\n started: Number(r.started),\n completed: Number(r.completed),\n }));\n\n return {\n enrolled: Number(summary?.enrolled ?? 0),\n started: Number(summary?.started ?? 0),\n completed: Number(summary?.completed ?? 0),\n avgProgress: Number(summary?.avg_progress ?? 0),\n activities,\n };\n }\n\n // Grants per day for this course over the trailing window, zero-filled so the\n // series has one point per calendar day (UTC) even when nothing was granted.\n async courseEnrollments(\n orgId: string,\n courseId: string,\n days: number,\n ): Promise<CourseEnrollmentPoint[] | null> {\n if (!(await this.courseExists(orgId, courseId))) {\n return null;\n }\n const result = await this.db.execute(sql`\n select\n to_char(d.day, 'YYYY-MM-DD') as date,\n count(${entitlements.id})::int as count\n from generate_series(\n (now() at time zone 'utc')::date - (${days} - 1),\n (now() at time zone 'utc')::date,\n interval '1 day'\n ) as d(day)\n left join ${entitlements}\n on ${entitlements.orgId} = ${orgId}\n and ${entitlements.contentId} = ${courseId}\n and (${entitlements.grantedAt} at time zone 'utc')::date = d.day::date\n group by d.day\n order by d.day\n `);\n const rows = result.rows as { date: string; count: number }[];\n return rows.map((r) => ({ date: r.date, count: Number(r.count) }));\n }\n\n async courseLearners(\n orgId: string,\n courseId: string,\n query: CourseLearnersQuery,\n ): Promise<Page<CourseLearner> | null> {\n if (!(await this.courseExists(orgId, courseId))) {\n return null;\n }\n const expired = grantExpired(entitlements.expiresAt);\n const grantStatus = sql<LearnerGrantStatus>`case\n when ${entitlements.status} = 'revoked' then 'revoked'\n when ${expired} then 'expired'\n else 'active'\n end`;\n const grantRank = sql`case\n when ${entitlements.status} = 'revoked' then 2\n when ${expired} then 1\n else 0\n end`;\n const learnerGrant = this.db.$with(\"learner_grant\").as(\n this.db\n .selectDistinctOn([entitlements.orgUserId], {\n orgUserId: entitlements.orgUserId,\n status: grantStatus.as(\"grant_status\"),\n source: entitlements.source,\n grantedAt: entitlements.grantedAt,\n expiresAt: entitlements.expiresAt,\n })\n .from(entitlements)\n .where(\n and(eq(entitlements.orgId, orgId), grantOpens(entitlements, sql`${courseId}`, \"course\")),\n )\n .orderBy(\n entitlements.orgUserId,\n grantRank,\n sql`${entitlements.expiresAt} desc nulls first`,\n desc(entitlements.grantedAt),\n ),\n );\n const publishedActivities = sql`(\n select a.id from activities a\n where a.org_id = ${orgId}\n and a.course_id = ${courseId}\n and coalesce((a.settings ->> 'published')::boolean, true)\n )`;\n const completedActivities = sql<number>`(\n select count(*) from progress_records pr\n where pr.org_id = ${orgId}\n and pr.org_user_id = ${orgUsers.id}\n and pr.target_type = 'activity'\n and pr.completed_at is not null\n and pr.target_id in ${publishedActivities}\n )`;\n const lastActivityAt = sql<Date | string | null>`(\n select max(pr.updated_at) from progress_records pr\n where pr.org_id = ${orgId}\n and pr.org_user_id = ${orgUsers.id}\n and (\n (pr.target_type = 'activity' and pr.target_id in ${publishedActivities})\n or (pr.target_type = 'course' and pr.target_id = ${courseId})\n )\n )`;\n const completedAt = sql<Date | string | null>`(\n select pr.completed_at from progress_records pr\n where pr.org_id = ${orgId}\n and pr.org_user_id = ${orgUsers.id}\n and pr.target_type = 'course'\n and pr.target_id = ${courseId}\n )`;\n\n const filters: SQL[] = [eq(orgUsers.orgId, orgId)];\n const q = query.search?.trim();\n if (q) {\n const like = `%${q}%`;\n filters.push(\n or(ilike(studentFirstName, like), ilike(studentLastName, like), ilike(users.email, like))!,\n );\n }\n const where = and(...filters);\n const learner = and(eq(orgUsers.orgId, orgId), eq(orgUsers.id, learnerGrant.orgUserId));\n\n const [[totals], [activityCount], rows] = await Promise.all([\n this.db\n .with(learnerGrant)\n .select({ total: sql<number>`count(*)` })\n .from(learnerGrant)\n .innerJoin(orgUsers, learner)\n .innerJoin(users, eq(users.id, orgUsers.userId))\n .where(where),\n this.db\n .select({ total: sql<number>`count(*)` })\n .from(sql`${publishedActivities} as published_activities`),\n this.db\n .with(learnerGrant)\n .select({\n ...studentProfileColumns,\n status: learnerGrant.status,\n source: learnerGrant.source,\n grantedAt: learnerGrant.grantedAt,\n expiresAt: learnerGrant.expiresAt,\n completedActivities,\n lastActivityAt,\n completedAt,\n })\n .from(learnerGrant)\n .innerJoin(orgUsers, learner)\n .innerJoin(users, eq(users.id, orgUsers.userId))\n .leftJoin(user, eq(user.id, users.externalId))\n .where(where)\n .orderBy(asc(studentFirstName), asc(studentLastName), asc(orgUsers.id))\n .limit(query.pageSize)\n .offset((query.page - 1) * query.pageSize),\n ]);\n\n const totalActivities = Number(activityCount?.total ?? 0);\n return {\n rows: rows.map((row) => {\n const done = Number(row.completedActivities);\n return {\n id: row.id,\n firstName: row.firstName,\n lastName: row.lastName,\n email: row.email,\n image: row.image ?? null,\n status: row.status,\n source: row.source,\n grantedAt: new Date(row.grantedAt).toISOString(),\n expiresAt: row.expiresAt ? new Date(row.expiresAt).toISOString() : null,\n totalActivities,\n completedActivities: done,\n progress: totalActivities > 0 ? Math.round(Math.min(done / totalActivities, 1) * 100) : 0,\n lastActivityAt: row.lastActivityAt ? new Date(row.lastActivityAt).toISOString() : null,\n completedAt: row.completedAt ? new Date(row.completedAt).toISOString() : null,\n };\n }),\n total: Number(totals?.total ?? 0),\n page: query.page,\n pageSize: query.pageSize,\n };\n }\n\n async listStudents(\n orgId: string,\n query: StudentsQuery,\n memberRoleKey: string,\n ): Promise<Page<StudentRecord>> {\n const filters: SQL[] = [eq(orgUsers.orgId, orgId), isStudent(memberRoleKey)];\n const q = query.search?.trim();\n if (q) {\n const like = `%${q}%`;\n filters.push(\n or(ilike(studentFirstName, like), ilike(studentLastName, like), ilike(users.email, like))!,\n );\n }\n const where = and(...filters);\n\n const [totals] = await this.db\n .select({ total: sql<number>`count(*)` })\n .from(orgUsers)\n .innerJoin(users, eq(users.id, orgUsers.userId))\n .where(where);\n\n const rows = await this.db\n .select({\n ...studentProfileColumns,\n status: orgUsers.status,\n createdAt: orgUsers.createdAt,\n entitlementCount: entitlementCountExpr,\n avgProgress: avgProgressExpr,\n lastActiveAt: lastActiveExpr,\n })\n .from(orgUsers)\n .leftJoin(\n entitlements,\n and(eq(entitlements.orgId, orgUsers.orgId), eq(entitlements.orgUserId, orgUsers.id)),\n )\n .innerJoin(users, eq(users.id, orgUsers.userId))\n .leftJoin(user, eq(user.id, users.externalId))\n .where(where)\n // Group by the full composite PK (orgId, id): grouping by id alone gives\n // Postgres no functional dependency for the other students columns.\n .groupBy(\n orgUsers.orgId,\n orgUsers.id,\n orgUsers.status,\n users.id,\n users.externalId,\n users.email,\n users.firstName,\n users.lastName,\n user.image,\n )\n .orderBy(...this.resolveOrder(query.sort))\n .limit(query.pageSize)\n .offset((query.page - 1) * query.pageSize);\n\n const items = await this.itemsOf(\n orgId,\n rows.map((row) => row.id),\n );\n return {\n rows: rows.map((row) => toStudent(row, items.get(row.id) ?? [])),\n total: Number(totals?.total ?? 0),\n page: query.page,\n pageSize: query.pageSize,\n };\n }\n\n async getStudent(orgId: string, id: string): Promise<StudentRecord | null> {\n const [row] = await this.db\n .select({\n ...studentProfileColumns,\n status: orgUsers.status,\n createdAt: orgUsers.createdAt,\n entitlementCount: entitlementCountExpr,\n avgProgress: avgProgressExpr,\n lastActiveAt: lastActiveExpr,\n })\n .from(orgUsers)\n .leftJoin(\n entitlements,\n and(eq(entitlements.orgId, orgUsers.orgId), eq(entitlements.orgUserId, orgUsers.id)),\n )\n .innerJoin(users, eq(users.id, orgUsers.userId))\n .leftJoin(user, eq(user.id, users.externalId))\n .where(and(eq(orgUsers.orgId, orgId), eq(orgUsers.id, id)))\n .groupBy(\n orgUsers.orgId,\n orgUsers.id,\n orgUsers.status,\n users.id,\n users.externalId,\n users.email,\n users.firstName,\n users.lastName,\n user.image,\n )\n .limit(1);\n if (!row) {\n return null;\n }\n const items = await this.itemsOf(orgId, [row.id]);\n return toStudent(row, items.get(row.id) ?? []);\n }\n\n private async itemsOf(orgId: string, orgUserIds: string[]): Promise<Map<string, StudentItem[]>> {\n const byPerson = new Map<string, StudentItem[]>();\n if (orgUserIds.length === 0) {\n return byPerson;\n }\n const rows = await this.db\n .select({\n orgUserId: roleAssignments.orgUserId,\n contentId: roleAssignments.contentId,\n contentType: roleAssignments.contentType,\n })\n .from(roleAssignments)\n .where(\n and(\n eq(roleAssignments.orgId, orgId),\n inArray(roleAssignments.orgUserId, orgUserIds),\n sql`${roleAssignments.contentId} is not null`,\n ),\n )\n .orderBy(asc(roleAssignments.createdAt));\n for (const row of rows) {\n if (row.contentId === null || row.contentType === null) {\n continue;\n }\n const items = byPerson.get(row.orgUserId) ?? [];\n items.push({ contentId: row.contentId, contentType: row.contentType });\n byPerson.set(row.orgUserId, items);\n }\n return byPerson;\n }\n\n // Learner record: one row per course the student holds an effective-active\n // entitlement to (mirroring the course-analytics cohort rule), with progress\n // computed over published activities the same way the course analytics read does.\n async studentAnalytics(orgId: string, id: string): Promise<StudentAnalytics | null> {\n const [exists] = await this.db\n .select({ id: orgUsers.id })\n .from(orgUsers)\n .where(and(eq(orgUsers.orgId, orgId), eq(orgUsers.id, id)));\n if (!exists) {\n return null;\n }\n\n const result = await this.db.execute(sql`\n with cohort_courses as (\n select distinct c.id, c.title\n from content_access ca\n join courses c on c.org_id = ca.org_id and c.id = ca.content_id\n where ca.org_id = ${orgId}\n and ca.org_user_id = ${id}\n ),\n acts as (\n select a.course_id, a.id\n from activities a\n where a.org_id = ${orgId}\n and a.course_id in (select id from cohort_courses)\n and coalesce((a.settings ->> 'published')::boolean, true)\n )\n select\n cc.id as course_id,\n cc.title,\n count(a.id)::int as total_activities,\n count(pr.id) filter (where pr.completed_at is not null)::int as completed_activities,\n min(pr.started_at) as started_at,\n max(pr.updated_at) as last_activity_at,\n (\n select prc.completed_at from progress_records prc\n where prc.org_id = ${orgId}\n and prc.org_user_id = ${id}\n and prc.target_type = 'course'\n and prc.target_id = cc.id\n ) as completed_at\n from cohort_courses cc\n left join acts a on a.course_id = cc.id\n left join progress_records pr\n on pr.org_id = ${orgId}\n and pr.org_user_id = ${id}\n and pr.target_type = 'activity'\n and pr.target_id = a.id\n group by cc.id, cc.title\n order by cc.title\n `);\n\n const courses: StudentCourseProgress[] = (\n result.rows as {\n course_id: string;\n title: string;\n total_activities: number;\n completed_activities: number;\n started_at: Date | string | null;\n last_activity_at: Date | string | null;\n completed_at: Date | string | null;\n }[]\n ).map((r) => {\n const total = Number(r.total_activities);\n const done = Number(r.completed_activities);\n return {\n courseId: r.course_id,\n title: r.title,\n totalActivities: total,\n completedActivities: done,\n progress: total > 0 ? Math.round(Math.min(done / total, 1) * 100) : 0,\n startedAt: r.started_at ? new Date(r.started_at).toISOString() : null,\n lastActivityAt: r.last_activity_at ? new Date(r.last_activity_at).toISOString() : null,\n completedAt: r.completed_at ? new Date(r.completed_at).toISOString() : null,\n };\n });\n\n const started = courses.filter((c) => c.startedAt !== null).length;\n const completed = courses.filter((c) => c.completedAt !== null).length;\n const avgProgress =\n courses.length > 0\n ? Math.round(courses.reduce((sum, c) => sum + c.progress, 0) / courses.length)\n : 0;\n\n return { enrolled: courses.length, started, completed, avgProgress, courses };\n }\n\n private resolveOrder(sort?: string): SQL[] {\n const descending = sort?.startsWith(\"-\") ?? false;\n const field = sort ? (descending ? sort.slice(1) : sort) : \"firstName\";\n const dir = descending ? desc : asc;\n switch (field) {\n case \"email\":\n return [dir(users.email)];\n case \"entitlementCount\":\n return [dir(entitlementCountExpr)];\n case \"avgProgress\":\n return [dir(avgProgressExpr)];\n case \"joinedAt\":\n return [dir(orgUsers.createdAt)];\n case \"lastActiveAt\":\n return [dir(lastActiveExpr)];\n case \"lastName\":\n return [dir(studentLastName), dir(studentFirstName)];\n case \"firstName\":\n default:\n return [dir(studentFirstName), dir(studentLastName)];\n }\n }\n\n async communityAnalytics(orgId: string, communityId: string): Promise<CommunityAnalytics | null> {\n const [community] = await this.db\n .select({ id: communities.id })\n .from(communities)\n .where(and(eq(communities.orgId, orgId), eq(communities.id, communityId)))\n .limit(1);\n if (!community) {\n return null;\n }\n\n const opens = grantOpens(entitlements, sql`${communityId}`, \"community\");\n const lapsed = and(\n eq(entitlements.orgId, orgId),\n eq(entitlements.status, \"active\"),\n grantExpired(entitlements.expiresAt),\n opens,\n );\n const recent = sql`now() - interval '30 days'`;\n\n const [\n [members],\n [lapsedGrants],\n authors,\n postsPerSpace,\n commentsPerPost,\n [sent],\n [postFlagRow],\n [commentFlagRow],\n [messageFlagRow],\n lapsedHolders,\n ] = await Promise.all([\n this.db\n .select({ count: sql<number>`cast(count(distinct ${contentAccess.orgUserId}) as int)` })\n .from(contentAccess)\n .where(and(eq(contentAccess.orgId, orgId), eq(contentAccess.contentId, communityId))),\n this.db\n .select({ count: sql<number>`cast(count(*) as int)` })\n .from(entitlements)\n .where(lapsed),\n union(\n this.db\n .select({ id: posts.orgUserId })\n .from(posts)\n .where(\n and(\n eq(posts.orgId, orgId),\n eq(posts.communityId, communityId),\n sql`${posts.createdAt} >= ${recent}`,\n ),\n ),\n this.db\n .select({ id: comments.orgUserId })\n .from(comments)\n .where(\n and(\n eq(comments.orgId, orgId),\n eq(comments.communityId, communityId),\n sql`${comments.createdAt} >= ${recent}`,\n ),\n ),\n this.db\n .select({ id: messages.orgUserId })\n .from(messages)\n .where(\n and(\n eq(messages.orgId, orgId),\n eq(messages.communityId, communityId),\n sql`${messages.createdAt} >= ${recent}`,\n ),\n ),\n ),\n this.db\n .select({\n spaceId: spaces.id,\n count: sql<number>`cast(count(${posts.id}) as int)`,\n })\n .from(spaces)\n .leftJoin(\n posts,\n and(\n eq(posts.orgId, spaces.orgId),\n eq(posts.spaceId, spaces.id),\n eq(posts.status, \"published\"),\n ),\n )\n .where(and(eq(spaces.orgId, orgId), eq(spaces.communityId, communityId)))\n .groupBy(spaces.id),\n this.db\n .select({\n postId: posts.id,\n count: sql<number>`cast(count(${comments.id}) as int)`,\n })\n .from(posts)\n .leftJoin(\n comments,\n and(\n eq(comments.orgId, posts.orgId),\n eq(comments.postId, posts.id),\n eq(comments.status, \"published\"),\n ),\n )\n .where(\n and(\n eq(posts.orgId, orgId),\n eq(posts.communityId, communityId),\n eq(posts.status, \"published\"),\n ),\n )\n .groupBy(posts.id),\n this.db\n .select({ count: sql<number>`cast(count(*) as int)` })\n .from(messages)\n .where(and(eq(messages.orgId, orgId), eq(messages.communityId, communityId))),\n this.db\n .select({\n raised: sql<number>`cast(count(*) as int)`,\n resolved: sql<number>`cast(count(*) filter (where ${postFlags.resolvedAt} is not null) as int)`,\n })\n .from(postFlags)\n .innerJoin(posts, and(eq(posts.orgId, postFlags.orgId), eq(posts.id, postFlags.postId)))\n .where(and(eq(postFlags.orgId, orgId), eq(posts.communityId, communityId))),\n this.db\n .select({\n raised: sql<number>`cast(count(*) as int)`,\n resolved: sql<number>`cast(count(*) filter (where ${commentFlags.resolvedAt} is not null) as int)`,\n })\n .from(commentFlags)\n .innerJoin(\n comments,\n and(eq(comments.orgId, commentFlags.orgId), eq(comments.id, commentFlags.commentId)),\n )\n .where(and(eq(commentFlags.orgId, orgId), eq(comments.communityId, communityId))),\n this.db\n .select({\n raised: sql<number>`cast(count(*) as int)`,\n resolved: sql<number>`cast(count(*) filter (where ${messageFlags.resolvedAt} is not null) as int)`,\n })\n .from(messageFlags)\n .where(and(eq(messageFlags.orgId, orgId), eq(messageFlags.communityId, communityId))),\n this.db\n .select({ orgUserId: entitlements.orgUserId })\n .from(entitlements)\n .where(lapsed)\n .groupBy(entitlements.orgUserId),\n ]);\n\n const holderIds = lapsedHolders.map((row) => row.orgUserId);\n const [renewed] =\n holderIds.length === 0\n ? [{ count: 0 }]\n : await this.db\n .select({ count: sql<number>`cast(count(distinct ${contentAccess.orgUserId}) as int)` })\n .from(contentAccess)\n .where(\n and(\n eq(contentAccess.orgId, orgId),\n eq(contentAccess.contentId, communityId),\n inArray(contentAccess.orgUserId, holderIds),\n ),\n );\n\n const raised =\n Number(postFlagRow?.raised ?? 0) +\n Number(commentFlagRow?.raised ?? 0) +\n Number(messageFlagRow?.raised ?? 0);\n const resolved =\n Number(postFlagRow?.resolved ?? 0) +\n Number(commentFlagRow?.resolved ?? 0) +\n Number(messageFlagRow?.resolved ?? 0);\n\n return {\n memberCount: Number(members?.count ?? 0),\n activeMembers: authors.length,\n postsPerSpace: postsPerSpace.map((row) => ({\n spaceId: row.spaceId,\n count: Number(row.count),\n })),\n commentsPerPost: commentsPerPost.map((row) => ({\n postId: row.postId,\n count: Number(row.count),\n })),\n messagesSent: Number(sent?.count ?? 0),\n flagsRaised: raised,\n flagsResolved: resolved,\n grantsLapsed: Number(lapsedGrants?.count ?? 0),\n grantsRenewed: Number(renewed?.count ?? 0),\n };\n }\n\n async communityMembers(\n orgId: string,\n communityId: string,\n query: CommunityMembersQuery,\n ): Promise<Page<OrgUserProfile> | null> {\n const [community] = await this.db\n .select({ id: communities.id })\n .from(communities)\n .where(and(eq(communities.orgId, orgId), eq(communities.id, communityId)))\n .limit(1);\n if (!community) {\n return null;\n }\n const holds = exists(\n this.db\n .select({ id: contentAccess.entitlementId })\n .from(contentAccess)\n .where(\n and(\n eq(contentAccess.orgId, orgUsers.orgId),\n eq(contentAccess.orgUserId, orgUsers.id),\n eq(contentAccess.contentId, communityId),\n ),\n ),\n );\n const filters: SQL[] = [eq(orgUsers.orgId, orgId), holds];\n const q = query.search?.trim();\n if (q) {\n const like = `%${q}%`;\n filters.push(\n or(ilike(studentFirstName, like), ilike(studentLastName, like), ilike(users.email, like))!,\n );\n }\n const where = and(...filters);\n const [totals] = await this.db\n .select({ total: sql<number>`count(*)` })\n .from(orgUsers)\n .innerJoin(users, eq(users.id, orgUsers.userId))\n .where(where);\n const rows = await this.db\n .select(orgUserProfileColumns)\n .from(orgUsers)\n .innerJoin(users, eq(users.id, orgUsers.userId))\n .leftJoin(user, eq(user.id, users.externalId))\n .where(where)\n .orderBy(asc(users.firstName), asc(users.lastName), asc(orgUsers.id))\n .limit(query.pageSize)\n .offset((query.page - 1) * query.pageSize);\n return {\n rows: rows.map((row) => ({ ...row, image: row.image ?? null })),\n total: Number(totals?.total ?? 0),\n page: query.page,\n pageSize: query.pageSize,\n };\n }\n\n eventAttendees(\n orgId: string,\n eventId: string,\n page: { page: number; pageSize: number },\n ): Promise<Page<EventAttendeeRow>> {\n return eventAttendeePage(this.db, orgId, eventId, page);\n }\n\n eventAnalytics(\n orgId: string,\n eventId: string,\n startsAt: Date,\n ): Promise<Omit<EventAnalytics, \"capacity\">> {\n return eventAnalyticsFigures(this.db, orgId, eventId, startsAt);\n }\n}\ntranslateDbErrors(DrizzleAdminRepository);\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"admin.js","names":[],"sources":["../../src/repositories/admin.ts"],"sourcesContent":["// reporting/admin — Drizzle repository.\nimport { and, asc, desc, eq, exists, ilike, inArray, or, sql, type SQL } from \"drizzle-orm\";\nimport { union } from \"drizzle-orm/pg-core\";\nimport type { NodePgDatabase } from \"drizzle-orm/node-postgres\";\nimport type {\n AdminReportRepository,\n CommunityAnalytics,\n CommunityMembersQuery,\n CourseActivityEngagement,\n CourseAnalytics,\n CourseEnrollmentPoint,\n CourseLearner,\n CourseLearnersQuery,\n EnrollmentPoint,\n EventAnalytics,\n EventAttendeeRow,\n LearnerGrantStatus,\n OverviewStats,\n Page,\n StudentAnalytics,\n StudentCourseProgress,\n StudentItem,\n StudentRow as StudentRecord,\n StudentsQuery,\n} from \"@headless-lms/core/reporting/admin\";\nimport {\n commentFlags,\n comments,\n communities,\n contentAccess,\n courses,\n entitlements,\n invites,\n messageFlags,\n messages,\n orgUsers,\n roleAssignments,\n postFlags,\n posts,\n spaces,\n users,\n} from \"../schema/index.js\";\nimport { user } from \"../schema/better-auth.js\";\nimport type { OrgUserProfile, OrgUserStatus } from \"@headless-lms/core/types\";\nimport type { Logger } from \"@headless-lms/core/shared/ports\";\nimport { noopLogger } from \"@headless-lms/core/shared/logger\";\nimport {\n orgUserProfileColumns,\n studentFirstName,\n studentLastName,\n studentProfileColumns,\n} from \"./org-user-profile.js\";\nimport { translateDbErrors } from \"./pg-errors.js\";\nimport { grantExpired, grantOpens } from \"./grant-covers.js\";\nimport { eventAnalyticsFigures, eventAttendeePage, upcomingEventCount } from \"./event-reports.js\";\n\nconst entitlementCountExpr = sql<number>`count(${entitlements.id})`;\nfunction hasNoOrgRole(): SQL {\n return sql`not exists (\n select 1 from ${roleAssignments} ra\n where ra.org_id = ${orgUsers.orgId} and ra.org_user_id = ${orgUsers.id} and ra.content_id is null\n )`;\n}\n\nfunction invitedAs(roleKey: string): SQL {\n return sql`not exists (\n select 1 from ${invites} i\n where i.org_id = ${orgUsers.orgId} and lower(i.email) = lower(${users.email})\n and i.status = 'pending' and i.role <> ${roleKey}\n )`;\n}\n\nfunction isStudent(memberRoleKey: string): SQL {\n return and(\n hasNoOrgRole(),\n or(\n eq(orgUsers.status, \"active\"),\n and(eq(orgUsers.status, \"invited\"), invitedAs(memberRoleKey)),\n ),\n )!;\n}\n\n// The mean, over the courses the student can open, of the share of each\n// course's published activities they completed: the same figure as\n// studentAnalytics.avgProgress.\nconst avgProgressExpr = sql<number>`coalesce((\n select round(avg(per_course.progress))\n from (\n select case\n when count(a.id) = 0 then 0\n else round(\n least(count(pr.id) filter (where pr.completed_at is not null)::numeric / count(a.id), 1) * 100\n )\n end as progress\n from (\n select distinct ca.content_id\n from content_access ca\n join courses c on c.org_id = ca.org_id and c.id = ca.content_id\n where ca.org_id = ${orgUsers.orgId} and ca.org_user_id = ${orgUsers.id}\n ) held\n left join activities a\n on a.org_id = ${orgUsers.orgId}\n and a.course_id = held.content_id\n and coalesce((a.settings ->> 'published')::boolean, true)\n left join progress_records pr\n on pr.org_id = ${orgUsers.orgId}\n and pr.org_user_id = ${orgUsers.id}\n and pr.target_type = 'activity'\n and pr.target_id = a.id\n group by held.content_id\n ) per_course\n), 0)::int`;\n\n// Last learning activity: the newest touch on any of the student's progress\n// records. A scalar subquery so it neither fans out the entitlement count nor\n// widens the GROUP BY.\nconst lastActiveExpr = sql<Date | null>`(\n select max(pr.updated_at) from progress_records pr\n where pr.org_id = ${orgUsers.orgId} and pr.org_user_id = ${orgUsers.id}\n)`;\n\ninterface StudentRow {\n id: string;\n email: string;\n image: string | null;\n firstName: string | null;\n lastName: string | null;\n status: OrgUserStatus;\n createdAt: Date;\n entitlementCount: number;\n avgProgress: number;\n lastActiveAt: Date | null;\n}\n\nfunction toStudent(row: StudentRow, items: StudentItem[]): StudentRecord {\n return {\n items,\n id: row.id,\n email: row.email,\n image: row.image ?? null,\n firstName: row.firstName,\n lastName: row.lastName,\n status: row.status,\n entitlementCount: Number(row.entitlementCount),\n avgProgress: Number(row.avgProgress),\n joinedAt: row.createdAt.toISOString(),\n lastActiveAt: row.lastActiveAt ? new Date(row.lastActiveAt).toISOString() : null,\n };\n}\n\nexport class DrizzleAdminRepository implements AdminReportRepository {\n constructor(\n private readonly db: NodePgDatabase,\n private readonly logger: Logger = noopLogger,\n ) {}\n\n async overview(orgId: string): Promise<OverviewStats> {\n const [courseCounts] = await this.db\n .select({\n published: sql<number>`count(*) filter (where ${courses.status} = 'published')`,\n draft: sql<number>`count(*) filter (where ${courses.status} = 'draft')`,\n })\n .from(courses)\n .where(eq(courses.orgId, orgId));\n\n const [entitlementCounts] = await this.db\n .select({\n activeStudents: sql<number>`count(distinct ${contentAccess.orgUserId})`,\n expiringSoon: sql<number>`count(distinct ${contentAccess.entitlementId}) filter (where ${grantExpired(contentAccess.expiresAt, sql`now() + interval '14 days'`)})`,\n })\n .from(contentAccess)\n .where(eq(contentAccess.orgId, orgId));\n\n return {\n publishedCourses: Number(courseCounts?.published ?? 0),\n draftCourses: Number(courseCounts?.draft ?? 0),\n activeStudents: Number(entitlementCounts?.activeStudents ?? 0),\n expiringSoon: Number(entitlementCounts?.expiringSoon ?? 0),\n upcomingEvents: await upcomingEventCount(this.db, orgId),\n };\n }\n\n // Grants per day over the trailing window, zero-filled so the series has one\n // point per calendar day (UTC) even when nothing was granted.\n async enrollments(orgId: string, days: number): Promise<EnrollmentPoint[]> {\n const result = await this.db.execute(sql`\n select\n to_char(d.day, 'YYYY-MM-DD') as date,\n count(${entitlements.id})::int as count\n from generate_series(\n (now() at time zone 'utc')::date - (${days} - 1),\n (now() at time zone 'utc')::date,\n interval '1 day'\n ) as d(day)\n left join ${entitlements}\n on ${entitlements.orgId} = ${orgId}\n and (${entitlements.grantedAt} at time zone 'utc')::date = d.day::date\n group by d.day\n order by d.day\n `);\n const rows = result.rows as { date: string; count: number }[];\n return rows.map((r) => ({ date: r.date, count: Number(r.count) }));\n }\n\n private async courseExists(orgId: string, courseId: string): Promise<boolean> {\n const [row] = await this.db\n .select({ id: courses.id })\n .from(courses)\n .where(and(eq(courses.orgId, orgId), eq(courses.id, courseId)));\n return row !== undefined;\n }\n\n async courseAnalytics(orgId: string, courseId: string): Promise<CourseAnalytics | null> {\n if (!(await this.courseExists(orgId, courseId))) {\n return null;\n }\n\n const cohort = sql`\n select distinct org_user_id from content_access\n where org_id = ${orgId}\n and content_id = ${courseId}\n `;\n const publishedActivities = sql`\n select a.id, a.settings, a.seq, a.module_id\n from activities a\n where a.org_id = ${orgId}\n and a.course_id = ${courseId}\n and coalesce((a.settings ->> 'published')::boolean, true)\n `;\n\n const summaryResult = await this.db.execute(sql`\n with cohort as (${cohort}),\n acts as (${publishedActivities}),\n per_learner as (\n select c.org_user_id,\n count(pr.id) filter (where pr.completed_at is not null) as done\n from cohort c\n left join progress_records pr\n on pr.org_id = ${orgId}\n and pr.org_user_id = c.org_user_id\n and pr.target_type = 'activity'\n and pr.target_id in (select id from acts)\n group by c.org_user_id\n )\n select\n (select count(*) from cohort)::int as enrolled,\n (\n select count(distinct pr.org_user_id) from progress_records pr\n where pr.org_id = ${orgId}\n and pr.org_user_id in (select org_user_id from cohort)\n and (\n (pr.target_type = 'activity' and pr.target_id in (select id from acts))\n or (pr.target_type = 'course' and pr.target_id = ${courseId})\n )\n )::int as started,\n (\n select count(*) from progress_records pr\n where pr.org_id = ${orgId}\n and pr.org_user_id in (select org_user_id from cohort)\n and pr.target_type = 'course'\n and pr.target_id = ${courseId}\n and pr.completed_at is not null\n )::int as completed,\n coalesce((\n select round(avg(least(done::numeric / nullif((select count(*) from acts), 0), 1) * 100))\n from per_learner\n ), 0)::int as avg_progress\n `);\n const summary = summaryResult.rows[0] as {\n enrolled: number;\n started: number;\n completed: number;\n avg_progress: number;\n };\n\n const funnelResult = await this.db.execute(sql`\n with cohort as (${cohort})\n select\n a.id,\n coalesce(a.settings ->> 'title', '') as title,\n m.id as module_id,\n m.title as module_title,\n count(pr.id)::int as started,\n count(pr.id) filter (where pr.completed_at is not null)::int as completed\n from activities a\n join modules m on m.org_id = a.org_id and m.id = a.module_id\n left join progress_records pr\n on pr.org_id = ${orgId}\n and pr.target_type = 'activity'\n and pr.target_id = a.id\n and pr.org_user_id in (select org_user_id from cohort)\n where a.org_id = ${orgId}\n and a.course_id = ${courseId}\n and coalesce((a.settings ->> 'published')::boolean, true)\n group by a.id, a.settings, m.id, m.title, m.seq, a.seq\n order by m.seq, a.seq\n `);\n const activities: CourseActivityEngagement[] = (\n funnelResult.rows as {\n id: string;\n title: string;\n module_id: string;\n module_title: string;\n started: number;\n completed: number;\n }[]\n ).map((r) => ({\n activityId: r.id,\n title: r.title,\n moduleId: r.module_id,\n moduleTitle: r.module_title,\n started: Number(r.started),\n completed: Number(r.completed),\n }));\n\n return {\n enrolled: Number(summary?.enrolled ?? 0),\n started: Number(summary?.started ?? 0),\n completed: Number(summary?.completed ?? 0),\n avgProgress: Number(summary?.avg_progress ?? 0),\n activities,\n };\n }\n\n // Grants per day for this course over the trailing window, zero-filled so the\n // series has one point per calendar day (UTC) even when nothing was granted.\n async courseEnrollments(\n orgId: string,\n courseId: string,\n days: number,\n ): Promise<CourseEnrollmentPoint[] | null> {\n if (!(await this.courseExists(orgId, courseId))) {\n return null;\n }\n const result = await this.db.execute(sql`\n select\n to_char(d.day, 'YYYY-MM-DD') as date,\n count(${entitlements.id})::int as count\n from generate_series(\n (now() at time zone 'utc')::date - (${days} - 1),\n (now() at time zone 'utc')::date,\n interval '1 day'\n ) as d(day)\n left join ${entitlements}\n on ${entitlements.orgId} = ${orgId}\n and ${entitlements.contentId} = ${courseId}\n and (${entitlements.grantedAt} at time zone 'utc')::date = d.day::date\n group by d.day\n order by d.day\n `);\n const rows = result.rows as { date: string; count: number }[];\n return rows.map((r) => ({ date: r.date, count: Number(r.count) }));\n }\n\n async courseLearners(\n orgId: string,\n courseId: string,\n query: CourseLearnersQuery,\n ): Promise<Page<CourseLearner> | null> {\n if (!(await this.courseExists(orgId, courseId))) {\n return null;\n }\n const expired = grantExpired(entitlements.expiresAt);\n const grantStatus = sql<LearnerGrantStatus>`case\n when ${entitlements.status} = 'revoked' then 'revoked'\n when ${expired} then 'expired'\n else 'active'\n end`;\n const grantRank = sql`case\n when ${entitlements.status} = 'revoked' then 2\n when ${expired} then 1\n else 0\n end`;\n const learnerGrant = this.db.$with(\"learner_grant\").as(\n this.db\n .selectDistinctOn([entitlements.orgUserId], {\n orgUserId: entitlements.orgUserId,\n status: grantStatus.as(\"grant_status\"),\n source: entitlements.source,\n grantedAt: entitlements.grantedAt,\n expiresAt: entitlements.expiresAt,\n })\n .from(entitlements)\n .where(\n and(eq(entitlements.orgId, orgId), grantOpens(entitlements, sql`${courseId}`, \"course\")),\n )\n .orderBy(\n entitlements.orgUserId,\n grantRank,\n sql`${entitlements.expiresAt} desc nulls first`,\n desc(entitlements.grantedAt),\n ),\n );\n const publishedActivities = sql`(\n select a.id from activities a\n where a.org_id = ${orgId}\n and a.course_id = ${courseId}\n and coalesce((a.settings ->> 'published')::boolean, true)\n )`;\n const completedActivities = sql<number>`(\n select count(*) from progress_records pr\n where pr.org_id = ${orgId}\n and pr.org_user_id = ${orgUsers.id}\n and pr.target_type = 'activity'\n and pr.completed_at is not null\n and pr.target_id in ${publishedActivities}\n )`;\n const lastActivityAt = sql<Date | string | null>`(\n select max(pr.updated_at) from progress_records pr\n where pr.org_id = ${orgId}\n and pr.org_user_id = ${orgUsers.id}\n and (\n (pr.target_type = 'activity' and pr.target_id in ${publishedActivities})\n or (pr.target_type = 'course' and pr.target_id = ${courseId})\n )\n )`;\n const completedAt = sql<Date | string | null>`(\n select pr.completed_at from progress_records pr\n where pr.org_id = ${orgId}\n and pr.org_user_id = ${orgUsers.id}\n and pr.target_type = 'course'\n and pr.target_id = ${courseId}\n )`;\n\n const filters: SQL[] = [eq(orgUsers.orgId, orgId)];\n const q = query.search?.trim();\n if (q) {\n const like = `%${q}%`;\n filters.push(\n or(ilike(studentFirstName, like), ilike(studentLastName, like), ilike(users.email, like))!,\n );\n }\n const where = and(...filters);\n const learner = and(eq(orgUsers.orgId, orgId), eq(orgUsers.id, learnerGrant.orgUserId));\n\n const [[totals], [activityCount], rows] = await Promise.all([\n this.db\n .with(learnerGrant)\n .select({ total: sql<number>`count(*)` })\n .from(learnerGrant)\n .innerJoin(orgUsers, learner)\n .innerJoin(users, eq(users.id, orgUsers.userId))\n .where(where),\n this.db\n .select({ total: sql<number>`count(*)` })\n .from(sql`${publishedActivities} as published_activities`),\n this.db\n .with(learnerGrant)\n .select({\n ...studentProfileColumns,\n status: learnerGrant.status,\n source: learnerGrant.source,\n grantedAt: learnerGrant.grantedAt,\n expiresAt: learnerGrant.expiresAt,\n completedActivities,\n lastActivityAt,\n completedAt,\n })\n .from(learnerGrant)\n .innerJoin(orgUsers, learner)\n .innerJoin(users, eq(users.id, orgUsers.userId))\n .leftJoin(user, eq(user.id, users.externalId))\n .where(where)\n .orderBy(asc(studentFirstName), asc(studentLastName), asc(orgUsers.id))\n .limit(query.pageSize)\n .offset((query.page - 1) * query.pageSize),\n ]);\n\n const totalActivities = Number(activityCount?.total ?? 0);\n return {\n rows: rows.map((row) => {\n const done = Number(row.completedActivities);\n return {\n id: row.id,\n firstName: row.firstName,\n lastName: row.lastName,\n email: row.email,\n image: row.image ?? null,\n status: row.status,\n source: row.source,\n grantedAt: new Date(row.grantedAt).toISOString(),\n expiresAt: row.expiresAt ? new Date(row.expiresAt).toISOString() : null,\n totalActivities,\n completedActivities: done,\n progress: totalActivities > 0 ? Math.round(Math.min(done / totalActivities, 1) * 100) : 0,\n lastActivityAt: row.lastActivityAt ? new Date(row.lastActivityAt).toISOString() : null,\n completedAt: row.completedAt ? new Date(row.completedAt).toISOString() : null,\n };\n }),\n total: Number(totals?.total ?? 0),\n page: query.page,\n pageSize: query.pageSize,\n };\n }\n\n async listStudents(\n orgId: string,\n query: StudentsQuery,\n memberRoleKey: string,\n ): Promise<Page<StudentRecord>> {\n const filters: SQL[] = [eq(orgUsers.orgId, orgId), isStudent(memberRoleKey)];\n const q = query.search?.trim();\n if (q) {\n const like = `%${q}%`;\n filters.push(\n or(ilike(studentFirstName, like), ilike(studentLastName, like), ilike(users.email, like))!,\n );\n }\n const where = and(...filters);\n\n const [totals] = await this.db\n .select({ total: sql<number>`count(*)` })\n .from(orgUsers)\n .innerJoin(users, eq(users.id, orgUsers.userId))\n .where(where);\n\n const rows = await this.db\n .select({\n ...studentProfileColumns,\n status: orgUsers.status,\n createdAt: orgUsers.createdAt,\n entitlementCount: entitlementCountExpr,\n avgProgress: avgProgressExpr,\n lastActiveAt: lastActiveExpr,\n })\n .from(orgUsers)\n .leftJoin(\n entitlements,\n and(eq(entitlements.orgId, orgUsers.orgId), eq(entitlements.orgUserId, orgUsers.id)),\n )\n .innerJoin(users, eq(users.id, orgUsers.userId))\n .leftJoin(user, eq(user.id, users.externalId))\n .where(where)\n // Group by the full composite PK (orgId, id): grouping by id alone gives\n // Postgres no functional dependency for the other students columns.\n .groupBy(\n orgUsers.orgId,\n orgUsers.id,\n orgUsers.status,\n users.id,\n users.externalId,\n users.email,\n users.firstName,\n users.lastName,\n user.image,\n )\n .orderBy(...this.resolveOrder(query.sort))\n .limit(query.pageSize)\n .offset((query.page - 1) * query.pageSize);\n\n const items = await this.itemsOf(\n orgId,\n rows.map((row) => row.id),\n );\n return {\n rows: rows.map((row) => toStudent(row, items.get(row.id) ?? [])),\n total: Number(totals?.total ?? 0),\n page: query.page,\n pageSize: query.pageSize,\n };\n }\n\n async getStudent(orgId: string, id: string): Promise<StudentRecord | null> {\n const [row] = await this.db\n .select({\n ...studentProfileColumns,\n status: orgUsers.status,\n createdAt: orgUsers.createdAt,\n entitlementCount: entitlementCountExpr,\n avgProgress: avgProgressExpr,\n lastActiveAt: lastActiveExpr,\n })\n .from(orgUsers)\n .leftJoin(\n entitlements,\n and(eq(entitlements.orgId, orgUsers.orgId), eq(entitlements.orgUserId, orgUsers.id)),\n )\n .innerJoin(users, eq(users.id, orgUsers.userId))\n .leftJoin(user, eq(user.id, users.externalId))\n .where(and(eq(orgUsers.orgId, orgId), eq(orgUsers.id, id)))\n .groupBy(\n orgUsers.orgId,\n orgUsers.id,\n orgUsers.status,\n users.id,\n users.externalId,\n users.email,\n users.firstName,\n users.lastName,\n user.image,\n )\n .limit(1);\n if (!row) {\n return null;\n }\n const items = await this.itemsOf(orgId, [row.id]);\n return toStudent(row, items.get(row.id) ?? []);\n }\n\n private async itemsOf(orgId: string, orgUserIds: string[]): Promise<Map<string, StudentItem[]>> {\n const byPerson = new Map<string, StudentItem[]>();\n if (orgUserIds.length === 0) {\n return byPerson;\n }\n const rows = await this.db\n .select({\n orgUserId: roleAssignments.orgUserId,\n contentId: roleAssignments.contentId,\n contentType: roleAssignments.contentType,\n })\n .from(roleAssignments)\n .where(\n and(\n eq(roleAssignments.orgId, orgId),\n inArray(roleAssignments.orgUserId, orgUserIds),\n sql`${roleAssignments.contentId} is not null`,\n ),\n )\n .orderBy(asc(roleAssignments.createdAt));\n for (const row of rows) {\n if (row.contentId === null || row.contentType === null) {\n continue;\n }\n const items = byPerson.get(row.orgUserId) ?? [];\n items.push({ contentId: row.contentId, contentType: row.contentType });\n byPerson.set(row.orgUserId, items);\n }\n return byPerson;\n }\n\n // Learner record: one row per course the student holds an effective-active\n // entitlement to (mirroring the course-analytics cohort rule), with progress\n // computed over published activities the same way the course analytics read does.\n async studentAnalytics(orgId: string, id: string): Promise<StudentAnalytics | null> {\n const [exists] = await this.db\n .select({ id: orgUsers.id })\n .from(orgUsers)\n .where(and(eq(orgUsers.orgId, orgId), eq(orgUsers.id, id)));\n if (!exists) {\n return null;\n }\n\n const result = await this.db.execute(sql`\n with cohort_courses as (\n select distinct c.id, c.title\n from content_access ca\n join courses c on c.org_id = ca.org_id and c.id = ca.content_id\n where ca.org_id = ${orgId}\n and ca.org_user_id = ${id}\n ),\n acts as (\n select a.course_id, a.id\n from activities a\n where a.org_id = ${orgId}\n and a.course_id in (select id from cohort_courses)\n and coalesce((a.settings ->> 'published')::boolean, true)\n )\n select\n cc.id as course_id,\n cc.title,\n count(a.id)::int as total_activities,\n count(pr.id) filter (where pr.completed_at is not null)::int as completed_activities,\n min(pr.started_at) as started_at,\n max(pr.updated_at) as last_activity_at,\n (\n select prc.completed_at from progress_records prc\n where prc.org_id = ${orgId}\n and prc.org_user_id = ${id}\n and prc.target_type = 'course'\n and prc.target_id = cc.id\n ) as completed_at\n from cohort_courses cc\n left join acts a on a.course_id = cc.id\n left join progress_records pr\n on pr.org_id = ${orgId}\n and pr.org_user_id = ${id}\n and pr.target_type = 'activity'\n and pr.target_id = a.id\n group by cc.id, cc.title\n order by cc.title\n `);\n\n const courses: StudentCourseProgress[] = (\n result.rows as {\n course_id: string;\n title: string;\n total_activities: number;\n completed_activities: number;\n started_at: Date | string | null;\n last_activity_at: Date | string | null;\n completed_at: Date | string | null;\n }[]\n ).map((r) => {\n const total = Number(r.total_activities);\n const done = Number(r.completed_activities);\n return {\n courseId: r.course_id,\n title: r.title,\n totalActivities: total,\n completedActivities: done,\n progress: total > 0 ? Math.round(Math.min(done / total, 1) * 100) : 0,\n startedAt: r.started_at ? new Date(r.started_at).toISOString() : null,\n lastActivityAt: r.last_activity_at ? new Date(r.last_activity_at).toISOString() : null,\n completedAt: r.completed_at ? new Date(r.completed_at).toISOString() : null,\n };\n });\n\n const started = courses.filter((c) => c.startedAt !== null).length;\n const completed = courses.filter((c) => c.completedAt !== null).length;\n const avgProgress =\n courses.length > 0\n ? Math.round(courses.reduce((sum, c) => sum + c.progress, 0) / courses.length)\n : 0;\n\n return { enrolled: courses.length, started, completed, avgProgress, courses };\n }\n\n private resolveOrder(sort?: string): SQL[] {\n const descending = sort?.startsWith(\"-\") ?? false;\n const field = sort ? (descending ? sort.slice(1) : sort) : \"firstName\";\n const dir = descending ? desc : asc;\n const nullsLast = (expr: SQL): SQL =>\n descending ? sql`${expr} desc nulls last` : sql`${expr} asc nulls last`;\n switch (field) {\n case \"email\":\n return [dir(users.email)];\n case \"entitlementCount\":\n return [dir(entitlementCountExpr)];\n case \"avgProgress\":\n return [dir(avgProgressExpr)];\n case \"joinedAt\":\n return [dir(orgUsers.createdAt)];\n case \"lastActiveAt\":\n return [nullsLast(lastActiveExpr)];\n case \"lastName\":\n return [dir(studentLastName), dir(studentFirstName)];\n case \"firstName\":\n default:\n return [dir(studentFirstName), dir(studentLastName)];\n }\n }\n\n async communityAnalytics(orgId: string, communityId: string): Promise<CommunityAnalytics | null> {\n const [community] = await this.db\n .select({ id: communities.id })\n .from(communities)\n .where(and(eq(communities.orgId, orgId), eq(communities.id, communityId)))\n .limit(1);\n if (!community) {\n return null;\n }\n\n const opens = grantOpens(entitlements, sql`${communityId}`, \"community\");\n const lapsed = and(\n eq(entitlements.orgId, orgId),\n eq(entitlements.status, \"active\"),\n grantExpired(entitlements.expiresAt),\n opens,\n );\n const recent = sql`now() - interval '30 days'`;\n\n const [\n [members],\n [lapsedGrants],\n authors,\n postsPerSpace,\n commentsPerPost,\n [sent],\n [postFlagRow],\n [commentFlagRow],\n [messageFlagRow],\n lapsedHolders,\n ] = await Promise.all([\n this.db\n .select({ count: sql<number>`cast(count(distinct ${contentAccess.orgUserId}) as int)` })\n .from(contentAccess)\n .where(and(eq(contentAccess.orgId, orgId), eq(contentAccess.contentId, communityId))),\n this.db\n .select({ count: sql<number>`cast(count(*) as int)` })\n .from(entitlements)\n .where(lapsed),\n union(\n this.db\n .select({ id: posts.orgUserId })\n .from(posts)\n .where(\n and(\n eq(posts.orgId, orgId),\n eq(posts.communityId, communityId),\n sql`${posts.createdAt} >= ${recent}`,\n ),\n ),\n this.db\n .select({ id: comments.orgUserId })\n .from(comments)\n .where(\n and(\n eq(comments.orgId, orgId),\n eq(comments.communityId, communityId),\n sql`${comments.createdAt} >= ${recent}`,\n ),\n ),\n this.db\n .select({ id: messages.orgUserId })\n .from(messages)\n .where(\n and(\n eq(messages.orgId, orgId),\n eq(messages.communityId, communityId),\n sql`${messages.createdAt} >= ${recent}`,\n ),\n ),\n ),\n this.db\n .select({\n spaceId: spaces.id,\n count: sql<number>`cast(count(${posts.id}) as int)`,\n })\n .from(spaces)\n .leftJoin(\n posts,\n and(\n eq(posts.orgId, spaces.orgId),\n eq(posts.spaceId, spaces.id),\n eq(posts.status, \"published\"),\n ),\n )\n .where(and(eq(spaces.orgId, orgId), eq(spaces.communityId, communityId)))\n .groupBy(spaces.id),\n this.db\n .select({\n postId: posts.id,\n count: sql<number>`cast(count(${comments.id}) as int)`,\n })\n .from(posts)\n .leftJoin(\n comments,\n and(\n eq(comments.orgId, posts.orgId),\n eq(comments.postId, posts.id),\n eq(comments.status, \"published\"),\n ),\n )\n .where(\n and(\n eq(posts.orgId, orgId),\n eq(posts.communityId, communityId),\n eq(posts.status, \"published\"),\n ),\n )\n .groupBy(posts.id),\n this.db\n .select({ count: sql<number>`cast(count(*) as int)` })\n .from(messages)\n .where(and(eq(messages.orgId, orgId), eq(messages.communityId, communityId))),\n this.db\n .select({\n raised: sql<number>`cast(count(*) as int)`,\n resolved: sql<number>`cast(count(*) filter (where ${postFlags.resolvedAt} is not null) as int)`,\n })\n .from(postFlags)\n .innerJoin(posts, and(eq(posts.orgId, postFlags.orgId), eq(posts.id, postFlags.postId)))\n .where(and(eq(postFlags.orgId, orgId), eq(posts.communityId, communityId))),\n this.db\n .select({\n raised: sql<number>`cast(count(*) as int)`,\n resolved: sql<number>`cast(count(*) filter (where ${commentFlags.resolvedAt} is not null) as int)`,\n })\n .from(commentFlags)\n .innerJoin(\n comments,\n and(eq(comments.orgId, commentFlags.orgId), eq(comments.id, commentFlags.commentId)),\n )\n .where(and(eq(commentFlags.orgId, orgId), eq(comments.communityId, communityId))),\n this.db\n .select({\n raised: sql<number>`cast(count(*) as int)`,\n resolved: sql<number>`cast(count(*) filter (where ${messageFlags.resolvedAt} is not null) as int)`,\n })\n .from(messageFlags)\n .where(and(eq(messageFlags.orgId, orgId), eq(messageFlags.communityId, communityId))),\n this.db\n .select({ orgUserId: entitlements.orgUserId })\n .from(entitlements)\n .where(lapsed)\n .groupBy(entitlements.orgUserId),\n ]);\n\n const holderIds = lapsedHolders.map((row) => row.orgUserId);\n const [renewed] =\n holderIds.length === 0\n ? [{ count: 0 }]\n : await this.db\n .select({ count: sql<number>`cast(count(distinct ${contentAccess.orgUserId}) as int)` })\n .from(contentAccess)\n .where(\n and(\n eq(contentAccess.orgId, orgId),\n eq(contentAccess.contentId, communityId),\n inArray(contentAccess.orgUserId, holderIds),\n ),\n );\n\n const raised =\n Number(postFlagRow?.raised ?? 0) +\n Number(commentFlagRow?.raised ?? 0) +\n Number(messageFlagRow?.raised ?? 0);\n const resolved =\n Number(postFlagRow?.resolved ?? 0) +\n Number(commentFlagRow?.resolved ?? 0) +\n Number(messageFlagRow?.resolved ?? 0);\n\n return {\n memberCount: Number(members?.count ?? 0),\n activeMembers: authors.length,\n postsPerSpace: postsPerSpace.map((row) => ({\n spaceId: row.spaceId,\n count: Number(row.count),\n })),\n commentsPerPost: commentsPerPost.map((row) => ({\n postId: row.postId,\n count: Number(row.count),\n })),\n messagesSent: Number(sent?.count ?? 0),\n flagsRaised: raised,\n flagsResolved: resolved,\n grantsLapsed: Number(lapsedGrants?.count ?? 0),\n grantsRenewed: Number(renewed?.count ?? 0),\n };\n }\n\n async communityMembers(\n orgId: string,\n communityId: string,\n query: CommunityMembersQuery,\n ): Promise<Page<OrgUserProfile> | null> {\n const [community] = await this.db\n .select({ id: communities.id })\n .from(communities)\n .where(and(eq(communities.orgId, orgId), eq(communities.id, communityId)))\n .limit(1);\n if (!community) {\n return null;\n }\n const holds = exists(\n this.db\n .select({ id: contentAccess.entitlementId })\n .from(contentAccess)\n .where(\n and(\n eq(contentAccess.orgId, orgUsers.orgId),\n eq(contentAccess.orgUserId, orgUsers.id),\n eq(contentAccess.contentId, communityId),\n ),\n ),\n );\n const filters: SQL[] = [eq(orgUsers.orgId, orgId), holds];\n const q = query.search?.trim();\n if (q) {\n const like = `%${q}%`;\n filters.push(\n or(ilike(studentFirstName, like), ilike(studentLastName, like), ilike(users.email, like))!,\n );\n }\n const where = and(...filters);\n const [totals] = await this.db\n .select({ total: sql<number>`count(*)` })\n .from(orgUsers)\n .innerJoin(users, eq(users.id, orgUsers.userId))\n .where(where);\n const rows = await this.db\n .select(orgUserProfileColumns)\n .from(orgUsers)\n .innerJoin(users, eq(users.id, orgUsers.userId))\n .leftJoin(user, eq(user.id, users.externalId))\n .where(where)\n .orderBy(asc(users.firstName), asc(users.lastName), asc(orgUsers.id))\n .limit(query.pageSize)\n .offset((query.page - 1) * query.pageSize);\n return {\n rows: rows.map((row) => ({ ...row, image: row.image ?? null })),\n total: Number(totals?.total ?? 0),\n page: query.page,\n pageSize: query.pageSize,\n };\n }\n\n eventAttendees(\n orgId: string,\n eventId: string,\n page: { page: number; pageSize: number },\n ): Promise<Page<EventAttendeeRow>> {\n return eventAttendeePage(this.db, orgId, eventId, page);\n }\n\n eventAnalytics(\n orgId: string,\n eventId: string,\n startsAt: Date,\n ): Promise<Omit<EventAnalytics, \"capacity\">> {\n return eventAnalyticsFigures(this.db, orgId, eventId, startsAt);\n }\n}\ntranslateDbErrors(DrizzleAdminRepository);\n"],"mappings":"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"}
|
package/dist/schema/assets.d.ts
CHANGED
|
@@ -76,7 +76,7 @@ declare const assets: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
|
76
76
|
tableName: "assets";
|
|
77
77
|
dataType: "string";
|
|
78
78
|
columnType: "PgText";
|
|
79
|
-
data: "
|
|
79
|
+
data: "library" | "member";
|
|
80
80
|
driverParam: string;
|
|
81
81
|
notNull: true;
|
|
82
82
|
hasDefault: true;
|
|
@@ -123,6 +123,7 @@ declare const automations: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
|
123
123
|
identity: undefined;
|
|
124
124
|
generated: undefined;
|
|
125
125
|
}, {}, {
|
|
126
|
+
size: undefined;
|
|
126
127
|
baseBuilder: import("drizzle-orm/pg-core").PgColumnBuilder<{
|
|
127
128
|
name: "triggers";
|
|
128
129
|
dataType: "string";
|
|
@@ -131,7 +132,6 @@ declare const automations: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
|
131
132
|
enumValues: [string, ...string[]];
|
|
132
133
|
driverParam: string;
|
|
133
134
|
}, {}, {}, import("drizzle-orm").ColumnBuilderExtraConfig>;
|
|
134
|
-
size: undefined;
|
|
135
135
|
}>;
|
|
136
136
|
actions: import("drizzle-orm/pg-core").PgColumn<{
|
|
137
137
|
name: "actions";
|
|
@@ -597,6 +597,8 @@ declare const notifications: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
|
597
597
|
identity: undefined;
|
|
598
598
|
generated: undefined;
|
|
599
599
|
}, {}, {
|
|
600
|
+
$type: Reason[];
|
|
601
|
+
size: undefined;
|
|
600
602
|
baseBuilder: import("drizzle-orm/pg-core").PgColumnBuilder<{
|
|
601
603
|
name: "reasons";
|
|
602
604
|
dataType: "string";
|
|
@@ -605,8 +607,6 @@ declare const notifications: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
|
605
607
|
enumValues: [string, ...string[]];
|
|
606
608
|
driverParam: string;
|
|
607
609
|
}, {}, {}, import("drizzle-orm").ColumnBuilderExtraConfig>;
|
|
608
|
-
size: undefined;
|
|
609
|
-
$type: Reason[];
|
|
610
610
|
}>;
|
|
611
611
|
matchedReason: import("drizzle-orm/pg-core").PgColumn<{
|
|
612
612
|
name: "matched_reason";
|
|
@@ -1156,6 +1156,7 @@ declare const oauthClient: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
|
1156
1156
|
identity: undefined;
|
|
1157
1157
|
generated: undefined;
|
|
1158
1158
|
}, {}, {
|
|
1159
|
+
size: undefined;
|
|
1159
1160
|
baseBuilder: import("drizzle-orm/pg-core").PgColumnBuilder<{
|
|
1160
1161
|
name: "scopes";
|
|
1161
1162
|
dataType: "string";
|
|
@@ -1164,7 +1165,6 @@ declare const oauthClient: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
|
1164
1165
|
enumValues: [string, ...string[]];
|
|
1165
1166
|
driverParam: string;
|
|
1166
1167
|
}, {}, {}, import("drizzle-orm").ColumnBuilderExtraConfig>;
|
|
1167
|
-
size: undefined;
|
|
1168
1168
|
}>;
|
|
1169
1169
|
clientCredentialsScopes: import("drizzle-orm/pg-core").PgColumn<{
|
|
1170
1170
|
name: "client_credentials_scopes";
|
|
@@ -1199,6 +1199,7 @@ declare const oauthClient: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
|
1199
1199
|
identity: undefined;
|
|
1200
1200
|
generated: undefined;
|
|
1201
1201
|
}, {}, {
|
|
1202
|
+
size: undefined;
|
|
1202
1203
|
baseBuilder: import("drizzle-orm/pg-core").PgColumnBuilder<{
|
|
1203
1204
|
name: "client_credentials_scopes";
|
|
1204
1205
|
dataType: "string";
|
|
@@ -1207,7 +1208,6 @@ declare const oauthClient: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
|
1207
1208
|
enumValues: [string, ...string[]];
|
|
1208
1209
|
driverParam: string;
|
|
1209
1210
|
}, {}, {}, import("drizzle-orm").ColumnBuilderExtraConfig>;
|
|
1210
|
-
size: undefined;
|
|
1211
1211
|
}>;
|
|
1212
1212
|
userId: import("drizzle-orm/pg-core").PgColumn<{
|
|
1213
1213
|
name: "user_id";
|
|
@@ -1344,6 +1344,7 @@ declare const oauthClient: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
|
1344
1344
|
identity: undefined;
|
|
1345
1345
|
generated: undefined;
|
|
1346
1346
|
}, {}, {
|
|
1347
|
+
size: undefined;
|
|
1347
1348
|
baseBuilder: import("drizzle-orm/pg-core").PgColumnBuilder<{
|
|
1348
1349
|
name: "contacts";
|
|
1349
1350
|
dataType: "string";
|
|
@@ -1352,7 +1353,6 @@ declare const oauthClient: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
|
1352
1353
|
enumValues: [string, ...string[]];
|
|
1353
1354
|
driverParam: string;
|
|
1354
1355
|
}, {}, {}, import("drizzle-orm").ColumnBuilderExtraConfig>;
|
|
1355
|
-
size: undefined;
|
|
1356
1356
|
}>;
|
|
1357
1357
|
tos: import("drizzle-orm/pg-core").PgColumn<{
|
|
1358
1358
|
name: "tos";
|
|
@@ -1472,6 +1472,7 @@ declare const oauthClient: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
|
1472
1472
|
identity: undefined;
|
|
1473
1473
|
generated: undefined;
|
|
1474
1474
|
}, {}, {
|
|
1475
|
+
size: undefined;
|
|
1475
1476
|
baseBuilder: import("drizzle-orm/pg-core").PgColumnBuilder<{
|
|
1476
1477
|
name: "redirect_uris";
|
|
1477
1478
|
dataType: "string";
|
|
@@ -1480,7 +1481,6 @@ declare const oauthClient: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
|
1480
1481
|
enumValues: [string, ...string[]];
|
|
1481
1482
|
driverParam: string;
|
|
1482
1483
|
}, {}, {}, import("drizzle-orm").ColumnBuilderExtraConfig>;
|
|
1483
|
-
size: undefined;
|
|
1484
1484
|
}>;
|
|
1485
1485
|
postLogoutRedirectUris: import("drizzle-orm/pg-core").PgColumn<{
|
|
1486
1486
|
name: "post_logout_redirect_uris";
|
|
@@ -1515,6 +1515,7 @@ declare const oauthClient: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
|
1515
1515
|
identity: undefined;
|
|
1516
1516
|
generated: undefined;
|
|
1517
1517
|
}, {}, {
|
|
1518
|
+
size: undefined;
|
|
1518
1519
|
baseBuilder: import("drizzle-orm/pg-core").PgColumnBuilder<{
|
|
1519
1520
|
name: "post_logout_redirect_uris";
|
|
1520
1521
|
dataType: "string";
|
|
@@ -1523,7 +1524,6 @@ declare const oauthClient: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
|
1523
1524
|
enumValues: [string, ...string[]];
|
|
1524
1525
|
driverParam: string;
|
|
1525
1526
|
}, {}, {}, import("drizzle-orm").ColumnBuilderExtraConfig>;
|
|
1526
|
-
size: undefined;
|
|
1527
1527
|
}>;
|
|
1528
1528
|
backchannelLogoutUri: import("drizzle-orm/pg-core").PgColumn<{
|
|
1529
1529
|
name: "backchannel_logout_uri";
|
|
@@ -1660,6 +1660,7 @@ declare const oauthClient: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
|
1660
1660
|
identity: undefined;
|
|
1661
1661
|
generated: undefined;
|
|
1662
1662
|
}, {}, {
|
|
1663
|
+
size: undefined;
|
|
1663
1664
|
baseBuilder: import("drizzle-orm/pg-core").PgColumnBuilder<{
|
|
1664
1665
|
name: "grant_types";
|
|
1665
1666
|
dataType: "string";
|
|
@@ -1668,7 +1669,6 @@ declare const oauthClient: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
|
1668
1669
|
enumValues: [string, ...string[]];
|
|
1669
1670
|
driverParam: string;
|
|
1670
1671
|
}, {}, {}, import("drizzle-orm").ColumnBuilderExtraConfig>;
|
|
1671
|
-
size: undefined;
|
|
1672
1672
|
}>;
|
|
1673
1673
|
responseTypes: import("drizzle-orm/pg-core").PgColumn<{
|
|
1674
1674
|
name: "response_types";
|
|
@@ -1703,6 +1703,7 @@ declare const oauthClient: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
|
1703
1703
|
identity: undefined;
|
|
1704
1704
|
generated: undefined;
|
|
1705
1705
|
}, {}, {
|
|
1706
|
+
size: undefined;
|
|
1706
1707
|
baseBuilder: import("drizzle-orm/pg-core").PgColumnBuilder<{
|
|
1707
1708
|
name: "response_types";
|
|
1708
1709
|
dataType: "string";
|
|
@@ -1711,7 +1712,6 @@ declare const oauthClient: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
|
1711
1712
|
enumValues: [string, ...string[]];
|
|
1712
1713
|
driverParam: string;
|
|
1713
1714
|
}, {}, {}, import("drizzle-orm").ColumnBuilderExtraConfig>;
|
|
1714
|
-
size: undefined;
|
|
1715
1715
|
}>;
|
|
1716
1716
|
requirePKCE: import("drizzle-orm/pg-core").PgColumn<{
|
|
1717
1717
|
name: "require_pkce";
|
|
@@ -1940,6 +1940,7 @@ declare const oauthRefreshToken: import("drizzle-orm/pg-core").PgTableWithColumn
|
|
|
1940
1940
|
identity: undefined;
|
|
1941
1941
|
generated: undefined;
|
|
1942
1942
|
}, {}, {
|
|
1943
|
+
size: undefined;
|
|
1943
1944
|
baseBuilder: import("drizzle-orm/pg-core").PgColumnBuilder<{
|
|
1944
1945
|
name: "resources";
|
|
1945
1946
|
dataType: "string";
|
|
@@ -1948,7 +1949,6 @@ declare const oauthRefreshToken: import("drizzle-orm/pg-core").PgTableWithColumn
|
|
|
1948
1949
|
enumValues: [string, ...string[]];
|
|
1949
1950
|
driverParam: string;
|
|
1950
1951
|
}, {}, {}, import("drizzle-orm").ColumnBuilderExtraConfig>;
|
|
1951
|
-
size: undefined;
|
|
1952
1952
|
}>;
|
|
1953
1953
|
requestedUserInfoClaims: import("drizzle-orm/pg-core").PgColumn<{
|
|
1954
1954
|
name: "requested_user_info_claims";
|
|
@@ -1983,6 +1983,7 @@ declare const oauthRefreshToken: import("drizzle-orm/pg-core").PgTableWithColumn
|
|
|
1983
1983
|
identity: undefined;
|
|
1984
1984
|
generated: undefined;
|
|
1985
1985
|
}, {}, {
|
|
1986
|
+
size: undefined;
|
|
1986
1987
|
baseBuilder: import("drizzle-orm/pg-core").PgColumnBuilder<{
|
|
1987
1988
|
name: "requested_user_info_claims";
|
|
1988
1989
|
dataType: "string";
|
|
@@ -1991,7 +1992,6 @@ declare const oauthRefreshToken: import("drizzle-orm/pg-core").PgTableWithColumn
|
|
|
1991
1992
|
enumValues: [string, ...string[]];
|
|
1992
1993
|
driverParam: string;
|
|
1993
1994
|
}, {}, {}, import("drizzle-orm").ColumnBuilderExtraConfig>;
|
|
1994
|
-
size: undefined;
|
|
1995
1995
|
}>;
|
|
1996
1996
|
expiresAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
1997
1997
|
name: "expires_at";
|
|
@@ -2162,6 +2162,7 @@ declare const oauthRefreshToken: import("drizzle-orm/pg-core").PgTableWithColumn
|
|
|
2162
2162
|
identity: undefined;
|
|
2163
2163
|
generated: undefined;
|
|
2164
2164
|
}, {}, {
|
|
2165
|
+
size: undefined;
|
|
2165
2166
|
baseBuilder: import("drizzle-orm/pg-core").PgColumnBuilder<{
|
|
2166
2167
|
name: "scopes";
|
|
2167
2168
|
dataType: "string";
|
|
@@ -2170,7 +2171,6 @@ declare const oauthRefreshToken: import("drizzle-orm/pg-core").PgTableWithColumn
|
|
|
2170
2171
|
enumValues: [string, ...string[]];
|
|
2171
2172
|
driverParam: string;
|
|
2172
2173
|
}, {}, {}, import("drizzle-orm").ColumnBuilderExtraConfig>;
|
|
2173
|
-
size: undefined;
|
|
2174
2174
|
}>;
|
|
2175
2175
|
};
|
|
2176
2176
|
dialect: "pg";
|
|
@@ -2331,6 +2331,7 @@ declare const oauthAccessToken: import("drizzle-orm/pg-core").PgTableWithColumns
|
|
|
2331
2331
|
identity: undefined;
|
|
2332
2332
|
generated: undefined;
|
|
2333
2333
|
}, {}, {
|
|
2334
|
+
size: undefined;
|
|
2334
2335
|
baseBuilder: import("drizzle-orm/pg-core").PgColumnBuilder<{
|
|
2335
2336
|
name: "resources";
|
|
2336
2337
|
dataType: "string";
|
|
@@ -2339,7 +2340,6 @@ declare const oauthAccessToken: import("drizzle-orm/pg-core").PgTableWithColumns
|
|
|
2339
2340
|
enumValues: [string, ...string[]];
|
|
2340
2341
|
driverParam: string;
|
|
2341
2342
|
}, {}, {}, import("drizzle-orm").ColumnBuilderExtraConfig>;
|
|
2342
|
-
size: undefined;
|
|
2343
2343
|
}>;
|
|
2344
2344
|
requestedUserInfoClaims: import("drizzle-orm/pg-core").PgColumn<{
|
|
2345
2345
|
name: "requested_user_info_claims";
|
|
@@ -2374,6 +2374,7 @@ declare const oauthAccessToken: import("drizzle-orm/pg-core").PgTableWithColumns
|
|
|
2374
2374
|
identity: undefined;
|
|
2375
2375
|
generated: undefined;
|
|
2376
2376
|
}, {}, {
|
|
2377
|
+
size: undefined;
|
|
2377
2378
|
baseBuilder: import("drizzle-orm/pg-core").PgColumnBuilder<{
|
|
2378
2379
|
name: "requested_user_info_claims";
|
|
2379
2380
|
dataType: "string";
|
|
@@ -2382,7 +2383,6 @@ declare const oauthAccessToken: import("drizzle-orm/pg-core").PgTableWithColumns
|
|
|
2382
2383
|
enumValues: [string, ...string[]];
|
|
2383
2384
|
driverParam: string;
|
|
2384
2385
|
}, {}, {}, import("drizzle-orm").ColumnBuilderExtraConfig>;
|
|
2385
|
-
size: undefined;
|
|
2386
2386
|
}>;
|
|
2387
2387
|
refreshId: import("drizzle-orm/pg-core").PgColumn<{
|
|
2388
2388
|
name: "refresh_id";
|
|
@@ -2502,6 +2502,7 @@ declare const oauthAccessToken: import("drizzle-orm/pg-core").PgTableWithColumns
|
|
|
2502
2502
|
identity: undefined;
|
|
2503
2503
|
generated: undefined;
|
|
2504
2504
|
}, {}, {
|
|
2505
|
+
size: undefined;
|
|
2505
2506
|
baseBuilder: import("drizzle-orm/pg-core").PgColumnBuilder<{
|
|
2506
2507
|
name: "scopes";
|
|
2507
2508
|
dataType: "string";
|
|
@@ -2510,7 +2511,6 @@ declare const oauthAccessToken: import("drizzle-orm/pg-core").PgTableWithColumns
|
|
|
2510
2511
|
enumValues: [string, ...string[]];
|
|
2511
2512
|
driverParam: string;
|
|
2512
2513
|
}, {}, {}, import("drizzle-orm").ColumnBuilderExtraConfig>;
|
|
2513
|
-
size: undefined;
|
|
2514
2514
|
}>;
|
|
2515
2515
|
};
|
|
2516
2516
|
dialect: "pg";
|
|
@@ -2620,6 +2620,7 @@ declare const oauthConsent: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
|
2620
2620
|
identity: undefined;
|
|
2621
2621
|
generated: undefined;
|
|
2622
2622
|
}, {}, {
|
|
2623
|
+
size: undefined;
|
|
2623
2624
|
baseBuilder: import("drizzle-orm/pg-core").PgColumnBuilder<{
|
|
2624
2625
|
name: "resources";
|
|
2625
2626
|
dataType: "string";
|
|
@@ -2628,7 +2629,6 @@ declare const oauthConsent: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
|
2628
2629
|
enumValues: [string, ...string[]];
|
|
2629
2630
|
driverParam: string;
|
|
2630
2631
|
}, {}, {}, import("drizzle-orm").ColumnBuilderExtraConfig>;
|
|
2631
|
-
size: undefined;
|
|
2632
2632
|
}>;
|
|
2633
2633
|
requestedUserInfoClaims: import("drizzle-orm/pg-core").PgColumn<{
|
|
2634
2634
|
name: "requested_user_info_claims";
|
|
@@ -2663,6 +2663,7 @@ declare const oauthConsent: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
|
2663
2663
|
identity: undefined;
|
|
2664
2664
|
generated: undefined;
|
|
2665
2665
|
}, {}, {
|
|
2666
|
+
size: undefined;
|
|
2666
2667
|
baseBuilder: import("drizzle-orm/pg-core").PgColumnBuilder<{
|
|
2667
2668
|
name: "requested_user_info_claims";
|
|
2668
2669
|
dataType: "string";
|
|
@@ -2671,7 +2672,6 @@ declare const oauthConsent: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
|
2671
2672
|
enumValues: [string, ...string[]];
|
|
2672
2673
|
driverParam: string;
|
|
2673
2674
|
}, {}, {}, import("drizzle-orm").ColumnBuilderExtraConfig>;
|
|
2674
|
-
size: undefined;
|
|
2675
2675
|
}>;
|
|
2676
2676
|
scopes: import("drizzle-orm/pg-core").PgColumn<{
|
|
2677
2677
|
name: "scopes";
|
|
@@ -2706,6 +2706,7 @@ declare const oauthConsent: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
|
2706
2706
|
identity: undefined;
|
|
2707
2707
|
generated: undefined;
|
|
2708
2708
|
}, {}, {
|
|
2709
|
+
size: undefined;
|
|
2709
2710
|
baseBuilder: import("drizzle-orm/pg-core").PgColumnBuilder<{
|
|
2710
2711
|
name: "scopes";
|
|
2711
2712
|
dataType: "string";
|
|
@@ -2714,7 +2715,6 @@ declare const oauthConsent: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
|
2714
2715
|
enumValues: [string, ...string[]];
|
|
2715
2716
|
driverParam: string;
|
|
2716
2717
|
}, {}, {}, import("drizzle-orm").ColumnBuilderExtraConfig>;
|
|
2717
|
-
size: undefined;
|
|
2718
2718
|
}>;
|
|
2719
2719
|
createdAt: import("drizzle-orm/pg-core").PgColumn<{
|
|
2720
2720
|
name: "created_at";
|
|
@@ -2909,6 +2909,7 @@ declare const oauthResource: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
|
2909
2909
|
identity: undefined;
|
|
2910
2910
|
generated: undefined;
|
|
2911
2911
|
}, {}, {
|
|
2912
|
+
size: undefined;
|
|
2912
2913
|
baseBuilder: import("drizzle-orm/pg-core").PgColumnBuilder<{
|
|
2913
2914
|
name: "allowed_scopes";
|
|
2914
2915
|
dataType: "string";
|
|
@@ -2917,7 +2918,6 @@ declare const oauthResource: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
|
2917
2918
|
enumValues: [string, ...string[]];
|
|
2918
2919
|
driverParam: string;
|
|
2919
2920
|
}, {}, {}, import("drizzle-orm").ColumnBuilderExtraConfig>;
|
|
2920
|
-
size: undefined;
|
|
2921
2921
|
}>;
|
|
2922
2922
|
customClaims: import("drizzle-orm/pg-core").PgColumn<{
|
|
2923
2923
|
name: "custom_claims";
|
|
@@ -93,7 +93,7 @@ declare const entitlements: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
|
93
93
|
tableName: "entitlements";
|
|
94
94
|
dataType: "string";
|
|
95
95
|
columnType: "PgText";
|
|
96
|
-
data: "
|
|
96
|
+
data: "active" | "revoked";
|
|
97
97
|
driverParam: string;
|
|
98
98
|
notNull: true;
|
|
99
99
|
hasDefault: true;
|
|
@@ -287,7 +287,7 @@ declare const spaceMembers: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
|
287
287
|
tableName: "space_members";
|
|
288
288
|
dataType: "string";
|
|
289
289
|
columnType: "PgText";
|
|
290
|
-
data: "
|
|
290
|
+
data: "invited" | "active" | "revoked" | "requested";
|
|
291
291
|
driverParam: string;
|
|
292
292
|
notNull: true;
|
|
293
293
|
hasDefault: false;
|
|
@@ -330,7 +330,7 @@ declare const invites: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
|
330
330
|
tableName: "invites";
|
|
331
331
|
dataType: "string";
|
|
332
332
|
columnType: "PgText";
|
|
333
|
-
data: "pending" | "
|
|
333
|
+
data: "pending" | "accepted" | "rejected" | "canceled";
|
|
334
334
|
driverParam: string;
|
|
335
335
|
notNull: true;
|
|
336
336
|
hasDefault: false;
|
package/dist/schema/roles.d.ts
CHANGED
|
@@ -138,6 +138,7 @@ declare const orgRoles: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
|
138
138
|
identity: undefined;
|
|
139
139
|
generated: undefined;
|
|
140
140
|
}, {}, {
|
|
141
|
+
size: undefined;
|
|
141
142
|
baseBuilder: import("drizzle-orm/pg-core").PgColumnBuilder<{
|
|
142
143
|
name: "levels";
|
|
143
144
|
dataType: "string";
|
|
@@ -146,7 +147,6 @@ declare const orgRoles: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
|
146
147
|
enumValues: ["org", "course", "download", "community", "event"];
|
|
147
148
|
driverParam: string;
|
|
148
149
|
}, {}, {}, import("drizzle-orm").ColumnBuilderExtraConfig>;
|
|
149
|
-
size: undefined;
|
|
150
150
|
}>;
|
|
151
151
|
permissions: import("drizzle-orm/pg-core").PgColumn<{
|
|
152
152
|
name: "permissions";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@headless-lms/adapter-db",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"dependencies": {
|
|
22
22
|
"drizzle-orm": "^0.45.2",
|
|
23
23
|
"pg": "^8.13.1",
|
|
24
|
-
"@headless-lms/core": "0.
|
|
24
|
+
"@headless-lms/core": "0.9.0"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
27
|
"@types/node": "^22.10.2",
|