@goscribe/server 1.3.3 → 1.5.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 (378) 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/controllers/admin.controller.d.ts +715 -0
  6. package/dist/controllers/admin.controller.js +9 -0
  7. package/dist/controllers/annotations.controller.d.ts +439 -0
  8. package/dist/controllers/annotations.controller.js +9 -0
  9. package/dist/controllers/app-router.controller.d.ts +3011 -0
  10. package/dist/controllers/app-router.controller.js +38 -0
  11. package/dist/controllers/app-router.controller.test.d.ts +1 -0
  12. package/dist/controllers/app-router.controller.test.js +36 -0
  13. package/dist/controllers/auth.controller.d.ts +323 -0
  14. package/dist/controllers/auth.controller.js +9 -0
  15. package/dist/controllers/base.controller.d.ts +4 -0
  16. package/dist/controllers/base.controller.js +5 -0
  17. package/dist/controllers/chat.controller.d.ts +341 -0
  18. package/dist/controllers/chat.controller.js +9 -0
  19. package/dist/controllers/copilot.controller.d.ts +397 -0
  20. package/dist/controllers/copilot.controller.js +9 -0
  21. package/dist/controllers/flashcards.controller.d.ts +651 -0
  22. package/dist/controllers/flashcards.controller.js +9 -0
  23. package/dist/controllers/members.controller.d.ts +339 -0
  24. package/dist/controllers/members.controller.js +9 -0
  25. package/dist/controllers/notifications.controller.d.ts +199 -0
  26. package/dist/controllers/notifications.controller.js +9 -0
  27. package/dist/controllers/payment.controller.d.ts +181 -0
  28. package/dist/controllers/payment.controller.js +9 -0
  29. package/dist/controllers/podcast.controller.d.ts +575 -0
  30. package/dist/controllers/podcast.controller.js +9 -0
  31. package/dist/controllers/router-module.controller.d.ts +5 -0
  32. package/dist/controllers/router-module.controller.js +6 -0
  33. package/dist/controllers/studyguide.controller.d.ts +73 -0
  34. package/dist/controllers/studyguide.controller.js +9 -0
  35. package/dist/controllers/worksheets.controller.d.ts +829 -0
  36. package/dist/controllers/worksheets.controller.js +9 -0
  37. package/dist/controllers/workspace.controller.d.ts +1207 -0
  38. package/dist/controllers/workspace.controller.js +9 -0
  39. package/dist/lib/activity_human_description.test.js +16 -15
  40. package/dist/lib/activity_log_service.test.js +28 -23
  41. package/dist/lib/ai/config.d.ts +20 -0
  42. package/dist/lib/ai/config.js +31 -0
  43. package/dist/lib/ai/embedding-client.d.ts +8 -0
  44. package/dist/lib/ai/embedding-client.js +30 -0
  45. package/dist/lib/ai/index.d.ts +47 -0
  46. package/dist/lib/ai/index.js +28 -0
  47. package/dist/lib/ai/inference-backend/client.d.ts +28 -0
  48. package/dist/lib/ai/inference-backend/client.js +301 -0
  49. package/dist/lib/ai/inference-backend/mocks.d.ts +12 -0
  50. package/dist/lib/ai/inference-backend/mocks.js +133 -0
  51. package/dist/lib/ai/inference-backend/types.d.ts +44 -0
  52. package/dist/lib/ai/inference-backend/types.js +1 -0
  53. package/dist/lib/ai/json-parse.d.ts +2 -0
  54. package/dist/lib/ai/json-parse.js +34 -0
  55. package/dist/lib/ai/llm-client.d.ts +6 -0
  56. package/dist/lib/ai/llm-client.js +19 -0
  57. package/dist/lib/ai/mock.d.ts +2 -0
  58. package/dist/lib/ai/mock.js +10 -0
  59. package/dist/lib/ai/types.d.ts +9 -0
  60. package/dist/lib/ai/types.js +1 -0
  61. package/dist/lib/chunking.d.ts +19 -0
  62. package/dist/lib/chunking.js +47 -0
  63. package/dist/lib/curated-kb-seed.d.ts +12 -0
  64. package/dist/lib/curated-kb-seed.js +155 -0
  65. package/dist/lib/email.js +67 -108
  66. package/dist/lib/embeddings.d.ts +2 -0
  67. package/dist/lib/embeddings.js +1 -0
  68. package/dist/lib/ensure-curated-kb-catalog.d.ts +6 -0
  69. package/dist/lib/ensure-curated-kb-catalog.js +53 -0
  70. package/dist/lib/env.d.ts +1 -5
  71. package/dist/lib/env.js +2 -7
  72. package/dist/lib/inference.d.ts +1 -8
  73. package/dist/lib/inference.js +1 -19
  74. package/dist/lib/kb-meta.d.ts +8 -0
  75. package/dist/lib/kb-meta.js +77 -0
  76. package/dist/lib/note-text.d.ts +1 -0
  77. package/dist/lib/note-text.js +47 -0
  78. package/dist/lib/notification-service.test.js +37 -36
  79. package/dist/lib/pdf.d.ts +11 -0
  80. package/dist/lib/pdf.js +11 -0
  81. package/dist/lib/usage_service.d.ts +2 -1
  82. package/dist/lib/usage_service.js +30 -12
  83. package/dist/lib/worksheet-generation.js +4 -4
  84. package/dist/lib/worksheet-generation.test.js +32 -17
  85. package/dist/lib/workspace-kb.d.ts +5 -0
  86. package/dist/lib/workspace-kb.js +7 -0
  87. package/dist/models/controller-context.model.d.ts +8 -0
  88. package/dist/models/controller-context.model.js +1 -0
  89. package/dist/repositories/artifact.repository.d.ts +60 -0
  90. package/dist/repositories/artifact.repository.js +40 -0
  91. package/dist/repositories/base.repository.d.ts +14 -0
  92. package/dist/repositories/base.repository.js +14 -0
  93. package/dist/repositories/invitation.repository.d.ts +94 -0
  94. package/dist/repositories/invitation.repository.js +44 -0
  95. package/dist/repositories/notification.repository.d.ts +72 -0
  96. package/dist/repositories/notification.repository.js +44 -0
  97. package/dist/repositories/router-module.repository.d.ts +10 -0
  98. package/dist/repositories/router-module.repository.js +14 -0
  99. package/dist/repositories/user.repository.d.ts +74 -0
  100. package/dist/repositories/user.repository.js +37 -0
  101. package/dist/repositories/workspace-member.repository.d.ts +31 -0
  102. package/dist/repositories/workspace-member.repository.js +31 -0
  103. package/dist/repositories/workspace.repository.d.ts +97 -0
  104. package/dist/repositories/workspace.repository.js +79 -0
  105. package/dist/routers/_app.d.ts +528 -33
  106. package/dist/routers/_app.js +4 -0
  107. package/dist/routers/admin.d.ts +0 -4
  108. package/dist/routers/admin.js +21 -549
  109. package/dist/routers/annotations.js +12 -170
  110. package/dist/routers/artifactVersions.d.ts +65 -0
  111. package/dist/routers/artifactVersions.js +14 -0
  112. package/dist/routers/auth.d.ts +0 -6
  113. package/dist/routers/auth.js +36 -421
  114. package/dist/routers/chat.js +15 -229
  115. package/dist/routers/copilot.d.ts +14 -13
  116. package/dist/routers/copilot.js +13 -532
  117. package/dist/routers/flashcards.d.ts +5 -5
  118. package/dist/routers/flashcards.js +23 -349
  119. package/dist/routers/knowledgeBase.d.ts +421 -0
  120. package/dist/routers/knowledgeBase.js +118 -0
  121. package/dist/routers/members.d.ts +0 -41
  122. package/dist/routers/members.js +22 -710
  123. package/dist/routers/notes.d.ts +94 -0
  124. package/dist/routers/notes.js +37 -0
  125. package/dist/routers/notifications.js +7 -109
  126. package/dist/routers/payment.d.ts +3 -2
  127. package/dist/routers/payment.js +11 -393
  128. package/dist/routers/podcast.d.ts +1 -1
  129. package/dist/routers/podcast.js +11 -784
  130. package/dist/routers/studyguide.js +3 -129
  131. package/dist/routers/worksheets.d.ts +29 -14
  132. package/dist/routers/worksheets.js +49 -628
  133. package/dist/routers/workspace.d.ts +0 -4
  134. package/dist/routers/workspace.js +28 -922
  135. package/dist/scripts/purge-deleted-users.js +2 -2
  136. package/dist/server.js +10 -3
  137. package/dist/services/activity/activity-human-description.service.d.ts +13 -0
  138. package/dist/services/activity/activity-human-description.service.js +221 -0
  139. package/dist/services/activity/activity-human-description.service.test.d.ts +1 -0
  140. package/dist/services/activity/activity-human-description.service.test.js +16 -0
  141. package/dist/services/activity/activity-log.service.d.ts +87 -0
  142. package/dist/services/activity/activity-log.service.js +276 -0
  143. package/dist/services/activity/activity-log.service.test.d.ts +1 -0
  144. package/dist/services/activity/activity-log.service.test.js +27 -0
  145. package/dist/services/activity-human-description.service.d.ts +13 -0
  146. package/dist/services/activity-human-description.service.js +221 -0
  147. package/dist/services/activity-human-description.service.test.d.ts +1 -0
  148. package/dist/services/activity-human-description.service.test.js +16 -0
  149. package/dist/services/activity-log.service.d.ts +87 -0
  150. package/dist/services/activity-log.service.js +276 -0
  151. package/dist/services/activity-log.service.test.d.ts +1 -0
  152. package/dist/services/activity-log.service.test.js +27 -0
  153. package/dist/services/admin/admin.service.d.ts +270 -0
  154. package/dist/services/admin/admin.service.js +476 -0
  155. package/dist/services/admin.service.d.ts +270 -0
  156. package/dist/services/admin.service.js +476 -0
  157. package/dist/services/ai/ai-session.service.d.ts +5 -0
  158. package/dist/services/ai/ai-session.service.js +4 -0
  159. package/dist/services/ai-session.service.d.ts +60 -0
  160. package/dist/services/ai-session.service.js +561 -0
  161. package/dist/services/annotation.service.d.ts +177 -0
  162. package/dist/services/annotation.service.js +154 -0
  163. package/dist/services/artifact-notification.service.d.ts +14 -0
  164. package/dist/services/artifact-notification.service.js +20 -0
  165. package/dist/services/artifact-version.service.d.ts +38 -0
  166. package/dist/services/artifact-version.service.js +129 -0
  167. package/dist/services/artifacts/annotation.service.d.ts +177 -0
  168. package/dist/services/artifacts/annotation.service.js +154 -0
  169. package/dist/services/artifacts/artifact-version.service.d.ts +38 -0
  170. package/dist/services/artifacts/artifact-version.service.js +129 -0
  171. package/dist/services/artifacts/chat.service.d.ts +127 -0
  172. package/dist/services/artifacts/chat.service.js +182 -0
  173. package/dist/services/artifacts/study-guide.service.d.ts +18 -0
  174. package/dist/services/artifacts/study-guide.service.js +65 -0
  175. package/dist/services/auth/auth.service.d.ts +94 -0
  176. package/dist/services/auth/auth.service.js +368 -0
  177. package/dist/services/auth.service.d.ts +94 -0
  178. package/dist/services/auth.service.js +368 -0
  179. package/dist/services/base.service.d.ts +14 -0
  180. package/dist/services/base.service.js +14 -0
  181. package/dist/services/billing/payment.service.d.ts +55 -0
  182. package/dist/services/billing/payment.service.js +368 -0
  183. package/dist/services/billing/subscription.service.d.ts +37 -0
  184. package/dist/services/billing/subscription.service.js +654 -0
  185. package/dist/services/billing/usage.service.d.ts +27 -0
  186. package/dist/services/billing/usage.service.js +77 -0
  187. package/dist/services/chat.service.d.ts +127 -0
  188. package/dist/services/chat.service.js +182 -0
  189. package/dist/services/content/copilot.service.d.ts +113 -0
  190. package/dist/services/content/copilot.service.js +453 -0
  191. package/dist/services/content/flashcard-progress.service.d.ts +159 -0
  192. package/dist/services/content/flashcard-progress.service.js +432 -0
  193. package/dist/services/content/flashcard.service.d.ts +140 -0
  194. package/dist/services/content/flashcard.service.js +326 -0
  195. package/dist/services/content/media-analysis.service.d.ts +23 -0
  196. package/dist/services/content/media-analysis.service.js +404 -0
  197. package/dist/services/content/podcast.service.d.ts +267 -0
  198. package/dist/services/content/podcast.service.js +653 -0
  199. package/dist/services/content/worksheet-content.service.d.ts +37 -0
  200. package/dist/services/content/worksheet-content.service.js +84 -0
  201. package/dist/services/content/worksheet-content.service.test.d.ts +1 -0
  202. package/dist/services/content/worksheet-content.service.test.js +69 -0
  203. package/dist/services/content/worksheet-generation.service.d.ts +91 -0
  204. package/dist/services/content/worksheet-generation.service.js +95 -0
  205. package/dist/services/content/worksheet-generation.service.test.d.ts +1 -0
  206. package/dist/services/content/worksheet-generation.service.test.js +20 -0
  207. package/dist/services/content/worksheet.service.d.ts +347 -0
  208. package/dist/services/content/worksheet.service.js +599 -0
  209. package/dist/services/copilot.service.d.ts +116 -0
  210. package/dist/services/copilot.service.js +447 -0
  211. package/dist/services/flashcard-progress.service.d.ts +2 -2
  212. package/dist/services/flashcard-progress.service.js +3 -2
  213. package/dist/services/flashcard.service.d.ts +140 -0
  214. package/dist/services/flashcard.service.js +325 -0
  215. package/dist/services/invitation.service.d.ts +66 -0
  216. package/dist/services/invitation.service.js +348 -0
  217. package/dist/services/knowledge/knowledge-base.service.d.ts +316 -0
  218. package/dist/services/knowledge/knowledge-base.service.js +544 -0
  219. package/dist/services/knowledge-base.service.d.ts +316 -0
  220. package/dist/services/knowledge-base.service.js +536 -0
  221. package/dist/services/media-analysis.service.d.ts +23 -0
  222. package/dist/services/media-analysis.service.js +384 -0
  223. package/dist/services/member.service.d.ts +36 -0
  224. package/dist/services/member.service.js +193 -0
  225. package/dist/services/members/invitation.service.d.ts +66 -0
  226. package/dist/services/members/invitation.service.js +348 -0
  227. package/dist/services/members/member.service.d.ts +36 -0
  228. package/dist/services/members/member.service.js +193 -0
  229. package/dist/services/note.service.d.ts +55 -0
  230. package/dist/services/note.service.js +111 -0
  231. package/dist/services/notification.service.d.ts +214 -0
  232. package/dist/services/notification.service.js +550 -0
  233. package/dist/services/notification.service.test.d.ts +1 -0
  234. package/dist/services/notification.service.test.js +87 -0
  235. package/dist/services/notifications/notification.service.d.ts +214 -0
  236. package/dist/services/notifications/notification.service.js +550 -0
  237. package/dist/services/notifications/notification.service.test.d.ts +1 -0
  238. package/dist/services/notifications/notification.service.test.js +87 -0
  239. package/dist/services/payment.service.d.ts +55 -0
  240. package/dist/services/payment.service.js +368 -0
  241. package/dist/services/podcast.service.d.ts +267 -0
  242. package/dist/services/podcast.service.js +654 -0
  243. package/dist/services/router-module.service.d.ts +7 -0
  244. package/dist/services/router-module.service.js +10 -0
  245. package/dist/services/study-guide.service.d.ts +18 -0
  246. package/dist/services/study-guide.service.js +65 -0
  247. package/dist/services/subscription.service.d.ts +37 -0
  248. package/dist/services/subscription.service.js +654 -0
  249. package/dist/services/usage-limit-policy.service.d.ts +12 -0
  250. package/dist/services/usage-limit-policy.service.js +22 -0
  251. package/dist/services/usage-limit-policy.service.test.d.ts +1 -0
  252. package/dist/services/usage-limit-policy.service.test.js +46 -0
  253. package/dist/services/usage.service.d.ts +27 -0
  254. package/dist/services/usage.service.js +77 -0
  255. package/dist/services/worksheet-content.service.d.ts +42 -0
  256. package/dist/services/worksheet-content.service.js +84 -0
  257. package/dist/services/worksheet-content.service.test.d.ts +1 -0
  258. package/dist/services/worksheet-content.service.test.js +69 -0
  259. package/dist/services/worksheet-generation.service.d.ts +91 -0
  260. package/dist/services/worksheet-generation.service.js +95 -0
  261. package/dist/services/worksheet-generation.service.test.d.ts +1 -0
  262. package/dist/services/worksheet-generation.service.test.js +20 -0
  263. package/dist/services/worksheet.service.d.ts +385 -0
  264. package/dist/services/worksheet.service.js +596 -0
  265. package/dist/services/workspace/workspace-analytics.service.d.ts +24 -0
  266. package/dist/services/workspace/workspace-analytics.service.js +95 -0
  267. package/dist/services/workspace/workspace-kb.service.d.ts +40 -0
  268. package/dist/services/workspace/workspace-kb.service.js +184 -0
  269. package/dist/services/workspace/workspace.service.d.ts +307 -0
  270. package/dist/services/workspace/workspace.service.js +394 -0
  271. package/dist/services/workspace-analytics.service.d.ts +24 -0
  272. package/dist/services/workspace-analytics.service.js +95 -0
  273. package/dist/services/workspace-kb.service.d.ts +40 -0
  274. package/dist/services/workspace-kb.service.js +184 -0
  275. package/dist/services/workspace-progress.service.d.ts +27 -0
  276. package/dist/services/workspace-progress.service.js +56 -0
  277. package/dist/services/workspace-progress.service.test.d.ts +1 -0
  278. package/dist/services/workspace-progress.service.test.js +49 -0
  279. package/dist/services/workspace.service.d.ts +307 -0
  280. package/dist/services/workspace.service.js +390 -0
  281. package/dist/trpc.js +2 -2
  282. package/package.json +5 -6
  283. package/prisma/migrations/20260509000001_add_knowledge_base/migration.sql +99 -0
  284. package/prisma/migrations/20260509000002_curate_knowledge_base/migration.sql +52 -0
  285. package/prisma/migrations/20260522000000_add_notes/migration.sql +27 -0
  286. package/prisma/migrations/20260524000000_remove_notes/migration.sql +3 -0
  287. package/prisma/schema.prisma +150 -48
  288. package/prisma/seed.mjs +67 -0
  289. package/scripts/debug/README.md +4 -0
  290. package/src/README.md +63 -0
  291. package/src/lib/ai/config.ts +34 -0
  292. package/src/lib/ai/embedding-client.ts +47 -0
  293. package/src/lib/ai/index.ts +62 -0
  294. package/src/lib/ai/inference-backend/client.ts +479 -0
  295. package/src/lib/ai/inference-backend/mocks.ts +171 -0
  296. package/src/lib/ai/inference-backend/types.ts +50 -0
  297. package/src/lib/ai/json-parse.ts +35 -0
  298. package/src/lib/ai/llm-client.ts +31 -0
  299. package/src/lib/ai/mock.ts +12 -0
  300. package/src/lib/ai/types.ts +11 -0
  301. package/src/lib/chunking.ts +81 -0
  302. package/src/lib/curated-kb-seed.ts +164 -0
  303. package/src/lib/email.ts +77 -115
  304. package/src/lib/embeddings.ts +9 -0
  305. package/src/lib/ensure-curated-kb-catalog.ts +60 -0
  306. package/src/lib/env.ts +2 -7
  307. package/src/lib/inference.ts +1 -21
  308. package/src/lib/kb-meta.ts +81 -0
  309. package/src/lib/pdf.ts +23 -0
  310. package/src/lib/workspace-kb.ts +7 -0
  311. package/src/repositories/artifact.repository.ts +55 -0
  312. package/src/repositories/base.repository.ts +19 -0
  313. package/src/repositories/invitation.repository.ts +53 -0
  314. package/src/repositories/notification.repository.ts +53 -0
  315. package/src/repositories/user.repository.ts +44 -0
  316. package/src/repositories/workspace-member.repository.ts +38 -0
  317. package/src/repositories/workspace.repository.ts +89 -0
  318. package/src/routers/_app.ts +4 -0
  319. package/src/routers/admin.ts +124 -692
  320. package/src/routers/annotations.ts +25 -203
  321. package/src/routers/artifactVersions.ts +32 -0
  322. package/src/routers/auth.ts +81 -519
  323. package/src/routers/chat.ts +42 -245
  324. package/src/routers/copilot.ts +41 -666
  325. package/src/routers/flashcards.ts +108 -404
  326. package/src/routers/knowledgeBase.ts +216 -0
  327. package/src/routers/members.ts +60 -782
  328. package/src/routers/notifications.ts +15 -117
  329. package/src/routers/payment.ts +37 -446
  330. package/src/routers/podcast.ts +36 -898
  331. package/src/routers/studyguide.ts +5 -144
  332. package/src/routers/worksheets.ts +171 -735
  333. package/src/routers/workspace.ts +138 -1109
  334. package/src/scripts/purge-deleted-users.ts +2 -2
  335. package/src/server.ts +10 -3
  336. package/src/{lib/activity_human_description.test.ts → services/activity/activity-human-description.service.test.ts} +1 -1
  337. package/src/{lib/activity_log_service.test.ts → services/activity/activity-log.service.test.ts} +1 -1
  338. package/src/{lib/activity_log_service.ts → services/activity/activity-log.service.ts} +2 -2
  339. package/src/services/admin/admin.service.ts +612 -0
  340. package/src/services/ai/ai-session.service.ts +5 -0
  341. package/src/services/artifacts/annotation.service.ts +189 -0
  342. package/src/services/artifacts/artifact-version.service.ts +151 -0
  343. package/src/services/artifacts/chat.service.ts +197 -0
  344. package/src/services/artifacts/study-guide.service.ts +72 -0
  345. package/src/services/auth/auth.service.ts +473 -0
  346. package/src/services/base.service.ts +19 -0
  347. package/src/services/billing/payment.service.ts +436 -0
  348. package/src/{lib/subscription_service.ts → services/billing/subscription.service.ts} +5 -5
  349. package/src/{lib/usage_service.ts → services/billing/usage.service.ts} +32 -12
  350. package/src/services/content/copilot.service.ts +596 -0
  351. package/src/services/{flashcard-progress.service.ts → content/flashcard-progress.service.ts} +6 -3
  352. package/src/services/content/flashcard.service.ts +394 -0
  353. package/src/services/content/media-analysis.service.ts +556 -0
  354. package/src/services/content/podcast.service.ts +777 -0
  355. package/src/services/content/worksheet-content.service.test.ts +83 -0
  356. package/src/services/content/worksheet-content.service.ts +117 -0
  357. package/src/{lib/worksheet-generation.test.ts → services/content/worksheet-generation.service.test.ts} +1 -1
  358. package/src/services/content/worksheet.service.ts +751 -0
  359. package/src/services/knowledge/knowledge-base.service.ts +705 -0
  360. package/src/services/members/invitation.service.ts +427 -0
  361. package/src/services/members/member.service.ts +241 -0
  362. package/src/{lib/notification-service.test.ts → services/notifications/notification.service.test.ts} +2 -2
  363. package/src/{lib/notification-service.ts → services/notifications/notification.service.ts} +102 -1
  364. package/src/services/workspace/workspace-analytics.service.ts +107 -0
  365. package/src/services/workspace/workspace-kb.service.ts +273 -0
  366. package/src/services/workspace/workspace.service.ts +481 -0
  367. package/src/trpc.ts +2 -2
  368. package/src/lib/ai-session.ts +0 -704
  369. package/src/lib/workspace-access.ts +0 -13
  370. /package/{check-difficulty.cjs → scripts/debug/check-difficulty.cjs} +0 -0
  371. /package/{check-questions.cjs → scripts/debug/check-questions.cjs} +0 -0
  372. /package/{db-summary.cjs → scripts/debug/db-summary.cjs} +0 -0
  373. /package/{mcq-test.cjs → scripts/debug/mcq-test.cjs} +0 -0
  374. /package/{test-generate.js → scripts/debug/test-generate.js} +0 -0
  375. /package/{test-ratio.cjs → scripts/debug/test-ratio.cjs} +0 -0
  376. /package/{zod-test.cjs → scripts/debug/zod-test.cjs} +0 -0
  377. /package/src/{lib/activity_human_description.ts → services/activity/activity-human-description.service.ts} +0 -0
  378. /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 './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,14 @@
1
+ import type { PrismaClient } from '@prisma/client';
2
+ import { logger } from '../lib/logger.js';
3
+ /**
4
+ * Shared base class for domain services.
5
+ *
6
+ * Services own business logic and orchestration. They receive a Prisma client
7
+ * via the constructor, never reach into globals, and are instantiated per
8
+ * request in the tRPC router layer (e.g. `new FooService(ctx.db)`).
9
+ */
10
+ export declare abstract class BaseService {
11
+ protected readonly db: PrismaClient;
12
+ protected readonly logger: typeof logger;
13
+ constructor(db: PrismaClient);
14
+ }
@@ -0,0 +1,14 @@
1
+ import { logger } from '../lib/logger.js';
2
+ /**
3
+ * Shared base class for domain services.
4
+ *
5
+ * Services own business logic and orchestration. They receive a Prisma client
6
+ * via the constructor, never reach into globals, and are instantiated per
7
+ * request in the tRPC router layer (e.g. `new FooService(ctx.db)`).
8
+ */
9
+ export class BaseService {
10
+ constructor(db) {
11
+ this.db = db;
12
+ this.logger = logger;
13
+ }
14
+ }
@@ -0,0 +1,55 @@
1
+ import type { PrismaClient } from '@prisma/client';
2
+ import type { ArtifactType } from '@prisma/client';
3
+ import { BaseService } from '../base.service.js';
4
+ export declare class PaymentService extends BaseService {
5
+ constructor(db: PrismaClient);
6
+ getPlans(userId?: string): Promise<any[]>;
7
+ createCheckoutSession(userId: string, input: {
8
+ planId: string;
9
+ }): Promise<{
10
+ url: string | null;
11
+ }>;
12
+ confirmCheckoutSuccess(userId: string, input: {
13
+ sessionId: string;
14
+ }): Promise<{
15
+ confirmed: boolean;
16
+ reason: string;
17
+ kind?: undefined;
18
+ } | {
19
+ confirmed: boolean;
20
+ kind: "payment";
21
+ reason?: undefined;
22
+ } | {
23
+ confirmed: boolean;
24
+ kind: "subscription";
25
+ reason?: undefined;
26
+ }>;
27
+ createResourcePurchaseSession(userId: string, input: {
28
+ resourceType: ArtifactType;
29
+ quantity: number;
30
+ }): Promise<{
31
+ url: string | null;
32
+ }>;
33
+ getUsageOverview(userId: string): Promise<{
34
+ usage: import("./usage.service.js").UserUsage;
35
+ limits: {
36
+ id: string;
37
+ planId: string;
38
+ maxStorageBytes: bigint;
39
+ maxWorksheets: number;
40
+ maxFlashcards: number;
41
+ maxPodcasts: number;
42
+ maxStudyGuides: number;
43
+ createdAt: Date;
44
+ updatedAt: Date;
45
+ isFallbackPlan: boolean;
46
+ };
47
+ hasActivePlan: boolean;
48
+ }>;
49
+ getResourcePrices(): import("@prisma/client").Prisma.PrismaPromise<{
50
+ id: string;
51
+ updatedAt: Date;
52
+ resourceType: import("@prisma/client").$Enums.ArtifactType;
53
+ priceCents: number;
54
+ }[]>;
55
+ }