@goscribe/server 1.5.0 → 1.7.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.
Files changed (321) hide show
  1. package/dist/context.d.ts +14 -1
  2. package/dist/context.js +23 -2
  3. package/dist/generated/prisma/client.d.ts +224 -0
  4. package/dist/generated/prisma/client.js +34 -0
  5. package/dist/generated/prisma/commonInputTypes.d.ts +941 -0
  6. package/dist/generated/prisma/commonInputTypes.js +10 -0
  7. package/dist/generated/prisma/enums.d.ts +67 -0
  8. package/dist/generated/prisma/enums.js +66 -0
  9. package/dist/generated/prisma/internal/class.d.ts +539 -0
  10. package/dist/generated/prisma/internal/class.js +49 -0
  11. package/dist/generated/prisma/internal/prismaNamespace.d.ts +3924 -0
  12. package/dist/generated/prisma/internal/prismaNamespace.js +557 -0
  13. package/dist/generated/prisma/models/ActivityLog.d.ts +1847 -0
  14. package/dist/generated/prisma/models/ActivityLog.js +1 -0
  15. package/dist/generated/prisma/models/Artifact.d.ts +2345 -0
  16. package/dist/generated/prisma/models/Artifact.js +1 -0
  17. package/dist/generated/prisma/models/ArtifactVersion.d.ts +1550 -0
  18. package/dist/generated/prisma/models/ArtifactVersion.js +1 -0
  19. package/dist/generated/prisma/models/Channel.d.ts +1257 -0
  20. package/dist/generated/prisma/models/Channel.js +1 -0
  21. package/dist/generated/prisma/models/Chat.d.ts +1339 -0
  22. package/dist/generated/prisma/models/Chat.js +1 -0
  23. package/dist/generated/prisma/models/CopilotConversation.d.ts +1450 -0
  24. package/dist/generated/prisma/models/CopilotConversation.js +1 -0
  25. package/dist/generated/prisma/models/CopilotMessage.d.ts +1179 -0
  26. package/dist/generated/prisma/models/CopilotMessage.js +1 -0
  27. package/dist/generated/prisma/models/FileAsset.d.ts +1832 -0
  28. package/dist/generated/prisma/models/FileAsset.js +1 -0
  29. package/dist/generated/prisma/models/Flashcard.d.ts +1460 -0
  30. package/dist/generated/prisma/models/Flashcard.js +1 -0
  31. package/dist/generated/prisma/models/FlashcardProgress.d.ts +1782 -0
  32. package/dist/generated/prisma/models/FlashcardProgress.js +1 -0
  33. package/dist/generated/prisma/models/Folder.d.ts +1685 -0
  34. package/dist/generated/prisma/models/Folder.js +1 -0
  35. package/dist/generated/prisma/models/IdempotencyRecord.d.ts +1319 -0
  36. package/dist/generated/prisma/models/IdempotencyRecord.js +1 -0
  37. package/dist/generated/prisma/models/Invoice.d.ts +1586 -0
  38. package/dist/generated/prisma/models/Invoice.js +1 -0
  39. package/dist/generated/prisma/models/KnowledgeBase.d.ts +1721 -0
  40. package/dist/generated/prisma/models/KnowledgeBase.js +1 -0
  41. package/dist/generated/prisma/models/KnowledgeBaseChunk.d.ts +1333 -0
  42. package/dist/generated/prisma/models/KnowledgeBaseChunk.js +1 -0
  43. package/dist/generated/prisma/models/KnowledgeBaseDocument.d.ts +1695 -0
  44. package/dist/generated/prisma/models/KnowledgeBaseDocument.js +1 -0
  45. package/dist/generated/prisma/models/Notification.d.ts +1992 -0
  46. package/dist/generated/prisma/models/Notification.js +1 -0
  47. package/dist/generated/prisma/models/PasswordResetToken.d.ts +1210 -0
  48. package/dist/generated/prisma/models/PasswordResetToken.js +1 -0
  49. package/dist/generated/prisma/models/Plan.d.ts +1431 -0
  50. package/dist/generated/prisma/models/Plan.js +1 -0
  51. package/dist/generated/prisma/models/PlanLimit.d.ts +1328 -0
  52. package/dist/generated/prisma/models/PlanLimit.js +1 -0
  53. package/dist/generated/prisma/models/PodcastSegment.d.ts +1564 -0
  54. package/dist/generated/prisma/models/PodcastSegment.js +1 -0
  55. package/dist/generated/prisma/models/ResourcePrice.d.ts +1008 -0
  56. package/dist/generated/prisma/models/ResourcePrice.js +1 -0
  57. package/dist/generated/prisma/models/Role.d.ts +1065 -0
  58. package/dist/generated/prisma/models/Role.js +1 -0
  59. package/dist/generated/prisma/models/Session.d.ts +1105 -0
  60. package/dist/generated/prisma/models/Session.js +1 -0
  61. package/dist/generated/prisma/models/StripeEvent.d.ts +1081 -0
  62. package/dist/generated/prisma/models/StripeEvent.js +1 -0
  63. package/dist/generated/prisma/models/StudyGuideComment.d.ts +1321 -0
  64. package/dist/generated/prisma/models/StudyGuideComment.js +1 -0
  65. package/dist/generated/prisma/models/StudyGuideHighlight.d.ts +1629 -0
  66. package/dist/generated/prisma/models/StudyGuideHighlight.js +1 -0
  67. package/dist/generated/prisma/models/Subscription.d.ts +1677 -0
  68. package/dist/generated/prisma/models/Subscription.js +1 -0
  69. package/dist/generated/prisma/models/User.d.ts +7559 -0
  70. package/dist/generated/prisma/models/User.js +1 -0
  71. package/dist/generated/prisma/models/UserCredit.d.ts +1249 -0
  72. package/dist/generated/prisma/models/UserCredit.js +1 -0
  73. package/dist/generated/prisma/models/VerificationToken.d.ts +946 -0
  74. package/dist/generated/prisma/models/VerificationToken.js +1 -0
  75. package/dist/generated/prisma/models/WorksheetPreset.d.ts +1433 -0
  76. package/dist/generated/prisma/models/WorksheetPreset.js +1 -0
  77. package/dist/generated/prisma/models/WorksheetQuestion.d.ts +1491 -0
  78. package/dist/generated/prisma/models/WorksheetQuestion.js +1 -0
  79. package/dist/generated/prisma/models/WorksheetQuestionProgress.d.ts +1620 -0
  80. package/dist/generated/prisma/models/WorksheetQuestionProgress.js +1 -0
  81. package/dist/generated/prisma/models/Workspace.d.ts +3620 -0
  82. package/dist/generated/prisma/models/Workspace.js +1 -0
  83. package/dist/generated/prisma/models/WorkspaceInvitation.d.ts +1490 -0
  84. package/dist/generated/prisma/models/WorkspaceInvitation.js +1 -0
  85. package/dist/generated/prisma/models/WorkspaceKnowledgeBase.d.ts +1410 -0
  86. package/dist/generated/prisma/models/WorkspaceKnowledgeBase.js +1 -0
  87. package/dist/generated/prisma/models/WorkspaceMember.d.ts +1326 -0
  88. package/dist/generated/prisma/models/WorkspaceMember.js +1 -0
  89. package/dist/generated/prisma/models.d.ts +39 -0
  90. package/dist/generated/prisma/models.js +1 -0
  91. package/dist/lib/ai/index.d.ts +3 -2
  92. package/dist/lib/ai/index.js +3 -2
  93. package/dist/lib/ai/llm-client.d.ts +1 -0
  94. package/dist/lib/ai/llm-client.js +17 -0
  95. package/dist/routers/_app.d.ts +40 -80
  96. package/dist/routers/auth.js +1 -1
  97. package/dist/routers/flashcards.d.ts +12 -1
  98. package/dist/routers/payment.d.ts +1 -12
  99. package/dist/routers/workspace.d.ts +27 -67
  100. package/dist/routers/workspace.js +1 -0
  101. package/dist/services/billing/payment.service.d.ts +1 -12
  102. package/dist/services/billing/payment.service.js +3 -6
  103. package/dist/services/billing/usage.service.d.ts +30 -10
  104. package/dist/services/billing/usage.service.js +87 -15
  105. package/dist/services/content/copilot.service.js +15 -29
  106. package/dist/services/content/flashcard-progress.service.js +9 -9
  107. package/dist/services/content/flashcard.service.d.ts +45 -1
  108. package/dist/services/content/flashcard.service.js +81 -68
  109. package/dist/services/content/media-analysis.service.js +27 -27
  110. package/dist/services/content/worksheet-generation.service.test.js +2 -2
  111. package/dist/services/workspace/workspace.service.d.ts +23 -67
  112. package/dist/services/workspace/workspace.service.js +69 -62
  113. package/dist/src/context.d.ts +27 -0
  114. package/dist/src/context.js +33 -0
  115. package/dist/src/index.d.ts +3 -0
  116. package/dist/src/index.js +1 -0
  117. package/dist/src/lib/ai/config.d.ts +20 -0
  118. package/dist/src/lib/ai/config.js +31 -0
  119. package/dist/src/lib/ai/embedding-client.d.ts +8 -0
  120. package/dist/src/lib/ai/embedding-client.js +30 -0
  121. package/dist/src/lib/ai/index.d.ts +48 -0
  122. package/dist/src/lib/ai/index.js +29 -0
  123. package/dist/src/lib/ai/inference-backend/client.d.ts +28 -0
  124. package/dist/src/lib/ai/inference-backend/client.js +301 -0
  125. package/dist/src/lib/ai/inference-backend/mocks.d.ts +12 -0
  126. package/dist/src/lib/ai/inference-backend/mocks.js +133 -0
  127. package/dist/src/lib/ai/inference-backend/types.d.ts +44 -0
  128. package/dist/src/lib/ai/inference-backend/types.js +1 -0
  129. package/dist/src/lib/ai/json-parse.d.ts +2 -0
  130. package/dist/src/lib/ai/json-parse.js +34 -0
  131. package/dist/src/lib/ai/llm-client.d.ts +7 -0
  132. package/dist/src/lib/ai/llm-client.js +36 -0
  133. package/dist/src/lib/ai/mock.d.ts +2 -0
  134. package/dist/src/lib/ai/mock.js +10 -0
  135. package/dist/src/lib/ai/types.d.ts +9 -0
  136. package/dist/src/lib/ai/types.js +1 -0
  137. package/dist/src/lib/auth.d.ts +36 -0
  138. package/dist/src/lib/auth.js +117 -0
  139. package/dist/src/lib/chunking.d.ts +19 -0
  140. package/dist/src/lib/chunking.js +47 -0
  141. package/dist/src/lib/constants.d.ts +13 -0
  142. package/dist/src/lib/constants.js +12 -0
  143. package/dist/src/lib/curated-kb-seed.d.ts +12 -0
  144. package/dist/src/lib/curated-kb-seed.js +155 -0
  145. package/dist/src/lib/email.d.ts +11 -0
  146. package/dist/src/lib/email.js +152 -0
  147. package/dist/src/lib/embeddings.d.ts +2 -0
  148. package/dist/src/lib/embeddings.js +1 -0
  149. package/dist/src/lib/ensure-curated-kb-catalog.d.ts +6 -0
  150. package/dist/src/lib/ensure-curated-kb-catalog.js +53 -0
  151. package/dist/src/lib/env.d.ts +41 -0
  152. package/dist/src/lib/env.js +57 -0
  153. package/dist/src/lib/errors.d.ts +33 -0
  154. package/dist/src/lib/errors.js +78 -0
  155. package/dist/src/lib/file.d.ts +0 -0
  156. package/dist/src/lib/file.js +1 -0
  157. package/dist/src/lib/inference.d.ts +1 -0
  158. package/dist/src/lib/inference.js +1 -0
  159. package/dist/src/lib/kb-meta.d.ts +8 -0
  160. package/dist/src/lib/kb-meta.js +77 -0
  161. package/dist/src/lib/logger.d.ts +62 -0
  162. package/dist/src/lib/logger.js +364 -0
  163. package/dist/src/lib/pdf.d.ts +11 -0
  164. package/dist/src/lib/pdf.js +11 -0
  165. package/dist/src/lib/prisma.d.ts +3 -0
  166. package/dist/src/lib/prisma.js +15 -0
  167. package/dist/src/lib/pusher.d.ts +38 -0
  168. package/dist/src/lib/pusher.js +170 -0
  169. package/dist/src/lib/retry.d.ts +15 -0
  170. package/dist/src/lib/retry.js +37 -0
  171. package/dist/src/lib/storage.d.ts +11 -0
  172. package/dist/src/lib/storage.js +71 -0
  173. package/dist/src/lib/stripe.d.ts +10 -0
  174. package/dist/src/lib/stripe.js +36 -0
  175. package/dist/src/lib/validation.d.ts +51 -0
  176. package/dist/src/lib/validation.js +64 -0
  177. package/dist/src/lib/workspace-kb.d.ts +5 -0
  178. package/dist/src/lib/workspace-kb.js +7 -0
  179. package/dist/src/repositories/artifact.repository.d.ts +64 -0
  180. package/dist/src/repositories/artifact.repository.js +40 -0
  181. package/dist/src/repositories/base.repository.d.ts +14 -0
  182. package/dist/src/repositories/base.repository.js +14 -0
  183. package/dist/src/repositories/invitation.repository.d.ts +104 -0
  184. package/dist/src/repositories/invitation.repository.js +44 -0
  185. package/dist/src/repositories/notification.repository.d.ts +76 -0
  186. package/dist/src/repositories/notification.repository.js +44 -0
  187. package/dist/src/repositories/user.repository.d.ts +84 -0
  188. package/dist/src/repositories/user.repository.js +37 -0
  189. package/dist/src/repositories/workspace-member.repository.d.ts +35 -0
  190. package/dist/src/repositories/workspace-member.repository.js +31 -0
  191. package/dist/src/repositories/workspace.repository.d.ts +101 -0
  192. package/dist/src/repositories/workspace.repository.js +79 -0
  193. package/dist/src/routers/_app.d.ts +3464 -0
  194. package/dist/src/routers/_app.js +36 -0
  195. package/dist/src/routers/admin.d.ts +358 -0
  196. package/dist/src/routers/admin.js +105 -0
  197. package/dist/src/routers/annotations.d.ts +219 -0
  198. package/dist/src/routers/annotations.js +29 -0
  199. package/dist/src/routers/artifactVersions.d.ts +65 -0
  200. package/dist/src/routers/artifactVersions.js +14 -0
  201. package/dist/src/routers/auth.d.ts +161 -0
  202. package/dist/src/routers/auth.js +97 -0
  203. package/dist/src/routers/chat.d.ts +170 -0
  204. package/dist/src/routers/chat.js +32 -0
  205. package/dist/src/routers/copilot.d.ts +200 -0
  206. package/dist/src/routers/copilot.js +52 -0
  207. package/dist/src/routers/flashcards.d.ts +336 -0
  208. package/dist/src/routers/flashcards.js +93 -0
  209. package/dist/src/routers/knowledgeBase.d.ts +421 -0
  210. package/dist/src/routers/knowledgeBase.js +118 -0
  211. package/dist/src/routers/members.d.ts +169 -0
  212. package/dist/src/routers/members.js +47 -0
  213. package/dist/src/routers/notifications.d.ts +99 -0
  214. package/dist/src/routers/notifications.js +25 -0
  215. package/dist/src/routers/payment.d.ts +80 -0
  216. package/dist/src/routers/payment.js +21 -0
  217. package/dist/src/routers/podcast.d.ts +287 -0
  218. package/dist/src/routers/podcast.js +34 -0
  219. package/dist/src/routers/studyguide.d.ts +36 -0
  220. package/dist/src/routers/studyguide.js +8 -0
  221. package/dist/src/routers/worksheets.d.ts +429 -0
  222. package/dist/src/routers/worksheets.js +139 -0
  223. package/dist/src/routers/workspace.d.ts +563 -0
  224. package/dist/src/routers/workspace.js +104 -0
  225. package/dist/src/scripts/purge-deleted-users.d.ts +1 -0
  226. package/dist/src/scripts/purge-deleted-users.js +148 -0
  227. package/dist/src/server.d.ts +1 -0
  228. package/dist/src/server.js +190 -0
  229. package/dist/src/services/activity/activity-human-description.service.d.ts +13 -0
  230. package/dist/src/services/activity/activity-human-description.service.js +221 -0
  231. package/dist/src/services/activity/activity-human-description.service.test.d.ts +1 -0
  232. package/dist/src/services/activity/activity-human-description.service.test.js +16 -0
  233. package/dist/src/services/activity/activity-log.service.d.ts +87 -0
  234. package/dist/src/services/activity/activity-log.service.js +276 -0
  235. package/dist/src/services/activity/activity-log.service.test.d.ts +1 -0
  236. package/dist/src/services/activity/activity-log.service.test.js +27 -0
  237. package/dist/src/services/admin/admin.service.d.ts +270 -0
  238. package/dist/src/services/admin/admin.service.js +476 -0
  239. package/dist/src/services/ai/ai-session.service.d.ts +5 -0
  240. package/dist/src/services/ai/ai-session.service.js +4 -0
  241. package/dist/src/services/artifacts/annotation.service.d.ts +177 -0
  242. package/dist/src/services/artifacts/annotation.service.js +154 -0
  243. package/dist/src/services/artifacts/artifact-version.service.d.ts +38 -0
  244. package/dist/src/services/artifacts/artifact-version.service.js +129 -0
  245. package/dist/src/services/artifacts/chat.service.d.ts +127 -0
  246. package/dist/src/services/artifacts/chat.service.js +182 -0
  247. package/dist/src/services/artifacts/study-guide.service.d.ts +18 -0
  248. package/dist/src/services/artifacts/study-guide.service.js +65 -0
  249. package/dist/src/services/auth/auth.service.d.ts +94 -0
  250. package/dist/src/services/auth/auth.service.js +368 -0
  251. package/dist/src/services/base.service.d.ts +14 -0
  252. package/dist/src/services/base.service.js +14 -0
  253. package/dist/src/services/billing/payment.service.d.ts +44 -0
  254. package/dist/src/services/billing/payment.service.js +365 -0
  255. package/dist/src/services/billing/subscription.service.d.ts +37 -0
  256. package/dist/src/services/billing/subscription.service.js +654 -0
  257. package/dist/src/services/billing/usage.service.d.ts +47 -0
  258. package/dist/src/services/billing/usage.service.js +149 -0
  259. package/dist/src/services/content/copilot.service.d.ts +113 -0
  260. package/dist/src/services/content/copilot.service.js +439 -0
  261. package/dist/src/services/content/flashcard-progress.service.d.ts +159 -0
  262. package/dist/src/services/content/flashcard-progress.service.js +432 -0
  263. package/dist/src/services/content/flashcard.service.d.ts +184 -0
  264. package/dist/src/services/content/flashcard.service.js +339 -0
  265. package/dist/src/services/content/media-analysis.service.d.ts +23 -0
  266. package/dist/src/services/content/media-analysis.service.js +404 -0
  267. package/dist/src/services/content/podcast.service.d.ts +267 -0
  268. package/dist/src/services/content/podcast.service.js +653 -0
  269. package/dist/src/services/content/worksheet-content.service.d.ts +37 -0
  270. package/dist/src/services/content/worksheet-content.service.js +84 -0
  271. package/dist/src/services/content/worksheet-content.service.test.d.ts +1 -0
  272. package/dist/src/services/content/worksheet-content.service.test.js +69 -0
  273. package/dist/src/services/content/worksheet-generation.service.d.ts +91 -0
  274. package/dist/src/services/content/worksheet-generation.service.js +95 -0
  275. package/dist/src/services/content/worksheet-generation.service.test.d.ts +1 -0
  276. package/dist/src/services/content/worksheet-generation.service.test.js +20 -0
  277. package/dist/src/services/content/worksheet.service.d.ts +347 -0
  278. package/dist/src/services/content/worksheet.service.js +599 -0
  279. package/dist/src/services/knowledge/knowledge-base.service.d.ts +316 -0
  280. package/dist/src/services/knowledge/knowledge-base.service.js +544 -0
  281. package/dist/src/services/members/invitation.service.d.ts +66 -0
  282. package/dist/src/services/members/invitation.service.js +348 -0
  283. package/dist/src/services/members/member.service.d.ts +36 -0
  284. package/dist/src/services/members/member.service.js +193 -0
  285. package/dist/src/services/notifications/notification.service.d.ts +214 -0
  286. package/dist/src/services/notifications/notification.service.js +550 -0
  287. package/dist/src/services/notifications/notification.service.test.d.ts +1 -0
  288. package/dist/src/services/notifications/notification.service.test.js +87 -0
  289. package/dist/src/services/workspace/workspace-analytics.service.d.ts +24 -0
  290. package/dist/src/services/workspace/workspace-analytics.service.js +95 -0
  291. package/dist/src/services/workspace/workspace-kb.service.d.ts +40 -0
  292. package/dist/src/services/workspace/workspace-kb.service.js +184 -0
  293. package/dist/src/services/workspace/workspace.service.d.ts +263 -0
  294. package/dist/src/services/workspace/workspace.service.js +401 -0
  295. package/dist/src/trpc.d.ts +60 -0
  296. package/dist/src/trpc.js +217 -0
  297. package/dist/src/types/index.d.ts +126 -0
  298. package/dist/src/types/index.js +1 -0
  299. package/dist/trpc.d.ts +12 -4
  300. package/dist/trpc.js +5 -11
  301. package/package.json +8 -9
  302. package/prisma/schema.prisma +3 -4
  303. package/prisma/seed.mjs +5 -2
  304. package/prisma.config.ts +16 -0
  305. package/src/context.ts +33 -3
  306. package/src/lib/ai/index.ts +3 -0
  307. package/src/lib/ai/llm-client.ts +23 -0
  308. package/src/lib/prisma.ts +18 -9
  309. package/src/routers/auth.ts +1 -1
  310. package/src/routers/workspace.ts +4 -0
  311. package/src/scripts/purge-deleted-users.ts +1 -3
  312. package/src/services/billing/payment.service.ts +3 -6
  313. package/src/services/billing/usage.service.ts +190 -77
  314. package/src/services/content/copilot.service.ts +23 -32
  315. package/src/services/content/flashcard-progress.service.ts +12 -9
  316. package/src/services/content/flashcard.service.ts +89 -66
  317. package/src/services/content/media-analysis.service.ts +34 -29
  318. package/src/services/content/worksheet-generation.service.test.ts +2 -2
  319. package/src/services/workspace/workspace.service.ts +73 -66
  320. package/src/trpc.ts +5 -13
  321. package/tsconfig.json +3 -0
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Activity log persistence (append-only). Env:
3
+ * - ACTIVITY_LOG_ENABLED=true|false (default true)
4
+ * - ACTIVITY_LOG_SAMPLE_RATE=0..1 (default 1)
5
+ * - ACTIVITY_LOG_RETENTION_DAYS (default 365; used by admin.activityPurgeRetention)
6
+ */
7
+ import type { Prisma, PrismaClient } from "@prisma/client";
8
+ import { ActivityLogCategory, ActivityLogStatus } from "@prisma/client";
9
+ import type { IncomingMessage } from "node:http";
10
+ export declare function isActivityLogEnabled(): boolean;
11
+ export declare function shouldSampleActivity(): boolean;
12
+ /** Recursive redaction for JSON-safe metadata (never log raw credentials). */
13
+ export declare function redactSensitive(value: unknown): unknown;
14
+ export declare function inferCategoryFromTrpcPath(path: string): ActivityLogCategory;
15
+ /** Best-effort workspace id from tRPC input (nested objects included). */
16
+ export declare function extractWorkspaceIdFromInput(raw: unknown): string | undefined;
17
+ export declare function truncateUserAgent(ua: string | undefined, max?: number): string | undefined;
18
+ export declare function getClientIp(req: IncomingMessage): string | undefined;
19
+ export type RecordActivityInput = {
20
+ db: PrismaClient;
21
+ actorUserId: string;
22
+ actorEmailSnapshot?: string | null;
23
+ path: string;
24
+ type: "query" | "mutation" | "subscription";
25
+ status: ActivityLogStatus;
26
+ durationMs: number;
27
+ errorCode?: string | null;
28
+ rawInput?: unknown;
29
+ ipAddress?: string | null;
30
+ userAgent?: string | null;
31
+ httpMethod?: string | null;
32
+ };
33
+ export type RecordExplicitActivityInput = {
34
+ db: PrismaClient;
35
+ actorUserId?: string | null;
36
+ actorEmailSnapshot?: string | null;
37
+ /**
38
+ * A stable action label shown in admin UI/CSV.
39
+ * Example: `cron.purgeDeletedUsers`, `stripe.webhook.checkout.session.completed`
40
+ */
41
+ action: string;
42
+ category: ActivityLogCategory;
43
+ resourceType?: string | null;
44
+ resourceId?: string | null;
45
+ workspaceId?: string | null;
46
+ trpcPath?: string | null;
47
+ httpMethod?: string | null;
48
+ status: ActivityLogStatus;
49
+ errorCode?: string | null;
50
+ durationMs: number;
51
+ ipAddress?: string | null;
52
+ userAgent?: string | null;
53
+ metadata?: unknown;
54
+ /**
55
+ * When true, bypasses ACTIVITY_LOG_SAMPLE_RATE sampling.
56
+ * Useful for low-volume admin-auditable system events (cron/webhooks).
57
+ */
58
+ forceWrite?: boolean;
59
+ };
60
+ /**
61
+ * Persists one activity row. Prefer `scheduleRecordActivity` from request path to avoid blocking.
62
+ */
63
+ export declare function recordActivity(input: RecordActivityInput): Promise<void>;
64
+ export declare function scheduleRecordActivity(input: RecordActivityInput): void;
65
+ /**
66
+ * Persists one explicit (non-tRPC) activity row.
67
+ * This is used for cron jobs, webhooks, and other system-wide background operations.
68
+ */
69
+ export declare function recordExplicitActivity(input: RecordExplicitActivityInput): Promise<void>;
70
+ export declare function scheduleRecordExplicitActivity(input: RecordExplicitActivityInput): void;
71
+ /** Default retention: 365 days. Override with ACTIVITY_LOG_RETENTION_DAYS. */
72
+ export declare function getActivityRetentionDays(): number;
73
+ export declare function deleteActivityLogsOlderThan(db: PrismaClient, cutoff: Date): Promise<{
74
+ deleted: number;
75
+ }>;
76
+ export type ActivityLogFilter = {
77
+ actorUserId?: string;
78
+ workspaceId?: string;
79
+ from?: Date;
80
+ to?: Date;
81
+ category?: ActivityLogCategory;
82
+ status?: ActivityLogStatus;
83
+ search?: string;
84
+ };
85
+ export declare function buildActivityLogWhere(filter: ActivityLogFilter, options?: {
86
+ forceActorUserId?: string;
87
+ }): Prisma.ActivityLogWhereInput;
@@ -0,0 +1,276 @@
1
+ import { ActivityLogCategory } from "@prisma/client";
2
+ import { logger } from "../../lib/logger.js";
3
+ import { getTrpcPathsMatchingDescriptionSearch } from "./activity-human-description.service.js";
4
+ const SENSITIVE_KEY_FRAGMENTS = [
5
+ "password",
6
+ "token",
7
+ "secret",
8
+ "authorization",
9
+ "cookie",
10
+ "credit",
11
+ "card",
12
+ "cvv",
13
+ "ssn",
14
+ ];
15
+ export function isActivityLogEnabled() {
16
+ const v = process.env.ACTIVITY_LOG_ENABLED;
17
+ if (v === undefined || v === "")
18
+ return true;
19
+ return v === "1" || v.toLowerCase() === "true";
20
+ }
21
+ function activitySampleRate() {
22
+ const raw = process.env.ACTIVITY_LOG_SAMPLE_RATE;
23
+ if (!raw)
24
+ return 1;
25
+ const n = Number(raw);
26
+ if (!Number.isFinite(n) || n <= 0)
27
+ return 0;
28
+ if (n > 1)
29
+ return 1;
30
+ return n;
31
+ }
32
+ export function shouldSampleActivity() {
33
+ const rate = activitySampleRate();
34
+ if (rate >= 1)
35
+ return true;
36
+ if (rate <= 0)
37
+ return false;
38
+ return Math.random() < rate;
39
+ }
40
+ /** Recursive redaction for JSON-safe metadata (never log raw credentials). */
41
+ export function redactSensitive(value) {
42
+ if (value === null || value === undefined)
43
+ return value;
44
+ if (typeof value === "string") {
45
+ if (value.length > 2000)
46
+ return `${value.slice(0, 2000)}…[truncated]`;
47
+ return value;
48
+ }
49
+ if (typeof value !== "object")
50
+ return value;
51
+ if (Array.isArray(value)) {
52
+ return value.map((v) => redactSensitive(v));
53
+ }
54
+ const out = {};
55
+ for (const [k, v] of Object.entries(value)) {
56
+ const lower = k.toLowerCase();
57
+ if (SENSITIVE_KEY_FRAGMENTS.some((f) => lower.includes(f))) {
58
+ out[k] = "[REDACTED]";
59
+ continue;
60
+ }
61
+ out[k] = redactSensitive(v);
62
+ }
63
+ return out;
64
+ }
65
+ export function inferCategoryFromTrpcPath(path) {
66
+ const p = path.toLowerCase();
67
+ if (p.startsWith("auth."))
68
+ return ActivityLogCategory.AUTH;
69
+ if (p.startsWith("payment."))
70
+ return ActivityLogCategory.BILLING;
71
+ if (p.startsWith("admin."))
72
+ return ActivityLogCategory.ADMIN;
73
+ if (p.startsWith("workspace.") || p.startsWith("members.") || p.startsWith("member."))
74
+ return ActivityLogCategory.WORKSPACE;
75
+ if (p.startsWith("flashcards.") ||
76
+ p.startsWith("worksheets.") ||
77
+ p.startsWith("studyguide.") ||
78
+ p.startsWith("podcast.") ||
79
+ p.startsWith("annotations.") ||
80
+ p.startsWith("chat.") ||
81
+ p.startsWith("copilot."))
82
+ return ActivityLogCategory.CONTENT;
83
+ if (p.startsWith("notifications."))
84
+ return ActivityLogCategory.SYSTEM;
85
+ return ActivityLogCategory.SYSTEM;
86
+ }
87
+ /** Best-effort workspace id from tRPC input (nested objects included). */
88
+ export function extractWorkspaceIdFromInput(raw) {
89
+ if (raw === null || raw === undefined)
90
+ return undefined;
91
+ if (typeof raw === "object" && !Array.isArray(raw)) {
92
+ const o = raw;
93
+ const direct = o.workspaceId ?? o.workspace_id;
94
+ if (typeof direct === "string" && direct.length > 0)
95
+ return direct;
96
+ for (const v of Object.values(o)) {
97
+ const nested = extractWorkspaceIdFromInput(v);
98
+ if (nested)
99
+ return nested;
100
+ }
101
+ }
102
+ if (Array.isArray(raw)) {
103
+ for (const item of raw) {
104
+ const nested = extractWorkspaceIdFromInput(item);
105
+ if (nested)
106
+ return nested;
107
+ }
108
+ }
109
+ return undefined;
110
+ }
111
+ export function truncateUserAgent(ua, max = 512) {
112
+ if (!ua)
113
+ return undefined;
114
+ return ua.length > max ? ua.slice(0, max) : ua;
115
+ }
116
+ export function getClientIp(req) {
117
+ const xf = req.headers["x-forwarded-for"];
118
+ if (typeof xf === "string")
119
+ return xf.split(",")[0]?.trim();
120
+ if (Array.isArray(xf))
121
+ return xf[0]?.split(",")[0]?.trim();
122
+ const ra = req.socket?.remoteAddress;
123
+ return ra ?? undefined;
124
+ }
125
+ function buildMetadata(path, rawInput) {
126
+ if (rawInput === undefined)
127
+ return undefined;
128
+ try {
129
+ const redacted = redactSensitive(rawInput);
130
+ return { trpcInputPreview: redacted };
131
+ }
132
+ catch {
133
+ return { trpcInputPreview: "[unserializable]" };
134
+ }
135
+ }
136
+ function buildExplicitMetadata(metadata) {
137
+ if (metadata === undefined)
138
+ return undefined;
139
+ try {
140
+ return redactSensitive(metadata);
141
+ }
142
+ catch {
143
+ return { metadata: "[unserializable]" };
144
+ }
145
+ }
146
+ /**
147
+ * Persists one activity row. Prefer `scheduleRecordActivity` from request path to avoid blocking.
148
+ */
149
+ export async function recordActivity(input) {
150
+ if (!isActivityLogEnabled())
151
+ return;
152
+ if (!shouldSampleActivity())
153
+ return;
154
+ const category = inferCategoryFromTrpcPath(input.path);
155
+ const workspaceId = extractWorkspaceIdFromInput(input.rawInput) ?? undefined;
156
+ const action = `trpc.${input.path}`;
157
+ const metadata = buildMetadata(input.path, input.rawInput);
158
+ try {
159
+ await input.db.activityLog.create({
160
+ data: {
161
+ actorUserId: input.actorUserId,
162
+ actorEmailSnapshot: input.actorEmailSnapshot ?? undefined,
163
+ action,
164
+ category,
165
+ trpcPath: input.path,
166
+ httpMethod: input.httpMethod ?? undefined,
167
+ status: input.status,
168
+ errorCode: input.errorCode ?? undefined,
169
+ durationMs: input.durationMs,
170
+ workspaceId,
171
+ ipAddress: input.ipAddress ?? undefined,
172
+ userAgent: truncateUserAgent(input.userAgent ?? undefined),
173
+ metadata: metadata ?? undefined,
174
+ },
175
+ });
176
+ }
177
+ catch (e) {
178
+ logger.error(`ActivityLog write failed: ${e instanceof Error ? e.message : String(e)}`, "ACTIVITY");
179
+ }
180
+ }
181
+ export function scheduleRecordActivity(input) {
182
+ void Promise.resolve()
183
+ .then(() => recordActivity(input))
184
+ .catch((e) => logger.error(`ActivityLog async error: ${e instanceof Error ? e.message : String(e)}`, "ACTIVITY"));
185
+ }
186
+ /**
187
+ * Persists one explicit (non-tRPC) activity row.
188
+ * This is used for cron jobs, webhooks, and other system-wide background operations.
189
+ */
190
+ export async function recordExplicitActivity(input) {
191
+ if (!isActivityLogEnabled())
192
+ return;
193
+ if (!input.forceWrite && !shouldSampleActivity())
194
+ return;
195
+ const metadata = buildExplicitMetadata(input.metadata);
196
+ try {
197
+ await input.db.activityLog.create({
198
+ data: {
199
+ actorUserId: input.actorUserId ?? undefined,
200
+ actorEmailSnapshot: input.actorEmailSnapshot ?? undefined,
201
+ action: input.action,
202
+ category: input.category,
203
+ resourceType: input.resourceType ?? undefined,
204
+ resourceId: input.resourceId ?? undefined,
205
+ workspaceId: input.workspaceId ?? undefined,
206
+ trpcPath: input.trpcPath ?? undefined,
207
+ httpMethod: input.httpMethod ?? undefined,
208
+ status: input.status,
209
+ errorCode: input.errorCode ?? undefined,
210
+ durationMs: input.durationMs,
211
+ ipAddress: input.ipAddress ?? undefined,
212
+ userAgent: truncateUserAgent(input.userAgent ?? undefined),
213
+ metadata: metadata ?? undefined,
214
+ },
215
+ });
216
+ }
217
+ catch (e) {
218
+ logger.error(`ActivityLog explicit write failed: ${e instanceof Error ? e.message : String(e)}`, "ACTIVITY");
219
+ }
220
+ }
221
+ export function scheduleRecordExplicitActivity(input) {
222
+ void Promise.resolve()
223
+ .then(() => recordExplicitActivity(input))
224
+ .catch((e) => logger.error(`ActivityLog explicit async error: ${e instanceof Error ? e.message : String(e)}`, "ACTIVITY"));
225
+ }
226
+ /** Default retention: 365 days. Override with ACTIVITY_LOG_RETENTION_DAYS. */
227
+ export function getActivityRetentionDays() {
228
+ const raw = process.env.ACTIVITY_LOG_RETENTION_DAYS;
229
+ if (!raw)
230
+ return 365;
231
+ const n = Number(raw);
232
+ if (!Number.isFinite(n) || n < 1)
233
+ return 365;
234
+ return Math.min(Math.floor(n), 3650);
235
+ }
236
+ export async function deleteActivityLogsOlderThan(db, cutoff) {
237
+ const result = await db.activityLog.deleteMany({
238
+ where: { createdAt: { lt: cutoff } },
239
+ });
240
+ return { deleted: result.count };
241
+ }
242
+ export function buildActivityLogWhere(filter, options) {
243
+ const where = {};
244
+ if (options?.forceActorUserId) {
245
+ where.actorUserId = options.forceActorUserId;
246
+ }
247
+ else if (filter.actorUserId) {
248
+ where.actorUserId = filter.actorUserId;
249
+ }
250
+ if (filter.workspaceId)
251
+ where.workspaceId = filter.workspaceId;
252
+ if (filter.category)
253
+ where.category = filter.category;
254
+ if (filter.status)
255
+ where.status = filter.status;
256
+ if (filter.from || filter.to) {
257
+ where.createdAt = {};
258
+ if (filter.from)
259
+ where.createdAt.gte = filter.from;
260
+ if (filter.to)
261
+ where.createdAt.lte = filter.to;
262
+ }
263
+ if (filter.search?.trim()) {
264
+ const s = filter.search.trim();
265
+ const pathsFromDescription = getTrpcPathsMatchingDescriptionSearch(s);
266
+ where.OR = [
267
+ { action: { contains: s, mode: "insensitive" } },
268
+ { trpcPath: { contains: s, mode: "insensitive" } },
269
+ { errorCode: { contains: s, mode: "insensitive" } },
270
+ ...(pathsFromDescription.length > 0
271
+ ? [{ trpcPath: { in: pathsFromDescription } }]
272
+ : []),
273
+ ];
274
+ }
275
+ return where;
276
+ }
@@ -0,0 +1,27 @@
1
+ import { test } from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { ActivityLogCategory, ActivityLogStatus } from "@prisma/client";
4
+ import { buildActivityLogWhere, inferCategoryFromTrpcPath, redactSensitive, } from "./activity-log.service.js";
5
+ test("redactSensitive redacts sensitive keys", () => {
6
+ const out = redactSensitive({
7
+ email: "a@b.com",
8
+ password: "secret",
9
+ nested: { authToken: "t", safe: 1 },
10
+ });
11
+ assert.equal(out.password, "[REDACTED]");
12
+ const nested = out.nested;
13
+ assert.equal(nested.authToken, "[REDACTED]");
14
+ assert.equal(nested.safe, 1);
15
+ });
16
+ test("inferCategoryFromTrpcPath maps routers", () => {
17
+ assert.equal(inferCategoryFromTrpcPath("auth.login"), ActivityLogCategory.AUTH);
18
+ assert.equal(inferCategoryFromTrpcPath("payment.checkout"), ActivityLogCategory.BILLING);
19
+ assert.equal(inferCategoryFromTrpcPath("admin.listUsers"), ActivityLogCategory.ADMIN);
20
+ assert.equal(inferCategoryFromTrpcPath("workspace.list"), ActivityLogCategory.WORKSPACE);
21
+ assert.equal(inferCategoryFromTrpcPath("flashcards.list"), ActivityLogCategory.CONTENT);
22
+ });
23
+ test("buildActivityLogWhere forces actor for user scope", () => {
24
+ const w = buildActivityLogWhere({ status: ActivityLogStatus.SUCCESS }, { forceActorUserId: "user-1" });
25
+ assert.equal(w.actorUserId, "user-1");
26
+ assert.equal(w.status, ActivityLogStatus.SUCCESS);
27
+ });
@@ -0,0 +1,270 @@
1
+ import type { PrismaClient } from '@prisma/client';
2
+ import { ActivityLogCategory, ActivityLogStatus, ArtifactType, InvoiceType } from '@prisma/client';
3
+ import { BaseService } from '../base.service.js';
4
+ export interface ActivityFilters {
5
+ from?: Date;
6
+ to?: Date;
7
+ actorUserId?: string;
8
+ workspaceId?: string;
9
+ category?: ActivityLogCategory;
10
+ status?: ActivityLogStatus;
11
+ search?: string;
12
+ }
13
+ export interface ListUsersInput {
14
+ page: number;
15
+ pageSize: number;
16
+ search?: string;
17
+ emailVerified: 'all' | 'yes' | 'no';
18
+ joinedFrom?: Date;
19
+ joinedTo?: Date;
20
+ }
21
+ export interface ListInvoicesInput {
22
+ page: number;
23
+ pageSize: number;
24
+ search?: string;
25
+ status?: string;
26
+ type?: InvoiceType;
27
+ userId?: string;
28
+ from?: Date;
29
+ to?: Date;
30
+ }
31
+ export interface UpsertPlanInput {
32
+ id?: string;
33
+ name: string;
34
+ description?: string;
35
+ type: string;
36
+ price: number;
37
+ stripePriceId?: string;
38
+ interval: string | null;
39
+ active: boolean;
40
+ limits: {
41
+ maxStorageBytes: number;
42
+ maxWorksheets: number;
43
+ maxFlashcards: number;
44
+ maxPodcasts: number;
45
+ maxStudyGuides: number;
46
+ };
47
+ }
48
+ export declare class AdminService extends BaseService {
49
+ constructor(db: PrismaClient);
50
+ getSystemStats(): Promise<{
51
+ totalUsers: number;
52
+ totalWorkspaces: number;
53
+ totalSubscriptions: number;
54
+ revenue: number;
55
+ subscriptionRevenue: number;
56
+ topupRevenue: number;
57
+ }>;
58
+ listUsers(input: ListUsersInput): Promise<{
59
+ users: any[];
60
+ page: number;
61
+ pageSize: number;
62
+ totalCount: number;
63
+ totalPages: number;
64
+ }>;
65
+ listInvoices(input: ListInvoicesInput): Promise<{
66
+ items: any;
67
+ page: number;
68
+ pageSize: number;
69
+ totalCount: any;
70
+ totalPages: number;
71
+ }>;
72
+ listWorkspaces(input: {
73
+ limit: number;
74
+ cursor?: string | null;
75
+ }): Promise<{
76
+ workspaces: ({
77
+ owner: {
78
+ email: string | null;
79
+ name: string | null;
80
+ };
81
+ } & {
82
+ id: string;
83
+ createdAt: Date;
84
+ updatedAt: Date;
85
+ ownerId: string;
86
+ title: string;
87
+ description: string | null;
88
+ icon: string;
89
+ color: string;
90
+ markerColor: string | null;
91
+ folderId: string | null;
92
+ fileBeingAnalyzed: boolean;
93
+ analysisProgress: import("@prisma/client/runtime/client").JsonValue | null;
94
+ needsAnalysis: boolean;
95
+ })[];
96
+ nextCursor: string | undefined;
97
+ }>;
98
+ updateUserRole(actorId: string, input: {
99
+ userId: string;
100
+ roleName: string;
101
+ }): Promise<{
102
+ id: string;
103
+ email: string | null;
104
+ stripe_customer_id: string | null;
105
+ name: string | null;
106
+ emailVerified: Date | null;
107
+ passwordHash: string | null;
108
+ createdAt: Date;
109
+ updatedAt: Date;
110
+ fileAssetId: string | null;
111
+ roleId: string | null;
112
+ deletedAt: Date | null;
113
+ }>;
114
+ listPlans(): import("../../../generated/prisma/internal/prismaNamespace.js").PrismaPromise<({
115
+ limit: {
116
+ id: string;
117
+ createdAt: Date;
118
+ updatedAt: Date;
119
+ planId: string;
120
+ maxStorageBytes: bigint;
121
+ maxWorksheets: number;
122
+ maxFlashcards: number;
123
+ maxPodcasts: number;
124
+ maxStudyGuides: number;
125
+ } | null;
126
+ } & {
127
+ id: string;
128
+ name: string;
129
+ createdAt: Date;
130
+ description: string | null;
131
+ type: string;
132
+ active: boolean;
133
+ price: number;
134
+ stripePriceId: string;
135
+ interval: string | null;
136
+ })[]>;
137
+ upsertPlan(actorId: string, input: UpsertPlanInput): Promise<{
138
+ id: string;
139
+ name: string;
140
+ createdAt: Date;
141
+ description: string | null;
142
+ type: string;
143
+ active: boolean;
144
+ price: number;
145
+ stripePriceId: string;
146
+ interval: string | null;
147
+ }>;
148
+ deletePlan(actorId: string, id: string): Promise<{
149
+ success: boolean;
150
+ message: string;
151
+ }>;
152
+ getUserInvoices(input: {
153
+ userId: string;
154
+ limit?: number;
155
+ }): any;
156
+ getUserDetailedInfo(userId: string): Promise<{
157
+ totalSpent: number;
158
+ purchasedCredits: {
159
+ type: ArtifactType;
160
+ amount: number;
161
+ }[];
162
+ profilePicture: string | null;
163
+ role: {
164
+ id: string;
165
+ name: string;
166
+ } | null;
167
+ subscriptions: ({
168
+ plan: {
169
+ id: string;
170
+ name: string;
171
+ createdAt: Date;
172
+ description: string | null;
173
+ type: string;
174
+ active: boolean;
175
+ price: number;
176
+ stripePriceId: string;
177
+ interval: string | null;
178
+ };
179
+ } & {
180
+ userId: string;
181
+ id: string;
182
+ createdAt: Date;
183
+ planId: string;
184
+ stripeSubscriptionId: string;
185
+ stripeCustomerId: string | null;
186
+ status: string;
187
+ currentPeriodStart: Date;
188
+ currentPeriodEnd: Date;
189
+ cancelAt: Date | null;
190
+ canceledAt: Date | null;
191
+ })[];
192
+ _count: {
193
+ artifacts: number;
194
+ workspaces: number;
195
+ };
196
+ id: string;
197
+ email: string | null;
198
+ stripe_customer_id: string | null;
199
+ name: string | null;
200
+ emailVerified: Date | null;
201
+ passwordHash: string | null;
202
+ createdAt: Date;
203
+ updatedAt: Date;
204
+ fileAssetId: string | null;
205
+ roleId: string | null;
206
+ deletedAt: Date | null;
207
+ }>;
208
+ debugInvoices(): Promise<{
209
+ count: any;
210
+ all: any;
211
+ }>;
212
+ listResourcePrices(): import("../../../generated/prisma/internal/prismaNamespace.js").PrismaPromise<{
213
+ id: string;
214
+ updatedAt: Date;
215
+ resourceType: ArtifactType;
216
+ priceCents: number;
217
+ }[]>;
218
+ upsertResourcePrice(actorId: string, input: {
219
+ resourceType: ArtifactType;
220
+ priceCents: number;
221
+ }): Promise<{
222
+ id: string;
223
+ updatedAt: Date;
224
+ resourceType: ArtifactType;
225
+ priceCents: number;
226
+ }>;
227
+ listRecentInvoices(limit: number): Promise<any>;
228
+ activityList(input: ActivityFilters & {
229
+ page: number;
230
+ limit: number;
231
+ }): Promise<{
232
+ items: {
233
+ id: string;
234
+ createdAt: Date;
235
+ action: string;
236
+ description: string;
237
+ category: ActivityLogCategory;
238
+ trpcPath: string | null;
239
+ status: ActivityLogStatus;
240
+ durationMs: number;
241
+ errorCode: string | null;
242
+ ipAddress: string | null;
243
+ actor: {
244
+ id: string;
245
+ email: string | null;
246
+ name: string | null;
247
+ } | null;
248
+ workspace: {
249
+ id: string;
250
+ title: string;
251
+ } | null;
252
+ metadata: import("@prisma/client/runtime/client").JsonValue;
253
+ }[];
254
+ page: number;
255
+ pageSize: number;
256
+ totalCount: number;
257
+ totalPages: number;
258
+ }>;
259
+ activityExportCsv(input: ActivityFilters & {
260
+ maxRows: number;
261
+ }): Promise<{
262
+ csv: string;
263
+ count: number;
264
+ }>;
265
+ activityPurgeRetention(): Promise<{
266
+ deleted: number;
267
+ retentionDays: number;
268
+ cutoff: Date;
269
+ }>;
270
+ }