@vex-chat/spire 0.8.0 → 1.0.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 (370) hide show
  1. package/README.md +82 -26
  2. package/dist/ClientManager.d.ts +23 -25
  3. package/dist/ClientManager.js +230 -249
  4. package/dist/ClientManager.js.map +1 -0
  5. package/dist/Database.d.ts +49 -47
  6. package/dist/Database.js +698 -773
  7. package/dist/Database.js.map +1 -0
  8. package/dist/Spire.d.ts +22 -14
  9. package/dist/Spire.js +496 -236
  10. package/dist/Spire.js.map +1 -0
  11. package/dist/__tests__/Database.spec.js +116 -75
  12. package/dist/__tests__/Database.spec.js.map +1 -0
  13. package/dist/db/schema.d.ts +134 -0
  14. package/dist/db/schema.js +2 -0
  15. package/dist/db/schema.js.map +1 -0
  16. package/dist/index.d.ts +1 -1
  17. package/dist/index.js +3 -5
  18. package/dist/index.js.map +1 -0
  19. package/dist/middleware/validate.d.ts +12 -0
  20. package/dist/middleware/validate.js +35 -0
  21. package/dist/middleware/validate.js.map +1 -0
  22. package/dist/migrations/2026-04-06_initial-schema.d.ts +3 -0
  23. package/dist/migrations/2026-04-06_initial-schema.js +192 -0
  24. package/dist/migrations/2026-04-06_initial-schema.js.map +1 -0
  25. package/dist/run.js +26 -21
  26. package/dist/run.js.map +1 -0
  27. package/dist/server/avatar.d.ts +3 -4
  28. package/dist/server/avatar.js +64 -64
  29. package/dist/server/avatar.js.map +1 -0
  30. package/dist/server/errors.d.ts +59 -0
  31. package/dist/server/errors.js +94 -0
  32. package/dist/server/errors.js.map +1 -0
  33. package/dist/server/file.d.ts +3 -4
  34. package/dist/server/file.js +81 -62
  35. package/dist/server/file.js.map +1 -0
  36. package/dist/server/index.d.ts +8 -10
  37. package/dist/server/index.js +414 -405
  38. package/dist/server/index.js.map +1 -0
  39. package/dist/server/invite.d.ts +4 -5
  40. package/dist/server/invite.js +18 -52
  41. package/dist/server/invite.js.map +1 -0
  42. package/dist/server/openapi.d.ts +2 -0
  43. package/dist/server/openapi.js +40 -0
  44. package/dist/server/openapi.js.map +1 -0
  45. package/dist/server/permissions.d.ts +16 -0
  46. package/dist/server/permissions.js +22 -0
  47. package/dist/server/permissions.js.map +1 -0
  48. package/dist/server/rateLimit.d.ts +28 -0
  49. package/dist/server/rateLimit.js +58 -0
  50. package/dist/server/rateLimit.js.map +1 -0
  51. package/dist/server/user.d.ts +4 -7
  52. package/dist/server/user.js +55 -66
  53. package/dist/server/user.js.map +1 -0
  54. package/dist/server/utils.d.ts +35 -7
  55. package/dist/server/utils.js +50 -6
  56. package/dist/server/utils.js.map +1 -0
  57. package/dist/types/express.d.ts +20 -0
  58. package/dist/types/express.js +2 -0
  59. package/dist/types/express.js.map +1 -0
  60. package/dist/utils/createLogger.js +13 -19
  61. package/dist/utils/createLogger.js.map +1 -0
  62. package/dist/utils/createUint8UUID.js +6 -10
  63. package/dist/utils/createUint8UUID.js.map +1 -0
  64. package/dist/utils/jwtSecret.d.ts +7 -0
  65. package/dist/utils/jwtSecret.js +15 -0
  66. package/dist/utils/jwtSecret.js.map +1 -0
  67. package/dist/utils/loadEnv.js +7 -22
  68. package/dist/utils/loadEnv.js.map +1 -0
  69. package/dist/utils/msgpack.d.ts +2 -0
  70. package/dist/utils/msgpack.js +4 -0
  71. package/dist/utils/msgpack.js.map +1 -0
  72. package/package.json +91 -65
  73. package/src/ClientManager.ts +434 -0
  74. package/src/Database.ts +925 -0
  75. package/src/Spire.ts +878 -0
  76. package/src/__tests__/Database.spec.ts +167 -0
  77. package/src/ambient-modules.d.ts +1 -0
  78. package/src/db/schema.ts +165 -0
  79. package/src/index.ts +3 -0
  80. package/src/middleware/validate.ts +38 -0
  81. package/src/migrations/2026-04-06_initial-schema.ts +218 -0
  82. package/src/run.ts +37 -0
  83. package/src/server/avatar.ts +141 -0
  84. package/src/server/errors.ts +133 -0
  85. package/src/server/file.ts +172 -0
  86. package/src/server/index.ts +855 -0
  87. package/src/server/invite.ts +65 -0
  88. package/src/server/openapi.ts +51 -0
  89. package/src/server/permissions.ts +40 -0
  90. package/src/server/rateLimit.ts +86 -0
  91. package/src/server/user.ts +125 -0
  92. package/src/server/utils.ts +59 -0
  93. package/src/types/express.ts +23 -0
  94. package/src/utils/createLogger.ts +47 -0
  95. package/src/utils/createUint8UUID.ts +9 -0
  96. package/src/utils/jwtSecret.ts +16 -0
  97. package/src/utils/loadEnv.ts +15 -0
  98. package/src/utils/msgpack.ts +4 -0
  99. package/avatars/052242d0-4129-4a6e-8076-22709c157549 +0 -0
  100. package/avatars/0a677c9a-4986-4b2c-ae2e-12faf22f55db +0 -0
  101. package/avatars/0ba21b91-decb-4a3e-ac86-4dd54d805a9a +0 -0
  102. package/avatars/0c48d8b6-1d1b-4297-a6c6-2fe50af6fc35 +0 -0
  103. package/avatars/0d993cdf-19a6-4299-a4ee-a06579d106cf +0 -0
  104. package/avatars/17b000e4-ac38-46ec-9dec-d2568086129a +0 -0
  105. package/avatars/19dc5594-0f06-4ac6-af18-8740dd39ef6b +0 -0
  106. package/avatars/20444fa3-6d5e-429e-b55f-b81c3d2c61ee +0 -0
  107. package/avatars/21c0512a-5630-4931-9442-d66db66737be +0 -0
  108. package/avatars/22830a60-0b6f-4912-83a5-72245465f332 +0 -0
  109. package/avatars/243639ce-f59f-4404-a1f1-4ec0eb5d2af3 +0 -0
  110. package/avatars/30d2c01d-7b7f-4ea9-9859-1c90837a23f7 +0 -0
  111. package/avatars/315a04f0-9a6f-4b0f-bb9f-5fa774c4752b +0 -0
  112. package/avatars/3563d333-53fe-4885-ac2d-9a4f761db85e +0 -0
  113. package/avatars/36a10c00-3b4c-437f-8e1f-4428ecde0003 +0 -0
  114. package/avatars/40b83eeb-c6e8-4268-82ab-69799a796405 +0 -0
  115. package/avatars/45b5ddb9-ad2c-4404-8ab3-cb4699e6d61c +0 -0
  116. package/avatars/4e4f0ffb-9a75-479a-bccb-446d0bf85020 +0 -0
  117. package/avatars/4e62c3bd-08c6-4fdd-bd65-f01c7322ed64 +0 -0
  118. package/avatars/5004d2e7-51af-44af-8776-6c71f7019843 +0 -0
  119. package/avatars/5041eb29-5c4b-4dea-8c1b-31ba4473161a +0 -0
  120. package/avatars/5065cf78-31c5-46cb-8d5c-c0b6be2d994e +0 -0
  121. package/avatars/51b91d2c-8956-4d73-b4ad-ca6a8d9da9a8 +0 -0
  122. package/avatars/58264a2c-5651-4a42-8ca2-a9907b311e48 +0 -0
  123. package/avatars/58c2357c-8080-4725-a0ce-182c96b037c4 +0 -0
  124. package/avatars/59b5f6dd-8e04-4d15-b4dc-c1c652558a74 +0 -0
  125. package/avatars/5b417a78-b274-48bc-98a4-6e54b74ee62d +0 -0
  126. package/avatars/611f5a93-1ed4-45a1-bc8e-e8e413f9b171 +0 -0
  127. package/avatars/65abe919-9921-46f6-9bc9-183e9cc53c8a +0 -0
  128. package/avatars/6934202d-1546-4270-8a15-97ba8b8c6fa9 +0 -0
  129. package/avatars/6acd24be-f4b7-4399-9e7c-807821828d29 +0 -0
  130. package/avatars/6b2d6ac5-e35c-4297-994e-f0eb6fe56740 +0 -0
  131. package/avatars/6cae9ddd-f163-44e7-a632-30425716a159 +0 -0
  132. package/avatars/6d90e79e-b9c1-4b89-843b-96636de8d26b +0 -0
  133. package/avatars/6f82c7bb-a974-4372-8a64-ce287e668c8c +0 -0
  134. package/avatars/74a45091-5a76-4bb7-ae8a-cd7373adc128 +0 -0
  135. package/avatars/7d071ab2-e0b5-4dbb-8bf5-1fae50c3663f +0 -0
  136. package/avatars/7de818c8-bda1-4f51-976e-160fc087184b +0 -0
  137. package/avatars/873937df-8cb1-427a-92bf-f829f4259624 +0 -0
  138. package/avatars/8b45ffa5-3322-4109-bfa0-1be088336135 +0 -0
  139. package/avatars/8efaa426-22a9-42a2-b4ac-a275717b812f +0 -0
  140. package/avatars/903fd1a6-d6ea-431c-b98f-f21e424a2852 +0 -0
  141. package/avatars/943d8533-5174-4199-990f-1ec69e5d60c4 +0 -0
  142. package/avatars/952d014e-3804-4cd2-a4a0-ffe40a11e4ac +0 -0
  143. package/avatars/95d3acb0-724d-4413-b20f-edad55812d5d +0 -0
  144. package/avatars/9641a946-f613-471b-bedd-c1730b96b51e +0 -0
  145. package/avatars/9b01cbbf-f6b2-43fb-b569-589b6f2a8134 +0 -0
  146. package/avatars/9cd4424d-a34f-4467-acc0-93cf82703e0d +0 -0
  147. package/avatars/9d9ad3b0-e5a6-420a-a6c5-fb9085b70376 +0 -0
  148. package/avatars/9e9e34b5-4e63-4c4b-9722-c7f5674b47aa +0 -0
  149. package/avatars/a387d5c1-59eb-4a6b-80c1-a8982ed12c33 +0 -0
  150. package/avatars/a3e86d21-d881-4824-8ebf-45e3bf0f9186 +0 -0
  151. package/avatars/a8d5cc1c-3f42-4b7b-8d33-f9a9ef77f96b +0 -0
  152. package/avatars/a91d815f-badc-4604-a7be-6c7a44e6101d +0 -0
  153. package/avatars/aa8d0324-bcec-4737-a8c4-bdbff914148d +0 -0
  154. package/avatars/abb8a941-8b6b-47d7-a2f9-8b153ba44aa2 +0 -0
  155. package/avatars/b011bb38-1ef3-4d22-82fa-8bf60faf7b5d +0 -0
  156. package/avatars/b24bcbc1-11f0-473d-a8b9-ba8ce4ca127d +0 -0
  157. package/avatars/b2607346-af1c-4e98-b725-7650a766db2a +0 -0
  158. package/avatars/b6300f7c-cb37-459b-b1bf-8a0a0e797a52 +0 -0
  159. package/avatars/b7d3cff3-84dd-4547-93a1-de1aaa8aa34c +0 -0
  160. package/avatars/baa4b51c-e97f-4f51-bcb3-f27bc506cfaf +0 -0
  161. package/avatars/be7022e4-e292-4515-80d5-f9b61ebeb4ce +0 -0
  162. package/avatars/bed596a3-7569-4854-9e76-f52d33c0a541 +0 -0
  163. package/avatars/bf69992f-3f72-4930-99bb-0ffe17f3aebf +0 -0
  164. package/avatars/ca00c250-c6d4-464d-a6de-1c8467a18fe8 +0 -0
  165. package/avatars/ca19d78f-c0bc-4bd5-b26f-6923cb19996d +0 -0
  166. package/avatars/cda4d6e1-e0a4-4024-ac95-6de98e713b98 +0 -0
  167. package/avatars/cf72c30d-2da8-4e81-aa71-735b9e714274 +0 -0
  168. package/avatars/d5a35b78-99b3-4564-b6b9-b2ccab28c470 +0 -0
  169. package/avatars/dadb38c1-2a9d-47a3-8d92-b56b6166973c +0 -0
  170. package/avatars/e68705c7-375d-4423-9a86-29a16bd3ee0e +0 -0
  171. package/avatars/e9af3e4c-1f62-4302-8b99-b68ce93b7a86 +0 -0
  172. package/avatars/ea7e7331-e845-4189-8248-5f5b1d63f5e3 +0 -0
  173. package/avatars/ef4f8dcb-ef6c-4e7a-9be1-0476161bfce5 +0 -0
  174. package/avatars/ef7d0917-a206-4f88-8b60-93f8253774dc +0 -0
  175. package/avatars/f1a554d6-1db3-4ff5-b0dc-b607d6c3b4ff +0 -0
  176. package/avatars/f1fecc21-c81f-49a8-88f3-f942a0a679f6 +0 -0
  177. package/avatars/f30a2427-1755-4053-813e-129a179e1dd3 +0 -0
  178. package/avatars/f5370717-5109-46a5-a8d7-e1dd996d0615 +0 -0
  179. package/avatars/f6dd7126-1144-4998-bbd3-d4e0fbee2e95 +0 -0
  180. package/avatars/f83413d5-0003-4756-9ece-745fd61cc468 +0 -0
  181. package/avatars/f9b3149e-7ec8-4bb3-a9b9-dcbe66dac197 +0 -0
  182. package/avatars/fa41d70b-857e-4423-bd7d-26ddcddc13b9 +0 -0
  183. package/avatars/fb551ee8-99c7-400b-8e1d-322ce4619998 +0 -0
  184. package/avatars/fe83a6d4-abb0-4ab0-b61d-76a7cc08be84 +0 -0
  185. package/dist/migrations/20210103192527_users.d.ts +0 -3
  186. package/dist/migrations/20210103192527_users.js +0 -30
  187. package/dist/migrations/20210103193502_mail.d.ts +0 -3
  188. package/dist/migrations/20210103193502_mail.js +0 -35
  189. package/dist/migrations/20210103193525_preKeys.d.ts +0 -3
  190. package/dist/migrations/20210103193525_preKeys.js +0 -30
  191. package/dist/migrations/20210103193553_oneTimeKeys.d.ts +0 -3
  192. package/dist/migrations/20210103193553_oneTimeKeys.js +0 -30
  193. package/dist/migrations/20210103193615_servers.d.ts +0 -3
  194. package/dist/migrations/20210103193615_servers.js +0 -28
  195. package/dist/migrations/20210103193729_channels.d.ts +0 -3
  196. package/dist/migrations/20210103193729_channels.js +0 -28
  197. package/dist/migrations/20210103193749_permissions.d.ts +0 -3
  198. package/dist/migrations/20210103193749_permissions.js +0 -30
  199. package/dist/migrations/20210103193801_files.d.ts +0 -3
  200. package/dist/migrations/20210103193801_files.js +0 -28
  201. package/emoji/04d98632-2c86-421b-a407-17f14fe86f8f +0 -0
  202. package/emoji/1160ed6e-1163-4043-9808-4029e863ed30 +0 -0
  203. package/emoji/1547ab18-1635-4a80-a82d-ebbb767b9932 +0 -0
  204. package/emoji/16922521-f6cb-4de4-860c-27916b22c6ba +0 -0
  205. package/emoji/198a9432-0e41-4866-994a-448d4775afcb +0 -0
  206. package/emoji/1be886b3-c9c5-4593-b516-f357ed931f96 +0 -0
  207. package/emoji/1c2b3d1d-637f-4103-b066-4bc4511a3ad7 +0 -0
  208. package/emoji/1efd27e7-b15f-475c-8b32-9159d26b169d +0 -0
  209. package/emoji/270b9409-0ea5-4be2-a239-a8dce13f9c31 +0 -0
  210. package/emoji/27812f76-fee2-49dd-a217-363de6d159dc +0 -0
  211. package/emoji/297ec202-8c24-44c6-aead-689d6d461883 +0 -0
  212. package/emoji/2bf06d86-17cb-4f40-a5ef-bd75d239a1a3 +0 -0
  213. package/emoji/31a75163-1cce-4dc1-b0a2-ecad6a4c500b +0 -0
  214. package/emoji/35235635-fdbd-4273-8428-f3cb3e1e8fd3 +0 -0
  215. package/emoji/3690fff2-6824-4403-a6e3-16a6a54979a9 +0 -0
  216. package/emoji/391014c2-59e0-46a8-85ec-7a7fdaca1d2d +0 -0
  217. package/emoji/3b383dcb-6e76-4e85-8e16-7c68040c06c2 +0 -0
  218. package/emoji/42d617a7-b104-42f5-9618-473181f752cf +0 -0
  219. package/emoji/482495d3-cce9-4f88-bf2a-f6003f03a9b5 +0 -0
  220. package/emoji/48390e06-0efb-404c-89bd-5f2be241bd50 +0 -0
  221. package/emoji/4b808d8d-3248-4149-b919-71b108391bcf +0 -0
  222. package/emoji/4bc13544-d82a-4e32-bd17-a70592274314 +0 -0
  223. package/emoji/4fcebf70-8623-4343-8243-67c8547b2edd +0 -0
  224. package/emoji/509d09aa-1214-459c-8081-50918a17b9af +0 -0
  225. package/emoji/5272abd8-d4d7-4b90-acd2-bf30e6c27243 +0 -0
  226. package/emoji/53c272ce-48bd-4d7e-bfb8-a6482b88be54 +0 -0
  227. package/emoji/5b279e65-06f7-4b26-8b4c-d1b48fba728d +0 -0
  228. package/emoji/5bd141f9-4394-4108-9376-66ebbc2c2bc1 +0 -0
  229. package/emoji/5c769156-f9cb-40bd-ab89-4edeece613fd +0 -0
  230. package/emoji/5c85fba9-8ba7-4fc9-b1b2-48dc30d24a1b +0 -0
  231. package/emoji/61a5e565-d20b-40ba-a139-b0c73a6027f3 +0 -0
  232. package/emoji/6913f43d-dd45-456c-9641-a126104d9ae5 +0 -0
  233. package/emoji/6957e74e-9622-492d-a950-242db3752260 +0 -0
  234. package/emoji/6a14bab5-26af-4bfa-9c17-be7c2511976d +0 -0
  235. package/emoji/6be09439-509e-4095-a30a-b1c7c573895d +0 -0
  236. package/emoji/6cc435b1-fe53-433c-b5a9-2b2019053997 +0 -0
  237. package/emoji/74f1b2af-bc7e-4a0f-802a-64ded185d5e2 +0 -0
  238. package/emoji/7890ba09-f02f-428e-807d-006d03d51d4a +0 -0
  239. package/emoji/7dc69179-6b3c-4f40-b20b-0ff573deea2d +0 -0
  240. package/emoji/7dd1b6b1-439d-4279-916a-995408863172 +0 -0
  241. package/emoji/820498ad-a2c8-43a2-ab83-d26f9c2246d4 +0 -0
  242. package/emoji/8319469c-2787-44e5-91a6-c8c39810dd7c +0 -0
  243. package/emoji/86745d1d-9e59-4607-b2b0-46c741079be1 +0 -0
  244. package/emoji/887b3cff-ae9e-4b5f-ad00-3ca9fc72f689 +0 -0
  245. package/emoji/8c6cf621-71d6-4fca-abe6-e19f4dd7f883 +0 -0
  246. package/emoji/8ca6d32e-a1ef-4956-a416-d8d0d680f085 +0 -0
  247. package/emoji/8d979f5e-38a2-4dd1-b3e4-80938bbe499b +0 -0
  248. package/emoji/99f68ea0-e3fe-4f03-9cc3-5f7f5315404d +0 -0
  249. package/emoji/9ad3aedc-7f79-4d68-a144-82e5b5dc3033 +0 -0
  250. package/emoji/9e418c2f-1f0f-46c4-be39-3bda38a28545 +0 -0
  251. package/emoji/a1f616bf-7402-4e24-9111-18acaebabb48 +0 -0
  252. package/emoji/a25ed9c1-3f9c-4e5f-ade2-7b159fb9fbf4 +0 -0
  253. package/emoji/a5176bc2-39a8-467b-8c75-6fbbc81b59c7 +0 -0
  254. package/emoji/a584215c-6547-438b-8ae8-dd490b51890e +0 -0
  255. package/emoji/a739895f-cf61-4b7c-b350-8e8283aaf751 +0 -0
  256. package/emoji/aaa10dd2-02a2-499e-9e17-c83787436508 +0 -0
  257. package/emoji/ae90baf2-a0ef-4d4d-9cc4-94f8ddd60f45 +0 -0
  258. package/emoji/b0564c48-feae-431a-95f9-df597c6c124c +0 -0
  259. package/emoji/b218bb93-e69c-4793-a669-83316650c4e7 +0 -0
  260. package/emoji/b2998c27-85d5-4598-ab41-469aa8e0fcad +0 -0
  261. package/emoji/b3da08ba-4179-4b5d-826f-5fc15e1a3ad2 +0 -0
  262. package/emoji/b840eb6a-a917-4bb2-854b-8f1022e7904b +0 -0
  263. package/emoji/b84baa76-b4b0-4b83-bc83-78661cb4f1d4 +0 -0
  264. package/emoji/baf69d80-8b1d-4032-855f-605cf0d489c3 +0 -0
  265. package/emoji/bb4d372c-ccd0-4a47-b157-b6a3b9f763e2 +0 -0
  266. package/emoji/bdbd1627-c81d-42d9-b3f5-8979e2ab74dc +0 -0
  267. package/emoji/c257388f-8b85-450b-b168-ebdf8d8c3026 +0 -0
  268. package/emoji/c573fde1-faa9-4c1d-a172-e283645afcfd +0 -0
  269. package/emoji/c8e27810-e8ea-47ce-b7ec-cef0a6becb28 +0 -0
  270. package/emoji/cdeef182-b220-4850-9ecf-5d7c472fd754 +0 -0
  271. package/emoji/d59c1aa9-6f81-4c07-96dd-9953401ff211 +0 -0
  272. package/emoji/dd407dbf-a077-40ba-957f-337b3c5efdc7 +0 -0
  273. package/emoji/e01f6e06-5728-4e2c-90fb-314a5827b766 +0 -0
  274. package/emoji/e1f9ed12-a2ce-433d-b454-b833438a1f9c +0 -0
  275. package/emoji/e697e5c4-acd6-41cd-a43e-edee8da3ab7b +0 -0
  276. package/emoji/e8182220-3464-4e31-8c08-466baead7bfc +0 -0
  277. package/emoji/eb0f3fd5-abc9-4abf-b816-d8458aeb7ec8 +0 -0
  278. package/emoji/eb38ecf7-0d13-4c51-b96a-3777f79321c4 +0 -0
  279. package/emoji/ee515c85-b7ce-4493-a427-994cc0af0d59 +0 -0
  280. package/emoji/f485cef2-d3fa-4d59-88af-b79a3105cacf +0 -0
  281. package/emoji/ff0dab2a-7015-4e8c-b0d0-3569058359dc +0 -0
  282. package/files/01087968-07b6-4fdb-9aeb-fa9dc061be94 +0 -0
  283. package/files/030455b3-17cc-415f-b3b3-2bb56c92ee8b +0 -0
  284. package/files/06129f52-a858-4031-ad85-4d7f2bb793af +0 -0
  285. package/files/0a6155a9-069f-45e9-8a06-56992fe55187 +0 -0
  286. package/files/12b9dda5-feb1-4f20-a987-ad422db5ba73 +0 -0
  287. package/files/13c8fa1e-8821-4628-b607-9e4fa4510df7 +0 -0
  288. package/files/159b0ad3-1a30-419d-af22-28a1096ce825 +0 -0
  289. package/files/1699c563-6769-431b-8041-99a6a4386f25 +0 -0
  290. package/files/176b916c-0dd9-4b93-bfdc-b8ccabf15a96 +0 -0
  291. package/files/1a27dad9-8cc9-4a0c-9d4e-7bde63adda60 +0 -0
  292. package/files/1d29832c-059a-4190-bca6-b83ac77540d9 +0 -0
  293. package/files/1dcde013-8833-4369-8726-81236e4eb30e +0 -0
  294. package/files/2080fc9f-d2c4-4fbc-af84-232fe4900a4f +0 -0
  295. package/files/20889623-6869-46a2-999b-c07708c12521 +0 -0
  296. package/files/2107e243-c378-418d-a183-7df13873c65b +0 -0
  297. package/files/225ed61e-8f8c-4d17-b675-9a9f9918d5b4 +0 -0
  298. package/files/29ffba15-5acc-4ef8-b5a4-5ce61d3f6e85 +0 -0
  299. package/files/2a69434c-1d8a-4e9d-90d4-569aaeaae7e3 +0 -0
  300. package/files/2aec8fcf-25bf-478e-b2f6-fe67ad753071 +0 -0
  301. package/files/2c64490e-c7de-4cb2-b22e-70e2ac69d88f +0 -0
  302. package/files/2d80b4f4-6389-4f5d-8d32-f0ed93820907 +0 -0
  303. package/files/2f9dd26e-363f-4445-8ee5-28548007a33a +0 -0
  304. package/files/360dd8f5-76c4-4e86-ba22-9025dc7ca2a4 +0 -0
  305. package/files/3a0a7f5b-45a5-4340-ba7c-6a0fa2c63871 +0 -0
  306. package/files/3a8fa6be-8acf-4b7b-b653-25edc6b28cdc +0 -5
  307. package/files/3d9e191b-2c15-42aa-9928-c2cdbb5e14ca +0 -0
  308. package/files/3dd0f2ef-0d4e-4837-bffc-22aa645cbe85 +0 -0
  309. package/files/402c4b9b-adbf-4ab6-a21d-c17369b48abf +0 -0
  310. package/files/4685d988-33eb-4902-872f-3b824f497c8b +0 -0
  311. package/files/495f6c55-07f8-4713-a444-a2261a789b94 +0 -0
  312. package/files/4acba8e3-567b-4062-b81b-340e205a01de +0 -0
  313. package/files/4c0a10cc-395b-474a-8140-677ed607da89 +0 -0
  314. package/files/537584d9-25ad-4830-808a-a1e3d63e2a52 +0 -0
  315. package/files/5519f10d-745e-4474-8ca8-6c111693704c +0 -0
  316. package/files/5563cf92-a5e3-4be3-867d-9647a02298d4 +0 -0
  317. package/files/55b70d9b-fd58-4800-832c-d6b4521ba6d0 +0 -0
  318. package/files/5623cff3-ce9b-403b-9915-50d2bcbc981c +0 -0
  319. package/files/576314ba-2a1a-4753-8d77-2a9e04f509d1 +0 -0
  320. package/files/58ed97ae-0eac-4e04-add1-76646effa2d5 +0 -0
  321. package/files/68638efd-5389-481d-a841-36164c62c078 +0 -0
  322. package/files/6936d34b-a1f8-4a9d-b005-5544dbdcf5e5 +0 -0
  323. package/files/6bda9e88-3a28-47f7-994e-900ede6bf984 +0 -0
  324. package/files/7024a3b9-a863-4618-9dbd-fa6502017ae0 +0 -0
  325. package/files/707caccb-3780-456d-9056-c20bfdfc0e5b +0 -0
  326. package/files/74b8c73c-032b-4640-a5b3-d30dd270cbcb +0 -0
  327. package/files/767cb262-2974-40f8-8182-f7770b431923 +0 -0
  328. package/files/7a669935-cb48-4349-b26b-7705f8a04fbc +0 -0
  329. package/files/7bc8f678-6ee7-454c-a1f8-bb9358b89c95 +0 -0
  330. package/files/7ccc2699-07ec-4177-a9ce-ee7dc952fda1 +0 -0
  331. package/files/7e0eabf4-b334-4683-8156-ab8d949a0532 +0 -0
  332. package/files/7f0644ab-02a3-4121-adfc-29a7c55cf804 +0 -0
  333. package/files/7ff3266d-f103-48ce-90dd-95b9dfe5fcc9 +0 -0
  334. package/files/836e2e8e-aefd-4b4a-a9b9-bf7436158a8c +0 -0
  335. package/files/875f7ae5-fa23-4fc5-b04a-8433a7f7089c +0 -0
  336. package/files/8ca62da9-f204-49e3-b418-9451661b2904 +0 -0
  337. package/files/9283054c-107f-474d-b61e-f1d0061bcb86 +0 -0
  338. package/files/93b1ce7f-9566-452b-b4be-30f87d3de150 +0 -0
  339. package/files/93fb51c7-e19b-4ac1-9dc3-aeb8da0672ed +0 -0
  340. package/files/9b54a3a1-534a-4ed5-b016-3c74ed4c9edd +0 -0
  341. package/files/9b5beb6f-712d-4969-b127-fd66c9b2a9c6 +0 -0
  342. package/files/9e964fbf-d063-498e-b2e3-79f8d6afcf5f +0 -0
  343. package/files/a66b7a9f-58c2-47a8-a429-a6f0647c6fe9 +0 -0
  344. package/files/a7cbda7d-81ba-40f7-a997-51146af63e5f +0 -0
  345. package/files/ac01e83a-e572-41b6-81ab-c992cff7c170 +0 -0
  346. package/files/ad11a58f-f963-4233-bd29-1658b6b7e600 +0 -0
  347. package/files/ae60a4a8-08b9-4521-a0a3-d015a8b3ed08 +0 -0
  348. package/files/b1d3cf27-8d76-4cf9-aa51-d7c6bfd1b3bf +0 -0
  349. package/files/b2c68863-8554-4ac6-8e99-821f0267cf91 +0 -0
  350. package/files/b3043e01-a771-44af-bf19-5327646ff929 +0 -0
  351. package/files/b6d01b89-def5-4c7c-8e97-a3d88617f8f4 +0 -0
  352. package/files/b8760b32-bb1e-4cd7-a9d6-29c6e0b071bc +0 -0
  353. package/files/ba5e0470-44f7-47b3-bcb5-eeab3ca8c292 +0 -0
  354. package/files/c3969b5f-43ae-43f3-9bdd-d3959c79ca01 +0 -0
  355. package/files/caecd488-dbe6-4a30-a400-bced2ba8dae6 +0 -0
  356. package/files/d7d865b8-3a05-4ed1-b95d-b93dc1ebb9a9 +0 -0
  357. package/files/dbca5f31-cf38-4c1e-83b0-5ec8473196fc +0 -0
  358. package/files/dbf07e82-fff6-4985-bebe-d62c0458bfd0 +0 -0
  359. package/files/dd759c20-eead-4e57-9e74-4d3a2b978e91 +0 -5
  360. package/files/de0b2cf1-981b-4e4a-a04e-ac185d1620cd +0 -0
  361. package/files/de6837fe-5aa2-4ff9-a067-2646a008c780 +0 -0
  362. package/files/e2fde852-91cb-4e01-88a2-ee086b5f227c +0 -0
  363. package/files/e391d1ce-8d39-460c-a462-791730131f7f +0 -0
  364. package/files/e6d9b60f-2b1b-4c6f-ba6d-02f6de90d40f +0 -0
  365. package/files/f693c62d-2ac8-49fd-aa38-30904e013e3c +0 -0
  366. package/files/f749fdf5-193e-41f7-b643-5696f67c6402 +0 -0
  367. package/files/f8910384-e75c-4d65-825f-52a6748f6475 +0 -0
  368. package/files/fad8826b-e952-4acb-a509-3e6543b94d61 +0 -0
  369. package/jest.config.js +0 -13
  370. package/spire.sqlite +0 -0
@@ -0,0 +1,192 @@
1
+ import { sql } from "kysely";
2
+ export async function down(db) {
3
+ const tables = [
4
+ "service_metrics",
5
+ "invites",
6
+ "emojis",
7
+ "files",
8
+ "permissions",
9
+ "channels",
10
+ "servers",
11
+ "oneTimeKeys",
12
+ "preKeys",
13
+ "mail",
14
+ "devices",
15
+ "users",
16
+ ];
17
+ for (const table of tables) {
18
+ await db.schema.dropTable(table).ifExists().execute();
19
+ }
20
+ }
21
+ export async function up(db) {
22
+ await db.schema
23
+ .createTable("users")
24
+ .ifNotExists()
25
+ .addColumn("userID", "varchar(255)", (cb) => cb.primaryKey())
26
+ .addColumn("username", "varchar(255)", (cb) => cb.unique())
27
+ .addColumn("passwordHash", "text")
28
+ .addColumn("passwordSalt", "text")
29
+ .addColumn("lastSeen", "text")
30
+ .execute();
31
+ await db.schema
32
+ .createTable("devices")
33
+ .ifNotExists()
34
+ .addColumn("deviceID", "varchar(255)", (cb) => cb.primaryKey())
35
+ .addColumn("signKey", "varchar(255)", (cb) => cb.unique())
36
+ .addColumn("owner", "varchar(255)")
37
+ .addColumn("name", "varchar(255)")
38
+ .addColumn("lastLogin", "text")
39
+ .addColumn("deleted", "integer", (cb) => cb.defaultTo(0))
40
+ .execute();
41
+ await db.schema
42
+ .createTable("mail")
43
+ .ifNotExists()
44
+ .addColumn("nonce", "varchar(255)", (cb) => cb.primaryKey())
45
+ .addColumn("recipient", "varchar(255)")
46
+ .addColumn("mailID", "varchar(255)")
47
+ .addColumn("sender", "varchar(255)")
48
+ .addColumn("header", "text")
49
+ .addColumn("cipher", "text")
50
+ .addColumn("group", "varchar(255)")
51
+ .addColumn("extra", "text")
52
+ .addColumn("mailType", "integer")
53
+ .addColumn("time", "text")
54
+ .addColumn("forward", "integer", (cb) => cb.defaultTo(0))
55
+ .addColumn("authorID", "varchar(255)")
56
+ .addColumn("readerID", "varchar(255)")
57
+ .execute();
58
+ await db.schema
59
+ .createIndex("mail_recipient_idx")
60
+ .ifNotExists()
61
+ .on("mail")
62
+ .column("recipient")
63
+ .execute();
64
+ await db.schema
65
+ .createTable("preKeys")
66
+ .ifNotExists()
67
+ .addColumn("keyID", "varchar(255)", (cb) => cb.primaryKey())
68
+ .addColumn("userID", "varchar(255)")
69
+ .addColumn("deviceID", "varchar(255)", (cb) => cb.unique())
70
+ .addColumn("publicKey", "text")
71
+ .addColumn("signature", "text")
72
+ .addColumn("index", "integer")
73
+ .execute();
74
+ await db.schema
75
+ .createIndex("preKeys_userID_idx")
76
+ .ifNotExists()
77
+ .on("preKeys")
78
+ .column("userID")
79
+ .execute();
80
+ await db.schema
81
+ .createIndex("preKeys_deviceID_idx")
82
+ .ifNotExists()
83
+ .on("preKeys")
84
+ .column("deviceID")
85
+ .execute();
86
+ await db.schema
87
+ .createTable("oneTimeKeys")
88
+ .ifNotExists()
89
+ .addColumn("keyID", "varchar(255)", (cb) => cb.primaryKey())
90
+ .addColumn("userID", "varchar(255)")
91
+ .addColumn("deviceID", "varchar(255)")
92
+ .addColumn("publicKey", "text")
93
+ .addColumn("signature", "text")
94
+ .addColumn("index", "integer")
95
+ .execute();
96
+ await db.schema
97
+ .createIndex("oneTimeKeys_userID_idx")
98
+ .ifNotExists()
99
+ .on("oneTimeKeys")
100
+ .column("userID")
101
+ .execute();
102
+ await db.schema
103
+ .createIndex("oneTimeKeys_deviceID_idx")
104
+ .ifNotExists()
105
+ .on("oneTimeKeys")
106
+ .column("deviceID")
107
+ .execute();
108
+ await db.schema
109
+ .createTable("servers")
110
+ .ifNotExists()
111
+ .addColumn("serverID", "varchar(255)", (cb) => cb.primaryKey())
112
+ .addColumn("name", "varchar(255)")
113
+ .addColumn("icon", "varchar(255)")
114
+ .execute();
115
+ await db.schema
116
+ .createTable("channels")
117
+ .ifNotExists()
118
+ .addColumn("channelID", "varchar(255)", (cb) => cb.primaryKey())
119
+ .addColumn("serverID", "varchar(255)")
120
+ .addColumn("name", "varchar(255)")
121
+ .execute();
122
+ await db.schema
123
+ .createTable("permissions")
124
+ .ifNotExists()
125
+ .addColumn("permissionID", "varchar(255)", (cb) => cb.primaryKey())
126
+ .addColumn("userID", "varchar(255)")
127
+ .addColumn("resourceType", "varchar(255)")
128
+ .addColumn("resourceID", "varchar(255)")
129
+ .addColumn("powerLevel", "integer")
130
+ .execute();
131
+ await db.schema
132
+ .createIndex("permissions_userID_idx")
133
+ .ifNotExists()
134
+ .on("permissions")
135
+ .column("userID")
136
+ .execute();
137
+ await db.schema
138
+ .createIndex("permissions_resourceID_idx")
139
+ .ifNotExists()
140
+ .on("permissions")
141
+ .column("resourceID")
142
+ .execute();
143
+ await db.schema
144
+ .createTable("files")
145
+ .ifNotExists()
146
+ .addColumn("fileID", "varchar(255)", (cb) => cb.primaryKey())
147
+ .addColumn("owner", "varchar(255)")
148
+ .addColumn("nonce", "varchar(255)")
149
+ .execute();
150
+ await db.schema
151
+ .createIndex("files_owner_idx")
152
+ .ifNotExists()
153
+ .on("files")
154
+ .column("owner")
155
+ .execute();
156
+ await db.schema
157
+ .createTable("emojis")
158
+ .ifNotExists()
159
+ .addColumn("emojiID", "varchar(255)", (cb) => cb.primaryKey())
160
+ .addColumn("owner", "varchar(255)")
161
+ .addColumn("name", "varchar(255)")
162
+ .execute();
163
+ await db.schema
164
+ .createIndex("emojis_owner_idx")
165
+ .ifNotExists()
166
+ .on("emojis")
167
+ .column("owner")
168
+ .execute();
169
+ await db.schema
170
+ .createTable("invites")
171
+ .ifNotExists()
172
+ .addColumn("inviteID", "varchar(255)", (cb) => cb.primaryKey())
173
+ .addColumn("serverID", "varchar(255)")
174
+ .addColumn("owner", "varchar(255)")
175
+ .addColumn("expiration", "text")
176
+ .execute();
177
+ await db.schema
178
+ .createIndex("invites_serverID_idx")
179
+ .ifNotExists()
180
+ .on("invites")
181
+ .column("serverID")
182
+ .execute();
183
+ await db.schema
184
+ .createTable("service_metrics")
185
+ .ifNotExists()
186
+ .addColumn("metric_key", "varchar(255)", (cb) => cb.primaryKey())
187
+ .addColumn("metric_value", "bigint", (cb) => cb.notNull().defaultTo(0))
188
+ .execute();
189
+ // Seed the requests_total metric row
190
+ await sql `INSERT OR IGNORE INTO service_metrics (metric_key, metric_value) VALUES ('requests_total', 0)`.execute(db);
191
+ }
192
+ //# sourceMappingURL=2026-04-06_initial-schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"2026-04-06_initial-schema.js","sourceRoot":"","sources":["../../src/migrations/2026-04-06_initial-schema.ts"],"names":[],"mappings":"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"}
package/dist/run.js CHANGED
@@ -1,25 +1,30 @@
1
- "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
10
- };
11
- Object.defineProperty(exports, "__esModule", { value: true });
12
- const Spire_1 = require("./Spire");
13
- const loadEnv_1 = require("./utils/loadEnv");
1
+ import { Spire } from "./Spire.js";
2
+ import { loadEnv } from "./utils/loadEnv.js";
14
3
  function main() {
15
- return __awaiter(this, void 0, void 0, function* () {
16
- // load the environment variables
17
- loadEnv_1.loadEnv();
18
- const server = new Spire_1.Spire(process.env.SPK, {
19
- apiPort: Number(process.env.API_PORT),
20
- dbType: process.env.DB_TYPE,
21
- logLevel: "info",
22
- });
4
+ // load the environment variables loadEnv() exits if required vars are missing
5
+ loadEnv();
6
+ const spk = process.env["SPK"];
7
+ if (!spk) {
8
+ throw new Error("SPK must be set (loadEnv should have caught this).");
9
+ }
10
+ const apiPort = process.env["API_PORT"];
11
+ const dbType = parseDbType(process.env["DB_TYPE"]);
12
+ new Spire(spk, {
13
+ ...(apiPort !== undefined ? { apiPort: Number(apiPort) } : {}),
14
+ ...(dbType !== undefined ? { dbType } : {}),
15
+ logLevel: "info",
23
16
  });
24
17
  }
18
+ function parseDbType(value) {
19
+ switch (value) {
20
+ case "mysql":
21
+ case "sqlite":
22
+ case "sqlite3":
23
+ case "sqlite3mem":
24
+ return value;
25
+ default:
26
+ return undefined;
27
+ }
28
+ }
25
29
  main();
30
+ //# sourceMappingURL=run.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.js","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAE7C,SAAS,IAAI;IACT,gFAAgF;IAChF,OAAO,EAAE,CAAC;IAEV,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAC/B,IAAI,CAAC,GAAG,EAAE,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,oDAAoD,CAAC,CAAC;IAC1E,CAAC;IAED,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IACxC,MAAM,MAAM,GAAG,WAAW,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC;IAEnD,IAAI,KAAK,CAAC,GAAG,EAAE;QACX,GAAG,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9D,GAAG,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3C,QAAQ,EAAE,MAAM;KACnB,CAAC,CAAC;AACP,CAAC;AAED,SAAS,WAAW,CAAC,KAAyB;IAC1C,QAAQ,KAAK,EAAE,CAAC;QACZ,KAAK,OAAO,CAAC;QACb,KAAK,QAAQ,CAAC;QACd,KAAK,SAAS,CAAC;QACf,KAAK,YAAY;YACb,OAAO,KAAK,CAAC;QACjB;YACI,OAAO,SAAS,CAAC;IACzB,CAAC;AACL,CAAC;AAED,IAAI,EAAE,CAAC"}
@@ -1,4 +1,3 @@
1
- /// <reference types="express-ws" />
2
- import winston from "winston";
3
- import { Database } from "../Database";
4
- export declare const getAvatarRouter: (db: Database, log: winston.Logger) => import("express-ws").Router;
1
+ import type { Database } from "../Database.ts";
2
+ import type winston from "winston";
3
+ export declare const getAvatarRouter: (db: Database, log: winston.Logger) => import("express-serve-static-core").Router;
@@ -1,110 +1,110 @@
1
- "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
10
- };
11
- var __importDefault = (this && this.__importDefault) || function (mod) {
12
- return (mod && mod.__esModule) ? mod : { "default": mod };
13
- };
14
- Object.defineProperty(exports, "__esModule", { value: true });
15
- exports.getAvatarRouter = void 0;
16
- const fs_1 = __importDefault(require("fs"));
17
- const crypto_1 = require("@vex-chat/crypto");
18
- const express_1 = __importDefault(require("express"));
19
- const file_type_1 = __importDefault(require("file-type"));
20
- const multer_1 = __importDefault(require("multer"));
21
- const _1 = require(".");
22
- const getAvatarRouter = (db, log) => {
23
- const router = express_1.default.Router();
24
- router.get("/:userID", (req, res) => __awaiter(void 0, void 0, void 0, function* () {
25
- const stream = fs_1.default.createReadStream("./avatars/" + req.params.userID);
26
- stream.on("error", (err) => {
27
- // log.error(err.toString());
1
+ import * as fs from "node:fs";
2
+ import * as fsp from "node:fs/promises";
3
+ import express from "express";
4
+ import { XUtils } from "@vex-chat/crypto";
5
+ import { FilePayloadSchema } from "@vex-chat/types";
6
+ import { fileTypeFromBuffer, fileTypeFromFile } from "file-type";
7
+ import multer from "multer";
8
+ import { z } from "zod/v4";
9
+ import { uploadLimiter } from "./rateLimit.js";
10
+ import { getParam, getUser } from "./utils.js";
11
+ import { ALLOWED_IMAGE_TYPES, protect } from "./index.js";
12
+ const safePathParam = z.string().regex(/^[a-zA-Z0-9._-]+$/);
13
+ export const getAvatarRouter = (db, log) => {
14
+ const router = express.Router();
15
+ router.get("/:userID", async (req, res) => {
16
+ const safeId = safePathParam.safeParse(getParam(req, "userID"));
17
+ if (!safeId.success) {
18
+ res.sendStatus(400);
19
+ return;
20
+ }
21
+ const filePath = "./avatars/" + safeId.data;
22
+ const typeDetails = await fileTypeFromFile(filePath).catch(() => null);
23
+ if (!typeDetails) {
28
24
  res.sendStatus(404);
29
- });
30
- const typeDetails = yield file_type_1.default.fromStream(stream);
31
- if (typeDetails) {
32
- res.set("Content-type", typeDetails.mime);
25
+ return;
33
26
  }
27
+ res.set("Content-type", typeDetails.mime);
34
28
  res.set("Cache-control", "public, max-age=31536000");
35
- const stream2 = fs_1.default.createReadStream("./avatars/" + req.params.userID);
36
- stream2.on("error", (err) => {
29
+ const stream = fs.createReadStream(filePath);
30
+ stream.on("error", (err) => {
37
31
  log.error(err.toString());
38
32
  res.sendStatus(500);
39
33
  });
40
- stream2.pipe(res);
41
- }));
42
- router.post("/:userID/json", _1.protect, (req, res) => __awaiter(void 0, void 0, void 0, function* () {
43
- const payload = req.body;
44
- const userDetails = req.user;
45
- const deviceDetails = req
46
- .device;
34
+ stream.pipe(res);
35
+ });
36
+ router.post("/:userID/json", protect, async (req, res) => {
37
+ const parsed = FilePayloadSchema.safeParse(req.body);
38
+ if (!parsed.success) {
39
+ res.status(400).json({
40
+ error: "Invalid file payload",
41
+ issues: parsed.error.issues,
42
+ });
43
+ return;
44
+ }
45
+ const payload = parsed.data;
46
+ const userDetails = getUser(req);
47
+ const deviceDetails = req.device;
47
48
  if (!deviceDetails) {
48
49
  res.sendStatus(401);
49
50
  return;
50
51
  }
51
52
  if (!payload.file) {
52
- console.warn("MISSING FILE");
53
+ log.warn("MISSING FILE");
53
54
  res.sendStatus(400);
54
55
  return;
55
56
  }
56
- const buf = Buffer.from(crypto_1.XUtils.decodeBase64(payload.file));
57
- const mimeType = yield file_type_1.default.fromBuffer(buf);
58
- if (!_1.ALLOWED_IMAGE_TYPES.includes((mimeType === null || mimeType === void 0 ? void 0 : mimeType.mime) || "no/type")) {
57
+ const buf = Buffer.from(XUtils.decodeBase64(payload.file));
58
+ const mimeType = await fileTypeFromBuffer(buf);
59
+ if (!ALLOWED_IMAGE_TYPES.includes(mimeType?.mime || "no/type")) {
59
60
  res.status(400).send({
60
- error: "Unsupported file type. Expected jpeg, png, gif, apng, avif, or svg but received " + (mimeType === null || mimeType === void 0 ? void 0 : mimeType.ext),
61
+ error: "Unsupported file type. Expected jpeg, png, gif, apng, avif, or svg but received " +
62
+ String(mimeType?.ext),
61
63
  });
62
64
  return;
63
65
  }
64
66
  try {
65
67
  // write the file to disk
66
- fs_1.default.writeFile("avatars/" + userDetails.userID, buf, () => {
67
- log.info("Wrote new avatar " + userDetails.userID);
68
- });
68
+ await fsp.writeFile("avatars/" + userDetails.userID, buf);
69
+ log.info("Wrote new avatar " + userDetails.userID);
69
70
  res.sendStatus(200);
70
71
  }
71
72
  catch (err) {
72
- log.warn(err);
73
+ log.warn(String(err));
73
74
  res.sendStatus(500);
74
75
  }
75
- }));
76
- router.post("/:userID", _1.protect, multer_1.default().single("avatar"), (req, res) => __awaiter(void 0, void 0, void 0, function* () {
77
- const userDetails = req.user;
78
- const deviceDetails = req
79
- .device;
76
+ });
77
+ router.post("/:userID", uploadLimiter, protect, multer().single("avatar"), async (req, res) => {
78
+ const userDetails = getUser(req);
79
+ const deviceDetails = req.device;
80
80
  if (!deviceDetails) {
81
81
  res.sendStatus(401);
82
82
  return;
83
83
  }
84
84
  if (!req.file) {
85
- console.warn("MISSING FILE");
85
+ log.warn("MISSING FILE");
86
86
  res.sendStatus(400);
87
87
  return;
88
88
  }
89
- const mimeType = yield file_type_1.default.fromBuffer(req.file.buffer);
90
- if (!_1.ALLOWED_IMAGE_TYPES.includes((mimeType === null || mimeType === void 0 ? void 0 : mimeType.mime) || "no/type")) {
89
+ const mimeType = await fileTypeFromBuffer(req.file.buffer);
90
+ if (!ALLOWED_IMAGE_TYPES.includes(mimeType?.mime || "no/type")) {
91
91
  res.status(400).send({
92
- error: "Unsupported file type. Expected jpeg, png, gif, apng, avif, or svg but received " + (mimeType === null || mimeType === void 0 ? void 0 : mimeType.ext),
92
+ error: "Unsupported file type. Expected jpeg, png, gif, apng, avif, or svg but received " +
93
+ String(mimeType?.ext),
93
94
  });
94
95
  return;
95
96
  }
96
97
  try {
97
98
  // write the file to disk
98
- fs_1.default.writeFile("avatars/" + userDetails.userID, req.file.buffer, () => {
99
- log.info("Wrote new avatar " + userDetails.userID);
100
- });
99
+ await fsp.writeFile("avatars/" + userDetails.userID, req.file.buffer);
100
+ log.info("Wrote new avatar " + userDetails.userID);
101
101
  res.sendStatus(200);
102
102
  }
103
103
  catch (err) {
104
- log.warn(err);
104
+ log.warn(String(err));
105
105
  res.sendStatus(500);
106
106
  }
107
- }));
107
+ });
108
108
  return router;
109
109
  };
110
- exports.getAvatarRouter = getAvatarRouter;
110
+ //# sourceMappingURL=avatar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"avatar.js","sourceRoot":"","sources":["../../src/server/avatar.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Centralized HTTP error handling.
3
+ *
4
+ * Fixes two classes of CodeQL findings:
5
+ *
6
+ * - **CWE-209 / CWE-497 — Information exposure through stack trace.**
7
+ * Previously, every route catch block did
8
+ * `res.status(500).send(String(err))`, leaking raw error objects
9
+ * (which include database internals, file paths, and stack traces)
10
+ * back to the client. The new pattern: throw `AppError(status, msg)`
11
+ * or let an unknown error propagate, and the single 4-arg middleware
12
+ * below converts it into a JSON response with a generic message
13
+ * while logging the full internals server-side via winston.
14
+ *
15
+ * - **CWE-79 / CWE-116 — Exception text reinterpreted as HTML.** Express's
16
+ * default finalhandler sends thrown `Error` objects as
17
+ * `Content-Type: text/html`, which means `throw new Error("bad param: "
18
+ * + req.params.name)` became reflected XSS when the error page
19
+ * rendered. The handler below ALWAYS sends `application/json`, so
20
+ * even if a message somehow contains user input, the browser can't
21
+ * execute it. The companion fix is `getParam()` in `utils.ts` now
22
+ * throwing `AppError(400, "Missing route parameter")` with no user
23
+ * input in the message string.
24
+ *
25
+ * Express 5 has native async support, so throwing from an async
26
+ * handler auto-forwards to `next(err)` and hits this middleware. No
27
+ * `express-async-errors` shim needed.
28
+ */
29
+ import type { ErrorRequestHandler } from "express";
30
+ import type winston from "winston";
31
+ /**
32
+ * Operational HTTP errors that are safe to surface to the client.
33
+ *
34
+ * - `status` — HTTP status code (400, 401, 403, 404, 409, etc.)
35
+ * - `message` — client-safe message, MUST NOT contain request data
36
+ * (route params, body fields, query strings). Anything operator-
37
+ * only (database errors, file paths) belongs in winston logs, not
38
+ * in this string.
39
+ *
40
+ * Anything that isn't an `AppError` (raw `Error`, `TypeError`, a
41
+ * rejected promise from a DB query, etc.) is treated by the central
42
+ * handler as a **programmer error** — the client gets a generic 500
43
+ * with no detail, and the real error is logged server-side.
44
+ */
45
+ export declare class AppError extends Error {
46
+ readonly status: number;
47
+ constructor(status: number, message: string);
48
+ }
49
+ /**
50
+ * Factory producing the central Express 5 error middleware.
51
+ *
52
+ * Register this as the LAST middleware in the app, after every
53
+ * route and router has been mounted:
54
+ *
55
+ * api.use("/user", userRouter);
56
+ * // ... all other routers ...
57
+ * api.use(errorHandler(log));
58
+ */
59
+ export declare const errorHandler: (log: winston.Logger) => ErrorRequestHandler;
@@ -0,0 +1,94 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { ZodError } from "zod/v4";
3
+ /**
4
+ * Operational HTTP errors that are safe to surface to the client.
5
+ *
6
+ * - `status` — HTTP status code (400, 401, 403, 404, 409, etc.)
7
+ * - `message` — client-safe message, MUST NOT contain request data
8
+ * (route params, body fields, query strings). Anything operator-
9
+ * only (database errors, file paths) belongs in winston logs, not
10
+ * in this string.
11
+ *
12
+ * Anything that isn't an `AppError` (raw `Error`, `TypeError`, a
13
+ * rejected promise from a DB query, etc.) is treated by the central
14
+ * handler as a **programmer error** — the client gets a generic 500
15
+ * with no detail, and the real error is logged server-side.
16
+ */
17
+ export class AppError extends Error {
18
+ status;
19
+ constructor(status, message) {
20
+ super(message);
21
+ this.name = "AppError";
22
+ this.status = status;
23
+ }
24
+ }
25
+ /**
26
+ * Factory producing the central Express 5 error middleware.
27
+ *
28
+ * Register this as the LAST middleware in the app, after every
29
+ * route and router has been mounted:
30
+ *
31
+ * api.use("/user", userRouter);
32
+ * // ... all other routers ...
33
+ * api.use(errorHandler(log));
34
+ */
35
+ export const errorHandler = (log) => (err, req, res, _next) => {
36
+ // If headers already went out there's nothing safe to do except
37
+ // let Express's default handler close the socket.
38
+ if (res.headersSent) {
39
+ _next(err);
40
+ return;
41
+ }
42
+ const requestId = randomUUID();
43
+ let status = 500;
44
+ let clientMessage = "Internal Server Error";
45
+ let details;
46
+ if (err instanceof ZodError) {
47
+ // Validation failure at a trust boundary. The issue list is
48
+ // structured JSON (no raw user input as a rendered string),
49
+ // so it's safe to surface to help clients fix their payload.
50
+ status = 400;
51
+ clientMessage = "Validation failed";
52
+ details = err.issues;
53
+ }
54
+ else if (err instanceof AppError) {
55
+ status = err.status;
56
+ clientMessage = err.message;
57
+ }
58
+ // Log the full internals server-side. `err instanceof Error` also
59
+ // catches AppError (extends Error), so we get stacks and messages
60
+ // for every branch in the logs.
61
+ if (err instanceof Error) {
62
+ log.error("request failed", {
63
+ error: {
64
+ message: err.message,
65
+ name: err.name,
66
+ stack: err.stack,
67
+ },
68
+ method: req.method,
69
+ path: req.path,
70
+ requestId,
71
+ status,
72
+ });
73
+ }
74
+ else {
75
+ log.error("request failed", {
76
+ error: String(err),
77
+ method: req.method,
78
+ path: req.path,
79
+ requestId,
80
+ status,
81
+ });
82
+ }
83
+ // ALWAYS JSON — prevents the exception-text-as-HTML XSS vector.
84
+ res.status(status)
85
+ .type("application/json")
86
+ .json({
87
+ error: {
88
+ message: clientMessage,
89
+ requestId,
90
+ ...(details !== undefined ? { details } : {}),
91
+ },
92
+ });
93
+ };
94
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/server/errors.ts"],"names":[],"mappings":"AA+BA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAElC;;;;;;;;;;;;;GAaG;AACH,MAAM,OAAO,QAAS,SAAQ,KAAK;IACf,MAAM,CAAS;IAE/B,YAAY,MAAc,EAAE,OAAe;QACvC,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;QACvB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACzB,CAAC;CACJ;AAED;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,YAAY,GACrB,CAAC,GAAmB,EAAuB,EAAE,CAC7C,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE;IACrB,gEAAgE;IAChE,kDAAkD;IAClD,IAAI,GAAG,CAAC,WAAW,EAAE,CAAC;QAClB,KAAK,CAAC,GAAG,CAAC,CAAC;QACX,OAAO;IACX,CAAC;IAED,MAAM,SAAS,GAAG,UAAU,EAAE,CAAC;IAE/B,IAAI,MAAM,GAAG,GAAG,CAAC;IACjB,IAAI,aAAa,GAAG,uBAAuB,CAAC;IAC5C,IAAI,OAAgB,CAAC;IAErB,IAAI,GAAG,YAAY,QAAQ,EAAE,CAAC;QAC1B,4DAA4D;QAC5D,4DAA4D;QAC5D,6DAA6D;QAC7D,MAAM,GAAG,GAAG,CAAC;QACb,aAAa,GAAG,mBAAmB,CAAC;QACpC,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC;IACzB,CAAC;SAAM,IAAI,GAAG,YAAY,QAAQ,EAAE,CAAC;QACjC,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC;QACpB,aAAa,GAAG,GAAG,CAAC,OAAO,CAAC;IAChC,CAAC;IAED,kEAAkE;IAClE,kEAAkE;IAClE,gCAAgC;IAChC,IAAI,GAAG,YAAY,KAAK,EAAE,CAAC;QACvB,GAAG,CAAC,KAAK,CAAC,gBAAgB,EAAE;YACxB,KAAK,EAAE;gBACH,OAAO,EAAE,GAAG,CAAC,OAAO;gBACpB,IAAI,EAAE,GAAG,CAAC,IAAI;gBACd,KAAK,EAAE,GAAG,CAAC,KAAK;aACnB;YACD,MAAM,EAAE,GAAG,CAAC,MAAM;YAClB,IAAI,EAAE,GAAG,CAAC,IAAI;YACd,SAAS;YACT,MAAM;SACT,CAAC,CAAC;IACP,CAAC;SAAM,CAAC;QACJ,GAAG,CAAC,KAAK,CAAC,gBAAgB,EAAE;YACxB,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC;YAClB,MAAM,EAAE,GAAG,CAAC,MAAM;YAClB,IAAI,EAAE,GAAG,CAAC,IAAI;YACd,SAAS;YACT,MAAM;SACT,CAAC,CAAC;IACP,CAAC;IAED,gEAAgE;IAChE,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC;SACb,IAAI,CAAC,kBAAkB,CAAC;SACxB,IAAI,CAAC;QACF,KAAK,EAAE;YACH,OAAO,EAAE,aAAa;YACtB,SAAS;YACT,GAAG,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAChD;KACJ,CAAC,CAAC;AACX,CAAC,CAAC"}
@@ -1,4 +1,3 @@
1
- /// <reference types="express-ws" />
2
- import winston from "winston";
3
- import { Database } from "../Database";
4
- export declare const getFileRouter: (db: Database, log: winston.Logger) => import("express-ws").Router;
1
+ import type { Database } from "../Database.ts";
2
+ import type winston from "winston";
3
+ export declare const getFileRouter: (db: Database, log: winston.Logger) => import("express-serve-static-core").Router;