@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
@@ -0,0 +1,368 @@
1
+ import { TRPCError } from '@trpc/server';
2
+ import bcrypt from 'bcryptjs';
3
+ import crypto from 'node:crypto';
4
+ import { BaseService } from '../base.service.js';
5
+ import { supabaseClient } from '../../lib/storage.js';
6
+ import { sendVerificationEmail, sendAccountDeletionScheduledEmail, sendAccountRestoredEmail, sendPasswordResetEmail, } from '../../lib/email.js';
7
+ import { createStripeCustomer } from '../../lib/stripe.js';
8
+ import { notifyAdminsAccountDeletionScheduled, notifyAdminsOnSignup, } from '../notifications/notification.service.js';
9
+ export function hashPasswordResetToken(rawToken) {
10
+ return crypto.createHash('sha256').update(rawToken, 'utf8').digest('hex');
11
+ }
12
+ export function createCustomAuthToken(userId) {
13
+ const secret = process.env.AUTH_SECRET;
14
+ if (!secret) {
15
+ throw new TRPCError({ code: 'INTERNAL_SERVER_ERROR', message: 'AUTH_SECRET is not set' });
16
+ }
17
+ const base64UserId = Buffer.from(userId, 'utf8').toString('base64url');
18
+ const hmac = crypto.createHmac('sha256', secret);
19
+ hmac.update(base64UserId);
20
+ const signature = hmac.digest('hex');
21
+ return `${base64UserId}.${signature}`;
22
+ }
23
+ function passwordResetDb(db) {
24
+ return db.passwordResetToken;
25
+ }
26
+ export class AuthService extends BaseService {
27
+ constructor(db) {
28
+ super(db);
29
+ }
30
+ async updateProfile(userId, input) {
31
+ await this.db.user.update({
32
+ where: { id: userId },
33
+ data: { name: input.name },
34
+ });
35
+ return { success: true, message: 'Profile updated successfully' };
36
+ }
37
+ async changePassword(userId, input) {
38
+ const user = await this.db.user.findUnique({
39
+ where: { id: userId },
40
+ select: { id: true, passwordHash: true },
41
+ });
42
+ if (!user) {
43
+ throw new TRPCError({ code: 'NOT_FOUND', message: 'User not found' });
44
+ }
45
+ if (!user.passwordHash) {
46
+ throw new TRPCError({
47
+ code: 'BAD_REQUEST',
48
+ message: 'Password change is unavailable for this account.',
49
+ });
50
+ }
51
+ const validCurrentPassword = await bcrypt.compare(input.currentPassword, user.passwordHash);
52
+ if (!validCurrentPassword) {
53
+ throw new TRPCError({ code: 'UNAUTHORIZED', message: 'Current password is incorrect' });
54
+ }
55
+ const isSamePassword = await bcrypt.compare(input.newPassword, user.passwordHash);
56
+ if (isSamePassword) {
57
+ throw new TRPCError({
58
+ code: 'BAD_REQUEST',
59
+ message: 'New password must be different from current password',
60
+ });
61
+ }
62
+ const newHash = await bcrypt.hash(input.newPassword, 10);
63
+ await this.db.user.update({
64
+ where: { id: user.id },
65
+ data: { passwordHash: newHash },
66
+ });
67
+ return { success: true, message: 'Password changed successfully' };
68
+ }
69
+ async uploadProfilePicture(userId) {
70
+ this.logger.info(`Generating upload URL for user ${userId}`, 'AUTH');
71
+ const objectKey = `profile_picture_${userId}`;
72
+ const { data: signedUrlData, error: signedUrlError } = await supabaseClient.storage
73
+ .from('media')
74
+ .createSignedUploadUrl(objectKey, { upsert: true });
75
+ if (signedUrlError) {
76
+ this.logger.error(`Failed to generate upload URL: ${signedUrlError.message}`, 'AUTH');
77
+ throw new TRPCError({
78
+ code: 'INTERNAL_SERVER_ERROR',
79
+ message: `Failed to generate upload URL: ${signedUrlError.message}`,
80
+ });
81
+ }
82
+ const fileAsset = await this.db.fileAsset.create({
83
+ data: {
84
+ userId,
85
+ name: 'Profile Picture',
86
+ mimeType: 'image/jpeg',
87
+ size: 0,
88
+ bucket: 'media',
89
+ objectKey,
90
+ },
91
+ });
92
+ await this.db.user.update({
93
+ where: { id: userId },
94
+ data: { fileAssetId: fileAsset.id },
95
+ });
96
+ this.logger.info(`Profile picture asset created and linked for user ${userId}`, 'AUTH');
97
+ return {
98
+ success: true,
99
+ message: 'Profile picture uploaded successfully',
100
+ signedUrl: signedUrlData.signedUrl,
101
+ };
102
+ }
103
+ /**
104
+ * Signup creates a user + sends verification email + kicks off Stripe customer
105
+ * creation. The caller (router) does NOT need to set any cookies here.
106
+ */
107
+ async signup(input) {
108
+ const existing = await this.db.user.findUnique({
109
+ where: { email: input.email },
110
+ });
111
+ if (existing) {
112
+ throw new TRPCError({ code: 'CONFLICT', message: 'Email already registered' });
113
+ }
114
+ const hash = await bcrypt.hash(input.password, 10);
115
+ const userRole = await this.db.role.findUnique({
116
+ where: { name: 'User' },
117
+ });
118
+ const user = await this.db.user.create({
119
+ data: {
120
+ name: input.name,
121
+ email: input.email,
122
+ passwordHash: hash,
123
+ roleId: userRole?.id,
124
+ },
125
+ });
126
+ await notifyAdminsOnSignup(this.db, {
127
+ id: user.id,
128
+ name: user.name,
129
+ email: user.email,
130
+ });
131
+ const token = crypto.randomUUID();
132
+ await this.db.verificationToken.create({
133
+ data: {
134
+ identifier: input.email,
135
+ token,
136
+ expires: new Date(Date.now() + 24 * 60 * 60 * 1000),
137
+ },
138
+ });
139
+ sendVerificationEmail(input.email, token, input.name).catch(() => { });
140
+ createStripeCustomer(input.email, input.name).then(async (stripeCustomerId) => {
141
+ if (stripeCustomerId) {
142
+ await this.db.user
143
+ .update({
144
+ where: { id: user.id },
145
+ data: { stripe_customer_id: stripeCustomerId },
146
+ })
147
+ .catch((err) => this.logger.error(`Failed to update user with stripe_customer_id: ${err.message}`, 'AUTH'));
148
+ }
149
+ });
150
+ return { id: user.id, email: user.email, name: user.name };
151
+ }
152
+ async verifyEmail(token) {
153
+ const record = await this.db.verificationToken.findUnique({
154
+ where: { token },
155
+ });
156
+ if (!record) {
157
+ throw new TRPCError({
158
+ code: 'NOT_FOUND',
159
+ message: 'Invalid or expired verification link',
160
+ });
161
+ }
162
+ if (record.expires < new Date()) {
163
+ await this.db.verificationToken.deleteMany({ where: { token } });
164
+ throw new TRPCError({
165
+ code: 'BAD_REQUEST',
166
+ message: 'Verification link has expired. Please request a new one.',
167
+ });
168
+ }
169
+ await this.db.user.update({
170
+ where: { email: record.identifier },
171
+ data: { emailVerified: new Date() },
172
+ });
173
+ await this.db.verificationToken.deleteMany({ where: { token } });
174
+ return { success: true, message: 'Email verified successfully' };
175
+ }
176
+ async resendVerification(sessionUserId) {
177
+ if (!sessionUserId) {
178
+ throw new TRPCError({ code: 'UNAUTHORIZED', message: 'Not logged in' });
179
+ }
180
+ const user = await this.db.user.findUnique({ where: { id: sessionUserId } });
181
+ if (!user || !user.email) {
182
+ throw new TRPCError({ code: 'NOT_FOUND', message: 'User not found' });
183
+ }
184
+ if (user.emailVerified) {
185
+ return { success: true, message: 'Email is already verified' };
186
+ }
187
+ await this.db.verificationToken.deleteMany({
188
+ where: { identifier: user.email },
189
+ });
190
+ const token = crypto.randomUUID();
191
+ await this.db.verificationToken.create({
192
+ data: {
193
+ identifier: user.email,
194
+ token,
195
+ expires: new Date(Date.now() + 24 * 60 * 60 * 1000),
196
+ },
197
+ });
198
+ const sent = await sendVerificationEmail(user.email, token, user.name);
199
+ if (!sent) {
200
+ throw new TRPCError({
201
+ code: 'INTERNAL_SERVER_ERROR',
202
+ message: 'Failed to send email. Please try again.',
203
+ });
204
+ }
205
+ return { success: true, message: 'Verification email sent' };
206
+ }
207
+ /**
208
+ * Verifies credentials and returns the user + an auth token. The router
209
+ * is responsible for setting the cookie (Express `res` is not available
210
+ * here — we keep services HTTP-free).
211
+ */
212
+ async login(input) {
213
+ const user = await this.db.user.findUnique({ where: { email: input.email } });
214
+ if (!user) {
215
+ throw new TRPCError({ code: 'UNAUTHORIZED', message: 'Invalid credentials' });
216
+ }
217
+ if (user.deletedAt) {
218
+ throw new TRPCError({
219
+ code: 'UNAUTHORIZED',
220
+ message: 'Account scheduled for deletion. Please check your email for a restore link.',
221
+ });
222
+ }
223
+ const valid = await bcrypt.compare(input.password, user.passwordHash);
224
+ if (!valid) {
225
+ throw new TRPCError({ code: 'UNAUTHORIZED', message: 'Invalid credentials' });
226
+ }
227
+ const authToken = createCustomAuthToken(user.id);
228
+ return {
229
+ id: user.id,
230
+ email: user.email,
231
+ name: user.name,
232
+ token: authToken,
233
+ };
234
+ }
235
+ async requestPasswordReset(input) {
236
+ const email = input.email.trim().toLowerCase();
237
+ const generic = {
238
+ success: true,
239
+ message: 'If an account exists for this email, we sent password reset instructions.',
240
+ };
241
+ const user = await this.db.user.findUnique({
242
+ where: { email },
243
+ select: {
244
+ id: true,
245
+ email: true,
246
+ name: true,
247
+ passwordHash: true,
248
+ deletedAt: true,
249
+ },
250
+ });
251
+ if (!user?.passwordHash || user.deletedAt || !user.email) {
252
+ return generic;
253
+ }
254
+ await passwordResetDb(this.db).deleteMany({
255
+ where: { userId: user.id, usedAt: null },
256
+ });
257
+ const rawToken = crypto.randomBytes(32).toString('hex');
258
+ const tokenHash = hashPasswordResetToken(rawToken);
259
+ const expiresAt = new Date(Date.now() + 60 * 60 * 1000);
260
+ await passwordResetDb(this.db).create({
261
+ data: { userId: user.id, tokenHash, expiresAt },
262
+ });
263
+ sendPasswordResetEmail(user.email, rawToken, user.name).catch(() => { });
264
+ return generic;
265
+ }
266
+ async resetPassword(input) {
267
+ const tokenHash = hashPasswordResetToken(input.token);
268
+ const record = await passwordResetDb(this.db).findUnique({
269
+ where: { tokenHash },
270
+ });
271
+ if (!record || record.usedAt || record.expiresAt < new Date()) {
272
+ throw new TRPCError({
273
+ code: 'BAD_REQUEST',
274
+ message: 'Invalid or expired reset link. Please request a new one.',
275
+ });
276
+ }
277
+ const newHash = await bcrypt.hash(input.newPassword, 10);
278
+ await this.db.user.update({
279
+ where: { id: record.userId },
280
+ data: { passwordHash: newHash },
281
+ });
282
+ await passwordResetDb(this.db).update({
283
+ where: { id: record.id },
284
+ data: { usedAt: new Date() },
285
+ });
286
+ await passwordResetDb(this.db).deleteMany({
287
+ where: { userId: record.userId, id: { not: record.id } },
288
+ });
289
+ return { success: true, message: 'Password updated. You can sign in now.' };
290
+ }
291
+ async getSession(sessionUserId) {
292
+ if (!sessionUserId) {
293
+ throw new TRPCError({ code: 'UNAUTHORIZED', message: 'No session found' });
294
+ }
295
+ const user = await this.db.user.findUnique({
296
+ where: { id: sessionUserId },
297
+ include: { profilePicture: true, role: true },
298
+ });
299
+ if (!user) {
300
+ throw new TRPCError({ code: 'NOT_FOUND', message: 'User not found' });
301
+ }
302
+ const profilePictureUrl = user.profilePicture?.objectKey
303
+ ? `/profile-picture/${user.profilePicture.objectKey}?t=${new Date(user.updatedAt).getTime()}`
304
+ : null;
305
+ this.logger.info(`Session fetched for user ${sessionUserId}, profilePicture: ${profilePictureUrl}`, 'AUTH');
306
+ return {
307
+ user: {
308
+ id: user.id,
309
+ email: user.email,
310
+ name: user.name,
311
+ emailVerified: !!user.emailVerified,
312
+ profilePicture: profilePictureUrl,
313
+ role: user.role,
314
+ },
315
+ };
316
+ }
317
+ async requestAccountDeletion(userId) {
318
+ const user = await this.db.user.findUnique({ where: { id: userId } });
319
+ if (!user) {
320
+ throw new TRPCError({ code: 'NOT_FOUND', message: 'User not found' });
321
+ }
322
+ await this.db.user.update({
323
+ where: { id: user.id },
324
+ data: { deletedAt: new Date() },
325
+ });
326
+ await notifyAdminsAccountDeletionScheduled(this.db, {
327
+ id: user.id,
328
+ name: user.name,
329
+ email: user.email,
330
+ }).catch(() => { });
331
+ await this.db.verificationToken.deleteMany({
332
+ where: { identifier: `restore-${user.email}` },
333
+ });
334
+ const token = crypto.randomUUID();
335
+ await this.db.verificationToken.create({
336
+ data: {
337
+ identifier: `restore-${user.email}`,
338
+ token,
339
+ expires: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000),
340
+ },
341
+ });
342
+ if (user.email) {
343
+ sendAccountDeletionScheduledEmail(user.email, token).catch(() => { });
344
+ }
345
+ return { success: true, message: 'Account scheduled for deletion' };
346
+ }
347
+ async restoreAccount(token) {
348
+ const record = await this.db.verificationToken.findUnique({ where: { token } });
349
+ if (!record || !record.identifier.startsWith('restore-')) {
350
+ throw new TRPCError({
351
+ code: 'NOT_FOUND',
352
+ message: 'Invalid or expired restore link',
353
+ });
354
+ }
355
+ if (record.expires < new Date()) {
356
+ await this.db.verificationToken.deleteMany({ where: { token } });
357
+ throw new TRPCError({ code: 'BAD_REQUEST', message: 'Restore link has expired.' });
358
+ }
359
+ const email = record.identifier.replace('restore-', '');
360
+ await this.db.user.update({
361
+ where: { email },
362
+ data: { deletedAt: null },
363
+ });
364
+ await this.db.verificationToken.deleteMany({ where: { token } });
365
+ sendAccountRestoredEmail(email).catch(() => { });
366
+ return { success: true, message: 'Account restored successfully' };
367
+ }
368
+ }
@@ -0,0 +1,94 @@
1
+ import type { PrismaClient } from '@prisma/client';
2
+ import { BaseService } from './base.service.js';
3
+ export declare function hashPasswordResetToken(rawToken: string): string;
4
+ export declare function createCustomAuthToken(userId: string): string;
5
+ export declare class AuthService extends BaseService {
6
+ constructor(db: PrismaClient);
7
+ updateProfile(userId: string, input: {
8
+ name: string;
9
+ }): Promise<{
10
+ success: boolean;
11
+ message: string;
12
+ }>;
13
+ changePassword(userId: string, input: {
14
+ currentPassword: string;
15
+ newPassword: string;
16
+ }): Promise<{
17
+ success: boolean;
18
+ message: string;
19
+ }>;
20
+ uploadProfilePicture(userId: string): Promise<{
21
+ success: boolean;
22
+ message: string;
23
+ signedUrl: string;
24
+ }>;
25
+ /**
26
+ * Signup creates a user + sends verification email + kicks off Stripe customer
27
+ * creation. The caller (router) does NOT need to set any cookies here.
28
+ */
29
+ signup(input: {
30
+ name: string;
31
+ email: string;
32
+ password: string;
33
+ }): Promise<{
34
+ id: string;
35
+ email: string | null;
36
+ name: string | null;
37
+ }>;
38
+ verifyEmail(token: string): Promise<{
39
+ success: boolean;
40
+ message: string;
41
+ }>;
42
+ resendVerification(sessionUserId: string | undefined): Promise<{
43
+ success: boolean;
44
+ message: string;
45
+ }>;
46
+ /**
47
+ * Verifies credentials and returns the user + an auth token. The router
48
+ * is responsible for setting the cookie (Express `res` is not available
49
+ * here — we keep services HTTP-free).
50
+ */
51
+ login(input: {
52
+ email: string;
53
+ password: string;
54
+ }): Promise<{
55
+ id: string;
56
+ email: string | null;
57
+ name: string | null;
58
+ token: string;
59
+ }>;
60
+ requestPasswordReset(input: {
61
+ email: string;
62
+ }): Promise<{
63
+ success: true;
64
+ message: string;
65
+ }>;
66
+ resetPassword(input: {
67
+ token: string;
68
+ newPassword: string;
69
+ }): Promise<{
70
+ success: boolean;
71
+ message: string;
72
+ }>;
73
+ getSession(sessionUserId: string | undefined): Promise<{
74
+ user: {
75
+ id: string;
76
+ email: string | null;
77
+ name: string | null;
78
+ emailVerified: boolean;
79
+ profilePicture: string | null;
80
+ role: {
81
+ name: string;
82
+ id: string;
83
+ } | null;
84
+ };
85
+ }>;
86
+ requestAccountDeletion(userId: string): Promise<{
87
+ success: boolean;
88
+ message: string;
89
+ }>;
90
+ restoreAccount(token: string): Promise<{
91
+ success: boolean;
92
+ message: string;
93
+ }>;
94
+ }