@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
@@ -1,735 +1,47 @@
1
1
  import { z } from 'zod';
2
- import { TRPCError } from '@trpc/server';
3
2
  import { router, publicProcedure, authedProcedure } from '../trpc.js';
4
- import { logger } from '../lib/logger.js';
5
- import { sendInvitationEmail } from '../lib/email.js';
6
- import PusherService from '../lib/pusher.js';
7
- import { notifyInviteAccepted, notifyInviteRecipient, notifyWorkspaceMembershipRemoved, notifyWorkspaceRoleChanged, } from '../lib/notification-service.js';
8
- /**
9
- * Members router for workspace member management
10
- *
11
- * Features:
12
- * - Get workspace members
13
- * - Invite new members via email
14
- * - Accept invitations via UUID
15
- * - Change member roles
16
- * - Remove members
17
- * - Get current user's role
18
- */
3
+ import { MemberService } from '../services/members/member.service.js';
4
+ import { InvitationService } from '../services/members/invitation.service.js';
19
5
  export const members = router({
20
- /**
21
- * Get all members of a workspace
22
- */
23
6
  getMembers: authedProcedure
24
- .input(z.object({
25
- workspaceId: z.string(),
26
- }))
27
- .query(async ({ ctx, input }) => {
28
- // Check if user has access to this workspace
29
- const workspace = await ctx.db.workspace.findFirst({
30
- where: {
31
- id: input.workspaceId,
32
- OR: [
33
- { ownerId: ctx.session.user.id },
34
- { members: { some: { userId: ctx.session.user.id } } }
35
- ]
36
- },
37
- include: {
38
- owner: {
39
- select: {
40
- id: true,
41
- name: true,
42
- email: true,
43
- profilePicture: {
44
- select: {
45
- id: true,
46
- name: true,
47
- url: true,
48
- }
49
- }
50
- }
51
- },
52
- members: {
53
- include: {
54
- user: {
55
- select: {
56
- id: true,
57
- name: true,
58
- email: true,
59
- profilePicture: {
60
- select: {
61
- id: true,
62
- name: true,
63
- url: true,
64
- }
65
- }
66
- }
67
- }
68
- }
69
- }
70
- }
71
- });
72
- if (!workspace) {
73
- throw new TRPCError({
74
- code: 'NOT_FOUND',
75
- message: 'Workspace not found or access denied'
76
- });
77
- }
78
- const workspaceMembers = [
79
- {
80
- id: workspace.owner.id,
81
- name: workspace.owner.name || 'Unknown',
82
- email: workspace.owner.email || '',
83
- role: 'owner',
84
- joinedAt: workspace.createdAt,
85
- },
86
- ...workspace.members.map(membership => ({
87
- id: membership.user.id,
88
- name: membership.user.name || 'Unknown',
89
- email: membership.user.email || '',
90
- role: membership.role,
91
- joinedAt: membership.joinedAt,
92
- }))
93
- ];
94
- logger.info(`👥 Fetched ${workspaceMembers.length} members for workspace ${input.workspaceId}`, 'WORKSPACE', {
95
- memberEmails: workspaceMembers.map(m => m.email)
96
- });
97
- return workspaceMembers;
98
- }),
99
- /**
100
- * Get current user's role in a workspace
101
- */
7
+ .input(z.object({ workspaceId: z.string() }))
8
+ .query(({ ctx, input }) => new MemberService(ctx.db).getMembers(ctx.session.user.id, input.workspaceId)),
102
9
  getCurrentUserRole: authedProcedure
103
- .input(z.object({
104
- workspaceId: z.string(),
105
- }))
106
- .query(async ({ ctx, input }) => {
107
- const workspace = await ctx.db.workspace.findFirst({
108
- where: { id: input.workspaceId },
109
- select: {
110
- ownerId: true,
111
- members: {
112
- where: { userId: ctx.session.user.id },
113
- select: { role: true }
114
- }
115
- }
116
- });
117
- if (!workspace) {
118
- throw new TRPCError({
119
- code: 'NOT_FOUND',
120
- message: 'Workspace not found'
121
- });
122
- }
123
- if (workspace.ownerId === ctx.session.user.id) {
124
- return 'owner';
125
- }
126
- if (workspace.members.length > 0) {
127
- return workspace.members[0].role;
128
- }
129
- throw new TRPCError({
130
- code: 'FORBIDDEN',
131
- message: 'Access denied to this workspace'
132
- });
133
- }),
134
- /**
135
- * Invite a new member to the workspace
136
- */
10
+ .input(z.object({ workspaceId: z.string() }))
11
+ .query(({ ctx, input }) => new MemberService(ctx.db).getCurrentUserRole(ctx.session.user.id, input.workspaceId)),
137
12
  inviteMember: authedProcedure
138
13
  .input(z.object({
139
14
  workspaceId: z.string(),
140
15
  email: z.string().email(),
141
16
  role: z.enum(['admin', 'member']).default('member'),
142
17
  }))
143
- .mutation(async ({ ctx, input }) => {
144
- // Check if user is owner or admin of the workspace
145
- const workspace = await ctx.db.workspace.findFirst({
146
- where: {
147
- id: input.workspaceId,
148
- ownerId: ctx.session.user.id // Only owners can invite for now
149
- }
150
- });
151
- if (!workspace) {
152
- throw new TRPCError({
153
- code: 'NOT_FOUND',
154
- message: 'Workspace not found or insufficient permissions'
155
- });
156
- }
157
- // Check if user is already a member
158
- const existingMember = await ctx.db.user.findFirst({
159
- where: {
160
- email: input.email,
161
- OR: [
162
- { id: workspace.ownerId },
163
- { workspaceMemberships: { some: { workspaceId: input.workspaceId } } }
164
- ]
165
- }
166
- });
167
- if (existingMember) {
168
- throw new TRPCError({
169
- code: 'BAD_REQUEST',
170
- message: 'User is already a member of this workspace'
171
- });
172
- }
173
- // Check if there's already a pending invitation
174
- const existingInvitation = await ctx.db.workspaceInvitation.findFirst({
175
- where: {
176
- workspaceId: input.workspaceId,
177
- email: input.email,
178
- acceptedAt: null,
179
- expiresAt: { gt: new Date() }
180
- }
181
- });
182
- if (existingInvitation) {
183
- throw new TRPCError({
184
- code: 'BAD_REQUEST',
185
- message: 'Invitation already sent to this email'
186
- });
187
- }
188
- // Create invitation
189
- const invitation = await ctx.db.workspaceInvitation.create({
190
- data: {
191
- workspaceId: input.workspaceId,
192
- email: input.email,
193
- role: input.role,
194
- invitedById: ctx.session.user.id,
195
- },
196
- include: {
197
- workspace: {
198
- select: {
199
- title: true,
200
- owner: {
201
- select: {
202
- name: true,
203
- email: true,
204
- }
205
- }
206
- }
207
- }
208
- }
209
- });
210
- const invitedExistingUser = await ctx.db.user.findUnique({
211
- where: { email: input.email },
212
- select: { id: true, name: true },
213
- });
214
- if (invitedExistingUser) {
215
- await notifyInviteRecipient(ctx.db, {
216
- invitedUserId: invitedExistingUser.id,
217
- inviterUserId: ctx.session.user.id,
218
- workspaceId: input.workspaceId,
219
- workspaceTitle: invitation.workspace.title,
220
- invitationId: invitation.id,
221
- invitationToken: invitation.token,
222
- inviterName: invitation.workspace.owner.name || invitation.workspace.owner.email,
223
- });
224
- }
225
- logger.info(`🎫 Invitation created for ${input.email} to workspace ${input.workspaceId} with role ${input.role}`, 'WORKSPACE', {
226
- invitationId: invitation.id,
227
- workspaceId: input.workspaceId,
228
- email: input.email,
229
- role: input.role,
230
- invitedBy: ctx.session.user.id
231
- });
232
- // Send email notification
233
- await sendInvitationEmail({
234
- email: invitation.email,
235
- token: invitation.token,
236
- role: invitation.role,
237
- workspaceTitle: invitation.workspace.title,
238
- invitedByName: invitation.workspace.owner.name || invitation.workspace.owner.email || 'Someone',
239
- });
240
- return {
241
- invitationId: invitation.id,
242
- token: invitation.token,
243
- email: invitation.email,
244
- role: invitation.role,
245
- expiresAt: invitation.expiresAt,
246
- workspaceTitle: invitation.workspace.title,
247
- invitedByName: invitation.workspace.owner.name || invitation.workspace.owner.email,
248
- };
249
- }),
18
+ .mutation(({ ctx, input }) => new InvitationService(ctx.db).inviteMember(ctx.session.user.id, input)),
250
19
  getInvitations: authedProcedure
251
- .input(z.object({
252
- workspaceId: z.string(),
253
- }))
254
- .query(async ({ ctx, input }) => {
255
- const invitations = await ctx.db.workspaceInvitation.findMany({
256
- where: { workspaceId: input.workspaceId },
257
- });
258
- return invitations;
259
- }),
260
- /**
261
- * Accept an invitation (public endpoint)
262
- */
20
+ .input(z.object({ workspaceId: z.string() }))
21
+ .query(({ ctx, input }) => new InvitationService(ctx.db).getInvitations(input.workspaceId)),
263
22
  acceptInvite: publicProcedure
264
- .input(z.object({
265
- token: z.string(),
266
- }))
267
- .mutation(async ({ ctx, input }) => {
268
- // Find the invitation
269
- const invitation = await ctx.db.workspaceInvitation.findFirst({
270
- where: {
271
- token: input.token,
272
- acceptedAt: null,
273
- expiresAt: { gt: new Date() }
274
- },
275
- include: {
276
- workspace: {
277
- select: {
278
- id: true,
279
- title: true,
280
- ownerId: true,
281
- owner: {
282
- select: {
283
- name: true,
284
- email: true,
285
- }
286
- }
287
- }
288
- }
289
- }
290
- });
291
- if (!invitation) {
292
- throw new TRPCError({
293
- code: 'NOT_FOUND',
294
- message: 'Invalid or expired invitation'
295
- });
296
- }
297
- // Check if user is authenticated
298
- if (!ctx.session?.user) {
299
- throw new TRPCError({
300
- code: 'UNAUTHORIZED',
301
- message: 'Please log in to accept this invitation'
302
- });
303
- }
304
- const user = await ctx.db.user.findFirst({ where: { id: ctx.session.user.id } });
305
- if (!user || !user.email)
306
- throw new TRPCError({ code: 'NOT_FOUND' });
307
- logger.info(`🔍 Verification check for ${user.email} accepting invite for ${invitation.email}`, 'WORKSPACE');
308
- // Check if the email matches the user's email (case-insensitive)
309
- if (user.email.toLowerCase() !== invitation.email.toLowerCase()) {
310
- logger.warn(`❌ Invitation email mismatch: user ${user.email} vs invite ${invitation.email}`, 'WORKSPACE');
311
- throw new TRPCError({
312
- code: 'BAD_REQUEST',
313
- message: 'This invitation was sent to a different email address'
314
- });
315
- }
316
- // Check if user is already a member
317
- const isAlreadyMember = await ctx.db.workspace.findFirst({
318
- where: {
319
- id: invitation.workspaceId,
320
- OR: [
321
- { ownerId: ctx.session.user.id },
322
- { members: { some: { userId: ctx.session.user.id } } }
323
- ]
324
- }
325
- });
326
- if (isAlreadyMember) {
327
- logger.info(`ℹ️ User ${ctx.session.user.id} is already a member of workspace ${invitation.workspaceId}. Marking invite as accepted.`, 'WORKSPACE');
328
- // Mark invitation as accepted even if already a member
329
- await ctx.db.workspaceInvitation.update({
330
- where: { id: invitation.id },
331
- data: { acceptedAt: new Date() }
332
- });
333
- return {
334
- workspaceId: invitation.workspaceId,
335
- workspaceTitle: invitation.workspace.title,
336
- role: invitation.role,
337
- ownerName: invitation.workspace.owner.name || invitation.workspace.owner.email,
338
- message: 'You are already a member of this workspace'
339
- };
340
- }
341
- // Add user to workspace with proper role.
342
- // This can race if accept is triggered twice (e.g. redirects/retries),
343
- // so treat duplicate membership as a successful, idempotent accept.
344
- let memberAdded = false;
345
- try {
346
- await ctx.db.workspaceMember.create({
347
- data: {
348
- workspaceId: invitation.workspaceId,
349
- userId: ctx.session.user.id,
350
- role: invitation.role,
351
- }
352
- });
353
- memberAdded = true;
354
- }
355
- catch (error) {
356
- if (error?.code !== 'P2002') {
357
- throw error;
358
- }
359
- logger.info(`ℹ️ Duplicate invite accept handled for user ${ctx.session.user.id} in workspace ${invitation.workspaceId}`, 'WORKSPACE');
360
- }
361
- // Mark invitation as accepted
362
- await ctx.db.workspaceInvitation.update({
363
- where: { id: invitation.id },
364
- data: { acceptedAt: new Date() }
365
- });
366
- if (memberAdded) {
367
- await notifyInviteAccepted(ctx.db, {
368
- recipientUserIds: [invitation.invitedById, invitation.workspace.ownerId],
369
- actorUserId: ctx.session.user.id,
370
- workspaceId: invitation.workspaceId,
371
- workspaceTitle: invitation.workspace.title,
372
- memberName: user.name || user.email,
373
- invitationId: invitation.id,
374
- });
375
- }
376
- logger.info(`✅ Invitation accepted by ${ctx.session.user.id} (${user.email}) for workspace ${invitation.workspaceId}`, 'WORKSPACE', {
377
- invitationId: invitation.id,
378
- workspaceId: invitation.workspaceId,
379
- userId: ctx.session.user.id,
380
- email: invitation.email
381
- });
382
- // Try to emit a Pusher event if possible
383
- try {
384
- if (memberAdded) {
385
- await PusherService.emitMemberJoined(invitation.workspaceId, {
386
- id: ctx.session.user.id,
387
- name: user.name || 'Member',
388
- email: user.email,
389
- role: invitation.role,
390
- });
391
- }
392
- }
393
- catch (e) {
394
- logger.error('Failed to emit member joined event', e);
395
- }
396
- return {
397
- workspaceId: invitation.workspaceId,
398
- workspaceTitle: invitation.workspace.title,
399
- role: invitation.role,
400
- ownerName: invitation.workspace.owner.name || invitation.workspace.owner.email,
401
- ...(memberAdded ? {} : { message: 'You are already a member of this workspace' }),
402
- };
403
- }),
404
- /**
405
- * Change a member's role (owner only)
406
- */
23
+ .input(z.object({ token: z.string() }))
24
+ .mutation(({ ctx, input }) => new InvitationService(ctx.db).acceptInvite(ctx.session?.user?.id, input.token)),
407
25
  changeMemberRole: authedProcedure
408
26
  .input(z.object({
409
27
  workspaceId: z.string(),
410
28
  memberId: z.string(),
411
29
  role: z.enum(['admin', 'member']),
412
30
  }))
413
- .mutation(async ({ ctx, input }) => {
414
- // Check if user is owner of the workspace
415
- const workspace = await ctx.db.workspace.findFirst({
416
- where: {
417
- id: input.workspaceId,
418
- ownerId: ctx.session.user.id
419
- },
420
- select: { id: true, title: true, ownerId: true },
421
- });
422
- if (!workspace) {
423
- throw new TRPCError({
424
- code: 'NOT_FOUND',
425
- message: 'Workspace not found or insufficient permissions'
426
- });
427
- }
428
- // Check if member exists and is not the owner
429
- if (input.memberId === workspace.ownerId) {
430
- throw new TRPCError({
431
- code: 'BAD_REQUEST',
432
- message: 'Cannot change owner role'
433
- });
434
- }
435
- const member = await ctx.db.workspaceMember.findFirst({
436
- where: {
437
- workspaceId: input.workspaceId,
438
- userId: input.memberId
439
- }
440
- });
441
- if (!member) {
442
- throw new TRPCError({
443
- code: 'NOT_FOUND',
444
- message: 'Member not found in this workspace'
445
- });
446
- }
447
- // Update the member's role
448
- const updatedMember = await ctx.db.workspaceMember.update({
449
- where: { id: member.id },
450
- data: { role: input.role },
451
- include: {
452
- user: {
453
- select: {
454
- id: true,
455
- name: true,
456
- email: true,
457
- }
458
- }
459
- }
460
- });
461
- logger.info(`🔄 Member role changed for ${input.memberId} from ${member.role} to ${input.role} in workspace ${input.workspaceId}`, 'WORKSPACE', {
462
- workspaceId: input.workspaceId,
463
- memberId: input.memberId,
464
- oldRole: member.role,
465
- newRole: input.role,
466
- changedBy: ctx.session.user.id
467
- });
468
- const actor = await ctx.db.user.findUnique({
469
- where: { id: ctx.session.user.id },
470
- select: { name: true, email: true },
471
- });
472
- await notifyWorkspaceRoleChanged(ctx.db, {
473
- memberUserId: input.memberId,
474
- workspaceId: input.workspaceId,
475
- workspaceTitle: workspace.title,
476
- newRole: input.role,
477
- oldRole: member.role,
478
- actorUserId: ctx.session.user.id,
479
- actorName: actor?.name || actor?.email || 'A workspace admin',
480
- }).catch(() => { });
481
- return {
482
- memberId: input.memberId,
483
- role: input.role,
484
- memberName: updatedMember.user.name || updatedMember.user.email,
485
- message: 'Role changed successfully'
486
- };
487
- }),
488
- /**
489
- * Remove a member from the workspace (owner only)
490
- */
31
+ .mutation(({ ctx, input }) => new MemberService(ctx.db).changeMemberRole(ctx.session.user.id, input)),
491
32
  removeMember: authedProcedure
492
- .input(z.object({
493
- workspaceId: z.string(),
494
- memberId: z.string(),
495
- }))
496
- .mutation(async ({ ctx, input }) => {
497
- // Check if user is owner of the workspace
498
- const workspace = await ctx.db.workspace.findFirst({
499
- where: {
500
- id: input.workspaceId,
501
- ownerId: ctx.session.user.id
502
- },
503
- select: { id: true, title: true, ownerId: true },
504
- });
505
- if (!workspace) {
506
- throw new TRPCError({
507
- code: 'NOT_FOUND',
508
- message: 'Workspace not found or insufficient permissions'
509
- });
510
- }
511
- // Check if trying to remove the owner
512
- if (input.memberId === workspace.ownerId) {
513
- throw new TRPCError({
514
- code: 'BAD_REQUEST',
515
- message: 'Cannot remove workspace owner'
516
- });
517
- }
518
- // Check if member exists
519
- const member = await ctx.db.workspaceMember.findFirst({
520
- where: {
521
- workspaceId: input.workspaceId,
522
- userId: input.memberId
523
- },
524
- include: {
525
- user: {
526
- select: {
527
- name: true,
528
- email: true,
529
- }
530
- }
531
- }
532
- });
533
- if (!member) {
534
- throw new TRPCError({
535
- code: 'NOT_FOUND',
536
- message: 'Member not found in this workspace'
537
- });
538
- }
539
- const actor = await ctx.db.user.findUnique({
540
- where: { id: ctx.session.user.id },
541
- select: { name: true, email: true },
542
- });
543
- await notifyWorkspaceMembershipRemoved(ctx.db, {
544
- memberUserId: input.memberId,
545
- workspaceId: input.workspaceId,
546
- workspaceTitle: workspace.title,
547
- actorUserId: ctx.session.user.id,
548
- actorName: actor?.name || actor?.email || 'A workspace admin',
549
- }).catch(() => { });
550
- // Remove member from workspace
551
- await ctx.db.workspaceMember.delete({
552
- where: { id: member.id }
553
- });
554
- logger.info(`🗑️ Member ${input.memberId} removed from workspace ${input.workspaceId}`, 'WORKSPACE', {
555
- workspaceId: input.workspaceId,
556
- memberId: input.memberId,
557
- removedBy: ctx.session.user.id
558
- });
559
- return {
560
- memberId: input.memberId,
561
- message: 'Member removed successfully'
562
- };
563
- }),
564
- /**
565
- * Get pending invitations for a workspace (owner only)
566
- */
33
+ .input(z.object({ workspaceId: z.string(), memberId: z.string() }))
34
+ .mutation(({ ctx, input }) => new MemberService(ctx.db).removeMember(ctx.session.user.id, input)),
567
35
  getPendingInvitations: authedProcedure
568
- .input(z.object({
569
- workspaceId: z.string(),
570
- }))
571
- .query(async ({ ctx, input }) => {
572
- // Check if user is owner of the workspace
573
- const workspace = await ctx.db.workspace.findFirst({
574
- where: {
575
- id: input.workspaceId,
576
- ownerId: ctx.session.user.id
577
- }
578
- });
579
- if (!workspace) {
580
- throw new TRPCError({
581
- code: 'NOT_FOUND',
582
- message: 'Workspace not found or insufficient permissions'
583
- });
584
- }
585
- const invitations = await ctx.db.workspaceInvitation.findMany({
586
- where: {
587
- workspaceId: input.workspaceId,
588
- acceptedAt: null,
589
- expiresAt: { gt: new Date() }
590
- },
591
- include: {
592
- invitedBy: {
593
- select: {
594
- name: true,
595
- email: true,
596
- }
597
- }
598
- },
599
- orderBy: { createdAt: 'desc' }
600
- });
601
- return invitations.map(invitation => ({
602
- id: invitation.id,
603
- email: invitation.email,
604
- role: invitation.role,
605
- token: invitation.token,
606
- expiresAt: invitation.expiresAt,
607
- createdAt: invitation.createdAt,
608
- invitedByName: invitation.invitedBy.name || invitation.invitedBy.email,
609
- }));
610
- }),
611
- /**
612
- * Cancel a pending invitation (owner only)
613
- */
36
+ .input(z.object({ workspaceId: z.string() }))
37
+ .query(({ ctx, input }) => new InvitationService(ctx.db).getPendingInvitations(ctx.session.user.id, input.workspaceId)),
614
38
  cancelInvitation: authedProcedure
615
- .input(z.object({
616
- invitationId: z.string(),
617
- }))
618
- .mutation(async ({ ctx, input }) => {
619
- // Check if user is owner of the workspace
620
- const invitation = await ctx.db.workspaceInvitation.findFirst({
621
- where: {
622
- id: input.invitationId,
623
- acceptedAt: null,
624
- workspace: {
625
- ownerId: ctx.session.user.id
626
- }
627
- }
628
- });
629
- if (!invitation) {
630
- throw new TRPCError({
631
- code: 'NOT_FOUND',
632
- message: 'Invitation not found or insufficient permissions'
633
- });
634
- }
635
- // Delete the invitation
636
- await ctx.db.workspaceInvitation.delete({
637
- where: { id: input.invitationId }
638
- });
639
- logger.info(`❌ Invitation cancelled for ${invitation.email} to workspace ${invitation.workspaceId}`, 'WORKSPACE', {
640
- invitationId: input.invitationId,
641
- workspaceId: invitation.workspaceId,
642
- email: invitation.email,
643
- cancelledBy: ctx.session.user.id
644
- });
645
- return {
646
- invitationId: input.invitationId,
647
- message: 'Invitation cancelled successfully'
648
- };
649
- }),
650
- /**
651
- * Resend a pending invitation (owner only)
652
- */
39
+ .input(z.object({ invitationId: z.string() }))
40
+ .mutation(({ ctx, input }) => new InvitationService(ctx.db).cancelInvitation(ctx.session.user.id, input.invitationId)),
653
41
  resendInvitation: authedProcedure
654
- .input(z.object({
655
- invitationId: z.string(),
656
- }))
657
- .mutation(async ({ ctx, input }) => {
658
- // Check if user is owner of the workspace and invitation is pending
659
- const invitation = await ctx.db.workspaceInvitation.findFirst({
660
- where: {
661
- id: input.invitationId,
662
- acceptedAt: null,
663
- workspace: {
664
- ownerId: ctx.session.user.id
665
- }
666
- },
667
- include: {
668
- workspace: {
669
- select: {
670
- title: true,
671
- owner: {
672
- select: {
673
- name: true,
674
- email: true,
675
- }
676
- }
677
- }
678
- }
679
- }
680
- });
681
- if (!invitation) {
682
- throw new TRPCError({
683
- code: 'NOT_FOUND',
684
- message: 'Invitation not found or insufficient permissions'
685
- });
686
- }
687
- // Check if expired and update expiry if needed
688
- if (invitation.expiresAt < new Date()) {
689
- const newExpiry = new Date();
690
- newExpiry.setDate(newExpiry.getDate() + 7);
691
- await ctx.db.workspaceInvitation.update({
692
- where: { id: invitation.id },
693
- data: { expiresAt: newExpiry }
694
- });
695
- invitation.expiresAt = newExpiry;
696
- }
697
- // Send email notification
698
- await sendInvitationEmail({
699
- email: invitation.email,
700
- token: invitation.token,
701
- role: invitation.role,
702
- workspaceTitle: invitation.workspace.title,
703
- invitedByName: invitation.workspace.owner.name || invitation.workspace.owner.email || 'Someone',
704
- });
705
- logger.info(`📧 Invitation resent to ${invitation.email} for workspace ${invitation.workspaceId}`, 'WORKSPACE', {
706
- invitationId: invitation.id,
707
- workspaceId: invitation.workspaceId,
708
- email: invitation.email,
709
- resentBy: ctx.session.user.id
710
- });
711
- return {
712
- invitationId: invitation.id,
713
- message: 'Invitation email resent successfully'
714
- };
715
- }),
716
- /**
717
- * DEBUG ONLY: Get all invitations for a workspace
718
- */
42
+ .input(z.object({ invitationId: z.string() }))
43
+ .mutation(({ ctx, input }) => new InvitationService(ctx.db).resendInvitation(ctx.session.user.id, input.invitationId)),
719
44
  getAllInvitationsDebug: authedProcedure
720
- .input(z.object({
721
- workspaceId: z.string(),
722
- }))
723
- .query(async ({ ctx, input }) => {
724
- // Check if user is owner
725
- const workspace = await ctx.db.workspace.findFirst({
726
- where: { id: input.workspaceId, ownerId: ctx.session.user.id }
727
- });
728
- if (!workspace)
729
- throw new TRPCError({ code: 'UNAUTHORIZED' });
730
- return ctx.db.workspaceInvitation.findMany({
731
- where: { workspaceId: input.workspaceId },
732
- orderBy: { createdAt: 'desc' }
733
- });
734
- }),
45
+ .input(z.object({ workspaceId: z.string() }))
46
+ .query(({ ctx, input }) => new InvitationService(ctx.db).getAllInvitationsDebug(ctx.session.user.id, input.workspaceId)),
735
47
  });