@goscribe/server 1.3.4 → 1.6.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 (383) hide show
  1. package/.env.example +12 -0
  2. package/.vscode/settings.json +3 -0
  3. package/REFACTOR_NOTES.md +60 -0
  4. package/TESTING_PROMPT.md +225 -0
  5. package/dist/context.d.ts +14 -1
  6. package/dist/context.js +23 -2
  7. package/dist/controllers/admin.controller.d.ts +715 -0
  8. package/dist/controllers/admin.controller.js +9 -0
  9. package/dist/controllers/annotations.controller.d.ts +439 -0
  10. package/dist/controllers/annotations.controller.js +9 -0
  11. package/dist/controllers/app-router.controller.d.ts +3011 -0
  12. package/dist/controllers/app-router.controller.js +38 -0
  13. package/dist/controllers/app-router.controller.test.d.ts +1 -0
  14. package/dist/controllers/app-router.controller.test.js +36 -0
  15. package/dist/controllers/auth.controller.d.ts +323 -0
  16. package/dist/controllers/auth.controller.js +9 -0
  17. package/dist/controllers/base.controller.d.ts +4 -0
  18. package/dist/controllers/base.controller.js +5 -0
  19. package/dist/controllers/chat.controller.d.ts +341 -0
  20. package/dist/controllers/chat.controller.js +9 -0
  21. package/dist/controllers/copilot.controller.d.ts +397 -0
  22. package/dist/controllers/copilot.controller.js +9 -0
  23. package/dist/controllers/flashcards.controller.d.ts +651 -0
  24. package/dist/controllers/flashcards.controller.js +9 -0
  25. package/dist/controllers/members.controller.d.ts +339 -0
  26. package/dist/controllers/members.controller.js +9 -0
  27. package/dist/controllers/notifications.controller.d.ts +199 -0
  28. package/dist/controllers/notifications.controller.js +9 -0
  29. package/dist/controllers/payment.controller.d.ts +181 -0
  30. package/dist/controllers/payment.controller.js +9 -0
  31. package/dist/controllers/podcast.controller.d.ts +575 -0
  32. package/dist/controllers/podcast.controller.js +9 -0
  33. package/dist/controllers/router-module.controller.d.ts +5 -0
  34. package/dist/controllers/router-module.controller.js +6 -0
  35. package/dist/controllers/studyguide.controller.d.ts +73 -0
  36. package/dist/controllers/studyguide.controller.js +9 -0
  37. package/dist/controllers/worksheets.controller.d.ts +829 -0
  38. package/dist/controllers/worksheets.controller.js +9 -0
  39. package/dist/controllers/workspace.controller.d.ts +1207 -0
  40. package/dist/controllers/workspace.controller.js +9 -0
  41. package/dist/lib/activity_human_description.test.js +16 -15
  42. package/dist/lib/activity_log_service.test.js +28 -23
  43. package/dist/lib/ai/config.d.ts +20 -0
  44. package/dist/lib/ai/config.js +31 -0
  45. package/dist/lib/ai/embedding-client.d.ts +8 -0
  46. package/dist/lib/ai/embedding-client.js +30 -0
  47. package/dist/lib/ai/index.d.ts +48 -0
  48. package/dist/lib/ai/index.js +29 -0
  49. package/dist/lib/ai/inference-backend/client.d.ts +28 -0
  50. package/dist/lib/ai/inference-backend/client.js +301 -0
  51. package/dist/lib/ai/inference-backend/mocks.d.ts +12 -0
  52. package/dist/lib/ai/inference-backend/mocks.js +133 -0
  53. package/dist/lib/ai/inference-backend/types.d.ts +44 -0
  54. package/dist/lib/ai/inference-backend/types.js +1 -0
  55. package/dist/lib/ai/json-parse.d.ts +2 -0
  56. package/dist/lib/ai/json-parse.js +34 -0
  57. package/dist/lib/ai/llm-client.d.ts +7 -0
  58. package/dist/lib/ai/llm-client.js +36 -0
  59. package/dist/lib/ai/mock.d.ts +2 -0
  60. package/dist/lib/ai/mock.js +10 -0
  61. package/dist/lib/ai/types.d.ts +9 -0
  62. package/dist/lib/ai/types.js +1 -0
  63. package/dist/lib/chunking.d.ts +19 -0
  64. package/dist/lib/chunking.js +47 -0
  65. package/dist/lib/curated-kb-seed.d.ts +12 -0
  66. package/dist/lib/curated-kb-seed.js +155 -0
  67. package/dist/lib/email.js +67 -108
  68. package/dist/lib/embeddings.d.ts +2 -0
  69. package/dist/lib/embeddings.js +1 -0
  70. package/dist/lib/ensure-curated-kb-catalog.d.ts +6 -0
  71. package/dist/lib/ensure-curated-kb-catalog.js +53 -0
  72. package/dist/lib/env.d.ts +1 -5
  73. package/dist/lib/env.js +2 -7
  74. package/dist/lib/inference.d.ts +1 -8
  75. package/dist/lib/inference.js +1 -19
  76. package/dist/lib/kb-meta.d.ts +8 -0
  77. package/dist/lib/kb-meta.js +77 -0
  78. package/dist/lib/note-text.d.ts +1 -0
  79. package/dist/lib/note-text.js +47 -0
  80. package/dist/lib/notification-service.test.js +37 -36
  81. package/dist/lib/pdf.d.ts +11 -0
  82. package/dist/lib/pdf.js +11 -0
  83. package/dist/lib/usage_service.d.ts +2 -1
  84. package/dist/lib/usage_service.js +30 -12
  85. package/dist/lib/worksheet-generation.js +4 -4
  86. package/dist/lib/worksheet-generation.test.js +32 -17
  87. package/dist/lib/workspace-kb.d.ts +5 -0
  88. package/dist/lib/workspace-kb.js +7 -0
  89. package/dist/models/controller-context.model.d.ts +8 -0
  90. package/dist/models/controller-context.model.js +1 -0
  91. package/dist/repositories/artifact.repository.d.ts +60 -0
  92. package/dist/repositories/artifact.repository.js +40 -0
  93. package/dist/repositories/base.repository.d.ts +14 -0
  94. package/dist/repositories/base.repository.js +14 -0
  95. package/dist/repositories/invitation.repository.d.ts +94 -0
  96. package/dist/repositories/invitation.repository.js +44 -0
  97. package/dist/repositories/notification.repository.d.ts +72 -0
  98. package/dist/repositories/notification.repository.js +44 -0
  99. package/dist/repositories/router-module.repository.d.ts +10 -0
  100. package/dist/repositories/router-module.repository.js +14 -0
  101. package/dist/repositories/user.repository.d.ts +74 -0
  102. package/dist/repositories/user.repository.js +37 -0
  103. package/dist/repositories/workspace-member.repository.d.ts +31 -0
  104. package/dist/repositories/workspace-member.repository.js +31 -0
  105. package/dist/repositories/workspace.repository.d.ts +97 -0
  106. package/dist/repositories/workspace.repository.js +79 -0
  107. package/dist/routers/_app.d.ts +566 -111
  108. package/dist/routers/_app.js +4 -0
  109. package/dist/routers/admin.d.ts +0 -4
  110. package/dist/routers/admin.js +21 -549
  111. package/dist/routers/annotations.js +12 -170
  112. package/dist/routers/artifactVersions.d.ts +65 -0
  113. package/dist/routers/artifactVersions.js +14 -0
  114. package/dist/routers/auth.d.ts +0 -6
  115. package/dist/routers/auth.js +37 -422
  116. package/dist/routers/chat.js +15 -229
  117. package/dist/routers/copilot.d.ts +14 -13
  118. package/dist/routers/copilot.js +13 -532
  119. package/dist/routers/flashcards.d.ts +17 -6
  120. package/dist/routers/flashcards.js +23 -349
  121. package/dist/routers/knowledgeBase.d.ts +421 -0
  122. package/dist/routers/knowledgeBase.js +118 -0
  123. package/dist/routers/members.d.ts +0 -41
  124. package/dist/routers/members.js +22 -710
  125. package/dist/routers/notes.d.ts +94 -0
  126. package/dist/routers/notes.js +37 -0
  127. package/dist/routers/notifications.js +7 -109
  128. package/dist/routers/payment.d.ts +2 -12
  129. package/dist/routers/payment.js +11 -393
  130. package/dist/routers/podcast.d.ts +1 -1
  131. package/dist/routers/podcast.js +11 -784
  132. package/dist/routers/studyguide.js +3 -129
  133. package/dist/routers/worksheets.d.ts +29 -14
  134. package/dist/routers/worksheets.js +49 -628
  135. package/dist/routers/workspace.d.ts +27 -71
  136. package/dist/routers/workspace.js +29 -922
  137. package/dist/scripts/purge-deleted-users.js +2 -2
  138. package/dist/server.js +10 -3
  139. package/dist/services/activity/activity-human-description.service.d.ts +13 -0
  140. package/dist/services/activity/activity-human-description.service.js +221 -0
  141. package/dist/services/activity/activity-human-description.service.test.d.ts +1 -0
  142. package/dist/services/activity/activity-human-description.service.test.js +16 -0
  143. package/dist/services/activity/activity-log.service.d.ts +87 -0
  144. package/dist/services/activity/activity-log.service.js +276 -0
  145. package/dist/services/activity/activity-log.service.test.d.ts +1 -0
  146. package/dist/services/activity/activity-log.service.test.js +27 -0
  147. package/dist/services/activity-human-description.service.d.ts +13 -0
  148. package/dist/services/activity-human-description.service.js +221 -0
  149. package/dist/services/activity-human-description.service.test.d.ts +1 -0
  150. package/dist/services/activity-human-description.service.test.js +16 -0
  151. package/dist/services/activity-log.service.d.ts +87 -0
  152. package/dist/services/activity-log.service.js +276 -0
  153. package/dist/services/activity-log.service.test.d.ts +1 -0
  154. package/dist/services/activity-log.service.test.js +27 -0
  155. package/dist/services/admin/admin.service.d.ts +270 -0
  156. package/dist/services/admin/admin.service.js +476 -0
  157. package/dist/services/admin.service.d.ts +270 -0
  158. package/dist/services/admin.service.js +476 -0
  159. package/dist/services/ai/ai-session.service.d.ts +5 -0
  160. package/dist/services/ai/ai-session.service.js +4 -0
  161. package/dist/services/ai-session.service.d.ts +60 -0
  162. package/dist/services/ai-session.service.js +561 -0
  163. package/dist/services/annotation.service.d.ts +177 -0
  164. package/dist/services/annotation.service.js +154 -0
  165. package/dist/services/artifact-notification.service.d.ts +14 -0
  166. package/dist/services/artifact-notification.service.js +20 -0
  167. package/dist/services/artifact-version.service.d.ts +38 -0
  168. package/dist/services/artifact-version.service.js +129 -0
  169. package/dist/services/artifacts/annotation.service.d.ts +177 -0
  170. package/dist/services/artifacts/annotation.service.js +154 -0
  171. package/dist/services/artifacts/artifact-version.service.d.ts +38 -0
  172. package/dist/services/artifacts/artifact-version.service.js +129 -0
  173. package/dist/services/artifacts/chat.service.d.ts +127 -0
  174. package/dist/services/artifacts/chat.service.js +182 -0
  175. package/dist/services/artifacts/study-guide.service.d.ts +18 -0
  176. package/dist/services/artifacts/study-guide.service.js +65 -0
  177. package/dist/services/auth/auth.service.d.ts +94 -0
  178. package/dist/services/auth/auth.service.js +368 -0
  179. package/dist/services/auth.service.d.ts +94 -0
  180. package/dist/services/auth.service.js +368 -0
  181. package/dist/services/base.service.d.ts +14 -0
  182. package/dist/services/base.service.js +14 -0
  183. package/dist/services/billing/payment.service.d.ts +44 -0
  184. package/dist/services/billing/payment.service.js +365 -0
  185. package/dist/services/billing/subscription.service.d.ts +37 -0
  186. package/dist/services/billing/subscription.service.js +654 -0
  187. package/dist/services/billing/usage.service.d.ts +47 -0
  188. package/dist/services/billing/usage.service.js +149 -0
  189. package/dist/services/chat.service.d.ts +127 -0
  190. package/dist/services/chat.service.js +182 -0
  191. package/dist/services/content/copilot.service.d.ts +113 -0
  192. package/dist/services/content/copilot.service.js +439 -0
  193. package/dist/services/content/flashcard-progress.service.d.ts +159 -0
  194. package/dist/services/content/flashcard-progress.service.js +432 -0
  195. package/dist/services/content/flashcard.service.d.ts +184 -0
  196. package/dist/services/content/flashcard.service.js +339 -0
  197. package/dist/services/content/media-analysis.service.d.ts +23 -0
  198. package/dist/services/content/media-analysis.service.js +404 -0
  199. package/dist/services/content/podcast.service.d.ts +267 -0
  200. package/dist/services/content/podcast.service.js +653 -0
  201. package/dist/services/content/worksheet-content.service.d.ts +37 -0
  202. package/dist/services/content/worksheet-content.service.js +84 -0
  203. package/dist/services/content/worksheet-content.service.test.d.ts +1 -0
  204. package/dist/services/content/worksheet-content.service.test.js +69 -0
  205. package/dist/services/content/worksheet-generation.service.d.ts +91 -0
  206. package/dist/services/content/worksheet-generation.service.js +95 -0
  207. package/dist/services/content/worksheet-generation.service.test.d.ts +1 -0
  208. package/dist/services/content/worksheet-generation.service.test.js +20 -0
  209. package/dist/services/content/worksheet.service.d.ts +347 -0
  210. package/dist/services/content/worksheet.service.js +599 -0
  211. package/dist/services/copilot.service.d.ts +116 -0
  212. package/dist/services/copilot.service.js +447 -0
  213. package/dist/services/flashcard-progress.service.d.ts +2 -2
  214. package/dist/services/flashcard-progress.service.js +3 -2
  215. package/dist/services/flashcard.service.d.ts +140 -0
  216. package/dist/services/flashcard.service.js +325 -0
  217. package/dist/services/invitation.service.d.ts +66 -0
  218. package/dist/services/invitation.service.js +348 -0
  219. package/dist/services/knowledge/knowledge-base.service.d.ts +316 -0
  220. package/dist/services/knowledge/knowledge-base.service.js +544 -0
  221. package/dist/services/knowledge-base.service.d.ts +316 -0
  222. package/dist/services/knowledge-base.service.js +536 -0
  223. package/dist/services/media-analysis.service.d.ts +23 -0
  224. package/dist/services/media-analysis.service.js +384 -0
  225. package/dist/services/member.service.d.ts +36 -0
  226. package/dist/services/member.service.js +193 -0
  227. package/dist/services/members/invitation.service.d.ts +66 -0
  228. package/dist/services/members/invitation.service.js +348 -0
  229. package/dist/services/members/member.service.d.ts +36 -0
  230. package/dist/services/members/member.service.js +193 -0
  231. package/dist/services/note.service.d.ts +55 -0
  232. package/dist/services/note.service.js +111 -0
  233. package/dist/services/notification.service.d.ts +214 -0
  234. package/dist/services/notification.service.js +550 -0
  235. package/dist/services/notification.service.test.d.ts +1 -0
  236. package/dist/services/notification.service.test.js +87 -0
  237. package/dist/services/notifications/notification.service.d.ts +214 -0
  238. package/dist/services/notifications/notification.service.js +550 -0
  239. package/dist/services/notifications/notification.service.test.d.ts +1 -0
  240. package/dist/services/notifications/notification.service.test.js +87 -0
  241. package/dist/services/payment.service.d.ts +55 -0
  242. package/dist/services/payment.service.js +368 -0
  243. package/dist/services/podcast.service.d.ts +267 -0
  244. package/dist/services/podcast.service.js +654 -0
  245. package/dist/services/router-module.service.d.ts +7 -0
  246. package/dist/services/router-module.service.js +10 -0
  247. package/dist/services/study-guide.service.d.ts +18 -0
  248. package/dist/services/study-guide.service.js +65 -0
  249. package/dist/services/subscription.service.d.ts +37 -0
  250. package/dist/services/subscription.service.js +654 -0
  251. package/dist/services/usage-limit-policy.service.d.ts +12 -0
  252. package/dist/services/usage-limit-policy.service.js +22 -0
  253. package/dist/services/usage-limit-policy.service.test.d.ts +1 -0
  254. package/dist/services/usage-limit-policy.service.test.js +46 -0
  255. package/dist/services/usage.service.d.ts +27 -0
  256. package/dist/services/usage.service.js +77 -0
  257. package/dist/services/worksheet-content.service.d.ts +42 -0
  258. package/dist/services/worksheet-content.service.js +84 -0
  259. package/dist/services/worksheet-content.service.test.d.ts +1 -0
  260. package/dist/services/worksheet-content.service.test.js +69 -0
  261. package/dist/services/worksheet-generation.service.d.ts +91 -0
  262. package/dist/services/worksheet-generation.service.js +95 -0
  263. package/dist/services/worksheet-generation.service.test.d.ts +1 -0
  264. package/dist/services/worksheet-generation.service.test.js +20 -0
  265. package/dist/services/worksheet.service.d.ts +385 -0
  266. package/dist/services/worksheet.service.js +596 -0
  267. package/dist/services/workspace/workspace-analytics.service.d.ts +24 -0
  268. package/dist/services/workspace/workspace-analytics.service.js +95 -0
  269. package/dist/services/workspace/workspace-kb.service.d.ts +40 -0
  270. package/dist/services/workspace/workspace-kb.service.js +184 -0
  271. package/dist/services/workspace/workspace.service.d.ts +263 -0
  272. package/dist/services/workspace/workspace.service.js +401 -0
  273. package/dist/services/workspace-analytics.service.d.ts +24 -0
  274. package/dist/services/workspace-analytics.service.js +95 -0
  275. package/dist/services/workspace-kb.service.d.ts +40 -0
  276. package/dist/services/workspace-kb.service.js +184 -0
  277. package/dist/services/workspace-progress.service.d.ts +27 -0
  278. package/dist/services/workspace-progress.service.js +56 -0
  279. package/dist/services/workspace-progress.service.test.d.ts +1 -0
  280. package/dist/services/workspace-progress.service.test.js +49 -0
  281. package/dist/services/workspace.service.d.ts +307 -0
  282. package/dist/services/workspace.service.js +390 -0
  283. package/dist/trpc.d.ts +12 -4
  284. package/dist/trpc.js +7 -13
  285. package/package.json +5 -6
  286. package/prisma/migrations/20260509000001_add_knowledge_base/migration.sql +99 -0
  287. package/prisma/migrations/20260509000002_curate_knowledge_base/migration.sql +52 -0
  288. package/prisma/migrations/20260522000000_add_notes/migration.sql +27 -0
  289. package/prisma/migrations/20260524000000_remove_notes/migration.sql +3 -0
  290. package/prisma/schema.prisma +150 -48
  291. package/prisma/seed.mjs +67 -0
  292. package/scripts/debug/README.md +4 -0
  293. package/src/README.md +63 -0
  294. package/src/context.ts +33 -3
  295. package/src/lib/ai/config.ts +34 -0
  296. package/src/lib/ai/embedding-client.ts +47 -0
  297. package/src/lib/ai/index.ts +65 -0
  298. package/src/lib/ai/inference-backend/client.ts +479 -0
  299. package/src/lib/ai/inference-backend/mocks.ts +171 -0
  300. package/src/lib/ai/inference-backend/types.ts +50 -0
  301. package/src/lib/ai/json-parse.ts +35 -0
  302. package/src/lib/ai/llm-client.ts +54 -0
  303. package/src/lib/ai/mock.ts +12 -0
  304. package/src/lib/ai/types.ts +11 -0
  305. package/src/lib/chunking.ts +81 -0
  306. package/src/lib/curated-kb-seed.ts +164 -0
  307. package/src/lib/email.ts +77 -115
  308. package/src/lib/embeddings.ts +9 -0
  309. package/src/lib/ensure-curated-kb-catalog.ts +60 -0
  310. package/src/lib/env.ts +2 -7
  311. package/src/lib/inference.ts +1 -21
  312. package/src/lib/kb-meta.ts +81 -0
  313. package/src/lib/pdf.ts +23 -0
  314. package/src/lib/workspace-kb.ts +7 -0
  315. package/src/repositories/artifact.repository.ts +55 -0
  316. package/src/repositories/base.repository.ts +19 -0
  317. package/src/repositories/invitation.repository.ts +53 -0
  318. package/src/repositories/notification.repository.ts +53 -0
  319. package/src/repositories/user.repository.ts +44 -0
  320. package/src/repositories/workspace-member.repository.ts +38 -0
  321. package/src/repositories/workspace.repository.ts +89 -0
  322. package/src/routers/_app.ts +4 -0
  323. package/src/routers/admin.ts +124 -692
  324. package/src/routers/annotations.ts +25 -203
  325. package/src/routers/artifactVersions.ts +32 -0
  326. package/src/routers/auth.ts +82 -520
  327. package/src/routers/chat.ts +42 -245
  328. package/src/routers/copilot.ts +41 -666
  329. package/src/routers/flashcards.ts +108 -404
  330. package/src/routers/knowledgeBase.ts +216 -0
  331. package/src/routers/members.ts +60 -782
  332. package/src/routers/notifications.ts +15 -117
  333. package/src/routers/payment.ts +37 -446
  334. package/src/routers/podcast.ts +36 -898
  335. package/src/routers/studyguide.ts +5 -144
  336. package/src/routers/worksheets.ts +171 -735
  337. package/src/routers/workspace.ts +141 -1108
  338. package/src/scripts/purge-deleted-users.ts +2 -2
  339. package/src/server.ts +10 -3
  340. package/src/{lib/activity_human_description.test.ts → services/activity/activity-human-description.service.test.ts} +1 -1
  341. package/src/{lib/activity_log_service.test.ts → services/activity/activity-log.service.test.ts} +1 -1
  342. package/src/{lib/activity_log_service.ts → services/activity/activity-log.service.ts} +2 -2
  343. package/src/services/admin/admin.service.ts +612 -0
  344. package/src/services/ai/ai-session.service.ts +5 -0
  345. package/src/services/artifacts/annotation.service.ts +189 -0
  346. package/src/services/artifacts/artifact-version.service.ts +151 -0
  347. package/src/services/artifacts/chat.service.ts +197 -0
  348. package/src/services/artifacts/study-guide.service.ts +72 -0
  349. package/src/services/auth/auth.service.ts +473 -0
  350. package/src/services/base.service.ts +19 -0
  351. package/src/services/billing/payment.service.ts +433 -0
  352. package/src/{lib/subscription_service.ts → services/billing/subscription.service.ts} +5 -5
  353. package/src/services/billing/usage.service.ts +207 -0
  354. package/src/services/content/copilot.service.ts +587 -0
  355. package/src/services/{flashcard-progress.service.ts → content/flashcard-progress.service.ts} +18 -12
  356. package/src/services/content/flashcard.service.ts +417 -0
  357. package/src/services/content/media-analysis.service.ts +561 -0
  358. package/src/services/content/podcast.service.ts +777 -0
  359. package/src/services/content/worksheet-content.service.test.ts +83 -0
  360. package/src/services/content/worksheet-content.service.ts +117 -0
  361. package/src/{lib/worksheet-generation.test.ts → services/content/worksheet-generation.service.test.ts} +3 -3
  362. package/src/services/content/worksheet.service.ts +751 -0
  363. package/src/services/knowledge/knowledge-base.service.ts +705 -0
  364. package/src/services/members/invitation.service.ts +427 -0
  365. package/src/services/members/member.service.ts +241 -0
  366. package/src/{lib/notification-service.test.ts → services/notifications/notification.service.test.ts} +2 -2
  367. package/src/{lib/notification-service.ts → services/notifications/notification.service.ts} +102 -1
  368. package/src/services/workspace/workspace-analytics.service.ts +107 -0
  369. package/src/services/workspace/workspace-kb.service.ts +273 -0
  370. package/src/services/workspace/workspace.service.ts +488 -0
  371. package/src/trpc.ts +7 -15
  372. package/src/lib/ai-session.ts +0 -704
  373. package/src/lib/usage_service.ts +0 -74
  374. package/src/lib/workspace-access.ts +0 -13
  375. /package/{check-difficulty.cjs → scripts/debug/check-difficulty.cjs} +0 -0
  376. /package/{check-questions.cjs → scripts/debug/check-questions.cjs} +0 -0
  377. /package/{db-summary.cjs → scripts/debug/db-summary.cjs} +0 -0
  378. /package/{mcq-test.cjs → scripts/debug/mcq-test.cjs} +0 -0
  379. /package/{test-generate.js → scripts/debug/test-generate.js} +0 -0
  380. /package/{test-ratio.cjs → scripts/debug/test-ratio.cjs} +0 -0
  381. /package/{zod-test.cjs → scripts/debug/zod-test.cjs} +0 -0
  382. /package/src/{lib/activity_human_description.ts → services/activity/activity-human-description.service.ts} +0 -0
  383. /package/src/{lib/worksheet-generation.ts → services/content/worksheet-generation.service.ts} +0 -0
@@ -12,6 +12,8 @@ import { admin } from './admin.js';
12
12
  import { paymentRouter } from './payment.js';
13
13
  import { copilot } from './copilot.js';
14
14
  import { notifications } from './notifications.js';
15
+ import { knowledgeBase } from './knowledgeBase.js';
16
+ import { artifactVersions } from './artifactVersions.js';
15
17
  export const appRouter = router({
16
18
  auth,
17
19
  workspace,
@@ -25,6 +27,8 @@ export const appRouter = router({
25
27
  payment: paymentRouter,
26
28
  copilot,
27
29
  notifications,
30
+ knowledgeBase,
31
+ artifactVersions,
28
32
  // Public member endpoints (for invitation acceptance)
29
33
  member: router({
30
34
  acceptInvite: members.acceptInvite,
@@ -44,9 +44,6 @@ export declare const admin: import("@trpc/server").TRPCBuiltRouter<{
44
44
  };
45
45
  meta: object;
46
46
  }>;
47
- /**
48
- * Paginated global invoice list for admin Invoices page (search + filters).
49
- */
50
47
  listInvoices: import("@trpc/server").TRPCQueryProcedure<{
51
48
  input: {
52
49
  page?: number | undefined;
@@ -348,7 +345,6 @@ export declare const admin: import("@trpc/server").TRPCBuiltRouter<{
348
345
  };
349
346
  meta: object;
350
347
  }>;
351
- /** Deletes rows older than ACTIVITY_LOG_RETENTION_DAYS (default 365). Run manually or via cron. */
352
348
  activityPurgeRetention: import("@trpc/server").TRPCMutationProcedure<{
353
349
  input: void;
354
350
  output: {
@@ -1,18 +1,7 @@
1
1
  import { z } from 'zod';
2
2
  import { router, adminProcedure } from '../trpc.js';
3
- import { logger } from '../lib/logger.js';
4
- import { stripe } from '../lib/stripe.js';
5
3
  import { ActivityLogCategory, ActivityLogStatus, ArtifactType, InvoiceType } from '@prisma/client';
6
- import { buildActivityLogWhere, deleteActivityLogsOlderThan, getActivityRetentionDays, } from '../lib/activity_log_service.js';
7
- import { getActivityHumanDescription } from '../lib/activity_human_description.js';
8
- function csvEscapeCell(value) {
9
- if (value === null || value === undefined)
10
- return '';
11
- const s = String(value);
12
- if (/[",\n\r]/.test(s))
13
- return `"${s.replace(/"/g, '""')}"`;
14
- return s;
15
- }
4
+ import { AdminService } from '../services/admin/admin.service.js';
16
5
  const activityLogFiltersInput = z.object({
17
6
  from: z.coerce.date().optional(),
18
7
  to: z.coerce.date().optional(),
@@ -29,10 +18,8 @@ const activityListInput = activityLogFiltersInput.extend({
29
18
  const listUsersInput = z.object({
30
19
  page: z.number().int().min(1).default(1),
31
20
  pageSize: z.number().int().min(1).max(100).default(10),
32
- /** Trims; matches name, email, or id (substring). */
33
21
  search: z.string().max(200).optional(),
34
22
  emailVerified: z.enum(['all', 'yes', 'no']).default('all'),
35
- /** Filter by account creation time (joined date). */
36
23
  joinedFrom: z.coerce.date().optional(),
37
24
  joinedTo: z.coerce.date().optional(),
38
25
  });
@@ -47,258 +34,29 @@ const listInvoicesInput = z.object({
47
34
  to: z.coerce.date().optional(),
48
35
  });
49
36
  export const admin = router({
50
- getSystemStats: adminProcedure
51
- .query(async ({ ctx }) => {
52
- const totalUsers = await ctx.db.user.count();
53
- const totalWorkspaces = await ctx.db.workspace.count();
54
- const totalSubscriptions = await ctx.db.subscription.count({
55
- where: { status: 'active' }
56
- });
57
- // Calculate revenue breakdown
58
- const subRevenueResult = await ctx.db.invoice.aggregate({
59
- where: { status: 'paid', type: 'SUBSCRIPTION' },
60
- _sum: { amountPaid: true }
61
- });
62
- const topupRevenueResult = await ctx.db.invoice.aggregate({
63
- where: { status: 'paid', type: 'TOPUP' },
64
- _sum: { amountPaid: true }
65
- });
66
- const subRevenue = Number(subRevenueResult._sum.amountPaid || 0) / 100;
67
- const topupRevenue = Number(topupRevenueResult._sum.amountPaid || 0) / 100;
68
- return {
69
- totalUsers,
70
- totalWorkspaces,
71
- totalSubscriptions,
72
- revenue: subRevenue + topupRevenue,
73
- subscriptionRevenue: subRevenue,
74
- topupRevenue: topupRevenue,
75
- };
76
- }),
37
+ getSystemStats: adminProcedure.query(({ ctx }) => new AdminService(ctx.db).getSystemStats()),
77
38
  listUsers: adminProcedure
78
39
  .input(listUsersInput)
79
- .query(async ({ ctx, input }) => {
80
- const search = input.search?.trim();
81
- const baseRoleWhere = {
82
- role: {
83
- name: { not: 'System Admin' },
84
- },
85
- };
86
- const searchWhere = search && search.length > 0
87
- ? {
88
- OR: [
89
- { email: { contains: search, mode: 'insensitive' } },
90
- { name: { contains: search, mode: 'insensitive' } },
91
- { id: { contains: search } },
92
- ],
93
- }
94
- : undefined;
95
- const verifiedWhere = input.emailVerified === 'yes'
96
- ? { emailVerified: { not: null } }
97
- : input.emailVerified === 'no'
98
- ? { emailVerified: null }
99
- : undefined;
100
- const joinedWhere = input.joinedFrom || input.joinedTo
101
- ? {
102
- createdAt: {
103
- ...(input.joinedFrom ? { gte: input.joinedFrom } : {}),
104
- ...(input.joinedTo ? { lte: input.joinedTo } : {}),
105
- },
106
- }
107
- : undefined;
108
- const where = {
109
- AND: [
110
- baseRoleWhere,
111
- ...(searchWhere ? [searchWhere] : []),
112
- ...(verifiedWhere ? [verifiedWhere] : []),
113
- ...(joinedWhere ? [joinedWhere] : []),
114
- ],
115
- };
116
- const totalCount = await ctx.db.user.count({ where });
117
- const pageSize = input.pageSize;
118
- const totalPages = Math.max(1, Math.ceil(totalCount / pageSize));
119
- const page = Math.min(Math.max(1, input.page), totalPages);
120
- const skip = (page - 1) * pageSize;
121
- const users = await ctx.db.user.findMany({
122
- where,
123
- skip,
124
- take: pageSize,
125
- orderBy: { createdAt: 'desc' },
126
- include: {
127
- role: true,
128
- profilePicture: true,
129
- subscriptions: {
130
- orderBy: { createdAt: 'desc' },
131
- take: 1,
132
- include: {
133
- plan: true,
134
- },
135
- },
136
- },
137
- });
138
- return {
139
- users: users.map((user) => ({
140
- ...user,
141
- profilePicture: user.profilePicture?.objectKey
142
- ? `/profile-picture/${user.profilePicture.objectKey}?t=${new Date(user.updatedAt).getTime()}`
143
- : null,
144
- })),
145
- page,
146
- pageSize,
147
- totalCount,
148
- totalPages,
149
- };
150
- }),
151
- /**
152
- * Paginated global invoice list for admin Invoices page (search + filters).
153
- */
40
+ .query(({ ctx, input }) => new AdminService(ctx.db).listUsers(input)),
154
41
  listInvoices: adminProcedure
155
42
  .input(listInvoicesInput)
156
- .query(async ({ ctx, input }) => {
157
- const search = input.search?.trim();
158
- const searchWhere = search && search.length > 0
159
- ? {
160
- OR: [
161
- { id: { contains: search } },
162
- { stripeInvoiceId: { contains: search, mode: 'insensitive' } },
163
- {
164
- user: {
165
- email: { contains: search, mode: 'insensitive' },
166
- },
167
- },
168
- {
169
- user: {
170
- name: { contains: search, mode: 'insensitive' },
171
- },
172
- },
173
- ],
174
- }
175
- : undefined;
176
- const userIdWhere = input.userId?.trim()
177
- ? { userId: input.userId.trim() }
178
- : undefined;
179
- const statusWhere = input.status?.trim()
180
- ? { status: input.status.trim() }
181
- : undefined;
182
- const typeWhere = input.type ? { type: input.type } : undefined;
183
- const dateWhere = input.from || input.to
184
- ? {
185
- createdAt: {
186
- ...(input.from ? { gte: input.from } : {}),
187
- ...(input.to ? { lte: input.to } : {}),
188
- },
189
- }
190
- : undefined;
191
- const andParts = [
192
- ...(searchWhere ? [searchWhere] : []),
193
- ...(userIdWhere ? [userIdWhere] : []),
194
- ...(statusWhere ? [statusWhere] : []),
195
- ...(typeWhere ? [typeWhere] : []),
196
- ...(dateWhere ? [dateWhere] : []),
197
- ];
198
- const where = andParts.length > 0 ? { AND: andParts } : {};
199
- const totalCount = await ctx.db.invoice.count({ where });
200
- const pageSize = input.pageSize;
201
- const totalPages = Math.max(1, Math.ceil(totalCount / pageSize));
202
- const page = Math.min(Math.max(1, input.page), totalPages);
203
- const skip = (page - 1) * pageSize;
204
- const invoices = await ctx.db.invoice.findMany({
205
- where,
206
- skip,
207
- take: pageSize,
208
- orderBy: { createdAt: 'desc' },
209
- include: {
210
- user: {
211
- include: {
212
- profilePicture: true,
213
- },
214
- },
215
- subscription: {
216
- include: { plan: true },
217
- },
218
- },
219
- });
220
- const items = invoices.map((invoice) => ({
221
- ...invoice,
222
- user: invoice.user
223
- ? {
224
- ...invoice.user,
225
- profilePicture: invoice.user.profilePicture?.objectKey
226
- ? `/profile-picture/${invoice.user.profilePicture.objectKey}?t=${new Date(invoice.user.updatedAt).getTime()}`
227
- : null,
228
- }
229
- : invoice.user,
230
- }));
231
- return {
232
- items,
233
- page,
234
- pageSize,
235
- totalCount,
236
- totalPages,
237
- };
238
- }),
43
+ .query(({ ctx, input }) => new AdminService(ctx.db).listInvoices(input)),
239
44
  listWorkspaces: adminProcedure
240
45
  .input(z.object({
241
46
  limit: z.number().min(1).max(100).default(50),
242
47
  cursor: z.string().nullish(),
243
48
  }))
244
- .query(async ({ ctx, input }) => {
245
- const workspaces = await ctx.db.workspace.findMany({
246
- take: input.limit + 1,
247
- cursor: input.cursor ? { id: input.cursor } : undefined,
248
- orderBy: { createdAt: 'desc' },
249
- include: {
250
- owner: {
251
- select: {
252
- name: true,
253
- email: true,
254
- }
255
- }
256
- }
257
- });
258
- let nextCursor = undefined;
259
- if (workspaces.length > input.limit) {
260
- const nextItem = workspaces.pop();
261
- nextCursor = nextItem.id;
262
- }
263
- return {
264
- workspaces,
265
- nextCursor,
266
- };
267
- }),
49
+ .query(({ ctx, input }) => new AdminService(ctx.db).listWorkspaces(input)),
268
50
  updateUserRole: adminProcedure
269
- .input(z.object({
270
- userId: z.string(),
271
- roleName: z.string(),
272
- }))
273
- .mutation(async ({ ctx, input }) => {
274
- const role = await ctx.db.role.findUnique({
275
- where: { name: input.roleName }
276
- });
277
- if (!role) {
278
- throw new Error(`Role ${input.roleName} not found`);
279
- }
280
- const updatedUser = await ctx.db.user.update({
281
- where: { id: input.userId },
282
- data: { roleId: role.id },
283
- });
284
- logger.info(`User ${input.userId} role updated to ${input.roleName} by admin ${ctx.userId}`, 'ADMIN');
285
- return updatedUser;
286
- }),
287
- listPlans: adminProcedure
288
- .query(async ({ ctx }) => {
289
- return ctx.db.plan.findMany({
290
- include: {
291
- limit: true
292
- },
293
- orderBy: { price: 'asc' }
294
- });
295
- }),
51
+ .input(z.object({ userId: z.string(), roleName: z.string() }))
52
+ .mutation(({ ctx, input }) => new AdminService(ctx.db).updateUserRole(ctx.userId, input)),
53
+ listPlans: adminProcedure.query(({ ctx }) => new AdminService(ctx.db).listPlans()),
296
54
  upsertPlan: adminProcedure
297
55
  .input(z.object({
298
56
  id: z.string().optional(),
299
57
  name: z.string(),
300
58
  description: z.string().optional(),
301
- type: z.string(), // subscription or credits
59
+ type: z.string(),
302
60
  price: z.number(),
303
61
  stripePriceId: z.string().optional(),
304
62
  interval: z.string().nullable(),
@@ -309,325 +67,39 @@ export const admin = router({
309
67
  maxFlashcards: z.number(),
310
68
  maxPodcasts: z.number().default(0),
311
69
  maxStudyGuides: z.number().default(0),
312
- })
70
+ }),
313
71
  }))
314
- .mutation(async ({ ctx, input }) => {
315
- let { limits, id, stripePriceId, ...planData } = input;
316
- // Automation: Create Stripe Product and Price if stripePriceId is missing (new plan)
317
- if (!stripePriceId && !id) {
318
- if (!stripe) {
319
- throw new Error("Stripe is not configured on the server");
320
- }
321
- try {
322
- // 1. Create Product
323
- const product = await stripe.products.create({
324
- name: planData.name,
325
- description: planData.description,
326
- });
327
- // 2. Create Price
328
- const price = await stripe.prices.create({
329
- product: product.id,
330
- unit_amount: Math.round(planData.price * 100), // convert to cents
331
- currency: 'usd',
332
- recurring: planData.type === 'subscription'
333
- ? { interval: planData.interval || 'month' }
334
- : undefined,
335
- });
336
- stripePriceId = price.id;
337
- logger.info(`Automatically created Stripe Product (${product.id}) and Price (${price.id}) for plan: ${planData.name}`, 'STRIPE');
338
- }
339
- catch (err) {
340
- logger.error(`Failed to automate Stripe creation: ${err.message}`, 'STRIPE');
341
- throw new Error(`Stripe error: ${err.message}`);
342
- }
343
- }
344
- if (id) {
345
- // Update
346
- const updatedPlan = await ctx.db.plan.update({
347
- where: { id },
348
- data: {
349
- ...planData,
350
- stripePriceId,
351
- limit: {
352
- update: {
353
- maxStorageBytes: BigInt(limits.maxStorageBytes),
354
- maxWorksheets: limits.maxWorksheets,
355
- maxFlashcards: limits.maxFlashcards,
356
- maxPodcasts: limits.maxPodcasts,
357
- maxStudyGuides: limits.maxStudyGuides,
358
- }
359
- }
360
- }
361
- });
362
- logger.info(`Plan ${id} updated by admin ${ctx.userId}`, 'ADMIN');
363
- return updatedPlan;
364
- }
365
- else {
366
- // Create
367
- const newPlan = await ctx.db.plan.create({
368
- data: {
369
- ...planData,
370
- stripePriceId: stripePriceId || "", // Should be set by automation above
371
- limit: {
372
- create: {
373
- maxStorageBytes: BigInt(limits.maxStorageBytes),
374
- maxWorksheets: limits.maxWorksheets,
375
- maxFlashcards: limits.maxFlashcards,
376
- maxPodcasts: limits.maxPodcasts,
377
- maxStudyGuides: limits.maxStudyGuides,
378
- }
379
- }
380
- }
381
- });
382
- logger.info(`New plan ${newPlan.id} created by admin ${ctx.userId}`, 'ADMIN');
383
- return newPlan;
384
- }
385
- }),
72
+ .mutation(({ ctx, input }) => new AdminService(ctx.db).upsertPlan(ctx.userId, input)),
386
73
  deletePlan: adminProcedure
387
74
  .input(z.object({ id: z.string() }))
388
- .mutation(async ({ ctx, input }) => {
389
- // Check if plan has active subscriptions
390
- const activeSubs = await ctx.db.subscription.count({
391
- where: { planId: input.id, status: 'active' }
392
- });
393
- if (activeSubs > 0) {
394
- // If it has active subs, we should just deactivate it instead of deleting
395
- await ctx.db.plan.update({
396
- where: { id: input.id },
397
- data: { active: false }
398
- });
399
- return { success: true, message: "Plan deactivated because it has active subscribers." };
400
- }
401
- await ctx.db.plan.delete({ where: { id: input.id } });
402
- logger.info(`Plan ${input.id} deleted by admin ${ctx.userId}`, 'ADMIN');
403
- return { success: true, message: "Plan deleted." };
404
- }),
75
+ .mutation(({ ctx, input }) => new AdminService(ctx.db).deletePlan(ctx.userId, input.id)),
405
76
  getUserInvoices: adminProcedure
406
77
  .input(z.object({
407
78
  userId: z.string(),
408
- /** When set, return at most this many newest invoices (e.g. 5 for user detail sheet). Omit for full history. */
409
79
  limit: z.number().int().min(1).max(500).optional(),
410
80
  }))
411
- .query(async ({ ctx, input }) => {
412
- return ctx.db.invoice.findMany({
413
- where: { userId: input.userId },
414
- orderBy: { createdAt: 'desc' },
415
- take: input.limit,
416
- include: {
417
- subscription: {
418
- include: { plan: true }
419
- }
420
- }
421
- });
422
- }),
81
+ .query(({ ctx, input }) => new AdminService(ctx.db).getUserInvoices(input)),
423
82
  getUserDetailedInfo: adminProcedure
424
83
  .input(z.object({ userId: z.string() }))
425
- .query(async ({ ctx, input }) => {
426
- const user = await ctx.db.user.findUnique({
427
- where: { id: input.userId },
428
- include: {
429
- role: true,
430
- profilePicture: true,
431
- subscriptions: {
432
- orderBy: { createdAt: 'desc' },
433
- take: 1,
434
- include: { plan: true }
435
- },
436
- _count: {
437
- select: {
438
- workspaces: true,
439
- artifacts: true,
440
- }
441
- }
442
- }
443
- });
444
- if (!user)
445
- throw new Error("User not found");
446
- // Calculate total spent by this user
447
- const totalSpentResult = await ctx.db.invoice.aggregate({
448
- where: { userId: input.userId, status: 'paid' },
449
- _sum: { amountPaid: true }
450
- });
451
- const totalSpent = Number(totalSpentResult._sum.amountPaid || 0) / 100;
452
- // Get purchased credits summary
453
- const purchasedCredits = await ctx.db.userCredit.groupBy({
454
- by: ['resourceType'],
455
- where: { userId: input.userId },
456
- _sum: { amount: true }
457
- });
458
- const profilePictureUrl = user.profilePicture?.objectKey
459
- ? `/profile-picture/${user.profilePicture.objectKey}?t=${new Date(user.updatedAt).getTime()}`
460
- : null;
461
- return {
462
- ...user,
463
- totalSpent,
464
- purchasedCredits: purchasedCredits.map(c => ({
465
- type: c.resourceType,
466
- amount: c._sum.amount || 0
467
- })),
468
- profilePicture: profilePictureUrl,
469
- };
470
- }),
471
- debugInvoices: adminProcedure
472
- .query(async ({ ctx }) => {
473
- const count = await ctx.db.invoice.count();
474
- const all = await ctx.db.invoice.findMany({ take: 10 });
475
- return { count, all };
476
- }),
477
- listResourcePrices: adminProcedure
478
- .query(async ({ ctx }) => {
479
- return ctx.db.resourcePrice.findMany({
480
- orderBy: { resourceType: 'asc' }
481
- });
482
- }),
84
+ .query(({ ctx, input }) => new AdminService(ctx.db).getUserDetailedInfo(input.userId)),
85
+ debugInvoices: adminProcedure.query(({ ctx }) => new AdminService(ctx.db).debugInvoices()),
86
+ listResourcePrices: adminProcedure.query(({ ctx }) => new AdminService(ctx.db).listResourcePrices()),
483
87
  upsertResourcePrice: adminProcedure
484
88
  .input(z.object({
485
89
  resourceType: z.nativeEnum(ArtifactType),
486
90
  priceCents: z.number().min(0),
487
91
  }))
488
- .mutation(async ({ ctx, input }) => {
489
- const price = await ctx.db.resourcePrice.upsert({
490
- where: { resourceType: input.resourceType },
491
- update: { priceCents: input.priceCents },
492
- create: {
493
- resourceType: input.resourceType,
494
- priceCents: input.priceCents,
495
- },
496
- });
497
- logger.info(`Resource price for ${input.resourceType} updated to ${input.priceCents} by admin ${ctx.userId}`, 'ADMIN');
498
- return price;
499
- }),
92
+ .mutation(({ ctx, input }) => new AdminService(ctx.db).upsertResourcePrice(ctx.userId, input)),
500
93
  listRecentInvoices: adminProcedure
501
94
  .input(z.object({ limit: z.number().default(10) }))
502
- .query(async ({ ctx, input }) => {
503
- const invoices = await ctx.db.invoice.findMany({
504
- take: input.limit,
505
- orderBy: { createdAt: 'desc' },
506
- include: {
507
- user: {
508
- include: {
509
- profilePicture: true,
510
- }
511
- }
512
- }
513
- });
514
- return invoices.map((invoice) => ({
515
- ...invoice,
516
- user: {
517
- ...invoice.user,
518
- profilePicture: invoice.user.profilePicture?.objectKey
519
- ? `/profile-picture/${invoice.user.profilePicture.objectKey}?t=${new Date(invoice.user.updatedAt).getTime()}`
520
- : null,
521
- }
522
- }));
523
- }),
95
+ .query(({ ctx, input }) => new AdminService(ctx.db).listRecentInvoices(input.limit)),
524
96
  activityList: adminProcedure
525
97
  .input(activityListInput)
526
- .query(async ({ ctx, input }) => {
527
- const where = buildActivityLogWhere({
528
- from: input.from,
529
- to: input.to,
530
- actorUserId: input.actorUserId,
531
- workspaceId: input.workspaceId,
532
- category: input.category,
533
- status: input.status,
534
- search: input.search,
535
- });
536
- const totalCount = await ctx.db.activityLog.count({ where });
537
- const totalPages = Math.max(1, Math.ceil(totalCount / input.limit));
538
- const page = Math.min(Math.max(1, input.page), totalPages);
539
- const skip = (page - 1) * input.limit;
540
- const rows = await ctx.db.activityLog.findMany({
541
- where,
542
- skip,
543
- take: input.limit,
544
- orderBy: { createdAt: 'desc' },
545
- include: {
546
- actor: { select: { id: true, email: true, name: true } },
547
- workspace: { select: { id: true, title: true } },
548
- },
549
- });
550
- return {
551
- items: rows.map((r) => ({
552
- id: r.id,
553
- createdAt: r.createdAt,
554
- action: r.action,
555
- description: getActivityHumanDescription(r.trpcPath, r.action),
556
- category: r.category,
557
- trpcPath: r.trpcPath,
558
- status: r.status,
559
- durationMs: r.durationMs,
560
- errorCode: r.errorCode,
561
- ipAddress: r.ipAddress,
562
- actor: r.actor,
563
- workspace: r.workspace,
564
- metadata: r.metadata,
565
- })),
566
- page,
567
- pageSize: input.limit,
568
- totalCount,
569
- totalPages,
570
- };
571
- }),
98
+ .query(({ ctx, input }) => new AdminService(ctx.db).activityList(input)),
572
99
  activityExportCsv: adminProcedure
573
100
  .input(activityLogFiltersInput.extend({
574
101
  maxRows: z.number().min(1).max(5000).default(2000),
575
102
  }))
576
- .query(async ({ ctx, input }) => {
577
- const where = buildActivityLogWhere({
578
- from: input.from,
579
- to: input.to,
580
- actorUserId: input.actorUserId,
581
- workspaceId: input.workspaceId,
582
- category: input.category,
583
- status: input.status,
584
- search: input.search,
585
- });
586
- const rows = await ctx.db.activityLog.findMany({
587
- where,
588
- orderBy: { createdAt: 'desc' },
589
- take: input.maxRows,
590
- include: {
591
- actor: { select: { email: true, name: true } },
592
- workspace: { select: { title: true } },
593
- },
594
- });
595
- const header = [
596
- 'createdAt',
597
- 'actorEmail',
598
- 'actorName',
599
- 'description',
600
- 'trpcPath',
601
- 'category',
602
- 'status',
603
- 'durationMs',
604
- 'workspaceTitle',
605
- 'errorCode',
606
- ].join(',');
607
- const lines = rows.map((r) => [
608
- csvEscapeCell(r.createdAt.toISOString()),
609
- csvEscapeCell(r.actor?.email),
610
- csvEscapeCell(r.actor?.name),
611
- csvEscapeCell(getActivityHumanDescription(r.trpcPath, r.action)),
612
- csvEscapeCell(r.trpcPath),
613
- csvEscapeCell(r.category),
614
- csvEscapeCell(r.status),
615
- csvEscapeCell(r.durationMs),
616
- csvEscapeCell(r.workspace?.title),
617
- csvEscapeCell(r.errorCode),
618
- ].join(','));
619
- return {
620
- csv: [header, ...lines].join('\n'),
621
- count: rows.length,
622
- };
623
- }),
624
- /** Deletes rows older than ACTIVITY_LOG_RETENTION_DAYS (default 365). Run manually or via cron. */
625
- activityPurgeRetention: adminProcedure.mutation(async ({ ctx }) => {
626
- const days = getActivityRetentionDays();
627
- const cutoff = new Date();
628
- cutoff.setDate(cutoff.getDate() - days);
629
- const { deleted } = await deleteActivityLogsOlderThan(ctx.db, cutoff);
630
- logger.info(`ActivityLog retention purge: deleted ${deleted} rows older than ${days} days (cutoff ${cutoff.toISOString()})`, 'ADMIN');
631
- return { deleted, retentionDays: days, cutoff };
632
- }),
103
+ .query(({ ctx, input }) => new AdminService(ctx.db).activityExportCsv(input)),
104
+ activityPurgeRetention: adminProcedure.mutation(({ ctx }) => new AdminService(ctx.db).activityPurgeRetention()),
633
105
  });