@vex-chat/spire 0.8.0 → 1.0.1
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.
- package/README.md +83 -28
- package/dist/ClientManager.d.ts +22 -26
- package/dist/ClientManager.js +198 -259
- package/dist/ClientManager.js.map +1 -0
- package/dist/Database.d.ts +71 -49
- package/dist/Database.js +737 -774
- package/dist/Database.js.map +1 -0
- package/dist/Spire.d.ts +20 -15
- package/dist/Spire.js +471 -249
- package/dist/Spire.js.map +1 -0
- package/dist/__tests__/Database.spec.js +102 -75
- package/dist/__tests__/Database.spec.js.map +1 -0
- package/dist/db/schema.d.ts +135 -0
- package/dist/db/schema.js +2 -0
- package/dist/db/schema.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +3 -5
- package/dist/index.js.map +1 -0
- package/dist/middleware/validate.d.ts +12 -0
- package/dist/middleware/validate.js +35 -0
- package/dist/middleware/validate.js.map +1 -0
- package/dist/migrations/2026-04-06_initial-schema.d.ts +3 -0
- package/dist/migrations/2026-04-06_initial-schema.js +192 -0
- package/dist/migrations/2026-04-06_initial-schema.js.map +1 -0
- package/dist/migrations/2026-04-14_argon2id-password-hashing.d.ts +3 -0
- package/dist/migrations/2026-04-14_argon2id-password-hashing.js +12 -0
- package/dist/migrations/2026-04-14_argon2id-password-hashing.js.map +1 -0
- package/dist/run.js +25 -21
- package/dist/run.js.map +1 -0
- package/dist/server/avatar.d.ts +1 -4
- package/dist/server/avatar.js +63 -67
- package/dist/server/avatar.js.map +1 -0
- package/dist/server/errors.d.ts +56 -0
- package/dist/server/errors.js +68 -0
- package/dist/server/errors.js.map +1 -0
- package/dist/server/file.d.ts +2 -4
- package/dist/server/file.js +81 -64
- package/dist/server/file.js.map +1 -0
- package/dist/server/index.d.ts +7 -10
- package/dist/server/index.js +414 -416
- package/dist/server/index.js.map +1 -0
- package/dist/server/invite.d.ts +3 -5
- package/dist/server/invite.js +18 -52
- package/dist/server/invite.js.map +1 -0
- package/dist/server/openapi.d.ts +2 -0
- package/dist/server/openapi.js +40 -0
- package/dist/server/openapi.js.map +1 -0
- package/dist/server/permissions.d.ts +16 -0
- package/dist/server/permissions.js +22 -0
- package/dist/server/permissions.js.map +1 -0
- package/dist/server/rateLimit.d.ts +28 -0
- package/dist/server/rateLimit.js +58 -0
- package/dist/server/rateLimit.js.map +1 -0
- package/dist/server/user.d.ts +3 -7
- package/dist/server/user.js +56 -69
- package/dist/server/user.js.map +1 -0
- package/dist/server/utils.d.ts +35 -7
- package/dist/server/utils.js +50 -6
- package/dist/server/utils.js.map +1 -0
- package/dist/types/express.d.ts +20 -0
- package/dist/types/express.js +2 -0
- package/dist/types/express.js.map +1 -0
- package/dist/utils/createUint8UUID.js +6 -10
- package/dist/utils/createUint8UUID.js.map +1 -0
- package/dist/utils/jwtSecret.d.ts +7 -0
- package/dist/utils/jwtSecret.js +14 -0
- package/dist/utils/jwtSecret.js.map +1 -0
- package/dist/utils/loadEnv.js +7 -22
- package/dist/utils/loadEnv.js.map +1 -0
- package/dist/utils/msgpack.d.ts +2 -0
- package/dist/utils/msgpack.js +4 -0
- package/dist/utils/msgpack.js.map +1 -0
- package/package.json +93 -65
- package/src/ClientManager.ts +382 -0
- package/src/Database.ts +987 -0
- package/src/Spire.ts +807 -0
- package/src/__tests__/Database.spec.ts +150 -0
- package/src/ambient-modules.d.ts +1 -0
- package/src/db/schema.ts +166 -0
- package/src/index.ts +3 -0
- package/src/middleware/validate.ts +38 -0
- package/src/migrations/2026-04-06_initial-schema.ts +218 -0
- package/src/migrations/2026-04-14_argon2id-password-hashing.ts +18 -0
- package/src/run.ts +36 -0
- package/src/server/avatar.ts +134 -0
- package/src/server/errors.ts +104 -0
- package/src/server/file.ts +167 -0
- package/src/server/index.ts +841 -0
- package/src/server/invite.ts +63 -0
- package/src/server/openapi.ts +51 -0
- package/src/server/permissions.ts +40 -0
- package/src/server/rateLimit.ts +86 -0
- package/src/server/user.ts +121 -0
- package/src/server/utils.ts +59 -0
- package/src/types/express.ts +23 -0
- package/src/utils/createUint8UUID.ts +9 -0
- package/src/utils/jwtSecret.ts +15 -0
- package/src/utils/loadEnv.ts +15 -0
- package/src/utils/msgpack.ts +4 -0
- package/avatars/052242d0-4129-4a6e-8076-22709c157549 +0 -0
- package/avatars/0a677c9a-4986-4b2c-ae2e-12faf22f55db +0 -0
- package/avatars/0ba21b91-decb-4a3e-ac86-4dd54d805a9a +0 -0
- package/avatars/0c48d8b6-1d1b-4297-a6c6-2fe50af6fc35 +0 -0
- package/avatars/0d993cdf-19a6-4299-a4ee-a06579d106cf +0 -0
- package/avatars/17b000e4-ac38-46ec-9dec-d2568086129a +0 -0
- package/avatars/19dc5594-0f06-4ac6-af18-8740dd39ef6b +0 -0
- package/avatars/20444fa3-6d5e-429e-b55f-b81c3d2c61ee +0 -0
- package/avatars/21c0512a-5630-4931-9442-d66db66737be +0 -0
- package/avatars/22830a60-0b6f-4912-83a5-72245465f332 +0 -0
- package/avatars/243639ce-f59f-4404-a1f1-4ec0eb5d2af3 +0 -0
- package/avatars/30d2c01d-7b7f-4ea9-9859-1c90837a23f7 +0 -0
- package/avatars/315a04f0-9a6f-4b0f-bb9f-5fa774c4752b +0 -0
- package/avatars/3563d333-53fe-4885-ac2d-9a4f761db85e +0 -0
- package/avatars/36a10c00-3b4c-437f-8e1f-4428ecde0003 +0 -0
- package/avatars/40b83eeb-c6e8-4268-82ab-69799a796405 +0 -0
- package/avatars/45b5ddb9-ad2c-4404-8ab3-cb4699e6d61c +0 -0
- package/avatars/4e4f0ffb-9a75-479a-bccb-446d0bf85020 +0 -0
- package/avatars/4e62c3bd-08c6-4fdd-bd65-f01c7322ed64 +0 -0
- package/avatars/5004d2e7-51af-44af-8776-6c71f7019843 +0 -0
- package/avatars/5041eb29-5c4b-4dea-8c1b-31ba4473161a +0 -0
- package/avatars/5065cf78-31c5-46cb-8d5c-c0b6be2d994e +0 -0
- package/avatars/51b91d2c-8956-4d73-b4ad-ca6a8d9da9a8 +0 -0
- package/avatars/58264a2c-5651-4a42-8ca2-a9907b311e48 +0 -0
- package/avatars/58c2357c-8080-4725-a0ce-182c96b037c4 +0 -0
- package/avatars/59b5f6dd-8e04-4d15-b4dc-c1c652558a74 +0 -0
- package/avatars/5b417a78-b274-48bc-98a4-6e54b74ee62d +0 -0
- package/avatars/611f5a93-1ed4-45a1-bc8e-e8e413f9b171 +0 -0
- package/avatars/65abe919-9921-46f6-9bc9-183e9cc53c8a +0 -0
- package/avatars/6934202d-1546-4270-8a15-97ba8b8c6fa9 +0 -0
- package/avatars/6acd24be-f4b7-4399-9e7c-807821828d29 +0 -0
- package/avatars/6b2d6ac5-e35c-4297-994e-f0eb6fe56740 +0 -0
- package/avatars/6cae9ddd-f163-44e7-a632-30425716a159 +0 -0
- package/avatars/6d90e79e-b9c1-4b89-843b-96636de8d26b +0 -0
- package/avatars/6f82c7bb-a974-4372-8a64-ce287e668c8c +0 -0
- package/avatars/74a45091-5a76-4bb7-ae8a-cd7373adc128 +0 -0
- package/avatars/7d071ab2-e0b5-4dbb-8bf5-1fae50c3663f +0 -0
- package/avatars/7de818c8-bda1-4f51-976e-160fc087184b +0 -0
- package/avatars/873937df-8cb1-427a-92bf-f829f4259624 +0 -0
- package/avatars/8b45ffa5-3322-4109-bfa0-1be088336135 +0 -0
- package/avatars/8efaa426-22a9-42a2-b4ac-a275717b812f +0 -0
- package/avatars/903fd1a6-d6ea-431c-b98f-f21e424a2852 +0 -0
- package/avatars/943d8533-5174-4199-990f-1ec69e5d60c4 +0 -0
- package/avatars/952d014e-3804-4cd2-a4a0-ffe40a11e4ac +0 -0
- package/avatars/95d3acb0-724d-4413-b20f-edad55812d5d +0 -0
- package/avatars/9641a946-f613-471b-bedd-c1730b96b51e +0 -0
- package/avatars/9b01cbbf-f6b2-43fb-b569-589b6f2a8134 +0 -0
- package/avatars/9cd4424d-a34f-4467-acc0-93cf82703e0d +0 -0
- package/avatars/9d9ad3b0-e5a6-420a-a6c5-fb9085b70376 +0 -0
- package/avatars/9e9e34b5-4e63-4c4b-9722-c7f5674b47aa +0 -0
- package/avatars/a387d5c1-59eb-4a6b-80c1-a8982ed12c33 +0 -0
- package/avatars/a3e86d21-d881-4824-8ebf-45e3bf0f9186 +0 -0
- package/avatars/a8d5cc1c-3f42-4b7b-8d33-f9a9ef77f96b +0 -0
- package/avatars/a91d815f-badc-4604-a7be-6c7a44e6101d +0 -0
- package/avatars/aa8d0324-bcec-4737-a8c4-bdbff914148d +0 -0
- package/avatars/abb8a941-8b6b-47d7-a2f9-8b153ba44aa2 +0 -0
- package/avatars/b011bb38-1ef3-4d22-82fa-8bf60faf7b5d +0 -0
- package/avatars/b24bcbc1-11f0-473d-a8b9-ba8ce4ca127d +0 -0
- package/avatars/b2607346-af1c-4e98-b725-7650a766db2a +0 -0
- package/avatars/b6300f7c-cb37-459b-b1bf-8a0a0e797a52 +0 -0
- package/avatars/b7d3cff3-84dd-4547-93a1-de1aaa8aa34c +0 -0
- package/avatars/baa4b51c-e97f-4f51-bcb3-f27bc506cfaf +0 -0
- package/avatars/be7022e4-e292-4515-80d5-f9b61ebeb4ce +0 -0
- package/avatars/bed596a3-7569-4854-9e76-f52d33c0a541 +0 -0
- package/avatars/bf69992f-3f72-4930-99bb-0ffe17f3aebf +0 -0
- package/avatars/ca00c250-c6d4-464d-a6de-1c8467a18fe8 +0 -0
- package/avatars/ca19d78f-c0bc-4bd5-b26f-6923cb19996d +0 -0
- package/avatars/cda4d6e1-e0a4-4024-ac95-6de98e713b98 +0 -0
- package/avatars/cf72c30d-2da8-4e81-aa71-735b9e714274 +0 -0
- package/avatars/d5a35b78-99b3-4564-b6b9-b2ccab28c470 +0 -0
- package/avatars/dadb38c1-2a9d-47a3-8d92-b56b6166973c +0 -0
- package/avatars/e68705c7-375d-4423-9a86-29a16bd3ee0e +0 -0
- package/avatars/e9af3e4c-1f62-4302-8b99-b68ce93b7a86 +0 -0
- package/avatars/ea7e7331-e845-4189-8248-5f5b1d63f5e3 +0 -0
- package/avatars/ef4f8dcb-ef6c-4e7a-9be1-0476161bfce5 +0 -0
- package/avatars/ef7d0917-a206-4f88-8b60-93f8253774dc +0 -0
- package/avatars/f1a554d6-1db3-4ff5-b0dc-b607d6c3b4ff +0 -0
- package/avatars/f1fecc21-c81f-49a8-88f3-f942a0a679f6 +0 -0
- package/avatars/f30a2427-1755-4053-813e-129a179e1dd3 +0 -0
- package/avatars/f5370717-5109-46a5-a8d7-e1dd996d0615 +0 -0
- package/avatars/f6dd7126-1144-4998-bbd3-d4e0fbee2e95 +0 -0
- package/avatars/f83413d5-0003-4756-9ece-745fd61cc468 +0 -0
- package/avatars/f9b3149e-7ec8-4bb3-a9b9-dcbe66dac197 +0 -0
- package/avatars/fa41d70b-857e-4423-bd7d-26ddcddc13b9 +0 -0
- package/avatars/fb551ee8-99c7-400b-8e1d-322ce4619998 +0 -0
- package/avatars/fe83a6d4-abb0-4ab0-b61d-76a7cc08be84 +0 -0
- package/dist/migrations/20210103192527_users.d.ts +0 -3
- package/dist/migrations/20210103192527_users.js +0 -30
- package/dist/migrations/20210103193502_mail.d.ts +0 -3
- package/dist/migrations/20210103193502_mail.js +0 -35
- package/dist/migrations/20210103193525_preKeys.d.ts +0 -3
- package/dist/migrations/20210103193525_preKeys.js +0 -30
- package/dist/migrations/20210103193553_oneTimeKeys.d.ts +0 -3
- package/dist/migrations/20210103193553_oneTimeKeys.js +0 -30
- package/dist/migrations/20210103193615_servers.d.ts +0 -3
- package/dist/migrations/20210103193615_servers.js +0 -28
- package/dist/migrations/20210103193729_channels.d.ts +0 -3
- package/dist/migrations/20210103193729_channels.js +0 -28
- package/dist/migrations/20210103193749_permissions.d.ts +0 -3
- package/dist/migrations/20210103193749_permissions.js +0 -30
- package/dist/migrations/20210103193801_files.d.ts +0 -3
- package/dist/migrations/20210103193801_files.js +0 -28
- package/dist/utils/createLogger.d.ts +0 -5
- package/dist/utils/createLogger.js +0 -41
- package/emoji/04d98632-2c86-421b-a407-17f14fe86f8f +0 -0
- package/emoji/1160ed6e-1163-4043-9808-4029e863ed30 +0 -0
- package/emoji/1547ab18-1635-4a80-a82d-ebbb767b9932 +0 -0
- package/emoji/16922521-f6cb-4de4-860c-27916b22c6ba +0 -0
- package/emoji/198a9432-0e41-4866-994a-448d4775afcb +0 -0
- package/emoji/1be886b3-c9c5-4593-b516-f357ed931f96 +0 -0
- package/emoji/1c2b3d1d-637f-4103-b066-4bc4511a3ad7 +0 -0
- package/emoji/1efd27e7-b15f-475c-8b32-9159d26b169d +0 -0
- package/emoji/270b9409-0ea5-4be2-a239-a8dce13f9c31 +0 -0
- package/emoji/27812f76-fee2-49dd-a217-363de6d159dc +0 -0
- package/emoji/297ec202-8c24-44c6-aead-689d6d461883 +0 -0
- package/emoji/2bf06d86-17cb-4f40-a5ef-bd75d239a1a3 +0 -0
- package/emoji/31a75163-1cce-4dc1-b0a2-ecad6a4c500b +0 -0
- package/emoji/35235635-fdbd-4273-8428-f3cb3e1e8fd3 +0 -0
- package/emoji/3690fff2-6824-4403-a6e3-16a6a54979a9 +0 -0
- package/emoji/391014c2-59e0-46a8-85ec-7a7fdaca1d2d +0 -0
- package/emoji/3b383dcb-6e76-4e85-8e16-7c68040c06c2 +0 -0
- package/emoji/42d617a7-b104-42f5-9618-473181f752cf +0 -0
- package/emoji/482495d3-cce9-4f88-bf2a-f6003f03a9b5 +0 -0
- package/emoji/48390e06-0efb-404c-89bd-5f2be241bd50 +0 -0
- package/emoji/4b808d8d-3248-4149-b919-71b108391bcf +0 -0
- package/emoji/4bc13544-d82a-4e32-bd17-a70592274314 +0 -0
- package/emoji/4fcebf70-8623-4343-8243-67c8547b2edd +0 -0
- package/emoji/509d09aa-1214-459c-8081-50918a17b9af +0 -0
- package/emoji/5272abd8-d4d7-4b90-acd2-bf30e6c27243 +0 -0
- package/emoji/53c272ce-48bd-4d7e-bfb8-a6482b88be54 +0 -0
- package/emoji/5b279e65-06f7-4b26-8b4c-d1b48fba728d +0 -0
- package/emoji/5bd141f9-4394-4108-9376-66ebbc2c2bc1 +0 -0
- package/emoji/5c769156-f9cb-40bd-ab89-4edeece613fd +0 -0
- package/emoji/5c85fba9-8ba7-4fc9-b1b2-48dc30d24a1b +0 -0
- package/emoji/61a5e565-d20b-40ba-a139-b0c73a6027f3 +0 -0
- package/emoji/6913f43d-dd45-456c-9641-a126104d9ae5 +0 -0
- package/emoji/6957e74e-9622-492d-a950-242db3752260 +0 -0
- package/emoji/6a14bab5-26af-4bfa-9c17-be7c2511976d +0 -0
- package/emoji/6be09439-509e-4095-a30a-b1c7c573895d +0 -0
- package/emoji/6cc435b1-fe53-433c-b5a9-2b2019053997 +0 -0
- package/emoji/74f1b2af-bc7e-4a0f-802a-64ded185d5e2 +0 -0
- package/emoji/7890ba09-f02f-428e-807d-006d03d51d4a +0 -0
- package/emoji/7dc69179-6b3c-4f40-b20b-0ff573deea2d +0 -0
- package/emoji/7dd1b6b1-439d-4279-916a-995408863172 +0 -0
- package/emoji/820498ad-a2c8-43a2-ab83-d26f9c2246d4 +0 -0
- package/emoji/8319469c-2787-44e5-91a6-c8c39810dd7c +0 -0
- package/emoji/86745d1d-9e59-4607-b2b0-46c741079be1 +0 -0
- package/emoji/887b3cff-ae9e-4b5f-ad00-3ca9fc72f689 +0 -0
- package/emoji/8c6cf621-71d6-4fca-abe6-e19f4dd7f883 +0 -0
- package/emoji/8ca6d32e-a1ef-4956-a416-d8d0d680f085 +0 -0
- package/emoji/8d979f5e-38a2-4dd1-b3e4-80938bbe499b +0 -0
- package/emoji/99f68ea0-e3fe-4f03-9cc3-5f7f5315404d +0 -0
- package/emoji/9ad3aedc-7f79-4d68-a144-82e5b5dc3033 +0 -0
- package/emoji/9e418c2f-1f0f-46c4-be39-3bda38a28545 +0 -0
- package/emoji/a1f616bf-7402-4e24-9111-18acaebabb48 +0 -0
- package/emoji/a25ed9c1-3f9c-4e5f-ade2-7b159fb9fbf4 +0 -0
- package/emoji/a5176bc2-39a8-467b-8c75-6fbbc81b59c7 +0 -0
- package/emoji/a584215c-6547-438b-8ae8-dd490b51890e +0 -0
- package/emoji/a739895f-cf61-4b7c-b350-8e8283aaf751 +0 -0
- package/emoji/aaa10dd2-02a2-499e-9e17-c83787436508 +0 -0
- package/emoji/ae90baf2-a0ef-4d4d-9cc4-94f8ddd60f45 +0 -0
- package/emoji/b0564c48-feae-431a-95f9-df597c6c124c +0 -0
- package/emoji/b218bb93-e69c-4793-a669-83316650c4e7 +0 -0
- package/emoji/b2998c27-85d5-4598-ab41-469aa8e0fcad +0 -0
- package/emoji/b3da08ba-4179-4b5d-826f-5fc15e1a3ad2 +0 -0
- package/emoji/b840eb6a-a917-4bb2-854b-8f1022e7904b +0 -0
- package/emoji/b84baa76-b4b0-4b83-bc83-78661cb4f1d4 +0 -0
- package/emoji/baf69d80-8b1d-4032-855f-605cf0d489c3 +0 -0
- package/emoji/bb4d372c-ccd0-4a47-b157-b6a3b9f763e2 +0 -0
- package/emoji/bdbd1627-c81d-42d9-b3f5-8979e2ab74dc +0 -0
- package/emoji/c257388f-8b85-450b-b168-ebdf8d8c3026 +0 -0
- package/emoji/c573fde1-faa9-4c1d-a172-e283645afcfd +0 -0
- package/emoji/c8e27810-e8ea-47ce-b7ec-cef0a6becb28 +0 -0
- package/emoji/cdeef182-b220-4850-9ecf-5d7c472fd754 +0 -0
- package/emoji/d59c1aa9-6f81-4c07-96dd-9953401ff211 +0 -0
- package/emoji/dd407dbf-a077-40ba-957f-337b3c5efdc7 +0 -0
- package/emoji/e01f6e06-5728-4e2c-90fb-314a5827b766 +0 -0
- package/emoji/e1f9ed12-a2ce-433d-b454-b833438a1f9c +0 -0
- package/emoji/e697e5c4-acd6-41cd-a43e-edee8da3ab7b +0 -0
- package/emoji/e8182220-3464-4e31-8c08-466baead7bfc +0 -0
- package/emoji/eb0f3fd5-abc9-4abf-b816-d8458aeb7ec8 +0 -0
- package/emoji/eb38ecf7-0d13-4c51-b96a-3777f79321c4 +0 -0
- package/emoji/ee515c85-b7ce-4493-a427-994cc0af0d59 +0 -0
- package/emoji/f485cef2-d3fa-4d59-88af-b79a3105cacf +0 -0
- package/emoji/ff0dab2a-7015-4e8c-b0d0-3569058359dc +0 -0
- package/files/01087968-07b6-4fdb-9aeb-fa9dc061be94 +0 -0
- package/files/030455b3-17cc-415f-b3b3-2bb56c92ee8b +0 -0
- package/files/06129f52-a858-4031-ad85-4d7f2bb793af +0 -0
- package/files/0a6155a9-069f-45e9-8a06-56992fe55187 +0 -0
- package/files/12b9dda5-feb1-4f20-a987-ad422db5ba73 +0 -0
- package/files/13c8fa1e-8821-4628-b607-9e4fa4510df7 +0 -0
- package/files/159b0ad3-1a30-419d-af22-28a1096ce825 +0 -0
- package/files/1699c563-6769-431b-8041-99a6a4386f25 +0 -0
- package/files/176b916c-0dd9-4b93-bfdc-b8ccabf15a96 +0 -0
- package/files/1a27dad9-8cc9-4a0c-9d4e-7bde63adda60 +0 -0
- package/files/1d29832c-059a-4190-bca6-b83ac77540d9 +0 -0
- package/files/1dcde013-8833-4369-8726-81236e4eb30e +0 -0
- package/files/2080fc9f-d2c4-4fbc-af84-232fe4900a4f +0 -0
- package/files/20889623-6869-46a2-999b-c07708c12521 +0 -0
- package/files/2107e243-c378-418d-a183-7df13873c65b +0 -0
- package/files/225ed61e-8f8c-4d17-b675-9a9f9918d5b4 +0 -0
- package/files/29ffba15-5acc-4ef8-b5a4-5ce61d3f6e85 +0 -0
- package/files/2a69434c-1d8a-4e9d-90d4-569aaeaae7e3 +0 -0
- package/files/2aec8fcf-25bf-478e-b2f6-fe67ad753071 +0 -0
- package/files/2c64490e-c7de-4cb2-b22e-70e2ac69d88f +0 -0
- package/files/2d80b4f4-6389-4f5d-8d32-f0ed93820907 +0 -0
- package/files/2f9dd26e-363f-4445-8ee5-28548007a33a +0 -0
- package/files/360dd8f5-76c4-4e86-ba22-9025dc7ca2a4 +0 -0
- package/files/3a0a7f5b-45a5-4340-ba7c-6a0fa2c63871 +0 -0
- package/files/3a8fa6be-8acf-4b7b-b653-25edc6b28cdc +0 -5
- package/files/3d9e191b-2c15-42aa-9928-c2cdbb5e14ca +0 -0
- package/files/3dd0f2ef-0d4e-4837-bffc-22aa645cbe85 +0 -0
- package/files/402c4b9b-adbf-4ab6-a21d-c17369b48abf +0 -0
- package/files/4685d988-33eb-4902-872f-3b824f497c8b +0 -0
- package/files/495f6c55-07f8-4713-a444-a2261a789b94 +0 -0
- package/files/4acba8e3-567b-4062-b81b-340e205a01de +0 -0
- package/files/4c0a10cc-395b-474a-8140-677ed607da89 +0 -0
- package/files/537584d9-25ad-4830-808a-a1e3d63e2a52 +0 -0
- package/files/5519f10d-745e-4474-8ca8-6c111693704c +0 -0
- package/files/5563cf92-a5e3-4be3-867d-9647a02298d4 +0 -0
- package/files/55b70d9b-fd58-4800-832c-d6b4521ba6d0 +0 -0
- package/files/5623cff3-ce9b-403b-9915-50d2bcbc981c +0 -0
- package/files/576314ba-2a1a-4753-8d77-2a9e04f509d1 +0 -0
- package/files/58ed97ae-0eac-4e04-add1-76646effa2d5 +0 -0
- package/files/68638efd-5389-481d-a841-36164c62c078 +0 -0
- package/files/6936d34b-a1f8-4a9d-b005-5544dbdcf5e5 +0 -0
- package/files/6bda9e88-3a28-47f7-994e-900ede6bf984 +0 -0
- package/files/7024a3b9-a863-4618-9dbd-fa6502017ae0 +0 -0
- package/files/707caccb-3780-456d-9056-c20bfdfc0e5b +0 -0
- package/files/74b8c73c-032b-4640-a5b3-d30dd270cbcb +0 -0
- package/files/767cb262-2974-40f8-8182-f7770b431923 +0 -0
- package/files/7a669935-cb48-4349-b26b-7705f8a04fbc +0 -0
- package/files/7bc8f678-6ee7-454c-a1f8-bb9358b89c95 +0 -0
- package/files/7ccc2699-07ec-4177-a9ce-ee7dc952fda1 +0 -0
- package/files/7e0eabf4-b334-4683-8156-ab8d949a0532 +0 -0
- package/files/7f0644ab-02a3-4121-adfc-29a7c55cf804 +0 -0
- package/files/7ff3266d-f103-48ce-90dd-95b9dfe5fcc9 +0 -0
- package/files/836e2e8e-aefd-4b4a-a9b9-bf7436158a8c +0 -0
- package/files/875f7ae5-fa23-4fc5-b04a-8433a7f7089c +0 -0
- package/files/8ca62da9-f204-49e3-b418-9451661b2904 +0 -0
- package/files/9283054c-107f-474d-b61e-f1d0061bcb86 +0 -0
- package/files/93b1ce7f-9566-452b-b4be-30f87d3de150 +0 -0
- package/files/93fb51c7-e19b-4ac1-9dc3-aeb8da0672ed +0 -0
- package/files/9b54a3a1-534a-4ed5-b016-3c74ed4c9edd +0 -0
- package/files/9b5beb6f-712d-4969-b127-fd66c9b2a9c6 +0 -0
- package/files/9e964fbf-d063-498e-b2e3-79f8d6afcf5f +0 -0
- package/files/a66b7a9f-58c2-47a8-a429-a6f0647c6fe9 +0 -0
- package/files/a7cbda7d-81ba-40f7-a997-51146af63e5f +0 -0
- package/files/ac01e83a-e572-41b6-81ab-c992cff7c170 +0 -0
- package/files/ad11a58f-f963-4233-bd29-1658b6b7e600 +0 -0
- package/files/ae60a4a8-08b9-4521-a0a3-d015a8b3ed08 +0 -0
- package/files/b1d3cf27-8d76-4cf9-aa51-d7c6bfd1b3bf +0 -0
- package/files/b2c68863-8554-4ac6-8e99-821f0267cf91 +0 -0
- package/files/b3043e01-a771-44af-bf19-5327646ff929 +0 -0
- package/files/b6d01b89-def5-4c7c-8e97-a3d88617f8f4 +0 -0
- package/files/b8760b32-bb1e-4cd7-a9d6-29c6e0b071bc +0 -0
- package/files/ba5e0470-44f7-47b3-bcb5-eeab3ca8c292 +0 -0
- package/files/c3969b5f-43ae-43f3-9bdd-d3959c79ca01 +0 -0
- package/files/caecd488-dbe6-4a30-a400-bced2ba8dae6 +0 -0
- package/files/d7d865b8-3a05-4ed1-b95d-b93dc1ebb9a9 +0 -0
- package/files/dbca5f31-cf38-4c1e-83b0-5ec8473196fc +0 -0
- package/files/dbf07e82-fff6-4985-bebe-d62c0458bfd0 +0 -0
- package/files/dd759c20-eead-4e57-9e74-4d3a2b978e91 +0 -5
- package/files/de0b2cf1-981b-4e4a-a04e-ac185d1620cd +0 -0
- package/files/de6837fe-5aa2-4ff9-a067-2646a008c780 +0 -0
- package/files/e2fde852-91cb-4e01-88a2-ee086b5f227c +0 -0
- package/files/e391d1ce-8d39-460c-a462-791730131f7f +0 -0
- package/files/e6d9b60f-2b1b-4c6f-ba6d-02f6de90d40f +0 -0
- package/files/f693c62d-2ac8-49fd-aa38-30904e013e3c +0 -0
- package/files/f749fdf5-193e-41f7-b643-5696f67c6402 +0 -0
- package/files/f8910384-e75c-4d65-825f-52a6748f6475 +0 -0
- package/files/fad8826b-e952-4acb-a509-3e6543b94d61 +0 -0
- package/jest.config.js +0 -13
- package/spire.sqlite +0 -0
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as fsp from "node:fs/promises";
|
|
3
|
+
|
|
4
|
+
import express from "express";
|
|
5
|
+
|
|
6
|
+
import { XUtils } from "@vex-chat/crypto";
|
|
7
|
+
import { FilePayloadSchema } from "@vex-chat/types";
|
|
8
|
+
|
|
9
|
+
import { fileTypeFromBuffer, fileTypeFromFile } from "file-type";
|
|
10
|
+
import multer from "multer";
|
|
11
|
+
import { z } from "zod/v4";
|
|
12
|
+
|
|
13
|
+
import { uploadLimiter } from "./rateLimit.ts";
|
|
14
|
+
import { getParam, getUser } from "./utils.ts";
|
|
15
|
+
|
|
16
|
+
import { ALLOWED_IMAGE_TYPES, protect } from "./index.ts";
|
|
17
|
+
|
|
18
|
+
const safePathParam = z.string().regex(/^[a-zA-Z0-9._-]+$/);
|
|
19
|
+
|
|
20
|
+
export const getAvatarRouter = () => {
|
|
21
|
+
const router = express.Router();
|
|
22
|
+
|
|
23
|
+
router.get("/:userID", async (req, res) => {
|
|
24
|
+
const safeId = safePathParam.safeParse(getParam(req, "userID"));
|
|
25
|
+
if (!safeId.success) {
|
|
26
|
+
res.sendStatus(400);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const filePath = "./avatars/" + safeId.data;
|
|
30
|
+
const typeDetails = await fileTypeFromFile(filePath).catch(() => null);
|
|
31
|
+
if (!typeDetails) {
|
|
32
|
+
res.sendStatus(404);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
res.set("Content-type", typeDetails.mime);
|
|
36
|
+
res.set("Cache-control", "public, max-age=31536000");
|
|
37
|
+
|
|
38
|
+
const stream = fs.createReadStream(filePath);
|
|
39
|
+
stream.on("error", (_err) => {
|
|
40
|
+
// debugger: avatar stream read error
|
|
41
|
+
res.sendStatus(500);
|
|
42
|
+
});
|
|
43
|
+
stream.pipe(res);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
router.post("/:userID/json", protect, async (req, res) => {
|
|
47
|
+
const parsed = FilePayloadSchema.safeParse(req.body);
|
|
48
|
+
if (!parsed.success) {
|
|
49
|
+
res.status(400).json({
|
|
50
|
+
error: "Invalid file payload",
|
|
51
|
+
issues: parsed.error.issues,
|
|
52
|
+
});
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
const payload = parsed.data;
|
|
56
|
+
const userDetails = getUser(req);
|
|
57
|
+
const deviceDetails = req.device;
|
|
58
|
+
|
|
59
|
+
if (!deviceDetails) {
|
|
60
|
+
res.sendStatus(401);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (!payload.file) {
|
|
65
|
+
res.sendStatus(400);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const buf = Buffer.from(XUtils.decodeBase64(payload.file));
|
|
70
|
+
const mimeType = await fileTypeFromBuffer(buf);
|
|
71
|
+
if (!ALLOWED_IMAGE_TYPES.includes(mimeType?.mime || "no/type")) {
|
|
72
|
+
res.status(400).send({
|
|
73
|
+
error:
|
|
74
|
+
"Unsupported file type. Expected jpeg, png, gif, apng, avif, or svg but received " +
|
|
75
|
+
String(mimeType?.ext),
|
|
76
|
+
});
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
// write the file to disk
|
|
82
|
+
await fsp.writeFile("avatars/" + userDetails.userID, buf);
|
|
83
|
+
res.sendStatus(200);
|
|
84
|
+
} catch (_err: unknown) {
|
|
85
|
+
// debugger: avatar write failed
|
|
86
|
+
res.sendStatus(500);
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
router.post(
|
|
91
|
+
"/:userID",
|
|
92
|
+
uploadLimiter,
|
|
93
|
+
protect,
|
|
94
|
+
multer().single("avatar"),
|
|
95
|
+
async (req, res) => {
|
|
96
|
+
const userDetails = getUser(req);
|
|
97
|
+
const deviceDetails = req.device;
|
|
98
|
+
|
|
99
|
+
if (!deviceDetails) {
|
|
100
|
+
res.sendStatus(401);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (!req.file) {
|
|
105
|
+
res.sendStatus(400);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const mimeType = await fileTypeFromBuffer(req.file.buffer);
|
|
110
|
+
if (!ALLOWED_IMAGE_TYPES.includes(mimeType?.mime || "no/type")) {
|
|
111
|
+
res.status(400).send({
|
|
112
|
+
error:
|
|
113
|
+
"Unsupported file type. Expected jpeg, png, gif, apng, avif, or svg but received " +
|
|
114
|
+
String(mimeType?.ext),
|
|
115
|
+
});
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
try {
|
|
120
|
+
// write the file to disk
|
|
121
|
+
await fsp.writeFile(
|
|
122
|
+
"avatars/" + userDetails.userID,
|
|
123
|
+
req.file.buffer,
|
|
124
|
+
);
|
|
125
|
+
res.sendStatus(200);
|
|
126
|
+
} catch (_err: unknown) {
|
|
127
|
+
// debugger: avatar write failed
|
|
128
|
+
res.sendStatus(500);
|
|
129
|
+
}
|
|
130
|
+
},
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
return router;
|
|
134
|
+
};
|
|
@@ -0,0 +1,104 @@
|
|
|
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
|
+
*
|
|
14
|
+
* - **CWE-79 / CWE-116 — Exception text reinterpreted as HTML.** Express's
|
|
15
|
+
* default finalhandler sends thrown `Error` objects as
|
|
16
|
+
* `Content-Type: text/html`, which means `throw new Error("bad param: "
|
|
17
|
+
* + req.params.name)` became reflected XSS when the error page
|
|
18
|
+
* rendered. The handler below ALWAYS sends `application/json`, so
|
|
19
|
+
* even if a message somehow contains user input, the browser can't
|
|
20
|
+
* execute it. The companion fix is `getParam()` in `utils.ts` now
|
|
21
|
+
* throwing `AppError(400, "Missing route parameter")` with no user
|
|
22
|
+
* input in the message string.
|
|
23
|
+
*
|
|
24
|
+
* Express 5 has native async support, so throwing from an async
|
|
25
|
+
* handler auto-forwards to `next(err)` and hits this middleware. No
|
|
26
|
+
* `express-async-errors` shim needed.
|
|
27
|
+
*/
|
|
28
|
+
import type { ErrorRequestHandler } from "express";
|
|
29
|
+
|
|
30
|
+
import { randomUUID } from "node:crypto";
|
|
31
|
+
|
|
32
|
+
import { ZodError } from "zod/v4";
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Operational HTTP errors that are safe to surface to the client.
|
|
36
|
+
*
|
|
37
|
+
* - `status` — HTTP status code (400, 401, 403, 404, 409, etc.)
|
|
38
|
+
* - `message` — client-safe message, MUST NOT contain request data
|
|
39
|
+
* (route params, body fields, query strings). Anything operator-
|
|
40
|
+
* only (database errors, file paths) should not appear in this string.
|
|
41
|
+
*
|
|
42
|
+
* Anything that isn't an `AppError` (raw `Error`, `TypeError`, a
|
|
43
|
+
* rejected promise from a DB query, etc.) is treated by the central
|
|
44
|
+
* handler as a **programmer error** — the client gets a generic 500
|
|
45
|
+
* with no detail, and the real error is logged server-side.
|
|
46
|
+
*/
|
|
47
|
+
export class AppError extends Error {
|
|
48
|
+
public readonly status: number;
|
|
49
|
+
|
|
50
|
+
constructor(status: number, message: string) {
|
|
51
|
+
super(message);
|
|
52
|
+
this.name = "AppError";
|
|
53
|
+
this.status = status;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Factory producing the central Express 5 error middleware.
|
|
59
|
+
*
|
|
60
|
+
* Register this as the LAST middleware in the app, after every
|
|
61
|
+
* route and router has been mounted:
|
|
62
|
+
*
|
|
63
|
+
* api.use("/user", userRouter);
|
|
64
|
+
* // ... all other routers ...
|
|
65
|
+
* api.use(errorHandler(log));
|
|
66
|
+
*/
|
|
67
|
+
export const errorHandler =
|
|
68
|
+
(): ErrorRequestHandler => (err, _req, res, _next) => {
|
|
69
|
+
// If headers already went out there's nothing safe to do except
|
|
70
|
+
// let Express's default handler close the socket.
|
|
71
|
+
if (res.headersSent) {
|
|
72
|
+
_next(err);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const requestId = randomUUID();
|
|
77
|
+
|
|
78
|
+
let status = 500;
|
|
79
|
+
let clientMessage = "Internal Server Error";
|
|
80
|
+
let details: unknown;
|
|
81
|
+
|
|
82
|
+
if (err instanceof ZodError) {
|
|
83
|
+
// Validation failure at a trust boundary. The issue list is
|
|
84
|
+
// structured JSON (no raw user input as a rendered string),
|
|
85
|
+
// so it's safe to surface to help clients fix their payload.
|
|
86
|
+
status = 400;
|
|
87
|
+
clientMessage = "Validation failed";
|
|
88
|
+
details = err.issues;
|
|
89
|
+
} else if (err instanceof AppError) {
|
|
90
|
+
status = err.status;
|
|
91
|
+
clientMessage = err.message;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// ALWAYS JSON — prevents the exception-text-as-HTML XSS vector.
|
|
95
|
+
res.status(status)
|
|
96
|
+
.type("application/json")
|
|
97
|
+
.json({
|
|
98
|
+
error: {
|
|
99
|
+
message: clientMessage,
|
|
100
|
+
requestId,
|
|
101
|
+
...(details !== undefined ? { details } : {}),
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
};
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import type { Database } from "../Database.ts";
|
|
2
|
+
import type { FileSQL } from "@vex-chat/types";
|
|
3
|
+
|
|
4
|
+
import * as fs from "node:fs";
|
|
5
|
+
import * as fsp from "node:fs/promises";
|
|
6
|
+
import * as path from "node:path";
|
|
7
|
+
|
|
8
|
+
import express from "express";
|
|
9
|
+
|
|
10
|
+
import { XUtils } from "@vex-chat/crypto";
|
|
11
|
+
import { FilePayloadSchema } from "@vex-chat/types";
|
|
12
|
+
|
|
13
|
+
import multer from "multer";
|
|
14
|
+
import { z } from "zod/v4";
|
|
15
|
+
|
|
16
|
+
import { msgpack } from "../utils/msgpack.ts";
|
|
17
|
+
|
|
18
|
+
import { uploadLimiter } from "./rateLimit.ts";
|
|
19
|
+
import { getParam } from "./utils.ts";
|
|
20
|
+
|
|
21
|
+
import { protect } from "./index.ts";
|
|
22
|
+
|
|
23
|
+
const safePathParam = z.string().regex(/^[a-zA-Z0-9._-]+$/);
|
|
24
|
+
|
|
25
|
+
export const getFileRouter = (db: Database) => {
|
|
26
|
+
const router = express.Router();
|
|
27
|
+
|
|
28
|
+
router.get("/:id", protect, async (req, res) => {
|
|
29
|
+
const safeId = safePathParam.safeParse(getParam(req, "id"));
|
|
30
|
+
if (!safeId.success) {
|
|
31
|
+
res.sendStatus(400);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const entry = await db.retrieveFile(safeId.data);
|
|
35
|
+
if (!entry) {
|
|
36
|
+
res.sendStatus(404);
|
|
37
|
+
} else {
|
|
38
|
+
const stream = fs.createReadStream("./files/" + entry.fileID);
|
|
39
|
+
stream.on("error", (_err) => {
|
|
40
|
+
// debugger: file stream read error
|
|
41
|
+
res.sendStatus(500);
|
|
42
|
+
});
|
|
43
|
+
stream.pipe(res);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
router.get("/:id/details", protect, async (req, res) => {
|
|
48
|
+
const safeId = safePathParam.safeParse(getParam(req, "id"));
|
|
49
|
+
if (!safeId.success) {
|
|
50
|
+
res.sendStatus(400);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const entry = await db.retrieveFile(safeId.data);
|
|
54
|
+
if (!entry) {
|
|
55
|
+
res.sendStatus(404);
|
|
56
|
+
} else {
|
|
57
|
+
fs.stat(path.resolve("./files/" + entry.fileID), (err, stat) => {
|
|
58
|
+
if (err) {
|
|
59
|
+
res.sendStatus(500);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
res.set("Cache-control", "public, max-age=31536000");
|
|
63
|
+
res.send(
|
|
64
|
+
msgpack.encode({
|
|
65
|
+
...entry,
|
|
66
|
+
birthtime: stat.birthtime,
|
|
67
|
+
size: stat.size,
|
|
68
|
+
}),
|
|
69
|
+
);
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
router.post("/json", protect, async (req, res) => {
|
|
75
|
+
const deviceDetails = req.device;
|
|
76
|
+
|
|
77
|
+
if (!deviceDetails) {
|
|
78
|
+
res.sendStatus(401);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const parsed = FilePayloadSchema.safeParse(req.body);
|
|
83
|
+
if (!parsed.success) {
|
|
84
|
+
res.status(400).json({
|
|
85
|
+
error: "Invalid file payload",
|
|
86
|
+
issues: parsed.error.issues,
|
|
87
|
+
});
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const payload = parsed.data;
|
|
91
|
+
|
|
92
|
+
if (payload.nonce === "") {
|
|
93
|
+
res.sendStatus(400);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (!payload.file) {
|
|
98
|
+
res.sendStatus(400);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const buf = Buffer.from(XUtils.decodeBase64(payload.file));
|
|
103
|
+
|
|
104
|
+
const newFile: FileSQL = {
|
|
105
|
+
fileID: crypto.randomUUID(),
|
|
106
|
+
nonce: payload.nonce,
|
|
107
|
+
owner: deviceDetails.deviceID,
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
await fsp.writeFile("files/" + newFile.fileID, buf);
|
|
111
|
+
await db.createFile(newFile);
|
|
112
|
+
res.send(msgpack.encode(newFile));
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
// Multipart file upload — form fields are strings from multer, not full FilePayload
|
|
116
|
+
const multipartFields = z.object({
|
|
117
|
+
nonce: z.string().min(1),
|
|
118
|
+
owner: z.string().min(1),
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
router.post(
|
|
122
|
+
"/",
|
|
123
|
+
uploadLimiter,
|
|
124
|
+
protect,
|
|
125
|
+
multer().single("file"),
|
|
126
|
+
async (req, res) => {
|
|
127
|
+
const deviceDetails = req.device;
|
|
128
|
+
|
|
129
|
+
if (!deviceDetails) {
|
|
130
|
+
res.sendStatus(400);
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const parsed = multipartFields.safeParse(req.body);
|
|
135
|
+
if (!parsed.success) {
|
|
136
|
+
res.status(400).json({
|
|
137
|
+
error: "Invalid file payload",
|
|
138
|
+
issues: parsed.error.issues,
|
|
139
|
+
});
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
const payload = parsed.data;
|
|
143
|
+
|
|
144
|
+
if (req.file === undefined) {
|
|
145
|
+
res.sendStatus(400);
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (payload.nonce === "") {
|
|
150
|
+
res.sendStatus(400);
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const newFile: FileSQL = {
|
|
155
|
+
fileID: crypto.randomUUID(),
|
|
156
|
+
nonce: payload.nonce,
|
|
157
|
+
owner: deviceDetails.deviceID,
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
await fsp.writeFile("files/" + newFile.fileID, req.file.buffer);
|
|
161
|
+
await db.createFile(newFile);
|
|
162
|
+
res.send(msgpack.encode(newFile));
|
|
163
|
+
},
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
return router;
|
|
167
|
+
};
|