@headless-lms/server 0.2.0 → 0.4.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 (351) hide show
  1. package/dist/_virtual/_rolldown/runtime.js +5 -0
  2. package/dist/app/community-mail.d.ts +24 -0
  3. package/dist/app/community-mail.js +92 -0
  4. package/dist/app/community-mail.js.map +1 -0
  5. package/dist/app/container.d.ts +40 -19
  6. package/dist/app/container.js +294 -45
  7. package/dist/app/container.js.map +1 -1
  8. package/dist/app/mcp-endpoints.d.ts +13 -0
  9. package/dist/app/mcp-endpoints.js +25 -0
  10. package/dist/app/mcp-endpoints.js.map +1 -0
  11. package/dist/app/notification-channels.d.ts +47 -0
  12. package/dist/app/notification-channels.js +174 -0
  13. package/dist/app/notification-channels.js.map +1 -0
  14. package/dist/app/notification-reads.d.ts +23 -0
  15. package/dist/app/notification-reads.js +95 -0
  16. package/dist/app/notification-reads.js.map +1 -0
  17. package/dist/http/config.d.ts +2 -1
  18. package/dist/http/errors.d.ts +15 -0
  19. package/dist/http/errors.js +162 -0
  20. package/dist/http/errors.js.map +1 -0
  21. package/dist/http/mcp/catalogue.d.ts +37 -0
  22. package/dist/http/mcp/catalogue.js +69 -0
  23. package/dist/http/mcp/catalogue.js.map +1 -0
  24. package/dist/http/mcp/challenge.d.ts +7 -0
  25. package/dist/http/mcp/challenge.js +35 -0
  26. package/dist/http/mcp/challenge.js.map +1 -0
  27. package/dist/http/mcp/input-schema.d.ts +7 -0
  28. package/dist/http/mcp/input-schema.js +28 -0
  29. package/dist/http/mcp/input-schema.js.map +1 -0
  30. package/dist/http/mcp/route.d.ts +8 -0
  31. package/dist/http/mcp/route.js +75 -0
  32. package/dist/http/mcp/route.js.map +1 -0
  33. package/dist/http/mcp/server.d.ts +8 -0
  34. package/dist/http/mcp/server.js +31 -0
  35. package/dist/http/mcp/server.js.map +1 -0
  36. package/dist/http/mcp/test-support.d.ts +55 -0
  37. package/dist/http/mcp/test-support.js +151 -0
  38. package/dist/http/mcp/test-support.js.map +1 -0
  39. package/dist/http/mcp/tool.d.ts +48 -0
  40. package/dist/http/mcp/tool.js +58 -0
  41. package/dist/http/mcp/tool.js.map +1 -0
  42. package/dist/http/mcp/tools/automations.d.ts +6 -0
  43. package/dist/http/mcp/tools/automations.js +120 -0
  44. package/dist/http/mcp/tools/automations.js.map +1 -0
  45. package/dist/http/mcp/tools/catalog.d.ts +6 -0
  46. package/dist/http/mcp/tools/catalog.js +200 -0
  47. package/dist/http/mcp/tools/catalog.js.map +1 -0
  48. package/dist/http/mcp/tools/comments.d.ts +6 -0
  49. package/dist/http/mcp/tools/comments.js +70 -0
  50. package/dist/http/mcp/tools/comments.js.map +1 -0
  51. package/dist/http/mcp/tools/communities.d.ts +6 -0
  52. package/dist/http/mcp/tools/communities.js +175 -0
  53. package/dist/http/mcp/tools/communities.js.map +1 -0
  54. package/dist/http/mcp/tools/community-members.d.ts +6 -0
  55. package/dist/http/mcp/tools/community-members.js +89 -0
  56. package/dist/http/mcp/tools/community-members.js.map +1 -0
  57. package/dist/http/mcp/tools/community-moderation.d.ts +6 -0
  58. package/dist/http/mcp/tools/community-moderation.js +144 -0
  59. package/dist/http/mcp/tools/community-moderation.js.map +1 -0
  60. package/dist/http/mcp/tools/courses.d.ts +6 -0
  61. package/dist/http/mcp/tools/courses.js +121 -0
  62. package/dist/http/mcp/tools/courses.js.map +1 -0
  63. package/dist/http/mcp/tools/index.d.ts +6 -0
  64. package/dist/http/mcp/tools/index.js +23 -0
  65. package/dist/http/mcp/tools/index.js.map +1 -0
  66. package/dist/http/mcp/tools/people.d.ts +6 -0
  67. package/dist/http/mcp/tools/people.js +145 -0
  68. package/dist/http/mcp/tools/people.js.map +1 -0
  69. package/dist/http/plugins/auth.js +13 -5
  70. package/dist/http/plugins/auth.js.map +1 -1
  71. package/dist/http/plugins/error-handler.d.ts +2 -1
  72. package/dist/http/plugins/error-handler.js +51 -65
  73. package/dist/http/plugins/error-handler.js.map +1 -1
  74. package/dist/http/plugins/openapi.js +49 -2
  75. package/dist/http/plugins/openapi.js.map +1 -1
  76. package/dist/http/routes/access.d.ts +7 -0
  77. package/dist/http/routes/access.js +140 -0
  78. package/dist/http/routes/access.js.map +1 -0
  79. package/dist/http/routes/activities.js +68 -23
  80. package/dist/http/routes/activities.js.map +1 -1
  81. package/dist/http/routes/assets.js +60 -55
  82. package/dist/http/routes/assets.js.map +1 -1
  83. package/dist/http/routes/automations.js +44 -24
  84. package/dist/http/routes/automations.js.map +1 -1
  85. package/dist/http/routes/bundles.js +29 -18
  86. package/dist/http/routes/bundles.js.map +1 -1
  87. package/dist/http/routes/communities.d.ts +7 -0
  88. package/dist/http/routes/communities.js +522 -0
  89. package/dist/http/routes/communities.js.map +1 -0
  90. package/dist/http/routes/community-moderation.d.ts +7 -0
  91. package/dist/http/routes/community-moderation.js +425 -0
  92. package/dist/http/routes/community-moderation.js.map +1 -0
  93. package/dist/http/routes/course-analytics.js +9 -7
  94. package/dist/http/routes/course-analytics.js.map +1 -1
  95. package/dist/http/routes/courses.js +30 -15
  96. package/dist/http/routes/courses.js.map +1 -1
  97. package/dist/http/routes/dashboard.js +15 -6
  98. package/dist/http/routes/dashboard.js.map +1 -1
  99. package/dist/http/routes/discussion.js +34 -26
  100. package/dist/http/routes/discussion.js.map +1 -1
  101. package/dist/http/routes/downloads.js +48 -30
  102. package/dist/http/routes/downloads.js.map +1 -1
  103. package/dist/http/routes/entitlements.js +16 -7
  104. package/dist/http/routes/entitlements.js.map +1 -1
  105. package/dist/http/routes/integrations.js +38 -41
  106. package/dist/http/routes/integrations.js.map +1 -1
  107. package/dist/http/routes/member/actor.d.ts +14 -0
  108. package/dist/http/routes/member/actor.js +11 -0
  109. package/dist/http/routes/member/actor.js.map +1 -0
  110. package/dist/http/routes/member/assets.d.ts +7 -0
  111. package/dist/http/routes/member/assets.js +79 -0
  112. package/dist/http/routes/member/assets.js.map +1 -0
  113. package/dist/http/routes/member/chats.d.ts +7 -0
  114. package/dist/http/routes/member/chats.js +435 -0
  115. package/dist/http/routes/member/chats.js.map +1 -0
  116. package/dist/http/routes/member/comments.d.ts +7 -0
  117. package/dist/http/routes/member/comments.js +187 -0
  118. package/dist/http/routes/member/comments.js.map +1 -0
  119. package/dist/http/routes/member/community.d.ts +7 -0
  120. package/dist/http/routes/member/community.js +312 -0
  121. package/dist/http/routes/member/community.js.map +1 -0
  122. package/dist/http/routes/member/courses.d.ts +7 -0
  123. package/dist/http/routes/member/courses.js +101 -0
  124. package/dist/http/routes/member/courses.js.map +1 -0
  125. package/dist/http/routes/member/devices.d.ts +7 -0
  126. package/dist/http/routes/member/devices.js +98 -0
  127. package/dist/http/routes/member/devices.js.map +1 -0
  128. package/dist/http/routes/member/downloads.d.ts +7 -0
  129. package/dist/http/routes/member/downloads.js +56 -0
  130. package/dist/http/routes/member/downloads.js.map +1 -0
  131. package/dist/http/routes/member/files.d.ts +7 -0
  132. package/dist/http/routes/member/files.js +184 -0
  133. package/dist/http/routes/member/files.js.map +1 -0
  134. package/dist/http/routes/member/index.d.ts +7 -0
  135. package/dist/http/routes/member/index.js +35 -0
  136. package/dist/http/routes/member/index.js.map +1 -0
  137. package/dist/http/routes/member/notification-settings.d.ts +7 -0
  138. package/dist/http/routes/member/notification-settings.js +108 -0
  139. package/dist/http/routes/member/notification-settings.js.map +1 -0
  140. package/dist/http/routes/member/notifications.d.ts +7 -0
  141. package/dist/http/routes/member/notifications.js +125 -0
  142. package/dist/http/routes/member/notifications.js.map +1 -0
  143. package/dist/http/routes/member/org.d.ts +7 -0
  144. package/dist/http/routes/member/org.js +56 -0
  145. package/dist/http/routes/member/org.js.map +1 -0
  146. package/dist/http/routes/member/posts.d.ts +7 -0
  147. package/dist/http/routes/member/posts.js +612 -0
  148. package/dist/http/routes/member/posts.js.map +1 -0
  149. package/dist/http/routes/member/profile.d.ts +7 -0
  150. package/dist/http/routes/member/profile.js +48 -0
  151. package/dist/http/routes/member/profile.js.map +1 -0
  152. package/dist/http/routes/member/progress.d.ts +7 -0
  153. package/dist/http/routes/member/progress.js +60 -0
  154. package/dist/http/routes/member/progress.js.map +1 -0
  155. package/dist/http/routes/organizations.js +171 -25
  156. package/dist/http/routes/organizations.js.map +1 -1
  157. package/dist/http/routes/students.js +32 -47
  158. package/dist/http/routes/students.js.map +1 -1
  159. package/dist/http/routes/webhooks.js +26 -18
  160. package/dist/http/routes/webhooks.js.map +1 -1
  161. package/dist/http/routes.js +8 -2
  162. package/dist/http/routes.js.map +1 -1
  163. package/dist/http/schemas/access.d.ts +146 -0
  164. package/dist/http/schemas/access.js +36 -0
  165. package/dist/http/schemas/access.js.map +1 -0
  166. package/dist/http/schemas/activities.d.ts +3 -1
  167. package/dist/http/schemas/activities.js +4 -4
  168. package/dist/http/schemas/activities.js.map +1 -1
  169. package/dist/http/schemas/assets.d.ts +79 -80
  170. package/dist/http/schemas/assets.js +23 -13
  171. package/dist/http/schemas/assets.js.map +1 -1
  172. package/dist/http/schemas/automations.d.ts +6 -0
  173. package/dist/http/schemas/bundles.js +6 -6
  174. package/dist/http/schemas/bundles.js.map +1 -1
  175. package/dist/http/schemas/chats.d.ts +409 -0
  176. package/dist/http/schemas/chats.js +56 -0
  177. package/dist/http/schemas/chats.js.map +1 -0
  178. package/dist/http/schemas/community.d.ts +597 -0
  179. package/dist/http/schemas/community.js +132 -0
  180. package/dist/http/schemas/community.js.map +1 -0
  181. package/dist/http/schemas/content.d.ts +4 -2
  182. package/dist/http/schemas/content.js +11 -8
  183. package/dist/http/schemas/content.js.map +1 -1
  184. package/dist/http/schemas/discussion.d.ts +98 -14
  185. package/dist/http/schemas/discussion.js +25 -12
  186. package/dist/http/schemas/discussion.js.map +1 -1
  187. package/dist/http/schemas/downloads.d.ts +11 -5
  188. package/dist/http/schemas/downloads.js +14 -11
  189. package/dist/http/schemas/downloads.js.map +1 -1
  190. package/dist/http/schemas/entitlements.d.ts +6 -6
  191. package/dist/http/schemas/entitlements.js +6 -6
  192. package/dist/http/schemas/entitlements.js.map +1 -1
  193. package/dist/http/schemas/files.d.ts +45 -0
  194. package/dist/http/schemas/files.js +25 -0
  195. package/dist/http/schemas/files.js.map +1 -0
  196. package/dist/http/schemas/index.d.ts +15 -8
  197. package/dist/http/schemas/index.js +15 -8
  198. package/dist/http/schemas/integrations.d.ts +4 -4
  199. package/dist/http/schemas/invites.d.ts +4 -0
  200. package/dist/http/schemas/invites.js +1 -0
  201. package/dist/http/schemas/invites.js.map +1 -1
  202. package/dist/http/schemas/{learn.d.ts → member.d.ts} +31 -18
  203. package/dist/http/schemas/{learn.js → member.js} +17 -13
  204. package/dist/http/schemas/member.js.map +1 -0
  205. package/dist/http/schemas/members.d.ts +35 -2
  206. package/dist/http/schemas/members.js +17 -3
  207. package/dist/http/schemas/members.js.map +1 -1
  208. package/dist/http/schemas/notifications.d.ts +550 -0
  209. package/dist/http/schemas/notifications.js +164 -0
  210. package/dist/http/schemas/notifications.js.map +1 -0
  211. package/dist/http/schemas/posts.d.ts +350 -0
  212. package/dist/http/schemas/posts.js +47 -0
  213. package/dist/http/schemas/posts.js.map +1 -0
  214. package/dist/http/schemas/profile.d.ts +23 -0
  215. package/dist/http/schemas/profile.js +15 -0
  216. package/dist/http/schemas/profile.js.map +1 -0
  217. package/dist/http/schemas/shared.d.ts +32 -1
  218. package/dist/http/schemas/shared.js +20 -2
  219. package/dist/http/schemas/shared.js.map +1 -1
  220. package/dist/http/schemas/students.d.ts +1 -7
  221. package/dist/http/schemas/students.js +1 -7
  222. package/dist/http/schemas/students.js.map +1 -1
  223. package/dist/http/scope.d.ts +3 -1
  224. package/dist/http/scope.js +26 -4
  225. package/dist/http/scope.js.map +1 -1
  226. package/dist/http/server.js +6 -1
  227. package/dist/http/server.js.map +1 -1
  228. package/dist/http/web-bridge.d.ts +1 -1
  229. package/dist/http/web-bridge.js +2 -2
  230. package/dist/http/web-bridge.js.map +1 -1
  231. package/dist/http/well-known.d.ts +7 -0
  232. package/dist/http/well-known.js +22 -0
  233. package/dist/http/well-known.js.map +1 -0
  234. package/dist/index.d.ts +3 -1
  235. package/dist/index.js +4 -2
  236. package/dist/index.js.map +1 -1
  237. package/dist/node_modules/.pnpm/@jridgewell_sourcemap-codec@1.5.5/node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.js +78 -0
  238. package/dist/node_modules/.pnpm/@jridgewell_sourcemap-codec@1.5.5/node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.js.map +1 -0
  239. package/dist/node_modules/.pnpm/@vitest_expect@2.1.9/node_modules/@vitest/expect/dist/index.js +1267 -0
  240. package/dist/node_modules/.pnpm/@vitest_expect@2.1.9/node_modules/@vitest/expect/dist/index.js.map +1 -0
  241. package/dist/node_modules/.pnpm/@vitest_pretty-format@2.1.9/node_modules/@vitest/pretty-format/dist/index.js +814 -0
  242. package/dist/node_modules/.pnpm/@vitest_pretty-format@2.1.9/node_modules/@vitest/pretty-format/dist/index.js.map +1 -0
  243. package/dist/node_modules/.pnpm/@vitest_runner@2.1.9/node_modules/@vitest/runner/dist/chunk-tasks.js +43 -0
  244. package/dist/node_modules/.pnpm/@vitest_runner@2.1.9/node_modules/@vitest/runner/dist/chunk-tasks.js.map +1 -0
  245. package/dist/node_modules/.pnpm/@vitest_runner@2.1.9/node_modules/@vitest/runner/dist/index.js +527 -0
  246. package/dist/node_modules/.pnpm/@vitest_runner@2.1.9/node_modules/@vitest/runner/dist/index.js.map +1 -0
  247. package/dist/node_modules/.pnpm/@vitest_runner@2.1.9/node_modules/@vitest/runner/dist/utils.js +2 -0
  248. package/dist/node_modules/.pnpm/@vitest_snapshot@2.1.9/node_modules/@vitest/snapshot/dist/index.js +1737 -0
  249. package/dist/node_modules/.pnpm/@vitest_snapshot@2.1.9/node_modules/@vitest/snapshot/dist/index.js.map +1 -0
  250. package/dist/node_modules/.pnpm/@vitest_spy@2.1.9/node_modules/@vitest/spy/dist/index.js +137 -0
  251. package/dist/node_modules/.pnpm/@vitest_spy@2.1.9/node_modules/@vitest/spy/dist/index.js.map +1 -0
  252. package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/chunk-_commonjsHelpers.js +118 -0
  253. package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/chunk-_commonjsHelpers.js.map +1 -0
  254. package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/diff.js +1222 -0
  255. package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/diff.js.map +1 -0
  256. package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/error.js +91 -0
  257. package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/error.js.map +1 -0
  258. package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/helpers.js +113 -0
  259. package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/helpers.js.map +1 -0
  260. package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/index.js +449 -0
  261. package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/index.js.map +1 -0
  262. package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/source-map.js +163 -0
  263. package/dist/node_modules/.pnpm/@vitest_utils@2.1.9/node_modules/@vitest/utils/dist/source-map.js.map +1 -0
  264. package/dist/node_modules/.pnpm/chai@5.3.3/node_modules/chai/index.js +3187 -0
  265. package/dist/node_modules/.pnpm/chai@5.3.3/node_modules/chai/index.js.map +1 -0
  266. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/arguments.js +11 -0
  267. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/arguments.js.map +1 -0
  268. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/array.js +16 -0
  269. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/array.js.map +1 -0
  270. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/bigint.js +11 -0
  271. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/bigint.js.map +1 -0
  272. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/class.js +15 -0
  273. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/class.js.map +1 -0
  274. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/date.js +13 -0
  275. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/date.js.map +1 -0
  276. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/error.js +34 -0
  277. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/error.js.map +1 -0
  278. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/function.js +12 -0
  279. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/function.js.map +1 -0
  280. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/helpers.js +122 -0
  281. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/helpers.js.map +1 -0
  282. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/html.js +39 -0
  283. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/html.js.map +1 -0
  284. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/index.js +99 -0
  285. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/index.js.map +1 -0
  286. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/map.js +25 -0
  287. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/map.js.map +1 -0
  288. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/number.js +14 -0
  289. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/number.js.map +1 -0
  290. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/object.js +21 -0
  291. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/object.js.map +1 -0
  292. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/promise.js +6 -0
  293. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/promise.js.map +1 -0
  294. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/regexp.js +12 -0
  295. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/regexp.js.map +1 -0
  296. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/set.js +18 -0
  297. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/set.js.map +1 -0
  298. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/string.js +24 -0
  299. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/string.js.map +1 -0
  300. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/symbol.js +9 -0
  301. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/symbol.js.map +1 -0
  302. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/typedarray.js +30 -0
  303. package/dist/node_modules/.pnpm/loupe@3.2.1/node_modules/loupe/lib/typedarray.js.map +1 -0
  304. package/dist/node_modules/.pnpm/magic-string@0.30.21/node_modules/magic-string/dist/magic-string.es.js +939 -0
  305. package/dist/node_modules/.pnpm/magic-string@0.30.21/node_modules/magic-string/dist/magic-string.es.js.map +1 -0
  306. package/dist/node_modules/.pnpm/pathe@1.1.2/node_modules/pathe/dist/shared/pathe.ff20891b.js +82 -0
  307. package/dist/node_modules/.pnpm/pathe@1.1.2/node_modules/pathe/dist/shared/pathe.ff20891b.js.map +1 -0
  308. package/dist/node_modules/.pnpm/tinyrainbow@1.2.0/node_modules/tinyrainbow/dist/chunk-BVHSVHOK.js +82 -0
  309. package/dist/node_modules/.pnpm/tinyrainbow@1.2.0/node_modules/tinyrainbow/dist/chunk-BVHSVHOK.js.map +1 -0
  310. package/dist/node_modules/.pnpm/tinyrainbow@1.2.0/node_modules/tinyrainbow/dist/node.js +8 -0
  311. package/dist/node_modules/.pnpm/tinyrainbow@1.2.0/node_modules/tinyrainbow/dist/node.js.map +1 -0
  312. package/dist/node_modules/.pnpm/tinyspy@3.0.2/node_modules/tinyspy/dist/index.js +83 -0
  313. package/dist/node_modules/.pnpm/tinyspy@3.0.2/node_modules/tinyspy/dist/index.js.map +1 -0
  314. package/dist/node_modules/.pnpm/vitest@2.1.9_@types_node@22.20.0_jsdom@25.0.1_lightningcss@1.32.0/node_modules/vitest/dist/chunks/_commonjsHelpers.BFTU3MAI.js +9 -0
  315. package/dist/node_modules/.pnpm/vitest@2.1.9_@types_node@22.20.0_jsdom@25.0.1_lightningcss@1.32.0/node_modules/vitest/dist/chunks/_commonjsHelpers.BFTU3MAI.js.map +1 -0
  316. package/dist/node_modules/.pnpm/vitest@2.1.9_@types_node@22.20.0_jsdom@25.0.1_lightningcss@1.32.0/node_modules/vitest/dist/chunks/date.W2xKR2qe.js +51 -0
  317. package/dist/node_modules/.pnpm/vitest@2.1.9_@types_node@22.20.0_jsdom@25.0.1_lightningcss@1.32.0/node_modules/vitest/dist/chunks/date.W2xKR2qe.js.map +1 -0
  318. package/dist/node_modules/.pnpm/vitest@2.1.9_@types_node@22.20.0_jsdom@25.0.1_lightningcss@1.32.0/node_modules/vitest/dist/chunks/utils.C8RiOc4B.js +48 -0
  319. package/dist/node_modules/.pnpm/vitest@2.1.9_@types_node@22.20.0_jsdom@25.0.1_lightningcss@1.32.0/node_modules/vitest/dist/chunks/utils.C8RiOc4B.js.map +1 -0
  320. package/dist/node_modules/.pnpm/vitest@2.1.9_@types_node@22.20.0_jsdom@25.0.1_lightningcss@1.32.0/node_modules/vitest/dist/chunks/vi.DgezovHB.js +2426 -0
  321. package/dist/node_modules/.pnpm/vitest@2.1.9_@types_node@22.20.0_jsdom@25.0.1_lightningcss@1.32.0/node_modules/vitest/dist/chunks/vi.DgezovHB.js.map +1 -0
  322. package/dist/packages/server/package.js +6 -0
  323. package/dist/packages/server/package.js.map +1 -0
  324. package/package.json +9 -7
  325. package/dist/app/notifications.d.ts +0 -7
  326. package/dist/app/notifications.js +0 -26
  327. package/dist/app/notifications.js.map +0 -1
  328. package/dist/http/routes/learn/assets.d.ts +0 -8
  329. package/dist/http/routes/learn/assets.js +0 -32
  330. package/dist/http/routes/learn/assets.js.map +0 -1
  331. package/dist/http/routes/learn/comments.d.ts +0 -7
  332. package/dist/http/routes/learn/comments.js +0 -175
  333. package/dist/http/routes/learn/comments.js.map +0 -1
  334. package/dist/http/routes/learn/courses.d.ts +0 -7
  335. package/dist/http/routes/learn/courses.js +0 -98
  336. package/dist/http/routes/learn/courses.js.map +0 -1
  337. package/dist/http/routes/learn/downloads.d.ts +0 -7
  338. package/dist/http/routes/learn/downloads.js +0 -76
  339. package/dist/http/routes/learn/downloads.js.map +0 -1
  340. package/dist/http/routes/learn/index.d.ts +0 -7
  341. package/dist/http/routes/learn/index.js +0 -19
  342. package/dist/http/routes/learn/index.js.map +0 -1
  343. package/dist/http/routes/learn/org.d.ts +0 -7
  344. package/dist/http/routes/learn/org.js +0 -50
  345. package/dist/http/routes/learn/org.js.map +0 -1
  346. package/dist/http/routes/learn/progress.d.ts +0 -7
  347. package/dist/http/routes/learn/progress.js +0 -63
  348. package/dist/http/routes/learn/progress.js.map +0 -1
  349. package/dist/http/schemas/learn.js.map +0 -1
  350. package/dist/package.js +0 -6
  351. package/dist/package.js.map +0 -1
@@ -0,0 +1,56 @@
1
+ import { refusals } from "../../schemas/shared.js";
2
+ import { DownloadIdParam } from "../../schemas/downloads.js";
3
+ import { MemberDownload, MemberDownloads } from "../../schemas/member.js";
4
+ import "../../schemas/index.js";
5
+ import { actorOf } from "./actor.js";
6
+ import { NotFoundError } from "@headless-lms/core/shared/errors";
7
+ //#region src/http/routes/member/downloads.ts
8
+ async function memberDownloadsRoutes(app, container) {
9
+ const r = app.withTypeProvider();
10
+ const member = container.reporting.member;
11
+ r.route({
12
+ method: "GET",
13
+ url: "/api/member/downloads",
14
+ preHandler: app.requireOrgSession,
15
+ schema: {
16
+ operationId: "listMemberDownloads",
17
+ tags: ["Content"],
18
+ summary: "List the downloads you can open",
19
+ description: "For a member of the organization. Published downloads you hold, directly or through a bundle.",
20
+ response: {
21
+ 200: MemberDownloads,
22
+ ...refusals(403)
23
+ }
24
+ },
25
+ handler: async (req) => {
26
+ const orgUser = await actorOf(container, req.orgId, req.userId);
27
+ return member.listDownloads(req.orgId, orgUser.id);
28
+ }
29
+ });
30
+ r.route({
31
+ method: "GET",
32
+ url: "/api/member/downloads/:downloadId",
33
+ preHandler: app.requireOrgSession,
34
+ schema: {
35
+ operationId: "getMemberDownload",
36
+ tags: ["Content"],
37
+ summary: "One download you can open, with its files in order",
38
+ description: "For a member of the organization. 404 when the download is not published or you do not hold it.",
39
+ params: DownloadIdParam,
40
+ response: {
41
+ 200: MemberDownload,
42
+ ...refusals(403, 404)
43
+ }
44
+ },
45
+ handler: async (req) => {
46
+ const orgUser = await actorOf(container, req.orgId, req.userId);
47
+ const result = await member.getDownload(req.orgId, orgUser.id, req.params.downloadId);
48
+ if (!result) throw new NotFoundError("Download", req.params.downloadId);
49
+ return result;
50
+ }
51
+ });
52
+ }
53
+ //#endregion
54
+ export { memberDownloadsRoutes };
55
+
56
+ //# sourceMappingURL=downloads.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"downloads.js","names":[],"sources":["../../../../src/http/routes/member/downloads.ts"],"sourcesContent":["import type { FastifyInstance } from 'fastify';\nimport type { ZodTypeProvider } from 'fastify-type-provider-zod';\nimport { DownloadIdParam, MemberDownload, MemberDownloads, refusals } from '../../schemas/index.js';\nimport { NotFoundError } from '@headless-lms/core/shared/errors';\nimport type { Container } from '../../../app/container.js';\nimport { actorOf } from './actor.js';\n\nexport async function memberDownloadsRoutes(\n app: FastifyInstance,\n container: Container,\n): Promise<void> {\n const r = app.withTypeProvider<ZodTypeProvider>();\n const member = container.reporting.member;\n\n r.route({\n method: 'GET',\n url: '/api/member/downloads',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'listMemberDownloads',\n tags: ['Content'],\n summary: 'List the downloads you can open',\n description:\n 'For a member of the organization. Published downloads you hold, directly or through a bundle.',\n response: { 200: MemberDownloads, ...refusals(403) },\n },\n handler: async (req) => {\n const orgUser = await actorOf(container, req.orgId, req.userId);\n return member.listDownloads(req.orgId, orgUser.id);\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/member/downloads/:downloadId',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'getMemberDownload',\n tags: ['Content'],\n summary: 'One download you can open, with its files in order',\n description:\n 'For a member of the organization. 404 when the download is not published or you do not hold it.',\n params: DownloadIdParam,\n response: { 200: MemberDownload, ...refusals(403, 404) },\n },\n handler: async (req) => {\n const orgUser = await actorOf(container, req.orgId, req.userId);\n const result = await member.getDownload(req.orgId, orgUser.id, req.params.downloadId);\n if (!result) {\n throw new NotFoundError('Download', req.params.downloadId);\n }\n return result;\n },\n });\n}\n"],"mappings":";;;;;;;AAOA,eAAsB,sBACpB,KACA,WACe;CACf,MAAM,IAAI,IAAI,iBAAkC;CAChD,MAAM,SAAS,UAAU,UAAU;CAEnC,EAAE,MAAM;EACN,QAAQ;EACR,KAAK;EACL,YAAY,IAAI;EAChB,QAAQ;GACN,aAAa;GACb,MAAM,CAAC,SAAS;GAChB,SAAS;GACT,aACE;GACF,UAAU;IAAE,KAAK;IAAiB,GAAG,SAAS,GAAG;GAAE;EACrD;EACA,SAAS,OAAO,QAAQ;GACtB,MAAM,UAAU,MAAM,QAAQ,WAAW,IAAI,OAAO,IAAI,MAAM;GAC9D,OAAO,OAAO,cAAc,IAAI,OAAO,QAAQ,EAAE;EACnD;CACF,CAAC;CAED,EAAE,MAAM;EACN,QAAQ;EACR,KAAK;EACL,YAAY,IAAI;EAChB,QAAQ;GACN,aAAa;GACb,MAAM,CAAC,SAAS;GAChB,SAAS;GACT,aACE;GACF,QAAQ;GACR,UAAU;IAAE,KAAK;IAAgB,GAAG,SAAS,KAAK,GAAG;GAAE;EACzD;EACA,SAAS,OAAO,QAAQ;GACtB,MAAM,UAAU,MAAM,QAAQ,WAAW,IAAI,OAAO,IAAI,MAAM;GAC9D,MAAM,SAAS,MAAM,OAAO,YAAY,IAAI,OAAO,QAAQ,IAAI,IAAI,OAAO,UAAU;GACpF,IAAI,CAAC,QACH,MAAM,IAAI,cAAc,YAAY,IAAI,OAAO,UAAU;GAE3D,OAAO;EACT;CACF,CAAC;AACH"}
@@ -0,0 +1,7 @@
1
+ import { Container } from "../../../app/container.js";
2
+ import { FastifyInstance } from "fastify";
3
+ //#region src/http/routes/member/files.d.ts
4
+ declare function memberFileRoutes(app: FastifyInstance, container: Container): Promise<void>;
5
+ //#endregion
6
+ export { memberFileRoutes };
7
+ //# sourceMappingURL=files.d.ts.map
@@ -0,0 +1,184 @@
1
+ import { refusals } from "../../schemas/shared.js";
2
+ import { ActivityFileParams, CommunityThumbnailParams, CourseThumbnailParams, DownloadFileParams, DownloadThumbnailParams, MessageAttachmentParams, PersonImageParams, PostCoverParams, UploadFileParams } from "../../schemas/files.js";
3
+ import "../../schemas/index.js";
4
+ import { actorOf } from "./actor.js";
5
+ import { z } from "zod";
6
+ import { NotFoundError } from "@headless-lms/core/shared/errors";
7
+ //#region src/http/routes/member/files.ts
8
+ const tags = ["Files"];
9
+ const responses = {
10
+ 302: z.void(),
11
+ ...refusals(403, 404)
12
+ };
13
+ function redirect(reply, url, what, id) {
14
+ if (!url) throw new NotFoundError(what, id);
15
+ return reply.header("cache-control", "no-store").header("referrer-policy", "no-referrer").redirect(url, 302);
16
+ }
17
+ async function memberFileRoutes(app, container) {
18
+ const r = app.withTypeProvider();
19
+ const member = container.reporting.member;
20
+ async function actorOf$1(orgId, userId) {
21
+ const orgUser = await actorOf(container, orgId, userId);
22
+ return {
23
+ id: orgUser.id,
24
+ role: orgUser.role
25
+ };
26
+ }
27
+ r.route({
28
+ method: "GET",
29
+ url: "/api/member/courses/:courseId/thumbnail",
30
+ preHandler: app.requireOrgSession,
31
+ schema: {
32
+ operationId: "getCourseThumbnail",
33
+ tags,
34
+ summary: "Redirect to a course's thumbnail",
35
+ description: "Any org user may fetch the thumbnail of a published course; staff may fetch any. 404 when the course has no thumbnail.",
36
+ params: CourseThumbnailParams,
37
+ response: responses
38
+ },
39
+ handler: async (req, reply) => {
40
+ const actor = await actorOf$1(req.orgId, req.userId);
41
+ return redirect(reply, await member.thumbnailFile(req.orgId, actor, "course", req.params.courseId), "Course thumbnail", req.params.courseId);
42
+ }
43
+ });
44
+ r.route({
45
+ method: "GET",
46
+ url: "/api/member/downloads/:downloadId/thumbnail",
47
+ preHandler: app.requireOrgSession,
48
+ schema: {
49
+ operationId: "getDownloadThumbnail",
50
+ tags,
51
+ summary: "Redirect to a download's thumbnail",
52
+ description: "Any org user may fetch the thumbnail of a published download; staff may fetch any. 404 when the download has no thumbnail.",
53
+ params: DownloadThumbnailParams,
54
+ response: responses
55
+ },
56
+ handler: async (req, reply) => {
57
+ const actor = await actorOf$1(req.orgId, req.userId);
58
+ return redirect(reply, await member.thumbnailFile(req.orgId, actor, "download", req.params.downloadId), "Download thumbnail", req.params.downloadId);
59
+ }
60
+ });
61
+ r.route({
62
+ method: "GET",
63
+ url: "/api/member/communities/:communityId/thumbnail",
64
+ preHandler: app.requireOrgSession,
65
+ schema: {
66
+ operationId: "getCommunityThumbnail",
67
+ tags,
68
+ summary: "Redirect to a community's thumbnail",
69
+ description: "Any org user may fetch the thumbnail of a published community; staff may fetch any. 404 when the community has no thumbnail.",
70
+ params: CommunityThumbnailParams,
71
+ response: responses
72
+ },
73
+ handler: async (req, reply) => {
74
+ const actor = await actorOf$1(req.orgId, req.userId);
75
+ return redirect(reply, await member.thumbnailFile(req.orgId, actor, "community", req.params.communityId), "Community thumbnail", req.params.communityId);
76
+ }
77
+ });
78
+ r.route({
79
+ method: "GET",
80
+ url: "/api/member/activities/:activityId/assets/:assetId",
81
+ preHandler: app.requireOrgSession,
82
+ schema: {
83
+ operationId: "getActivityFile",
84
+ tags,
85
+ summary: "Redirect to a file used in a lesson",
86
+ description: "Served when the student can open the course, the course and the lesson are published, and the lesson lists the file. Staff may fetch any file a lesson lists. Use the URL as an img, video or audio src.",
87
+ params: ActivityFileParams,
88
+ response: responses
89
+ },
90
+ handler: async (req, reply) => {
91
+ const actor = await actorOf$1(req.orgId, req.userId);
92
+ return redirect(reply, await member.activityFile(req.orgId, actor, req.params.activityId, req.params.assetId), "Lesson file", req.params.assetId);
93
+ }
94
+ });
95
+ r.route({
96
+ method: "GET",
97
+ url: "/api/member/downloads/:downloadId/assets/:assetId",
98
+ preHandler: app.requireOrgSession,
99
+ schema: {
100
+ operationId: "getDownloadFile",
101
+ tags,
102
+ summary: "Redirect to a file in a download, saved under its display name",
103
+ description: "Served when the student can open the download, it is published, and it lists the file. Staff may fetch any file a download lists. The storage link lives DELIVERY_URL_EXPIRY seconds.",
104
+ params: DownloadFileParams,
105
+ response: responses
106
+ },
107
+ handler: async (req, reply) => {
108
+ const actor = await actorOf$1(req.orgId, req.userId);
109
+ return redirect(reply, await member.downloadFile(req.orgId, actor, req.params.downloadId, req.params.assetId), "Download file", req.params.assetId);
110
+ }
111
+ });
112
+ r.route({
113
+ method: "GET",
114
+ url: "/api/member/posts/:postId/cover",
115
+ preHandler: app.requireOrgSession,
116
+ schema: {
117
+ operationId: "getPostCover",
118
+ tags,
119
+ summary: "Redirect to a post's cover image",
120
+ description: "Served to whoever can read the post. 404 when the post has no cover.",
121
+ params: PostCoverParams,
122
+ response: responses
123
+ },
124
+ handler: async (req, reply) => {
125
+ const actor = await actorOf$1(req.orgId, req.userId);
126
+ return redirect(reply, await container.posts.coverFile(req.orgId, actor, req.params.postId), "Post cover", req.params.postId);
127
+ }
128
+ });
129
+ r.route({
130
+ method: "GET",
131
+ url: "/api/member/chats/:chatId/messages/:messageId/attachments/:assetId",
132
+ preHandler: app.requireOrgSession,
133
+ schema: {
134
+ operationId: "getMessageAttachment",
135
+ tags,
136
+ summary: "Redirect to an image attached to a chat message",
137
+ description: "Served to the chat's participants while the message stands (staff also after it is deleted), and to moderators while the message is flagged.",
138
+ params: MessageAttachmentParams,
139
+ response: responses
140
+ },
141
+ handler: async (req, reply) => {
142
+ const actor = await actorOf$1(req.orgId, req.userId);
143
+ return redirect(reply, await container.chats.attachmentFile(req.orgId, actor, req.params.chatId, req.params.messageId, req.params.assetId), "Message attachment", req.params.assetId);
144
+ }
145
+ });
146
+ r.route({
147
+ method: "GET",
148
+ url: "/api/member/people/:orgUserId/image",
149
+ preHandler: app.requireOrgSession,
150
+ schema: {
151
+ operationId: "getPersonImage",
152
+ tags,
153
+ summary: "Redirect to an org user's profile photo",
154
+ description: "Served to org users of the same organization when the photo belongs to it. 404 when the person has no photo here.",
155
+ params: PersonImageParams,
156
+ response: responses
157
+ },
158
+ handler: async (req, reply) => {
159
+ await actorOf$1(req.orgId, req.userId);
160
+ return redirect(reply, await member.personImage(req.orgId, req.params.orgUserId), "Profile photo", req.params.orgUserId);
161
+ }
162
+ });
163
+ r.route({
164
+ method: "GET",
165
+ url: "/api/member/uploads/:id/file",
166
+ preHandler: app.requireOrgSession,
167
+ schema: {
168
+ operationId: "getMemberUploadFile",
169
+ tags,
170
+ summary: "Redirect to an image you uploaded",
171
+ description: "Served only to the person who uploaded it, once it is finalized.",
172
+ params: UploadFileParams,
173
+ response: responses
174
+ },
175
+ handler: async (req, reply) => {
176
+ const actor = await actorOf$1(req.orgId, req.userId);
177
+ return redirect(reply, await container.assets.ownUploadFile(req.orgId, req.params.id, actor.id), "Upload", req.params.id);
178
+ }
179
+ });
180
+ }
181
+ //#endregion
182
+ export { memberFileRoutes };
183
+
184
+ //# sourceMappingURL=files.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"files.js","names":["actorOf","memberOf"],"sources":["../../../../src/http/routes/member/files.ts"],"sourcesContent":["// Member file proxies. Each route checks access with the context that owns the\n// file, then redirects to a short-lived signed storage URL. Every refusal is a\n// 404, so a response never confirms that a file exists. A 403 only means the\n// caller is not a member of the organization.\nimport type { FastifyInstance, FastifyReply } from 'fastify';\nimport type { ZodTypeProvider } from 'fastify-type-provider-zod';\nimport { z } from 'zod';\nimport { NotFoundError } from '@headless-lms/core/shared/errors';\nimport {\n ActivityFileParams,\n CommunityThumbnailParams,\n CourseThumbnailParams,\n DownloadFileParams,\n DownloadThumbnailParams,\n MessageAttachmentParams,\n PersonImageParams,\n PostCoverParams,\n UploadFileParams,\n refusals,\n} from '../../schemas/index.js';\nimport { actorOf as memberOf } from './actor.js';\nimport type { Container } from '../../../app/container.js';\n\nconst tags = ['Files'];\nconst responses = { 302: z.void(), ...refusals(403, 404) };\n\nfunction redirect(reply: FastifyReply, url: string | null, what: string, id: string) {\n if (!url) {\n throw new NotFoundError(what, id);\n }\n return reply\n .header('cache-control', 'no-store')\n .header('referrer-policy', 'no-referrer')\n .redirect(url, 302);\n}\n\nexport async function memberFileRoutes(app: FastifyInstance, container: Container): Promise<void> {\n const r = app.withTypeProvider<ZodTypeProvider>();\n const member = container.reporting.member;\n\n async function actorOf(orgId: string, userId: string) {\n const orgUser = await memberOf(container, orgId, userId);\n return { id: orgUser.id, role: orgUser.role };\n }\n\n r.route({\n method: 'GET',\n url: '/api/member/courses/:courseId/thumbnail',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'getCourseThumbnail',\n tags,\n summary: \"Redirect to a course's thumbnail\",\n description:\n 'Any org user may fetch the thumbnail of a published course; staff may fetch any. 404 when the course has no thumbnail.',\n params: CourseThumbnailParams,\n response: responses,\n },\n handler: async (req, reply) => {\n const actor = await actorOf(req.orgId, req.userId);\n const url = await member.thumbnailFile(req.orgId, actor, 'course', req.params.courseId);\n return redirect(reply, url, 'Course thumbnail', req.params.courseId);\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/member/downloads/:downloadId/thumbnail',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'getDownloadThumbnail',\n tags,\n summary: \"Redirect to a download's thumbnail\",\n description:\n 'Any org user may fetch the thumbnail of a published download; staff may fetch any. 404 when the download has no thumbnail.',\n params: DownloadThumbnailParams,\n response: responses,\n },\n handler: async (req, reply) => {\n const actor = await actorOf(req.orgId, req.userId);\n const url = await member.thumbnailFile(req.orgId, actor, 'download', req.params.downloadId);\n return redirect(reply, url, 'Download thumbnail', req.params.downloadId);\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/member/communities/:communityId/thumbnail',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'getCommunityThumbnail',\n tags,\n summary: \"Redirect to a community's thumbnail\",\n description:\n 'Any org user may fetch the thumbnail of a published community; staff may fetch any. 404 when the community has no thumbnail.',\n params: CommunityThumbnailParams,\n response: responses,\n },\n handler: async (req, reply) => {\n const actor = await actorOf(req.orgId, req.userId);\n const url = await member.thumbnailFile(req.orgId, actor, 'community', req.params.communityId);\n return redirect(reply, url, 'Community thumbnail', req.params.communityId);\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/member/activities/:activityId/assets/:assetId',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'getActivityFile',\n tags,\n summary: 'Redirect to a file used in a lesson',\n description:\n 'Served when the student can open the course, the course and the lesson are published, and the lesson lists the file. Staff may fetch any file a lesson lists. Use the URL as an img, video or audio src.',\n params: ActivityFileParams,\n response: responses,\n },\n handler: async (req, reply) => {\n const actor = await actorOf(req.orgId, req.userId);\n const url = await member.activityFile(\n req.orgId,\n actor,\n req.params.activityId,\n req.params.assetId,\n );\n return redirect(reply, url, 'Lesson file', req.params.assetId);\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/member/downloads/:downloadId/assets/:assetId',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'getDownloadFile',\n tags,\n summary: 'Redirect to a file in a download, saved under its display name',\n description:\n 'Served when the student can open the download, it is published, and it lists the file. Staff may fetch any file a download lists. The storage link lives DELIVERY_URL_EXPIRY seconds.',\n params: DownloadFileParams,\n response: responses,\n },\n handler: async (req, reply) => {\n const actor = await actorOf(req.orgId, req.userId);\n const url = await member.downloadFile(\n req.orgId,\n actor,\n req.params.downloadId,\n req.params.assetId,\n );\n return redirect(reply, url, 'Download file', req.params.assetId);\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/member/posts/:postId/cover',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'getPostCover',\n tags,\n summary: \"Redirect to a post's cover image\",\n description: 'Served to whoever can read the post. 404 when the post has no cover.',\n params: PostCoverParams,\n response: responses,\n },\n handler: async (req, reply) => {\n const actor = await actorOf(req.orgId, req.userId);\n const url = await container.posts.coverFile(req.orgId, actor, req.params.postId);\n return redirect(reply, url, 'Post cover', req.params.postId);\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/member/chats/:chatId/messages/:messageId/attachments/:assetId',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'getMessageAttachment',\n tags,\n summary: 'Redirect to an image attached to a chat message',\n description:\n \"Served to the chat's participants while the message stands (staff also after it is deleted), and to moderators while the message is flagged.\",\n params: MessageAttachmentParams,\n response: responses,\n },\n handler: async (req, reply) => {\n const actor = await actorOf(req.orgId, req.userId);\n const url = await container.chats.attachmentFile(\n req.orgId,\n actor,\n req.params.chatId,\n req.params.messageId,\n req.params.assetId,\n );\n return redirect(reply, url, 'Message attachment', req.params.assetId);\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/member/people/:orgUserId/image',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'getPersonImage',\n tags,\n summary: \"Redirect to an org user's profile photo\",\n description:\n 'Served to org users of the same organization when the photo belongs to it. 404 when the person has no photo here.',\n params: PersonImageParams,\n response: responses,\n },\n handler: async (req, reply) => {\n await actorOf(req.orgId, req.userId);\n const url = await member.personImage(req.orgId, req.params.orgUserId);\n return redirect(reply, url, 'Profile photo', req.params.orgUserId);\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/member/uploads/:id/file',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'getMemberUploadFile',\n tags,\n summary: 'Redirect to an image you uploaded',\n description: 'Served only to the person who uploaded it, once it is finalized.',\n params: UploadFileParams,\n response: responses,\n },\n handler: async (req, reply) => {\n const actor = await actorOf(req.orgId, req.userId);\n const url = await container.assets.ownUploadFile(req.orgId, req.params.id, actor.id);\n return redirect(reply, url, 'Upload', req.params.id);\n },\n });\n}\n"],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import { Container } from "../../../app/container.js";
2
+ import { FastifyInstance } from "fastify";
3
+ //#region src/http/routes/member/index.d.ts
4
+ declare function memberRoutes(app: FastifyInstance, container: Container): Promise<void>;
5
+ //#endregion
6
+ export { memberRoutes };
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,35 @@
1
+ import { memberAssetsRoutes } from "./assets.js";
2
+ import { memberChatRoutes } from "./chats.js";
3
+ import { memberCommentsRoutes } from "./comments.js";
4
+ import { memberCommunityRoutes } from "./community.js";
5
+ import { memberCoursesRoutes } from "./courses.js";
6
+ import { memberDownloadsRoutes } from "./downloads.js";
7
+ import { memberFileRoutes } from "./files.js";
8
+ import { memberNotificationRoutes } from "./notifications.js";
9
+ import { memberNotificationSettingsRoutes } from "./notification-settings.js";
10
+ import { memberDeviceRoutes } from "./devices.js";
11
+ import { memberOrgRoutes } from "./org.js";
12
+ import { memberPostRoutes } from "./posts.js";
13
+ import { memberProfileRoutes } from "./profile.js";
14
+ import { memberProgressRoutes } from "./progress.js";
15
+ //#region src/http/routes/member/index.ts
16
+ async function memberRoutes(app, container) {
17
+ await memberOrgRoutes(app, container);
18
+ await memberProfileRoutes(app, container);
19
+ await memberCommunityRoutes(app, container);
20
+ await memberPostRoutes(app, container);
21
+ await memberChatRoutes(app, container);
22
+ await memberCoursesRoutes(app, container);
23
+ await memberProgressRoutes(app, container);
24
+ await memberCommentsRoutes(app, container);
25
+ await memberAssetsRoutes(app, container);
26
+ await memberDownloadsRoutes(app, container);
27
+ await memberFileRoutes(app, container);
28
+ await memberNotificationRoutes(app, container);
29
+ await memberNotificationSettingsRoutes(app, container);
30
+ await memberDeviceRoutes(app, container);
31
+ }
32
+ //#endregion
33
+ export { memberRoutes };
34
+
35
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../../src/http/routes/member/index.ts"],"sourcesContent":["// Student HTTP API under `/api/member`. Session-guarded like every\n// back-office route, but scoped by the session's `org_users` row —\n// the reads gate on the person's enrollments, not on staff role. One file per\n// resource; everything under `/api/member` lives in this directory and nowhere\n// else. The course, download, and progress reads call reporting/member.\nimport type { FastifyInstance } from 'fastify';\nimport type { Container } from '../../../app/container.js';\nimport { memberAssetsRoutes } from './assets.js';\nimport { memberChatRoutes } from './chats.js';\nimport { memberCommentsRoutes } from './comments.js';\nimport { memberCommunityRoutes } from './community.js';\nimport { memberCoursesRoutes } from './courses.js';\nimport { memberDownloadsRoutes } from './downloads.js';\nimport { memberFileRoutes } from './files.js';\nimport { memberNotificationRoutes } from './notifications.js';\nimport { memberNotificationSettingsRoutes } from './notification-settings.js';\nimport { memberDeviceRoutes } from './devices.js';\nimport { memberOrgRoutes } from './org.js';\nimport { memberPostRoutes } from './posts.js';\nimport { memberProfileRoutes } from './profile.js';\nimport { memberProgressRoutes } from './progress.js';\n\nexport async function memberRoutes(app: FastifyInstance, container: Container): Promise<void> {\n await memberOrgRoutes(app, container);\n await memberProfileRoutes(app, container);\n await memberCommunityRoutes(app, container);\n await memberPostRoutes(app, container);\n await memberChatRoutes(app, container);\n await memberCoursesRoutes(app, container);\n await memberProgressRoutes(app, container);\n await memberCommentsRoutes(app, container);\n await memberAssetsRoutes(app, container);\n await memberDownloadsRoutes(app, container);\n await memberFileRoutes(app, container);\n await memberNotificationRoutes(app, container);\n await memberNotificationSettingsRoutes(app, container);\n await memberDeviceRoutes(app, container);\n}\n"],"mappings":";;;;;;;;;;;;;;;AAsBA,eAAsB,aAAa,KAAsB,WAAqC;CAC5F,MAAM,gBAAgB,KAAK,SAAS;CACpC,MAAM,oBAAoB,KAAK,SAAS;CACxC,MAAM,sBAAsB,KAAK,SAAS;CAC1C,MAAM,iBAAiB,KAAK,SAAS;CACrC,MAAM,iBAAiB,KAAK,SAAS;CACrC,MAAM,oBAAoB,KAAK,SAAS;CACxC,MAAM,qBAAqB,KAAK,SAAS;CACzC,MAAM,qBAAqB,KAAK,SAAS;CACzC,MAAM,mBAAmB,KAAK,SAAS;CACvC,MAAM,sBAAsB,KAAK,SAAS;CAC1C,MAAM,iBAAiB,KAAK,SAAS;CACrC,MAAM,yBAAyB,KAAK,SAAS;CAC7C,MAAM,iCAAiC,KAAK,SAAS;CACrD,MAAM,mBAAmB,KAAK,SAAS;AACzC"}
@@ -0,0 +1,7 @@
1
+ import { Container } from "../../../app/container.js";
2
+ import { FastifyInstance } from "fastify";
3
+ //#region src/http/routes/member/notification-settings.d.ts
4
+ declare function memberNotificationSettingsRoutes(app: FastifyInstance, container: Container): Promise<void>;
5
+ //#endregion
6
+ export { memberNotificationSettingsRoutes };
7
+ //# sourceMappingURL=notification-settings.d.ts.map
@@ -0,0 +1,108 @@
1
+ import { refusals } from "../../schemas/shared.js";
2
+ import { CommunityParam } from "../../schemas/chats.js";
3
+ import { CommunityNotificationSettings, OrgNotificationSettings, PatchNotificationSettings } from "../../schemas/notifications.js";
4
+ import "../../schemas/index.js";
5
+ import { actorOf } from "./actor.js";
6
+ import { z } from "zod";
7
+ import { NotFoundError } from "@headless-lms/core/shared/errors";
8
+ //#region src/http/routes/member/notification-settings.ts
9
+ async function memberNotificationSettingsRoutes(app, container) {
10
+ const r = app.withTypeProvider();
11
+ const tags = ["Notifications"];
12
+ r.route({
13
+ method: "GET",
14
+ url: "/api/member/communities/:communityId/settings/notifications",
15
+ preHandler: app.requireOrgSession,
16
+ schema: {
17
+ operationId: "getMemberCommunityNotificationSettings",
18
+ tags,
19
+ summary: "Read how you are told about each notification category in a community",
20
+ description: "For anyone who can open the community, staff included. One entry per category of the list in use, each with the channels it offers on this deployment, whether each is on for you and why (`locked`, `space`, `community`, `spaceDefault` or `default`), and `default`, what the channel would be without your own setting at that level. A per-space category also lists every space you can open, with its name. `digest` is the digest schedule of the community, null when it sends none. 404 when you cannot open the community.",
21
+ params: CommunityParam,
22
+ response: {
23
+ 200: CommunityNotificationSettings,
24
+ ...refusals(403, 404)
25
+ }
26
+ },
27
+ handler: async (req) => {
28
+ const actor = await actorOf(container, req.orgId, req.userId);
29
+ const view = await container.reporting.member.communityNotificationSettings(req.orgId, actor, req.userId, req.params.communityId);
30
+ if (!view) throw new NotFoundError("Community", req.params.communityId);
31
+ return view;
32
+ }
33
+ });
34
+ r.route({
35
+ method: "PATCH",
36
+ url: "/api/member/communities/:communityId/settings/notifications",
37
+ preHandler: app.requireOrgSession,
38
+ schema: {
39
+ operationId: "setMemberCommunityNotificationSettings",
40
+ tags,
41
+ summary: "Switch notification categories on or off per channel in a community",
42
+ description: "For anyone who can open the community, staff included. Each setting switches one category on one channel, for all spaces or, for a per-space category, for one space; `enabled: null` removes your choice so the default applies again. Settings left out keep their values. 404 when you cannot open the community; 422 when a category does not exist, does not offer the channel, the channel is locked, or `spaceId` is given for a category that is not per space.",
43
+ params: CommunityParam,
44
+ body: PatchNotificationSettings,
45
+ response: {
46
+ 204: z.void(),
47
+ ...refusals(400, 403, 404, 422)
48
+ }
49
+ },
50
+ handler: async (req, reply) => {
51
+ const actor = await actorOf(container, req.orgId, req.userId);
52
+ await container.notificationSettings.set(req.orgId, {
53
+ id: actor.id,
54
+ role: actor.role,
55
+ userId: req.userId
56
+ }, { communityId: req.params.communityId }, req.body.settings);
57
+ return reply.code(204).send();
58
+ }
59
+ });
60
+ r.route({
61
+ method: "GET",
62
+ url: "/api/member/settings/notifications",
63
+ preHandler: app.requireOrgSession,
64
+ schema: {
65
+ operationId: "getMemberNotificationSettings",
66
+ tags,
67
+ summary: "Read how you are told about notifications outside any community",
68
+ description: "For anyone signed in to the organization. The organization's categories, such as access and course content, each with the channels it offers, whether each is on for you, and `default`, what it would be without your own setting.",
69
+ response: {
70
+ 200: OrgNotificationSettings,
71
+ ...refusals(403)
72
+ }
73
+ },
74
+ handler: async (req) => {
75
+ await actorOf(container, req.orgId, req.userId);
76
+ return container.reporting.member.orgNotificationSettings(req.orgId, req.userId);
77
+ }
78
+ });
79
+ r.route({
80
+ method: "PATCH",
81
+ url: "/api/member/settings/notifications",
82
+ preHandler: app.requireOrgSession,
83
+ schema: {
84
+ operationId: "setMemberNotificationSettings",
85
+ tags,
86
+ summary: "Switch notification categories outside any community on or off per channel",
87
+ description: "For anyone signed in to the organization. Works like the community settings, against the organization's categories; `enabled: null` removes your choice. 422 when a category does not exist, does not offer the channel, the channel is locked, or `spaceId` is given.",
88
+ body: PatchNotificationSettings,
89
+ response: {
90
+ 204: z.void(),
91
+ ...refusals(400, 403, 422)
92
+ }
93
+ },
94
+ handler: async (req, reply) => {
95
+ const actor = await actorOf(container, req.orgId, req.userId);
96
+ await container.notificationSettings.set(req.orgId, {
97
+ id: actor.id,
98
+ role: actor.role,
99
+ userId: req.userId
100
+ }, "org", req.body.settings);
101
+ return reply.code(204).send();
102
+ }
103
+ });
104
+ }
105
+ //#endregion
106
+ export { memberNotificationSettingsRoutes };
107
+
108
+ //# sourceMappingURL=notification-settings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notification-settings.js","names":[],"sources":["../../../../src/http/routes/member/notification-settings.ts"],"sourcesContent":["import type { FastifyInstance } from 'fastify';\nimport type { ZodTypeProvider } from 'fastify-type-provider-zod';\nimport { z } from 'zod';\nimport type { Container } from '../../../app/container.js';\nimport { NotFoundError } from '@headless-lms/core/shared/errors';\nimport { actorOf } from './actor.js';\nimport {\n CommunityParam,\n CommunityNotificationSettings,\n OrgNotificationSettings,\n PatchNotificationSettings,\n refusals,\n} from '../../schemas/index.js';\n\nexport async function memberNotificationSettingsRoutes(\n app: FastifyInstance,\n container: Container,\n): Promise<void> {\n const r = app.withTypeProvider<ZodTypeProvider>();\n const tags = ['Notifications'];\n\n r.route({\n method: 'GET',\n url: '/api/member/communities/:communityId/settings/notifications',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'getMemberCommunityNotificationSettings',\n tags,\n summary: 'Read how you are told about each notification category in a community',\n description:\n 'For anyone who can open the community, staff included. One entry per category of the list in use, each with the channels it offers on this deployment, whether each is on for you and why (`locked`, `space`, `community`, `spaceDefault` or `default`), and `default`, what the channel would be without your own setting at that level. A per-space category also lists every space you can open, with its name. `digest` is the digest schedule of the community, null when it sends none. 404 when you cannot open the community.',\n params: CommunityParam,\n response: { 200: CommunityNotificationSettings, ...refusals(403, 404) },\n },\n handler: async (req) => {\n const actor = await actorOf(container, req.orgId, req.userId);\n const view = await container.reporting.member.communityNotificationSettings(\n req.orgId,\n actor,\n req.userId,\n req.params.communityId,\n );\n if (!view) {\n throw new NotFoundError('Community', req.params.communityId);\n }\n return view;\n },\n });\n\n r.route({\n method: 'PATCH',\n url: '/api/member/communities/:communityId/settings/notifications',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'setMemberCommunityNotificationSettings',\n tags,\n summary: 'Switch notification categories on or off per channel in a community',\n description:\n 'For anyone who can open the community, staff included. Each setting switches one category on one channel, for all spaces or, for a per-space category, for one space; `enabled: null` removes your choice so the default applies again. Settings left out keep their values. 404 when you cannot open the community; 422 when a category does not exist, does not offer the channel, the channel is locked, or `spaceId` is given for a category that is not per space.',\n params: CommunityParam,\n body: PatchNotificationSettings,\n response: { 204: z.void(), ...refusals(400, 403, 404, 422) },\n },\n handler: async (req, reply) => {\n const actor = await actorOf(container, req.orgId, req.userId);\n await container.notificationSettings.set(\n req.orgId,\n { id: actor.id, role: actor.role, userId: req.userId },\n { communityId: req.params.communityId },\n req.body.settings,\n );\n return reply.code(204).send();\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/member/settings/notifications',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'getMemberNotificationSettings',\n tags,\n summary: 'Read how you are told about notifications outside any community',\n description:\n \"For anyone signed in to the organization. The organization's categories, such as access and course content, each with the channels it offers, whether each is on for you, and `default`, what it would be without your own setting.\",\n response: { 200: OrgNotificationSettings, ...refusals(403) },\n },\n handler: async (req) => {\n await actorOf(container, req.orgId, req.userId);\n return container.reporting.member.orgNotificationSettings(req.orgId, req.userId);\n },\n });\n\n r.route({\n method: 'PATCH',\n url: '/api/member/settings/notifications',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'setMemberNotificationSettings',\n tags,\n summary: 'Switch notification categories outside any community on or off per channel',\n description:\n \"For anyone signed in to the organization. Works like the community settings, against the organization's categories; `enabled: null` removes your choice. 422 when a category does not exist, does not offer the channel, the channel is locked, or `spaceId` is given.\",\n body: PatchNotificationSettings,\n response: { 204: z.void(), ...refusals(400, 403, 422) },\n },\n handler: async (req, reply) => {\n const actor = await actorOf(container, req.orgId, req.userId);\n await container.notificationSettings.set(\n req.orgId,\n { id: actor.id, role: actor.role, userId: req.userId },\n 'org',\n req.body.settings,\n );\n return reply.code(204).send();\n },\n });\n}\n"],"mappings":";;;;;;;;AAcA,eAAsB,iCACpB,KACA,WACe;CACf,MAAM,IAAI,IAAI,iBAAkC;CAChD,MAAM,OAAO,CAAC,eAAe;CAE7B,EAAE,MAAM;EACN,QAAQ;EACR,KAAK;EACL,YAAY,IAAI;EAChB,QAAQ;GACN,aAAa;GACb;GACA,SAAS;GACT,aACE;GACF,QAAQ;GACR,UAAU;IAAE,KAAK;IAA+B,GAAG,SAAS,KAAK,GAAG;GAAE;EACxE;EACA,SAAS,OAAO,QAAQ;GACtB,MAAM,QAAQ,MAAM,QAAQ,WAAW,IAAI,OAAO,IAAI,MAAM;GAC5D,MAAM,OAAO,MAAM,UAAU,UAAU,OAAO,8BAC5C,IAAI,OACJ,OACA,IAAI,QACJ,IAAI,OAAO,WACb;GACA,IAAI,CAAC,MACH,MAAM,IAAI,cAAc,aAAa,IAAI,OAAO,WAAW;GAE7D,OAAO;EACT;CACF,CAAC;CAED,EAAE,MAAM;EACN,QAAQ;EACR,KAAK;EACL,YAAY,IAAI;EAChB,QAAQ;GACN,aAAa;GACb;GACA,SAAS;GACT,aACE;GACF,QAAQ;GACR,MAAM;GACN,UAAU;IAAE,KAAK,EAAE,KAAK;IAAG,GAAG,SAAS,KAAK,KAAK,KAAK,GAAG;GAAE;EAC7D;EACA,SAAS,OAAO,KAAK,UAAU;GAC7B,MAAM,QAAQ,MAAM,QAAQ,WAAW,IAAI,OAAO,IAAI,MAAM;GAC5D,MAAM,UAAU,qBAAqB,IACnC,IAAI,OACJ;IAAE,IAAI,MAAM;IAAI,MAAM,MAAM;IAAM,QAAQ,IAAI;GAAO,GACrD,EAAE,aAAa,IAAI,OAAO,YAAY,GACtC,IAAI,KAAK,QACX;GACA,OAAO,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK;EAC9B;CACF,CAAC;CAED,EAAE,MAAM;EACN,QAAQ;EACR,KAAK;EACL,YAAY,IAAI;EAChB,QAAQ;GACN,aAAa;GACb;GACA,SAAS;GACT,aACE;GACF,UAAU;IAAE,KAAK;IAAyB,GAAG,SAAS,GAAG;GAAE;EAC7D;EACA,SAAS,OAAO,QAAQ;GACtB,MAAM,QAAQ,WAAW,IAAI,OAAO,IAAI,MAAM;GAC9C,OAAO,UAAU,UAAU,OAAO,wBAAwB,IAAI,OAAO,IAAI,MAAM;EACjF;CACF,CAAC;CAED,EAAE,MAAM;EACN,QAAQ;EACR,KAAK;EACL,YAAY,IAAI;EAChB,QAAQ;GACN,aAAa;GACb;GACA,SAAS;GACT,aACE;GACF,MAAM;GACN,UAAU;IAAE,KAAK,EAAE,KAAK;IAAG,GAAG,SAAS,KAAK,KAAK,GAAG;GAAE;EACxD;EACA,SAAS,OAAO,KAAK,UAAU;GAC7B,MAAM,QAAQ,MAAM,QAAQ,WAAW,IAAI,OAAO,IAAI,MAAM;GAC5D,MAAM,UAAU,qBAAqB,IACnC,IAAI,OACJ;IAAE,IAAI,MAAM;IAAI,MAAM,MAAM;IAAM,QAAQ,IAAI;GAAO,GACrD,OACA,IAAI,KAAK,QACX;GACA,OAAO,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK;EAC9B;CACF,CAAC;AACH"}
@@ -0,0 +1,7 @@
1
+ import { Container } from "../../../app/container.js";
2
+ import { FastifyInstance } from "fastify";
3
+ //#region src/http/routes/member/notifications.d.ts
4
+ declare function memberNotificationRoutes(app: FastifyInstance, container: Container): Promise<void>;
5
+ //#endregion
6
+ export { memberNotificationRoutes };
7
+ //# sourceMappingURL=notifications.d.ts.map
@@ -0,0 +1,125 @@
1
+ import { refusals } from "../../schemas/shared.js";
2
+ import { NotificationFilterQuery, NotificationIdParam, NotificationListQuery, NotificationUnreadCounts, NotificationView, NotificationsPage } from "../../schemas/notifications.js";
3
+ import "../../schemas/index.js";
4
+ import { actorOf } from "./actor.js";
5
+ import { z } from "zod";
6
+ import { InvalidInputError, NotFoundError } from "@headless-lms/core/shared/errors";
7
+ //#region src/http/routes/member/notifications.ts
8
+ async function memberNotificationRoutes(app, container) {
9
+ const r = app.withTypeProvider();
10
+ const tags = ["Notifications"];
11
+ r.route({
12
+ method: "GET",
13
+ url: "/api/member/notifications",
14
+ preHandler: app.requireOrgSession,
15
+ schema: {
16
+ operationId: "listMemberNotifications",
17
+ tags,
18
+ summary: "List your notifications, newest first, with the unread count of the same filters",
19
+ description: "For any member of the organization. Filters combine with AND; the values of one filter combine with OR, and `includeOrg=true` with `communityId` adds the org list, so one query reads a community with the course and account notifications. 400 for `includeOrg` without `communityId` or with `scope`. Each notification carries the current titles, names and excerpts of what it refers to; a subject or parent you can no longer open, or that was deleted, is null. `display` is the text stored when it was sent.",
20
+ querystring: NotificationListQuery,
21
+ response: {
22
+ 200: NotificationsPage,
23
+ ...refusals(400, 403)
24
+ }
25
+ },
26
+ handler: async (req) => {
27
+ const actor = await actorOf(container, req.orgId, req.userId);
28
+ const { page, pageSize, ...filter } = req.query;
29
+ return container.reporting.member.notifications(req.orgId, actor, req.userId, filter, {
30
+ page,
31
+ pageSize
32
+ });
33
+ }
34
+ });
35
+ r.route({
36
+ method: "GET",
37
+ url: "/api/member/notifications/unread-counts",
38
+ preHandler: app.requireOrgSession,
39
+ schema: {
40
+ operationId: "getMemberNotificationUnreadCounts",
41
+ tags,
42
+ summary: "Count your unread notifications in the org list and in each community",
43
+ description: "For any member of the organization. `org` counts the org list; `communities` has one entry per community you can open, staff included.",
44
+ response: {
45
+ 200: NotificationUnreadCounts,
46
+ ...refusals(403)
47
+ }
48
+ },
49
+ handler: async (req) => {
50
+ const actor = await actorOf(container, req.orgId, req.userId);
51
+ return container.reporting.member.notificationUnreadCounts(req.orgId, actor, req.userId);
52
+ }
53
+ });
54
+ r.route({
55
+ method: "GET",
56
+ url: "/api/member/notifications/:notificationId",
57
+ preHandler: app.requireOrgSession,
58
+ schema: {
59
+ operationId: "getMemberNotification",
60
+ tags,
61
+ summary: "Read one of your notifications",
62
+ description: "For any member of the organization. The notification as the list shows it, with current titles, names and excerpts; reading it does not mark it read. 404 when it is not yours or was not shown in the app.",
63
+ params: NotificationIdParam,
64
+ response: {
65
+ 200: NotificationView,
66
+ ...refusals(403, 404)
67
+ }
68
+ },
69
+ handler: async (req) => {
70
+ const actor = await actorOf(container, req.orgId, req.userId);
71
+ const view = await container.reporting.member.notification(req.orgId, actor, req.userId, req.params.notificationId);
72
+ if (!view) throw new NotFoundError("Notification", req.params.notificationId);
73
+ return view;
74
+ }
75
+ });
76
+ r.route({
77
+ method: "POST",
78
+ url: "/api/member/notifications/:notificationId/read",
79
+ preHandler: app.requireOrgSession,
80
+ schema: {
81
+ operationId: "markMemberNotificationRead",
82
+ tags,
83
+ summary: "Mark one of your notifications read",
84
+ description: "For any member of the organization. Marking a read notification keeps its first read time. 404 when it is not yours.",
85
+ params: NotificationIdParam,
86
+ response: {
87
+ 204: z.void(),
88
+ ...refusals(403, 404)
89
+ }
90
+ },
91
+ handler: async (req, reply) => {
92
+ await actorOf(container, req.orgId, req.userId);
93
+ await container.notificationList.markRead(req.orgId, req.userId, req.params.notificationId);
94
+ return reply.code(204).send();
95
+ }
96
+ });
97
+ r.route({
98
+ method: "POST",
99
+ url: "/api/member/notifications/read",
100
+ preHandler: app.requireOrgSession,
101
+ schema: {
102
+ operationId: "markAllMemberNotificationsRead",
103
+ tags,
104
+ summary: "Mark your notifications matching the filters read",
105
+ description: "For any member of the organization. Takes the filters of the list in the query string, `includeOrg` included; with none, marks every notification read. 400 when the request has a body.",
106
+ querystring: NotificationFilterQuery,
107
+ response: {
108
+ 204: z.void(),
109
+ ...refusals(400, 403)
110
+ }
111
+ },
112
+ preValidation: async (req) => {
113
+ if (req.body !== void 0) throw new InvalidInputError("Mark-read takes its filters in the query string, not in a body");
114
+ },
115
+ handler: async (req, reply) => {
116
+ await actorOf(container, req.orgId, req.userId);
117
+ await container.notificationList.markAllRead(req.orgId, req.userId, req.query);
118
+ return reply.code(204).send();
119
+ }
120
+ });
121
+ }
122
+ //#endregion
123
+ export { memberNotificationRoutes };
124
+
125
+ //# sourceMappingURL=notifications.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notifications.js","names":[],"sources":["../../../../src/http/routes/member/notifications.ts"],"sourcesContent":["import type { FastifyInstance } from 'fastify';\nimport type { ZodTypeProvider } from 'fastify-type-provider-zod';\nimport { z } from 'zod';\nimport type { Container } from '../../../app/container.js';\nimport { InvalidInputError, NotFoundError } from '@headless-lms/core/shared/errors';\nimport { actorOf } from './actor.js';\nimport {\n NotificationFilterQuery,\n NotificationIdParam,\n NotificationListQuery,\n NotificationUnreadCounts,\n NotificationView,\n NotificationsPage,\n refusals,\n} from '../../schemas/index.js';\n\nexport async function memberNotificationRoutes(\n app: FastifyInstance,\n container: Container,\n): Promise<void> {\n const r = app.withTypeProvider<ZodTypeProvider>();\n const tags = ['Notifications'];\n\n r.route({\n method: 'GET',\n url: '/api/member/notifications',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'listMemberNotifications',\n tags,\n summary: 'List your notifications, newest first, with the unread count of the same filters',\n description:\n 'For any member of the organization. Filters combine with AND; the values of one filter combine with OR, and `includeOrg=true` with `communityId` adds the org list, so one query reads a community with the course and account notifications. 400 for `includeOrg` without `communityId` or with `scope`. Each notification carries the current titles, names and excerpts of what it refers to; a subject or parent you can no longer open, or that was deleted, is null. `display` is the text stored when it was sent.',\n querystring: NotificationListQuery,\n response: { 200: NotificationsPage, ...refusals(400, 403) },\n },\n handler: async (req) => {\n const actor = await actorOf(container, req.orgId, req.userId);\n const { page, pageSize, ...filter } = req.query;\n return container.reporting.member.notifications(req.orgId, actor, req.userId, filter, {\n page,\n pageSize,\n });\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/member/notifications/unread-counts',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'getMemberNotificationUnreadCounts',\n tags,\n summary: 'Count your unread notifications in the org list and in each community',\n description:\n 'For any member of the organization. `org` counts the org list; `communities` has one entry per community you can open, staff included.',\n response: { 200: NotificationUnreadCounts, ...refusals(403) },\n },\n handler: async (req) => {\n const actor = await actorOf(container, req.orgId, req.userId);\n return container.reporting.member.notificationUnreadCounts(req.orgId, actor, req.userId);\n },\n });\n\n r.route({\n method: 'GET',\n url: '/api/member/notifications/:notificationId',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'getMemberNotification',\n tags,\n summary: 'Read one of your notifications',\n description:\n 'For any member of the organization. The notification as the list shows it, with current titles, names and excerpts; reading it does not mark it read. 404 when it is not yours or was not shown in the app.',\n params: NotificationIdParam,\n response: { 200: NotificationView, ...refusals(403, 404) },\n },\n handler: async (req) => {\n const actor = await actorOf(container, req.orgId, req.userId);\n const view = await container.reporting.member.notification(\n req.orgId,\n actor,\n req.userId,\n req.params.notificationId,\n );\n if (!view) {\n throw new NotFoundError('Notification', req.params.notificationId);\n }\n return view;\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/member/notifications/:notificationId/read',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'markMemberNotificationRead',\n tags,\n summary: 'Mark one of your notifications read',\n description:\n 'For any member of the organization. Marking a read notification keeps its first read time. 404 when it is not yours.',\n params: NotificationIdParam,\n response: { 204: z.void(), ...refusals(403, 404) },\n },\n handler: async (req, reply) => {\n await actorOf(container, req.orgId, req.userId);\n await container.notificationList.markRead(req.orgId, req.userId, req.params.notificationId);\n return reply.code(204).send();\n },\n });\n\n r.route({\n method: 'POST',\n url: '/api/member/notifications/read',\n preHandler: app.requireOrgSession,\n schema: {\n operationId: 'markAllMemberNotificationsRead',\n tags,\n summary: 'Mark your notifications matching the filters read',\n description:\n 'For any member of the organization. Takes the filters of the list in the query string, `includeOrg` included; with none, marks every notification read. 400 when the request has a body.',\n querystring: NotificationFilterQuery,\n response: { 204: z.void(), ...refusals(400, 403) },\n },\n preValidation: async (req) => {\n if (req.body !== undefined) {\n throw new InvalidInputError('Mark-read takes its filters in the query string, not in a body');\n }\n },\n handler: async (req, reply) => {\n await actorOf(container, req.orgId, req.userId);\n await container.notificationList.markAllRead(req.orgId, req.userId, req.query);\n return reply.code(204).send();\n },\n });\n}\n"],"mappings":";;;;;;;AAgBA,eAAsB,yBACpB,KACA,WACe;CACf,MAAM,IAAI,IAAI,iBAAkC;CAChD,MAAM,OAAO,CAAC,eAAe;CAE7B,EAAE,MAAM;EACN,QAAQ;EACR,KAAK;EACL,YAAY,IAAI;EAChB,QAAQ;GACN,aAAa;GACb;GACA,SAAS;GACT,aACE;GACF,aAAa;GACb,UAAU;IAAE,KAAK;IAAmB,GAAG,SAAS,KAAK,GAAG;GAAE;EAC5D;EACA,SAAS,OAAO,QAAQ;GACtB,MAAM,QAAQ,MAAM,QAAQ,WAAW,IAAI,OAAO,IAAI,MAAM;GAC5D,MAAM,EAAE,MAAM,UAAU,GAAG,WAAW,IAAI;GAC1C,OAAO,UAAU,UAAU,OAAO,cAAc,IAAI,OAAO,OAAO,IAAI,QAAQ,QAAQ;IACpF;IACA;GACF,CAAC;EACH;CACF,CAAC;CAED,EAAE,MAAM;EACN,QAAQ;EACR,KAAK;EACL,YAAY,IAAI;EAChB,QAAQ;GACN,aAAa;GACb;GACA,SAAS;GACT,aACE;GACF,UAAU;IAAE,KAAK;IAA0B,GAAG,SAAS,GAAG;GAAE;EAC9D;EACA,SAAS,OAAO,QAAQ;GACtB,MAAM,QAAQ,MAAM,QAAQ,WAAW,IAAI,OAAO,IAAI,MAAM;GAC5D,OAAO,UAAU,UAAU,OAAO,yBAAyB,IAAI,OAAO,OAAO,IAAI,MAAM;EACzF;CACF,CAAC;CAED,EAAE,MAAM;EACN,QAAQ;EACR,KAAK;EACL,YAAY,IAAI;EAChB,QAAQ;GACN,aAAa;GACb;GACA,SAAS;GACT,aACE;GACF,QAAQ;GACR,UAAU;IAAE,KAAK;IAAkB,GAAG,SAAS,KAAK,GAAG;GAAE;EAC3D;EACA,SAAS,OAAO,QAAQ;GACtB,MAAM,QAAQ,MAAM,QAAQ,WAAW,IAAI,OAAO,IAAI,MAAM;GAC5D,MAAM,OAAO,MAAM,UAAU,UAAU,OAAO,aAC5C,IAAI,OACJ,OACA,IAAI,QACJ,IAAI,OAAO,cACb;GACA,IAAI,CAAC,MACH,MAAM,IAAI,cAAc,gBAAgB,IAAI,OAAO,cAAc;GAEnE,OAAO;EACT;CACF,CAAC;CAED,EAAE,MAAM;EACN,QAAQ;EACR,KAAK;EACL,YAAY,IAAI;EAChB,QAAQ;GACN,aAAa;GACb;GACA,SAAS;GACT,aACE;GACF,QAAQ;GACR,UAAU;IAAE,KAAK,EAAE,KAAK;IAAG,GAAG,SAAS,KAAK,GAAG;GAAE;EACnD;EACA,SAAS,OAAO,KAAK,UAAU;GAC7B,MAAM,QAAQ,WAAW,IAAI,OAAO,IAAI,MAAM;GAC9C,MAAM,UAAU,iBAAiB,SAAS,IAAI,OAAO,IAAI,QAAQ,IAAI,OAAO,cAAc;GAC1F,OAAO,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK;EAC9B;CACF,CAAC;CAED,EAAE,MAAM;EACN,QAAQ;EACR,KAAK;EACL,YAAY,IAAI;EAChB,QAAQ;GACN,aAAa;GACb;GACA,SAAS;GACT,aACE;GACF,aAAa;GACb,UAAU;IAAE,KAAK,EAAE,KAAK;IAAG,GAAG,SAAS,KAAK,GAAG;GAAE;EACnD;EACA,eAAe,OAAO,QAAQ;GAC5B,IAAI,IAAI,SAAS,KAAA,GACf,MAAM,IAAI,kBAAkB,gEAAgE;EAEhG;EACA,SAAS,OAAO,KAAK,UAAU;GAC7B,MAAM,QAAQ,WAAW,IAAI,OAAO,IAAI,MAAM;GAC9C,MAAM,UAAU,iBAAiB,YAAY,IAAI,OAAO,IAAI,QAAQ,IAAI,KAAK;GAC7E,OAAO,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK;EAC9B;CACF,CAAC;AACH"}
@@ -0,0 +1,7 @@
1
+ import { Container } from "../../../app/container.js";
2
+ import { FastifyInstance } from "fastify";
3
+ //#region src/http/routes/member/org.d.ts
4
+ declare function memberOrgRoutes(app: FastifyInstance, container: Container): Promise<void>;
5
+ //#endregion
6
+ export { memberOrgRoutes };
7
+ //# sourceMappingURL=org.d.ts.map